mathematica.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. // Mathematica mode copyright (c) 2015 by Calin Barbat
  4. // Based on code by Patrick Scheibe (halirutan)
  5. // See: https://github.com/halirutan/Mathematica-Source-Highlighting/tree/master/src/lang-mma.js
  6. ;(function (mod) {
  7. if (typeof exports == 'object' && typeof module == 'object')
  8. // CommonJS
  9. mod(require('../../lib/codemirror'))
  10. else if (typeof define == 'function' && define.amd)
  11. // AMD
  12. define(['../../lib/codemirror'], mod)
  13. // Plain browser env
  14. else mod(CodeMirror)
  15. })(function (CodeMirror) {
  16. 'use strict'
  17. CodeMirror.defineMode('mathematica', function (_config, _parserConfig) {
  18. // used pattern building blocks
  19. var Identifier = '[a-zA-Z\\$][a-zA-Z0-9\\$]*'
  20. var pBase = '(?:\\d+)'
  21. var pFloat = '(?:\\.\\d+|\\d+\\.\\d*|\\d+)'
  22. var pFloatBase = '(?:\\.\\w+|\\w+\\.\\w*|\\w+)'
  23. var pPrecision = '(?:`(?:`?' + pFloat + ')?)'
  24. // regular expressions
  25. var reBaseForm = new RegExp('(?:' + pBase + '(?:\\^\\^' + pFloatBase + pPrecision + '?(?:\\*\\^[+-]?\\d+)?))')
  26. var reFloatForm = new RegExp('(?:' + pFloat + pPrecision + '?(?:\\*\\^[+-]?\\d+)?)')
  27. var reIdInContext = new RegExp('(?:`?)(?:' + Identifier + ')(?:`(?:' + Identifier + '))*(?:`?)')
  28. function tokenBase(stream, state) {
  29. var ch
  30. // get next character
  31. ch = stream.next()
  32. // string
  33. if (ch === '"') {
  34. state.tokenize = tokenString
  35. return state.tokenize(stream, state)
  36. }
  37. // comment
  38. if (ch === '(') {
  39. if (stream.eat('*')) {
  40. state.commentLevel++
  41. state.tokenize = tokenComment
  42. return state.tokenize(stream, state)
  43. }
  44. }
  45. // go back one character
  46. stream.backUp(1)
  47. // look for numbers
  48. // Numbers in a baseform
  49. if (stream.match(reBaseForm, true, false)) {
  50. return 'number'
  51. }
  52. // Mathematica numbers. Floats (1.2, .2, 1.) can have optionally a precision (`float) or an accuracy definition
  53. // (``float). Note: while 1.2` is possible 1.2`` is not. At the end an exponent (float*^+12) can follow.
  54. if (stream.match(reFloatForm, true, false)) {
  55. return 'number'
  56. }
  57. /* In[23] and Out[34] */
  58. if (stream.match(/(?:In|Out)\[[0-9]*\]/, true, false)) {
  59. return 'atom'
  60. }
  61. // usage
  62. if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::usage)/, true, false)) {
  63. return 'meta'
  64. }
  65. // message
  66. if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::[a-zA-Z\$][a-zA-Z0-9\$]*):?/, true, false)) {
  67. return 'string-2'
  68. }
  69. // this makes a look-ahead match for something like variable:{_Integer}
  70. // the match is then forwarded to the mma-patterns tokenizer.
  71. if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*\s*:)(?:(?:[a-zA-Z\$][a-zA-Z0-9\$]*)|(?:[^:=>~@\^\&\*\)\[\]'\?,\|])).*/, true, false)) {
  72. return 'variable-2'
  73. }
  74. // catch variables which are used together with Blank (_), BlankSequence (__) or BlankNullSequence (___)
  75. // Cannot start with a number, but can have numbers at any other position. Examples
  76. // blub__Integer, a1_, b34_Integer32
  77. if (stream.match(/[a-zA-Z\$][a-zA-Z0-9\$]*_+[a-zA-Z\$][a-zA-Z0-9\$]*/, true, false)) {
  78. return 'variable-2'
  79. }
  80. if (stream.match(/[a-zA-Z\$][a-zA-Z0-9\$]*_+/, true, false)) {
  81. return 'variable-2'
  82. }
  83. if (stream.match(/_+[a-zA-Z\$][a-zA-Z0-9\$]*/, true, false)) {
  84. return 'variable-2'
  85. }
  86. // Named characters in Mathematica, like \[Gamma].
  87. if (stream.match(/\\\[[a-zA-Z\$][a-zA-Z0-9\$]*\]/, true, false)) {
  88. return 'variable-3'
  89. }
  90. // Match all braces separately
  91. if (stream.match(/(?:\[|\]|{|}|\(|\))/, true, false)) {
  92. return 'bracket'
  93. }
  94. // Catch Slots (#, ##, #3, ##9 and the V10 named slots #name). I have never seen someone using more than one digit after #, so we match
  95. // only one.
  96. if (stream.match(/(?:#[a-zA-Z\$][a-zA-Z0-9\$]*|#+[0-9]?)/, true, false)) {
  97. return 'variable-2'
  98. }
  99. // Literals like variables, keywords, functions
  100. if (stream.match(reIdInContext, true, false)) {
  101. return 'keyword'
  102. }
  103. // operators. Note that operators like @@ or /; are matched separately for each symbol.
  104. if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%)/, true, false)) {
  105. return 'operator'
  106. }
  107. // everything else is an error
  108. stream.next() // advance the stream.
  109. return 'error'
  110. }
  111. function tokenString(stream, state) {
  112. var next,
  113. end = false,
  114. escaped = false
  115. while ((next = stream.next()) != null) {
  116. if (next === '"' && !escaped) {
  117. end = true
  118. break
  119. }
  120. escaped = !escaped && next === '\\'
  121. }
  122. if (end && !escaped) {
  123. state.tokenize = tokenBase
  124. }
  125. return 'string'
  126. }
  127. function tokenComment(stream, state) {
  128. var prev, next
  129. while (state.commentLevel > 0 && (next = stream.next()) != null) {
  130. if (prev === '(' && next === '*') state.commentLevel++
  131. if (prev === '*' && next === ')') state.commentLevel--
  132. prev = next
  133. }
  134. if (state.commentLevel <= 0) {
  135. state.tokenize = tokenBase
  136. }
  137. return 'comment'
  138. }
  139. return {
  140. startState: function () {
  141. return { tokenize: tokenBase, commentLevel: 0 }
  142. },
  143. token: function (stream, state) {
  144. if (stream.eatSpace()) return null
  145. return state.tokenize(stream, state)
  146. },
  147. blockCommentStart: '(*',
  148. blockCommentEnd: '*)',
  149. }
  150. })
  151. CodeMirror.defineMIME('text/x-mathematica', {
  152. name: 'mathematica',
  153. })
  154. })