gofront 0.0.1 → 0.0.3

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/src/parser.js CHANGED
@@ -1,5 +1,10 @@
1
1
  // Recursive-descent parser. Produces an AST from a GoFront token stream.
2
2
  //
3
+ // Split into sub-modules under parser/:
4
+ // types.js — type expression parsing (slice, map, struct, interface, etc.)
5
+ // statements.js — block, control flow, simple statements
6
+ // expressions.js — operator precedence, unary, postfix, primary, literals
7
+ //
3
8
  // Node kinds used throughout the compiler:
4
9
  // Program, PackageDecl, ImportDecl
5
10
  // FuncDecl, MethodDecl, VarDecl, ConstDecl, TypeDecl
@@ -12,6 +17,9 @@
12
17
  // CompositeLit, KeyValueExpr, FuncLit, AwaitExpr
13
18
 
14
19
  import { T } from "./lexer.js";
20
+ import { expressionParserMethods } from "./parser/expressions.js";
21
+ import { statementParserMethods } from "./parser/statements.js";
22
+ import { typeParserMethods } from "./parser/types.js";
15
23
 
16
24
  export class ParseError extends Error {
17
25
  constructor(msg, token, filename, sourceCode) {
@@ -33,28 +41,6 @@ export class ParseError extends Error {
33
41
  }
34
42
  }
35
43
 
36
- // Binary operator precedence (higher = binds tighter, matching Go spec)
37
- const PREC = {
38
- [T.OR]: 1,
39
- [T.AND]: 2,
40
- [T.EQ]: 3,
41
- [T.NEQ]: 3,
42
- [T.LT]: 3,
43
- [T.GT]: 3,
44
- [T.LTE]: 3,
45
- [T.GTE]: 3,
46
- [T.PLUS]: 4,
47
- [T.MINUS]: 4,
48
- [T.PIPE]: 4,
49
- [T.CARET]: 4,
50
- [T.STAR]: 5,
51
- [T.SLASH]: 5,
52
- [T.PERCENT]: 5,
53
- [T.LSHIFT]: 5,
54
- [T.RSHIFT]: 5,
55
- [T.AMP]: 5,
56
- };
57
-
58
44
  export class Parser {
59
45
  constructor(tokens, filename = null, source = null) {
60
46
  this.tokens = tokens;
@@ -116,7 +102,11 @@ export class Parser {
116
102
  const imports = [];
117
103
  while (this.check(T.IMPORT)) imports.push(this.parseImport());
118
104
  const decls = [];
119
- while (!this.check(T.EOF)) decls.push(this.parseTopDecl());
105
+ while (!this.check(T.EOF)) {
106
+ const result = this.parseTopDecl();
107
+ if (Array.isArray(result)) decls.push(...result);
108
+ else decls.push(result);
109
+ }
120
110
  return { kind: "Program", pkg, imports, decls, _filename: this.filename };
121
111
  }
122
112
 
@@ -129,18 +119,30 @@ export class Parser {
129
119
 
130
120
  parseImport() {
131
121
  this.expect(T.IMPORT);
132
- const paths = [];
122
+ const imports = [];
123
+ const parseOne = () => {
124
+ const _line = this.peek().line;
125
+ let alias = null;
126
+ if (this.check(T.IDENT)) {
127
+ alias = this.advance().value;
128
+ } else if (this.check(T.DOT)) {
129
+ alias = ".";
130
+ this.advance();
131
+ }
132
+ const path = this.expect(T.STRING).value;
133
+ return { path, alias, _line };
134
+ };
133
135
  if (this.match(T.LPAREN)) {
134
136
  while (!this.check(T.RPAREN) && !this.check(T.EOF)) {
135
- paths.push(this.expect(T.STRING).value);
137
+ imports.push(parseOne());
136
138
  this.semi();
137
139
  }
138
140
  this.expect(T.RPAREN);
139
141
  } else {
140
- paths.push(this.expect(T.STRING).value);
142
+ imports.push(parseOne());
141
143
  }
142
144
  this.semi();
143
- return { kind: "ImportDecl", paths };
145
+ return { kind: "ImportDecl", imports };
144
146
  }
145
147
 
146
148
  // ── Top-level declarations ───────────────────────────────────
@@ -155,7 +157,7 @@ export class Parser {
155
157
  case T.TYPE:
156
158
  return this.parseTypeDecl();
157
159
  case T.VAR:
158
- return this.parseVarDecl(true);
160
+ return this.parseVarDecl();
159
161
  case T.CONST:
160
162
  return this.parseConstDecl();
161
163
  default:
@@ -276,23 +278,18 @@ export class Parser {
276
278
  tok.type === T.FUNC ||
277
279
  tok.type === T.INTERFACE ||
278
280
  tok.type === T.STRUCT ||
279
- (tok.type === T.IDENT && tok.value !== "_") ||
280
- [
281
- "int",
282
- "float64",
283
- "string",
284
- "bool",
285
- "any",
286
- "byte",
287
- "rune",
288
- "error",
289
- ].includes(tok.value)
281
+ (tok.type === T.IDENT && tok.value !== "_")
290
282
  );
291
283
  }
292
284
 
293
285
  parseReturnType(inTypeExpr = false) {
294
286
  // No return type
295
- if (this.check(T.LBRACE) || this.check(T.SEMICOLON) || this.check(T.EOF))
287
+ if (
288
+ this.check(T.LBRACE) ||
289
+ this.check(T.RBRACE) ||
290
+ this.check(T.SEMICOLON) ||
291
+ this.check(T.EOF)
292
+ )
296
293
  return null;
297
294
  // Inside a type expression (e.g. func type in a tuple), `,` and `)` also end the type
298
295
  if (inTypeExpr && (this.check(T.COMMA) || this.check(T.RPAREN)))
@@ -329,13 +326,27 @@ export class Parser {
329
326
 
330
327
  parseTypeDecl() {
331
328
  this.expect(T.TYPE);
329
+ if (this.match(T.LPAREN)) {
330
+ const decls = [];
331
+ while (!this.check(T.RPAREN) && !this.check(T.EOF)) {
332
+ const name = this.expect(T.IDENT).value;
333
+ const isAlias = this.match(T.ASSIGN);
334
+ const type = this.parseType();
335
+ decls.push({ kind: "TypeDecl", name, type, isAlias });
336
+ this.semi();
337
+ }
338
+ this.expect(T.RPAREN);
339
+ this.semi();
340
+ return decls;
341
+ }
332
342
  const name = this.expect(T.IDENT).value;
343
+ const isAlias = this.match(T.ASSIGN);
333
344
  const type = this.parseType();
334
345
  this.semi();
335
- return { kind: "TypeDecl", name, type };
346
+ return { kind: "TypeDecl", name, type, isAlias };
336
347
  }
337
348
 
338
- parseVarDecl(_topLevel = false) {
349
+ parseVarDecl() {
339
350
  this.expect(T.VAR);
340
351
  const decls = [];
341
352
  if (this.match(T.LPAREN)) {
@@ -414,707 +425,8 @@ export class Parser {
414
425
  this.semi();
415
426
  return { kind: "ConstDecl", decls };
416
427
  }
417
-
418
- // ── Types ────────────────────────────────────────────────────
419
-
420
- parseType() {
421
- const t = this.peek();
422
- switch (t.type) {
423
- case T.LBRACKET:
424
- return this.parseSliceOrArrayType();
425
- case T.MAP:
426
- return this.parseMapType();
427
- case T.FUNC:
428
- return this.parseFuncType();
429
- case T.STRUCT:
430
- return this.parseStructType();
431
- case T.INTERFACE:
432
- return this.parseInterfaceType();
433
- case T.STAR:
434
- this.advance();
435
- return { kind: "PointerType", base: this.parseType() };
436
- case T.IDENT:
437
- case "int":
438
- case "float64":
439
- case "string":
440
- case "bool":
441
- case "any":
442
- case "byte":
443
- case "rune":
444
- case "error":
445
- return this.parseTypeName();
446
- default:
447
- this.err(`Expected type, got '${t.value}'`);
448
- }
449
- }
450
-
451
- parseTypeName() {
452
- const t = this.advance();
453
- let name = t.value;
454
- // qualified: pkg.Type
455
- if (this.check(T.DOT)) {
456
- this.advance();
457
- name += `.${this.expect(T.IDENT).value}`;
458
- }
459
- return { kind: "TypeName", name };
460
- }
461
-
462
- parseSliceOrArrayType() {
463
- this.expect(T.LBRACKET);
464
- if (this.check(T.RBRACKET)) {
465
- // slice type: []T
466
- this.advance();
467
- return { kind: "SliceType", elem: this.parseType() };
468
- }
469
- // fixed array: [n]T — treated as slice for JS purposes
470
- const size = this.parseExpr();
471
- this.expect(T.RBRACKET);
472
- return { kind: "ArrayType", size, elem: this.parseType() };
473
- }
474
-
475
- parseMapType() {
476
- this.expect(T.MAP);
477
- this.expect(T.LBRACKET);
478
- const key = this.parseType();
479
- this.expect(T.RBRACKET);
480
- const value = this.parseType();
481
- return { kind: "MapType", key, value };
482
- }
483
-
484
- parseFuncType() {
485
- this.expect(T.FUNC);
486
- const params = this.parseParamList();
487
- const returnType = this.parseReturnType(true);
488
- return { kind: "FuncType", params, returnType };
489
- }
490
-
491
- parseStructType() {
492
- this.expect(T.STRUCT);
493
- this.expect(T.LBRACE);
494
- const fields = [];
495
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
496
- const names = [this.expect(T.IDENT).value];
497
- while (this.match(T.COMMA)) names.push(this.expect(T.IDENT).value);
498
- // Check if this is an embedded field (just a type name)
499
- // e.g., type T struct { Base }
500
- if (this.check(T.SEMICOLON) || this.check(T.RBRACE)) {
501
- const typeName = names[0]; // assuming no comma list for embedded
502
- fields.push({
503
- names: [],
504
- type: { kind: "TypeName", name: typeName },
505
- embedded: true,
506
- });
507
- this.semi();
508
- continue;
509
- }
510
- const type = this.parseType();
511
- fields.push({ names, type });
512
- this.semi();
513
- }
514
- this.expect(T.RBRACE);
515
- return { kind: "StructType", fields };
516
- }
517
-
518
- parseInterfaceType() {
519
- this.expect(T.INTERFACE);
520
- this.expect(T.LBRACE);
521
- const methods = [];
522
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
523
- const name = this.expect(T.IDENT).value;
524
- const params = this.parseParamList();
525
- const returnType = this.parseReturnType();
526
- methods.push({ name, params, returnType });
527
- this.semi();
528
- }
529
- this.expect(T.RBRACE);
530
- return { kind: "InterfaceType", methods };
531
- }
532
-
533
- // ── Statements ───────────────────────────────────────────────
534
-
535
- parseBlock() {
536
- this.expect(T.LBRACE);
537
- const stmts = [];
538
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
539
- stmts.push(this.parseStmt());
540
- this.semi();
541
- }
542
- this.expect(T.RBRACE);
543
- return { kind: "Block", stmts };
544
- }
545
-
546
- parseStmt() {
547
- const t = this.peek();
548
- const stmt = this._parseStmt(t);
549
- stmt._line = t.line;
550
- return stmt;
551
- }
552
-
553
- _parseStmt(t) {
554
- switch (t.type) {
555
- case T.VAR:
556
- return this.parseVarDecl();
557
- case T.CONST:
558
- return this.parseConstDecl();
559
- case T.TYPE:
560
- return this.parseTypeDecl();
561
- case T.RETURN:
562
- return this.parseReturn();
563
- case T.DEFER:
564
- return this.parseDefer();
565
- case T.BREAK:
566
- this.advance();
567
- this.semi();
568
- return { kind: "BranchStmt", keyword: "break" };
569
- case T.CONTINUE:
570
- this.advance();
571
- this.semi();
572
- return { kind: "BranchStmt", keyword: "continue" };
573
- case T.FALLTHROUGH:
574
- this.advance();
575
- this.semi();
576
- return { kind: "BranchStmt", keyword: "fallthrough" };
577
- case T.IF:
578
- return this.parseIf();
579
- case T.FOR:
580
- return this.parseFor();
581
- case T.SWITCH:
582
- return this.parseSwitch();
583
- case T.LBRACE:
584
- return this.parseBlock();
585
- default:
586
- return this.parseSimpleStmt();
587
- }
588
- }
589
-
590
- parseDefer() {
591
- this.expect(T.DEFER);
592
- const call = this.parseExpr();
593
- return { kind: "DeferStmt", call };
594
- }
595
-
596
- parseReturn() {
597
- this.expect(T.RETURN);
598
- if (this.check(T.SEMICOLON) || this.check(T.RBRACE)) {
599
- return { kind: "ReturnStmt", values: [] };
600
- }
601
- const values = this.parseExprList();
602
- return { kind: "ReturnStmt", values };
603
- }
604
-
605
- parseIf() {
606
- this.expect(T.IF);
607
- let init = null;
608
- let cond;
609
- // if init; cond { } — use noCompLit to avoid "{" being parsed as composite literal
610
- const first = this.parseSimpleStmtRaw(true);
611
- // Only treat as "init; cond" if semicolon is followed by a non-brace token
612
- if (this.check(T.SEMICOLON) && this.peek2().type !== T.LBRACE) {
613
- this.advance(); // consume ;
614
- init = first;
615
- cond = this.parseExpr(0, true);
616
- } else {
617
- this.match(T.SEMICOLON); // consume any auto-inserted ;
618
- cond = this.stmtToExpr(first);
619
- }
620
- const body = this.parseBlock();
621
- // Consume auto-inserted ; before else (Go allows "}\nelse")
622
- this.match(T.SEMICOLON);
623
- let elseBody = null;
624
- if (this.match(T.ELSE)) {
625
- elseBody = this.check(T.IF) ? this.parseIf() : this.parseBlock();
626
- }
627
- return { kind: "IfStmt", init, cond, body, elseBody };
628
- }
629
-
630
- parseFor() {
631
- this.expect(T.FOR);
632
-
633
- // for { } — infinite loop
634
- if (this.check(T.LBRACE)) {
635
- return {
636
- kind: "ForStmt",
637
- init: null,
638
- cond: null,
639
- post: null,
640
- body: this.parseBlock(),
641
- };
642
- }
643
-
644
- const first = this.parseSimpleStmtRaw(true);
645
-
646
- if (this.check(T.LBRACE)) {
647
- // for k, v := range expr { } — range stmt ends right before {
648
- if (this.isRangeStmt(first)) {
649
- return {
650
- kind: "ForStmt",
651
- init: first,
652
- cond: null,
653
- post: null,
654
- body: this.parseBlock(),
655
- };
656
- }
657
- // for cond { }
658
- return {
659
- kind: "ForStmt",
660
- init: null,
661
- cond: this.stmtToExpr(first),
662
- post: null,
663
- body: this.parseBlock(),
664
- };
665
- }
666
-
667
- this.expect(T.SEMICOLON);
668
-
669
- // for init; cond; post { }
670
- let cond = null;
671
- if (!this.check(T.SEMICOLON)) cond = this.parseExpr(0, true);
672
- this.expect(T.SEMICOLON);
673
- let post = null;
674
- if (!this.check(T.LBRACE)) post = this.parseSimpleStmtRaw();
675
- const body = this.parseBlock();
676
- return { kind: "ForStmt", init: first, cond, post, body };
677
- }
678
-
679
- isRangeStmt(stmt) {
680
- if (!stmt) return false;
681
- if (stmt.kind !== "DefineStmt" && stmt.kind !== "AssignStmt") return false;
682
- return stmt.rhs?.[0]?.kind === "RangeExpr";
683
- }
684
-
685
- parseSwitch() {
686
- this.expect(T.SWITCH);
687
- let init = null;
688
- let tag = null;
689
- if (!this.check(T.LBRACE)) {
690
- const expr = this.parseExpr(0, true);
691
- if (this.match(T.SEMICOLON)) {
692
- init = this.exprToStmt(expr);
693
- if (!this.check(T.LBRACE)) tag = this.parseExpr(0, true);
694
- } else {
695
- tag = expr;
696
- }
697
- }
698
- this.expect(T.LBRACE);
699
- const cases = [];
700
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
701
- if (this.check(T.CASE)) {
702
- this.advance();
703
- const list = this.parseExprList();
704
- this.expect(T.COLON);
705
- const stmts = [];
706
- while (
707
- !this.check(T.CASE) &&
708
- !this.check(T.DEFAULT) &&
709
- !this.check(T.RBRACE)
710
- ) {
711
- stmts.push(this.parseStmt());
712
- this.semi();
713
- }
714
- cases.push({ kind: "CaseClause", list, stmts });
715
- } else if (this.check(T.DEFAULT)) {
716
- this.advance();
717
- this.expect(T.COLON);
718
- const stmts = [];
719
- while (
720
- !this.check(T.CASE) &&
721
- !this.check(T.DEFAULT) &&
722
- !this.check(T.RBRACE)
723
- ) {
724
- stmts.push(this.parseStmt());
725
- this.semi();
726
- }
727
- cases.push({ kind: "CaseClause", list: null, stmts });
728
- } else {
729
- this.err("Expected case or default");
730
- }
731
- }
732
- this.expect(T.RBRACE);
733
- return { kind: "SwitchStmt", init, tag, cases };
734
- }
735
-
736
- // Parses a "simple statement": assignment, define, inc/dec, or expression.
737
- // Returns a statement node.
738
- parseSimpleStmt() {
739
- const s = this.parseSimpleStmtRaw();
740
- return s;
741
- }
742
-
743
- parseSimpleStmtRaw(noCompLit = false) {
744
- // Parse left-hand side (could be expr list for multi-assign)
745
- const first = this.parseExpr(0, noCompLit);
746
- const t = this.peek();
747
-
748
- // a, b, c := ... or x, y = ...
749
- if (t.type === T.COMMA) {
750
- const lhs = [first];
751
- while (this.match(T.COMMA)) lhs.push(this.parseExpr());
752
- const op = this.advance(); // := or =
753
- const rhs = this.parseExprList();
754
- if (op.type === T.DEFINE) return { kind: "DefineStmt", lhs, rhs };
755
- return { kind: "AssignStmt", lhs, op: "=", rhs };
756
- }
757
-
758
- // x := expr
759
- if (t.type === T.DEFINE) {
760
- this.advance();
761
- const rhs = this.parseExprList();
762
- return { kind: "DefineStmt", lhs: [first], rhs };
763
- }
764
-
765
- // x = expr or x += expr etc.
766
- const assignOps = [
767
- T.ASSIGN,
768
- T.PLUS_ASSIGN,
769
- T.MINUS_ASSIGN,
770
- T.STAR_ASSIGN,
771
- T.SLASH_ASSIGN,
772
- T.PERCENT_ASSIGN,
773
- ];
774
- if (assignOps.includes(t.type)) {
775
- const op = this.advance().value;
776
- const rhs = this.parseExprList();
777
- return { kind: "AssignStmt", lhs: [first], op, rhs };
778
- }
779
-
780
- // x++ x--
781
- if (t.type === T.INC || t.type === T.DEC) {
782
- const op = this.advance().value;
783
- return { kind: "IncDecStmt", expr: first, op };
784
- }
785
-
786
- // for range detection: "for k, v := range expr"
787
- // range keyword after :=
788
- if (first.kind === "Ident" && first.name === "range") {
789
- const expr = this.parseExpr();
790
- return { kind: "RangeExpr", expr };
791
- }
792
-
793
- return { kind: "ExprStmt", expr: first };
794
- }
795
-
796
- // Converts an ExprStmt back to its expression
797
- stmtToExpr(stmt) {
798
- if (!stmt) return null;
799
- if (stmt.kind === "ExprStmt") return stmt.expr;
800
- return stmt;
801
- }
802
-
803
- exprToStmt(expr) {
804
- return { kind: "ExprStmt", expr };
805
- }
806
-
807
- // ── Expressions ──────────────────────────────────────────────
808
-
809
- parseExprList() {
810
- const exprs = [this.parseExpr()];
811
- while (this.match(T.COMMA)) exprs.push(this.parseExpr());
812
- return exprs;
813
- }
814
-
815
- // noCompLit=true disables composite literal parsing in if/for/switch conditions
816
- // to resolve the ambiguity between "if x {" and "if Type{ } {"
817
- parseExpr(minPrec = 0, noCompLit = false) {
818
- let left = this.parseUnary(noCompLit);
819
- while (true) {
820
- const prec = PREC[this.peek().type];
821
- if (!prec || prec <= minPrec) break;
822
- const op = this.advance().value;
823
- const right = this.parseExpr(prec, noCompLit);
824
- left = { kind: "BinaryExpr", left, op, right };
825
- }
826
- return left;
827
- }
828
-
829
- parseUnary(noCompLit = false) {
830
- const t = this.peek();
831
- if (
832
- t.type === T.NOT ||
833
- t.type === T.MINUS ||
834
- t.type === T.PLUS ||
835
- t.type === T.CARET ||
836
- t.type === T.AMP ||
837
- t.type === T.STAR
838
- ) {
839
- const op = this.advance().value;
840
- return { kind: "UnaryExpr", op, operand: this.parseUnary(noCompLit) };
841
- }
842
- if (t.type === T.AWAIT) {
843
- this.advance();
844
- return { kind: "AwaitExpr", expr: this.parseUnary(noCompLit) };
845
- }
846
- return this.parsePostfix(noCompLit);
847
- }
848
-
849
- parsePostfix(noCompLit = false) {
850
- let expr = this.parsePrimary();
851
- while (true) {
852
- const t = this.peek();
853
- if (t.type === T.DOT) {
854
- this.advance();
855
- // type assertion: expr.(Type)
856
- if (this.check(T.LPAREN)) {
857
- this.advance();
858
- const type = this.parseType();
859
- this.expect(T.RPAREN);
860
- expr = { kind: "TypeAssertExpr", expr, type };
861
- } else {
862
- const field = this.advance().value;
863
- expr = { kind: "SelectorExpr", expr, field };
864
- }
865
- } else if (t.type === T.LBRACKET) {
866
- this.advance();
867
- const low = this.check(T.COLON) ? null : this.parseExpr();
868
- if (this.match(T.COLON)) {
869
- const high = this.check(T.RBRACKET) ? null : this.parseExpr();
870
- this.expect(T.RBRACKET);
871
- expr = { kind: "SliceExpr", expr, low, high };
872
- } else {
873
- this.expect(T.RBRACKET);
874
- expr = { kind: "IndexExpr", expr, index: low };
875
- }
876
- } else if (t.type === T.LPAREN) {
877
- expr = this.parseCall(expr);
878
- } else if (
879
- t.type === T.LBRACE &&
880
- !noCompLit &&
881
- this.isCompositeLitContext(expr)
882
- ) {
883
- expr = this.parseCompositeLit(expr);
884
- } else {
885
- break;
886
- }
887
- }
888
- return expr;
889
- }
890
-
891
- // Composite literals are only allowed after a type name (not arbitrary exprs)
892
- isCompositeLitContext(expr) {
893
- return (
894
- expr.kind === "Ident" ||
895
- expr.kind === "SelectorExpr" ||
896
- expr.kind === "TypeExpr"
897
- );
898
- }
899
-
900
- parseCall(fn) {
901
- this.expect(T.LPAREN);
902
- const args = [];
903
- if (!this.check(T.RPAREN)) {
904
- do {
905
- if (this.check(T.RPAREN)) break;
906
- // map[K]V and []T as call args must be parsed as types, not expressions,
907
- // when they are NOT followed by { (which would make them composite literals).
908
- // Needed for make(map[K]V), make([]T, n), new(T), etc.
909
- const t = this.peek();
910
- const isSliceOrArrayType =
911
- t.type === T.LBRACKET &&
912
- (this.peek2()?.type === T.RBRACKET || // []T
913
- this.peek2()?.type === T.INT); // [n]T
914
- if (
915
- (t.type === T.MAP || isSliceOrArrayType) &&
916
- !this.looksLikeCompositeLitArg()
917
- ) {
918
- args.push({ kind: "TypeExpr", type: this.parseType() });
919
- } else {
920
- args.push(this.parseExpr());
921
- }
922
- this.match(T.ELLIPSIS); // variadic spread, ignore for now
923
- } while (this.match(T.COMMA));
924
- }
925
- this.expect(T.RPAREN);
926
- return { kind: "CallExpr", func: fn, args };
927
- }
928
-
929
- // Returns true if the current position looks like a composite literal argument,
930
- // e.g. []int{1,2,3} or map[string]int{"a":1}. We scan ahead past the type
931
- // tokens to check whether a { follows, without advancing this.pos permanently.
932
- looksLikeCompositeLitArg() {
933
- let i = this.pos;
934
- const src = this.tokens;
935
- // Skip over the type tokens: [, ], map, [, K, ], V, ident, etc.
936
- // Simple approach: skip until we hit something that can't be part of a type
937
- let depth = 0;
938
- while (i < src.length) {
939
- const tt = src[i].type;
940
- if (tt === T.LBRACKET) {
941
- depth++;
942
- i++;
943
- continue;
944
- }
945
- if (tt === T.RBRACKET) {
946
- depth--;
947
- i++;
948
- continue;
949
- }
950
- if (depth > 0) {
951
- i++;
952
- continue;
953
- }
954
- if (
955
- tt === T.MAP ||
956
- tt === T.IDENT ||
957
- tt === T.STAR ||
958
- tt === T.INT ||
959
- tt === T.FLOAT ||
960
- tt === T.STRING
961
- ) {
962
- i++;
963
- continue;
964
- }
965
- break;
966
- }
967
- // Skip whitespace tokens (shouldn't exist post-lex, but be safe)
968
- while (i < src.length && src[i].type === T.SEMICOLON) i++;
969
- return i < src.length && src[i].type === T.LBRACE;
970
- }
971
-
972
- parseCompositeLit(typeExpr) {
973
- this.expect(T.LBRACE);
974
- const elems = [];
975
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
976
- // Implicit composite literal: {X: 1} inside []Point{{X:1}} or map[string]Point{"a": {X:1}}
977
- if (this.check(T.LBRACE)) {
978
- elems.push(this.parseCompositeLit(null)); // null typeExpr = inferred
979
- } else {
980
- const first = this.parseExpr();
981
- if (this.match(T.COLON)) {
982
- // Map key: value — value may also be an implicit composite lit
983
- const val = this.check(T.LBRACE)
984
- ? this.parseCompositeLit(null)
985
- : this.parseExpr();
986
- elems.push({ kind: "KeyValueExpr", key: first, value: val });
987
- } else {
988
- elems.push(first);
989
- }
990
- }
991
- this.match(T.COMMA);
992
- }
993
- this.expect(T.RBRACE);
994
- return { kind: "CompositeLit", typeExpr, elems };
995
- }
996
-
997
- parsePrimary() {
998
- const line = this.peek().line;
999
- const expr = this._parsePrimary();
1000
- if (!expr._line) expr._line = line;
1001
- return expr;
1002
- }
1003
-
1004
- _parsePrimary() {
1005
- const t = this.peek();
1006
-
1007
- // Literals
1008
- if (t.type === T.INT) {
1009
- this.advance();
1010
- return { kind: "BasicLit", litKind: "INT", value: t.value };
1011
- }
1012
- if (t.type === T.FLOAT) {
1013
- this.advance();
1014
- return { kind: "BasicLit", litKind: "FLOAT", value: t.value };
1015
- }
1016
- if (t.type === T.STRING) {
1017
- this.advance();
1018
- return { kind: "BasicLit", litKind: "STRING", value: t.value };
1019
- }
1020
- if (t.type === T.TRUE) {
1021
- this.advance();
1022
- return { kind: "BasicLit", litKind: "BOOL", value: "true" };
1023
- }
1024
- if (t.type === T.FALSE) {
1025
- this.advance();
1026
- return { kind: "BasicLit", litKind: "BOOL", value: "false" };
1027
- }
1028
- if (t.type === T.NIL) {
1029
- this.advance();
1030
- return { kind: "BasicLit", litKind: "NIL", value: "null" };
1031
- }
1032
-
1033
- // Parenthesised expression
1034
- if (t.type === T.LPAREN) {
1035
- this.advance();
1036
- const expr = this.parseExpr();
1037
- this.expect(T.RPAREN);
1038
- return expr;
1039
- }
1040
-
1041
- // Function literal (sync or async)
1042
- if (t.type === T.FUNC || t.type === T.ASYNC) {
1043
- const isAsync = t.type === T.ASYNC;
1044
- this.advance();
1045
- if (isAsync) this.expect(T.FUNC);
1046
- const params = this.parseParamList();
1047
- const returnType = this.parseReturnType();
1048
- const body = this.parseBlock();
1049
- return { kind: "FuncLit", params, returnType, body, async: isAsync };
1050
- }
1051
-
1052
- // range expression (only valid inside a for statement)
1053
- if (t.type === T.RANGE) {
1054
- this.advance();
1055
- // Always use noCompLit here: "range slice {" — { is the for body, not a composite lit
1056
- return { kind: "RangeExpr", expr: this.parseExpr(0, true) };
1057
- }
1058
-
1059
- // Builtin / identifier
1060
- if (
1061
- t.type === T.IDENT ||
1062
- this.isTypeKeyword(t) ||
1063
- this.isBuiltinKeyword(t)
1064
- ) {
1065
- this.advance();
1066
- // Type conversion or composite lit: Type(expr) or Type{...}
1067
- if (this.isTypeKeyword(t) || this.check(T.LPAREN)) {
1068
- if (this.check(T.LPAREN) && this.isTypeKeyword(t)) {
1069
- // type conversion: int(x), string(x), etc.
1070
- this.advance();
1071
- const expr = this.parseExpr();
1072
- this.expect(T.RPAREN);
1073
- return {
1074
- kind: "TypeConversion",
1075
- targetType: { kind: "TypeName", name: t.value },
1076
- expr,
1077
- };
1078
- }
1079
- }
1080
- return { kind: "Ident", name: t.value };
1081
- }
1082
-
1083
- // Slice/array/map composite literals: []int{1,2}, map[string]int{"a":1}
1084
- if (t.type === T.LBRACKET || t.type === T.MAP) {
1085
- const typeExpr = this.parseType();
1086
- return this.parseCompositeLit(typeExpr);
1087
- }
1088
-
1089
- this.err(`Unexpected token in expression: '${t.value}'`);
1090
- }
1091
-
1092
- isTypeKeyword(t) {
1093
- return [
1094
- "int",
1095
- "float64",
1096
- "string",
1097
- "bool",
1098
- "any",
1099
- "byte",
1100
- "rune",
1101
- "error",
1102
- ].includes(t.value);
1103
- }
1104
-
1105
- isBuiltinKeyword(t) {
1106
- return [
1107
- "new",
1108
- "make",
1109
- "len",
1110
- "cap",
1111
- "append",
1112
- "delete",
1113
- "copy",
1114
- "print",
1115
- "println",
1116
- "panic",
1117
- "error",
1118
- ].includes(t.value);
1119
- }
1120
428
  }
429
+
430
+ Object.assign(Parser.prototype, typeParserMethods);
431
+ Object.assign(Parser.prototype, statementParserMethods);
432
+ Object.assign(Parser.prototype, expressionParserMethods);