@thomasfosterau/effect-parse 0.5.0 → 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 +19 -1
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +5 -3
- 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 +20 -3
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +4 -0
- package/dist/Bits.js.map +1 -1
- package/dist/Input.d.ts +21 -3
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +31 -4
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts.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 +10 -4
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +10 -24
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.d.ts.map +1 -1
- package/dist/ParserSchema.js +1 -2
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +119 -12
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +282 -55
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.js +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +13 -7
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +10 -1
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +14 -5
- 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 +7 -17
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +2 -1
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/graph.d.ts +23 -1
- package/dist/internal/compiler/graph.d.ts.map +1 -1
- package/dist/internal/compiler/graph.js +27 -5
- package/dist/internal/compiler/graph.js.map +1 -1
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +26 -13
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +1 -1
- package/dist/internal/compiler/wasm.d.ts.map +1 -1
- package/dist/internal/compiler/wasm.js +105 -34
- package/dist/internal/compiler/wasm.js.map +1 -1
- package/dist/internal/node.d.ts +19 -1
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +42 -8
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/run.d.ts +3 -1
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +4 -2
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +8 -0
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +17 -1
- package/dist/internal/stack.js.map +1 -1
- package/dist/internal/tokens.d.ts +10 -3
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +22 -9
- 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 +17 -10
- package/src/Ascii.ts +5 -3
- package/src/Binary.ts +24 -3
- package/src/Bits.ts +4 -0
- package/src/Input.ts +37 -4
- package/src/ParseError.ts +206 -52
- package/src/Parser.ts +19 -29
- package/src/ParserSchema.ts +1 -2
- package/src/ParserStream.ts +336 -62
- package/src/Text.ts +16 -12
- package/src/Token.ts +14 -5
- package/src/internal/compiler/closure.ts +9 -18
- package/src/internal/compiler/first.ts +6 -0
- package/src/internal/compiler/graph.ts +32 -4
- package/src/internal/compiler/jit.ts +37 -17
- package/src/internal/compiler/wasm.ts +141 -68
- package/src/internal/node.ts +49 -3
- package/src/internal/run.ts +4 -2
- package/src/internal/stack.ts +17 -0
- package/src/internal/tokens.ts +20 -10
- package/src/internal/tuple.ts +141 -0
package/src/internal/node.ts
CHANGED
|
@@ -197,13 +197,59 @@ export const describedNode = (p: ParserLike): Node | undefined =>
|
|
|
197
197
|
/** The description of a parser: `Opaque` when it has none. */
|
|
198
198
|
export const nodeOf = (p: ParserLike): Node => describedNode(p) ?? { _tag: "Opaque", step: p.step }
|
|
199
199
|
|
|
200
|
-
/**
|
|
200
|
+
/** How a rejected argument reads in an error: never the argument's own text. */
|
|
201
|
+
const describeArgument = (u: unknown): string =>
|
|
202
|
+
Predicate.isNumber(u) || Predicate.isBigInt(u) || u === undefined || u === null
|
|
203
|
+
? String(u)
|
|
204
|
+
: Predicate.isString(u)
|
|
205
|
+
? "a string"
|
|
206
|
+
: "a value that is not a number"
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* `count` as given, which must be a non-negative safe integer, else a
|
|
210
|
+
* `RangeError`. A count is a `number` only in the types, and each backend
|
|
211
|
+
* read a negative, fractional, `NaN` or non-number count its own way: the
|
|
212
|
+
* interpreter moved the cursor backwards, and the JIT wrote the value into
|
|
213
|
+
* the source it compiles.
|
|
214
|
+
*/
|
|
215
|
+
export const checkCount = (count: number, what: string = "count"): number => {
|
|
216
|
+
if (!Number.isSafeInteger(count) || count < 0)
|
|
217
|
+
throw new RangeError(`invalid ${what}: expected a non-negative integer, got ${describeArgument(count)}`)
|
|
218
|
+
return count
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Normalizes a range, as `repeat`, `takeWhile`, ... do. Throws a
|
|
223
|
+
* `RangeError` unless both bounds are non-negative integers (`max` may be
|
|
224
|
+
* `Infinity`) with `min <= max` (see {@link checkCount}).
|
|
225
|
+
*/
|
|
201
226
|
export const bounds = (
|
|
202
227
|
range: number | { readonly min?: number | undefined; readonly max?: number | undefined } | undefined
|
|
203
228
|
): Bounds => {
|
|
204
229
|
if (range === undefined) return { min: 0, max: Infinity }
|
|
205
|
-
if (Predicate.
|
|
206
|
-
|
|
230
|
+
if (!Predicate.isObject(range)) {
|
|
231
|
+
const count = checkCount(range)
|
|
232
|
+
return { min: count, max: count }
|
|
233
|
+
}
|
|
234
|
+
const min = checkCount(range.min ?? 0, "range: min")
|
|
235
|
+
const max = range.max ?? Infinity
|
|
236
|
+
if (max !== Infinity) checkCount(max, "range: max")
|
|
237
|
+
if (min > max) throw new RangeError(`invalid range: min (${min}) > max (${max})`)
|
|
238
|
+
return { min, max }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* The code of a control character given as a one-character string or a
|
|
243
|
+
* code: a non-negative integer, else a `RangeError` (it is compared with
|
|
244
|
+
* input units, and the JIT writes it into the source it compiles).
|
|
245
|
+
*/
|
|
246
|
+
export const controlCode = (control: string | number, read: (s: string) => number): number => {
|
|
247
|
+
const code = Predicate.isString(control) ? read(control) : control
|
|
248
|
+
if (!Number.isSafeInteger(code) || code < 0)
|
|
249
|
+
throw new RangeError(
|
|
250
|
+
`invalid control character: expected a character or its code, got ${describeArgument(control)}`
|
|
251
|
+
)
|
|
252
|
+
return code
|
|
207
253
|
}
|
|
208
254
|
|
|
209
255
|
/**
|
package/src/internal/run.ts
CHANGED
|
@@ -33,10 +33,12 @@ export const runOn = <A>(
|
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Runs a compiled step on `state`: directly when it guards against
|
|
36
|
-
* call-stack overflows itself (the JIT's), else under `Stack.run`.
|
|
36
|
+
* call-stack overflows itself (the JIT's), else under `Stack.run`. While it
|
|
37
|
+
* runs, `state.compiledRun` is set, so the interpreted steps it calls skip
|
|
38
|
+
* building the failures it throws away (#82).
|
|
37
39
|
*/
|
|
38
40
|
export const runCompiled = <A>(step: (state: State<any>) => A, guarded: boolean, state: State<any>): A | Fail =>
|
|
39
|
-
|
|
41
|
+
Stack.runCompiled(step, guarded, state)
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
44
|
* Runs the compiled step of `compiled` (or, if compiling failed, the
|
package/src/internal/stack.ts
CHANGED
|
@@ -40,6 +40,23 @@ export const run = <S extends State<any>, A>(step: (state: S) => A, state: S): A
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Runs a compiled step with `state.compiledRun` set, so that the
|
|
45
|
+
* interpreted steps it calls skip building the failures it throws away
|
|
46
|
+
* (#82): directly when the step guards against call-stack overflows itself
|
|
47
|
+
* (the JIT's), else under {@link run}. The flag is restored however the
|
|
48
|
+
* step ends, a throw included, since the state may be the caller's.
|
|
49
|
+
*/
|
|
50
|
+
export const runCompiled = <S extends State<any>, A>(step: (state: S) => A, guarded: boolean, state: S): A | Fail => {
|
|
51
|
+
const outer = state.compiledRun
|
|
52
|
+
state.compiledRun = true
|
|
53
|
+
try {
|
|
54
|
+
return guarded ? step(state) : run(step, state)
|
|
55
|
+
} finally {
|
|
56
|
+
state.compiledRun = outer
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
43
60
|
/**
|
|
44
61
|
* Records the `TooDeep` failure of a call-stack overflow. The JIT's
|
|
45
62
|
* generated entry catches overflows itself, with this, so that the runners
|
package/src/internal/tokens.ts
CHANGED
|
@@ -417,27 +417,37 @@ export const countTokens = (state: State<Input>, start: number, end: number): nu
|
|
|
417
417
|
/**
|
|
418
418
|
* The first index at or after `from` of any of `needles` within
|
|
419
419
|
* `[from, state.end)`, or -1. The state remembers, under `needles`, where
|
|
420
|
-
* each one next occurs (`[from, at]` at `
|
|
421
|
-
* forwards over one input search each needle once per occurrence
|
|
422
|
-
* than once per call (as `nextStop` does for `takeTill`).
|
|
420
|
+
* each one next occurs (`[from, at, end]` at `3 * i`), so successive
|
|
421
|
+
* searches forwards over one input search each needle once per occurrence
|
|
422
|
+
* rather than once per call (as `nextStop` does for `takeTill`).
|
|
423
|
+
*
|
|
424
|
+
* A remembered answer is used only where it still holds: an occurrence, when
|
|
425
|
+
* the search starts between where it was searched from and where it was
|
|
426
|
+
* found, and it still fits before the end; "not found", when the search
|
|
427
|
+
* starts no earlier and the end is no further than the end that search ran
|
|
428
|
+
* to (a parser may narrow `state.end` for a while, and `reset` widens it
|
|
429
|
+
* again, so a miss within a narrower end says nothing about a wider one).
|
|
423
430
|
*/
|
|
424
431
|
export const indexOfAny = (state: State<Input>, needles: ReadonlyArray<Input>, from: number): number => {
|
|
425
432
|
const searches = (state.searches ??= new Map())
|
|
426
433
|
let memo = searches.get(needles)
|
|
427
434
|
if (memo === undefined) {
|
|
428
|
-
memo = needles.flatMap(() => [-1, -1])
|
|
435
|
+
memo = needles.flatMap(() => [-1, -1, -1])
|
|
429
436
|
searches.set(needles, memo)
|
|
430
437
|
}
|
|
431
438
|
let best = -1
|
|
432
439
|
for (let i = 0; i < needles.length; i++) {
|
|
433
|
-
const known = memo[
|
|
434
|
-
let at = memo[
|
|
440
|
+
const known = memo[3 * i] ?? -1
|
|
441
|
+
let at = memo[3 * i + 1] ?? -1
|
|
442
|
+
const searchedTo = memo[3 * i + 2] ?? -1
|
|
435
443
|
const needle = needles[i] ?? ""
|
|
436
|
-
|
|
437
|
-
|
|
444
|
+
const stale =
|
|
445
|
+
known < 0 || from < known || (at >= 0 ? at < from || at > state.end - needle.length : state.end > searchedTo)
|
|
446
|
+
if (stale) {
|
|
438
447
|
at = indexOf(state, needle, from)
|
|
439
|
-
memo[
|
|
440
|
-
memo[
|
|
448
|
+
memo[3 * i] = from
|
|
449
|
+
memo[3 * i + 1] = at
|
|
450
|
+
memo[3 * i + 2] = state.end
|
|
441
451
|
}
|
|
442
452
|
if (at >= 0 && (best < 0 || at < best)) best = at
|
|
443
453
|
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tuple steps: run steps in sequence and collect their outputs in an array.
|
|
3
|
+
*
|
|
4
|
+
* V8 pretenures by allocation site, and only array and object literals have
|
|
5
|
+
* one: an array from `new Array(n)` starts young, so tuples that a parse keeps
|
|
6
|
+
* (a `repeat(seq(...))`, a tree) are copied by a scavenge or two before they
|
|
7
|
+
* are promoted. A literal's site learns that its arrays survive, and V8 then
|
|
8
|
+
* allocates them straight into the old generation. So arities up to
|
|
9
|
+
* {@link maxLiteral} build their tuple with a literal of that arity, each its
|
|
10
|
+
* own site; longer tuples fall back to `new Array(n)` (#86).
|
|
11
|
+
*/
|
|
12
|
+
import { Fail } from "../Input.js"
|
|
13
|
+
import type { State } from "../Input.js"
|
|
14
|
+
|
|
15
|
+
type Step = (state: State<any>) => unknown
|
|
16
|
+
|
|
17
|
+
/** The longest tuple built with a literal. */
|
|
18
|
+
const maxLiteral = 8
|
|
19
|
+
|
|
20
|
+
/** A step that runs `steps` in order and returns their outputs as an array, or {@link Fail}. */
|
|
21
|
+
export const tupleStep = (steps: ReadonlyArray<Step>): ((state: State<any>) => Array<unknown> | Fail) => {
|
|
22
|
+
const n = steps.length
|
|
23
|
+
if (n <= maxLiteral) return literals[n](steps)
|
|
24
|
+
return (state) => {
|
|
25
|
+
const out = new Array<unknown>(n)
|
|
26
|
+
for (let i = 0; i < n; i++) {
|
|
27
|
+
const a = steps[i](state)
|
|
28
|
+
if (a === Fail) return Fail
|
|
29
|
+
out[i] = a
|
|
30
|
+
}
|
|
31
|
+
return out
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const literals: ReadonlyArray<(steps: ReadonlyArray<Step>) => (state: State<any>) => Array<unknown> | Fail> = [
|
|
36
|
+
() => () => [],
|
|
37
|
+
([s0]) =>
|
|
38
|
+
(state) => {
|
|
39
|
+
const a0 = s0(state)
|
|
40
|
+
if (a0 === Fail) return Fail
|
|
41
|
+
return [a0]
|
|
42
|
+
},
|
|
43
|
+
([s0, s1]) =>
|
|
44
|
+
(state) => {
|
|
45
|
+
const a0 = s0(state)
|
|
46
|
+
if (a0 === Fail) return Fail
|
|
47
|
+
const a1 = s1(state)
|
|
48
|
+
if (a1 === Fail) return Fail
|
|
49
|
+
return [a0, a1]
|
|
50
|
+
},
|
|
51
|
+
([s0, s1, s2]) =>
|
|
52
|
+
(state) => {
|
|
53
|
+
const a0 = s0(state)
|
|
54
|
+
if (a0 === Fail) return Fail
|
|
55
|
+
const a1 = s1(state)
|
|
56
|
+
if (a1 === Fail) return Fail
|
|
57
|
+
const a2 = s2(state)
|
|
58
|
+
if (a2 === Fail) return Fail
|
|
59
|
+
return [a0, a1, a2]
|
|
60
|
+
},
|
|
61
|
+
([s0, s1, s2, s3]) =>
|
|
62
|
+
(state) => {
|
|
63
|
+
const a0 = s0(state)
|
|
64
|
+
if (a0 === Fail) return Fail
|
|
65
|
+
const a1 = s1(state)
|
|
66
|
+
if (a1 === Fail) return Fail
|
|
67
|
+
const a2 = s2(state)
|
|
68
|
+
if (a2 === Fail) return Fail
|
|
69
|
+
const a3 = s3(state)
|
|
70
|
+
if (a3 === Fail) return Fail
|
|
71
|
+
return [a0, a1, a2, a3]
|
|
72
|
+
},
|
|
73
|
+
([s0, s1, s2, s3, s4]) =>
|
|
74
|
+
(state) => {
|
|
75
|
+
const a0 = s0(state)
|
|
76
|
+
if (a0 === Fail) return Fail
|
|
77
|
+
const a1 = s1(state)
|
|
78
|
+
if (a1 === Fail) return Fail
|
|
79
|
+
const a2 = s2(state)
|
|
80
|
+
if (a2 === Fail) return Fail
|
|
81
|
+
const a3 = s3(state)
|
|
82
|
+
if (a3 === Fail) return Fail
|
|
83
|
+
const a4 = s4(state)
|
|
84
|
+
if (a4 === Fail) return Fail
|
|
85
|
+
return [a0, a1, a2, a3, a4]
|
|
86
|
+
},
|
|
87
|
+
([s0, s1, s2, s3, s4, s5]) =>
|
|
88
|
+
(state) => {
|
|
89
|
+
const a0 = s0(state)
|
|
90
|
+
if (a0 === Fail) return Fail
|
|
91
|
+
const a1 = s1(state)
|
|
92
|
+
if (a1 === Fail) return Fail
|
|
93
|
+
const a2 = s2(state)
|
|
94
|
+
if (a2 === Fail) return Fail
|
|
95
|
+
const a3 = s3(state)
|
|
96
|
+
if (a3 === Fail) return Fail
|
|
97
|
+
const a4 = s4(state)
|
|
98
|
+
if (a4 === Fail) return Fail
|
|
99
|
+
const a5 = s5(state)
|
|
100
|
+
if (a5 === Fail) return Fail
|
|
101
|
+
return [a0, a1, a2, a3, a4, a5]
|
|
102
|
+
},
|
|
103
|
+
([s0, s1, s2, s3, s4, s5, s6]) =>
|
|
104
|
+
(state) => {
|
|
105
|
+
const a0 = s0(state)
|
|
106
|
+
if (a0 === Fail) return Fail
|
|
107
|
+
const a1 = s1(state)
|
|
108
|
+
if (a1 === Fail) return Fail
|
|
109
|
+
const a2 = s2(state)
|
|
110
|
+
if (a2 === Fail) return Fail
|
|
111
|
+
const a3 = s3(state)
|
|
112
|
+
if (a3 === Fail) return Fail
|
|
113
|
+
const a4 = s4(state)
|
|
114
|
+
if (a4 === Fail) return Fail
|
|
115
|
+
const a5 = s5(state)
|
|
116
|
+
if (a5 === Fail) return Fail
|
|
117
|
+
const a6 = s6(state)
|
|
118
|
+
if (a6 === Fail) return Fail
|
|
119
|
+
return [a0, a1, a2, a3, a4, a5, a6]
|
|
120
|
+
},
|
|
121
|
+
([s0, s1, s2, s3, s4, s5, s6, s7]) =>
|
|
122
|
+
(state) => {
|
|
123
|
+
const a0 = s0(state)
|
|
124
|
+
if (a0 === Fail) return Fail
|
|
125
|
+
const a1 = s1(state)
|
|
126
|
+
if (a1 === Fail) return Fail
|
|
127
|
+
const a2 = s2(state)
|
|
128
|
+
if (a2 === Fail) return Fail
|
|
129
|
+
const a3 = s3(state)
|
|
130
|
+
if (a3 === Fail) return Fail
|
|
131
|
+
const a4 = s4(state)
|
|
132
|
+
if (a4 === Fail) return Fail
|
|
133
|
+
const a5 = s5(state)
|
|
134
|
+
if (a5 === Fail) return Fail
|
|
135
|
+
const a6 = s6(state)
|
|
136
|
+
if (a6 === Fail) return Fail
|
|
137
|
+
const a7 = s7(state)
|
|
138
|
+
if (a7 === Fail) return Fail
|
|
139
|
+
return [a0, a1, a2, a3, a4, a5, a6, a7]
|
|
140
|
+
}
|
|
141
|
+
]
|