// CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE // Mathematica mode copyright (c) 2015 by Calin Barbat // Based on code by Patrick Scheibe (halirutan) // See: https://github.com/halirutan/Mathematica-Source-Highlighting/tree/master/src/lang-mma.js ;(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) // Plain browser env else mod(CodeMirror) })(function (CodeMirror) { 'use strict' CodeMirror.defineMode('mathematica', function (_config, _parserConfig) { // used pattern building blocks var Identifier = '[a-zA-Z\\$][a-zA-Z0-9\\$]*' var pBase = '(?:\\d+)' var pFloat = '(?:\\.\\d+|\\d+\\.\\d*|\\d+)' var pFloatBase = '(?:\\.\\w+|\\w+\\.\\w*|\\w+)' var pPrecision = '(?:`(?:`?' + pFloat + ')?)' // regular expressions var reBaseForm = new RegExp('(?:' + pBase + '(?:\\^\\^' + pFloatBase + pPrecision + '?(?:\\*\\^[+-]?\\d+)?))') var reFloatForm = new RegExp('(?:' + pFloat + pPrecision + '?(?:\\*\\^[+-]?\\d+)?)') var reIdInContext = new RegExp('(?:`?)(?:' + Identifier + ')(?:`(?:' + Identifier + '))*(?:`?)') 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.commentLevel++ state.tokenize = tokenComment return state.tokenize(stream, state) } } // go back one character stream.backUp(1) // look for numbers // Numbers in a baseform if (stream.match(reBaseForm, true, false)) { return 'number' } // Mathematica numbers. Floats (1.2, .2, 1.) can have optionally a precision (`float) or an accuracy definition // (``float). Note: while 1.2` is possible 1.2`` is not. At the end an exponent (float*^+12) can follow. if (stream.match(reFloatForm, true, false)) { return 'number' } /* In[23] and Out[34] */ if (stream.match(/(?:In|Out)\[[0-9]*\]/, true, false)) { return 'atom' } // usage if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::usage)/, true, false)) { return 'meta' } // message if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::[a-zA-Z\$][a-zA-Z0-9\$]*):?/, true, false)) { return 'string-2' } // this makes a look-ahead match for something like variable:{_Integer} // the match is then forwarded to the mma-patterns tokenizer. if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*\s*:)(?:(?:[a-zA-Z\$][a-zA-Z0-9\$]*)|(?:[^:=>~@\^\&\*\)\[\]'\?,\|])).*/, true, false)) { return 'variable-2' } // catch variables which are used together with Blank (_), BlankSequence (__) or BlankNullSequence (___) // Cannot start with a number, but can have numbers at any other position. Examples // blub__Integer, a1_, b34_Integer32 if (stream.match(/[a-zA-Z\$][a-zA-Z0-9\$]*_+[a-zA-Z\$][a-zA-Z0-9\$]*/, true, false)) { return 'variable-2' } if (stream.match(/[a-zA-Z\$][a-zA-Z0-9\$]*_+/, true, false)) { return 'variable-2' } if (stream.match(/_+[a-zA-Z\$][a-zA-Z0-9\$]*/, true, false)) { return 'variable-2' } // Named characters in Mathematica, like \[Gamma]. if (stream.match(/\\\[[a-zA-Z\$][a-zA-Z0-9\$]*\]/, true, false)) { return 'variable-3' } // Match all braces separately if (stream.match(/(?:\[|\]|{|}|\(|\))/, true, false)) { return 'bracket' } // Catch Slots (#, ##, #3, ##9 and the V10 named slots #name). I have never seen someone using more than one digit after #, so we match // only one. if (stream.match(/(?:#[a-zA-Z\$][a-zA-Z0-9\$]*|#+[0-9]?)/, true, false)) { return 'variable-2' } // Literals like variables, keywords, functions if (stream.match(reIdInContext, true, false)) { return 'keyword' } // operators. Note that operators like @@ or /; are matched separately for each symbol. if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%)/, true, false)) { return 'operator' } // everything else is an error stream.next() // advance the stream. 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 (state.commentLevel > 0 && (next = stream.next()) != null) { if (prev === '(' && next === '*') state.commentLevel++ if (prev === '*' && next === ')') state.commentLevel-- prev = next } if (state.commentLevel <= 0) { state.tokenize = tokenBase } return 'comment' } return { startState: function () { return { tokenize: tokenBase, commentLevel: 0 } }, token: function (stream, state) { if (stream.eatSpace()) return null return state.tokenize(stream, state) }, blockCommentStart: '(*', blockCommentEnd: '*)', } }) CodeMirror.defineMIME('text/x-mathematica', { name: 'mathematica', }) })