@xnoxs/flux-lang 3.5.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1571 +1 @@
1
- // ── Flux stdlib ──
2
-
3
- function map(arr, fn) { return arr.map(fn); }
4
-
5
- function join(arr, sep) { return arr.join(sep != null ? sep : ','); }
6
- // ── end stdlib ──
7
-
8
- // Generated by Flux Transpiler v3.2.0
9
- "use strict";
10
-
11
- const { T } = require("./lexer");
12
- function makeParseError(msg, tok) {
13
- const line = (tok?.line ?? "?");
14
- const col = (tok?.col ?? "?");
15
- const err = new Error(`[Parser ${line}:${col}] ${msg}`);
16
- err.name = "ParseError";
17
- err.tok = tok;
18
- return err;
19
- }
20
- class Parser {
21
- constructor(tokens, pos) {
22
- this.tokens = tokens;
23
- this.pos = pos;
24
- }
25
-
26
- peek(n = 0) {
27
- return (this.tokens[(this.pos + n)] ?? { type: "EOF", value: null, line: 0, col: 0 });
28
- }
29
-
30
- check(typ) {
31
- return (this.peek().type == typ);
32
- }
33
-
34
- at(a, b = null, c = null, d = null, e = null, f = null) {
35
- const t = this.peek().type;
36
- return ((((((t == a) || ((b != null) && (t == b))) || ((c != null) && (t == c))) || ((d != null) && (t == d))) || ((e != null) && (t == e))) || ((f != null) && (t == f)));
37
- }
38
-
39
- eat(typ) {
40
- const tok = this.peek();
41
- if ((tok.type != typ)) {
42
- throw makeParseError(`Expected '${typ}', got '${tok.type}' (${JSON.stringify(tok.value)})`, tok);
43
- }
44
- this.pos = (this.pos + 1);
45
- return tok;
46
- }
47
-
48
- maybe(typ) {
49
- if (this.check(typ)) {
50
- this.pos = (this.pos + 1);
51
- return true;
52
- }
53
- return false;
54
- }
55
-
56
- skip() {
57
- const t = this.tokens[this.pos];
58
- this.pos = (this.pos + 1);
59
- return t;
60
- }
61
-
62
- skipNewlines() {
63
- while (this.check(T.NEWLINE)) {
64
- this.pos = (this.pos + 1);
65
- }
66
- }
67
-
68
- err(msg) {
69
- throw makeParseError(msg, this.peek());
70
- }
71
-
72
- parseBlock() {
73
- this.eat(T.COLON);
74
- if (this.check(T.NEWLINE)) {
75
- this.pos = (this.pos + 1);
76
- this.eat(T.INDENT);
77
- const body = this.parseStmtList();
78
- this.maybe(T.DEDENT);
79
- return body;
80
- }
81
- const stmt = this.parseOneStmt();
82
- return [stmt];
83
- }
84
-
85
- parseStmtList() {
86
- const stmts = [];
87
- while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
88
- this.skipNewlines();
89
- if ((this.check(T.DEDENT) || this.check(T.EOF))) {
90
- break;
91
- }
92
- stmts.push(this.parseOneStmt());
93
- }
94
- return stmts;
95
- }
96
-
97
- parse() {
98
- this.skipNewlines();
99
- const body = [];
100
- while (!this.check(T.EOF)) {
101
- this.skipNewlines();
102
- if (this.check(T.EOF)) {
103
- break;
104
- }
105
- body.push(this.parseOneStmt());
106
- }
107
- return { type: "Program", body };
108
- }
109
-
110
- parseOneStmt() {
111
- const tok = this.peek();
112
- if (((tok.type == T.VAR) || (tok.type == T.VAL))) {
113
- return this.parseVarDecl();
114
- }
115
- if ((tok.type == T.FN)) {
116
- return this.parseFnDecl(false);
117
- }
118
- if ((tok.type == T.ASYNC)) {
119
- return this.parseAsyncFn();
120
- }
121
- if ((tok.type == T.CLASS)) {
122
- return this.parseClassDecl();
123
- }
124
- if ((tok.type == T.IF)) {
125
- return this.parseIf();
126
- }
127
- if ((tok.type == T.FOR)) {
128
- return this.parseFor();
129
- }
130
- if ((tok.type == T.WHILE)) {
131
- return this.parseWhile();
132
- }
133
- if ((tok.type == T.DO)) {
134
- return this.parseDoWhile();
135
- }
136
- if ((tok.type == T.MATCH)) {
137
- return this.parseMatch();
138
- }
139
- if ((tok.type == T.RETURN)) {
140
- return this.parseReturn();
141
- }
142
- if ((tok.type == T.BREAK)) {
143
- this.skip();
144
- this.skipNewlines();
145
- return { type: "BreakStmt", loc: tok };
146
- }
147
- if ((tok.type == T.CONTINUE)) {
148
- this.skip();
149
- this.skipNewlines();
150
- return { type: "ContinueStmt", loc: tok };
151
- }
152
- if ((tok.type == T.IMPORT)) {
153
- return this.parseImport();
154
- }
155
- if ((tok.type == T.EXPORT)) {
156
- return this.parseExport();
157
- }
158
- if ((tok.type == T.TRY)) {
159
- return this.parseTryCatch();
160
- }
161
- if ((tok.type == T.THROW)) {
162
- return this.parseThrow();
163
- }
164
- if ((tok.type == T.TYPE)) {
165
- return this.parseTypeDecl();
166
- }
167
- if ((tok.type == T.INTERFACE)) {
168
- return this.parseInterfaceDecl();
169
- }
170
- if ((tok.type == T.ENUM)) {
171
- return this.parseEnumDecl();
172
- }
173
- return this.parseExprStmt();
174
- }
175
-
176
- parseVarDecl() {
177
- const kind = ((this.peek().type == T.VAR) ? "var" : "val");
178
- const loc = this.skip();
179
- if (this.check(T.LBRACE)) {
180
- const pattern = this.parseObjectDestructurePattern();
181
- this.eat(T.EQ);
182
- const init = this.parseExpr();
183
- this.skipNewlines();
184
- return { type: "DestructureDecl", kind, patternType: "object", pattern, init, loc };
185
- }
186
- if (this.check(T.LBRACKET)) {
187
- const pattern = this.parseArrayDestructurePattern();
188
- this.eat(T.EQ);
189
- const init = this.parseExpr();
190
- this.skipNewlines();
191
- return { type: "DestructureDecl", kind, patternType: "array", pattern, init, loc };
192
- }
193
- const name = this.eat(T.IDENT).value;
194
- let typeAnn = null;
195
- if (this.maybe(T.COLON)) {
196
- typeAnn = this.parseTypeAnn();
197
- }
198
- let init = null;
199
- if (this.maybe(T.EQ)) {
200
- init = this.parseExpr();
201
- }
202
- this.skipNewlines();
203
- return { type: "VarDecl", kind, name, typeAnn, init, loc };
204
- }
205
-
206
- parseObjectDestructurePattern() {
207
- this.eat(T.LBRACE);
208
- const props = [];
209
- while ((!this.check(T.RBRACE) && !this.check(T.EOF))) {
210
- let rest = false;
211
- if (this.check(T.DOTDOTDOT)) {
212
- this.pos = (this.pos + 1);
213
- rest = true;
214
- }
215
- const key = this.eat(T.IDENT).value;
216
- if (rest) {
217
- props.push({ key, alias: key, rest: true });
218
- break;
219
- }
220
- let alias = key;
221
- if (this.maybe(T.COLON)) {
222
- alias = this.eat(T.IDENT).value;
223
- }
224
- let defaultVal = null;
225
- if (this.maybe(T.EQ)) {
226
- defaultVal = this.parseExpr();
227
- }
228
- props.push({ key, alias, defaultVal, rest: false });
229
- if (!this.maybe(T.COMMA)) {
230
- break;
231
- }
232
- }
233
- this.eat(T.RBRACE);
234
- return props;
235
- }
236
-
237
- parseArrayDestructurePattern() {
238
- this.eat(T.LBRACKET);
239
- const items = [];
240
- while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) {
241
- if (this.check(T.COMMA)) {
242
- items.push(null);
243
- this.pos = (this.pos + 1);
244
- continue;
245
- }
246
- let rest = false;
247
- if (this.check(T.DOTDOTDOT)) {
248
- this.pos = (this.pos + 1);
249
- rest = true;
250
- }
251
- const name = this.eat(T.IDENT).value;
252
- let defaultVal = null;
253
- if (this.maybe(T.EQ)) {
254
- defaultVal = this.parseExpr();
255
- }
256
- items.push({ name, defaultVal, rest });
257
- if (rest) {
258
- break;
259
- }
260
- if (!this.maybe(T.COMMA)) {
261
- break;
262
- }
263
- }
264
- this.eat(T.RBRACKET);
265
- return items;
266
- }
267
-
268
- parseTypeAnn() {
269
- let name = this.parseIntersectionType();
270
- while (this.check(T.PIPEB)) {
271
- this.pos = (this.pos + 1);
272
- name = ((name + " | ") + this.parseIntersectionType());
273
- }
274
- return name;
275
- }
276
-
277
- parseIntersectionType() {
278
- let name = this.parseSingleType();
279
- while (this.check(T.AMPERSAND)) {
280
- this.pos = (this.pos + 1);
281
- name = ((name + " & ") + this.parseSingleType());
282
- }
283
- return name;
284
- }
285
-
286
- parseSingleType() {
287
- const tok = this.peek();
288
- if ((tok.type == T.LBRACKET)) {
289
- this.pos = (this.pos + 1);
290
- const parts = [];
291
- while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) {
292
- parts.push(this.parseTypeAnn());
293
- if (!this.maybe(T.COMMA)) {
294
- break;
295
- }
296
- }
297
- this.eat(T.RBRACKET);
298
- return (("[" + parts.join(", ")) + "]");
299
- }
300
- if ((tok.type == T.LBRACE)) {
301
- this.pos = (this.pos + 1);
302
- const pairs = [];
303
- while ((!this.check(T.RBRACE) && !this.check(T.EOF))) {
304
- if (this.check(T.LBRACKET)) {
305
- this.pos = (this.pos + 1);
306
- const iname = this.eat(T.IDENT).value;
307
- this.eat(T.COLON);
308
- const iktype = this.parseTypeAnn();
309
- this.eat(T.RBRACKET);
310
- this.eat(T.COLON);
311
- const ivtype = this.parseTypeAnn();
312
- pairs.push(`[${iname}: ${iktype}]: ${ivtype}`);
313
- }
314
- else {
315
- const key = (this.at(T.IDENT, T.STRING) ? this.skip().value : this.eat(T.IDENT).value);
316
- let optional = false;
317
- if (this.check(T.QUESTION)) {
318
- this.pos = (this.pos + 1);
319
- optional = true;
320
- }
321
- this.eat(T.COLON);
322
- const valType = this.parseTypeAnn();
323
- const optStr = (optional ? "?" : "");
324
- pairs.push(`${key}${optStr}: ${valType}`);
325
- }
326
- this.maybe(T.COMMA);
327
- }
328
- this.eat(T.RBRACE);
329
- return (("{ " + pairs.join(", ")) + " }");
330
- }
331
- if ((tok.type == T.LPAREN)) {
332
- this.pos = (this.pos + 1);
333
- const inner = this.parseTypeAnn();
334
- this.eat(T.RPAREN);
335
- return (("(" + inner) + ")");
336
- }
337
- if (((tok.type == T.IDENT) && (tok.value == "keyof"))) {
338
- this.pos = (this.pos + 1);
339
- return ("keyof " + this.parseSingleType());
340
- }
341
- if ((tok.type == T.TYPEOF)) {
342
- this.pos = (this.pos + 1);
343
- return ("typeof " + this.eat(T.IDENT).value);
344
- }
345
- if ((tok.type == T.READONLY)) {
346
- this.pos = (this.pos + 1);
347
- return ("readonly " + this.parseSingleType());
348
- }
349
- if (((tok.type == T.IDENT) && (tok.value == "infer"))) {
350
- this.pos = (this.pos + 1);
351
- return ("infer " + this.eat(T.IDENT).value);
352
- }
353
- if ((tok.type == T.FN)) {
354
- this.pos = (this.pos + 1);
355
- const paramTypes = [];
356
- if (this.check(T.LPAREN)) {
357
- this.pos = (this.pos + 1);
358
- while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
359
- let pt = "";
360
- if ((this.check(T.IDENT) && (this.peek(1).type == T.COLON))) {
361
- this.pos = (this.pos + 2);
362
- pt = this.parseTypeAnn();
363
- }
364
- else {
365
- pt = this.parseTypeAnn();
366
- }
367
- paramTypes.push(pt);
368
- if (!this.maybe(T.COMMA)) {
369
- break;
370
- }
371
- }
372
- this.eat(T.RPAREN);
373
- }
374
- let retType = "Void";
375
- if (this.check(T.ARROW)) {
376
- this.pos = (this.pos + 1);
377
- retType = this.parseTypeAnn();
378
- }
379
- return ((("fn(" + paramTypes.join(", ")) + ") -> ") + retType);
380
- }
381
- let name = "";
382
- if ((((tok.type == T.IDENT) || (tok.type == T.CONST)) || (tok.type == T.TYPE))) {
383
- name = this.skip().value;
384
- }
385
- else {
386
- name = this.eat(T.IDENT).value;
387
- }
388
- if (this.check(T.EXTENDS)) {
389
- this.pos = (this.pos + 1);
390
- const constraint = this.parseSingleType();
391
- this.eat(T.QUESTION);
392
- const thenType = this.parseTypeAnn();
393
- this.eat(T.COLON);
394
- const elseType = this.parseTypeAnn();
395
- return `${name} extends ${constraint} ? ${thenType} : ${elseType}`;
396
- }
397
- if (this.check(T.LT)) {
398
- this.pos = (this.pos + 1);
399
- const params = [this.parseTypeAnn()];
400
- while (this.maybe(T.COMMA)) {
401
- params.push(this.parseTypeAnn());
402
- }
403
- this.eat(T.GT);
404
- name = (((name + "<") + params.join(", ")) + ">");
405
- }
406
- while ((this.check(T.LBRACKET) && (this.peek(1).type == T.RBRACKET))) {
407
- this.pos = (this.pos + 2);
408
- name = (name + "[]");
409
- }
410
- if ((this.check(T.QUESTION) && (this.peek(1).type != T.DOT))) {
411
- this.pos = (this.pos + 1);
412
- name = (name + "?");
413
- }
414
- return name;
415
- }
416
-
417
- isTypeAnnBeforeColon() {
418
- const saved = this.pos;
419
- try {
420
- const tok0 = this.peek();
421
- if ((((tok0.type == T.LBRACKET) || (tok0.type == T.LBRACE)) || (tok0.type == T.LPAREN))) {
422
- let depth = 0;
423
- let i = 0;
424
- while ((this.tokens[(this.pos + i)] && (this.tokens[(this.pos + i)].type != T.EOF))) {
425
- const tt = this.tokens[(this.pos + i)].type;
426
- if (((((tt == T.LBRACKET) || (tt == T.LBRACE)) || (tt == T.LPAREN)) || (tt == T.LT))) {
427
- depth = (depth + 1);
428
- }
429
- if (((((tt == T.RBRACKET) || (tt == T.RBRACE)) || (tt == T.RPAREN)) || (tt == T.GT))) {
430
- depth = (depth - 1);
431
- }
432
- i = (i + 1);
433
- if ((depth == 0)) {
434
- break;
435
- }
436
- }
437
- if ((this.tokens[(this.pos + i)] && (this.tokens[(this.pos + i)].type == T.QUESTION))) {
438
- i = (i + 1);
439
- }
440
- const nextType = this.tokens[(this.pos + i)]?.type;
441
- this.pos = saved;
442
- return (nextType == T.COLON);
443
- }
444
- if (((((tok0.type != T.IDENT) && (tok0.type != T.TYPEOF)) && (tok0.type != T.READONLY)) && (tok0.type != T.FN))) {
445
- this.pos = saved;
446
- return false;
447
- }
448
- this.parseTypeAnn();
449
- const result = (this.check(T.NEWLINE) || this.check(T.EOF));
450
- this.pos = saved;
451
- return result;
452
- }
453
- catch (e) {
454
- this.pos = saved;
455
- return false;
456
- }
457
- }
458
-
459
- isColonReturnType() {
460
- const saved = this.pos;
461
- try {
462
- this.pos = (this.pos + 1);
463
- if (!this.at(T.IDENT, T.LBRACKET, T.LBRACE, T.LPAREN, T.FN, T.READONLY, T.TYPEOF)) {
464
- this.pos = saved;
465
- return false;
466
- }
467
- this.parseTypeAnn();
468
- const result = (this.check(T.NEWLINE) || this.check(T.EOF));
469
- this.pos = saved;
470
- return result;
471
- }
472
- catch (e) {
473
- this.pos = saved;
474
- return false;
475
- }
476
- }
477
-
478
- parseTypeDecl() {
479
- const loc = this.eat(T.TYPE);
480
- const name = this.eat(T.IDENT).value;
481
- const typeParams = [];
482
- if (this.check(T.LT)) {
483
- this.pos = (this.pos + 1);
484
- while ((!this.check(T.GT) && !this.check(T.EOF))) {
485
- typeParams.push(this.eat(T.IDENT).value);
486
- if (!this.maybe(T.COMMA)) {
487
- break;
488
- }
489
- }
490
- this.eat(T.GT);
491
- }
492
- this.eat(T.EQ);
493
- const variants = [];
494
- let running = true;
495
- while (running) {
496
- const vname = this.eat(T.IDENT).value;
497
- const fields = [];
498
- const fieldTypes = { };
499
- if (this.check(T.LPAREN)) {
500
- this.eat(T.LPAREN);
501
- while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
502
- const ftok = this.peek();
503
- let fname = "";
504
- if ((ftok.type == T.IDENT)) {
505
- fname = this.skip().value;
506
- }
507
- else {
508
- fname = this.skip().value;
509
- }
510
- if (this.maybe(T.COLON)) {
511
- fieldTypes[fname] = this.parseTypeAnn();
512
- }
513
- fields.push(fname);
514
- if (!this.maybe(T.COMMA)) {
515
- break;
516
- }
517
- }
518
- this.eat(T.RPAREN);
519
- }
520
- variants.push({ name: vname, fields, fieldTypes });
521
- if (!this.check(T.PIPEB)) {
522
- running = false;
523
- }
524
- else {
525
- this.pos = (this.pos + 1);
526
- }
527
- }
528
- this.skipNewlines();
529
- return { type: "TypeDecl", name, variants, loc };
530
- }
531
-
532
- parseInterfaceDecl() {
533
- const loc = this.eat(T.INTERFACE);
534
- const name = this.eat(T.IDENT).value;
535
- const typeParams = [];
536
- if (this.check(T.LT)) {
537
- this.pos = (this.pos + 1);
538
- while ((!this.check(T.GT) && !this.check(T.EOF))) {
539
- typeParams.push(this.eat(T.IDENT).value);
540
- if (!this.maybe(T.COMMA)) {
541
- break;
542
- }
543
- }
544
- this.eat(T.GT);
545
- }
546
- const superInterfaces = [];
547
- if (this.maybe(T.EXTENDS)) {
548
- superInterfaces.push(this.eat(T.IDENT).value);
549
- while (this.maybe(T.COMMA)) {
550
- superInterfaces.push(this.eat(T.IDENT).value);
551
- }
552
- }
553
- this.eat(T.COLON);
554
- if (this.check(T.NEWLINE)) {
555
- this.pos = (this.pos + 1);
556
- }
557
- this.eat(T.INDENT);
558
- const members = [];
559
- while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
560
- this.skipNewlines();
561
- if ((this.check(T.DEDENT) || this.check(T.EOF))) {
562
- break;
563
- }
564
- const mods = this.parseAccessModifiers();
565
- if (this.check(T.FN)) {
566
- this.eat(T.FN);
567
- const mname = this.eat(T.IDENT).value;
568
- const params = this.parseParamList();
569
- let retType = null;
570
- if (this.maybe(T.ARROW)) {
571
- retType = this.parseTypeAnn();
572
- }
573
- this.skipNewlines();
574
- members.push({ kind: "method", name: mname, params, retType, modifiers: mods, isAsync: false });
575
- }
576
- else if (this.check(T.IDENT)) {
577
- const fname = this.eat(T.IDENT).value;
578
- let optional = false;
579
- if (this.check(T.QUESTION)) {
580
- this.pos = (this.pos + 1);
581
- optional = true;
582
- }
583
- this.eat(T.COLON);
584
- const ftype = this.parseTypeAnn();
585
- this.skipNewlines();
586
- members.push({ kind: "field", name: fname, typeAnn: ftype, optional, modifiers: mods });
587
- }
588
- else {
589
- this.skip();
590
- }
591
- }
592
- this.maybe(T.DEDENT);
593
- return { type: "InterfaceDecl", name, typeParams, superInterfaces, members, loc };
594
- }
595
-
596
- parseEnumDecl() {
597
- const loc = this.eat(T.ENUM);
598
- const name = this.eat(T.IDENT).value;
599
- this.eat(T.COLON);
600
- if (this.check(T.NEWLINE)) {
601
- this.pos = (this.pos + 1);
602
- this.eat(T.INDENT);
603
- }
604
- const members = [];
605
- let autoIndex = 0;
606
- while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
607
- this.skipNewlines();
608
- if ((this.check(T.DEDENT) || this.check(T.EOF))) {
609
- break;
610
- }
611
- const mname = this.eat(T.IDENT).value;
612
- let value = { type: "NumberLit", value: autoIndex };
613
- if (this.maybe(T.EQ)) {
614
- const parsed = this.parseExpr();
615
- value = parsed;
616
- if ((parsed.type == "NumberLit")) {
617
- autoIndex = parsed.value;
618
- }
619
- }
620
- else {
621
- value = { type: "NumberLit", value: autoIndex };
622
- }
623
- autoIndex = (autoIndex + 1);
624
- this.skipNewlines();
625
- members.push({ name: mname, value });
626
- }
627
- this.maybe(T.DEDENT);
628
- return { type: "EnumDecl", name, members, loc };
629
- }
630
-
631
- parseIf() {
632
- const loc = this.eat(T.IF);
633
- const cond = this.parseExpr();
634
- const then = this.parseBlock();
635
- const elseifs = [];
636
- let else_ = null;
637
- this.skipNewlines();
638
- while (this.check(T.ELSE)) {
639
- this.pos = (this.pos + 1);
640
- if (this.check(T.IF)) {
641
- this.pos = (this.pos + 1);
642
- const eic = this.parseExpr();
643
- const eib = this.parseBlock();
644
- elseifs.push({ cond: eic, body: eib });
645
- this.skipNewlines();
646
- }
647
- else {
648
- else_ = this.parseBlock();
649
- break;
650
- }
651
- }
652
- return { type: "IfStmt", cond, then, elseifs, else_, loc };
653
- }
654
-
655
- parseFor() {
656
- const loc = this.eat(T.FOR);
657
- let isAwait = false;
658
- if (this.check(T.AWAIT)) {
659
- this.pos = (this.pos + 1);
660
- isAwait = true;
661
- }
662
- const varName = this.eat(T.IDENT).value;
663
- this.eat(T.IN);
664
- const iter = this.parseExpr();
665
- const body = this.parseBlock();
666
- return { type: "ForInStmt", var: varName, iter, body, isAwait, loc };
667
- }
668
-
669
- parseWhile() {
670
- const loc = this.eat(T.WHILE);
671
- const cond = this.parseExpr();
672
- const body = this.parseBlock();
673
- return { type: "WhileStmt", cond, body, loc };
674
- }
675
-
676
- parseDoWhile() {
677
- const loc = this.eat(T.DO);
678
- const body = this.parseBlock();
679
- this.skipNewlines();
680
- this.eat(T.WHILE);
681
- const cond = this.parseExpr();
682
- this.skipNewlines();
683
- return { type: "DoWhileStmt", body, cond, loc };
684
- }
685
-
686
- parseMatch() {
687
- const loc = this.eat(T.MATCH);
688
- const subject = this.parseExpr();
689
- this.eat(T.COLON);
690
- if (this.check(T.NEWLINE)) {
691
- this.pos = (this.pos + 1);
692
- }
693
- this.eat(T.INDENT);
694
- const arms = [];
695
- while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
696
- this.skipNewlines();
697
- if ((this.check(T.DEDENT) || this.check(T.EOF))) {
698
- break;
699
- }
700
- this.eat(T.WHEN);
701
- const pattern = this.parsePattern();
702
- let guard = null;
703
- if (this.check(T.IF)) {
704
- this.pos = (this.pos + 1);
705
- guard = this.parseExpr();
706
- }
707
- if (this.check(T.ARROW)) {
708
- this.pos = (this.pos + 1);
709
- const expr = this.parseExpr();
710
- this.skipNewlines();
711
- arms.push({ pattern, guard, body: [{ type: "ExprStmt", expr }], inline: true });
712
- }
713
- else if (this.check(T.COLON)) {
714
- const isInline = (this.peek(1).type != T.NEWLINE);
715
- const body = this.parseBlock();
716
- const inlineArm = ((isInline && (body.length == 1)) && (body[0].type == "ExprStmt"));
717
- arms.push({ pattern, guard, body, inline: inlineArm });
718
- }
719
- }
720
- this.maybe(T.DEDENT);
721
- return { type: "MatchStmt", subject, arms, loc };
722
- }
723
-
724
- parsePattern() {
725
- if (this.check(T.WILDCARD)) {
726
- this.skip();
727
- return { type: "WildcardPat" };
728
- }
729
- if ((this.check(T.IDENT) && (this.peek(1).type == T.LPAREN))) {
730
- const vname = this.eat(T.IDENT).value;
731
- this.eat(T.LPAREN);
732
- const bindings = [];
733
- while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
734
- if (this.check(T.WILDCARD)) {
735
- bindings.push("_");
736
- this.pos = (this.pos + 1);
737
- }
738
- else {
739
- bindings.push(this.eat(T.IDENT).value);
740
- }
741
- if (!this.maybe(T.COMMA)) {
742
- break;
743
- }
744
- }
745
- this.eat(T.RPAREN);
746
- return { type: "VariantPat", variant: vname, bindings };
747
- }
748
- let left = this.parsePrimary();
749
- while (this.check(T.DOT)) {
750
- this.pos = (this.pos + 1);
751
- const prop = this.skip().value;
752
- left = { type: "MemberExpr", obj: left, prop };
753
- }
754
- if (this.check(T.DOTDOT)) {
755
- this.pos = (this.pos + 1);
756
- const right = this.parsePrimary();
757
- return { type: "RangePat", start: left, end: right };
758
- }
759
- return { type: "LiteralPat", value: left };
760
- }
761
-
762
- parseReturn() {
763
- const loc = this.eat(T.RETURN);
764
- let value = null;
765
- if (!this.at(T.NEWLINE, T.EOF, T.DEDENT)) {
766
- value = this.parseExpr();
767
- }
768
- this.skipNewlines();
769
- return { type: "ReturnStmt", value, loc };
770
- }
771
-
772
- parseTryCatch() {
773
- const loc = this.eat(T.TRY);
774
- const tryBody = this.parseBlock();
775
- let catchParam = null;
776
- let catchBody = null;
777
- let finallyBody = null;
778
- this.skipNewlines();
779
- if (this.check(T.CATCH)) {
780
- this.pos = (this.pos + 1);
781
- if (this.check(T.LPAREN)) {
782
- this.pos = (this.pos + 1);
783
- catchParam = this.eat(T.IDENT).value;
784
- if (this.maybe(T.COLON)) {
785
- this.parseTypeAnn();
786
- }
787
- this.eat(T.RPAREN);
788
- }
789
- catchBody = this.parseBlock();
790
- this.skipNewlines();
791
- }
792
- if (this.check(T.FINALLY)) {
793
- this.pos = (this.pos + 1);
794
- finallyBody = this.parseBlock();
795
- }
796
- return { type: "TryCatchStmt", tryBody, catchParam, catchBody, finallyBody, loc };
797
- }
798
-
799
- parseThrow() {
800
- const loc = this.eat(T.THROW);
801
- const value = this.parseExpr();
802
- this.skipNewlines();
803
- return { type: "ThrowStmt", value, loc };
804
- }
805
-
806
- parseImport() {
807
- this.eat(T.IMPORT);
808
- if (this.check(T.STAR)) {
809
- this.pos = (this.pos + 1);
810
- this.eat(T.AS);
811
- const namespaceName = this.eat(T.IDENT).value;
812
- this.eat(T.FROM);
813
- const source = this.eat(T.STRING).value;
814
- this.skipNewlines();
815
- return { type: "ImportDecl", names: [], defaultName: null, namespaceName, source };
816
- }
817
- if (this.check(T.IDENT)) {
818
- const defaultName = this.eat(T.IDENT).value;
819
- this.eat(T.FROM);
820
- const source = this.eat(T.STRING).value;
821
- this.skipNewlines();
822
- return { type: "ImportDecl", names: [], defaultName, source };
823
- }
824
- const names = [];
825
- if (this.maybe(T.LBRACE)) {
826
- while ((!this.check(T.RBRACE) && !this.check(T.EOF))) {
827
- const name = this.eat(T.IDENT).value;
828
- let alias = name;
829
- if (this.check(T.AS)) {
830
- this.pos = (this.pos + 1);
831
- alias = this.eat(T.IDENT).value;
832
- }
833
- names.push({ name, alias });
834
- if (!this.maybe(T.COMMA)) {
835
- break;
836
- }
837
- }
838
- this.eat(T.RBRACE);
839
- }
840
- this.eat(T.FROM);
841
- const source = this.eat(T.STRING).value;
842
- this.skipNewlines();
843
- return { type: "ImportDecl", names, defaultName: null, source };
844
- }
845
-
846
- parseExport() {
847
- this.eat(T.EXPORT);
848
- if (this.check(T.DEFAULT)) {
849
- this.pos = (this.pos + 1);
850
- if (this.check(T.ASYNC)) {
851
- this.pos = (this.pos + 1);
852
- const decl = this.parseFnDecl(true);
853
- return { type: "ExportDecl", isDefault: true, decl };
854
- }
855
- if (this.check(T.FN)) {
856
- const decl = this.parseFnDecl(false);
857
- return { type: "ExportDecl", isDefault: true, decl };
858
- }
859
- const value = this.parseExpr();
860
- this.skipNewlines();
861
- return { type: "ExportDecl", isDefault: true, decl: value };
862
- }
863
- if (this.check(T.ASYNC)) {
864
- this.pos = (this.pos + 1);
865
- if (!this.check(T.FN)) {
866
- this.err("Expected fn after async");
867
- }
868
- const decl = this.parseFnDecl(true);
869
- return { type: "ExportDecl", isDefault: false, decl };
870
- }
871
- const decl = this.parseOneStmt();
872
- return { type: "ExportDecl", isDefault: false, decl };
873
- }
874
-
875
- parseFnDecl(isAsync = false) {
876
- const loc = this.eat(T.FN);
877
- let name = null;
878
- if ((((((((this.check(T.IDENT) || this.check(T.NEW)) || this.check(T.FROM)) || this.check(T.AS)) || this.check(T.DEFAULT)) || this.check(T.IS)) || this.check(T.IN)) || this.check(T.TYPE))) {
879
- name = this.skip().value;
880
- }
881
- const params = this.parseParamList();
882
- let retType = null;
883
- if (this.check(T.ARROW)) {
884
- this.pos = (this.pos + 1);
885
- if (this.isTypeAnnBeforeColon()) {
886
- retType = this.parseTypeAnn();
887
- const body = this.parseBlock();
888
- return { type: "FnDecl", name, params, retType, body, inline: false, async: isAsync, loc };
889
- }
890
- const body = this.parseExpr();
891
- this.skipNewlines();
892
- return { type: "FnDecl", name, params, retType: null, body, inline: true, async: isAsync, loc };
893
- }
894
- if (this.check(T.COLON)) {
895
- if (this.isColonReturnType()) {
896
- this.pos = (this.pos + 1);
897
- retType = this.parseTypeAnn();
898
- if (this.check(T.NEWLINE)) {
899
- this.pos = (this.pos + 1);
900
- }
901
- if (this.check(T.INDENT)) {
902
- this.pos = (this.pos + 1);
903
- const body = this.parseStmtList();
904
- this.maybe(T.DEDENT);
905
- return { type: "FnDecl", name, params, retType, body, inline: false, async: isAsync, loc };
906
- }
907
- const stmt = this.parseOneStmt();
908
- return { type: "FnDecl", name, params, retType, body: [stmt], inline: false, async: isAsync, loc };
909
- }
910
- const body = this.parseBlock();
911
- return { type: "FnDecl", name, params, retType: null, body, inline: false, async: isAsync, loc };
912
- }
913
- this.err("Expected -> or : after function signature");
914
- }
915
-
916
- parseAsyncFn() {
917
- this.eat(T.ASYNC);
918
- if (!this.check(T.FN)) {
919
- this.err("Expected fn after async");
920
- }
921
- return this.parseFnDecl(true);
922
- }
923
-
924
- parseParamList() {
925
- this.eat(T.LPAREN);
926
- const params = [];
927
- while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
928
- let rest = false;
929
- if (this.check(T.DOTDOTDOT)) {
930
- this.pos = (this.pos + 1);
931
- rest = true;
932
- }
933
- const name = this.eat(T.IDENT).value;
934
- let optional = false;
935
- let typeAnn = null;
936
- if ((!rest && this.check(T.QUESTION))) {
937
- this.pos = (this.pos + 1);
938
- optional = true;
939
- }
940
- if ((!rest && this.maybe(T.COLON))) {
941
- typeAnn = this.parseTypeAnn();
942
- }
943
- let defaultVal = null;
944
- if ((!rest && this.maybe(T.EQ))) {
945
- defaultVal = this.parseExpr();
946
- }
947
- params.push({ name, typeAnn, optional, defaultVal, rest });
948
- if (rest) {
949
- break;
950
- }
951
- if (!this.maybe(T.COMMA)) {
952
- break;
953
- }
954
- }
955
- this.eat(T.RPAREN);
956
- return params;
957
- }
958
-
959
- parseAccessModifiers() {
960
- const mods = new Set([]);
961
- let running = true;
962
- while (running) {
963
- const t = this.peek().type;
964
- if ((((((((t == T.PRIVATE) || (t == T.PUBLIC)) || (t == T.PROTECTED)) || (t == T.READONLY)) || (t == T.STATIC)) || (t == T.ABSTRACT)) || (t == T.OVERRIDE))) {
965
- mods.add(this.skip().value);
966
- }
967
- else {
968
- running = false;
969
- }
970
- }
971
- return mods;
972
- }
973
-
974
- parseClassDecl() {
975
- const loc = this.eat(T.CLASS);
976
- const name = this.eat(T.IDENT).value;
977
- let superClass = null;
978
- const interfaces = [];
979
- const typeParams = [];
980
- if (this.check(T.LT)) {
981
- this.pos = (this.pos + 1);
982
- typeParams.push(this.eat(T.IDENT).value);
983
- while (this.maybe(T.COMMA)) {
984
- typeParams.push(this.eat(T.IDENT).value);
985
- }
986
- this.eat(T.GT);
987
- }
988
- if (this.maybe(T.EXTENDS)) {
989
- superClass = this.eat(T.IDENT).value;
990
- }
991
- if (this.maybe(T.IMPLEMENTS)) {
992
- interfaces.push(this.eat(T.IDENT).value);
993
- while (this.maybe(T.COMMA)) {
994
- interfaces.push(this.eat(T.IDENT).value);
995
- }
996
- }
997
- this.eat(T.COLON);
998
- if (this.check(T.NEWLINE)) {
999
- this.pos = (this.pos + 1);
1000
- this.eat(T.INDENT);
1001
- }
1002
- const fields = [];
1003
- const methods = [];
1004
- while ((!this.check(T.DEDENT) && !this.check(T.EOF))) {
1005
- this.skipNewlines();
1006
- if ((this.check(T.DEDENT) || this.check(T.EOF))) {
1007
- break;
1008
- }
1009
- const mods = this.parseAccessModifiers();
1010
- if (this.check(T.FN)) {
1011
- const m = this.parseFnDecl(false);
1012
- m.modifiers = mods;
1013
- methods.push(m);
1014
- }
1015
- else if (this.check(T.ASYNC)) {
1016
- const m = this.parseAsyncFn();
1017
- m.modifiers = mods;
1018
- methods.push(m);
1019
- }
1020
- else if ((this.check(T.STATIC) && (this.peek(1).type == T.FN))) {
1021
- this.skip();
1022
- const m = this.parseFnDecl(false);
1023
- m.modifiers = mods;
1024
- m.modifiers.add("static");
1025
- methods.push(m);
1026
- }
1027
- else if (this.check(T.IDENT)) {
1028
- const fname = this.eat(T.IDENT).value;
1029
- let optional = false;
1030
- if (this.check(T.QUESTION)) {
1031
- this.pos = (this.pos + 1);
1032
- optional = true;
1033
- }
1034
- this.eat(T.COLON);
1035
- const ftype = this.parseTypeAnn();
1036
- this.skipNewlines();
1037
- fields.push({ name: fname, typeAnn: ftype, optional, modifiers: mods });
1038
- }
1039
- else {
1040
- this.skip();
1041
- }
1042
- }
1043
- this.maybe(T.DEDENT);
1044
- return { type: "ClassDecl", name, typeParams, superClass, interfaces, fields, methods, loc };
1045
- }
1046
-
1047
- parseExprStmt() {
1048
- const expr = this.parseExpr();
1049
- this.skipNewlines();
1050
- return { type: "ExprStmt", expr };
1051
- }
1052
-
1053
- parseExpr() {
1054
- return this.parsePipe();
1055
- }
1056
-
1057
- parsePipe() {
1058
- let left = this.parseAssign();
1059
- let running = true;
1060
- while (running) {
1061
- if (this.check(T.PIPE)) {
1062
- this.pos = (this.pos + 1);
1063
- const right = this.parseAssign();
1064
- left = { type: "PipeExpr", left, right };
1065
- }
1066
- else if (this.check(T.NEWLINE)) {
1067
- let i = 1;
1068
- while ((this.peek(i).type == T.NEWLINE)) {
1069
- i = (i + 1);
1070
- }
1071
- if ((this.peek(i).type == T.PIPE)) {
1072
- while (this.check(T.NEWLINE)) {
1073
- this.pos = (this.pos + 1);
1074
- }
1075
- this.pos = (this.pos + 1);
1076
- const right = this.parseAssign();
1077
- left = { type: "PipeExpr", left, right };
1078
- }
1079
- else {
1080
- running = false;
1081
- }
1082
- }
1083
- else {
1084
- running = false;
1085
- }
1086
- }
1087
- return left;
1088
- }
1089
-
1090
- parseAssign() {
1091
- const left = this.parseTernary();
1092
- const t = this.peek().type;
1093
- let op = "";
1094
- if ((t == T.EQ)) {
1095
- op = "=";
1096
- }
1097
- else if ((t == T.PLUSEQ)) {
1098
- op = "+=";
1099
- }
1100
- else if ((t == T.MINUSEQ)) {
1101
- op = "-=";
1102
- }
1103
- else if ((t == T.STAREQ)) {
1104
- op = "*=";
1105
- }
1106
- else if ((t == T.SLASHEQ)) {
1107
- op = "/=";
1108
- }
1109
- else if ((t == T.PERCENTEQ)) {
1110
- op = "%=";
1111
- }
1112
- if (op) {
1113
- this.pos = (this.pos + 1);
1114
- return { type: "AssignExpr", target: left, op, value: this.parseAssign() };
1115
- }
1116
- return left;
1117
- }
1118
-
1119
- parseTernary() {
1120
- const cond = this.parseNullish();
1121
- if (this.maybe(T.QUESTION)) {
1122
- const then = this.parseNullish();
1123
- this.eat(T.COLON);
1124
- const else_ = this.parseTernary();
1125
- return { type: "TernaryExpr", cond, then, else_ };
1126
- }
1127
- return cond;
1128
- }
1129
-
1130
- parseNullish() {
1131
- let l = this.parseOr();
1132
- while (this.check(T.NULLISH)) {
1133
- this.pos = (this.pos + 1);
1134
- const r = this.parseOr();
1135
- l = { type: "BinaryExpr", op: "??", left: l, right: r };
1136
- }
1137
- return l;
1138
- }
1139
-
1140
- parseOr() {
1141
- let l = this.parseAnd();
1142
- while ((this.check(T.OR) || this.check(T.OROR))) {
1143
- this.pos = (this.pos + 1);
1144
- const r = this.parseAnd();
1145
- l = { type: "BinaryExpr", op: "||", left: l, right: r };
1146
- }
1147
- return l;
1148
- }
1149
-
1150
- parseAnd() {
1151
- let l = this.parseBitOr();
1152
- while ((this.check(T.AND) || this.check(T.ANDAND))) {
1153
- this.pos = (this.pos + 1);
1154
- const r = this.parseBitOr();
1155
- l = { type: "BinaryExpr", op: "&&", left: l, right: r };
1156
- }
1157
- return l;
1158
- }
1159
-
1160
- parseBitOr() {
1161
- let l = this.parseBitXor();
1162
- while (this.check(T.PIPEB)) {
1163
- this.pos = (this.pos + 1);
1164
- const r = this.parseBitXor();
1165
- l = { type: "BinaryExpr", op: "|", left: l, right: r };
1166
- }
1167
- return l;
1168
- }
1169
-
1170
- parseBitXor() {
1171
- let l = this.parseBitAnd();
1172
- while (this.check(T.CARET)) {
1173
- this.pos = (this.pos + 1);
1174
- const r = this.parseBitAnd();
1175
- l = { type: "BinaryExpr", op: "^", left: l, right: r };
1176
- }
1177
- return l;
1178
- }
1179
-
1180
- parseBitAnd() {
1181
- let l = this.parseEq();
1182
- while (this.check(T.AMPERSAND)) {
1183
- this.pos = (this.pos + 1);
1184
- const r = this.parseEq();
1185
- l = { type: "BinaryExpr", op: "&", left: l, right: r };
1186
- }
1187
- return l;
1188
- }
1189
-
1190
- parseEq() {
1191
- let l = this.parseRel();
1192
- while (this.at(T.EQEQ, T.NEQ, T.EQEQEQ, T.NEQEQ)) {
1193
- const op = this.skip().value;
1194
- const r = this.parseRel();
1195
- l = { type: "BinaryExpr", op, left: l, right: r };
1196
- }
1197
- return l;
1198
- }
1199
-
1200
- parseRel() {
1201
- let l = this.parseShift();
1202
- while ((this.at(T.LT, T.LTE, T.GT, T.GTE) || this.check(T.IN))) {
1203
- const op = this.skip().value;
1204
- const r = this.parseShift();
1205
- l = { type: "BinaryExpr", op, left: l, right: r };
1206
- }
1207
- return l;
1208
- }
1209
-
1210
- parseShift() {
1211
- let l = this.parseRange();
1212
- while (this.at(T.LSHIFT, T.RSHIFT)) {
1213
- const op = this.skip().value;
1214
- const r = this.parseRange();
1215
- l = { type: "BinaryExpr", op, left: l, right: r };
1216
- }
1217
- return l;
1218
- }
1219
-
1220
- parseRange() {
1221
- const l = this.parseAdd();
1222
- if (this.check(T.DOTDOT)) {
1223
- this.pos = (this.pos + 1);
1224
- const r = this.parseAdd();
1225
- return { type: "RangeExpr", start: l, end: r };
1226
- }
1227
- return l;
1228
- }
1229
-
1230
- parseAdd() {
1231
- let l = this.parseMul();
1232
- while (this.at(T.PLUS, T.MINUS)) {
1233
- const op = this.skip().value;
1234
- const r = this.parseMul();
1235
- l = { type: "BinaryExpr", op, left: l, right: r };
1236
- }
1237
- return l;
1238
- }
1239
-
1240
- parseMul() {
1241
- let l = this.parsePow();
1242
- while (this.at(T.STAR, T.SLASH, T.PERCENT)) {
1243
- const op = this.skip().value;
1244
- const r = this.parsePow();
1245
- l = { type: "BinaryExpr", op, left: l, right: r };
1246
- }
1247
- return l;
1248
- }
1249
-
1250
- parsePow() {
1251
- const l = this.parseUnary();
1252
- if (this.check(T.STARSTAR)) {
1253
- this.pos = (this.pos + 1);
1254
- return { type: "BinaryExpr", op: "**", left: l, right: this.parsePow() };
1255
- }
1256
- return l;
1257
- }
1258
-
1259
- parseUnary() {
1260
- if (this.check(T.MINUS)) {
1261
- this.pos = (this.pos + 1);
1262
- return { type: "UnaryExpr", op: "-", operand: this.parseUnary() };
1263
- }
1264
- if (this.check(T.NOT)) {
1265
- this.pos = (this.pos + 1);
1266
- return { type: "UnaryExpr", op: "!", operand: this.parseUnary() };
1267
- }
1268
- if (this.check(T.BANG)) {
1269
- this.pos = (this.pos + 1);
1270
- return { type: "UnaryExpr", op: "!", operand: this.parseUnary() };
1271
- }
1272
- if (this.check(T.TILDE)) {
1273
- this.pos = (this.pos + 1);
1274
- return { type: "UnaryExpr", op: "~", operand: this.parseUnary() };
1275
- }
1276
- if (this.check(T.PLUSPLUS)) {
1277
- this.pos = (this.pos + 1);
1278
- return { type: "UpdateExpr", op: "++", prefix: true, operand: this.parseUnary() };
1279
- }
1280
- if (this.check(T.MINUSMINUS)) {
1281
- this.pos = (this.pos + 1);
1282
- return { type: "UpdateExpr", op: "--", prefix: true, operand: this.parseUnary() };
1283
- }
1284
- if (this.check(T.AWAIT)) {
1285
- this.pos = (this.pos + 1);
1286
- return { type: "AwaitExpr", operand: this.parseUnary() };
1287
- }
1288
- if (this.check(T.TYPEOF)) {
1289
- this.pos = (this.pos + 1);
1290
- return { type: "TypeofExpr", operand: this.parseUnary() };
1291
- }
1292
- return this.parseLambdaOrPostfix();
1293
- }
1294
-
1295
- parseLambdaOrPostfix() {
1296
- const saved = this.pos;
1297
- if ((this.check(T.IDENT) || this.check(T.WILDCARD))) {
1298
- const name = (this.skip().value ?? "_");
1299
- if (this.check(T.ARROW)) {
1300
- this.pos = (this.pos + 1);
1301
- const body = this.parseExpr();
1302
- return { type: "LambdaExpr", params: [{ name }], body };
1303
- }
1304
- this.pos = saved;
1305
- }
1306
- return this.parsePostfix();
1307
- }
1308
-
1309
- parsePostfix() {
1310
- let expr = this.parsePrimary();
1311
- let running = true;
1312
- while (running) {
1313
- if (this.check(T.PLUSPLUS)) {
1314
- this.pos = (this.pos + 1);
1315
- expr = { type: "UpdateExpr", op: "++", prefix: false, operand: expr };
1316
- }
1317
- else if (this.check(T.MINUSMINUS)) {
1318
- this.pos = (this.pos + 1);
1319
- expr = { type: "UpdateExpr", op: "--", prefix: false, operand: expr };
1320
- }
1321
- else if (this.check(T.DOT)) {
1322
- this.pos = (this.pos + 1);
1323
- const propTok = this.peek();
1324
- const prop = this.skip().value;
1325
- expr = { type: "MemberExpr", obj: expr, prop };
1326
- }
1327
- else if (this.check(T.QUESTIONDOT)) {
1328
- this.pos = (this.pos + 1);
1329
- if (this.check(T.LPAREN)) {
1330
- const args = this.parseArgList();
1331
- expr = { type: "OptCallExpr", callee: expr, args };
1332
- }
1333
- else if (this.check(T.LBRACKET)) {
1334
- this.pos = (this.pos + 1);
1335
- const idx = this.parseExpr();
1336
- this.eat(T.RBRACKET);
1337
- expr = { type: "OptIndexExpr", obj: expr, idx };
1338
- }
1339
- else {
1340
- const prop = this.skip().value;
1341
- expr = { type: "OptMemberExpr", obj: expr, prop };
1342
- }
1343
- }
1344
- else if (this.check(T.INSTANCEOF)) {
1345
- this.pos = (this.pos + 1);
1346
- const right = this.eat(T.IDENT).value;
1347
- expr = { type: "BinaryExpr", op: "instanceof", left: expr, right: { type: "Identifier", name: right } };
1348
- }
1349
- else if (this.check(T.AS)) {
1350
- this.pos = (this.pos + 1);
1351
- if (this.check(T.CONST)) {
1352
- this.pos = (this.pos + 1);
1353
- expr = { type: "AsConstExpr", expr };
1354
- }
1355
- else {
1356
- const castType = this.parseTypeAnn();
1357
- expr = { type: "CastExpr", expr, castType };
1358
- }
1359
- }
1360
- else if (this.check(T.SATISFIES)) {
1361
- this.pos = (this.pos + 1);
1362
- const satType = this.parseTypeAnn();
1363
- expr = { type: "SatisfiesExpr", expr, satType };
1364
- }
1365
- else if (this.check(T.IS)) {
1366
- this.pos = (this.pos + 1);
1367
- const isType = this.parseTypeAnn();
1368
- expr = { type: "IsExpr", expr, isType };
1369
- }
1370
- else if (this.check(T.BANG)) {
1371
- this.pos = (this.pos + 1);
1372
- expr = { type: "NonNullExpr", expr };
1373
- }
1374
- else if (this.check(T.LBRACKET)) {
1375
- this.pos = (this.pos + 1);
1376
- const idx = this.parseExpr();
1377
- this.eat(T.RBRACKET);
1378
- expr = { type: "IndexExpr", obj: expr, idx };
1379
- }
1380
- else if (this.check(T.LPAREN)) {
1381
- const args = this.parseArgList();
1382
- if (this.check(T.ARROW)) {
1383
- this.pos = (this.pos + 1);
1384
- const body = this.parseExpr();
1385
- const lambdaParams = args.map((a) => { name: ((a.type == "Identifier") ? a.name : "_") });
1386
- return { type: "LambdaExpr", params: lambdaParams, body };
1387
- }
1388
- expr = { type: "CallExpr", callee: expr, args };
1389
- }
1390
- else {
1391
- running = false;
1392
- }
1393
- }
1394
- return expr;
1395
- }
1396
-
1397
- parseArgList() {
1398
- this.eat(T.LPAREN);
1399
- const args = [];
1400
- while ((!this.check(T.RPAREN) && !this.check(T.EOF))) {
1401
- if (this.check(T.DOTDOTDOT)) {
1402
- this.pos = (this.pos + 1);
1403
- args.push({ type: "SpreadExpr", expr: this.parseExpr() });
1404
- }
1405
- else {
1406
- args.push(this.parseExpr());
1407
- }
1408
- if (!this.maybe(T.COMMA)) {
1409
- break;
1410
- }
1411
- }
1412
- this.eat(T.RPAREN);
1413
- return args;
1414
- }
1415
-
1416
- parsePrimary() {
1417
- const tok = this.peek();
1418
- if ((tok.type == T.NUMBER)) {
1419
- this.pos = (this.pos + 1);
1420
- return { type: "NumberLit", value: tok.value, loc: tok };
1421
- }
1422
- if ((tok.type == T.BOOL)) {
1423
- this.pos = (this.pos + 1);
1424
- return { type: "BoolLit", value: tok.value, loc: tok };
1425
- }
1426
- if ((tok.type == T.NULL)) {
1427
- this.pos = (this.pos + 1);
1428
- return { type: "NullLit", loc: tok };
1429
- }
1430
- if ((tok.type == T.SELF)) {
1431
- this.pos = (this.pos + 1);
1432
- return { type: "SelfExpr", loc: tok };
1433
- }
1434
- if ((tok.type == T.WILDCARD)) {
1435
- this.pos = (this.pos + 1);
1436
- return { type: "Identifier", name: "_", loc: tok };
1437
- }
1438
- if ((tok.type == T.IDENT)) {
1439
- this.pos = (this.pos + 1);
1440
- return { type: "Identifier", name: tok.value, loc: tok };
1441
- }
1442
- if ((tok.type == T.STRING)) {
1443
- this.pos = (this.pos + 1);
1444
- if ((tok.value && tok.value.template)) {
1445
- return { type: "TemplateLit", parts: tok.value.parts, loc: tok };
1446
- }
1447
- return { type: "StringLit", value: tok.value, loc: tok };
1448
- }
1449
- if ((tok.type == T.REGEX)) {
1450
- this.pos = (this.pos + 1);
1451
- return { type: "RegexLit", value: tok.value, loc: tok };
1452
- }
1453
- if ((tok.type == T.NEW)) {
1454
- this.pos = (this.pos + 1);
1455
- const callee = this.eat(T.IDENT).value;
1456
- const args = this.parseArgList();
1457
- return { type: "NewExpr", callee, args };
1458
- }
1459
- if ((tok.type == T.MATCH)) {
1460
- return this.parseMatch();
1461
- }
1462
- if ((tok.type == T.FN)) {
1463
- this.pos = (this.pos + 1);
1464
- const params = this.parseParamList();
1465
- if (this.check(T.ARROW)) {
1466
- this.pos = (this.pos + 1);
1467
- if (this.isTypeAnnBeforeColon()) {
1468
- const retType = this.parseTypeAnn();
1469
- const body = this.parseBlock();
1470
- return { type: "FnDecl", name: null, params, retType, body, inline: false, async: false, loc: tok };
1471
- }
1472
- const body = this.parseExpr();
1473
- return { type: "FnDecl", name: null, params, retType: null, body, inline: true, async: false, loc: tok };
1474
- }
1475
- if (this.check(T.COLON)) {
1476
- const body = this.parseBlock();
1477
- return { type: "FnDecl", name: null, params, retType: null, body, inline: false, async: false, loc: tok };
1478
- }
1479
- this.err("Expected -> or : after anonymous fn");
1480
- }
1481
- if ((tok.type == T.LPAREN)) {
1482
- this.pos = (this.pos + 1);
1483
- if (this.check(T.RPAREN)) {
1484
- this.pos = (this.pos + 1);
1485
- if (this.check(T.ARROW)) {
1486
- this.pos = (this.pos + 1);
1487
- const body = this.parseExpr();
1488
- return { type: "LambdaExpr", params: [], body };
1489
- }
1490
- return { type: "NullLit" };
1491
- }
1492
- const first = this.parseExpr();
1493
- if (this.check(T.RPAREN)) {
1494
- this.pos = (this.pos + 1);
1495
- if (this.check(T.ARROW)) {
1496
- this.pos = (this.pos + 1);
1497
- const body = this.parseExpr();
1498
- const pname = ((first.type == "Identifier") ? first.name : "_");
1499
- return { type: "LambdaExpr", params: [{ name: pname }], body };
1500
- }
1501
- return first;
1502
- }
1503
- const items = [first];
1504
- while (this.maybe(T.COMMA)) {
1505
- items.push(this.parseExpr());
1506
- }
1507
- this.eat(T.RPAREN);
1508
- if (this.check(T.ARROW)) {
1509
- this.pos = (this.pos + 1);
1510
- const body = this.parseExpr();
1511
- const lambdaParams = items.map((i) => { name: ((i.type == "Identifier") ? i.name : "_") });
1512
- return { type: "LambdaExpr", params: lambdaParams, body };
1513
- }
1514
- return items[0];
1515
- }
1516
- if ((tok.type == T.LBRACKET)) {
1517
- this.pos = (this.pos + 1);
1518
- const items = [];
1519
- while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) {
1520
- if (this.check(T.DOTDOTDOT)) {
1521
- this.pos = (this.pos + 1);
1522
- items.push({ type: "SpreadExpr", expr: this.parseExpr() });
1523
- }
1524
- else {
1525
- items.push(this.parseExpr());
1526
- }
1527
- if (!this.maybe(T.COMMA)) {
1528
- break;
1529
- }
1530
- }
1531
- this.eat(T.RBRACKET);
1532
- return { type: "ArrayExpr", items };
1533
- }
1534
- if ((tok.type == T.LBRACE)) {
1535
- this.pos = (this.pos + 1);
1536
- const pairs = [];
1537
- while ((!this.check(T.RBRACE) && !this.check(T.EOF))) {
1538
- if (this.check(T.DOTDOTDOT)) {
1539
- this.pos = (this.pos + 1);
1540
- pairs.push({ spread: true, value: this.parseExpr() });
1541
- if (!this.maybe(T.COMMA)) {
1542
- break;
1543
- }
1544
- continue;
1545
- }
1546
- const key = (this.check(T.STRING) ? this.eat(T.STRING).value : (this.check(T.IDENT) ? this.skip().value : this.skip().value));
1547
- if (!this.check(T.COLON)) {
1548
- pairs.push({ key, value: { type: "Identifier", name: key } });
1549
- }
1550
- else {
1551
- this.eat(T.COLON);
1552
- const v = this.parseExpr();
1553
- pairs.push({ key, value: v });
1554
- }
1555
- if (!this.maybe(T.COMMA)) {
1556
- break;
1557
- }
1558
- }
1559
- this.eat(T.RBRACE);
1560
- return { type: "ObjectExpr", pairs };
1561
- }
1562
- this.err(`Unexpected token: ${tok.type} (${JSON.stringify(tok.value)})`);
1563
- }
1564
-
1565
- }
1566
-
1567
- module.exports.Parser = Parser;
1568
- function makeParser(tokens) {
1569
- return new Parser(tokens, 0);
1570
- }
1571
- module.exports.makeParser = makeParser;
1
+ function map(arr, fn) { return arr.map(fn); } function join(arr, sep) { return arr.join(sep != null ? sep : ','); } "use strict"; const { T } = require("./lexer"); function _a(_b, _c) { const line = (_c?.line ?? "?"); const col = (_c?.col ?? "?"); const err = new Error(`[Parser ${line}:${col}] ${_b}`); err.name = "ParseError"; err.tok = _c; return err; } class Parser { constructor(tokens, pos) { this.tokens = tokens; this.pos = pos; } peek(_d = 0) { return (this.tokens[(this.pos + _d)] ?? { type: "EOF", value: null, line: 0, col: 0 }); } check(_e) { return (this.peek().type == _e); } at(_f, _g = null, _h = null, _i = null, _j = null, _k = null) { const t = this.peek().type; return ((((((t == _f) || ((_g != null) && (t == _g))) || ((_h != null) && (t == _h))) || ((_i != null) && (t == _i))) || ((_j != null) && (t == _j))) || ((_k != null) && (t == _k))); } eat(_e) { const _c = this.peek(); if ((_c.type != _e)) { throw _a(`Expected '${_e}', got '${_c.type}' (${JSON.stringify(_c.value)})`, _c); } this.pos = (this.pos + 1); return _c; } maybe(_e) { if (this.check(_e)) { this.pos = (this.pos + 1); return true; } return false; } skip() { const t = this.tokens[this.pos]; this.pos = (this.pos + 1); return t; } skipNewlines() { while (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } } err(_b) { throw _a(_b, this.peek()); } parseBlock() { this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); this.eat(T.INDENT); const body = this.parseStmtList(); this.maybe(T.DEDENT); return body; } const stmt = this.parseOneStmt(); return [stmt]; } parseStmtList() { const stmts = []; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } stmts.push(this.parseOneStmt()); } return stmts; } parse() { this.skipNewlines(); const body = []; while (!this.check(T.EOF)) { this.skipNewlines(); if (this.check(T.EOF)) { break; } body.push(this.parseOneStmt()); } return { type: "Program", body }; } parseOneStmt() { const _c = this.peek(); if (((_c.type == T.VAR) || (_c.type == T.VAL))) { return this.parseVarDecl(); } if ((_c.type == T.FN)) { return this.parseFnDecl(false); } if ((_c.type == T.ASYNC)) { return this.parseAsyncFn(); } if ((_c.type == T.CLASS)) { return this.parseClassDecl(); } if ((_c.type == T.DECLARE)) { return this.parseDeclareDecl(); } if ((_c.type == T.IF)) { return this.parseIf(); } if ((_c.type == T.FOR)) { return this.parseFor(); } if ((_c.type == T.WHILE)) { return this.parseWhile(); } if ((_c.type == T.DO)) { return this.parseDoWhile(); } if ((_c.type == T.MATCH)) { return this.parseMatch(); } if ((_c.type == T.RETURN)) { return this.parseReturn(); } if ((_c.type == T.BREAK)) { this.skip(); this.skipNewlines(); return { type: "BreakStmt", loc: _c }; } if ((_c.type == T.CONTINUE)) { this.skip(); this.skipNewlines(); return { type: "ContinueStmt", loc: _c }; } if ((_c.type == T.IMPORT)) { return this.parseImport(); } if ((_c.type == T.EXPORT)) { return this.parseExport(); } if ((_c.type == T.TRY)) { return this.parseTryCatch(); } if ((_c.type == T.THROW)) { return this.parseThrow(); } if ((_c.type == T.TYPE)) { return this.parseTypeDecl(); } if ((_c.type == T.INTERFACE)) { return this.parseInterfaceDecl(); } if ((_c.type == T.ENUM)) { return this.parseEnumDecl(); } return this.parseExprStmt(); } parseVarDecl() { const kind = ((this.peek().type == T.VAR) ? "var" : "val"); const loc = this.skip(); if (this.check(T.LBRACE)) { const pattern = this.parseObjectDestructurePattern(); this.eat(T.EQ); const init = this.parseExpr(); this.skipNewlines(); return { type: "DestructureDecl", kind, patternType: "object", pattern, init, loc }; } if (this.check(T.LBRACKET)) { const pattern = this.parseArrayDestructurePattern(); this.eat(T.EQ); const init = this.parseExpr(); this.skipNewlines(); return { type: "DestructureDecl", kind, patternType: "array", pattern, init, loc }; } const name = this.eat(T.IDENT).value; let typeAnn = null; if (this.maybe(T.COLON)) { typeAnn = this.parseTypeAnn(); } let init = null; if (this.maybe(T.EQ)) { init = this.parseExpr(); } this.skipNewlines(); return { type: "VarDecl", kind, name, typeAnn, init, loc }; } parseObjectDestructurePattern() { this.eat(T.LBRACE); const props = []; while ((!this.check(T.RBRACE) && !this.check(T.EOF))) { let rest = false; if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); rest = true; } const key = this.eat(T.IDENT).value; if (rest) { props.push({ key, alias: key, rest: true }); break; } let alias = key; if (this.maybe(T.COLON)) { alias = this.eat(T.IDENT).value; } let defaultVal = null; if (this.maybe(T.EQ)) { defaultVal = this.parseExpr(); } props.push({ key, alias, defaultVal, rest: false }); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACE); return props; } parseArrayDestructurePattern() { this.eat(T.LBRACKET); const items = []; while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) { if (this.check(T.COMMA)) { items.push(null); this.pos = (this.pos + 1); continue; } let rest = false; if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); rest = true; } const name = this.eat(T.IDENT).value; let defaultVal = null; if (this.maybe(T.EQ)) { defaultVal = this.parseExpr(); } items.push({ name, defaultVal, rest }); if (rest) { break; } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACKET); return items; } parseTypeAnn() { let name = this.parseIntersectionType(); while (this.check(T.PIPEB)) { this.pos = (this.pos + 1); name = ((name + " | ") + this.parseIntersectionType()); } return name; } parseIntersectionType() { let name = this.parseSingleType(); while (this.check(T.AMPERSAND)) { this.pos = (this.pos + 1); name = ((name + " & ") + this.parseSingleType()); } return name; } parseSingleType() { const _c = this.peek(); if ((_c.type == T.LBRACKET)) { this.pos = (this.pos + 1); const parts = []; while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) { parts.push(this.parseTypeAnn()); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACKET); return (("[" + parts.join(", ")) + "]"); } if ((_c.type == T.LBRACE)) { this.pos = (this.pos + 1); const pairs = []; while ((!this.check(T.RBRACE) && !this.check(T.EOF))) { if (this.check(T.LBRACKET)) { this.pos = (this.pos + 1); const iname = this.eat(T.IDENT).value; this.eat(T.COLON); const iktype = this.parseTypeAnn(); this.eat(T.RBRACKET); this.eat(T.COLON); const ivtype = this.parseTypeAnn(); pairs.push(`[${iname}: ${iktype}]: ${ivtype}`); } else { const key = (this.at(T.IDENT, T.STRING) ? this.skip().value : this.eat(T.IDENT).value); let optional = false; if (this.check(T.QUESTION)) { this.pos = (this.pos + 1); optional = true; } this.eat(T.COLON); const valType = this.parseTypeAnn(); const optStr = (optional ? "?" : ""); pairs.push(`${key}${optStr}: ${valType}`); } this.maybe(T.COMMA); } this.eat(T.RBRACE); return (("{ " + pairs.join(", ")) + " }"); } if ((_c.type == T.LPAREN)) { this.pos = (this.pos + 1); const inner = this.parseTypeAnn(); this.eat(T.RPAREN); return (("(" + inner) + ")"); } if (((_c.type == T.IDENT) && (_c.value == "keyof"))) { this.pos = (this.pos + 1); return ("keyof " + this.parseSingleType()); } if ((_c.type == T.TYPEOF)) { this.pos = (this.pos + 1); return ("typeof " + this.eat(T.IDENT).value); } if ((_c.type == T.READONLY)) { this.pos = (this.pos + 1); return ("readonly " + this.parseSingleType()); } if (((_c.type == T.IDENT) && (_c.value == "infer"))) { this.pos = (this.pos + 1); return ("infer " + this.eat(T.IDENT).value); } if ((_c.type == T.FN)) { this.pos = (this.pos + 1); const paramTypes = []; if (this.check(T.LPAREN)) { this.pos = (this.pos + 1); while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { let pt = ""; if ((this.check(T.IDENT) && (this.peek(1).type == T.COLON))) { this.pos = (this.pos + 2); pt = this.parseTypeAnn(); } else { pt = this.parseTypeAnn(); } paramTypes.push(pt); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); } let retType = "Void"; if (this.check(T.ARROW)) { this.pos = (this.pos + 1); retType = this.parseTypeAnn(); } return ((("fn(" + paramTypes.join(", ")) + ") -> ") + retType); } let name = ""; if ((((_c.type == T.IDENT) || (_c.type == T.CONST)) || (_c.type == T.TYPE))) { name = this.skip().value; } else { name = this.eat(T.IDENT).value; } if (this.check(T.EXTENDS)) { this.pos = (this.pos + 1); const constraint = this.parseSingleType(); this.eat(T.QUESTION); const thenType = this.parseTypeAnn(); this.eat(T.COLON); const elseType = this.parseTypeAnn(); return `${name} extends ${constraint} ? ${thenType} : ${elseType}`; } if (this.check(T.LT)) { this.pos = (this.pos + 1); const params = [this.parseTypeAnn()]; while (this.maybe(T.COMMA)) { params.push(this.parseTypeAnn()); } this.eat(T.GT); name = (((name + "<") + params.join(", ")) + ">"); } while ((this.check(T.LBRACKET) && (this.peek(1).type == T.RBRACKET))) { this.pos = (this.pos + 2); name = (name + "[]"); } if ((this.check(T.QUESTION) && (this.peek(1).type != T.DOT))) { this.pos = (this.pos + 1); name = (name + "?"); } return name; } isTypeAnnBeforeColon() { const saved = this.pos; try { const tok0 = this.peek(); if ((((tok0.type == T.LBRACKET) || (tok0.type == T.LBRACE)) || (tok0.type == T.LPAREN))) { let depth = 0; let i = 0; while ((this.tokens[(this.pos + i)] && (this.tokens[(this.pos + i)].type != T.EOF))) { const tt = this.tokens[(this.pos + i)].type; if (((((tt == T.LBRACKET) || (tt == T.LBRACE)) || (tt == T.LPAREN)) || (tt == T.LT))) { depth = (depth + 1); } if (((((tt == T.RBRACKET) || (tt == T.RBRACE)) || (tt == T.RPAREN)) || (tt == T.GT))) { depth = (depth - 1); } i = (i + 1); if ((depth == 0)) { break; } } if ((this.tokens[(this.pos + i)] && (this.tokens[(this.pos + i)].type == T.QUESTION))) { i = (i + 1); } const nextType = this.tokens[(this.pos + i)]?.type; this.pos = saved; return (nextType == T.COLON); } if (((((tok0.type != T.IDENT) && (tok0.type != T.TYPEOF)) && (tok0.type != T.READONLY)) && (tok0.type != T.FN))) { this.pos = saved; return false; } this.parseTypeAnn(); const result = (this.check(T.NEWLINE) || this.check(T.EOF)); this.pos = saved; return result; } catch (_j) { this.pos = saved; return false; } } isColonReturnType() { const saved = this.pos; try { this.pos = (this.pos + 1); if (!this.at(T.IDENT, T.LBRACKET, T.LBRACE, T.LPAREN, T.FN, T.READONLY, T.TYPEOF)) { this.pos = saved; return false; } this.parseTypeAnn(); const result = (this.check(T.NEWLINE) || this.check(T.EOF)); this.pos = saved; return result; } catch (_j) { this.pos = saved; return false; } } parseTypeDecl() { const loc = this.eat(T.TYPE); const name = this.eat(T.IDENT).value; const typeParams = []; if (this.check(T.LT)) { this.pos = (this.pos + 1); while ((!this.check(T.GT) && !this.check(T.EOF))) { typeParams.push(this.eat(T.IDENT).value); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.GT); } this.eat(T.EQ); const variants = []; let running = true; while (running) { const vname = this.eat(T.IDENT).value; const fields = []; const fieldTypes = { }; if (this.check(T.LPAREN)) { this.eat(T.LPAREN); while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { const ftok = this.peek(); let fname = ""; if ((ftok.type == T.IDENT)) { fname = this.skip().value; } else { fname = this.skip().value; } if (this.maybe(T.COLON)) { fieldTypes[fname] = this.parseTypeAnn(); } fields.push(fname); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); } variants.push({ name: vname, fields, fieldTypes }); if (!this.check(T.PIPEB)) { running = false; } else { this.pos = (this.pos + 1); } } this.skipNewlines(); return { type: "TypeDecl", name, variants, loc }; } parseInterfaceDecl() { const loc = this.eat(T.INTERFACE); const name = this.eat(T.IDENT).value; const typeParams = []; if (this.check(T.LT)) { this.pos = (this.pos + 1); while ((!this.check(T.GT) && !this.check(T.EOF))) { typeParams.push(this.eat(T.IDENT).value); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.GT); } const superInterfaces = []; if (this.maybe(T.EXTENDS)) { superInterfaces.push(this.eat(T.IDENT).value); while (this.maybe(T.COMMA)) { superInterfaces.push(this.eat(T.IDENT).value); } } this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } this.eat(T.INDENT); const members = []; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } const mods = this.parseAccessModifiers(); if (this.check(T.FN)) { this.eat(T.FN); const mname = this.eat(T.IDENT).value; const params = this.parseParamList(); let retType = null; if (this.maybe(T.ARROW)) { retType = this.parseTypeAnn(); } this.skipNewlines(); members.push({ kind: "method", name: mname, params, retType, modifiers: mods, isAsync: false }); } else if (this.check(T.IDENT)) { const fname = this.eat(T.IDENT).value; let optional = false; if (this.check(T.QUESTION)) { this.pos = (this.pos + 1); optional = true; } this.eat(T.COLON); const ftype = this.parseTypeAnn(); this.skipNewlines(); members.push({ kind: "field", name: fname, typeAnn: ftype, optional, modifiers: mods }); } else { this.skip(); } } this.maybe(T.DEDENT); return { type: "InterfaceDecl", name, typeParams, superInterfaces, members, loc }; } parseEnumDecl() { const loc = this.eat(T.ENUM); const name = this.eat(T.IDENT).value; this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); this.eat(T.INDENT); } const members = []; let autoIndex = 0; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } const mname = this.eat(T.IDENT).value; let value = { type: "NumberLit", value: autoIndex }; if (this.maybe(T.EQ)) { const parsed = this.parseExpr(); value = parsed; if ((parsed.type == "NumberLit")) { autoIndex = parsed.value; } } else { value = { type: "NumberLit", value: autoIndex }; } autoIndex = (autoIndex + 1); this.skipNewlines(); members.push({ name: mname, value }); } this.maybe(T.DEDENT); return { type: "EnumDecl", name, members, loc }; } parseIf() { const loc = this.eat(T.IF); const cond = this.parseExpr(); const then = this.parseBlock(); const elseifs = []; let else_ = null; this.skipNewlines(); while (this.check(T.ELSE)) { this.pos = (this.pos + 1); if (this.check(T.IF)) { this.pos = (this.pos + 1); const eic = this.parseExpr(); const eib = this.parseBlock(); elseifs.push({ cond: eic, body: eib }); this.skipNewlines(); } else { else_ = this.parseBlock(); break; } } return { type: "IfStmt", cond, then, elseifs, else_, loc }; } parseFor() { const loc = this.eat(T.FOR); let isAwait = false; if (this.check(T.AWAIT)) { this.pos = (this.pos + 1); isAwait = true; } const varName = this.eat(T.IDENT).value; this.eat(T.IN); const iter = this.parseExpr(); const body = this.parseBlock(); return { type: "ForInStmt", var: varName, iter, body, isAwait, loc }; } parseWhile() { const loc = this.eat(T.WHILE); const cond = this.parseExpr(); const body = this.parseBlock(); return { type: "WhileStmt", cond, body, loc }; } parseDoWhile() { const loc = this.eat(T.DO); const body = this.parseBlock(); this.skipNewlines(); this.eat(T.WHILE); const cond = this.parseExpr(); this.skipNewlines(); return { type: "DoWhileStmt", body, cond, loc }; } parseMatch() { const loc = this.eat(T.MATCH); const subject = this.parseExpr(); this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } this.eat(T.INDENT); const arms = []; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } this.eat(T.WHEN); const pattern = this.parsePattern(); let guard = null; if (this.check(T.IF)) { this.pos = (this.pos + 1); guard = this.parseExpr(); } if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const expr = this.parseExpr(); this.skipNewlines(); arms.push({ pattern, guard, body: [{ type: "ExprStmt", expr }], inline: true }); } else if (this.check(T.COLON)) { const isInline = (this.peek(1).type != T.NEWLINE); const body = this.parseBlock(); const inlineArm = ((isInline && (body.length == 1)) && (body[0].type == "ExprStmt")); arms.push({ pattern, guard, body, inline: inlineArm }); } } this.maybe(T.DEDENT); return { type: "MatchStmt", subject, arms, loc }; } parsePattern() { if (this.check(T.WILDCARD)) { this.skip(); return { type: "WildcardPat" }; } if ((this.check(T.IDENT) && (this.peek(1).type == T.LPAREN))) { const vname = this.eat(T.IDENT).value; this.eat(T.LPAREN); const bindings = []; while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { if (this.check(T.WILDCARD)) { bindings.push("_"); this.pos = (this.pos + 1); } else { bindings.push(this.eat(T.IDENT).value); } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); return { type: "VariantPat", variant: vname, bindings }; } let left = this.parsePrimary(); while (this.check(T.DOT)) { this.pos = (this.pos + 1); const prop = this.skip().value; left = { type: "MemberExpr", obj: left, prop }; } if (this.check(T.DOTDOT)) { this.pos = (this.pos + 1); const right = this.parsePrimary(); return { type: "RangePat", start: left, end: right }; } return { type: "LiteralPat", value: left }; } parseReturn() { const loc = this.eat(T.RETURN); let value = null; if (!this.at(T.NEWLINE, T.EOF, T.DEDENT)) { value = this.parseExpr(); } this.skipNewlines(); return { type: "ReturnStmt", value, loc }; } parseTryCatch() { const loc = this.eat(T.TRY); const tryBody = this.parseBlock(); let catchParam = null; let catchBody = null; let finallyBody = null; this.skipNewlines(); if (this.check(T.CATCH)) { this.pos = (this.pos + 1); if (this.check(T.LPAREN)) { this.pos = (this.pos + 1); catchParam = this.eat(T.IDENT).value; if (this.maybe(T.COLON)) { this.parseTypeAnn(); } this.eat(T.RPAREN); } catchBody = this.parseBlock(); this.skipNewlines(); } if (this.check(T.FINALLY)) { this.pos = (this.pos + 1); finallyBody = this.parseBlock(); } return { type: "TryCatchStmt", tryBody, catchParam, catchBody, finallyBody, loc }; } parseThrow() { const loc = this.eat(T.THROW); const value = this.parseExpr(); this.skipNewlines(); return { type: "ThrowStmt", value, loc }; } parseImport() { this.eat(T.IMPORT); if (this.check(T.STAR)) { this.pos = (this.pos + 1); this.eat(T.AS); const namespaceName = this.eat(T.IDENT).value; this.eat(T.FROM); const source = this.eat(T.STRING).value; this.skipNewlines(); return { type: "ImportDecl", names: [], defaultName: null, namespaceName, source }; } if (this.check(T.IDENT)) { const defaultName = this.eat(T.IDENT).value; this.eat(T.FROM); const source = this.eat(T.STRING).value; this.skipNewlines(); return { type: "ImportDecl", names: [], defaultName, source }; } const names = []; if (this.maybe(T.LBRACE)) { while ((!this.check(T.RBRACE) && !this.check(T.EOF))) { const name = this.eat(T.IDENT).value; let alias = name; if (this.check(T.AS)) { this.pos = (this.pos + 1); alias = this.eat(T.IDENT).value; } names.push({ name, alias }); if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACE); } this.eat(T.FROM); const source = this.eat(T.STRING).value; this.skipNewlines(); return { type: "ImportDecl", names, defaultName: null, source }; } parseExport() { this.eat(T.EXPORT); if (this.check(T.DEFAULT)) { this.pos = (this.pos + 1); if (this.check(T.ASYNC)) { this.pos = (this.pos + 1); const decl = this.parseFnDecl(true); return { type: "ExportDecl", isDefault: true, decl }; } if (this.check(T.FN)) { const decl = this.parseFnDecl(false); return { type: "ExportDecl", isDefault: true, decl }; } const value = this.parseExpr(); this.skipNewlines(); return { type: "ExportDecl", isDefault: true, decl: value }; } if (this.check(T.ASYNC)) { this.pos = (this.pos + 1); if (!this.check(T.FN)) { this.err("Expected fn after async"); } const decl = this.parseFnDecl(true); return { type: "ExportDecl", isDefault: false, decl }; } const decl = this.parseOneStmt(); return { type: "ExportDecl", isDefault: false, decl }; } parseFnDecl(_l = false) { const loc = this.eat(T.FN); let name = null; if ((((((((this.check(T.IDENT) || this.check(T.NEW)) || this.check(T.FROM)) || this.check(T.AS)) || this.check(T.DEFAULT)) || this.check(T.IS)) || this.check(T.IN)) || this.check(T.TYPE))) { name = this.skip().value; } const params = this.parseParamList(); let retType = null; if (this.check(T.ARROW)) { this.pos = (this.pos + 1); if (this.isTypeAnnBeforeColon()) { retType = this.parseTypeAnn(); const body = this.parseBlock(); return { type: "FnDecl", name, params, retType, body, inline: false, async: _l, loc }; } const body = this.parseExpr(); this.skipNewlines(); return { type: "FnDecl", name, params, retType: null, body, inline: true, async: _l, loc }; } if (this.check(T.COLON)) { if (this.isColonReturnType()) { this.pos = (this.pos + 1); retType = this.parseTypeAnn(); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } if (this.check(T.INDENT)) { this.pos = (this.pos + 1); const body = this.parseStmtList(); this.maybe(T.DEDENT); return { type: "FnDecl", name, params, retType, body, inline: false, async: _l, loc }; } const stmt = this.parseOneStmt(); return { type: "FnDecl", name, params, retType, body: [stmt], inline: false, async: _l, loc }; } const body = this.parseBlock(); return { type: "FnDecl", name, params, retType: null, body, inline: false, async: _l, loc }; } this.err("Expected -> or : after function signature"); } parseAsyncFn() { this.eat(T.ASYNC); if (!this.check(T.FN)) { this.err("Expected fn after async"); } return this.parseFnDecl(true); } parseDeclareDecl() { const loc = this.eat(T.DECLARE); const _c = this.peek(); if (((_c.type == T.FN) || (_c.type == T.ASYNC))) { const _l = (_c.type == T.ASYNC); this.skip(); if (_l) { this.eat(T.FN); } const name = (this.check(T.IDENT) ? this.skip().value : null); const params = this.parseParamList(); let retType = null; if (this.check(T.ARROW)) { this.pos = (this.pos + 1); retType = this.parseTypeAnn(); } else if (this.check(T.COLON)) { this.pos = (this.pos + 1); retType = this.parseTypeAnn(); } this.skipNewlines(); const decl = { type: "FnDecl", name, params, retType, body: [], inline: false, async: _l, loc: _c }; return { type: "DeclareDecl", decl, loc }; } if (((_c.type == T.VAL) || (_c.type == T.VAR))) { const kind = ((_c.type == T.VAR) ? "var" : "val"); this.skip(); const name = this.eat(T.IDENT).value; let typeAnn = null; if (this.maybe(T.COLON)) { typeAnn = this.parseTypeAnn(); } this.skipNewlines(); const decl = { type: "VarDecl", kind, name, typeAnn, init: null, loc: _c }; return { type: "DeclareDecl", decl, loc }; } if ((_c.type == T.CLASS)) { this.skip(); const name = this.eat(T.IDENT).value; this.skipNewlines(); const decl = { type: "ClassDecl", name, fields: [], methods: [], loc: _c }; return { type: "DeclareDecl", decl, loc }; } this.err("Expected fn, async fn, val, var, or class after declare"); } parseParamList() { this.eat(T.LPAREN); const params = []; while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { let rest = false; if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); rest = true; } const name = this.eat(T.IDENT).value; let optional = false; let typeAnn = null; if ((!rest && this.check(T.QUESTION))) { this.pos = (this.pos + 1); optional = true; } if ((!rest && this.maybe(T.COLON))) { typeAnn = this.parseTypeAnn(); } let defaultVal = null; if ((!rest && this.maybe(T.EQ))) { defaultVal = this.parseExpr(); } params.push({ name, typeAnn, optional, defaultVal, rest }); if (rest) { break; } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); return params; } parseAccessModifiers() { const mods = new Set([]); let running = true; while (running) { const t = this.peek().type; if ((((((((t == T.PRIVATE) || (t == T.PUBLIC)) || (t == T.PROTECTED)) || (t == T.READONLY)) || (t == T.STATIC)) || (t == T.ABSTRACT)) || (t == T.OVERRIDE))) { mods.add(this.skip().value); } else { running = false; } } return mods; } parseClassDecl() { const loc = this.eat(T.CLASS); const name = this.eat(T.IDENT).value; let superClass = null; const interfaces = []; const typeParams = []; if (this.check(T.LT)) { this.pos = (this.pos + 1); typeParams.push(this.eat(T.IDENT).value); while (this.maybe(T.COMMA)) { typeParams.push(this.eat(T.IDENT).value); } this.eat(T.GT); } if (this.maybe(T.EXTENDS)) { superClass = this.eat(T.IDENT).value; } if (this.maybe(T.IMPLEMENTS)) { interfaces.push(this.eat(T.IDENT).value); while (this.maybe(T.COMMA)) { interfaces.push(this.eat(T.IDENT).value); } } this.eat(T.COLON); if (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); this.eat(T.INDENT); } const fields = []; const methods = []; while ((!this.check(T.DEDENT) && !this.check(T.EOF))) { this.skipNewlines(); if ((this.check(T.DEDENT) || this.check(T.EOF))) { break; } const mods = this.parseAccessModifiers(); if (this.check(T.FN)) { const m = this.parseFnDecl(false); m.modifiers = mods; methods.push(m); } else if (this.check(T.ASYNC)) { const m = this.parseAsyncFn(); m.modifiers = mods; methods.push(m); } else if ((this.check(T.STATIC) && (this.peek(1).type == T.FN))) { this.skip(); const m = this.parseFnDecl(false); m.modifiers = mods; m.modifiers.add("static"); methods.push(m); } else if (this.check(T.IDENT)) { const fname = this.eat(T.IDENT).value; let optional = false; if (this.check(T.QUESTION)) { this.pos = (this.pos + 1); optional = true; } this.eat(T.COLON); const ftype = this.parseTypeAnn(); this.skipNewlines(); fields.push({ name: fname, typeAnn: ftype, optional, modifiers: mods }); } else { this.skip(); } } this.maybe(T.DEDENT); return { type: "ClassDecl", name, typeParams, superClass, interfaces, fields, methods, loc }; } parseExprStmt() { const expr = this.parseExpr(); this.skipNewlines(); return { type: "ExprStmt", expr }; } parseExpr() { return this.parsePipe(); } parsePipe() { let left = this.parseAssign(); let running = true; while (running) { if (this.check(T.PIPE)) { this.pos = (this.pos + 1); const right = this.parseAssign(); left = { type: "PipeExpr", left, right }; } else if (this.check(T.NEWLINE)) { let i = 1; while ((this.peek(i).type == T.NEWLINE)) { i = (i + 1); } if ((this.peek(i).type == T.PIPE)) { while (this.check(T.NEWLINE)) { this.pos = (this.pos + 1); } this.pos = (this.pos + 1); const right = this.parseAssign(); left = { type: "PipeExpr", left, right }; } else { running = false; } } else { running = false; } } return left; } parseAssign() { const left = this.parseTernary(); const t = this.peek().type; let op = ""; if ((t == T.EQ)) { op = "="; } else if ((t == T.PLUSEQ)) { op = "+="; } else if ((t == T.MINUSEQ)) { op = "-="; } else if ((t == T.STAREQ)) { op = "*="; } else if ((t == T.SLASHEQ)) { op = "/="; } else if ((t == T.PERCENTEQ)) { op = "%="; } if (op) { this.pos = (this.pos + 1); return { type: "AssignExpr", target: left, op, value: this.parseAssign() }; } return left; } parseTernary() { const cond = this.parseNullish(); if (this.maybe(T.QUESTION)) { const then = this.parseNullish(); this.eat(T.COLON); const else_ = this.parseTernary(); return { type: "TernaryExpr", cond, then, else_ }; } return cond; } parseNullish() { let l = this.parseOr(); while (this.check(T.NULLISH)) { this.pos = (this.pos + 1); const r = this.parseOr(); l = { type: "BinaryExpr", op: "??", left: l, right: r }; } return l; } parseOr() { let l = this.parseAnd(); while ((this.check(T.OR) || this.check(T.OROR))) { this.pos = (this.pos + 1); const r = this.parseAnd(); l = { type: "BinaryExpr", op: "||", left: l, right: r }; } return l; } parseAnd() { let l = this.parseBitOr(); while ((this.check(T.AND) || this.check(T.ANDAND))) { this.pos = (this.pos + 1); const r = this.parseBitOr(); l = { type: "BinaryExpr", op: "&&", left: l, right: r }; } return l; } parseBitOr() { let l = this.parseBitXor(); while (this.check(T.PIPEB)) { this.pos = (this.pos + 1); const r = this.parseBitXor(); l = { type: "BinaryExpr", op: "|", left: l, right: r }; } return l; } parseBitXor() { let l = this.parseBitAnd(); while (this.check(T.CARET)) { this.pos = (this.pos + 1); const r = this.parseBitAnd(); l = { type: "BinaryExpr", op: "^", left: l, right: r }; } return l; } parseBitAnd() { let l = this.parseEq(); while (this.check(T.AMPERSAND)) { this.pos = (this.pos + 1); const r = this.parseEq(); l = { type: "BinaryExpr", op: "&", left: l, right: r }; } return l; } parseEq() { let l = this.parseRel(); while (this.at(T.EQEQ, T.NEQ, T.EQEQEQ, T.NEQEQ)) { const op = this.skip().value; const r = this.parseRel(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parseRel() { let l = this.parseShift(); while ((this.at(T.LT, T.LTE, T.GT, T.GTE) || this.check(T.IN))) { const op = this.skip().value; const r = this.parseShift(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parseShift() { let l = this.parseRange(); while (this.at(T.LSHIFT, T.RSHIFT)) { const op = this.skip().value; const r = this.parseRange(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parseRange() { const l = this.parseAdd(); if (this.check(T.DOTDOT)) { this.pos = (this.pos + 1); const r = this.parseAdd(); return { type: "RangeExpr", start: l, end: r }; } return l; } parseAdd() { let l = this.parseMul(); while (this.at(T.PLUS, T.MINUS)) { const op = this.skip().value; const r = this.parseMul(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parseMul() { let l = this.parsePow(); while (this.at(T.STAR, T.SLASH, T.PERCENT)) { const op = this.skip().value; const r = this.parsePow(); l = { type: "BinaryExpr", op, left: l, right: r }; } return l; } parsePow() { const l = this.parseUnary(); if (this.check(T.STARSTAR)) { this.pos = (this.pos + 1); return { type: "BinaryExpr", op: "**", left: l, right: this.parsePow() }; } return l; } parseUnary() { if (this.check(T.MINUS)) { this.pos = (this.pos + 1); return { type: "UnaryExpr", op: "-", operand: this.parseUnary() }; } if (this.check(T.NOT)) { this.pos = (this.pos + 1); return { type: "UnaryExpr", op: "!", operand: this.parseUnary() }; } if (this.check(T.BANG)) { this.pos = (this.pos + 1); return { type: "UnaryExpr", op: "!", operand: this.parseUnary() }; } if (this.check(T.TILDE)) { this.pos = (this.pos + 1); return { type: "UnaryExpr", op: "~", operand: this.parseUnary() }; } if (this.check(T.PLUSPLUS)) { this.pos = (this.pos + 1); return { type: "UpdateExpr", op: "++", prefix: true, operand: this.parseUnary() }; } if (this.check(T.MINUSMINUS)) { this.pos = (this.pos + 1); return { type: "UpdateExpr", op: "--", prefix: true, operand: this.parseUnary() }; } if (this.check(T.AWAIT)) { this.pos = (this.pos + 1); return { type: "AwaitExpr", operand: this.parseUnary() }; } if (this.check(T.TYPEOF)) { this.pos = (this.pos + 1); return { type: "TypeofExpr", operand: this.parseUnary() }; } return this.parseLambdaOrPostfix(); } parseLambdaOrPostfix() { const saved = this.pos; if ((this.check(T.IDENT) || this.check(T.WILDCARD))) { const name = (this.skip().value ?? "_"); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); return { type: "LambdaExpr", params: [{ name }], body }; } this.pos = saved; } return this.parsePostfix(); } parsePostfix() { let expr = this.parsePrimary(); let running = true; while (running) { if (this.check(T.PLUSPLUS)) { this.pos = (this.pos + 1); expr = { type: "UpdateExpr", op: "++", prefix: false, operand: expr }; } else if (this.check(T.MINUSMINUS)) { this.pos = (this.pos + 1); expr = { type: "UpdateExpr", op: "--", prefix: false, operand: expr }; } else if (this.check(T.DOT)) { this.pos = (this.pos + 1); const propTok = this.peek(); const prop = this.skip().value; expr = { type: "MemberExpr", obj: expr, prop }; } else if (this.check(T.QUESTIONDOT)) { this.pos = (this.pos + 1); if (this.check(T.LPAREN)) { const args = this.parseArgList(); expr = { type: "OptCallExpr", callee: expr, args }; } else if (this.check(T.LBRACKET)) { this.pos = (this.pos + 1); const idx = this.parseExpr(); this.eat(T.RBRACKET); expr = { type: "OptIndexExpr", obj: expr, idx }; } else { const prop = this.skip().value; expr = { type: "OptMemberExpr", obj: expr, prop }; } } else if (this.check(T.INSTANCEOF)) { this.pos = (this.pos + 1); const right = this.eat(T.IDENT).value; expr = { type: "BinaryExpr", op: "instanceof", left: expr, right: { type: "Identifier", name: right } }; } else if (this.check(T.AS)) { this.pos = (this.pos + 1); if (this.check(T.CONST)) { this.pos = (this.pos + 1); expr = { type: "AsConstExpr", expr }; } else { const castType = this.parseTypeAnn(); expr = { type: "CastExpr", expr, castType }; } } else if (this.check(T.SATISFIES)) { this.pos = (this.pos + 1); const satType = this.parseTypeAnn(); expr = { type: "SatisfiesExpr", expr, satType }; } else if (this.check(T.IS)) { this.pos = (this.pos + 1); const isType = this.parseTypeAnn(); expr = { type: "IsExpr", expr, isType }; } else if (this.check(T.BANG)) { this.pos = (this.pos + 1); expr = { type: "NonNullExpr", expr }; } else if (this.check(T.LBRACKET)) { this.pos = (this.pos + 1); const idx = this.parseExpr(); this.eat(T.RBRACKET); expr = { type: "IndexExpr", obj: expr, idx }; } else if (this.check(T.LPAREN)) { const args = this.parseArgList(); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); const lambdaParams = args.map((_f) => { name: ((_f.type == "Identifier") ? _f.name : "_") }); return { type: "LambdaExpr", params: lambdaParams, body }; } expr = { type: "CallExpr", callee: expr, args }; } else { running = false; } } return expr; } parseArgList() { this.eat(T.LPAREN); const args = []; while ((!this.check(T.RPAREN) && !this.check(T.EOF))) { if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); args.push({ type: "SpreadExpr", expr: this.parseExpr() }); } else { args.push(this.parseExpr()); } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RPAREN); return args; } parsePrimary() { const _c = this.peek(); if ((_c.type == T.NUMBER)) { this.pos = (this.pos + 1); return { type: "NumberLit", value: _c.value, loc: _c }; } if ((_c.type == T.BOOL)) { this.pos = (this.pos + 1); return { type: "BoolLit", value: _c.value, loc: _c }; } if ((_c.type == T.NULL)) { this.pos = (this.pos + 1); return { type: "NullLit", loc: _c }; } if ((_c.type == T.SELF)) { this.pos = (this.pos + 1); return { type: "SelfExpr", loc: _c }; } if ((_c.type == T.WILDCARD)) { this.pos = (this.pos + 1); return { type: "Identifier", name: "_", loc: _c }; } if ((_c.type == T.IDENT)) { this.pos = (this.pos + 1); return { type: "Identifier", name: _c.value, loc: _c }; } if ((_c.type == T.STRING)) { this.pos = (this.pos + 1); if ((_c.value && _c.value.template)) { return { type: "TemplateLit", parts: _c.value.parts, loc: _c }; } return { type: "StringLit", value: _c.value, loc: _c }; } if ((_c.type == T.REGEX)) { this.pos = (this.pos + 1); return { type: "RegexLit", value: _c.value, loc: _c }; } if ((_c.type == T.NEW)) { this.pos = (this.pos + 1); const callee = this.eat(T.IDENT).value; const args = this.parseArgList(); return { type: "NewExpr", callee, args }; } if ((_c.type == T.MATCH)) { return this.parseMatch(); } if ((_c.type == T.FN)) { this.pos = (this.pos + 1); const params = this.parseParamList(); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); if (this.isTypeAnnBeforeColon()) { const retType = this.parseTypeAnn(); const body = this.parseBlock(); return { type: "FnDecl", name: null, params, retType, body, inline: false, async: false, loc: _c }; } const body = this.parseExpr(); return { type: "FnDecl", name: null, params, retType: null, body, inline: true, async: false, loc: _c }; } if (this.check(T.COLON)) { const body = this.parseBlock(); return { type: "FnDecl", name: null, params, retType: null, body, inline: false, async: false, loc: _c }; } this.err("Expected -> or : after anonymous fn"); } if ((_c.type == T.LPAREN)) { this.pos = (this.pos + 1); if (this.check(T.RPAREN)) { this.pos = (this.pos + 1); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); return { type: "LambdaExpr", params: [], body }; } return { type: "NullLit" }; } const first = this.parseExpr(); if (this.check(T.RPAREN)) { this.pos = (this.pos + 1); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); const pname = ((first.type == "Identifier") ? first.name : "_"); return { type: "LambdaExpr", params: [{ name: pname }], body }; } return first; } const items = [first]; while (this.maybe(T.COMMA)) { items.push(this.parseExpr()); } this.eat(T.RPAREN); if (this.check(T.ARROW)) { this.pos = (this.pos + 1); const body = this.parseExpr(); const lambdaParams = items.map((_m) => { name: ((_m.type == "Identifier") ? _m.name : "_") }); return { type: "LambdaExpr", params: lambdaParams, body }; } return items[0]; } if ((_c.type == T.LBRACKET)) { this.pos = (this.pos + 1); const items = []; while ((!this.check(T.RBRACKET) && !this.check(T.EOF))) { if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); items.push({ type: "SpreadExpr", expr: this.parseExpr() }); } else { items.push(this.parseExpr()); } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACKET); return { type: "ArrayExpr", items }; } if ((_c.type == T.LBRACE)) { this.pos = (this.pos + 1); const pairs = []; while ((!this.check(T.RBRACE) && !this.check(T.EOF))) { if (this.check(T.DOTDOTDOT)) { this.pos = (this.pos + 1); pairs.push({ spread: true, value: this.parseExpr() }); if (!this.maybe(T.COMMA)) { break; } continue; } const key = (this.check(T.STRING) ? this.eat(T.STRING).value : (this.check(T.IDENT) ? this.skip().value : this.skip().value)); if (!this.check(T.COLON)) { pairs.push({ key, value: { type: "Identifier", name: key } }); } else { this.eat(T.COLON); const v = this.parseExpr(); pairs.push({ key, value: v }); } if (!this.maybe(T.COMMA)) { break; } } this.eat(T.RBRACE); return { type: "ObjectExpr", pairs }; } this.err(`Unexpected token: ${_c.type} (${JSON.stringify(_c.value)})`); } } module.exports.Parser = Parser; function makeParser(_n) { return new Parser(_n, 0); } module.exports.makeParser = makeParser;