summaryrefslogtreecommitdiff
path: root/src/lang/core/eval.ts
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2025-11-02 19:42:39 -0800
committerKai Stevenson <kai@kaistevenson.com>2025-11-02 19:53:23 -0800
commitd0390f56137f68570a18817899761009d4e9bc87 (patch)
tree01db5a69b2724e089177ace03c9ba21fa1c9f22e /src/lang/core/eval.ts
parente9f3c782bc10d4c5c44faf768aa60cd6bcc66574 (diff)
fn, callmap
Diffstat (limited to 'src/lang/core/eval.ts')
-rw-r--r--src/lang/core/eval.ts128
1 files changed, 107 insertions, 21 deletions
diff --git a/src/lang/core/eval.ts b/src/lang/core/eval.ts
index 6a25a6c..61eb63a 100644
--- a/src/lang/core/eval.ts
+++ b/src/lang/core/eval.ts
@@ -4,39 +4,125 @@ import {
BUILTIN_Mul,
BUILTIN_ToString,
} from "../builtin";
+import { ToString } from "../util";
import { ASTNode, NodeType } from "./common";
import { Lex } from "./lexer";
import { Parse } from "./parser";
+export type GetEvaluatedChildren<
+ Node extends ASTNode,
+ Frame extends StackFrame
+> = Node["children"] extends infer Children extends readonly ASTNode[]
+ ? {
+ [Idx in keyof Children]: Children[Idx] extends ASTNode
+ ? Evaluate<Children[Idx], Frame>
+ : never;
+ }
+ : never;
+
export type SENTINEL_NO_BUILTIN = "__NO_BUILTIN__";
-export type MapBuiltins<Node extends ASTNode> =
- Node["children"] extends infer Children extends readonly ASTNode[]
- ? {
- [Idx in keyof Children]: Children[Idx] extends ASTNode
- ? Evaluate<Children[Idx]>
- : never;
- } extends infer Args extends readonly any[]
- ? Node["name"] extends "tostring"
- ? BUILTIN_ToString<Args>
- : Node["name"] extends "arr"
- ? BUILTIN_Arr<Args>
- : Node["name"] extends "add"
- ? BUILTIN_Add<Args>
- : Node["name"] extends "mul"
- ? BUILTIN_Mul<Args>
- : SENTINEL_NO_BUILTIN
- : never
- : never;
+export type MapBuiltins<
+ Node extends ASTNode,
+ Frame extends StackFrame
+> = GetEvaluatedChildren<Node, Frame> extends infer Args extends readonly any[]
+ ? Node["name"] extends "tostring"
+ ? BUILTIN_ToString<Args>
+ : Node["name"] extends "arr"
+ ? BUILTIN_Arr<Args>
+ : Node["name"] extends "add"
+ ? BUILTIN_Add<Args>
+ : Node["name"] extends "mul"
+ ? BUILTIN_Mul<Args>
+ : SENTINEL_NO_BUILTIN
+ : never;
export type EvalError<T extends string> = `Eval error: ${T}`;
-export type Evaluate<Node extends ASTNode> = Node["type"] extends NodeType.INT
+export type StackFrame<
+ Bindings extends Record<ASTNode["name"], any> = Record<ASTNode["name"], any>,
+ Parent extends StackFrame | null = any
+> = {
+ bindings: Bindings;
+ parent: Parent;
+};
+
+export type EmptyStackFrame = StackFrame<{}, null>;
+
+export type WithPushedBindings<
+ OldFrame extends StackFrame,
+ Bindings extends StackFrame["bindings"]
+> = StackFrame<Bindings, OldFrame>;
+
+export type FindInStack<
+ Frame extends StackFrame,
+ NameToFind
+> = NameToFind extends keyof Frame["bindings"]
+ ? Frame["bindings"][NameToFind]
+ : Frame["parent"] extends null
+ ? EvalError<`Can't find name "${ToString<NameToFind>}" on the stack`>
+ : FindInStack<Frame["parent"], NameToFind>;
+
+export type MapOnStack<
+ Node extends ASTNode,
+ Frame extends StackFrame
+> = FindInStack<Frame, Node["name"]>;
+
+export type FnPrim<
+ Args extends readonly ASTNode[] = readonly ASTNode[],
+ Fn extends ASTNode = any
+> = { args: Args; fn: Fn };
+
+// Can support multiple args, just need to make the last arg be the fn
+export type HandleFn<Node extends ASTNode> = Node["children"] extends [
+ infer Arg extends ASTNode,
+ infer Fn extends ASTNode
+]
+ ? FnPrim<[Arg], Fn>
+ : never;
+
+type MapZip<T extends readonly ASTNode[], U extends readonly PropertyKey[]> = {
+ [Idx in Exclude<
+ keyof T,
+ keyof any[]
+ > as T[Idx] extends infer Node extends ASTNode
+ ? Node["name"]
+ : never]: Idx extends keyof U ? U[Idx] : never;
+};
+
+export type CallFn<
+ Fn extends FnPrim,
+ Values extends readonly any[],
+ Frame extends StackFrame
+> = Evaluate<Fn["fn"], StackFrame<MapZip<Fn["args"], Values>, Frame>>;
+
+export type Evaluate<
+ Node extends ASTNode,
+ Frame extends StackFrame = EmptyStackFrame
+> = Node["type"] extends NodeType.INT
? Node["value"]
: Node["type"] extends NodeType.EXT
- ? MapBuiltins<Node>
+ ? // special builtin
+ Node["name"] extends "call"
+ ? GetEvaluatedChildren<Node, Frame> extends [
+ infer Fn extends FnPrim,
+ ...infer Values extends readonly any[]
+ ]
+ ? CallFn<Fn, Values, Frame>
+ : EvalError<`Invalid params for function call: ${ToString<
+ GetEvaluatedChildren<Node, Frame>
+ >}`>
+ : // special builtin
+ Node["name"] extends "fn"
+ ? HandleFn<Node>
+ : MapBuiltins<Node, Frame> extends infer BI
+ ? BI extends SENTINEL_NO_BUILTIN
+ ? MapOnStack<Node, Frame>
+ : BI
+ : never
: EvalError<`Unhandled node type ${Node["type"]}`>;
-const input = `` as const;
+// const input = `map(arr(1,2,3), fn(n, add(n, 1)))` as const;
+const input = `call(fn(a, add(a, 5)), 12)` as const;
const lex_result = null as unknown as Lex<typeof input>;
const parse_result = null as unknown as Parse<typeof lex_result>;
const eval_result = null as unknown as Evaluate<typeof parse_result>;