gofront 1.5.1 → 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.
- package/README.md +174 -466
- package/package.json +16 -20
- package/src/backend/js/expressions.js +288 -128
- package/src/backend/js/index.js +138 -56
- package/src/backend/js/runtime.js +1055 -53
- package/src/backend/js/source-map.js +32 -0
- package/src/backend/js/statements.js +151 -83
- package/src/backend/js/stdlib/base64.js +12 -0
- package/src/backend/js/stdlib/binary.js +102 -0
- package/src/backend/js/stdlib/bits.js +15 -1
- package/src/backend/js/stdlib/builder.js +28 -9
- package/src/backend/js/stdlib/bytes.js +75 -18
- package/src/backend/js/stdlib/errors.js +22 -3
- package/src/backend/js/stdlib/fmt.js +95 -20
- package/src/backend/js/stdlib/hex.js +17 -0
- package/src/backend/js/stdlib/html.js +1 -1
- package/src/backend/js/stdlib/index.js +19 -0
- package/src/backend/js/stdlib/io.js +105 -3
- package/src/backend/js/stdlib/maps.js +1 -1
- package/src/backend/js/stdlib/math.js +13 -1
- package/src/backend/js/stdlib/path.js +20 -6
- package/src/backend/js/stdlib/rand.js +10 -5
- package/src/backend/js/stdlib/sha256.js +12 -0
- package/src/backend/js/stdlib/shared.js +17 -0
- package/src/backend/js/stdlib/slices.js +53 -27
- package/src/backend/js/stdlib/sort.js +1 -1
- package/src/backend/js/stdlib/strconv.js +52 -11
- package/src/backend/js/stdlib/strings.js +152 -16
- package/src/backend/js/stdlib/sync.js +13 -0
- package/src/backend/js/stdlib/testing.js +2 -2
- package/src/backend/js/stdlib/time.js +93 -22
- package/src/backend/js/stdlib/unicode.js +2 -2
- package/src/backend/js/templ.js +1 -1
- package/src/backend/wasm/boundary.js +1677 -58
- package/src/backend/wasm/const-fold.js +205 -0
- package/src/backend/wasm/emit-builtins.js +1230 -0
- package/src/backend/wasm/emit-exprs.js +2096 -0
- package/src/backend/wasm/emit-maps.js +1999 -0
- package/src/backend/wasm/emit-shared.js +519 -0
- package/src/backend/wasm/emit-stdlib.js +13 -0
- package/src/backend/wasm/emit-stmts.js +2466 -0
- package/src/backend/wasm/emit.js +656 -5031
- package/src/backend/wasm/encode.js +259 -85
- package/src/backend/wasm/glue.js +19 -5
- package/src/backend/wasm/index.js +2432 -86
- package/src/backend/wasm/monomorph.js +711 -0
- package/src/backend/wasm/optimize.js +236 -0
- package/src/backend/wasm/stdlib/bytes.js +5039 -0
- package/src/backend/wasm/stdlib/core.js +8903 -0
- package/src/backend/wasm/stdlib/index.js +40 -0
- package/src/backend/wasm/stdlib/io.js +2161 -0
- package/src/backend/wasm/stdlib/packages.js +2400 -0
- package/src/backend/wasm/stdlib/pure.js +720 -0
- package/src/backend/wasm/stdlib/time.js +1200 -0
- package/src/backend/wasm/types.js +312 -36
- package/src/backend/wasm/wat.js +49 -2
- package/src/cli-core.js +276 -73
- package/src/compiler.js +162 -25
- package/src/dts-parser.js +79 -126
- package/src/index.js +88 -121
- package/src/lexer.js +80 -29
- package/src/lower/boxing.js +4 -6
- package/src/lower/captures.js +12 -23
- package/src/lower/escape.js +4 -9
- package/src/lower/functions.js +277 -5
- package/src/lower/globals.js +7 -15
- package/src/lower/index.js +5 -2
- package/src/lower/ownership.js +35 -19
- package/src/lower/range.js +2 -5
- package/src/lower/walk.js +25 -0
- package/src/minifier.js +20 -68
- package/src/parser/declarations.js +3 -6
- package/src/parser/expressions.js +3 -8
- package/src/parser/index.js +18 -3
- package/src/parser/statements.js +25 -38
- package/src/parser/types.js +1 -2
- package/src/resolver.js +3 -27
- package/src/templ-lexer.js +29 -63
- package/src/templ-parser.js +22 -45
- package/src/test-runner.js +20 -0
- package/src/typechecker/assignability.js +123 -14
- package/src/typechecker/expressions.js +244 -4
- package/src/typechecker/index.js +27 -1
- package/src/typechecker/resolve.js +71 -12
- package/src/typechecker/statements.js +0 -7
- package/src/typechecker/stdlib/core.js +292 -103
- package/src/typechecker/stdlib/extended.js +471 -4
- package/src/typechecker/stdlib/shared.js +53 -0
- package/src/typechecker/stdlib.js +2 -0
- package/src/typechecker/types.js +545 -8
- package/src/vendor.js +2 -2
|
@@ -6,26 +6,40 @@ const PATH_DISPATCH = {
|
|
|
6
6
|
Base: (a) =>
|
|
7
7
|
`((p) => { if (!p) return "."; const stripped = p.replace(/\\/+$/, ""); if (!stripped) return "/"; const i = stripped.lastIndexOf("/"); return i < 0 ? stripped : stripped.slice(i + 1) || "/"; })(${a[0]})`,
|
|
8
8
|
Dir: (a) =>
|
|
9
|
-
`((p) => { const i = p.lastIndexOf("/"); if (i < 0) return ".";
|
|
9
|
+
`((p) => { const i = p.lastIndexOf("/"); if (i < 0) return "."; const res = p.slice(0, i); return res === "" ? "/" : __pathClean(res); })(${a[0]})`,
|
|
10
10
|
Ext: (a) =>
|
|
11
11
|
`((p) => { const b = p.slice(p.lastIndexOf("/") + 1); const i = b.lastIndexOf("."); return i <= 0 ? "" : b.slice(i); })(${a[0]})`,
|
|
12
12
|
IsAbs: (a) => `(${a[0]}).startsWith("/")`,
|
|
13
13
|
Split: (a) =>
|
|
14
14
|
`((p) => { const i = p.lastIndexOf("/"); return i < 0 ? ["", p] : [p.slice(0, i + 1), p.slice(i + 1)]; })(${a[0]})`,
|
|
15
15
|
Match: (a) =>
|
|
16
|
-
`((pat, name) => {
|
|
16
|
+
`((pat, name) => { const [m, err] = __pathMatch(pat, name); return [m, err ? __path_err_bad_pattern : null]; })(${a[0]}, ${a[1]})`,
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
/** @type {ThisType<CodeGen>} */
|
|
20
20
|
export const pathMethods = {
|
|
21
21
|
_genPath(fn, a, expr) {
|
|
22
|
+
if (fn === "Match") {
|
|
23
|
+
this.useHelper("error");
|
|
24
|
+
this.useHelper("pathError");
|
|
25
|
+
this.useHelper("pathClean");
|
|
26
|
+
}
|
|
27
|
+
if (fn === "Dir") {
|
|
28
|
+
this.useHelper("pathClean");
|
|
29
|
+
}
|
|
22
30
|
if (fn === "Join") {
|
|
23
|
-
this.
|
|
24
|
-
const allArgs = expr.args
|
|
25
|
-
|
|
31
|
+
this.useHelper("pathClean");
|
|
32
|
+
const allArgs = expr.args
|
|
33
|
+
.map((e) =>
|
|
34
|
+
e._spread || expr.ellipsis
|
|
35
|
+
? `...(${this.genExpr(e)})`
|
|
36
|
+
: this.genExpr(e),
|
|
37
|
+
)
|
|
38
|
+
.join(", ");
|
|
39
|
+
return `((arr) => arr.length === 0 ? "" : __pathClean(arr.join("/")))([${allArgs}].filter(x => x !== ""))`;
|
|
26
40
|
}
|
|
27
41
|
if (fn === "Clean") {
|
|
28
|
-
this.
|
|
42
|
+
this.useHelper("pathClean");
|
|
29
43
|
return `__pathClean(${a()[0]})`;
|
|
30
44
|
}
|
|
31
45
|
const gen = PATH_DISPATCH[fn];
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
4
|
|
|
5
5
|
const RAND_DISPATCH = {
|
|
6
|
-
Intn: (a) =>
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
Intn: (a) =>
|
|
7
|
+
`((n) => { if (n <= 0) throw new Error("invalid argument to Intn"); return Math.floor(Math.random() * n); })(${a[0]})`,
|
|
8
|
+
Int63n: (a) =>
|
|
9
|
+
`((n) => { if (n <= 0) throw new Error("invalid argument to Int63n"); return Math.floor(Math.random() * n); })(${a[0]})`,
|
|
10
|
+
Int31n: (a) =>
|
|
11
|
+
`((n) => { if (n <= 0) throw new Error("invalid argument to Int31n"); return Math.floor(Math.random() * n); })(${a[0]})`,
|
|
9
12
|
Float64: () => "Math.random()",
|
|
10
13
|
Float32: () => "Math.random()",
|
|
11
14
|
Int: () => "Math.floor(Math.random() * 2147483647)",
|
|
@@ -13,14 +16,16 @@ const RAND_DISPATCH = {
|
|
|
13
16
|
Int63: () => "Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)",
|
|
14
17
|
Seed: () => "(void 0)",
|
|
15
18
|
Shuffle: (a) =>
|
|
16
|
-
`((n, f) => { for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); f(i, j); } })(${a[0]}, ${a[1]})`,
|
|
19
|
+
`((n, f) => { if (n < 0) throw new Error("invalid argument to Shuffle"); for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); f(i, j); } })(${a[0]}, ${a[1]})`,
|
|
17
20
|
Perm: (a) =>
|
|
18
|
-
`((n) => { const a = Array.from({length: n}, (_, i) => i); for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; })(${a[0]})`,
|
|
21
|
+
`((n) => { if (n < 0) throw new Error("invalid argument to Perm"); const a = Array.from({length: n}, (_, i) => i); for (let i = n - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; })(${a[0]})`,
|
|
22
|
+
Read: (a) => `__crypto_rand_read(${a[0]})`,
|
|
19
23
|
};
|
|
20
24
|
|
|
21
25
|
/** @type {ThisType<CodeGen>} */
|
|
22
26
|
export const randMethods = {
|
|
23
27
|
_genRand(fn, a) {
|
|
28
|
+
if (fn === "Read") this.useHelper("cryptoRand");
|
|
24
29
|
const gen = RAND_DISPATCH[fn];
|
|
25
30
|
return gen ? gen(a()) : undefined;
|
|
26
31
|
},
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// src/backend/js/stdlib/sha256.js
|
|
2
|
+
// CodeGen for Go crypto/sha256 package.
|
|
3
|
+
|
|
4
|
+
export const sha256Methods = {
|
|
5
|
+
_genSha256(fn, a) {
|
|
6
|
+
this.useHelper("sha256");
|
|
7
|
+
const args = a();
|
|
8
|
+
if (fn === "Sum256") return `__sha256.Sum256(${args[0]})`;
|
|
9
|
+
if (fn === "New") return "__sha256.New()";
|
|
10
|
+
return undefined;
|
|
11
|
+
},
|
|
12
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// CodeGen for the `gofront/shared` package: in js packages a shared buffer is
|
|
2
|
+
// just a TypedArray (no linear memory is involved); in wasm packages the facade
|
|
3
|
+
// hands out zero-copy views onto the module memory.
|
|
4
|
+
|
|
5
|
+
import { SHARED_KINDS } from "../../../typechecker/stdlib/shared.js";
|
|
6
|
+
|
|
7
|
+
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
8
|
+
|
|
9
|
+
/** @type {ThisType<CodeGen>} */
|
|
10
|
+
export const sharedMethods = {
|
|
11
|
+
_genShared(fn, a) {
|
|
12
|
+
if (!fn.startsWith("New")) return undefined;
|
|
13
|
+
const info = SHARED_KINDS[fn.slice(3)];
|
|
14
|
+
if (!info) return undefined;
|
|
15
|
+
return `new ${info.ctor}(${a()[0]})`;
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -1,51 +1,77 @@
|
|
|
1
|
-
|
|
1
|
+
import { isTypedArraySlice } from "../../../typechecker/types.js";
|
|
2
2
|
|
|
3
3
|
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
4
|
|
|
5
5
|
const SLICES_DISPATCH = {
|
|
6
6
|
Contains: (a) => `${a[0]}.includes(${a[1]})`,
|
|
7
|
+
ContainsFunc: (a) =>
|
|
8
|
+
`((s, f) => { s = s || []; for (let i = 0; i < s.length; i++) { if (f(s[i])) return true; } return false; })(${a[0]}, ${a[1]})`,
|
|
7
9
|
Index: (a) => `${a[0]}.indexOf(${a[1]})`,
|
|
10
|
+
IndexFunc: (a) =>
|
|
11
|
+
`((s, f) => { s = s || []; for (let i = 0; i < s.length; i++) { if (f(s[i])) return i; } return -1; })(${a[0]}, ${a[1]})`,
|
|
12
|
+
BinarySearch: (a) =>
|
|
13
|
+
`((s, target) => { s = s || []; let i = 0, j = s.length; while (i < j) { const h = (i + j) >>> 1; if (s[h] < target) i = h + 1; else j = h; } return [i, i < s.length && s[i] === target]; })(${a[0]}, ${a[1]})`,
|
|
14
|
+
BinarySearchFunc: (a) =>
|
|
15
|
+
`((s, target, cmp) => { s = s || []; let i = 0, j = s.length; while (i < j) { const h = (i + j) >>> 1; if (cmp(s[h], target) < 0) i = h + 1; else j = h; } return [i, i < s.length && cmp(s[i], target) === 0]; })(${a[0]}, ${a[1]}, ${a[2]})`,
|
|
8
16
|
Compare: (a) =>
|
|
9
|
-
`((a, b) => { for (let i = 0; i < a.length && i < b.length; i++) {
|
|
10
|
-
Sort: (a) =>
|
|
11
|
-
|
|
12
|
-
|
|
17
|
+
`((a, b) => { a = a || []; b = b || []; for (let i = 0; i < a.length && i < b.length; i++) { const x = a[i], y = b[i]; const xN = typeof x === "number" && isNaN(x), yN = typeof y === "number" && isNaN(y); if (xN && yN) continue; if (xN) return -1; if (yN) return 1; if (x < y) return -1; if (x > y) return 1; } return a.length < b.length ? -1 : a.length > b.length ? 1 : 0; })(${a[0]}, ${a[1]})`,
|
|
18
|
+
Sort: (a) =>
|
|
19
|
+
`((s) => s ? s.sort((a, b) => a < b ? -1 : a > b ? 1 : 0) : null)(${a[0]})`,
|
|
20
|
+
SortFunc: (a) => `((s, f) => s ? s.sort(f) : null)(${a[0]}, ${a[1]})`,
|
|
21
|
+
SortStableFunc: (a) => `((s, f) => s ? s.sort(f) : null)(${a[0]}, ${a[1]})`,
|
|
13
22
|
IsSorted: (a) =>
|
|
14
|
-
`((s) => { for (let i =
|
|
23
|
+
`((s) => { s = s || []; for (let i = 0; i < s.length; i++) { if (typeof s[i] === "number" && isNaN(s[i])) return false; if (i > 0 && s[i] < s[i-1]) return false; } return true; })(${a[0]})`,
|
|
15
24
|
IsSortedFunc: (a) =>
|
|
16
|
-
`((s, f) => { for (let i = 1; i < s.length; i++) { if (f(s[i], s[i-1]) < 0) return false; } return true; })(${a[0]}, ${a[1]})`,
|
|
17
|
-
Reverse: (a) =>
|
|
18
|
-
Max: (a) =>
|
|
19
|
-
|
|
25
|
+
`((s, f) => { s = s || []; for (let i = 1; i < s.length; i++) { if (f(s[i], s[i-1]) < 0) return false; } return true; })(${a[0]}, ${a[1]})`,
|
|
26
|
+
Reverse: (a) => `((s) => s ? s.reverse() : null)(${a[0]})`,
|
|
27
|
+
Max: (a) =>
|
|
28
|
+
`((s) => { if (!s || s.length === 0) throw new Error("slices.Max: empty list"); let m = s[0]; for (let i = 1; i < s.length; i++) { const x = s[i]; if ((typeof x === "number" && isNaN(x)) || x > m || (x === 0 && m === 0 && Object.is(m, -0) && !Object.is(x, -0))) m = x; } return m; })(${a[0]})`,
|
|
29
|
+
Min: (a) =>
|
|
30
|
+
`((s) => { if (!s || s.length === 0) throw new Error("slices.Min: empty list"); let m = s[0]; for (let i = 1; i < s.length; i++) { const x = s[i]; if ((typeof x === "number" && isNaN(x)) || x < m || (x === 0 && m === 0 && Object.is(x, -0))) m = x; } return m; })(${a[0]})`,
|
|
20
31
|
MaxFunc: (a) =>
|
|
21
|
-
`((s, f) => s.reduce((m, x) => f(x, m) > 0 ? x : m))(${a[0]}, ${a[1]})`,
|
|
32
|
+
`((s, f) => { if (!s || s.length === 0) throw new Error("slices.MaxFunc: empty list"); return s.reduce((m, x) => f(x, m) > 0 ? x : m); })(${a[0]}, ${a[1]})`,
|
|
22
33
|
MinFunc: (a) =>
|
|
23
|
-
`((s, f) => s.reduce((m, x) => f(x, m) < 0 ? x : m))(${a[0]}, ${a[1]})`,
|
|
24
|
-
Clone: (a) =>
|
|
34
|
+
`((s, f) => { if (!s || s.length === 0) throw new Error("slices.MinFunc: empty list"); return s.reduce((m, x) => f(x, m) < 0 ? x : m); })(${a[0]}, ${a[1]})`,
|
|
35
|
+
Clone: (a) => `((s) => s ? s.slice() : null)(${a[0]})`,
|
|
25
36
|
Compact: (a) =>
|
|
26
|
-
`((s) => s.
|
|
37
|
+
`((s) => { if (!s || s.length === 0) return s; let k = 1; for (let i = 1; i < s.length; i++) { if (s[i] !== s[k - 1]) s[k++] = s[i]; } return ArrayBuffer.isView(s) ? s.subarray(0, k) : (s.length = k, s); })(${a[0]})`,
|
|
27
38
|
CompactFunc: (a) =>
|
|
28
|
-
`((s, f) => s.
|
|
29
|
-
Concat: (a) =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
`((s, f) => { if (!s || s.length === 0) return s; let k = 1; for (let i = 1; i < s.length; i++) { if (!f(s[i], s[k - 1])) s[k++] = s[i]; } return ArrayBuffer.isView(s) ? s.subarray(0, k) : (s.length = k, s); })(${a[0]}, ${a[1]})`,
|
|
40
|
+
Concat: (a) =>
|
|
41
|
+
a.length === 0
|
|
42
|
+
? "null"
|
|
43
|
+
: `((...slices) => { const nonNull = slices.filter(Boolean); if (nonNull.length === 0) return slices[0] === null ? null : []; if (ArrayBuffer.isView(nonNull[0])) { const totalLen = nonNull.reduce((sum, s) => sum + s.length, 0); const out = new nonNull[0].constructor(totalLen); let off = 0; for (const s of nonNull) { out.set(s, off); off += s.length; } return out; } return [].concat(...slices.map(s => s ? (ArrayBuffer.isView(s) ? Array.from(s) : s) : [])); })(${a.map((x) => `(${x})`).join(", ")})`,
|
|
44
|
+
Delete: (a) =>
|
|
45
|
+
`((s, i, j) => { if (!s && i === 0 && j === 0) return s; s = s || []; if (i < 0 || j < i || j > s.length) throw new Error("runtime error: slice bounds out of range"); if (ArrayBuffer.isView(s)) { s.copyWithin(i, j); return s.subarray(0, s.length - (j - i)); } s.splice(i, j - i); return s; })(${a[0]}, ${a[1]}, ${a[2]})`,
|
|
46
|
+
DeleteFunc: (a) =>
|
|
47
|
+
`((s, f) => { if (!s || s.length === 0) return s; let k = 0; for (let i = 0; i < s.length; i++) { if (!f(s[i])) s[k++] = s[i]; } return ArrayBuffer.isView(s) ? s.subarray(0, k) : (s.length = k, s); })(${a[0]}, ${a[1]})`,
|
|
48
|
+
Insert: (a, isTyped) => {
|
|
49
|
+
const items = a.slice(2);
|
|
50
|
+
const nilInit = isTyped ? "new Uint8Array(0)" : "[]";
|
|
51
|
+
return `((s, i, ...v) => { if (!s && i === 0) s = ${nilInit}; if (!s || i < 0 || i > s.length) throw new Error("runtime error: slice bounds out of range"); if (ArrayBuffer.isView(s)) { const res = new s.constructor(s.length + v.length); res.set(s.subarray(0, i), 0); res.set(v, i); res.set(s.subarray(i), i + v.length); return res; } s.splice(i, 0, ...v); return s; })(${a[0]}, ${a[1]}${items.length ? `, ${items.join(", ")}` : ""})`;
|
|
52
|
+
},
|
|
53
|
+
Replace: (a, isTyped) => {
|
|
54
|
+
const items = a.slice(3);
|
|
55
|
+
const nilInit = isTyped ? "new Uint8Array(0)" : "[]";
|
|
56
|
+
return `((s, i, j, ...v) => { if (!s && i === 0 && j === 0) s = ${nilInit}; if (!s || i < 0 || j < i || j > s.length) throw new Error("runtime error: slice bounds out of range"); if (ArrayBuffer.isView(s)) { const res = new s.constructor(s.length - (j - i) + v.length); res.set(s.subarray(0, i), 0); res.set(v, i); res.set(s.subarray(j), i + v.length); return res; } s.splice(i, j - i, ...v); return s; })(${a[0]}, ${a[1]}, ${a[2]}${items.length ? `, ${items.join(", ")}` : ""})`;
|
|
57
|
+
},
|
|
58
|
+
Grow: (a, isTyped) => {
|
|
59
|
+
const nilInit = isTyped ? "new Uint8Array(0)" : "[]";
|
|
60
|
+
return `((s, n) => { if (n < 0) throw new Error("runtime error: makeslice: len out of range"); if (!s) return ${nilInit}; return s.slice(); })(${a[0]}, ${a[1]})`;
|
|
61
|
+
},
|
|
62
|
+
Clip: (a) => `((s) => s ? s.slice() : null)(${a[0]})`,
|
|
38
63
|
};
|
|
39
64
|
|
|
40
65
|
/** @type {ThisType<CodeGen>} */
|
|
41
66
|
export const slicesMethods = {
|
|
42
|
-
_genSlices(fn, a) {
|
|
67
|
+
_genSlices(fn, a, expr) {
|
|
43
68
|
if (fn === "Equal") {
|
|
44
|
-
this.
|
|
69
|
+
this.useHelper("equal");
|
|
45
70
|
const [a1, b1] = a();
|
|
46
71
|
return `__equal(${a1}, ${b1})`;
|
|
47
72
|
}
|
|
73
|
+
const isTyped = isTypedArraySlice(expr?.args?.[0]?._type);
|
|
48
74
|
const gen = SLICES_DISPATCH[fn];
|
|
49
|
-
return gen ? gen(a()) : undefined;
|
|
75
|
+
return gen ? gen(a(), isTyped) : undefined;
|
|
50
76
|
},
|
|
51
77
|
};
|
|
@@ -23,7 +23,7 @@ const SORT_DISPATCH = {
|
|
|
23
23
|
/** @type {ThisType<CodeGen>} */
|
|
24
24
|
export const sortMethods = {
|
|
25
25
|
_genSort(fn, a) {
|
|
26
|
-
if (fn === "Slice" || fn === "SliceStable") this.
|
|
26
|
+
if (fn === "Slice" || fn === "SliceStable") this.useHelper("sortSlice");
|
|
27
27
|
const gen = SORT_DISPATCH[fn];
|
|
28
28
|
return gen ? gen(a()) : undefined;
|
|
29
29
|
},
|
|
@@ -2,32 +2,73 @@
|
|
|
2
2
|
|
|
3
3
|
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
4
|
|
|
5
|
+
// Parsers return [value, msg|null] from the shared __strconv_* helpers
|
|
6
|
+
// (runtime.js); the message becomes a Go error value.
|
|
7
|
+
const parsed = (call, toValue = "r[0]") =>
|
|
8
|
+
`((r) => [${toValue}, r[1] === null ? null : __error(r[1], r[1].includes("invalid syntax") ? __strconv_err_syntax : r[1].includes("value out of range") ? __strconv_err_range : null)])(${call})`;
|
|
9
|
+
|
|
5
10
|
const STRCONV_DISPATCH = {
|
|
6
11
|
Itoa: (a) => `String(${a[0]})`,
|
|
7
|
-
Atoi: (a) =>
|
|
8
|
-
`(Number.isNaN(Number(${a[0]})) ? [0, "invalid syntax"] : [Number(${a[0]}) | 0, null])`,
|
|
12
|
+
Atoi: (a) => parsed(`__strconv_atoi(${a[0]})`, "Number(r[0])"),
|
|
9
13
|
FormatBool: (a) => `String(${a[0]})`,
|
|
10
14
|
FormatInt: (a) => `(${a[0]}).toString(${a[1]})`,
|
|
11
|
-
FormatFloat: (a) =>
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
FormatFloat: (a) =>
|
|
16
|
+
a.length < 3
|
|
17
|
+
? `String(${a[0]})`
|
|
18
|
+
: `__strconv_format_float(${a[0]}, ${a[1]}, ${a[2]})`,
|
|
19
|
+
ParseFloat: (a) => parsed(`__strconv_parse_float(${a[0]})`),
|
|
14
20
|
ParseInt: (a) =>
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
parsed(
|
|
22
|
+
`__strconv_parse_int(${a[0]}, ${a[1]}, ${a[2] ?? 64})`,
|
|
23
|
+
"Number(r[0])",
|
|
24
|
+
),
|
|
25
|
+
ParseBool: (a) => parsed(`__strconv_parse_bool(${a[0]})`),
|
|
18
26
|
Quote: (a) => `JSON.stringify(${a[0]})`,
|
|
19
27
|
Unquote: (a) =>
|
|
20
|
-
`((s) => { try { const v = JSON.parse(s); return [v, null]; } catch(e) { return ["",
|
|
28
|
+
`((s) => { if (typeof s === "string" && s.startsWith("\`") && s.endsWith("\`") && s.length >= 2) { if (s.slice(1, -1).includes("\`")) return ["", __strconv_err_syntax]; return [s.slice(1, -1), null]; } try { const v = JSON.parse(s); if (typeof v !== "string") throw 0; return [v, null]; } catch(e) { return ["", __strconv_err_syntax]; } })(${a[0]})`,
|
|
21
29
|
AppendInt: (a) =>
|
|
22
|
-
`
|
|
30
|
+
`((d, s) => { const e = new TextEncoder().encode(s); const dl = d ? d.length : 0; const r = new Uint8Array(dl + e.length); if (dl > 0) r.set(d, 0); r.set(e, dl); return r; })(${a[0]}, (${a[1]}).toString(${a[2]}))`,
|
|
31
|
+
AppendUint: (a) =>
|
|
32
|
+
`((d, s) => { const e = new TextEncoder().encode(s); const dl = d ? d.length : 0; const r = new Uint8Array(dl + e.length); if (dl > 0) r.set(d, 0); r.set(e, dl); return r; })(${a[0]}, (BigInt.asUintN(64, BigInt(${a[1]}))).toString(${a[2]}))`,
|
|
33
|
+
AppendBool: (a) =>
|
|
34
|
+
`((d, s) => { const e = new TextEncoder().encode(s); const dl = d ? d.length : 0; const r = new Uint8Array(dl + e.length); if (dl > 0) r.set(d, 0); r.set(e, dl); return r; })(${a[0]}, String(${a[1]}))`,
|
|
23
35
|
AppendFloat: (a) =>
|
|
24
|
-
`
|
|
36
|
+
`((d, s) => { const e = new TextEncoder().encode(s); const dl = d ? d.length : 0; const r = new Uint8Array(dl + e.length); if (dl > 0) r.set(d, 0); r.set(e, dl); return r; })(${a[0]}, __strconv_format_float(${a[1]}, typeof (${a[2]}) === "string" ? (${a[2]}).charCodeAt(0) : (${a[2]}), ${a[3]}))`,
|
|
37
|
+
FormatUint: (a) => `(BigInt.asUintN(64, BigInt(${a[0]}))).toString(${a[1]})`,
|
|
38
|
+
ParseUint: (a) =>
|
|
39
|
+
parsed(
|
|
40
|
+
`__strconv_parse_uint(${a[0]}, ${a[1]}, ${a[2] ?? 64})`,
|
|
41
|
+
"Number(r[0])",
|
|
42
|
+
),
|
|
25
43
|
};
|
|
26
44
|
|
|
45
|
+
const USES_ERROR = new Set([
|
|
46
|
+
"Atoi",
|
|
47
|
+
"ParseFloat",
|
|
48
|
+
"ParseInt",
|
|
49
|
+
"ParseUint",
|
|
50
|
+
"ParseBool",
|
|
51
|
+
"Unquote",
|
|
52
|
+
]);
|
|
53
|
+
const USES_STRCONV = new Set([
|
|
54
|
+
"Atoi",
|
|
55
|
+
"ParseFloat",
|
|
56
|
+
"ParseInt",
|
|
57
|
+
"ParseUint",
|
|
58
|
+
"ParseBool",
|
|
59
|
+
"FormatFloat",
|
|
60
|
+
"AppendFloat",
|
|
61
|
+
]);
|
|
62
|
+
|
|
27
63
|
/** @type {ThisType<CodeGen>} */
|
|
28
64
|
export const strconvMethods = {
|
|
29
65
|
_genStrconv(fn, a) {
|
|
30
66
|
const gen = STRCONV_DISPATCH[fn];
|
|
67
|
+
if (gen && USES_ERROR.has(fn)) {
|
|
68
|
+
this.useHelper("error");
|
|
69
|
+
this.useHelper("strconvError");
|
|
70
|
+
}
|
|
71
|
+
if (gen && USES_STRCONV.has(fn)) this.useHelper("strconv");
|
|
31
72
|
return gen ? gen(a()) : undefined;
|
|
32
73
|
},
|
|
33
74
|
};
|
|
@@ -21,30 +21,87 @@ const STRINGS_M2 = {
|
|
|
21
21
|
const STRINGS_DISPATCH = {
|
|
22
22
|
Count: ([s, s2]) =>
|
|
23
23
|
`((s, sep) => sep === "" ? s.length + 1 : s.split(sep).length - 1)(${s}, ${s2})`,
|
|
24
|
-
Replace: ([s, s2, s3]) =>
|
|
24
|
+
Replace: ([s, s2, s3, n]) =>
|
|
25
|
+
n === undefined
|
|
26
|
+
? `${s}.replace(${s2}, ${s3})`
|
|
27
|
+
: `((str, o, nw, cnt) => { if (cnt < 0) return str.replaceAll(o, nw); let r = ""; let i = 0; for (; cnt > 0; cnt--) { const j = str.indexOf(o, i); if (j < 0) break; r += str.slice(i, j) + nw; i = j + o.length; if (o.length === 0) { if (i >= str.length) break; r += str[i]; i++; } } return r + str.slice(i); })(${s}, ${s2}, ${s3}, ${n})`,
|
|
25
28
|
ReplaceAll: ([s, s2, s3]) => `${s}.replaceAll(${s2}, ${s3})`,
|
|
26
29
|
Trim: ([s, s2]) =>
|
|
27
|
-
|
|
30
|
+
`((str, cut) => { const cs = new Set(cut); let i = 0, j = str.length; while (i < j && cs.has(str[i])) i++; while (j > i && cs.has(str[j - 1])) j--; return str.slice(i, j); })(${s}, ${s2})`,
|
|
28
31
|
TrimPrefix: ([s, s2]) =>
|
|
29
32
|
`((s, pre) => s.startsWith(pre) ? s.slice(pre.length) : s)(${s}, ${s2})`,
|
|
30
33
|
TrimSuffix: ([s, s2]) =>
|
|
31
34
|
`((s, suf) => !suf.length || !s.endsWith(suf) ? s : s.slice(0, -suf.length))(${s}, ${s2})`,
|
|
32
|
-
TrimLeft: ([s, s2]) =>
|
|
33
|
-
|
|
35
|
+
TrimLeft: ([s, s2]) =>
|
|
36
|
+
`((str, cut) => { const cs = new Set(cut); let i = 0; while (i < str.length && cs.has(str[i])) i++; return str.slice(i); })(${s}, ${s2})`,
|
|
37
|
+
TrimRight: ([s, s2]) =>
|
|
38
|
+
`((str, cut) => { const cs = new Set(cut); let j = str.length; while (j > 0 && cs.has(str[j - 1])) j--; return str.slice(0, j); })(${s}, ${s2})`,
|
|
34
39
|
EqualFold: ([s, s2]) => `${s}.toLowerCase() === ${s2}.toLowerCase()`,
|
|
35
40
|
Fields: ([s]) => `(${s}).trim() === '' ? [] : (${s}).trim().split(/\\s+/)`,
|
|
41
|
+
FieldsFunc: ([s, f]) =>
|
|
42
|
+
`((s, fn) => { const res = []; const runes = Array.from(s ?? ""); let cur = ""; for (const ch of runes) { const cp = ch.codePointAt(0); if (fn(cp)) { if (cur.length > 0) { res.push(cur); cur = ""; } } else { cur += ch; } } if (cur.length > 0) res.push(cur); return res; })(${s}, ${f})`,
|
|
36
43
|
Cut: ([s, s2]) =>
|
|
37
44
|
`((s, sep) => { const i = s.indexOf(sep); return i < 0 ? [s, "", false] : [s.slice(0, i), s.slice(i + sep.length), true]; })(${s}, ${s2})`,
|
|
38
45
|
CutPrefix: ([s, s2]) =>
|
|
39
|
-
`(
|
|
46
|
+
`((str, pre) => str.startsWith(pre) ? [str.slice(pre.length), true] : [str, false])(${s}, ${s2})`,
|
|
40
47
|
CutSuffix: ([s, s2]) =>
|
|
41
|
-
`(
|
|
48
|
+
`((str, suf) => { if (suf === "") return [str, true]; return str.endsWith(suf) ? [str.slice(0, -suf.length), true] : [str, false]; })(${s}, ${s2})`,
|
|
42
49
|
SplitN: ([s, s2, s3]) =>
|
|
43
|
-
`((s, sep, n) => {
|
|
50
|
+
`((s, sep, n) => {
|
|
51
|
+
if (n === 0) return null;
|
|
52
|
+
if (n < 0) return sep === "" ? [...s] : s.split(sep);
|
|
53
|
+
if (sep === "") {
|
|
54
|
+
const chars = [...s];
|
|
55
|
+
if (chars.length <= n) return chars;
|
|
56
|
+
const r = chars.slice(0, n - 1);
|
|
57
|
+
r.push(chars.slice(n - 1).join(""));
|
|
58
|
+
return r;
|
|
59
|
+
}
|
|
60
|
+
const r = [];
|
|
61
|
+
let cur = s;
|
|
62
|
+
for (let i = 1; i < n && cur.length; i++) {
|
|
63
|
+
const j = cur.indexOf(sep);
|
|
64
|
+
if (j < 0) break;
|
|
65
|
+
r.push(cur.slice(0, j));
|
|
66
|
+
cur = cur.slice(j + sep.length);
|
|
67
|
+
}
|
|
68
|
+
r.push(cur);
|
|
69
|
+
return r;
|
|
70
|
+
})(${s}, ${s2}, ${s3})`,
|
|
44
71
|
SplitAfter: ([s, s2]) =>
|
|
45
|
-
`((s, sep) => {
|
|
72
|
+
`((s, sep) => {
|
|
73
|
+
if (sep === "") return [...s];
|
|
74
|
+
const r = [];
|
|
75
|
+
let cur = s;
|
|
76
|
+
while (true) {
|
|
77
|
+
const j = cur.indexOf(sep);
|
|
78
|
+
if (j < 0) { r.push(cur); break; }
|
|
79
|
+
r.push(cur.slice(0, j + sep.length));
|
|
80
|
+
cur = cur.slice(j + sep.length);
|
|
81
|
+
}
|
|
82
|
+
return r;
|
|
83
|
+
})(${s}, ${s2})`,
|
|
46
84
|
SplitAfterN: ([s, s2, s3]) =>
|
|
47
|
-
`((s, sep, n) => {
|
|
85
|
+
`((s, sep, n) => {
|
|
86
|
+
if (n === 0) return null;
|
|
87
|
+
if (sep === "") {
|
|
88
|
+
const chars = [...s];
|
|
89
|
+
if (n < 0 || chars.length <= n) return chars;
|
|
90
|
+
const r = chars.slice(0, n - 1);
|
|
91
|
+
r.push(chars.slice(n - 1).join(""));
|
|
92
|
+
return r;
|
|
93
|
+
}
|
|
94
|
+
const r = [];
|
|
95
|
+
let cur = s;
|
|
96
|
+
for (let i = 1; (n < 0 || i < n) && cur.length; i++) {
|
|
97
|
+
const j = cur.indexOf(sep);
|
|
98
|
+
if (j < 0) break;
|
|
99
|
+
r.push(cur.slice(0, j + sep.length));
|
|
100
|
+
cur = cur.slice(j + sep.length);
|
|
101
|
+
}
|
|
102
|
+
r.push(cur);
|
|
103
|
+
return r;
|
|
104
|
+
})(${s}, ${s2}, ${s3})`,
|
|
48
105
|
IndexAny: ([s, s2]) =>
|
|
49
106
|
`((s, chars) => { let m = -1; for (const c of chars) { const i = s.indexOf(c); if (i >= 0 && (m < 0 || i < m)) m = i; } return m; })(${s}, ${s2})`,
|
|
50
107
|
LastIndexAny: ([s, s2]) =>
|
|
@@ -55,27 +112,106 @@ const STRINGS_DISPATCH = {
|
|
|
55
112
|
IndexByte: ([s, s2]) => `(${s}).indexOf(String.fromCharCode(${s2}))`,
|
|
56
113
|
LastIndexByte: ([s, s2]) => `(${s}).lastIndexOf(String.fromCharCode(${s2}))`,
|
|
57
114
|
Map: ([s, s2]) =>
|
|
58
|
-
`
|
|
115
|
+
`((f, s) => { let out = ""; for (const c of s) { const r = f(c.codePointAt(0)); if (r >= 0) out += String.fromCodePoint(r); } return out; })(${s}, ${s2})`,
|
|
59
116
|
Title: ([s]) => `(${s}).replace(/\\b\\w/g, c => c.toUpperCase())`,
|
|
60
117
|
TrimFunc: ([s, s2]) =>
|
|
61
|
-
`((s, f) => {
|
|
118
|
+
`((s, f) => {
|
|
119
|
+
let l = 0, r = s.length;
|
|
120
|
+
while (l < r) {
|
|
121
|
+
const cp = s.codePointAt(l);
|
|
122
|
+
if (!f(cp)) break;
|
|
123
|
+
l += cp > 0xFFFF ? 2 : 1;
|
|
124
|
+
}
|
|
125
|
+
while (r > l) {
|
|
126
|
+
let cp, step;
|
|
127
|
+
if (r > 1 && s.charCodeAt(r - 1) >= 0xDC00 && s.charCodeAt(r - 1) <= 0xDFFF &&
|
|
128
|
+
s.charCodeAt(r - 2) >= 0xD800 && s.charCodeAt(r - 2) <= 0xDBFF) {
|
|
129
|
+
cp = s.codePointAt(r - 2);
|
|
130
|
+
step = 2;
|
|
131
|
+
} else {
|
|
132
|
+
cp = s.codePointAt(r - 1);
|
|
133
|
+
step = 1;
|
|
134
|
+
}
|
|
135
|
+
if (!f(cp)) break;
|
|
136
|
+
r -= step;
|
|
137
|
+
}
|
|
138
|
+
return s.slice(l, r);
|
|
139
|
+
})(${s}, ${s2})`,
|
|
62
140
|
TrimLeftFunc: ([s, s2]) =>
|
|
63
|
-
`((s, f) => {
|
|
141
|
+
`((s, f) => {
|
|
142
|
+
let l = 0;
|
|
143
|
+
while (l < s.length) {
|
|
144
|
+
const cp = s.codePointAt(l);
|
|
145
|
+
if (!f(cp)) break;
|
|
146
|
+
l += cp > 0xFFFF ? 2 : 1;
|
|
147
|
+
}
|
|
148
|
+
return s.slice(l);
|
|
149
|
+
})(${s}, ${s2})`,
|
|
64
150
|
TrimRightFunc: ([s, s2]) =>
|
|
65
|
-
`((s, f) => {
|
|
151
|
+
`((s, f) => {
|
|
152
|
+
let r = s.length;
|
|
153
|
+
while (r > 0) {
|
|
154
|
+
let cp, step;
|
|
155
|
+
if (r > 1 && s.charCodeAt(r - 1) >= 0xDC00 && s.charCodeAt(r - 1) <= 0xDFFF &&
|
|
156
|
+
s.charCodeAt(r - 2) >= 0xD800 && s.charCodeAt(r - 2) <= 0xDBFF) {
|
|
157
|
+
cp = s.codePointAt(r - 2);
|
|
158
|
+
step = 2;
|
|
159
|
+
} else {
|
|
160
|
+
cp = s.codePointAt(r - 1);
|
|
161
|
+
step = 1;
|
|
162
|
+
}
|
|
163
|
+
if (!f(cp)) break;
|
|
164
|
+
r -= step;
|
|
165
|
+
}
|
|
166
|
+
return s.slice(0, r);
|
|
167
|
+
})(${s}, ${s2})`,
|
|
66
168
|
IndexFunc: ([s, s2]) =>
|
|
67
169
|
`((s, f) => { for (let i = 0; i < s.length; i++) { const cp = s.codePointAt(i); if (f(cp)) return i; if (cp > 0xFFFF) i++; } return -1; })(${s}, ${s2})`,
|
|
68
170
|
LastIndexFunc: ([s, s2]) =>
|
|
69
|
-
`((s, f) => { for (let i = s.length - 1; i >= 0; i--) {
|
|
171
|
+
`((s, f) => { for (let i = s.length - 1; i >= 0; i--) { let cp = s.codePointAt(i); if (i > 0 && s.charCodeAt(i) >= 0xDC00 && s.charCodeAt(i) <= 0xDFFF && s.charCodeAt(i - 1) >= 0xD800 && s.charCodeAt(i - 1) <= 0xDBFF) { cp = s.codePointAt(i - 1); i--; } if (f(cp)) return i; } return -1; })(${s}, ${s2})`,
|
|
70
172
|
NewReader: ([s]) =>
|
|
71
|
-
`{
|
|
173
|
+
`((s) => ({ _bytes: new TextEncoder().encode(s), _pos: 0, Read(p) { if (this._pos >= this._bytes.length) return [0, __io_eof]; const n = Math.min(p.length, this._bytes.length - this._pos); for (let i = 0; i < n; i++) p[i] = this._bytes[this._pos + i]; this._pos += n; return [n, null]; }, Len() { return Math.max(0, this._bytes.length - this._pos); }, Reset(s2) { this._bytes = new TextEncoder().encode(s2); this._pos = 0; } }))(${s})`,
|
|
72
174
|
NewReplacer: (args) =>
|
|
73
|
-
`((...pairs) => {
|
|
175
|
+
`((...pairs) => {
|
|
176
|
+
const p = [];
|
|
177
|
+
for (let i = 0; i < pairs.length; i += 2) {
|
|
178
|
+
if (pairs[i] !== undefined && pairs[i+1] !== undefined) {
|
|
179
|
+
p.push([pairs[i], pairs[i+1]]);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
_p: p,
|
|
184
|
+
Replace(s) {
|
|
185
|
+
s = s ?? "";
|
|
186
|
+
if (!this._p || this._p.length === 0 || s === "") return s;
|
|
187
|
+
let out = "";
|
|
188
|
+
let i = 0;
|
|
189
|
+
while (i < s.length) {
|
|
190
|
+
let matched = false;
|
|
191
|
+
for (let k = 0; k < this._p.length; k++) {
|
|
192
|
+
const [oldStr, newStr] = this._p[k];
|
|
193
|
+
if (oldStr.length > 0 && s.startsWith(oldStr, i)) {
|
|
194
|
+
out += newStr;
|
|
195
|
+
i += oldStr.length;
|
|
196
|
+
matched = true;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (!matched) {
|
|
201
|
+
out += s[i];
|
|
202
|
+
i++;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
})(${args.join(", ")})`,
|
|
74
209
|
};
|
|
75
210
|
|
|
76
211
|
/** @type {ThisType<CodeGen>} */
|
|
77
212
|
export const stringsMethods = {
|
|
78
213
|
_genStrings(fn, a) {
|
|
214
|
+
if (fn === "NewReader") this.useHelper("ioEOF");
|
|
79
215
|
const args = a();
|
|
80
216
|
const [s, s2] = args;
|
|
81
217
|
if (STRINGS_M1[fn]) return `${s}.${STRINGS_M1[fn]}()`;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/backend/js/stdlib/sync.js
|
|
2
|
+
// CodeGen for Go sync package.
|
|
3
|
+
|
|
4
|
+
export const syncMethods = {
|
|
5
|
+
_genSync(fn) {
|
|
6
|
+
this.useHelper("sync");
|
|
7
|
+
if (fn === "Once") return "__sync_once()";
|
|
8
|
+
if (fn === "Mutex") return "__sync_mutex()";
|
|
9
|
+
if (fn === "RWMutex") return "__sync_rwmutex()";
|
|
10
|
+
if (fn === "WaitGroup") return "__sync_waitgroup()";
|
|
11
|
+
return undefined;
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -15,7 +15,7 @@ export const testingMethods = {
|
|
|
15
15
|
},
|
|
16
16
|
|
|
17
17
|
_genTestingMethodCall(method, expr) {
|
|
18
|
-
this.
|
|
18
|
+
this.useHelper("testing");
|
|
19
19
|
const recv = expr.func.expr;
|
|
20
20
|
const t = this.genExpr(recv);
|
|
21
21
|
const args = expr.args
|
|
@@ -27,7 +27,7 @@ export const testingMethods = {
|
|
|
27
27
|
method === "Logf" ||
|
|
28
28
|
method === "Skipf"
|
|
29
29
|
) {
|
|
30
|
-
this.
|
|
30
|
+
this.useHelper("sprintf");
|
|
31
31
|
}
|
|
32
32
|
return `${t}.${method}(${args})`;
|
|
33
33
|
},
|