pearc 0.1.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,1047 @@
1
+ "use strict";
2
+ // Pear Language Parser
3
+ // Recursive descent parser producing an AST
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.Parser = exports.PEAR_TYPE_MAP = void 0;
6
+ exports.parse = parse;
7
+ const lexer_1 = require("./lexer");
8
+ // ─── Parser ──────────────────────────────────────────────────────────────────
9
+ const TYPE_KEYWORDS = new Set([
10
+ lexer_1.TokenType.I8, lexer_1.TokenType.I16, lexer_1.TokenType.I32, lexer_1.TokenType.I64,
11
+ lexer_1.TokenType.U8, lexer_1.TokenType.U16, lexer_1.TokenType.U32, lexer_1.TokenType.U64,
12
+ lexer_1.TokenType.F32, lexer_1.TokenType.F64,
13
+ lexer_1.TokenType.V, lexer_1.TokenType.C, lexer_1.TokenType.B, lexer_1.TokenType.SZ,
14
+ ]);
15
+ const PEAR_TYPE_MAP = {
16
+ i8: 'int8_t',
17
+ i16: 'int16_t',
18
+ i32: 'int32_t',
19
+ i64: 'int64_t',
20
+ u8: 'uint8_t',
21
+ u16: 'uint16_t',
22
+ u32: 'uint32_t',
23
+ u64: 'uint64_t',
24
+ f32: 'float',
25
+ f64: 'double',
26
+ v: 'void',
27
+ c: 'char',
28
+ b: 'bool',
29
+ sz: 'size_t',
30
+ };
31
+ exports.PEAR_TYPE_MAP = PEAR_TYPE_MAP;
32
+ class Parser {
33
+ constructor(tokens) {
34
+ this.pos = 0;
35
+ // Filter newlines for the parser (they're only relevant for preprocessor which we handle specially)
36
+ this.tokens = tokens.filter(t => t.type !== lexer_1.TokenType.NEWLINE);
37
+ }
38
+ peek(offset = 0) {
39
+ const idx = this.pos + offset;
40
+ return this.tokens[idx] ?? { type: lexer_1.TokenType.EOF, value: '', line: 0, col: 0 };
41
+ }
42
+ advance() {
43
+ const tok = this.tokens[this.pos];
44
+ this.pos++;
45
+ return tok;
46
+ }
47
+ check(...types) {
48
+ return types.includes(this.peek().type);
49
+ }
50
+ eat(type) {
51
+ const tok = this.peek();
52
+ if (tok.type !== type) {
53
+ throw new Error(`Expected ${type} but got ${tok.type} ('${tok.value}') at line ${tok.line}:${tok.col}`);
54
+ }
55
+ return this.advance();
56
+ }
57
+ tryEat(...types) {
58
+ if (types.includes(this.peek().type))
59
+ return this.advance();
60
+ return null;
61
+ }
62
+ // Accept any token that can serve as an identifier name
63
+ // This handles cases like parameter named 'b', 'c', 'v', 'sz', etc.
64
+ eatIdent() {
65
+ const tok = this.peek();
66
+ // Allow IDENT plus any short keyword/type that might be used as a name
67
+ const identLike = new Set([
68
+ lexer_1.TokenType.IDENT,
69
+ lexer_1.TokenType.B, lexer_1.TokenType.C, lexer_1.TokenType.V, lexer_1.TokenType.SZ,
70
+ lexer_1.TokenType.I8, lexer_1.TokenType.I16, lexer_1.TokenType.I32, lexer_1.TokenType.I64,
71
+ lexer_1.TokenType.U8, lexer_1.TokenType.U16, lexer_1.TokenType.U32, lexer_1.TokenType.U64,
72
+ lexer_1.TokenType.F32, lexer_1.TokenType.F64,
73
+ // Also allow Pear keywords that might appear as identifiers in context
74
+ lexer_1.TokenType.IN, // 'in' can be a variable name
75
+ ]);
76
+ if (identLike.has(tok.type))
77
+ return this.advance();
78
+ throw new Error(`Expected identifier but got ${tok.type} ('${tok.value}') at line ${tok.line}:${tok.col}`);
79
+ }
80
+ // ─── Type Parsing ──────────────────────────────────────────────────────────
81
+ parseQualifiers() {
82
+ let isConst = false, isVolatile = false, isStatic = false, isExtern = false, isInline = false;
83
+ while (true) {
84
+ if (this.tryEat(lexer_1.TokenType.CN)) {
85
+ isConst = true;
86
+ continue;
87
+ }
88
+ if (this.tryEat(lexer_1.TokenType.VL)) {
89
+ isVolatile = true;
90
+ continue;
91
+ }
92
+ if (this.tryEat(lexer_1.TokenType.SC)) {
93
+ isStatic = true;
94
+ continue;
95
+ }
96
+ if (this.tryEat(lexer_1.TokenType.EX)) {
97
+ isExtern = true;
98
+ continue;
99
+ }
100
+ if (this.tryEat(lexer_1.TokenType.IN)) {
101
+ isInline = true;
102
+ continue;
103
+ }
104
+ break;
105
+ }
106
+ return { isConst, isVolatile, isStatic, isExtern, isInline };
107
+ }
108
+ parseType() {
109
+ const quals = this.parseQualifiers();
110
+ // Additional qualifiers after the first
111
+ let isConst = quals.isConst;
112
+ let isVolatile = quals.isVolatile;
113
+ // Pear supports *T syntax for pointer-to-T (prefix stars)
114
+ let prefixPointers = 0;
115
+ while (this.check(lexer_1.TokenType.STAR)) {
116
+ this.advance();
117
+ prefixPointers++;
118
+ }
119
+ let base = '';
120
+ if (TYPE_KEYWORDS.has(this.peek().type)) {
121
+ const tok = this.advance();
122
+ base = PEAR_TYPE_MAP[tok.value] ?? tok.value;
123
+ }
124
+ else if (this.check(lexer_1.TokenType.ST)) {
125
+ this.advance();
126
+ const name = this.peek().type === lexer_1.TokenType.IDENT ? this.advance().value : '';
127
+ base = 'struct ' + name;
128
+ }
129
+ else if (this.check(lexer_1.TokenType.UN)) {
130
+ this.advance();
131
+ const name = this.peek().type === lexer_1.TokenType.IDENT ? this.advance().value : '';
132
+ base = 'union ' + name;
133
+ }
134
+ else if (this.check(lexer_1.TokenType.EN)) {
135
+ this.advance();
136
+ const name = this.peek().type === lexer_1.TokenType.IDENT ? this.advance().value : '';
137
+ base = 'enum ' + name;
138
+ }
139
+ else if (this.check(lexer_1.TokenType.IDENT)) {
140
+ base = this.advance().value;
141
+ }
142
+ else if (prefixPointers > 0) {
143
+ // e.g. *v means void pointer — base type already consumed
144
+ throw new Error(`Expected base type after '*' at line ${this.peek().line}:${this.peek().col}, got '${this.peek().value}'`);
145
+ }
146
+ else {
147
+ throw new Error(`Expected type at line ${this.peek().line}:${this.peek().col}, got '${this.peek().value}'`);
148
+ }
149
+ // Count trailing pointer levels (C-style, after base type)
150
+ let pointers = prefixPointers;
151
+ while (this.check(lexer_1.TokenType.STAR)) {
152
+ this.advance();
153
+ pointers++;
154
+ // const after * applies to pointer
155
+ if (this.check(lexer_1.TokenType.CN)) {
156
+ this.advance();
157
+ isConst = true;
158
+ }
159
+ }
160
+ return {
161
+ base,
162
+ pointers,
163
+ isConst,
164
+ isVolatile,
165
+ isStatic: quals.isStatic,
166
+ isExtern: quals.isExtern,
167
+ isInline: quals.isInline,
168
+ arraySize: null,
169
+ };
170
+ }
171
+ // ─── Top Level ─────────────────────────────────────────────────────────────
172
+ parse() {
173
+ const body = [];
174
+ while (!this.check(lexer_1.TokenType.EOF)) {
175
+ const node = this.parseTopLevel();
176
+ if (node)
177
+ body.push(node);
178
+ }
179
+ return { kind: 'Program', body };
180
+ }
181
+ parseTopLevel() {
182
+ // Raw preprocessor (from # tokens)
183
+ if (this.check(lexer_1.TokenType.HASH)) {
184
+ return { kind: 'RawPreprocessor', text: this.advance().value };
185
+ }
186
+ // im <path> or im "path"
187
+ if (this.check(lexer_1.TokenType.IM)) {
188
+ return this.parseInclude();
189
+ }
190
+ // df NAME value
191
+ if (this.check(lexer_1.TokenType.DF)) {
192
+ return this.parseDefine();
193
+ }
194
+ // nd NAME (include guard)
195
+ if (this.check(lexer_1.TokenType.ND)) {
196
+ this.advance();
197
+ const name = this.eat(lexer_1.TokenType.IDENT).value;
198
+ return { kind: 'IncludeGuardStart', name };
199
+ }
200
+ // dn (#endif)
201
+ if (this.check(lexer_1.TokenType.DN)) {
202
+ this.advance();
203
+ return { kind: 'IncludeGuardEnd' };
204
+ }
205
+ // pr value
206
+ if (this.check(lexer_1.TokenType.PR)) {
207
+ this.advance();
208
+ let val = '';
209
+ while (!this.check(lexer_1.TokenType.EOF) && !this.check(lexer_1.TokenType.NEWLINE)) {
210
+ val += this.advance().value + ' ';
211
+ }
212
+ return { kind: 'PragmaDirective', value: val.trim() };
213
+ }
214
+ // tp (typedef)
215
+ if (this.check(lexer_1.TokenType.TP)) {
216
+ return this.parseTypedef();
217
+ }
218
+ // st / un / en at top level (struct/union/enum definition)
219
+ if (this.check(lexer_1.TokenType.ST)) {
220
+ return this.parseStructDecl(false);
221
+ }
222
+ if (this.check(lexer_1.TokenType.UN)) {
223
+ return this.parseUnionDecl(false);
224
+ }
225
+ if (this.check(lexer_1.TokenType.EN)) {
226
+ return this.parseEnumDecl(false);
227
+ }
228
+ // fn (function)
229
+ if (this.check(lexer_1.TokenType.FN)) {
230
+ return this.parseFunctionDecl();
231
+ }
232
+ // Variable declaration at top level (qualifiers + type + name : type)
233
+ // or just a type followed by name
234
+ return this.parseVarOrFuncDecl();
235
+ }
236
+ parseInclude() {
237
+ this.eat(lexer_1.TokenType.IM);
238
+ // path token was already consumed by the lexer and placed as STRING
239
+ const pathTok = this.eat(lexer_1.TokenType.STRING);
240
+ return { kind: 'IncludeDirective', path: pathTok.value };
241
+ }
242
+ parseDefine() {
243
+ this.eat(lexer_1.TokenType.DF);
244
+ const name = this.advance().value; // macro name
245
+ let params = null;
246
+ let body = '';
247
+ // function-like macro: df NAME(a,b) body
248
+ if (this.check(lexer_1.TokenType.LPAREN)) {
249
+ this.advance();
250
+ params = [];
251
+ while (!this.check(lexer_1.TokenType.RPAREN) && !this.check(lexer_1.TokenType.EOF)) {
252
+ if (this.check(lexer_1.TokenType.ELLIPSIS)) {
253
+ params.push('...');
254
+ this.advance();
255
+ }
256
+ else {
257
+ params.push(this.advance().value);
258
+ }
259
+ this.tryEat(lexer_1.TokenType.COMMA);
260
+ }
261
+ this.eat(lexer_1.TokenType.RPAREN);
262
+ }
263
+ // rest of line is body
264
+ // We collect tokens until EOF or newline (but newlines were filtered — collect until semicolon or next keyword that starts a declaration)
265
+ // Actually define body can be complex; we gather remaining tokens on the "logical line"
266
+ // Since newlines are filtered, we use a heuristic: collect everything that could be part of an expression
267
+ const bodyToks = [];
268
+ // Gather tokens that are likely part of the define body
269
+ // Stop at top-level declaration starters
270
+ while (!this.check(lexer_1.TokenType.EOF) &&
271
+ !this.check(lexer_1.TokenType.FN) &&
272
+ !this.check(lexer_1.TokenType.ST) &&
273
+ !this.check(lexer_1.TokenType.UN) &&
274
+ !this.check(lexer_1.TokenType.EN) &&
275
+ !this.check(lexer_1.TokenType.TP) &&
276
+ !this.check(lexer_1.TokenType.IM) &&
277
+ !this.check(lexer_1.TokenType.DF) &&
278
+ !this.check(lexer_1.TokenType.ND) &&
279
+ !this.check(lexer_1.TokenType.DN) &&
280
+ !this.check(lexer_1.TokenType.PR) &&
281
+ !this.check(lexer_1.TokenType.HASH)) {
282
+ bodyToks.push(this.advance());
283
+ }
284
+ // Smart join: don't put spaces around punctuation/operators
285
+ const noSpaceBefore = new Set(['(', ')', '[', ']', ',', ';', '.', '->', '++', '--']);
286
+ const noSpaceAfter = new Set(['(', '[', '.', '->']);
287
+ body = bodyToks.map((t, i) => {
288
+ const prev = bodyToks[i - 1];
289
+ if (!prev)
290
+ return t.value;
291
+ if (noSpaceBefore.has(t.value) || noSpaceAfter.has(prev.value))
292
+ return t.value;
293
+ // identifiers and numbers need a space before them
294
+ if (t.type === lexer_1.TokenType.IDENT || t.type === lexer_1.TokenType.NUMBER ||
295
+ t.type === lexer_1.TokenType.STRING)
296
+ return ' ' + t.value;
297
+ return t.value;
298
+ }).join('').trim();
299
+ return { kind: 'DefineDirective', name, params, body };
300
+ }
301
+ parseTypedef() {
302
+ this.eat(lexer_1.TokenType.TP);
303
+ if (this.check(lexer_1.TokenType.ST)) {
304
+ const st = this.parseStructDecl(true);
305
+ return st;
306
+ }
307
+ if (this.check(lexer_1.TokenType.UN)) {
308
+ const un = this.parseUnionDecl(true);
309
+ return un;
310
+ }
311
+ if (this.check(lexer_1.TokenType.EN)) {
312
+ const en = this.parseEnumDecl(true);
313
+ return en;
314
+ }
315
+ // tp i32 MyInt
316
+ const type = this.parseType();
317
+ const alias = this.eat(lexer_1.TokenType.IDENT).value;
318
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
319
+ return { kind: 'TypedefDecl', inner: { kind: 'VarDecl', name: '', type, init: null, qualifiers: [] }, alias };
320
+ }
321
+ parseStructDecl(isTypedef) {
322
+ this.eat(lexer_1.TokenType.ST);
323
+ let name = null;
324
+ if (this.check(lexer_1.TokenType.IDENT))
325
+ name = this.advance().value;
326
+ const fields = [];
327
+ if (this.check(lexer_1.TokenType.LBRACE)) {
328
+ this.eat(lexer_1.TokenType.LBRACE);
329
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.check(lexer_1.TokenType.EOF)) {
330
+ fields.push(this.parseFieldDecl());
331
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
332
+ }
333
+ this.eat(lexer_1.TokenType.RBRACE);
334
+ }
335
+ let typedefName = null;
336
+ if (isTypedef) {
337
+ typedefName = this.eat(lexer_1.TokenType.IDENT).value;
338
+ }
339
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
340
+ return { kind: 'StructDecl', name, fields, isTypedef, typedefName };
341
+ }
342
+ parseUnionDecl(isTypedef) {
343
+ this.eat(lexer_1.TokenType.UN);
344
+ let name = null;
345
+ if (this.check(lexer_1.TokenType.IDENT))
346
+ name = this.advance().value;
347
+ const fields = [];
348
+ if (this.check(lexer_1.TokenType.LBRACE)) {
349
+ this.eat(lexer_1.TokenType.LBRACE);
350
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.check(lexer_1.TokenType.EOF)) {
351
+ fields.push(this.parseFieldDecl());
352
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
353
+ }
354
+ this.eat(lexer_1.TokenType.RBRACE);
355
+ }
356
+ let typedefName = null;
357
+ if (isTypedef)
358
+ typedefName = this.eat(lexer_1.TokenType.IDENT).value;
359
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
360
+ return { kind: 'UnionDecl', name, fields, isTypedef, typedefName };
361
+ }
362
+ parseFieldDecl() {
363
+ const name = this.eatIdent().value;
364
+ this.eat(lexer_1.TokenType.COLON);
365
+ const type = this.parseType();
366
+ // array field
367
+ if (this.check(lexer_1.TokenType.LBRACKET)) {
368
+ this.advance();
369
+ if (!this.check(lexer_1.TokenType.RBRACKET)) {
370
+ type.arraySize = this.parseExpr();
371
+ }
372
+ this.eat(lexer_1.TokenType.RBRACKET);
373
+ }
374
+ // bitfield
375
+ let bitfield;
376
+ if (this.check(lexer_1.TokenType.COLON)) {
377
+ this.advance();
378
+ bitfield = this.parseExpr();
379
+ }
380
+ return { name, type, bitfield };
381
+ }
382
+ parseEnumDecl(isTypedef) {
383
+ this.eat(lexer_1.TokenType.EN);
384
+ let name = null;
385
+ if (this.check(lexer_1.TokenType.IDENT))
386
+ name = this.advance().value;
387
+ const members = [];
388
+ if (this.check(lexer_1.TokenType.LBRACE)) {
389
+ this.eat(lexer_1.TokenType.LBRACE);
390
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.check(lexer_1.TokenType.EOF)) {
391
+ const mname = this.advance().value;
392
+ let value = null;
393
+ if (this.tryEat(lexer_1.TokenType.ASSIGN)) {
394
+ value = this.parseExpr();
395
+ }
396
+ members.push({ name: mname, value });
397
+ if (!this.tryEat(lexer_1.TokenType.COMMA))
398
+ break;
399
+ }
400
+ this.eat(lexer_1.TokenType.RBRACE);
401
+ }
402
+ let typedefName = null;
403
+ if (isTypedef)
404
+ typedefName = this.eat(lexer_1.TokenType.IDENT).value;
405
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
406
+ return { kind: 'EnumDecl', name, members, isTypedef, typedefName };
407
+ }
408
+ parseFunctionDecl() {
409
+ this.eat(lexer_1.TokenType.FN);
410
+ const name = this.eat(lexer_1.TokenType.IDENT).value;
411
+ this.eat(lexer_1.TokenType.LPAREN);
412
+ const params = [];
413
+ while (!this.check(lexer_1.TokenType.RPAREN) && !this.check(lexer_1.TokenType.EOF)) {
414
+ if (this.check(lexer_1.TokenType.ELLIPSIS)) {
415
+ params.push({ name: '...', type: { base: '', pointers: 0, isConst: false, isVolatile: false, isStatic: false, isExtern: false, isInline: false, arraySize: null }, isVararg: true });
416
+ this.advance();
417
+ break;
418
+ }
419
+ const pname = this.eatIdent().value;
420
+ this.eat(lexer_1.TokenType.COLON);
421
+ const ptype = this.parseType();
422
+ // array param
423
+ if (this.check(lexer_1.TokenType.LBRACKET)) {
424
+ this.advance();
425
+ if (!this.check(lexer_1.TokenType.RBRACKET))
426
+ ptype.arraySize = this.parseExpr();
427
+ this.eat(lexer_1.TokenType.RBRACKET);
428
+ ptype.pointers = Math.max(ptype.pointers, 1);
429
+ }
430
+ params.push({ name: pname, type: ptype });
431
+ if (!this.tryEat(lexer_1.TokenType.COMMA))
432
+ break;
433
+ }
434
+ this.eat(lexer_1.TokenType.RPAREN);
435
+ let returnType = { base: 'void', pointers: 0, isConst: false, isVolatile: false, isStatic: false, isExtern: false, isInline: false, arraySize: null };
436
+ // Optional return type
437
+ if (this.check(lexer_1.TokenType.ARROW)) {
438
+ this.advance();
439
+ returnType = this.parseType();
440
+ }
441
+ // Body or semicolon (forward decl)
442
+ let body = null;
443
+ if (this.check(lexer_1.TokenType.LBRACE)) {
444
+ body = this.parseBlock();
445
+ }
446
+ else {
447
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
448
+ }
449
+ return { kind: 'FunctionDecl', name, params, returnType, body, qualifiers: [] };
450
+ }
451
+ parseVarOrFuncDecl() {
452
+ // Handle qualifiers
453
+ const qualifiers = [];
454
+ while (this.check(lexer_1.TokenType.SC, lexer_1.TokenType.EX, lexer_1.TokenType.IN, lexer_1.TokenType.CN, lexer_1.TokenType.VL)) {
455
+ qualifiers.push(this.advance().value);
456
+ }
457
+ // name:type[=init] or just type-based declarations
458
+ // In Pear, declarations are: name:type[=init]
459
+ // But we might also have identifiers being used as statements
460
+ if (this.isIdentLike(this.peek()) && this.peek(1).type === lexer_1.TokenType.COLON) {
461
+ return this.parseVarDecl(qualifiers);
462
+ }
463
+ // Expression statement
464
+ const expr = this.parseExpr();
465
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
466
+ return { kind: 'ExprStmt', expr };
467
+ }
468
+ parseVarDecl(qualifiers = []) {
469
+ const name = this.eatIdent().value;
470
+ this.eat(lexer_1.TokenType.COLON);
471
+ const type = this.parseType();
472
+ // Array declaration: name:type[size]
473
+ if (this.check(lexer_1.TokenType.LBRACKET)) {
474
+ this.advance();
475
+ if (!this.check(lexer_1.TokenType.RBRACKET)) {
476
+ type.arraySize = this.parseExpr();
477
+ }
478
+ this.eat(lexer_1.TokenType.RBRACKET);
479
+ }
480
+ let init = null;
481
+ if (this.tryEat(lexer_1.TokenType.ASSIGN)) {
482
+ if (this.check(lexer_1.TokenType.LBRACE)) {
483
+ init = this.parseInitList();
484
+ }
485
+ else {
486
+ init = this.parseExpr();
487
+ }
488
+ }
489
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
490
+ return { kind: 'VarDecl', name, type, init, qualifiers };
491
+ }
492
+ // ─── Statements ────────────────────────────────────────────────────────────
493
+ parseStatement() {
494
+ if (this.check(lexer_1.TokenType.HASH)) {
495
+ return { kind: 'RawPreprocessor', text: this.advance().value };
496
+ }
497
+ if (this.check(lexer_1.TokenType.LBRACE))
498
+ return this.parseBlock();
499
+ if (this.check(lexer_1.TokenType.IF))
500
+ return this.parseIf();
501
+ if (this.check(lexer_1.TokenType.LP))
502
+ return this.parseFor();
503
+ if (this.check(lexer_1.TokenType.WH))
504
+ return this.parseWhile();
505
+ if (this.check(lexer_1.TokenType.DW))
506
+ return this.parseDoWhile();
507
+ if (this.check(lexer_1.TokenType.SW))
508
+ return this.parseSwitch();
509
+ if (this.check(lexer_1.TokenType.RT))
510
+ return this.parseReturn();
511
+ if (this.check(lexer_1.TokenType.BK)) {
512
+ this.advance();
513
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
514
+ return { kind: 'BreakStmt' };
515
+ }
516
+ if (this.check(lexer_1.TokenType.CT)) {
517
+ this.advance();
518
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
519
+ return { kind: 'ContinueStmt' };
520
+ }
521
+ if (this.check(lexer_1.TokenType.GT))
522
+ return this.parseGoto();
523
+ if (this.check(lexer_1.TokenType.ST))
524
+ return this.parseStructDecl(false);
525
+ if (this.check(lexer_1.TokenType.UN))
526
+ return this.parseUnionDecl(false);
527
+ if (this.check(lexer_1.TokenType.EN))
528
+ return this.parseEnumDecl(false);
529
+ if (this.check(lexer_1.TokenType.TP))
530
+ return this.parseTypedef();
531
+ if (this.check(lexer_1.TokenType.DF))
532
+ return this.parseDefine();
533
+ if (this.check(lexer_1.TokenType.IM))
534
+ return this.parseInclude();
535
+ if (this.check(lexer_1.TokenType.ND)) {
536
+ this.advance();
537
+ const name = this.eat(lexer_1.TokenType.IDENT).value;
538
+ return { kind: 'IncludeGuardStart', name };
539
+ }
540
+ if (this.check(lexer_1.TokenType.DN)) {
541
+ this.advance();
542
+ return { kind: 'IncludeGuardEnd' };
543
+ }
544
+ // Qualifiers
545
+ if (this.check(lexer_1.TokenType.SC, lexer_1.TokenType.EX, lexer_1.TokenType.IN, lexer_1.TokenType.VL)) {
546
+ return this.parseVarOrFuncDecl();
547
+ }
548
+ // cn might be a qualifier or part of something else
549
+ if (this.check(lexer_1.TokenType.CN)) {
550
+ return this.parseVarOrFuncDecl();
551
+ }
552
+ // var decl: name:type
553
+ // name can be IDENT or a short keyword used as identifier (b, c, v, sz, i32, etc.)
554
+ if (this.isIdentLike(this.peek()) && this.peek(1).type === lexer_1.TokenType.COLON) {
555
+ // Determine if this is a label or a variable declaration.
556
+ // It's a label only if:
557
+ // 1. The token is a plain IDENT (not a type keyword)
558
+ // 2. After the colon there's NO type (no type keyword, no IDENT, no STAR)
559
+ // 3. After the colon is a statement keyword or structural token
560
+ const afterColon = this.peek(2);
561
+ const isLabel = this.check(lexer_1.TokenType.IDENT) &&
562
+ !this.isTypeToken(afterColon) &&
563
+ !this.isTypeStartToken(afterColon) &&
564
+ afterColon.type !== lexer_1.TokenType.STAR &&
565
+ afterColon.type !== lexer_1.TokenType.IDENT; // IDENT after colon = typedef'd type name → var decl
566
+ if (isLabel) {
567
+ const label = this.advance().value;
568
+ this.advance(); // colon
569
+ const body = this.parseStatement();
570
+ return { kind: 'LabelStmt', label, body };
571
+ }
572
+ return this.parseVarDecl();
573
+ }
574
+ // cs (case)
575
+ if (this.check(lexer_1.TokenType.CS)) {
576
+ this.advance();
577
+ const val = this.parseExpr();
578
+ this.eat(lexer_1.TokenType.COLON);
579
+ const body = [];
580
+ while (!this.check(lexer_1.TokenType.CS) && !this.check(lexer_1.TokenType.DV) && !this.check(lexer_1.TokenType.RBRACE) && !this.check(lexer_1.TokenType.EOF)) {
581
+ body.push(this.parseStatement());
582
+ }
583
+ return { kind: 'CaseStmt', value: val, body };
584
+ }
585
+ // dv (default)
586
+ if (this.check(lexer_1.TokenType.DV)) {
587
+ this.advance();
588
+ this.eat(lexer_1.TokenType.COLON);
589
+ const body = [];
590
+ while (!this.check(lexer_1.TokenType.CS) && !this.check(lexer_1.TokenType.RBRACE) && !this.check(lexer_1.TokenType.EOF)) {
591
+ body.push(this.parseStatement());
592
+ }
593
+ return { kind: 'DefaultStmt', body };
594
+ }
595
+ // expression statement
596
+ const expr = this.parseExpr();
597
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
598
+ return { kind: 'ExprStmt', expr };
599
+ }
600
+ isIdentLike(tok) {
601
+ return tok.type === lexer_1.TokenType.IDENT ||
602
+ tok.type === lexer_1.TokenType.B || tok.type === lexer_1.TokenType.C ||
603
+ tok.type === lexer_1.TokenType.V || tok.type === lexer_1.TokenType.SZ ||
604
+ tok.type === lexer_1.TokenType.I8 || tok.type === lexer_1.TokenType.I16 ||
605
+ tok.type === lexer_1.TokenType.I32 || tok.type === lexer_1.TokenType.I64 ||
606
+ tok.type === lexer_1.TokenType.U8 || tok.type === lexer_1.TokenType.U16 ||
607
+ tok.type === lexer_1.TokenType.U32 || tok.type === lexer_1.TokenType.U64 ||
608
+ tok.type === lexer_1.TokenType.F32 || tok.type === lexer_1.TokenType.F64 ||
609
+ tok.type === lexer_1.TokenType.IN;
610
+ }
611
+ isTypeToken(tok) {
612
+ return TYPE_KEYWORDS.has(tok.type) || tok.type === lexer_1.TokenType.ST || tok.type === lexer_1.TokenType.UN || tok.type === lexer_1.TokenType.EN;
613
+ }
614
+ isTypeStartToken(tok) {
615
+ if (this.isTypeToken(tok))
616
+ return true;
617
+ if (tok.type === lexer_1.TokenType.CN || tok.type === lexer_1.TokenType.VL ||
618
+ tok.type === lexer_1.TokenType.SC || tok.type === lexer_1.TokenType.EX ||
619
+ tok.type === lexer_1.TokenType.IN)
620
+ return true;
621
+ // Could also be a typedef'd name (IDENT) — assume it is if followed by reasonable stuff
622
+ return false;
623
+ }
624
+ parseBlock() {
625
+ this.eat(lexer_1.TokenType.LBRACE);
626
+ const body = [];
627
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.check(lexer_1.TokenType.EOF)) {
628
+ body.push(this.parseStatement());
629
+ }
630
+ this.eat(lexer_1.TokenType.RBRACE);
631
+ return { kind: 'BlockStmt', body };
632
+ }
633
+ parseIf() {
634
+ this.eat(lexer_1.TokenType.IF);
635
+ this.eat(lexer_1.TokenType.LPAREN);
636
+ const condition = this.parseExpr();
637
+ this.eat(lexer_1.TokenType.RPAREN);
638
+ const consequent = this.parseStatement();
639
+ let alternate = null;
640
+ if (this.check(lexer_1.TokenType.EI)) {
641
+ this.advance();
642
+ this.eat(lexer_1.TokenType.LPAREN);
643
+ const eicond = this.parseExpr();
644
+ this.eat(lexer_1.TokenType.RPAREN);
645
+ const eibody = this.parseStatement();
646
+ alternate = { kind: 'IfStmt', condition: eicond, consequent: eibody, alternate: null };
647
+ // chain else-ifs
648
+ let cur = alternate;
649
+ while (this.check(lexer_1.TokenType.EI)) {
650
+ this.advance();
651
+ this.eat(lexer_1.TokenType.LPAREN);
652
+ const c2 = this.parseExpr();
653
+ this.eat(lexer_1.TokenType.RPAREN);
654
+ const b2 = this.parseStatement();
655
+ const next = { kind: 'IfStmt', condition: c2, consequent: b2, alternate: null };
656
+ cur.alternate = next;
657
+ cur = next;
658
+ }
659
+ if (this.check(lexer_1.TokenType.EL)) {
660
+ this.advance();
661
+ cur.alternate = this.parseStatement();
662
+ }
663
+ }
664
+ else if (this.check(lexer_1.TokenType.EL)) {
665
+ this.advance();
666
+ alternate = this.parseStatement();
667
+ }
668
+ return { kind: 'IfStmt', condition, consequent, alternate };
669
+ }
670
+ parseFor() {
671
+ this.eat(lexer_1.TokenType.LP);
672
+ this.eat(lexer_1.TokenType.LPAREN);
673
+ let init = null;
674
+ if (!this.check(lexer_1.TokenType.SEMICOLON)) {
675
+ if (this.isIdentLike(this.peek()) && this.peek(1).type === lexer_1.TokenType.COLON) {
676
+ // var decl without trailing semicolon eaten
677
+ const name = this.advance().value;
678
+ this.advance(); // colon
679
+ const type = this.parseType();
680
+ let initExpr = null;
681
+ if (this.tryEat(lexer_1.TokenType.ASSIGN)) {
682
+ initExpr = this.parseExpr();
683
+ }
684
+ init = { kind: 'VarDecl', name, type, init: initExpr, qualifiers: [] };
685
+ }
686
+ else {
687
+ const expr = this.parseExpr();
688
+ init = { kind: 'ExprStmt', expr };
689
+ }
690
+ }
691
+ this.eat(lexer_1.TokenType.SEMICOLON);
692
+ let condition = null;
693
+ if (!this.check(lexer_1.TokenType.SEMICOLON)) {
694
+ condition = this.parseExpr();
695
+ }
696
+ this.eat(lexer_1.TokenType.SEMICOLON);
697
+ let update = null;
698
+ if (!this.check(lexer_1.TokenType.RPAREN)) {
699
+ update = this.parseExpr();
700
+ }
701
+ this.eat(lexer_1.TokenType.RPAREN);
702
+ const body = this.parseStatement();
703
+ return { kind: 'ForStmt', init, condition, update, body };
704
+ }
705
+ parseWhile() {
706
+ this.eat(lexer_1.TokenType.WH);
707
+ this.eat(lexer_1.TokenType.LPAREN);
708
+ const condition = this.parseExpr();
709
+ this.eat(lexer_1.TokenType.RPAREN);
710
+ const body = this.parseStatement();
711
+ return { kind: 'WhileStmt', condition, body };
712
+ }
713
+ parseDoWhile() {
714
+ this.eat(lexer_1.TokenType.DW);
715
+ const body = this.parseStatement();
716
+ this.eat(lexer_1.TokenType.WH);
717
+ this.eat(lexer_1.TokenType.LPAREN);
718
+ const condition = this.parseExpr();
719
+ this.eat(lexer_1.TokenType.RPAREN);
720
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
721
+ return { kind: 'DoWhileStmt', body, condition };
722
+ }
723
+ parseSwitch() {
724
+ this.eat(lexer_1.TokenType.SW);
725
+ this.eat(lexer_1.TokenType.LPAREN);
726
+ const expr = this.parseExpr();
727
+ this.eat(lexer_1.TokenType.RPAREN);
728
+ const body = this.parseBlock();
729
+ return { kind: 'SwitchStmt', expr, body };
730
+ }
731
+ parseReturn() {
732
+ this.eat(lexer_1.TokenType.RT);
733
+ let value = null;
734
+ if (!this.check(lexer_1.TokenType.SEMICOLON) && !this.check(lexer_1.TokenType.RBRACE) && !this.check(lexer_1.TokenType.EOF)) {
735
+ value = this.parseExpr();
736
+ }
737
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
738
+ return { kind: 'ReturnStmt', value };
739
+ }
740
+ parseGoto() {
741
+ this.eat(lexer_1.TokenType.GT);
742
+ const label = this.eat(lexer_1.TokenType.IDENT).value;
743
+ this.tryEat(lexer_1.TokenType.SEMICOLON);
744
+ return { kind: 'GotoStmt', label };
745
+ }
746
+ parseInitList() {
747
+ this.eat(lexer_1.TokenType.LBRACE);
748
+ const elements = [];
749
+ while (!this.check(lexer_1.TokenType.RBRACE) && !this.check(lexer_1.TokenType.EOF)) {
750
+ if (this.check(lexer_1.TokenType.LBRACE)) {
751
+ elements.push(this.parseInitList());
752
+ }
753
+ else {
754
+ elements.push(this.parseExpr());
755
+ }
756
+ if (!this.tryEat(lexer_1.TokenType.COMMA))
757
+ break;
758
+ }
759
+ this.eat(lexer_1.TokenType.RBRACE);
760
+ return { kind: 'InitListExpr', elements };
761
+ }
762
+ // ─── Expression Parsing (Pratt-style) ──────────────────────────────────────
763
+ parseExpr(minPrec = 0) {
764
+ return this.parseAssignment();
765
+ }
766
+ parseAssignment() {
767
+ const left = this.parseTernary();
768
+ const assignOps = [
769
+ lexer_1.TokenType.ASSIGN, lexer_1.TokenType.PLUS_ASSIGN, lexer_1.TokenType.MINUS_ASSIGN,
770
+ lexer_1.TokenType.STAR_ASSIGN, lexer_1.TokenType.SLASH_ASSIGN, lexer_1.TokenType.PERCENT_ASSIGN,
771
+ lexer_1.TokenType.AMP_ASSIGN, lexer_1.TokenType.PIPE_ASSIGN, lexer_1.TokenType.CARET_ASSIGN,
772
+ lexer_1.TokenType.LSHIFT_ASSIGN, lexer_1.TokenType.RSHIFT_ASSIGN,
773
+ ];
774
+ if (assignOps.includes(this.peek().type)) {
775
+ const op = this.advance().value;
776
+ const right = this.parseAssignment();
777
+ return { kind: 'AssignExpr', op, left, right };
778
+ }
779
+ return left;
780
+ }
781
+ parseTernary() {
782
+ const cond = this.parseOr();
783
+ if (this.tryEat(lexer_1.TokenType.QUESTION)) {
784
+ const consequent = this.parseExpr();
785
+ this.eat(lexer_1.TokenType.COLON);
786
+ const alternate = this.parseTernary();
787
+ return { kind: 'TernaryExpr', condition: cond, consequent, alternate };
788
+ }
789
+ return cond;
790
+ }
791
+ parseOr() {
792
+ let left = this.parseAnd();
793
+ while (this.check(lexer_1.TokenType.OR)) {
794
+ const op = this.advance().value;
795
+ left = { kind: 'BinaryExpr', op, left, right: this.parseAnd() };
796
+ }
797
+ return left;
798
+ }
799
+ parseAnd() {
800
+ let left = this.parseBitOr();
801
+ while (this.check(lexer_1.TokenType.AND)) {
802
+ const op = this.advance().value;
803
+ left = { kind: 'BinaryExpr', op, left, right: this.parseBitOr() };
804
+ }
805
+ return left;
806
+ }
807
+ parseBitOr() {
808
+ let left = this.parseBitXor();
809
+ while (this.check(lexer_1.TokenType.PIPE)) {
810
+ const op = this.advance().value;
811
+ left = { kind: 'BinaryExpr', op, left, right: this.parseBitXor() };
812
+ }
813
+ return left;
814
+ }
815
+ parseBitXor() {
816
+ let left = this.parseBitAnd();
817
+ while (this.check(lexer_1.TokenType.CARET)) {
818
+ const op = this.advance().value;
819
+ left = { kind: 'BinaryExpr', op, left, right: this.parseBitAnd() };
820
+ }
821
+ return left;
822
+ }
823
+ parseBitAnd() {
824
+ let left = this.parseEquality();
825
+ while (this.check(lexer_1.TokenType.AMP)) {
826
+ const op = this.advance().value;
827
+ left = { kind: 'BinaryExpr', op, left, right: this.parseEquality() };
828
+ }
829
+ return left;
830
+ }
831
+ parseEquality() {
832
+ let left = this.parseRelational();
833
+ while (this.check(lexer_1.TokenType.EQ) || this.check(lexer_1.TokenType.NEQ)) {
834
+ const op = this.advance().value;
835
+ left = { kind: 'BinaryExpr', op, left, right: this.parseRelational() };
836
+ }
837
+ return left;
838
+ }
839
+ parseRelational() {
840
+ let left = this.parseShift();
841
+ while (this.check(lexer_1.TokenType.LT) || this.check(lexer_1.TokenType.GT_OP) || this.check(lexer_1.TokenType.LTE) || this.check(lexer_1.TokenType.GTE)) {
842
+ const op = this.advance().value;
843
+ left = { kind: 'BinaryExpr', op, left, right: this.parseShift() };
844
+ }
845
+ return left;
846
+ }
847
+ parseShift() {
848
+ let left = this.parseAddSub();
849
+ while (this.check(lexer_1.TokenType.LSHIFT) || this.check(lexer_1.TokenType.RSHIFT)) {
850
+ const op = this.advance().value;
851
+ left = { kind: 'BinaryExpr', op, left, right: this.parseAddSub() };
852
+ }
853
+ return left;
854
+ }
855
+ parseAddSub() {
856
+ let left = this.parseMulDiv();
857
+ while (this.check(lexer_1.TokenType.PLUS) || this.check(lexer_1.TokenType.MINUS)) {
858
+ const op = this.advance().value;
859
+ left = { kind: 'BinaryExpr', op, left, right: this.parseMulDiv() };
860
+ }
861
+ return left;
862
+ }
863
+ parseMulDiv() {
864
+ let left = this.parseUnary();
865
+ while (this.check(lexer_1.TokenType.STAR) || this.check(lexer_1.TokenType.SLASH) || this.check(lexer_1.TokenType.PERCENT)) {
866
+ const op = this.advance().value;
867
+ left = { kind: 'BinaryExpr', op, left, right: this.parseUnary() };
868
+ }
869
+ return left;
870
+ }
871
+ parseUnary() {
872
+ // Prefix operators
873
+ if (this.check(lexer_1.TokenType.MINUS)) {
874
+ this.advance();
875
+ return { kind: 'UnaryExpr', op: '-', operand: this.parseUnary(), prefix: true };
876
+ }
877
+ if (this.check(lexer_1.TokenType.NOT)) {
878
+ this.advance();
879
+ return { kind: 'UnaryExpr', op: '!', operand: this.parseUnary(), prefix: true };
880
+ }
881
+ if (this.check(lexer_1.TokenType.TILDE)) {
882
+ this.advance();
883
+ return { kind: 'UnaryExpr', op: '~', operand: this.parseUnary(), prefix: true };
884
+ }
885
+ if (this.check(lexer_1.TokenType.INC)) {
886
+ this.advance();
887
+ return { kind: 'UnaryExpr', op: '++', operand: this.parseUnary(), prefix: true };
888
+ }
889
+ if (this.check(lexer_1.TokenType.DEC)) {
890
+ this.advance();
891
+ return { kind: 'UnaryExpr', op: '--', operand: this.parseUnary(), prefix: true };
892
+ }
893
+ // Address-of & and dereference *
894
+ if (this.check(lexer_1.TokenType.AMP)) {
895
+ this.advance();
896
+ return { kind: 'UnaryExpr', op: '&', operand: this.parseUnary(), prefix: true };
897
+ }
898
+ if (this.check(lexer_1.TokenType.STAR)) {
899
+ this.advance();
900
+ return { kind: 'UnaryExpr', op: '*', operand: this.parseUnary(), prefix: true };
901
+ }
902
+ if (this.check(lexer_1.TokenType.PLUS)) {
903
+ this.advance();
904
+ return { kind: 'UnaryExpr', op: '+', operand: this.parseUnary(), prefix: true };
905
+ }
906
+ // sizeof
907
+ if (this.check(lexer_1.TokenType.SO)) {
908
+ this.advance();
909
+ this.eat(lexer_1.TokenType.LPAREN);
910
+ // Check if it's a type or expression
911
+ if (this.isTypeStartToken(this.peek()) || TYPE_KEYWORDS.has(this.peek().type)) {
912
+ const t = this.parseType();
913
+ this.eat(lexer_1.TokenType.RPAREN);
914
+ return { kind: 'SizeofExpr', operand: t, isType: true };
915
+ }
916
+ const expr = this.parseExpr();
917
+ this.eat(lexer_1.TokenType.RPAREN);
918
+ return { kind: 'SizeofExpr', operand: expr, isType: false };
919
+ }
920
+ return this.parsePostfix();
921
+ }
922
+ parsePostfix() {
923
+ let expr = this.parsePrimary();
924
+ while (true) {
925
+ if (this.check(lexer_1.TokenType.INC)) {
926
+ this.advance();
927
+ expr = { kind: 'PostfixExpr', op: '++', operand: expr };
928
+ continue;
929
+ }
930
+ if (this.check(lexer_1.TokenType.DEC)) {
931
+ this.advance();
932
+ expr = { kind: 'PostfixExpr', op: '--', operand: expr };
933
+ continue;
934
+ }
935
+ if (this.check(lexer_1.TokenType.LBRACKET)) {
936
+ this.advance();
937
+ const idx = this.parseExpr();
938
+ this.eat(lexer_1.TokenType.RBRACKET);
939
+ expr = { kind: 'IndexExpr', object: expr, index: idx };
940
+ continue;
941
+ }
942
+ if (this.check(lexer_1.TokenType.LPAREN)) {
943
+ this.advance();
944
+ const args = [];
945
+ while (!this.check(lexer_1.TokenType.RPAREN) && !this.check(lexer_1.TokenType.EOF)) {
946
+ args.push(this.parseExpr());
947
+ if (!this.tryEat(lexer_1.TokenType.COMMA))
948
+ break;
949
+ }
950
+ this.eat(lexer_1.TokenType.RPAREN);
951
+ expr = { kind: 'CallExpr', callee: expr, args };
952
+ continue;
953
+ }
954
+ if (this.check(lexer_1.TokenType.DOT)) {
955
+ this.advance();
956
+ const member = this.advance().value;
957
+ expr = { kind: 'MemberExpr', object: expr, member, arrow: false };
958
+ continue;
959
+ }
960
+ if (this.check(lexer_1.TokenType.ARROW)) {
961
+ this.advance();
962
+ const member = this.advance().value;
963
+ expr = { kind: 'MemberExpr', object: expr, member, arrow: true };
964
+ continue;
965
+ }
966
+ break;
967
+ }
968
+ return expr;
969
+ }
970
+ parsePrimary() {
971
+ const tok = this.peek();
972
+ if (tok.type === lexer_1.TokenType.NUMBER) {
973
+ this.advance();
974
+ return { kind: 'NumberLiteral', value: tok.value };
975
+ }
976
+ if (tok.type === lexer_1.TokenType.STRING) {
977
+ this.advance();
978
+ // Handle adjacent string literal concatenation
979
+ let val = tok.value;
980
+ while (this.check(lexer_1.TokenType.STRING)) {
981
+ val = val.slice(0, -1) + this.advance().value.slice(1);
982
+ }
983
+ return { kind: 'StringLiteral', value: val };
984
+ }
985
+ if (tok.type === lexer_1.TokenType.CHAR) {
986
+ this.advance();
987
+ return { kind: 'CharLiteral', value: tok.value };
988
+ }
989
+ // Parenthesized expression or cast
990
+ if (tok.type === lexer_1.TokenType.LPAREN) {
991
+ this.advance();
992
+ // Check for cast: (type)expr
993
+ if (this.isTypeStartToken(this.peek()) || TYPE_KEYWORDS.has(this.peek().type)) {
994
+ // Could be cast or just grouped expr
995
+ const savedPos = this.pos;
996
+ try {
997
+ const type = this.parseType();
998
+ if (this.check(lexer_1.TokenType.RPAREN)) {
999
+ this.advance();
1000
+ // Check it's followed by something that can be an expression
1001
+ if (!this.check(lexer_1.TokenType.SEMICOLON) && !this.check(lexer_1.TokenType.COMMA) &&
1002
+ !this.check(lexer_1.TokenType.RPAREN) && !this.check(lexer_1.TokenType.RBRACKET) &&
1003
+ !this.check(lexer_1.TokenType.EOF)) {
1004
+ const expr = this.parseUnary();
1005
+ return { kind: 'CastExpr', targetType: type, expr };
1006
+ }
1007
+ }
1008
+ }
1009
+ catch {
1010
+ // not a cast
1011
+ }
1012
+ this.pos = savedPos;
1013
+ }
1014
+ const expr = this.parseExpr();
1015
+ this.eat(lexer_1.TokenType.RPAREN);
1016
+ return expr;
1017
+ }
1018
+ // Identifiers (including keywords used as identifiers in context)
1019
+ if (tok.type === lexer_1.TokenType.IDENT ||
1020
+ tok.type === lexer_1.TokenType.B || // 'b' as a type used in expression context might just be an identifier
1021
+ tok.type === lexer_1.TokenType.C ||
1022
+ tok.type === lexer_1.TokenType.V) {
1023
+ this.advance();
1024
+ if (tok.value === 'NULL')
1025
+ return { kind: 'NullLiteral' };
1026
+ if (tok.value === 'true')
1027
+ return { kind: 'BoolLiteral', value: true };
1028
+ if (tok.value === 'false')
1029
+ return { kind: 'BoolLiteral', value: false };
1030
+ return { kind: 'Identifier', name: tok.value };
1031
+ }
1032
+ // Any keyword that might be used as an identifier
1033
+ if (tok.type !== lexer_1.TokenType.EOF) {
1034
+ this.advance();
1035
+ return { kind: 'Identifier', name: tok.value };
1036
+ }
1037
+ throw new Error(`Unexpected token '${tok.value}' (${tok.type}) at line ${tok.line}:${tok.col}`);
1038
+ }
1039
+ }
1040
+ exports.Parser = Parser;
1041
+ function parse(source) {
1042
+ const lexer = new lexer_1.Lexer(source);
1043
+ const tokens = lexer.tokenize();
1044
+ const parser = new Parser(tokens);
1045
+ return parser.parse();
1046
+ }
1047
+ //# sourceMappingURL=parser.js.map