@thomasfosterau/effect-parse 0.4.1 → 0.5.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/README.md +37 -13
- package/dist/Ascii.d.ts +11 -4
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +7 -1
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +64 -35
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +7 -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 +37 -4
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +42 -0
- 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.map +1 -1
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +366 -26
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +390 -19
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.js +1 -1
- package/dist/ParserStream.d.ts +3 -2
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +13 -9
- 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.map +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +9 -9
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +55 -10
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +59 -21
- package/dist/Token.js.map +1 -1
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +257 -97
- 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 +7 -1
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/graph.d.ts +17 -0
- package/dist/internal/compiler/graph.d.ts.map +1 -0
- package/dist/internal/compiler/graph.js +56 -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 +185 -96
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +10 -2
- 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 +2769 -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 +58 -2
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +13 -3
- 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 +12 -0
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +51 -11
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +6 -0
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +8 -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 +13 -0
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +59 -39
- package/dist/internal/tokens.js.map +1 -1
- package/package.json +5 -4
- package/src/Ascii.ts +42 -9
- package/src/Binary.ts +151 -71
- package/src/Bits.ts +12 -6
- package/src/Compiler.ts +101 -10
- package/src/Expression.ts +94 -81
- package/src/Input.ts +65 -6
- package/src/Nom.ts +15 -5
- package/src/ParseError.ts +3 -0
- package/src/Parser.ts +678 -127
- package/src/ParserStream.ts +37 -18
- package/src/Syntax.ts +60 -36
- package/src/Text.ts +16 -15
- package/src/Token.ts +138 -29
- package/src/internal/compiler/closure.ts +362 -135
- package/src/internal/compiler/entries.ts +4 -2
- package/src/internal/compiler/first.ts +10 -0
- package/src/internal/compiler/graph.ts +68 -0
- package/src/internal/compiler/jit.ts +284 -154
- 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 +3203 -0
- package/src/internal/compiler/wasmAsm.ts +484 -0
- package/src/internal/entries.ts +8 -2
- package/src/internal/node.ts +54 -4
- package/src/internal/numbers.ts +3 -1
- package/src/internal/run.ts +71 -12
- package/src/internal/stack.ts +8 -1
- package/src/internal/syntax.ts +39 -18
- package/src/internal/text.ts +97 -9
- package/src/internal/tokens.ts +96 -14
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"
|
|
@@ -51,7 +53,7 @@ import * as Run from "./internal/run.js"
|
|
|
51
53
|
import * as Stack from "./internal/stack.js"
|
|
52
54
|
import { TiedFailures } from "./internal/ties.js"
|
|
53
55
|
import type { Node } from "./internal/node.js"
|
|
54
|
-
import { bounds, NodeId } from "./internal/node.js"
|
|
56
|
+
import { bounds, Defer as DeferSymbol, describedNode, NodeId } from "./internal/node.js"
|
|
55
57
|
|
|
56
58
|
// -----------------------------------------------------------------------------
|
|
57
59
|
// Models
|
|
@@ -146,6 +148,15 @@ export declare namespace Parser {
|
|
|
146
148
|
: [P] extends [Parser<infer _A, infer _E, infer _R, infer I>]
|
|
147
149
|
? I
|
|
148
150
|
: never
|
|
151
|
+
/**
|
|
152
|
+
* The steps {@link combinator}'s `build` receives: one per parser, in
|
|
153
|
+
* order, each returning its parser's output or `Fail`.
|
|
154
|
+
*
|
|
155
|
+
* @since 0.5.0
|
|
156
|
+
*/
|
|
157
|
+
export type Steps<Ps extends ReadonlyArray<Any>> = {
|
|
158
|
+
readonly [K in keyof Ps]: (state: State<InputOf<Ps[number]>>) => Success<Ps[K]> | Fail
|
|
159
|
+
}
|
|
149
160
|
}
|
|
150
161
|
|
|
151
162
|
/**
|
|
@@ -225,7 +236,7 @@ class SingleShotGen<T> {
|
|
|
225
236
|
}
|
|
226
237
|
|
|
227
238
|
class ParserImpl<A, E, R, I extends Input> implements Parser<A, E, R, I> {
|
|
228
|
-
readonly [TypeId]
|
|
239
|
+
readonly [TypeId]: Parser.Variance<A, E, R, I> = variance
|
|
229
240
|
/** The description used by the compiler; `undefined` for `Opaque`. */
|
|
230
241
|
readonly [NodeId]: Node | undefined
|
|
231
242
|
constructor(
|
|
@@ -239,7 +250,7 @@ class ParserImpl<A, E, R, I extends Input> implements Parser<A, E, R, I> {
|
|
|
239
250
|
return pipeArguments(this, arguments)
|
|
240
251
|
}
|
|
241
252
|
[Symbol.iterator](): ParserIterator<Parser<A, E, R, I>> {
|
|
242
|
-
return new SingleShotGen(this)
|
|
253
|
+
return new SingleShotGen(this)
|
|
243
254
|
}
|
|
244
255
|
toString() {
|
|
245
256
|
return this.name === undefined ? "Parser" : `Parser(${this.name})`
|
|
@@ -315,7 +326,61 @@ export const described = <A, E = never, R = never, I extends Input = string>(
|
|
|
315
326
|
node: Node
|
|
316
327
|
): Parser<A, E, R, I> => new ParserImpl(step, name, node)
|
|
317
328
|
|
|
318
|
-
|
|
329
|
+
/**
|
|
330
|
+
* Creates a combinator over `parsers` that compiles: `build` makes the
|
|
331
|
+
* combinator's step from the parsers' steps. Write combinators with it
|
|
332
|
+
* rather than with {@link make} when they run other parsers: a `make`
|
|
333
|
+
* parser is opaque to the compilers, so in a compiled grammar it runs
|
|
334
|
+
* interpreted, and so does every parser it runs. Interpreted, `build`
|
|
335
|
+
* receives the parsers' own steps; compiled (see the `Compiler` module), it
|
|
336
|
+
* receives their compiled steps, so the parsers inside compile with the
|
|
337
|
+
* grammar.
|
|
338
|
+
*
|
|
339
|
+
* Each step runs its parser on the state it is given, as `Parser.run` does,
|
|
340
|
+
* and returns the parser's output or `Fail`. The steps must run on the
|
|
341
|
+
* combinator's own state (they are compiled for its input kind): a
|
|
342
|
+
* combinator that runs a parser on another input, as {@link mapParser}
|
|
343
|
+
* does, is written with `make`. `build` may be called more than once (once
|
|
344
|
+
* interpreted, and once for each compiled build), so state its step shares
|
|
345
|
+
* between calls belongs inside `build`.
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```ts
|
|
349
|
+
* import { Ascii, Input, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
350
|
+
*
|
|
351
|
+
* // One or more `item`s separated by `sep`, returning their count
|
|
352
|
+
* const count = <A, B>(item: Parser.Parser<A>, sep: Parser.Parser<B>) =>
|
|
353
|
+
* Parser.combinator([item, sep], ([itemStep, sepStep]) => (state) => {
|
|
354
|
+
* if (itemStep(state) === Input.Fail) return Input.Fail
|
|
355
|
+
* let n = 1
|
|
356
|
+
* while (true) {
|
|
357
|
+
* const start = state.pos
|
|
358
|
+
* if (sepStep(state) === Input.Fail || itemStep(state) === Input.Fail) {
|
|
359
|
+
* if (state.mode !== "Backtrack") return Input.Fail
|
|
360
|
+
* state.rewind(start)
|
|
361
|
+
* return n
|
|
362
|
+
* }
|
|
363
|
+
* n++
|
|
364
|
+
* }
|
|
365
|
+
* })
|
|
366
|
+
*
|
|
367
|
+
* Parser.parse(count(Ascii.digit1, Token.literal(",")), "1,22,333") // Result.succeed(3)
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* @since 0.5.0
|
|
371
|
+
* @category constructors
|
|
372
|
+
*/
|
|
373
|
+
export const combinator = <const Ps extends ReadonlyArray<Parser.Any>, A, E = never>(
|
|
374
|
+
parsers: Ps,
|
|
375
|
+
build: (steps: Parser.Steps<Ps>) => (state: State<Parser.InputOf<Ps[number]>>) => A | Fail,
|
|
376
|
+
name?: string
|
|
377
|
+
): Parser<A, E | Parser.Error<Ps[number]>, Parser.Services<Ps[number]>, Parser.InputOf<Ps[number]>> => {
|
|
378
|
+
const untyped = (steps: ReadonlyArray<(state: State<any>) => unknown>) =>
|
|
379
|
+
// SAFETY: `steps` holds each parser's step (or, compiled, an equivalent
|
|
380
|
+
// compiled step), in order, which returns that parser's output or `Fail`
|
|
381
|
+
build(steps as Parser.Steps<Ps>)
|
|
382
|
+
return described(untyped(parsers.map((p) => p.step)), name, { _tag: "Combinator", parsers, build: untyped })
|
|
383
|
+
}
|
|
319
384
|
|
|
320
385
|
/**
|
|
321
386
|
* Gives a parser a name, used by `toString`.
|
|
@@ -329,7 +394,7 @@ export const named: {
|
|
|
329
394
|
} = /*#__PURE__*/ dual(
|
|
330
395
|
2,
|
|
331
396
|
<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,
|
|
397
|
+
new ParserImpl(self.step, name, describedNode(self))
|
|
333
398
|
)
|
|
334
399
|
|
|
335
400
|
/**
|
|
@@ -390,6 +455,40 @@ export const fail = <E>(error: E): Parser<never, E, never, Input> =>
|
|
|
390
455
|
|
|
391
456
|
const failBacktrack: Node = { _tag: "Fail", mode: "Backtrack" }
|
|
392
457
|
|
|
458
|
+
/**
|
|
459
|
+
* A parser that fails (backtracks) with the given user error, reporting it
|
|
460
|
+
* at `offset` rather than where the parser runs. Use it for a check that
|
|
461
|
+
* can only run once a whole construct has parsed but should point at the
|
|
462
|
+
* part at fault: return it from a {@link flatMap} continuation, with an
|
|
463
|
+
* offset taken from {@link location} or {@link withSpan}. Like any failure,
|
|
464
|
+
* one at a later offset is preferred over the failures of other
|
|
465
|
+
* alternatives (the furthest failure wins). The offset is clamped to the
|
|
466
|
+
* input.
|
|
467
|
+
*
|
|
468
|
+
* @example
|
|
469
|
+
* ```ts
|
|
470
|
+
* import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
471
|
+
*
|
|
472
|
+
* const tag = (open: string) => Parser.delimited(Token.literal(open), Ascii.alpha1, Token.literal(">"))
|
|
473
|
+
* const element = Parser.seq(tag("<"), Parser.location, tag("</")).pipe(
|
|
474
|
+
* Parser.flatMap(([start, at, end]) =>
|
|
475
|
+
* start === end ? Parser.succeed(start) : Parser.failAt(at, `expected </${start}>`)
|
|
476
|
+
* )
|
|
477
|
+
* )
|
|
478
|
+
* // The error points at the end tag (offset 3), not at the start of the element
|
|
479
|
+
* Parser.parse(element, "<a></b>")
|
|
480
|
+
* ```
|
|
481
|
+
*
|
|
482
|
+
* @since 0.5.0
|
|
483
|
+
* @category constructors
|
|
484
|
+
*/
|
|
485
|
+
export const failAt = <E>(offset: number, error: E): Parser<never, E, never, Input> =>
|
|
486
|
+
described<never, E, never, Input>(
|
|
487
|
+
(state) => state.failCause("Backtrack", "Fail", error, offset < 0 ? 0 : offset > state.end ? state.end : offset),
|
|
488
|
+
"failAt",
|
|
489
|
+
failBacktrack
|
|
490
|
+
)
|
|
491
|
+
|
|
393
492
|
/**
|
|
394
493
|
* A parser that always fails (backtracks) without a cause. This is winnow's
|
|
395
494
|
* `fail` and nom's `fail()`.
|
|
@@ -737,6 +836,72 @@ export const tryMap: {
|
|
|
737
836
|
}
|
|
738
837
|
)
|
|
739
838
|
|
|
839
|
+
/**
|
|
840
|
+
* Transforms the output of a parser with a function that returns
|
|
841
|
+
* `Input.Fail` to reject it: the allocation-free form of {@link tryMap} and
|
|
842
|
+
* {@link filterMap}, for checks that run on nearly every token. Compiled
|
|
843
|
+
* code compares the result with the sentinel instead of allocating and
|
|
844
|
+
* unwrapping a `Result` or an `Option` on every success.
|
|
845
|
+
*
|
|
846
|
+
* On failure the parser backtracks to where it started. With `orFailWith`,
|
|
847
|
+
* the user error it builds (only on failure) becomes the `cause` of the
|
|
848
|
+
* `ParseError`, as with `tryMap`; without, the failure has no cause, as
|
|
849
|
+
* with `filterMap`.
|
|
850
|
+
*
|
|
851
|
+
* @example
|
|
852
|
+
* ```ts
|
|
853
|
+
* import { Ascii, Input, Parser } from "@thomasfosterau/effect-parse"
|
|
854
|
+
*
|
|
855
|
+
* const byte = Ascii.decUint.pipe(
|
|
856
|
+
* Parser.mapOrFail(
|
|
857
|
+
* (n) => (n < 256 ? n : Input.Fail),
|
|
858
|
+
* (n) => `${n} is not a byte`
|
|
859
|
+
* )
|
|
860
|
+
* )
|
|
861
|
+
* Parser.parse(byte, "300") // fails with the cause "300 is not a byte"
|
|
862
|
+
* ```
|
|
863
|
+
*
|
|
864
|
+
* @since 0.5.0
|
|
865
|
+
* @category mapping
|
|
866
|
+
*/
|
|
867
|
+
export const mapOrFail: {
|
|
868
|
+
<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>
|
|
869
|
+
<A, B, E2>(
|
|
870
|
+
f: (a: A) => B | Fail,
|
|
871
|
+
orFailWith: (a: A) => E2
|
|
872
|
+
): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<Exclude<B, Fail>, E | E2, R, I>
|
|
873
|
+
<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>
|
|
874
|
+
<A, E, R, I extends Input, B, E2>(
|
|
875
|
+
self: Parser<A, E, R, I>,
|
|
876
|
+
f: (a: A) => B | Fail,
|
|
877
|
+
orFailWith: (a: A) => E2
|
|
878
|
+
): Parser<Exclude<B, Fail>, E | E2, R, I>
|
|
879
|
+
} = /*#__PURE__*/ dual(
|
|
880
|
+
(args) => isParser(args[0]),
|
|
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<B, E | E2, R, I> => {
|
|
886
|
+
const step = self.step
|
|
887
|
+
return described(
|
|
888
|
+
(state) => {
|
|
889
|
+
const start = state.pos
|
|
890
|
+
const a = step(state)
|
|
891
|
+
if (a === Fail) return Fail
|
|
892
|
+
const b = f(a)
|
|
893
|
+
if (b !== Fail) return b
|
|
894
|
+
state.rewind(start)
|
|
895
|
+
return orFailWith === undefined
|
|
896
|
+
? state.backtrack("Verify")
|
|
897
|
+
: state.failCause("Backtrack", "Verify", orFailWith(a))
|
|
898
|
+
},
|
|
899
|
+
self.name,
|
|
900
|
+
{ _tag: "MapOrFail", self, f }
|
|
901
|
+
)
|
|
902
|
+
}
|
|
903
|
+
)
|
|
904
|
+
|
|
740
905
|
/**
|
|
741
906
|
* Like {@link tryMap}, but the function may throw. Thrown values become the
|
|
742
907
|
* `cause` of the error.
|
|
@@ -952,6 +1117,51 @@ export const withSpan = <A, E, R, I extends Input>(
|
|
|
952
1117
|
)
|
|
953
1118
|
}
|
|
954
1119
|
|
|
1120
|
+
/**
|
|
1121
|
+
* Transforms the output of a parser together with the offsets it consumed,
|
|
1122
|
+
* `[start, end)`: the allocation-free spelling of
|
|
1123
|
+
* `map(withSpan(p), ([a, span]) => f(a, span.start, span.end))`, which
|
|
1124
|
+
* builds a tuple and a span object on every success. Compiled code passes
|
|
1125
|
+
* the offsets to `f` directly.
|
|
1126
|
+
*
|
|
1127
|
+
* @example
|
|
1128
|
+
* ```ts
|
|
1129
|
+
* import { Ascii, Parser } from "@thomasfosterau/effect-parse"
|
|
1130
|
+
*
|
|
1131
|
+
* const name = Ascii.alpha1.pipe(Parser.mapSpan((text, start, end) => ({ text, start, end })))
|
|
1132
|
+
* Parser.parse(name, "abc") // Result.succeed({ text: "abc", start: 0, end: 3 })
|
|
1133
|
+
* ```
|
|
1134
|
+
*
|
|
1135
|
+
* @since 0.5.0
|
|
1136
|
+
* @category consumed input
|
|
1137
|
+
*/
|
|
1138
|
+
export const mapSpan: {
|
|
1139
|
+
<A, B>(
|
|
1140
|
+
f: (a: A, start: number, end: number) => B
|
|
1141
|
+
): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E, R, I>
|
|
1142
|
+
<A, E, R, I extends Input, B>(
|
|
1143
|
+
self: Parser<A, E, R, I>,
|
|
1144
|
+
f: (a: A, start: number, end: number) => B
|
|
1145
|
+
): Parser<B, E, R, I>
|
|
1146
|
+
} = /*#__PURE__*/ dual(
|
|
1147
|
+
2,
|
|
1148
|
+
<A, E, R, I extends Input, B>(
|
|
1149
|
+
self: Parser<A, E, R, I>,
|
|
1150
|
+
f: (a: A, start: number, end: number) => B
|
|
1151
|
+
): Parser<B, E, R, I> => {
|
|
1152
|
+
const step = self.step
|
|
1153
|
+
return described(
|
|
1154
|
+
(state) => {
|
|
1155
|
+
const start = state.pos
|
|
1156
|
+
const a = step(state)
|
|
1157
|
+
return a === Fail ? Fail : f(a, start, state.pos)
|
|
1158
|
+
},
|
|
1159
|
+
self.name,
|
|
1160
|
+
{ _tag: "MapSpan", self, f }
|
|
1161
|
+
)
|
|
1162
|
+
}
|
|
1163
|
+
)
|
|
1164
|
+
|
|
955
1165
|
// -----------------------------------------------------------------------------
|
|
956
1166
|
// Error management
|
|
957
1167
|
// -----------------------------------------------------------------------------
|
|
@@ -1004,6 +1214,9 @@ export const cut = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<
|
|
|
1004
1214
|
state.mode = "Cut"
|
|
1005
1215
|
return a
|
|
1006
1216
|
}
|
|
1217
|
+
// SAFETY: `state.deepest` is declared `Failure | undefined`; the
|
|
1218
|
+
// assertion only undoes the narrowing to `undefined` from the
|
|
1219
|
+
// assignment above, which misses that `step` may have recorded a failure
|
|
1007
1220
|
const inner = state.deepest as Failure | undefined
|
|
1008
1221
|
if (deepest !== undefined && (inner === undefined || deepest.offset > inner.offset)) state.deepest = deepest
|
|
1009
1222
|
return a
|
|
@@ -1114,6 +1327,212 @@ export const expected: {
|
|
|
1114
1327
|
}
|
|
1115
1328
|
)
|
|
1116
1329
|
|
|
1330
|
+
/**
|
|
1331
|
+
* Runs a parser without letting it add to the expectations of later errors
|
|
1332
|
+
* (chumsky's `quiet`). Use it on skippers, such as whitespace and comments,
|
|
1333
|
+
* that may appear between any two tokens: they stop by failing, and without
|
|
1334
|
+
* `quiet` what they would have accepted (`" "`, `"(:"`, ...) is listed in
|
|
1335
|
+
* every error raised after them.
|
|
1336
|
+
*
|
|
1337
|
+
* When the parser succeeds, the hints and the furthest failure it recorded
|
|
1338
|
+
* are forgotten, as after a lookahead that succeeded. When it fails
|
|
1339
|
+
* recoverably without consuming input, its failure expects nothing, so an
|
|
1340
|
+
* enclosing `alt` or `opt` reports only the other alternatives. A failure
|
|
1341
|
+
* after consuming input (an unterminated comment) or an unrecoverable one
|
|
1342
|
+
* is kept as it is. `Parser.expected` cannot do this: it relabels a failure,
|
|
1343
|
+
* and a skipper that matches zero or more times never fails.
|
|
1344
|
+
*
|
|
1345
|
+
* @example
|
|
1346
|
+
* ```ts
|
|
1347
|
+
* import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
1348
|
+
*
|
|
1349
|
+
* const comment = Parser.delimited(Token.literal("(:"), Token.takeTill(":"), Token.literal(":)"))
|
|
1350
|
+
* const skip = Parser.quiet(Parser.repeat(Parser.alt(Ascii.multispace1, comment)))
|
|
1351
|
+
* const number = Parser.preceded(skip, Ascii.digit1)
|
|
1352
|
+
* // Fails expecting only a digit: without `quiet`, also whitespace and "(:"
|
|
1353
|
+
* Parser.parse(number, " (: one :) x")
|
|
1354
|
+
* ```
|
|
1355
|
+
*
|
|
1356
|
+
* @since 0.5.0
|
|
1357
|
+
* @category error management
|
|
1358
|
+
*/
|
|
1359
|
+
export const quiet = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<A, E, R, I> => {
|
|
1360
|
+
const step = self.step
|
|
1361
|
+
return described(
|
|
1362
|
+
(state) => {
|
|
1363
|
+
const start = state.pos
|
|
1364
|
+
const hintOffset = state.hintOffset
|
|
1365
|
+
const hintExpected = state.hintExpected
|
|
1366
|
+
const deepest = state.deepest
|
|
1367
|
+
const a = step(state)
|
|
1368
|
+
if (a !== Fail) {
|
|
1369
|
+
state.hintOffset = hintOffset
|
|
1370
|
+
state.hintExpected = hintExpected
|
|
1371
|
+
state.deepest = deepest
|
|
1372
|
+
return a
|
|
1373
|
+
}
|
|
1374
|
+
if (state.mode === "Backtrack" && state.failure.offset === start) {
|
|
1375
|
+
const f = state.failure
|
|
1376
|
+
const expected = hintOffset === start ? hintExpected : []
|
|
1377
|
+
state.hintOffset = hintOffset
|
|
1378
|
+
state.hintExpected = hintExpected
|
|
1379
|
+
state.deepest = deepest
|
|
1380
|
+
state.failure = new Failure(f.offset, f.kind, expected, f.context, f.hasCause, f.cause, f.message, f.needed)
|
|
1381
|
+
}
|
|
1382
|
+
return a
|
|
1383
|
+
},
|
|
1384
|
+
self.name,
|
|
1385
|
+
{ _tag: "Quiet", self }
|
|
1386
|
+
)
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
/**
|
|
1390
|
+
* What a fast path (see {@link withFastPath}) returns to leave the input at
|
|
1391
|
+
* the current offset to its grammar.
|
|
1392
|
+
*
|
|
1393
|
+
* @since 0.5.0
|
|
1394
|
+
* @category fast paths
|
|
1395
|
+
*/
|
|
1396
|
+
export const Defer: typeof DeferSymbol = DeferSymbol
|
|
1397
|
+
|
|
1398
|
+
/**
|
|
1399
|
+
* The type of {@link Defer}.
|
|
1400
|
+
*
|
|
1401
|
+
* @since 0.5.0
|
|
1402
|
+
* @category fast paths
|
|
1403
|
+
*/
|
|
1404
|
+
export type Defer = typeof DeferSymbol
|
|
1405
|
+
|
|
1406
|
+
/**
|
|
1407
|
+
* Puts a hand-written fast path in front of a grammar, which stays the
|
|
1408
|
+
* specification: `fast` runs on the state, at the offset where the grammar
|
|
1409
|
+
* would, and either returns the value the grammar would return, with
|
|
1410
|
+
* `state.pos` where the grammar would stop, or returns {@link Defer}, and
|
|
1411
|
+
* the grammar runs from that offset instead. Use it where a scanner written
|
|
1412
|
+
* by hand (`indexOf`, a lookup table, interned names) beats compiled code on
|
|
1413
|
+
* the tokens that dominate an input, and let the grammar handle everything
|
|
1414
|
+
* else.
|
|
1415
|
+
*
|
|
1416
|
+
* The fast path runs in compiled code only (see the `Compiler` module).
|
|
1417
|
+
* Interpreted runs, and so every error, every `Syntax` tree and every trace,
|
|
1418
|
+
* come from the grammar alone, so they are exactly the grammar's. It must
|
|
1419
|
+
* defer wherever it would not return exactly what the grammar returns, and
|
|
1420
|
+
* on partial input wherever the grammar might report `Incomplete`; check it
|
|
1421
|
+
* against the grammar with {@link verifyFastPath}.
|
|
1422
|
+
*
|
|
1423
|
+
* @example
|
|
1424
|
+
* ```ts
|
|
1425
|
+
* import { Compiler, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
1426
|
+
*
|
|
1427
|
+
* const text = Token.takeTill("<", { min: 1 })
|
|
1428
|
+
* // The same run, found with `indexOf`; anything else (an empty run, the end of a partial input) defers
|
|
1429
|
+
* const fastText = Compiler.jit(
|
|
1430
|
+
* Parser.withFastPath(text, (state) => {
|
|
1431
|
+
* const end = state.input.indexOf("<", state.pos)
|
|
1432
|
+
* if (end <= state.pos || end > state.end) return Parser.Defer
|
|
1433
|
+
* const value = state.input.slice(state.pos, end)
|
|
1434
|
+
* state.pos = end
|
|
1435
|
+
* return value
|
|
1436
|
+
* })
|
|
1437
|
+
* )
|
|
1438
|
+
* ```
|
|
1439
|
+
*
|
|
1440
|
+
* @since 0.5.0
|
|
1441
|
+
* @category fast paths
|
|
1442
|
+
*/
|
|
1443
|
+
export const withFastPath: {
|
|
1444
|
+
<A, I extends Input>(fast: (state: State<I>) => A | Defer): <E, R>(grammar: Parser<A, E, R, I>) => Parser<A, E, R, I>
|
|
1445
|
+
<A, E, R, I extends Input>(grammar: Parser<A, E, R, I>, fast: (state: State<I>) => A | Defer): Parser<A, E, R, I>
|
|
1446
|
+
} = /*#__PURE__*/ dual(
|
|
1447
|
+
2,
|
|
1448
|
+
<A, E, R, I extends Input>(grammar: Parser<A, E, R, I>, fast: (state: State<I>) => A | Defer): Parser<A, E, R, I> =>
|
|
1449
|
+
// The interpreter runs the grammar alone: it is the reference, and it builds every error
|
|
1450
|
+
described(grammar.step, grammar.name, { _tag: "FastPath", self: grammar, fast })
|
|
1451
|
+
)
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* A place where a fast path disagrees with its grammar (see
|
|
1455
|
+
* {@link verifyFastPath}).
|
|
1456
|
+
*
|
|
1457
|
+
* @since 0.5.0
|
|
1458
|
+
* @category fast paths
|
|
1459
|
+
*/
|
|
1460
|
+
export interface FastPathMismatch<A, I extends Input> {
|
|
1461
|
+
readonly input: I
|
|
1462
|
+
readonly offset: number
|
|
1463
|
+
readonly partial: boolean
|
|
1464
|
+
/** What the fast path returned, and where it stopped. */
|
|
1465
|
+
readonly fast: { readonly value: A; readonly end: number }
|
|
1466
|
+
/** What the grammar returned, and where it stopped, or how it failed. */
|
|
1467
|
+
readonly grammar: { readonly value: A; readonly end: number } | { readonly failed: ErrMode }
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* Checks a fast path against its grammar (see {@link withFastPath}): on
|
|
1472
|
+
* every input, at every offset, on complete and on partial input, wherever
|
|
1473
|
+
* `fast` returns a value, the grammar (run interpreted) must return an
|
|
1474
|
+
* equal value (by `Equal.equals`, or `options.equivalence`) and stop at the
|
|
1475
|
+
* same offset. Returns every place where it doesn't, so a test asserts that
|
|
1476
|
+
* the result is empty; generated inputs, mutated and truncated, find the
|
|
1477
|
+
* most. Grammars that use services are not supported.
|
|
1478
|
+
*
|
|
1479
|
+
* @example
|
|
1480
|
+
* ```ts
|
|
1481
|
+
* import { Input, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
1482
|
+
*
|
|
1483
|
+
* const text = Token.takeTill("<", { min: 1 })
|
|
1484
|
+
* const fast = (state: Input.State<string>) => {
|
|
1485
|
+
* const end = state.input.indexOf("<", state.pos)
|
|
1486
|
+
* if (end <= state.pos || end > state.end) return Parser.Defer
|
|
1487
|
+
* const value = state.input.slice(state.pos, end)
|
|
1488
|
+
* state.pos = end
|
|
1489
|
+
* return value
|
|
1490
|
+
* }
|
|
1491
|
+
* Parser.verifyFastPath(text, fast, ["a<b", "<", "abc"]) // []
|
|
1492
|
+
* ```
|
|
1493
|
+
*
|
|
1494
|
+
* @since 0.5.0
|
|
1495
|
+
* @category fast paths
|
|
1496
|
+
*/
|
|
1497
|
+
export const verifyFastPath = <A, E, I extends Input>(
|
|
1498
|
+
grammar: Parser<A, E, never, I>,
|
|
1499
|
+
fast: (state: State<I>) => A | Defer,
|
|
1500
|
+
inputs: Iterable<I>,
|
|
1501
|
+
options?: { readonly equivalence?: ((a: A, b: A) => boolean) | undefined }
|
|
1502
|
+
): ReadonlyArray<FastPathMismatch<A, I>> => {
|
|
1503
|
+
const equivalence = options?.equivalence ?? Equal.equals
|
|
1504
|
+
const mismatches: Array<FastPathMismatch<A, I>> = []
|
|
1505
|
+
for (const input of inputs) {
|
|
1506
|
+
const length = new State(input).end
|
|
1507
|
+
for (let offset = 0; offset <= length; offset++) {
|
|
1508
|
+
for (const partial of [false, true]) {
|
|
1509
|
+
const mismatch = checkFastPath(grammar, fast, input, offset, partial, equivalence)
|
|
1510
|
+
if (mismatch !== undefined) mismatches.push(mismatch)
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
return mismatches
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
/** Where `fast` disagrees with `grammar` at `offset`, if it does (see {@link verifyFastPath}). */
|
|
1518
|
+
const checkFastPath = <A, E, I extends Input>(
|
|
1519
|
+
grammar: Parser<A, E, never, I>,
|
|
1520
|
+
fast: (state: State<I>) => A | Defer,
|
|
1521
|
+
input: I,
|
|
1522
|
+
offset: number,
|
|
1523
|
+
partial: boolean,
|
|
1524
|
+
equivalence: (a: A, b: A) => boolean
|
|
1525
|
+
): FastPathMismatch<A, I> | undefined => {
|
|
1526
|
+
const state = new State(input, { offset, partial })
|
|
1527
|
+
const a = fast(state)
|
|
1528
|
+
if (a === Defer) return undefined
|
|
1529
|
+
const expected = new State(input, { offset, partial })
|
|
1530
|
+
const b = Stack.run(grammar.step, expected)
|
|
1531
|
+
if (b !== Fail && expected.pos === state.pos && equivalence(a, b)) return undefined
|
|
1532
|
+
const grammarOutcome = b === Fail ? { failed: expected.mode } : { value: b, end: expected.pos }
|
|
1533
|
+
return { input, offset, partial, fast: { value: a, end: state.pos }, grammar: grammarOutcome }
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1117
1536
|
/**
|
|
1118
1537
|
* Transforms the user error (cause) of a parser's failures.
|
|
1119
1538
|
*
|
|
@@ -1132,6 +1551,8 @@ export const mapError: {
|
|
|
1132
1551
|
const a = step(state)
|
|
1133
1552
|
if (a === Fail && state.failure.hasCause) {
|
|
1134
1553
|
const x = state.failure
|
|
1554
|
+
// SAFETY: a failure with `hasCause` from `self` carries a cause raised
|
|
1555
|
+
// by a `fail` / `mapError` inside it, whose user error type is `E`
|
|
1135
1556
|
state.replaceFailure(
|
|
1136
1557
|
new Failure(x.offset, x.kind, x.expected, x.context, true, f(x.cause as E), x.message, x.needed)
|
|
1137
1558
|
)
|
|
@@ -1376,6 +1797,7 @@ export const seq = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1376
1797
|
Parser.Services<Ps[number]>,
|
|
1377
1798
|
Parser.InputOf<Ps[number]>
|
|
1378
1799
|
> => {
|
|
1800
|
+
type Out = { -readonly [K in keyof Ps]: Parser.Success<Ps[K]> }
|
|
1379
1801
|
const steps = parsers.map((p) => p.step)
|
|
1380
1802
|
const n = steps.length
|
|
1381
1803
|
return described(
|
|
@@ -1386,13 +1808,58 @@ export const seq = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1386
1808
|
if (a === Fail) return Fail
|
|
1387
1809
|
out[i] = a
|
|
1388
1810
|
}
|
|
1389
|
-
|
|
1811
|
+
// SAFETY: `out` has one slot per parser, and slot `i` holds the
|
|
1812
|
+
// output of `parsers[i]`, which is `Parser.Success<Ps[i]>`
|
|
1813
|
+
return out as Out
|
|
1390
1814
|
},
|
|
1391
1815
|
"seq",
|
|
1392
1816
|
{ _tag: "Seq", parsers, output: "tuple" }
|
|
1393
1817
|
)
|
|
1394
1818
|
}
|
|
1395
1819
|
|
|
1820
|
+
/**
|
|
1821
|
+
* Runs parsers in sequence and combines their outputs with `f`, which
|
|
1822
|
+
* receives one argument per parser: the allocation-free spelling of
|
|
1823
|
+
* `map(seq(...parsers), ([a, b, ...]) => f(a, b, ...))`, which builds a
|
|
1824
|
+
* tuple on every success. Compiled code calls `f` with the values directly.
|
|
1825
|
+
* {@link zipWith} is the two-parser, pipeable form.
|
|
1826
|
+
*
|
|
1827
|
+
* @example
|
|
1828
|
+
* ```ts
|
|
1829
|
+
* import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
1830
|
+
*
|
|
1831
|
+
* const point = Parser.seqWith(
|
|
1832
|
+
* [Ascii.decInt, Token.literal(","), Ascii.decInt],
|
|
1833
|
+
* (x, _, y) => ({ x, y })
|
|
1834
|
+
* )
|
|
1835
|
+
* Parser.parse(point, "3,-4") // Result.succeed({ x: 3, y: -4 })
|
|
1836
|
+
* ```
|
|
1837
|
+
*
|
|
1838
|
+
* @since 0.5.0
|
|
1839
|
+
* @category sequencing
|
|
1840
|
+
*/
|
|
1841
|
+
export const seqWith = <const Ps extends ReadonlyArray<Parser.Any>, B>(
|
|
1842
|
+
parsers: Ps,
|
|
1843
|
+
f: (...values: { -readonly [K in keyof Ps]: Parser.Success<Ps[K]> }) => B
|
|
1844
|
+
): Parser<B, Parser.Error<Ps[number]>, Parser.Services<Ps[number]>, Parser.InputOf<Ps[number]>> => {
|
|
1845
|
+
const steps = parsers.map((p) => p.step)
|
|
1846
|
+
const n = steps.length
|
|
1847
|
+
const g: (...values: Array<any>) => B = f
|
|
1848
|
+
return described(
|
|
1849
|
+
(state: State<any>) => {
|
|
1850
|
+
const values = new Array<unknown>(n)
|
|
1851
|
+
for (let i = 0; i < n; i++) {
|
|
1852
|
+
const a = steps[i](state)
|
|
1853
|
+
if (a === Fail) return Fail
|
|
1854
|
+
values[i] = a
|
|
1855
|
+
}
|
|
1856
|
+
return g(...values)
|
|
1857
|
+
},
|
|
1858
|
+
"seqWith",
|
|
1859
|
+
{ _tag: "SeqWith", parsers, f }
|
|
1860
|
+
)
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1396
1863
|
/**
|
|
1397
1864
|
* Runs a record of parsers in sequence (in key order), collecting their
|
|
1398
1865
|
* outputs in a record. This is the Effect counterpart to winnow's
|
|
@@ -1409,18 +1876,21 @@ export const struct = <const Fields extends Readonly<Record<string, Parser.Any>>
|
|
|
1409
1876
|
Parser.Services<Fields[keyof Fields]>,
|
|
1410
1877
|
Parser.InputOf<Fields[keyof Fields]>
|
|
1411
1878
|
> => {
|
|
1412
|
-
|
|
1879
|
+
type Out = Simplify<{ -readonly [K in keyof Fields]: Parser.Success<Fields[K]> }>
|
|
1880
|
+
const keys = Rec.keys(fields)
|
|
1413
1881
|
const steps = keys.map((k) => fields[k].step)
|
|
1414
1882
|
const n = keys.length
|
|
1415
1883
|
return described(
|
|
1416
1884
|
(state: State<any>) => {
|
|
1417
|
-
const out: Record<string,
|
|
1885
|
+
const out: Record<string, Parser.Success<Fields[keyof Fields]>> = {}
|
|
1418
1886
|
for (let i = 0; i < n; i++) {
|
|
1419
1887
|
const a = steps[i](state)
|
|
1420
1888
|
if (a === Fail) return Fail
|
|
1421
1889
|
out[keys[i]] = a
|
|
1422
1890
|
}
|
|
1423
|
-
|
|
1891
|
+
// SAFETY: the loop has set every own key of `fields`, each to the
|
|
1892
|
+
// output of that key's parser, `Parser.Success<Fields[K]>`
|
|
1893
|
+
return out as Out
|
|
1424
1894
|
},
|
|
1425
1895
|
"struct",
|
|
1426
1896
|
{ _tag: "Struct", keys, parsers: keys.map((k) => fields[k]) }
|
|
@@ -1676,9 +2146,14 @@ export const gen = <Eff extends Parser.Any, AEff>(
|
|
|
1676
2146
|
while (!next.done) {
|
|
1677
2147
|
const a = next.value.step(state)
|
|
1678
2148
|
if (a === Fail) {
|
|
1679
|
-
|
|
2149
|
+
// SAFETY: the value passed to `return` only becomes the completion value
|
|
2150
|
+
// of the abandoned generator, which nothing reads
|
|
2151
|
+
iterator.return?.(undefined as AEff)
|
|
1680
2152
|
return Fail
|
|
1681
2153
|
}
|
|
2154
|
+
// SAFETY: `a` is the output of the parser just yielded, which is what its
|
|
2155
|
+
// `yield*` evaluates to (typed by `ParserIterator`'s return value, not by
|
|
2156
|
+
// the generator's `never` next type)
|
|
1682
2157
|
next = iterator.next(a as never)
|
|
1683
2158
|
}
|
|
1684
2159
|
return next.value
|
|
@@ -1786,7 +2261,7 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1786
2261
|
> => {
|
|
1787
2262
|
const steps = parsers.map((p) => p.step)
|
|
1788
2263
|
const n = steps.length
|
|
1789
|
-
if (n === 0) return reject
|
|
2264
|
+
if (n === 0) return reject
|
|
1790
2265
|
if (n === 1) return parsers[0]
|
|
1791
2266
|
return described(
|
|
1792
2267
|
(state: State<any>) => {
|
|
@@ -1839,6 +2314,36 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1839
2314
|
)
|
|
1840
2315
|
}
|
|
1841
2316
|
|
|
2317
|
+
/**
|
|
2318
|
+
* The overloads of {@link dispatch}.
|
|
2319
|
+
*
|
|
2320
|
+
* @since 0.5.0
|
|
2321
|
+
* @category choice
|
|
2322
|
+
*/
|
|
2323
|
+
export interface DispatchFn {
|
|
2324
|
+
<K, E, R, I extends Input, P extends Parser.Any>(
|
|
2325
|
+
selector: Parser<K, E, R, I>,
|
|
2326
|
+
cases: (key: K) => P | undefined
|
|
2327
|
+
): Parser<Parser.Success<P>, E | Parser.Error<P>, R | Parser.Services<P>, I & Parser.InputOf<P>>
|
|
2328
|
+
<
|
|
2329
|
+
K extends PropertyKey,
|
|
2330
|
+
E,
|
|
2331
|
+
R,
|
|
2332
|
+
I extends Input,
|
|
2333
|
+
const Cases extends { readonly [key in K]?: Parser.Any },
|
|
2334
|
+
Otherwise extends Parser.Any = never
|
|
2335
|
+
>(
|
|
2336
|
+
selector: Parser<K, E, R, I>,
|
|
2337
|
+
cases: Cases,
|
|
2338
|
+
otherwise?: Otherwise
|
|
2339
|
+
): Parser<
|
|
2340
|
+
Parser.Success<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
2341
|
+
E | Parser.Error<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
2342
|
+
R | Parser.Services<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
2343
|
+
I & Parser.InputOf<NonNullable<Cases[keyof Cases]> | Otherwise>
|
|
2344
|
+
>
|
|
2345
|
+
}
|
|
2346
|
+
|
|
1842
2347
|
/**
|
|
1843
2348
|
* Selects the parser to run next based on the output of `selector`, like a
|
|
1844
2349
|
* `match` over the input (winnow's `dispatch!`). If no case matches, the
|
|
@@ -1873,29 +2378,7 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1873
2378
|
* @since 0.1.0
|
|
1874
2379
|
* @category choice
|
|
1875
2380
|
*/
|
|
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
|
-
} = (
|
|
2381
|
+
export const dispatch: DispatchFn = (
|
|
1899
2382
|
selector: Parser.Any,
|
|
1900
2383
|
cases: ((key: any) => Parser.Any | undefined) | Record<PropertyKey, Parser.Any>,
|
|
1901
2384
|
otherwise?: Parser.Any
|
|
@@ -1943,37 +2426,44 @@ export const permutation = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1943
2426
|
Parser.Services<Ps[number]>,
|
|
1944
2427
|
Parser.InputOf<Ps[number]>
|
|
1945
2428
|
> => {
|
|
1946
|
-
|
|
1947
|
-
const n =
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
let
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
2429
|
+
type Out = { -readonly [K in keyof Ps]: Parser.Success<Ps[K]> }
|
|
2430
|
+
const n = parsers.length
|
|
2431
|
+
// The parsers inside compile: the step is built from theirs (see `combinator`)
|
|
2432
|
+
return combinator(
|
|
2433
|
+
parsers,
|
|
2434
|
+
(steps) => (state: State<any>) => {
|
|
2435
|
+
// A round's failures, merged only if no parser matches in it (#44)
|
|
2436
|
+
const ties = new TiedFailures()
|
|
2437
|
+
const done = new Array<boolean>(n).fill(false)
|
|
2438
|
+
const out = new Array(n)
|
|
2439
|
+
let remaining = n
|
|
2440
|
+
while (remaining > 0) {
|
|
2441
|
+
ties.clear()
|
|
2442
|
+
let matched = false
|
|
2443
|
+
for (let i = 0; i < n; i++) {
|
|
2444
|
+
if (done[i]) continue
|
|
2445
|
+
const start = state.pos
|
|
2446
|
+
const a = steps[i](state)
|
|
2447
|
+
if (a !== Fail) {
|
|
2448
|
+
out[i] = a
|
|
2449
|
+
done[i] = true
|
|
2450
|
+
remaining--
|
|
2451
|
+
matched = true
|
|
2452
|
+
break
|
|
2453
|
+
}
|
|
2454
|
+
if (state.mode !== "Backtrack") return Fail
|
|
2455
|
+
state.rewind(start)
|
|
2456
|
+
ties.add(state)
|
|
1967
2457
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
ties.add(state)
|
|
2458
|
+
// Some parser is not done yet, and each one that fails is folded in
|
|
2459
|
+
if (!matched) return ties.fail(state)
|
|
1971
2460
|
}
|
|
1972
|
-
//
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
2461
|
+
// SAFETY: every parser has matched once (`remaining` is 0), and slot
|
|
2462
|
+
// `i` holds the output of `parsers[i]`, which is `Parser.Success<Ps[i]>`
|
|
2463
|
+
return out as Out
|
|
2464
|
+
},
|
|
2465
|
+
"permutation"
|
|
2466
|
+
)
|
|
1977
2467
|
}
|
|
1978
2468
|
|
|
1979
2469
|
// -----------------------------------------------------------------------------
|
|
@@ -2021,9 +2511,13 @@ const foldImpl = <A, B, E, R, I extends Input>(
|
|
|
2021
2511
|
const entry = state.pos
|
|
2022
2512
|
let acc: B
|
|
2023
2513
|
let count = 0
|
|
2514
|
+
// SAFETY: `key` belongs to this parser, which saves only
|
|
2515
|
+
// `FoldCheckpoint<B>`s under it (and a `Resume.Done`, narrowed below)
|
|
2024
2516
|
const resumed = state.resume === undefined ? undefined : (Resume.take(state, key, entry) as FoldCheckpoint<B>)
|
|
2025
2517
|
if (resumed instanceof Resume.Done) {
|
|
2026
2518
|
state.pos = resumed.pos
|
|
2519
|
+
// SAFETY: a `Done` under `key` is recorded by `Resume.done` below,
|
|
2520
|
+
// with this fold's result, a `B`
|
|
2027
2521
|
return resumed.value as B
|
|
2028
2522
|
}
|
|
2029
2523
|
if (resumed !== undefined) {
|
|
@@ -2262,10 +2756,14 @@ export const separated: {
|
|
|
2262
2756
|
(state) => {
|
|
2263
2757
|
const entry = state.pos
|
|
2264
2758
|
let acc: Array<A>
|
|
2759
|
+
// SAFETY: `key` belongs to this parser, which saves only
|
|
2760
|
+
// `SeparatedCheckpoint<A>`s under it (and a `Resume.Done`, narrowed below)
|
|
2265
2761
|
const resumed =
|
|
2266
2762
|
state.resume === undefined ? undefined : (Resume.take(state, key, entry) as SeparatedCheckpoint<A>)
|
|
2267
2763
|
if (resumed instanceof Resume.Done) {
|
|
2268
2764
|
state.pos = resumed.pos
|
|
2765
|
+
// SAFETY: a `Done` under `key` is recorded by `Resume.done` below,
|
|
2766
|
+
// with the elements collected, an `Array<A>`
|
|
2269
2767
|
return resumed.value as Array<A>
|
|
2270
2768
|
}
|
|
2271
2769
|
if (resumed !== undefined) {
|
|
@@ -2345,10 +2843,15 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2345
2843
|
return described(
|
|
2346
2844
|
(state) => {
|
|
2347
2845
|
const entry = state.pos
|
|
2846
|
+
// SAFETY: `key` belongs to this parser, which saves only
|
|
2847
|
+
// `FoldCheckpoint<A>`s under it, through `saveFold` (and a
|
|
2848
|
+
// `Resume.Done`, narrowed below)
|
|
2348
2849
|
const resumed = state.resume === undefined ? undefined : (Resume.take(state, key, entry) as FoldCheckpoint<A>)
|
|
2349
2850
|
let acc: A | Fail
|
|
2350
2851
|
if (resumed instanceof Resume.Done) {
|
|
2351
2852
|
state.pos = resumed.pos
|
|
2853
|
+
// SAFETY: a `Done` under `key` is recorded by `Resume.done` below,
|
|
2854
|
+
// with the folded value, an `A`
|
|
2352
2855
|
return resumed.value as A
|
|
2353
2856
|
}
|
|
2354
2857
|
if (resumed !== undefined) {
|
|
@@ -2365,7 +2868,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2365
2868
|
if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
|
|
2366
2869
|
if (state.mode !== "Backtrack") return Fail
|
|
2367
2870
|
recover(state, start)
|
|
2368
|
-
return Resume.done(state, key, entry, resumed !== undefined, acc
|
|
2871
|
+
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2369
2872
|
}
|
|
2370
2873
|
if (state.pos === start) return assertConsumes(state, "separatedFoldl1")
|
|
2371
2874
|
const a = step(state)
|
|
@@ -2373,7 +2876,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2373
2876
|
if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
|
|
2374
2877
|
if (state.mode !== "Backtrack") return Fail
|
|
2375
2878
|
state.rewind(start)
|
|
2376
|
-
return Resume.done(state, key, entry, resumed !== undefined, acc
|
|
2879
|
+
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2377
2880
|
}
|
|
2378
2881
|
acc = op(acc, s, a)
|
|
2379
2882
|
}
|
|
@@ -2423,9 +2926,9 @@ export const separatedFoldr1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2423
2926
|
seps.push(s)
|
|
2424
2927
|
values.push(a)
|
|
2425
2928
|
}
|
|
2426
|
-
let acc = values[values.length - 1]
|
|
2929
|
+
let acc = values[values.length - 1]
|
|
2427
2930
|
for (let i = seps.length - 1; i >= 0; i--) {
|
|
2428
|
-
acc = op(values[i]
|
|
2931
|
+
acc = op(values[i], seps[i], acc)
|
|
2429
2932
|
}
|
|
2430
2933
|
return acc
|
|
2431
2934
|
},
|
|
@@ -2501,7 +3004,7 @@ export const provideService: {
|
|
|
2501
3004
|
return described(
|
|
2502
3005
|
(state) => {
|
|
2503
3006
|
const prev = state.context
|
|
2504
|
-
state.context = Context.add(prev, key, service)
|
|
3007
|
+
state.context = Context.add(prev, key, service)
|
|
2505
3008
|
try {
|
|
2506
3009
|
return step(state)
|
|
2507
3010
|
} finally {
|
|
@@ -2538,7 +3041,7 @@ export const provide: {
|
|
|
2538
3041
|
return described(
|
|
2539
3042
|
(state) => {
|
|
2540
3043
|
const prev = state.context
|
|
2541
|
-
state.context = Context.merge(prev, context)
|
|
3044
|
+
state.context = Context.merge(prev, context)
|
|
2542
3045
|
try {
|
|
2543
3046
|
return step(state)
|
|
2544
3047
|
} finally {
|
|
@@ -2734,18 +3237,20 @@ const NotCompiled: unique symbol = /*#__PURE__*/ Symbol("NotCompiled")
|
|
|
2734
3237
|
* input), in which case the state must be thrown away. `resumable` selects
|
|
2735
3238
|
* the build that handles partial input.
|
|
2736
3239
|
*/
|
|
2737
|
-
const runCompiled = <I extends Input>(
|
|
2738
|
-
self: Parser
|
|
3240
|
+
const runCompiled = <A, I extends Input>(
|
|
3241
|
+
self: Parser<A, unknown, unknown, I>,
|
|
2739
3242
|
state: State<I>,
|
|
2740
3243
|
whole: boolean,
|
|
2741
3244
|
resumable = false
|
|
2742
|
-
):
|
|
3245
|
+
): A | Fail | typeof NotCompiled => {
|
|
2743
3246
|
if (state.tracer !== undefined || state.recovered !== undefined) return NotCompiled
|
|
2744
|
-
const
|
|
2745
|
-
if (step === undefined) return NotCompiled
|
|
2746
|
-
const a =
|
|
3247
|
+
const compiled = Registry.compiled(self, state.inputKind, resumable)
|
|
3248
|
+
if (compiled?.step === undefined) return NotCompiled
|
|
3249
|
+
const a = Run.runCompiled(compiled.step, compiled.guarded, state)
|
|
2747
3250
|
if (a === Fail || (whole && state.pos < state.end)) return Fail
|
|
2748
|
-
|
|
3251
|
+
// SAFETY: the compilers never change semantics (the differential suites
|
|
3252
|
+
// check it), so `self`'s compiled step returns what its step returns, an `A`
|
|
3253
|
+
return a as A
|
|
2749
3254
|
}
|
|
2750
3255
|
|
|
2751
3256
|
// -----------------------------------------------------------------------------
|
|
@@ -2816,7 +3321,7 @@ const parseComplete = <A, E, R, I extends Input>(
|
|
|
2816
3321
|
const state = makeState(input, options, false)
|
|
2817
3322
|
const a = runCompiled(self, state, true)
|
|
2818
3323
|
if (a === NotCompiled) return runComplete(self, state)
|
|
2819
|
-
if (a !== Fail) return Result.succeed(a
|
|
3324
|
+
if (a !== Fail) return Result.succeed(a)
|
|
2820
3325
|
// Re-run interpreted for the diagnostics
|
|
2821
3326
|
return runComplete(self, makeState(input, options, false))
|
|
2822
3327
|
}
|
|
@@ -2834,6 +3339,25 @@ const runComplete = <A, E, R, I extends Input>(
|
|
|
2834
3339
|
return Result.succeed(a)
|
|
2835
3340
|
}
|
|
2836
3341
|
|
|
3342
|
+
/**
|
|
3343
|
+
* The overloads of {@link parse}.
|
|
3344
|
+
*
|
|
3345
|
+
* @since 0.5.0
|
|
3346
|
+
* @category running
|
|
3347
|
+
*/
|
|
3348
|
+
export interface ParseFn {
|
|
3349
|
+
<A, E, I extends Input>(
|
|
3350
|
+
self: Parser<A, E, never, I>,
|
|
3351
|
+
input: NoInfer<Source<I>>,
|
|
3352
|
+
options?: ParseOptions
|
|
3353
|
+
): Result.Result<A, ParseError.ParseError<E>>
|
|
3354
|
+
<A, E, R, I extends Input>(
|
|
3355
|
+
self: Parser<A, E, R, I>,
|
|
3356
|
+
input: NoInfer<Source<I>>,
|
|
3357
|
+
options: ParseOptionsWithContext<R>
|
|
3358
|
+
): Result.Result<A, ParseError.ParseError<E>>
|
|
3359
|
+
}
|
|
3360
|
+
|
|
2837
3361
|
/**
|
|
2838
3362
|
* Runs a parser against a complete input, requiring the whole input to be
|
|
2839
3363
|
* consumed (winnow's `Parser::parse`).
|
|
@@ -2850,37 +3374,34 @@ const runComplete = <A, E, R, I extends Input>(
|
|
|
2850
3374
|
* @since 0.1.0
|
|
2851
3375
|
* @category running
|
|
2852
3376
|
*/
|
|
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>(
|
|
3377
|
+
export const parse: ParseFn = <A, E, R, I extends Input>(
|
|
2865
3378
|
self: Parser<A, E, R, I>,
|
|
2866
3379
|
input: I | Source<I>,
|
|
2867
3380
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
2868
3381
|
): Result.Result<A, ParseError.ParseError<E>> => parseComplete(self, input, options)
|
|
2869
3382
|
|
|
2870
3383
|
/**
|
|
2871
|
-
*
|
|
3384
|
+
* The overloads of {@link parseSync}.
|
|
2872
3385
|
*
|
|
2873
|
-
* @since 0.
|
|
3386
|
+
* @since 0.5.0
|
|
2874
3387
|
* @category running
|
|
2875
3388
|
*/
|
|
2876
|
-
export
|
|
3389
|
+
export interface ParseSyncFn {
|
|
2877
3390
|
<A, E, I extends Input>(self: Parser<A, E, never, I>, input: NoInfer<Source<I>>, options?: ParseOptions): A
|
|
2878
3391
|
<A, E, R, I extends Input>(
|
|
2879
3392
|
self: Parser<A, E, R, I>,
|
|
2880
3393
|
input: NoInfer<Source<I>>,
|
|
2881
3394
|
options: ParseOptionsWithContext<R>
|
|
2882
3395
|
): A
|
|
2883
|
-
}
|
|
3396
|
+
}
|
|
3397
|
+
|
|
3398
|
+
/**
|
|
3399
|
+
* Like {@link parse}, but throws the `ParseError` on failure.
|
|
3400
|
+
*
|
|
3401
|
+
* @since 0.1.0
|
|
3402
|
+
* @category running
|
|
3403
|
+
*/
|
|
3404
|
+
export const parseSync: ParseSyncFn = <A, E, R, I extends Input>(
|
|
2884
3405
|
self: Parser<A, E, R, I>,
|
|
2885
3406
|
input: I | Source<I>,
|
|
2886
3407
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
@@ -2891,18 +3412,12 @@ export const parseSync: {
|
|
|
2891
3412
|
}
|
|
2892
3413
|
|
|
2893
3414
|
/**
|
|
2894
|
-
*
|
|
2895
|
-
* input along with the output, nom-style (winnow's `parse_peek`, nom's
|
|
2896
|
-
* `IResult`).
|
|
3415
|
+
* The overloads of {@link parsePeek}.
|
|
2897
3416
|
*
|
|
2898
|
-
*
|
|
2899
|
-
* how a caller running from an `offset` (see {@link ParseOptions}) finds
|
|
2900
|
-
* where to continue.
|
|
2901
|
-
*
|
|
2902
|
-
* @since 0.1.0
|
|
3417
|
+
* @since 0.5.0
|
|
2903
3418
|
* @category running
|
|
2904
3419
|
*/
|
|
2905
|
-
export
|
|
3420
|
+
export interface ParsePeekFn {
|
|
2906
3421
|
<A, E, I extends Input>(
|
|
2907
3422
|
self: Parser<A, E, never, I>,
|
|
2908
3423
|
input: NoInfer<Source<I>>,
|
|
@@ -2913,7 +3428,21 @@ export const parsePeek: {
|
|
|
2913
3428
|
input: NoInfer<Source<I>>,
|
|
2914
3429
|
options: ParseOptionsWithContext<R>
|
|
2915
3430
|
): Result.Result<[remaining: Slice<I>, output: A], ParseError.ParseError<E>>
|
|
2916
|
-
}
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3433
|
+
/**
|
|
3434
|
+
* Runs a parser against the start of an input, returning the remaining
|
|
3435
|
+
* input along with the output, nom-style (winnow's `parse_peek`, nom's
|
|
3436
|
+
* `IResult`).
|
|
3437
|
+
*
|
|
3438
|
+
* The parser ended at offset `input.length - remaining.length`, which is
|
|
3439
|
+
* how a caller running from an `offset` (see {@link ParseOptions}) finds
|
|
3440
|
+
* where to continue.
|
|
3441
|
+
*
|
|
3442
|
+
* @since 0.1.0
|
|
3443
|
+
* @category running
|
|
3444
|
+
*/
|
|
3445
|
+
export const parsePeek: ParsePeekFn = <A, E, R, I extends Input>(
|
|
2917
3446
|
self: Parser<A, E, R, I>,
|
|
2918
3447
|
input: I | Source<I>,
|
|
2919
3448
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
@@ -2921,7 +3450,7 @@ export const parsePeek: {
|
|
|
2921
3450
|
let state = makeState(input, options, false)
|
|
2922
3451
|
const compiled = runCompiled(self, state, false)
|
|
2923
3452
|
if (compiled !== NotCompiled && compiled !== Fail) {
|
|
2924
|
-
return Result.succeed([state.slice(state.pos, state.end), compiled
|
|
3453
|
+
return Result.succeed([state.slice(state.pos, state.end), compiled])
|
|
2925
3454
|
}
|
|
2926
3455
|
if (compiled === Fail) state = makeState(input, options, false)
|
|
2927
3456
|
const a = Stack.run(self.step, state)
|
|
@@ -2942,6 +3471,25 @@ export type PartialResult<A> =
|
|
|
2942
3471
|
| { readonly _tag: "Done"; readonly value: A; readonly offset: number }
|
|
2943
3472
|
| { readonly _tag: "Incomplete"; readonly needed: Option.Option<number> }
|
|
2944
3473
|
|
|
3474
|
+
/**
|
|
3475
|
+
* The overloads of {@link parsePartial}.
|
|
3476
|
+
*
|
|
3477
|
+
* @since 0.5.0
|
|
3478
|
+
* @category running
|
|
3479
|
+
*/
|
|
3480
|
+
export interface ParsePartialFn {
|
|
3481
|
+
<A, E, I extends Input>(
|
|
3482
|
+
self: Parser<A, E, never, I>,
|
|
3483
|
+
input: NoInfer<Source<I>>,
|
|
3484
|
+
options?: ParseOptions
|
|
3485
|
+
): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
|
|
3486
|
+
<A, E, R, I extends Input>(
|
|
3487
|
+
self: Parser<A, E, R, I>,
|
|
3488
|
+
input: NoInfer<Source<I>>,
|
|
3489
|
+
options: ParseOptionsWithContext<R>
|
|
3490
|
+
): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
|
|
3491
|
+
}
|
|
3492
|
+
|
|
2945
3493
|
/**
|
|
2946
3494
|
* Runs a parser against a *partial* input, i.e. a chunk of a stream that may
|
|
2947
3495
|
* be followed by more data (winnow's `Partial`, nom's `streaming` parsers).
|
|
@@ -2971,18 +3519,7 @@ export type PartialResult<A> =
|
|
|
2971
3519
|
* @since 0.1.0
|
|
2972
3520
|
* @category running
|
|
2973
3521
|
*/
|
|
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>(
|
|
3522
|
+
export const parsePartial: ParsePartialFn = <A, E, R, I extends Input>(
|
|
2986
3523
|
self: Parser<A, E, R, I>,
|
|
2987
3524
|
input: I | Source<I>,
|
|
2988
3525
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
@@ -2990,19 +3527,25 @@ export const parsePartial: {
|
|
|
2990
3527
|
let state = makeState(input, options, true)
|
|
2991
3528
|
const compiled = runCompiled(self, state, false, true)
|
|
2992
3529
|
if (compiled !== NotCompiled && compiled !== Fail) {
|
|
2993
|
-
return Result.succeed({ _tag: "Done", value: compiled
|
|
3530
|
+
return Result.succeed({ _tag: "Done", value: compiled, offset: state.pos })
|
|
2994
3531
|
}
|
|
3532
|
+
// Compiled code reports `Incomplete` where the interpreter does, with the same failure: the leaf that
|
|
3533
|
+
// ran out of input is the interpreter's own step, or records what it would. Only an error needs the re-run.
|
|
3534
|
+
if (compiled === Fail && state.mode === "Incomplete") return Result.succeed(incompleteOf(state))
|
|
2995
3535
|
// Re-run interpreted for `needed` and the diagnostics
|
|
2996
3536
|
if (compiled === Fail) state = makeState(input, options, true)
|
|
2997
3537
|
const a = Stack.run(self.step, state)
|
|
2998
3538
|
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
|
-
}
|
|
3539
|
+
if (state.mode === "Incomplete") return Result.succeed(incompleteOf(state))
|
|
3003
3540
|
return Result.fail(toError(state))
|
|
3004
3541
|
}
|
|
3005
3542
|
|
|
3543
|
+
/** The `Incomplete` result of a state whose parse ran out of partial input. */
|
|
3544
|
+
const incompleteOf = <A>(state: State<any>): PartialResult<A> => {
|
|
3545
|
+
const needed = state.failure.needed
|
|
3546
|
+
return { _tag: "Incomplete", needed: needed > 0 ? Option.some(needed) : Option.none() }
|
|
3547
|
+
}
|
|
3548
|
+
|
|
3006
3549
|
/**
|
|
3007
3550
|
* Runs a parser against a complete input as an `Effect`. Services required
|
|
3008
3551
|
* by the parser are taken from the Effect's context.
|
|
@@ -3121,14 +3664,12 @@ export interface RecoverableResult<A, E> {
|
|
|
3121
3664
|
}
|
|
3122
3665
|
|
|
3123
3666
|
/**
|
|
3124
|
-
*
|
|
3125
|
-
* stream). Failures handled by {@link resumeAfter} and {@link retryAfter}
|
|
3126
|
-
* are collected instead of aborting the parse.
|
|
3667
|
+
* The overloads of {@link parseRecoverable}.
|
|
3127
3668
|
*
|
|
3128
|
-
* @since 0.
|
|
3669
|
+
* @since 0.5.0
|
|
3129
3670
|
* @category running
|
|
3130
3671
|
*/
|
|
3131
|
-
export
|
|
3672
|
+
export interface ParseRecoverableFn {
|
|
3132
3673
|
<A, E, I extends Input>(
|
|
3133
3674
|
self: Parser<A, E, never, I>,
|
|
3134
3675
|
input: NoInfer<Source<I>>,
|
|
@@ -3139,7 +3680,17 @@ export const parseRecoverable: {
|
|
|
3139
3680
|
input: NoInfer<Source<I>>,
|
|
3140
3681
|
options: ParseOptionsWithContext<R>
|
|
3141
3682
|
): RecoverableResult<A, E>
|
|
3142
|
-
}
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
/**
|
|
3686
|
+
* Runs a parser with error recovery enabled (winnow's `Recoverable`
|
|
3687
|
+
* stream). Failures handled by {@link resumeAfter} and {@link retryAfter}
|
|
3688
|
+
* are collected instead of aborting the parse.
|
|
3689
|
+
*
|
|
3690
|
+
* @since 0.1.0
|
|
3691
|
+
* @category running
|
|
3692
|
+
*/
|
|
3693
|
+
export const parseRecoverable: ParseRecoverableFn = <A, E, R, I extends Input>(
|
|
3143
3694
|
self: Parser<A, E, R, I>,
|
|
3144
3695
|
input: I | Source<I>,
|
|
3145
3696
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|