Write the semantics section using Bergamot
Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
This commit is contained in:
128
content/blog/05_spa_agda_semantics/parser.js
Normal file
128
content/blog/05_spa_agda_semantics/parser.js
Normal file
@@ -0,0 +1,128 @@
|
||||
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);
|
||||
Reference in New Issue
Block a user