From 5fdaa70d0af1356652de38f66fccef4bd3088a26 Mon Sep 17 00:00:00 2001 From: Kai Stevenson Date: Sun, 2 Nov 2025 19:42:39 -0800 Subject: fn, call --- src/lang/core/eval.ts | 123 +++++++++++++++++++++++++++++++++++++++--------- src/lang/core/parser.ts | 59 +++-------------------- 2 files changed, 108 insertions(+), 74 deletions(-) (limited to 'src/lang/core') diff --git a/src/lang/core/eval.ts b/src/lang/core/eval.ts index 6a25a6c..258999a 100644 --- a/src/lang/core/eval.ts +++ b/src/lang/core/eval.ts @@ -3,40 +3,121 @@ import { BUILTIN_Arr, BUILTIN_Mul, BUILTIN_ToString, + SBUILTIN_Call, + SBUILTIN_Map, } 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 + : never; + } + : never; + export type SENTINEL_NO_BUILTIN = "__NO_BUILTIN__"; -export type MapBuiltins = - Node["children"] extends infer Children extends readonly ASTNode[] - ? { - [Idx in keyof Children]: Children[Idx] extends ASTNode - ? Evaluate - : never; - } extends infer Args extends readonly any[] - ? Node["name"] extends "tostring" - ? BUILTIN_ToString - : Node["name"] extends "arr" - ? BUILTIN_Arr - : Node["name"] extends "add" - ? BUILTIN_Add - : Node["name"] extends "mul" - ? BUILTIN_Mul - : SENTINEL_NO_BUILTIN - : never - : never; +export type MapBuiltins< + Node extends ASTNode, + Frame extends StackFrame +> = GetEvaluatedChildren extends infer Args extends readonly any[] + ? Node["name"] extends "call" + ? SBUILTIN_Call + : Node["name"] extends "map" + ? SBUILTIN_Map + : Node["name"] extends "tostring" + ? BUILTIN_ToString + : Node["name"] extends "arr" + ? BUILTIN_Arr + : Node["name"] extends "add" + ? BUILTIN_Add + : Node["name"] extends "mul" + ? BUILTIN_Mul + : SENTINEL_NO_BUILTIN + : never; export type EvalError = `Eval error: ${T}`; -export type Evaluate = Node["type"] extends NodeType.INT +export type StackFrame< + Bindings extends Record = Record, + 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; + +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}" on the stack`> + : FindInStack; + +export type MapOnStack< + Node extends ASTNode, + Frame extends StackFrame +> = FindInStack; + +export type FnPrim< + Args extends readonly ASTNode[] = readonly ASTNode[], + Fn extends ASTNode = ASTNode +> = { args: Args; fn: Fn }; + +// Can support multiple args, just need to make the last arg be the fn +export type HandleFn = Node["children"] extends [ + infer Arg extends ASTNode, + infer Fn extends ASTNode +] + ? FnPrim<[Arg], Fn> + : never; + +type MapZip = { + [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, Frame>>; + +export type Evaluate< + Node extends ASTNode, + Frame extends StackFrame = EmptyStackFrame +> = Node["type"] extends NodeType.INT ? Node["value"] : Node["type"] extends NodeType.EXT - ? MapBuiltins + ? // special builtin + Node["name"] extends "fn" + ? HandleFn + : MapBuiltins extends infer BI + ? BI extends SENTINEL_NO_BUILTIN + ? MapOnStack + : BI + : never : EvalError<`Unhandled node type ${Node["type"]}`>; -const input = `` as const; +const input = `map(arr(5,5,5), fn(n, add(n, 1)))` as const; const lex_result = null as unknown as Lex; const parse_result = null as unknown as Parse; const eval_result = null as unknown as Evaluate; diff --git a/src/lang/core/parser.ts b/src/lang/core/parser.ts index 79218e9..2b481fd 100644 --- a/src/lang/core/parser.ts +++ b/src/lang/core/parser.ts @@ -8,51 +8,6 @@ import { } from "./common"; import { Lex } from "./lexer"; -/* -start -if no 'lastName' -then: - expect nextToken to be a name - lastName = nextToken - goto start - -else: - if nextToken is name - then: - // we already have a lastName - mutate last element of stack to push lastName as child - lastName = nextToken - goto start - - else: - //nextToken is openParen or close paren - if nextToken is closeParen - then: - set last element of stack as child of prev element on stack - pop stack - // [stack[last - 1].children.push(stack.pop) - goto start - else if nextToken is openParen: - push lastName onto stack - goto start - - -finally: - // only one element remains on the stack - return stack[0] - - - CALL ( param, CALL2 ( param2 ) ) - - param2 ret call2 param ret call - - | call - |-- param - |-- | call2 - |-- param2 - - */ - export type Error = ASTNode< NodeType.PARSER_ERROR, "Error", @@ -131,9 +86,9 @@ export type _Parse = Ctx["remainingTokens"] extends [ ] ? Ctx["lastToken"] extends Token ? Head["type"] extends TokenType.NAME - ? // we already have a lastName - // mutate last element of stack to push lastName as child - // lastName = nextToken + ? // we already have a lastToken + // mutate last element of stack to push lastToken as child + // lastToken = nextToken // goto start _Parse<{ lastToken: Head; @@ -145,7 +100,7 @@ export type _Parse = Ctx["remainingTokens"] extends [ }> : //nextToken is openParen or close paren Head["type"] extends TokenType.CLOSE_PAREN - ? // handle lastName + ? // handle lastToken // set last element of stack as child of prev element on stack // pop stack // [stack[last - 1].children.push(stack.pop) @@ -167,7 +122,7 @@ export type _Parse = Ctx["remainingTokens"] extends [ >; }> : Head["type"] extends TokenType.OPEN_PAREN - ? // push lastName onto stack + ? // push lastToken onto stack // goto start _Parse<{ lastToken: null; @@ -177,7 +132,7 @@ export type _Parse = Ctx["remainingTokens"] extends [ : Ctx & Error<`Was not expecting ${Head["type"]}`> : // expect nextToken to be a name or close paren Head["type"] extends TokenType.NAME - ? // lastName = nextToken + ? // lastToken = nextToken // goto start _Parse<{ lastToken: Head; @@ -216,5 +171,3 @@ export type Parse = _Parse<{ remainingTokens: Raw; stack: [ASTNode]; }>; - -const test_result = null as unknown as Parse>; -- cgit v1.2.3-70-g09d2