haskell.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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") // 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("haskell", function(_config, modeConfig) {
  13. function switchState(source, setState, f) {
  14. setState(f);
  15. return f(source, setState);
  16. }
  17. // These should all be Unicode extended, as per the Haskell 2010 report
  18. var smallRE = /[a-z_]/;
  19. var largeRE = /[A-Z]/;
  20. var digitRE = /\d/;
  21. var hexitRE = /[0-9A-Fa-f]/;
  22. var octitRE = /[0-7]/;
  23. var idRE = /[a-z_A-Z0-9'\xa1-\uffff]/;
  24. var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:]/;
  25. var specialRE = /[(),;[\]`{}]/;
  26. var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer
  27. function normal(source, setState) {
  28. if (source.eatWhile(whiteCharRE)) {
  29. return null;
  30. }
  31. var ch = source.next();
  32. if (specialRE.test(ch)) {
  33. if (ch == '{' && source.eat('-')) {
  34. var t = "comment";
  35. if (source.eat('#')) {
  36. t = "meta";
  37. }
  38. return switchState(source, setState, ncomment(t, 1));
  39. }
  40. return null;
  41. }
  42. if (ch == '\'') {
  43. if (source.eat('\\')) {
  44. source.next(); // should handle other escapes here
  45. }
  46. else {
  47. source.next();
  48. }
  49. if (source.eat('\'')) {
  50. return "string";
  51. }
  52. return "string error";
  53. }
  54. if (ch == '"') {
  55. return switchState(source, setState, stringLiteral);
  56. }
  57. if (largeRE.test(ch)) {
  58. source.eatWhile(idRE);
  59. if (source.eat('.')) {
  60. return "qualifier";
  61. }
  62. return "variable-2";
  63. }
  64. if (smallRE.test(ch)) {
  65. source.eatWhile(idRE);
  66. return "variable";
  67. }
  68. if (digitRE.test(ch)) {
  69. if (ch == '0') {
  70. if (source.eat(/[xX]/)) {
  71. source.eatWhile(hexitRE); // should require at least 1
  72. return "integer";
  73. }
  74. if (source.eat(/[oO]/)) {
  75. source.eatWhile(octitRE); // should require at least 1
  76. return "number";
  77. }
  78. }
  79. source.eatWhile(digitRE);
  80. var t = "number";
  81. if (source.match(/^\.\d+/)) {
  82. t = "number";
  83. }
  84. if (source.eat(/[eE]/)) {
  85. t = "number";
  86. source.eat(/[-+]/);
  87. source.eatWhile(digitRE); // should require at least 1
  88. }
  89. return t;
  90. }
  91. if (ch == "." && source.eat("."))
  92. return "keyword";
  93. if (symbolRE.test(ch)) {
  94. if (ch == '-' && source.eat(/-/)) {
  95. source.eatWhile(/-/);
  96. if (!source.eat(symbolRE)) {
  97. source.skipToEnd();
  98. return "comment";
  99. }
  100. }
  101. var t = "variable";
  102. if (ch == ':') {
  103. t = "variable-2";
  104. }
  105. source.eatWhile(symbolRE);
  106. return t;
  107. }
  108. return "error";
  109. }
  110. function ncomment(type, nest) {
  111. if (nest == 0) {
  112. return normal;
  113. }
  114. return function(source, setState) {
  115. var currNest = nest;
  116. while (!source.eol()) {
  117. var ch = source.next();
  118. if (ch == '{' && source.eat('-')) {
  119. ++currNest;
  120. }
  121. else if (ch == '-' && source.eat('}')) {
  122. --currNest;
  123. if (currNest == 0) {
  124. setState(normal);
  125. return type;
  126. }
  127. }
  128. }
  129. setState(ncomment(type, currNest));
  130. return type;
  131. };
  132. }
  133. function stringLiteral(source, setState) {
  134. while (!source.eol()) {
  135. var ch = source.next();
  136. if (ch == '"') {
  137. setState(normal);
  138. return "string";
  139. }
  140. if (ch == '\\') {
  141. if (source.eol() || source.eat(whiteCharRE)) {
  142. setState(stringGap);
  143. return "string";
  144. }
  145. if (source.eat('&')) {
  146. }
  147. else {
  148. source.next(); // should handle other escapes here
  149. }
  150. }
  151. }
  152. setState(normal);
  153. return "string error";
  154. }
  155. function stringGap(source, setState) {
  156. if (source.eat('\\')) {
  157. return switchState(source, setState, stringLiteral);
  158. }
  159. source.next();
  160. setState(normal);
  161. return "error";
  162. }
  163. var wellKnownWords = (function() {
  164. var wkw = {};
  165. function setType(t) {
  166. return function () {
  167. for (var i = 0; i < arguments.length; i++)
  168. wkw[arguments[i]] = t;
  169. };
  170. }
  171. setType("keyword")(
  172. "case", "class", "data", "default", "deriving", "do", "else", "foreign",
  173. "if", "import", "in", "infix", "infixl", "infixr", "instance", "let",
  174. "module", "newtype", "of", "then", "type", "where", "_");
  175. setType("keyword")(
  176. "\.\.", ":", "::", "=", "\\", "<-", "->", "@", "~", "=>");
  177. setType("builtin")(
  178. "!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<*", "<=",
  179. "<$>", "<*>", "=<<", "==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*",
  180. "*>", "**");
  181. setType("builtin")(
  182. "Applicative", "Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum",
  183. "Eq", "False", "FilePath", "Float", "Floating", "Fractional", "Functor",
  184. "GT", "IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left",
  185. "Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read",
  186. "ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS",
  187. "String", "True");
  188. setType("builtin")(
  189. "abs", "acos", "acosh", "all", "and", "any", "appendFile", "asTypeOf",
  190. "asin", "asinh", "atan", "atan2", "atanh", "break", "catch", "ceiling",
  191. "compare", "concat", "concatMap", "const", "cos", "cosh", "curry",
  192. "cycle", "decodeFloat", "div", "divMod", "drop", "dropWhile", "either",
  193. "elem", "encodeFloat", "enumFrom", "enumFromThen", "enumFromThenTo",
  194. "enumFromTo", "error", "even", "exp", "exponent", "fail", "filter",
  195. "flip", "floatDigits", "floatRadix", "floatRange", "floor", "fmap",
  196. "foldl", "foldl1", "foldr", "foldr1", "fromEnum", "fromInteger",
  197. "fromIntegral", "fromRational", "fst", "gcd", "getChar", "getContents",
  198. "getLine", "head", "id", "init", "interact", "ioError", "isDenormalized",
  199. "isIEEE", "isInfinite", "isNaN", "isNegativeZero", "iterate", "last",
  200. "lcm", "length", "lex", "lines", "log", "logBase", "lookup", "map",
  201. "mapM", "mapM_", "max", "maxBound", "maximum", "maybe", "min", "minBound",
  202. "minimum", "mod", "negate", "not", "notElem", "null", "odd", "or",
  203. "otherwise", "pi", "pred", "print", "product", "properFraction", "pure",
  204. "putChar", "putStr", "putStrLn", "quot", "quotRem", "read", "readFile",
  205. "readIO", "readList", "readLn", "readParen", "reads", "readsPrec",
  206. "realToFrac", "recip", "rem", "repeat", "replicate", "return", "reverse",
  207. "round", "scaleFloat", "scanl", "scanl1", "scanr", "scanr1", "seq",
  208. "sequence", "sequence_", "show", "showChar", "showList", "showParen",
  209. "showString", "shows", "showsPrec", "significand", "signum", "sin",
  210. "sinh", "snd", "span", "splitAt", "sqrt", "subtract", "succ", "sum",
  211. "tail", "take", "takeWhile", "tan", "tanh", "toEnum", "toInteger",
  212. "toRational", "truncate", "uncurry", "undefined", "unlines", "until",
  213. "unwords", "unzip", "unzip3", "userError", "words", "writeFile", "zip",
  214. "zip3", "zipWith", "zipWith3");
  215. var override = modeConfig.overrideKeywords;
  216. if (override) for (var word in override) if (override.hasOwnProperty(word))
  217. wkw[word] = override[word];
  218. return wkw;
  219. })();
  220. return {
  221. startState: function () { return { f: normal }; },
  222. copyState: function (s) { return { f: s.f }; },
  223. token: function(stream, state) {
  224. var t = state.f(stream, function(s) { state.f = s; });
  225. var w = stream.current();
  226. return wellKnownWords.hasOwnProperty(w) ? wellKnownWords[w] : t;
  227. },
  228. blockCommentStart: "{-",
  229. blockCommentEnd: "-}",
  230. lineComment: "--"
  231. };
  232. });
  233. CodeMirror.defineMIME("text/x-haskell", "haskell");
  234. });