@xnoxs/flux-lang 3.1.1

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/README.md +1089 -0
  3. package/bin/flux.js +1397 -0
  4. package/dist/flux.cjs.js +6664 -0
  5. package/dist/flux.esm.js +6674 -0
  6. package/dist/flux.min.js +263 -0
  7. package/index.d.ts +202 -0
  8. package/index.js +26 -0
  9. package/package.json +77 -0
  10. package/scripts/build.js +76 -0
  11. package/src/bundler.js +216 -0
  12. package/src/checker.js +322 -0
  13. package/src/codegen.js +785 -0
  14. package/src/css-preprocessor.js +399 -0
  15. package/src/formatter.js +140 -0
  16. package/src/jsx.js +480 -0
  17. package/src/lexer.js +518 -0
  18. package/src/linter.js +758 -0
  19. package/src/mangler.js +280 -0
  20. package/src/parser.js +1671 -0
  21. package/src/self/bundler.flux +167 -0
  22. package/src/self/bundler.js +187 -0
  23. package/src/self/checker.flux +249 -0
  24. package/src/self/checker.js +338 -0
  25. package/src/self/codegen.flux +555 -0
  26. package/src/self/codegen.js +784 -0
  27. package/src/self/css-preprocessor.flux +373 -0
  28. package/src/self/css-preprocessor.js +387 -0
  29. package/src/self/formatter.flux +93 -0
  30. package/src/self/formatter.js +114 -0
  31. package/src/self/jsx.flux +430 -0
  32. package/src/self/jsx.js +396 -0
  33. package/src/self/lexer.flux +529 -0
  34. package/src/self/lexer.js +709 -0
  35. package/src/self/lexer.stage2.js +700 -0
  36. package/src/self/linter.flux +515 -0
  37. package/src/self/linter.js +804 -0
  38. package/src/self/mangler.flux +253 -0
  39. package/src/self/mangler.js +348 -0
  40. package/src/self/parser.flux +1146 -0
  41. package/src/self/parser.js +1571 -0
  42. package/src/self/sourcemap.flux +66 -0
  43. package/src/self/sourcemap.js +72 -0
  44. package/src/self/stdlib.flux +356 -0
  45. package/src/self/stdlib.js +396 -0
  46. package/src/self/test-runner.flux +201 -0
  47. package/src/self/test-runner.js +132 -0
  48. package/src/self/transpiler.flux +123 -0
  49. package/src/self/transpiler.js +83 -0
  50. package/src/self/type-checker.flux +821 -0
  51. package/src/self/type-checker.js +1106 -0
  52. package/src/sourcemap.js +82 -0
  53. package/src/stdlib.js +436 -0
  54. package/src/test-runner.js +239 -0
  55. package/src/transpiler.js +172 -0
  56. package/src/type-checker.js +1206 -0
@@ -0,0 +1,1571 @@
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.1.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;