ebnf.js 5.9 KB

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