summaryrefslogtreecommitdiff
path: root/src/lib/core/parser.ts
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2025-10-28 22:49:46 -0700
committerKai Stevenson <kai@kaistevenson.com>2025-10-28 22:49:46 -0700
commit8896b33a652563d90cb211a85dd2f7213f42e5d1 (patch)
treee9adf51a2ccf91e58f5e8edae4d3d6a4bb03e986 /src/lib/core/parser.ts
parse, lex works
Diffstat (limited to 'src/lib/core/parser.ts')
-rw-r--r--src/lib/core/parser.ts212
1 files changed, 212 insertions, 0 deletions
diff --git a/src/lib/core/parser.ts b/src/lib/core/parser.ts
new file mode 100644
index 0000000..fbbbdc2
--- /dev/null
+++ b/src/lib/core/parser.ts
@@ -0,0 +1,212 @@
+import { ASTNode, NodeType, ParserCtx, Token, TokenType } from "./common";
+import { IsWhitespace, 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, T, []>;
+
+export type PushChild<Node extends ASTNode, Child extends ASTNode> = {
+ type: Node["type"];
+ name: Node["name"];
+ children: [...Node["children"], Child];
+};
+
+export type PushChildToLastElementOfStack<
+ Stack extends ParserCtx["stack"],
+ Child extends ASTNode
+> = Stack extends [...infer Head, infer Tail extends ASTNode]
+ ? [...Head, PushChild<Tail, Child>]
+ : Stack extends [infer Only extends ASTNode]
+ ? [PushChild<Only, Child>]
+ : never;
+
+export type PushChildToSecondLastElementOfStack<
+ Stack extends ParserCtx["stack"],
+ Child extends ASTNode
+> = Stack extends [
+ ...infer Head,
+ infer Tail extends ASTNode,
+ infer Final extends ASTNode
+]
+ ? [...Head, PushChild<Tail, Child>, Final]
+ : Stack extends [infer Only extends ASTNode, infer Final extends ASTNode]
+ ? [PushChild<Only, Child>, Final]
+ : never;
+
+export type GetLastOnStack<Stack extends ParserCtx["stack"]> = Stack extends [
+ ...infer Head,
+ infer Tail extends ASTNode
+]
+ ? Tail
+ : Stack extends [infer Only extends ASTNode]
+ ? Only
+ : never;
+
+export type StackWithoutLast<Stack extends ParserCtx["stack"]> = Stack extends [
+ ...infer Head extends ASTNode[],
+ infer Tail
+]
+ ? [...Head]
+ : Stack extends [infer Only extends ASTNode]
+ ? []
+ : never;
+
+export type _Parse<Ctx extends ParserCtx> = Ctx["remainingTokens"] extends [
+ infer Head extends Token,
+ ...infer Tail extends readonly Token[]
+]
+ ? Ctx["lastName"] extends string
+ ? Head["type"] extends TokenType.UNIQUE_SYMBOL
+ ? // we already have a lastName
+ // mutate last element of stack to push lastName as child
+ // lastName = nextToken
+ // goto start
+ _Parse<{
+ lastName: Head["name"];
+ remainingTokens: Tail;
+ stack: PushChildToLastElementOfStack<
+ Ctx["stack"],
+ ASTNode<NodeType.LITERAL, Ctx["lastName"], []>
+ >;
+ }>
+ : //nextToken is openParen or close paren
+ Head["type"] extends TokenType.CLOSE_PAREN
+ ? // handle lastName
+ // set last element of stack as child of prev element on stack
+ // pop stack
+ // [stack[last - 1].children.push(stack.pop)
+ // goto start
+ _Parse<{
+ lastName: null;
+ remainingTokens: Tail;
+ // first push the last name onto the children of the top
+ // then push the top onto the children of the next
+ // then remove the top
+ stack: StackWithoutLast<
+ PushChildToSecondLastElementOfStack<
+ Ctx["stack"],
+ PushChild<
+ GetLastOnStack<Ctx["stack"]>,
+ ASTNode<NodeType.LITERAL, Ctx["lastName"], []>
+ >
+ >
+ >;
+ }>
+ : Head["type"] extends TokenType.OPEN_PAREN
+ ? // push lastName onto stack
+ // goto start
+ _Parse<{
+ lastName: null;
+ remainingTokens: Tail;
+ stack: [...Ctx["stack"], ASTNode<NodeType.CALL, Ctx["lastName"], []>];
+ }>
+ : Ctx & Error<`Was not expecting ${Head["type"]}`>
+ : // expect nextToken to be a name or close paren
+ Head["type"] extends TokenType.UNIQUE_SYMBOL
+ ? // lastName = nextToken
+ // goto start
+ _Parse<{
+ lastName: Head["name"];
+ remainingTokens: Tail;
+ stack: Ctx["stack"];
+ }>
+ : Head["type"] extends TokenType.CLOSE_PAREN
+ ? _Parse<{
+ lastName: null;
+ remainingTokens: Tail;
+ // push the top onto the children of the next
+ // then remove the top
+ stack: StackWithoutLast<
+ PushChildToSecondLastElementOfStack<
+ Ctx["stack"],
+ GetLastOnStack<Ctx["stack"]>
+ >
+ >;
+ }>
+ : Ctx &
+ Error<`Expected nextToken to be a name or close paren at ${Head["type"]}`>
+ : Ctx["stack"];
+
+// v1
+// ? Ctx["isCollecting"] extends true
+// ? Head["type"] extends TokenType.CLOSE_PAREN
+// ? "return"
+// : Head["type"] extends TokenType.UNIQUE_SYMBOL
+// ? _Parse<{
+// remainingTokens: Tail;
+// isCollecting: true;
+// name: Ctx["name"];
+// // fuck, how to do this without advancing seek pointer??
+// ret: [...Ctx["ret"], ASTNode;
+// }>
+// : Error<"Expected another name, or `)`">
+// : Head["type"] extends TokenType.OPEN_PAREN
+// ? _Parse<{
+// remainingTokens: Tail;
+// isCollecting: true;
+// name: Ctx["name"];
+// ret: Ctx["ret"];
+// }>
+// : Error<"Expected open paren">
+// : Ctx["ret"];
+
+//v2
+// ? Ctx["previousName"] extends null
+// ? Head["type"] extends TokenType.UNIQUE_SYMBOL
+// ? _Parse<{ remainingTokens: Tail; previousName: Head; node: Ctx["node"] }>
+// : ASTNode<NodeType.PARSER_ERROR, "Expected name", []>
+// : IsWhitespace<`${Head["type"]}`> extends true
+// ? ASTNode<NodeType.PARSER_ERROR, "1", []>
+// : ASTNode<NodeType.PARSER_ERROR, "2", []>
+// : Ctx["node"];
+
+export type Parse<Raw extends readonly Token[]> = _Parse<{
+ lastName: null;
+ remainingTokens: Raw;
+ stack: [ASTNode<NodeType.ROOT, "ROOT", []>];
+}>;
+
+const a = "a" as any as Parse<Lex<"test(a)">>;