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
package/src/parser/templ.js
CHANGED
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
// TemplText { kind, value }
|
|
11
11
|
// TemplExpr { kind, tokens } ← raw Go token array
|
|
12
12
|
// TemplComponent { kind, tokens } ← raw Go token array for call
|
|
13
|
-
// TemplChildren { kind }
|
|
13
|
+
// TemplChildren { kind, name } ← `{ name... }`, name of a variadic slot
|
|
14
14
|
// TemplIf { kind, condTokens, then: TemplNode[], else_: TemplNode[] | null }
|
|
15
15
|
// TemplFor { kind, stmtTokens, body: TemplNode[] }
|
|
16
16
|
// TemplSwitch { kind, exprTokens, cases: TemplCase[] }
|
|
17
17
|
// TemplCase { kind, caseTokens: tokens | null (null = default), body: TemplNode[] }
|
|
18
|
+
// (a `case a, b:` also carries `exprs`, one expression per value)
|
|
18
19
|
//
|
|
19
20
|
// Go expressions embedded in templ (`{ expr }`, attribute values, `if`
|
|
20
21
|
// conditions, `switch` and `case` expressions, `@Comp(...)` calls) and `for`
|
|
@@ -71,12 +72,45 @@ function parseExprTokens(tokens, allowEmpty = true) {
|
|
|
71
72
|
if (allowEmpty && (!tokens || tokens.length === 0)) return { expr: null };
|
|
72
73
|
try {
|
|
73
74
|
const eofTok = new Token(T.EOF, "", 1, 1);
|
|
74
|
-
|
|
75
|
+
const p = new Parser([...tokens, eofTok]);
|
|
76
|
+
const expr = p.parseExpr();
|
|
77
|
+
while (p.check(T.SEMICOLON)) p.advance();
|
|
78
|
+
if (!p.check(T.EOF))
|
|
79
|
+
p.err(`unexpected '${p.peek().value}' after expression`);
|
|
80
|
+
return { expr };
|
|
75
81
|
} catch (exprError) {
|
|
76
82
|
return { expr: null, exprError };
|
|
77
83
|
}
|
|
78
84
|
}
|
|
79
85
|
|
|
86
|
+
// Parses `tokens` as a comma-separated expression list (`case 1, 2:`). `expr`
|
|
87
|
+
// and `exprError` describe the first expression (or the first error); `exprs`
|
|
88
|
+
// holds every parsed expression.
|
|
89
|
+
function parseExprListTokens(tokens) {
|
|
90
|
+
if (!tokens || tokens.length === 0) return { expr: null };
|
|
91
|
+
const parts = [[]];
|
|
92
|
+
let depth = 0;
|
|
93
|
+
for (const t of trimSemicolons(tokens ?? [])) {
|
|
94
|
+
if (t.type === T.LPAREN || t.type === T.LBRACKET || t.type === T.LBRACE)
|
|
95
|
+
depth++;
|
|
96
|
+
else if (
|
|
97
|
+
t.type === T.RPAREN ||
|
|
98
|
+
t.type === T.RBRACKET ||
|
|
99
|
+
t.type === T.RBRACE
|
|
100
|
+
)
|
|
101
|
+
depth--;
|
|
102
|
+
if (t.type === T.COMMA && depth === 0) parts.push([]);
|
|
103
|
+
else parts[parts.length - 1].push(t);
|
|
104
|
+
}
|
|
105
|
+
const exprs = [];
|
|
106
|
+
for (const part of parts) {
|
|
107
|
+
const parsed = parseExprTokens(part, false);
|
|
108
|
+
if (parsed.exprError) return { expr: null, exprError: parsed.exprError };
|
|
109
|
+
exprs.push(parsed.expr);
|
|
110
|
+
}
|
|
111
|
+
return { expr: exprs[0] ?? null, exprs };
|
|
112
|
+
}
|
|
113
|
+
|
|
80
114
|
// Parses an `if` condition; the lexer may leave the `if` keyword in front.
|
|
81
115
|
function parseCondTokens(tokens) {
|
|
82
116
|
if (!tokens || tokens.length === 0) return { expr: null };
|
|
@@ -220,7 +254,10 @@ export class TemplParser extends Parser {
|
|
|
220
254
|
return this.templComponent(t.value);
|
|
221
255
|
case TT.TEMPL_CHILDREN:
|
|
222
256
|
this.advance();
|
|
223
|
-
return
|
|
257
|
+
return attach(
|
|
258
|
+
{ kind: "TemplChildren", name: t.value },
|
|
259
|
+
parseExprTokens([new Token(T.IDENT, t.value, t.line, t.col)]),
|
|
260
|
+
);
|
|
224
261
|
case TT.TEMPL_IF: {
|
|
225
262
|
this.advance();
|
|
226
263
|
return this.parseTemplIf(t);
|
|
@@ -243,7 +280,9 @@ export class TemplParser extends Parser {
|
|
|
243
280
|
// `@templ.Raw(arg)` parses `arg`; any other component parses the whole call.
|
|
244
281
|
templComponent(tokens) {
|
|
245
282
|
const raw = isTemplRaw(tokens);
|
|
246
|
-
const parsed = parseExprTokens(
|
|
283
|
+
const parsed = parseExprTokens(
|
|
284
|
+
raw ? trimSemicolons(tokens).slice(4, -1) : tokens,
|
|
285
|
+
);
|
|
247
286
|
return attach({ kind: "TemplComponent", tokens }, { raw, ...parsed });
|
|
248
287
|
}
|
|
249
288
|
|
|
@@ -331,7 +370,7 @@ export class TemplParser extends Parser {
|
|
|
331
370
|
cases.push(
|
|
332
371
|
attach(
|
|
333
372
|
{ kind: "TemplCase", caseTokens, body },
|
|
334
|
-
isDefault ? { expr: null } :
|
|
373
|
+
isDefault ? { expr: null } : parseExprListTokens(caseTokens),
|
|
335
374
|
),
|
|
336
375
|
);
|
|
337
376
|
} else {
|
package/src/parser/types.js
CHANGED
|
@@ -269,6 +269,7 @@ export const typeParserMethods = {
|
|
|
269
269
|
names: [],
|
|
270
270
|
type: { kind: "TypeName", name: typeName },
|
|
271
271
|
embedded: true,
|
|
272
|
+
embeddedName: typeName,
|
|
272
273
|
tag,
|
|
273
274
|
});
|
|
274
275
|
this.semi();
|
|
@@ -328,6 +329,17 @@ export const typeParserMethods = {
|
|
|
328
329
|
this.semi();
|
|
329
330
|
continue;
|
|
330
331
|
}
|
|
332
|
+
// Type-set element that is not a bare name: *T, []int, map[K]V, ...
|
|
333
|
+
if (!this.check(T.IDENT)) {
|
|
334
|
+
const terms = [{ approx: false, type: this.parseType() }];
|
|
335
|
+
while (this.match(T.PIPE)) {
|
|
336
|
+
const approx = this.match(T.TILDE);
|
|
337
|
+
terms.push({ approx, type: this.parseType() });
|
|
338
|
+
}
|
|
339
|
+
unionConstraint = { kind: "UnionConstraint", terms };
|
|
340
|
+
this.semi();
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
331
343
|
const name = this.expect(T.IDENT).value;
|
|
332
344
|
if (this.check(T.LPAREN)) {
|
|
333
345
|
// Method signature: Name(params) returnType
|
package/src/resolver.js
CHANGED
|
@@ -39,8 +39,7 @@ export function isBuiltinPackage(importPath) {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// Go-source stdlib: `stdlib/<import path>/*.go`, written in GoFront Go
|
|
42
|
-
// and compiled into the caller's target.
|
|
43
|
-
// in STDLIB_PACKAGES is an overlay: its declarations extend that namespace.
|
|
42
|
+
// and compiled into the caller's target.
|
|
44
43
|
const STDLIB_SOURCE_ROOT = fileURLToPath(
|
|
45
44
|
new URL("../stdlib/", import.meta.url),
|
|
46
45
|
);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Installed as a mixin on TypeChecker.prototype.
|
|
3
3
|
|
|
4
4
|
import { constIntValue } from "./const-fold.js";
|
|
5
|
+
import { exprText, operandKind } from "./operand-text.js";
|
|
5
6
|
import { sharedInfo } from "./stdlib/shared.js";
|
|
6
7
|
import {
|
|
7
8
|
ANY,
|
|
@@ -9,7 +10,9 @@ import {
|
|
|
9
10
|
CMP_OPS,
|
|
10
11
|
COMPLEX128,
|
|
11
12
|
FLOAT64,
|
|
13
|
+
is64BitInt,
|
|
12
14
|
isAny,
|
|
15
|
+
isBool,
|
|
13
16
|
isComplex,
|
|
14
17
|
isComplex64,
|
|
15
18
|
isComplexOrNumeric,
|
|
@@ -25,9 +28,11 @@ import {
|
|
|
25
28
|
SIZED_INT_NAMES,
|
|
26
29
|
STRING,
|
|
27
30
|
typeStr,
|
|
31
|
+
UNTYPED_BOOL,
|
|
28
32
|
UNTYPED_COMPLEX,
|
|
29
33
|
UNTYPED_FLOAT,
|
|
30
34
|
UNTYPED_INT,
|
|
35
|
+
UNTYPED_RUNE,
|
|
31
36
|
} from "./types.js";
|
|
32
37
|
|
|
33
38
|
// Unnamed type literal kinds for Go's "identical underlying types" assignability rule.
|
|
@@ -61,6 +66,23 @@ const UNTYPED_COMPAT = {
|
|
|
61
66
|
complex128: new Set(["complex128", "complex64"]),
|
|
62
67
|
};
|
|
63
68
|
|
|
69
|
+
// `&&` and `||` of two untyped bools stay untyped; a typed operand (including
|
|
70
|
+
// a defined bool type) gives its type, as in Go.
|
|
71
|
+
// Two typed operands must have the same type, and the result has it.
|
|
72
|
+
function logicalResultType(lt, rt) {
|
|
73
|
+
if (lt?.kind === "untyped" && rt?.kind === "untyped") return UNTYPED_BOOL;
|
|
74
|
+
if (rt?.kind === "untyped" && isBool(lt)) return lt;
|
|
75
|
+
if (lt?.kind === "untyped" && isBool(rt)) return rt;
|
|
76
|
+
if (isBool(lt) && isBool(rt) && typeStr(lt) === typeStr(rt)) return lt;
|
|
77
|
+
return BOOL;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Two untyped numeric operands combine to the later of int, rune, float.
|
|
81
|
+
function untypedIntegerKind(isFloat, lt, rt) {
|
|
82
|
+
if (isFloat) return UNTYPED_FLOAT;
|
|
83
|
+
return lt.rune || rt.rune ? UNTYPED_RUNE : UNTYPED_INT;
|
|
84
|
+
}
|
|
85
|
+
|
|
64
86
|
/** @type {ThisType<TypeChecker>} */
|
|
65
87
|
export const assignabilityMethods = {
|
|
66
88
|
// ── Binary result types ───────────────────────────────────────────
|
|
@@ -69,12 +91,26 @@ export const assignabilityMethods = {
|
|
|
69
91
|
if (lt?.kind === "typeParam" || rt?.kind === "typeParam")
|
|
70
92
|
return this._binaryResultTypeTypeParam(op, lt, rt);
|
|
71
93
|
if (CMP_OPS.has(op)) return this._binaryResultTypeCmp(op, lt, rt, node);
|
|
72
|
-
if (LOG_OPS.has(op))
|
|
94
|
+
if (LOG_OPS.has(op)) {
|
|
95
|
+
this._checkLogicalOperands(lt, rt, node);
|
|
96
|
+
return logicalResultType(lt, rt);
|
|
97
|
+
}
|
|
73
98
|
return this._binaryResultTypeNonComplex(op, lt, rt, node);
|
|
74
99
|
},
|
|
75
100
|
|
|
101
|
+
// `B && bool` mixes two distinct bool types, which Go rejects.
|
|
102
|
+
_checkLogicalOperands(lt, rt, node) {
|
|
103
|
+
if (!node || lt?.kind === "untyped" || rt?.kind === "untyped") return;
|
|
104
|
+
if (!isBool(lt) || !isBool(rt) || typeStr(lt) === typeStr(rt)) return;
|
|
105
|
+
this.err(
|
|
106
|
+
`invalid operation: ${exprText(node)} (mismatched types ${typeStr(lt)} and ${typeStr(rt)})`,
|
|
107
|
+
node,
|
|
108
|
+
);
|
|
109
|
+
},
|
|
110
|
+
|
|
76
111
|
_binaryResultTypeTypeParam(op, lt, rt) {
|
|
77
|
-
if (CMP_OPS.has(op)
|
|
112
|
+
if (CMP_OPS.has(op)) return UNTYPED_BOOL;
|
|
113
|
+
if (LOG_OPS.has(op)) return BOOL;
|
|
78
114
|
return lt?.kind === "typeParam" ? lt : rt;
|
|
79
115
|
},
|
|
80
116
|
|
|
@@ -133,8 +169,10 @@ export const assignabilityMethods = {
|
|
|
133
169
|
|
|
134
170
|
_binaryResultTypeCmp(op, lt, rt, node) {
|
|
135
171
|
if (this._checkComplexCmpOp(op, lt, rt, node)) return ANY;
|
|
172
|
+
this._checkIntegerCmpKinds(op, lt, rt, node);
|
|
136
173
|
this._checkEqualityComparable(op, lt, rt, node);
|
|
137
|
-
|
|
174
|
+
// A comparison is an untyped bool: it also fits a defined bool type.
|
|
175
|
+
return UNTYPED_BOOL;
|
|
138
176
|
},
|
|
139
177
|
|
|
140
178
|
_binaryResultTypeComplex(op, lt, rt, node) {
|
|
@@ -160,7 +198,7 @@ export const assignabilityMethods = {
|
|
|
160
198
|
const rFloat = rName === "float32" || rName === "float64";
|
|
161
199
|
const isFloat = lFloat || rFloat;
|
|
162
200
|
if (lt.kind === "untyped" && rt.kind === "untyped")
|
|
163
|
-
return isFloat
|
|
201
|
+
return untypedIntegerKind(isFloat, lt, rt);
|
|
164
202
|
if (lt.kind === "untyped")
|
|
165
203
|
return isFloat && rt.name !== "float64" && rt.name !== "float32"
|
|
166
204
|
? FLOAT64
|
|
@@ -173,6 +211,17 @@ export const assignabilityMethods = {
|
|
|
173
211
|
return isFloat ? FLOAT64 : lt;
|
|
174
212
|
},
|
|
175
213
|
|
|
214
|
+
// Comparing two typed integers of different types (`byte != int`) is a
|
|
215
|
+
// mismatch in Go; untyped constants adapt to either side.
|
|
216
|
+
_checkIntegerCmpKinds(op, lt, rt, node) {
|
|
217
|
+
if (!node || lt.kind === "untyped" || rt.kind === "untyped") return;
|
|
218
|
+
if (!isInteger(lt) || !isInteger(rt) || typeStr(lt) === typeStr(rt)) return;
|
|
219
|
+
this.err(
|
|
220
|
+
`invalid operation: ${exprText(node)} (mismatched types ${typeStr(lt)} and ${typeStr(rt)})`,
|
|
221
|
+
node,
|
|
222
|
+
);
|
|
223
|
+
},
|
|
224
|
+
|
|
176
225
|
_checkComplexCmpOp(op, lt, rt, node) {
|
|
177
226
|
if (isComplex(lt) || isComplex(rt)) {
|
|
178
227
|
if (op !== "==" && op !== "!=") {
|
|
@@ -204,13 +253,93 @@ export const assignabilityMethods = {
|
|
|
204
253
|
|
|
205
254
|
// ── Assignability ─────────────────────────────────────────────────
|
|
206
255
|
|
|
207
|
-
|
|
256
|
+
_tag64BitNode(target, node) {
|
|
257
|
+
if (node && typeof node === "object" && is64BitInt(target)) {
|
|
258
|
+
node._is64Bit = true;
|
|
259
|
+
if (node.kind === "KeyValueExpr" && node.value)
|
|
260
|
+
node.value._is64Bit = true;
|
|
261
|
+
if (node.kind === "UnaryExpr" && node.operand)
|
|
262
|
+
node.operand._is64Bit = true;
|
|
263
|
+
if (node.kind === "ParenExpr" && node.expr) node.expr._is64Bit = true;
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
// An untyped numeric constant that takes type float32 is rounded to
|
|
268
|
+
// float32 in JS output (`var c float32 = 0.01`, `v == 0.01`).
|
|
269
|
+
_tagFloat32Const(target, source, node) {
|
|
270
|
+
if (!node || typeof node !== "object" || !node._const) return;
|
|
271
|
+
if (!isUntyped(source) || this._float32Base(target) !== "float32") return;
|
|
272
|
+
node._f32Const = true;
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
_float32Base(t) {
|
|
276
|
+
const r = t?.kind === "named" ? t.underlying : t;
|
|
277
|
+
return r?.kind === "basic" || r?.kind === "untyped" ? r.name : null;
|
|
278
|
+
},
|
|
279
|
+
|
|
280
|
+
// `var m uint64 = (1 << k) - 1`: a non-constant shift whose left operand is
|
|
281
|
+
// an untyped constant takes the type its context gives it, so codegen
|
|
282
|
+
// computes it in that type (BigInt for 64-bit integers).
|
|
283
|
+
_retypeUntypedShift(target, source, valueNode) {
|
|
284
|
+
if (!isUntyped(source) || !isInteger(target)) return;
|
|
285
|
+
this._retypeShiftTree(valueNode, target);
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
_retypeShiftTree(e, target) {
|
|
289
|
+
if (!e || typeof e !== "object" || e._type?.kind !== "untyped") return;
|
|
290
|
+
if (e.kind === "ParenExpr") this._retypeShiftTree(e.expr, target);
|
|
291
|
+
else if (e.kind === "UnaryExpr") this._retypeShiftTree(e.operand, target);
|
|
292
|
+
else if (e.kind === "BinaryExpr" && !this._constValue(e)) {
|
|
293
|
+
const isShift = e.op === "<<" || e.op === ">>";
|
|
294
|
+
this._retypeShiftTree(e.left, target);
|
|
295
|
+
if (isShift && e.left._type?.kind === "untyped") {
|
|
296
|
+
e.left._type = target;
|
|
297
|
+
if (is64BitInt(target)) e.left._is64Bit = true;
|
|
298
|
+
} else if (!isShift) this._retypeShiftTree(e.right, target);
|
|
299
|
+
} else return;
|
|
300
|
+
if (this._constValue(e)) return;
|
|
301
|
+
e._type = target;
|
|
302
|
+
if (is64BitInt(target)) e._is64Bit = true;
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
// An untyped real constant flowing into a complex type becomes a complex
|
|
306
|
+
// value in codegen.
|
|
307
|
+
_tagComplexConst(target, source, valueNode) {
|
|
308
|
+
if (
|
|
309
|
+
valueNode &&
|
|
310
|
+
typeof valueNode === "object" &&
|
|
311
|
+
isComplex(target) &&
|
|
312
|
+
isUntyped(source) &&
|
|
313
|
+
!isComplex(source)
|
|
314
|
+
)
|
|
315
|
+
valueNode._asComplex = true;
|
|
316
|
+
this._tagComplex64Const(target, source, valueNode);
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
// An untyped constant that takes type complex64 is rounded to float32 in
|
|
320
|
+
// JS output: a real constant through `_f32Const`, a complex constant
|
|
321
|
+
// (`1 + 0.01i`) through `_c64Const`.
|
|
322
|
+
_tagComplex64Const(target, source, node) {
|
|
323
|
+
if (!node || typeof node !== "object") return;
|
|
324
|
+
if (!isComplex64(target) || !isUntyped(source)) return;
|
|
325
|
+
if (isComplex(source)) node._c64Const = true;
|
|
326
|
+
else if (node._const) node._f32Const = true;
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
// `operand` is the expression whose value flows in, when `node` (the
|
|
330
|
+
// position and codegen tag) is not that expression.
|
|
331
|
+
assertAssignable(target, source, node, ctx = "assignment", operand = node) {
|
|
208
332
|
if (!target || !source) return;
|
|
209
333
|
if (this._checkConstOverflow(target, source, node, ctx)) return;
|
|
210
334
|
target = this.resolveType(target);
|
|
211
335
|
source = this.resolveType(source);
|
|
336
|
+
this._tag64BitNode(target, node);
|
|
337
|
+
this._tagFloat32Const(target, source, operand ?? node);
|
|
338
|
+
this._retypeUntypedShift(target, source, operand ?? node);
|
|
339
|
+
this._tagComplexConst(target, source, operand ?? node);
|
|
212
340
|
this._markIfaceBox(target, source, node);
|
|
213
341
|
this._checkWasmIfaceProvider(target, source, node);
|
|
342
|
+
if (this._checkIfaceToConcrete(target, source, node, ctx, operand)) return;
|
|
214
343
|
if (this._assertAssignableEarlyReturn(target, source)) return;
|
|
215
344
|
if (isPointer(target) && isPointer(source)) {
|
|
216
345
|
this.assertAssignable(
|
|
@@ -221,14 +350,7 @@ export const assignabilityMethods = {
|
|
|
221
350
|
return;
|
|
222
351
|
}
|
|
223
352
|
if (isUntyped(source) && this._isUntypedAssignable(target, source)) return;
|
|
224
|
-
if (this.
|
|
225
|
-
if (!this._isGenericCallResult(node)) return;
|
|
226
|
-
this.err(
|
|
227
|
-
`mismatched types ${typeStr(source)} and ${typeStr(target)} in ${ctx}`,
|
|
228
|
-
node,
|
|
229
|
-
);
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
353
|
+
if (this._checkNumericCoercion(target, source, node, ctx, operand)) return;
|
|
232
354
|
if (this._checkArrayAssignable(target, source, node)) return;
|
|
233
355
|
if (this._checkTypedArrayAssignable(target, source, node)) return;
|
|
234
356
|
if (this._checkFuncAssignable(target, source, node)) return;
|
|
@@ -300,7 +422,7 @@ export const assignabilityMethods = {
|
|
|
300
422
|
return { type: t, ptr: isPtr };
|
|
301
423
|
if (!methods || methods.size === 0) return null;
|
|
302
424
|
// Only user-declared types (methods carry _ptrRecv); stdlib named types
|
|
303
|
-
// such as
|
|
425
|
+
// such as strings.Builder have no box class.
|
|
304
426
|
if (![...methods.values()].some((m) => Object.hasOwn(m ?? {}, "_ptrRecv")))
|
|
305
427
|
return null;
|
|
306
428
|
return { type: t, ptr: isPtr };
|
|
@@ -379,6 +501,55 @@ export const assignabilityMethods = {
|
|
|
379
501
|
);
|
|
380
502
|
},
|
|
381
503
|
|
|
504
|
+
// An interface-typed operand needs an explicit type assertion before it can
|
|
505
|
+
// flow into a concrete (non-interface) type, or into an interface with
|
|
506
|
+
// methods (`var e error = b` with `b any`).
|
|
507
|
+
_checkIfaceToConcrete(target, source, node, ctx, operand) {
|
|
508
|
+
if (!source?._declared || !operand || typeof operand !== "object")
|
|
509
|
+
return false;
|
|
510
|
+
if (!this._isConcreteTarget(target) && !this._isMethodIfaceTarget(target))
|
|
511
|
+
return false;
|
|
512
|
+
this.err(
|
|
513
|
+
`cannot use ${exprText(operand)} (${operandKind(operand, typeStr(source))}) as ${typeStr(target)} value in ${ctx}: need type assertion`,
|
|
514
|
+
node,
|
|
515
|
+
);
|
|
516
|
+
return true;
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
// `int(x)`, `(*T)(x)` and `I(x)` (I with methods) with a source-written
|
|
520
|
+
// `any` operand need an assertion.
|
|
521
|
+
_checkIfaceConversion(srcType, targetType, expr, operand) {
|
|
522
|
+
if (!srcType?._declared || !operand) return false;
|
|
523
|
+
if (
|
|
524
|
+
!this._isConcreteTarget(targetType) &&
|
|
525
|
+
!this._isMethodIfaceTarget(targetType)
|
|
526
|
+
)
|
|
527
|
+
return false;
|
|
528
|
+
this.err(
|
|
529
|
+
`cannot convert ${exprText(operand)} (${operandKind(operand, typeStr(srcType))}) to type ${typeStr(targetType)}: need type assertion`,
|
|
530
|
+
expr,
|
|
531
|
+
);
|
|
532
|
+
return true;
|
|
533
|
+
},
|
|
534
|
+
|
|
535
|
+
// An interface type (named or literal) with at least one method.
|
|
536
|
+
_isMethodIfaceTarget(target) {
|
|
537
|
+
if (!target || isAny(target)) return false;
|
|
538
|
+
const base = this.resolveType(
|
|
539
|
+
target.kind === "named" ? target.underlying : target,
|
|
540
|
+
);
|
|
541
|
+
return base?.kind === "interface" && base.methods?.size > 0;
|
|
542
|
+
},
|
|
543
|
+
|
|
544
|
+
_isConcreteTarget(target) {
|
|
545
|
+
if (!target || target.kind === "typeParam" || isAny(target)) return false;
|
|
546
|
+
if (isNil(target) || isUntyped(target)) return false;
|
|
547
|
+
const base = this.resolveType(
|
|
548
|
+
target.kind === "named" ? target.underlying : target,
|
|
549
|
+
);
|
|
550
|
+
return !!base && base.kind !== "interface" && base.kind !== "typeParam";
|
|
551
|
+
},
|
|
552
|
+
|
|
382
553
|
_assertAssignableEarlyReturn(target, source) {
|
|
383
554
|
return (
|
|
384
555
|
target?.kind === "typeParam" ||
|
|
@@ -500,12 +671,36 @@ export const assignabilityMethods = {
|
|
|
500
671
|
_isNumericCoercible(target, source) {
|
|
501
672
|
if (target.kind !== "basic" || source.kind !== "basic") return false;
|
|
502
673
|
const pair = `${target.name}:${source.name}`;
|
|
674
|
+
return pair === "float64:int";
|
|
675
|
+
},
|
|
676
|
+
|
|
677
|
+
// The lenient numeric pairs: accepted, except that a generic call result
|
|
678
|
+
// is a mismatch and a float64 never reaches an int implicitly.
|
|
679
|
+
// Returns true when the pair was handled.
|
|
680
|
+
_checkNumericCoercion(target, source, node, ctx, operand) {
|
|
681
|
+
const floatToInt = this._isFloatToInt(target, source);
|
|
682
|
+
if (!floatToInt && !this._isNumericCoercible(target, source)) return false;
|
|
683
|
+
if (this._isGenericCallResult(node))
|
|
684
|
+
this.err(
|
|
685
|
+
`mismatched types ${typeStr(source)} and ${typeStr(target)} in ${ctx}`,
|
|
686
|
+
node,
|
|
687
|
+
);
|
|
688
|
+
else if (floatToInt)
|
|
689
|
+
this.err(
|
|
690
|
+
`cannot use ${exprText(operand)} (${operandKind(operand, typeStr(source))}) as ${typeStr(target)} value in ${ctx}`,
|
|
691
|
+
node,
|
|
692
|
+
);
|
|
693
|
+
return true;
|
|
694
|
+
},
|
|
695
|
+
|
|
696
|
+
// Go has no implicit float64 -> int conversion; only an untyped constant
|
|
697
|
+
// (handled earlier) or an explicit `int(x)` reaches an int.
|
|
698
|
+
_isFloatToInt(target, source) {
|
|
503
699
|
return (
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
pair === "int32:int"
|
|
700
|
+
target.kind === "basic" &&
|
|
701
|
+
source.kind === "basic" &&
|
|
702
|
+
target.name === "int" &&
|
|
703
|
+
source.name === "float64"
|
|
509
704
|
);
|
|
510
705
|
},
|
|
511
706
|
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
// Go type until they are converted or assigned
|
|
9
9
|
// i — for an untyped integral FLOAT, its exact BigInt value, so that
|
|
10
10
|
// `1e18 + 1` stays exact when it later becomes an integer
|
|
11
|
+
// r — for an untyped decimal FLOAT, its exact value `{ n, e }` = n * 10^-e
|
|
12
|
+
// (BigInt n), so `0.2 + 0.1` is exactly 0.3 before it rounds to float64
|
|
11
13
|
//
|
|
12
14
|
// The checker evaluates constant expressions with these helpers and attaches
|
|
13
15
|
// the result to the AST node (`_const`); both backends emit that value.
|
|
@@ -22,6 +24,7 @@ const BOOL = "BOOL";
|
|
|
22
24
|
// Shift counts beyond this are left to the runtime (Go allows larger shifts
|
|
23
25
|
// of untyped constants, but no program needs a 4096-bit intermediate).
|
|
24
26
|
const MAX_SHIFT = 4096n;
|
|
27
|
+
const MAX_DECIMAL_EXP = 400;
|
|
25
28
|
|
|
26
29
|
// Value ranges of the integer types.
|
|
27
30
|
export const INT_RANGES = {
|
|
@@ -42,24 +45,6 @@ export const INT_RANGES = {
|
|
|
42
45
|
|
|
43
46
|
const FLOAT_TYPES = new Set(["float32", "float64"]);
|
|
44
47
|
|
|
45
|
-
// Untyped integer constants of package `math` (Go declares them untyped).
|
|
46
|
-
// `MaxInt`, `MinInt` and `MaxUint` are left out: the JS backend maps them to
|
|
47
|
-
// the safe-integer range on purpose.
|
|
48
|
-
export const MATH_INT_CONSTS = {
|
|
49
|
-
MaxInt8: 2n ** 7n - 1n,
|
|
50
|
-
MinInt8: -(2n ** 7n),
|
|
51
|
-
MaxInt16: 2n ** 15n - 1n,
|
|
52
|
-
MinInt16: -(2n ** 15n),
|
|
53
|
-
MaxInt32: 2n ** 31n - 1n,
|
|
54
|
-
MinInt32: -(2n ** 31n),
|
|
55
|
-
MaxInt64: 2n ** 63n - 1n,
|
|
56
|
-
MinInt64: -(2n ** 63n),
|
|
57
|
-
MaxUint8: 2n ** 8n - 1n,
|
|
58
|
-
MaxUint16: 2n ** 16n - 1n,
|
|
59
|
-
MaxUint32: 2n ** 32n - 1n,
|
|
60
|
-
MaxUint64: 2n ** 64n - 1n,
|
|
61
|
-
};
|
|
62
|
-
|
|
63
48
|
export const intConst = (v) => ({ k: INT, v });
|
|
64
49
|
|
|
65
50
|
// Exact integer value of a decimal float literal (`1e18`, `2.50e1`), or null.
|
|
@@ -76,6 +61,40 @@ function exactIntOfFloatText(text) {
|
|
|
76
61
|
return cut > 0 ? BigInt(digits.slice(0, cut)) : 0n;
|
|
77
62
|
}
|
|
78
63
|
|
|
64
|
+
// Exact decimal value `{ n, e }` (n * 10^-e) of a decimal float literal, or null.
|
|
65
|
+
function exactDecimalOfFloatText(text) {
|
|
66
|
+
const m = /^(\d*)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/.exec(text);
|
|
67
|
+
if (!m) return null;
|
|
68
|
+
const frac = m[2] ?? "";
|
|
69
|
+
const digits = `${m[1]}${frac}` || "0";
|
|
70
|
+
const e = frac.length - Number(m[3] ?? 0);
|
|
71
|
+
if (Math.abs(e) > MAX_DECIMAL_EXP) return null;
|
|
72
|
+
return e >= 0
|
|
73
|
+
? { n: BigInt(digits), e }
|
|
74
|
+
: { n: BigInt(digits) * 10n ** BigInt(-e), e: 0 };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Exact decimal value of an untyped INT or FLOAT constant, or null.
|
|
78
|
+
function exactDecimal(val) {
|
|
79
|
+
if (val.t) return null;
|
|
80
|
+
if (val.r) return val.r;
|
|
81
|
+
const i = constIntValue(val);
|
|
82
|
+
return i === null ? null : { n: i, e: 0 };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function decimalToFloat({ n, e }) {
|
|
86
|
+
return Number(`${n}e-${e}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function floatConstFromText(s) {
|
|
90
|
+
const n = Number(s);
|
|
91
|
+
if (!Number.isFinite(n)) return null;
|
|
92
|
+
const i = exactIntOfFloatText(s);
|
|
93
|
+
const r = exactDecimalOfFloatText(s);
|
|
94
|
+
const out = i === null ? { k: FLOAT, v: n } : { k: FLOAT, v: n, i };
|
|
95
|
+
return r === null ? out : { ...out, r };
|
|
96
|
+
}
|
|
97
|
+
|
|
79
98
|
export function constFromLit(lit) {
|
|
80
99
|
switch (lit.litKind) {
|
|
81
100
|
case INT: {
|
|
@@ -83,13 +102,8 @@ export function constFromLit(lit) {
|
|
|
83
102
|
const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
|
|
84
103
|
return { k: INT, v: BigInt(clean) };
|
|
85
104
|
}
|
|
86
|
-
case FLOAT:
|
|
87
|
-
|
|
88
|
-
const n = Number(s);
|
|
89
|
-
if (!Number.isFinite(n)) return null;
|
|
90
|
-
const i = exactIntOfFloatText(s);
|
|
91
|
-
return i === null ? { k: FLOAT, v: n } : { k: FLOAT, v: n, i };
|
|
92
|
-
}
|
|
105
|
+
case FLOAT:
|
|
106
|
+
return floatConstFromText(String(lit.value).replaceAll("_", ""));
|
|
93
107
|
case STRING:
|
|
94
108
|
return { k: STRING, v: String(lit.value ?? "") };
|
|
95
109
|
case BOOL:
|
|
@@ -146,13 +160,19 @@ function typedFloat(v, t) {
|
|
|
146
160
|
return t ? { k: FLOAT, v: r, t } : { k: FLOAT, v: r };
|
|
147
161
|
}
|
|
148
162
|
|
|
163
|
+
function negateFloat(x) {
|
|
164
|
+
const r = typedFloat(-x.v, x.t);
|
|
165
|
+
if (r && x.i !== undefined) r.i = -x.i;
|
|
166
|
+
if (r && x.r && !x.t) r.r = { n: -x.r.n, e: x.r.e };
|
|
167
|
+
return r;
|
|
168
|
+
}
|
|
169
|
+
|
|
149
170
|
export function foldUnary(op, x) {
|
|
150
171
|
if (op === "+" && (x.k === INT || x.k === FLOAT)) return x;
|
|
151
172
|
if (op === "!" && x.k === BOOL) return { ...x, v: !x.v };
|
|
152
173
|
if (op === "-" && x.k === INT) return typedInt(-x.v, x.t);
|
|
153
174
|
if (op === "-" && x.k === FLOAT) {
|
|
154
|
-
|
|
155
|
-
return r && x.i !== undefined ? { ...r, i: -x.i } : r;
|
|
175
|
+
return negateFloat(x);
|
|
156
176
|
}
|
|
157
177
|
if (op === "^" && x.k === INT) {
|
|
158
178
|
// Unsigned: flip the bits of the type's width; otherwise -x-1.
|
|
@@ -211,6 +231,27 @@ function foldInts(op, a, b, t) {
|
|
|
211
231
|
}
|
|
212
232
|
}
|
|
213
233
|
|
|
234
|
+
// `x op y` on exact decimals for + - *.
|
|
235
|
+
function combineDecimals(op, x, y) {
|
|
236
|
+
if (op === "*") return { n: x.n * y.n, e: x.e + y.e };
|
|
237
|
+
const e = Math.max(x.e, y.e);
|
|
238
|
+
const xn = x.n * 10n ** BigInt(e - x.e);
|
|
239
|
+
const yn = y.n * 10n ** BigInt(e - y.e);
|
|
240
|
+
return { n: op === "+" ? xn + yn : xn - yn, e };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Untyped decimal + - * in exact arithmetic, rounded to float64 once at the end.
|
|
244
|
+
function foldExactDecimals(op, a, b) {
|
|
245
|
+
if (op !== "+" && op !== "-" && op !== "*") return null;
|
|
246
|
+
const x = exactDecimal(a);
|
|
247
|
+
const y = exactDecimal(b);
|
|
248
|
+
if (!x || !y) return null;
|
|
249
|
+
const r = combineDecimals(op, x, y);
|
|
250
|
+
if (r.e > MAX_DECIMAL_EXP) return null;
|
|
251
|
+
const v = decimalToFloat(r);
|
|
252
|
+
return Number.isFinite(v) ? { k: FLOAT, v, r } : null;
|
|
253
|
+
}
|
|
254
|
+
|
|
214
255
|
function foldFloats(op, a, b, t) {
|
|
215
256
|
// Untyped integral operands stay exact for + - * (`1e18 + 1`).
|
|
216
257
|
const ai = t ? null : constIntValue(a);
|
|
@@ -219,6 +260,8 @@ function foldFloats(op, a, b, t) {
|
|
|
219
260
|
const i = op === "+" ? ai + bi : op === "-" ? ai - bi : ai * bi;
|
|
220
261
|
return { k: FLOAT, v: Number(i), i };
|
|
221
262
|
}
|
|
263
|
+
const exact = t ? null : foldExactDecimals(op, a, b);
|
|
264
|
+
if (exact) return exact;
|
|
222
265
|
const x = asFloat(a);
|
|
223
266
|
const y = asFloat(b);
|
|
224
267
|
switch (op) {
|