diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2025-11-06 00:18:26 -0800 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2025-11-06 20:28:00 -0800 |
| commit | 490b9c94fba16f484be3bb58b8a4a4880b9396bc (patch) | |
| tree | a94bd52ca129828fe284ee96651018613e42f6c6 /src/lang/js-lang/core/eval.ts | |
| parent | d8a969e231135978c4dd1fa67559101f506ad6f3 (diff) | |
implement recursion properly with closures
Diffstat (limited to 'src/lang/js-lang/core/eval.ts')
| -rw-r--r-- | src/lang/js-lang/core/eval.ts | 45 |
1 files changed, 36 insertions, 9 deletions
diff --git a/src/lang/js-lang/core/eval.ts b/src/lang/js-lang/core/eval.ts index 60a2059..1a9e292 100644 --- a/src/lang/js-lang/core/eval.ts +++ b/src/lang/js-lang/core/eval.ts @@ -6,6 +6,7 @@ import { NodeType, FnPrim, SENTINEL_NO_BUILTIN, + NamedFnPrim, } from "../../ts-lang"; import { nameToBUILTIN, nameToSBUILTIN, V_SBUILTIN_Map } from "../builtin"; @@ -27,11 +28,24 @@ const findInStack = (frame: StackFrame, nameToFind: string) => { return frame.bindings[nameToFind]; } - if (!frame.parent) { - throw new Error(`Can't find name ${nameToFind} on the stack`); + throw new Error(`Can't find name ${nameToFind} on the stack`); +}; + +const handleBind = (node: ASTNode, frame: StackFrame) => { + const inner = _evaluate(node.children[1], frame); + + if (inner.fn) { + const named: NamedFnPrim<any, any, any, any> = { + args: inner.args, + fn: inner.fn, + name: node.children[0].name, + frame, + }; + + return named; } - return findInStack(frame.parent, nameToFind); + return inner; }; const handleFn = (node: ASTNode): FnPrim => { @@ -46,13 +60,22 @@ const handleFn = (node: ASTNode): FnPrim => { const mapZip = (args: ASTNode[], values: any[]) => Object.fromEntries(args.map(({ name }, i) => [name, values[i]])); -export const callFn = (fn: FnPrim, values: any[], frame: StackFrame) => - _evaluate(fn.fn, { - bindings: mapZip(fn.args as ASTNode[], values), - parent: frame, +export const callFn = (fn: FnPrim, values: any[], frame: StackFrame) => { + if ((fn as NamedFnPrim<any, any, any, any>).frame) { + return _evaluate(fn.fn, { + bindings: { + ...mapZip(fn.args as ASTNode[], values), + ...frame.bindings, + ...(fn as NamedFnPrim<any, any, any, any>).frame.bindings, + }, + }); + } + return _evaluate(fn.fn, { + bindings: { ...mapZip(fn.args as ASTNode[], values), ...frame.bindings }, }); +}; -export const _evaluate = (node: ASTNode, frame: StackFrame) => { +export const _evaluate = (node: ASTNode, frame: StackFrame): any => { if (node.type === NodeType.INT) { return node.value; } @@ -62,6 +85,10 @@ export const _evaluate = (node: ASTNode, frame: StackFrame) => { return handleFn(node); } + if (node.name === "bind") { + return handleBind(node, frame); + } + const builtinResult = mapBuiltins(node, frame); if (builtinResult !== V_SENTINEL_NO_BUILTIN) { return builtinResult; @@ -76,7 +103,7 @@ export const _evaluate = (node: ASTNode, frame: StackFrame) => { export const getEvaluatedChildren = (node: ASTNode, frame: StackFrame) => node.children.map((child) => _evaluate(child, frame)); -export const emptyStackFrame: EmptyStackFrame = { bindings: {}, parent: null }; +export const emptyStackFrame: EmptyStackFrame = { bindings: {} }; export const evaluate = <const Node extends ASTNode>( node: Node |
