all repos — NoPaste @ edb00b0ab136a96023b51a3e1c59d21a807d66a2

Resurrected - The PussTheCat.org fork of NoPaste

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

  1// CodeMirror, copyright (c) by Marijn Haverbeke and others
  2// Distributed under an MIT license: https://codemirror.net/LICENSE
  3
  4// Mathematica mode copyright (c) 2015 by Calin Barbat
  5// Based on code by Patrick Scheibe (halirutan)
  6// See: https://github.com/halirutan/Mathematica-Source-Highlighting/tree/master/src/lang-mma.js
  7
  8(function(mod) {
  9  if (typeof exports == "object" && typeof module == "object") // CommonJS
 10    mod(require("../../lib/codemirror"));
 11  else if (typeof define == "function" && define.amd) // AMD
 12    define(["../../lib/codemirror"], mod);
 13  else // Plain browser env
 14    mod(CodeMirror);
 15})(function(CodeMirror) {
 16"use strict";
 17
 18CodeMirror.defineMode('mathematica', function(_config, _parserConfig) {
 19
 20  // used pattern building blocks
 21  var Identifier = '[a-zA-Z\\$][a-zA-Z0-9\\$]*';
 22  var pBase      = "(?:\\d+)";
 23  var pFloat     = "(?:\\.\\d+|\\d+\\.\\d*|\\d+)";
 24  var pFloatBase = "(?:\\.\\w+|\\w+\\.\\w*|\\w+)";
 25  var pPrecision = "(?:`(?:`?"+pFloat+")?)";
 26
 27  // regular expressions
 28  var reBaseForm        = new RegExp('(?:'+pBase+'(?:\\^\\^'+pFloatBase+pPrecision+'?(?:\\*\\^[+-]?\\d+)?))');
 29  var reFloatForm       = new RegExp('(?:' + pFloat + pPrecision + '?(?:\\*\\^[+-]?\\d+)?)');
 30  var reIdInContext     = new RegExp('(?:`?)(?:' + Identifier + ')(?:`(?:' + Identifier + '))*(?:`?)');
 31
 32  function tokenBase(stream, state) {
 33    var ch;
 34
 35    // get next character
 36    ch = stream.next();
 37
 38    // string
 39    if (ch === '"') {
 40      state.tokenize = tokenString;
 41      return state.tokenize(stream, state);
 42    }
 43
 44    // comment
 45    if (ch === '(') {
 46      if (stream.eat('*')) {
 47        state.commentLevel++;
 48        state.tokenize = tokenComment;
 49        return state.tokenize(stream, state);
 50      }
 51    }
 52
 53    // go back one character
 54    stream.backUp(1);
 55
 56    // look for numbers
 57    // Numbers in a baseform
 58    if (stream.match(reBaseForm, true, false)) {
 59      return 'number';
 60    }
 61
 62    // Mathematica numbers. Floats (1.2, .2, 1.) can have optionally a precision (`float) or an accuracy definition
 63    // (``float). Note: while 1.2` is possible 1.2`` is not. At the end an exponent (float*^+12) can follow.
 64    if (stream.match(reFloatForm, true, false)) {
 65      return 'number';
 66    }
 67
 68    /* In[23] and Out[34] */
 69    if (stream.match(/(?:In|Out)\[[0-9]*\]/, true, false)) {
 70      return 'atom';
 71    }
 72
 73    // usage
 74    if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::usage)/, true, false)) {
 75      return 'meta';
 76    }
 77
 78    // message
 79    if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::[a-zA-Z\$][a-zA-Z0-9\$]*):?/, true, false)) {
 80      return 'string-2';
 81    }
 82
 83    // this makes a look-ahead match for something like variable:{_Integer}
 84    // the match is then forwarded to the mma-patterns tokenizer.
 85    if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*\s*:)(?:(?:[a-zA-Z\$][a-zA-Z0-9\$]*)|(?:[^:=>~@\^\&\*\)\[\]'\?,\|])).*/, true, false)) {
 86      return 'variable-2';
 87    }
 88
 89    // catch variables which are used together with Blank (_), BlankSequence (__) or BlankNullSequence (___)
 90    // Cannot start with a number, but can have numbers at any other position. Examples
 91    // blub__Integer, a1_, b34_Integer32
 92    if (stream.match(/[a-zA-Z\$][a-zA-Z0-9\$]*_+[a-zA-Z\$][a-zA-Z0-9\$]*/, true, false)) {
 93      return 'variable-2';
 94    }
 95    if (stream.match(/[a-zA-Z\$][a-zA-Z0-9\$]*_+/, true, false)) {
 96      return 'variable-2';
 97    }
 98    if (stream.match(/_+[a-zA-Z\$][a-zA-Z0-9\$]*/, true, false)) {
 99      return 'variable-2';
100    }
101
102    // Named characters in Mathematica, like \[Gamma].
103    if (stream.match(/\\\[[a-zA-Z\$][a-zA-Z0-9\$]*\]/, true, false)) {
104      return 'variable-3';
105    }
106
107    // Match all braces separately
108    if (stream.match(/(?:\[|\]|{|}|\(|\))/, true, false)) {
109      return 'bracket';
110    }
111
112    // Catch Slots (#, ##, #3, ##9 and the V10 named slots #name). I have never seen someone using more than one digit after #, so we match
113    // only one.
114    if (stream.match(/(?:#[a-zA-Z\$][a-zA-Z0-9\$]*|#+[0-9]?)/, true, false)) {
115      return 'variable-2';
116    }
117
118    // Literals like variables, keywords, functions
119    if (stream.match(reIdInContext, true, false)) {
120      return 'keyword';
121    }
122
123    // operators. Note that operators like @@ or /; are matched separately for each symbol.
124    if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%)/, true, false)) {
125      return 'operator';
126    }
127
128    // everything else is an error
129    stream.next(); // advance the stream.
130    return 'error';
131  }
132
133  function tokenString(stream, state) {
134    var next, end = false, escaped = false;
135    while ((next = stream.next()) != null) {
136      if (next === '"' && !escaped) {
137        end = true;
138        break;
139      }
140      escaped = !escaped && next === '\\';
141    }
142    if (end && !escaped) {
143      state.tokenize = tokenBase;
144    }
145    return 'string';
146  };
147
148  function tokenComment(stream, state) {
149    var prev, next;
150    while(state.commentLevel > 0 && (next = stream.next()) != null) {
151      if (prev === '(' && next === '*') state.commentLevel++;
152      if (prev === '*' && next === ')') state.commentLevel--;
153      prev = next;
154    }
155    if (state.commentLevel <= 0) {
156      state.tokenize = tokenBase;
157    }
158    return 'comment';
159  }
160
161  return {
162    startState: function() {return {tokenize: tokenBase, commentLevel: 0};},
163    token: function(stream, state) {
164      if (stream.eatSpace()) return null;
165      return state.tokenize(stream, state);
166    },
167    blockCommentStart: "(*",
168    blockCommentEnd: "*)"
169  };
170});
171
172CodeMirror.defineMIME('text/x-mathematica', {
173  name: 'mathematica'
174});
175
176});