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