gofront 1.6.0 → 1.6.1

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 (68) hide show
  1. package/package.json +1 -1
  2. package/src/backend/js/expressions.js +238 -109
  3. package/src/backend/js/index.js +38 -1
  4. package/src/backend/js/runtime.js +953 -53
  5. package/src/backend/js/statements.js +55 -67
  6. package/src/backend/js/stdlib/base64.js +12 -0
  7. package/src/backend/js/stdlib/binary.js +102 -0
  8. package/src/backend/js/stdlib/bits.js +15 -1
  9. package/src/backend/js/stdlib/builder.js +28 -9
  10. package/src/backend/js/stdlib/bytes.js +75 -18
  11. package/src/backend/js/stdlib/errors.js +20 -1
  12. package/src/backend/js/stdlib/fmt.js +70 -13
  13. package/src/backend/js/stdlib/hex.js +17 -0
  14. package/src/backend/js/stdlib/html.js +1 -1
  15. package/src/backend/js/stdlib/index.js +16 -0
  16. package/src/backend/js/stdlib/io.js +105 -3
  17. package/src/backend/js/stdlib/math.js +13 -1
  18. package/src/backend/js/stdlib/path.js +18 -4
  19. package/src/backend/js/stdlib/rand.js +10 -5
  20. package/src/backend/js/stdlib/sha256.js +12 -0
  21. package/src/backend/js/stdlib/slices.js +52 -26
  22. package/src/backend/js/stdlib/strconv.js +21 -5
  23. package/src/backend/js/stdlib/strings.js +148 -15
  24. package/src/backend/js/stdlib/sync.js +13 -0
  25. package/src/backend/js/stdlib/time.js +90 -19
  26. package/src/backend/js/stdlib/unicode.js +2 -2
  27. package/src/backend/wasm/boundary.js +1207 -41
  28. package/src/backend/wasm/const-fold.js +205 -0
  29. package/src/backend/wasm/emit-builtins.js +203 -76
  30. package/src/backend/wasm/emit-exprs.js +464 -276
  31. package/src/backend/wasm/emit-maps.js +144 -8
  32. package/src/backend/wasm/emit-stdlib.js +12 -3005
  33. package/src/backend/wasm/emit-stmts.js +264 -270
  34. package/src/backend/wasm/emit.js +115 -87
  35. package/src/backend/wasm/encode.js +55 -29
  36. package/src/backend/wasm/index.js +1001 -32
  37. package/src/backend/wasm/optimize.js +13 -4
  38. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  39. package/src/backend/wasm/stdlib/core.js +8903 -0
  40. package/src/backend/wasm/stdlib/index.js +40 -0
  41. package/src/backend/wasm/stdlib/io.js +2161 -0
  42. package/src/backend/wasm/stdlib/packages.js +2400 -0
  43. package/src/backend/wasm/stdlib/pure.js +720 -0
  44. package/src/backend/wasm/stdlib/time.js +1200 -0
  45. package/src/backend/wasm/types.js +153 -1
  46. package/src/cli-core.js +1 -1
  47. package/src/compiler.js +18 -4
  48. package/src/dts-parser.js +79 -126
  49. package/src/index.js +2 -6
  50. package/src/lexer.js +80 -29
  51. package/src/lower/boxing.js +1 -2
  52. package/src/lower/captures.js +2 -6
  53. package/src/lower/ownership.js +30 -9
  54. package/src/minifier.js +20 -68
  55. package/src/parser/declarations.js +3 -6
  56. package/src/parser/expressions.js +3 -8
  57. package/src/parser/index.js +18 -3
  58. package/src/parser/statements.js +25 -38
  59. package/src/parser/types.js +1 -2
  60. package/src/resolver.js +2 -27
  61. package/src/templ-lexer.js +29 -63
  62. package/src/templ-parser.js +22 -45
  63. package/src/typechecker/assignability.js +81 -13
  64. package/src/typechecker/expressions.js +102 -2
  65. package/src/typechecker/resolve.js +36 -6
  66. package/src/typechecker/stdlib/core.js +280 -89
  67. package/src/typechecker/stdlib/extended.js +453 -1
  68. package/src/typechecker/types.js +344 -6
@@ -696,6 +696,39 @@ export const statementGenMethods = {
696
696
  return nodeAssigns(node, name);
697
697
  },
698
698
 
