129 lines
3.4 KiB
JavaScript
129 lines
3.4 KiB
JavaScript
|
const match = str => input => {
|
||
|
if (input.startsWith(str)) {
|
||
|
return [[str, input.slice(str.length)]]
|
||
|
}
|
||
|
return [];
|
||
|
};
|
||
|
|
||
|
const map = (f, m) => input => {
|
||
|
return m(input).map(([v, rest]) => [f(v), rest]);
|
||
|
};
|
||
|
|
||
|
const apply = (m1, m2) => input => {
|
||
|
return m1(input).flatMap(([f, rest]) => m2(rest).map(([v, rest]) => [f(v), rest]));
|
||
|
};
|
||
|
|
||
|
const pure = v => input => [[v, input]];
|
||
|
|
||
|
const liftA = (f, ...ms) => input => {
|
||
|
if (ms.length <= 0) return []
|
||
|
|
||
|
let results = map(v => [v], ms[0])(input);
|
||
|
for (let i = 1; i < ms.length; i++) {
|
||
|
results = results.flatMap(([vals, rest]) =>
|
||
|
ms[i](rest).map(([val, rest]) => [[...vals, val], rest])
|
||
|
);
|
||
|
}
|
||
|
return results.map(([vals, rest]) => [f(...vals), rest]);
|
||
|
};
|
||
|
|
||
|
const many1 = (m) => liftA((x, xs) => [x].concat(xs), m, oneOf([
|
||
|
lazy(() => many1(m)),
|
||
|
pure([])
|
||
|
]));
|
||
|
|
||
|
const many = (m) => oneOf([ pure([]), many1(m) ]);
|
||
|
|
||
|
const oneOf = ms => input => {
|
||
|
return ms.flatMap(m => m(input));
|
||
|
};
|
||
|
|
||
|
const takeWhileRegex0 = regex => input => {
|
||
|
let idx = 0;
|
||
|
while (idx < input.length && regex.test(input[idx])) {
|
||
|
idx++;
|
||
|
}
|
||
|
return [[input.slice(0, idx), input.slice(idx)]];
|
||
|
};
|
||
|
|
||
|
const takeWhileRegex = regex => input => {
|
||
|
const result = takeWhileRegex0(regex)(input);
|
||
|
if (result[0][0].length > 0) return result;
|
||
|
return [];
|
||
|
};
|
||
|
|
||
|
const spaces = takeWhileRegex0(/\s/);
|
||
|
|
||
|
const digits = takeWhileRegex(/\d/);
|
||
|
|
||
|
const alphas = takeWhileRegex(/[a-zA-Z]/);
|
||
|
|
||
|
const left = (m1, m2) => liftA((a, _) => a, m1, m2);
|
||
|
|
||
|
const right = (m1, m2) => liftA((_, b) => b, m1, m2);
|
||
|
|
||
|
const word = s => left(match(s), spaces);
|
||
|
|
||
|
const end = s => s.length == 0 ? [['', '']] : [];
|
||
|
|
||
|
const lazy = deferred => input => deferred()(input);
|
||
|
|
||
|
const ident = left(alphas, spaces);
|
||
|
|
||
|
const number = oneOf([
|
||
|
liftA((a, b) => a + b, word("-"), left(digits, spaces)),
|
||
|
left(digits, spaces),
|
||
|
]);
|
||
|
|
||
|
const basicExpr = oneOf([
|
||
|
map(n => `lit(${n})`, number),
|
||
|
map(x => `var(${x})`, ident),
|
||
|
liftA((lp, v, rp) => v, word("("), lazy(() => expr), word(")")),
|
||
|
]);
|
||
|
|
||
|
const opExpr = oneOf([
|
||
|
liftA((_a, _b, e) => ["plus", e], word("+"), spaces, lazy(() => expr)),
|
||
|
liftA((_a, _b, e) => ["minus", e], word("-"), spaces, lazy(() => expr)),
|
||
|
]);
|
||
|
|
||
|
const flatten = (e, es) => {
|
||
|
return es.reduce((e1, [op, e2]) => `${op}(${e1}, ${e2})`, e);
|
||
|
}
|
||
|
|
||
|
const expr = oneOf([
|
||
|
basicExpr,
|
||
|
liftA(flatten, basicExpr, many(opExpr)),
|
||
|
]);
|
||
|
|
||
|
const basicStmt = oneOf([
|
||
|
liftA((x, _, e) => `assign(${x}, ${e})`, ident, word("="), expr),
|
||
|
word("noop"),
|
||
|
]);
|
||
|
|
||
|
const stmt = oneOf([
|
||
|
basicStmt,
|
||
|
liftA((_if, _lp_, cond, _rp, _lbr1_, s1, _rbr1, _else, _lbr2, s2, _rbr2) => `if(${cond}, ${s1}, ${s2})`,
|
||
|
word("if"), word("("), expr, word(")"),
|
||
|
word("{"), lazy(() => stmtSeq), word("}"),
|
||
|
word("else"), word("{"), lazy(() => stmtSeq), word("}")),
|
||
|
liftA((_while, _lp_, cond, _rp, _lbr_, s1, _rbr) => `while(${cond}, ${s1})`,
|
||
|
word("while"), word("("), expr, word(")"),
|
||
|
word("{"), lazy(() => stmtSeq), word("}")),
|
||
|
]);
|
||
|
|
||
|
const stmtSeq = oneOf([
|
||
|
liftA((s1, _semi, rest) => `seq(${s1}, ${rest})`, stmt, word(";"), lazy(() => stmtSeq)),
|
||
|
stmt,
|
||
|
]);
|
||
|
|
||
|
const parseWhole = m => string => {
|
||
|
const result = left(m, end)(string);
|
||
|
console.log(result);
|
||
|
if (result.length > 0) return result[0][0];
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
window.parseExpr = parseWhole(expr);
|
||
|
window.parseBasicStmt = parseWhole(basicStmt);
|
||
|
window.parseStmt = parseWhole(stmtSeq);
|