summaryrefslogtreecommitdiff
path: root/src/lang/js-lang/core/eval.ts
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2025-11-03 23:41:31 -0800
committerKai Stevenson <kai@kaistevenson.com>2025-11-03 23:41:31 -0800
commit8b610f2bcfc223333254ce9679730c42dce6d26e (patch)
treeac1eab726395523f8725bda3d040e22214cba409 /src/lang/js-lang/core/eval.ts
parent56040f3ff85e77311f0c864a89afd63fcf1bdb50 (diff)
add createFn
Diffstat (limited to 'src/lang/js-lang/core/eval.ts')
-rw-r--r--src/lang/js-lang/core/eval.ts83
1 files changed, 83 insertions, 0 deletions
diff --git a/src/lang/js-lang/core/eval.ts b/src/lang/js-lang/core/eval.ts
new file mode 100644
index 0000000..60a2059
--- /dev/null
+++ b/src/lang/js-lang/core/eval.ts
@@ -0,0 +1,83 @@
+import {
+ ASTNode,
+ StackFrame,
+ Evaluate,
+ EmptyStackFrame,
+ NodeType,
+ FnPrim,
+ SENTINEL_NO_BUILTIN,
+} from "../../ts-lang";
+import { nameToBUILTIN, nameToSBUILTIN, V_SBUILTIN_Map } from "../builtin";
+
+const V_SENTINEL_NO_BUILTIN: SENTINEL_NO_BUILTIN = "__NO_BUILTIN__";
+
+const mapBuiltins = (node: ASTNode, frame: StackFrame): any => {
+ if (node.name in nameToSBUILTIN) {
+ return nameToSBUILTIN[node.name](node, frame);
+ }
+ if (node.name in nameToBUILTIN) {
+ return nameToBUILTIN[node.name](getEvaluatedChildren(node, frame));
+ }
+
+ return V_SENTINEL_NO_BUILTIN;
+};
+
+const findInStack = (frame: StackFrame, nameToFind: string) => {
+ if (nameToFind in frame.bindings) {
+ return frame.bindings[nameToFind];
+ }
+
+ if (!frame.parent) {
+ throw new Error(`Can't find name ${nameToFind} on the stack`);
+ }
+
+ return findInStack(frame.parent, nameToFind);
+};
+
+const handleFn = (node: ASTNode): FnPrim => {
+ const fn = node.children[node.children.length - 1];
+
+ return {
+ args: node.children.slice(0, node.children.length - 1),
+ fn,
+ };
+};
+
+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 _evaluate = (node: ASTNode, frame: StackFrame) => {
+ if (node.type === NodeType.INT) {
+ return node.value;
+ }
+
+ if (node.type === NodeType.EXT) {
+ if (node.name === "fn") {
+ return handleFn(node);
+ }
+
+ const builtinResult = mapBuiltins(node, frame);
+ if (builtinResult !== V_SENTINEL_NO_BUILTIN) {
+ return builtinResult;
+ }
+
+ return findInStack(frame, node.name);
+ }
+
+ throw new Error(`Unhandled node type ${node.type}`);
+};
+
+export const getEvaluatedChildren = (node: ASTNode, frame: StackFrame) =>
+ node.children.map((child) => _evaluate(child, frame));
+
+export const emptyStackFrame: EmptyStackFrame = { bindings: {}, parent: null };
+
+export const evaluate = <const Node extends ASTNode>(
+ node: Node
+): Evaluate<Node> => _evaluate(node, emptyStackFrame) as Evaluate<Node>;