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/README.md +175 -51
- package/package.json +7 -3
- package/src/codegen/expressions.js +804 -0
- package/src/codegen/source-map.js +45 -0
- package/src/codegen/statements.js +406 -0
- package/src/codegen.js +34 -990
- package/src/compiler.js +36 -22
- package/src/index.js +3 -3
- package/src/lexer.js +94 -6
- package/src/parser/expressions.js +389 -0
- package/src/parser/statements.js +355 -0
- package/src/parser/types.js +140 -0
- package/src/parser.js +40 -853
- package/src/typechecker/expressions.js +359 -0
- package/src/typechecker/statements.js +250 -0
- package/src/typechecker/types.js +226 -0
- package/src/typechecker.js +352 -703
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))
|
|
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
|
|
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);
|