@jalvin/compiler 1.0.0

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