0
|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
|
|
2 // Distributed under an MIT license: https://codemirror.net/5/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
|
|
17 CodeMirror.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
|
|
200 CodeMirror.defineMIME('text/x-yacas', {
|
|
201 name: 'yacas'
|
|
202 });
|
|
203
|
|
204 });
|