gofront 0.0.1 → 0.0.2
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 +209 -9
- package/package.json +1 -1
- package/src/codegen.js +198 -43
- package/src/compiler.js +13 -6
- package/src/dts-parser.js +0 -4
- package/src/index.js +8 -17
- package/src/lexer.js +76 -6
- package/src/parser.js +172 -47
- package/src/resolver.js +1 -1
- package/src/typechecker.js +107 -13
package/src/codegen.js
CHANGED
|
@@ -210,10 +210,6 @@ export class CodeGen {
|
|
|
210
210
|
|
|
211
211
|
// ── Type declarations ────────────────────────────────────────
|
|
212
212
|
|
|
213
|
-
genTypeDecl(decl) {
|
|
214
|
-
this.genTypeDeclWithMethods(decl, []);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
213
|
genTypeDeclWithMethods(decl, methodDecls) {
|
|
218
214
|
if (decl.type.kind === "StructType") {
|
|
219
215
|
this.genStruct(decl.name, decl.type, methodDecls);
|
|
@@ -339,22 +335,36 @@ export class CodeGen {
|
|
|
339
335
|
this.namedReturnVars = prev;
|
|
340
336
|
}
|
|
341
337
|
|
|
342
|
-
// Emit a function body, wrapping in try/finally for defer if needed.
|
|
338
|
+
// Emit a function body, wrapping in try/catch/finally for defer if needed.
|
|
343
339
|
_genBody(body) {
|
|
344
340
|
if (!this._hasDefer(body)) {
|
|
345
|
-
this.genBlock(body
|
|
341
|
+
this.genBlock(body);
|
|
346
342
|
return;
|
|
347
343
|
}
|
|
348
344
|
this.line("const __defers = [];");
|
|
345
|
+
this.line("let __panic = null;");
|
|
349
346
|
this.line("try {");
|
|
350
|
-
this.indented(() => this.genBlock(body
|
|
347
|
+
this.indented(() => this.genBlock(body));
|
|
348
|
+
this.line("} catch (__err) {");
|
|
349
|
+
this.indented(() => this.line("__panic = __err;"));
|
|
351
350
|
this.line("} finally {");
|
|
352
351
|
this.indented(() => {
|
|
353
352
|
this.line(
|
|
354
353
|
"for (let __i = __defers.length - 1; __i >= 0; __i--) __defers[__i]();",
|
|
355
354
|
);
|
|
355
|
+
this.line("if (__panic !== null) throw __panic;");
|
|
356
356
|
});
|
|
357
357
|
this.line("}");
|
|
358
|
+
// If a recover() cleared __panic, execution reaches here.
|
|
359
|
+
// Return named return vars so deferred mutations are visible to the caller.
|
|
360
|
+
if (this.namedReturnVars?.length > 0) {
|
|
361
|
+
const vars = this.namedReturnVars;
|
|
362
|
+
this.line(
|
|
363
|
+
vars.length === 1
|
|
364
|
+
? `return ${vars[0]};`
|
|
365
|
+
: `return [${vars.join(", ")}];`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
358
368
|
}
|
|
359
369
|
|
|
360
370
|
// Recursively check if a block (or nested blocks) contains any DeferStmt.
|
|
@@ -410,7 +420,7 @@ export class CodeGen {
|
|
|
410
420
|
|
|
411
421
|
// ── Statements ───────────────────────────────────────────────
|
|
412
422
|
|
|
413
|
-
genBlock(block
|
|
423
|
+
genBlock(block) {
|
|
414
424
|
for (const stmt of block.stmts) this.genStmt(stmt);
|
|
415
425
|
}
|
|
416
426
|
|
|
@@ -426,7 +436,7 @@ export class CodeGen {
|
|
|
426
436
|
this.genConstDecl(stmt);
|
|
427
437
|
break;
|
|
428
438
|
case "TypeDecl":
|
|
429
|
-
this.
|
|
439
|
+
this.genTypeDeclWithMethods(stmt, []);
|
|
430
440
|
break;
|
|
431
441
|
|
|
432
442
|
case "DefineStmt": {
|
|
@@ -550,12 +560,24 @@ export class CodeGen {
|
|
|
550
560
|
this.genSwitch(stmt);
|
|
551
561
|
break;
|
|
552
562
|
|
|
563
|
+
case "TypeSwitchStmt":
|
|
564
|
+
this.genTypeSwitch(stmt);
|
|
565
|
+
break;
|
|
566
|
+
|
|
553
567
|
case "DeferStmt":
|
|
554
568
|
this.line(`__defers.push(() => { ${this.genExpr(stmt.call)}; });`);
|
|
555
569
|
break;
|
|
556
570
|
|
|
571
|
+
case "LabeledStmt":
|
|
572
|
+
this.line(`${stmt.label}:`);
|
|
573
|
+
this.genStmt(stmt.body);
|
|
574
|
+
break;
|
|
575
|
+
|
|
557
576
|
case "BranchStmt":
|
|
558
|
-
if (stmt.keyword !== "fallthrough")
|
|
577
|
+
if (stmt.keyword !== "fallthrough")
|
|
578
|
+
this.line(
|
|
579
|
+
stmt.label ? `${stmt.keyword} ${stmt.label};` : `${stmt.keyword};`,
|
|
580
|
+
);
|
|
559
581
|
// fallthrough: omit — JS switch falls through naturally without a break
|
|
560
582
|
break;
|
|
561
583
|
|
|
@@ -603,6 +625,15 @@ export class CodeGen {
|
|
|
603
625
|
return;
|
|
604
626
|
}
|
|
605
627
|
|
|
628
|
+
// for range N { } — no variable, just repeat N times
|
|
629
|
+
if (stmt.cond?.kind === "RangeExpr") {
|
|
630
|
+
const iteree = this.genExpr(stmt.cond.expr);
|
|
631
|
+
this.line(`for (let _$ = 0; _$ < ${iteree}; _$++) {`);
|
|
632
|
+
this.indented(() => this.genBlock(stmt.body));
|
|
633
|
+
this.line("}");
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
|
|
606
637
|
if (!stmt.init && !stmt.post) {
|
|
607
638
|
if (!stmt.cond) {
|
|
608
639
|
// infinite loop
|
|
@@ -639,6 +670,18 @@ export class CodeGen {
|
|
|
639
670
|
const iterType = range.expr._type;
|
|
640
671
|
|
|
641
672
|
let iterExpr;
|
|
673
|
+
if (
|
|
674
|
+
lhs.length <= 1 &&
|
|
675
|
+
iterType?.kind === "basic" &&
|
|
676
|
+
(iterType.name === "int" || iterType.name === "float64")
|
|
677
|
+
) {
|
|
678
|
+
// integer range (Go 1.22): for i := range n → C-style for loop
|
|
679
|
+
const v = lhs[0] === "_" ? "_$" : lhs[0];
|
|
680
|
+
this.line(`for (let ${v} = 0; ${v} < ${iteree}; ${v}++) {`);
|
|
681
|
+
this.indented(() => this.genBlock(stmt.body));
|
|
682
|
+
this.line("}");
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
642
685
|
if (
|
|
643
686
|
iterType?.kind === "map" ||
|
|
644
687
|
(iterType?.kind === "named" && iterType.underlying?.kind === "map")
|
|
@@ -697,44 +740,82 @@ export class CodeGen {
|
|
|
697
740
|
}
|
|
698
741
|
|
|
699
742
|
genSwitch(stmt) {
|
|
743
|
+
const genBody = () => {
|
|
744
|
+
const tag = stmt.tag ? this.genExpr(stmt.tag) : "true";
|
|
745
|
+
this.line(`switch (${tag}) {`);
|
|
746
|
+
this.indented(() => {
|
|
747
|
+
for (const c of stmt.cases) {
|
|
748
|
+
if (c.list) {
|
|
749
|
+
for (const e of c.list) this.line(`case ${this.genExpr(e)}:`);
|
|
750
|
+
} else {
|
|
751
|
+
this.line("default:");
|
|
752
|
+
}
|
|
753
|
+
// Wrap each case body in {} so `let` declarations don't bleed
|
|
754
|
+
// across sibling cases (JS switch shares one block scope).
|
|
755
|
+
this.line("{");
|
|
756
|
+
this.indented(() => {
|
|
757
|
+
for (const s of c.stmts) this.genStmt(s);
|
|
758
|
+
// Go doesn't fall through by default — add break unless last stmt is return/break
|
|
759
|
+
const last = c.stmts[c.stmts.length - 1];
|
|
760
|
+
if (
|
|
761
|
+
!last ||
|
|
762
|
+
(last.kind !== "ReturnStmt" && last.kind !== "BranchStmt")
|
|
763
|
+
) {
|
|
764
|
+
this.line("break;");
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
this.line("}");
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
this.line("}");
|
|
771
|
+
};
|
|
772
|
+
|
|
700
773
|
if (stmt.init) {
|
|
701
774
|
this.line("{");
|
|
702
|
-
this.
|
|
703
|
-
|
|
775
|
+
this.indented(() => {
|
|
776
|
+
this.genStmt(stmt.init);
|
|
777
|
+
genBody();
|
|
778
|
+
});
|
|
779
|
+
this.line("}");
|
|
780
|
+
} else {
|
|
781
|
+
genBody();
|
|
704
782
|
}
|
|
783
|
+
}
|
|
705
784
|
|
|
706
|
-
|
|
707
|
-
this.line(
|
|
785
|
+
genTypeSwitch(stmt) {
|
|
786
|
+
this.line("{");
|
|
708
787
|
this.indented(() => {
|
|
788
|
+
const val = this.genExpr(stmt.expr);
|
|
789
|
+
this.line(`const __tsw = ${val};`);
|
|
790
|
+
if (stmt.assign) this.line(`let ${stmt.assign} = __tsw;`);
|
|
791
|
+
|
|
792
|
+
let first = true;
|
|
793
|
+
let hasDefault = false;
|
|
709
794
|
for (const c of stmt.cases) {
|
|
710
|
-
if (c.
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
795
|
+
if (!c.types) {
|
|
796
|
+
// default case — emit last
|
|
797
|
+
hasDefault = c;
|
|
798
|
+
continue;
|
|
714
799
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
800
|
+
const cond = c.types
|
|
801
|
+
.map((t) => this._typeCheckExpr(t, "__tsw"))
|
|
802
|
+
.join(" || ");
|
|
803
|
+
this.line(`${first ? "if" : "else if"} (${cond}) {`);
|
|
718
804
|
this.indented(() => {
|
|
719
805
|
for (const s of c.stmts) this.genStmt(s);
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
806
|
+
});
|
|
807
|
+
this.line("}");
|
|
808
|
+
first = false;
|
|
809
|
+
}
|
|
810
|
+
if (hasDefault) {
|
|
811
|
+
this.line(first ? "{" : "else {");
|
|
812
|
+
this.indented(() => {
|
|
813
|
+
for (const s of hasDefault.stmts) this.genStmt(s);
|
|
728
814
|
});
|
|
729
815
|
this.line("}");
|
|
730
816
|
}
|
|
731
817
|
});
|
|
732
818
|
this.line("}");
|
|
733
|
-
|
|
734
|
-
if (stmt.init) {
|
|
735
|
-
this.indent--;
|
|
736
|
-
this.line("}");
|
|
737
|
-
}
|
|
738
819
|
}
|
|
739
820
|
|
|
740
821
|
// ── Expressions ──────────────────────────────────────────────
|
|
@@ -766,7 +847,10 @@ export class CodeGen {
|
|
|
766
847
|
) {
|
|
767
848
|
return `Math.trunc(${l} / ${r})`;
|
|
768
849
|
}
|
|
769
|
-
|
|
850
|
+
// Go == and != are strict — map to JS === / !==
|
|
851
|
+
const op =
|
|
852
|
+
expr.op === "==" ? "===" : expr.op === "!=" ? "!==" : expr.op;
|
|
853
|
+
return `${l} ${op} ${r}`;
|
|
770
854
|
}
|
|
771
855
|
|
|
772
856
|
case "CallExpr":
|
|
@@ -818,7 +902,27 @@ export class CodeGen {
|
|
|
818
902
|
|
|
819
903
|
case "TypeConversion": {
|
|
820
904
|
const inner = this.genExpr(expr.expr);
|
|
821
|
-
const
|
|
905
|
+
const t = expr.targetType;
|
|
906
|
+
|
|
907
|
+
// Slice type conversions
|
|
908
|
+
if (t?.kind === "SliceType") {
|
|
909
|
+
const elem = t.elem?.name;
|
|
910
|
+
// []byte(s) → UTF-8 byte array
|
|
911
|
+
if (elem === "byte" || elem === "uint8") {
|
|
912
|
+
return `Array.from(new TextEncoder().encode(${inner}))`;
|
|
913
|
+
}
|
|
914
|
+
// []rune(s) → Unicode code point array (only when source is a string)
|
|
915
|
+
if (elem === "rune" || elem === "int32" || elem === "int") {
|
|
916
|
+
const srcType = expr.expr._type;
|
|
917
|
+
if (srcType?.kind === "basic" && srcType?.name === "string") {
|
|
918
|
+
return `Array.from(${inner}, __c => __c.codePointAt(0))`;
|
|
919
|
+
}
|
|
920
|
+
return `Array.from(${inner})`;
|
|
921
|
+
}
|
|
922
|
+
return `Array.from(${inner})`;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
const target = t?.name;
|
|
822
926
|
switch (target) {
|
|
823
927
|
case "string":
|
|
824
928
|
return `String(${inner})`;
|
|
@@ -893,11 +997,33 @@ export class CodeGen {
|
|
|
893
997
|
const arg = this.genExpr(expr.args[0]);
|
|
894
998
|
return `(() => { throw new Error(${arg}); })()`;
|
|
895
999
|
}
|
|
1000
|
+
case "recover":
|
|
1001
|
+
return `(typeof __panic !== "undefined" && __panic !== null ? (() => { const __r = __panic.message ?? String(__panic); __panic = null; return __r; })() : null)`;
|
|
896
1002
|
case "error": {
|
|
897
1003
|
// errors are plain strings; nil (null) means no error
|
|
898
1004
|
const arg = this.genExpr(expr.args[0]);
|
|
899
1005
|
return arg;
|
|
900
1006
|
}
|
|
1007
|
+
case "min": {
|
|
1008
|
+
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1009
|
+
return `Math.min(${args})`;
|
|
1010
|
+
}
|
|
1011
|
+
case "max": {
|
|
1012
|
+
const args = expr.args.map((a) => this.genExpr(a)).join(", ");
|
|
1013
|
+
return `Math.max(${args})`;
|
|
1014
|
+
}
|
|
1015
|
+
case "clear": {
|
|
1016
|
+
const arg = expr.args[0];
|
|
1017
|
+
const t = arg?._type;
|
|
1018
|
+
const js = this.genExpr(arg);
|
|
1019
|
+
if (
|
|
1020
|
+
t?.kind === "map" ||
|
|
1021
|
+
(t?.kind === "named" && t.underlying?.kind === "map")
|
|
1022
|
+
) {
|
|
1023
|
+
return `((__m) => { for (const __k in __m) delete __m[__k]; })(${js})`;
|
|
1024
|
+
}
|
|
1025
|
+
return `(${js}).length = 0`;
|
|
1026
|
+
}
|
|
901
1027
|
}
|
|
902
1028
|
}
|
|
903
1029
|
|
|
@@ -936,14 +1062,20 @@ export class CodeGen {
|
|
|
936
1062
|
}
|
|
937
1063
|
}
|
|
938
1064
|
|
|
939
|
-
const
|
|
940
|
-
|
|
1065
|
+
const rawFn = this.genExpr(expr.func);
|
|
1066
|
+
// Wrap function literals in parens so `function(){}()` → `(function(){})()`
|
|
1067
|
+
const fn = expr.func.kind === "FuncLit" ? `(${rawFn})` : rawFn;
|
|
1068
|
+
const args = expr.args
|
|
1069
|
+
.map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)))
|
|
1070
|
+
.join(", ");
|
|
941
1071
|
return `${fn}(${args})`;
|
|
942
1072
|
}
|
|
943
1073
|
|
|
944
1074
|
genAppend(expr) {
|
|
945
1075
|
const slice = this.genExpr(expr.args[0]);
|
|
946
|
-
const elems = expr.args
|
|
1076
|
+
const elems = expr.args
|
|
1077
|
+
.slice(1)
|
|
1078
|
+
.map((a) => (a._spread ? `...${this.genExpr(a)}` : this.genExpr(a)));
|
|
947
1079
|
if (elems.length === 0) return slice;
|
|
948
1080
|
this._usesAppend = true;
|
|
949
1081
|
return `__append(${slice}, ${elems.join(", ")})`;
|
|
@@ -973,10 +1105,7 @@ export class CodeGen {
|
|
|
973
1105
|
}
|
|
974
1106
|
return `new Array(${n}).fill(${zero})`;
|
|
975
1107
|
}
|
|
976
|
-
|
|
977
|
-
return "{}";
|
|
978
|
-
}
|
|
979
|
-
// Fallback
|
|
1108
|
+
// map or fallback
|
|
980
1109
|
return "{}";
|
|
981
1110
|
}
|
|
982
1111
|
|
|
@@ -1079,6 +1208,17 @@ export class CodeGen {
|
|
|
1079
1208
|
_zeroForBasicName(name) {
|
|
1080
1209
|
switch (name) {
|
|
1081
1210
|
case "int":
|
|
1211
|
+
case "uint":
|
|
1212
|
+
case "int8":
|
|
1213
|
+
case "int16":
|
|
1214
|
+
case "int32":
|
|
1215
|
+
case "int64":
|
|
1216
|
+
case "uint8":
|
|
1217
|
+
case "uint16":
|
|
1218
|
+
case "uint32":
|
|
1219
|
+
case "uint64":
|
|
1220
|
+
case "uintptr":
|
|
1221
|
+
case "float32":
|
|
1082
1222
|
case "float64":
|
|
1083
1223
|
case "byte":
|
|
1084
1224
|
case "rune":
|
|
@@ -1141,6 +1281,17 @@ export class CodeGen {
|
|
|
1141
1281
|
if (typeNode.kind === "TypeName") {
|
|
1142
1282
|
switch (typeNode.name) {
|
|
1143
1283
|
case "int":
|
|
1284
|
+
case "uint":
|
|
1285
|
+
case "int8":
|
|
1286
|
+
case "int16":
|
|
1287
|
+
case "int32":
|
|
1288
|
+
case "int64":
|
|
1289
|
+
case "uint8":
|
|
1290
|
+
case "uint16":
|
|
1291
|
+
case "uint32":
|
|
1292
|
+
case "uint64":
|
|
1293
|
+
case "uintptr":
|
|
1294
|
+
case "float32":
|
|
1144
1295
|
case "float64":
|
|
1145
1296
|
case "byte":
|
|
1146
1297
|
case "rune":
|
|
@@ -1149,6 +1300,10 @@ export class CodeGen {
|
|
|
1149
1300
|
return `typeof ${val} === "string"`;
|
|
1150
1301
|
case "bool":
|
|
1151
1302
|
return `typeof ${val} === "boolean"`;
|
|
1303
|
+
case "nil":
|
|
1304
|
+
return `${val} === null`;
|
|
1305
|
+
case "error":
|
|
1306
|
+
return `typeof ${val} === "string"`;
|
|
1152
1307
|
default:
|
|
1153
1308
|
if (this.structNames.has(typeNode.name))
|
|
1154
1309
|
return `${val} instanceof ${typeNode.name}`;
|
package/src/compiler.js
CHANGED
|
@@ -86,8 +86,9 @@ export function compileFiles(files, options = {}) {
|
|
|
86
86
|
|
|
87
87
|
// js: prefix — local .d.ts files
|
|
88
88
|
for (const imp of allImports) {
|
|
89
|
-
for (const path of imp.
|
|
89
|
+
for (const { path, alias } of imp.imports) {
|
|
90
90
|
if (!path.startsWith("js:")) continue;
|
|
91
|
+
if (alias === "_") continue; // side-effect import — no type definitions
|
|
91
92
|
const dtsPath = join(fromDir, path.slice(3));
|
|
92
93
|
try {
|
|
93
94
|
const { types, values } = parseDts(readFileSync(dtsPath, "utf8"));
|
|
@@ -98,8 +99,12 @@ export function compileFiles(files, options = {}) {
|
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
// npm packages
|
|
102
|
-
const
|
|
102
|
+
// npm packages — exclude side-effect imports (alias "_") from type resolution
|
|
103
|
+
const allImportsNoSideEffect = allImports.map((imp) => ({
|
|
104
|
+
...imp,
|
|
105
|
+
imports: imp.imports.filter(({ alias }) => alias !== "_"),
|
|
106
|
+
}));
|
|
107
|
+
const resolved = resolveAll(allImportsNoSideEffect, dummyFromFile, parseDts);
|
|
103
108
|
for (const [path, info] of resolved) {
|
|
104
109
|
if (!info) continue;
|
|
105
110
|
checker.addDefinitions(info.types, info.values);
|
|
@@ -109,7 +114,7 @@ export function compileFiles(files, options = {}) {
|
|
|
109
114
|
// local GoFront packages (./subdir)
|
|
110
115
|
const seenLocalPaths = new Set();
|
|
111
116
|
for (const imp of allImports) {
|
|
112
|
-
for (const path of imp.
|
|
117
|
+
for (const { path, alias } of imp.imports) {
|
|
113
118
|
if (!isLocalPath(path) || seenLocalPaths.has(path)) continue;
|
|
114
119
|
seenLocalPaths.add(path);
|
|
115
120
|
|
|
@@ -124,9 +129,11 @@ export function compileFiles(files, options = {}) {
|
|
|
124
129
|
// Recursively compile dependency
|
|
125
130
|
const dep = compileDir(depDir);
|
|
126
131
|
preambles.push(dep.js);
|
|
127
|
-
|
|
132
|
+
if (alias === "_") continue; // side-effect import — bundle code but don't expose namespace
|
|
133
|
+
const nameUsed = alias ?? dep.pkgName;
|
|
134
|
+
bundledPackages.add(nameUsed);
|
|
128
135
|
checker.addPackageNamespace(
|
|
129
|
-
|
|
136
|
+
nameUsed,
|
|
130
137
|
dep.exportedSymbols,
|
|
131
138
|
dep.exportedTypes,
|
|
132
139
|
);
|
package/src/dts-parser.js
CHANGED
|
@@ -404,13 +404,11 @@ export class DtsParser {
|
|
|
404
404
|
if (this.eof() || this.ch() === "}") return;
|
|
405
405
|
|
|
406
406
|
// Skip modifiers (export, declare, static, readonly, etc.)
|
|
407
|
-
let _exported = false;
|
|
408
407
|
while (true) {
|
|
409
408
|
this.skip();
|
|
410
409
|
let found = false;
|
|
411
410
|
for (const mod of SKIP_MODIFIERS) {
|
|
412
411
|
if (this.matchKw(mod)) {
|
|
413
|
-
if (mod === "export") _exported = true;
|
|
414
412
|
found = true;
|
|
415
413
|
break;
|
|
416
414
|
}
|
|
@@ -604,13 +602,11 @@ export class DtsParser {
|
|
|
604
602
|
if (this.eof() || this.ch() === "}") break;
|
|
605
603
|
|
|
606
604
|
// Collect modifiers
|
|
607
|
-
let _isExport = false;
|
|
608
605
|
while (true) {
|
|
609
606
|
this.skip();
|
|
610
607
|
let found = false;
|
|
611
608
|
for (const mod of SKIP_MODIFIERS) {
|
|
612
609
|
if (this.matchKw(mod)) {
|
|
613
|
-
if (mod === "export") _isExport = true;
|
|
614
610
|
found = true;
|
|
615
611
|
break;
|
|
616
612
|
}
|
package/src/index.js
CHANGED
|
@@ -148,16 +148,14 @@ function runCompile() {
|
|
|
148
148
|
throw new Error(`cannot read '${inputArg}': ${e.message}`);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
tokens = new Lexer(source, basename(inputPath)).tokenize();
|
|
151
|
+
const tokens = new Lexer(source, basename(inputPath)).tokenize();
|
|
153
152
|
|
|
154
153
|
if (dumpTokens) {
|
|
155
154
|
for (const tok of tokens) console.log(tok.toString());
|
|
156
155
|
process.exit(0);
|
|
157
156
|
}
|
|
158
157
|
|
|
159
|
-
|
|
160
|
-
ast = new Parser(tokens, basename(inputPath), source).parse();
|
|
158
|
+
const ast = new Parser(tokens, basename(inputPath), source).parse();
|
|
161
159
|
ast._source = source;
|
|
162
160
|
|
|
163
161
|
if (dumpAst) {
|
|
@@ -170,7 +168,7 @@ function runCompile() {
|
|
|
170
168
|
const jsImports = new Map();
|
|
171
169
|
|
|
172
170
|
for (const imp of ast.imports) {
|
|
173
|
-
for (const path of imp.
|
|
171
|
+
for (const { path } of imp.imports) {
|
|
174
172
|
if (!path.startsWith("js:")) continue;
|
|
175
173
|
const dtsPath = join(fromDir, path.slice(3));
|
|
176
174
|
try {
|
|
@@ -193,7 +191,7 @@ function runCompile() {
|
|
|
193
191
|
const preambles = [];
|
|
194
192
|
|
|
195
193
|
for (const imp of ast.imports) {
|
|
196
|
-
for (const path of imp.
|
|
194
|
+
for (const { path, alias } of imp.imports) {
|
|
197
195
|
if (!isLocalPath(path)) continue;
|
|
198
196
|
const depDir = resolveGwDir(path, inputPath);
|
|
199
197
|
if (!depDir) {
|
|
@@ -204,9 +202,10 @@ function runCompile() {
|
|
|
204
202
|
}
|
|
205
203
|
const dep = compileDir(depDir);
|
|
206
204
|
preambles.push(dep.js);
|
|
207
|
-
|
|
205
|
+
const nameUsed = alias ?? dep.pkgName;
|
|
206
|
+
bundledPackages.add(nameUsed);
|
|
208
207
|
checker.addPackageNamespace(
|
|
209
|
-
|
|
208
|
+
nameUsed,
|
|
210
209
|
dep.exportedSymbols,
|
|
211
210
|
dep.exportedTypes,
|
|
212
211
|
);
|
|
@@ -228,15 +227,7 @@ function runCompile() {
|
|
|
228
227
|
}
|
|
229
228
|
|
|
230
229
|
const output = preambles.length > 0 ? `${preambles.join("\n")}\n${js}` : js;
|
|
231
|
-
return { js: output
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function _writeOutput(js) {
|
|
235
|
-
if (outputFile) {
|
|
236
|
-
writeFileSync(outputFile, `${js}\n`);
|
|
237
|
-
} else {
|
|
238
|
-
console.log(js);
|
|
239
|
-
}
|
|
230
|
+
return { js: output };
|
|
240
231
|
}
|
|
241
232
|
|
|
242
233
|
// ── Minify helper ────────────────────────────────────────────
|
package/src/lexer.js
CHANGED
|
@@ -58,6 +58,11 @@ export const T = {
|
|
|
58
58
|
STAR_ASSIGN: "*=",
|
|
59
59
|
SLASH_ASSIGN: "/=",
|
|
60
60
|
PERCENT_ASSIGN: "%=",
|
|
61
|
+
AMP_ASSIGN: "&=",
|
|
62
|
+
PIPE_ASSIGN: "|=",
|
|
63
|
+
CARET_ASSIGN: "^=",
|
|
64
|
+
LSHIFT_ASSIGN: "<<=",
|
|
65
|
+
RSHIFT_ASSIGN: ">>=",
|
|
61
66
|
INC: "++",
|
|
62
67
|
DEC: "--",
|
|
63
68
|
AMP: "&",
|
|
@@ -267,7 +272,7 @@ export class Lexer {
|
|
|
267
272
|
}
|
|
268
273
|
}
|
|
269
274
|
if (this.pos >= this.src.length)
|
|
270
|
-
throw new LexError("Unterminated string", l, c, this.filename);
|
|
275
|
+
throw new LexError("Unterminated string", l, c, this.filename, this.src);
|
|
271
276
|
this.advance(); // closing "
|
|
272
277
|
return s;
|
|
273
278
|
}
|
|
@@ -281,6 +286,60 @@ export class Lexer {
|
|
|
281
286
|
return s;
|
|
282
287
|
}
|
|
283
288
|
|
|
289
|
+
readRuneLiteral() {
|
|
290
|
+
const l = this.line,
|
|
291
|
+
c = this.col;
|
|
292
|
+
this.advance(); // opening '
|
|
293
|
+
let code;
|
|
294
|
+
if (this.peek() === "\\") {
|
|
295
|
+
this.advance();
|
|
296
|
+
const esc = this.advance();
|
|
297
|
+
switch (esc) {
|
|
298
|
+
case "n":
|
|
299
|
+
code = 10;
|
|
300
|
+
break;
|
|
301
|
+
case "t":
|
|
302
|
+
code = 9;
|
|
303
|
+
break;
|
|
304
|
+
case "r":
|
|
305
|
+
code = 13;
|
|
306
|
+
break;
|
|
307
|
+
case "'":
|
|
308
|
+
code = 39;
|
|
309
|
+
break;
|
|
310
|
+
case "\\":
|
|
311
|
+
code = 92;
|
|
312
|
+
break;
|
|
313
|
+
case "0":
|
|
314
|
+
code = 0;
|
|
315
|
+
break;
|
|
316
|
+
default:
|
|
317
|
+
throw new LexError(
|
|
318
|
+
`Unknown escape in rune literal: \\${esc}`,
|
|
319
|
+
l,
|
|
320
|
+
c,
|
|
321
|
+
this.filename,
|
|
322
|
+
this.src,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
} else if (this.peek() !== "'") {
|
|
326
|
+
code = this.src.codePointAt(this.pos);
|
|
327
|
+
this.advance();
|
|
328
|
+
} else {
|
|
329
|
+
throw new LexError("Empty rune literal", l, c, this.filename, this.src);
|
|
330
|
+
}
|
|
331
|
+
if (this.peek() !== "'")
|
|
332
|
+
throw new LexError(
|
|
333
|
+
"Rune literal must contain exactly one character",
|
|
334
|
+
l,
|
|
335
|
+
c,
|
|
336
|
+
this.filename,
|
|
337
|
+
this.src,
|
|
338
|
+
);
|
|
339
|
+
this.advance(); // closing '
|
|
340
|
+
return String(code);
|
|
341
|
+
}
|
|
342
|
+
|
|
284
343
|
readNumber() {
|
|
285
344
|
let n = "";
|
|
286
345
|
let isFloat = false;
|
|
@@ -347,6 +406,10 @@ export class Lexer {
|
|
|
347
406
|
this.push(T.STRING, this.readRawString(), l, c);
|
|
348
407
|
continue;
|
|
349
408
|
}
|
|
409
|
+
if (ch === "'") {
|
|
410
|
+
this.push(T.INT, this.readRuneLiteral(), l, c);
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
350
413
|
|
|
351
414
|
// Numbers
|
|
352
415
|
if (/[0-9]/.test(ch)) {
|
|
@@ -396,25 +459,32 @@ export class Lexer {
|
|
|
396
459
|
else this.push(T.NOT, "!", l, c);
|
|
397
460
|
break;
|
|
398
461
|
case "<":
|
|
399
|
-
if (this.match("<"))
|
|
400
|
-
|
|
462
|
+
if (this.match("<")) {
|
|
463
|
+
if (this.match("=")) this.push(T.LSHIFT_ASSIGN, "<<=", l, c);
|
|
464
|
+
else this.push(T.LSHIFT, "<<", l, c);
|
|
465
|
+
} else if (this.match("=")) this.push(T.LTE, "<=", l, c);
|
|
401
466
|
else this.push(T.LT, "<", l, c);
|
|
402
467
|
break;
|
|
403
468
|
case ">":
|
|
404
|
-
if (this.match(">"))
|
|
405
|
-
|
|
469
|
+
if (this.match(">")) {
|
|
470
|
+
if (this.match("=")) this.push(T.RSHIFT_ASSIGN, ">>=", l, c);
|
|
471
|
+
else this.push(T.RSHIFT, ">>", l, c);
|
|
472
|
+
} else if (this.match("=")) this.push(T.GTE, ">=", l, c);
|
|
406
473
|
else this.push(T.GT, ">", l, c);
|
|
407
474
|
break;
|
|
408
475
|
case "&":
|
|
409
476
|
if (this.match("&")) this.push(T.AND, "&&", l, c);
|
|
477
|
+
else if (this.match("=")) this.push(T.AMP_ASSIGN, "&=", l, c);
|
|
410
478
|
else this.push(T.AMP, "&", l, c);
|
|
411
479
|
break;
|
|
412
480
|
case "|":
|
|
413
481
|
if (this.match("|")) this.push(T.OR, "||", l, c);
|
|
482
|
+
else if (this.match("=")) this.push(T.PIPE_ASSIGN, "|=", l, c);
|
|
414
483
|
else this.push(T.PIPE, "|", l, c);
|
|
415
484
|
break;
|
|
416
485
|
case "^":
|
|
417
|
-
this.push(T.
|
|
486
|
+
if (this.match("=")) this.push(T.CARET_ASSIGN, "^=", l, c);
|
|
487
|
+
else this.push(T.CARET, "^", l, c);
|
|
418
488
|
break;
|
|
419
489
|
case ":":
|
|
420
490
|
if (this.match("=")) this.push(T.DEFINE, ":=", l, c);
|