yacas.js
204 lines
| 5.3 KiB
| application/javascript
|
JavascriptLexer
r4105 | // 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' | ||||
}); | ||||
}); | ||||