1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
import {
ASTNode,
NodeType,
ParserCtx,
Token,
TokenSubType,
TokenType,
} 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",
T,
[]
>;
export type PushChild<Node extends ASTNode, Child extends ASTNode> = {
type: Node["type"];
value: Node["value"];
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 ResolveNodeFromToken<_Token extends Token> =
_Token["subType"] extends TokenSubType.LITERAL
? ASTNode<NodeType.INT, "UNNAMED", _Token["name"], []>
: ASTNode<NodeType.EXT, _Token["name"], null, []>;
export type _Parse<Ctx extends ParserCtx> = Ctx["remainingTokens"] extends [
infer Head extends Token,
...infer Tail extends readonly Token[]
]
? 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
// goto start
_Parse<{
lastToken: Head;
remainingTokens: Tail;
stack: PushChildToLastElementOfStack<
Ctx["stack"],
ResolveNodeFromToken<Ctx["lastToken"]>
>;
}>
: //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<{
lastToken: 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"]>,
ResolveNodeFromToken<Ctx["lastToken"]>
>
>
>;
}>
: Head["type"] extends TokenType.OPEN_PAREN
? // push lastName onto stack
// goto start
_Parse<{
lastToken: null;
remainingTokens: Tail;
stack: [...Ctx["stack"], ResolveNodeFromToken<Ctx["lastToken"]>];
}>
: Ctx & Error<`Was not expecting ${Head["type"]}`>
: // expect nextToken to be a name or close paren
Head["type"] extends TokenType.NAME
? // lastName = nextToken
// goto start
_Parse<{
lastToken: Head;
remainingTokens: Tail;
stack: Ctx["stack"];
}>
: Head["type"] extends TokenType.CLOSE_PAREN
? _Parse<{
lastToken: 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"][0];
export type Parse<Raw extends readonly Token[]> = _Parse<{
lastToken: null;
remainingTokens: Raw;
stack: [ASTNode<NodeType.ROOT, "ROOT", null, []>];
}>;
const test_result = null as unknown as Parse<Lex<"test(a)">>;
|