diff --git a/mode/yacas/yacas.js b/mode/yacas/yacas.js index 51399fbf5942c6fad4e4436208e87badab61d00c..30bd60b2ffadf340ac753a2406e7c38b1190947e 100644 --- a/mode/yacas/yacas.js +++ b/mode/yacas/yacas.js @@ -16,6 +16,19 @@ 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\\$']*)"; @@ -53,6 +66,33 @@ CodeMirror.defineMode('yacas', function(_config, _parserConfig) { // 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'; @@ -120,12 +160,37 @@ CodeMirror.defineMode('yacas', function(_config, _parserConfig) { 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, commentLevel: 0};}, + 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: "//"