gofront 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/package.json +8 -37
- package/src/backend/js/expressions.js +1341 -148
- package/src/backend/js/fmt-runtime.js +573 -0
- package/src/backend/js/index.js +375 -57
- package/src/backend/js/runtime.js +1460 -133
- package/src/backend/js/statements.js +900 -86
- package/src/backend/js/stdlib/bits.js +87 -14
- package/src/backend/js/stdlib/builder.js +41 -6
- package/src/backend/js/stdlib/bytes.js +253 -7
- package/src/backend/js/stdlib/errors.js +2 -1
- package/src/backend/js/stdlib/fmt.js +349 -10
- package/src/backend/js/stdlib/index.js +8 -3
- package/src/backend/js/stdlib/json.js +247 -52
- package/src/backend/js/stdlib/maps.js +0 -8
- package/src/backend/js/stdlib/math.js +17 -1
- package/src/backend/js/stdlib/simd.js +10 -0
- package/src/backend/js/stdlib/slices.js +36 -6
- package/src/backend/js/stdlib/strconv.js +10 -5
- package/src/backend/js/stdlib/strings.js +83 -4
- package/src/backend/js/stdlib/testing.js +7 -0
- package/src/backend/js/stdlib/time.js +79 -55
- package/src/backend/js/stdlib/unicode.js +1 -1
- package/src/backend/js/stdlib/utf8.js +1 -1
- package/src/backend/js/templ.js +38 -76
- package/src/backend/js/type-info.js +272 -0
- package/src/backend/shared/fmt-types.js +188 -0
- package/src/backend/wasm/boundary.js +436 -80
- package/src/backend/wasm/emit-builtins.js +303 -13
- package/src/backend/wasm/emit-exprs.js +1397 -203
- package/src/backend/wasm/emit-maps.js +61 -13
- package/src/backend/wasm/emit-stmts.js +1359 -164
- package/src/backend/wasm/emit.js +512 -77
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +452 -0
- package/src/backend/wasm/index.js +194 -2489
- package/src/backend/wasm/method-expr.js +381 -0
- package/src/backend/wasm/module-emitter.js +2240 -0
- package/src/backend/wasm/monomorph.js +62 -4
- package/src/backend/wasm/prescan.js +397 -0
- package/src/backend/wasm/stdlib/bytes.js +1804 -127
- package/src/backend/wasm/stdlib/core.js +1574 -199
- package/src/backend/wasm/stdlib/index.js +0 -5
- package/src/backend/wasm/stdlib/io.js +1567 -150
- package/src/backend/wasm/stdlib/packages.js +2 -0
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/stdlib/time.js +1041 -12
- package/src/backend/wasm/types.js +200 -15
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +236 -10
- package/src/{test-runner.js → cli/test-runner.js} +332 -39
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +107 -269
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +102 -55
- package/src/{tokens.js → lexer/tokens.js} +1 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +515 -0
- package/src/link/wasm.js +294 -0
- package/src/lower/bounds.js +394 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +176 -32
- package/src/lower/index.js +26 -18
- package/src/lower/json-codecs.js +1398 -0
- package/src/lower/ownership.js +22 -1
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +132 -0
- package/src/parser/declarations.js +58 -13
- package/src/parser/expressions.js +29 -7
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +37 -22
- package/src/{templ-parser.js → parser/templ.js} +138 -15
- package/src/parser/types.js +69 -9
- package/src/resolver.js +22 -1
- package/src/typechecker/assignability.js +176 -17
- package/src/typechecker/const-fold.js +356 -0
- package/src/typechecker/constants.js +256 -0
- package/src/typechecker/expressions.js +931 -122
- package/src/typechecker/index.js +459 -67
- package/src/typechecker/init-order.js +330 -0
- package/src/typechecker/resolve.js +451 -56
- package/src/typechecker/statements.js +90 -9
- package/src/typechecker/stdlib/core.js +333 -55
- package/src/typechecker/stdlib/excluded.js +898 -0
- package/src/typechecker/stdlib/extended.js +322 -26
- package/src/typechecker/{stdlib.js → stdlib/index.js} +5 -5
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +1 -1
- package/src/typechecker/types.js +149 -25
- package/stdlib/bytes/iter.go +192 -0
- package/stdlib/bytes/search.go +240 -0
- package/stdlib/bytes/split.go +218 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/overlay.go +12 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/internal/jsonrt/dec.go +738 -0
- package/stdlib/internal/jsonrt/enc.go +366 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +649 -0
- package/stdlib/io/io.go +346 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/iter.go +65 -0
- package/stdlib/math/extra.go +270 -0
- package/stdlib/slices/extra.go +73 -0
- package/stdlib/slices/iter.go +113 -0
- package/stdlib/sort/sort.go +561 -0
- package/stdlib/strconv/quote.go +341 -0
- package/stdlib/strings/extra.go +35 -0
- package/stdlib/strings/iter.go +122 -0
- package/stdlib/sync/extra.go +105 -0
- package/stdlib/unicode/unicode.go +112 -0
- package/stdlib/unicode/utf8/bytes.go +239 -0
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
- /package/src/{templ-lexer.js → lexer/templ-lexer.js} +0 -0
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
// CodeGen for Go `fmt` package — installed onto CodeGen.prototype via stdlibGenMethods.
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
fmtDescHasFloat,
|
|
5
|
+
fmtDescIsSeq,
|
|
6
|
+
fmtTypeDesc,
|
|
7
|
+
fmtWantsTypes,
|
|
8
|
+
structOwnFields,
|
|
9
|
+
} from "../../shared/fmt-types.js";
|
|
10
|
+
|
|
3
11
|
/** @typedef {import('../index.js').CodeGen} CodeGen */
|
|
4
12
|
|
|
5
13
|
const _SSCAN_LOOP =
|
|
@@ -14,23 +22,341 @@ const _SSCAN_LOOP =
|
|
|
14
22
|
`else if (typeof p.value === "boolean") { const tl = String(t).toLowerCase(); if (tl === "true" || tl === "1" || tl === "t") p.value = true; else if (tl === "false" || tl === "0" || tl === "f") p.value = false; else { err = __error("expected boolean"); break; } } ` +
|
|
15
23
|
`else p.value = t; n++; }`;
|
|
16
24
|
|
|
25
|
+
const FMT_METHODS = ["Error", "String"];
|
|
26
|
+
const isExportedName = (n) => /^\p{Lu}/u.test(n);
|
|
27
|
+
// Descriptor of a struct value whose class has a pointer-receiver Error/String:
|
|
28
|
+
// a value element or field must not be taken for a pointer because an earlier
|
|
29
|
+
// &x tagged the same object (see isPointer in __sprintf).
|
|
30
|
+
const FMT_STRUCT_VALUE = "{ sv: 1 }";
|
|
31
|
+
|
|
17
32
|
/** @type {ThisType<CodeGen>} */
|
|
18
33
|
export const fmtMethods = {
|
|
19
|
-
|
|
34
|
+
// ── Stringer/error dispatch metadata (read by __sprintf) ─────────────
|
|
35
|
+
//
|
|
36
|
+
// Named non-struct values are raw numbers/strings/arrays inside slices,
|
|
37
|
+
// maps and struct fields, and a struct and a pointer to it share one class.
|
|
38
|
+
// The type is therefore carried two ways, both only where a method can be
|
|
39
|
+
// called: a descriptor of the container's element types, attached when a
|
|
40
|
+
// slice/array/map is converted to an interface (static type passing, see
|
|
41
|
+
// __ifd), and per-class metadata for struct fields (__ff) and for
|
|
42
|
+
// pointer-receiver Error/String methods (__pr).
|
|
43
|
+
|
|
44
|
+
// JS source of the fmt descriptor for static type `t`, or null when no
|
|
45
|
+
// value of the type needs one:
|
|
46
|
+
// { b: Class, n } a raw value of a named type with a value-receiver
|
|
47
|
+
// Error/String; Class boxes it, n: numeric (map keys)
|
|
48
|
+
// { sv: 1 } a struct value with a pointer-receiver Error/String
|
|
49
|
+
// { e } elements of a slice or array
|
|
50
|
+
// { k, v } keys and values of a map
|
|
51
|
+
// { np: obj } a *T whose Error/String has a pointer receiver: obj
|
|
52
|
+
// stands in for a nil element (see _fmtNilPtrOperand)
|
|
53
|
+
// { m: 1, ... } a map that is not boxed itself (a struct field or an
|
|
54
|
+
// element): the object map carries no marker of its own
|
|
55
|
+
// `bare`: the value is the operand being boxed (a map is then marked by
|
|
56
|
+
// the boxing itself).
|
|
57
|
+
_fmtDesc(t, seen = new Set(), bare = true) {
|
|
58
|
+
t = this.checker?.resolveType?.(t) ?? t;
|
|
59
|
+
if (!t || seen.has(t)) return null;
|
|
60
|
+
const next = new Set(seen).add(t);
|
|
61
|
+
if (t.kind === "named") return this._fmtNamedDesc(t, next, bare);
|
|
62
|
+
if (t.kind === "slice" || t.kind === "array") {
|
|
63
|
+
const e = this._fmtDesc(t.elem, next, false);
|
|
64
|
+
return e ? `{ e: ${e} }` : null;
|
|
65
|
+
}
|
|
66
|
+
if (t.kind === "struct") return this._fmtAnonStructDesc(t, next);
|
|
67
|
+
if (t.kind === "pointer") {
|
|
68
|
+
if (bare) return null;
|
|
69
|
+
const np = this._fmtNilPtrOperand(t);
|
|
70
|
+
return np ? `{ np: ${np} }` : null;
|
|
71
|
+
}
|
|
72
|
+
if (t.kind !== "map") return null;
|
|
73
|
+
const parts = [
|
|
74
|
+
["k", this._fmtDesc(t.key, next, false)],
|
|
75
|
+
["v", this._fmtDesc(t.value, next, false)],
|
|
76
|
+
].filter(([, d]) => d);
|
|
77
|
+
if (!bare) parts.unshift(["m", "1"]);
|
|
78
|
+
return parts.length
|
|
79
|
+
? `{ ${parts.map(([n, d]) => `${n}: ${d}`).join(", ")} }`
|
|
80
|
+
: null;
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// An anonymous struct has no class to carry field descriptors: { ff } holds
|
|
84
|
+
// them, keyed by field name.
|
|
85
|
+
_fmtAnonStructDesc(st, seen) {
|
|
86
|
+
const fields = [];
|
|
87
|
+
for (const [fname, ftype] of structOwnFields(st)) {
|
|
88
|
+
const desc = this._fmtDesc(ftype, seen, false);
|
|
89
|
+
if (desc) fields.push(`${JSON.stringify(fname)}: ${desc}`);
|
|
90
|
+
}
|
|
91
|
+
return fields.length > 0 ? `{ ff: { ${fields.join(", ")} } }` : null;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
_fmtNamedDesc(t, seen, bare = true) {
|
|
95
|
+
const u = this.checker?.resolveType?.(t.underlying) ?? t.underlying;
|
|
96
|
+
const info = this._namedBoxInfo(t);
|
|
97
|
+
if (
|
|
98
|
+
info &&
|
|
99
|
+
FMT_METHODS.some((m) => info.methods.get(m)?._ptrRecv === false)
|
|
100
|
+
) {
|
|
101
|
+
const numeric = u?.kind === "basic" && /^(u?int|float)/.test(u.name);
|
|
102
|
+
return `{ b: ${info.cls}, n: ${numeric ? 1 : 0} }`;
|
|
103
|
+
}
|
|
104
|
+
if (u?.kind === "struct" && this._fmtHasPtrMethod(u))
|
|
105
|
+
return FMT_STRUCT_VALUE;
|
|
106
|
+
if (!u || u.kind === "struct" || u.kind === "interface") return null;
|
|
107
|
+
return this._fmtDesc(u, seen, bare);
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
// Whether struct `st` has a pointer-receiver Error/String, declared or
|
|
111
|
+
// promoted from an embedded struct.
|
|
112
|
+
_fmtHasPtrMethod(st, seen = new Set()) {
|
|
113
|
+
if (seen.has(st)) return false;
|
|
114
|
+
seen.add(st);
|
|
115
|
+
if (FMT_METHODS.some((m) => st.methods?.get(m)?._ptrRecv)) return true;
|
|
116
|
+
return (st._embeds ?? []).some((raw) => {
|
|
117
|
+
const inner = (raw.kind === "pointer" ? raw.base : raw).underlying;
|
|
118
|
+
return inner?.kind === "struct" && this._fmtHasPtrMethod(inner, seen);
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// Error/String promoted from embedded interface fields, as { method: field }.
|
|
123
|
+
_fmtPromoted(st, methodDecls) {
|
|
124
|
+
const out = {};
|
|
125
|
+
for (const f of st._structFields ?? []) {
|
|
126
|
+
if (!f.embedded) continue;
|
|
127
|
+
const t = this.checker?.resolveType?.(f.type) ?? f.type;
|
|
128
|
+
const it = t?.kind === "named" ? t.underlying : t;
|
|
129
|
+
const key = (f.astType?.name ?? t?.name ?? "").split(".").pop();
|
|
130
|
+
if (it?.kind !== "interface" || !key) continue;
|
|
131
|
+
for (const mn of FMT_METHODS) {
|
|
132
|
+
const m = it.methods?.get(mn);
|
|
133
|
+
if (!m || m.params?.length > 0 || out[mn]) continue;
|
|
134
|
+
if (methodDecls.some((d) => d.name === mn)) continue;
|
|
135
|
+
out[mn] = key;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
// Per-class fmt metadata of a struct: which of Error/String have a pointer
|
|
142
|
+
// receiver, and the descriptors of the exported fields that hold raw
|
|
143
|
+
// Stringer values.
|
|
144
|
+
_genFmtClassMeta(name, methodDecls) {
|
|
145
|
+
const st = this.checker?.types.get(name)?.underlying;
|
|
146
|
+
if (st?.kind !== "struct") return;
|
|
147
|
+
const ptrRecv = FMT_METHODS.filter((mn) =>
|
|
148
|
+
this._fmtPtrRecv(st, methodDecls, mn),
|
|
149
|
+
);
|
|
150
|
+
if (ptrRecv.length > 0)
|
|
151
|
+
this.line(
|
|
152
|
+
`Object.defineProperty(${name}.prototype, "__pr", { value: ${JSON.stringify(ptrRecv)} });`,
|
|
153
|
+
);
|
|
154
|
+
const promoted = this._fmtPromoted(st, methodDecls);
|
|
155
|
+
if (Object.keys(promoted).length > 0)
|
|
156
|
+
this.line(
|
|
157
|
+
`Object.defineProperty(${name}.prototype, "__pe", { value: ${JSON.stringify(promoted)} });`,
|
|
158
|
+
);
|
|
159
|
+
const fields = [];
|
|
160
|
+
const own = structOwnFields(st);
|
|
161
|
+
// The class also carries a copy of every promoted field (see
|
|
162
|
+
// _promotedPath); fmt lists only the fields the struct declares.
|
|
163
|
+
if (own.length < st.fields.size)
|
|
164
|
+
this.line(
|
|
165
|
+
`Object.defineProperty(${name}.prototype, "__fk", { value: ${JSON.stringify(own.map(([n]) => n))} });`,
|
|
166
|
+
);
|
|
167
|
+
for (const [fname, ftype] of own) {
|
|
168
|
+
const desc = this._fmtDesc(ftype, new Set(), false);
|
|
169
|
+
// Unexported fields get no method dispatch, only the value marker
|
|
170
|
+
// (and the map marker).
|
|
171
|
+
if (
|
|
172
|
+
desc &&
|
|
173
|
+
(isExportedName(fname) ||
|
|
174
|
+
desc === FMT_STRUCT_VALUE ||
|
|
175
|
+
/\bm: 1\b/.test(desc))
|
|
176
|
+
)
|
|
177
|
+
fields.push(`${fname}: ${desc}`);
|
|
178
|
+
}
|
|
179
|
+
// A thunk: the descriptors name classes declared further down.
|
|
180
|
+
if (fields.length > 0)
|
|
181
|
+
this.line(
|
|
182
|
+
`Object.defineProperty(${name}.prototype, "__ff", { value: () => ({ ${fields.join(", ")} }) });`,
|
|
183
|
+
);
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
// Whether method `mn` of struct `st` has a pointer receiver: declared on it,
|
|
187
|
+
// or promoted from an embedded struct (an embedded pointer promotes every
|
|
188
|
+
// method to the value's method set).
|
|
189
|
+
_fmtPtrRecv(st, methodDecls, mn) {
|
|
190
|
+
const decl = methodDecls.find((d) => d.name === mn);
|
|
191
|
+
if (decl) return Boolean(decl.recvPointer);
|
|
192
|
+
for (const raw of st._embeds ?? []) {
|
|
193
|
+
const isPtr = raw.kind === "pointer";
|
|
194
|
+
const m = (isPtr ? raw.base : raw).underlying?.methods?.get(mn);
|
|
195
|
+
if (m) return !isPtr && Boolean(m._ptrRecv);
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
// A nil `*T` whose Error/String has a pointer receiver still calls the method
|
|
201
|
+
// (Go does; a panic in it, usually a nil dereference, prints <nil>). The JS
|
|
202
|
+
// source of a stand-in for the nil pointer, or null when `t` is not such a
|
|
203
|
+
// pointer. Only operands whose static type is the pointer are covered.
|
|
204
|
+
_fmtNilPtrOperand(t) {
|
|
205
|
+
t = this.checker?.resolveType?.(t) ?? t;
|
|
206
|
+
if (t?.kind !== "pointer") return null;
|
|
207
|
+
const base = this.checker?.resolveType?.(t.base) ?? t.base;
|
|
208
|
+
const st =
|
|
209
|
+
this.checker?.resolveType?.(base?.underlying) ?? base?.underlying;
|
|
210
|
+
if (base?.kind !== "named" || st?.kind !== "struct" || !base.name)
|
|
211
|
+
return null;
|
|
212
|
+
for (const mn of FMT_METHODS) {
|
|
213
|
+
const m = st.methods?.get(mn) ?? base.methods?.get(mn);
|
|
214
|
+
// Instantiated generic methods are copies without _ptrRecv.
|
|
215
|
+
const generic = base.typeArgs
|
|
216
|
+
? this.checker?.types.get(base.name)?._generic
|
|
217
|
+
: null;
|
|
218
|
+
const ptrRecv = m?._ptrRecv ?? generic?.methods?.get(mn)?._ptrRecv;
|
|
219
|
+
if (!m || !ptrRecv || m.params?.length > 0) continue;
|
|
220
|
+
const cls = this._dequalify(base.name).replace(/\[.*$/, "");
|
|
221
|
+
if (cls.includes(".")) return null;
|
|
222
|
+
return `{ ${mn}() { try { return ${cls}.prototype.${mn}.call(null); } catch (e) { return "<nil>"; } }, __p: true, __nilp: true }`;
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
// `typed`: the format string needs operand types (`%T`, `%#v`, EXTRA, see
|
|
228
|
+
// fmtWantsTypes), so the operand travels with its static type as
|
|
229
|
+
// `__ft(value, descriptor)`; operands of interface type carry their dynamic
|
|
230
|
+
// type and stay bare.
|
|
231
|
+
_fmtArgJs(e, typed = false) {
|
|
232
|
+
// `xs...` passes each element as its own operand.
|
|
233
|
+
if (e?._spread) return `...(${this.genExpr(e)} ?? [])`;
|
|
234
|
+
// A multi-value call (`f()` returning a tuple) passes every result.
|
|
235
|
+
if (e?._type?.kind === "tuple") return this._fmtTupleJs(e);
|
|
236
|
+
const nilOperand = this._fmtNilPtrOperand(e?._type);
|
|
237
|
+
if (nilOperand) return `(${this.genExpr(e)} ?? ${nilOperand})`;
|
|
20
238
|
const js = this.genExpr(e);
|
|
239
|
+
// An operand of a type parameter type: the type argument knows how fmt
|
|
240
|
+
// should see it (a Stringer named type is boxed, a slice gets its elements).
|
|
241
|
+
const tpf =
|
|
242
|
+
e?._type?.kind === "typeParam"
|
|
243
|
+
? this._typeParamZeroFactory(e._type.name)
|
|
244
|
+
: null;
|
|
245
|
+
if (tpf) return `(${tpf}.fb ?? ${tpf}.b ?? ((__v) => __v))(${js})`;
|
|
21
246
|
const base =
|
|
22
247
|
e?._type?.kind === "named" ? (e._type.underlying ?? e._type) : e?._type;
|
|
23
|
-
|
|
248
|
+
if (e?._type?.name === "time.Duration") {
|
|
249
|
+
this.useHelper("duration");
|
|
250
|
+
this.useHelper("sprintf");
|
|
251
|
+
return `__ft(${js}, { k: "named", n: "time.Duration" })`;
|
|
252
|
+
}
|
|
253
|
+
const desc = fmtTypeDesc(
|
|
254
|
+
e?._type,
|
|
255
|
+
(t) => this.checker?.resolveType?.(t) ?? t,
|
|
256
|
+
);
|
|
257
|
+
if ((typed || fmtDescHasFloat(desc)) && desc && desc.k !== "i") {
|
|
258
|
+
this.useHelper("sprintf");
|
|
259
|
+
return `__ft(${js}, ${JSON.stringify(desc)})`;
|
|
260
|
+
}
|
|
261
|
+
return this._fmtNilValue(base, js, false);
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
// A nil slice or map operand prints as `[]` / `map[]`: coalesce the null
|
|
265
|
+
// to an empty value. Shared by the single-operand and the tuple path.
|
|
266
|
+
// `unboxed`: the map still needs its marker (the caller boxes the result).
|
|
267
|
+
_fmtNilValue(base, js, unboxed = true) {
|
|
268
|
+
if (base?.kind === "slice") return `(${js} ?? [])`;
|
|
269
|
+
if (unboxed && base?.kind === "map") return `(${js} ?? {})`;
|
|
270
|
+
return js;
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
// `js` (a value of static type `t`) boxed as converting it to an `any` does,
|
|
274
|
+
// with the same boxing the single-operand path gets from the checker
|
|
275
|
+
// (_markIfaceBox): named types with methods, pointers, slices, maps, structs
|
|
276
|
+
// and the fmt descriptor of Stringer elements.
|
|
277
|
+
_fmtBoxValue(js, t) {
|
|
278
|
+
const resolve = (x) => this.checker?.resolveType?.(x) ?? x;
|
|
279
|
+
let base = resolve(t);
|
|
280
|
+
if (base?.kind === "named") base = resolve(base.underlying);
|
|
281
|
+
const meta = this.checker?._ifaceBoxMeta?.(t);
|
|
282
|
+
if (meta?.type)
|
|
283
|
+
return this._genNamedIfaceBox(js, meta.type, meta.box === "named_ptr");
|
|
284
|
+
let out = this._fmtNilValue(base, js);
|
|
285
|
+
if (meta) out = this._ifaceBoxWrap(out, meta.box, meta.ptrFields);
|
|
286
|
+
const desc = this._fmtDesc(t);
|
|
287
|
+
return desc ? this._fmtDescWrap(out, desc) : out;
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
// The results of a multi-value call as separate operands. Each result is
|
|
291
|
+
// boxed as if it were assigned to an `any`, so pointers print as `&{...}`
|
|
292
|
+
// and floats keep their width.
|
|
293
|
+
_fmtTupleJs(e) {
|
|
294
|
+
const resolve = (t) => this.checker?.resolveType?.(t) ?? t;
|
|
295
|
+
const parts = e._type.types.map((t, i) => {
|
|
296
|
+
const boxed = this._fmtBoxValue(`__t[${i}]`, t);
|
|
297
|
+
const desc = fmtTypeDesc(t, resolve);
|
|
298
|
+
if (fmtDescHasFloat(desc) && desc.k !== "i") {
|
|
299
|
+
this.useHelper("sprintf");
|
|
300
|
+
return `__ft(${boxed}, ${JSON.stringify(desc)})`;
|
|
301
|
+
}
|
|
302
|
+
return boxed;
|
|
303
|
+
});
|
|
304
|
+
return `...((__t) => [${parts.join(", ")}])(${this.genExpr(e)})`;
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
// The operands of a Printf-style call whose format is args[fmtIdx].
|
|
308
|
+
_fmtFormatArgsJs(args, fmtIdx) {
|
|
309
|
+
const lit = args[fmtIdx];
|
|
310
|
+
const spread = args.some((e) => e?._spread);
|
|
311
|
+
const resolve = (t) => this.checker?.resolveType?.(t) ?? t;
|
|
312
|
+
const seqArgs = args
|
|
313
|
+
.slice(fmtIdx + 1)
|
|
314
|
+
.some((e) => fmtDescIsSeq(fmtTypeDesc(e?._type, resolve)));
|
|
315
|
+
const typed =
|
|
316
|
+
lit?.kind === "BasicLit" &&
|
|
317
|
+
fmtWantsTypes(lit.value, spread ? -1 : args.length - fmtIdx - 1, seqArgs);
|
|
318
|
+
if (typed) this.useHelper("sprintf");
|
|
319
|
+
return args.map((e, idx) =>
|
|
320
|
+
idx < fmtIdx
|
|
321
|
+
? this.genExpr(e)
|
|
322
|
+
: idx === fmtIdx
|
|
323
|
+
? this.genExpr(e)
|
|
324
|
+
: this._fmtArgJs(e, typed),
|
|
325
|
+
);
|
|
24
326
|
},
|
|
25
327
|
|
|
26
328
|
_genFmt(fn, _a, expr) {
|
|
27
|
-
const fmtArgs =
|
|
28
|
-
.
|
|
29
|
-
|
|
329
|
+
const fmtArgs = ["Sprintf", "Printf", "Errorf"].includes(fn)
|
|
330
|
+
? this._fmtFormatArgsJs(expr.args, 0).join(", ")
|
|
331
|
+
: "";
|
|
30
332
|
switch (fn) {
|
|
31
333
|
case "Sprintf":
|
|
32
334
|
this.useHelper("sprintf");
|
|
33
335
|
return `__sprintf(${fmtArgs})`;
|
|
336
|
+
case "Sprint":
|
|
337
|
+
case "Sprintln":
|
|
338
|
+
this.useHelper("sprint");
|
|
339
|
+
return `__sprint(${fn === "Sprintln"}${expr.args.map((e) => `, ${this._fmtArgJs(e)}`).join("")})`;
|
|
340
|
+
case "Append":
|
|
341
|
+
case "Appendln":
|
|
342
|
+
case "Appendf": {
|
|
343
|
+
this.useHelper("sprint");
|
|
344
|
+
const [dst, ...rest] = expr.args;
|
|
345
|
+
const restJs = (
|
|
346
|
+
fn === "Appendf"
|
|
347
|
+
? this._fmtFormatArgsJs(rest, 0)
|
|
348
|
+
: rest.map((e) => this._fmtArgJs(e))
|
|
349
|
+
).join(", ");
|
|
350
|
+
const str =
|
|
351
|
+
fn === "Appendf"
|
|
352
|
+
? `__sprintf(${restJs})`
|
|
353
|
+
: `__sprint(${fn === "Appendln"}${restJs ? `, ${restJs}` : ""})`;
|
|
354
|
+
// Like append: write in place only when the whole result fits in
|
|
355
|
+
// dst's spare capacity, otherwise copy into one fresh array and
|
|
356
|
+
// leave dst's backing array untouched (never chunk the write).
|
|
357
|
+
this.useHelper("append");
|
|
358
|
+
return `((d, s) => { const e = new TextEncoder().encode(s); if (d == null && e.length === 0) return null; const r = d ?? new Uint8Array(0); const n = r.length + e.length; const c = __tcap.get(r); if (c !== undefined && n <= c) { const v = new Uint8Array(r.buffer, r.byteOffset, n); v.set(e, r.length); if (c > n) __tcap.set(v, c); return v; } const v = new Uint8Array(n); v.set(r); v.set(e, r.length); return v; })(${this.genExpr(dst)}, ${str})`;
|
|
359
|
+
}
|
|
34
360
|
case "Errorf": {
|
|
35
361
|
this.useHelper("sprintf");
|
|
36
362
|
this.useHelper("error");
|
|
@@ -46,7 +372,10 @@ export const fmtMethods = {
|
|
|
46
372
|
return this._genStdoutWrite(`__sprintf(${fmtArgs})`);
|
|
47
373
|
case "Print":
|
|
48
374
|
this.useHelper("sprintf");
|
|
49
|
-
|
|
375
|
+
this.useHelper("sprint");
|
|
376
|
+
return this._genStdoutWrite(
|
|
377
|
+
`__sprint(false${expr.args.map((e) => `, ${this._fmtArgJs(e)}`).join("")})`,
|
|
378
|
+
);
|
|
50
379
|
case "Println":
|
|
51
380
|
this.useHelper("sprintf");
|
|
52
381
|
// console.log supplies the trailing newline.
|
|
@@ -60,7 +389,7 @@ export const fmtMethods = {
|
|
|
60
389
|
const strArg = this.genExpr(expr.args[0]);
|
|
61
390
|
const restArgs = expr.args
|
|
62
391
|
.slice(1)
|
|
63
|
-
.map((e) => this.genExpr(e))
|
|
392
|
+
.map((e) => (e._spread ? `...${this.genExpr(e)}` : this.genExpr(e)))
|
|
64
393
|
.join(", ");
|
|
65
394
|
return `((str, ...ptrs) => { const tokens = str.trim().split(/\\s+/).filter(Boolean); ${_SSCAN_LOOP} return [n, err ? err : (n < ptrs.length ? __error("unexpected EOF") : null)]; })(${strArg}, ${restArgs})`;
|
|
66
395
|
}
|
|
@@ -69,7 +398,7 @@ export const fmtMethods = {
|
|
|
69
398
|
const strArg = this.genExpr(expr.args[0]);
|
|
70
399
|
const restArgs = expr.args
|
|
71
400
|
.slice(1)
|
|
72
|
-
.map((e) => this.genExpr(e))
|
|
401
|
+
.map((e) => (e._spread ? `...${this.genExpr(e)}` : this.genExpr(e)))
|
|
73
402
|
.join(", ");
|
|
74
403
|
return `((str, ...ptrs) => { const tokens = str.split("\\n")[0].trim().split(/\\s+/).filter(Boolean); ${_SSCAN_LOOP} return [n, err ? err : (n < ptrs.length ? __error("unexpected EOF") : null)]; })(${strArg}, ${restArgs})`;
|
|
75
404
|
}
|
|
@@ -79,7 +408,7 @@ export const fmtMethods = {
|
|
|
79
408
|
const fmtArg = this.genExpr(expr.args[1]);
|
|
80
409
|
const restArgs = expr.args
|
|
81
410
|
.slice(2)
|
|
82
|
-
.map((e) => this.genExpr(e))
|
|
411
|
+
.map((e) => (e._spread ? `...${this.genExpr(e)}` : this.genExpr(e)))
|
|
83
412
|
.join(", ");
|
|
84
413
|
return (
|
|
85
414
|
`((str, fmt, ...ptrs) => { ` +
|
|
@@ -132,7 +461,11 @@ export const fmtMethods = {
|
|
|
132
461
|
this.useHelper("sprintf");
|
|
133
462
|
const writerArg = expr.args[0];
|
|
134
463
|
const rest = expr.args.slice(1);
|
|
135
|
-
const restJs =
|
|
464
|
+
const restJs = (
|
|
465
|
+
fn === "Fprintf"
|
|
466
|
+
? this._fmtFormatArgsJs(rest, 0)
|
|
467
|
+
: rest.map((e) => this._fmtArgJs(e))
|
|
468
|
+
).join(", ");
|
|
136
469
|
const writerType = writerArg._type;
|
|
137
470
|
const targetTypeName =
|
|
138
471
|
writerType?.name ??
|
|
@@ -162,6 +495,12 @@ export const fmtMethods = {
|
|
|
162
495
|
_genPrintOperands(expr, sep, suffix) {
|
|
163
496
|
const argc = expr.args.length;
|
|
164
497
|
if (argc === 0) return JSON.stringify(suffix);
|
|
498
|
+
if (expr.args.some((e) => e._spread || e._type?.kind === "tuple")) {
|
|
499
|
+
// The operand count is only known at run time: join with Sprint's rules.
|
|
500
|
+
this.useHelper("sprint");
|
|
501
|
+
const joined = `__sprint(${sep === " "}, ${expr.args.map((e) => this._fmtArgJs(e)).join(", ")})`;
|
|
502
|
+
return sep === " " ? `${joined}.slice(0, -1)` : joined;
|
|
503
|
+
}
|
|
165
504
|
const fmt = Array(argc).fill("%v").join(sep) + suffix;
|
|
166
505
|
const args = expr.args.map((e) => this._fmtArgJs(e)).join(", ");
|
|
167
506
|
return `__sprintf(${JSON.stringify(fmt)}, ${args})`;
|
|
@@ -86,6 +86,9 @@ const dispatchMethods = {
|
|
|
86
86
|
// Dispatch table for stdlib namespace calls (pkg.Func → inline JS).
|
|
87
87
|
// Returns the generated JS string, or undefined if not handled.
|
|
88
88
|
_genStdlibCall(ns, fn, expr) {
|
|
89
|
+
if (ns === "gom") return this._genGom(fn, expr);
|
|
90
|
+
const method = STDLIB_METHOD_MAP[ns];
|
|
91
|
+
if (!method) return undefined;
|
|
89
92
|
// Stdlib shims take raw values; only fmt and errors use the interface box
|
|
90
93
|
// of a named value (Error() dispatch, error comparisons).
|
|
91
94
|
const isNamespace = expr.func?.expr?._type?.kind === "namespace";
|
|
@@ -93,9 +96,11 @@ const dispatchMethods = {
|
|
|
93
96
|
for (const e of expr.args) if (e) e._noNamedBox = true;
|
|
94
97
|
}
|
|
95
98
|
const a = () => expr.args.map((e) => this.genExpr(e));
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
const res = this[method](fn, a, expr);
|
|
100
|
+
if (res === undefined && isNamespace && ns !== "fmt" && ns !== "errors") {
|
|
101
|
+
for (const e of expr.args) if (e) delete e._noNamedBox;
|
|
102
|
+
}
|
|
103
|
+
return res;
|
|
99
104
|
},
|
|
100
105
|
};
|
|
101
106
|
|