function interpretFun(funNode) {
function interpretFunBody(ctx, argsKey, body) {
return (...args) => {
ctx[argsKey] = args;
try {
const res = interpretNode(ctx, body);
if (body instanceof Assignments) {
return res();
} else {
return res;
}
} catch (e) {
if (e instanceof ReturnException) {
return e.value;
} else if (e instanceof BreakException || e instanceof ContinueException) {
throw new Error('Break or continue without loop');
} else {
throw e;
}
}
}
}
function interpretNode(ctx, node, params={}) {
if (node instanceof NameUsage) {
if (node.isRef) {
return node.name.name;
} else {
return ctx[node.name.name];
}
} else if (node instanceof FunUsage) {
return interpretFun(node.target);
} else if (node instanceof Assignments) {
return () => {
let res;
for (const assignment of node.assignments) {
res = interpretNode(ctx, assignment);
}
return res;
}
} else if (node instanceof Assignment) {
const name = node.nameUsage.name.name;
const val = interpretNode(ctx, node.val);
ctx[name] = val;
return val;
} else if (node instanceof Data) {
return node.value;
} else if (node instanceof Lambda) {
return interpretFunBody(ctx, '$', node.body);
} else if (node instanceof Call) {
const head = interpretNode(ctx, node.head);
const args = node.args.map(arg => interpretNode(ctx, arg));
if (head.passCtx) {
return head(ctx, ...args);
} else {
return head(...args);
}
} else {
throw new Error('Unsupported node class');
}
}
let res;
if (funNode.meta.interpret) {
res = funNode.meta.interpret;
} else {
res = interpretFunBody({}, '', funNode.body);
}
if (funNode.meta.passCtx) {
res.passCtx = true;
}
return res;
}