123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- // 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',
- })
- })
|