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
|
@@ -43,6 +43,28 @@ export class SyntaxError extends jsonError {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// Register *json.SyntaxError so a Go `err.(*json.SyntaxError)` assertion in
|
|
47
|
+
// compiled code resolves to this class and exposes the Offset field.
|
|
48
|
+
$.registerStructType(
|
|
49
|
+
'json.SyntaxError',
|
|
50
|
+
new SyntaxError(),
|
|
51
|
+
[
|
|
52
|
+
{
|
|
53
|
+
name: 'Error',
|
|
54
|
+
args: [],
|
|
55
|
+
returns: [{ type: { kind: $.TypeKind.Basic, name: 'string' } }],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
SyntaxError,
|
|
59
|
+
[
|
|
60
|
+
{
|
|
61
|
+
name: 'Offset',
|
|
62
|
+
key: 'Offset',
|
|
63
|
+
type: { kind: $.TypeKind.Basic, name: 'int64' },
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
)
|
|
67
|
+
|
|
46
68
|
export class InvalidUTF8Error extends jsonError {
|
|
47
69
|
public S: string
|
|
48
70
|
|
|
@@ -145,23 +167,53 @@ export class Decoder {
|
|
|
145
167
|
private disallowUnknownFields = false
|
|
146
168
|
private inputOffset = 0
|
|
147
169
|
private useNumber = false
|
|
170
|
+
// buf holds the fully buffered input; pos is the index of the next unread
|
|
171
|
+
// character, so successive Decode/Token calls consume one value at a time and
|
|
172
|
+
// leave the remainder buffered, like Go's streaming Decoder.
|
|
173
|
+
private buf = ''
|
|
174
|
+
private pos = 0
|
|
175
|
+
private filled = false
|
|
176
|
+
private readErr: $.GoError = null
|
|
148
177
|
|
|
149
178
|
public constructor(private readonly reader: io.Reader) {}
|
|
150
179
|
|
|
151
|
-
|
|
180
|
+
private fill(): void {
|
|
181
|
+
if (this.filled) {
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
this.filled = true
|
|
152
185
|
const [data, err] = readAllSync(this.reader)
|
|
153
186
|
if (err !== null) {
|
|
154
|
-
|
|
187
|
+
this.readErr = err
|
|
188
|
+
return
|
|
155
189
|
}
|
|
156
|
-
this.
|
|
157
|
-
|
|
190
|
+
this.buf = $.bytesToString(data)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public Decode(v: unknown): $.GoError {
|
|
194
|
+
this.fill()
|
|
195
|
+
this.pos = skipJSONWhitespace(this.buf, this.pos)
|
|
196
|
+
if (this.pos >= this.buf.length) {
|
|
197
|
+
return this.readErr ?? $.newError('EOF')
|
|
198
|
+
}
|
|
199
|
+
let end: number
|
|
200
|
+
try {
|
|
201
|
+
;[, end] = scanJSONValue(this.buf, this.pos, this.useNumber)
|
|
202
|
+
} catch (err) {
|
|
203
|
+
return goError(err)
|
|
204
|
+
}
|
|
205
|
+
const raw = $.stringToBytes(this.buf.slice(this.pos, end))
|
|
206
|
+
this.pos = end
|
|
207
|
+
this.inputOffset = jsonByteOffset(this.buf, end)
|
|
208
|
+
return decode(raw, v, {
|
|
158
209
|
disallowUnknownFields: this.disallowUnknownFields,
|
|
159
210
|
useNumber: this.useNumber,
|
|
160
211
|
})
|
|
161
212
|
}
|
|
162
213
|
|
|
163
214
|
public Buffered(): io.Reader {
|
|
164
|
-
|
|
215
|
+
this.fill()
|
|
216
|
+
return bytes.NewBufferString(this.buf.slice(this.pos))!
|
|
165
217
|
}
|
|
166
218
|
|
|
167
219
|
public DisallowUnknownFields(): void {
|
|
@@ -173,11 +225,46 @@ export class Decoder {
|
|
|
173
225
|
}
|
|
174
226
|
|
|
175
227
|
public More(): boolean {
|
|
176
|
-
|
|
228
|
+
this.fill()
|
|
229
|
+
const next = skipJSONWhitespace(this.buf, this.pos)
|
|
230
|
+
if (next >= this.buf.length) {
|
|
231
|
+
return false
|
|
232
|
+
}
|
|
233
|
+
const c = this.buf[next]
|
|
234
|
+
return c !== ']' && c !== '}'
|
|
177
235
|
}
|
|
178
236
|
|
|
179
237
|
public Token(): [Token, $.GoError] {
|
|
180
|
-
|
|
238
|
+
this.fill()
|
|
239
|
+
let cursor = skipJSONWhitespace(this.buf, this.pos)
|
|
240
|
+
// Object/array separators are implicit in Go's token stream; skip them.
|
|
241
|
+
while (cursor < this.buf.length) {
|
|
242
|
+
const sep = this.buf[cursor]
|
|
243
|
+
if (sep === ',' || sep === ':') {
|
|
244
|
+
cursor++
|
|
245
|
+
cursor = skipJSONWhitespace(this.buf, cursor)
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
break
|
|
249
|
+
}
|
|
250
|
+
this.pos = cursor
|
|
251
|
+
if (this.pos >= this.buf.length) {
|
|
252
|
+
return [null, this.readErr ?? $.newError('EOF')]
|
|
253
|
+
}
|
|
254
|
+
const c = this.buf[this.pos]
|
|
255
|
+
if (c === '{' || c === '[' || c === '}' || c === ']') {
|
|
256
|
+
this.pos++
|
|
257
|
+
this.inputOffset = jsonByteOffset(this.buf, this.pos)
|
|
258
|
+
return [c.charCodeAt(0), null]
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
const [value, end] = scanJSONValue(this.buf, this.pos, this.useNumber)
|
|
262
|
+
this.pos = end
|
|
263
|
+
this.inputOffset = jsonByteOffset(this.buf, end)
|
|
264
|
+
return [value, null]
|
|
265
|
+
} catch (err) {
|
|
266
|
+
return [null, goError(err)]
|
|
267
|
+
}
|
|
181
268
|
}
|
|
182
269
|
|
|
183
270
|
public UseNumber(): void {
|
|
@@ -262,6 +349,282 @@ $.registerInterfaceType('json.Unmarshaler', null, [
|
|
|
262
349
|
},
|
|
263
350
|
])
|
|
264
351
|
|
|
352
|
+
// RawJSON wraps a pre-serialized JSON fragment (from a Marshaler or RawMessage)
|
|
353
|
+
// so the encoder can emit its exact token spelling instead of round-tripping it
|
|
354
|
+
// through JSON.parse/stringify, which would normalize numbers like 1e+00 to 1.
|
|
355
|
+
class RawJSON {
|
|
356
|
+
constructor(public raw: string) {}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// stripJSONWhitespace removes insignificant whitespace from a JSON document
|
|
360
|
+
// while leaving string contents and every number/literal token byte-for-byte
|
|
361
|
+
// intact, matching Go's json.Compact.
|
|
362
|
+
function stripJSONWhitespace(text: string): string {
|
|
363
|
+
let out = ''
|
|
364
|
+
let inStr = false
|
|
365
|
+
for (let i = 0; i < text.length; i++) {
|
|
366
|
+
const c = text[i]
|
|
367
|
+
if (inStr) {
|
|
368
|
+
out += c
|
|
369
|
+
if (c === '\\') {
|
|
370
|
+
out += text[++i] ?? ''
|
|
371
|
+
} else if (c === '"') {
|
|
372
|
+
inStr = false
|
|
373
|
+
}
|
|
374
|
+
continue
|
|
375
|
+
}
|
|
376
|
+
if (c === '"') {
|
|
377
|
+
inStr = true
|
|
378
|
+
out += c
|
|
379
|
+
continue
|
|
380
|
+
}
|
|
381
|
+
if (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
|
|
382
|
+
continue
|
|
383
|
+
}
|
|
384
|
+
out += c
|
|
385
|
+
}
|
|
386
|
+
return out
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// indentJSON pretty-prints a compact JSON document, preserving every token
|
|
390
|
+
// spelling, matching Go's json.Indent layout: a space after a colon, newlines
|
|
391
|
+
// with prefix + indent*depth after structural characters, and empty {} / []
|
|
392
|
+
// left inline.
|
|
393
|
+
function indentJSON(compact: string, prefix: string, indent: string): string {
|
|
394
|
+
let out = ''
|
|
395
|
+
let depth = 0
|
|
396
|
+
let inStr = false
|
|
397
|
+
const newline = (d: number) => '\n' + prefix + indent.repeat(d)
|
|
398
|
+
for (let i = 0; i < compact.length; i++) {
|
|
399
|
+
const c = compact[i]
|
|
400
|
+
if (inStr) {
|
|
401
|
+
out += c
|
|
402
|
+
if (c === '\\') {
|
|
403
|
+
out += compact[++i] ?? ''
|
|
404
|
+
} else if (c === '"') {
|
|
405
|
+
inStr = false
|
|
406
|
+
}
|
|
407
|
+
continue
|
|
408
|
+
}
|
|
409
|
+
switch (c) {
|
|
410
|
+
case '"':
|
|
411
|
+
inStr = true
|
|
412
|
+
out += c
|
|
413
|
+
break
|
|
414
|
+
case '{':
|
|
415
|
+
case '[':
|
|
416
|
+
if (compact[i + 1] === '}' || compact[i + 1] === ']') {
|
|
417
|
+
out += c
|
|
418
|
+
} else {
|
|
419
|
+
depth++
|
|
420
|
+
out += c + newline(depth)
|
|
421
|
+
}
|
|
422
|
+
break
|
|
423
|
+
case '}':
|
|
424
|
+
case ']':
|
|
425
|
+
if (compact[i - 1] === '{' || compact[i - 1] === '[') {
|
|
426
|
+
out += c
|
|
427
|
+
} else {
|
|
428
|
+
depth--
|
|
429
|
+
out += newline(depth) + c
|
|
430
|
+
}
|
|
431
|
+
break
|
|
432
|
+
case ',':
|
|
433
|
+
out += c + newline(depth)
|
|
434
|
+
break
|
|
435
|
+
case ':':
|
|
436
|
+
out += ': '
|
|
437
|
+
break
|
|
438
|
+
default:
|
|
439
|
+
out += c
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return out
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// encodeJSON serializes a marshal-prepared value to compact JSON. RawJSON
|
|
446
|
+
// fragments are emitted verbatim; all other values use plain token forms. The
|
|
447
|
+
// caller applies HTML escaping and indentation afterward, matching Go's
|
|
448
|
+
// Marshal-then-indent pipeline.
|
|
449
|
+
function encodeJSON(v: unknown): string {
|
|
450
|
+
if (v instanceof RawJSON) {
|
|
451
|
+
return v.raw
|
|
452
|
+
}
|
|
453
|
+
if (v === null || v === undefined) {
|
|
454
|
+
return 'null'
|
|
455
|
+
}
|
|
456
|
+
const t = typeof v
|
|
457
|
+
if (t === 'boolean') {
|
|
458
|
+
return v ? 'true' : 'false'
|
|
459
|
+
}
|
|
460
|
+
if (t === 'number') {
|
|
461
|
+
return JSON.stringify(v)
|
|
462
|
+
}
|
|
463
|
+
if (t === 'bigint') {
|
|
464
|
+
return (v as bigint).toString()
|
|
465
|
+
}
|
|
466
|
+
if (t === 'string') {
|
|
467
|
+
return JSON.stringify(v)
|
|
468
|
+
}
|
|
469
|
+
if (Array.isArray(v)) {
|
|
470
|
+
return '[' + v.map(encodeJSON).join(',') + ']'
|
|
471
|
+
}
|
|
472
|
+
if (t === 'object') {
|
|
473
|
+
const parts: string[] = []
|
|
474
|
+
for (const [key, value] of Object.entries(v as Record<string, unknown>)) {
|
|
475
|
+
parts.push(JSON.stringify(key) + ':' + encodeJSON(value))
|
|
476
|
+
}
|
|
477
|
+
return '{' + parts.join(',') + '}'
|
|
478
|
+
}
|
|
479
|
+
return JSON.stringify(v)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const JSON_WHITESPACE = ' \t\n\r'
|
|
483
|
+
|
|
484
|
+
// jsonByteOffset returns the Go byte offset (UTF-8) of character index i, so a
|
|
485
|
+
// SyntaxError reports the same Offset Go would for multibyte input.
|
|
486
|
+
function jsonByteOffset(text: string, i: number): number {
|
|
487
|
+
return $.len($.stringToBytes(text.slice(0, i)))
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function jsonSyntaxError(text: string, i: number, msg: string): SyntaxError {
|
|
491
|
+
return new SyntaxError({ Offset: jsonByteOffset(text, i), Message: msg })
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function skipJSONWhitespace(text: string, i: number): number {
|
|
495
|
+
while (i < text.length && JSON_WHITESPACE.includes(text[i])) {
|
|
496
|
+
i++
|
|
497
|
+
}
|
|
498
|
+
return i
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// scanJSONValue parses one JSON value beginning at the first non-whitespace
|
|
502
|
+
// character at or after start, returning the decoded value and the index just
|
|
503
|
+
// past it. Numbers decode to JS numbers, or to their exact json.Number source
|
|
504
|
+
// literal when useNumber is set. Malformed input throws a SyntaxError whose
|
|
505
|
+
// Offset is the Go byte offset of the offending character.
|
|
506
|
+
function scanJSONValue(
|
|
507
|
+
text: string,
|
|
508
|
+
start: number,
|
|
509
|
+
useNumber: boolean,
|
|
510
|
+
): [unknown, number] {
|
|
511
|
+
let i = start
|
|
512
|
+
const skipWs = () => {
|
|
513
|
+
i = skipJSONWhitespace(text, i)
|
|
514
|
+
}
|
|
515
|
+
const fail = (msg = 'invalid character'): never => {
|
|
516
|
+
// Go's Offset counts bytes read up to and including the offending byte.
|
|
517
|
+
const at = Math.min(i + 1, text.length)
|
|
518
|
+
throw jsonSyntaxError(text, at, msg)
|
|
519
|
+
}
|
|
520
|
+
const parseString = (): string => {
|
|
521
|
+
let raw = text[i++] // opening quote
|
|
522
|
+
while (i < text.length) {
|
|
523
|
+
const c = text[i++]
|
|
524
|
+
raw += c
|
|
525
|
+
if (c === '\\') {
|
|
526
|
+
raw += text[i++] ?? ''
|
|
527
|
+
} else if (c === '"') {
|
|
528
|
+
try {
|
|
529
|
+
return JSON.parse(raw) as string
|
|
530
|
+
} catch {
|
|
531
|
+
i -= raw.length
|
|
532
|
+
return fail('invalid string literal')
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return fail('unexpected end of JSON input')
|
|
537
|
+
}
|
|
538
|
+
const parseValue = (): unknown => {
|
|
539
|
+
skipWs()
|
|
540
|
+
if (i >= text.length) {
|
|
541
|
+
return fail('unexpected end of JSON input')
|
|
542
|
+
}
|
|
543
|
+
const c = text[i]
|
|
544
|
+
if (c === '{') {
|
|
545
|
+
i++
|
|
546
|
+
const obj: Record<string, unknown> = {}
|
|
547
|
+
skipWs()
|
|
548
|
+
if (text[i] === '}') {
|
|
549
|
+
i++
|
|
550
|
+
return obj
|
|
551
|
+
}
|
|
552
|
+
for (;;) {
|
|
553
|
+
skipWs()
|
|
554
|
+
if (text[i] !== '"') {
|
|
555
|
+
return fail()
|
|
556
|
+
}
|
|
557
|
+
const key = parseString()
|
|
558
|
+
skipWs()
|
|
559
|
+
if (text[i++] !== ':') {
|
|
560
|
+
i--
|
|
561
|
+
return fail("expected ':' after object key")
|
|
562
|
+
}
|
|
563
|
+
obj[key] = parseValue()
|
|
564
|
+
skipWs()
|
|
565
|
+
const sep = text[i++]
|
|
566
|
+
if (sep === '}') {
|
|
567
|
+
return obj
|
|
568
|
+
}
|
|
569
|
+
if (sep !== ',') {
|
|
570
|
+
i--
|
|
571
|
+
return fail("expected ',' or '}' after object value")
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (c === '[') {
|
|
576
|
+
i++
|
|
577
|
+
const arr: unknown[] = []
|
|
578
|
+
skipWs()
|
|
579
|
+
if (text[i] === ']') {
|
|
580
|
+
i++
|
|
581
|
+
return arr
|
|
582
|
+
}
|
|
583
|
+
for (;;) {
|
|
584
|
+
arr.push(parseValue())
|
|
585
|
+
skipWs()
|
|
586
|
+
const sep = text[i++]
|
|
587
|
+
if (sep === ']') {
|
|
588
|
+
return arr
|
|
589
|
+
}
|
|
590
|
+
if (sep !== ',') {
|
|
591
|
+
i--
|
|
592
|
+
return fail("expected ',' or ']' after array element")
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
if (c === '"') {
|
|
597
|
+
return parseString()
|
|
598
|
+
}
|
|
599
|
+
if (text.startsWith('true', i)) {
|
|
600
|
+
i += 4
|
|
601
|
+
return true
|
|
602
|
+
}
|
|
603
|
+
if (text.startsWith('false', i)) {
|
|
604
|
+
i += 5
|
|
605
|
+
return false
|
|
606
|
+
}
|
|
607
|
+
if (text.startsWith('null', i)) {
|
|
608
|
+
i += 4
|
|
609
|
+
return null
|
|
610
|
+
}
|
|
611
|
+
const numStart = i
|
|
612
|
+
if (c === '-') {
|
|
613
|
+
i++
|
|
614
|
+
}
|
|
615
|
+
while (i < text.length && '0123456789.eE+-'.includes(text[i])) {
|
|
616
|
+
i++
|
|
617
|
+
}
|
|
618
|
+
if (i === numStart) {
|
|
619
|
+
return fail()
|
|
620
|
+
}
|
|
621
|
+
const literal = text.slice(numStart, i)
|
|
622
|
+
return useNumber ? literal : Number(literal)
|
|
623
|
+
}
|
|
624
|
+
const value = parseValue()
|
|
625
|
+
return [value, i]
|
|
626
|
+
}
|
|
627
|
+
|
|
265
628
|
export function Marshal(v: unknown): [$.Slice<number>, $.GoError] {
|
|
266
629
|
return marshalBytes(v, '', '', true)
|
|
267
630
|
}
|
|
@@ -273,15 +636,12 @@ function marshalBytes(
|
|
|
273
636
|
escapeHTML: boolean,
|
|
274
637
|
): [$.Slice<number>, $.GoError] {
|
|
275
638
|
try {
|
|
276
|
-
let text =
|
|
639
|
+
let text = encodeJSON(marshalValue(v))
|
|
277
640
|
if (escapeHTML) {
|
|
278
641
|
text = escapeHTMLString(text)
|
|
279
642
|
}
|
|
280
|
-
if (prefix !== '') {
|
|
281
|
-
text = text
|
|
282
|
-
.split('\n')
|
|
283
|
-
.map((line, idx) => (idx === 0 ? line : prefix + line))
|
|
284
|
-
.join('\n')
|
|
643
|
+
if (indent !== '' || prefix !== '') {
|
|
644
|
+
text = indentJSON(text, prefix, indent)
|
|
285
645
|
}
|
|
286
646
|
return [$.stringToBytes(text), null]
|
|
287
647
|
} catch (err) {
|
|
@@ -294,7 +654,9 @@ export function Compact(
|
|
|
294
654
|
src: $.Slice<number>,
|
|
295
655
|
): $.GoError {
|
|
296
656
|
try {
|
|
297
|
-
const
|
|
657
|
+
const source = $.bytesToString(src)
|
|
658
|
+
JSON.parse(source) // validate; Go's Compact rejects malformed input
|
|
659
|
+
const text = stripJSONWhitespace(source)
|
|
298
660
|
const [, err] = $.pointerValue<bytes.Buffer>(dst).Write(
|
|
299
661
|
$.stringToBytes(text),
|
|
300
662
|
)
|
|
@@ -334,16 +696,14 @@ export function Indent(
|
|
|
334
696
|
indent: string,
|
|
335
697
|
): $.GoError {
|
|
336
698
|
try {
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
.join('\n')
|
|
344
|
-
)
|
|
699
|
+
const source = $.bytesToString(src)
|
|
700
|
+
JSON.parse(source) // validate; Go's Indent rejects malformed input
|
|
701
|
+
const compact = stripJSONWhitespace(source)
|
|
702
|
+
// Go's Indent copies trailing whitespace after the value verbatim.
|
|
703
|
+
const trailing = source.slice(source.trimEnd().length)
|
|
704
|
+
const text = indentJSON(compact, prefix, indent) + trailing
|
|
345
705
|
const [, err] = $.pointerValue<bytes.Buffer>(dst).Write(
|
|
346
|
-
$.stringToBytes(
|
|
706
|
+
$.stringToBytes(text),
|
|
347
707
|
)
|
|
348
708
|
return err
|
|
349
709
|
} catch (err) {
|
|
@@ -506,11 +866,13 @@ function marshalValue(v: unknown): unknown {
|
|
|
506
866
|
if (err !== null) {
|
|
507
867
|
throw new MarshalerError({ Err: err })
|
|
508
868
|
}
|
|
869
|
+
const raw = $.bytesToString(data)
|
|
509
870
|
try {
|
|
510
|
-
|
|
871
|
+
JSON.parse(raw) // validate; preserve exact token spelling below
|
|
511
872
|
} catch (parseErr) {
|
|
512
873
|
throw new MarshalerError({ Err: goError(parseErr) })
|
|
513
874
|
}
|
|
875
|
+
return new RawJSON(stripJSONWhitespace(raw))
|
|
514
876
|
}
|
|
515
877
|
if ($.isVarRef(v)) {
|
|
516
878
|
return marshalValue(v.value)
|
|
@@ -565,11 +927,16 @@ function marshalFieldValue(value: unknown, fieldType: unknown): unknown {
|
|
|
565
927
|
if (target === null || target === undefined) {
|
|
566
928
|
return null
|
|
567
929
|
}
|
|
930
|
+
const raw = $.bytesToString(target as $.Slice<number>)
|
|
931
|
+
if (raw === '') {
|
|
932
|
+
return null
|
|
933
|
+
}
|
|
568
934
|
try {
|
|
569
|
-
|
|
935
|
+
JSON.parse(raw) // validate; preserve exact token spelling below
|
|
570
936
|
} catch (err) {
|
|
571
937
|
throw new MarshalerError({ Err: goError(err) })
|
|
572
938
|
}
|
|
939
|
+
return new RawJSON(stripJSONWhitespace(raw))
|
|
573
940
|
}
|
|
574
941
|
return marshalValue(value)
|
|
575
942
|
}
|
|
@@ -610,12 +977,16 @@ function validUnmarshalTarget(value: unknown): boolean {
|
|
|
610
977
|
|
|
611
978
|
function parseJSON(data: $.Slice<number>, opts: decodeOptions): unknown {
|
|
612
979
|
const text = $.bytesToString(data)
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
980
|
+
const [value, end] = scanJSONValue(text, 0, !!opts.useNumber)
|
|
981
|
+
const rest = skipJSONWhitespace(text, end)
|
|
982
|
+
if (rest !== text.length) {
|
|
983
|
+
throw jsonSyntaxError(
|
|
984
|
+
text,
|
|
985
|
+
rest + 1,
|
|
986
|
+
'invalid character after top-level value',
|
|
616
987
|
)
|
|
617
988
|
}
|
|
618
|
-
return
|
|
989
|
+
return value
|
|
619
990
|
}
|
|
620
991
|
|
|
621
992
|
function assignDecodedValue(
|
package/gs/errors/errors.test.ts
CHANGED
|
@@ -2,7 +2,16 @@ import { describe, expect, it } from 'vitest'
|
|
|
2
2
|
|
|
3
3
|
import * as $ from '@goscript/builtin/index.js'
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
AsType,
|
|
7
|
+
ErrUnsupported,
|
|
8
|
+
Errorf,
|
|
9
|
+
Is,
|
|
10
|
+
Join,
|
|
11
|
+
New,
|
|
12
|
+
Wrap,
|
|
13
|
+
Wrapf,
|
|
14
|
+
} from './errors.js'
|
|
6
15
|
|
|
7
16
|
class DNSError {
|
|
8
17
|
public readonly IsNotFound = true
|
|
@@ -83,3 +92,37 @@ describe('errors github.com/pkg/errors compatibility helpers', () => {
|
|
|
83
92
|
expect(Wrapf(null, 'context %d', 7)).toBe(null)
|
|
84
93
|
})
|
|
85
94
|
})
|
|
95
|
+
|
|
96
|
+
describe('errors.Is identity semantics', () => {
|
|
97
|
+
it('does not match distinct errors with equal text', () => {
|
|
98
|
+
expect(Is(New('boom'), New('boom'))).toBe(false)
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('does not match ErrUnsupported by message text', () => {
|
|
102
|
+
expect(Is(New('unsupported operation'), ErrUnsupported)).toBe(false)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('matches the same error value', () => {
|
|
106
|
+
const e = New('boom')
|
|
107
|
+
expect(Is(e, e)).toBe(true)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('finds a target in any Join position depth-first', () => {
|
|
111
|
+
const a = New('a')
|
|
112
|
+
const b = New('b')
|
|
113
|
+
expect(Is(Join(a, b), b)).toBe(true)
|
|
114
|
+
expect(Is(Join(a, b), a)).toBe(true)
|
|
115
|
+
expect(Is(Join(a, b), New('b'))).toBe(false)
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('matches a wrapped sentinel through Wrap', () => {
|
|
119
|
+
expect(Is(Wrap(ErrUnsupported, 'ctx'), ErrUnsupported)).toBe(true)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('finds a typed error in a later Join position via AsType', () => {
|
|
123
|
+
const dns = $.interfaceValue<$.GoError>(new DNSError(), '*net.DNSError')
|
|
124
|
+
const [matched, ok] = AsType(dnsTypeArgs, Join(New('first'), dns))
|
|
125
|
+
expect(ok).toBe(true)
|
|
126
|
+
expect(matched).toBe(dns)
|
|
127
|
+
})
|
|
128
|
+
})
|
package/gs/errors/errors.ts
CHANGED
|
@@ -123,42 +123,32 @@ export function Is(err: $.GoError, target: $.GoError): boolean {
|
|
|
123
123
|
return false
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
//
|
|
126
|
+
// Match by identity only. Go's errors.Is never matches by message text:
|
|
127
|
+
// two distinct errors.New values with the same string are not equal.
|
|
127
128
|
if (err === target) {
|
|
128
129
|
return true
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
//
|
|
132
|
-
if (err.Error() === target.Error()) {
|
|
133
|
-
return true
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Check if err has an Is method
|
|
132
|
+
// An error may declare equivalence to target via an Is method.
|
|
137
133
|
if (typeof (err as any).Is === 'function') {
|
|
138
134
|
if ((err as any).Is(target)) {
|
|
139
135
|
return true
|
|
140
136
|
}
|
|
141
137
|
}
|
|
142
138
|
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return Is(unwrapped, target)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Handle multiple wrapped errors
|
|
139
|
+
// Depth-first traversal of the wrap tree. A single-error Unwrap recurses into
|
|
140
|
+
// its child; an Unwrap returning []error (errors.Join) visits every child, so
|
|
141
|
+
// a target in any position matches, not just the first.
|
|
150
142
|
if (typeof (err as any).Unwrap === 'function') {
|
|
151
143
|
const result = (err as any).Unwrap()
|
|
152
144
|
if (Array.isArray(result)) {
|
|
153
145
|
for (const wrappedErr of result) {
|
|
154
|
-
if (
|
|
155
|
-
wrappedErr &&
|
|
156
|
-
typeof wrappedErr.Error === 'function' &&
|
|
157
|
-
Is(wrappedErr, target)
|
|
158
|
-
) {
|
|
146
|
+
if (wrappedErr !== null && Is(wrappedErr, target)) {
|
|
159
147
|
return true
|
|
160
148
|
}
|
|
161
149
|
}
|
|
150
|
+
} else if (result !== null && typeof result?.Error === 'function') {
|
|
151
|
+
return Is(result, target)
|
|
162
152
|
}
|
|
163
153
|
}
|
|
164
154
|
|
|
@@ -202,25 +192,19 @@ export function As(err: $.GoError, target: any): boolean {
|
|
|
202
192
|
}
|
|
203
193
|
}
|
|
204
194
|
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return As(unwrapped, target)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Handle multiple wrapped errors
|
|
195
|
+
// Depth-first traversal of the wrap tree, matching errors.Is: a single-error
|
|
196
|
+
// Unwrap recurses into its child; an Unwrap returning []error visits every
|
|
197
|
+
// child so a match in any position is found.
|
|
212
198
|
if (typeof (err as any).Unwrap === 'function') {
|
|
213
199
|
const result = (err as any).Unwrap()
|
|
214
200
|
if (Array.isArray(result)) {
|
|
215
201
|
for (const wrappedErr of result) {
|
|
216
|
-
if (
|
|
217
|
-
wrappedErr &&
|
|
218
|
-
typeof wrappedErr.Error === 'function' &&
|
|
219
|
-
As(wrappedErr, target)
|
|
220
|
-
) {
|
|
202
|
+
if (wrappedErr !== null && As(wrappedErr, target)) {
|
|
221
203
|
return true
|
|
222
204
|
}
|
|
223
205
|
}
|
|
206
|
+
} else if (result !== null && typeof result?.Error === 'function') {
|
|
207
|
+
return As(result, target)
|
|
224
208
|
}
|
|
225
209
|
}
|
|
226
210
|
|