haskell.js (8109B)
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: http://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 14 CodeMirror.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 setType("builtin")( 204 "Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum", "Eq", 205 "False", "FilePath", "Float", "Floating", "Fractional", "Functor", "GT", 206 "IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left", 207 "Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read", 208 "ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS", 209 "String", "True"); 210 211 setType("builtin")( 212 "abs", "acos", "acosh", "all", "and", "any", "appendFile", "asTypeOf", 213 "asin", "asinh", "atan", "atan2", "atanh", "break", "catch", "ceiling", 214 "compare", "concat", "concatMap", "const", "cos", "cosh", "curry", 215 "cycle", "decodeFloat", "div", "divMod", "drop", "dropWhile", "either", 216 "elem", "encodeFloat", "enumFrom", "enumFromThen", "enumFromThenTo", 217 "enumFromTo", "error", "even", "exp", "exponent", "fail", "filter", 218 "flip", "floatDigits", "floatRadix", "floatRange", "floor", "fmap", 219 "foldl", "foldl1", "foldr", "foldr1", "fromEnum", "fromInteger", 220 "fromIntegral", "fromRational", "fst", "gcd", "getChar", "getContents", 221 "getLine", "head", "id", "init", "interact", "ioError", "isDenormalized", 222 "isIEEE", "isInfinite", "isNaN", "isNegativeZero", "iterate", "last", 223 "lcm", "length", "lex", "lines", "log", "logBase", "lookup", "map", 224 "mapM", "mapM_", "max", "maxBound", "maximum", "maybe", "min", "minBound", 225 "minimum", "mod", "negate", "not", "notElem", "null", "odd", "or", 226 "otherwise", "pi", "pred", "print", "product", "properFraction", 227 "putChar", "putStr", "putStrLn", "quot", "quotRem", "read", "readFile", 228 "readIO", "readList", "readLn", "readParen", "reads", "readsPrec", 229 "realToFrac", "recip", "rem", "repeat", "replicate", "return", "reverse", 230 "round", "scaleFloat", "scanl", "scanl1", "scanr", "scanr1", "seq", 231 "sequence", "sequence_", "show", "showChar", "showList", "showParen", 232 "showString", "shows", "showsPrec", "significand", "signum", "sin", 233 "sinh", "snd", "span", "splitAt", "sqrt", "subtract", "succ", "sum", 234 "tail", "take", "takeWhile", "tan", "tanh", "toEnum", "toInteger", 235 "toRational", "truncate", "uncurry", "undefined", "unlines", "until", 236 "unwords", "unzip", "unzip3", "userError", "words", "writeFile", "zip", 237 "zip3", "zipWith", "zipWith3"); 238 239 var override = modeConfig.overrideKeywords; 240 if (override) for (var word in override) if (override.hasOwnProperty(word)) 241 wkw[word] = override[word]; 242 243 return wkw; 244 })(); 245 246 247 248 return { 249 startState: function () { return { f: normal }; }, 250 copyState: function (s) { return { f: s.f }; }, 251 252 token: function(stream, state) { 253 var t = state.f(stream, function(s) { state.f = s; }); 254 var w = stream.current(); 255 return wellKnownWords.hasOwnProperty(w) ? wellKnownWords[w] : t; 256 }, 257 258 blockCommentStart: "{-", 259 blockCommentEnd: "-}", 260 lineComment: "--" 261 }; 262 263 }); 264 265 CodeMirror.defineMIME("text/x-haskell", "haskell"); 266 267 });