@thomasfosterau/effect-parse 0.4.0 → 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 +40 -14
- 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 +2 -2
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js +1 -1
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +370 -28
- 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 +2 -2
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +3 -2
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +15 -10
- 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 +1 -2
- 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 +9 -4
- package/src/Parser.ts +682 -129
- package/src/ParserSchema.ts +1 -1
- package/src/ParserStream.ts +40 -19
- 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 +10 -4
- 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
|
|
@@ -83,6 +85,8 @@ export interface Parser<out A, out E = never, out R = never, in I extends Input
|
|
|
83
85
|
/** A human-readable name, used by `toString` and tracing. */
|
|
84
86
|
readonly name: string | undefined
|
|
85
87
|
[Symbol.iterator](): ParserIterator<Parser<A, E, R, I>>
|
|
88
|
+
/** Describes the parser: `Parser(<name>)`, or `Parser` when it has no name. */
|
|
89
|
+
toString(): string
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
/**
|
|
@@ -144,6 +148,15 @@ export declare namespace Parser {
|
|
|
144
148
|
: [P] extends [Parser<infer _A, infer _E, infer _R, infer I>]
|
|
145
149
|
? I
|
|
146
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
|
+
}
|
|
147
160
|
}
|
|
148
161
|
|
|
149
162
|
/**
|
|
@@ -223,7 +236,7 @@ class SingleShotGen<T> {
|
|
|
223
236
|
}
|
|
224
237
|
|
|
225
238
|
class ParserImpl<A, E, R, I extends Input> implements Parser<A, E, R, I> {
|
|
226
|
-
readonly [TypeId]
|
|
239
|
+
readonly [TypeId]: Parser.Variance<A, E, R, I> = variance
|
|
227
240
|
/** The description used by the compiler; `undefined` for `Opaque`. */
|
|
228
241
|
readonly [NodeId]: Node | undefined
|
|
229
242
|
constructor(
|
|
@@ -237,7 +250,7 @@ class ParserImpl<A, E, R, I extends Input> implements Parser<A, E, R, I> {
|
|
|
237
250
|
return pipeArguments(this, arguments)
|
|
238
251
|
}
|
|
239
252
|
[Symbol.iterator](): ParserIterator<Parser<A, E, R, I>> {
|
|
240
|
-
return new SingleShotGen(this)
|
|
253
|
+
return new SingleShotGen(this)
|
|
241
254
|
}
|
|
242
255
|
toString() {
|
|
243
256
|
return this.name === undefined ? "Parser" : `Parser(${this.name})`
|
|
@@ -313,7 +326,61 @@ export const described = <A, E = never, R = never, I extends Input = string>(
|
|
|
313
326
|
node: Node
|
|
314
327
|
): Parser<A, E, R, I> => new ParserImpl(step, name, node)
|
|
315
328
|
|
|
316
|
-
|
|
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
|
+
}
|
|
317
384
|
|
|
318
385
|
/**
|
|
319
386
|
* Gives a parser a name, used by `toString`.
|
|
@@ -327,7 +394,7 @@ export const named: {
|
|
|
327
394
|
} = /*#__PURE__*/ dual(
|
|
328
395
|
2,
|
|
329
396
|
<A, E, R, I extends Input>(self: Parser<A, E, R, I>, name: string): Parser<A, E, R, I> =>
|
|
330
|
-
new ParserImpl(self.step, name,
|
|
397
|
+
new ParserImpl(self.step, name, describedNode(self))
|
|
331
398
|
)
|
|
332
399
|
|
|
333
400
|
/**
|
|
@@ -388,6 +455,40 @@ export const fail = <E>(error: E): Parser<never, E, never, Input> =>
|
|
|
388
455
|
|
|
389
456
|
const failBacktrack: Node = { _tag: "Fail", mode: "Backtrack" }
|
|
390
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
|
+
|
|
391
492
|
/**
|
|
392
493
|
* A parser that always fails (backtracks) without a cause. This is winnow's
|
|
393
494
|
* `fail` and nom's `fail()`.
|
|
@@ -735,6 +836,72 @@ export const tryMap: {
|
|
|
735
836
|
}
|
|
736
837
|
)
|
|
737
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
|
+
|
|
738
905
|
/**
|
|
739
906
|
* Like {@link tryMap}, but the function may throw. Thrown values become the
|
|
740
907
|
* `cause` of the error.
|
|
@@ -743,8 +910,8 @@ export const tryMap: {
|
|
|
743
910
|
* @category mapping
|
|
744
911
|
*/
|
|
745
912
|
export const tryMapUnknown: {
|
|
746
|
-
<A, B>(f: (a: A) => B): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B,
|
|
747
|
-
<A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B,
|
|
913
|
+
<A, B>(f: (a: A) => B): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, unknown, R, I>
|
|
914
|
+
<A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B, unknown, R, I>
|
|
748
915
|
} = /*#__PURE__*/ dual(
|
|
749
916
|
2,
|
|
750
917
|
<A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B, unknown, R, I> =>
|
|
@@ -950,6 +1117,51 @@ export const withSpan = <A, E, R, I extends Input>(
|
|
|
950
1117
|
)
|
|
951
1118
|
}
|
|
952
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
|
+
|
|
953
1165
|
// -----------------------------------------------------------------------------
|
|
954
1166
|
// Error management
|
|
955
1167
|
// -----------------------------------------------------------------------------
|
|
@@ -1002,6 +1214,9 @@ export const cut = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<
|
|
|
1002
1214
|
state.mode = "Cut"
|
|
1003
1215
|
return a
|
|
1004
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
|
|
1005
1220
|
const inner = state.deepest as Failure | undefined
|
|
1006
1221
|
if (deepest !== undefined && (inner === undefined || deepest.offset > inner.offset)) state.deepest = deepest
|
|
1007
1222
|
return a
|
|
@@ -1112,6 +1327,212 @@ export const expected: {
|
|
|
1112
1327
|
}
|
|
1113
1328
|
)
|
|
1114
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
|
+
|
|
1115
1536
|
/**
|
|
1116
1537
|
* Transforms the user error (cause) of a parser's failures.
|
|
1117
1538
|
*
|
|
@@ -1130,6 +1551,8 @@ export const mapError: {
|
|
|
1130
1551
|
const a = step(state)
|
|
1131
1552
|
if (a === Fail && state.failure.hasCause) {
|
|
1132
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`
|
|
1133
1556
|
state.replaceFailure(
|
|
1134
1557
|
new Failure(x.offset, x.kind, x.expected, x.context, true, f(x.cause as E), x.message, x.needed)
|
|
1135
1558
|
)
|
|
@@ -1374,6 +1797,7 @@ export const seq = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1374
1797
|
Parser.Services<Ps[number]>,
|
|
1375
1798
|
Parser.InputOf<Ps[number]>
|
|
1376
1799
|
> => {
|
|
1800
|
+
type Out = { -readonly [K in keyof Ps]: Parser.Success<Ps[K]> }
|
|
1377
1801
|
const steps = parsers.map((p) => p.step)
|
|
1378
1802
|
const n = steps.length
|
|
1379
1803
|
return described(
|
|
@@ -1384,13 +1808,58 @@ export const seq = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1384
1808
|
if (a === Fail) return Fail
|
|
1385
1809
|
out[i] = a
|
|
1386
1810
|
}
|
|
1387
|
-
|
|
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
|
|
1388
1814
|
},
|
|
1389
1815
|
"seq",
|
|
1390
1816
|
{ _tag: "Seq", parsers, output: "tuple" }
|
|
1391
1817
|
)
|
|
1392
1818
|
}
|
|
1393
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
|
+
|
|
1394
1863
|
/**
|
|
1395
1864
|
* Runs a record of parsers in sequence (in key order), collecting their
|
|
1396
1865
|
* outputs in a record. This is the Effect counterpart to winnow's
|
|
@@ -1407,18 +1876,21 @@ export const struct = <const Fields extends Readonly<Record<string, Parser.Any>>
|
|
|
1407
1876
|
Parser.Services<Fields[keyof Fields]>,
|
|
1408
1877
|
Parser.InputOf<Fields[keyof Fields]>
|
|
1409
1878
|
> => {
|
|
1410
|
-
|
|
1879
|
+
type Out = Simplify<{ -readonly [K in keyof Fields]: Parser.Success<Fields[K]> }>
|
|
1880
|
+
const keys = Rec.keys(fields)
|
|
1411
1881
|
const steps = keys.map((k) => fields[k].step)
|
|
1412
1882
|
const n = keys.length
|
|
1413
1883
|
return described(
|
|
1414
1884
|
(state: State<any>) => {
|
|
1415
|
-
const out: Record<string,
|
|
1885
|
+
const out: Record<string, Parser.Success<Fields[keyof Fields]>> = {}
|
|
1416
1886
|
for (let i = 0; i < n; i++) {
|
|
1417
1887
|
const a = steps[i](state)
|
|
1418
1888
|
if (a === Fail) return Fail
|
|
1419
1889
|
out[keys[i]] = a
|
|
1420
1890
|
}
|
|
1421
|
-
|
|
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
|
|
1422
1894
|
},
|
|
1423
1895
|
"struct",
|
|
1424
1896
|
{ _tag: "Struct", keys, parsers: keys.map((k) => fields[k]) }
|
|
@@ -1674,9 +2146,14 @@ export const gen = <Eff extends Parser.Any, AEff>(
|
|
|
1674
2146
|
while (!next.done) {
|
|
1675
2147
|
const a = next.value.step(state)
|
|
1676
2148
|
if (a === Fail) {
|
|
1677
|
-
|
|
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)
|
|
1678
2152
|
return Fail
|
|
1679
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)
|
|
1680
2157
|
next = iterator.next(a as never)
|
|
1681
2158
|
}
|
|
1682
2159
|
return next.value
|
|
@@ -1784,7 +2261,7 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1784
2261
|
> => {
|
|
1785
2262
|
const steps = parsers.map((p) => p.step)
|
|
1786
2263
|
const n = steps.length
|
|
1787
|
-
if (n === 0) return reject
|
|
2264
|
+
if (n === 0) return reject
|
|
1788
2265
|
if (n === 1) return parsers[0]
|
|
1789
2266
|
return described(
|
|
1790
2267
|
(state: State<any>) => {
|
|
@@ -1837,6 +2314,36 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1837
2314
|
)
|
|
1838
2315
|
}
|
|
1839
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
|
+
|
|
1840
2347
|
/**
|
|
1841
2348
|
* Selects the parser to run next based on the output of `selector`, like a
|
|
1842
2349
|
* `match` over the input (winnow's `dispatch!`). If no case matches, the
|
|
@@ -1871,29 +2378,7 @@ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1871
2378
|
* @since 0.1.0
|
|
1872
2379
|
* @category choice
|
|
1873
2380
|
*/
|
|
1874
|
-
export const dispatch:
|
|
1875
|
-
<K, E, R, I extends Input, P extends Parser.Any>(
|
|
1876
|
-
selector: Parser<K, E, R, I>,
|
|
1877
|
-
cases: (key: K) => P | undefined
|
|
1878
|
-
): Parser<Parser.Success<P>, E | Parser.Error<P>, R | Parser.Services<P>, I & Parser.InputOf<P>>
|
|
1879
|
-
<
|
|
1880
|
-
K extends PropertyKey,
|
|
1881
|
-
E,
|
|
1882
|
-
R,
|
|
1883
|
-
I extends Input,
|
|
1884
|
-
const Cases extends { readonly [key in K]?: Parser.Any },
|
|
1885
|
-
Otherwise extends Parser.Any = never
|
|
1886
|
-
>(
|
|
1887
|
-
selector: Parser<K, E, R, I>,
|
|
1888
|
-
cases: Cases,
|
|
1889
|
-
otherwise?: Otherwise
|
|
1890
|
-
): Parser<
|
|
1891
|
-
Parser.Success<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
1892
|
-
E | Parser.Error<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
1893
|
-
R | Parser.Services<NonNullable<Cases[keyof Cases]> | Otherwise>,
|
|
1894
|
-
I & Parser.InputOf<NonNullable<Cases[keyof Cases]> | Otherwise>
|
|
1895
|
-
>
|
|
1896
|
-
} = (
|
|
2381
|
+
export const dispatch: DispatchFn = (
|
|
1897
2382
|
selector: Parser.Any,
|
|
1898
2383
|
cases: ((key: any) => Parser.Any | undefined) | Record<PropertyKey, Parser.Any>,
|
|
1899
2384
|
otherwise?: Parser.Any
|
|
@@ -1941,37 +2426,44 @@ export const permutation = <const Ps extends ReadonlyArray<Parser.Any>>(
|
|
|
1941
2426
|
Parser.Services<Ps[number]>,
|
|
1942
2427
|
Parser.InputOf<Ps[number]>
|
|
1943
2428
|
> => {
|
|
1944
|
-
|
|
1945
|
-
const n =
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
let
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
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)
|
|
1965
2457
|
}
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
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)
|
|
1969
2460
|
}
|
|
1970
|
-
//
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
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
|
+
)
|
|
1975
2467
|
}
|
|
1976
2468
|
|
|
1977
2469
|
// -----------------------------------------------------------------------------
|
|
@@ -2019,9 +2511,13 @@ const foldImpl = <A, B, E, R, I extends Input>(
|
|
|
2019
2511
|
const entry = state.pos
|
|
2020
2512
|
let acc: B
|
|
2021
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)
|
|
2022
2516
|
const resumed = state.resume === undefined ? undefined : (Resume.take(state, key, entry) as FoldCheckpoint<B>)
|
|
2023
2517
|
if (resumed instanceof Resume.Done) {
|
|
2024
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`
|
|
2025
2521
|
return resumed.value as B
|
|
2026
2522
|
}
|
|
2027
2523
|
if (resumed !== undefined) {
|
|
@@ -2260,10 +2756,14 @@ export const separated: {
|
|
|
2260
2756
|
(state) => {
|
|
2261
2757
|
const entry = state.pos
|
|
2262
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)
|
|
2263
2761
|
const resumed =
|
|
2264
2762
|
state.resume === undefined ? undefined : (Resume.take(state, key, entry) as SeparatedCheckpoint<A>)
|
|
2265
2763
|
if (resumed instanceof Resume.Done) {
|
|
2266
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>`
|
|
2267
2767
|
return resumed.value as Array<A>
|
|
2268
2768
|
}
|
|
2269
2769
|
if (resumed !== undefined) {
|
|
@@ -2343,10 +2843,15 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2343
2843
|
return described(
|
|
2344
2844
|
(state) => {
|
|
2345
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)
|
|
2346
2849
|
const resumed = state.resume === undefined ? undefined : (Resume.take(state, key, entry) as FoldCheckpoint<A>)
|
|
2347
2850
|
let acc: A | Fail
|
|
2348
2851
|
if (resumed instanceof Resume.Done) {
|
|
2349
2852
|
state.pos = resumed.pos
|
|
2853
|
+
// SAFETY: a `Done` under `key` is recorded by `Resume.done` below,
|
|
2854
|
+
// with the folded value, an `A`
|
|
2350
2855
|
return resumed.value as A
|
|
2351
2856
|
}
|
|
2352
2857
|
if (resumed !== undefined) {
|
|
@@ -2363,7 +2868,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2363
2868
|
if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
|
|
2364
2869
|
if (state.mode !== "Backtrack") return Fail
|
|
2365
2870
|
recover(state, start)
|
|
2366
|
-
return Resume.done(state, key, entry, resumed !== undefined, acc
|
|
2871
|
+
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2367
2872
|
}
|
|
2368
2873
|
if (state.pos === start) return assertConsumes(state, "separatedFoldl1")
|
|
2369
2874
|
const a = step(state)
|
|
@@ -2371,7 +2876,7 @@ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2371
2876
|
if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
|
|
2372
2877
|
if (state.mode !== "Backtrack") return Fail
|
|
2373
2878
|
state.rewind(start)
|
|
2374
|
-
return Resume.done(state, key, entry, resumed !== undefined, acc
|
|
2879
|
+
return Resume.done(state, key, entry, resumed !== undefined, acc)
|
|
2375
2880
|
}
|
|
2376
2881
|
acc = op(acc, s, a)
|
|
2377
2882
|
}
|
|
@@ -2421,9 +2926,9 @@ export const separatedFoldr1 = <A, E, R, I extends Input, S, E2, R2, I2 extends
|
|
|
2421
2926
|
seps.push(s)
|
|
2422
2927
|
values.push(a)
|
|
2423
2928
|
}
|
|
2424
|
-
let acc = values[values.length - 1]
|
|
2929
|
+
let acc = values[values.length - 1]
|
|
2425
2930
|
for (let i = seps.length - 1; i >= 0; i--) {
|
|
2426
|
-
acc = op(values[i]
|
|
2931
|
+
acc = op(values[i], seps[i], acc)
|
|
2427
2932
|
}
|
|
2428
2933
|
return acc
|
|
2429
2934
|
},
|
|
@@ -2499,7 +3004,7 @@ export const provideService: {
|
|
|
2499
3004
|
return described(
|
|
2500
3005
|
(state) => {
|
|
2501
3006
|
const prev = state.context
|
|
2502
|
-
state.context = Context.add(prev, key, service)
|
|
3007
|
+
state.context = Context.add(prev, key, service)
|
|
2503
3008
|
try {
|
|
2504
3009
|
return step(state)
|
|
2505
3010
|
} finally {
|
|
@@ -2536,7 +3041,7 @@ export const provide: {
|
|
|
2536
3041
|
return described(
|
|
2537
3042
|
(state) => {
|
|
2538
3043
|
const prev = state.context
|
|
2539
|
-
state.context = Context.merge(prev, context)
|
|
3044
|
+
state.context = Context.merge(prev, context)
|
|
2540
3045
|
try {
|
|
2541
3046
|
return step(state)
|
|
2542
3047
|
} finally {
|
|
@@ -2732,18 +3237,20 @@ const NotCompiled: unique symbol = /*#__PURE__*/ Symbol("NotCompiled")
|
|
|
2732
3237
|
* input), in which case the state must be thrown away. `resumable` selects
|
|
2733
3238
|
* the build that handles partial input.
|
|
2734
3239
|
*/
|
|
2735
|
-
const runCompiled = <I extends Input>(
|
|
2736
|
-
self: Parser
|
|
3240
|
+
const runCompiled = <A, I extends Input>(
|
|
3241
|
+
self: Parser<A, unknown, unknown, I>,
|
|
2737
3242
|
state: State<I>,
|
|
2738
3243
|
whole: boolean,
|
|
2739
3244
|
resumable = false
|
|
2740
|
-
):
|
|
3245
|
+
): A | Fail | typeof NotCompiled => {
|
|
2741
3246
|
if (state.tracer !== undefined || state.recovered !== undefined) return NotCompiled
|
|
2742
|
-
const
|
|
2743
|
-
if (step === undefined) return NotCompiled
|
|
2744
|
-
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)
|
|
2745
3250
|
if (a === Fail || (whole && state.pos < state.end)) return Fail
|
|
2746
|
-
|
|
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
|
|
2747
3254
|
}
|
|
2748
3255
|
|
|
2749
3256
|
// -----------------------------------------------------------------------------
|
|
@@ -2814,7 +3321,7 @@ const parseComplete = <A, E, R, I extends Input>(
|
|
|
2814
3321
|
const state = makeState(input, options, false)
|
|
2815
3322
|
const a = runCompiled(self, state, true)
|
|
2816
3323
|
if (a === NotCompiled) return runComplete(self, state)
|
|
2817
|
-
if (a !== Fail) return Result.succeed(a
|
|
3324
|
+
if (a !== Fail) return Result.succeed(a)
|
|
2818
3325
|
// Re-run interpreted for the diagnostics
|
|
2819
3326
|
return runComplete(self, makeState(input, options, false))
|
|
2820
3327
|
}
|
|
@@ -2832,6 +3339,25 @@ const runComplete = <A, E, R, I extends Input>(
|
|
|
2832
3339
|
return Result.succeed(a)
|
|
2833
3340
|
}
|
|
2834
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
|
+
|
|
2835
3361
|
/**
|
|
2836
3362
|
* Runs a parser against a complete input, requiring the whole input to be
|
|
2837
3363
|
* consumed (winnow's `Parser::parse`).
|
|
@@ -2848,37 +3374,34 @@ const runComplete = <A, E, R, I extends Input>(
|
|
|
2848
3374
|
* @since 0.1.0
|
|
2849
3375
|
* @category running
|
|
2850
3376
|
*/
|
|
2851
|
-
export const parse:
|
|
2852
|
-
<A, E, I extends Input>(
|
|
2853
|
-
self: Parser<A, E, never, I>,
|
|
2854
|
-
input: NoInfer<Source<I>>,
|
|
2855
|
-
options?: ParseOptions
|
|
2856
|
-
): Result.Result<A, ParseError.ParseError<E>>
|
|
2857
|
-
<A, E, R, I extends Input>(
|
|
2858
|
-
self: Parser<A, E, R, I>,
|
|
2859
|
-
input: NoInfer<Source<I>>,
|
|
2860
|
-
options: ParseOptionsWithContext<R>
|
|
2861
|
-
): Result.Result<A, ParseError.ParseError<E>>
|
|
2862
|
-
} = <A, E, R, I extends Input>(
|
|
3377
|
+
export const parse: ParseFn = <A, E, R, I extends Input>(
|
|
2863
3378
|
self: Parser<A, E, R, I>,
|
|
2864
3379
|
input: I | Source<I>,
|
|
2865
3380
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
2866
3381
|
): Result.Result<A, ParseError.ParseError<E>> => parseComplete(self, input, options)
|
|
2867
3382
|
|
|
2868
3383
|
/**
|
|
2869
|
-
*
|
|
3384
|
+
* The overloads of {@link parseSync}.
|
|
2870
3385
|
*
|
|
2871
|
-
* @since 0.
|
|
3386
|
+
* @since 0.5.0
|
|
2872
3387
|
* @category running
|
|
2873
3388
|
*/
|
|
2874
|
-
export
|
|
3389
|
+
export interface ParseSyncFn {
|
|
2875
3390
|
<A, E, I extends Input>(self: Parser<A, E, never, I>, input: NoInfer<Source<I>>, options?: ParseOptions): A
|
|
2876
3391
|
<A, E, R, I extends Input>(
|
|
2877
3392
|
self: Parser<A, E, R, I>,
|
|
2878
3393
|
input: NoInfer<Source<I>>,
|
|
2879
3394
|
options: ParseOptionsWithContext<R>
|
|
2880
3395
|
): A
|
|
2881
|
-
}
|
|
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>(
|
|
2882
3405
|
self: Parser<A, E, R, I>,
|
|
2883
3406
|
input: I | Source<I>,
|
|
2884
3407
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
@@ -2889,18 +3412,12 @@ export const parseSync: {
|
|
|
2889
3412
|
}
|
|
2890
3413
|
|
|
2891
3414
|
/**
|
|
2892
|
-
*
|
|
2893
|
-
* input along with the output, nom-style (winnow's `parse_peek`, nom's
|
|
2894
|
-
* `IResult`).
|
|
3415
|
+
* The overloads of {@link parsePeek}.
|
|
2895
3416
|
*
|
|
2896
|
-
*
|
|
2897
|
-
* how a caller running from an `offset` (see {@link ParseOptions}) finds
|
|
2898
|
-
* where to continue.
|
|
2899
|
-
*
|
|
2900
|
-
* @since 0.1.0
|
|
3417
|
+
* @since 0.5.0
|
|
2901
3418
|
* @category running
|
|
2902
3419
|
*/
|
|
2903
|
-
export
|
|
3420
|
+
export interface ParsePeekFn {
|
|
2904
3421
|
<A, E, I extends Input>(
|
|
2905
3422
|
self: Parser<A, E, never, I>,
|
|
2906
3423
|
input: NoInfer<Source<I>>,
|
|
@@ -2911,7 +3428,21 @@ export const parsePeek: {
|
|
|
2911
3428
|
input: NoInfer<Source<I>>,
|
|
2912
3429
|
options: ParseOptionsWithContext<R>
|
|
2913
3430
|
): Result.Result<[remaining: Slice<I>, output: A], ParseError.ParseError<E>>
|
|
2914
|
-
}
|
|
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>(
|
|
2915
3446
|
self: Parser<A, E, R, I>,
|
|
2916
3447
|
input: I | Source<I>,
|
|
2917
3448
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
@@ -2919,7 +3450,7 @@ export const parsePeek: {
|
|
|
2919
3450
|
let state = makeState(input, options, false)
|
|
2920
3451
|
const compiled = runCompiled(self, state, false)
|
|
2921
3452
|
if (compiled !== NotCompiled && compiled !== Fail) {
|
|
2922
|
-
return Result.succeed([state.slice(state.pos, state.end), compiled
|
|
3453
|
+
return Result.succeed([state.slice(state.pos, state.end), compiled])
|
|
2923
3454
|
}
|
|
2924
3455
|
if (compiled === Fail) state = makeState(input, options, false)
|
|
2925
3456
|
const a = Stack.run(self.step, state)
|
|
@@ -2940,6 +3471,25 @@ export type PartialResult<A> =
|
|
|
2940
3471
|
| { readonly _tag: "Done"; readonly value: A; readonly offset: number }
|
|
2941
3472
|
| { readonly _tag: "Incomplete"; readonly needed: Option.Option<number> }
|
|
2942
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
|
+
|
|
2943
3493
|
/**
|
|
2944
3494
|
* Runs a parser against a *partial* input, i.e. a chunk of a stream that may
|
|
2945
3495
|
* be followed by more data (winnow's `Partial`, nom's `streaming` parsers).
|
|
@@ -2969,18 +3519,7 @@ export type PartialResult<A> =
|
|
|
2969
3519
|
* @since 0.1.0
|
|
2970
3520
|
* @category running
|
|
2971
3521
|
*/
|
|
2972
|
-
export const parsePartial:
|
|
2973
|
-
<A, E, I extends Input>(
|
|
2974
|
-
self: Parser<A, E, never, I>,
|
|
2975
|
-
input: NoInfer<Source<I>>,
|
|
2976
|
-
options?: ParseOptions
|
|
2977
|
-
): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
|
|
2978
|
-
<A, E, R, I extends Input>(
|
|
2979
|
-
self: Parser<A, E, R, I>,
|
|
2980
|
-
input: NoInfer<Source<I>>,
|
|
2981
|
-
options: ParseOptionsWithContext<R>
|
|
2982
|
-
): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
|
|
2983
|
-
} = <A, E, R, I extends Input>(
|
|
3522
|
+
export const parsePartial: ParsePartialFn = <A, E, R, I extends Input>(
|
|
2984
3523
|
self: Parser<A, E, R, I>,
|
|
2985
3524
|
input: I | Source<I>,
|
|
2986
3525
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|
|
@@ -2988,19 +3527,25 @@ export const parsePartial: {
|
|
|
2988
3527
|
let state = makeState(input, options, true)
|
|
2989
3528
|
const compiled = runCompiled(self, state, false, true)
|
|
2990
3529
|
if (compiled !== NotCompiled && compiled !== Fail) {
|
|
2991
|
-
return Result.succeed({ _tag: "Done", value: compiled
|
|
3530
|
+
return Result.succeed({ _tag: "Done", value: compiled, offset: state.pos })
|
|
2992
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))
|
|
2993
3535
|
// Re-run interpreted for `needed` and the diagnostics
|
|
2994
3536
|
if (compiled === Fail) state = makeState(input, options, true)
|
|
2995
3537
|
const a = Stack.run(self.step, state)
|
|
2996
3538
|
if (a !== Fail) return Result.succeed({ _tag: "Done", value: a, offset: state.pos })
|
|
2997
|
-
if (state.mode === "Incomplete")
|
|
2998
|
-
const needed = state.failure.needed
|
|
2999
|
-
return Result.succeed({ _tag: "Incomplete", needed: needed > 0 ? Option.some(needed) : Option.none() })
|
|
3000
|
-
}
|
|
3539
|
+
if (state.mode === "Incomplete") return Result.succeed(incompleteOf(state))
|
|
3001
3540
|
return Result.fail(toError(state))
|
|
3002
3541
|
}
|
|
3003
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
|
+
|
|
3004
3549
|
/**
|
|
3005
3550
|
* Runs a parser against a complete input as an `Effect`. Services required
|
|
3006
3551
|
* by the parser are taken from the Effect's context.
|
|
@@ -3119,14 +3664,12 @@ export interface RecoverableResult<A, E> {
|
|
|
3119
3664
|
}
|
|
3120
3665
|
|
|
3121
3666
|
/**
|
|
3122
|
-
*
|
|
3123
|
-
* stream). Failures handled by {@link resumeAfter} and {@link retryAfter}
|
|
3124
|
-
* are collected instead of aborting the parse.
|
|
3667
|
+
* The overloads of {@link parseRecoverable}.
|
|
3125
3668
|
*
|
|
3126
|
-
* @since 0.
|
|
3669
|
+
* @since 0.5.0
|
|
3127
3670
|
* @category running
|
|
3128
3671
|
*/
|
|
3129
|
-
export
|
|
3672
|
+
export interface ParseRecoverableFn {
|
|
3130
3673
|
<A, E, I extends Input>(
|
|
3131
3674
|
self: Parser<A, E, never, I>,
|
|
3132
3675
|
input: NoInfer<Source<I>>,
|
|
@@ -3137,7 +3680,17 @@ export const parseRecoverable: {
|
|
|
3137
3680
|
input: NoInfer<Source<I>>,
|
|
3138
3681
|
options: ParseOptionsWithContext<R>
|
|
3139
3682
|
): RecoverableResult<A, E>
|
|
3140
|
-
}
|
|
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>(
|
|
3141
3694
|
self: Parser<A, E, R, I>,
|
|
3142
3695
|
input: I | Source<I>,
|
|
3143
3696
|
options?: ParseOptions & { readonly context?: Context.Context<R> }
|