gofront 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +35 -39
  2. package/package.json +37 -4
  3. package/src/backend/js/expressions.js +559 -144
  4. package/src/backend/js/index.js +174 -49
  5. package/src/backend/js/runtime.js +1518 -54
  6. package/src/backend/js/statements.js +91 -73
  7. package/src/backend/js/stdlib/base64.js +12 -0
  8. package/src/backend/js/stdlib/binary.js +102 -0
  9. package/src/backend/js/stdlib/bits.js +15 -1
  10. package/src/backend/js/stdlib/builder.js +28 -9
  11. package/src/backend/js/stdlib/bytes.js +75 -18
  12. package/src/backend/js/stdlib/errors.js +25 -1
  13. package/src/backend/js/stdlib/fmt.js +70 -13
  14. package/src/backend/js/stdlib/hex.js +17 -0
  15. package/src/backend/js/stdlib/html.js +1 -1
  16. package/src/backend/js/stdlib/index.js +28 -0
  17. package/src/backend/js/stdlib/io.js +105 -3
  18. package/src/backend/js/stdlib/json.js +972 -0
  19. package/src/backend/js/stdlib/math.js +13 -1
  20. package/src/backend/js/stdlib/path.js +18 -4
  21. package/src/backend/js/stdlib/rand.js +10 -5
  22. package/src/backend/js/stdlib/sha256.js +12 -0
  23. package/src/backend/js/stdlib/simd.js +24 -0
  24. package/src/backend/js/stdlib/slices.js +52 -26
  25. package/src/backend/js/stdlib/strconv.js +21 -5
  26. package/src/backend/js/stdlib/strings.js +148 -15
  27. package/src/backend/js/stdlib/sync.js +13 -0
  28. package/src/backend/js/stdlib/time.js +90 -19
  29. package/src/backend/js/stdlib/unicode.js +2 -2
  30. package/src/backend/wasm/boundary.js +1225 -41
  31. package/src/backend/wasm/const-fold.js +205 -0
  32. package/src/backend/wasm/emit-builtins.js +220 -76
  33. package/src/backend/wasm/emit-exprs.js +475 -276
  34. package/src/backend/wasm/emit-maps.js +144 -8
  35. package/src/backend/wasm/emit-stdlib.js +12 -3005
  36. package/src/backend/wasm/emit-stmts.js +264 -270
  37. package/src/backend/wasm/emit.js +124 -87
  38. package/src/backend/wasm/encode.js +140 -29
  39. package/src/backend/wasm/index.js +1001 -32
  40. package/src/backend/wasm/optimize.js +17 -6
  41. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  42. package/src/backend/wasm/stdlib/core.js +8903 -0
  43. package/src/backend/wasm/stdlib/index.js +43 -0
  44. package/src/backend/wasm/stdlib/io.js +2161 -0
  45. package/src/backend/wasm/stdlib/packages.js +2400 -0
  46. package/src/backend/wasm/stdlib/pure.js +720 -0
  47. package/src/backend/wasm/stdlib/simd.js +234 -0
  48. package/src/backend/wasm/stdlib/time.js +1200 -0
  49. package/src/backend/wasm/types.js +174 -1
  50. package/src/backend/wasm/wat.js +37 -0
  51. package/src/cli-core.js +1 -1
  52. package/src/compiler.js +18 -4
  53. package/src/dts-parser.js +150 -140
  54. package/src/index.js +2 -6
  55. package/src/lexer.js +80 -29
  56. package/src/lower/boxing.js +1 -7
  57. package/src/lower/captures.js +2 -6
  58. package/src/lower/embedding.js +2 -1
  59. package/src/lower/ownership.js +30 -9
  60. package/src/minifier.js +20 -68
  61. package/src/parser/declarations.js +3 -6
  62. package/src/parser/expressions.js +3 -8
  63. package/src/parser/index.js +18 -3
  64. package/src/parser/statements.js +25 -38
  65. package/src/parser/struct-tag.js +144 -0
  66. package/src/parser/types.js +64 -8
  67. package/src/resolver.js +2 -27
  68. package/src/templ-lexer.js +29 -63
  69. package/src/templ-parser.js +22 -45
  70. package/src/typechecker/assignability.js +126 -13
  71. package/src/typechecker/expressions.js +168 -16
  72. package/src/typechecker/index.js +35 -8
  73. package/src/typechecker/resolve.js +187 -8
  74. package/src/typechecker/statements.js +68 -9
  75. package/src/typechecker/stdlib/core.js +280 -89
  76. package/src/typechecker/stdlib/extended.js +490 -1
  77. package/src/typechecker/stdlib/simd.js +73 -0
  78. package/src/typechecker/stdlib.js +2 -0
  79. package/src/typechecker/types.js +362 -9
  80. package/src/vendor.js +86 -11
@@ -184,8 +184,7 @@ export const statementGenMethods = {
184
184
  this.line(`const ${tmp} = ${rhs[0]};`);
185
185
  for (let i = 0; i < lhs.length; i++) {
186
186
  if (lhs[i] === "_") continue;
187
- const prefix = redecls[i] ? "" : "let ";
188
- this.line(`${prefix}${lhs[i]} = ${tmp}[${i}];`);
187
+ this._genDefineSingleVar([lhs[i]], [`${tmp}[${i}]`], [redecls[i]]);
189
188
  }
190
189
  } else {
191
190
  for (let i = 0; i < lhs.length; i++) {
@@ -193,12 +192,20 @@ export const statementGenMethods = {
193
192
  this.line(`${rhs[i]};`);
194
193
  continue;
195
194
  }
196
- const prefix = redecls[i] ? "" : "let ";
197
- this.line(`${prefix}${lhs[i]} = ${rhs[i]};`);
195
+ this._genDefineSingleVar([lhs[i]], [rhs[i]], [redecls[i]]);
198
196
  }
199
197
  }
200
198
  },
201
199
 
