@jalvin/compiler 2.0.32 → 2.0.33

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 (53) hide show
  1. package/dist/codegen/index.d.ts +179 -0
  2. package/dist/codegen/index.d.ts.map +1 -0
  3. package/dist/codegen/index.js +2207 -0
  4. package/dist/codegen/index.js.map +1 -0
  5. package/dist/codegen_freestanding_c.d.ts +9 -0
  6. package/dist/codegen_freestanding_c.d.ts.map +1 -0
  7. package/dist/codegen_freestanding_c.js +321 -0
  8. package/dist/codegen_freestanding_c.js.map +1 -0
  9. package/dist/diagnostics.d.ts +2 -0
  10. package/dist/diagnostics.d.ts.map +1 -1
  11. package/dist/diagnostics.js +3 -1
  12. package/dist/diagnostics.js.map +1 -1
  13. package/dist/lexer/chars.d.ts +5 -0
  14. package/dist/lexer/chars.d.ts.map +1 -0
  15. package/dist/lexer/chars.js +20 -0
  16. package/dist/lexer/chars.js.map +1 -0
  17. package/dist/lexer/index.d.ts +37 -0
  18. package/dist/lexer/index.d.ts.map +1 -0
  19. package/dist/lexer/index.js +630 -0
  20. package/dist/lexer/index.js.map +1 -0
  21. package/dist/lexer/tokens.d.ts +135 -0
  22. package/dist/lexer/tokens.d.ts.map +1 -0
  23. package/dist/lexer/tokens.js +89 -0
  24. package/dist/lexer/tokens.js.map +1 -0
  25. package/dist/parser/constants.d.ts +3 -0
  26. package/dist/parser/constants.d.ts.map +1 -0
  27. package/dist/parser/constants.js +7 -0
  28. package/dist/parser/constants.js.map +1 -0
  29. package/dist/parser/helpers.d.ts +2 -0
  30. package/dist/parser/helpers.d.ts.map +1 -0
  31. package/dist/parser/helpers.js +9 -0
  32. package/dist/parser/helpers.js.map +1 -0
  33. package/dist/parser/index.d.ts +118 -0
  34. package/dist/parser/index.d.ts.map +1 -0
  35. package/dist/parser/index.js +2387 -0
  36. package/dist/parser/index.js.map +1 -0
  37. package/dist/typechecker/globals.d.ts +2 -0
  38. package/dist/typechecker/globals.d.ts.map +1 -0
  39. package/dist/typechecker/globals.js +15 -0
  40. package/dist/typechecker/globals.js.map +1 -0
  41. package/dist/typechecker/index.d.ts +146 -0
  42. package/dist/typechecker/index.d.ts.map +1 -0
  43. package/dist/typechecker/index.js +2288 -0
  44. package/dist/typechecker/index.js.map +1 -0
  45. package/dist/typechecker/types.d.ts +66 -0
  46. package/dist/typechecker/types.d.ts.map +1 -0
  47. package/dist/typechecker/types.js +39 -0
  48. package/dist/typechecker/types.js.map +1 -0
  49. package/dist/typechecker.d.ts +12 -0
  50. package/dist/typechecker.d.ts.map +1 -1
  51. package/dist/typechecker.js +243 -149
  52. package/dist/typechecker.js.map +1 -1
  53. package/package.json +10 -15