699
+ _genStringRangeFor(stmt, iteree, isAssign) {
700
+ this._loopDepth = this._loopDepth ?? 0;
701
+ const d = this._loopDepth++;
702
+ const [idxNode, valNode] = stmt.init.lhs;
703
+ const s = `__s${d}`;
704
+ const i = `__i${d}`;
705
+ const cp = `__cp${d}`;
706
+ this.line(
707
+ `for (let ${i} = 0, ${s} = Array.from(${iteree} ?? ""); ${i} < ${s}.length; ${i}++) {`,
708
+ );
709
+ this.indented(() => {
710
+ this._emitRangeVar(idxNode, i, isAssign);
711
+ if (valNode && valNode.name !== "_") {
712
+ this.line(`const ${cp} = ${s}[${i}].codePointAt(0);`);
713
+ this._emitRangeVar(valNode, cp, isAssign);
714
+ }
715
+ const declared = isAssign
716
+ ? []
717
+ : [idxNode, valNode]
718
+ .filter((e) => e?.kind === "Ident" && e.name !== "_")
719
+ .map((e) => e.name);
720
+ if (this._bodyRedeclares(stmt.body, declared)) {
721
+ this.line("{");
722
+ this.indented(() => this.genBlock(stmt.body));
723
+ this.line("}");
724
+ } else {
725
+ this.genBlock(stmt.body);
726
+ }
727
+ });
728
+ this.line("}");
729
+ this._loopDepth--;
730
+ },
731
+
699
732
  _genIterFor(init, iterType, iteree, lhs, body) {
700
733
  const iterExpr = this._genRangeIterExpr(iterType, iteree, lhs);
701
734
  const binding =
@@ -725,12 +758,17 @@ export const statementGenMethods = {
725
758
  return;
726
759
  }
727
760
 
728
- if (!this._isMapRangeType(iterType) && !this._isStringRangeType(iterType)) {
729
- this._genSliceRangeFor(stmt, iteree, init.kind === "AssignStmt");
761
+ if (this._isStringRangeType(iterType)) {
762
+ this._genStringRangeFor(stmt, iteree, init.kind === "AssignStmt");
730
763
  return;
731
764
  }
732
765
 
733
- this._genIterFor(init, iterType, iteree, lhs, stmt.body);
766
+ if (this._isMapRangeType(iterType)) {
767
+ this._genIterFor(init, iterType, iteree, lhs, stmt.body);
768
+ return;
769
+ }
770
+
771
+ this._genSliceRangeFor(stmt, iteree, init.kind === "AssignStmt");
734
772
  },
735
773
 
736
774
  _genRangeIterExpr(iterType, iteree, lhs) {
@@ -738,23 +776,14 @@ export const statementGenMethods = {
738
776
  if (lhs.length === 1) return `Object.keys(${iteree})`;
739
777
  return `Object.entries(${iteree})`;
740
778
  }
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
779
  this.useHelper("sliceGuard");
746
780
  if (lhs.length === 1) return `__s(${iteree}).keys()`;
747
781
  return `__s(${iteree}).entries()`;
748
782
  },
749
783
 
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") {
784
+ _genIteratorLoop(itereeExpr, paramsStr, body) {
785
+ let iteree = this.genExpr(itereeExpr);
786
+ if (itereeExpr.kind === "FuncLit") {
758
787
  iteree = `(${iteree})`;
759
788
  }
760
789
 
@@ -763,17 +792,13 @@ export const statementGenMethods = {
763
792
  const retFlag = `__returned${d}`;
764
793
  const retVar = `__retVal${d}`;
765
794
 
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
795
  this.line("{");
770
796
  this.indented(() => {
771
797
  this.line(`let ${breakFlag} = false;`);
772
798
  this.line(`let ${retFlag} = false;`);
773
799
  this.line(`let ${retVar};`);
774
800
 
775
- const params = yieldParams.length === 0 ? "" : cbParams.join(", ");
776
- this.line(`${iteree}(function(${params}) {`);
801
+ this.line(`${iteree}(function(${paramsStr}) {`);
777
802
  this.indented(() => {
778
803
  this.line(`if (${breakFlag}) return false;`);
779
804
  const prev = {
@@ -787,7 +812,7 @@ export const statementGenMethods = {
787
812
  this._iterReturnFlag = retFlag;
788
813
  this._iterReturnVar = retVar;
789
814
 
790
- this.genBlock(stmt.body);
815
+ this.genBlock(body);
791
816
 
792
817
  this._inIteratorBody = prev.in;
793
818
  this._iterBreakFlag = prev.break;
@@ -803,54 +828,17 @@ export const statementGenMethods = {
803
828
  this._iterDepth--;
804
829
  },
805
830
 
831
+ genIteratorFor(stmt) {
832
+ const range = stmt.init.rhs[0];
833
+ const lhs = stmt.init.lhs.map((e) => e.name ?? this.genExpr(e));
834
+ const cbParams = lhs.map((n, i) => (n === "_" ? `_$${i}` : n));
835
+ const params = range._yieldParams.length === 0 ? "" : cbParams.join(", ");
836
+ this._genIteratorLoop(range.expr, params, stmt.body);
837
+ },
838
+
806
839
  // 0-param iterator: for range iter { body }
807
840
  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--;
841
+ this._genIteratorLoop(stmt.cond.expr, "", stmt.body);
854
842
  },
855
843
 
856
844
  // Inline a statement as a for-init/post string (no semicolon)
@@ -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,25 @@ 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
+ if (typeName) {
28
+ return `__errorAs(${args[0]}, ${args[1]}, (x) => (typeof ${typeName} === "function" && x instanceof ${typeName}) || x?.constructor?.name === "${typeName}" || x?.__struct === "${typeName}")`;
29
+ }
30
+ return `__errorAs(${args[0]}, ${args[1]})`;
31
+ }
32
+ if (fn === "Join") {
33
+ this.useHelper("error");
34
+ this.useHelper("errorJoin");
35
+ return `__errorJoin(${args.join(", ")})`;
36
+ }
18
37
  return undefined;
19
38
  },
20
39
  };