0
|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
2 // Distributed under an MIT license: https://codemirror.net/5/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
|
|
18 CodeMirror.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
|
|
172 CodeMirror.defineMIME('text/x-mathematica', {
|
|
173 name: 'mathematica'
|
|
174 });
|
|
175
|
|
176 });
|