in packages/core/nano/src/compiler.ts [119:176]
function compileAST(gensym: () => number, scope: Record<string, string>, f: ExpressionNode<string>): string {
switch (f.kind) {
case NodeKind.Literal:
return simpleSink.lit(f.value);
case NodeKind.Ident:
if (scope[f.value] !== undefined) {
return scope[f.value];
}
return simpleSink.ident(f.value, undefined, /* fieldAccess */ false);
case NodeKind.Paren:
return simpleSink.paren(compileAST(gensym, scope, f.value));
case NodeKind.Fun:
const children = f.children;
assert(children.length > 0);
const name = `$args${gensym()}`;
const freshScope = { ...scope };
for (let i = 0; i < children.length - 1; i += 1) {
const ident = children[i];
if (ident.kind === NodeKind.Ident) {
freshScope[ident.value] = `${name}[${i}]`;
continue;
}
return assertNever(ident as never, "FUN arg should be ident");
}
const body = compileAST(gensym, freshScope, children[children.length - 1]);
return `function(ef, origin, ${name}){return ${name}.length>=${children.length - 1}?${body}:err.functionArity;}`;
case NodeKind.App:
const head = compileAST(gensym, scope, f.children[0]);
const args = f.children.slice(1).map(child => compileAST(gensym, scope, child));
return simpleSink.app(head, args);
case NodeKind.Conditional:
return outputConditional(f.children.map(child => compileAST(gensym, scope, child)));
case NodeKind.Dot:
if (f.operand2.kind === NodeKind.Ident) {
return simpleSink.dot(compileAST(gensym, scope, f.operand1), JSON.stringify(f.operand2.value));
}
return assertNever(f.operand2.kind as never, "DOT field should be ident");
case NodeKind.BinaryOp:
return simpleSink.binOp(
f.op,
compileAST(gensym, scope, f.operand1),
compileAST(gensym, scope, f.operand2)
);
case NodeKind.UnaryOp:
return simpleSink.unaryOp(f.op, compileAST(gensym, scope, f.operand1));
default:
return assertNever(f as never, "Missing should not be compiled");
}
}