all repos — NoPaste @ 29b774f090102303e43cf939b38ac2083e62d9f1

Resurrected - The PussTheCat.org fork of NoPaste

scripts/CodeMirror/mode/yacas/yacas.js (view raw)

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