gofront 0.0.2 → 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
 
@@ -131,9 +121,16 @@ export class Parser {
131
121
  this.expect(T.IMPORT);
132
122
  const imports = [];
133
123
  const parseOne = () => {
134
- const alias = this.check(T.IDENT) ? this.advance().value : null;
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
+ }
135
132
  const path = this.expect(T.STRING).value;
136
- return { path, alias };
133
+ return { path, alias, _line };
137
134
  };
138
135
  if (this.match(T.LPAREN)) {
139
136
  while (!this.check(T.RPAREN) && !this.check(T.EOF)) {
@@ -281,17 +278,7 @@ export class Parser {
281
278
  tok.type === T.FUNC ||
282
279
  tok.type === T.INTERFACE ||
283
280
  tok.type === T.STRUCT ||
284
- (tok.type === T.IDENT && tok.value !== "_") ||
285
- [
286
- "int",
287
- "float64",
288
- "string",
289
- "bool",
290
- "any",
291
- "byte",
292
- "rune",
293
- "error",
294
- ].includes(tok.value)
281
+ (tok.type === T.IDENT && tok.value !== "_")
295
282
  );
296
283
  }
297
284
 
@@ -339,6 +326,19 @@ export class Parser {
339
326
 
340
327
  parseTypeDecl() {
341
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
+ }
342
342
  const name = this.expect(T.IDENT).value;
343
343
  const isAlias = this.match(T.ASSIGN);
344
344
  const type = this.parseType();
@@ -425,821 +425,8 @@ export class Parser {
425
425
  this.semi();
426
426
  return { kind: "ConstDecl", decls };
427
427
  }
428
-
429
- // ── Types ────────────────────────────────────────────────────
430
-
431
- parseType() {
432
- const t = this.peek();
433
- switch (t.type) {
434
- case T.LBRACKET:
435
- return this.parseSliceOrArrayType();
436
- case T.MAP:
437
- return this.parseMapType();
438
- case T.FUNC:
439
- return this.parseFuncType();
440
- case T.STRUCT:
441
- return this.parseStructType();
442
- case T.INTERFACE:
443
- return this.parseInterfaceType();
444
- case T.STAR:
445
- this.advance();
446
- return { kind: "PointerType", base: this.parseType() };
447
- case T.IDENT:
448
- case "int":
449
- case "float64":
450
- case "string":
451
- case "bool":
452
- case "any":
453
- case "byte":
454
- case "rune":
455
- case "error":
456
- return this.parseTypeName();
457
- default:
458
- this.err(`Expected type, got '${t.value}'`);
459
- }
460
- }
461
-
462
- parseTypeName() {
463
- const t = this.advance();
464
- let name = t.value;
465
- // qualified: pkg.Type
466
- if (this.check(T.DOT)) {
467
- this.advance();
468
- name += `.${this.expect(T.IDENT).value}`;
469
- }
470
- return { kind: "TypeName", name };
471
- }
472
-
473
- parseSliceOrArrayType() {
474
- this.expect(T.LBRACKET);
475
- if (this.check(T.RBRACKET)) {
476
- // slice type: []T
477
- this.advance();
478
- return { kind: "SliceType", elem: this.parseType() };
479
- }
480
- if (this.match(T.ELLIPSIS)) {
481
- // [...]T — array with inferred length from composite literal
482
- this.expect(T.RBRACKET);
483
- return { kind: "ArrayType", inferLen: true, elem: this.parseType() };
484
- }
485
- // fixed array: [n]T — treated as slice for JS purposes
486
- const size = this.parseExpr();
487
- this.expect(T.RBRACKET);
488
- return { kind: "ArrayType", size, elem: this.parseType() };
489
- }
490
-
491
- parseMapType() {
492
- this.expect(T.MAP);
493
- this.expect(T.LBRACKET);
494
- const key = this.parseType();
495
- this.expect(T.RBRACKET);
496
- const value = this.parseType();
497
- return { kind: "MapType", key, value };
498
- }
499
-
500
- parseFuncType() {
501
- this.expect(T.FUNC);
502
- const params = this.parseParamList();
503
- const returnType = this.parseReturnType(true);
504
- return { kind: "FuncType", params, returnType };
505
- }
506
-
507
- parseStructType() {
508
- this.expect(T.STRUCT);
509
- this.expect(T.LBRACE);
510
- const fields = [];
511
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
512
- const names = [this.expect(T.IDENT).value];
513
- while (this.match(T.COMMA)) names.push(this.expect(T.IDENT).value);
514
- // Check if this is an embedded field (just a type name)
515
- // e.g., type T struct { Base }
516
- if (this.check(T.SEMICOLON) || this.check(T.RBRACE)) {
517
- const typeName = names[0]; // assuming no comma list for embedded
518
- fields.push({
519
- names: [],
520
- type: { kind: "TypeName", name: typeName },
521
- embedded: true,
522
- });
523
- this.semi();
524
- continue;
525
- }
526
- const type = this.parseType();
527
- // Consume optional struct tag (backtick string), e.g. `json:"name"`
528
- if (this.check(T.STRING)) this.advance();
529
- fields.push({ names, type });
530
- this.semi();
531
- }
532
- this.expect(T.RBRACE);
533
- return { kind: "StructType", fields };
534
- }
535
-
536
- parseInterfaceType() {
537
- this.expect(T.INTERFACE);
538
- this.expect(T.LBRACE);
539
- const methods = [];
540
- const embeds = [];
541
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
542
- const name = this.expect(T.IDENT).value;
543
- if (this.check(T.LPAREN)) {
544
- // Method signature: Name(params) returnType
545
- const params = this.parseParamList();
546
- const returnType = this.parseReturnType();
547
- methods.push({ name, params, returnType });
548
- } else {
549
- // Embedded interface: TypeName or pkg.TypeName
550
- let typeName = name;
551
- if (this.check(T.DOT)) {
552
- this.advance();
553
- typeName += `.${this.expect(T.IDENT).value}`;
554
- }
555
- embeds.push({ kind: "TypeName", name: typeName });
556
- }
557
- this.semi();
558
- }
559
- this.expect(T.RBRACE);
560
- return { kind: "InterfaceType", methods, embeds };
561
- }
562
-
563
- // ── Statements ───────────────────────────────────────────────
564
-
565
- parseBlock() {
566
- this.expect(T.LBRACE);
567
- const stmts = [];
568
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
569
- stmts.push(this.parseStmt());
570
- this.semi();
571
- }
572
- this.expect(T.RBRACE);
573
- return { kind: "Block", stmts };
574
- }
575
-
576
- parseStmt() {
577
- const t = this.peek();
578
- const stmt = this._parseStmt(t);
579
- stmt._line = t.line;
580
- return stmt;
581
- }
582
-
583
- _parseStmt(t) {
584
- switch (t.type) {
585
- case T.VAR:
586
- return this.parseVarDecl();
587
- case T.CONST:
588
- return this.parseConstDecl();
589
- case T.TYPE:
590
- return this.parseTypeDecl();
591
- case T.RETURN:
592
- return this.parseReturn();
593
- case T.DEFER:
594
- return this.parseDefer();
595
- case T.BREAK:
596
- this.advance();
597
- if (this.check(T.IDENT)) {
598
- const label = this.advance().value;
599
- this.semi();
600
- return { kind: "BranchStmt", keyword: "break", label };
601
- }
602
- this.semi();
603
- return { kind: "BranchStmt", keyword: "break" };
604
- case T.CONTINUE:
605
- this.advance();
606
- if (this.check(T.IDENT)) {
607
- const label = this.advance().value;
608
- this.semi();
609
- return { kind: "BranchStmt", keyword: "continue", label };
610
- }
611
- this.semi();
612
- return { kind: "BranchStmt", keyword: "continue" };
613
- case T.FALLTHROUGH:
614
- this.advance();
615
- this.semi();
616
- return { kind: "BranchStmt", keyword: "fallthrough" };
617
- case T.IF:
618
- return this.parseIf();
619
- case T.FOR:
620
- return this.parseFor();
621
- case T.SWITCH:
622
- return this.parseSwitch();
623
- case T.LBRACE:
624
- return this.parseBlock();
625
- case T.IDENT:
626
- if (this.check2(T.COLON)) {
627
- const label = this.advance().value; // consume IDENT
628
- this.advance(); // consume COLON
629
- const body = this.parseStmt();
630
- return { kind: "LabeledStmt", label, body };
631
- }
632
- return this.parseSimpleStmt();
633
- default:
634
- return this.parseSimpleStmt();
635
- }
636
- }
637
-
638
- parseDefer() {
639
- this.expect(T.DEFER);
640
- const call = this.parseExpr();
641
- return { kind: "DeferStmt", call };
642
- }
643
-
644
- parseReturn() {
645
- this.expect(T.RETURN);
646
- if (this.check(T.SEMICOLON) || this.check(T.RBRACE)) {
647
- return { kind: "ReturnStmt", values: [] };
648
- }
649
- const values = this.parseExprList();
650
- return { kind: "ReturnStmt", values };
651
- }
652
-
653
- parseIf() {
654
- this.expect(T.IF);
655
- let init = null;
656
- let cond;
657
- // if init; cond { } — use noCompLit to avoid "{" being parsed as composite literal
658
- const first = this.parseSimpleStmtRaw(true);
659
- // Only treat as "init; cond" if semicolon is followed by a non-brace token
660
- if (this.check(T.SEMICOLON) && this.peek2().type !== T.LBRACE) {
661
- this.advance(); // consume ;
662
- init = first;
663
- cond = this.parseExpr(0, true);
664
- } else {
665
- this.match(T.SEMICOLON); // consume any auto-inserted ;
666
- cond = this.stmtToExpr(first);
667
- }
668
- const body = this.parseBlock();
669
- // Consume auto-inserted ; before else (Go allows "}\nelse")
670
- this.match(T.SEMICOLON);
671
- let elseBody = null;
672
- if (this.match(T.ELSE)) {
673
- elseBody = this.check(T.IF) ? this.parseIf() : this.parseBlock();
674
- }
675
- return { kind: "IfStmt", init, cond, body, elseBody };
676
- }
677
-
678
- parseFor() {
679
- this.expect(T.FOR);
680
-
681
- // for { } — infinite loop
682
- if (this.check(T.LBRACE)) {
683
- return {
684
- kind: "ForStmt",
685
- init: null,
686
- cond: null,
687
- post: null,
688
- body: this.parseBlock(),
689
- };
690
- }
691
-
692
- const first = this.parseSimpleStmtRaw(true);
693
-
694
- if (this.check(T.LBRACE)) {
695
- // for k, v := range expr { } — range stmt ends right before {
696
- if (this.isRangeStmt(first)) {
697
- return {
698
- kind: "ForStmt",
699
- init: first,
700
- cond: null,
701
- post: null,
702
- body: this.parseBlock(),
703
- };
704
- }
705
- // for cond { }
706
- return {
707
- kind: "ForStmt",
708
- init: null,
709
- cond: this.stmtToExpr(first),
710
- post: null,
711
- body: this.parseBlock(),
712
- };
713
- }
714
-
715
- this.expect(T.SEMICOLON);
716
-
717
- // for init; cond; post { }
718
- let cond = null;
719
- if (!this.check(T.SEMICOLON)) cond = this.parseExpr(0, true);
720
- this.expect(T.SEMICOLON);
721
- let post = null;
722
- if (!this.check(T.LBRACE)) post = this.parseSimpleStmtRaw();
723
- const body = this.parseBlock();
724
- return { kind: "ForStmt", init: first, cond, post, body };
725
- }
726
-
727
- isRangeStmt(stmt) {
728
- if (!stmt) return false;
729
- if (stmt.kind !== "DefineStmt" && stmt.kind !== "AssignStmt") return false;
730
- return stmt.rhs?.[0]?.kind === "RangeExpr";
731
- }
732
-
733
- parseSwitch() {
734
- this.expect(T.SWITCH);
735
- let init = null;
736
- let tag = null;
737
- if (!this.check(T.LBRACE)) {
738
- const first = this.parseSimpleStmtRaw(true);
739
- if (this.match(T.SEMICOLON)) {
740
- init = first;
741
- if (!this.check(T.LBRACE))
742
- tag = this.stmtToExpr(this.parseSimpleStmtRaw(true));
743
- } else {
744
- tag = this.stmtToExpr(first);
745
- }
746
- }
747
-
748
- // Detect type switch: switch x.(type) or switch v := x.(type)
749
- if (tag?.kind === "TypeSwitchExpr") {
750
- return this._parseTypeSwitchBody(null, tag.expr);
751
- }
752
- if (tag?.kind === "DefineStmt" && tag.rhs[0]?.kind === "TypeSwitchExpr") {
753
- return this._parseTypeSwitchBody(
754
- tag.lhs[0]?.name ?? null,
755
- tag.rhs[0].expr,
756
- );
757
- }
758
-
759
- this.expect(T.LBRACE);
760
- const cases = [];
761
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
762
- if (this.check(T.CASE)) {
763
- this.advance();
764
- const list = this.parseExprList();
765
- this.expect(T.COLON);
766
- cases.push({ kind: "CaseClause", list, stmts: this._parseCaseBody() });
767
- } else if (this.check(T.DEFAULT)) {
768
- this.advance();
769
- this.expect(T.COLON);
770
- cases.push({
771
- kind: "CaseClause",
772
- list: null,
773
- stmts: this._parseCaseBody(),
774
- });
775
- } else {
776
- this.err("Expected case or default");
777
- }
778
- }
779
- this.expect(T.RBRACE);
780
- return { kind: "SwitchStmt", init, tag, cases };
781
- }
782
-
783
- _parseCaseBody() {
784
- const stmts = [];
785
- while (
786
- !this.check(T.CASE) &&
787
- !this.check(T.DEFAULT) &&
788
- !this.check(T.RBRACE)
789
- ) {
790
- stmts.push(this.parseStmt());
791
- this.semi();
792
- }
793
- return stmts;
794
- }
795
-
796
- _parseTypeSwitchBody(assign, expr) {
797
- this.expect(T.LBRACE);
798
- const cases = [];
799
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
800
- if (this.check(T.CASE)) {
801
- this.advance();
802
- const types = this._parseTypeSwitchTypeList();
803
- this.expect(T.COLON);
804
- cases.push({ types, stmts: this._parseCaseBody() });
805
- } else if (this.check(T.DEFAULT)) {
806
- this.advance();
807
- this.expect(T.COLON);
808
- cases.push({ types: null, stmts: this._parseCaseBody() });
809
- } else {
810
- this.err("Expected case or default");
811
- }
812
- }
813
- this.expect(T.RBRACE);
814
- return { kind: "TypeSwitchStmt", assign, expr, cases };
815
- }
816
-
817
- _parseTypeSwitchTypeList() {
818
- const parseOne = () => {
819
- // nil is a keyword, not a type name, so handle it explicitly
820
- if (this.check(T.NIL)) {
821
- this.advance();
822
- return { kind: "TypeName", name: "nil" };
823
- }
824
- return this.parseType();
825
- };
826
- const types = [parseOne()];
827
- while (this.match(T.COMMA)) types.push(parseOne());
828
- return types;
829
- }
830
-
831
- // Parses a "simple statement": assignment, define, inc/dec, or expression.
832
- // Returns a statement node.
833
- parseSimpleStmt() {
834
- const s = this.parseSimpleStmtRaw();
835
- return s;
836
- }
837
-
838
- parseSimpleStmtRaw(noCompLit = false) {
839
- // Parse left-hand side (could be expr list for multi-assign)
840
- const first = this.parseExpr(0, noCompLit);
841
- const t = this.peek();
842
-
843
- // a, b, c := ... or x, y = ...
844
- if (t.type === T.COMMA) {
845
- const lhs = [first];
846
- while (this.match(T.COMMA)) lhs.push(this.parseExpr());
847
- const op = this.advance(); // := or =
848
- const rhs = this.parseExprList();
849
- if (op.type === T.DEFINE) return { kind: "DefineStmt", lhs, rhs };
850
- return { kind: "AssignStmt", lhs, op: "=", rhs };
851
- }
852
-
853
- // x := expr
854
- if (t.type === T.DEFINE) {
855
- this.advance();
856
- const rhs = this.parseExprList();
857
- return { kind: "DefineStmt", lhs: [first], rhs };
858
- }
859
-
860
- // x = expr or x += expr etc.
861
- const assignOps = [
862
- T.ASSIGN,
863
- T.PLUS_ASSIGN,
864
- T.MINUS_ASSIGN,
865
- T.STAR_ASSIGN,
866
- T.SLASH_ASSIGN,
867
- T.PERCENT_ASSIGN,
868
- T.AMP_ASSIGN,
869
- T.PIPE_ASSIGN,
870
- T.CARET_ASSIGN,
871
- T.LSHIFT_ASSIGN,
872
- T.RSHIFT_ASSIGN,
873
- ];
874
- if (assignOps.includes(t.type)) {
875
- const op = this.advance().value;
876
- const rhs = this.parseExprList();
877
- return { kind: "AssignStmt", lhs: [first], op, rhs };
878
- }
879
-
880
- // x++ x--
881
- if (t.type === T.INC || t.type === T.DEC) {
882
- const op = this.advance().value;
883
- return { kind: "IncDecStmt", expr: first, op };
884
- }
885
-
886
- // for range detection: "for k, v := range expr"
887
- // range keyword after :=
888
- if (first.kind === "Ident" && first.name === "range") {
889
- const expr = this.parseExpr();
890
- return { kind: "RangeExpr", expr };
891
- }
892
-
893
- return { kind: "ExprStmt", expr: first };
894
- }
895
-
896
- // Converts an ExprStmt back to its expression
897
- stmtToExpr(stmt) {
898
- if (!stmt) return null;
899
- if (stmt.kind === "ExprStmt") return stmt.expr;
900
- return stmt;
901
- }
902
-
903
- // ── Expressions ──────────────────────────────────────────────
904
-
905
- parseExprList() {
906
- const exprs = [this.parseExpr()];
907
- while (this.match(T.COMMA)) exprs.push(this.parseExpr());
908
- return exprs;
909
- }
910
-
911
- // noCompLit=true disables composite literal parsing in if/for/switch conditions
912
- // to resolve the ambiguity between "if x {" and "if Type{ } {"
913
- parseExpr(minPrec = 0, noCompLit = false) {
914
- let left = this.parseUnary(noCompLit);
915
- while (true) {
916
- const prec = PREC[this.peek().type];
917
- if (!prec || prec <= minPrec) break;
918
- const op = this.advance().value;
919
- const right = this.parseExpr(prec, noCompLit);
920
- left = { kind: "BinaryExpr", left, op, right };
921
- }
922
- return left;
923
- }
924
-
925
- parseUnary(noCompLit = false) {
926
- const t = this.peek();
927
- if (
928
- t.type === T.NOT ||
929
- t.type === T.MINUS ||
930
- t.type === T.PLUS ||
931
- t.type === T.CARET ||
932
- t.type === T.AMP ||
933
- t.type === T.STAR
934
- ) {
935
- const op = this.advance().value;
936
- return { kind: "UnaryExpr", op, operand: this.parseUnary(noCompLit) };
937
- }
938
- if (t.type === T.AWAIT) {
939
- this.advance();
940
- return { kind: "AwaitExpr", expr: this.parseUnary(noCompLit) };
941
- }
942
- return this.parsePostfix(noCompLit);
943
- }
944
-
945
- parsePostfix(noCompLit = false) {
946
- let expr = this.parsePrimary();
947
- while (true) {
948
- const t = this.peek();
949
- if (t.type === T.DOT) {
950
- this.advance();
951
- // type assertion: expr.(Type)
952
- if (this.check(T.LPAREN)) {
953
- this.advance();
954
- // x.(type) — type switch guard expression
955
- if (this.check(T.TYPE)) {
956
- this.advance();
957
- this.expect(T.RPAREN);
958
- expr = { kind: "TypeSwitchExpr", expr };
959
- } else {
960
- const type = this.parseType();
961
- this.expect(T.RPAREN);
962
- expr = { kind: "TypeAssertExpr", expr, type };
963
- }
964
- } else {
965
- const field = this.advance().value;
966
- expr = { kind: "SelectorExpr", expr, field };
967
- }
968
- } else if (t.type === T.LBRACKET) {
969
- this.advance();
970
- const low = this.check(T.COLON) ? null : this.parseExpr();
971
- if (this.match(T.COLON)) {
972
- const high = this.check(T.RBRACKET) ? null : this.parseExpr();
973
- this.expect(T.RBRACKET);
974
- expr = { kind: "SliceExpr", expr, low, high };
975
- } else {
976
- this.expect(T.RBRACKET);
977
- expr = { kind: "IndexExpr", expr, index: low };
978
- }
979
- } else if (t.type === T.LPAREN) {
980
- expr = this.parseCall(expr);
981
- } else if (
982
- t.type === T.LBRACE &&
983
- !noCompLit &&
984
- this.isCompositeLitContext(expr)
985
- ) {
986
- expr = this.parseCompositeLit(expr);
987
- } else {
988
- break;
989
- }
990
- }
991
- return expr;
992
- }
993
-
994
- // Composite literals are only allowed after a type name (not arbitrary exprs)
995
- isCompositeLitContext(expr) {
996
- return (
997
- expr.kind === "Ident" ||
998
- expr.kind === "SelectorExpr" ||
999
- expr.kind === "TypeExpr"
1000
- );
1001
- }
1002
-
1003
- parseCall(fn) {
1004
- this.expect(T.LPAREN);
1005
- const args = [];
1006
- if (!this.check(T.RPAREN)) {
1007
- do {
1008
- if (this.check(T.RPAREN)) break;
1009
- // map[K]V and []T as call args must be parsed as types, not expressions,
1010
- // when they are NOT followed by { (which would make them composite literals).
1011
- // Needed for make(map[K]V), make([]T, n), new(T), etc.
1012
- const t = this.peek();
1013
- const isSliceOrArrayType =
1014
- t.type === T.LBRACKET &&
1015
- (this.peek2()?.type === T.RBRACKET || // []T
1016
- this.peek2()?.type === T.INT); // [n]T
1017
- if (
1018
- (t.type === T.MAP || isSliceOrArrayType) &&
1019
- !this.looksLikeCompositeLitArg()
1020
- ) {
1021
- const typeNode = this.parseType();
1022
- // []byte(s) / []rune(s) used as a call argument — it's a type conversion
1023
- if (this.check(T.LPAREN)) {
1024
- this.advance();
1025
- const convExpr = this.parseExpr();
1026
- this.expect(T.RPAREN);
1027
- args.push({
1028
- kind: "TypeConversion",
1029
- targetType: typeNode,
1030
- expr: convExpr,
1031
- });
1032
- } else {
1033
- args.push({ kind: "TypeExpr", type: typeNode });
1034
- }
1035
- } else {
1036
- const arg = this.parseExpr();
1037
- if (this.match(T.ELLIPSIS)) arg._spread = true;
1038
- args.push(arg);
1039
- }
1040
- } while (this.match(T.COMMA));
1041
- }
1042
- this.expect(T.RPAREN);
1043
- return { kind: "CallExpr", func: fn, args };
1044
- }
1045
-
1046
- // Returns true if the current position looks like a composite literal argument,
1047
- // e.g. []int{1,2,3} or map[string]int{"a":1}. We scan ahead past the type
1048
- // tokens to check whether a { follows, without advancing this.pos permanently.
1049
- looksLikeCompositeLitArg() {
1050
- let i = this.pos;
1051
- const src = this.tokens;
1052
- // Skip over the type tokens: [, ], map, [, K, ], V, ident, etc.
1053
- // Simple approach: skip until we hit something that can't be part of a type
1054
- let depth = 0;
1055
- while (i < src.length) {
1056
- const tt = src[i].type;
1057
- if (tt === T.LBRACKET) {
1058
- depth++;
1059
- i++;
1060
- continue;
1061
- }
1062
- if (tt === T.RBRACKET) {
1063
- depth--;
1064
- i++;
1065
- continue;
1066
- }
1067
- if (depth > 0) {
1068
- i++;
1069
- continue;
1070
- }
1071
- if (
1072
- tt === T.MAP ||
1073
- tt === T.IDENT ||
1074
- tt === T.STAR ||
1075
- tt === T.INT ||
1076
- tt === T.FLOAT ||
1077
- tt === T.STRING
1078
- ) {
1079
- i++;
1080
- continue;
1081
- }
1082
- break;
1083
- }
1084
- // Skip whitespace tokens (shouldn't exist post-lex, but be safe)
1085
- while (i < src.length && src[i].type === T.SEMICOLON) i++;
1086
- return i < src.length && src[i].type === T.LBRACE;
1087
- }
1088
-
1089
- parseCompositeLit(typeExpr) {
1090
- this.expect(T.LBRACE);
1091
- const elems = [];
1092
- while (!this.check(T.RBRACE) && !this.check(T.EOF)) {
1093
- // Implicit composite literal: {X: 1} inside []Point{{X:1}} or map[string]Point{"a": {X:1}}
1094
- if (this.check(T.LBRACE)) {
1095
- elems.push(this.parseCompositeLit(null)); // null typeExpr = inferred
1096
- } else {
1097
- const first = this.parseExpr();
1098
- if (this.match(T.COLON)) {
1099
- // Map key: value — value may also be an implicit composite lit
1100
- const val = this.check(T.LBRACE)
1101
- ? this.parseCompositeLit(null)
1102
- : this.parseExpr();
1103
- elems.push({ kind: "KeyValueExpr", key: first, value: val });
1104
- } else {
1105
- elems.push(first);
1106
- }
1107
- }
1108
- this.match(T.COMMA);
1109
- }
1110
- this.expect(T.RBRACE);
1111
- return { kind: "CompositeLit", typeExpr, elems };
1112
- }
1113
-
1114
- parsePrimary() {
1115
- const line = this.peek().line;
1116
- const expr = this._parsePrimary();
1117
- if (!expr._line) expr._line = line;
1118
- return expr;
1119
- }
1120
-
1121
- _parsePrimary() {
1122
- const t = this.peek();
1123
-
1124
- // Literals
1125
- if (t.type === T.INT) {
1126
- this.advance();
1127
- return { kind: "BasicLit", litKind: "INT", value: t.value };
1128
- }
1129
- if (t.type === T.FLOAT) {
1130
- this.advance();
1131
- return { kind: "BasicLit", litKind: "FLOAT", value: t.value };
1132
- }
1133
- if (t.type === T.STRING) {
1134
- this.advance();
1135
- return { kind: "BasicLit", litKind: "STRING", value: t.value };
1136
- }
1137
- if (t.type === T.TRUE) {
1138
- this.advance();
1139
- return { kind: "BasicLit", litKind: "BOOL", value: "true" };
1140
- }
1141
- if (t.type === T.FALSE) {
1142
- this.advance();
1143
- return { kind: "BasicLit", litKind: "BOOL", value: "false" };
1144
- }
1145
- if (t.type === T.NIL) {
1146
- this.advance();
1147
- return { kind: "BasicLit", litKind: "NIL", value: "null" };
1148
- }
1149
-
1150
- // Parenthesised expression
1151
- if (t.type === T.LPAREN) {
1152
- this.advance();
1153
- const expr = this.parseExpr();
1154
- this.expect(T.RPAREN);
1155
- return expr;
1156
- }
1157
-
1158
- // Function literal (sync or async)
1159
- if (t.type === T.FUNC || t.type === T.ASYNC) {
1160
- const isAsync = t.type === T.ASYNC;
1161
- this.advance();
1162
- if (isAsync) this.expect(T.FUNC);
1163
- const params = this.parseParamList();
1164
- const returnType = this.parseReturnType();
1165
- const body = this.parseBlock();
1166
- return { kind: "FuncLit", params, returnType, body, async: isAsync };
1167
- }
1168
-
1169
- // range expression (only valid inside a for statement)
1170
- if (t.type === T.RANGE) {
1171
- this.advance();
1172
- // Always use noCompLit here: "range slice {" — { is the for body, not a composite lit
1173
- return { kind: "RangeExpr", expr: this.parseExpr(0, true) };
1174
- }
1175
-
1176
- // Builtin / identifier
1177
- if (
1178
- t.type === T.IDENT ||
1179
- this.isTypeKeyword(t) ||
1180
- this.isBuiltinKeyword(t)
1181
- ) {
1182
- this.advance();
1183
- // Type conversion or composite lit: Type(expr) or Type{...}
1184
- if (this.isTypeKeyword(t) || this.check(T.LPAREN)) {
1185
- if (this.check(T.LPAREN) && this.isTypeKeyword(t)) {
1186
- // type conversion: int(x), string(x), etc.
1187
- this.advance();
1188
- const expr = this.parseExpr();
1189
- this.expect(T.RPAREN);
1190
- return {
1191
- kind: "TypeConversion",
1192
- targetType: { kind: "TypeName", name: t.value },
1193
- expr,
1194
- };
1195
- }
1196
- }
1197
- return { kind: "Ident", name: t.value };
1198
- }
1199
-
1200
- // Slice/array/map composite literals or type conversions:
1201
- // []int{1,2} → CompositeLit
1202
- // []byte(s), []rune(s) → TypeConversion
1203
- if (t.type === T.LBRACKET || t.type === T.MAP) {
1204
- const typeExpr = this.parseType();
1205
- if (this.check(T.LPAREN)) {
1206
- this.advance();
1207
- const expr = this.parseExpr();
1208
- this.expect(T.RPAREN);
1209
- return { kind: "TypeConversion", targetType: typeExpr, expr };
1210
- }
1211
- return this.parseCompositeLit(typeExpr);
1212
- }
1213
-
1214
- this.err(`Unexpected token in expression: '${t.value}'`);
1215
- }
1216
-
1217
- isTypeKeyword(t) {
1218
- return [
1219
- "int",
1220
- "float64",
1221
- "string",
1222
- "bool",
1223
- "any",
1224
- "byte",
1225
- "rune",
1226
- "error",
1227
- ].includes(t.value);
1228
- }
1229
-
1230
- isBuiltinKeyword(t) {
1231
- return [
1232
- "new",
1233
- "make",
1234
- "len",
1235
- "cap",
1236
- "append",
1237
- "delete",
1238
- "copy",
1239
- "print",
1240
- "println",
1241
- "panic",
1242
- "error",
1243
- ].includes(t.value);
1244
- }
1245
428
  }
429
+
430
+ Object.assign(Parser.prototype, typeParserMethods);
431
+ Object.assign(Parser.prototype, statementParserMethods);
432
+ Object.assign(Parser.prototype, expressionParserMethods);