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});