205 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// CodeMirror, copyright (c) by Marijn Haverbeke and others
 | 
						|
// Distributed under an MIT license: https://codemirror.net/LICENSE
 | 
						|
 | 
						|
// Yacas mode copyright (c) 2015 by Grzegorz Mazur
 | 
						|
// Loosely based on mathematica mode by Calin Barbat
 | 
						|
 | 
						|
(function(mod) {
 | 
						|
  if (typeof exports == "object" && typeof module == "object") // CommonJS
 | 
						|
    mod(require("../../lib/codemirror"));
 | 
						|
  else if (typeof define == "function" && define.amd) // AMD
 | 
						|
    define(["../../lib/codemirror"], mod);
 | 
						|
  else // Plain browser env
 | 
						|
    mod(CodeMirror);
 | 
						|
})(function(CodeMirror) {
 | 
						|
"use strict";
 | 
						|
 | 
						|
CodeMirror.defineMode('yacas', function(_config, _parserConfig) {
 | 
						|
 | 
						|
  function words(str) {
 | 
						|
    var obj = {}, words = str.split(" ");
 | 
						|
    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
 | 
						|
    return obj;
 | 
						|
  }
 | 
						|
 | 
						|
  var bodiedOps = words("Assert BackQuote D Defun Deriv For ForEach FromFile " +
 | 
						|
                        "FromString Function Integrate InverseTaylor Limit " +
 | 
						|
                        "LocalSymbols Macro MacroRule MacroRulePattern " +
 | 
						|
                        "NIntegrate Rule RulePattern Subst TD TExplicitSum " +
 | 
						|
                        "TSum Taylor Taylor1 Taylor2 Taylor3 ToFile " +
 | 
						|
                        "ToStdout ToString TraceRule Until While");
 | 
						|
 | 
						|
  // patterns
 | 
						|
  var pFloatForm  = "(?:(?:\\.\\d+|\\d+\\.\\d*|\\d+)(?:[eE][+-]?\\d+)?)";
 | 
						|
  var pIdentifier = "(?:[a-zA-Z\\$'][a-zA-Z0-9\\$']*)";
 | 
						|
 | 
						|
  // regular expressions
 | 
						|
  var reFloatForm    = new RegExp(pFloatForm);
 | 
						|
  var reIdentifier   = new RegExp(pIdentifier);
 | 
						|
  var rePattern      = new RegExp(pIdentifier + "?_" + pIdentifier);
 | 
						|
  var reFunctionLike = new RegExp(pIdentifier + "\\s*\\(");
 | 
						|
 | 
						|
  function tokenBase(stream, state) {
 | 
						|
    var ch;
 | 
						|
 | 
						|
    // get next character
 | 
						|
    ch = stream.next();
 | 
						|
 | 
						|
    // string
 | 
						|
    if (ch === '"') {
 | 
						|
      state.tokenize = tokenString;
 | 
						|
      return state.tokenize(stream, state);
 | 
						|
    }
 | 
						|
 | 
						|
    // comment
 | 
						|
    if (ch === '/') {
 | 
						|
      if (stream.eat('*')) {
 | 
						|
        state.tokenize = tokenComment;
 | 
						|
        return state.tokenize(stream, state);
 | 
						|
      }
 | 
						|
      if (stream.eat("/")) {
 | 
						|
        stream.skipToEnd();
 | 
						|
        return "comment";
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // go back one character
 | 
						|
    stream.backUp(1);
 | 
						|
 | 
						|
    // update scope info
 | 
						|
    var m = stream.match(/^(\w+)\s*\(/, false);
 | 
						|
    if (m !== null && bodiedOps.hasOwnProperty(m[1]))
 | 
						|
      state.scopes.push('bodied');
 | 
						|
 | 
						|
    var scope = currentScope(state);
 | 
						|
 | 
						|
    if (scope === 'bodied' && ch === '[')
 | 
						|
      state.scopes.pop();
 | 
						|
 | 
						|
    if (ch === '[' || ch === '{' || ch === '(')
 | 
						|
      state.scopes.push(ch);
 | 
						|
 | 
						|
    scope = currentScope(state);
 | 
						|
 | 
						|
    if (scope === '[' && ch === ']' ||
 | 
						|
        scope === '{' && ch === '}' ||
 | 
						|
        scope === '(' && ch === ')')
 | 
						|
      state.scopes.pop();
 | 
						|
 | 
						|
    if (ch === ';') {
 | 
						|
      while (scope === 'bodied') {
 | 
						|
        state.scopes.pop();
 | 
						|
        scope = currentScope(state);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // look for ordered rules
 | 
						|
    if (stream.match(/\d+ *#/, true, false)) {
 | 
						|
      return 'qualifier';
 | 
						|
    }
 | 
						|
 | 
						|
    // look for numbers
 | 
						|
    if (stream.match(reFloatForm, true, false)) {
 | 
						|
      return 'number';
 | 
						|
    }
 | 
						|
 | 
						|
    // look for placeholders
 | 
						|
    if (stream.match(rePattern, true, false)) {
 | 
						|
      return 'variable-3';
 | 
						|
    }
 | 
						|
 | 
						|
    // match all braces separately
 | 
						|
    if (stream.match(/(?:\[|\]|{|}|\(|\))/, true, false)) {
 | 
						|
      return 'bracket';
 | 
						|
    }
 | 
						|
 | 
						|
    // literals looking like function calls
 | 
						|
    if (stream.match(reFunctionLike, true, false)) {
 | 
						|
      stream.backUp(1);
 | 
						|
      return 'variable';
 | 
						|
    }
 | 
						|
 | 
						|
    // all other identifiers
 | 
						|
    if (stream.match(reIdentifier, true, false)) {
 | 
						|
      return 'variable-2';
 | 
						|
    }
 | 
						|
 | 
						|
    // operators; note that operators like @@ or /; are matched separately for each symbol.
 | 
						|
    if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%|#)/, true, false)) {
 | 
						|
      return 'operator';
 | 
						|
    }
 | 
						|
 | 
						|
    // everything else is an error
 | 
						|
    return 'error';
 | 
						|
  }
 | 
						|
 | 
						|
  function tokenString(stream, state) {
 | 
						|
    var next, end = false, escaped = false;
 | 
						|
    while ((next = stream.next()) != null) {
 | 
						|
      if (next === '"' && !escaped) {
 | 
						|
        end = true;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      escaped = !escaped && next === '\\';
 | 
						|
    }
 | 
						|
    if (end && !escaped) {
 | 
						|
      state.tokenize = tokenBase;
 | 
						|
    }
 | 
						|
    return 'string';
 | 
						|
  };
 | 
						|
 | 
						|
  function tokenComment(stream, state) {
 | 
						|
    var prev, next;
 | 
						|
    while((next = stream.next()) != null) {
 | 
						|
      if (prev === '*' && next === '/') {
 | 
						|
        state.tokenize = tokenBase;
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      prev = next;
 | 
						|
    }
 | 
						|
    return 'comment';
 | 
						|
  }
 | 
						|
 | 
						|
  function currentScope(state) {
 | 
						|
    var scope = null;
 | 
						|
    if (state.scopes.length > 0)
 | 
						|
      scope = state.scopes[state.scopes.length - 1];
 | 
						|
    return scope;
 | 
						|
  }
 | 
						|
 | 
						|
  return {
 | 
						|
    startState: function() {
 | 
						|
      return {
 | 
						|
        tokenize: tokenBase,
 | 
						|
        scopes: []
 | 
						|
      };
 | 
						|
    },
 | 
						|
    token: function(stream, state) {
 | 
						|
      if (stream.eatSpace()) return null;
 | 
						|
      return state.tokenize(stream, state);
 | 
						|
    },
 | 
						|
    indent: function(state, textAfter) {
 | 
						|
      if (state.tokenize !== tokenBase && state.tokenize !== null)
 | 
						|
        return CodeMirror.Pass;
 | 
						|
 | 
						|
      var delta = 0;
 | 
						|
      if (textAfter === ']' || textAfter === '];' ||
 | 
						|
          textAfter === '}' || textAfter === '};' ||
 | 
						|
          textAfter === ');')
 | 
						|
        delta = -1;
 | 
						|
 | 
						|
      return (state.scopes.length + delta) * _config.indentUnit;
 | 
						|
    },
 | 
						|
    electricChars: "{}[]();",
 | 
						|
    blockCommentStart: "/*",
 | 
						|
    blockCommentEnd: "*/",
 | 
						|
    lineComment: "//"
 | 
						|
  };
 | 
						|
});
 | 
						|
 | 
						|
CodeMirror.defineMIME('text/x-yacas', {
 | 
						|
  name: 'yacas'
 | 
						|
});
 | 
						|
 | 
						|
});
 |