goscript 0.2.5 → 0.2.7
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/cmd/goscript/cmd-compile.go +7 -0
- package/cmd/goscript/cmd_compile_test.go +83 -0
- package/compiler/compile-request.go +3 -0
- package/compiler/compiler-cache.go +828 -0
- package/compiler/compiler-cache_test.go +705 -0
- package/compiler/config.go +2 -0
- package/compiler/index.test.ts +26 -1
- package/compiler/index.ts +5 -0
- package/compiler/lowered-program.go +31 -20
- package/compiler/lowering.go +349 -93
- package/compiler/lowering_bench_test.go +1 -0
- package/compiler/override-facts.go +309 -8
- package/compiler/override-parity-verifier.go +45 -1
- package/compiler/override-parity-verifier_test.go +100 -0
- package/compiler/override-registry_test.go +1 -0
- package/compiler/package-graph.go +40 -12
- package/compiler/package-graph_test.go +29 -0
- package/compiler/runtime-contract.go +8 -0
- package/compiler/service.go +98 -11
- package/compiler/skeleton_test.go +110 -14
- package/compiler/typescript-emitter.go +120 -23
- package/dist/compiler/index.d.ts +2 -0
- package/dist/compiler/index.js +3 -0
- package/dist/compiler/index.js.map +1 -1
- package/dist/gs/builtin/builtin.d.ts +24 -33
- package/dist/gs/builtin/builtin.js +54 -61
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/hostio.d.ts +1 -0
- package/dist/gs/builtin/hostio.js +1 -1
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/index.d.ts +1 -0
- package/dist/gs/builtin/index.js +1 -0
- package/dist/gs/builtin/index.js.map +1 -1
- package/dist/gs/builtin/panic.d.ts +18 -0
- package/dist/gs/builtin/panic.js +98 -0
- package/dist/gs/builtin/panic.js.map +1 -0
- package/dist/gs/builtin/slice.d.ts +10 -0
- package/dist/gs/builtin/slice.js +110 -53
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.js +15 -3
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/builtin/varRef.d.ts +1 -1
- package/dist/gs/builtin/varRef.js +3 -2
- package/dist/gs/builtin/varRef.js.map +1 -1
- package/dist/gs/bytes/bytes.gs.js +51 -38
- package/dist/gs/bytes/bytes.gs.js.map +1 -1
- package/dist/gs/bytes/reader.gs.d.ts +1 -1
- package/dist/gs/bytes/reader.gs.js +6 -7
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/cmp/index.d.ts +1 -1
- package/dist/gs/cmp/index.js +43 -10
- package/dist/gs/cmp/index.js.map +1 -1
- package/dist/gs/context/context.d.ts +2 -2
- package/dist/gs/context/context.js +1 -1
- package/dist/gs/context/context.js.map +1 -1
- package/dist/gs/embed/index.js +1 -1
- package/dist/gs/embed/index.js.map +1 -1
- package/dist/gs/encoding/binary/index.js +201 -8
- package/dist/gs/encoding/binary/index.js.map +1 -1
- package/dist/gs/encoding/json/index.d.ts +5 -0
- package/dist/gs/encoding/json/index.js +388 -25
- package/dist/gs/encoding/json/index.js.map +1 -1
- package/dist/gs/errors/errors.js +17 -24
- package/dist/gs/errors/errors.js.map +1 -1
- package/dist/gs/fmt/fmt.js +129 -35
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.js +1 -1
- package/dist/gs/golang.org/x/crypto/cryptobyte/index.js.map +1 -1
- package/dist/gs/internal/bytealg/index.js +43 -8
- package/dist/gs/internal/bytealg/index.js.map +1 -1
- package/dist/gs/internal/byteorder/index.d.ts +2 -2
- package/dist/gs/internal/byteorder/index.js +2 -2
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/io/fs/format.js +2 -2
- package/dist/gs/io/fs/format.js.map +1 -1
- package/dist/gs/io/fs/fs.d.ts +1 -1
- package/dist/gs/io/fs/fs.js +1 -1
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/io/io.d.ts +21 -21
- package/dist/gs/io/io.js +49 -50
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/math/bits/index.js +26 -8
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/math/copysign.gs.js +10 -17
- package/dist/gs/math/copysign.gs.js.map +1 -1
- package/dist/gs/math/pow.gs.js +5 -0
- package/dist/gs/math/pow.gs.js.map +1 -1
- package/dist/gs/math/signbit.gs.js +6 -2
- package/dist/gs/math/signbit.gs.js.map +1 -1
- package/dist/gs/mime/index.js +1 -0
- package/dist/gs/mime/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +6 -6
- package/dist/gs/net/http/index.js +507 -43
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/os/stat.gs.d.ts +2 -2
- package/dist/gs/os/types.gs.d.ts +1 -1
- package/dist/gs/os/types.gs.js +1 -1
- package/dist/gs/os/types.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.d.ts +1 -1
- package/dist/gs/os/types_js.gs.js +7 -7
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.d.ts +1 -1
- package/dist/gs/os/types_unix.gs.js +1 -1
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/os/zero_copy_posix.gs.d.ts +1 -1
- package/dist/gs/os/zero_copy_posix.gs.js +1 -1
- package/dist/gs/os/zero_copy_posix.gs.js.map +1 -1
- package/dist/gs/path/filepath/match.js +8 -4
- package/dist/gs/path/filepath/match.js.map +1 -1
- package/dist/gs/path/filepath/path.js +216 -42
- package/dist/gs/path/filepath/path.js.map +1 -1
- package/dist/gs/path/match.js +6 -3
- package/dist/gs/path/match.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +5 -4
- package/dist/gs/reflect/type.js +29 -11
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/slices/slices.js +11 -11
- package/dist/gs/slices/slices.js.map +1 -1
- package/dist/gs/strconv/atof.gs.js +156 -43
- package/dist/gs/strconv/atof.gs.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.d.ts +3 -2
- package/dist/gs/strconv/atoi.gs.js +86 -67
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/strconv/ftoa.gs.js +73 -3
- package/dist/gs/strconv/ftoa.gs.js.map +1 -1
- package/dist/gs/strconv/itoa.gs.d.ts +4 -4
- package/dist/gs/strconv/itoa.gs.js +5 -4
- package/dist/gs/strconv/itoa.gs.js.map +1 -1
- package/dist/gs/strconv/quote.gs.d.ts +1 -1
- package/dist/gs/strconv/quote.gs.js +311 -103
- package/dist/gs/strconv/quote.gs.js.map +1 -1
- package/dist/gs/strings/reader.d.ts +1 -1
- package/dist/gs/strings/reader.js +8 -8
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/strings.js +87 -61
- package/dist/gs/strings/strings.js.map +1 -1
- package/dist/gs/sync/atomic/doc_64.gs.d.ts +14 -14
- package/dist/gs/sync/atomic/doc_64.gs.js +10 -10
- package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.d.ts +22 -22
- package/dist/gs/sync/atomic/type.gs.js +4 -4
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/sync/sync.js +50 -12
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/syscall/fs.d.ts +6 -6
- package/dist/gs/syscall/fs.js +1 -1
- package/dist/gs/syscall/fs.js.map +1 -1
- package/dist/gs/time/time.d.ts +18 -18
- package/dist/gs/time/time.js +58 -55
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unicode/tables.d.ts +11 -0
- package/dist/gs/unicode/tables.js +635 -0
- package/dist/gs/unicode/tables.js.map +1 -0
- package/dist/gs/unicode/unicode.d.ts +58 -38
- package/dist/gs/unicode/unicode.js +362 -278
- package/dist/gs/unicode/unicode.js.map +1 -1
- package/go.sum +13 -0
- package/gs/builtin/builtin.ts +83 -93
- package/gs/builtin/hostio.ts +1 -1
- package/gs/builtin/index.ts +1 -0
- package/gs/builtin/panic.test.ts +189 -0
- package/gs/builtin/panic.ts +107 -0
- package/gs/builtin/runtime-contract.test.ts +5 -5
- package/gs/builtin/slice.test.ts +23 -0
- package/gs/builtin/slice.ts +133 -95
- package/gs/builtin/type.ts +16 -3
- package/gs/builtin/varRef.ts +4 -2
- package/gs/builtin/wide-int.test.ts +41 -0
- package/gs/bytes/bytes.gs.ts +54 -41
- package/gs/bytes/bytes.test.ts +18 -1
- package/gs/bytes/reader.gs.ts +7 -8
- package/gs/cmp/index.test.ts +55 -0
- package/gs/cmp/index.ts +45 -9
- package/gs/context/context.ts +3 -3
- package/gs/embed/index.ts +2 -2
- package/gs/encoding/binary/index.test.ts +104 -0
- package/gs/encoding/binary/index.ts +259 -11
- package/gs/encoding/json/index.test.ts +107 -0
- package/gs/encoding/json/index.ts +400 -29
- package/gs/errors/errors.test.ts +44 -1
- package/gs/errors/errors.ts +15 -31
- package/gs/fmt/fmt.test.ts +70 -2
- package/gs/fmt/fmt.ts +128 -34
- package/gs/golang.org/x/crypto/cryptobyte/index.ts +1 -1
- package/gs/internal/bytealg/index.test.ts +26 -1
- package/gs/internal/bytealg/index.ts +44 -8
- package/gs/internal/byteorder/index.ts +6 -4
- package/gs/io/fs/format.ts +2 -2
- package/gs/io/fs/fs.ts +2 -2
- package/gs/io/fs/stat.test.ts +2 -2
- package/gs/io/fs/sub.test.ts +2 -2
- package/gs/io/fs/walk.test.ts +2 -2
- package/gs/io/io.test.ts +47 -5
- package/gs/io/io.ts +73 -73
- package/gs/io/limit.test.ts +103 -0
- package/gs/math/bits/index.test.ts +128 -0
- package/gs/math/bits/index.ts +26 -8
- package/gs/math/copysign.gs.test.ts +3 -1
- package/gs/math/copysign.gs.ts +10 -22
- package/gs/math/pow.gs.test.ts +4 -5
- package/gs/math/pow.gs.ts +5 -0
- package/gs/math/signbit.gs.test.ts +2 -1
- package/gs/math/signbit.gs.ts +6 -3
- package/gs/mime/index.ts +1 -0
- package/gs/net/http/index.test.ts +683 -2
- package/gs/net/http/index.ts +598 -57
- package/gs/net/http/meta.json +3 -0
- package/gs/os/stat.gs.ts +2 -2
- package/gs/os/types.gs.ts +2 -2
- package/gs/os/types_js.gs.ts +9 -9
- package/gs/os/types_unix.gs.ts +2 -2
- package/gs/os/zero_copy_posix.gs.ts +2 -2
- package/gs/path/filepath/match.test.ts +16 -0
- package/gs/path/filepath/match.ts +8 -4
- package/gs/path/filepath/path.test.ts +91 -9
- package/gs/path/filepath/path.ts +223 -49
- package/gs/path/match.test.ts +32 -0
- package/gs/path/match.ts +6 -3
- package/gs/reflect/deepequal.test.ts +1 -1
- package/gs/reflect/field.test.ts +1 -1
- package/gs/reflect/function-types.test.ts +6 -6
- package/gs/reflect/sliceat.test.ts +13 -13
- package/gs/reflect/structof.test.ts +4 -4
- package/gs/reflect/type.ts +34 -14
- package/gs/reflect/typefor.test.ts +5 -5
- package/gs/runtime/pprof/index.test.ts +20 -0
- package/gs/runtime/trace/index.test.ts +3 -0
- package/gs/slices/slices.test.ts +31 -0
- package/gs/slices/slices.ts +11 -11
- package/gs/strconv/append.test.ts +99 -0
- package/gs/strconv/atof.gs.ts +156 -42
- package/gs/strconv/atof.test.ts +45 -0
- package/gs/strconv/atoi.gs.ts +87 -69
- package/gs/strconv/atoi.test.ts +49 -0
- package/gs/strconv/ftoa.gs.ts +85 -10
- package/gs/strconv/ftoa.test.ts +43 -0
- package/gs/strconv/itoa.gs.ts +10 -9
- package/gs/strconv/quote.gs.ts +335 -108
- package/gs/strconv/quote.test.ts +111 -0
- package/gs/strings/reader.test.ts +10 -10
- package/gs/strings/reader.ts +9 -9
- package/gs/strings/strings.test.ts +18 -5
- package/gs/strings/strings.ts +81 -68
- package/gs/sync/atomic/doc_64.gs.ts +24 -24
- package/gs/sync/atomic/doc_64.test.ts +5 -5
- package/gs/sync/atomic/type.gs.ts +28 -28
- package/gs/sync/sync.test.ts +109 -1
- package/gs/sync/sync.ts +46 -12
- package/gs/syscall/fs.ts +8 -8
- package/gs/syscall/net.test.ts +1 -1
- package/gs/time/parse.test.ts +45 -0
- package/gs/time/time.test.ts +46 -23
- package/gs/time/time.ts +69 -66
- package/gs/unicode/gen.go +198 -0
- package/gs/unicode/tables.ts +646 -0
- package/gs/unicode/unicode.test.ts +69 -0
- package/gs/unicode/unicode.ts +396 -312
- package/package.json +2 -2
- package/dist/gs/github.com/aperturerobotics/util/conc/index.d.ts +0 -20
- package/dist/gs/github.com/aperturerobotics/util/conc/index.js +0 -134
- package/dist/gs/github.com/aperturerobotics/util/conc/index.js.map +0 -1
- package/gs/github.com/aperturerobotics/util/conc/index.test.ts +0 -30
- package/gs/github.com/aperturerobotics/util/conc/index.ts +0 -172
- package/gs/github.com/aperturerobotics/util/conc/meta.json +0 -9
package/gs/strconv/atof.gs.ts
CHANGED
|
@@ -1,60 +1,174 @@
|
|
|
1
1
|
import * as $ from "@goscript/builtin/index.js";
|
|
2
|
-
import { ErrSyntax, ErrRange, NumError } from "./atoi.gs.js";
|
|
2
|
+
import { ErrSyntax, ErrRange, NumError, underscoreOK } from "./atoi.gs.js";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
4
|
+
const maxFloat32 = 3.4028234663852886e+38;
|
|
5
|
+
|
|
6
|
+
// special matches Go's special-value parsing: an optional sign followed by a
|
|
7
|
+
// case-insensitive inf/infinity/nan, with nan rejecting a sign. It returns the
|
|
8
|
+
// value and whether s was a special form.
|
|
9
|
+
function special(s: string): [number, boolean] {
|
|
9
10
|
if (s === "") {
|
|
10
|
-
return [0,
|
|
11
|
+
return [0, false];
|
|
12
|
+
}
|
|
13
|
+
let sign = 1;
|
|
14
|
+
let nsign = 0;
|
|
15
|
+
if (s[0] === '+' || s[0] === '-') {
|
|
16
|
+
if (s[0] === '-') {
|
|
17
|
+
sign = -1;
|
|
18
|
+
}
|
|
19
|
+
nsign = 1;
|
|
20
|
+
}
|
|
21
|
+
const body = s.slice(nsign).toLowerCase();
|
|
22
|
+
if (body === "nan") {
|
|
23
|
+
if (nsign === 1) {
|
|
24
|
+
return [0, false]; // signed NaN is not accepted
|
|
25
|
+
}
|
|
26
|
+
return [NaN, true];
|
|
27
|
+
}
|
|
28
|
+
if (body === "inf" || body === "infinity") {
|
|
29
|
+
return [sign * Infinity, true];
|
|
30
|
+
}
|
|
31
|
+
return [0, false];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// parseHexFloat parses Go's hexadecimal floating-point syntax
|
|
35
|
+
// (0x1.8p3) into a float64, returning [value, ok]. The exponent letter p and a
|
|
36
|
+
// decimal exponent are mandatory; the value is mantissa * 2^(exp - 4*fracDigits).
|
|
37
|
+
function parseHexFloat(s: string): [number, boolean] {
|
|
38
|
+
let sign = 1;
|
|
39
|
+
let i = 0;
|
|
40
|
+
if (s[0] === '+' || s[0] === '-') {
|
|
41
|
+
if (s[0] === '-') {
|
|
42
|
+
sign = -1;
|
|
43
|
+
}
|
|
44
|
+
i = 1;
|
|
45
|
+
}
|
|
46
|
+
if (i + 1 >= s.length || s[i] !== '0' || s[i + 1].toLowerCase() !== 'x') {
|
|
47
|
+
return [0, false];
|
|
48
|
+
}
|
|
49
|
+
i += 2;
|
|
50
|
+
let intPart = "";
|
|
51
|
+
while (i < s.length && /[0-9a-fA-F]/.test(s[i])) {
|
|
52
|
+
intPart += s[i];
|
|
53
|
+
i++;
|
|
54
|
+
}
|
|
55
|
+
let fracPart = "";
|
|
56
|
+
if (i < s.length && s[i] === '.') {
|
|
57
|
+
i++;
|
|
58
|
+
while (i < s.length && /[0-9a-fA-F]/.test(s[i])) {
|
|
59
|
+
fracPart += s[i];
|
|
60
|
+
i++;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (intPart === "" && fracPart === "") {
|
|
64
|
+
return [0, false];
|
|
65
|
+
}
|
|
66
|
+
if (i >= s.length || s[i].toLowerCase() !== 'p') {
|
|
67
|
+
return [0, false]; // exponent is required for hex floats
|
|
11
68
|
}
|
|
69
|
+
i++;
|
|
70
|
+
let expSign = 1;
|
|
71
|
+
if (i < s.length && (s[i] === '+' || s[i] === '-')) {
|
|
72
|
+
if (s[i] === '-') {
|
|
73
|
+
expSign = -1;
|
|
74
|
+
}
|
|
75
|
+
i++;
|
|
76
|
+
}
|
|
77
|
+
if (i >= s.length) {
|
|
78
|
+
return [0, false];
|
|
79
|
+
}
|
|
80
|
+
let expStr = "";
|
|
81
|
+
while (i < s.length && s[i] >= '0' && s[i] <= '9') {
|
|
82
|
+
expStr += s[i];
|
|
83
|
+
i++;
|
|
84
|
+
}
|
|
85
|
+
if (expStr === "" || i !== s.length) {
|
|
86
|
+
return [0, false];
|
|
87
|
+
}
|
|
88
|
+
const mant = parseInt(intPart + fracPart, 16);
|
|
89
|
+
const exp = expSign * parseInt(expStr, 10) - 4 * fracPart.length;
|
|
90
|
+
return [sign * mant * Math.pow(2, exp), true];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// decimalSyntaxOK reports whether s is a syntactically valid Go decimal float
|
|
94
|
+
// literal (after underscores are removed): optional sign, a mantissa with digits
|
|
95
|
+
// on at least one side of an optional dot, and an optional decimal exponent.
|
|
96
|
+
function decimalSyntaxOK(s: string): boolean {
|
|
97
|
+
return /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(s);
|
|
98
|
+
}
|
|
12
99
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
100
|
+
// hasNonZeroDigit reports whether s contains a digit other than 0, used to tell
|
|
101
|
+
// a true zero from an underflow to zero.
|
|
102
|
+
function hasNonZeroDigit(s: string): boolean {
|
|
103
|
+
for (const ch of s) {
|
|
104
|
+
if (ch >= '1' && ch <= '9') {
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
16
107
|
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ParseFloat converts the string s to a floating-point number with the precision
|
|
112
|
+
// specified by bitSize: 32 for float32 or 64 for float64. The result always has
|
|
113
|
+
// type float64; for bitSize 32 it is rounded to float32 precision and reports
|
|
114
|
+
// ErrRange on overflow to infinity or underflow to zero. Any bitSize other than
|
|
115
|
+
// 32 is treated as float64.
|
|
116
|
+
export function ParseFloat(s: string, bitSize: number): [number, $.GoError] {
|
|
117
|
+
const syntax = (): [number, $.GoError] => [0, new NumError({Func: "ParseFloat", Num: s, Err: ErrSyntax})];
|
|
118
|
+
const range = (v: number): [number, $.GoError] => [v, new NumError({Func: "ParseFloat", Num: s, Err: ErrRange})];
|
|
17
119
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
switch (lower) {
|
|
21
|
-
case "+inf":
|
|
22
|
-
case "inf":
|
|
23
|
-
case "+infinity":
|
|
24
|
-
case "infinity":
|
|
25
|
-
return [Infinity, null];
|
|
26
|
-
case "-inf":
|
|
27
|
-
case "-infinity":
|
|
28
|
-
return [-Infinity, null];
|
|
29
|
-
case "nan":
|
|
30
|
-
return [NaN, null];
|
|
120
|
+
if (s === "") {
|
|
121
|
+
return syntax();
|
|
31
122
|
}
|
|
32
123
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
cleanS = s.replace(/_/g, '');
|
|
124
|
+
const [sv, isSpecial] = special(s);
|
|
125
|
+
if (isSpecial) {
|
|
126
|
+
return [sv, null];
|
|
37
127
|
}
|
|
38
128
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
129
|
+
let value: number;
|
|
130
|
+
let nonZero: boolean;
|
|
131
|
+
if (/^[+-]?0[xX]/.test(s)) {
|
|
132
|
+
const [hv, ok] = parseHexFloat(s);
|
|
133
|
+
if (!ok) {
|
|
134
|
+
return syntax();
|
|
135
|
+
}
|
|
136
|
+
value = hv;
|
|
137
|
+
nonZero = hv !== 0;
|
|
138
|
+
} else {
|
|
139
|
+
if (s.includes('_')) {
|
|
140
|
+
if (!underscoreOK(s)) {
|
|
141
|
+
return syntax();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const clean = s.replace(/_/g, '');
|
|
145
|
+
if (!decimalSyntaxOK(clean)) {
|
|
146
|
+
return syntax();
|
|
147
|
+
}
|
|
148
|
+
value = Number(clean);
|
|
149
|
+
nonZero = hasNonZeroDigit(clean);
|
|
44
150
|
}
|
|
45
151
|
|
|
46
|
-
// Check for range errors based on bitSize
|
|
47
152
|
if (bitSize === 32) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
153
|
+
const f = Math.fround(value);
|
|
154
|
+
if (!isFinite(f) && isFinite(value)) {
|
|
155
|
+
return range(value > 0 ? Infinity : -Infinity);
|
|
156
|
+
}
|
|
157
|
+
if (f === 0 && nonZero) {
|
|
158
|
+
return range(0);
|
|
53
159
|
}
|
|
54
|
-
|
|
55
|
-
|
|
160
|
+
// A finite float64 above float32 max also overflows.
|
|
161
|
+
if (isFinite(value) && Math.abs(value) > maxFloat32) {
|
|
162
|
+
return range(value > 0 ? Infinity : -Infinity);
|
|
56
163
|
}
|
|
164
|
+
return [f, null];
|
|
57
165
|
}
|
|
58
166
|
|
|
59
|
-
|
|
60
|
-
|
|
167
|
+
if (!isFinite(value) && nonZero) {
|
|
168
|
+
return range(value > 0 ? Infinity : -Infinity);
|
|
169
|
+
}
|
|
170
|
+
if (value === 0 && nonZero) {
|
|
171
|
+
return range(0);
|
|
172
|
+
}
|
|
173
|
+
return [value, null];
|
|
174
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { ParseFloat } from './atof.gs.js'
|
|
4
|
+
import { ErrRange, ErrSyntax, NumError } from './atoi.gs.js'
|
|
5
|
+
|
|
6
|
+
// Ground truth captured from go1.26.4 strconv.ParseFloat over the same inputs.
|
|
7
|
+
describe('strconv.ParseFloat (Go float syntax)', () => {
|
|
8
|
+
it('parses decimal floats and forms with a leading dot', () => {
|
|
9
|
+
expect(ParseFloat('1.5', 64)).toEqual([1.5, null])
|
|
10
|
+
expect(ParseFloat('.5', 64)).toEqual([0.5, null])
|
|
11
|
+
expect(ParseFloat('1_000.5', 64)).toEqual([1000.5, null])
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('rejects trailing junk and leading whitespace over the whole input', () => {
|
|
15
|
+
const [, errJunk] = ParseFloat('1.5abc', 64)
|
|
16
|
+
expect((errJunk as NumError).Err).toBe(ErrSyntax)
|
|
17
|
+
const [, errWs] = ParseFloat(' 1.5', 64)
|
|
18
|
+
expect((errWs as NumError).Err).toBe(ErrSyntax)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('parses Go hexadecimal floating-point syntax', () => {
|
|
22
|
+
expect(ParseFloat('0x1p2', 64)).toEqual([4, null])
|
|
23
|
+
expect(ParseFloat('0x1.8p3', 64)).toEqual([12, null])
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('reports ErrRange on overflow to infinity', () => {
|
|
27
|
+
const [v, err] = ParseFloat('1e400', 64)
|
|
28
|
+
expect(v).toBe(Infinity)
|
|
29
|
+
expect((err as NumError).Err).toBe(ErrRange)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('rounds to float32 precision and ranges float32 overflow', () => {
|
|
33
|
+
expect(ParseFloat('0.1', 32)).toEqual([0.10000000149011612, null])
|
|
34
|
+
const [v, err] = ParseFloat('3.5e38', 32)
|
|
35
|
+
expect(v).toBe(Infinity)
|
|
36
|
+
expect((err as NumError).Err).toBe(ErrRange)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('accepts inf and nan special forms', () => {
|
|
40
|
+
expect(ParseFloat('inf', 64)).toEqual([Infinity, null])
|
|
41
|
+
expect(ParseFloat('-Inf', 64)).toEqual([-Infinity, null])
|
|
42
|
+
const [nan] = ParseFloat('NaN', 64)
|
|
43
|
+
expect(Number.isNaN(nan)).toBe(true)
|
|
44
|
+
})
|
|
45
|
+
})
|
package/gs/strconv/atoi.gs.ts
CHANGED
|
@@ -90,17 +90,25 @@ export function bitSizeError(fn: string, str: string, bitSize: number): NumError
|
|
|
90
90
|
|
|
91
91
|
export let IntSize: number = 64;
|
|
92
92
|
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
// digitValue decodes the value of a single base-36 digit, returning 99 (an
|
|
94
|
+
// invalid sentinel above any legal base) for non-alphanumeric runes so the
|
|
95
|
+
// caller rejects them with a syntax error.
|
|
96
|
+
function digitValue(c: string): number {
|
|
97
|
+
if (c >= '0' && c <= '9') {
|
|
98
|
+
return c.charCodeAt(0) - 48;
|
|
99
|
+
}
|
|
100
|
+
const lc = c.toLowerCase();
|
|
101
|
+
if (lc >= 'a' && lc <= 'z') {
|
|
102
|
+
return lc.charCodeAt(0) - 97 + 10;
|
|
103
|
+
}
|
|
104
|
+
return 99;
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
// ParseUint is like ParseInt but for unsigned numbers.
|
|
100
|
-
// A sign prefix is not permitted.
|
|
101
|
-
export function ParseUint(s: string, base: number, bitSize: number): [
|
|
108
|
+
// A sign prefix is not permitted. It returns the uint64 value as a bigint.
|
|
109
|
+
export function ParseUint(s: string, base: number, bitSize: number): [bigint, $.GoError] {
|
|
102
110
|
if (s === "") {
|
|
103
|
-
return [
|
|
111
|
+
return [0n, syntaxError("ParseUint", s)];
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
const base0 = base === 0;
|
|
@@ -108,7 +116,7 @@ export function ParseUint(s: string, base: number, bitSize: number): [number, $.
|
|
|
108
116
|
|
|
109
117
|
// Handle base validation
|
|
110
118
|
if (base < 0 || base === 1 || base > 36) {
|
|
111
|
-
return [
|
|
119
|
+
return [0n, baseError("ParseUint", s0, base)];
|
|
112
120
|
}
|
|
113
121
|
|
|
114
122
|
// Handle base inference
|
|
@@ -141,118 +149,128 @@ export function ParseUint(s: string, base: number, bitSize: number): [number, $.
|
|
|
141
149
|
if (bitSize === 0) {
|
|
142
150
|
bitSize = 64;
|
|
143
151
|
} else if (bitSize < 0 || bitSize > 64) {
|
|
144
|
-
return [
|
|
152
|
+
return [0n, bitSizeError("ParseUint", s0, bitSize)];
|
|
145
153
|
}
|
|
146
154
|
|
|
147
|
-
//
|
|
155
|
+
// Underscores are permitted only when the base was auto-detected, and only
|
|
156
|
+
// between digits. Validate against the original string so the base prefix
|
|
157
|
+
// and hex-digit context are visible to underscoreOK.
|
|
148
158
|
if (base0 && s.includes('_')) {
|
|
149
|
-
if (!underscoreOK(
|
|
150
|
-
return [
|
|
159
|
+
if (!underscoreOK(s0)) {
|
|
160
|
+
return [0n, syntaxError("ParseUint", s0)];
|
|
151
161
|
}
|
|
152
162
|
s = s.replace(/_/g, '');
|
|
153
163
|
}
|
|
154
164
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (isNaN(result) || !isFinite(result)) {
|
|
159
|
-
return [0, syntaxError("ParseUint", s0)];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (result < 0) {
|
|
163
|
-
return [0, syntaxError("ParseUint", s0)];
|
|
165
|
+
if (s === "") {
|
|
166
|
+
return [0n, syntaxError("ParseUint", s0)];
|
|
164
167
|
}
|
|
165
168
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
169
|
+
const baseBig = BigInt(base);
|
|
170
|
+
const maxVal = (1n << BigInt(bitSize)) - 1n;
|
|
171
|
+
let n = 0n;
|
|
172
|
+
for (const ch of s) {
|
|
173
|
+
const d = digitValue(ch);
|
|
174
|
+
if (d >= base) {
|
|
175
|
+
return [0n, syntaxError("ParseUint", s0)];
|
|
176
|
+
}
|
|
177
|
+
n = n * baseBig + BigInt(d);
|
|
178
|
+
if (n > maxVal) {
|
|
179
|
+
return [maxVal, rangeError("ParseUint", s0)];
|
|
180
|
+
}
|
|
178
181
|
}
|
|
179
182
|
|
|
180
|
-
return [
|
|
183
|
+
return [n, null];
|
|
181
184
|
}
|
|
182
185
|
|
|
183
186
|
// ParseInt interprets a string s in the given base (0, 2 to 36) and
|
|
184
|
-
// bit size (0 to 64) and returns the corresponding value i.
|
|
185
|
-
export function ParseInt(s: string, base: number, bitSize: number): [
|
|
187
|
+
// bit size (0 to 64) and returns the corresponding value i as a bigint.
|
|
188
|
+
export function ParseInt(s: string, base: number, bitSize: number): [bigint, $.GoError] {
|
|
186
189
|
if (s === "") {
|
|
187
|
-
return [
|
|
190
|
+
return [0n, syntaxError("ParseInt", s)];
|
|
188
191
|
}
|
|
189
192
|
|
|
193
|
+
const s0 = s;
|
|
190
194
|
let neg = false;
|
|
191
195
|
if (s[0] === '+' || s[0] === '-') {
|
|
192
196
|
neg = s[0] === '-';
|
|
193
197
|
s = s.slice(1);
|
|
194
198
|
if (s.length < 1) {
|
|
195
|
-
return [
|
|
199
|
+
return [0n, syntaxError("ParseInt", s0)];
|
|
196
200
|
}
|
|
197
201
|
}
|
|
198
202
|
|
|
199
203
|
// Convert to unsigned first
|
|
200
204
|
const [un, err] = ParseUint(s, base, bitSize);
|
|
201
|
-
if (err !== null) {
|
|
205
|
+
if (err !== null && (err as NumError).Err !== ErrRange) {
|
|
202
206
|
const numErr = err as NumError;
|
|
203
207
|
numErr.Func = "ParseInt";
|
|
204
|
-
|
|
208
|
+
numErr.Num = s0;
|
|
209
|
+
return [0n, numErr];
|
|
205
210
|
}
|
|
206
211
|
|
|
207
212
|
if (bitSize === 0) {
|
|
208
213
|
bitSize = 64;
|
|
209
214
|
}
|
|
210
215
|
|
|
211
|
-
|
|
212
|
-
// so we use Math.pow() for larger bit sizes
|
|
213
|
-
let cutoff: number;
|
|
214
|
-
if (bitSize >= 53) {
|
|
215
|
-
// For 53+ bits, use MAX_SAFE_INTEGER as JavaScript can't represent larger integers accurately
|
|
216
|
-
cutoff = Number.MAX_SAFE_INTEGER;
|
|
217
|
-
} else {
|
|
218
|
-
cutoff = Math.pow(2, bitSize - 1);
|
|
219
|
-
}
|
|
216
|
+
const cutoff = 1n << BigInt(bitSize - 1);
|
|
220
217
|
if (!neg && un >= cutoff) {
|
|
221
|
-
return [
|
|
218
|
+
return [cutoff - 1n, rangeError("ParseInt", s0)];
|
|
222
219
|
}
|
|
223
220
|
if (neg && un > cutoff) {
|
|
224
|
-
return [
|
|
221
|
+
return [-cutoff, rangeError("ParseInt", s0)];
|
|
225
222
|
}
|
|
226
223
|
|
|
227
|
-
|
|
228
|
-
return [result, null];
|
|
224
|
+
return [neg ? -un : un, null];
|
|
229
225
|
}
|
|
230
226
|
|
|
231
227
|
// Atoi is equivalent to ParseInt(s, 10, 0), converted to type int.
|
|
232
228
|
export function Atoi(s: string): [number, $.GoError] {
|
|
233
229
|
const [i64, err] = ParseInt(s, 10, 0);
|
|
234
|
-
return [i64, err];
|
|
230
|
+
return [Number(i64), err];
|
|
235
231
|
}
|
|
236
232
|
|
|
237
|
-
// underscoreOK reports whether the underscores in s are allowed
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
233
|
+
// underscoreOK reports whether the underscores in s are allowed, matching Go's
|
|
234
|
+
// strconv.underscoreOK: an underscore must sit directly between two digits (or
|
|
235
|
+
// between a base prefix and a digit), never adjacent to a sign, prefix letter,
|
|
236
|
+
// dot, exponent marker, or another underscore. Used by ParseUint and ParseFloat.
|
|
237
|
+
export function underscoreOK(s: string): boolean {
|
|
238
|
+
// saw tracks the previous character class: '^' start, '0' digit or prefix,
|
|
239
|
+
// '_' underscore, '!' anything else.
|
|
240
|
+
let saw = '^';
|
|
241
|
+
let i = 0;
|
|
242
|
+
if (s.length >= 1 && (s[0] === '-' || s[0] === '+')) {
|
|
243
|
+
s = s.slice(1);
|
|
242
244
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
let hex = false;
|
|
246
|
+
if (
|
|
247
|
+
s.length >= 2 &&
|
|
248
|
+
s[0] === '0' &&
|
|
249
|
+
(s[1].toLowerCase() === 'b' || s[1].toLowerCase() === 'o' || s[1].toLowerCase() === 'x')
|
|
250
|
+
) {
|
|
251
|
+
i = 2;
|
|
252
|
+
saw = '0';
|
|
253
|
+
hex = s[1].toLowerCase() === 'x';
|
|
247
254
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (
|
|
255
|
+
for (; i < s.length; i++) {
|
|
256
|
+
const c = s[i];
|
|
257
|
+
const lc = c.toLowerCase();
|
|
258
|
+
if ((c >= '0' && c <= '9') || (hex && lc >= 'a' && lc <= 'f')) {
|
|
259
|
+
saw = '0';
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (c === '_') {
|
|
263
|
+
if (saw !== '0') {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
saw = '_';
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
if (saw === '_') {
|
|
252
270
|
return false;
|
|
253
271
|
}
|
|
272
|
+
saw = '!';
|
|
254
273
|
}
|
|
255
|
-
|
|
256
|
-
return true;
|
|
274
|
+
return saw !== '_';
|
|
257
275
|
}
|
|
258
276
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { ErrRange, ErrSyntax, NumError, ParseInt, ParseUint } from './atoi.gs.js'
|
|
4
|
+
|
|
5
|
+
// Ground truth captured from go1.26.4 strconv.ParseUint / ParseInt.
|
|
6
|
+
describe('strconv.ParseUint (Go base + validation)', () => {
|
|
7
|
+
it('rejects trailing non-digit junk', () => {
|
|
8
|
+
const [, err] = ParseUint('12abc', 10, 64)
|
|
9
|
+
expect((err as NumError).Err).toBe(ErrSyntax)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('infers base from 0x, 0b, and 0o prefixes when base is 0', () => {
|
|
13
|
+
expect(ParseUint('0xff', 0, 64)).toEqual([255n, null])
|
|
14
|
+
expect(ParseUint('0b101', 0, 64)).toEqual([5n, null])
|
|
15
|
+
expect(ParseUint('0o17', 0, 64)).toEqual([15n, null])
|
|
16
|
+
expect(ParseUint('0', 0, 64)).toEqual([0n, null])
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it('enforces the bit-size range ceiling', () => {
|
|
20
|
+
expect(ParseUint('255', 10, 8)).toEqual([255n, null])
|
|
21
|
+
const [v, err] = ParseUint('256', 10, 8)
|
|
22
|
+
expect(v).toBe(255n)
|
|
23
|
+
expect((err as NumError).Err).toBe(ErrRange)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('allows underscores only when the base is auto-detected', () => {
|
|
27
|
+
expect(ParseUint('1_000', 0, 64)).toEqual([1000n, null])
|
|
28
|
+
const [, err] = ParseUint('1_000', 10, 64)
|
|
29
|
+
expect((err as NumError).Err).toBe(ErrSyntax)
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe('strconv.ParseInt (Go sign + range)', () => {
|
|
34
|
+
it('parses signed values within range', () => {
|
|
35
|
+
expect(ParseInt('-128', 10, 8)).toEqual([-128n, null])
|
|
36
|
+
expect(ParseInt('+42', 10, 64)).toEqual([42n, null])
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('reports ErrRange past the signed ceiling', () => {
|
|
40
|
+
const [v, err] = ParseInt('-129', 10, 8)
|
|
41
|
+
expect(v).toBe(-128n)
|
|
42
|
+
expect((err as NumError).Err).toBe(ErrRange)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('rejects trailing junk', () => {
|
|
46
|
+
const [, err] = ParseInt('12x', 10, 64)
|
|
47
|
+
expect((err as NumError).Err).toBe(ErrSyntax)
|
|
48
|
+
})
|
|
49
|
+
})
|