gofront 1.8.0 → 1.9.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 +3 -3
- package/package.json +5 -4
- package/src/backend/js/expressions.js +849 -371
- package/src/backend/js/index.js +241 -147
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +512 -2547
- package/src/backend/js/statements.js +506 -103
- package/src/backend/js/stdlib/index.js +45 -93
- package/src/backend/js/templ.js +32 -45
- package/src/backend/js/type-info.js +177 -13
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +63 -171
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +490 -965
- package/src/backend/wasm/emit-builtins.js +643 -256
- package/src/backend/wasm/emit-exprs.js +124 -57
- package/src/backend/wasm/emit-maps.js +17 -367
- package/src/backend/wasm/emit-stmts.js +325 -75
- package/src/backend/wasm/emit.js +104 -13
- package/src/backend/wasm/globals.js +47 -8
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +36 -530
- package/src/backend/wasm/method-expr.js +12 -1
- package/src/backend/wasm/module-emitter.js +92 -717
- package/src/backend/wasm/monomorph.js +103 -6
- package/src/backend/wasm/natives.js +596 -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 +68 -20
- package/src/backend/wasm/variadic.js +3 -3
- package/src/cli/cli-core.js +5 -1
- package/src/cli/test-runner.js +115 -342
- package/src/compiler.js +108 -130
- package/src/lexer/lexer.js +105 -11
- package/src/lexer/templ-lexer.js +61 -18
- package/src/lexer/tokens.js +1 -0
- package/src/link/stdlib.js +354 -132
- package/src/link/type-names.js +148 -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 +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +7 -1
- package/src/lower/json-codecs.js +228 -247
- 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 +28 -5
- package/src/parser/expressions.js +86 -16
- package/src/parser/statements.js +1 -0
- package/src/parser/templ.js +44 -5
- package/src/parser/types.js +1 -0
- package/src/resolver.js +1 -2
- package/src/typechecker/assignability.js +124 -11
- package/src/typechecker/const-fold.js +70 -27
- package/src/typechecker/constants.js +32 -6
- package/src/typechecker/expressions.js +326 -98
- package/src/typechecker/index.js +208 -33
- package/src/typechecker/init-order.js +136 -40
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +192 -36
- package/src/typechecker/statements.js +122 -11
- 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 +203 -573
- 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
|
@@ -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,6 +10,7 @@ import {
|
|
|
9
10
|
CMP_OPS,
|
|
10
11
|
COMPLEX128,
|
|
11
12
|
FLOAT64,
|
|
13
|
+
is64BitInt,
|
|
12
14
|
isAny,
|
|
13
15
|
isComplex,
|
|
14
16
|
isComplex64,
|
|
@@ -133,6 +135,7 @@ export const assignabilityMethods = {
|
|
|
133
135
|
|
|
134
136
|
_binaryResultTypeCmp(op, lt, rt, node) {
|
|
135
137
|
if (this._checkComplexCmpOp(op, lt, rt, node)) return ANY;
|
|
138
|
+
this._checkIntegerCmpKinds(op, lt, rt, node);
|
|
136
139
|
this._checkEqualityComparable(op, lt, rt, node);
|
|
137
140
|
return BOOL;
|
|
138
141
|
},
|
|
@@ -173,6 +176,17 @@ export const assignabilityMethods = {
|
|
|
173
176
|
return isFloat ? FLOAT64 : lt;
|
|
174
177
|
},
|
|
175
178
|
|
|
179
|
+
// Comparing two typed integers of different types (`byte != int`) is a
|
|
180
|
+
// mismatch in Go; untyped constants adapt to either side.
|
|
181
|
+
_checkIntegerCmpKinds(op, lt, rt, node) {
|
|
182
|
+
if (!node || lt.kind === "untyped" || rt.kind === "untyped") return;
|
|
183
|
+
if (!isInteger(lt) || !isInteger(rt) || typeStr(lt) === typeStr(rt)) return;
|
|
184
|
+
this.err(
|
|
185
|
+
`invalid operation: ${exprText(node)} (mismatched types ${typeStr(lt)} and ${typeStr(rt)})`,
|
|
186
|
+
node,
|
|
187
|
+
);
|
|
188
|
+
},
|
|
189
|
+
|
|
176
190
|
_checkComplexCmpOp(op, lt, rt, node) {
|
|
177
191
|
if (isComplex(lt) || isComplex(rt)) {
|
|
178
192
|
if (op !== "==" && op !== "!=") {
|
|
@@ -204,13 +218,68 @@ export const assignabilityMethods = {
|
|
|
204
218
|
|
|
205
219
|
// ── Assignability ─────────────────────────────────────────────────
|
|
206
220
|
|
|
207
|
-
|
|
221
|
+
_tag64BitNode(target, node) {
|
|
222
|
+
if (node && typeof node === "object" && is64BitInt(target)) {
|
|
223
|
+
node._is64Bit = true;
|
|
224
|
+
if (node.kind === "KeyValueExpr" && node.value)
|
|
225
|
+
node.value._is64Bit = true;
|
|
226
|
+
if (node.kind === "UnaryExpr" && node.operand)
|
|
227
|
+
node.operand._is64Bit = true;
|
|
228
|
+
if (node.kind === "ParenExpr" && node.expr) node.expr._is64Bit = true;
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
// `var m uint64 = (1 << k) - 1`: a non-constant shift whose left operand is
|
|
233
|
+
// an untyped constant takes the type its context gives it, so codegen
|
|
234
|
+
// computes it in that type (BigInt for 64-bit integers).
|
|
235
|
+
_retypeUntypedShift(target, source, valueNode) {
|
|
236
|
+
if (!isUntyped(source) || !isInteger(target)) return;
|
|
237
|
+
this._retypeShiftTree(valueNode, target);
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
_retypeShiftTree(e, target) {
|
|
241
|
+
if (!e || typeof e !== "object" || e._type?.kind !== "untyped") return;
|
|
242
|
+
if (e.kind === "ParenExpr") this._retypeShiftTree(e.expr, target);
|
|
243
|
+
else if (e.kind === "UnaryExpr") this._retypeShiftTree(e.operand, target);
|
|
244
|
+
else if (e.kind === "BinaryExpr" && !this._constValue(e)) {
|
|
245
|
+
const isShift = e.op === "<<" || e.op === ">>";
|
|
246
|
+
this._retypeShiftTree(e.left, target);
|
|
247
|
+
if (isShift && e.left._type?.kind === "untyped") {
|
|
248
|
+
e.left._type = target;
|
|
249
|
+
if (is64BitInt(target)) e.left._is64Bit = true;
|
|
250
|
+
} else if (!isShift) this._retypeShiftTree(e.right, target);
|
|
251
|
+
} else return;
|
|
252
|
+
if (this._constValue(e)) return;
|
|
253
|
+
e._type = target;
|
|
254
|
+
if (is64BitInt(target)) e._is64Bit = true;
|
|
255
|
+
},
|
|
256
|
+
|
|
257
|
+
// An untyped real constant flowing into a complex type becomes a complex
|
|
258
|
+
// value in codegen.
|
|
259
|
+
_tagComplexConst(target, source, valueNode) {
|
|
260
|
+
if (
|
|
261
|
+
valueNode &&
|
|
262
|
+
typeof valueNode === "object" &&
|
|
263
|
+
isComplex(target) &&
|
|
264
|
+
isUntyped(source) &&
|
|
265
|
+
!isComplex(source)
|
|
266
|
+
)
|
|
267
|
+
valueNode._asComplex = true;
|
|
268
|
+
},
|
|
269
|
+
|
|
270
|
+
// `operand` is the expression whose value flows in, when `node` (the
|
|
271
|
+
// position and codegen tag) is not that expression.
|
|
272
|
+
assertAssignable(target, source, node, ctx = "assignment", operand = node) {
|
|
208
273
|
if (!target || !source) return;
|
|
209
274
|
if (this._checkConstOverflow(target, source, node, ctx)) return;
|
|
210
275
|
target = this.resolveType(target);
|
|
211
276
|
source = this.resolveType(source);
|
|
277
|
+
this._tag64BitNode(target, node);
|
|
278
|
+
this._retypeUntypedShift(target, source, operand ?? node);
|
|
279
|
+
this._tagComplexConst(target, source, operand ?? node);
|
|
212
280
|
this._markIfaceBox(target, source, node);
|
|
213
281
|
this._checkWasmIfaceProvider(target, source, node);
|
|
282
|
+
if (this._checkIfaceToConcrete(target, source, node, ctx, operand)) return;
|
|
214
283
|
if (this._assertAssignableEarlyReturn(target, source)) return;
|
|
215
284
|
if (isPointer(target) && isPointer(source)) {
|
|
216
285
|
this.assertAssignable(
|
|
@@ -221,14 +290,7 @@ export const assignabilityMethods = {
|
|
|
221
290
|
return;
|
|
222
291
|
}
|
|
223
292
|
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
|
-
}
|
|
293
|
+
if (this._checkNumericCoercion(target, source, node, ctx, operand)) return;
|
|
232
294
|
if (this._checkArrayAssignable(target, source, node)) return;
|
|
233
295
|
if (this._checkTypedArrayAssignable(target, source, node)) return;
|
|
234
296
|
if (this._checkFuncAssignable(target, source, node)) return;
|
|
@@ -300,7 +362,7 @@ export const assignabilityMethods = {
|
|
|
300
362
|
return { type: t, ptr: isPtr };
|
|
301
363
|
if (!methods || methods.size === 0) return null;
|
|
302
364
|
// Only user-declared types (methods carry _ptrRecv); stdlib named types
|
|
303
|
-
// such as
|
|
365
|
+
// such as strings.Builder have no box class.
|
|
304
366
|
if (![...methods.values()].some((m) => Object.hasOwn(m ?? {}, "_ptrRecv")))
|
|
305
367
|
return null;
|
|
306
368
|
return { type: t, ptr: isPtr };
|
|
@@ -379,6 +441,28 @@ export const assignabilityMethods = {
|
|
|
379
441
|
);
|
|
380
442
|
},
|
|
381
443
|
|
|
444
|
+
// An interface-typed operand needs an explicit type assertion before it can
|
|
445
|
+
// flow into a concrete (non-interface) type.
|
|
446
|
+
_checkIfaceToConcrete(target, source, node, ctx, operand) {
|
|
447
|
+
if (!source?._declared || !operand || typeof operand !== "object")
|
|
448
|
+
return false;
|
|
449
|
+
if (!this._isConcreteTarget(target)) return false;
|
|
450
|
+
this.err(
|
|
451
|
+
`cannot use ${exprText(operand)} (${operandKind(operand, typeStr(source))}) as ${typeStr(target)} value in ${ctx}: need type assertion`,
|
|
452
|
+
node,
|
|
453
|
+
);
|
|
454
|
+
return true;
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
_isConcreteTarget(target) {
|
|
458
|
+
if (!target || target.kind === "typeParam" || isAny(target)) return false;
|
|
459
|
+
if (isNil(target) || isUntyped(target)) return false;
|
|
460
|
+
const base = this.resolveType(
|
|
461
|
+
target.kind === "named" ? target.underlying : target,
|
|
462
|
+
);
|
|
463
|
+
return !!base && base.kind !== "interface" && base.kind !== "typeParam";
|
|
464
|
+
},
|
|
465
|
+
|
|
382
466
|
_assertAssignableEarlyReturn(target, source) {
|
|
383
467
|
return (
|
|
384
468
|
target?.kind === "typeParam" ||
|
|
@@ -502,13 +586,42 @@ export const assignabilityMethods = {
|
|
|
502
586
|
const pair = `${target.name}:${source.name}`;
|
|
503
587
|
return (
|
|
504
588
|
pair === "float64:int" ||
|
|
505
|
-
pair === "int:float64" ||
|
|
506
589
|
// rune was historically int in GoFront; keep rune/int interchangeable.
|
|
507
590
|
pair === "int:int32" ||
|
|
508
591
|
pair === "int32:int"
|
|
509
592
|
);
|
|
510
593
|
},
|
|
511
594
|
|
|
595
|
+
// The lenient numeric pairs: accepted, except that a generic call result
|
|
596
|
+
// is a mismatch and a float64 never reaches an int implicitly.
|
|
597
|
+
// Returns true when the pair was handled.
|
|
598
|
+
_checkNumericCoercion(target, source, node, ctx, operand) {
|
|
599
|
+
const floatToInt = this._isFloatToInt(target, source);
|
|
600
|
+
if (!floatToInt && !this._isNumericCoercible(target, source)) return false;
|
|
601
|
+
if (this._isGenericCallResult(node))
|
|
602
|
+
this.err(
|
|
603
|
+
`mismatched types ${typeStr(source)} and ${typeStr(target)} in ${ctx}`,
|
|
604
|
+
node,
|
|
605
|
+
);
|
|
606
|
+
else if (floatToInt)
|
|
607
|
+
this.err(
|
|
608
|
+
`cannot use ${exprText(operand)} (${operandKind(operand, typeStr(source))}) as ${typeStr(target)} value in ${ctx}`,
|
|
609
|
+
node,
|
|
610
|
+
);
|
|
611
|
+
return true;
|
|
612
|
+
},
|
|
613
|
+
|
|
614
|
+
// Go has no implicit float64 -> int conversion; only an untyped constant
|
|
615
|
+
// (handled earlier) or an explicit `int(x)` reaches an int.
|
|
616
|
+
_isFloatToInt(target, source) {
|
|
617
|
+
return (
|
|
618
|
+
target.kind === "basic" &&
|
|
619
|
+
source.kind === "basic" &&
|
|
620
|
+
target.name === "int" &&
|
|
621
|
+
source.name === "float64"
|
|
622
|
+
);
|
|
623
|
+
},
|
|
624
|
+
|
|
512
625
|
_checkArrayAssignable(target, source, node) {
|
|
513
626
|
if (target.kind === "array" && source.kind === "array") {
|
|
514
627
|
if (
|
|
@@ -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) {
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
foldUnary,
|
|
15
15
|
INT_RANGES,
|
|
16
16
|
intConst,
|
|
17
|
-
MATH_INT_CONSTS,
|
|
18
17
|
} from "./const-fold.js";
|
|
19
18
|
import { defaultType, isAny, typeStr, VOID } from "./types.js";
|
|
20
19
|
|
|
@@ -142,14 +141,12 @@ export const constantCheckMethods = {
|
|
|
142
141
|
return typed?.overflow ? null : typed;
|
|
143
142
|
},
|
|
144
143
|
|
|
145
|
-
// `pkg.K`
|
|
144
|
+
// `pkg.K` of an imported GoFront package.
|
|
146
145
|
_evalConstSelector(expr, scope) {
|
|
147
146
|
if (expr.expr?.kind !== "Ident") return null;
|
|
148
147
|
const ns = scope.lookup(expr.expr.name);
|
|
149
148
|
if (ns?.kind !== "namespace") return null;
|
|
150
|
-
|
|
151
|
-
const m = ns.name === "math" ? MATH_INT_CONSTS[expr.field] : undefined;
|
|
152
|
-
return m === undefined ? null : intConst(m);
|
|
149
|
+
return ns._gofront ? (ns._consts?.get(expr.field) ?? null) : null;
|
|
153
150
|
},
|
|
154
151
|
|
|
155
152
|
// Conversions, `len` of a constant string, and `min`/`max` of constants.
|
|
@@ -201,7 +198,18 @@ export const constantCheckMethods = {
|
|
|
201
198
|
);
|
|
202
199
|
if (base?.kind === "interface" || isAny(base)) target = defaultType(source);
|
|
203
200
|
const i = constIntValue(node._const);
|
|
204
|
-
const range =
|
|
201
|
+
const range = this._intRange(target);
|
|
202
|
+
if (i === null && range)
|
|
203
|
+
return this._reportTruncatedConst(target, node, ctx);
|
|
204
|
+
// A float or complex target holds the constant as a Number: Go rounds
|
|
205
|
+
// the same way, so the JS backend has nothing to warn about.
|
|
206
|
+
if (!range && node.kind) {
|
|
207
|
+
const u = this.resolveType(
|
|
208
|
+
target?.kind === "named" ? target.underlying : target,
|
|
209
|
+
);
|
|
210
|
+
if (u?.kind === "basic" && /^(float|complex)/.test(u.name))
|
|
211
|
+
node._floatConst = true;
|
|
212
|
+
}
|
|
205
213
|
if (!range) return false;
|
|
206
214
|
if (i >= range[0] && i <= range[1]) {
|
|
207
215
|
node._intConst = true; // the JS backend's precision warning reads this
|
|
@@ -223,6 +231,24 @@ export const constantCheckMethods = {
|
|
|
223
231
|
return true;
|
|
224
232
|
},
|
|
225
233
|
|
|
234
|
+
// `var i int = 1.5`: an untyped float constant with a fractional part
|
|
235
|
+
// cannot become an integer.
|
|
236
|
+
_reportTruncatedConst(target, node, ctx) {
|
|
237
|
+
const c = node._const;
|
|
238
|
+
if (c.k !== "FLOAT" || c.t) return false;
|
|
239
|
+
const shown =
|
|
240
|
+
node.kind === "BasicLit"
|
|
241
|
+
? `${node.value} (untyped float constant)`
|
|
242
|
+
: `${c.v} (untyped float constant ${c.v})`;
|
|
243
|
+
this.err(
|
|
244
|
+
ctx
|
|
245
|
+
? `cannot use ${shown} as ${typeStr(target)} value in ${ctx} (truncated)`
|
|
246
|
+
: `${shown} truncated to ${typeStr(target)}`,
|
|
247
|
+
node,
|
|
248
|
+
);
|
|
249
|
+
return true;
|
|
250
|
+
},
|
|
251
|
+
|
|
226
252
|
// `x := 1 << 63`, `println(1 << 64)`: the constant takes its default type.
|
|
227
253
|
_checkConstDefault(source, node, ctx) {
|
|
228
254
|
return this._checkConstOverflow(defaultType(source), source, node, ctx);
|