|
@@ -0,0 +1,128 @@
|
|
|
+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: "\*)"}}
|
|
|
+ }
|
|
|
+};
|