ebnf.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // CodeMirror, copyright (c) by Marijn Haverbeke and others
  2. // Distributed under an MIT license: https://codemirror.net/LICENSE
  3. ;(function (mod) {
  4. if (typeof exports == 'object' && typeof module == 'object')
  5. // CommonJS
  6. mod(require('../../lib/codemirror'))
  7. else if (typeof define == 'function' && define.amd)
  8. // AMD
  9. define(['../../lib/codemirror'], mod)
  10. // Plain browser env
  11. else mod(CodeMirror)
  12. })(function (CodeMirror) {
  13. 'use strict'
  14. CodeMirror.defineMode('ebnf', function (config) {
  15. var commentType = { slash: 0, parenthesis: 1 }
  16. var stateType = { comment: 0, _string: 1, characterClass: 2 }
  17. var bracesMode = null
  18. if (config.bracesMode) bracesMode = CodeMirror.getMode(config, config.bracesMode)
  19. return {
  20. startState: function () {
  21. return {
  22. stringType: null,
  23. commentType: null,
  24. braced: 0,
  25. lhs: true,
  26. localState: null,
  27. stack: [],
  28. inDefinition: false,
  29. }
  30. },
  31. token: function (stream, state) {
  32. if (!stream) return
  33. //check for state changes
  34. if (state.stack.length === 0) {
  35. //strings
  36. if (stream.peek() == '"' || stream.peek() == "'") {
  37. state.stringType = stream.peek()
  38. stream.next() // Skip quote
  39. state.stack.unshift(stateType._string)
  40. } else if (stream.match('/*')) {
  41. //comments starting with /*
  42. state.stack.unshift(stateType.comment)
  43. state.commentType = commentType.slash
  44. } else if (stream.match('(*')) {
  45. //comments starting with (*
  46. state.stack.unshift(stateType.comment)
  47. state.commentType = commentType.parenthesis
  48. }
  49. }
  50. //return state
  51. //stack has
  52. switch (state.stack[0]) {
  53. case stateType._string:
  54. while (state.stack[0] === stateType._string && !stream.eol()) {
  55. if (stream.peek() === state.stringType) {
  56. stream.next() // Skip quote
  57. state.stack.shift() // Clear flag
  58. } else if (stream.peek() === '\\') {
  59. stream.next()
  60. stream.next()
  61. } else {
  62. stream.match(/^.[^\\\"\']*/)
  63. }
  64. }
  65. return state.lhs ? 'property string' : 'string' // Token style
  66. case stateType.comment:
  67. while (state.stack[0] === stateType.comment && !stream.eol()) {
  68. if (state.commentType === commentType.slash && stream.match('*/')) {
  69. state.stack.shift() // Clear flag
  70. state.commentType = null
  71. } else if (state.commentType === commentType.parenthesis && stream.match('*)')) {
  72. state.stack.shift() // Clear flag
  73. state.commentType = null
  74. } else {
  75. stream.match(/^.[^\*]*/)
  76. }
  77. }
  78. return 'comment'
  79. case stateType.characterClass:
  80. while (state.stack[0] === stateType.characterClass && !stream.eol()) {
  81. if (!(stream.match(/^[^\]\\]+/) || stream.match('.'))) {
  82. state.stack.shift()
  83. }
  84. }
  85. return 'operator'
  86. }
  87. var peek = stream.peek()
  88. if (bracesMode !== null && (state.braced || peek === '{')) {
  89. if (state.localState === null) state.localState = CodeMirror.startState(bracesMode)
  90. var token = bracesMode.token(stream, state.localState),
  91. text = stream.current()
  92. if (!token) {
  93. for (var i = 0; i < text.length; i++) {
  94. if (text[i] === '{') {
  95. if (state.braced === 0) {
  96. token = 'matchingbracket'
  97. }
  98. state.braced++
  99. } else if (text[i] === '}') {
  100. state.braced--
  101. if (state.braced === 0) {
  102. token = 'matchingbracket'
  103. }
  104. }
  105. }
  106. }
  107. return token
  108. }
  109. //no stack
  110. switch (peek) {
  111. case '[':
  112. stream.next()
  113. state.stack.unshift(stateType.characterClass)
  114. return 'bracket'
  115. case ':':
  116. case '|':
  117. case ';':
  118. stream.next()
  119. return 'operator'
  120. case '%':
  121. if (stream.match('%%')) {
  122. return 'header'
  123. } else if (stream.match(/[%][A-Za-z]+/)) {
  124. return 'keyword'
  125. } else if (stream.match(/[%][}]/)) {
  126. return 'matchingbracket'
  127. }
  128. break
  129. case '/':
  130. if (stream.match(/[\/][A-Za-z]+/)) {
  131. return 'keyword'
  132. }
  133. case '\\':
  134. if (stream.match(/[\][a-z]+/)) {
  135. return 'string-2'
  136. }
  137. case '.':
  138. if (stream.match('.')) {
  139. return 'atom'
  140. }
  141. case '*':
  142. case '-':
  143. case '+':
  144. case '^':
  145. if (stream.match(peek)) {
  146. return 'atom'
  147. }
  148. case '$':
  149. if (stream.match('$$')) {
  150. return 'builtin'
  151. } else if (stream.match(/[$][0-9]+/)) {
  152. return 'variable-3'
  153. }
  154. case '<':
  155. if (stream.match(/<<[a-zA-Z_]+>>/)) {
  156. return 'builtin'
  157. }
  158. }
  159. if (stream.match('//')) {
  160. stream.skipToEnd()
  161. return 'comment'
  162. } else if (stream.match('return')) {
  163. return 'operator'
  164. } else if (stream.match(/^[a-zA-Z_][a-zA-Z0-9_]*/)) {
  165. if (stream.match(/(?=[\(.])/)) {
  166. return 'variable'
  167. } else if (stream.match(/(?=[\s\n]*[:=])/)) {
  168. return 'def'
  169. }
  170. return 'variable-2'
  171. } else if (['[', ']', '(', ')'].indexOf(stream.peek()) != -1) {
  172. stream.next()
  173. return 'bracket'
  174. } else if (!stream.eatSpace()) {
  175. stream.next()
  176. }
  177. return null
  178. },
  179. }
  180. })
  181. CodeMirror.defineMIME('text/x-ebnf', 'ebnf')
  182. })