haskell.js 7.3 KB

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