@thomasfosterau/effect-parse 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +202 -0
- package/README.md +56 -14
- package/dist/Ascii.d.ts +11 -4
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +12 -4
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts +5 -2
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +83 -37
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +11 -4
- package/dist/Bits.js.map +1 -1
- package/dist/Compiler.d.ts +74 -2
- package/dist/Compiler.d.ts.map +1 -1
- package/dist/Compiler.js +78 -1
- package/dist/Compiler.js.map +1 -1
- package/dist/Expression.d.ts.map +1 -1
- package/dist/Expression.js +86 -74
- package/dist/Expression.js.map +1 -1
- package/dist/Input.d.ts +56 -5
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +70 -1
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts +5 -15
- package/dist/Nom.d.ts.map +1 -1
- package/dist/Nom.js.map +1 -1
- package/dist/ParseError.d.ts +69 -11
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js +129 -28
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +375 -29
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +394 -37
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.d.ts.map +1 -1
- package/dist/ParserSchema.js +2 -3
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +122 -14
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +293 -62
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.d.ts +23 -5
- package/dist/Syntax.d.ts.map +1 -1
- package/dist/Syntax.js +1 -1
- package/dist/Syntax.js.map +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +21 -15
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +65 -11
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +73 -26
- package/dist/Token.js.map +1 -1
- package/dist/internal/compiler/closure.d.ts +1 -1
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +263 -113
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/entries.d.ts +3 -1
- package/dist/internal/compiler/entries.d.ts.map +1 -1
- package/dist/internal/compiler/entries.js +3 -2
- package/dist/internal/compiler/entries.js.map +1 -1
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +9 -2
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/graph.d.ts +39 -0
- package/dist/internal/compiler/graph.d.ts.map +1 -0
- package/dist/internal/compiler/graph.js +78 -0
- package/dist/internal/compiler/graph.js.map +1 -0
- package/dist/internal/compiler/jit.d.ts +5 -0
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +208 -106
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +11 -3
- package/dist/internal/compiler/registry.d.ts.map +1 -1
- package/dist/internal/compiler/registry.js.map +1 -1
- package/dist/internal/compiler/search.d.ts +32 -0
- package/dist/internal/compiler/search.d.ts.map +1 -0
- package/dist/internal/compiler/search.js +68 -0
- package/dist/internal/compiler/search.js.map +1 -0
- package/dist/internal/compiler/sets.d.ts.map +1 -1
- package/dist/internal/compiler/sets.js +1 -1
- package/dist/internal/compiler/sets.js.map +1 -1
- package/dist/internal/compiler/wasm.d.ts +8 -0
- package/dist/internal/compiler/wasm.d.ts.map +1 -0
- package/dist/internal/compiler/wasm.js +2840 -0
- package/dist/internal/compiler/wasm.js.map +1 -0
- package/dist/internal/compiler/wasmAsm.d.ts +199 -0
- package/dist/internal/compiler/wasmAsm.d.ts.map +1 -0
- package/dist/internal/compiler/wasmAsm.js +427 -0
- package/dist/internal/compiler/wasmAsm.js.map +1 -0
- package/dist/internal/entries.d.ts +3 -1
- package/dist/internal/entries.d.ts.map +1 -1
- package/dist/internal/entries.js.map +1 -1
- package/dist/internal/node.d.ts +77 -3
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +54 -10
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/numbers.d.ts.map +1 -1
- package/dist/internal/numbers.js.map +1 -1
- package/dist/internal/run.d.ts +14 -0
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +53 -11
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +14 -0
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +24 -2
- package/dist/internal/stack.js.map +1 -1
- package/dist/internal/syntax.d.ts +4 -4
- package/dist/internal/syntax.d.ts.map +1 -1
- package/dist/internal/syntax.js +18 -10
- package/dist/internal/syntax.js.map +1 -1
- package/dist/internal/text.d.ts +32 -0
- package/dist/internal/text.d.ts.map +1 -1
- package/dist/internal/text.js +68 -14
- package/dist/internal/text.js.map +1 -1
- package/dist/internal/tokens.d.ts +20 -0
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +72 -39
- package/dist/internal/tokens.js.map +1 -1
- package/dist/internal/tuple.d.ts +6 -0
- package/dist/internal/tuple.d.ts.map +1 -0
- package/dist/internal/tuple.js +170 -0
- package/dist/internal/tuple.js.map +1 -0
- package/package.json +18 -10
- package/src/Ascii.ts +47 -12
- package/src/Binary.ts +174 -73
- package/src/Bits.ts +16 -6
- package/src/Compiler.ts +101 -10
- package/src/Expression.ts +94 -81
- package/src/Input.ts +99 -7
- package/src/Nom.ts +15 -5
- package/src/ParseError.ts +209 -52
- package/src/Parser.ts +684 -143
- package/src/ParserSchema.ts +1 -2
- package/src/ParserStream.ts +361 -68
- package/src/Syntax.ts +60 -36
- package/src/Text.ts +32 -27
- package/src/Token.ts +152 -34
- package/src/internal/compiler/closure.ts +371 -153
- package/src/internal/compiler/entries.ts +4 -2
- package/src/internal/compiler/first.ts +16 -0
- package/src/internal/compiler/graph.ts +96 -0
- package/src/internal/compiler/jit.ts +319 -169
- package/src/internal/compiler/registry.ts +10 -2
- package/src/internal/compiler/search.ts +68 -0
- package/src/internal/compiler/sets.ts +7 -3
- package/src/internal/compiler/wasm.ts +3276 -0
- package/src/internal/compiler/wasmAsm.ts +484 -0
- package/src/internal/entries.ts +8 -2
- package/src/internal/node.ts +103 -7
- package/src/internal/numbers.ts +3 -1
- package/src/internal/run.ts +73 -12
- package/src/internal/stack.ts +25 -1
- package/src/internal/syntax.ts +39 -18
- package/src/internal/text.ts +97 -9
- package/src/internal/tokens.ts +106 -14
- package/src/internal/tuple.ts +141 -0
package/src/Ascii.ts
CHANGED
|
@@ -31,6 +31,7 @@ import type { Parser } from "./Parser.js"
|
|
|
31
31
|
import * as P from "./Parser.js"
|
|
32
32
|
import * as numbers from "./internal/numbers.js"
|
|
33
33
|
import { Key } from "./internal/key.js"
|
|
34
|
+
import { controlCode } from "./internal/node.js"
|
|
34
35
|
import * as Resume from "./internal/resume.js"
|
|
35
36
|
import { codePointPredicate } from "./internal/tokens.js"
|
|
36
37
|
import { literalImpl, range, takeWhileImpl } from "./Token.js"
|
|
@@ -183,8 +184,13 @@ const alnumSet = [/*#__PURE__*/ range("a", "z"), /*#__PURE__*/ range("A", "Z"),
|
|
|
183
184
|
const spaceSet = " \t"
|
|
184
185
|
const multispaceSet = " \t\r\n"
|
|
185
186
|
|
|
186
|
-
const cls = (
|
|
187
|
-
|
|
187
|
+
const cls = (
|
|
188
|
+
set: Token.StringTokenSet,
|
|
189
|
+
min: 0 | 1,
|
|
190
|
+
description: string,
|
|
191
|
+
kind: ErrorKind,
|
|
192
|
+
hint = true
|
|
193
|
+
): Parser<string> => takeWhileImpl<string>(set, { min }, false, `${kind.toLowerCase()}${min}`, description, kind, hint)
|
|
188
194
|
|
|
189
195
|
// -----------------------------------------------------------------------------
|
|
190
196
|
// Character classes
|
|
@@ -359,6 +365,7 @@ export const tillLineEnding: Parser<string> = /*#__PURE__*/ P.described<string>(
|
|
|
359
365
|
const isString = state.inputKind === 0
|
|
360
366
|
let i = start
|
|
361
367
|
while (i < state.end) {
|
|
368
|
+
// SAFETY: `Ascii` parsers run on strings or `Uint8Array`s, and `isString` is `inputKind === 0`, set only for a string
|
|
362
369
|
const c = isString ? (input as string).charCodeAt(i) : (input as Uint8Array)[i]
|
|
363
370
|
if (c === 10) break
|
|
364
371
|
if (c === 13) {
|
|
@@ -368,6 +375,7 @@ export const tillLineEnding: Parser<string> = /*#__PURE__*/ P.described<string>(
|
|
|
368
375
|
if (state.partial) return state.incomplete(1, expectedLineEnding)
|
|
369
376
|
return state.backtrack("LineEnding", expectedLineEnding, i)
|
|
370
377
|
}
|
|
378
|
+
// SAFETY: `Ascii` parsers run on strings or `Uint8Array`s, and `isString` is `inputKind === 0`, set only for a string
|
|
371
379
|
const d = isString ? (input as string).charCodeAt(i + 1) : (input as Uint8Array)[i + 1]
|
|
372
380
|
if (d === 10) break
|
|
373
381
|
return state.backtrack("LineEnding", expectedLineEnding, i)
|
|
@@ -376,6 +384,7 @@ export const tillLineEnding: Parser<string> = /*#__PURE__*/ P.described<string>(
|
|
|
376
384
|
}
|
|
377
385
|
if (i >= state.end && state.partial) return state.incomplete(1, expectedLineEnding)
|
|
378
386
|
state.pos = i
|
|
387
|
+
// SAFETY: on string input, the input type of a `Parser<string>`, the slice is a string (`bytes` retypes it for bytes)
|
|
379
388
|
return state.slice(start, i) as string
|
|
380
389
|
},
|
|
381
390
|
"tillLineEnding",
|
|
@@ -471,7 +480,7 @@ const unsignedFloat = /*#__PURE__*/ P.alt(
|
|
|
471
480
|
const floatText = /*#__PURE__*/ numbers.floatText(
|
|
472
481
|
/*#__PURE__*/ P.taken(
|
|
473
482
|
/*#__PURE__*/ P.alt(/*#__PURE__*/ literalImpl("nan", true), /*#__PURE__*/ P.seq(sign, unsignedFloat))
|
|
474
|
-
).pipe(/*#__PURE__*/ P.expected("number"))
|
|
483
|
+
).pipe(/*#__PURE__*/ P.expected("number"))
|
|
475
484
|
)
|
|
476
485
|
|
|
477
486
|
/** @internal */
|
|
@@ -501,6 +510,17 @@ export const float: Parser<number> = /*#__PURE__*/ floatText.pipe(
|
|
|
501
510
|
// Literals
|
|
502
511
|
// -----------------------------------------------------------------------------
|
|
503
512
|
|
|
513
|
+
/**
|
|
514
|
+
* The overloads of {@link caseless}.
|
|
515
|
+
*
|
|
516
|
+
* @since 0.5.0
|
|
517
|
+
* @category literals
|
|
518
|
+
*/
|
|
519
|
+
export interface CaselessFn {
|
|
520
|
+
(literal: string): Parser<string>
|
|
521
|
+
(literal: Uint8Array): Parser<Uint8Array, never, never, Uint8Array>
|
|
522
|
+
}
|
|
523
|
+
|
|
504
524
|
/**
|
|
505
525
|
* Matches a literal ignoring ASCII case (winnow's `Caseless`, nom's
|
|
506
526
|
* `tag_no_case`). Returns the input as written.
|
|
@@ -508,20 +528,19 @@ export const float: Parser<number> = /*#__PURE__*/ floatText.pipe(
|
|
|
508
528
|
* @since 0.1.0
|
|
509
529
|
* @category literals
|
|
510
530
|
*/
|
|
511
|
-
export const caseless:
|
|
512
|
-
(literal: string): Parser<string>
|
|
513
|
-
(literal: Uint8Array): Parser<Uint8Array, never, never, Uint8Array>
|
|
514
|
-
} = (lit: string | Uint8Array): any => literalImpl(lit, true)
|
|
531
|
+
export const caseless: CaselessFn = (lit: string | Uint8Array): any => literalImpl(lit, true)
|
|
515
532
|
|
|
516
533
|
// -----------------------------------------------------------------------------
|
|
517
534
|
// Escapes
|
|
518
535
|
// -----------------------------------------------------------------------------
|
|
519
536
|
|
|
520
|
-
|
|
537
|
+
/** The first UTF-16 unit of a control character: what `atControl` compares with a string's units. */
|
|
538
|
+
const firstUnit = (s: string): number => s.charCodeAt(0)
|
|
521
539
|
|
|
522
540
|
const atControl = (state: State<Input>, control: number): boolean => {
|
|
523
541
|
if (state.pos >= state.end) return false
|
|
524
542
|
const input = state.input
|
|
543
|
+
// SAFETY: escapes are read from strings or `Uint8Array`s, and `inputKind` 0 is set only for a string
|
|
525
544
|
const c = state.inputKind === 0 ? (input as string).charCodeAt(state.pos) : (input as Uint8Array)[state.pos]
|
|
526
545
|
return c === control
|
|
527
546
|
}
|
|
@@ -550,7 +569,7 @@ export const takeEscaped = <A, E, R, I extends Input, B, E2, R2, I2 extends Inpu
|
|
|
550
569
|
control: string | number,
|
|
551
570
|
escapable: Parser<B, E2, R2, I2>
|
|
552
571
|
): Parser<Slice<I & I2>, E | E2, R | R2, I & I2> => {
|
|
553
|
-
const ctrl = controlCode(control)
|
|
572
|
+
const ctrl = controlCode(control, firstUnit)
|
|
554
573
|
return takeEscapedImpl(
|
|
555
574
|
normal,
|
|
556
575
|
(state) => {
|
|
@@ -559,6 +578,7 @@ export const takeEscaped = <A, E, R, I extends Input, B, E2, R2, I2 extends Inpu
|
|
|
559
578
|
return true
|
|
560
579
|
},
|
|
561
580
|
escapable,
|
|
581
|
+
// SAFETY: the parser runs on input of type `I & I2`, and `State.slice` returns that input's own slice type
|
|
562
582
|
(state, start) => state.slice(start, state.pos) as Slice<I & I2>,
|
|
563
583
|
ctrl,
|
|
564
584
|
undefined
|
|
@@ -593,9 +613,11 @@ export const takeEscapedImpl = <A, E, R, I extends Input, B, E2, R2, I2 extends
|
|
|
593
613
|
const start = state.pos
|
|
594
614
|
let resumed = false
|
|
595
615
|
if (state.resume !== undefined) {
|
|
616
|
+
// SAFETY: under its own `key`, this parser saves only a resume offset (`suspendAt`) or a `Done`
|
|
596
617
|
const checkpoint = Resume.take(state, key, start) as number | Resume.Done | undefined
|
|
597
618
|
if (checkpoint instanceof Resume.Done) {
|
|
598
619
|
state.pos = checkpoint.pos
|
|
620
|
+
// SAFETY: a `Done` saved under this parser's `key` holds the `S` it returned
|
|
599
621
|
return checkpoint.value as S
|
|
600
622
|
}
|
|
601
623
|
if (checkpoint !== undefined) {
|
|
@@ -664,7 +686,7 @@ export const escaped = <E, R, I extends Input, E2, R2, I2 extends Input>(
|
|
|
664
686
|
control: string | number,
|
|
665
687
|
escape: Parser<string, E2, R2, I2>
|
|
666
688
|
): Parser<string, E | E2, R | R2, I & I2> => {
|
|
667
|
-
const ctrl = controlCode(control)
|
|
689
|
+
const ctrl = controlCode(control, firstUnit)
|
|
668
690
|
return escapedImpl(
|
|
669
691
|
normal,
|
|
670
692
|
(state) => {
|
|
@@ -701,9 +723,11 @@ export const escapedImpl = <E, R, I extends Input, E2, R2, I2 extends Input>(
|
|
|
701
723
|
let out = ""
|
|
702
724
|
let resumed = false
|
|
703
725
|
if (state.resume !== undefined) {
|
|
726
|
+
// SAFETY: under its own `key`, this parser saves only an `EscapedCheckpoint` (`suspendAt`) or a `Done`
|
|
704
727
|
const checkpoint = Resume.take(state, key, start) as EscapedCheckpoint | Resume.Done | undefined
|
|
705
728
|
if (checkpoint instanceof Resume.Done) {
|
|
706
729
|
state.pos = checkpoint.pos
|
|
730
|
+
// SAFETY: a `Done` saved under this parser's `key` holds the string it returned
|
|
707
731
|
return checkpoint.value as string
|
|
708
732
|
}
|
|
709
733
|
if (checkpoint !== undefined) {
|
|
@@ -754,6 +778,14 @@ type Bytes<P> =
|
|
|
754
778
|
? Parser<A extends string ? Uint8Array : A, E, R, Uint8Array>
|
|
755
779
|
: never
|
|
756
780
|
|
|
781
|
+
/**
|
|
782
|
+
* Gives a value the type of another view of the same parsers (see
|
|
783
|
+
* {@link bytes}).
|
|
784
|
+
*/
|
|
785
|
+
const retype = <T>(value: unknown): T =>
|
|
786
|
+
// SAFETY: callers pass the byte views' parsers, which are the string parsers themselves (below)
|
|
787
|
+
value as T
|
|
788
|
+
|
|
757
789
|
/**
|
|
758
790
|
* The parsers of this module, typed for `Uint8Array` input. Slices are
|
|
759
791
|
* returned as `Uint8Array`s.
|
|
@@ -803,7 +835,7 @@ export const bytes: {
|
|
|
803
835
|
const lit = (s: string) => Token.literal(new TextEncoder().encode(s))
|
|
804
836
|
const crlfB = lit("\r\n")
|
|
805
837
|
const newlineB = lit("\n")
|
|
806
|
-
|
|
838
|
+
const views = {
|
|
807
839
|
alpha0,
|
|
808
840
|
alpha1,
|
|
809
841
|
digit0,
|
|
@@ -829,5 +861,8 @@ export const bytes: {
|
|
|
829
861
|
decBigInt,
|
|
830
862
|
hexUint,
|
|
831
863
|
float
|
|
832
|
-
}
|
|
864
|
+
}
|
|
865
|
+
// SAFETY: the string parsers run on byte input unchanged (their slices of a `Uint8Array` are `Uint8Array`s),
|
|
866
|
+
// so only their declared input and slice types differ from the byte views'
|
|
867
|
+
return retype<typeof bytes>(views)
|
|
833
868
|
})()
|
package/src/Binary.ts
CHANGED
|
@@ -44,45 +44,61 @@ export type Endianness = "big" | "little" | "native"
|
|
|
44
44
|
|
|
45
45
|
const nativeLittle = /*#__PURE__*/ new Uint8Array(/*#__PURE__*/ new Uint16Array([1]).buffer)[0] === 1
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
/** Reads a number at an offset of a state's bytes. */
|
|
48
|
+
type Read<A> = (bytes: Uint8Array, offset: number, state: State<Uint8Array>) => A
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A `DataView` of a state's bytes, for the numbers integer arithmetic can't
|
|
52
|
+
* read: created once per state (whose input never changes), as one per
|
|
53
|
+
* read cost most of a number's time.
|
|
54
|
+
*/
|
|
55
|
+
const viewOf = (state: State<Uint8Array>): DataView => {
|
|
56
|
+
const b = state.input
|
|
57
|
+
return (state.view ??= new DataView(b.buffer, b.byteOffset, b.byteLength))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** A {@link Read} through the state's view. */
|
|
61
|
+
const viewed =
|
|
62
|
+
<A>(read: (view: DataView, offset: number) => A): Read<A> =>
|
|
63
|
+
(_, o, s) =>
|
|
64
|
+
read(viewOf(s), o)
|
|
65
|
+
|
|
66
|
+
const fixed = <A>(size: number, name: string, read: Read<A>): Parser<A, never, never, Uint8Array> => {
|
|
52
67
|
const expected = [`${size} byte${size === 1 ? "" : "s"}`]
|
|
53
|
-
return P.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
return P.described(
|
|
69
|
+
(state: State<Uint8Array>) => {
|
|
70
|
+
const pos = state.pos
|
|
71
|
+
const avail = state.end - pos
|
|
72
|
+
if (avail < size) return state.eoi("Token", expected, size - avail)
|
|
73
|
+
state.pos = pos + size
|
|
74
|
+
return read(state.input, pos, state)
|
|
75
|
+
},
|
|
76
|
+
name,
|
|
77
|
+
{ _tag: "Fixed", size, read }
|
|
78
|
+
)
|
|
62
79
|
}
|
|
63
80
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
// Integers of up to 32 bits are read from the bytes; the rest through a view
|
|
82
|
+
const readU24 =
|
|
83
|
+
(little: boolean): Read<number> =>
|
|
84
|
+
(b, o) =>
|
|
85
|
+
little ? b[o] | (b[o + 1] << 8) | (b[o + 2] << 16) : (b[o] << 16) | (b[o + 1] << 8) | b[o + 2]
|
|
68
86
|
|
|
69
|
-
const readI24 = (little: boolean) => {
|
|
87
|
+
const readI24 = (little: boolean): Read<number> => {
|
|
70
88
|
const u = readU24(little)
|
|
71
|
-
return (
|
|
72
|
-
const x = u(v, o)
|
|
73
|
-
return x & 0x800000 ? x - 0x1000000 : x
|
|
74
|
-
}
|
|
89
|
+
return (b, o, s) => (u(b, o, s) << 8) >> 8
|
|
75
90
|
}
|
|
76
91
|
|
|
77
|
-
const readU128 = (little: boolean)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
92
|
+
const readU128 = (little: boolean): Read<bigint> =>
|
|
93
|
+
viewed((v, o) => {
|
|
94
|
+
const a = v.getBigUint64(o, little)
|
|
95
|
+
const b = v.getBigUint64(o + 8, little)
|
|
96
|
+
return little ? (b << 64n) | a : (a << 64n) | b
|
|
97
|
+
})
|
|
82
98
|
|
|
83
|
-
const readI128 = (little: boolean) => {
|
|
99
|
+
const readI128 = (little: boolean): Read<bigint> => {
|
|
84
100
|
const u = readU128(little)
|
|
85
|
-
return (
|
|
101
|
+
return (b, o, s) => BigInt.asIntN(128, u(b, o, s))
|
|
86
102
|
}
|
|
87
103
|
|
|
88
104
|
/**
|
|
@@ -91,7 +107,7 @@ const readI128 = (little: boolean) => {
|
|
|
91
107
|
* @since 0.1.0
|
|
92
108
|
* @category integers
|
|
93
109
|
*/
|
|
94
|
-
export const u8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(1, "u8", (
|
|
110
|
+
export const u8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(1, "u8", (b, o) => b[o])
|
|
95
111
|
|
|
96
112
|
/**
|
|
97
113
|
* A signed byte.
|
|
@@ -99,23 +115,31 @@ export const u8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
|
99
115
|
* @since 0.1.0
|
|
100
116
|
* @category integers
|
|
101
117
|
*/
|
|
102
|
-
export const i8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(1, "i8", (
|
|
118
|
+
export const i8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(1, "i8", (b, o) => (b[o] << 24) >> 24)
|
|
103
119
|
|
|
104
120
|
/** @since 0.1.0 @category integers */
|
|
105
|
-
export const beU16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
106
|
-
|
|
121
|
+
export const beU16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
122
|
+
2,
|
|
123
|
+
"beU16",
|
|
124
|
+
(b, o) => (b[o] << 8) | b[o + 1]
|
|
107
125
|
)
|
|
108
126
|
/** @since 0.1.0 @category integers */
|
|
109
|
-
export const leU16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
110
|
-
|
|
127
|
+
export const leU16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
128
|
+
2,
|
|
129
|
+
"leU16",
|
|
130
|
+
(b, o) => b[o] | (b[o + 1] << 8)
|
|
111
131
|
)
|
|
112
132
|
/** @since 0.1.0 @category integers */
|
|
113
|
-
export const beI16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
114
|
-
|
|
133
|
+
export const beI16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
134
|
+
2,
|
|
135
|
+
"beI16",
|
|
136
|
+
(b, o) => (((b[o] << 8) | b[o + 1]) << 16) >> 16
|
|
115
137
|
)
|
|
116
138
|
/** @since 0.1.0 @category integers */
|
|
117
|
-
export const leI16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
118
|
-
|
|
139
|
+
export const leI16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
140
|
+
2,
|
|
141
|
+
"leI16",
|
|
142
|
+
(b, o) => ((b[o] | (b[o + 1] << 8)) << 16) >> 16
|
|
119
143
|
)
|
|
120
144
|
/** @since 0.1.0 @category integers */
|
|
121
145
|
export const beU24: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
@@ -142,36 +166,52 @@ export const leI24: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fix
|
|
|
142
166
|
/*#__PURE__*/ readI24(true)
|
|
143
167
|
)
|
|
144
168
|
/** @since 0.1.0 @category integers */
|
|
145
|
-
export const beU32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
146
|
-
|
|
169
|
+
export const beU32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
170
|
+
4,
|
|
171
|
+
"beU32",
|
|
172
|
+
(b, o) => ((b[o] << 24) | (b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]) >>> 0
|
|
147
173
|
)
|
|
148
174
|
/** @since 0.1.0 @category integers */
|
|
149
|
-
export const leU32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
150
|
-
|
|
175
|
+
export const leU32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
176
|
+
4,
|
|
177
|
+
"leU32",
|
|
178
|
+
(b, o) => (b[o] | (b[o + 1] << 8) | (b[o + 2] << 16) | (b[o + 3] << 24)) >>> 0
|
|
151
179
|
)
|
|
152
180
|
/** @since 0.1.0 @category integers */
|
|
153
|
-
export const beI32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
154
|
-
|
|
181
|
+
export const beI32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
182
|
+
4,
|
|
183
|
+
"beI32",
|
|
184
|
+
(b, o) => (b[o] << 24) | (b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]
|
|
155
185
|
)
|
|
156
186
|
/** @since 0.1.0 @category integers */
|
|
157
|
-
export const leI32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
158
|
-
|
|
187
|
+
export const leI32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
188
|
+
4,
|
|
189
|
+
"leI32",
|
|
190
|
+
(b, o) => b[o] | (b[o + 1] << 8) | (b[o + 2] << 16) | (b[o + 3] << 24)
|
|
159
191
|
)
|
|
160
192
|
/** @since 0.1.0 @category integers */
|
|
161
|
-
export const beU64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
162
|
-
|
|
193
|
+
export const beU64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
194
|
+
8,
|
|
195
|
+
"beU64",
|
|
196
|
+
/*#__PURE__*/ viewed((v, o) => v.getBigUint64(o, false))
|
|
163
197
|
)
|
|
164
198
|
/** @since 0.1.0 @category integers */
|
|
165
|
-
export const leU64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
166
|
-
|
|
199
|
+
export const leU64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
200
|
+
8,
|
|
201
|
+
"leU64",
|
|
202
|
+
/*#__PURE__*/ viewed((v, o) => v.getBigUint64(o, true))
|
|
167
203
|
)
|
|
168
204
|
/** @since 0.1.0 @category integers */
|
|
169
|
-
export const beI64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
170
|
-
|
|
205
|
+
export const beI64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
206
|
+
8,
|
|
207
|
+
"beI64",
|
|
208
|
+
/*#__PURE__*/ viewed((v, o) => v.getBigInt64(o, false))
|
|
171
209
|
)
|
|
172
210
|
/** @since 0.1.0 @category integers */
|
|
173
|
-
export const leI64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
174
|
-
|
|
211
|
+
export const leI64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
212
|
+
8,
|
|
213
|
+
"leI64",
|
|
214
|
+
/*#__PURE__*/ viewed((v, o) => v.getBigInt64(o, true))
|
|
175
215
|
)
|
|
176
216
|
/** @since 0.1.0 @category integers */
|
|
177
217
|
export const beU128: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
@@ -199,20 +239,28 @@ export const leI128: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fi
|
|
|
199
239
|
)
|
|
200
240
|
|
|
201
241
|
/** @since 0.1.0 @category floats */
|
|
202
|
-
export const beF32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
203
|
-
|
|
242
|
+
export const beF32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
243
|
+
4,
|
|
244
|
+
"beF32",
|
|
245
|
+
/*#__PURE__*/ viewed((v, o) => v.getFloat32(o, false))
|
|
204
246
|
)
|
|
205
247
|
/** @since 0.1.0 @category floats */
|
|
206
|
-
export const leF32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
207
|
-
|
|
248
|
+
export const leF32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
249
|
+
4,
|
|
250
|
+
"leF32",
|
|
251
|
+
/*#__PURE__*/ viewed((v, o) => v.getFloat32(o, true))
|
|
208
252
|
)
|
|
209
253
|
/** @since 0.1.0 @category floats */
|
|
210
|
-
export const beF64: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
211
|
-
|
|
254
|
+
export const beF64: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
255
|
+
8,
|
|
256
|
+
"beF64",
|
|
257
|
+
/*#__PURE__*/ viewed((v, o) => v.getFloat64(o, false))
|
|
212
258
|
)
|
|
213
259
|
/** @since 0.1.0 @category floats */
|
|
214
|
-
export const leF64: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
215
|
-
|
|
260
|
+
export const leF64: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
261
|
+
8,
|
|
262
|
+
"leF64",
|
|
263
|
+
/*#__PURE__*/ viewed((v, o) => v.getFloat64(o, true))
|
|
216
264
|
)
|
|
217
265
|
|
|
218
266
|
const byEndian =
|
|
@@ -286,11 +334,46 @@ export const f64: (endian: Endianness) => Parser<number, never, never, Uint8Arra
|
|
|
286
334
|
leF64
|
|
287
335
|
)
|
|
288
336
|
|
|
337
|
+
const MaxCount = /*#__PURE__*/ BigInt(Number.MAX_SAFE_INTEGER)
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Whether a length read from the input is a count: a non-negative integer
|
|
341
|
+
* of at most `Number.MAX_SAFE_INTEGER`. A signed, fractional or `NaN`
|
|
342
|
+
* length is the input's to choose, so it fails the parse where it was read
|
|
343
|
+
* (as `Parser.filter` does) and never reaches `Token.take` or
|
|
344
|
+
* `Parser.repeat`, which throw on one.
|
|
345
|
+
*/
|
|
346
|
+
const isCount = (n: number | bigint): boolean =>
|
|
347
|
+
Predicate.isBigInt(n) ? n >= 0n && n <= MaxCount : Number.isSafeInteger(n) && n >= 0
|
|
348
|
+
|
|
289
349
|
const toCount = (n: number | bigint): number => (Predicate.isBigInt(n) ? Number(n) : n)
|
|
290
350
|
|
|
351
|
+
/** How many counts {@link byCount} keeps the parsers of. */
|
|
352
|
+
const CountCache = 256
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* `build`, keeping the parsers it built for the first {@link CountCache}
|
|
356
|
+
* counts seen: length-prefixed data repeats a few lengths, and building a
|
|
357
|
+
* parser per value cost several times the parse.
|
|
358
|
+
*/
|
|
359
|
+
const byCount = <A>(build: (n: number) => A): ((n: number | bigint) => A) => {
|
|
360
|
+
const built = new Map<number, A>()
|
|
361
|
+
return (n) => {
|
|
362
|
+
const count = toCount(n)
|
|
363
|
+
let a = built.get(count)
|
|
364
|
+
if (a === undefined) {
|
|
365
|
+
a = build(count)
|
|
366
|
+
if (built.size < CountCache) built.set(count, a)
|
|
367
|
+
}
|
|
368
|
+
return a
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
291
372
|
/**
|
|
292
373
|
* Parses a length with `count`, then takes that many tokens (winnow's
|
|
293
|
-
* `length_take`, nom's `length_data`).
|
|
374
|
+
* `length_take`, nom's `length_data`). A length that is not a non-negative
|
|
375
|
+
* integer (a negative `i8`, a fractional float) fails the parse at the
|
|
376
|
+
* length.
|
|
294
377
|
*
|
|
295
378
|
* @since 0.1.0
|
|
296
379
|
* @category length-prefixed
|
|
@@ -298,17 +381,31 @@ const toCount = (n: number | bigint): number => (Predicate.isBigInt(n) ? Number(
|
|
|
298
381
|
export const lengthTake = <E, R, I extends Input = Uint8Array>(
|
|
299
382
|
count: Parser<number | bigint, E, R, I>
|
|
300
383
|
): Parser<Slice<I>, E, R, I> => {
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
384
|
+
const take = byCount((n) => Token.take<I>(n).step)
|
|
385
|
+
// The count compiles with the grammar: the step is built from its (see `Parser.combinator`)
|
|
386
|
+
return P.combinator(
|
|
387
|
+
[count],
|
|
388
|
+
([countStep]) =>
|
|
389
|
+
(state: State<I>) => {
|
|
390
|
+
const start = state.pos
|
|
391
|
+
// SAFETY: the count parser's step, interpreted or compiled, returns its output (a number or bigint) or `Fail`
|
|
392
|
+
const n = countStep(state) as number | bigint | Fail
|
|
393
|
+
if (n === Fail) return Fail
|
|
394
|
+
// As `Parser.filter` fails: at the length, backtracking
|
|
395
|
+
if (!isCount(n)) {
|
|
396
|
+
state.rewind(start)
|
|
397
|
+
return state.backtrack("Verify")
|
|
398
|
+
}
|
|
399
|
+
return take(n)(state)
|
|
400
|
+
},
|
|
401
|
+
"lengthTake"
|
|
402
|
+
)
|
|
307
403
|
}
|
|
308
404
|
|
|
309
405
|
/**
|
|
310
406
|
* Parses a length with `count`, then applies `parser` that many times
|
|
311
|
-
* (winnow's `length_repeat`, nom's `length_count`).
|
|
407
|
+
* (winnow's `length_repeat`, nom's `length_count`). A length that is not a
|
|
408
|
+
* non-negative integer fails the parse at the length.
|
|
312
409
|
*
|
|
313
410
|
* @since 0.1.0
|
|
314
411
|
* @category length-prefixed
|
|
@@ -317,7 +414,10 @@ export const lengthRepeat = <E, R, I extends Input, A, E2, R2, I2 extends Input>
|
|
|
317
414
|
count: Parser<number | bigint, E, R, I>,
|
|
318
415
|
parser: Parser<A, E2, R2, I2>
|
|
319
416
|
): Parser<Array<A>, E | E2, R | R2, I & I2> =>
|
|
320
|
-
P.flatMap(
|
|
417
|
+
P.flatMap(
|
|
418
|
+
P.filter(count, isCount),
|
|
419
|
+
byCount((n) => P.repeat(parser, n))
|
|
420
|
+
)
|
|
321
421
|
|
|
322
422
|
/**
|
|
323
423
|
* Parses a length with `count`, takes that many tokens, and applies
|
|
@@ -331,4 +431,5 @@ export const lengthAndThen = <E, R, I extends Input, A, E2, R2>(
|
|
|
331
431
|
count: Parser<number | bigint, E, R, I>,
|
|
332
432
|
parser: Parser<A, E2, R2, Slice<I> & Input>
|
|
333
433
|
): Parser<A, E | E2, R | R2, I> =>
|
|
434
|
+
// SAFETY: a slice of an input is itself an input (a string, a `Uint8Array` or an array), so `Slice<I>` is an `Input`
|
|
334
435
|
P.mapParser(lengthTake(count) as Parser<Slice<I> & Input, E, R, I>, P.complete(parser))
|
package/src/Bits.ts
CHANGED
|
@@ -24,6 +24,7 @@ import type { State } from "./Input.js"
|
|
|
24
24
|
import { Fail, Failure } from "./Input.js"
|
|
25
25
|
import type { Parser } from "./Parser.js"
|
|
26
26
|
import { make } from "./Parser.js"
|
|
27
|
+
import { checkCount } from "./internal/node.js"
|
|
27
28
|
|
|
28
29
|
declare const BitInputBrand: unique symbol
|
|
29
30
|
|
|
@@ -93,7 +94,7 @@ const inUnits = <A>(state: State<any>, factor: number, step: (state: State<any>)
|
|
|
93
94
|
* @category combinators
|
|
94
95
|
*/
|
|
95
96
|
export const bits = <A, E, R>(self: Parser<A, E, R, BitInput>): Parser<A, E, R, Uint8Array> => {
|
|
96
|
-
const step = self.step
|
|
97
|
+
const step = self.step
|
|
97
98
|
return make((state) => inUnits(state, 8, step), "bits")
|
|
98
99
|
}
|
|
99
100
|
|
|
@@ -105,18 +106,24 @@ export const bits = <A, E, R>(self: Parser<A, E, R, BitInput>): Parser<A, E, R,
|
|
|
105
106
|
* @category combinators
|
|
106
107
|
*/
|
|
107
108
|
export const bytes = <A, E, R>(self: Parser<A, E, R, Uint8Array>): Parser<A, E, R, BitInput> => {
|
|
108
|
-
const step = self.step
|
|
109
|
+
const step = self.step
|
|
109
110
|
return make((state: State<any>) => inUnits(state, 1 / 8, step), "bytes")
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
const readBits = (state: State<any>, count: number): number => {
|
|
114
|
+
// SAFETY: bit-level parsers run inside `bits`, on the `Uint8Array` it was given (`BitInput` only re-indexes it in bits)
|
|
113
115
|
const input = state.input as Uint8Array
|
|
114
116
|
let pos = state.pos
|
|
115
117
|
let value = 0
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
pos
|
|
118
|
+
let left = count
|
|
119
|
+
// As many bits as the current byte holds at once, not one at a time
|
|
120
|
+
while (left > 0) {
|
|
121
|
+
const avail = 8 - (pos & 7)
|
|
122
|
+
const n = left < avail ? left : avail
|
|
123
|
+
// Multiplying, not shifting: the value can have up to 53 bits
|
|
124
|
+
value = value * (1 << n) + ((input[pos >>> 3] >>> (avail - n)) & ((1 << n) - 1))
|
|
125
|
+
pos += n
|
|
126
|
+
left -= n
|
|
120
127
|
}
|
|
121
128
|
state.pos = pos
|
|
122
129
|
return value
|
|
@@ -130,6 +137,7 @@ const readBits = (state: State<any>, count: number): number => {
|
|
|
130
137
|
* @category parsers
|
|
131
138
|
*/
|
|
132
139
|
export const take = (count: number): Parser<number, never, never, BitInput> => {
|
|
140
|
+
checkCount(count)
|
|
133
141
|
if (count > 53) throw new RangeError("Bits.take: use Bits.takeBig for more than 53 bits")
|
|
134
142
|
const expected = [`${count} bits`]
|
|
135
143
|
return make((state: State<any>) => {
|
|
@@ -146,6 +154,7 @@ export const take = (count: number): Parser<number, never, never, BitInput> => {
|
|
|
146
154
|
* @category parsers
|
|
147
155
|
*/
|
|
148
156
|
export const takeBig = (count: number): Parser<bigint, never, never, BitInput> => {
|
|
157
|
+
checkCount(count)
|
|
149
158
|
const expected = [`${count} bits`]
|
|
150
159
|
return make((state: State<any>) => {
|
|
151
160
|
const avail = state.end - state.pos
|
|
@@ -169,6 +178,7 @@ export const takeBig = (count: number): Parser<bigint, never, never, BitInput> =
|
|
|
169
178
|
* @category parsers
|
|
170
179
|
*/
|
|
171
180
|
export const pattern = (pattern: number, count: number): Parser<number, never, never, BitInput> => {
|
|
181
|
+
checkCount(count)
|
|
172
182
|
const expected = [`bit pattern ${pattern.toString(2).padStart(count, "0")}`]
|
|
173
183
|
return make((state: State<any>) => {
|
|
174
184
|
const start = state.pos
|