@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/Parser.ts
CHANGED
|
@@ -32,9 +32,11 @@
|
|
|
32
32
|
import * as Predicate from "effect/Predicate"
|
|
33
33
|
import * as Context from "effect/Context"
|
|
34
34
|
import * as Effect from "effect/Effect"
|
|
35
|
+
import * as Equal from "effect/Equal"
|
|
35
36
|
import { dual } from "effect/Function"
|
|
36
37
|
import { NodeInspectSymbol } from "effect/Inspectable"
|
|
37
38
|
import * as Option from "effect/Option"
|
|
39
|
+
import * as Rec from "effect/Record"
|
|
38
40
|
import type { Pipeable } from "effect/Pipeable"
|
|
39
41
|
import { pipeArguments } from "effect/Pipeable"
|
|
40
42
|
import * as Result from "effect/Result"
|
|
@@ -50,8 +52,9 @@ import * as Resume from "./internal/resume.js"
|
|
|
50
52
|
import * as Run from "./internal/run.js"
|
|
51
53
|
import * as Stack from "./internal/stack.js"
|
|
52
54
|
import { TiedFailures } from "./internal/ties.js"
|
|
55
|
+
import { tupleStep } from "./internal/tuple.js"
|
|
53
56
|
import type { Node } from "./internal/node.js"
|
|
54
|
-
import { bounds, NodeId } from "./internal/node.js"
|
|
57
|
+
import { bounds, Defer as DeferSymbol, describedNode, NodeId } from "./internal/node.js"
|
|
55
58
|
|
|
56
59
|
// -----------------------------------------------------------------------------
|
|
57
60
|
// Models
|
|
@@ -146,6 +149,15 @@ export declare namespace Parser {
|
|
|
146
149
|
: [P] extends [Parser<infer _A, infer _E, infer _R, infer I>]
|
|
147
150
|
? I
|
|
148
151
|
: never
|
|
152
|
+
/**
|
|
153
|
+
* The steps {@link combinator}'s `build` receives: one per parser, in
|
|
154
|
+
* order, each returning its parser's output or `Fail`.
|
|
155
|
+
*
|
|
156
|
+
* @since 0.5.0
|
|
157
|
+
*/
|
|
158
|
+
export type Steps<Ps extends ReadonlyArray<Any>> = {
|
|
159
|
+
readonly [K in keyof Ps]: (state: State<InputOf<Ps[number]>>) => Success<Ps[K]> | Fail
|
|
160
|
+
}
|
|
149
161
|
}
|
|
150
162
|
|
|
151
163
|
/**
|
|
@@ -155,6 +167,12 @@ export declare namespace Parser {
|
|
|
155
167
|
* - `{ min, max }`: between `min` and `max` times (inclusive); `min`
|
|
156
168
|
* defaults to 0 and `max` to unbounded.
|
|
157
169
|
*
|
|
170
|
+
* A count and both bounds must be non-negative integers (`max` may be
|
|
171
|
+
* `Infinity`) with `min <= max`. Every combinator that takes a range
|
|
172
|
+
* throws a `RangeError` otherwise, when the parser is built: a range that
|
|
173
|
+
* comes from data (a config file, a query parameter) is checked there,
|
|
174
|
+
* whatever its static type says.
|
|
175
|
+
*
|
|
158
176
|
* @since 0.1.0
|
|
159
177
|
* @category models
|
|
160
178
|
*/
|
|
@@ -225,7 +243,7 @@ class SingleShotGen<T> {
|
|
|
225
243
|
}
|
|
226
244
|
|
|
227
245
|
class ParserImpl<A, E, R, I extends Input> implements Parser<A, E, R, I> {
|
|
228
|
-
readonly [TypeId]
|
|
246
|
+
readonly [TypeId]: Parser.Variance<A, E, R, I> = variance
|
|
229
247
|
/** The description used by the compiler; `undefined` for `Opaque`. */
|
|
230
248
|
readonly [NodeId]: Node | undefined
|
|
231
249
|
constructor(
|
|
@@ -239,7 +257,7 @@ class ParserImpl<A, E, R, I extends Input> implements Parser<A, E, R, I> {
|
|
|
239
257
|
return pipeArguments(this, arguments)
|
|
240
258
|
}
|
|
241
259
|
[Symbol.iterator](): ParserIterator<Parser<A, E, R, I>> {
|
|
242
|
-
return new SingleShotGen(this)
|
|
260
|
+
return new SingleShotGen(this)
|
|
243
261
|
}
|
|
244
262
|
toString() {
|
|
245
263
|
return this.name === undefined ? "Parser" : `Parser(${this.name})`
|
|
@@ -315,7 +333,61 @@ export const described = <A, E = never, R = never, I extends Input = string>(
|
|
|
315
333
|
node: Node
|
|
316
334
|
): Parser<A, E, R, I> => new ParserImpl(step, name, node)
|
|
317
335
|
|
|
318
|
-
|
|
336
|
+
/**
|
|
337
|
+
* Creates a combinator over `parsers` that compiles: `build` makes the
|
|
338
|
+
* combinator's step from the parsers' steps. Write combinators with it
|
|
339
|
+
* rather than with {@link make} when they run other parsers: a `make`
|
|
340
|
+
* parser is opaque to the compilers, so in a compiled grammar it runs
|
|
341
|
+
* interpreted, and so does every parser it runs. Interpreted, `build`
|
|
342
|
+
* receives the parsers' own steps; compiled (see the `Compiler` module), it
|
|
343
|
+
* receives their compiled steps, so the parsers inside compile with the
|
|
344
|
+
* grammar.
|
|
345
|
+
*
|
|
346
|
+
* Each step runs its parser on the state it is given, as `Parser.run` does,
|
|
347
|
+
* and returns the parser's output or `Fail`. The steps must run on the
|
|
348
|
+
* combinator's own state (they are compiled for its input kind): a
|
|
349
|
+
* combinator that runs a parser on another input, as {@link mapParser}
|
|
350
|
+
* does, is written with `make`. `build` may be called more than once (once
|
|
351
|
+
* interpreted, and once for each compiled build), so state its step shares
|
|
352
|
+
* between calls belongs inside `build`.
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```ts
|
|
356
|
+
* import { Ascii, Input, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
357
|
+
*
|
|
358
|
+
* // One or more `item`s separated by `sep`, returning their count
|
|
359
|
+
* const count = <A, B>(item: Parser.Parser<A>, sep: Parser.Parser<B>) =>
|
|
360
|
+
* Parser.combinator([item, sep], ([itemStep, sepStep]) => (state) => {
|
|
361
|
+
* if (itemStep(state) === Input.Fail) return Input.Fail
|
|
362
|
+
* let n = 1
|
|
363
|
+
* while (true) {
|
|
364
|
+
* const start = state.pos
|
|
365
|
+
* if (sepStep(state) === Input.Fail || itemStep(state) === Input.Fail) {
|
|
366
|
+
* if (state.mode !== "Backtrack") return Input.Fail
|
|
367
|
+
* state.rewind(start)
|
|
368
|
+
* return n
|
|
369
|
+
* }
|
|
370
|
+
* n++
|
|
371
|
+
* }
|
|
372
|
+
* })
|
|
373
|
+
*
|
|
374
|
+
* Parser.parse(count(Ascii.digit1, Token.literal(",")), "1,22,333") // Result.succeed(3)
|
|
375
|
+
* ```
|
|
376
|
+
*
|
|
377
|
+
* @since 0.5.0
|
|
378
|
+
* @category constructors
|
|
379
|
+
*/
|
|
380
|
+
export const combinator = <const Ps extends ReadonlyArray<Parser.Any>, A, E = never>(
|
|
381
|
+
parsers: Ps,
|
|
382
|
+
build: (steps: Parser.Steps<Ps>) => (state: State<Parser.InputOf<Ps[number]>>) => A | Fail,
|
|
383
|
+
name?: string
|
|
384
|
+
): Parser<A, E | Parser.Error<Ps[number]>, Parser.Services<Ps[number]>, Parser.InputOf<Ps[number]>> => {
|
|
385
|
+
const untyped = (steps: ReadonlyArray<(state: State<any>) => unknown>) =>
|
|
386
|
+
// SAFETY: `steps` holds each parser's step (or, compiled, an equivalent
|
|
387
|
+
// compiled step), in order, which returns that parser's output or `Fail`
|
|
388
|
+
build(steps as Parser.Steps<Ps>)
|
|
389
|
+
return described(untyped(parsers.map((p) => p.step)), name, { _tag: "Combinator", parsers, build: untyped })
|
|
390
|
+
}
|
|
319
391
|
|
|
320
392
|
/**
|
|
321
393
|
* Gives a parser a name, used by `toString`.
|
|
@@ -329,7 +401,7 @@ export const named: {
|
|
|
329
401
|
} = /*#__PURE__*/ dual(
|
|
330
402
|
2,
|
|
331
403
|
<A, E, R, I extends Input>(self: Parser<A, E, R, I>, name: string): Parser<A, E, R, I> =>
|
|
332
|
-
new ParserImpl(self.step, name,
|
|
404
|
+
new ParserImpl(self.step, name, describedNode(self))
|
|
333
405
|
)
|
|
334
406
|
|
|
335
407
|
/**
|
|
@@ -390,6 +462,40 @@ export const fail = <E>(error: E): Parser<never, E, never, Input> =>
|
|
|
390
462
|
|
|
391
463
|
const failBacktrack: Node = { _tag: "Fail", mode: "Backtrack" }
|
|
392
464
|
|
|
465
|
+
/**
|
|
466
|
+
* A parser that fails (backtracks) with the given user error, reporting it
|
|
467
|
+
* at `offset` rather than where the parser runs. Use it for a check that
|
|
468
|
+
* can only run once a whole construct has parsed but should point at the
|
|
469
|
+
* part at fault: return it from a {@link flatMap} continuation, with an
|
|
470
|
+
* offset taken from {@link location} or {@link withSpan}. Like any failure,
|
|
471
|
+
* one at a later offset is preferred over the failures of other
|
|
472
|
+
* alternatives (the furthest failure wins). The offset is clamped to the
|
|
473
|
+
* input.
|
|
474
|
+
*
|
|
475
|
+
* @example
|
|
476
|
+
* ```ts
|
|
477
|
+
* import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
478
|
+
*
|
|
479
|
+
* const tag = (open: string) => Parser.delimited(Token.literal(open), Ascii.alpha1, Token.literal(">"))
|
|
480
|
+
* const element = Parser.seq(tag("<"), Parser.location, tag("</")).pipe(
|
|
481
|
+
* Parser.flatMap(([start, at, end]) =>
|
|
482
|
+
* start === end ? Parser.succeed(start) : Parser.failAt(at, `expected </${start}>`)
|
|
483
|
+
* )
|
|
484
|
+
* )
|
|
485
|
+
* // The error points at the end tag (offset 3), not at the start of the element
|
|
486
|
+
* Parser.parse(element, "<a></b>")
|
|
487
|
+
* ```
|
|
488
|
+
*
|
|
489
|
+
* @since 0.5.0
|
|
490
|
+
* @category constructors
|
|
491
|
+
*/
|
|
492
|
+
export const failAt = <E>(offset: number, error: E): Parser<never, E, never, Input> =>
|
|
493
|
+
described<never, E, never, Input>(
|
|
494
|
+
(state) => state.failCause("Backtrack", "Fail", error, offset < 0 ? 0 : offset > state.end ? state.end : offset),
|
|
495
|
+
"failAt",
|
|
496
|
+
failBacktrack
|
|
497
|
+
)
|
|
498
|
+
|
|
393
499
|
/**
|
|
394
500
|
* A parser that always fails (backtracks) without a cause. This is winnow's
|
|
395
501
|
* `fail` and nom's `fail()`.
|
|
@@ -737,6 +843,72 @@ export const tryMap: {
|
|
|
737
843
|
}
|
|
738
844
|
)
|
|
739
845
|
|
|
846
|
+
/**
|
|
847
|
+
* Transforms the output of a parser with a function that returns
|
|
848
|
+
* `Input.Fail` to reject it: the allocation-free form of {@link tryMap} and
|
|
849
|
+
* {@link filterMap}, for checks that run on nearly every token. Compiled
|
|
850
|
+
* code compares the result with the sentinel instead of allocating and
|
|
851
|
+
* unwrapping a `Result` or an `Option` on every success.
|
|
852
|
+
*
|
|
853
|
+
* On failure the parser backtracks to where it started. With `orFailWith`,
|
|
854
|
+
* the user error it builds (only on failure) becomes the `cause` of the
|
|
855
|
+
* `ParseError`, as with `tryMap`; without, the failure has no cause, as
|
|
856
|
+
* with `filterMap`.
|
|
857
|
+
*
|
|
858
|
+
* @example
|
|
859
|
+
* ```ts
|
|
860
|
+
* import { Ascii, Input, Parser } from "@thomasfosterau/effect-parse"
|
|
861
|
+
*
|
|
862
|
+
* const byte = Ascii.decUint.pipe(
|
|
863
|
+
* Parser.mapOrFail(
|
|
864
|
+
* (n) => (n < 256 ? n : Input.Fail),
|
|
865
|
+
* (n) => `${n} is not a byte`
|
|
866
|
+
* )
|
|
867
|
+
* )
|
|
868
|
+
* Parser.parse(byte, "300") // fails with the cause "300 is not a byte"
|
|
869
|
+
* ```
|
|
870
|
+
*
|
|
871
|
+
* @since 0.5.0
|
|
872
|
+
* @category mapping
|
|
873
|
+
*/
|
|
874
|
+
export const mapOrFail: {
|
|
875
|
+
<A, B>(f: (a: A) => B | Fail): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<Exclude<B, Fail>, E, R, I>
|
|
876
|
+
<A, B, E2>(
|
|
877
|
+
f: (a: A) => B | Fail,
|
|
878
|
+
orFailWith: (a: A) => E2
|
|
879
|
+
): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<Exclude<B, Fail>, E | E2, R, I>
|
|
880
|
+
<A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B | Fail): Parser<Exclude<B, Fail>, E, R, I>
|
|
881
|
+
<A, E, R, I extends Input, B, E2>(
|
|
882
|
+
self: Parser<A, E, R, I>,
|
|
883
|
+
f: (a: A) => B | Fail,
|
|
884
|
+
orFailWith: (a: A) => E2
|
|
885
|
+
): Parser<Exclude<B, Fail>, E | E2, R, I>
|
|
886
|
+
} = /*#__PURE__*/ dual(
|
|
887
|
+
(args) => isParser(args[0]),
|
|
888
|
+
<A, E, R, I extends Input, B, E2>(
|
|
889
|
+
self: Parser<A, E, R, I>,
|
|
890
|
+
f: (a: A) => B | Fail,
|
|
891
|
+
orFailWith?: (a: A) => E2
|
|
892
|
+
): Parser<B, E | E2, R, I> => {
|
|
893
|
+
const step = self.step
|
|
894
|
+
return described(
|
|
895
|
+
(state) => {
|
|
896
|
+
const start = state.pos
|
|
897
|
+
const a = step(state)
|
|
898
|
+
if (a === Fail) return Fail
|
|
899
|
+
const b = f(a)
|
|
900
|
+
if (b !== Fail) return b
|
|
901
|
+
state.rewind(start)
|
|
902
|
+
return orFailWith === undefined
|
|
903
|
+
? state.backtrack("Verify")
|
|
904
|
+
: state.failCause("Backtrack", "Verify", orFailWith(a))
|
|
905
|
+
},
|
|
906
|
+
self.name,
|
|
907
|
+
{ _tag: "MapOrFail", self, f }
|
|
908
|
+
)
|
|
909
|
+
}
|
|
910
|
+
)
|
|
911
|
+
|
|
740
912
|
/**
|
|
741
913
|
* Like {@link tryMap}, but the function may throw. Thrown values become the
|
|
742
914
|
* `cause` of the error.
|
|
@@ -952,6 +1124,51 @@ export const withSpan = <A, E, R, I extends Input>(
|
|
|
952
1124
|
)
|
|
953
1125
|
}
|
|
954
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* Transforms the output of a parser together with the offsets it consumed,
|
|
1129
|
+
* `[start, end)`: the allocation-free spelling of
|
|
1130
|
+
* `map(withSpan(p), ([a, span]) => f(a, span.start, span.end))`, which
|
|
1131
|
+
* builds a tuple and a span object on every success. Compiled code passes
|
|
1132
|
+
* the offsets to `f` directly.
|
|
1133
|
+
*
|
|
1134
|
+
* @example
|
|
1135
|
+
* ```ts
|
|
1136
|
+
* import { Ascii, Parser } from "@thomasfosterau/effect-parse"
|
|
1137
|
+
*
|
|
1138
|
+
* const name = Ascii.alpha1.pipe(Parser.mapSpan((text, start, end) => ({ text, start, end })))
|
|
1139
|
+
* Parser.parse(name, "abc") // Result.succeed({ text: "abc", start: 0, end: 3 })
|
|
1140
|
+
* ```
|
|
1141
|
+
*
|
|
1142
|
+
* @since 0.5.0
|
|
1143
|
+
* @category consumed input
|
|
1144
|
+
*/
|
|
1145
|
+
export const mapSpan: {
|
|
1146
|
+
<A, B>(
|
|
1147
|
+
f: (a: A, start: number, end: number) => B
|
|
1148
|
+
): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E, R, I>
|
|
1149
|
+
<A, E, R, I extends Input, B>(
|
|
1150
|
+
self: Parser<A, E, R, I>,
|
|
1151
|
+
f: (a: A, start: number, end: number) => B
|
|
1152
|
+
): Parser<B, E, R, I>
|
|
1153
|
+
} = /*#__PURE__*/ dual(
|
|
1154
|
+
2,
|
|
1155
|
+
<A, E, R, I extends Input, B>(
|
|
1156
|
+
self: Parser<A, E, R, I>,
|
|
1157
|
+
f: (a: A, start: number, end: number) => B
|
|
1158
|
+
): Parser<B, E, R, I> => {
|
|
1159
|
+
const step = self.step
|
|
1160
|
+
return described(
|
|
1161
|
+
(state) => {
|
|
1162
|
+
const start = state.pos
|
|
1163
|
+
const a = step(state)
|
|
1164
|
+
return a === Fail ? Fail : f(a, start, state.pos)
|
|
1165
|
+
},
|
|
1166
|
+
self.name,
|
|
1167
|
+
{ _tag: "MapSpan", self, f }
|
|
1168
|
+
)
|
|
1169
|
+
}
|
|
1170
|
+
)
|
|
1171
|
+
|
|
955
1172
|
// -----------------------------------------------------------------------------
|
|
956
1173
|
// Error management
|
|
957
1174
|
// -----------------------------------------------------------------------------
|
|
@@ -1004,6 +1221,9 @@ export const cut = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<
|
|
|
1004
1221
|
state.mode = "Cut"
|
|
1005
1222
|
return a
|
|
1006
1223
|
}
|
|
1224
|
+
// SAFETY: `state.deepest` is declared `Failure | undefined`; the
|
|
1225
|
+
// assertion only undoes the narrowing to `undefined` from the
|
|
1226
|
+
// assignment above, which misses that `step` may have recorded a failure
|
|
1007
1227
|
const inner = state.deepest as Failure | undefined
|
|
1008
1228
|
if (deepest !== undefined && (inner === undefined || deepest.offset > inner.offset)) state.deepest = deepest
|
|
1009
1229
|
return a
|
|
@@ -1114,6 +1334,212 @@ export const expected: {
|
|
|
1114
1334
|
}
|
|
1115
1335
|
)
|
|
1116
1336
|
|
|
1337
|
+
/**
|
|
1338
|
+
* Runs a parser without letting it add to the expectations of later errors
|
|
1339
|
+
* (chumsky's `quiet`). Use it on skippers, such as whitespace and comments,
|
|
1340
|
+
* that may appear between any two tokens: they stop by failing, and without
|
|
1341
|
+
* `quiet` what they would have accepted (`" "`, `"(:"`, ...) is listed in
|
|
1342
|
+
* every error raised after them.
|
|
1343
|
+
*
|
|
1344
|
+
* When the parser succeeds, the hints and the furthest failure it recorded
|
|
1345
|
+
* are forgotten, as after a lookahead that succeeded. When it fails
|
|
1346
|
+
* recoverably without consuming input, its failure expects nothing, so an
|
|
1347
|
+
* enclosing `alt` or `opt` reports only the other alternatives. A failure
|
|
1348
|
+
* after consuming input (an unterminated comment) or an unrecoverable one
|
|
1349
|
+
* is kept as it is. `Parser.expected` cannot do this: it relabels a failure,
|
|
1350
|
+
* and a skipper that matches zero or more times never fails.
|
|
1351
|
+
*
|
|
1352
|
+
* @example
|
|
1353
|
+
* ```ts
|
|
1354
|
+
* import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
1355
|
+
*
|
|
1356
|
+
* const comment = Parser.delimited(Token.literal("(:"), Token.takeTill(":"), Token.literal(":)"))
|
|
1357
|
+
* const skip = Parser.quiet(Parser.repeat(Parser.alt(Ascii.multispace1, comment)))
|
|
1358
|
+
* const number = Parser.preceded(skip, Ascii.digit1)
|
|
1359
|
+
* // Fails expecting only a digit: without `quiet`, also whitespace and "(:"
|
|
1360
|
+
* Parser.parse(number, " (: one :) x")
|
|
1361
|
+
* ```
|
|
1362
|
+
*
|
|
1363
|
+
* @since 0.5.0
|
|
1364
|
+
* @category error management
|
|
1365
|
+
*/
|
|
1366
|
+
export const quiet = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<A, E, R, I> => {
|
|
1367
|
+
const step = self.step
|
|
1368
|
+
return described(
|
|
1369
|
+
(state) => {
|
|
1370
|
+
const start = state.pos
|
|
1371
|
+
const hintOffset = state.hintOffset
|
|
1372
|
+
const hintExpected = state.hintExpected
|
|
1373
|
+
const deepest = state.deepest
|
|
1374
|
+
const a = step(state)
|
|
1375
|
+
if (a !== Fail) {
|
|
1376
|
+
state.hintOffset = hintOffset
|
|
1377
|
+
state.hintExpected = hintExpected
|
|
1378
|
+
state.deepest = deepest
|
|
1379
|
+
return a
|
|
1380
|
+
}
|
|
1381
|
+
if (state.mode === "Backtrack" && state.failure.offset === start) {
|
|
1382
|
+
const f = state.failure
|
|
1383
|
+
const expected = hintOffset === start ? hintExpected : []
|
|
1384
|
+
state.hintOffset = hintOffset
|
|
1385
|
+
state.hintExpected = hintExpected
|
|
1386
|
+
state.deepest = deepest
|
|
1387
|
+
state.failure = new Failure(f.offset, f.kind, expected, f.context, f.hasCause, f.cause, f.message, f.needed)
|
|
1388
|
+
}
|
|
1389
|
+
return a
|
|
1390
|
+
},
|
|
1391
|
+
self.name,
|
|
1392
|
+
{ _tag: "Quiet", self }
|
|
1393
|
+
)
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
/**
|
|
1397
|
+
* What a fast path (see {@link withFastPath}) returns to leave the input at
|
|
1398
|
+
* the current offset to its grammar.
|
|
1399
|
+
*
|
|
1400
|
+
* @since 0.5.0
|
|
1401
|
+
* @category fast paths
|
|
1402
|
+
*/
|
|
1403
|
+
export const Defer: typeof DeferSymbol = DeferSymbol
|
|
1404
|
+
|
|
1405
|
+
/**
|
|
1406
|
+
* The type of {@link Defer}.
|
|
1407
|
+
*
|
|
1408
|
+
* @since 0.5.0
|
|
1409
|
+
* @category fast paths
|
|
1410
|
+
*/
|
|
1411
|
+
export type Defer = typeof DeferSymbol
|
|
1412
|
+
|
|
1413
|
+
/**
|
|
1414
|
+
* Puts a hand-written fast path in front of a grammar, which stays the
|
|
1415
|
+
* specification: `fast` runs on the state, at the offset where the grammar
|
|
1416
|
+
* would, and either returns the value the grammar would return, with
|
|
1417
|
+
* `state.pos` where the grammar would stop, or returns {@link Defer}, and
|
|
1418
|
+
* the grammar runs from that offset instead. Use it where a scanner written
|
|
1419
|
+
* by hand (`indexOf`, a lookup table, interned names) beats compiled code on
|
|
1420
|
+
* the tokens that dominate an input, and let the grammar handle everything
|
|
1421
|
+
* else.
|
|
1422
|
+
*
|
|
1423
|
+
* The fast path runs in compiled code only (see the `Compiler` module).
|
|
1424
|
+
* Interpreted runs, and so every error, every `Syntax` tree and every trace,
|
|
1425
|
+
* come from the grammar alone, so they are exactly the grammar's. It must
|
|
1426
|
+
* defer wherever it would not return exactly what the grammar returns, and
|
|
1427
|
+
* on partial input wherever the grammar might report `Incomplete`; check it
|
|
1428
|
+
* against the grammar with {@link verifyFastPath}.
|
|
1429
|
+
*
|
|
1430
|
+
* @example
|
|
1431
|
+
* ```ts
|
|
1432
|
+
* import { Compiler, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
1433
|
+
*
|
|
1434
|
+
* const text = Token.takeTill("<", { min: 1 })
|
|
1435
|
+
* // The same run, found with `indexOf`; anything else (an empty run, the end of a partial input) defers
|
|
1436
|
+
* const fastText = Compiler.jit(
|
|
1437
|
+
* Parser.withFastPath(text, (state) => {
|
|
1438
|
+
* const end = state.input.indexOf("<", state.pos)
|
|
1439
|
+
* if (end <= state.pos || end > state.end) return Parser.Defer
|
|
1440
|
+
* const value = state.input.slice(state.pos, end)
|
|
1441
|
+
* state.pos = end
|
|
1442
|
+
* return value
|
|
1443
|
+
* })
|
|
1444
|
+
* )
|
|
1445
|
+
* ```
|
|
1446
|
+
*
|
|
1447
|
+
* @since 0.5.0
|
|
1448
|
+
* @category fast paths
|
|
1449
|
+
*/
|
|
1450
|
+
export const withFastPath: {
|
|
1451
|
+
<A, I extends Input>(fast: (state: State<I>) => A | Defer): <E, R>(grammar: Parser<A, E, R, I>) => Parser<A, E, R, I>
|
|
1452
|
+
<A, E, R, I extends Input>(grammar: Parser<A, E, R, I>, fast: (state: State<I>) => A | Defer): Parser<A, E, R, I>
|
|
1453
|
+
} = /*#__PURE__*/ dual(
|
|
1454
|
+
2,
|
|
1455
|
+
<A, E, R, I extends Input>(grammar: Parser<A, E, R, I>, fast: (state: State<I>) => A | Defer): Parser<A, E, R, I> =>
|
|
1456
|
+
// The interpreter runs the grammar alone: it is the reference, and it builds every error
|
|
1457
|
+
described(grammar.step, grammar.name, { _tag: "FastPath", self: grammar, fast })
|
|
1458
|
+
)
|
|
1459
|
+
|
|
1460
|
+
/**
|
|
1461
|
+
* A place where a fast path disagrees with its grammar (see
|
|
1462
|
+
* {@link verifyFastPath}).
|
|
1463
|
+
*
|
|
1464
|
+
* @since 0.5.0
|
|
1465
|
+
* @category fast paths
|
|
1466
|
+
*/
|
|
1467
|
+
export interface FastPathMismatch<A, I extends Input> {
|
|
1468
|
+
readonly input: I
|
|
1469
|
+
readonly offset: number
|
|
1470
|
+
readonly partial: boolean
|
|
1471
|
+
/** What the fast path returned, and where it stopped. */
|
|
1472
|
+
readonly fast: { readonly value: A; readonly end: number }
|
|
1473
|
+
/** What the grammar returned, and where it stopped, or how it failed. */
|
|
1474
|
+
readonly grammar: { readonly value: A; readonly end: number } | { readonly failed: ErrMode }
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/**
|
|
1478
|
+
* Checks a fast path against its grammar (see {@link withFastPath}): on
|
|
1479
|
+
* every input, at every offset, on complete and on partial input, wherever
|
|
1480
|
+
* `fast` returns a value, the grammar (run interpreted) must return an
|
|
1481
|
+
* equal value (by `Equal.equals`, or `options.equivalence`) and stop at the
|
|
1482
|
+
* same offset. Returns every place where it doesn't, so a test asserts that
|
|
1483
|
+
* the result is empty; generated inputs, mutated and truncated, find the
|
|
1484
|
+
* most. Grammars that use services are not supported.
|
|
1485
|
+
*
|
|
1486
|
+
* @example
|
|
1487
|
+
* ```ts
|
|
1488
|
+
* import { Input, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
1489
|
+
*
|
|
1490
|
+
* const text = Token.takeTill("<", { min: 1 })
|
|
1491
|
+
* const fast = (state: Input.State<string>) => {
|
|
1492
|
+
* const end = state.input.indexOf("<", state.pos)
|
|
1493
|
+
* if (end <= state.pos || end > state.end) return Parser.Defer
|
|
1494
|
+
* const value = state.input.slice(state.pos, end)
|
|
1495
|
+
* state.pos = end
|
|
1496
|
+
* return value
|
|
1497
|
+
* }
|
|
1498
|
+
* Parser.verifyFastPath(text, fast, ["a<b", "<", "abc"]) // []
|
|
1499
|
+
* ```
|
|
1500
|
+
*
|
|
1501
|
+
* @since 0.5.0
|
|
1502
|
+
* @category fast paths
|
|
1503
|
+
*/
|
|
1504
|
+
export const verifyFastPath = <A, E, I extends Input>(
|
|
1505
|
+
grammar: Parser<A, E, never, I>,
|
|
1506
|
+
fast: (state: State<I>) => A | Defer,
|
|
1507
|
+
inputs: Iterable<I>,
|
|
1508
|
+
options?: { readonly equivalence?: ((a: A, b: A) => boolean) | undefined }
|
|
1509
|
+
): ReadonlyArray<FastPathMismatch<A, I>> => {
|
|
1510
|
+
const equivalence = options?.equivalence ?? Equal.equals
|
|
1511
|
+
const mismatches: Array<FastPathMismatch<A, I>> = []
|
|
1512
|
+
for (const input of inputs) {
|
|
1513
|
+
const length = new State(input).end
|
|
1514
|
+
for (let offset = 0; offset <= length; offset++) {
|
|
1515
|
+
for (const partial of [false, true]) {
|
|
1516
|
+
const mismatch = checkFastPath(grammar, fast, input, offset, partial, equivalence)
|
|
1517
|
+
if (mismatch !== undefined) mismatches.push(mismatch)
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return mismatches
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
/** Where `fast` disagrees with `grammar` at `offset`, if it does (see {@link verifyFastPath}). */
|
|
1525
|
+
const checkFastPath = <A, E, I extends Input>(
|
|
1526
|
+
grammar: Parser<A, E, never, I>,
|
|
1527
|
+
fast: (state: State<I>) => A | Defer,
|
|
1528
|
+
input: I,
|
|
1529
|
+
offset: number,
|
|
1530
|
+
partial: boolean,
|
|
1531
|
+
equivalence: (a: A, b: A) => boolean
|
|
1532
|
+
): FastPathMismatch<A, I> | undefined => {
|
|
1533
|
+
const state = new State(input, { offset, partial })
|
|
1534
|
+
const a = fast(state)
|
|
1535
|
+
if (a === Defer) return undefined
|
|
1536
|
+
const expected = new State(input, { offset, partial })
|
|
1537
|
+
const b = Stack.run(grammar.step, expected)
|
|
1538
|
+
if (b !== Fail && expected.pos === state.pos && equivalence(a, b)) return undefined
|
|
1539
|
+
const grammarOutcome = b === Fail ? { failed: expected.mode } : { value: b, end: expected.pos }
|
|
1540
|
+
return { input, offset, partial, fast: { value: a, end: state.pos }, grammar: grammarOutcome }
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1117
1543
|
/**
|
|
1118
1544
|
* Transforms the user error (cause) of a parser's failures.
|
|
1119
1545
|
*
|
|
@@ -1132,6 +1558,8 @@ export const mapError: {
|
|
|
1132
1558
|
const a = step(state)
|
|
1133
1559
|
if (a === Fail && state.failure.hasCause) {
|
|
1134
1560
|
const x = state.failure
|
|
1561
|
+
// SAFETY: a failure with `hasCause` from `self` carries a cause raised
|
|
1562
|
+
// by a `fail` / `mapError` inside it, whose user error type is `E`
|
|
1135
1563
|
state.replaceFailure(
|
|
1136
1564
|
new Failure(x.offset, x.kind, x.expected, x.context, true, f(x.cause as E), x.message, x.needed)
|
|
1137
1565
|
)
|
|
@@ -1376,20 +1804,53 @@ export const seq = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1376
1804
|
Parser.Services<Ps[number]>,
|
|
1377
1805
|
Parser.InputOf<Ps[number]>
|
|
1378
1806
|
> => {
|
|
1807
|
+
type Out = { -readonly [K in keyof Ps]: Parser.Success<Ps[K]> }
|
|
1808
|
+
// SAFETY: the tuple has one slot per parser, and slot `i` holds the
|
|
1809
|
+
// output of `parsers[i]`, which is `Parser.Success<Ps[i]>`
|
|
1810
|
+
const step = tupleStep(parsers.map((p) => p.step)) as (state: State<any>) => Out | Fail
|
|
1811
|
+
return described(step, "seq", { _tag: "Seq", parsers, output: "tuple" })
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
/**
|
|
1815
|
+
* Runs parsers in sequence and combines their outputs with `f`, which
|
|
1816
|
+
* receives one argument per parser: the allocation-free spelling of
|
|
1817
|
+
* `map(seq(...parsers), ([a, b, ...]) => f(a, b, ...))`, which builds a
|
|
1818
|
+
* tuple on every success. Compiled code calls `f` with the values directly.
|
|
1819
|
+
* {@link zipWith} is the two-parser, pipeable form.
|
|
1820
|
+
*
|
|
1821
|
+
* @example
|
|
1822
|
+
* ```ts
|
|
1823
|
+
* import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
1824
|
+
*
|
|
1825
|
+
* const point = Parser.seqWith(
|
|
1826
|
+
* [Ascii.decInt, Token.literal(","), Ascii.decInt],
|
|
1827
|
+
* (x, _, y) => ({ x, y })
|
|
1828
|
+
* )
|
|
1829
|
+
* Parser.parse(point, "3,-4") // Result.succeed({ x: 3, y: -4 })
|
|
1830
|
+
* ```
|
|
1831
|
+
*
|
|
1832
|
+
* @since 0.5.0
|
|
1833
|
+
* @category sequencing
|
|
1834
|
+
*/
|
|
1835
|
+
export const seqWith = <const Ps extends ReadonlyArray<Parser.Any>, B>(
|
|
1836
|
+
parsers: Ps,
|
|
1837
|
+
f: (...values: { -readonly [K in keyof Ps]: Parser.Success<Ps[K]> }) => B
|
|
1838
|
+
): Parser<B, Parser.Error<Ps[number]>, Parser.Services<Ps[number]>, Parser.InputOf<Ps[number]>> => {
|
|
1379
1839
|
const steps = parsers.map((p) => p.step)
|
|
1380
1840
|
const n = steps.length
|
|
1841
|
+
const g: (...values: Array<any>) => B = f
|
|
1381
1842
|
return described(
|
|
1382
1843
|
(state: State<any>) => {
|
|
1383
|
-
const
|
|
1844
|
+
const values = new Array<unknown>(n)
|
|
1384
1845
|
for (let i = 0; i < n; i++) {
|
|
1385
1846
|
const a = steps[i](state)
|
|
1386
1847
|
if (a === Fail) return Fail
|
|
1387
|
-
|
|
1848
|
+
values[i] = a
|
|
1388
1849
|
}
|
|
1389
|
-
return
|
|
1850
|
+
return g(...values)
|
|
1390
1851
|
},
|
|
1391
|
-
"
|
|
1392
|
-
{ _tag: "
|
|
1852
|
+
"seqWith",
|
|
1853
|
+
{ _tag: "SeqWith", parsers, f }
|
|
1393
1854
|
)
|
|
1394
1855
|
}
|
|
1395
1856
|
|
|
@@ -1409,18 +1870,21 @@ export const struct = <const Fields extends Readonly<Record<string, Parser.Any>>
|
|
|
1409
1870
|
Parser.Services<Fields[keyof Fields]>,
|
|
1410
1871
|
Parser.InputOf<Fields[keyof Fields]>
|
|
1411
1872
|
> => {
|
|
1412
|
-
|
|
1873
|
+
type Out = Simplify<{ -readonly [K in keyof Fields]: Parser.Success<Fields[K]> }>
|
|
1874
|
+
const keys = Rec.keys(fields)
|
|
1413
1875
|
const steps = keys.map((k) => fields[k].step)
|
|
1414
1876
|
const n = keys.length
|
|
1415
1877
|
return described(
|
|
1416
1878
|
(state: State<any>) => {
|
|
1417
|
-
const out: Record<string,
|
|
1879
|
+
const out: Record<string, Parser.Success<Fields[keyof Fields]>> = {}
|
|
1418
1880
|
for (let i = 0; i < n; i++) {
|
|
1419
1881
|
const a = steps[i](state)
|
|
1420
1882
|
if (a === Fail) return Fail
|
|
1421
1883
|
out[keys[i]] = a
|
|
1422
1884
|
}
|
|
1423
|
-
|
|
1885
|
+
// SAFETY: the loop has set every own key of `fields`, each to the
|
|
1886
|
+
// output of that key's parser, `Parser.Success<Fields[K]>`
|
|
1887
|
+
return out as Out
|
|
1424
1888
|
},
|
|
1425
1889
|
"struct",
|
|
1426
1890
|
{ _tag: "Struct", keys, parsers: keys.map((k) => fields[k]) }
|
|
@@ -1676,9 +2140,14 @@ export const gen = <Eff extends Parser.Any, AEff>(
|
|
|
1676
2140
|
while (!next.done) {
|
|
1677
2141
|
const a = next.value.step(state)
|
|
1678
2142
|
if (a === Fail) {
|
|
1679
|
-
|
|
2143
|
+
// SAFETY: the value passed to `return` only becomes the completion value
|
|
2144
|
+
// of the abandoned generator, which nothing reads
|
|
2145
|
+
iterator.return?.(undefined as AEff)
|
|
1680
2146
|
return Fail
|
|
1681
2147
|
}
|
|
2148
|
+
// SAFETY: `a` is the output of the parser just yielded, which is what its
|
|
2149
|
+
// `yield*` evaluates to (typed by `ParserIterator`'s return value, not by
|
|
2150
|
+
// the generator's `never` next type)
|
|
1682
2151
|
next = iterator.next(a as never)
|
|
1683
2152
|
}
|
|
1684
2153
|
return next.value
|
|
@@ -1786,7 +2255,7 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1786
2255
|
> => {
|
|
1787
2256
|
const steps = parsers.map((p) => p.step)
|
|
1788
2257
|
const n = steps.length
|
|
1789
|
-
if (n === 0) return reject
|
|
2258
|
+
if (n === 0) return reject
|
|
1790
2259
|
if (n === 1) return parsers[0]
|
|
1791
2260
|
return described(
|
|
1792
2261
|
(state: State<any>) => {
|
|
@@ -1839,6 +2308,36 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1839
2308
|
)
|
|
1840
2309
|
}
|
|
1841
2310
|
|
|
2311
|
+
/**
|
|
2312
|
+
* The overloads of {@link dispatch}.
|
|
2313
|
+
*
|
|
2314
|
+
* @since 0.5.0
|
|
2315
|
+
* @category choice
|
|
2316
|
+
*/
|
|
2317
|
+
export interface DispatchFn {
|
|
2318
|
+
<K, E, R, I extends Input, P extends Parser.Any>(
|
|
2319
|
+
selector: Parser<K, E, R, I>,
|
|
2320
|
+
cases: (key: K) => P | undefined
|
|
2321
|
+
): Parser<Parser.Success<P>, E | Parser.Error<P>, R | Parser.Services<P>, I & Parser.InputOf<P>>
|
|
2322
|
+
<
|
|
2323
|
+
K extends PropertyKey,
|
|
2324
|
+
E,
|
|
2325
|
+
R,
|
|
2326
|
+
I extends Input,
|
|
2327
|
+
const Cases extends { readonly [key in K]?: Parser.Any },
|
|
2328
|
+
Otherwise extends Parser.Any = never
|
|
2329
|
+
>(
|
|
2330
|
+
selector: Parser<K, E, R, I>,
|
|
2331
|
+
cases: Cases,
|
|
2332
|
+
otherwise?: Otherwise
|
|
2333
|
+
): Parser<
|
|
2334
|
+
Parser.Success<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
2335
|
+
E | Parser.Error<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
2336
|
+
R | Parser.Services<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
2337
|
+
I & Parser.InputOf<NonNullable<Cases[keyof Cases]> | Otherwise>
|
|
2338
|
+
>
|
|
2339
|
+
}
|
|
2340
|
+
|
|
1842
2341
|
/**
|
|
1843
2342
|
* Selects the parser to run next based on the output of `selector`, like a
|
|
1844
2343
|
* `match` over the input (winnow's `dispatch!`). If no case matches, the
|
|
@@ -1873,29 +2372,7 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1873
2372
|
* @since 0.1.0
|
|
1874
2373
|
* @category choice
|
|
1875
2374
|
*/
|
|
1876
|
-
export const dispatch:
|
|
1877
|
-
<K, E, R, I extends Input, P extends Parser.Any>(
|
|
1878
|
-
selector: Parser<K, E, R, I>,
|
|
1879
|
-
cases: (key: K) => P | undefined
|
|
1880
|
-
): Parser<Parser.Success<P>, E | Parser.Error<P>, R | Parser.Services<P>, I & Parser.InputOf<P>>
|
|
1881
|
-
<
|
|
1882
|
-
K extends PropertyKey,
|
|
1883
|
-
E,
|
|
1884
|
-
R,
|
|
1885
|
-
I extends Input,
|
|
1886
|
-
const Cases extends { readonly [key in K]?: Parser.Any },
|
|
1887
|
-
Otherwise extends Parser.Any = never
|
|
1888
|
-
>(
|
|
1889
|
-
selector: Parser<K, E, R, I>,
|
|
1890
|
-
cases: Cases,
|
|
1891
|
-
otherwise?: Otherwise
|
|
1892
|
-
): Parser<
|
|
1893
|
-
Parser.Success<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
1894
|
-
E | Parser.Error<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
1895
|
-
R | Parser.Services<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
1896
|
-
I & Parser.InputOf<NonNullable<Cases[keyof Cases]> | Otherwise>
|
|
1897
|
-
>
|
|
1898
|
-
} = (
|
|
2375
|
+
export const dispatch: DispatchFn = (
|
|
1899
2376
|
selector: Parser.Any,
|
|
1900
2377
|
cases: ((key: any) => Parser.Any | undefined) | Record<PropertyKey, Parser.Any>,
|
|
1901
2378
|
otherwise?: Parser.Any
|
|
@@ -1943,37 +2420,44 @@ export const permutation = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1943
2420
|
Parser.Services<Ps[number]>,
|
|
1944
2421
|
Parser.InputOf<Ps[number]>
|
|
1945
2422
|
> => {
|
|
1946
|
-
|
|
1947
|
-
const n =
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
let
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2423
|
+
type Out = { -readonly [K in keyof Ps]: Parser.Success<Ps[K]> }
|
|
2424
|
+
const n = parsers.length
|
|
2425
|
+
// The parsers inside compile: the step is built from theirs (see `combinator`)
|
|
2426
|
+
return combinator(
|
|
2427
|
+
parsers,
|
|
2428
|
+
(steps) => (state: State<any>) => {
|
|
2429
|
+
// A round's failures, merged only if no parser matches in it (#44)
|
|
2430
|
+
const ties = new TiedFailures()
|
|
2431
|
+
const done = new Array<boolean>(n).fill(false)
|
|
2432
|
+
const out = new Array(n)
|
|
2433
|
+
let remaining = n
|
|
2434
|
+
while (remaining > 0) {
|
|
2435
|
+
ties.clear()
|
|
2436
|
+
let matched = false
|
|
2437
|
+
for (let i = 0; i < n; i++) {
|
|
2438
|
+
if (done[i]) continue
|
|
2439
|
+
const start = state.pos
|
|
2440
|
+
const a = steps[i](state)
|
|
2441
|
+
if (a !== Fail) {
|
|
2442
|
+
out[i] = a
|
|
2443
|
+
done[i] = true
|
|
2444
|
+
remaining--
|
|
2445
|
+
matched = true
|
|
2446
|
+
break
|
|
2447
|
+
}
|
|
2448
|
+
if (state.mode !== "Backtrack") return Fail
|
|
2449
|
+
state.rewind(start)
|
|
2450
|
+
ties.add(state)
|
|
1967
2451
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
ties.add(state)
|
|
2452
|
+
// Some parser is not done yet, and each one that fails is folded in
|
|
2453
|
+
if (!matched) return ties.fail(state)
|
|
1971
2454
|
}
|
|
1972
|
-
//
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2455
|
+
// SAFETY: every parser has matched once (`remaining` is 0), and slot
|
|
2456
|
+
// `i` holds the output of `parsers[i]`, which is `Parser.Success<Ps[i]>`
|
|
2457
|
+
return out as Out
|
|
2458
|
+
},
|
|
2459
|
+
"permutation"
|
|
2460
|
+
)
|
|
1977
2461
|
}
|
|
1978
2462
|
|
|
1979
2463
|
// -----------------------------------------------------------------------------
|
|
@@ -1981,11 +2465,7 @@ export const permutation = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1981
2465
|
// -----------------------------------------------------------------------------
|
|
1982
2466
|
|
|
1983
2467
|
const normalizeRange = (range: Range | undefined): [min: number, max: number] => {
|
|
1984
|
-
|
|
1985
|
-
if (Predicate.isNumber(range)) return [range, range]
|
|
1986
|
-
const min = range.min ?? 0
|
|
1987
|
-
const max = range.max ?? Infinity
|
|
1988
|
-
if (min > max) throw new RangeError(`invalid range: min (${min}) > max (${max})`)
|
|
2468
|
+
const { max, min } = bounds(range)
|
|
1989
2469
|
return [min, max]
|
|
1990
2470
|
}
|
|
1991
2471
|
|
|
@@ -2014,16 +2494,20 @@ const foldImpl = <A, B, E, R, I extends Input>(
|
|
|
2014
2494
|
): Parser<B, E, R, I> => {
|
|
2015
2495
|
const [min, max] = normalizeRange(range)
|
|
2016
2496
|
const step = self.step
|
|
2017
|
-
|
|
2497
|
+
// Bounded or not, the repetition checks progress below (#115)
|
|
2018
2498
|
const key = new Key()
|
|
2019
2499
|
return described(
|
|
2020
2500
|
(state) => {
|
|
2021
2501
|
const entry = state.pos
|
|
2022
2502
|
let acc: B
|
|
2023
2503
|
let count = 0
|
|
2504
|
+
// SAFETY: `key` belongs to this parser, which saves only
|
|
2505
|
+
// `FoldCheckpoint<B>`s under it (and a `Resume.Done`, narrowed below)
|
|
2024
2506
|
const resumed = state.resume === undefined ? undefined : (Resume.take(state, key, entry) as FoldCheckpoint<B>)
|
|
2025
2507
|
if (resumed instanceof Resume.Done) {
|
|
2026
2508
|
state.pos = resumed.pos
|
|
2509
|
+
// SAFETY: a `Done` under `key` is recorded by `Resume.done` below,
|
|
2510
|
+
// with this fold's result, a `B`
|
|
2027
2511
|
return resumed.value as B
|
|
2028
2512
|
}
|
|
2029
2513
|
if (resumed !== undefined) {
|
|
@@ -2045,7 +2529,7 @@ const foldImpl = <A, B, E, R, I extends Input>(
|
|
|
2045
2529
|
recover(state, start)
|
|
2046
2530
|
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2047
2531
|
}
|
|
2048
|
-
if (
|
|
2532
|
+
if (state.pos === start) return assertConsumes(state, name)
|
|
2049
2533
|
acc = f(acc, a)
|
|
2050
2534
|
}
|
|
2051
2535
|
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
@@ -2063,9 +2547,9 @@ const foldImpl = <A, B, E, R, I extends Input>(
|
|
|
2063
2547
|
* exactly that many times. With a literal positive `min`, the output is
|
|
2064
2548
|
* typed as a non-empty array.
|
|
2065
2549
|
*
|
|
2066
|
-
*
|
|
2067
|
-
*
|
|
2068
|
-
*
|
|
2550
|
+
* A parser that succeeds without consuming input fails the repetition with
|
|
2551
|
+
* an unrecoverable `Assert` failure, whatever the range (winnow checks only
|
|
2552
|
+
* unbounded ones): a bound can come from the input (`Binary.lengthRepeat`).
|
|
2069
2553
|
*
|
|
2070
2554
|
* @example
|
|
2071
2555
|
* ```ts
|
|
@@ -2256,16 +2740,20 @@ export const separated: {
|
|
|
2256
2740
|
const [min, max] = normalizeRange(range)
|
|
2257
2741
|
const step = self.step
|
|
2258
2742
|
const sepStep = separator.step
|
|
2259
|
-
|
|
2743
|
+
// Bounded or not, the repetition checks progress below (#115)
|
|
2260
2744
|
const key = new Key()
|
|
2261
2745
|
return described(
|
|
2262
2746
|
(state) => {
|
|
2263
2747
|
const entry = state.pos
|
|
2264
2748
|
let acc: Array<A>
|
|
2749
|
+
// SAFETY: `key` belongs to this parser, which saves only
|
|
2750
|
+
// `SeparatedCheckpoint<A>`s under it (and a `Resume.Done`, narrowed below)
|
|
2265
2751
|
const resumed =
|
|
2266
2752
|
state.resume === undefined ? undefined : (Resume.take(state, key, entry) as SeparatedCheckpoint<A>)
|
|
2267
2753
|
if (resumed instanceof Resume.Done) {
|
|
2268
2754
|
state.pos = resumed.pos
|
|
2755
|
+
// SAFETY: a `Done` under `key` is recorded by `Resume.done` below,
|
|
2756
|
+
// with the elements collected, an `Array<A>`
|
|
2269
2757
|
return resumed.value as Array<A>
|
|
2270
2758
|
}
|
|
2271
2759
|
if (resumed !== undefined) {
|
|
@@ -2293,7 +2781,7 @@ export const separated: {
|
|
|
2293
2781
|
recover(state, start)
|
|
2294
2782
|
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2295
2783
|
}
|
|
2296
|
-
if (
|
|
2784
|
+
if (state.pos === start) return assertConsumes(state, "separated")
|
|
2297
2785
|
const a = step(state)
|
|
2298
2786
|
if (a === Fail) {
|
|
2299
2787
|
if (state.mode === "Incomplete" && state.resume !== undefined) {
|
|
@@ -2345,10 +2833,15 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2345
2833
|
return described(
|
|
2346
2834
|
(state) => {
|
|
2347
2835
|
const entry = state.pos
|
|
2836
|
+
// SAFETY: `key` belongs to this parser, which saves only
|
|
2837
|
+
// `FoldCheckpoint<A>`s under it, through `saveFold` (and a
|
|
2838
|
+
// `Resume.Done`, narrowed below)
|
|
2348
2839
|
const resumed = state.resume === undefined ? undefined : (Resume.take(state, key, entry) as FoldCheckpoint<A>)
|
|
2349
2840
|
let acc: A | Fail
|
|
2350
2841
|
if (resumed instanceof Resume.Done) {
|
|
2351
2842
|
state.pos = resumed.pos
|
|
2843
|
+
// SAFETY: a `Done` under `key` is recorded by `Resume.done` below,
|
|
2844
|
+
// with the folded value, an `A`
|
|
2352
2845
|
return resumed.value as A
|
|
2353
2846
|
}
|
|
2354
2847
|
if (resumed !== undefined) {
|
|
@@ -2365,7 +2858,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2365
2858
|
if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
|
|
2366
2859
|
if (state.mode !== "Backtrack") return Fail
|
|
2367
2860
|
recover(state, start)
|
|
2368
|
-
return Resume.done(state, key, entry, resumed !== undefined, acc
|
|
2861
|
+
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2369
2862
|
}
|
|
2370
2863
|
if (state.pos === start) return assertConsumes(state, "separatedFoldl1")
|
|
2371
2864
|
const a = step(state)
|
|
@@ -2373,7 +2866,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2373
2866
|
if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
|
|
2374
2867
|
if (state.mode !== "Backtrack") return Fail
|
|
2375
2868
|
state.rewind(start)
|
|
2376
|
-
return Resume.done(state, key, entry, resumed !== undefined, acc
|
|
2869
|
+
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2377
2870
|
}
|
|
2378
2871
|
acc = op(acc, s, a)
|
|
2379
2872
|
}
|
|
@@ -2423,9 +2916,9 @@ export const separatedFoldr1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2423
2916
|
seps.push(s)
|
|
2424
2917
|
values.push(a)
|
|
2425
2918
|
}
|
|
2426
|
-
let acc = values[values.length - 1]
|
|
2919
|
+
let acc = values[values.length - 1]
|
|
2427
2920
|
for (let i = seps.length - 1; i >= 0; i--) {
|
|
2428
|
-
acc = op(values[i]
|
|
2921
|
+
acc = op(values[i], seps[i], acc)
|
|
2429
2922
|
}
|
|
2430
2923
|
return acc
|
|
2431
2924
|
},
|
|
@@ -2501,7 +2994,7 @@ export const provideService: {
|
|
|
2501
2994
|
return described(
|
|
2502
2995
|
(state) => {
|
|
2503
2996
|
const prev = state.context
|
|
2504
|
-
state.context = Context.add(prev, key, service)
|
|
2997
|
+
state.context = Context.add(prev, key, service)
|
|
2505
2998
|
try {
|
|
2506
2999
|
return step(state)
|
|
2507
3000
|
} finally {
|
|
@@ -2538,7 +3031,7 @@ export const provide: {
|
|
|
2538
3031
|
return described(
|
|
2539
3032
|
(state) => {
|
|
2540
3033
|
const prev = state.context
|
|
2541
|
-
state.context = Context.merge(prev, context)
|
|
3034
|
+
state.context = Context.merge(prev, context)
|
|
2542
3035
|
try {
|
|
2543
3036
|
return step(state)
|
|
2544
3037
|
} finally {
|
|
@@ -2734,18 +3227,20 @@ const NotCompiled: unique symbol = /*#__PURE__*/ Symbol("NotCompiled")
|
|
|
2734
3227
|
* input), in which case the state must be thrown away. `resumable` selects
|
|
2735
3228
|
* the build that handles partial input.
|
|
2736
3229
|
*/
|
|
2737
|
-
const runCompiled = <I extends Input>(
|
|
2738
|
-
self: Parser
|
|
3230
|
+
const runCompiled = <A, I extends Input>(
|
|
3231
|
+
self: Parser<A, unknown, unknown, I>,
|
|
2739
3232
|
state: State<I>,
|
|
2740
3233
|
whole: boolean,
|
|
2741
3234
|
resumable = false
|
|
2742
|
-
):
|
|
3235
|
+
): A | Fail | typeof NotCompiled => {
|
|
2743
3236
|
if (state.tracer !== undefined || state.recovered !== undefined) return NotCompiled
|
|
2744
|
-
const
|
|
2745
|
-
if (step === undefined) return NotCompiled
|
|
2746
|
-
const a =
|
|
3237
|
+
const compiled = Registry.compiled(self, state.inputKind, resumable)
|
|
3238
|
+
if (compiled?.step === undefined) return NotCompiled
|
|
3239
|
+
const a = Run.runCompiled(compiled.step, compiled.guarded, state)
|
|
2747
3240
|
if (a === Fail || (whole && state.pos < state.end)) return Fail
|
|
2748
|
-
|
|
3241
|
+
// SAFETY: the compilers never change semantics (the differential suites
|
|
3242
|
+
// check it), so `self`'s compiled step returns what its step returns, an `A`
|
|
3243
|
+
return a as A
|
|
2749
3244
|
}
|
|
2750
3245
|
|
|
2751
3246
|
// -----------------------------------------------------------------------------
|
|
@@ -2816,7 +3311,7 @@ const parseComplete = <A, E, R, I extends Input>(
|
|
|
2816
3311
|
const state = makeState(input, options, false)
|
|
2817
3312
|
const a = runCompiled(self, state, true)
|
|
2818
3313
|
if (a === NotCompiled) return runComplete(self, state)
|
|
2819
|
-
if (a !== Fail) return Result.succeed(a
|
|
3314
|
+
if (a !== Fail) return Result.succeed(a)
|
|
2820
3315
|
// Re-run interpreted for the diagnostics
|
|
2821
3316
|
return runComplete(self, makeState(input, options, false))
|
|
2822
3317
|
}
|
|
@@ -2834,6 +3329,25 @@ const runComplete = <A, E, R, I extends Input>(
|
|
|
2834
3329
|
return Result.succeed(a)
|
|
2835
3330
|
}
|
|
2836
3331
|
|
|
3332
|
+
/**
|
|
3333
|
+
* The overloads of {@link parse}.
|
|
3334
|
+
*
|
|
3335
|
+
* @since 0.5.0
|
|
3336
|
+
* @category running
|
|
3337
|
+
*/
|
|
3338
|
+
export interface ParseFn {
|
|
3339
|
+
<A, E, I extends Input>(
|
|
3340
|
+
self: Parser<A, E, never, I>,
|
|
3341
|
+
input: NoInfer<Source<I>>,
|
|
3342
|
+
options?: ParseOptions
|
|
3343
|
+
): Result.Result<A, ParseError.ParseError<E>>
|
|
3344
|
+
<A, E, R, I extends Input>(
|
|
3345
|
+
self: Parser<A, E, R, I>,
|
|
3346
|
+
input: NoInfer<Source<I>>,
|
|
3347
|
+
options: ParseOptionsWithContext<R>
|
|
3348
|
+
): Result.Result<A, ParseError.ParseError<E>>
|
|
3349
|
+
}
|
|
3350
|
+
|
|
2837
3351
|
/**
|
|
2838
3352
|
* Runs a parser against a complete input, requiring the whole input to be
|
|
2839
3353
|
* consumed (winnow's `Parser::parse`).
|
|
@@ -2850,37 +3364,34 @@ const runComplete = <A, E, R, I extends Input>(
|
|
|
2850
3364
|
* @since 0.1.0
|
|
2851
3365
|
* @category running
|
|
2852
3366
|
*/
|
|
2853
|
-
export const parse:
|
|
2854
|
-
<A, E, I extends Input>(
|
|
2855
|
-
self: Parser<A, E, never, I>,
|
|
2856
|
-
input: NoInfer<Source<I>>,
|
|
2857
|
-
options?: ParseOptions
|
|
2858
|
-
): Result.Result<A, ParseError.ParseError<E>>
|
|
2859
|
-
<A, E, R, I extends Input>(
|
|
2860
|
-
self: Parser<A, E, R, I>,
|
|
2861
|
-
input: NoInfer<Source<I>>,
|
|
2862
|
-
options: ParseOptionsWithContext<R>
|
|
2863
|
-
): Result.Result<A, ParseError.ParseError<E>>
|
|
2864
|
-
} = <A, E, R, I extends Input>(
|
|
3367
|
+
export const parse: ParseFn = <A, E, R, I extends Input>(
|
|
2865
3368
|
self: Parser<A, E, R, I>,
|
|
2866
3369
|
input: I | Source<I>,
|
|
2867
3370
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
2868
3371
|
): Result.Result<A, ParseError.ParseError<E>> => parseComplete(self, input, options)
|
|
2869
3372
|
|
|
2870
3373
|
/**
|
|
2871
|
-
*
|
|
3374
|
+
* The overloads of {@link parseSync}.
|
|
2872
3375
|
*
|
|
2873
|
-
* @since 0.
|
|
3376
|
+
* @since 0.5.0
|
|
2874
3377
|
* @category running
|
|
2875
3378
|
*/
|
|
2876
|
-
export
|
|
3379
|
+
export interface ParseSyncFn {
|
|
2877
3380
|
<A, E, I extends Input>(self: Parser<A, E, never, I>, input: NoInfer<Source<I>>, options?: ParseOptions): A
|
|
2878
3381
|
<A, E, R, I extends Input>(
|
|
2879
3382
|
self: Parser<A, E, R, I>,
|
|
2880
3383
|
input: NoInfer<Source<I>>,
|
|
2881
3384
|
options: ParseOptionsWithContext<R>
|
|
2882
3385
|
): A
|
|
2883
|
-
}
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
/**
|
|
3389
|
+
* Like {@link parse}, but throws the `ParseError` on failure.
|
|
3390
|
+
*
|
|
3391
|
+
* @since 0.1.0
|
|
3392
|
+
* @category running
|
|
3393
|
+
*/
|
|
3394
|
+
export const parseSync: ParseSyncFn = <A, E, R, I extends Input>(
|
|
2884
3395
|
self: Parser<A, E, R, I>,
|
|
2885
3396
|
input: I | Source<I>,
|
|
2886
3397
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
@@ -2891,18 +3402,12 @@ export const parseSync: {
|
|
|
2891
3402
|
}
|
|
2892
3403
|
|
|
2893
3404
|
/**
|
|
2894
|
-
*
|
|
2895
|
-
* input along with the output, nom-style (winnow's `parse_peek`, nom's
|
|
2896
|
-
* `IResult`).
|
|
3405
|
+
* The overloads of {@link parsePeek}.
|
|
2897
3406
|
*
|
|
2898
|
-
*
|
|
2899
|
-
* how a caller running from an `offset` (see {@link ParseOptions}) finds
|
|
2900
|
-
* where to continue.
|
|
2901
|
-
*
|
|
2902
|
-
* @since 0.1.0
|
|
3407
|
+
* @since 0.5.0
|
|
2903
3408
|
* @category running
|
|
2904
3409
|
*/
|
|
2905
|
-
export
|
|
3410
|
+
export interface ParsePeekFn {
|
|
2906
3411
|
<A, E, I extends Input>(
|
|
2907
3412
|
self: Parser<A, E, never, I>,
|
|
2908
3413
|
input: NoInfer<Source<I>>,
|
|
@@ -2913,7 +3418,21 @@ export const parsePeek: {
|
|
|
2913
3418
|
input: NoInfer<Source<I>>,
|
|
2914
3419
|
options: ParseOptionsWithContext<R>
|
|
2915
3420
|
): Result.Result<[remaining: Slice<I>, output: A], ParseError.ParseError<E>>
|
|
2916
|
-
}
|
|
3421
|
+
}
|
|
3422
|
+
|
|
3423
|
+
/**
|
|
3424
|
+
* Runs a parser against the start of an input, returning the remaining
|
|
3425
|
+
* input along with the output, nom-style (winnow's `parse_peek`, nom's
|
|
3426
|
+
* `IResult`).
|
|
3427
|
+
*
|
|
3428
|
+
* The parser ended at offset `input.length - remaining.length`, which is
|
|
3429
|
+
* how a caller running from an `offset` (see {@link ParseOptions}) finds
|
|
3430
|
+
* where to continue.
|
|
3431
|
+
*
|
|
3432
|
+
* @since 0.1.0
|
|
3433
|
+
* @category running
|
|
3434
|
+
*/
|
|
3435
|
+
export const parsePeek: ParsePeekFn = <A, E, R, I extends Input>(
|
|
2917
3436
|
self: Parser<A, E, R, I>,
|
|
2918
3437
|
input: I | Source<I>,
|
|
2919
3438
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
@@ -2921,7 +3440,7 @@ export const parsePeek: {
|
|
|
2921
3440
|
let state = makeState(input, options, false)
|
|
2922
3441
|
const compiled = runCompiled(self, state, false)
|
|
2923
3442
|
if (compiled !== NotCompiled && compiled !== Fail) {
|
|
2924
|
-
return Result.succeed([state.slice(state.pos, state.end), compiled
|
|
3443
|
+
return Result.succeed([state.slice(state.pos, state.end), compiled])
|
|
2925
3444
|
}
|
|
2926
3445
|
if (compiled === Fail) state = makeState(input, options, false)
|
|
2927
3446
|
const a = Stack.run(self.step, state)
|
|
@@ -2942,6 +3461,25 @@ export type PartialResult<A> =
|
|
|
2942
3461
|
| { readonly _tag: "Done"; readonly value: A; readonly offset: number }
|
|
2943
3462
|
| { readonly _tag: "Incomplete"; readonly needed: Option.Option<number> }
|
|
2944
3463
|
|
|
3464
|
+
/**
|
|
3465
|
+
* The overloads of {@link parsePartial}.
|
|
3466
|
+
*
|
|
3467
|
+
* @since 0.5.0
|
|
3468
|
+
* @category running
|
|
3469
|
+
*/
|
|
3470
|
+
export interface ParsePartialFn {
|
|
3471
|
+
<A, E, I extends Input>(
|
|
3472
|
+
self: Parser<A, E, never, I>,
|
|
3473
|
+
input: NoInfer<Source<I>>,
|
|
3474
|
+
options?: ParseOptions
|
|
3475
|
+
): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
|
|
3476
|
+
<A, E, R, I extends Input>(
|
|
3477
|
+
self: Parser<A, E, R, I>,
|
|
3478
|
+
input: NoInfer<Source<I>>,
|
|
3479
|
+
options: ParseOptionsWithContext<R>
|
|
3480
|
+
): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
|
|
3481
|
+
}
|
|
3482
|
+
|
|
2945
3483
|
/**
|
|
2946
3484
|
* Runs a parser against a *partial* input, i.e. a chunk of a stream that may
|
|
2947
3485
|
* be followed by more data (winnow's `Partial`, nom's `streaming` parsers).
|
|
@@ -2971,18 +3509,7 @@ export type PartialResult<A> =
|
|
|
2971
3509
|
* @since 0.1.0
|
|
2972
3510
|
* @category running
|
|
2973
3511
|
*/
|
|
2974
|
-
export const parsePartial:
|
|
2975
|
-
<A, E, I extends Input>(
|
|
2976
|
-
self: Parser<A, E, never, I>,
|
|
2977
|
-
input: NoInfer<Source<I>>,
|
|
2978
|
-
options?: ParseOptions
|
|
2979
|
-
): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
|
|
2980
|
-
<A, E, R, I extends Input>(
|
|
2981
|
-
self: Parser<A, E, R, I>,
|
|
2982
|
-
input: NoInfer<Source<I>>,
|
|
2983
|
-
options: ParseOptionsWithContext<R>
|
|
2984
|
-
): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
|
|
2985
|
-
} = <A, E, R, I extends Input>(
|
|
3512
|
+
export const parsePartial: ParsePartialFn = <A, E, R, I extends Input>(
|
|
2986
3513
|
self: Parser<A, E, R, I>,
|
|
2987
3514
|
input: I | Source<I>,
|
|
2988
3515
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
@@ -2990,19 +3517,25 @@ export const parsePartial: {
|
|
|
2990
3517
|
let state = makeState(input, options, true)
|
|
2991
3518
|
const compiled = runCompiled(self, state, false, true)
|
|
2992
3519
|
if (compiled !== NotCompiled && compiled !== Fail) {
|
|
2993
|
-
return Result.succeed({ _tag: "Done", value: compiled
|
|
3520
|
+
return Result.succeed({ _tag: "Done", value: compiled, offset: state.pos })
|
|
2994
3521
|
}
|
|
3522
|
+
// Compiled code reports `Incomplete` where the interpreter does, with the same failure: the leaf that
|
|
3523
|
+
// ran out of input is the interpreter's own step, or records what it would. Only an error needs the re-run.
|
|
3524
|
+
if (compiled === Fail && state.mode === "Incomplete") return Result.succeed(incompleteOf(state))
|
|
2995
3525
|
// Re-run interpreted for `needed` and the diagnostics
|
|
2996
3526
|
if (compiled === Fail) state = makeState(input, options, true)
|
|
2997
3527
|
const a = Stack.run(self.step, state)
|
|
2998
3528
|
if (a !== Fail) return Result.succeed({ _tag: "Done", value: a, offset: state.pos })
|
|
2999
|
-
if (state.mode === "Incomplete")
|
|
3000
|
-
const needed = state.failure.needed
|
|
3001
|
-
return Result.succeed({ _tag: "Incomplete", needed: needed > 0 ? Option.some(needed) : Option.none() })
|
|
3002
|
-
}
|
|
3529
|
+
if (state.mode === "Incomplete") return Result.succeed(incompleteOf(state))
|
|
3003
3530
|
return Result.fail(toError(state))
|
|
3004
3531
|
}
|
|
3005
3532
|
|
|
3533
|
+
/** The `Incomplete` result of a state whose parse ran out of partial input. */
|
|
3534
|
+
const incompleteOf = <A>(state: State<any>): PartialResult<A> => {
|
|
3535
|
+
const needed = state.failure.needed
|
|
3536
|
+
return { _tag: "Incomplete", needed: needed > 0 ? Option.some(needed) : Option.none() }
|
|
3537
|
+
}
|
|
3538
|
+
|
|
3006
3539
|
/**
|
|
3007
3540
|
* Runs a parser against a complete input as an `Effect`. Services required
|
|
3008
3541
|
* by the parser are taken from the Effect's context.
|
|
@@ -3121,14 +3654,12 @@ export interface RecoverableResult<A, E> {
|
|
|
3121
3654
|
}
|
|
3122
3655
|
|
|
3123
3656
|
/**
|
|
3124
|
-
*
|
|
3125
|
-
* stream). Failures handled by {@link resumeAfter} and {@link retryAfter}
|
|
3126
|
-
* are collected instead of aborting the parse.
|
|
3657
|
+
* The overloads of {@link parseRecoverable}.
|
|
3127
3658
|
*
|
|
3128
|
-
* @since 0.
|
|
3659
|
+
* @since 0.5.0
|
|
3129
3660
|
* @category running
|
|
3130
3661
|
*/
|
|
3131
|
-
export
|
|
3662
|
+
export interface ParseRecoverableFn {
|
|
3132
3663
|
<A, E, I extends Input>(
|
|
3133
3664
|
self: Parser<A, E, never, I>,
|
|
3134
3665
|
input: NoInfer<Source<I>>,
|
|
@@ -3139,7 +3670,17 @@ export const parseRecoverable: {
|
|
|
3139
3670
|
input: NoInfer<Source<I>>,
|
|
3140
3671
|
options: ParseOptionsWithContext<R>
|
|
3141
3672
|
): RecoverableResult<A, E>
|
|
3142
|
-
}
|
|
3673
|
+
}
|
|
3674
|
+
|
|
3675
|
+
/**
|
|
3676
|
+
* Runs a parser with error recovery enabled (winnow's `Recoverable`
|
|
3677
|
+
* stream). Failures handled by {@link resumeAfter} and {@link retryAfter}
|
|
3678
|
+
* are collected instead of aborting the parse.
|
|
3679
|
+
*
|
|
3680
|
+
* @since 0.1.0
|
|
3681
|
+
* @category running
|
|
3682
|
+
*/
|
|
3683
|
+
export const parseRecoverable: ParseRecoverableFn = <A, E, R, I extends Input>(
|
|
3143
3684
|
self: Parser<A, E, R, I>,
|
|
3144
3685
|
input: I | Source<I>,
|
|
3145
3686
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|