@@ -0,0 +1,2387 @@
1
+ "use strict";
2
+ // ─────────────────────────────────────────────────────────────────────────────
3
+ // Jalvin Parser — recursive-descent, produces a fully-typed AST
4
+ //
5
+ // Grammar overview (informal EBNF):
6
+ // program = packageDecl? importDecl* topLevelDecl*
7
+ // topLevelDecl = funDecl | componentDecl | classDecl | dataClassDecl
8
+ // | sealedClassDecl | interfaceDecl | objectDecl
9
+ // | typeAliasDecl | propertyDecl | extensionFunDecl
10
+ // ...
11
+ // ─────────────────────────────────────────────────────────────────────────────
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.Parser = void 0;
47
+ exports.parse = parse;
48
+ const AST = __importStar(require("../ast.js"));
49
+ const lexer_js_1 = require("../lexer.js");
50
+ const diagnostics_js_1 = require("../diagnostics.js");
51
+ const constants_js_1 = require("./constants.js");
52
+ const helpers_js_1 = require("./helpers.js");
53
+ // ---------------------------------------------------------------------------
54
+ // Parser class
55
+ // ---------------------------------------------------------------------------
56
+ class Parser {
57
+ pos = 0;
58
+ tokens;
59
+ diag;
60
+ file;
61
+ source;
62
+ depth = 0;
63
+ constructor(tokens, file, diag, source = "") {
64
+ this.tokens = tokens;
65
+ this.diag = diag;
66
+ this.file = file;
67
+ this.source = source;
68
+ }
69
+ // ── Entry point ────────────────────────────────────────────────────────────
70
+ parseProgram() {
71
+ const start = this.current().span;
72
+ let packageDecl = null;
73
+ if (this.check("KwPackage" /* TokenKind.KwPackage */)) {
74
+ packageDecl = this.parsePackageDecl();
75
+ }
76
+ const imports = [];
77
+ while (this.check("KwImport" /* TokenKind.KwImport */)) {
78
+ imports.push(this.parseImportDecl());
79
+ }
80
+ const declarations = [];
81
+ while (!this.check("EOF" /* TokenKind.EOF */)) {
82
+ // skip stray semicolons
83
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
84
+ this.advance();
85
+ if (this.check("EOF" /* TokenKind.EOF */))
86
+ break;
87
+ const decl = this.parseTopLevelDecl();
88
+ if (decl)
89
+ declarations.push(decl);
90
+ }
91
+ const end = this.current().span;
92
+ return {
93
+ kind: "Program",
94
+ span: AST.spanFrom(start, end),
95
+ packageDecl,
96
+ imports,
97
+ declarations,
98
+ };
99
+ }
100
+ // ── Package & imports ──────────────────────────────────────────────────────
101
+ parsePackageDecl() {
102
+ const start = this.expect("KwPackage" /* TokenKind.KwPackage */).span;
103
+ const path = this.parseDottedName();
104
+ this.eatSemicolon();
105
+ return { kind: "PackageDecl", span: start, path };
106
+ }
107
+ parseImportDecl() {
108
+ const start = this.expect("KwImport" /* TokenKind.KwImport */).span;
109
+ const path = [];
110
+ if (this.check("At" /* TokenKind.At */)) {
111
+ this.advance();
112
+ path.push("@" + this.expectIdentOrKeyword());
113
+ }
114
+ else {
115
+ path.push(this.expectIdentOrKeyword());
116
+ }
117
+ while (this.check("Dot" /* TokenKind.Dot */) || this.check("Slash" /* TokenKind.Slash */)) {
118
+ this.advance();
119
+ if (this.check("Star" /* TokenKind.Star */)) {
120
+ this.advance();
121
+ this.eatSemicolon();
122
+ return { kind: "ImportDecl", span: start, path, star: true, alias: null };
123
+ }
124
+ path.push(this.expectIdentOrKeyword());
125
+ }
126
+ let alias = null;
127
+ if (this.check("KwAs" /* TokenKind.KwAs */)) {
128
+ this.advance();
129
+ alias = this.expectIdentifier();
130
+ }
131
+ this.eatSemicolon();
132
+ return { kind: "ImportDecl", span: start, path, star: false, alias };
133
+ }
134
+ // ── Top-level declarations ─────────────────────────────────────────────────
135
+ parseTopLevelDecl() {
136
+ // Collect modifiers and annotations
137
+ const mods = this.parseModifiers();
138
+ if (this.check("KwFun" /* TokenKind.KwFun */)) {
139
+ return this.parseFunOrExtension(mods);
140
+ }
141
+ if (this.check("KwComponent" /* TokenKind.KwComponent */)) {
142
+ return this.parseComponentDecl(mods);
143
+ }
144
+ if (this.check("KwData" /* TokenKind.KwData */) && this.checkNext("KwClass" /* TokenKind.KwClass */)) {
145
+ return this.parseDataClassDecl(mods);
146
+ }
147
+ if (this.check("KwSealed" /* TokenKind.KwSealed */) && this.checkNext("KwClass" /* TokenKind.KwClass */)) {
148
+ return this.parseSealedClassDecl(mods);
149
+ }
150
+ if (this.check("KwEnum" /* TokenKind.KwEnum */) && this.checkNext("KwClass" /* TokenKind.KwClass */)) {
151
+ return this.parseEnumClassDecl(mods);
152
+ }
153
+ if (this.check("KwClass" /* TokenKind.KwClass */)) {
154
+ return this.parseClassDecl(mods);
155
+ }
156
+ if (this.check("KwInterface" /* TokenKind.KwInterface */)) {
157
+ return this.parseInterfaceDecl(mods);
158
+ }
159
+ if (this.check("KwObject" /* TokenKind.KwObject */)) {
160
+ return this.parseObjectDecl(mods);
161
+ }
162
+ if (this.check("KwTypealias" /* TokenKind.KwTypealias */)) {
163
+ return this.parseTypeAliasDecl(mods);
164
+ }
165
+ if (this.check("KwVal" /* TokenKind.KwVal */) || this.check("KwVar" /* TokenKind.KwVar */)) {
166
+ if (this.checkNext("LParen" /* TokenKind.LParen */)) {
167
+ return this.parseDestructuringDecl(mods);
168
+ }
169
+ return this.parsePropertyDeclTopLevel(mods);
170
+ }
171
+ const tok = this.current();
172
+ this.diag.error(tok.span, diagnostics_js_1.E_UNEXPECTED_TOKEN, `Unexpected token '${tok.text}' at top level`);
173
+ this.advance();
174
+ return null;
175
+ }
176
+ // ── Modifiers ──────────────────────────────────────────────────────────────
177
+ parseModifiers() {
178
+ let visibility = "public";
179
+ const modifiers = [];
180
+ const annotations = [];
181
+ const seen = new Set();
182
+ // Collect leading annotations: @Name or @Name("...") or @Name(key=val,...)
183
+ while (this.check("At" /* TokenKind.At */)) {
184
+ const atSpan = this.current().span;
185
+ this.advance(); // consume @
186
+ const name = this.expectIdentOrKeyword();
187
+ let args = null;
188
+ if (this.check("LParen" /* TokenKind.LParen */)) {
189
+ this.advance(); // consume (
190
+ // Collect everything until matching )
191
+ const parts = [];
192
+ let depth = 1;
193
+ while (!this.check("EOF" /* TokenKind.EOF */) && depth > 0) {
194
+ const tok = this.current();
195
+ if (tok.kind === "LParen" /* TokenKind.LParen */) {
196
+ depth++;
197
+ parts.push(tok.text);
198
+ this.advance();
199
+ }
200
+ else if (tok.kind === "RParen" /* TokenKind.RParen */) {
201
+ depth--;
202
+ if (depth > 0)
203
+ parts.push(tok.text);
204
+ this.advance();
205
+ }
206
+ else {
207
+ parts.push(tok.text);
208
+ this.advance();
209
+ }
210
+ }
211
+ args = parts.join("");
212
+ }
213
+ annotations.push({ span: AST.spanFrom(atSpan, this.prevSpan()), name, args });
214
+ this.eatSemicolon();
215
+ }
216
+ const addMod = (m) => {
217
+ if (seen.has(m)) {
218
+ this.diag.error(this.current().span, diagnostics_js_1.E_DUPLICATE_MODIFIER, `Duplicate modifier '${m}'`);
219
+ return;
220
+ }
221
+ seen.add(m);
222
+ if (m === "public" || m === "private" || m === "protected" || m === "internal") {
223
+ if (seen.has("public") || seen.has("private") || seen.has("protected") || seen.has("internal")) {
224
+ if (seen.size > 1) {
225
+ this.diag.error(this.current().span, diagnostics_js_1.E_INCOMPATIBLE_MODIFIERS, "Multiple visibility modifiers");
226
+ }
227
+ }
228
+ visibility = m;
229
+ }
230
+ else {
231
+ modifiers.push(m);
232
+ }
233
+ };
234
+ loop: while (true) {
235
+ switch (this.current().kind) {
236
+ case "KwPublic" /* TokenKind.KwPublic */:
237
+ addMod("public");
238
+ this.advance();
239
+ break;
240
+ case "KwPrivate" /* TokenKind.KwPrivate */:
241
+ addMod("private");
242
+ this.advance();
243
+ break;
244
+ case "KwProtected" /* TokenKind.KwProtected */:
245
+ addMod("protected");
246
+ this.advance();
247
+ break;
248
+ case "KwInternal" /* TokenKind.KwInternal */:
249
+ addMod("internal");
250
+ this.advance();
251
+ break;
252
+ case "KwOpen" /* TokenKind.KwOpen */:
253
+ addMod("open");
254
+ this.advance();
255
+ break;
256
+ case "KwAbstract" /* TokenKind.KwAbstract */:
257
+ addMod("abstract");
258
+ this.advance();
259
+ break;
260
+ case "KwOverride" /* TokenKind.KwOverride */:
261
+ addMod("override");
262
+ this.advance();
263
+ break;
264
+ case "KwInline" /* TokenKind.KwInline */:
265
+ addMod("inline");
266
+ this.advance();
267
+ break;
268
+ case "KwOperator" /* TokenKind.KwOperator */:
269
+ addMod("operator");
270
+ this.advance();
271
+ break;
272
+ case "KwInfix" /* TokenKind.KwInfix */:
273
+ addMod("infix");
274
+ this.advance();
275
+ break;
276
+ case "KwExternal" /* TokenKind.KwExternal */:
277
+ addMod("external");
278
+ this.advance();
279
+ break;
280
+ case "KwSuspend" /* TokenKind.KwSuspend */:
281
+ addMod("suspend");
282
+ this.advance();
283
+ break;
284
+ case "KwTailrec" /* TokenKind.KwTailrec */:
285
+ addMod("tailrec");
286
+ this.advance();
287
+ break;
288
+ case "KwReified" /* TokenKind.KwReified */:
289
+ addMod("reified");
290
+ this.advance();
291
+ break;
292
+ case "KwConst" /* TokenKind.KwConst */:
293
+ addMod("const");
294
+ this.advance();
295
+ break;
296
+ case "KwLateinit" /* TokenKind.KwLateinit */:
297
+ addMod("lateinit");
298
+ this.advance();
299
+ break;
300
+ case "KwFinal" /* TokenKind.KwFinal */:
301
+ addMod("final");
302
+ this.advance();
303
+ break;
304
+ default: break loop;
305
+ }
306
+ }
307
+ return { visibility, modifiers, annotations };
308
+ }
309
+ // ── function / extension function ─────────────────────────────────────────
310
+ parseFunOrExtension(mods) {
311
+ const start = this.expect("KwFun" /* TokenKind.KwFun */).span;
312
+ const typeParams = this.parseTypeParams();
313
+ // Could be `fun Type.name(...) { }` — extension function
314
+ // We parse the first "name" and check for a dot after optional type args
315
+ let receiver = null;
316
+ // Peek: if there is a dotted-type followed by `.` before `(`, it's an extension
317
+ const savedPos = this.pos;
318
+ const possibleReceiver = this.tryParseTypeRef();
319
+ if (possibleReceiver && this.check("Dot" /* TokenKind.Dot */)) {
320
+ // Pattern: `fun GenericType<T>.name(...)` — receiver fully parsed, `.name` follows
321
+ receiver = possibleReceiver;
322
+ this.advance(); // consume '.'
323
+ }
324
+ else if (possibleReceiver?.kind === "SimpleTypeRef" &&
325
+ possibleReceiver.name.length >= 2 &&
326
+ (this.check("LParen" /* TokenKind.LParen */) || this.check("Lt" /* TokenKind.Lt */))) {
327
+ // Pattern: `fun Cart.subtotal()` — tryParseTypeRef greedily consumed "Cart.subtotal"
328
+ // as a qualified type name. Split: last component is function name, rest is receiver.
329
+ const parts = possibleReceiver.name;
330
+ const extReceiver = { kind: "SimpleTypeRef", span: possibleReceiver.span, name: parts.slice(0, -1) };
331
+ const extName = parts[parts.length - 1];
332
+ const params = this.parseParamList();
333
+ let returnType = null;
334
+ if (this.check("Colon" /* TokenKind.Colon */)) {
335
+ this.advance();
336
+ returnType = this.parseTypeRef();
337
+ }
338
+ let body = null;
339
+ if (this.check("Eq" /* TokenKind.Eq */)) {
340
+ this.advance();
341
+ body = this.parseExpr();
342
+ this.eatSemicolon();
343
+ }
344
+ else if (this.check("LBrace" /* TokenKind.LBrace */)) {
345
+ body = this.parseBlock();
346
+ }
347
+ else {
348
+ this.eatSemicolon();
349
+ }
350
+ const span = AST.spanFrom(start, this.prevSpan());
351
+ if (!body) {
352
+ body = { kind: "Block", span, statements: [] };
353
+ }
354
+ return { kind: "ExtensionFunDecl", span, modifiers: mods, name: extName, typeParams, receiver: extReceiver, params, returnType, body: body };
355
+ }
356
+ else {
357
+ this.pos = savedPos; // backtrack
358
+ }
359
+ const name = this.expectIdentifier();
360
+ const params = this.parseParamList();
361
+ let returnType = null;
362
+ if (this.check("Colon" /* TokenKind.Colon */)) {
363
+ this.advance();
364
+ returnType = this.parseTypeRef();
365
+ }
366
+ let body = null;
367
+ if (this.check("Eq" /* TokenKind.Eq */)) {
368
+ this.advance();
369
+ body = this.parseExpr();
370
+ this.eatSemicolon();
371
+ }
372
+ else if (this.check("LBrace" /* TokenKind.LBrace */)) {
373
+ body = this.parseBlock();
374
+ }
375
+ else {
376
+ this.eatSemicolon();
377
+ }
378
+ const span = AST.spanFrom(start, this.prevSpan());
379
+ if (receiver) {
380
+ if (!body) {
381
+ this.diag.error(span, diagnostics_js_1.E_EXPECTED_TOKEN, "Extension function must have a body");
382
+ body = { kind: "Block", span, statements: [] };
383
+ }
384
+ return {
385
+ kind: "ExtensionFunDecl",
386
+ span,
387
+ modifiers: mods,
388
+ name,
389
+ typeParams,
390
+ receiver,
391
+ params,
392
+ returnType,
393
+ body,
394
+ };
395
+ }
396
+ return {
397
+ kind: "FunDecl",
398
+ span,
399
+ modifiers: mods,
400
+ name,
401
+ typeParams,
402
+ receiver: null,
403
+ params,
404
+ returnType,
405
+ body,
406
+ };
407
+ }
408
+ parseComponentDecl(mods) {
409
+ const start = this.expect("KwComponent" /* TokenKind.KwComponent */).span;
410
+ this.expect("KwFun" /* TokenKind.KwFun */);
411
+ const name = this.expectIdentifier();
412
+ const params = this.parseParamList();
413
+ const body = this.parseBlock();
414
+ return {
415
+ kind: "ComponentDecl",
416
+ span: AST.spanFrom(start, body.span),
417
+ modifiers: mods,
418
+ name,
419
+ params,
420
+ body,
421
+ };
422
+ }
423
+ // ── Class declarations ─────────────────────────────────────────────────────
424
+ parseClassDecl(mods) {
425
+ const start = this.expect("KwClass" /* TokenKind.KwClass */).span;
426
+ const name = this.expectIdentifier();
427
+ const typeParams = this.parseTypeParams();
428
+ const primaryConstructor = this.parsePrimaryConstructorOpt();
429
+ const superTypes = this.parseSuperTypes();
430
+ const body = this.check("LBrace" /* TokenKind.LBrace */) ? this.parseClassBody() : null;
431
+ this.eatSemicolon();
432
+ return {
433
+ kind: "ClassDecl",
434
+ span: AST.spanFrom(start, this.prevSpan()),
435
+ modifiers: mods,
436
+ name,
437
+ typeParams,
438
+ primaryConstructor,
439
+ superTypes,
440
+ body,
441
+ };
442
+ }
443
+ parseDataClassDecl(mods) {
444
+ const start = this.expect("KwData" /* TokenKind.KwData */).span;
445
+ this.expect("KwClass" /* TokenKind.KwClass */);
446
+ const name = this.expectIdentifier();
447
+ const typeParams = this.parseTypeParams();
448
+ const primaryConstructor = this.parsePrimaryConstructor();
449
+ const superTypes = this.parseSuperTypes();
450
+ const body = this.check("LBrace" /* TokenKind.LBrace */) ? this.parseClassBody() : null;
451
+ this.eatSemicolon();
452
+ return {
453
+ kind: "DataClassDecl",
454
+ span: AST.spanFrom(start, this.prevSpan()),
455
+ modifiers: mods,
456
+ name,
457
+ typeParams,
458
+ primaryConstructor,
459
+ superTypes,
460
+ body,
461
+ };
462
+ }
463
+ parseSealedClassDecl(mods) {
464
+ const start = this.expect("KwSealed" /* TokenKind.KwSealed */).span;
465
+ this.expect("KwClass" /* TokenKind.KwClass */);
466
+ const name = this.expectIdentifier();
467
+ const typeParams = this.parseTypeParams();
468
+ const primaryConstructor = this.parsePrimaryConstructorOpt();
469
+ const superTypes = this.parseSuperTypes();
470
+ const body = this.check("LBrace" /* TokenKind.LBrace */) ? this.parseClassBody() : null;
471
+ this.eatSemicolon();
472
+ return {
473
+ kind: "SealedClassDecl",
474
+ span: AST.spanFrom(start, this.prevSpan()),
475
+ modifiers: mods,
476
+ name,
477
+ typeParams,
478
+ primaryConstructor,
479
+ superTypes,
480
+ body,
481
+ };
482
+ }
483
+ parseInterfaceDecl(mods) {
484
+ const start = this.expect("KwInterface" /* TokenKind.KwInterface */).span;
485
+ const name = this.expectIdentifier();
486
+ const typeParams = this.parseTypeParams();
487
+ const superTypes = this.parseSuperTypes();
488
+ const body = this.check("LBrace" /* TokenKind.LBrace */) ? this.parseClassBody() : null;
489
+ this.eatSemicolon();
490
+ return {
491
+ kind: "InterfaceDecl",
492
+ span: AST.spanFrom(start, this.prevSpan()),
493
+ modifiers: mods,
494
+ name,
495
+ typeParams,
496
+ superTypes,
497
+ body,
498
+ };
499
+ }
500
+ parseObjectDecl(mods) {
501
+ const start = this.expect("KwObject" /* TokenKind.KwObject */).span;
502
+ let name = null;
503
+ if (this.check("Identifier" /* TokenKind.Identifier */)) {
504
+ name = this.advance().text;
505
+ }
506
+ const superTypes = this.parseSuperTypes();
507
+ // Body is optional: `object Foo : Base()` is valid without `{ }`.
508
+ const body = this.check("LBrace" /* TokenKind.LBrace */)
509
+ ? this.parseClassBody()
510
+ : { span: this.prevSpan(), members: [] };
511
+ this.eatSemicolon();
512
+ return {
513
+ kind: "ObjectDecl",
514
+ span: AST.spanFrom(start, body.span),
515
+ modifiers: mods,
516
+ name,
517
+ superTypes,
518
+ body,
519
+ };
520
+ }
521
+ parseTypeAliasDecl(mods) {
522
+ const start = this.expect("KwTypealias" /* TokenKind.KwTypealias */).span;
523
+ const name = this.expectIdentifier();
524
+ const typeParams = this.parseTypeParams();
525
+ this.expect("Eq" /* TokenKind.Eq */);
526
+ const type = this.parseTypeRef();
527
+ this.eatSemicolon();
528
+ return {
529
+ kind: "TypeAliasDecl",
530
+ span: AST.spanFrom(start, this.prevSpan()),
531
+ modifiers: mods,
532
+ name,
533
+ typeParams,
534
+ type,
535
+ };
536
+ }
537
+ // ── enum class ────────────────────────────────────────────────────────────
538
+ parseEnumClassDecl(mods) {
539
+ const start = this.expect("KwEnum" /* TokenKind.KwEnum */).span;
540
+ this.expect("KwClass" /* TokenKind.KwClass */);
541
+ const name = this.expectIdentifier();
542
+ const typeParams = this.parseTypeParams();
543
+ const primaryConstructor = this.parsePrimaryConstructorOpt();
544
+ const superTypes = this.parseSuperTypes();
545
+ const entries = [];
546
+ let body = null;
547
+ if (this.check("LBrace" /* TokenKind.LBrace */)) {
548
+ const bodyStart = this.expect("LBrace" /* TokenKind.LBrace */).span;
549
+ const members = [];
550
+ // Parse enum entries (identifiers at the start of the block, before `;`)
551
+ while (!this.check("RBrace" /* TokenKind.RBrace */) && !this.check("EOF" /* TokenKind.EOF */)) {
552
+ while (this.check("Semicolon" /* TokenKind.Semicolon */)) {
553
+ this.advance();
554
+ // A lone `;` separates entries from the member body
555
+ break;
556
+ }
557
+ if (this.check("RBrace" /* TokenKind.RBrace */))
558
+ break;
559
+ // If we see an identifier and it looks like an enum entry (not a declaration keyword)
560
+ if (this.check("Identifier" /* TokenKind.Identifier */) &&
561
+ !this.isDeclarationKeyword(this.current().kind)) {
562
+ const entrySpan = this.current().span;
563
+ const entryName = this.advance().text;
564
+ let args = [];
565
+ if (this.check("LParen" /* TokenKind.LParen */)) {
566
+ args = this.parseCallArgs();
567
+ }
568
+ let entryBody = null;
569
+ if (this.check("LBrace" /* TokenKind.LBrace */)) {
570
+ entryBody = this.parseClassBody();
571
+ }
572
+ entries.push({ span: AST.spanFrom(entrySpan, this.prevSpan()), name: entryName, args, body: entryBody });
573
+ if (this.check("Comma" /* TokenKind.Comma */))
574
+ this.advance();
575
+ }
576
+ else {
577
+ // Non-entry member (method, property, etc.)
578
+ const member = this.parseClassMember();
579
+ if (member)
580
+ members.push(member);
581
+ }
582
+ }
583
+ const bodyEnd = this.expect("RBrace" /* TokenKind.RBrace */).span;
584
+ if (members.length > 0) {
585
+ body = { span: AST.spanFrom(bodyStart, bodyEnd), members };
586
+ }
587
+ }
588
+ this.eatSemicolon();
589
+ return {
590
+ kind: "EnumClassDecl",
591
+ span: AST.spanFrom(start, this.prevSpan()),
592
+ modifiers: mods,
593
+ name,
594
+ typeParams,
595
+ primaryConstructor,
596
+ superTypes,
597
+ entries,
598
+ body,
599
+ };
600
+ }
601
+ // ── Destructuring declaration — val (a, b) = expr ─────────────────────────
602
+ parseDestructuringDecl(mods) {
603
+ const start = this.current().span;
604
+ const mutable = this.check("KwVar" /* TokenKind.KwVar */);
605
+ this.advance(); // val | var
606
+ this.expect("LParen" /* TokenKind.LParen */);
607
+ const names = [];
608
+ const types = [];
609
+ while (!this.check("RParen" /* TokenKind.RParen */) && !this.check("EOF" /* TokenKind.EOF */)) {
610
+ if (this.check("Underscore" /* TokenKind.Underscore */)) {
611
+ this.advance();
612
+ names.push(null);
613
+ types.push(null);
614
+ }
615
+ else {
616
+ names.push(this.expectIdentifier());
617
+ if (this.check("Colon" /* TokenKind.Colon */)) {
618
+ this.advance();
619
+ types.push(this.parseTypeRef());
620
+ }
621
+ else {
622
+ types.push(null);
623
+ }
624
+ }
625
+ if (this.check("Comma" /* TokenKind.Comma */))
626
+ this.advance();
627
+ }
628
+ this.expect("RParen" /* TokenKind.RParen */);
629
+ this.expect("Eq" /* TokenKind.Eq */);
630
+ const initializer = this.parseExpr();
631
+ this.eatSemicolon();
632
+ return {
633
+ kind: "DestructuringDecl",
634
+ span: AST.spanFrom(start, this.prevSpan()),
635
+ modifiers: mods,
636
+ mutable,
637
+ names,
638
+ types,
639
+ initializer,
640
+ };
641
+ }
642
+ isDeclarationKeyword(kind) {
643
+ return (kind === "KwFun" /* TokenKind.KwFun */ ||
644
+ kind === "KwVal" /* TokenKind.KwVal */ ||
645
+ kind === "KwVar" /* TokenKind.KwVar */ ||
646
+ kind === "KwClass" /* TokenKind.KwClass */ ||
647
+ kind === "KwInterface" /* TokenKind.KwInterface */ ||
648
+ kind === "KwObject" /* TokenKind.KwObject */ ||
649
+ kind === "KwData" /* TokenKind.KwData */ ||
650
+ kind === "KwSealed" /* TokenKind.KwSealed */ ||
651
+ kind === "KwEnum" /* TokenKind.KwEnum */ ||
652
+ kind === "KwOverride" /* TokenKind.KwOverride */ ||
653
+ kind === "KwAbstract" /* TokenKind.KwAbstract */ ||
654
+ kind === "KwOpen" /* TokenKind.KwOpen */ ||
655
+ kind === "KwPrivate" /* TokenKind.KwPrivate */ ||
656
+ kind === "KwProtected" /* TokenKind.KwProtected */ ||
657
+ kind === "KwInternal" /* TokenKind.KwInternal */ ||
658
+ kind === "KwPublic" /* TokenKind.KwPublic */ ||
659
+ kind === "KwSuspend" /* TokenKind.KwSuspend */ ||
660
+ kind === "KwInline" /* TokenKind.KwInline */ ||
661
+ kind === "KwLateinit" /* TokenKind.KwLateinit */);
662
+ }
663
+ parsePropertyDeclTopLevel(mods) {
664
+ return this.parsePropertyDecl(mods);
665
+ }
666
+ // ── Class body ─────────────────────────────────────────────────────────────
667
+ parseClassBody() {
668
+ const start = this.expect("LBrace" /* TokenKind.LBrace */).span;
669
+ const members = [];
670
+ while (!this.check("RBrace" /* TokenKind.RBrace */) && !this.check("EOF" /* TokenKind.EOF */)) {
671
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
672
+ this.advance();
673
+ if (this.check("RBrace" /* TokenKind.RBrace */))
674
+ break;
675
+ const m = this.parseClassMember();
676
+ if (m)
677
+ members.push(m);
678
+ }
679
+ const end = this.expect("RBrace" /* TokenKind.RBrace */).span;
680
+ return { span: AST.spanFrom(start, end), members };
681
+ }
682
+ parseClassMember() {
683
+ if (this.check("KwInit" /* TokenKind.KwInit */)) {
684
+ return this.parseInitBlock();
685
+ }
686
+ if (this.check("KwConstructor" /* TokenKind.KwConstructor */)) {
687
+ return this.parseSecondaryConstructor();
688
+ }
689
+ if (this.check("KwCompanion" /* TokenKind.KwCompanion */)) {
690
+ return this.parseCompanionObject();
691
+ }
692
+ const mods = this.parseModifiers();
693
+ if (this.check("KwFun" /* TokenKind.KwFun */))
694
+ return this.parseFunOrExtension(mods);
695
+ if (this.check("KwComponent" /* TokenKind.KwComponent */))
696
+ return this.parseComponentDecl(mods);
697
+ if (this.check("KwVal" /* TokenKind.KwVal */) || this.check("KwVar" /* TokenKind.KwVar */)) {
698
+ return this.parsePropertyDecl(mods);
699
+ }
700
+ if (this.check("KwData" /* TokenKind.KwData */) && this.checkNext("KwClass" /* TokenKind.KwClass */)) {
701
+ return this.parseDataClassDecl(mods);
702
+ }
703
+ if (this.check("KwSealed" /* TokenKind.KwSealed */) && this.checkNext("KwClass" /* TokenKind.KwClass */)) {
704
+ return this.parseSealedClassDecl(mods);
705
+ }
706
+ if (this.check("KwEnum" /* TokenKind.KwEnum */) && this.checkNext("KwClass" /* TokenKind.KwClass */)) {
707
+ return this.parseEnumClassDecl(mods);
708
+ }
709
+ if (this.check("KwClass" /* TokenKind.KwClass */))
710
+ return this.parseClassDecl(mods);
711
+ if (this.check("KwObject" /* TokenKind.KwObject */))
712
+ return this.parseObjectDecl(mods);
713
+ const tok = this.current();
714
+ this.diag.error(tok.span, diagnostics_js_1.E_UNEXPECTED_TOKEN, `Unexpected token '${tok.text}' in class body`);
715
+ this.advance();
716
+ return null;
717
+ }
718
+ parseInitBlock() {
719
+ const start = this.expect("KwInit" /* TokenKind.KwInit */).span;
720
+ const body = this.parseBlock();
721
+ return { kind: "InitBlock", span: AST.spanFrom(start, body.span), body };
722
+ }
723
+ parseSecondaryConstructor() {
724
+ const start = this.expect("KwConstructor" /* TokenKind.KwConstructor */).span;
725
+ const mods = AST.DEFAULT_MODIFIERS;
726
+ const params = this.parseParamList();
727
+ let delegation = null;
728
+ const delegateArgs = [];
729
+ if (this.check("Colon" /* TokenKind.Colon */)) {
730
+ this.advance();
731
+ if (this.check("KwThis" /* TokenKind.KwThis */)) {
732
+ delegation = "this";
733
+ this.advance();
734
+ }
735
+ else if (this.check("KwSuper" /* TokenKind.KwSuper */)) {
736
+ delegation = "super";
737
+ this.advance();
738
+ }
739
+ delegateArgs.push(...this.parseCallArgs());
740
+ }
741
+ const body = this.parseBlock();
742
+ return {
743
+ kind: "SecondaryConstructor",
744
+ span: AST.spanFrom(start, body.span),
745
+ modifiers: mods,
746
+ params,
747
+ delegation,
748
+ delegateArgs,
749
+ body,
750
+ };
751
+ }
752
+ parseCompanionObject() {
753
+ const start = this.expect("KwCompanion" /* TokenKind.KwCompanion */).span;
754
+ this.expect("KwObject" /* TokenKind.KwObject */);
755
+ let name = null;
756
+ if (this.check("Identifier" /* TokenKind.Identifier */)) {
757
+ name = this.advance().text;
758
+ }
759
+ const superTypes = this.parseSuperTypes();
760
+ const body = this.parseClassBody();
761
+ return {
762
+ kind: "CompanionObject",
763
+ span: AST.spanFrom(start, body.span),
764
+ name,
765
+ superTypes,
766
+ body,
767
+ };
768
+ }
769
+ // ── Property declaration ───────────────────────────────────────────────────
770
+ parsePropertyDecl(mods) {
771
+ const start = this.current().span;
772
+ const mutable = this.check("KwVar" /* TokenKind.KwVar */);
773
+ this.advance(); // val | var
774
+ const typeParams = this.parseTypeParams();
775
+ const name = this.expectIdentifier();
776
+ let type = null;
777
+ if (this.check("Colon" /* TokenKind.Colon */)) {
778
+ this.advance();
779
+ type = this.parseTypeRef();
780
+ }
781
+ let initializer = null;
782
+ let delegate = null;
783
+ if (this.check("Eq" /* TokenKind.Eq */)) {
784
+ this.advance();
785
+ initializer = this.parseExpr();
786
+ }
787
+ else if (this.check("KwBy" /* TokenKind.KwBy */)) {
788
+ this.advance();
789
+ delegate = this.parseExpr();
790
+ }
791
+ let getter = null;
792
+ let setter = null;
793
+ // Parse get/set accessors
794
+ while (true) {
795
+ if (this.checkIdent("get")) {
796
+ getter = this.parsePropertyAccessor();
797
+ }
798
+ else if (this.checkIdent("set")) {
799
+ setter = this.parsePropertyAccessor();
800
+ }
801
+ else {
802
+ break;
803
+ }
804
+ }
805
+ this.eatSemicolon();
806
+ return {
807
+ kind: "PropertyDecl",
808
+ span: AST.spanFrom(start, this.prevSpan()),
809
+ modifiers: mods,
810
+ mutable,
811
+ name,
812
+ typeParams,
813
+ type,
814
+ initializer,
815
+ delegate,
816
+ getter,
817
+ setter,
818
+ };
819
+ }
820
+ parsePropertyAccessor() {
821
+ const start = this.advance().span; // consume 'get' | 'set'
822
+ const params = [];
823
+ if (this.check("LParen" /* TokenKind.LParen */)) {
824
+ this.advance();
825
+ if (!this.check("RParen" /* TokenKind.RParen */)) {
826
+ params.push(this.parseParam());
827
+ }
828
+ this.expect("RParen" /* TokenKind.RParen */);
829
+ }
830
+ let body = null;
831
+ if (this.check("Eq" /* TokenKind.Eq */)) {
832
+ this.advance();
833
+ body = this.parseExpr();
834
+ this.eatSemicolon();
835
+ }
836
+ else if (this.check("LBrace" /* TokenKind.LBrace */)) {
837
+ body = this.parseBlock();
838
+ }
839
+ else {
840
+ this.eatSemicolon();
841
+ }
842
+ return {
843
+ span: AST.spanFrom(start, this.prevSpan()),
844
+ modifiers: AST.DEFAULT_MODIFIERS,
845
+ params,
846
+ body,
847
+ };
848
+ }
849
+ // ── Constructor / params ───────────────────────────────────────────────────
850
+ parsePrimaryConstructor() {
851
+ const start = this.current().span;
852
+ let mods = AST.DEFAULT_MODIFIERS;
853
+ if (this.check("KwConstructor" /* TokenKind.KwConstructor */)) {
854
+ this.advance();
855
+ }
856
+ const params = this.parseParamList();
857
+ return { span: AST.spanFrom(start, this.prevSpan()), modifiers: mods, params };
858
+ }
859
+ parsePrimaryConstructorOpt() {
860
+ if (this.check("LParen" /* TokenKind.LParen */) || this.check("KwConstructor" /* TokenKind.KwConstructor */)) {
861
+ return this.parsePrimaryConstructor();
862
+ }
863
+ return null;
864
+ }
865
+ parseParamList() {
866
+ this.expect("LParen" /* TokenKind.LParen */);
867
+ const params = [];
868
+ while (!this.check("RParen" /* TokenKind.RParen */) && !this.check("EOF" /* TokenKind.EOF */)) {
869
+ // Skip ASI-inserted semicolons between params
870
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
871
+ this.advance();
872
+ if (this.check("RParen" /* TokenKind.RParen */) || this.check("EOF" /* TokenKind.EOF */))
873
+ break;
874
+ const prevPos = this.pos;
875
+ params.push(this.parseParam());
876
+ // Safety: if parseParam made no progress, break to avoid infinite loop
877
+ if (this.pos === prevPos)
878
+ break;
879
+ // Skip trailing semicolons/ASI before the comma or close paren
880
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
881
+ this.advance();
882
+ if (!this.check("RParen" /* TokenKind.RParen */))
883
+ this.expect("Comma" /* TokenKind.Comma */);
884
+ }
885
+ this.expect("RParen" /* TokenKind.RParen */);
886
+ return params;
887
+ }
888
+ parseParam() {
889
+ const start = this.current().span;
890
+ let vararg = false;
891
+ let propertyKind = null;
892
+ if (this.checkIdent("vararg")) {
893
+ vararg = true;
894
+ this.advance();
895
+ }
896
+ if (this.check("KwVal" /* TokenKind.KwVal */)) {
897
+ propertyKind = "val";
898
+ this.advance();
899
+ }
900
+ else if (this.check("KwVar" /* TokenKind.KwVar */)) {
901
+ propertyKind = "var";
902
+ this.advance();
903
+ }
904
+ const name = this.expectIdentOrKeyword();
905
+ this.expect("Colon" /* TokenKind.Colon */);
906
+ const type = this.parseTypeRef();
907
+ let defaultValue = null;
908
+ if (this.check("Eq" /* TokenKind.Eq */)) {
909
+ this.advance();
910
+ defaultValue = this.parseExpr();
911
+ }
912
+ return {
913
+ span: AST.spanFrom(start, this.prevSpan()),
914
+ propertyKind,
915
+ name,
916
+ type,
917
+ defaultValue,
918
+ vararg,
919
+ };
920
+ }
921
+ // ── Super types ────────────────────────────────────────────────────────────
922
+ parseSuperTypes() {
923
+ const entries = [];
924
+ if (!this.check("Colon" /* TokenKind.Colon */))
925
+ return entries;
926
+ this.advance();
927
+ entries.push(this.parseSuperTypeEntry());
928
+ while (this.check("Comma" /* TokenKind.Comma */)) {
929
+ this.advance();
930
+ entries.push(this.parseSuperTypeEntry());
931
+ }
932
+ return entries;
933
+ }
934
+ parseSuperTypeEntry() {
935
+ const start = this.current().span;
936
+ const type = this.parseTypeRef();
937
+ let delegateArgs = null;
938
+ if (this.check("LParen" /* TokenKind.LParen */)) {
939
+ delegateArgs = this.parseCallArgs();
940
+ }
941
+ return { span: AST.spanFrom(start, this.prevSpan()), type, delegateArgs };
942
+ }
943
+ // ── Type references ────────────────────────────────────────────────────────
944
+ parseTypeRef() {
945
+ const t = this.parseTypeRefInner();
946
+ if (this.check("Question" /* TokenKind.Question */)) {
947
+ const span = this.advance().span;
948
+ return { kind: "NullableTypeRef", span: AST.spanFrom(t.span, span), base: t };
949
+ }
950
+ return t;
951
+ }
952
+ tryParseTypeRef() {
953
+ const saved = this.pos;
954
+ try {
955
+ const t = this.parseTypeRef();
956
+ return t;
957
+ }
958
+ catch {
959
+ this.pos = saved;
960
+ return null;
961
+ }
962
+ }
963
+ parseTypeRefInner() {
964
+ const start = this.current().span;
965
+ // Function type: (A, B) -> C or Receiver.(A) -> B
966
+ // Use lookahead to confirm `(...) ->` before speculatively parsing.
967
+ if (this.check("LParen" /* TokenKind.LParen */) && this.looksLikeFunctionType()) {
968
+ const saved = this.pos;
969
+ try {
970
+ return this.parseFunctionType(null);
971
+ }
972
+ catch {
973
+ this.pos = saved;
974
+ // fall through to simple type
975
+ }
976
+ }
977
+ // Parenthesized type: (T) — supports nullable function types like (() -> Unit)?
978
+ if (this.check("LParen" /* TokenKind.LParen */)) {
979
+ const saved = this.pos;
980
+ try {
981
+ this.advance(); // consume (
982
+ const inner = this.parseTypeRef();
983
+ if (this.check("RParen" /* TokenKind.RParen */)) {
984
+ this.advance(); // consume )
985
+ return inner;
986
+ }
987
+ this.pos = saved;
988
+ }
989
+ catch {
990
+ this.pos = saved;
991
+ }
992
+ }
993
+ // Simple or generic type
994
+ const name = [this.expectIdentOrKeyword()];
995
+ while (this.check("Dot" /* TokenKind.Dot */) && !this.check("DotDot" /* TokenKind.DotDot */)) {
996
+ this.advance();
997
+ name.push(this.expectIdentOrKeyword());
998
+ }
999
+ const simple = {
1000
+ kind: "SimpleTypeRef",
1001
+ span: AST.spanFrom(start, this.prevSpan()),
1002
+ name,
1003
+ };
1004
+ if (this.check("Lt" /* TokenKind.Lt */)) {
1005
+ const args = this.parseTypeArgs();
1006
+ return {
1007
+ kind: "GenericTypeRef",
1008
+ span: AST.spanFrom(start, this.prevSpan()),
1009
+ base: simple,
1010
+ args,
1011
+ };
1012
+ }
1013
+ return simple;
1014
+ }
1015
+ parseFunctionType(receiver) {
1016
+ const start = this.current().span;
1017
+ this.expect("LParen" /* TokenKind.LParen */);
1018
+ const params = [];
1019
+ while (!this.check("RParen" /* TokenKind.RParen */) && !this.check("EOF" /* TokenKind.EOF */)) {
1020
+ // Named param: `name: Type` — we just discard the name
1021
+ if (this.check("Identifier" /* TokenKind.Identifier */) && this.checkNext("Colon" /* TokenKind.Colon */)) {
1022
+ this.advance();
1023
+ this.advance();
1024
+ }
1025
+ const prevPos = this.pos;
1026
+ params.push(this.parseTypeRef());
1027
+ // Safety: if parseTypeRef didn't advance, bail to prevent infinite loop
1028
+ if (this.pos === prevPos)
1029
+ break;
1030
+ if (!this.check("RParen" /* TokenKind.RParen */))
1031
+ this.expect("Comma" /* TokenKind.Comma */);
1032
+ }
1033
+ this.expect("RParen" /* TokenKind.RParen */);
1034
+ this.expect("Arrow" /* TokenKind.Arrow */);
1035
+ const returnType = this.parseTypeRef();
1036
+ return {
1037
+ kind: "FunctionTypeRef",
1038
+ span: AST.spanFrom(start, this.prevSpan()),
1039
+ receiver,
1040
+ params,
1041
+ returnType,
1042
+ };
1043
+ }
1044
+ /** Lookahead: scan past matching parens to see if followed by `->` */
1045
+ looksLikeFunctionType() {
1046
+ let i = this.pos + 1; // skip the opening `(`
1047
+ let depth = 1;
1048
+ while (i < this.tokens.length && depth > 0) {
1049
+ const k = this.tokens[i].kind;
1050
+ if (k === "LParen" /* TokenKind.LParen */)
1051
+ depth++;
1052
+ else if (k === "RParen" /* TokenKind.RParen */)
1053
+ depth--;
1054
+ else if (k === "EOF" /* TokenKind.EOF */)
1055
+ return false;
1056
+ i++;
1057
+ }
1058
+ // After closing `)`, the next token must be `->`
1059
+ return this.tokens[i]?.kind === "Arrow" /* TokenKind.Arrow */;
1060
+ }
1061
+ parseTypeArgs() {
1062
+ this.expect("Lt" /* TokenKind.Lt */);
1063
+ const args = [];
1064
+ while (!this.check("Gt" /* TokenKind.Gt */) && !this.check("EOF" /* TokenKind.EOF */)) {
1065
+ const prevPos = this.pos;
1066
+ const span = this.current().span;
1067
+ if (this.check("Star" /* TokenKind.Star */)) {
1068
+ this.advance();
1069
+ args.push({ span, variance: null, star: true, type: null });
1070
+ }
1071
+ else {
1072
+ let variance = null;
1073
+ if (this.check("KwIn" /* TokenKind.KwIn */)) {
1074
+ variance = "in";
1075
+ this.advance();
1076
+ }
1077
+ else if (this.checkIdent("out")) {
1078
+ variance = "out";
1079
+ this.advance();
1080
+ }
1081
+ const t = this.parseTypeRef();
1082
+ args.push({ span: AST.spanFrom(span, t.span), variance, star: false, type: t });
1083
+ }
1084
+ // Safety: if no progress was made, break to prevent infinite loop
1085
+ if (this.pos === prevPos)
1086
+ break;
1087
+ if (!this.check("Gt" /* TokenKind.Gt */))
1088
+ this.expect("Comma" /* TokenKind.Comma */);
1089
+ }
1090
+ this.expect("Gt" /* TokenKind.Gt */);
1091
+ return args;
1092
+ }
1093
+ parseTypeParams() {
1094
+ if (!this.check("Lt" /* TokenKind.Lt */))
1095
+ return [];
1096
+ this.advance();
1097
+ const params = [];
1098
+ while (!this.check("Gt" /* TokenKind.Gt */) && !this.check("EOF" /* TokenKind.EOF */)) {
1099
+ const span = this.current().span;
1100
+ let reified = false;
1101
+ let variance = null;
1102
+ if (this.checkIdent("reified")) {
1103
+ reified = true;
1104
+ this.advance();
1105
+ }
1106
+ if (this.check("KwIn" /* TokenKind.KwIn */)) {
1107
+ variance = "in";
1108
+ this.advance();
1109
+ }
1110
+ else if (this.checkIdent("out")) {
1111
+ variance = "out";
1112
+ this.advance();
1113
+ }
1114
+ const name = this.expectIdentifier();
1115
+ let upperBound = null;
1116
+ if (this.check("Colon" /* TokenKind.Colon */)) {
1117
+ this.advance();
1118
+ upperBound = this.parseTypeRef();
1119
+ }
1120
+ params.push({ span: AST.spanFrom(span, this.prevSpan()), name, variance, reified, upperBound });
1121
+ if (!this.check("Gt" /* TokenKind.Gt */))
1122
+ this.expect("Comma" /* TokenKind.Comma */);
1123
+ }
1124
+ this.expect("Gt" /* TokenKind.Gt */);
1125
+ return params;
1126
+ }
1127
+ // ── Block ──────────────────────────────────────────────────────────────────
1128
+ parseBlock() {
1129
+ const start = this.expect("LBrace" /* TokenKind.LBrace */).span;
1130
+ const statements = [];
1131
+ while (!this.check("RBrace" /* TokenKind.RBrace */) && !this.check("EOF" /* TokenKind.EOF */)) {
1132
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
1133
+ this.advance();
1134
+ if (this.check("RBrace" /* TokenKind.RBrace */))
1135
+ break;
1136
+ const s = this.parseStmt();
1137
+ if (s)
1138
+ statements.push(s);
1139
+ }
1140
+ const end = this.expect("RBrace" /* TokenKind.RBrace */).span;
1141
+ return { kind: "Block", span: AST.spanFrom(start, end), statements };
1142
+ }
1143
+ // ── Statements ─────────────────────────────────────────────────────────────
1144
+ parseStmt() {
1145
+ const tok = this.current();
1146
+ switch (tok.kind) {
1147
+ case "KwVal" /* TokenKind.KwVal */:
1148
+ case "KwVar" /* TokenKind.KwVar */:
1149
+ if (this.checkNext("LParen" /* TokenKind.LParen */)) {
1150
+ return this.parseDestructuringDecl(AST.DEFAULT_MODIFIERS);
1151
+ }
1152
+ return this.parseLocalProperty();
1153
+ case "KwReturn" /* TokenKind.KwReturn */:
1154
+ return this.parseReturnStmt();
1155
+ case "KwThrow" /* TokenKind.KwThrow */:
1156
+ return this.parseThrowStmt();
1157
+ case "KwBreak" /* TokenKind.KwBreak */:
1158
+ return this.parseBreakStmt();
1159
+ case "KwContinue" /* TokenKind.KwContinue */:
1160
+ return this.parseContinueStmt();
1161
+ case "KwIf" /* TokenKind.KwIf */:
1162
+ return this.parseIfStmt();
1163
+ case "KwWhen" /* TokenKind.KwWhen */:
1164
+ return this.parseWhenStmt();
1165
+ case "KwFor" /* TokenKind.KwFor */:
1166
+ return this.parseForStmt();
1167
+ case "KwWhile" /* TokenKind.KwWhile */:
1168
+ return this.parseWhileStmt();
1169
+ case "KwDo" /* TokenKind.KwDo */:
1170
+ return this.parseDoWhileStmt();
1171
+ case "KwTry" /* TokenKind.KwTry */:
1172
+ return this.parseTryCatchStmt();
1173
+ case "LBrace" /* TokenKind.LBrace */:
1174
+ return this.parseBlock();
1175
+ default: {
1176
+ // Could be a labeled statement: `label@ while (...) {}`
1177
+ if (tok.kind === "Identifier" /* TokenKind.Identifier */ && this.checkNext("At" /* TokenKind.At */)) {
1178
+ return this.parseLabeledStmt();
1179
+ }
1180
+ // Expression statement (assignment, call, etc.)
1181
+ const expr = this.parseExpr();
1182
+ this.eatSemicolon();
1183
+ return { kind: "ExprStmt", span: expr.span, expr };
1184
+ }
1185
+ }
1186
+ }
1187
+ parseLocalProperty() {
1188
+ return this.parsePropertyDecl(AST.DEFAULT_MODIFIERS);
1189
+ }
1190
+ parseReturnStmt() {
1191
+ const start = this.expect("KwReturn" /* TokenKind.KwReturn */).span;
1192
+ let label = null;
1193
+ if (this.check("At" /* TokenKind.At */)) {
1194
+ this.advance();
1195
+ label = this.expectIdentifier();
1196
+ }
1197
+ let value = null;
1198
+ if (!this.check("Semicolon" /* TokenKind.Semicolon */) && !this.check("RBrace" /* TokenKind.RBrace */) && !this.check("EOF" /* TokenKind.EOF */)) {
1199
+ value = this.parseExpr();
1200
+ }
1201
+ this.eatSemicolon();
1202
+ return { kind: "ReturnStmt", span: AST.spanFrom(start, this.prevSpan()), label, value };
1203
+ }
1204
+ parseThrowStmt() {
1205
+ const start = this.expect("KwThrow" /* TokenKind.KwThrow */).span;
1206
+ const value = this.parseExpr();
1207
+ this.eatSemicolon();
1208
+ return { kind: "ThrowStmt", span: AST.spanFrom(start, value.span), value };
1209
+ }
1210
+ parseBreakStmt() {
1211
+ const start = this.expect("KwBreak" /* TokenKind.KwBreak */).span;
1212
+ let label = null;
1213
+ if (this.check("At" /* TokenKind.At */)) {
1214
+ this.advance();
1215
+ label = this.expectIdentifier();
1216
+ }
1217
+ this.eatSemicolon();
1218
+ return { kind: "BreakStmt", span: AST.spanFrom(start, this.prevSpan()), label };
1219
+ }
1220
+ parseContinueStmt() {
1221
+ const start = this.expect("KwContinue" /* TokenKind.KwContinue */).span;
1222
+ let label = null;
1223
+ if (this.check("At" /* TokenKind.At */)) {
1224
+ this.advance();
1225
+ label = this.expectIdentifier();
1226
+ }
1227
+ this.eatSemicolon();
1228
+ return { kind: "ContinueStmt", span: AST.spanFrom(start, this.prevSpan()), label };
1229
+ }
1230
+ parseIfStmt() {
1231
+ const start = this.expect("KwIf" /* TokenKind.KwIf */).span;
1232
+ this.expect("LParen" /* TokenKind.LParen */);
1233
+ const condition = this.parseExpr();
1234
+ this.expect("RParen" /* TokenKind.RParen */);
1235
+ const then = this.parseBlock();
1236
+ // Skip ASI-inserted semicolons between the then-block's `}` and `else`
1237
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
1238
+ this.advance();
1239
+ let elseClause = null;
1240
+ if (this.check("KwElse" /* TokenKind.KwElse */)) {
1241
+ this.advance();
1242
+ if (this.check("KwIf" /* TokenKind.KwIf */)) {
1243
+ elseClause = this.parseIfStmt();
1244
+ }
1245
+ else {
1246
+ elseClause = this.parseBlock();
1247
+ }
1248
+ }
1249
+ return {
1250
+ kind: "IfStmt",
1251
+ span: AST.spanFrom(start, this.prevSpan()),
1252
+ condition,
1253
+ then,
1254
+ else: elseClause,
1255
+ };
1256
+ }
1257
+ parseWhenStmt() {
1258
+ const start = this.expect("KwWhen" /* TokenKind.KwWhen */).span;
1259
+ let subject = null;
1260
+ if (this.check("LParen" /* TokenKind.LParen */)) {
1261
+ const subStart = this.advance().span;
1262
+ let binding = null;
1263
+ if (this.check("KwVal" /* TokenKind.KwVal */) && this.peekKindAt(2) === "Eq" /* TokenKind.Eq */) {
1264
+ this.advance();
1265
+ binding = this.expectIdentifier();
1266
+ this.expect("Eq" /* TokenKind.Eq */);
1267
+ }
1268
+ const expr = this.parseExpr();
1269
+ this.expect("RParen" /* TokenKind.RParen */);
1270
+ subject = { span: AST.spanFrom(subStart, this.prevSpan()), binding, expr };
1271
+ }
1272
+ this.expect("LBrace" /* TokenKind.LBrace */);
1273
+ const branches = [];
1274
+ while (!this.check("RBrace" /* TokenKind.RBrace */) && !this.check("EOF" /* TokenKind.EOF */)) {
1275
+ branches.push(this.parseWhenBranch());
1276
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
1277
+ this.advance();
1278
+ }
1279
+ const end = this.expect("RBrace" /* TokenKind.RBrace */).span;
1280
+ return { kind: "WhenStmt", span: AST.spanFrom(start, end), subject, branches };
1281
+ }
1282
+ parseWhenBranch() {
1283
+ const start = this.current().span;
1284
+ let isElse = false;
1285
+ const conditions = [];
1286
+ if (this.check("KwElse" /* TokenKind.KwElse */)) {
1287
+ isElse = true;
1288
+ this.advance();
1289
+ }
1290
+ else {
1291
+ conditions.push(this.parseWhenCondition());
1292
+ while (this.check("Comma" /* TokenKind.Comma */)) {
1293
+ this.advance();
1294
+ conditions.push(this.parseWhenCondition());
1295
+ }
1296
+ }
1297
+ this.expect("Arrow" /* TokenKind.Arrow */);
1298
+ let body;
1299
+ if (this.check("LBrace" /* TokenKind.LBrace */)) {
1300
+ body = this.parseBlock();
1301
+ }
1302
+ else {
1303
+ body = this.parseExpr();
1304
+ this.eatSemicolon();
1305
+ }
1306
+ return { span: AST.spanFrom(start, this.prevSpan()), conditions, isElse, body };
1307
+ }
1308
+ parseWhenCondition() {
1309
+ const span = this.current().span;
1310
+ const negated = this.check("Bang" /* TokenKind.Bang */);
1311
+ if (negated)
1312
+ this.advance();
1313
+ if (this.check("KwIs" /* TokenKind.KwIs */)) {
1314
+ this.advance();
1315
+ const type = this.parseTypeRef();
1316
+ return { kind: "WhenIsCondition", span, negated, type };
1317
+ }
1318
+ if (this.check("KwIn" /* TokenKind.KwIn */)) {
1319
+ this.advance();
1320
+ const expr = this.parseExpr();
1321
+ return { kind: "WhenInCondition", span, negated, expr };
1322
+ }
1323
+ const expr = this.parseExpr();
1324
+ return { kind: "WhenExprCondition", span, expr };
1325
+ }
1326
+ parseForStmt() {
1327
+ const start = this.expect("KwFor" /* TokenKind.KwFor */).span;
1328
+ this.expect("LParen" /* TokenKind.LParen */);
1329
+ const binding = this.parseForBinding();
1330
+ this.expect("KwIn" /* TokenKind.KwIn */);
1331
+ const iterable = this.parseExpr();
1332
+ this.expect("RParen" /* TokenKind.RParen */);
1333
+ const body = this.parseBlock();
1334
+ return { kind: "ForStmt", span: AST.spanFrom(start, body.span), binding, iterable, body };
1335
+ }
1336
+ parseForBinding() {
1337
+ if (this.check("LParen" /* TokenKind.LParen */)) {
1338
+ this.advance();
1339
+ const names = [];
1340
+ while (!this.check("RParen" /* TokenKind.RParen */) && !this.check("EOF" /* TokenKind.EOF */)) {
1341
+ if (this.check("Underscore" /* TokenKind.Underscore */)) {
1342
+ names.push(null);
1343
+ this.advance();
1344
+ }
1345
+ else
1346
+ names.push(this.expectIdentifier());
1347
+ if (!this.check("RParen" /* TokenKind.RParen */))
1348
+ this.expect("Comma" /* TokenKind.Comma */);
1349
+ }
1350
+ this.expect("RParen" /* TokenKind.RParen */);
1351
+ return { kind: "TupleDestructure", names };
1352
+ }
1353
+ return this.expectIdentifier();
1354
+ }
1355
+ parseWhileStmt() {
1356
+ const start = this.expect("KwWhile" /* TokenKind.KwWhile */).span;
1357
+ this.expect("LParen" /* TokenKind.LParen */);
1358
+ const condition = this.parseExpr();
1359
+ this.expect("RParen" /* TokenKind.RParen */);
1360
+ const body = this.parseBlock();
1361
+ return { kind: "WhileStmt", span: AST.spanFrom(start, body.span), condition, body };
1362
+ }
1363
+ parseDoWhileStmt() {
1364
+ const start = this.expect("KwDo" /* TokenKind.KwDo */).span;
1365
+ const body = this.parseBlock();
1366
+ this.expect("KwWhile" /* TokenKind.KwWhile */);
1367
+ this.expect("LParen" /* TokenKind.LParen */);
1368
+ const condition = this.parseExpr();
1369
+ this.expect("RParen" /* TokenKind.RParen */);
1370
+ this.eatSemicolon();
1371
+ return { kind: "DoWhileStmt", span: AST.spanFrom(start, this.prevSpan()), body, condition };
1372
+ }
1373
+ parseTryCatchStmt() {
1374
+ const start = this.expect("KwTry" /* TokenKind.KwTry */).span;
1375
+ const body = this.parseBlock();
1376
+ const catches = [];
1377
+ while (this.check("KwCatch" /* TokenKind.KwCatch */)) {
1378
+ catches.push(this.parseCatchClause());
1379
+ }
1380
+ let fin = null;
1381
+ if (this.check("KwFinally" /* TokenKind.KwFinally */)) {
1382
+ this.advance();
1383
+ fin = this.parseBlock();
1384
+ }
1385
+ return { kind: "TryCatchStmt", span: AST.spanFrom(start, this.prevSpan()), body, catches, finally: fin };
1386
+ }
1387
+ parseCatchClause() {
1388
+ const start = this.expect("KwCatch" /* TokenKind.KwCatch */).span;
1389
+ this.expect("LParen" /* TokenKind.LParen */);
1390
+ const name = this.expectIdentifier();
1391
+ this.expect("Colon" /* TokenKind.Colon */);
1392
+ const type = this.parseTypeRef();
1393
+ this.expect("RParen" /* TokenKind.RParen */);
1394
+ const body = this.parseBlock();
1395
+ return { span: AST.spanFrom(start, body.span), name, type, body };
1396
+ }
1397
+ parseLabeledStmt() {
1398
+ const start = this.current().span;
1399
+ const label = this.advance().text; // label name
1400
+ this.expect("At" /* TokenKind.At */);
1401
+ const body = this.parseStmt();
1402
+ return { kind: "LabeledStmt", span: AST.spanFrom(start, body.span), label, body };
1403
+ }
1404
+ // ── Expressions (Pratt parser / precedence climbing) ──────────────────────
1405
+ parseExpr() {
1406
+ if (this.depth > constants_js_1.MAX_EXPR_DEPTH) {
1407
+ const tok = this.current();
1408
+ this.diag.error(tok.span, diagnostics_js_1.E_EXPECTED_EXPRESSION, `Expression nesting too deep near '${tok.text || tok.kind}'`);
1409
+ // Make forward progress so callers can recover instead of looping.
1410
+ if (!this.check("EOF" /* TokenKind.EOF */))
1411
+ this.advance();
1412
+ return { kind: "NullLiteralExpr", span: tok.span };
1413
+ }
1414
+ this.depth++;
1415
+ try {
1416
+ return this.parseAssign();
1417
+ }
1418
+ finally {
1419
+ this.depth--;
1420
+ }
1421
+ }
1422
+ parseAssign() {
1423
+ const left = this.parseElvis();
1424
+ const assignOps = {
1425
+ ["Eq" /* TokenKind.Eq */]: "=",
1426
+ ["PlusEq" /* TokenKind.PlusEq */]: "+=",
1427
+ ["MinusEq" /* TokenKind.MinusEq */]: "-=",
1428
+ ["StarEq" /* TokenKind.StarEq */]: "*=",
1429
+ ["SlashEq" /* TokenKind.SlashEq */]: "/=",
1430
+ ["PercentEq" /* TokenKind.PercentEq */]: "%=",
1431
+ };
1432
+ const op = assignOps[this.current().kind];
1433
+ if (op) {
1434
+ this.advance();
1435
+ const right = this.parseAssign();
1436
+ if (op === "=") {
1437
+ return {
1438
+ kind: "AssignExpr",
1439
+ span: AST.spanFrom(left.span, right.span),
1440
+ target: left,
1441
+ value: right,
1442
+ };
1443
+ }
1444
+ return {
1445
+ kind: "CompoundAssignExpr",
1446
+ span: AST.spanFrom(left.span, right.span),
1447
+ op: op,
1448
+ target: left,
1449
+ value: right,
1450
+ };
1451
+ }
1452
+ return left;
1453
+ }
1454
+ parseElvis() {
1455
+ let left = this.parseOr();
1456
+ while (this.check("QuestionColon" /* TokenKind.QuestionColon */)) {
1457
+ this.advance();
1458
+ const right = this.parseOr();
1459
+ left = { kind: "ElvisExpr", span: AST.spanFrom(left.span, right.span), left, right };
1460
+ }
1461
+ return left;
1462
+ }
1463
+ parseOr() {
1464
+ let left = this.parseAnd();
1465
+ while (this.check("PipePipe" /* TokenKind.PipePipe */)) {
1466
+ this.advance();
1467
+ const right = this.parseAnd();
1468
+ left = { kind: "BinaryExpr", span: AST.spanFrom(left.span, right.span), op: "||", left, right };
1469
+ }
1470
+ return left;
1471
+ }
1472
+ parseAnd() {
1473
+ let left = this.parseEquality();
1474
+ while (this.check("AmpAmp" /* TokenKind.AmpAmp */)) {
1475
+ this.advance();
1476
+ const right = this.parseEquality();
1477
+ left = { kind: "BinaryExpr", span: AST.spanFrom(left.span, right.span), op: "&&", left, right };
1478
+ }
1479
+ return left;
1480
+ }
1481
+ parseEquality() {
1482
+ let left = this.parseComparison();
1483
+ while (true) {
1484
+ let op = null;
1485
+ if (this.check("EqEq" /* TokenKind.EqEq */))
1486
+ op = "==";
1487
+ else if (this.check("BangEq" /* TokenKind.BangEq */))
1488
+ op = "!=";
1489
+ else if (this.check("EqEqEq" /* TokenKind.EqEqEq */))
1490
+ op = "===";
1491
+ else if (this.check("BangEqEq" /* TokenKind.BangEqEq */))
1492
+ op = "!==";
1493
+ else
1494
+ break;
1495
+ this.advance();
1496
+ const right = this.parseComparison();
1497
+ left = { kind: "BinaryExpr", span: AST.spanFrom(left.span, right.span), op, left, right };
1498
+ }
1499
+ return left;
1500
+ }
1501
+ parseComparison() {
1502
+ let left = this.parseRange();
1503
+ while (true) {
1504
+ let op = null;
1505
+ if (this.check("Lt" /* TokenKind.Lt */))
1506
+ op = "<";
1507
+ else if (this.check("Gt" /* TokenKind.Gt */))
1508
+ op = ">";
1509
+ else if (this.check("LtEq" /* TokenKind.LtEq */))
1510
+ op = "<=";
1511
+ else if (this.check("GtEq" /* TokenKind.GtEq */))
1512
+ op = ">=";
1513
+ else
1514
+ break;
1515
+ this.advance();
1516
+ const right = this.parseRange();
1517
+ left = { kind: "BinaryExpr", span: AST.spanFrom(left.span, right.span), op, left, right };
1518
+ }
1519
+ return left;
1520
+ }
1521
+ parseRange() {
1522
+ const left = this.parseInfix();
1523
+ if (this.check("DotDot" /* TokenKind.DotDot */)) {
1524
+ this.advance();
1525
+ const right = this.parseInfix();
1526
+ return { kind: "RangeExpr", span: AST.spanFrom(left.span, right.span), from: left, to: right, inclusive: true };
1527
+ }
1528
+ if (this.check("DotDotLt" /* TokenKind.DotDotLt */)) {
1529
+ this.advance();
1530
+ const right = this.parseInfix();
1531
+ return { kind: "RangeExpr", span: AST.spanFrom(left.span, right.span), from: left, to: right, inclusive: false };
1532
+ }
1533
+ return left;
1534
+ }
1535
+ parseInfix() {
1536
+ let left = this.parseAdditive();
1537
+ // Infix function calls: `a infixFun b` → `a.infixFun(b)`
1538
+ // An identifier at this position (not a keyword, not the start of a block) is an infix call.
1539
+ while (this.check("Identifier" /* TokenKind.Identifier */)) {
1540
+ // Disambiguate: peek to see if the token after the identifier is a valid start of an expression.
1541
+ // If the next next token is `(`, it's a regular call like `foo bar(...)`, not infix.
1542
+ // We accept any identifier followed by a non-( expression start.
1543
+ const saved = this.pos;
1544
+ const name = this.current().text;
1545
+ this.advance(); // consume identifier
1546
+ // If followed immediately by `(` it looks like a regular call, not infix — back off
1547
+ if (this.check("LParen" /* TokenKind.LParen */)) {
1548
+ this.pos = saved;
1549
+ break;
1550
+ }
1551
+ // Try to parse the right-hand operand
1552
+ const right = this.parseAdditive();
1553
+ const callee = {
1554
+ kind: "MemberExpr",
1555
+ span: left.span,
1556
+ target: left,
1557
+ member: name,
1558
+ };
1559
+ const callArg = {
1560
+ span: right.span,
1561
+ name: null,
1562
+ spread: false,
1563
+ value: right,
1564
+ };
1565
+ left = {
1566
+ kind: "CallExpr",
1567
+ span: AST.spanFrom(left.span, right.span),
1568
+ callee,
1569
+ typeArgs: [],
1570
+ args: [callArg],
1571
+ trailingLambda: null,
1572
+ };
1573
+ }
1574
+ // `is` and `!is` checks
1575
+ while (this.check("KwIs" /* TokenKind.KwIs */) || (this.check("Bang" /* TokenKind.Bang */) && this.checkNext("KwIs" /* TokenKind.KwIs */))) {
1576
+ const negated = this.check("Bang" /* TokenKind.Bang */);
1577
+ if (negated)
1578
+ this.advance();
1579
+ this.advance(); // `is`
1580
+ const type = this.parseTypeRef();
1581
+ left = { kind: "TypeCheckExpr", span: AST.spanFrom(left.span, type.span), negated, expr: left, type };
1582
+ }
1583
+ // `as` and `as?`
1584
+ while (this.check("KwAs" /* TokenKind.KwAs */)) {
1585
+ this.advance();
1586
+ const safe = this.check("Question" /* TokenKind.Question */);
1587
+ if (safe)
1588
+ this.advance();
1589
+ const type = this.parseTypeRef();
1590
+ if (safe) {
1591
+ left = { kind: "SafeCastExpr", span: AST.spanFrom(left.span, type.span), expr: left, type };
1592
+ }
1593
+ else {
1594
+ left = { kind: "TypeCastExpr", span: AST.spanFrom(left.span, type.span), expr: left, type };
1595
+ }
1596
+ }
1597
+ return left;
1598
+ }
1599
+ parseAdditive() {
1600
+ let left = this.parseMultiplicative();
1601
+ while (this.check("Plus" /* TokenKind.Plus */) || this.check("Minus" /* TokenKind.Minus */)) {
1602
+ const op = this.check("Plus" /* TokenKind.Plus */) ? "+" : "-";
1603
+ this.advance();
1604
+ const right = this.parseMultiplicative();
1605
+ left = { kind: "BinaryExpr", span: AST.spanFrom(left.span, right.span), op, left, right };
1606
+ }
1607
+ return left;
1608
+ }
1609
+ parseMultiplicative() {
1610
+ let left = this.parseUnary();
1611
+ while (this.check("Star" /* TokenKind.Star */) || this.check("Slash" /* TokenKind.Slash */) || this.check("Percent" /* TokenKind.Percent */)) {
1612
+ const op = this.check("Star" /* TokenKind.Star */) ? "*" : this.check("Slash" /* TokenKind.Slash */) ? "/" : "%";
1613
+ this.advance();
1614
+ const right = this.parseUnary();
1615
+ left = { kind: "BinaryExpr", span: AST.spanFrom(left.span, right.span), op, left, right };
1616
+ }
1617
+ return left;
1618
+ }
1619
+ parseUnary() {
1620
+ const span = this.current().span;
1621
+ if (this.check("Bang" /* TokenKind.Bang */)) {
1622
+ this.advance();
1623
+ const operand = this.parseUnary();
1624
+ return { kind: "UnaryExpr", span: AST.spanFrom(span, operand.span), op: "!", operand, prefix: true };
1625
+ }
1626
+ if (this.check("Minus" /* TokenKind.Minus */)) {
1627
+ this.advance();
1628
+ const operand = this.parseUnary();
1629
+ return { kind: "UnaryExpr", span: AST.spanFrom(span, operand.span), op: "-", operand, prefix: true };
1630
+ }
1631
+ if (this.check("Plus" /* TokenKind.Plus */)) {
1632
+ this.advance();
1633
+ return this.parseUnary();
1634
+ }
1635
+ // Prefix ++ / --
1636
+ if (this.check("PlusPlus" /* TokenKind.PlusPlus */) || this.check("MinusMinus" /* TokenKind.MinusMinus */)) {
1637
+ const op = this.check("PlusPlus" /* TokenKind.PlusPlus */) ? "++" : "--";
1638
+ this.advance();
1639
+ const target = this.parsePostfix();
1640
+ return { kind: "IncrDecrExpr", span: AST.spanFrom(span, target.span), op, target, prefix: true };
1641
+ }
1642
+ return this.parsePostfix();
1643
+ }
1644
+ parsePostfix() {
1645
+ let expr = this.parsePrimary();
1646
+ loop: while (true) {
1647
+ switch (this.current().kind) {
1648
+ case "Dot" /* TokenKind.Dot */: {
1649
+ this.advance();
1650
+ const member = this.expectIdentOrKeyword();
1651
+ expr = { kind: "MemberExpr", span: AST.spanFrom(expr.span, this.prevSpan()), target: expr, member };
1652
+ break;
1653
+ }
1654
+ case "QuestionDot" /* TokenKind.QuestionDot */: {
1655
+ this.advance();
1656
+ // x?.() — safe invocation of a nullable function value
1657
+ if (this.check("LParen" /* TokenKind.LParen */)) {
1658
+ const args = this.parseCallArgs();
1659
+ let trailingLambda = null;
1660
+ const savedPosAfterArgs = this.pos;
1661
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
1662
+ this.advance();
1663
+ if (this.check("LBrace" /* TokenKind.LBrace */)) {
1664
+ trailingLambda = this.parseLambda();
1665
+ }
1666
+ else {
1667
+ this.pos = savedPosAfterArgs;
1668
+ }
1669
+ expr = {
1670
+ kind: "SafeCallExpr",
1671
+ span: AST.spanFrom(expr.span, this.prevSpan()),
1672
+ callee: expr,
1673
+ args,
1674
+ trailingLambda,
1675
+ };
1676
+ break;
1677
+ }
1678
+ const member = this.expectIdentOrKeyword();
1679
+ expr = { kind: "SafeMemberExpr", span: AST.spanFrom(expr.span, this.prevSpan()), target: expr, member };
1680
+ break;
1681
+ }
1682
+ case "BangBang" /* TokenKind.BangBang */: {
1683
+ const span = this.advance().span;
1684
+ expr = { kind: "NotNullExpr", span: AST.spanFrom(expr.span, span), expr };
1685
+ break;
1686
+ }
1687
+ case "LParen" /* TokenKind.LParen */: {
1688
+ const args = this.parseCallArgs();
1689
+ let trailingLambda = null;
1690
+ // Eat ASI-inserted semicolons between ) and { so Compose-style
1691
+ // `foo(args) \n { ... }` works (like Kotlin trailing lambdas).
1692
+ // Restore position if no { follows so statement boundaries are preserved.
1693
+ const savedPosAfterArgs = this.pos;
1694
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
1695
+ this.advance();
1696
+ if (this.check("LBrace" /* TokenKind.LBrace */)) {
1697
+ trailingLambda = this.parseLambda();
1698
+ }
1699
+ else {
1700
+ this.pos = savedPosAfterArgs;
1701
+ }
1702
+ expr = {
1703
+ kind: "CallExpr",
1704
+ span: AST.spanFrom(expr.span, this.prevSpan()),
1705
+ callee: expr,
1706
+ typeArgs: [],
1707
+ args,
1708
+ trailingLambda,
1709
+ };
1710
+ break;
1711
+ }
1712
+ case "Lt" /* TokenKind.Lt */: {
1713
+ if (this.current().span.startOffset > 0 && this.source[this.current().span.startOffset - 1] === "\n")
1714
+ break loop;
1715
+ // Only attempt type-arg parsing when the next token can actually start a type
1716
+ // argument (* or `in` or an Identifier). All other tokens should be treated as
1717
+ // the < comparison operator.
1718
+ const nextKind = this.tokens[this.pos + 1]?.kind;
1719
+ if (nextKind !== "Star" /* TokenKind.Star */ && nextKind !== "KwIn" /* TokenKind.KwIn */ && nextKind !== "Identifier" /* TokenKind.Identifier */)
1720
+ break loop;
1721
+ const saved = this.pos;
1722
+ const diagCheckpoint = this.diag.checkpoint();
1723
+ try {
1724
+ const typeArgs = this.parseTypeArgs();
1725
+ if (this.check("LParen" /* TokenKind.LParen */)) {
1726
+ const args = this.parseCallArgs();
1727
+ let trailingLambda = null;
1728
+ if (this.check("LBrace" /* TokenKind.LBrace */)) {
1729
+ trailingLambda = this.parseLambda();
1730
+ }
1731
+ expr = {
1732
+ kind: "CallExpr",
1733
+ span: AST.spanFrom(expr.span, this.prevSpan()),
1734
+ callee: expr,
1735
+ typeArgs,
1736
+ args,
1737
+ trailingLambda,
1738
+ };
1739
+ break;
1740
+ }
1741
+ }
1742
+ catch { /**/ }
1743
+ this.pos = saved;
1744
+ this.diag.rollback(diagCheckpoint);
1745
+ break loop;
1746
+ }
1747
+ case "LBracket" /* TokenKind.LBracket */: {
1748
+ this.advance();
1749
+ const index = this.parseExpr();
1750
+ const end = this.expect("RBracket" /* TokenKind.RBracket */).span;
1751
+ expr = { kind: "IndexExpr", span: AST.spanFrom(expr.span, end), target: expr, index };
1752
+ break;
1753
+ }
1754
+ case "PlusPlus" /* TokenKind.PlusPlus */:
1755
+ case "MinusMinus" /* TokenKind.MinusMinus */: {
1756
+ const op = this.check("PlusPlus" /* TokenKind.PlusPlus */) ? "++" : "--";
1757
+ const end = this.advance().span;
1758
+ expr = {
1759
+ kind: "IncrDecrExpr",
1760
+ span: AST.spanFrom(expr.span, end),
1761
+ op,
1762
+ target: expr,
1763
+ prefix: false,
1764
+ };
1765
+ break;
1766
+ }
1767
+ case "LBrace" /* TokenKind.LBrace */: {
1768
+ if (!this.isStartOfNewStatement()) {
1769
+ const lambda = this.parseLambda();
1770
+ expr = {
1771
+ kind: "CallExpr",
1772
+ span: AST.spanFrom(expr.span, lambda.span),
1773
+ callee: expr,
1774
+ typeArgs: [],
1775
+ args: [],
1776
+ trailingLambda: lambda,
1777
+ };
1778
+ }
1779
+ else {
1780
+ break loop;
1781
+ }
1782
+ break;
1783
+ }
1784
+ default:
1785
+ break loop;
1786
+ }
1787
+ }
1788
+ return expr;
1789
+ }
1790
+ parsePrimary() {
1791
+ const tok = this.current();
1792
+ switch (tok.kind) {
1793
+ case "IntLiteral" /* TokenKind.IntLiteral */:
1794
+ this.advance();
1795
+ return { kind: "IntLiteralExpr", span: tok.span, value: tok.value };
1796
+ case "LongLiteral" /* TokenKind.LongLiteral */:
1797
+ this.advance();
1798
+ return { kind: "LongLiteralExpr", span: tok.span, value: tok.value };
1799
+ case "FloatLiteral" /* TokenKind.FloatLiteral */:
1800
+ this.advance();
1801
+ return { kind: "FloatLiteralExpr", span: tok.span, value: tok.value };
1802
+ case "DoubleLiteral" /* TokenKind.DoubleLiteral */:
1803
+ this.advance();
1804
+ return { kind: "DoubleLiteralExpr", span: tok.span, value: tok.value };
1805
+ case "BooleanLiteral" /* TokenKind.BooleanLiteral */:
1806
+ this.advance();
1807
+ return { kind: "BooleanLiteralExpr", span: tok.span, value: tok.value };
1808
+ case "NullLiteral" /* TokenKind.NullLiteral */:
1809
+ this.advance();
1810
+ return { kind: "NullLiteralExpr", span: tok.span };
1811
+ case "StringLiteral" /* TokenKind.StringLiteral */:
1812
+ return this.parseStringLiteralExpr();
1813
+ case "RawStringLiteral" /* TokenKind.RawStringLiteral */: {
1814
+ this.advance();
1815
+ return { kind: "StringLiteralExpr", span: tok.span, value: tok.value, raw: true };
1816
+ }
1817
+ case "Identifier" /* TokenKind.Identifier */:
1818
+ this.advance();
1819
+ return { kind: "NameExpr", span: tok.span, name: tok.text };
1820
+ case "KwThis" /* TokenKind.KwThis */: {
1821
+ this.advance();
1822
+ let label = null;
1823
+ if (this.check("At" /* TokenKind.At */)) {
1824
+ this.advance();
1825
+ label = this.expectIdentifier();
1826
+ }
1827
+ return { kind: "ThisExpr", span: tok.span, label };
1828
+ }
1829
+ case "KwSuper" /* TokenKind.KwSuper */: {
1830
+ this.advance();
1831
+ let label = null;
1832
+ if (this.check("At" /* TokenKind.At */)) {
1833
+ this.advance();
1834
+ label = this.expectIdentifier();
1835
+ }
1836
+ return { kind: "SuperExpr", span: tok.span, label };
1837
+ }
1838
+ case "KwIf" /* TokenKind.KwIf */:
1839
+ return this.parseIfExpr();
1840
+ case "KwWhen" /* TokenKind.KwWhen */:
1841
+ return this.parseWhenExpr();
1842
+ case "KwTry" /* TokenKind.KwTry */:
1843
+ return this.parseTryCatchExpr();
1844
+ case "KwLaunch" /* TokenKind.KwLaunch */:
1845
+ return this.parseLaunchExpr();
1846
+ case "KwAsync" /* TokenKind.KwAsync */:
1847
+ return this.parseAsyncExpr();
1848
+ case "KwReturn" /* TokenKind.KwReturn */: {
1849
+ this.advance();
1850
+ let label = null;
1851
+ if (this.check("At" /* TokenKind.At */)) {
1852
+ this.advance();
1853
+ label = this.expectIdentifier();
1854
+ }
1855
+ let value = null;
1856
+ if (!this.check("Semicolon" /* TokenKind.Semicolon */) && !this.check("RBrace" /* TokenKind.RBrace */)) {
1857
+ value = this.parseExpr();
1858
+ }
1859
+ return { kind: "ReturnExpr", span: AST.spanFrom(tok.span, this.prevSpan()), label, value };
1860
+ }
1861
+ case "KwBreak" /* TokenKind.KwBreak */: {
1862
+ this.advance();
1863
+ let label = null;
1864
+ if (this.check("At" /* TokenKind.At */)) {
1865
+ this.advance();
1866
+ label = this.expectIdentifier();
1867
+ }
1868
+ return { kind: "BreakExpr", span: tok.span, label };
1869
+ }
1870
+ case "KwContinue" /* TokenKind.KwContinue */: {
1871
+ this.advance();
1872
+ let label = null;
1873
+ if (this.check("At" /* TokenKind.At */)) {
1874
+ this.advance();
1875
+ label = this.expectIdentifier();
1876
+ }
1877
+ return { kind: "ContinueExpr", span: tok.span, label };
1878
+ }
1879
+ case "LParen" /* TokenKind.LParen */: {
1880
+ // Check if this is an arrow function: (params) => body
1881
+ if (this.looksLikeArrowFunction()) {
1882
+ return this.parseArrowFunction();
1883
+ }
1884
+ this.advance();
1885
+ const expr = this.parseExpr();
1886
+ this.expect("RParen" /* TokenKind.RParen */);
1887
+ return { kind: "ParenExpr", span: AST.spanFrom(tok.span, this.prevSpan()), expr };
1888
+ }
1889
+ case "LBrace" /* TokenKind.LBrace */:
1890
+ return this.parseLambda();
1891
+ case "KwObject" /* TokenKind.KwObject */:
1892
+ return this.parseObjectExpr();
1893
+ case "Lt" /* TokenKind.Lt */: {
1894
+ // JSX: <TagName ...> or <TagName ... />
1895
+ // Only treat as JSX if immediately followed by an identifier (tag name)
1896
+ if (this.tokens[this.pos + 1]?.kind === "Identifier" /* TokenKind.Identifier */) {
1897
+ return this.parseJsxElement();
1898
+ }
1899
+ // fall through to error
1900
+ this.diag.error(tok.span, diagnostics_js_1.E_EXPECTED_EXPRESSION, `Expected an expression, got '${tok.text}'`);
1901
+ this.advance();
1902
+ return { kind: "NullLiteralExpr", span: tok.span };
1903
+ }
1904
+ // listOf(...), setOf(...), mapOf(...) are stdlib calls, handled normally
1905
+ default: {
1906
+ this.diag.error(tok.span, diagnostics_js_1.E_EXPECTED_EXPRESSION, `Expected an expression, got '${tok.text}'`);
1907
+ this.advance();
1908
+ // Return a placeholder to allow parser to continue
1909
+ return { kind: "NullLiteralExpr", span: tok.span };
1910
+ }
1911
+ }
1912
+ }
1913
+ // ── JSX ────────────────────────────────────────────────────────────────────
1914
+ parseJsxElement() {
1915
+ const start = this.expect("Lt" /* TokenKind.Lt */).span;
1916
+ // Tag name (may contain dots: Router.Link)
1917
+ let tag = this.current().text;
1918
+ this.advance();
1919
+ while (this.check("Dot" /* TokenKind.Dot */)) {
1920
+ this.advance();
1921
+ tag += "." + this.current().text;
1922
+ this.advance();
1923
+ }
1924
+ // Attributes
1925
+ const attrs = [];
1926
+ while (!this.check("Gt" /* TokenKind.Gt */) && !this.check("Slash" /* TokenKind.Slash */) && !this.check("EOF" /* TokenKind.EOF */)) {
1927
+ // Skip synthetic semicolons (ASI can insert them in multi-line JSX)
1928
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
1929
+ this.advance();
1930
+ if (this.check("Gt" /* TokenKind.Gt */) || this.check("Slash" /* TokenKind.Slash */))
1931
+ break;
1932
+ const attrStart = this.current().span;
1933
+ const attrName = this.current().text; // accept keywords (class, for) as attr names
1934
+ this.advance();
1935
+ if (!this.check("Eq" /* TokenKind.Eq */)) {
1936
+ attrs.push({ span: attrStart, name: attrName, value: null });
1937
+ continue;
1938
+ }
1939
+ this.advance(); // consume "="
1940
+ if (this.check("StringLiteral" /* TokenKind.StringLiteral */)) {
1941
+ const expr = this.parseStringLiteralExpr();
1942
+ attrs.push({ span: AST.spanFrom(attrStart, expr.span), name: attrName, value: expr });
1943
+ }
1944
+ else if (this.check("LBrace" /* TokenKind.LBrace */)) {
1945
+ this.advance();
1946
+ const expr = this.parseExpr();
1947
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
1948
+ this.advance();
1949
+ this.expect("RBrace" /* TokenKind.RBrace */);
1950
+ attrs.push({ span: AST.spanFrom(attrStart, this.prevSpan()), name: attrName, value: expr });
1951
+ }
1952
+ }
1953
+ // Self-closing: />
1954
+ if (this.check("Slash" /* TokenKind.Slash */)) {
1955
+ this.advance();
1956
+ this.expect("Gt" /* TokenKind.Gt */);
1957
+ return { kind: "JsxElement", span: AST.spanFrom(start, this.prevSpan()), tag, attrs, children: [] };
1958
+ }
1959
+ this.expect("Gt" /* TokenKind.Gt */); // consume opening ">"
1960
+ const children = [];
1961
+ while (!this.check("EOF" /* TokenKind.EOF */)) {
1962
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
1963
+ this.advance();
1964
+ // Closing tag: </
1965
+ if (this.check("Lt" /* TokenKind.Lt */) && this.checkNext("Slash" /* TokenKind.Slash */))
1966
+ break;
1967
+ // Expression child: { expr }
1968
+ if (this.check("LBrace" /* TokenKind.LBrace */)) {
1969
+ const childStart = this.advance().span;
1970
+ const expr = this.parseExpr();
1971
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
1972
+ this.advance();
1973
+ this.expect("RBrace" /* TokenKind.RBrace */);
1974
+ children.push({ kind: "JsxExprChild", span: AST.spanFrom(childStart, this.prevSpan()), expr });
1975
+ continue;
1976
+ }
1977
+ // Nested element: < Identifier
1978
+ if (this.check("Lt" /* TokenKind.Lt */) && this.tokens[this.pos + 1]?.kind === "Identifier" /* TokenKind.Identifier */) {
1979
+ children.push(this.parseJsxElement());
1980
+ continue;
1981
+ }
1982
+ // Text content: collect until { or <, extracting raw source text
1983
+ const textStartOffset = this.current().span.startOffset;
1984
+ const textStartSpan = this.current().span;
1985
+ while (!this.check("EOF" /* TokenKind.EOF */) &&
1986
+ !this.check("LBrace" /* TokenKind.LBrace */) &&
1987
+ !this.check("Lt" /* TokenKind.Lt */) &&
1988
+ !this.check("Semicolon" /* TokenKind.Semicolon */)) {
1989
+ this.advance();
1990
+ }
1991
+ const textEndOffset = this.current().span.startOffset;
1992
+ // Extract raw source and normalize whitespace
1993
+ const rawText = this.source
1994
+ ? this.source.slice(textStartOffset, textEndOffset)
1995
+ : "";
1996
+ const text = rawText.replace(/\s+/g, " ").trim();
1997
+ if (text) {
1998
+ children.push({ kind: "JsxTextChild", span: AST.spanFrom(textStartSpan, this.prevSpan()), text });
1999
+ }
2000
+ }
2001
+ // Consume closing tag: </tagname>
2002
+ this.expect("Lt" /* TokenKind.Lt */);
2003
+ this.expect("Slash" /* TokenKind.Slash */);
2004
+ while (!this.check("Gt" /* TokenKind.Gt */) && !this.check("EOF" /* TokenKind.EOF */))
2005
+ this.advance();
2006
+ const end = this.expect("Gt" /* TokenKind.Gt */).span;
2007
+ return { kind: "JsxElement", span: AST.spanFrom(start, end), tag, attrs, children };
2008
+ }
2009
+ parseStringLiteralExpr() {
2010
+ const tok = this.current();
2011
+ this.advance();
2012
+ const raw = tok.value;
2013
+ // Check if contains ${...} pattern — if so, parse as template
2014
+ if (raw.includes("${")) {
2015
+ const parts = [];
2016
+ let i = 0;
2017
+ let cur = "";
2018
+ while (i < raw.length) {
2019
+ if (raw[i] === "$" && raw[i + 1] === "{") {
2020
+ if (cur)
2021
+ parts.push({ kind: "LiteralPart", value: cur });
2022
+ cur = "";
2023
+ i += 2;
2024
+ let depth = 1;
2025
+ let exprSrc = "";
2026
+ while (i < raw.length && depth > 0) {
2027
+ if (raw[i] === "{")
2028
+ depth++;
2029
+ else if (raw[i] === "}") {
2030
+ depth--;
2031
+ if (depth === 0) {
2032
+ i++;
2033
+ break;
2034
+ }
2035
+ }
2036
+ exprSrc += raw[i++];
2037
+ }
2038
+ // Re-lex and re-parse the embedded expression
2039
+ const innerDiag = new diagnostics_js_1.DiagnosticBag();
2040
+ const innerTokens = new lexer_js_1.Lexer(exprSrc, tok.span.file, innerDiag).tokenize();
2041
+ const innerParser = new Parser(innerTokens, tok.span.file, innerDiag);
2042
+ const innerExpr = innerParser.parseExpr();
2043
+ this.diag.merge(innerDiag);
2044
+ parts.push({ kind: "ExprPart", expr: innerExpr });
2045
+ }
2046
+ else {
2047
+ cur += raw[i++];
2048
+ }
2049
+ }
2050
+ if (cur)
2051
+ parts.push({ kind: "LiteralPart", value: cur });
2052
+ return { kind: "StringTemplateExpr", span: tok.span, parts };
2053
+ }
2054
+ return { kind: "StringLiteralExpr", span: tok.span, value: raw, raw: false };
2055
+ }
2056
+ parseIfExpr() {
2057
+ const start = this.expect("KwIf" /* TokenKind.KwIf */).span;
2058
+ this.expect("LParen" /* TokenKind.LParen */);
2059
+ const condition = this.parseExpr();
2060
+ this.expect("RParen" /* TokenKind.RParen */);
2061
+ // Skip ASI semicolons inserted between `if (cond)` and the then-branch
2062
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
2063
+ this.advance();
2064
+ const then = this.check("LBrace" /* TokenKind.LBrace */)
2065
+ ? this.parseBlock()
2066
+ : this.parseExpr();
2067
+ // Skip synthetic semicolons inserted by ASI between then-branch and else
2068
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
2069
+ this.advance();
2070
+ this.expect("KwElse" /* TokenKind.KwElse */);
2071
+ // Skip ASI semicolons between else and the else-branch
2072
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
2073
+ this.advance();
2074
+ const elseExpr = this.check("KwIf" /* TokenKind.KwIf */)
2075
+ ? this.parseIfExpr()
2076
+ : this.check("LBrace" /* TokenKind.LBrace */)
2077
+ ? this.parseBlock()
2078
+ : this.parseExpr();
2079
+ return { kind: "IfExpr", span: AST.spanFrom(start, this.prevSpan()), condition, then, else: elseExpr };
2080
+ }
2081
+ parseWhenExpr() {
2082
+ const start = this.expect("KwWhen" /* TokenKind.KwWhen */).span;
2083
+ let subject = null;
2084
+ if (this.check("LParen" /* TokenKind.LParen */)) {
2085
+ const subStart = this.advance().span;
2086
+ let binding = null;
2087
+ if (this.check("KwVal" /* TokenKind.KwVal */) && this.peekKindAt(2) === "Eq" /* TokenKind.Eq */) {
2088
+ this.advance();
2089
+ binding = this.expectIdentifier();
2090
+ this.expect("Eq" /* TokenKind.Eq */);
2091
+ }
2092
+ const expr = this.parseExpr();
2093
+ this.expect("RParen" /* TokenKind.RParen */);
2094
+ subject = { span: AST.spanFrom(subStart, this.prevSpan()), binding, expr };
2095
+ }
2096
+ this.expect("LBrace" /* TokenKind.LBrace */);
2097
+ const branches = [];
2098
+ while (!this.check("RBrace" /* TokenKind.RBrace */) && !this.check("EOF" /* TokenKind.EOF */)) {
2099
+ branches.push(this.parseWhenBranch());
2100
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
2101
+ this.advance();
2102
+ }
2103
+ const end = this.expect("RBrace" /* TokenKind.RBrace */).span;
2104
+ return { kind: "WhenExpr", span: AST.spanFrom(start, end), subject, branches };
2105
+ }
2106
+ parseTryCatchExpr() {
2107
+ const start = this.expect("KwTry" /* TokenKind.KwTry */).span;
2108
+ const body = this.parseBlock();
2109
+ const catches = [];
2110
+ while (this.check("KwCatch" /* TokenKind.KwCatch */))
2111
+ catches.push(this.parseCatchClause());
2112
+ let fin = null;
2113
+ if (this.check("KwFinally" /* TokenKind.KwFinally */)) {
2114
+ this.advance();
2115
+ fin = this.parseBlock();
2116
+ }
2117
+ return { kind: "TryCatchExpr", span: AST.spanFrom(start, this.prevSpan()), body, catches, finally: fin };
2118
+ }
2119
+ parseLaunchExpr() {
2120
+ const start = this.expect("KwLaunch" /* TokenKind.KwLaunch */).span;
2121
+ let context = null;
2122
+ if (this.check("LParen" /* TokenKind.LParen */)) {
2123
+ this.advance();
2124
+ context = this.parseExpr();
2125
+ this.expect("RParen" /* TokenKind.RParen */);
2126
+ }
2127
+ const body = this.parseBlock();
2128
+ return { kind: "LaunchExpr", span: AST.spanFrom(start, body.span), context, body };
2129
+ }
2130
+ parseAsyncExpr() {
2131
+ const start = this.expect("KwAsync" /* TokenKind.KwAsync */).span;
2132
+ let context = null;
2133
+ if (this.check("LParen" /* TokenKind.LParen */)) {
2134
+ this.advance();
2135
+ context = this.parseExpr();
2136
+ this.expect("RParen" /* TokenKind.RParen */);
2137
+ }
2138
+ const body = this.parseBlock();
2139
+ return { kind: "AsyncExpr", span: AST.spanFrom(start, body.span), context, body };
2140
+ }
2141
+ parseObjectExpr() {
2142
+ const start = this.expect("KwObject" /* TokenKind.KwObject */).span;
2143
+ const superTypes = this.parseSuperTypes();
2144
+ const body = this.parseClassBody();
2145
+ return { kind: "ObjectExpr", span: AST.spanFrom(start, body.span), superTypes, body };
2146
+ }
2147
+ parseLambda() {
2148
+ const start = this.expect("LBrace" /* TokenKind.LBrace */).span;
2149
+ const params = [];
2150
+ // Check if there are explicit params before `->` or `=>`
2151
+ const hasParams = this.detectLambdaParams();
2152
+ if (hasParams) {
2153
+ // Parse params — stop at either -> or =>
2154
+ while (!this.check("Arrow" /* TokenKind.Arrow */) && !this.check("FatArrow" /* TokenKind.FatArrow */) && !this.check("EOF" /* TokenKind.EOF */)) {
2155
+ const pSpan = this.current().span;
2156
+ let name = null;
2157
+ let type = null;
2158
+ if (this.check("Underscore" /* TokenKind.Underscore */)) {
2159
+ this.advance();
2160
+ }
2161
+ else {
2162
+ name = this.expectIdentifier();
2163
+ if (this.check("Colon" /* TokenKind.Colon */)) {
2164
+ this.advance();
2165
+ type = this.parseTypeRef();
2166
+ }
2167
+ }
2168
+ params.push({ span: AST.spanFrom(pSpan, this.prevSpan()), name, type });
2169
+ if (!this.check("Arrow" /* TokenKind.Arrow */) && !this.check("FatArrow" /* TokenKind.FatArrow */))
2170
+ this.expect("Comma" /* TokenKind.Comma */);
2171
+ }
2172
+ // Consume either -> or =>
2173
+ if (this.check("FatArrow" /* TokenKind.FatArrow */))
2174
+ this.advance();
2175
+ else
2176
+ this.expect("Arrow" /* TokenKind.Arrow */);
2177
+ }
2178
+ const body = [];
2179
+ while (!this.check("RBrace" /* TokenKind.RBrace */) && !this.check("EOF" /* TokenKind.EOF */)) {
2180
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
2181
+ this.advance();
2182
+ if (this.check("RBrace" /* TokenKind.RBrace */))
2183
+ break;
2184
+ const s = this.parseStmt();
2185
+ if (s)
2186
+ body.push(s);
2187
+ }
2188
+ const end = this.expect("RBrace" /* TokenKind.RBrace */).span;
2189
+ return { kind: "LambdaExpr", span: AST.spanFrom(start, end), params, returnType: null, body };
2190
+ }
2191
+ /** Heuristically determine if this lambda has explicit parameters before `->` or `=>` */
2192
+ detectLambdaParams() {
2193
+ let i = this.pos;
2194
+ let depth = 0;
2195
+ while (i < this.tokens.length) {
2196
+ const k = this.tokens[i].kind;
2197
+ if (k === "LBrace" /* TokenKind.LBrace */ || k === "LParen" /* TokenKind.LParen */)
2198
+ depth++;
2199
+ else if (k === "RBrace" /* TokenKind.RBrace */ || k === "RParen" /* TokenKind.RParen */) {
2200
+ if (depth === 0)
2201
+ return false;
2202
+ depth--;
2203
+ }
2204
+ else if ((k === "Arrow" /* TokenKind.Arrow */ || k === "FatArrow" /* TokenKind.FatArrow */) && depth === 0) {
2205
+ return true;
2206
+ }
2207
+ else if (k === "Semicolon" /* TokenKind.Semicolon */ && depth === 0) {
2208
+ return false;
2209
+ }
2210
+ i++;
2211
+ }
2212
+ return false;
2213
+ }
2214
+ /** Heuristic lookahead: return true if we are at `(params) =>` (arrow function). */
2215
+ looksLikeArrowFunction() {
2216
+ let i = this.pos + 1; // skip the opening `(`
2217
+ let depth = 1;
2218
+ while (i < this.tokens.length) {
2219
+ const k = this.tokens[i].kind;
2220
+ if (k === "LParen" /* TokenKind.LParen */)
2221
+ depth++;
2222
+ else if (k === "RParen" /* TokenKind.RParen */) {
2223
+ depth--;
2224
+ if (depth === 0) {
2225
+ return this.tokens[i + 1]?.kind === "FatArrow" /* TokenKind.FatArrow */;
2226
+ }
2227
+ }
2228
+ else if (k === "EOF" /* TokenKind.EOF */)
2229
+ return false;
2230
+ i++;
2231
+ }
2232
+ return false;
2233
+ }
2234
+ /** Parse `(params) => body` as a LambdaExpr (JS-style arrow function). */
2235
+ parseArrowFunction() {
2236
+ const start = this.expect("LParen" /* TokenKind.LParen */).span;
2237
+ const params = [];
2238
+ while (!this.check("RParen" /* TokenKind.RParen */) && !this.check("EOF" /* TokenKind.EOF */)) {
2239
+ const pSpan = this.current().span;
2240
+ let name = null;
2241
+ let type = null;
2242
+ if (this.check("Underscore" /* TokenKind.Underscore */)) {
2243
+ this.advance();
2244
+ }
2245
+ else {
2246
+ name = this.expectIdentifier();
2247
+ if (this.check("Colon" /* TokenKind.Colon */)) {
2248
+ this.advance();
2249
+ type = this.parseTypeRef();
2250
+ }
2251
+ }
2252
+ params.push({ span: AST.spanFrom(pSpan, this.prevSpan()), name, type });
2253
+ if (!this.check("RParen" /* TokenKind.RParen */)) {
2254
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
2255
+ this.advance();
2256
+ if (!this.check("RParen" /* TokenKind.RParen */))
2257
+ this.expect("Comma" /* TokenKind.Comma */);
2258
+ }
2259
+ }
2260
+ this.expect("RParen" /* TokenKind.RParen */);
2261
+ this.expect("FatArrow" /* TokenKind.FatArrow */);
2262
+ // Body: block `{ ... }` or single expression
2263
+ const body = [];
2264
+ if (this.check("LBrace" /* TokenKind.LBrace */)) {
2265
+ const block = this.parseBlock();
2266
+ return { kind: "LambdaExpr", span: AST.spanFrom(start, block.span), params, returnType: null, body: block.statements };
2267
+ }
2268
+ const expr = this.parseExpr();
2269
+ body.push({ kind: "ExprStmt", span: expr.span, expr });
2270
+ return { kind: "LambdaExpr", span: AST.spanFrom(start, expr.span), params, returnType: null, body };
2271
+ }
2272
+ // ── Call arguments ─────────────────────────────────────────────────────────
2273
+ parseCallArgs() {
2274
+ this.expect("LParen" /* TokenKind.LParen */);
2275
+ const args = [];
2276
+ while (!this.check("RParen" /* TokenKind.RParen */) && !this.check("EOF" /* TokenKind.EOF */)) {
2277
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
2278
+ this.advance();
2279
+ if (this.check("RParen" /* TokenKind.RParen */) || this.check("EOF" /* TokenKind.EOF */))
2280
+ break;
2281
+ const prevPos = this.pos;
2282
+ args.push(this.parseCallArg());
2283
+ if (this.pos === prevPos)
2284
+ break;
2285
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
2286
+ this.advance();
2287
+ if (!this.check("RParen" /* TokenKind.RParen */))
2288
+ this.expect("Comma" /* TokenKind.Comma */);
2289
+ }
2290
+ this.expect("RParen" /* TokenKind.RParen */);
2291
+ return args;
2292
+ }
2293
+ parseCallArg() {
2294
+ const start = this.current().span;
2295
+ let name = null;
2296
+ let spread = false;
2297
+ if (this.check("Star" /* TokenKind.Star */)) {
2298
+ spread = true;
2299
+ this.advance();
2300
+ }
2301
+ if (this.check("Identifier" /* TokenKind.Identifier */) && this.checkNext("Eq" /* TokenKind.Eq */)) {
2302
+ name = this.advance().text;
2303
+ this.advance(); // =
2304
+ }
2305
+ const value = this.parseExpr();
2306
+ return { span: AST.spanFrom(start, value.span), name, spread, value };
2307
+ }
2308
+ // ── Utilities ──────────────────────────────────────────────────────────────
2309
+ current() {
2310
+ return this.tokens[this.pos] ?? this.tokens[this.tokens.length - 1];
2311
+ }
2312
+ advance() {
2313
+ const tok = this.current();
2314
+ this.pos++;
2315
+ return tok;
2316
+ }
2317
+ prevSpan() {
2318
+ return this.tokens[Math.max(0, this.pos - 1)].span;
2319
+ }
2320
+ check(kind) {
2321
+ return this.current().kind === kind;
2322
+ }
2323
+ checkNext(kind) {
2324
+ return (this.tokens[this.pos + 1]?.kind ?? "EOF" /* TokenKind.EOF */) === kind;
2325
+ }
2326
+ checkIdent(name) {
2327
+ const tok = this.current();
2328
+ return tok.kind === "Identifier" /* TokenKind.Identifier */ && tok.text === name;
2329
+ }
2330
+ peekKindAt(offset) {
2331
+ return this.tokens[this.pos + offset]?.kind ?? "EOF" /* TokenKind.EOF */;
2332
+ }
2333
+ expect(kind) {
2334
+ if (this.check(kind))
2335
+ return this.advance();
2336
+ const tok = this.current();
2337
+ this.diag.error(tok.span, diagnostics_js_1.E_EXPECTED_TOKEN, `Expected '${kind}' but got '${tok.text}' (${tok.kind})`);
2338
+ // Return a fake token so parsing can continue
2339
+ return { kind, span: tok.span, text: "" };
2340
+ }
2341
+ expectIdentifier() {
2342
+ const tok = this.current();
2343
+ if (tok.kind === "Identifier" /* TokenKind.Identifier */) {
2344
+ this.advance();
2345
+ return tok.text;
2346
+ }
2347
+ this.diag.error(tok.span, diagnostics_js_1.E_EXPECTED_TOKEN, `Expected identifier, got '${tok.text}'`);
2348
+ return "<error>";
2349
+ }
2350
+ expectIdentOrKeyword() {
2351
+ const tok = this.current();
2352
+ if (tok.kind === "Identifier" /* TokenKind.Identifier */ || (0, helpers_js_1.isIdentifierText)(tok.text)) {
2353
+ this.advance();
2354
+ return tok.text;
2355
+ }
2356
+ this.diag.error(tok.span, diagnostics_js_1.E_EXPECTED_TOKEN, `Expected identifier, got '${tok.text}'`);
2357
+ return "<error>";
2358
+ }
2359
+ eatSemicolon() {
2360
+ while (this.check("Semicolon" /* TokenKind.Semicolon */))
2361
+ this.advance();
2362
+ }
2363
+ parseDottedName() {
2364
+ const parts = [this.expectIdentifier()];
2365
+ while (this.check("Dot" /* TokenKind.Dot */)) {
2366
+ this.advance();
2367
+ parts.push(this.expectIdentifier());
2368
+ }
2369
+ return parts;
2370
+ }
2371
+ /**
2372
+ * A trailing `{` is the start of a new statement (not a trailing lambda)
2373
+ * when preceded by a newline-induced semicolon.
2374
+ */
2375
+ isStartOfNewStatement() {
2376
+ const prev = this.tokens[this.pos - 1];
2377
+ return prev?.kind === "Semicolon" /* TokenKind.Semicolon */ && prev.text === "\n";
2378
+ }
2379
+ }
2380
+ exports.Parser = Parser;
2381
+ // ---------------------------------------------------------------------------
2382
+ // Public helper
2383
+ // ---------------------------------------------------------------------------
2384
+ function parse(tokens, file, diag, source = "") {
2385
+ return new Parser(tokens, file, diag, source).parseProgram();
2386
+ }
2387
+ //# sourceMappingURL=index.js.map