200
+ // Address-taken variables introduced by a destructuring declaration are
201
+ // re-bound to their { value } box.
202
+ _boxDeclaredNames(names) {
203
+ for (const n of names) {
204
+ if (n && n !== "_" && this._boxedVars.has(n))
205
+ this.line(`${n} = { value: ${n} };`);
206
+ }
207
+ },
208
+
202
209
  _genDefineStmt(stmt) {
203
210
  const rhsNode = stmt.rhs[0];
204
211
  if (this._isCommaOkMapExpr(stmt, rhsNode)) {
@@ -221,6 +228,7 @@ export const statementGenMethods = {
221
228
  const rhsStr = rhs.length === 1 ? rhs[0] : `[${rhs.join(", ")}]`;
222
229
  const names = lhs.map((n) => (n === "_" ? "" : n));
223
230
  this.line(`let [${names.join(", ")}] = ${rhsStr};`);
231
+ this._boxDeclaredNames(names);
224
232
  } else {
225
233
  this._genDefineMixedRedecl(lhs, rhs, redecls);
226
234
  }
@@ -696,6 +704,39 @@ export const statementGenMethods = {
696
704
  return nodeAssigns(node, name);
697
705
  },
698
706
 
707
+ _genStringRangeFor(stmt, iteree, isAssign) {
708
+ this._loopDepth = this._loopDepth ?? 0;
709
+ const d = this._loopDepth++;
710
+ const [idxNode, valNode] = stmt.init.lhs;
711
+ const s = `__s${d}`;
712
+ const i = `__i${d}`;
713
+ const cp = `__cp${d}`;
714
+ this.line(
715
+ `for (let ${i} = 0, ${s} = Array.from(${iteree} ?? ""); ${i} < ${s}.length; ${i}++) {`,
716
+ );
717
+ this.indented(() => {
718
+ this._emitRangeVar(idxNode, i, isAssign);
719
+ if (valNode && valNode.name !== "_") {
720
+ this.line(`const ${cp} = ${s}[${i}].codePointAt(0);`);
721
+ this._emitRangeVar(valNode, cp, isAssign);
722
+ }
723
+ const declared = isAssign
724
+ ? []
725
+ : [idxNode, valNode]
726
+ .filter((e) => e?.kind === "Ident" && e.name !== "_")
727
+ .map((e) => e.name);
728
+ if (this._bodyRedeclares(stmt.body, declared)) {
729
+ this.line("{");
730
+ this.indented(() => this.genBlock(stmt.body));
731
+ this.line("}");
732
+ } else {
733
+ this.genBlock(stmt.body);
734
+ }
735
+ });
736
+ this.line("}");
737
+ this._loopDepth--;
738
+ },
739
+
699
740
  _genIterFor(init, iterType, iteree, lhs, body) {
700
741
  const iterExpr = this._genRangeIterExpr(iterType, iteree, lhs);
701
742
  const binding =
@@ -725,12 +766,17 @@ export const statementGenMethods = {
725
766
  return;
726
767
  }
727
768
 
728
- if (!this._isMapRangeType(iterType) && !this._isStringRangeType(iterType)) {
729
- this._genSliceRangeFor(stmt, iteree, init.kind === "AssignStmt");
769
+ if (this._isStringRangeType(iterType)) {
770
+ this._genStringRangeFor(stmt, iteree, init.kind === "AssignStmt");
771
+ return;
772
+ }
773
+
774
+ if (this._isMapRangeType(iterType)) {
775
+ this._genIterFor(init, iterType, iteree, lhs, stmt.body);
730
776
  return;
731
777
  }
732
778
 
733
- this._genIterFor(init, iterType, iteree, lhs, stmt.body);
779
+ this._genSliceRangeFor(stmt, iteree, init.kind === "AssignStmt");
734
780
  },
735
781
 
736
782
  _genRangeIterExpr(iterType, iteree, lhs) {
@@ -738,23 +784,14 @@ export const statementGenMethods = {
738
784
  if (lhs.length === 1) return `Object.keys(${iteree})`;
739
785
  return `Object.entries(${iteree})`;
740
786
  }
741
- if (this._isStringRangeType(iterType)) {
742
- if (lhs.length === 1) return `Array.from(${iteree}).keys()`;
743
- return `Array.from(${iteree}, (__c, __i) => [__i, __c.codePointAt(0)])`;
744
- }
745
787
  this.useHelper("sliceGuard");
746
788
  if (lhs.length === 1) return `__s(${iteree}).keys()`;
747
789
  return `__s(${iteree}).entries()`;
748
790
  },
749
791
 
750
- genIteratorFor(stmt) {
751
- const range = stmt.init.rhs[0];
752
- const lhs = stmt.init.lhs.map((e) => e.name ?? this.genExpr(e));
753
- let iteree = this.genExpr(range.expr);
754
- const yieldParams = range._yieldParams;
755
-
756
- // Wrap bare function expressions to avoid "Function statements require a name"
757
- if (range.expr.kind === "FuncLit") {
792
+ _genIteratorLoop(itereeExpr, paramsStr, body) {
793
+ let iteree = this.genExpr(itereeExpr);
794
+ if (itereeExpr.kind === "FuncLit") {
758
795
  iteree = `(${iteree})`;
759
796
  }
760
797
 
@@ -763,17 +800,13 @@ export const statementGenMethods = {
763
800
  const retFlag = `__returned${d}`;
764
801
  const retVar = `__retVal${d}`;
765
802
 
766
- // Yield callback param names (blank vars get _$N to avoid JS syntax error)
767
- const cbParams = lhs.map((n, i) => (n === "_" ? `_$${i}` : n));
768
-
769
803
  this.line("{");
770
804
  this.indented(() => {
771
805
  this.line(`let ${breakFlag} = false;`);
772
806
  this.line(`let ${retFlag} = false;`);
773
807
  this.line(`let ${retVar};`);
774
808
 
775
- const params = yieldParams.length === 0 ? "" : cbParams.join(", ");
776
- this.line(`${iteree}(function(${params}) {`);
809
+ this.line(`${iteree}(function(${paramsStr}) {`);
777
810
  this.indented(() => {
778
811
  this.line(`if (${breakFlag}) return false;`);
779
812
  const prev = {
@@ -787,7 +820,7 @@ export const statementGenMethods = {
787
820
  this._iterReturnFlag = retFlag;
788
821
  this._iterReturnVar = retVar;
789
822
 
790
- this.genBlock(stmt.body);
823
+ this.genBlock(body);
791
824
 
792
825
  this._inIteratorBody = prev.in;
793
826
  this._iterBreakFlag = prev.break;
@@ -803,54 +836,17 @@ export const statementGenMethods = {
803
836
  this._iterDepth--;
804
837
  },
805
838
 
839
+ genIteratorFor(stmt) {
840
+ const range = stmt.init.rhs[0];
841
+ const lhs = stmt.init.lhs.map((e) => e.name ?? this.genExpr(e));
842
+ const cbParams = lhs.map((n, i) => (n === "_" ? `_$${i}` : n));
843
+ const params = range._yieldParams.length === 0 ? "" : cbParams.join(", ");
844
+ this._genIteratorLoop(range.expr, params, stmt.body);
845
+ },
846
+
806
847
  // 0-param iterator: for range iter { body }
807
848
  genIteratorForCond(stmt) {
808
- const range = stmt.cond;
809
- let iteree = this.genExpr(range.expr);
810
-
811
- if (range.expr.kind === "FuncLit") {
812
- iteree = `(${iteree})`;
813
- }
814
-
815
- const d = this._iterDepth++;
816
- const breakFlag = `__broke${d}`;
817
- const retFlag = `__returned${d}`;
818
- const retVar = `__retVal${d}`;
819
-
820
- this.line("{");
821
- this.indented(() => {
822
- this.line(`let ${breakFlag} = false;`);
823
- this.line(`let ${retFlag} = false;`);
824
- this.line(`let ${retVar};`);
825
-
826
- this.line(`${iteree}(function() {`);
827
- this.indented(() => {
828
- this.line(`if (${breakFlag}) return false;`);
829
- const prev = {
830
- in: this._inIteratorBody,
831
- break: this._iterBreakFlag,
832
- ret: this._iterReturnFlag,
833
- retVar: this._iterReturnVar,
834
- };
835
- this._inIteratorBody = true;
836
- this._iterBreakFlag = breakFlag;
837
- this._iterReturnFlag = retFlag;
838
- this._iterReturnVar = retVar;
839
-
840
- this.genBlock(stmt.body);
841
-
842
- this._inIteratorBody = prev.in;
843
- this._iterBreakFlag = prev.break;
844
- this._iterReturnFlag = prev.ret;
845
- this._iterReturnVar = prev.retVar;
846
-
847
- this.line("return true;");
848
- });
849
- this.line("});");
850
- this.line(`if (${retFlag}) return ${retVar};`);
851
- });
852
- this.line("}");
853
- this._iterDepth--;
849
+ this._genIteratorLoop(stmt.cond.expr, "", stmt.body);
854
850
  },
855
851
 
856
852
  // Inline a statement as a for-init/post string (no semicolon)
@@ -875,14 +871,29 @@ export const statementGenMethods = {
875
871
  }
876
872
  },
877
873
 
874
+ // Interface-typed tags may hold boxed named values; cases compare with __ifeq.
875
+ _switchTagAndCases(stmt) {
876
+ const tag = stmt.tag ? this.genExpr(stmt.tag) : "true";
877
+ if (!stmt.tag || !this._isIfaceType(stmt.tag._type))
878
+ return { tag, caseExpr: (e) => this.genExpr(e) };
879
+ this.useHelper("ifeq");
880
+ this._swCounter = (this._swCounter ?? 0) + 1;
881
+ const tmp = `__sw${this._swCounter}`;
882
+ this.line(`const ${tmp} = ${tag};`);
883
+ return {
884
+ tag: "true",
885
+ caseExpr: (e) => `__ifeq(${tmp}, ${this._genIfaceOperand(e)})`,
886
+ };
887
+ },
888
+
878
889
  genSwitch(stmt) {
879
890
  const genBody = () => {
880
- const tag = stmt.tag ? this.genExpr(stmt.tag) : "true";
891
+ const { tag, caseExpr } = this._switchTagAndCases(stmt);
881
892
  this.line(`switch (${tag}) {`);
882
893
  this.indented(() => {
883
894
  for (const c of stmt.cases) {
884
895
  if (c.list) {
885
- for (const e of c.list) this.line(`case ${this.genExpr(e)}:`);
896
+ for (const e of c.list) this.line(`case ${caseExpr(e)}:`);
886
897
  } else {
887
898
  this.line("default:");
888
899
  }
@@ -938,6 +949,13 @@ export const statementGenMethods = {
938
949
  .join(" || ");
939
950
  this.line(`${first ? "if" : "else if"} (${cond}) {`);
940
951
  this.indented(() => {
952
+ // A single named non-struct case binds the unboxed value.
953
+ if (
954
+ stmt.assign &&
955
+ c.types.length === 1 &&
956
+ this._isNamedBoxTypeNode(c.types[0])
957
+ )
958
+ this.line(`const ${stmt.assign} = __tsw.__gfUnbox();`);
941
959
  for (const s of c.stmts) this.genStmt(s);
942
960
  });
943
961
  this.line("}");
@@ -0,0 +1,12 @@
1
+ // src/backend/js/stdlib/base64.js
2
+ // CodeGen for Go encoding/base64 package.
3
+
4
+ export const base64Methods = {
5
+ _genBase64(fn, a) {
6
+ this.useHelper("base64");
7
+ this.useHelper("error");
8
+ const args = a();
9
+ if (fn === "NewEncoding") return `__base64_make(${args[0]}, "=")`;
10
+ return undefined;
11
+ },
12
+ };
@@ -0,0 +1,102 @@
1
+ // src/backend/js/stdlib/binary.js
2
+ // CodeGen for Go encoding/binary package.
3
+
4
+ function sizeOfGoType(gt) {
5
+ if (!gt) return -1;
6
+ if (gt.kind === "basic") {
7
+ switch (gt.name) {
8
+ case "bool":
9
+ case "int8":
10
+ case "uint8":
11
+ case "byte":
12
+ return 1;
13
+ case "int16":
14
+ case "uint16":
15
+ return 2;
16
+ case "int32":
17
+ case "uint32":
18
+ case "float32":
19
+ case "rune":
20
+ return 4;
21
+ case "int64":
22
+ case "uint64":
23
+ case "float64":
24
+ case "int":
25
+ case "uint":
26
+ case "uintptr":
27
+ return 8;
28
+ default:
29
+ return -1;
30
+ }
31
+ }
32
+ if (gt.kind === "array" || gt.kind === "ArrayType") {
33
+ const es = sizeOfGoType(gt.elem);
34
+ const len = gt.len ?? gt.size ?? 0;
35
+ return es > 0 ? es * len : -1;
36
+ }
37
+ if (
38
+ gt.kind === "pointer" ||
39
+ gt.kind === "PointerType" ||
40
+ gt.kind === "StarExpr"
41
+ ) {
42
+ return sizeOfGoType(gt.base ?? gt.expr ?? gt.operand);
43
+ }
44
+ if (gt.kind === "struct" || gt.kind === "StructType") {
45
+ let total = 0;
46
+ if (gt.fields instanceof Map) {
47
+ for (const fType of gt.fields.values()) {
48
+ const s = sizeOfGoType(fType);
49
+ if (s < 0) return -1;
50
+ total += s;
51
+ }
52
+ return total;
53
+ }
54
+ }
55
+ return -1;
56
+ }
57
+
58
+ export const binaryMethods = {
59
+ _genBinary(fn, a, expr) {
60
+ this.useHelper("binary");
61
+ this.useHelper("error");
62
+ const args = a();
63
+ if (fn === "Read") {
64
+ const targetType = expr?.args?.[2]?._type;
65
+ const baseType = targetType?.base ?? targetType;
66
+ const resolved =
67
+ baseType?.kind === "named"
68
+ ? (baseType.underlying ?? baseType)
69
+ : baseType;
70
+ const sz = sizeOfGoType(resolved);
71
+ const typeTag =
72
+ resolved?.name ??
73
+ (resolved?.kind === "basic" ? resolved.name : (baseType?.name ?? ""));
74
+ return `__binary_read(${args[0]}, ${args[1]}, ${args[2]}${sz > 0 ? `, ${sz}` : ", 0"}, "${typeTag}")`;
75
+ }
76
+ if (fn === "Write") {
77
+ const targetType = expr?.args?.[2]?._type;
78
+ const baseType = targetType?.base ?? targetType;
79
+ const resolved =
80
+ baseType?.kind === "named"
81
+ ? (baseType.underlying ?? baseType)
82
+ : baseType;
83
+ const sz = sizeOfGoType(resolved);
84
+ const typeTag =
85
+ resolved?.name ??
86
+ (resolved?.kind === "basic" ? resolved.name : (baseType?.name ?? ""));
87
+ return `__binary_write(${args[0]}, ${args[1]}, ${args[2]}${sz > 0 ? `, ${sz}` : ", 0"}, "${typeTag}")`;
88
+ }
89
+ if (fn === "Size") {
90
+ const sz = sizeOfGoType(expr?.args?.[0]?._type);
91
+ if (sz >= 0) return `${sz}`;
92
+ return `__binary_size(${args[0]})`;
93
+ }
94
+ if (fn === "AppendUint16")
95
+ return `__binary_le.AppendUint16(${args[0]}, ${args[1]})`;
96
+ if (fn === "AppendUint32")
97
+ return `__binary_le.AppendUint32(${args[0]}, ${args[1]})`;
98
+ if (fn === "AppendUint64")
99
+ return `__binary_le.AppendUint64(${args[0]}, ${args[1]})`;
100
+ return undefined;
101
+ },
102
+ };
@@ -19,7 +19,21 @@ const BITS_MAP = {
19
19
  OnesCount64: ([x]) =>
20
20
  `((() => { let v = BigInt.asUintN(64, BigInt(${x})); let c = 0; while (v > 0n) { if (v & 1n) c++; v >>= 1n; } return c; })())`,
21
21
  RotateLeft64: ([x, k]) =>
22
- `((() => { const s = BigInt(${k}) & 63n; const v = BigInt.asUintN(64, BigInt(${x})); return BigInt.asUintN(64, (v << s) | (v >> ((64n - s) & 63n))); })())`,
22
+ `((() => { const s = BigInt(${k}) & 63n; const v = BigInt.asUintN(64, BigInt(${x})); return Number(BigInt.asUintN(64, (v << s) | (v >> ((64n - s) & 63n)))); })())`,
23
+
24
+ Len32: ([x]) => `((v => 32 - Math.clz32(v >>> 0))(${x}))`,
25
+ Len64: ([x]) =>
26
+ `((() => { const v = BigInt.asUintN(64, BigInt(${x})); if (v === 0n) return 0; const hi = Number(v >> 32n); return hi !== 0 ? 64 - Math.clz32(hi) : 32 - Math.clz32(Number(v & 0xffffffffn)); })())`,
27
+ ReverseBytes16: ([x]) =>
28
+ `((v => ((v & 0xff) << 8) | ((v >>> 8) & 0xff))((${x}) & 0xffff))`,
29
+ ReverseBytes32: ([x]) =>
30
+ `((v => (((v & 0xff) << 24) | ((v & 0xff00) << 8) | ((v >>> 8) & 0xff00) | ((v >>> 24) & 0xff)) >>> 0)((${x}) >>> 0))`,
31
+ ReverseBytes64: ([x]) =>
32
+ `((() => { const v = BigInt.asUintN(64, BigInt(${x})); let r = 0n; for (let i = 0n; i < 8n; i++) r = (r << 8n) | ((v >> (i * 8n)) & 0xffn); return Number(r); })())`,
33
+ Reverse32: ([x]) =>
34
+ `((() => { let v = (${x}) >>> 0; v = ((v >>> 1) & 0x55555555) | ((v & 0x55555555) << 1); v = ((v >>> 2) & 0x33333333) | ((v & 0x33333333) << 2); v = ((v >>> 4) & 0x0f0f0f0f) | ((v & 0x0f0f0f0f) << 4); v = ((v >>> 8) & 0x00ff00ff) | ((v & 0x00ff00ff) << 8); return ((v >>> 16) | (v << 16)) >>> 0; })())`,
35
+ Reverse64: ([x]) =>
36
+ `((() => { const rev32 = (v) => { v = ((v >>> 1) & 0x55555555) | ((v & 0x55555555) << 1); v = ((v >>> 2) & 0x33333333) | ((v & 0x33333333) << 2); v = ((v >>> 4) & 0x0f0f0f0f) | ((v & 0x0f0f0f0f) << 4); v = ((v >>> 8) & 0x00ff00ff) | ((v & 0x00ff00ff) << 8); return ((v >>> 16) | (v << 16)) >>> 0; }; const v = BigInt.asUintN(64, BigInt(${x})); const lo = Number(v & 0xffffffffn); const hi = Number((v >> 32n) & 0xffffffffn); return Number((BigInt(rev32(lo)) << 32n) | BigInt(rev32(hi))); })())`,
23
37
  };
24
38
 
25
39
  /** @type {ThisType<CodeGen>} */
@@ -3,25 +3,40 @@
3
3
  /** @typedef {import('../index.js').CodeGen} CodeGen */
4
4
 
5
5
  const BUILDER_STR = {
6
- WriteString: (b, a) => `(${b}._buf += ${a[0]}, [${a[0]}.length, null])`,
7
- WriteByte: (b, a) => `(${b}._buf += String.fromCodePoint(${a[0]}))`,
8
- Write: (b, a) => `(${b}._buf += String.fromCharCode(...${a[0]}))`,
6
+ WriteString: (b, a) =>
7
+ `((__s) => { ${b}._buf += __s; return [new TextEncoder().encode(__s).length, null]; })(${a[0]})`,
8
+ WriteByte: (b, a) => `(${b}._buf += String.fromCodePoint(${a[0]}), null)`,
9
+ WriteRune: (b, a) =>
10
+ `((cp) => { const r = (cp < 0 || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff)) ? 0xfffd : cp; const s = String.fromCodePoint(r); ${b}._buf += s; return [new TextEncoder().encode(s).length, null]; })(${a[0]})`,
11
+ Write: (b, a) =>
12
+ `(${b}._buf += new TextDecoder().decode(new Uint8Array(${a[0]})), [${a[0]}.length, null])`,
9
13
  String: (b) => `${b}._buf`,
10
14
  Reset: (b) => `(${b}._buf = "")`,
15
+ Len: (b) => `new TextEncoder().encode(${b}._buf).length`,
16
+ Cap: (b) =>
17
+ `Math.max(${b}._cap ?? 0, new TextEncoder().encode(${b}._buf).length)`,
18
+ Grow: (b, a) =>
19
+ `((n) => { if (n < 0) throw new Error("strings.Builder.Grow: negative count"); ${b}._cap = Math.max(${b}._cap ?? 0, new TextEncoder().encode(${b}._buf).length) + n; })(${a[0]})`,
11
20
  };
12
21
  const BUILDER_BYTES = {
13
22
  WriteString: (b, a) =>
14
- `(${b}._buf.push(...new TextEncoder().encode(${a[0]})), [${a[0]}.length, null])`,
15
- WriteByte: (b, a) => `${b}._buf.push(${a[0]})`,
16
- Write: (b, a) => `(${b}._buf.push(...${a[0]}), [${a[0]}.length, null])`,
23
+ `((buf, s) => { const bytes = new TextEncoder().encode(s); for (let i = 0; i < bytes.length; i++) buf._buf.push(bytes[i]); return [bytes.length, null]; })(${b}, ${a[0]})`,
24
+ WriteByte: (b, a) => `(${b}._buf.push(${a[0]}), null)`,
25
+ WriteRune: (b, a) =>
26
+ `((buf, cp) => { const r = (cp < 0 || cp > 0x10ffff || (cp >= 0xd800 && cp <= 0xdfff)) ? 0xfffd : cp; const bytes = new TextEncoder().encode(String.fromCodePoint(r)); for (let i = 0; i < bytes.length; i++) buf._buf.push(bytes[i]); return [bytes.length, null]; })(${b}, ${a[0]})`,
27
+ Write: (b, a) =>
28
+ `((buf, d) => { for (let i = 0; i < d.length; i++) buf._buf.push(d[i]); return [d.length, null]; })(${b}, ${a[0]})`,
17
29
  String: (b) => `new TextDecoder().decode(new Uint8Array(${b}._buf))`,
18
30
  Reset: (b) => `(${b}._buf = [])`,
31
+ Read: (b, a) =>
32
+ `((buf, p) => { if (buf._buf.length === 0) return [0, __io_eof]; const n = Math.min(p.length, buf._buf.length); for (let i = 0; i < n; i++) p[i] = buf._buf.shift(); return [n, null]; })(${b}, ${a[0]})`,
19
33
  };
20
34
  const BUILDER_COMMON = {
21
- WriteRune: (b, a) => `(${b}._buf += String.fromCodePoint(${a[0]}))`,
22
- Bytes: (b) => `${b}._buf.slice()`,
35
+ Bytes: (b) => `new Uint8Array(${b}._buf)`,
23
36
  Len: (b) => `${b}._buf.length`,
24
- Grow: () => "undefined",
37
+ Cap: (b) => `Math.max(${b}._cap ?? 0, ${b}._buf.length)`,
38
+ Grow: (b, a) =>
39
+ `((n) => { if (n < 0) throw new Error("bytes.Buffer.Grow: negative count"); ${b}._cap = Math.max(${b}._cap ?? 0, ${b}._buf.length) + n; })(${a[0]})`,
25
40
  };
26
41
 
27
42
  /** @type {ThisType<CodeGen>} */
@@ -30,6 +45,10 @@ export const builderMethods = {
30
45
  const recv = expr.func.expr;
31
46
  const base = this._unboxWriter(this.genExpr(recv));
32
47
  const args = expr.args.map((a) => this.genExpr(a));
48
+ if (method === "Read") {
49
+ this.useHelper("ioEOF");
50
+ this.useHelper("error");
51
+ }
33
52
  const isStr = typeName === "strings.Builder";
34
53
  const table = isStr ? BUILDER_STR : BUILDER_BYTES;
35
54
  const gen = table[method] ?? BUILDER_COMMON[method];
@@ -2,8 +2,8 @@
2
2
 
3
3
  /** @typedef {import('../index.js').CodeGen} CodeGen */
4
4
 
5
- const __bs = `(b => String.fromCharCode(...b))`;
6
- const __sb = `(s => [...s].map(c => c.charCodeAt(0)))`;
5
+ const __bs = `(b => typeof TextDecoder !== "undefined" ? new TextDecoder().decode(b instanceof Uint8Array ? b : new Uint8Array(b)) : String.fromCharCode.apply(null, b))`;
6
+ const __sb = `(s => typeof TextEncoder !== "undefined" ? new TextEncoder().encode(s) : new Uint8Array([...s].map(c => c.charCodeAt(0))))`;
7
7
 
8
8
  const BYTES_DISPATCH = {
9
9
  Contains: (a) => `${__bs}(${a[0]}).includes(${__bs}(${a[1]}))`,
@@ -16,49 +16,106 @@ const BYTES_DISPATCH = {
16
16
  `((b, sep) => sep.length === 0 ? b.length + 1 : ${__bs}(b).split(${__bs}(sep)).length - 1)(${a[0]}, ${a[1]})`,
17
17
  Repeat: (a) => `${__sb}(${__bs}(${a[0]}).repeat(${a[1]}))`,
18
18
  Replace: (a) =>
19
- `((b, o, n, cnt) => { let s = ${__bs}(b), os = ${__bs}(o), ns = ${__bs}(n); if (cnt < 0) return ${__sb}(s.replaceAll(os, ns)); for (let i = 0; i < cnt; i++) s = s.replace(os, ns); return ${__sb}(s); })(${a[0]}, ${a[1]}, ${a[2]}, ${a[3]})`,
20
- ToUpper: (a) => `${__sb}(${__bs}(${a[0]}).toUpperCase())`,
21
- ToLower: (a) => `${__sb}(${__bs}(${a[0]}).toLowerCase())`,
22
- TrimSpace: (a) => `${__sb}(${__bs}(${a[0]}).trim())`,
19
+ `((b, o, n, cnt) => { let str = ${__bs}(b), os = ${__bs}(o), nw = ${__bs}(n); if (cnt < 0) return ${__sb}(str.replaceAll(os, () => nw)); let r = ""; let i = 0; for (; cnt > 0; cnt--) { const j = str.indexOf(os, i); if (j < 0) break; r += str.slice(i, j) + nw; i = j + os.length; if (os.length === 0) { if (i >= str.length) break; r += str[i]; i++; } } return ${__sb}(r + str.slice(i)); })(${a[0]}, ${a[1]}, ${a[2]}, ${a[3]})`,
20
+ ToUpper: (a) =>
21
+ `new TextEncoder().encode(new TextDecoder().decode(new Uint8Array(${a[0]})).toUpperCase())`,
22
+ ToLower: (a) =>
23
+ `new TextEncoder().encode(new TextDecoder().decode(new Uint8Array(${a[0]})).toLowerCase())`,
24
+ ToTitle: (a) =>
25
+ `new TextEncoder().encode(new TextDecoder().decode(new Uint8Array(${a[0]})).toUpperCase())`,
26
+ TrimSpace: (a) =>
27
+ `new TextEncoder().encode(new TextDecoder().decode(new Uint8Array(${a[0]})).trim())`,
23
28
  Trim: (a) =>
24
- `${__sb}(${__bs}(${a[0]}).replace(new RegExp(\`^[\${${a[1]}}]+|[\${${a[1]}}]+$\`, "g"), ""))`,
29
+ `((b, c) => { const s = new TextDecoder().decode(new Uint8Array(b)); const cs = new Set(Array.from(c)); const chars = Array.from(s); let l = 0, r = chars.length; while (l < r && cs.has(chars[l])) l++; while (r > l && cs.has(chars[r - 1])) r--; return new TextEncoder().encode(chars.slice(l, r).join("")); })(${a[0]}, ${a[1]})`,
25
30
  Equal: (a) =>
26
31
  `((a, b) => { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false; return true; })(${a[0]}, ${a[1]})`,
27
32
  Split: (a) => `${__bs}(${a[0]}).split(${__bs}(${a[1]})).map(p => ${__sb}(p))`,
28
33
  Join: (a) => `${__sb}(${a[0]}.map(p => ${__bs}(p)).join(${__bs}(${a[1]})))`,
29
34
  ReplaceAll: (a) =>
30
- `((b, o, n) => { const r = []; let i = 0; while (i <= b.length - o.length) { if (o.length > 0 && o.every((v, j) => b[i + j] === v)) { r.push(...n); i += o.length; } else { r.push(b[i++]); } } for (; i < b.length; i++) r.push(b[i]); return ArrayBuffer.isView(b) ? new Uint8Array(r) : r; })(${a[0]}, ${a[1]}, ${a[2]})`,
35
+ `((b, o, n) => { const r = []; let i = 0; while (i <= b.length - o.length) { if (o.length > 0 && o.every((v, j) => b[i + j] === v)) { r.push(...n); i += o.length; } else { r.push(b[i++]); } } for (; i < b.length; i++) r.push(b[i]); return new Uint8Array(r); })(${a[0]}, ${a[1]}, ${a[2]})`,
31
36
  TrimPrefix: (a) =>
32
37
  `((b, p) => p.every((v, i) => b[i] === v) ? b.slice(p.length) : b.slice())(${a[0]}, ${a[1]})`,
33
38
  TrimSuffix: (a) =>
34
39
  `((b, s) => s.length && s.every((v, i) => b[b.length - s.length + i] === v) ? b.slice(0, -s.length) : b.slice())(${a[0]}, ${a[1]})`,
35
40
  TrimLeft: (a) =>
36
- `((b, c) => { let i = 0; while (i < b.length && c.includes(String.fromCharCode(b[i]))) i++; return b.slice(i); })(${a[0]}, ${a[1]})`,
41
+ `((b, c) => { const s = new TextDecoder().decode(new Uint8Array(b)); const cs = new Set(Array.from(c)); const chars = Array.from(s); let l = 0, r = chars.length; while (l < r && cs.has(chars[l])) l++; return new TextEncoder().encode(chars.slice(l, r).join("")); })(${a[0]}, ${a[1]})`,
37
42
  TrimRight: (a) =>
38
- `((b, c) => { let i = b.length; while (i > 0 && c.includes(String.fromCharCode(b[i - 1]))) i--; return b.slice(0, i); })(${a[0]}, ${a[1]})`,
43
+ `((b, c) => { const s = new TextDecoder().decode(new Uint8Array(b)); const cs = new Set(Array.from(c)); const chars = Array.from(s); let l = 0, r = chars.length; while (r > l && cs.has(chars[r - 1])) r--; return new TextEncoder().encode(chars.slice(l, r).join("")); })(${a[0]}, ${a[1]})`,
39
44
  TrimFunc: (a) =>
40
- `((b, f) => { let l = 0, r = b.length; while (l < r && f(b[l])) l++; while (r > l && f(b[r - 1])) r--; return b.slice(l, r); })(${a[0]}, ${a[1]})`,
45
+ `((b, f) => { const s = new TextDecoder().decode(new Uint8Array(b)); const chars = Array.from(s); let l = 0, r = chars.length; while (l < r && f(chars[l].codePointAt(0))) l++; while (r > l && f(chars[r - 1].codePointAt(0))) r--; return new TextEncoder().encode(chars.slice(l, r).join("")); })(${a[0]}, ${a[1]})`,
41
46
  IndexByte: (a) => `(${a[0]}).indexOf(${a[1]})`,
42
47
  LastIndex: (a) =>
43
48
  `((b, s) => { for (let i = b.length - s.length; i >= 0; i--) if (s.every((v, j) => b[i + j] === v)) return i; return -1; })(${a[0]}, ${a[1]})`,
44
49
  LastIndexByte: (a) => `(${a[0]}).lastIndexOf(${a[1]})`,
45
50
  Fields: (a) =>
46
- `((b) => { const s = String.fromCharCode(...b).trim(); return s === '' ? [] : s.split(/\\s+/).map(w => [...w].map(c => c.charCodeAt(0))); })(${a[0]})`,
51
+ `((b) => { const s = new TextDecoder().decode(new Uint8Array(b ?? [])).trim(); return s === '' ? [] : s.split(/\\s+/).map(w => new TextEncoder().encode(w)); })(${a[0]})`,
52
+ FieldsFunc: (a) =>
53
+ `((b, fn) => { const s = new TextDecoder().decode(new Uint8Array(b ?? [])); const res = []; let cur = ""; for (const ch of s) { const cp = ch.codePointAt(0); if (fn(cp)) { if (cur.length > 0) { res.push(new TextEncoder().encode(cur)); cur = ""; } } else { cur += ch; } } if (cur.length > 0) res.push(new TextEncoder().encode(cur)); return res; })(${a[0]}, ${a[1]})`,
54
+ Clone: (a) =>
55
+ `(${a[0]} ? (${a[0]}.slice ? ${a[0]}.slice() : Array.from(${a[0]})) : null)`,
47
56
  Cut: (a) =>
48
- `((b, sep) => { for (let i = 0; i <= b.length - sep.length; i++) if (sep.every((v, j) => b[i + j] === v)) return [b.slice(0, i), b.slice(i + sep.length), true]; return [b.slice(), [], false]; })(${a[0]}, ${a[1]})`,
57
+ `((b, sep) => { for (let i = 0; i <= b.length - sep.length; i++) if (sep.every((v, j) => b[i + j] === v)) return [b.slice(0, i), b.slice(i + sep.length), true]; return [b.slice(), new Uint8Array(0), false]; })(${a[0]}, ${a[1]})`,
49
58
  ContainsAny: (a) =>
50
- `[...(${a[1]})].some(c => (${a[0]}).includes(c.charCodeAt(0)))`,
51
- ContainsRune: (a) => `(${a[0]}).includes(${a[1]})`,
52
- Map: (a) => `(${a[1]}).map(v => (${a[0]})(v))`,
59
+ `((b, chars) => { const s = new TextDecoder().decode(new Uint8Array(b)); for (const c of chars) if (s.includes(c)) return true; return false; })(${a[0]}, ${a[1]})`,
60
+ ContainsRune: (a) =>
61
+ `((b, r) => { const s = new TextDecoder().decode(new Uint8Array(b)); return s.includes(String.fromCodePoint(r < 0 || r > 0x10ffff || (r >= 0xd800 && r <= 0xdfff) ? 0xfffd : r)); })(${a[0]}, ${a[1]})`,
62
+ Map: (a) =>
63
+ `((f, b) => { const s = new TextDecoder().decode(new Uint8Array(b)); let res = ""; for (const c of s) { const r = f(c.codePointAt(0)); if (r >= 0) res += String.fromCodePoint(r); } return new TextEncoder().encode(res); })(${a[0]}, ${a[1]})`,
53
64
  SplitN: (a) =>
54
- `((b, sep, n) => { if (n === 0) return []; if (n < 0) return ((b, sep) => { const r = []; let i = 0; while (i <= b.length) { const j = b.findIndex((_, k) => k >= i && sep.every((v, l) => b[k + l] === sep[l])); if (j < 0 || j >= b.length) break; r.push(b.slice(i, j)); i = j + sep.length; } r.push(b.slice(i)); return r; })(b, sep); const r = []; let i = 0; for (let c = 1; c < n; c++) { const j = b.findIndex((_, k) => k >= i && sep.every((v, l) => b[k + l] === sep[l])); if (j < 0) break; r.push(b.slice(i, j)); i = j + sep.length; } r.push(b.slice(i)); return r; })(${a[0]}, ${a[1]}, ${a[2]})`,
65
+ `((b, sep, n) => {
66
+ if (n === 0) return null;
67
+ if (sep.length === 0) {
68
+ if (n < 0 || b.length <= n) {
69
+ const r = [];
70
+ for (let k = 0; k < b.length; k++) r.push(b.slice(k, k + 1));
71
+ return r;
72
+ }
73
+ const r = [];
74
+ for (let k = 0; k < n - 1; k++) r.push(b.slice(k, k + 1));
75
+ r.push(b.slice(n - 1));
76
+ return r;
77
+ }
78
+ const findNext = (start) => {
79
+ for (let k = start; k <= b.length - sep.length; k++) {
80
+ let match = true;
81
+ for (let l = 0; l < sep.length; l++) {
82
+ if (b[k + l] !== sep[l]) { match = false; break; }
83
+ }
84
+ if (match) return k;
85
+ }
86
+ return -1;
87
+ };
88
+ if (n < 0) {
89
+ const r = [];
90
+ let i = 0;
91
+ while (i <= b.length) {
92
+ const j = findNext(i);
93
+ if (j < 0) break;
94
+ r.push(b.slice(i, j));
95
+ i = j + sep.length;
96
+ }
97
+ r.push(b.slice(i));
98
+ return r;
99
+ }
100
+ const r = [];
101
+ let i = 0;
102
+ for (let c = 1; c < n; c++) {
103
+ const j = findNext(i);
104
+ if (j < 0) break;
105
+ r.push(b.slice(i, j));
106
+ i = j + sep.length;
107
+ }
108
+ r.push(b.slice(i));
109
+ return r;
110
+ })(${a[0]}, ${a[1]}, ${a[2]})`,
55
111
  NewReader: (a) =>
56
- `{_src: ${a[0]}, _pos: 0, Read(p) { const n = Math.min(p.length, this._src.length - this._pos); for (let i = 0; i < n; i++) p[i] = this._src[this._pos + i]; this._pos += n; return [n, n === 0 ? "EOF" : null]; }, Len() { return this._src.length - this._pos; }, Reset(b) { this._src = b; this._pos = 0; }}`,
112
+ `((b) => ({ _src: b, _pos: 0, Read(p) { if (this._pos >= this._src.length) return [0, __io_eof]; const n = Math.min(p.length, this._src.length - this._pos); for (let i = 0; i < n; i++) p[i] = this._src[this._pos + i]; this._pos += n; return [n, null]; }, Len() { return Math.max(0, this._src.length - this._pos); }, Reset(b2) { this._src = b2; this._pos = 0; } }))(${a[0]})`,
57
113
  };
58
114
 
59
115
  /** @type {ThisType<CodeGen>} */
60
116
  export const bytesMethods = {
61
117
  _genBytes(fn, a) {
118
+ if (fn === "NewReader") this.useHelper("ioEOF");
62
119
  const gen = BYTES_DISPATCH[fn];
63
120
  return gen ? gen(a()) : undefined;
64
121
  },
@@ -4,7 +4,7 @@
4
4
 
5
5
  /** @type {ThisType<CodeGen>} */
6
6
  export const errorsMethods = {
7
- _genErrors(fn, a) {
7
+ _genErrors(fn, a, expr) {
8
8
  const args = a();
9
9
  if (fn === "New") {
10
10
  this.useHelper("error");
@@ -15,6 +15,30 @@ export const errorsMethods = {
15
15
  return `__errorIs(${args[0]}, ${args[1]})`;
16
16
  }
17
17
  if (fn === "Unwrap") return `(${args[0]}?._cause ?? null)`;
18
+ if (fn === "As") {
19
+ this.useHelper("errorAs");
20
+ const targetGoType = expr?.args?.[1]?._type;
21
+ const baseGoType = targetGoType?.base ?? targetGoType;
22
+ const concreteGoType = baseGoType?.base ?? baseGoType;
23
+ const typeName = concreteGoType?.name;
24
+ if (concreteGoType?.kind === "interface" || typeName === "error") {
25
+ return `__errorAs(${args[0]}, ${args[1]}, (x) => x !== null && (typeof x === "object" ? typeof x.Error === "function" : true))`;
26
+ }
27
+ // Named non-struct error types sit in interfaces as boxes; store the raw value.
28
+ const namedBox = this._namedBoxInfo(concreteGoType);
29
+ if (namedBox) {
30
+ return `__errorAs(${args[0]}, ${args[1]}, (x) => x instanceof ${namedBox.cls}, (x) => x.__gfUnbox())`;
31
+ }
32
+ if (typeName) {
33
+ return `__errorAs(${args[0]}, ${args[1]}, (x) => (typeof ${typeName} === "function" && x instanceof ${typeName}) || x?.constructor?.name === "${typeName}" || x?.__struct === "${typeName}")`;
34
+ }
35
+ return `__errorAs(${args[0]}, ${args[1]})`;
36
+ }
37
+ if (fn === "Join") {
38
+ this.useHelper("error");
39
+ this.useHelper("errorJoin");
40
+ return `__errorJoin(${args.join(", ")})`;
41
+ }
18
42
  return undefined;
19
43
  },
20
44
  };