You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
5.3 KiB

7 months ago
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/5/LICENSE
  3. // Yacas mode copyright (c) 2015 by Grzegorz Mazur
  4. // Loosely based on mathematica mode by Calin Barbat
  5. (function(mod) {
  6. if (typeof exports == "object" && typeof module == "object") // CommonJS
  7. mod(require("../../lib/codemirror"));
  8. else if (typeof define == "function" && define.amd) // AMD
  9. define(["../../lib/codemirror"], mod);
  10. else // Plain browser env
  11. mod(CodeMirror);
  12. })(function(CodeMirror) {
  13. "use strict";
  14. CodeMirror.defineMode('yacas', function(_config, _parserConfig) {
  15. function words(str) {
  16. var obj = {}, words = str.split(" ");
  17. for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
  18. return obj;
  19. }
  20. var bodiedOps = words("Assert BackQuote D Defun Deriv For ForEach FromFile " +
  21. "FromString Function Integrate InverseTaylor Limit " +
  22. "LocalSymbols Macro MacroRule MacroRulePattern " +
  23. "NIntegrate Rule RulePattern Subst TD TExplicitSum " +
  24. "TSum Taylor Taylor1 Taylor2 Taylor3 ToFile " +
  25. "ToStdout ToString TraceRule Until While");
  26. // patterns
  27. var pFloatForm = "(?:(?:\\.\\d+|\\d+\\.\\d*|\\d+)(?:[eE][+-]?\\d+)?)";
  28. var pIdentifier = "(?:[a-zA-Z\\$'][a-zA-Z0-9\\$']*)";
  29. // regular expressions
  30. var reFloatForm = new RegExp(pFloatForm);
  31. var reIdentifier = new RegExp(pIdentifier);
  32. var rePattern = new RegExp(pIdentifier + "?_" + pIdentifier);
  33. var reFunctionLike = new RegExp(pIdentifier + "\\s*\\(");
  34. function tokenBase(stream, state) {
  35. var ch;
  36. // get next character
  37. ch = stream.next();
  38. // string
  39. if (ch === '"') {
  40. state.tokenize = tokenString;
  41. return state.tokenize(stream, state);
  42. }
  43. // comment
  44. if (ch === '/') {
  45. if (stream.eat('*')) {
  46. state.tokenize = tokenComment;
  47. return state.tokenize(stream, state);
  48. }
  49. if (stream.eat("/")) {
  50. stream.skipToEnd();
  51. return "comment";
  52. }
  53. }
  54. // go back one character
  55. stream.backUp(1);
  56. // update scope info
  57. var m = stream.match(/^(\w+)\s*\(/, false);
  58. if (m !== null && bodiedOps.hasOwnProperty(m[1]))
  59. state.scopes.push('bodied');
  60. var scope = currentScope(state);
  61. if (scope === 'bodied' && ch === '[')
  62. state.scopes.pop();
  63. if (ch === '[' || ch === '{' || ch === '(')
  64. state.scopes.push(ch);
  65. scope = currentScope(state);
  66. if (scope === '[' && ch === ']' ||
  67. scope === '{' && ch === '}' ||
  68. scope === '(' && ch === ')')
  69. state.scopes.pop();
  70. if (ch === ';') {
  71. while (scope === 'bodied') {
  72. state.scopes.pop();
  73. scope = currentScope(state);
  74. }
  75. }
  76. // look for ordered rules
  77. if (stream.match(/\d+ *#/, true, false)) {
  78. return 'qualifier';
  79. }
  80. // look for numbers
  81. if (stream.match(reFloatForm, true, false)) {
  82. return 'number';
  83. }
  84. // look for placeholders
  85. if (stream.match(rePattern, true, false)) {
  86. return 'variable-3';
  87. }
  88. // match all braces separately
  89. if (stream.match(/(?:\[|\]|{|}|\(|\))/, true, false)) {
  90. return 'bracket';
  91. }
  92. // literals looking like function calls
  93. if (stream.match(reFunctionLike, true, false)) {
  94. stream.backUp(1);
  95. return 'variable';
  96. }
  97. // all other identifiers
  98. if (stream.match(reIdentifier, true, false)) {
  99. return 'variable-2';
  100. }
  101. // operators; note that operators like @@ or /; are matched separately for each symbol.
  102. if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%|#)/, true, false)) {
  103. return 'operator';
  104. }
  105. // everything else is an error
  106. return 'error';
  107. }
  108. function tokenString(stream, state) {
  109. var next, end = false, escaped = false;
  110. while ((next = stream.next()) != null) {
  111. if (next === '"' && !escaped) {
  112. end = true;
  113. break;
  114. }
  115. escaped = !escaped && next === '\\';
  116. }
  117. if (end && !escaped) {
  118. state.tokenize = tokenBase;
  119. }
  120. return 'string';
  121. };
  122. function tokenComment(stream, state) {
  123. var prev, next;
  124. while((next = stream.next()) != null) {
  125. if (prev === '*' && next === '/') {
  126. state.tokenize = tokenBase;
  127. break;
  128. }
  129. prev = next;
  130. }
  131. return 'comment';
  132. }
  133. function currentScope(state) {
  134. var scope = null;
  135. if (state.scopes.length > 0)
  136. scope = state.scopes[state.scopes.length - 1];
  137. return scope;
  138. }
  139. return {
  140. startState: function() {
  141. return {
  142. tokenize: tokenBase,
  143. scopes: []
  144. };
  145. },
  146. token: function(stream, state) {
  147. if (stream.eatSpace()) return null;
  148. return state.tokenize(stream, state);
  149. },
  150. indent: function(state, textAfter) {
  151. if (state.tokenize !== tokenBase && state.tokenize !== null)
  152. return CodeMirror.Pass;
  153. var delta = 0;
  154. if (textAfter === ']' || textAfter === '];' ||
  155. textAfter === '}' || textAfter === '};' ||
  156. textAfter === ');')
  157. delta = -1;
  158. return (state.scopes.length + delta) * _config.indentUnit;
  159. },
  160. electricChars: "{}[]();",
  161. blockCommentStart: "/*",
  162. blockCommentEnd: "*/",
  163. lineComment: "//"
  164. };
  165. });
  166. CodeMirror.defineMIME('text/x-yacas', {
  167. name: 'yacas'
  168. });
  169. });