UNPKG

@alexo/lambda

Version:
227 lines (186 loc) 3.04 kB
const gfv = new Set(); let id = 0; function mkvar(fresh) { if (fresh) ++id; return `v${id}`; } function mkid(name) { const fv = new Set(); const obj = { node: "atom", fv: fv }; if (name) { obj.name = name; gfv.add(name); return obj; } do { name = mkvar(true); } while (gfv.has(name)); obj.name = name; fv.add(name); return obj; } function mkhole() { const obj = {}; obj.fv = new Set(); obj.bv = new Set(); obj.hole = obj; return obj; } function subst(hole, obj) { const parent = hole.parent; const body = obj.body; const left = obj.left; const right = obj.right; if (parent) obj.parent = hole.parent; else delete obj.parent; Object.assign(hole, obj); if (body) body.parent = hole; if (left) left.parent = hole; if (right) right.parent = hole; } function eta(obj) { let parent, left, right, name; if ("appl" != obj.node) return; parent = obj.parent; if (!parent) return; if ("abst" != parent.node) return; right = obj.right; if ("atom" != right.node) return; name = parent.bound; if (name != right.name) return; left = obj.left; if (left.fv.has(name)) return; subst(parent, left); eta(parent); } function atom(context, obj, name) { const ofv = obj.fv; const cfv = context.fv; const bv = context.bv; const chole = context.hole; const ohole = obj.hole; if (name) bv.add(name); ofv.forEach(x => { if (!bv.has(x)) cfv.add(x); }); subst(chole, obj); if (ohole) { delete chole.hole; context.hole = ohole; } else { delete context.hole; eta(chole); } return context; } function abst(context) { const hole = mkhole(); const name = mkvar(); const obj = { node: "abst", bound: name, body: hole, fv: new Set(), hole: hole }; hole.parent = obj; return atom(context, obj, name); } function appl(left) { const context = mkhole(); const hole = mkhole(); const obj = { node: "appl", left: left, right: hole, fv: new Set(left.fv), hole: hole }; left.parent = obj; hole.parent = obj; return atom(context, obj); } function clone(obj, root, hole, parent) { const copy = {}; if (!obj) return; if (!root) { root = copy; hole = obj.hole; } copy.node = obj.node; copy.bound = obj.bound; copy.name = obj.name; copy.parent = parent; copy.body = clone(obj.body, root, hole, copy); copy.left = clone(obj.left, root, hole, copy); copy.right = clone(obj.right, root, hole, copy); copy.fv = new Set(obj.fv); copy.bv = new Set(obj.bv); if (obj === hole) root.hole = copy; return copy; } this.clone = clone; this.mkid = mkid; this.mkvar = mkvar; this.mkhole = mkhole; this.abst = abst; this.appl = appl; this.atom = atom; function noop() { } this.beta = 0; this.total = 0; this.cb = Object.assign({ a: noop, b: noop, d: noop, o: noop }, this.cb); this.debug = { a: () => { ++this.total; this.cb.a(); }, b: () => { ++this.beta; ++this.total; this.cb.b(); }, d: () => { ++this.total; this.cb.d(); }, o: () => { ++this.total; this.cb.o(); } };