const prefixRE = (words) => { return new RegExp("^(?:" + words.join("|") + ")", "i"); } const wordRE = (words) => { return new RegExp("^(?:" + words.join("|") + ")$", "i"); } const builtins = wordRE([ // useful helpers "rep", // string manipulation "str/upper", "str/lower", "str/capitalize", "wd", "se", // arithmetic "add", "sub", "mul", // tuple stuff "tuple/len", "tuple/concat", "tuple/flatten", "tuple/join", "tuple/index", "tuple/rep", "tuple/replace", "tuple/fold", "tuple/map", ]); const keywords = wordRE([ "puts", "case", "let", "in", "fix", "record", ]); const normal = (stream, state) => { let ch = stream.next(); if (ch == "(") { if (stream.eat("*")) { comment(stream, state); return "comment"; } } if (ch == "\"" || ch == "'") return (state.cur = string(ch))(stream, state); if (ch == "[" && /[\[=]/.test(stream.peek())) return (state.cur = bracketed(readBracket(stream), "string"))(stream, state); if (/\d/.test(ch)) { stream.eatWhile(/[\w.%]/); return "number"; } if (/[\w_]/.test(ch)) { stream.eatWhile(/[\w\\\-_.]/); return "variable"; } return null; } const comment = (stream, state) => { let ch; while (ch = stream.next()) { if (ch == "*" && stream.next() == ")") { state.cur = normal; return "comment"; } } state.cur = comment; }; const bracketed = (level, style) => { return (stream, state) => { let curlev = null, ch; while ((ch = stream.next()) != null) { if (curlev == null) { if (ch == "]") curlev = 0; } else if (ch == "=") { ++curlev; } else if (ch == "]" && curlev == level) { state.cur = normal; break; } else { curlev = null; } } return style; }; } const string = (quote) => { return (stream, state) => { let escaped = false, ch; while ((ch = stream.next()) != null) { if (ch == quote && !escaped) break; escaped = !escaped && ch == "\\"; } if (!escaped) state.cur = normal; return "string"; }; } export const matzoLang = { startState: (basecol) => { return {cur: normal}; }, token: (stream, state) => { if (stream.eatSpace()) return null; let style = state.cur(stream, state); const word = stream.current(); if (style == "variable") { if (keywords.test(word)) style = "keyword"; else if (builtins.test(word)) style = "builtin"; } return style; }, languageData: { commentTokens: {block: {open: "(\*", close: "\*)"}} } };