@alexo/lambda
Version:
Macro Lambda Calculus
227 lines (186 loc) • 3.04 kB
Plain Text
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();
}
};