diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2025-11-02 19:42:39 -0800 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2025-11-02 19:53:23 -0800 |
| commit | d0390f56137f68570a18817899761009d4e9bc87 (patch) | |
| tree | 01db5a69b2724e089177ace03c9ba21fa1c9f22e /src/lang/core/eval.ts | |
| parent | e9f3c782bc10d4c5c44faf768aa60cd6bcc66574 (diff) | |
fn, callmap
Diffstat (limited to 'src/lang/core/eval.ts')
| -rw-r--r-- | src/lang/core/eval.ts | 128 |
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>; |
