gofront 1.8.0 → 1.9.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 +3 -3
- package/package.json +6 -5
- package/src/backend/js/expressions.js +1157 -408
- package/src/backend/js/index.js +327 -153
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +559 -2552
- package/src/backend/js/statements.js +670 -158
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +298 -19
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +91 -170
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +884 -256
- package/src/backend/wasm/emit-exprs.js +185 -77
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-shared.js +4 -1
- package/src/backend/wasm/emit-stmts.js +524 -240
- package/src/backend/wasm/emit.js +176 -38
- package/src/backend/wasm/globals.js +48 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +44 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +299 -770
- package/src/backend/wasm/monomorph.js +139 -8
- package/src/backend/wasm/natives.js +600 -0
- package/src/backend/wasm/prescan.js +5 -153
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -32
- package/src/backend/wasm/types.js +97 -20
- package/src/backend/wasm/variadic.js +9 -4
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +113 -130
- package/src/lexer/lexer.js +106 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +355 -132
- package/src/link/type-names.js +204 -0
- package/src/link/wasm.js +30 -6
- package/src/lower/bounds.js +153 -0
- package/src/lower/captures.js +92 -33
- package/src/lower/embedding.js +62 -5
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +929 -0
- package/src/lower/functions.js +225 -7
- package/src/lower/gen-types.js +293 -0
- package/src/lower/index.js +14 -2
- package/src/lower/json-codecs.js +334 -245
- package/src/lower/ownership.js +25 -7
- package/src/lower/reachability.js +69 -51
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +30 -5
- package/src/parser/expressions.js +111 -57
- package/src/parser/statements.js +108 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +12 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +214 -19
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +156 -8
- package/src/typechecker/expressions.js +490 -108
- package/src/typechecker/index.js +346 -42
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +41 -0
- package/src/typechecker/resolve.js +490 -65
- package/src/typechecker/statements.js +288 -27
- package/src/typechecker/stdlib/excluded.js +9 -5
- package/src/typechecker/stdlib/index.js +25 -9
- package/src/typechecker/termination.js +10 -1
- package/src/typechecker/types.js +287 -575
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +171 -38
- package/stdlib/internal/jsonrt/enc.go +186 -36
- package/stdlib/internal/jsonrt/scanner.go +17 -8
- package/stdlib/io/io.go +157 -1
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +16 -14
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +391 -452
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +235 -50
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +141 -3
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/fmt-runtime.js +0 -573
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -119
- package/src/backend/js/stdlib/builder.js +0 -98
- package/src/backend/js/stdlib/bytes.js +0 -368
- package/src/backend/js/stdlib/errors.js +0 -45
- package/src/backend/js/stdlib/fmt.js +0 -517
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -1167
- package/src/backend/js/stdlib/maps.js +0 -37
- package/src/backend/js/stdlib/math.js +0 -85
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -34
- package/src/backend/js/stdlib/slices.js +0 -107
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -79
- package/src/backend/js/stdlib/strings.js +0 -301
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -41
- package/src/backend/js/stdlib/time.js +0 -152
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/stdlib/bytes.js +0 -6716
- package/src/backend/wasm/stdlib/core.js +0 -10278
- package/src/backend/wasm/stdlib/io.js +0 -3578
- package/src/backend/wasm/stdlib/packages.js +0 -2402
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -2229
- package/src/typechecker/stdlib/core.js +0 -1079
- package/src/typechecker/stdlib/extended.js +0 -1210
- package/stdlib/bytes/iter.go +0 -192
- package/stdlib/bytes/search.go +0 -240
- package/stdlib/bytes/split.go +0 -218
- package/stdlib/errors/overlay.go +0 -12
- package/stdlib/maps/iter.go +0 -65
- package/stdlib/slices/extra.go +0 -73
- package/stdlib/slices/iter.go +0 -113
- package/stdlib/strings/extra.go +0 -35
- package/stdlib/strings/iter.go +0 -122
- package/stdlib/sync/extra.go +0 -105
- package/stdlib/unicode/utf8/bytes.go +0 -239
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
// JS natives: host code that replaces a call to a `//gofront:native` stdlib
|
|
2
|
+
// function. The Go body is the specification and is always emitted when
|
|
3
|
+
// reachable; an implementation either returns a JS expression for the call or
|
|
4
|
+
// `null` to decline, in which case the call goes to the Go body.
|
|
5
|
+
//
|
|
6
|
+
// An implementation is `(args, typeArgs) => string | null`. `args` are the
|
|
7
|
+
// call's argument expressions (opaque strings) and `typeArgs` the instantiated
|
|
8
|
+
// type arguments of a generic function. It must return a primary expression
|
|
9
|
+
// or parenthesise its own result, and must reproduce Go's behaviour (strict
|
|
10
|
+
// bounds panics, wrapping, nil slices) for every instantiation it accepts.
|
|
11
|
+
// The framework evaluates each argument once, in order, however many times the
|
|
12
|
+
// template mentions it.
|
|
13
|
+
|
|
14
|
+
import { FIXED_FLOAT_JS } from "../shared/fixed-float.js";
|
|
15
|
+
|
|
16
|
+
export const JS_NATIVES = new Map();
|
|
17
|
+
|
|
18
|
+
// Runtime helper (a HELPER_MAP key) a native's template calls.
|
|
19
|
+
export const NATIVE_HELPERS = new Map();
|
|
20
|
+
|
|
21
|
+
// Marks an argument position inside a template.
|
|
22
|
+
const MARK = (i) => `\u00ab${i}\u00bb`;
|
|
23
|
+
const MARK_RE = /\u00ab(\d+)\u00bb/g;
|
|
24
|
+
|
|
25
|
+
// Arguments that are safe to repeat or reorder: names, member chains and
|
|
26
|
+
// plain literals have no side effects and are cheap to evaluate twice.
|
|
27
|
+
const SIMPLE_ARG =
|
|
28
|
+
/^(?:[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*|-?\d+(?:\.\d+)?n?|"(?:[^"\\]|\\.)*")$/;
|
|
29
|
+
|
|
30
|
+
// The JS for a call of native `name`, or null when it is not registered or
|
|
31
|
+
// declines this instantiation. `args` are the already-generated argument
|
|
32
|
+
// expressions.
|
|
33
|
+
export function jsNativeCall(name, args, typeArgs) {
|
|
34
|
+
const impl = JS_NATIVES.get(name);
|
|
35
|
+
if (!impl) return null;
|
|
36
|
+
const template = impl(
|
|
37
|
+
args.map((_, i) => MARK(i)),
|
|
38
|
+
typeArgs,
|
|
39
|
+
);
|
|
40
|
+
if (template == null) return null;
|
|
41
|
+
const wrap = (a) => (SIMPLE_ARG.test(a) ? a : `(${a})`);
|
|
42
|
+
const seq = [...template.matchAll(MARK_RE)].map((m) => Number(m[1]));
|
|
43
|
+
const inOrder = seq.length === args.length && seq.every((v, i) => v === i);
|
|
44
|
+
if (inOrder || args.every((a) => SIMPLE_ARG.test(a)))
|
|
45
|
+
return template.replace(MARK_RE, (_, i) => wrap(args[Number(i)]));
|
|
46
|
+
// An argument with effects is repeated, dropped or reordered: evaluate
|
|
47
|
+
// every argument once, in order, into a temporary.
|
|
48
|
+
const names = args.map((_, i) => `$n${i}`);
|
|
49
|
+
const body = template.replace(MARK_RE, (_, i) => names[Number(i)]);
|
|
50
|
+
return `((${names.join(", ")}) => ${body})(${args.join(", ")})`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ── slices and sort ───────────────────────────────────────────
|
|
54
|
+
//
|
|
55
|
+
// Element types where JS `===`, `indexOf` and the default array methods
|
|
56
|
+
// agree with Go's `==` on a raw element: numbers, bigints, strings and
|
|
57
|
+
// booleans, named or not. Slices of other elements (structs, arrays,
|
|
58
|
+
// interfaces, pointers) decline and run the Go body.
|
|
59
|
+
|
|
60
|
+
const PRIMITIVE_KINDS = new Set([
|
|
61
|
+
"int",
|
|
62
|
+
"int8",
|
|
63
|
+
"int16",
|
|
64
|
+
"int32",
|
|
65
|
+
"int64",
|
|
66
|
+
"uint",
|
|
67
|
+
"uint8",
|
|
68
|
+
"uint16",
|
|
69
|
+
"uint32",
|
|
70
|
+
"uint64",
|
|
71
|
+
"uintptr",
|
|
72
|
+
"float32",
|
|
73
|
+
"float64",
|
|
74
|
+
"string",
|
|
75
|
+
"bool",
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
function baseType(t) {
|
|
79
|
+
let u = t;
|
|
80
|
+
for (let i = 0; u?.kind === "named" && i < 32; i++) u = u.underlying;
|
|
81
|
+
return u;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The element type argument of `slices.F[S, E]` when it is a primitive.
|
|
85
|
+
function primitiveElem(typeArgs) {
|
|
86
|
+
const e = baseType(typeArgs?.[1]);
|
|
87
|
+
return e?.kind === "basic" && PRIMITIVE_KINDS.has(e.name) ? e : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// `Contains` uses `indexOf`: `includes` would find a NaN, which Go never does.
|
|
91
|
+
JS_NATIVES.set("slices.Index", (a, targs) =>
|
|
92
|
+
primitiveElem(targs) ? `(${a[0]} ?? []).indexOf(${a[1]})` : null,
|
|
93
|
+
);
|
|
94
|
+
JS_NATIVES.set("slices.Contains", (a, targs) =>
|
|
95
|
+
primitiveElem(targs) ? `((${a[0]} ?? []).indexOf(${a[1]}) !== -1)` : null,
|
|
96
|
+
);
|
|
97
|
+
JS_NATIVES.set("slices.Reverse", (a, targs) =>
|
|
98
|
+
primitiveElem(targs) ? `(${a[0]} ?? []).reverse()` : null,
|
|
99
|
+
);
|
|
100
|
+
JS_NATIVES.set("slices.Clone", (a, targs) =>
|
|
101
|
+
primitiveElem(targs) ? `(${a[0]}?.slice() ?? null)` : null,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Sorting is only native for `int` and `string`, where equal elements cannot
|
|
105
|
+
// be told apart and the host order is Go's.
|
|
106
|
+
const SORT_ORDER = {
|
|
107
|
+
int: "(a, b) => a - b",
|
|
108
|
+
string: "(a, b) => (a < b ? -1 : a > b ? 1 : 0)",
|
|
109
|
+
};
|
|
110
|
+
function sortNative(elem, arg) {
|
|
111
|
+
const cmp = SORT_ORDER[elem?.name];
|
|
112
|
+
return cmp ? `(${arg} ?? []).sort(${cmp})` : null;
|
|
113
|
+
}
|
|
114
|
+
JS_NATIVES.set("slices.Sort", (a, targs) => {
|
|
115
|
+
const e = targs?.[1];
|
|
116
|
+
return e?.kind === "basic" ? sortNative(e, a[0]) : null;
|
|
117
|
+
});
|
|
118
|
+
JS_NATIVES.set("sort.Ints", (a) => sortNative({ name: "int" }, a[0]));
|
|
119
|
+
JS_NATIVES.set("sort.Strings", (a) => sortNative({ name: "string" }, a[0]));
|
|
120
|
+
|
|
121
|
+
// ── unicode/utf8 ──────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
// A string iterates by code point, as Go's `range` does.
|
|
124
|
+
JS_NATIVES.set("utf8.RuneCountInString", (a) => `[...${a[0]}].length`);
|
|
125
|
+
|
|
126
|
+
// ── bytes ─────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
// A nil slice reads as empty.
|
|
129
|
+
JS_NATIVES.set("bytes.IndexByte", (a) => `(${a[0]} ?? []).indexOf(${a[1]})`);
|
|
130
|
+
JS_NATIVES.set(
|
|
131
|
+
"bytes.Equal",
|
|
132
|
+
(a) =>
|
|
133
|
+
`((a, b) => { 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]})`,
|
|
134
|
+
);
|
|
135
|
+
JS_NATIVES.set(
|
|
136
|
+
"bytes.Compare",
|
|
137
|
+
(a) =>
|
|
138
|
+
`((a, b) => { a ??= []; b ??= []; const n = Math.min(a.length, b.length); for (let i = 0; i < n; i++) if (a[i] !== b[i]) return a[i] < b[i] ? -1 : 1; return a.length < b.length ? -1 : a.length > b.length ? 1 : 0; })(${a[0]}, ${a[1]})`,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
// ── strings ───────────────────────────────────────────────────
|
|
142
|
+
//
|
|
143
|
+
// GoFront strings are JS strings and offsets count UTF-16 units, so the host
|
|
144
|
+
// searches give the Go body's answers. Runes are code points: a rune that
|
|
145
|
+
// is not one (negative, past U+10FFFF or a surrogate) is never found, and a
|
|
146
|
+
// lone surrogate in a cutset or chars string never matches.
|
|
147
|
+
|
|
148
|
+
const GO_SPACE =
|
|
149
|
+
"[ \\t\\n\\v\\f\\r\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]";
|
|
150
|
+
const BAD_RUNE = "r < 0 || r > 0x10FFFF || (r >= 0xD800 && r <= 0xDFFF)";
|
|
151
|
+
const SURROGATE = "(v >= 0xD800 && v <= 0xDFFF)";
|
|
152
|
+
|
|
153
|
+
const strFn = (params, body) => (a) =>
|
|
154
|
+
`((${params}) => ${body})(${a.join(", ")})`;
|
|
155
|
+
|
|
156
|
+
JS_NATIVES.set("strings.Index", (a) => `${a[0]}.indexOf(${a[1]})`);
|
|
157
|
+
JS_NATIVES.set("strings.LastIndex", (a) => `${a[0]}.lastIndexOf(${a[1]})`);
|
|
158
|
+
JS_NATIVES.set(
|
|
159
|
+
"strings.IndexByte",
|
|
160
|
+
(a) => `${a[0]}.indexOf(String.fromCharCode(${a[1]}))`,
|
|
161
|
+
);
|
|
162
|
+
JS_NATIVES.set(
|
|
163
|
+
"strings.LastIndexByte",
|
|
164
|
+
(a) => `${a[0]}.lastIndexOf(String.fromCharCode(${a[1]}))`,
|
|
165
|
+
);
|
|
166
|
+
JS_NATIVES.set(
|
|
167
|
+
"strings.IndexRune",
|
|
168
|
+
strFn("s, r", `${BAD_RUNE} ? -1 : s.indexOf(String.fromCodePoint(r))`),
|
|
169
|
+
);
|
|
170
|
+
JS_NATIVES.set(
|
|
171
|
+
"strings.IndexAny",
|
|
172
|
+
strFn(
|
|
173
|
+
"s, cs",
|
|
174
|
+
`{ let m = -1; for (const c of cs) { const v = c.codePointAt(0); if (${SURROGATE}) continue; const i = s.indexOf(c); if (i >= 0 && (m < 0 || i < m)) m = i; } return m; }`,
|
|
175
|
+
),
|
|
176
|
+
);
|
|
177
|
+
JS_NATIVES.set(
|
|
178
|
+
"strings.LastIndexAny",
|
|
179
|
+
strFn(
|
|
180
|
+
"s, cs",
|
|
181
|
+
`{ let m = -1; for (const c of cs) { const v = c.codePointAt(0); if (${SURROGATE}) continue; const i = s.lastIndexOf(c); if (i > m) m = i; } return m; }`,
|
|
182
|
+
),
|
|
183
|
+
);
|
|
184
|
+
JS_NATIVES.set("strings.Contains", (a) => `${a[0]}.includes(${a[1]})`);
|
|
185
|
+
JS_NATIVES.set(
|
|
186
|
+
"strings.ContainsAny",
|
|
187
|
+
strFn(
|
|
188
|
+
"s, cs",
|
|
189
|
+
`{ for (const c of cs) { const v = c.codePointAt(0); if (!${SURROGATE} && s.includes(c)) return true; } return false; }`,
|
|
190
|
+
),
|
|
191
|
+
);
|
|
192
|
+
JS_NATIVES.set(
|
|
193
|
+
"strings.ContainsRune",
|
|
194
|
+
strFn("s, r", `!(${BAD_RUNE}) && s.includes(String.fromCodePoint(r))`),
|
|
195
|
+
);
|
|
196
|
+
JS_NATIVES.set("strings.HasPrefix", (a) => `${a[0]}.startsWith(${a[1]})`);
|
|
197
|
+
JS_NATIVES.set("strings.HasSuffix", (a) => `${a[0]}.endsWith(${a[1]})`);
|
|
198
|
+
// An empty substring counts the code points plus one, as in Go.
|
|
199
|
+
JS_NATIVES.set(
|
|
200
|
+
"strings.Count",
|
|
201
|
+
strFn(
|
|
202
|
+
"s, t",
|
|
203
|
+
`{ if (t === "") return [...s].length + 1; let n = 0; for (let i = s.indexOf(t); i !== -1; i = s.indexOf(t, i + t.length)) n++; return n; }`,
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
JS_NATIVES.set(
|
|
208
|
+
"strings.Repeat",
|
|
209
|
+
strFn(
|
|
210
|
+
"s, n",
|
|
211
|
+
`{ if (n < 0) throw new Error("strings: negative Repeat count"); return s.repeat(n); }`,
|
|
212
|
+
),
|
|
213
|
+
);
|
|
214
|
+
JS_NATIVES.set("strings.Join", (a) => `(${a[0]} ?? []).join(${a[1]})`);
|
|
215
|
+
// The replacement is a function so `$&` and friends stay literal. An empty
|
|
216
|
+
// old string matches before every code point and at the end.
|
|
217
|
+
const replaceFn = (n) =>
|
|
218
|
+
`(s, o, w${n === null ? ", n" : ""}) => { ${n === null ? "" : `const n = ${n}; `}if (o === w || n === 0) return s; if (o !== "") { if (n < 0) return s.replaceAll(o, () => w); let r = "", i = 0; for (let k = n; k > 0; k--) { const j = s.indexOf(o, i); if (j < 0) break; r += s.slice(i, j) + w; i = j + o.length; } return r + s.slice(i); } const cs = [...s]; const m = n < 0 || n > cs.length + 1 ? cs.length + 1 : n; let r = ""; for (let k = 0; k < m; k++) r += (k > 0 ? cs[k - 1] : "") + w; return r + cs.slice(m - 1).join(""); }`;
|
|
219
|
+
JS_NATIVES.set(
|
|
220
|
+
"strings.Replace",
|
|
221
|
+
(a) => `(${replaceFn(null)})(${a.join(", ")})`,
|
|
222
|
+
);
|
|
223
|
+
JS_NATIVES.set(
|
|
224
|
+
"strings.ReplaceAll",
|
|
225
|
+
(a) => `(${replaceFn(-1)})(${a.join(", ")})`,
|
|
226
|
+
);
|
|
227
|
+
JS_NATIVES.set(
|
|
228
|
+
"strings.Split",
|
|
229
|
+
strFn("s, sep", `sep === "" ? [...s] : s.split(sep)`),
|
|
230
|
+
);
|
|
231
|
+
JS_NATIVES.set(
|
|
232
|
+
"strings.Fields",
|
|
233
|
+
(a) => `${a[0]}.split(/${GO_SPACE}+/).filter((p) => p !== "")`,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// Case mapping goes letter by letter through the first code point of the
|
|
237
|
+
// host's mapping, as the Go body does through `unicode.ToUpper`; ASCII text
|
|
238
|
+
// takes the whole-string fast path. A letter whose upper case spans several
|
|
239
|
+
// code points (ß) stays as it is, as in Go's single-rune tables.
|
|
240
|
+
const caseNative = (method) =>
|
|
241
|
+
strFn(
|
|
242
|
+
"s",
|
|
243
|
+
`/^[\\x00-\\x7f]*$/.test(s) ? s.${method}() : Array.from(s, (c) => { const m = c.${method}(); const r = String.fromCodePoint(m.codePointAt(0)); return m.length > r.length && "${method}" === "toUpperCase" ? c : r; }).join("")`,
|
|
244
|
+
);
|
|
245
|
+
JS_NATIVES.set("strings.ToUpper", caseNative("toUpperCase"));
|
|
246
|
+
JS_NATIVES.set("strings.ToLower", caseNative("toLowerCase"));
|
|
247
|
+
JS_NATIVES.set(
|
|
248
|
+
"strings.TrimSpace",
|
|
249
|
+
strFn(
|
|
250
|
+
"s",
|
|
251
|
+
`{ const sp = /${GO_SPACE}/; let i = 0, j = s.length; while (i < j && sp.test(s[i])) i++; while (j > i && sp.test(s[j - 1])) j--; return s.slice(i, j); }`,
|
|
252
|
+
),
|
|
253
|
+
);
|
|
254
|
+
const TRIM_FN =
|
|
255
|
+
'(s, cs, l, r) => { if (s === "" || cs === "") return s; const set = new Set(cs); let i = 0, j = s.length; if (l) while (i < j) { const c = s.codePointAt(i); if (!set.has(String.fromCodePoint(c))) break; i += c > 0xFFFF ? 2 : 1; } if (r) while (j > i) { let k = j - 1; const lo = s.charCodeAt(k); if (lo >= 0xDC00 && lo <= 0xDFFF && k > i) { const hi = s.charCodeAt(k - 1); if (hi >= 0xD800 && hi <= 0xDBFF) k--; } if (!set.has(s.slice(k, j))) break; j = k; } return s.slice(i, j); }';
|
|
256
|
+
const trimNative = (l, r) => (a) =>
|
|
257
|
+
`(${TRIM_FN})(${a[0]}, ${a[1]}, ${l}, ${r})`;
|
|
258
|
+
JS_NATIVES.set("strings.Trim", trimNative(true, true));
|
|
259
|
+
JS_NATIVES.set("strings.TrimLeft", trimNative(true, false));
|
|
260
|
+
JS_NATIVES.set("strings.TrimRight", trimNative(false, true));
|
|
261
|
+
JS_NATIVES.set(
|
|
262
|
+
"strings.TrimPrefix",
|
|
263
|
+
strFn("s, p", "s.startsWith(p) ? s.slice(p.length) : s"),
|
|
264
|
+
);
|
|
265
|
+
JS_NATIVES.set(
|
|
266
|
+
"strings.TrimSuffix",
|
|
267
|
+
strFn("s, p", "s.endsWith(p) ? s.slice(0, s.length - p.length) : s"),
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
// ── strconv ───────────────────────────────────────────────────
|
|
271
|
+
//
|
|
272
|
+
// The host number printer gives the shortest digits that read back as the
|
|
273
|
+
// float64 and `Number` converts a decimal with correct rounding; both agree
|
|
274
|
+
// with the Go bodies.
|
|
275
|
+
|
|
276
|
+
JS_NATIVES.set("strconv.shortest", (a) => `${a[0]}.toExponential()`);
|
|
277
|
+
JS_NATIVES.set("strconv.fixed", (a) => `(${FIXED_FLOAT_JS})(${a[0]}, ${a[1]})`);
|
|
278
|
+
JS_NATIVES.set(
|
|
279
|
+
"strconv.parseDecimal",
|
|
280
|
+
(a) => `Number(${a[0]}.replaceAll("_", ""))`,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// ── math/bits ─────────────────────────────────────────────────
|
|
284
|
+
//
|
|
285
|
+
// Only the 8, 16 and 32 bit forms: their operands are Numbers that
|
|
286
|
+
// `Math.clz32` reads exactly. The 64 bit and `uint` forms hold BigInts or
|
|
287
|
+
// imprecise Numbers, so they decline and run the Go body.
|
|
288
|
+
|
|
289
|
+
for (const n of [8, 16, 32]) {
|
|
290
|
+
JS_NATIVES.set(`bits.Len${n}`, (a) => `(32 - Math.clz32(${a[0]}))`);
|
|
291
|
+
JS_NATIVES.set(
|
|
292
|
+
`bits.LeadingZeros${n}`,
|
|
293
|
+
(a) => `(Math.clz32(${a[0]}) - ${32 - n})`,
|
|
294
|
+
);
|
|
295
|
+
JS_NATIVES.set(
|
|
296
|
+
`bits.TrailingZeros${n}`,
|
|
297
|
+
(a) => `(${a[0]} === 0 ? ${n} : 31 - Math.clz32(${a[0]} & -${a[0]}))`,
|
|
298
|
+
);
|
|
299
|
+
JS_NATIVES.set(
|
|
300
|
+
`bits.OnesCount${n}`,
|
|
301
|
+
(a) =>
|
|
302
|
+
`((v) => { v -= (v >>> 1) & 0x55555555; v = (v & 0x33333333) + ((v >>> 2) & 0x33333333); return Math.imul((v + (v >>> 4)) & 0x0f0f0f0f, 0x01010101) >>> 24; })(${a[0]})`,
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// ── math ──────────────────────────────────────────────────────
|
|
307
|
+
//
|
|
308
|
+
// Host `Math.*` where it agrees with Go. An infinity beats a NaN in Go's
|
|
309
|
+
// `Max` and `Min`; `Math.round` rounds half toward +Inf where Go rounds half
|
|
310
|
+
// away from zero; the sign of a NaN is not observed.
|
|
311
|
+
|
|
312
|
+
JS_NATIVES.set("math.Abs", (a) => `Math.abs(${a[0]})`);
|
|
313
|
+
JS_NATIVES.set("math.Floor", (a) => `Math.floor(${a[0]})`);
|
|
314
|
+
JS_NATIVES.set("math.Ceil", (a) => `Math.ceil(${a[0]})`);
|
|
315
|
+
JS_NATIVES.set("math.Trunc", (a) => `Math.trunc(${a[0]})`);
|
|
316
|
+
JS_NATIVES.set("math.Sqrt", (a) => `Math.sqrt(${a[0]})`);
|
|
317
|
+
JS_NATIVES.set(
|
|
318
|
+
"math.Round",
|
|
319
|
+
(a) => `(Math.sign(${a[0]}) * Math.round(Math.abs(${a[0]})))`,
|
|
320
|
+
);
|
|
321
|
+
JS_NATIVES.set(
|
|
322
|
+
"math.Max",
|
|
323
|
+
(a) =>
|
|
324
|
+
`(${a[0]} === Infinity || ${a[1]} === Infinity ? Infinity : Math.max(${a[0]}, ${a[1]}))`,
|
|
325
|
+
);
|
|
326
|
+
JS_NATIVES.set(
|
|
327
|
+
"math.Min",
|
|
328
|
+
(a) =>
|
|
329
|
+
`(${a[0]} === -Infinity || ${a[1]} === -Infinity ? -Infinity : Math.min(${a[0]}, ${a[1]}))`,
|
|
330
|
+
);
|
|
331
|
+
JS_NATIVES.set("math.IsNaN", (a) => `Number.isNaN(${a[0]})`);
|
|
332
|
+
JS_NATIVES.set(
|
|
333
|
+
"math.IsInf",
|
|
334
|
+
(a) =>
|
|
335
|
+
`(${a[1]} > 0 ? ${a[0]} === Infinity : ${a[1]} < 0 ? ${a[0]} === -Infinity : Math.abs(${a[0]}) === Infinity)`,
|
|
336
|
+
);
|
|
337
|
+
JS_NATIVES.set("math.Inf", (a) => `(${a[0]} >= 0 ? Infinity : -Infinity)`);
|
|
338
|
+
JS_NATIVES.set("math.NaN", () => "NaN");
|
|
339
|
+
JS_NATIVES.set(
|
|
340
|
+
"math.Signbit",
|
|
341
|
+
(a) => `(${a[0]} < 0 || Object.is(${a[0]}, -0))`,
|
|
342
|
+
);
|
|
343
|
+
JS_NATIVES.set(
|
|
344
|
+
"math.Copysign",
|
|
345
|
+
(a) =>
|
|
346
|
+
`(Math.abs(${a[0]}) * (${a[1]} < 0 || Object.is(${a[1]}, -0) ? -1 : 1))`,
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
// ── internal/jsonrt ───────────────────────────────────────────
|
|
350
|
+
//
|
|
351
|
+
// The encoder writes into a byte buffer it owns (`buf`, `n`); these calls
|
|
352
|
+
// append to it without the per-write slice headers Go's append would make.
|
|
353
|
+
|
|
354
|
+
const JSONRT_CALLS = {
|
|
355
|
+
putString: "__jput",
|
|
356
|
+
putInt: "__jint",
|
|
357
|
+
putUint: "__jint",
|
|
358
|
+
putFloat: "__jfloat",
|
|
359
|
+
putQuoted: "__jquote",
|
|
360
|
+
};
|
|
361
|
+
for (const [name, helper] of Object.entries(JSONRT_CALLS)) {
|
|
362
|
+
JS_NATIVES.set(`jsonrt.${name}`, (a) => `${helper}(${a.join(", ")})`);
|
|
363
|
+
NATIVE_HELPERS.set(`jsonrt.${name}`, "jsonrt");
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// The decoder reads straight from its input bytes (`data`, `pos`); a string
|
|
367
|
+
// with escapes goes to the Go `unquote`, and input the host's JSON parser
|
|
368
|
+
// rejects goes to the Go scanner, which words the error.
|
|
369
|
+
JS_NATIVES.set(
|
|
370
|
+
"jsonrt.readStr",
|
|
371
|
+
(a) => `__jrstr(${a[0]}, unquote$$internal$jsonrt)`,
|
|
372
|
+
);
|
|
373
|
+
JS_NATIVES.set("jsonrt.readNum", (a) => `__jrnum(${a[0]})`);
|
|
374
|
+
JS_NATIVES.set(
|
|
375
|
+
"jsonrt.CheckValid",
|
|
376
|
+
(a) => `(__jvalid(${a[0]}) ? null : CheckValid$$internal$jsonrt(${a[0]}))`,
|
|
377
|
+
);
|
|
378
|
+
JS_NATIVES.set(
|
|
379
|
+
"jsonrt.LooksFloat32",
|
|
380
|
+
(a) => `(Math.fround(${a[0]}) === ${a[0]})`,
|
|
381
|
+
);
|
|
382
|
+
for (const n of ["readStr", "readNum", "CheckValid"])
|
|
383
|
+
NATIVE_HELPERS.set(`jsonrt.${n}`, "jsonrt");
|