diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2025-11-02 19:42:39 -0800 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2025-11-02 20:05:39 -0800 |
| commit | 5fdaa70d0af1356652de38f66fccef4bd3088a26 (patch) | |
| tree | 7825c48515fa6f0fcafd11cdb6975822b0d4f89a /src | |
| parent | e9f3c782bc10d4c5c44faf768aa60cd6bcc66574 (diff) | |
fn, call
Diffstat (limited to 'src')
| -rw-r--r-- | src/lang/builtin/builtin.ts | 33 | ||||
| -rw-r--r-- | src/lang/builtin/index.ts | 35 | ||||
| -rw-r--r-- | src/lang/builtin/sbuiltin.ts | 33 | ||||
| -rw-r--r-- | src/lang/core/eval.ts | 123 | ||||
| -rw-r--r-- | src/lang/core/parser.ts | 59 |
5 files changed, 176 insertions, 107 deletions
diff --git a/src/lang/builtin/builtin.ts b/src/lang/builtin/builtin.ts new file mode 100644 index 0000000..a289867 --- /dev/null +++ b/src/lang/builtin/builtin.ts @@ -0,0 +1,33 @@ +import { FnError } from "."; +import { + AddNumbers, + AddStrings, + Multiply, + ToString, + UnarrayIfOnlyHead, +} from "../util"; + +export type BUILTIN_Arr<Args extends readonly any[]> = Args; + +export type BUILTIN_ToString<Args extends readonly any[]> = ToString< + UnarrayIfOnlyHead<{ + [Idx in keyof Args]: ToString<Args[Idx]>; + }> +>; + +export type BUILTIN_Add<Args extends readonly any[]> = + Args extends readonly string[] + ? AddStrings<Args> + : Args extends readonly number[] + ? AddNumbers<Args> + : FnError<`Cannot add operands ${ToString<Args>}`>; + +export type BUILTIN_Mul<Args extends readonly any[]> = Args extends [ + infer A, + infer B, + infer C +] + ? FnError<`Can only multiply [number, number], but got ${ToString<Args>}`> + : Args extends [infer M extends number, infer N extends number] + ? Multiply<M, N> + : FnError<`Can only multiply [number, number], but got ${ToString<Args>}`>; diff --git a/src/lang/builtin/index.ts b/src/lang/builtin/index.ts index 373f54a..de2bee3 100644 --- a/src/lang/builtin/index.ts +++ b/src/lang/builtin/index.ts @@ -1,34 +1,3 @@ -import { - AddNumbers, - AddStrings, - Multiply, - ToString, - UnarrayIfOnlyHead, -} from "../util"; - export type FnError<T extends string> = `Function execution error: ${T}`; - -export type BUILTIN_Arr<Args extends readonly any[]> = Args; - -export type BUILTIN_ToString<Args extends readonly any[]> = ToString< - UnarrayIfOnlyHead<{ - [Idx in keyof Args]: ToString<Args[Idx]>; - }> ->; - -export type BUILTIN_Add<Args extends readonly any[]> = - Args extends readonly string[] - ? AddStrings<Args> - : Args extends readonly number[] - ? AddNumbers<Args> - : FnError<`Cannot add operands ${ToString<Args>}`>; - -export type BUILTIN_Mul<Args extends readonly any[]> = Args extends [ - infer A, - infer B, - infer C -] - ? FnError<`Can only multiply [number, number], but got ${ToString<Args>}`> - : Args extends [infer M extends number, infer N extends number] - ? Multiply<M, N> - : FnError<`Can only multiply [number, number], but got ${ToString<Args>}`>; +export * from "./builtin"; +export * from "./sbuiltin"; diff --git a/src/lang/builtin/sbuiltin.ts b/src/lang/builtin/sbuiltin.ts new file mode 100644 index 0000000..704e688 --- /dev/null +++ b/src/lang/builtin/sbuiltin.ts @@ -0,0 +1,33 @@ +import { ASTNode } from "../core/common"; +import { + CallFn, + FnPrim, + GetEvaluatedChildren, + StackFrame, + EvalError, +} from "../core/eval"; +import { ToString } from "../util"; + +export type SBUILTIN_Call< + Node extends ASTNode, + Frame extends StackFrame +> = 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> + >}`>; + +export type SBUILTIN_Map< + Node extends ASTNode, + Frame extends StackFrame +> = GetEvaluatedChildren<Node, Frame> extends [ + infer Arr extends readonly any[], + infer Fn extends FnPrim +] + ? { [Idx in keyof Arr]: CallFn<Fn, [Arr[Idx]], Frame> } + : EvalError<`Invalid params for map: ${ToString< + GetEvaluatedChildren<Node, Frame> + >}`>; 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<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 "call" + ? SBUILTIN_Call<Node, Frame> + : Node["name"] extends "map" + ? SBUILTIN_Map<Node, Frame> + : 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 = ASTNode +> = { 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 "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(5,5,5), fn(n, add(n, 1)))` 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>; 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<T extends string> = ASTNode< NodeType.PARSER_ERROR, "Error", @@ -131,9 +86,9 @@ export type _Parse<Ctx extends ParserCtx> = 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 extends ParserCtx> = 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 extends ParserCtx> = 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 extends ParserCtx> = 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<Raw extends readonly Token[]> = _Parse<{ remainingTokens: Raw; stack: [ASTNode<NodeType.EXT, "arr", null, []>]; }>; - -const test_result = null as unknown as Parse<Lex<`test(135)`>>; |
