summaryrefslogtreecommitdiff
path: root/src/lib/core/parser.ts
blob: 27133b2f314e64973fb16eab77c5a31d4e2545f7 (plain)
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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;

type NULL_SENTINEL = {
  NULL: true;
};

export type ParseNumberLiteral<T extends string> =
  T extends `${infer Inner extends number}` ? Inner : NULL_SENTINEL;

export type ParseStringLiteral<T extends string> =
  T extends `"${infer Inner extends string}"` ? Inner : NULL_SENTINEL;

export type ResolveNodeFromToken<_Token extends Token> = ParseNumberLiteral<
  _Token["name"]
> extends number
  ? ASTNode<NodeType.INT, "", ParseNumberLiteral<_Token["name"]>, []>
  : ParseStringLiteral<_Token["name"]> extends string
  ? ASTNode<NodeType.INT, "", ParseStringLiteral<_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["lastToken"] extends Token
  ? // case where we ended with a name
    _Parse<{
      lastToken: null;
      remainingTokens: [];
      stack: PushChildToLastElementOfStack<
        Ctx["stack"],
        ResolveNodeFromToken<Ctx["lastToken"]>
      >;
    }>
  : 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(135)`>>;