goscript 0.2.6 → 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 +1 -1
- 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/quote.gs.ts
CHANGED
|
@@ -1,165 +1,393 @@
|
|
|
1
1
|
import * as $ from "@goscript/builtin/index.js";
|
|
2
|
+
import * as unicode from "@goscript/unicode/index.js";
|
|
2
3
|
import { ErrSyntax } from "./atoi.gs.js";
|
|
3
4
|
|
|
5
|
+
const lowerhex = "0123456789abcdef";
|
|
6
|
+
|
|
7
|
+
// hexEscape emits Go's \xNN, \uNNNN, or \UNNNNNNNN escape with width hex digits.
|
|
8
|
+
function hexEscape(prefix: string, value: number, width: number): string {
|
|
9
|
+
let out = prefix;
|
|
10
|
+
for (let shift = (width - 1) * 4; shift >= 0; shift -= 4) {
|
|
11
|
+
out += lowerhex[(value >> shift) & 0xf];
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// appendEscapedRune returns the Go-escaped form of one rune, matching
|
|
17
|
+
// strconv.appendEscapedRune: printable runes pass through, the standard control
|
|
18
|
+
// escapes (\a \b \f \n \r \t \v) are emitted by name, control and DEL bytes
|
|
19
|
+
// become \xNN, invalid runes fold to U+FFFD, and the rest become \u/\U. This
|
|
20
|
+
// replaces the prior JSON.stringify / fromCharCode shortcuts, which used the
|
|
21
|
+
// wrong escape set and truncated runes above U+FFFF.
|
|
22
|
+
function appendEscapedRune(
|
|
23
|
+
r: number,
|
|
24
|
+
quote: number,
|
|
25
|
+
asciiOnly: boolean,
|
|
26
|
+
graphicOnly: boolean,
|
|
27
|
+
): string {
|
|
28
|
+
if (r === quote || r === 0x5c /* backslash */) {
|
|
29
|
+
return "\\" + String.fromCharCode(r);
|
|
30
|
+
}
|
|
31
|
+
if (asciiOnly) {
|
|
32
|
+
if (r < 0x80 && unicode.IsPrint(r)) {
|
|
33
|
+
return String.fromCharCode(r);
|
|
34
|
+
}
|
|
35
|
+
} else if (unicode.IsPrint(r) || (graphicOnly && unicode.IsGraphic(r))) {
|
|
36
|
+
return $.runeToString(r);
|
|
37
|
+
}
|
|
38
|
+
switch (r) {
|
|
39
|
+
case 0x07:
|
|
40
|
+
return "\\a";
|
|
41
|
+
case 0x08:
|
|
42
|
+
return "\\b";
|
|
43
|
+
case 0x0c:
|
|
44
|
+
return "\\f";
|
|
45
|
+
case 0x0a:
|
|
46
|
+
return "\\n";
|
|
47
|
+
case 0x0d:
|
|
48
|
+
return "\\r";
|
|
49
|
+
case 0x09:
|
|
50
|
+
return "\\t";
|
|
51
|
+
case 0x0b:
|
|
52
|
+
return "\\v";
|
|
53
|
+
}
|
|
54
|
+
if (r < 0x20 || r === 0x7f) {
|
|
55
|
+
return hexEscape("\\x", r & 0xff, 2);
|
|
56
|
+
}
|
|
57
|
+
if (r < 0 || r > 0x10ffff || (r >= 0xd800 && r <= 0xdfff)) {
|
|
58
|
+
r = 0xfffd; // !utf8.ValidRune: fold to RuneError before emitting \u.
|
|
59
|
+
}
|
|
60
|
+
if (r < 0x10000) {
|
|
61
|
+
return hexEscape("\\u", r, 4);
|
|
62
|
+
}
|
|
63
|
+
return hexEscape("\\U", r, 8);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// quoteWith renders s as a Go quoted literal using quote as the delimiter.
|
|
67
|
+
function quoteWith(
|
|
68
|
+
s: string,
|
|
69
|
+
quote: number,
|
|
70
|
+
asciiOnly: boolean,
|
|
71
|
+
graphicOnly: boolean,
|
|
72
|
+
): string {
|
|
73
|
+
let out = String.fromCharCode(quote);
|
|
74
|
+
for (const ch of s) {
|
|
75
|
+
out += appendEscapedRune(ch.codePointAt(0)!, quote, asciiOnly, graphicOnly);
|
|
76
|
+
}
|
|
77
|
+
return out + String.fromCharCode(quote);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// quoteRuneWith renders a single rune as a Go quoted character literal.
|
|
81
|
+
function quoteRuneWith(
|
|
82
|
+
r: number,
|
|
83
|
+
quote: number,
|
|
84
|
+
asciiOnly: boolean,
|
|
85
|
+
graphicOnly: boolean,
|
|
86
|
+
): string {
|
|
87
|
+
return (
|
|
88
|
+
String.fromCharCode(quote) +
|
|
89
|
+
appendEscapedRune(r, quote, asciiOnly, graphicOnly) +
|
|
90
|
+
String.fromCharCode(quote)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
4
94
|
// Quote returns a double-quoted Go string literal representing s.
|
|
5
95
|
// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for control characters and non-printable characters.
|
|
6
96
|
export function Quote(s: string): string {
|
|
7
|
-
return
|
|
97
|
+
return quoteWith(s, 0x22 /* " */, false, false);
|
|
8
98
|
}
|
|
9
99
|
|
|
10
100
|
// QuoteToASCII returns a double-quoted Go string literal representing s.
|
|
11
101
|
// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for control characters and non-ASCII characters.
|
|
12
102
|
export function QuoteToASCII(s: string): string {
|
|
13
|
-
|
|
14
|
-
const quoted = JSON.stringify(s);
|
|
15
|
-
return quoted.replace(/[\u0080-\uFFFF]/g, (match) => {
|
|
16
|
-
const code = match.charCodeAt(0);
|
|
17
|
-
if (code <= 0xFF) {
|
|
18
|
-
return '\\x' + code.toString(16).padStart(2, '0');
|
|
19
|
-
} else {
|
|
20
|
-
return '\\u' + code.toString(16).padStart(4, '0');
|
|
21
|
-
}
|
|
22
|
-
});
|
|
103
|
+
return quoteWith(s, 0x22, true, false);
|
|
23
104
|
}
|
|
24
105
|
|
|
25
106
|
// QuoteToGraphic returns a double-quoted Go string literal representing s.
|
|
26
107
|
// The returned string leaves Unicode graphic characters unchanged.
|
|
27
108
|
export function QuoteToGraphic(s: string): string {
|
|
28
|
-
return
|
|
109
|
+
return quoteWith(s, 0x22, false, true);
|
|
29
110
|
}
|
|
30
111
|
|
|
31
112
|
// QuoteRune returns a single-quoted Go character literal representing the rune.
|
|
32
113
|
export function QuoteRune(r: number): string {
|
|
33
|
-
|
|
34
|
-
if (r === 39) { // single quote
|
|
35
|
-
return "'\\'";
|
|
36
|
-
}
|
|
37
|
-
if (r === 92) { // backslash
|
|
38
|
-
return "'\\\\'";
|
|
39
|
-
}
|
|
40
|
-
if (r >= 32 && r <= 126) { // printable ASCII
|
|
41
|
-
return "'" + char + "'";
|
|
42
|
-
}
|
|
43
|
-
// Use escape sequences for non-printable
|
|
44
|
-
if (r <= 0xFF) {
|
|
45
|
-
return "'\\x" + r.toString(16).padStart(2, '0') + "'";
|
|
46
|
-
}
|
|
47
|
-
return "'\\u" + r.toString(16).padStart(4, '0') + "'";
|
|
114
|
+
return quoteRuneWith(r, 0x27 /* ' */, false, false);
|
|
48
115
|
}
|
|
49
116
|
|
|
50
117
|
// QuoteRuneToASCII returns a single-quoted Go character literal representing the rune.
|
|
51
118
|
export function QuoteRuneToASCII(r: number): string {
|
|
52
|
-
return
|
|
119
|
+
return quoteRuneWith(r, 0x27, true, false);
|
|
53
120
|
}
|
|
54
121
|
|
|
55
122
|
// QuoteRuneToGraphic returns a single-quoted Go character literal representing the rune.
|
|
56
123
|
export function QuoteRuneToGraphic(r: number): string {
|
|
57
|
-
return
|
|
124
|
+
return quoteRuneWith(r, 0x27, false, true);
|
|
58
125
|
}
|
|
59
126
|
|
|
60
|
-
// CanBackquote reports whether the string s can be represented unchanged as a
|
|
127
|
+
// CanBackquote reports whether the string s can be represented unchanged as a
|
|
128
|
+
// single-line backquoted string. Go decodes UTF-8 runes: a tab is allowed, but
|
|
129
|
+
// other control bytes below space, a backtick, DEL, an invalid byte
|
|
130
|
+
// (utf8.RuneError at width 1), and a BOM (U+FEFF, invisible) are rejected.
|
|
61
131
|
export function CanBackquote(s: string): boolean {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
132
|
+
const bytes = $.stringToBytes(s);
|
|
133
|
+
let i = 0;
|
|
134
|
+
while (i < bytes.length) {
|
|
135
|
+
const [r, wid] = decodeRune(bytes, i);
|
|
136
|
+
i += wid;
|
|
137
|
+
if (wid > 1) {
|
|
138
|
+
if (r === 0xfeff) {
|
|
139
|
+
return false; // BOMs are invisible and should not be quoted.
|
|
140
|
+
}
|
|
141
|
+
continue; // Other multibyte runes are valid and assumed printable.
|
|
142
|
+
}
|
|
143
|
+
if (r === 0xfffd) {
|
|
144
|
+
return false; // utf8.RuneError from an invalid single byte.
|
|
145
|
+
}
|
|
146
|
+
if ((r < 0x20 && r !== 0x09) || r === 0x60 || r === 0x7f) {
|
|
66
147
|
return false;
|
|
67
148
|
}
|
|
68
149
|
}
|
|
69
150
|
return true;
|
|
70
151
|
}
|
|
71
152
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return ["", ErrSyntax];
|
|
153
|
+
// unhex returns the value of the hex digit byte b and whether b is a hex digit.
|
|
154
|
+
function unhex(b: number): [number, boolean] {
|
|
155
|
+
if (b >= 0x30 && b <= 0x39) {
|
|
156
|
+
return [b - 0x30, true];
|
|
77
157
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
158
|
+
if (b >= 0x61 && b <= 0x66) {
|
|
159
|
+
return [b - 0x61 + 10, true];
|
|
160
|
+
}
|
|
161
|
+
if (b >= 0x41 && b <= 0x46) {
|
|
162
|
+
return [b - 0x41 + 10, true];
|
|
82
163
|
}
|
|
164
|
+
return [0, false];
|
|
165
|
+
}
|
|
83
166
|
|
|
84
|
-
|
|
167
|
+
// decodeRune decodes the UTF-8 rune at bytes[pos], returning the rune and the
|
|
168
|
+
// number of bytes consumed. Invalid sequences decode to U+FFFD with width 1,
|
|
169
|
+
// matching utf8.DecodeRune.
|
|
170
|
+
function decodeRune(bytes: Uint8Array, pos: number): [number, number] {
|
|
171
|
+
const b0 = bytes[pos];
|
|
172
|
+
if (b0 < 0x80) {
|
|
173
|
+
return [b0, 1];
|
|
174
|
+
}
|
|
175
|
+
let size: number;
|
|
176
|
+
let r: number;
|
|
177
|
+
if ((b0 & 0xe0) === 0xc0) {
|
|
178
|
+
size = 2;
|
|
179
|
+
r = b0 & 0x1f;
|
|
180
|
+
} else if ((b0 & 0xf0) === 0xe0) {
|
|
181
|
+
size = 3;
|
|
182
|
+
r = b0 & 0x0f;
|
|
183
|
+
} else if ((b0 & 0xf8) === 0xf0) {
|
|
184
|
+
size = 4;
|
|
185
|
+
r = b0 & 0x07;
|
|
186
|
+
} else {
|
|
187
|
+
return [0xfffd, 1];
|
|
188
|
+
}
|
|
189
|
+
if (pos + size > bytes.length) {
|
|
190
|
+
return [0xfffd, 1];
|
|
191
|
+
}
|
|
192
|
+
for (let j = 1; j < size; j++) {
|
|
193
|
+
const cb = bytes[pos + j];
|
|
194
|
+
if ((cb & 0xc0) !== 0x80) {
|
|
195
|
+
return [0xfffd, 1];
|
|
196
|
+
}
|
|
197
|
+
r = (r << 6) | (cb & 0x3f);
|
|
198
|
+
}
|
|
199
|
+
return [r, size];
|
|
200
|
+
}
|
|
85
201
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
202
|
+
// unquoteCharBytes decodes the first character or byte in the escaped byte
|
|
203
|
+
// sequence starting at pos, returning [value, multibyte, newPos, err]. It is the
|
|
204
|
+
// byte-oriented core shared by the exported UnquoteChar and Unquote so that
|
|
205
|
+
// \xNN raw bytes and multibyte source runes round-trip exactly as in Go.
|
|
206
|
+
function unquoteCharBytes(
|
|
207
|
+
bytes: Uint8Array,
|
|
208
|
+
pos: number,
|
|
209
|
+
quote: number,
|
|
210
|
+
): [number, boolean, number, $.GoError] {
|
|
211
|
+
if (pos >= bytes.length) {
|
|
212
|
+
return [0, false, pos, ErrSyntax];
|
|
213
|
+
}
|
|
214
|
+
const c = bytes[pos];
|
|
215
|
+
if (c === quote && (quote === 0x27 || quote === 0x22)) {
|
|
216
|
+
return [0, false, pos, ErrSyntax];
|
|
217
|
+
}
|
|
218
|
+
if (c >= 0x80) {
|
|
219
|
+
const [r, size] = decodeRune(bytes, pos);
|
|
220
|
+
return [r, true, pos + size, null];
|
|
221
|
+
}
|
|
222
|
+
if (c !== 0x5c /* backslash */) {
|
|
223
|
+
return [c, false, pos + 1, null];
|
|
224
|
+
}
|
|
225
|
+
if (pos + 1 >= bytes.length) {
|
|
226
|
+
return [0, false, pos, ErrSyntax];
|
|
227
|
+
}
|
|
228
|
+
const e = bytes[pos + 1];
|
|
229
|
+
let p = pos + 2;
|
|
230
|
+
switch (e) {
|
|
231
|
+
case 0x61: // \a
|
|
232
|
+
return [0x07, false, p, null];
|
|
233
|
+
case 0x62: // \b
|
|
234
|
+
return [0x08, false, p, null];
|
|
235
|
+
case 0x66: // \f
|
|
236
|
+
return [0x0c, false, p, null];
|
|
237
|
+
case 0x6e: // \n
|
|
238
|
+
return [0x0a, false, p, null];
|
|
239
|
+
case 0x72: // \r
|
|
240
|
+
return [0x0d, false, p, null];
|
|
241
|
+
case 0x74: // \t
|
|
242
|
+
return [0x09, false, p, null];
|
|
243
|
+
case 0x76: // \v
|
|
244
|
+
return [0x0b, false, p, null];
|
|
245
|
+
case 0x78: // \x
|
|
246
|
+
case 0x75: // \u
|
|
247
|
+
case 0x55: { // \U
|
|
248
|
+
const n = e === 0x78 ? 2 : e === 0x75 ? 4 : 8;
|
|
249
|
+
if (p + n > bytes.length) {
|
|
250
|
+
return [0, false, pos, ErrSyntax];
|
|
251
|
+
}
|
|
252
|
+
let v = 0;
|
|
253
|
+
for (let j = 0; j < n; j++) {
|
|
254
|
+
const [x, ok] = unhex(bytes[p + j]);
|
|
255
|
+
if (!ok) {
|
|
256
|
+
return [0, false, pos, ErrSyntax];
|
|
257
|
+
}
|
|
258
|
+
v = (v << 4) | x;
|
|
259
|
+
}
|
|
260
|
+
p += n;
|
|
261
|
+
if (e === 0x78) {
|
|
262
|
+
return [v, false, p, null]; // single byte
|
|
263
|
+
}
|
|
264
|
+
if (v < 0 || v > 0x10ffff || (v >= 0xd800 && v <= 0xdfff)) {
|
|
265
|
+
return [0, false, pos, ErrSyntax]; // !utf8.ValidRune
|
|
266
|
+
}
|
|
267
|
+
return [v, true, p, null];
|
|
90
268
|
}
|
|
91
|
-
|
|
92
|
-
|
|
269
|
+
case 0x30:
|
|
270
|
+
case 0x31:
|
|
271
|
+
case 0x32:
|
|
272
|
+
case 0x33:
|
|
273
|
+
case 0x34:
|
|
274
|
+
case 0x35:
|
|
275
|
+
case 0x36:
|
|
276
|
+
case 0x37: { // octal \N
|
|
277
|
+
let v = e - 0x30;
|
|
278
|
+
if (p + 2 > bytes.length) {
|
|
279
|
+
return [0, false, pos, ErrSyntax];
|
|
280
|
+
}
|
|
281
|
+
for (let j = 0; j < 2; j++) {
|
|
282
|
+
const x = bytes[p + j] - 0x30;
|
|
283
|
+
if (x < 0 || x > 7) {
|
|
284
|
+
return [0, false, pos, ErrSyntax];
|
|
285
|
+
}
|
|
286
|
+
v = (v << 3) | x;
|
|
287
|
+
}
|
|
288
|
+
p += 2;
|
|
289
|
+
if (v > 255) {
|
|
290
|
+
return [0, false, pos, ErrSyntax];
|
|
291
|
+
}
|
|
292
|
+
return [v, false, p, null];
|
|
93
293
|
}
|
|
94
|
-
|
|
294
|
+
case 0x5c: // backslash
|
|
295
|
+
return [0x5c, false, p, null];
|
|
296
|
+
case 0x27: // '
|
|
297
|
+
case 0x22: // "
|
|
298
|
+
if (e !== quote) {
|
|
299
|
+
return [0, false, pos, ErrSyntax];
|
|
300
|
+
}
|
|
301
|
+
return [e, false, p, null];
|
|
302
|
+
default:
|
|
303
|
+
return [0, false, pos, ErrSyntax];
|
|
95
304
|
}
|
|
305
|
+
}
|
|
96
306
|
|
|
97
|
-
|
|
98
|
-
|
|
307
|
+
// UnquoteChar decodes the first character or byte in the escaped string or
|
|
308
|
+
// character literal represented by the string s. quote is the delimiting byte
|
|
309
|
+
// (' or "), or 0 to disallow neither.
|
|
310
|
+
export function UnquoteChar(s: string, quote: number): [number, boolean, string, $.GoError] {
|
|
311
|
+
const bytes = $.stringToBytes(s);
|
|
312
|
+
const [value, multibyte, newPos, err] = unquoteCharBytes(bytes, 0, quote);
|
|
313
|
+
if (err !== null) {
|
|
314
|
+
return [0, false, "", err];
|
|
99
315
|
}
|
|
316
|
+
return [value, multibyte, $.bytesToString(bytes.subarray(newPos)), null];
|
|
317
|
+
}
|
|
100
318
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
319
|
+
// Unquote interprets s as a single-quoted, double-quoted, or backquoted Go
|
|
320
|
+
// string literal, returning the string value that s quotes.
|
|
321
|
+
export function Unquote(s: string): [string, $.GoError] {
|
|
322
|
+
const bytes = $.stringToBytes(s);
|
|
323
|
+
const n = bytes.length;
|
|
324
|
+
if (n < 2) {
|
|
325
|
+
return ["", ErrSyntax];
|
|
109
326
|
}
|
|
327
|
+
const quote = bytes[0];
|
|
328
|
+
if (quote !== bytes[n - 1]) {
|
|
329
|
+
return ["", ErrSyntax];
|
|
330
|
+
}
|
|
331
|
+
const inner = bytes.subarray(1, n - 1);
|
|
110
332
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
333
|
+
if (quote === 0x60 /* ` */) {
|
|
334
|
+
for (let i = 0; i < inner.length; i++) {
|
|
335
|
+
if (inner[i] === 0x60) {
|
|
336
|
+
return ["", ErrSyntax];
|
|
337
|
+
}
|
|
116
338
|
}
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
case "'": return ["'", null];
|
|
124
|
-
default: return ["", ErrSyntax];
|
|
339
|
+
if (inner.includes(0x0d)) {
|
|
340
|
+
const out: number[] = [];
|
|
341
|
+
for (let i = 0; i < inner.length; i++) {
|
|
342
|
+
if (inner[i] !== 0x0d) {
|
|
343
|
+
out.push(inner[i]);
|
|
344
|
+
}
|
|
125
345
|
}
|
|
346
|
+
return [$.bytesToString(Uint8Array.from(out)), null];
|
|
126
347
|
}
|
|
127
|
-
return [
|
|
348
|
+
return [$.bytesToString(inner), null];
|
|
128
349
|
}
|
|
129
350
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// UnquoteChar decodes the first character or byte in the escaped string or character literal represented by the string s.
|
|
134
|
-
export function UnquoteChar(s: string, quote: number): [number, boolean, string, $.GoError] {
|
|
135
|
-
// Simplified implementation
|
|
136
|
-
if (s.length === 0) {
|
|
137
|
-
return [0, false, "", ErrSyntax];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const c = s.charCodeAt(0);
|
|
141
|
-
if (c === quote) {
|
|
142
|
-
return [0, false, "", ErrSyntax];
|
|
351
|
+
if (quote !== 0x22 && quote !== 0x27) {
|
|
352
|
+
return ["", ErrSyntax];
|
|
143
353
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return [c, c >= 128, s.slice(1), null];
|
|
354
|
+
if (inner.includes(0x0a)) {
|
|
355
|
+
return ["", ErrSyntax]; // newline not allowed in " or ' literal
|
|
147
356
|
}
|
|
148
|
-
|
|
149
|
-
//
|
|
150
|
-
if (
|
|
151
|
-
|
|
357
|
+
|
|
358
|
+
// Fast path: no escape and no embedded quote byte.
|
|
359
|
+
if (!inner.includes(0x5c) && !inner.includes(quote)) {
|
|
360
|
+
if (quote === 0x22) {
|
|
361
|
+
return [$.bytesToString(inner), null];
|
|
362
|
+
}
|
|
363
|
+
// single-quoted: must hold exactly one rune
|
|
364
|
+
const [r, size] = decodeRune(inner, 0);
|
|
365
|
+
if (size === inner.length && (r !== 0xfffd || size !== 1)) {
|
|
366
|
+
return [$.bytesToString(inner), null];
|
|
367
|
+
}
|
|
152
368
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
369
|
+
|
|
370
|
+
const out: number[] = [];
|
|
371
|
+
let pos = 0;
|
|
372
|
+
while (pos < inner.length) {
|
|
373
|
+
const [c, multibyte, newPos, err] = unquoteCharBytes(inner, pos, quote);
|
|
374
|
+
if (err !== null) {
|
|
375
|
+
return ["", err];
|
|
376
|
+
}
|
|
377
|
+
pos = newPos;
|
|
378
|
+
if (c < 0x80 || !multibyte) {
|
|
379
|
+
out.push(c & 0xff);
|
|
380
|
+
} else {
|
|
381
|
+
const enc = $.stringToBytes($.runeToString(c));
|
|
382
|
+
for (let j = 0; j < enc.length; j++) {
|
|
383
|
+
out.push(enc[j]);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (quote === 0x27 && pos < inner.length) {
|
|
387
|
+
return ["", ErrSyntax]; // single-quoted must be one character
|
|
388
|
+
}
|
|
162
389
|
}
|
|
390
|
+
return [$.bytesToString(Uint8Array.from(out)), null];
|
|
163
391
|
}
|
|
164
392
|
|
|
165
393
|
// QuotedPrefix returns the quoted string (as understood by Unquote) at the prefix of s.
|
|
@@ -235,11 +463,10 @@ export function AppendQuoteRuneToGraphic(dst: $.Bytes, r: number): $.Bytes {
|
|
|
235
463
|
|
|
236
464
|
// IsPrint reports whether the rune is defined as printable by Go.
|
|
237
465
|
export function IsPrint(r: number): boolean {
|
|
238
|
-
|
|
239
|
-
return r >= 32 && r <= 126;
|
|
466
|
+
return unicode.IsPrint(r);
|
|
240
467
|
}
|
|
241
468
|
|
|
242
469
|
// IsGraphic reports whether the rune is defined as a Graphic by Unicode.
|
|
243
470
|
export function IsGraphic(r: number): boolean {
|
|
244
|
-
return
|
|
245
|
-
}
|
|
471
|
+
return unicode.IsGraphic(r);
|
|
472
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CanBackquote,
|
|
5
|
+
Quote,
|
|
6
|
+
QuoteRune,
|
|
7
|
+
QuoteToASCII,
|
|
8
|
+
Unquote,
|
|
9
|
+
UnquoteChar,
|
|
10
|
+
} from './quote.gs.js'
|
|
11
|
+
|
|
12
|
+
// Expected outputs are the literal strings Go's strconv produces (go1.26.4).
|
|
13
|
+
describe('strconv.Quote (Go escape semantics)', () => {
|
|
14
|
+
it('passes printable ASCII and Unicode through unchanged', () => {
|
|
15
|
+
expect(Quote('hello')).toBe('"hello"')
|
|
16
|
+
// CJK is printable; Go leaves graphic Unicode in place.
|
|
17
|
+
expect(Quote('背景')).toBe('"背景"')
|
|
18
|
+
// U+1F600 is graphic; not truncated and not escaped.
|
|
19
|
+
expect(Quote('😀')).toBe('"😀"')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('escapes the quote and backslash', () => {
|
|
23
|
+
expect(Quote('a"b\\c')).toBe('"a\\"b\\\\c"')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('uses Go named escapes including \\a and \\v that JSON lacks', () => {
|
|
27
|
+
expect(Quote('a\tb\n')).toBe('"a\\tb\\n"')
|
|
28
|
+
expect(Quote('\x07\x0b')).toBe('"\\a\\v"')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('uses \\xNN for control bytes and DEL, not \\uNNNN', () => {
|
|
32
|
+
expect(Quote('\x00\x1f')).toBe('"\\x00\\x1f"')
|
|
33
|
+
expect(Quote('\x7f')).toBe('"\\x7f"')
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
describe('strconv.QuoteToASCII', () => {
|
|
38
|
+
it('escapes non-ASCII as \\u and astral as \\U', () => {
|
|
39
|
+
expect(QuoteToASCII('背景')).toBe('"\\u80cc\\u666f"')
|
|
40
|
+
expect(QuoteToASCII('😀')).toBe('"\\U0001f600"')
|
|
41
|
+
expect(QuoteToASCII('hi')).toBe('"hi"')
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
describe('strconv.QuoteRune', () => {
|
|
46
|
+
it('quotes printable, control, and astral runes like Go', () => {
|
|
47
|
+
expect(QuoteRune('a'.charCodeAt(0))).toBe("'a'")
|
|
48
|
+
expect(QuoteRune(0x09)).toBe("'\\t'")
|
|
49
|
+
expect(QuoteRune(0x1f600)).toBe("'😀'")
|
|
50
|
+
expect(QuoteRune(0x27)).toBe("'\\''")
|
|
51
|
+
expect(QuoteRune(0x00)).toBe("'\\x00'")
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('folds an invalid rune to the U+FFFD escape', () => {
|
|
55
|
+
expect(QuoteRune(0x110000)).toBe("'\\ufffd'")
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// Ground truth captured from go1.26.4 strconv.Unquote / CanBackquote /
|
|
60
|
+
// UnquoteChar over the same inputs.
|
|
61
|
+
describe('strconv.Unquote (Go escape decoding)', () => {
|
|
62
|
+
it('decodes \\x, octal, \\u, and \\U escapes', () => {
|
|
63
|
+
expect(Unquote('"\\x41"')).toEqual(['A', null])
|
|
64
|
+
expect(Unquote('"\\101"')).toEqual(['A', null]) // octal 101 = 'A'
|
|
65
|
+
expect(Unquote('"\\u0041"')).toEqual(['A', null])
|
|
66
|
+
expect(Unquote('"\\U0001F600"')).toEqual(['😀', null])
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('decodes named escapes and passes printable Unicode through', () => {
|
|
70
|
+
expect(Unquote('"a\\tb"')).toEqual(['a\tb', null])
|
|
71
|
+
expect(Unquote('"é"')).toEqual(['é', null])
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('handles backquoted strings without processing escapes', () => {
|
|
75
|
+
expect(Unquote('`raw\\tno-escape`')).toEqual(['raw\\tno-escape', null])
|
|
76
|
+
expect(Unquote('`with`back`')[1]).not.toBeNull() // embedded backtick fails
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('requires single-quoted literals to hold exactly one rune', () => {
|
|
80
|
+
expect(Unquote("'a'")).toEqual(['a', null])
|
|
81
|
+
expect(Unquote("'\\x41'")).toEqual(['A', null])
|
|
82
|
+
expect(Unquote("'ab'")[1]).not.toBeNull()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('rejects malformed input', () => {
|
|
86
|
+
expect(Unquote('"')[1]).not.toBeNull()
|
|
87
|
+
expect(Unquote('"\\x4"')[1]).not.toBeNull() // short hex
|
|
88
|
+
expect(Unquote('"a')[1]).not.toBeNull() // unterminated
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('strconv.CanBackquote (Go control rules)', () => {
|
|
93
|
+
it('allows tab but rejects other control bytes, backtick, DEL, and BOM', () => {
|
|
94
|
+
expect(CanBackquote('a\tb')).toBe(true)
|
|
95
|
+
expect(CanBackquote('plain text')).toBe(true)
|
|
96
|
+
expect(CanBackquote('a\nb')).toBe(false)
|
|
97
|
+
expect(CanBackquote('a`b')).toBe(false)
|
|
98
|
+
expect(CanBackquote('a\x7fb')).toBe(false)
|
|
99
|
+
expect(CanBackquote('\uFEFF')).toBe(false)
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('strconv.UnquoteChar (tail + multibyte)', () => {
|
|
104
|
+
it('decodes one escape and returns the remaining tail', () => {
|
|
105
|
+
expect(UnquoteChar('\\x41rest', 0x22)).toEqual([0x41, false, 'rest', null])
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('flags multibyte runes from \\u escapes', () => {
|
|
109
|
+
expect(UnquoteChar('\\u00e9z', 0x22)).toEqual([0xe9, true, 'z', null])
|
|
110
|
+
})
|
|
111
|
+
})
|
|
@@ -126,8 +126,8 @@ describe('strings/Reader', () => {
|
|
|
126
126
|
it('should seek from start', () => {
|
|
127
127
|
const r = new Reader({ s: 'hello world' })
|
|
128
128
|
|
|
129
|
-
const [pos, err] = r.Seek(
|
|
130
|
-
expect(pos).toBe(
|
|
129
|
+
const [pos, err] = r.Seek(6n, io.SeekStart)
|
|
130
|
+
expect(pos).toBe(6n)
|
|
131
131
|
expect(err).toBeNull()
|
|
132
132
|
expect(r.Len()).toBe(5)
|
|
133
133
|
})
|
|
@@ -136,32 +136,32 @@ describe('strings/Reader', () => {
|
|
|
136
136
|
const r = new Reader({ s: 'hello world' })
|
|
137
137
|
r.ReadByte() // advance to position 1
|
|
138
138
|
|
|
139
|
-
const [pos, err] = r.Seek(
|
|
140
|
-
expect(pos).toBe(
|
|
139
|
+
const [pos, err] = r.Seek(2n, io.SeekCurrent)
|
|
140
|
+
expect(pos).toBe(3n)
|
|
141
141
|
expect(err).toBeNull()
|
|
142
142
|
})
|
|
143
143
|
|
|
144
144
|
it('should seek from end', () => {
|
|
145
145
|
const r = new Reader({ s: 'hello world' })
|
|
146
146
|
|
|
147
|
-
const [pos, err] = r.Seek(-
|
|
148
|
-
expect(pos).toBe(
|
|
147
|
+
const [pos, err] = r.Seek(-5n, io.SeekEnd)
|
|
148
|
+
expect(pos).toBe(6n)
|
|
149
149
|
expect(err).toBeNull()
|
|
150
150
|
})
|
|
151
151
|
|
|
152
152
|
it('should handle invalid whence in seek', () => {
|
|
153
153
|
const r = new Reader({ s: 'hello' })
|
|
154
154
|
|
|
155
|
-
const [pos, err] = r.Seek(
|
|
156
|
-
expect(pos).toBe(
|
|
155
|
+
const [pos, err] = r.Seek(0n, 999)
|
|
156
|
+
expect(pos).toBe(0n)
|
|
157
157
|
expect(err).not.toBeNull()
|
|
158
158
|
})
|
|
159
159
|
|
|
160
160
|
it('should handle negative position in seek', () => {
|
|
161
161
|
const r = new Reader({ s: 'hello' })
|
|
162
162
|
|
|
163
|
-
const [pos, err] = r.Seek(-
|
|
164
|
-
expect(pos).toBe(
|
|
163
|
+
const [pos, err] = r.Seek(-10n, io.SeekStart)
|
|
164
|
+
expect(pos).toBe(0n)
|
|
165
165
|
expect(err).not.toBeNull()
|
|
166
166
|
})
|
|
167
167
|
|