@thomasfosterau/effect-parse 0.0.0-stage → 0.0.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.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +550 -2
  3. package/dist/Ascii.d.ts +392 -0
  4. package/dist/Ascii.d.ts.map +1 -0
  5. package/dist/Ascii.js +610 -0
  6. package/dist/Ascii.js.map +1 -0
  7. package/dist/Binary.d.ts +153 -0
  8. package/dist/Binary.d.ts.map +1 -0
  9. package/dist/Binary.js +171 -0
  10. package/dist/Binary.js.map +1 -0
  11. package/dist/Bits.d.ts +60 -0
  12. package/dist/Bits.d.ts.map +1 -0
  13. package/dist/Bits.js +144 -0
  14. package/dist/Bits.js.map +1 -0
  15. package/dist/Encoding.d.ts +116 -0
  16. package/dist/Encoding.d.ts.map +1 -0
  17. package/dist/Encoding.js +85 -0
  18. package/dist/Encoding.js.map +1 -0
  19. package/dist/Expression.d.ts +124 -0
  20. package/dist/Expression.d.ts.map +1 -0
  21. package/dist/Expression.js +139 -0
  22. package/dist/Expression.js.map +1 -0
  23. package/dist/Input.d.ts +301 -0
  24. package/dist/Input.d.ts.map +1 -0
  25. package/dist/Input.js +312 -0
  26. package/dist/Input.js.map +1 -0
  27. package/dist/Nom.d.ts +518 -0
  28. package/dist/Nom.d.ts.map +1 -0
  29. package/dist/Nom.js +519 -0
  30. package/dist/Nom.js.map +1 -0
  31. package/dist/ParseError.d.ts +120 -0
  32. package/dist/ParseError.d.ts.map +1 -0
  33. package/dist/ParseError.js +301 -0
  34. package/dist/ParseError.js.map +1 -0
  35. package/dist/Parser.d.ts +1312 -0
  36. package/dist/Parser.d.ts.map +1 -0
  37. package/dist/Parser.js +1936 -0
  38. package/dist/Parser.js.map +1 -0
  39. package/dist/ParserSchema.d.ts +31 -0
  40. package/dist/ParserSchema.d.ts.map +1 -0
  41. package/dist/ParserSchema.js +56 -0
  42. package/dist/ParserSchema.js.map +1 -0
  43. package/dist/ParserStream.d.ts +127 -0
  44. package/dist/ParserStream.d.ts.map +1 -0
  45. package/dist/ParserStream.js +270 -0
  46. package/dist/ParserStream.js.map +1 -0
  47. package/dist/Text.d.ts +330 -0
  48. package/dist/Text.d.ts.map +1 -0
  49. package/dist/Text.js +529 -0
  50. package/dist/Text.js.map +1 -0
  51. package/dist/Token.d.ts +225 -0
  52. package/dist/Token.d.ts.map +1 -0
  53. package/dist/Token.js +294 -0
  54. package/dist/Token.js.map +1 -0
  55. package/dist/index.d.ts +17 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +17 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/internal/compiler/closure.d.ts +25 -0
  60. package/dist/internal/compiler/closure.d.ts.map +1 -0
  61. package/dist/internal/compiler/closure.js +1668 -0
  62. package/dist/internal/compiler/closure.js.map +1 -0
  63. package/dist/internal/compiler/entries.d.ts +19 -0
  64. package/dist/internal/compiler/entries.d.ts.map +1 -0
  65. package/dist/internal/compiler/entries.js +32 -0
  66. package/dist/internal/compiler/entries.js.map +1 -0
  67. package/dist/internal/compiler/first.d.ts +33 -0
  68. package/dist/internal/compiler/first.d.ts.map +1 -0
  69. package/dist/internal/compiler/first.js +235 -0
  70. package/dist/internal/compiler/first.js.map +1 -0
  71. package/dist/internal/compiler/jit.d.ts +20 -0
  72. package/dist/internal/compiler/jit.d.ts.map +1 -0
  73. package/dist/internal/compiler/jit.js +1030 -0
  74. package/dist/internal/compiler/jit.js.map +1 -0
  75. package/dist/internal/compiler/registry.d.ts +34 -0
  76. package/dist/internal/compiler/registry.d.ts.map +1 -0
  77. package/dist/internal/compiler/registry.js +46 -0
  78. package/dist/internal/compiler/registry.js.map +1 -0
  79. package/dist/internal/compiler/sets.d.ts +29 -0
  80. package/dist/internal/compiler/sets.d.ts.map +1 -0
  81. package/dist/internal/compiler/sets.js +85 -0
  82. package/dist/internal/compiler/sets.js.map +1 -0
  83. package/dist/internal/entries.d.ts +27 -0
  84. package/dist/internal/entries.d.ts.map +1 -0
  85. package/dist/internal/entries.js +42 -0
  86. package/dist/internal/entries.js.map +1 -0
  87. package/dist/internal/node.d.ts +251 -0
  88. package/dist/internal/node.d.ts.map +1 -0
  89. package/dist/internal/node.js +73 -0
  90. package/dist/internal/node.js.map +1 -0
  91. package/dist/internal/numbers.d.ts +41 -0
  92. package/dist/internal/numbers.d.ts.map +1 -0
  93. package/dist/internal/numbers.js +302 -0
  94. package/dist/internal/numbers.js.map +1 -0
  95. package/dist/internal/resume.d.ts +39 -0
  96. package/dist/internal/resume.d.ts.map +1 -0
  97. package/dist/internal/resume.js +37 -0
  98. package/dist/internal/resume.js.map +1 -0
  99. package/dist/internal/text.d.ts +72 -0
  100. package/dist/internal/text.d.ts.map +1 -0
  101. package/dist/internal/text.js +391 -0
  102. package/dist/internal/text.js.map +1 -0
  103. package/dist/internal/tokens.d.ts +93 -0
  104. package/dist/internal/tokens.d.ts.map +1 -0
  105. package/dist/internal/tokens.js +400 -0
  106. package/dist/internal/tokens.js.map +1 -0
  107. package/package.json +115 -3
  108. package/src/Ascii.ts +741 -0
  109. package/src/Binary.ts +228 -0
  110. package/src/Bits.ts +192 -0
  111. package/src/Encoding.ts +128 -0
  112. package/src/Expression.ts +245 -0
  113. package/src/Input.ts +429 -0
  114. package/src/Nom.ts +642 -0
  115. package/src/ParseError.ts +378 -0
  116. package/src/Parser.ts +2763 -0
  117. package/src/ParserSchema.ts +87 -0
  118. package/src/ParserStream.ts +390 -0
  119. package/src/Text.ts +757 -0
  120. package/src/Token.ts +416 -0
  121. package/src/index.ts +16 -0
  122. package/src/internal/compiler/closure.ts +1583 -0
  123. package/src/internal/compiler/entries.ts +36 -0
  124. package/src/internal/compiler/first.ts +252 -0
  125. package/src/internal/compiler/jit.ts +1091 -0
  126. package/src/internal/compiler/registry.ts +79 -0
  127. package/src/internal/compiler/sets.ts +100 -0
  128. package/src/internal/entries.ts +51 -0
  129. package/src/internal/node.ts +207 -0
  130. package/src/internal/numbers.ts +291 -0
  131. package/src/internal/resume.ts +55 -0
  132. package/src/internal/text.ts +411 -0
  133. package/src/internal/tokens.ts +409 -0
package/src/Parser.ts ADDED
@@ -0,0 +1,2763 @@
1
+ /**
2
+ * The `Parser` data type and its general-purpose combinators.
3
+ *
4
+ * A `Parser<A, E, R, I>` consumes input of type `I` and produces a value of
5
+ * type `A`. It may fail with a `ParseError<E>`, where `E` is the type of
6
+ * user supplied error causes, and it may require services `R` from an Effect
7
+ * `Context`.
8
+ *
9
+ * Under the hood a parser is a function over a mutable cursor (`Input.State`),
10
+ * as in winnow. Failure is cheap and backtracking is a matter of resetting an
11
+ * offset. On the surface the API follows Effect's conventions: parsers are
12
+ * pipeable, combinators are dual (data-first or data-last), and parsers can
13
+ * be written with generators:
14
+ *
15
+ * ```ts
16
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
17
+ *
18
+ * const point = Parser.gen(function*() {
19
+ * yield* Token.literal("(")
20
+ * const x = yield* Ascii.decInt
21
+ * yield* Token.literal(",")
22
+ * const y = yield* Ascii.decInt
23
+ * yield* Token.literal(")")
24
+ * return { x, y }
25
+ * })
26
+ *
27
+ * Parser.parse(point, "(1,-2)") // Result.succeed({ x: 1, y: -2 })
28
+ * ```
29
+ *
30
+ * @since 0.1.0
31
+ */
32
+ import * as Context from "effect/Context"
33
+ import * as Effect from "effect/Effect"
34
+ import { dual } from "effect/Function"
35
+ import { NodeInspectSymbol } from "effect/Inspectable"
36
+ import * as Option from "effect/Option"
37
+ import type { Pipeable } from "effect/Pipeable"
38
+ import { pipeArguments } from "effect/Pipeable"
39
+ import * as Result from "effect/Result"
40
+ import type { Contravariant, Covariant, Simplify } from "effect/Types"
41
+ import type { ErrMode, Input, Slice, Source, TraceEvent } from "./Input.js"
42
+ import { Fail, Failure, mergeExpected, mergeFailures, State } from "./Input.js"
43
+ import type { ErrorKind } from "./ParseError.js"
44
+ import * as ParseError from "./ParseError.js"
45
+ import * as Compiler from "./internal/compiler/registry.js"
46
+ import * as Entries from "./internal/entries.js"
47
+ import * as Resume from "./internal/resume.js"
48
+ import type { Node } from "./internal/node.js"
49
+ import { bounds, NodeId } from "./internal/node.js"
50
+
51
+ // -----------------------------------------------------------------------------
52
+ // Models
53
+ // -----------------------------------------------------------------------------
54
+
55
+ /**
56
+ * @since 0.1.0
57
+ * @category type ids
58
+ */
59
+ export const TypeId: "~effect-parse/Parser" = "~effect-parse/Parser"
60
+
61
+ /**
62
+ * A parser that consumes input of type `I`, producing a value of type `A`,
63
+ * possibly failing with a user cause of type `E`, and requiring services `R`.
64
+ *
65
+ * @since 0.1.0
66
+ * @category models
67
+ */
68
+ export interface Parser<out A, out E = never, out R = never, in I extends Input = string> extends Pipeable {
69
+ readonly [TypeId]: Parser.Variance<A, E, R, I>
70
+ /**
71
+ * Runs one parsing step against a cursor. Returns the parsed value, or the
72
+ * `Fail` sentinel after recording the failure on the state.
73
+ *
74
+ * Calling `step` directly is how custom combinators built with
75
+ * {@link make} invoke other parsers.
76
+ */
77
+ readonly step: (state: State<I>) => A | Fail
78
+ /** A human-readable name, used by `toString` and tracing. */
79
+ readonly name: string | undefined
80
+ [Symbol.iterator](): ParserIterator<Parser<A, E, R, I>>
81
+ }
82
+
83
+ /**
84
+ * The iterator used by `yield*` inside {@link gen}.
85
+ *
86
+ * @since 0.1.0
87
+ * @category models
88
+ */
89
+ export interface ParserIterator<P extends Parser.Any> {
90
+ next(...args: ReadonlyArray<any>): IteratorResult<P, Parser.Success<P>>
91
+ }
92
+
93
+ /**
94
+ * @since 0.1.0
95
+ * @category models
96
+ */
97
+ export declare namespace Parser {
98
+ /**
99
+ * @since 0.1.0
100
+ */
101
+ export interface Variance<A, E, R, I> {
102
+ readonly _A: Covariant<A>
103
+ readonly _E: Covariant<E>
104
+ readonly _R: Covariant<R>
105
+ readonly _I: Contravariant<I>
106
+ }
107
+ /**
108
+ * Any parser.
109
+ *
110
+ * @since 0.1.0
111
+ */
112
+ export type Any = Parser<any, any, any, any>
113
+ /**
114
+ * The output type of a parser.
115
+ *
116
+ * @since 0.1.0
117
+ */
118
+ export type Success<P> = P extends Parser<infer A, infer _E, infer _R, infer _I> ? A : never
119
+ /**
120
+ * The user error type of a parser.
121
+ *
122
+ * @since 0.1.0
123
+ */
124
+ export type Error<P> = P extends Parser<infer _A, infer E, infer _R, infer _I> ? E : never
125
+ /**
126
+ * The services required by a parser.
127
+ *
128
+ * @since 0.1.0
129
+ */
130
+ export type Services<P> = P extends Parser<infer _A, infer _E, infer R, infer _I> ? R : never
131
+ /**
132
+ * The input accepted by a parser (or by every member of a union of
133
+ * parsers).
134
+ *
135
+ * @since 0.1.0
136
+ */
137
+ export type InputOf<P> = [P] extends [never] ? Input
138
+ : [P] extends [Parser<infer _A, infer _E, infer _R, infer I>] ? I
139
+ : never
140
+ }
141
+
142
+ /**
143
+ * A repetition range, in the spirit of winnow's `impl Into<Range>`:
144
+ *
145
+ * - `n`: exactly `n` times
146
+ * - `{ min, max }`: between `min` and `max` times (inclusive); `min`
147
+ * defaults to 0 and `max` to unbounded.
148
+ *
149
+ * @since 0.1.0
150
+ * @category models
151
+ */
152
+ export type Range = number | { readonly min?: number | undefined; readonly max?: number | undefined }
153
+
154
+ /**
155
+ * The output of a repetition: a non-empty array when the range has a literal
156
+ * positive minimum.
157
+ *
158
+ * @since 0.1.0
159
+ * @category models
160
+ */
161
+ export type Repeated<A, Rg> = Rg extends number ? number extends Rg ? Array<A> : Rg extends 0 ? Array<A> : NonEmpty<A>
162
+ : Rg extends { readonly min: infer M extends number } ? number extends M ? Array<A> : M extends 0 ? Array<A>
163
+ : NonEmpty<A>
164
+ : Array<A>
165
+
166
+ type NonEmpty<A> = [A, ...Array<A>]
167
+
168
+ /**
169
+ * A span of input offsets, `[start, end)`.
170
+ *
171
+ * @since 0.1.0
172
+ * @category models
173
+ */
174
+ export interface Span {
175
+ readonly start: number
176
+ readonly end: number
177
+ }
178
+
179
+ // -----------------------------------------------------------------------------
180
+ // Internals
181
+ // -----------------------------------------------------------------------------
182
+
183
+ const variance: Parser.Variance<never, never, never, unknown> = {
184
+ _A: (_) => _,
185
+ _E: (_) => _,
186
+ _R: (_) => _,
187
+ _I: (_) => {}
188
+ }
189
+
190
+ class SingleShotGen<T> {
191
+ private called = false
192
+ constructor(readonly self: T) {}
193
+ next(a: any): IteratorResult<T, any> {
194
+ if (this.called) return { value: a, done: true }
195
+ this.called = true
196
+ return { value: this.self, done: false }
197
+ }
198
+ return(a: any): IteratorResult<T, any> {
199
+ return { value: a, done: true }
200
+ }
201
+ throw(e: unknown): never {
202
+ throw e
203
+ }
204
+ [Symbol.iterator](): SingleShotGen<T> {
205
+ return new SingleShotGen(this.self)
206
+ }
207
+ }
208
+
209
+ class ParserImpl<A, E, R, I extends Input> implements Parser<A, E, R, I> {
210
+ readonly [TypeId] = variance as Parser.Variance<A, E, R, I>
211
+ /** The description used by the compiler; `undefined` for `Opaque`. */
212
+ readonly [NodeId]: Node | undefined
213
+ constructor(
214
+ readonly step: (state: State<I>) => A | Fail,
215
+ readonly name: string | undefined,
216
+ node: Node | undefined
217
+ ) {
218
+ this[NodeId] = node
219
+ }
220
+ pipe() {
221
+ return pipeArguments(this, arguments)
222
+ }
223
+ [Symbol.iterator](): ParserIterator<Parser<A, E, R, I>> {
224
+ return new SingleShotGen(this) as any
225
+ }
226
+ toString() {
227
+ return this.name === undefined ? "Parser" : `Parser(${this.name})`
228
+ }
229
+ toJSON() {
230
+ return { _id: "Parser", name: this.name }
231
+ }
232
+ [NodeInspectSymbol]() {
233
+ return this.toJSON()
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Tests whether a value is a `Parser`.
239
+ *
240
+ * @since 0.1.0
241
+ * @category guards
242
+ */
243
+ export const isParser = (u: unknown): u is Parser<unknown, unknown, unknown, never> =>
244
+ typeof u === "object" && u !== null && TypeId in u
245
+
246
+ const isParserArg = (args: IArguments) => isParser(args[0])
247
+ const isTwoParserArgs = (args: IArguments) => isParser(args[0]) && isParser(args[1])
248
+
249
+ // -----------------------------------------------------------------------------
250
+ // Constructors
251
+ // -----------------------------------------------------------------------------
252
+
253
+ /**
254
+ * Creates a parser from a step function over the mutable {@link State}
255
+ * cursor. This is the escape hatch for writing low-level parsers, much like
256
+ * passing a plain function as a parser in winnow.
257
+ *
258
+ * The step function must either return the parsed value, having advanced
259
+ * `state.pos` past the consumed input, or return the result of one of
260
+ * `state.backtrack`, `state.cut`, `state.incomplete`, ... to signal failure.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * import { Parser } from "@thomasfosterau/effect-parse"
265
+ *
266
+ * // Parses a single ASCII uppercase letter
267
+ * const upper = Parser.make<string>((state) => {
268
+ * const c = state.input.charCodeAt(state.pos)
269
+ * if (state.pos < state.end && c >= 65 && c <= 90) {
270
+ * state.pos++
271
+ * return String.fromCharCode(c)
272
+ * }
273
+ * return state.pos >= state.end
274
+ * ? state.eoi("Token", ["uppercase letter"], 1)
275
+ * : state.backtrack("Token", ["uppercase letter"])
276
+ * })
277
+ * ```
278
+ *
279
+ * @since 0.1.0
280
+ * @category constructors
281
+ */
282
+ export const make = <A, E = never, R = never, I extends Input = string>(
283
+ step: (state: State<I>) => A | Fail,
284
+ name?: string
285
+ ): Parser<A, E, R, I> => new ParserImpl(step, name, undefined)
286
+
287
+ /**
288
+ * Creates a parser together with its description, used by the compiler.
289
+ *
290
+ * @internal
291
+ */
292
+ export const described = <A, E = never, R = never, I extends Input = string>(
293
+ step: (state: State<I>) => A | Fail,
294
+ name: string | undefined,
295
+ node: Node
296
+ ): Parser<A, E, R, I> => new ParserImpl(step, name, node)
297
+
298
+ const nodeOfParser = (p: Parser.Any): Node | undefined => (p as any)[NodeId]
299
+
300
+ /**
301
+ * Gives a parser a name, used by `toString`.
302
+ *
303
+ * @since 0.1.0
304
+ * @category utils
305
+ */
306
+ export const named: {
307
+ (name: string): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E, R, I>
308
+ <A, E, R, I extends Input>(self: Parser<A, E, R, I>, name: string): Parser<A, E, R, I>
309
+ } = dual(2, <A, E, R, I extends Input>(self: Parser<A, E, R, I>, name: string): Parser<A, E, R, I> =>
310
+ new ParserImpl(self.step, name, nodeOfParser(self)))
311
+
312
+ /**
313
+ * A parser that consumes nothing and succeeds with the given value.
314
+ *
315
+ * @since 0.1.0
316
+ * @category constructors
317
+ */
318
+ export const succeed = <A>(value: A): Parser<A, never, never, Input> =>
319
+ described(() => value, "succeed", { _tag: "Succeed", value })
320
+
321
+ /**
322
+ * A parser that consumes nothing and succeeds with the result of a thunk.
323
+ *
324
+ * @since 0.1.0
325
+ * @category constructors
326
+ */
327
+ export const sync = <A>(evaluate: () => A): Parser<A, never, never, Input> =>
328
+ described(() => evaluate(), "sync", { _tag: "Sync", evaluate })
329
+
330
+ /**
331
+ * A parser that consumes nothing and succeeds with `undefined` (winnow's
332
+ * `empty`, nom's `success(())`).
333
+ *
334
+ * @since 0.1.0
335
+ * @category constructors
336
+ */
337
+ export const empty: Parser<void, never, never, Input> = described(() => undefined, "empty", {
338
+ _tag: "Succeed",
339
+ value: undefined
340
+ })
341
+
342
+ /**
343
+ * Alias of {@link empty}, following Effect's `Effect.void`.
344
+ *
345
+ * @since 0.1.0
346
+ * @category constructors
347
+ */
348
+ export const void_: Parser<void, never, never, Input> = empty
349
+
350
+ export {
351
+ /**
352
+ * @since 0.1.0
353
+ * @category constructors
354
+ */
355
+ void_ as void
356
+ }
357
+
358
+ /**
359
+ * A parser that always fails (backtracks) with the given user error as its
360
+ * cause.
361
+ *
362
+ * @since 0.1.0
363
+ * @category constructors
364
+ */
365
+ export const fail = <E>(error: E): Parser<never, E, never, Input> =>
366
+ described<never, E, never, Input>((state) => state.failCause("Backtrack", "Fail", error), "fail", failBacktrack)
367
+
368
+ const failBacktrack: Node = { _tag: "Fail", mode: "Backtrack" }
369
+
370
+ /**
371
+ * A parser that always fails (backtracks) without a cause. This is winnow's
372
+ * `fail` and nom's `fail()`.
373
+ *
374
+ * @since 0.1.0
375
+ * @category constructors
376
+ */
377
+ export const reject: Parser<never, never, never, Input> = described<never, never, never, Input>(
378
+ (state) => state.backtrack("Fail"),
379
+ "reject",
380
+ failBacktrack
381
+ )
382
+
383
+ /**
384
+ * A parser that always fails with a message.
385
+ *
386
+ * @since 0.1.0
387
+ * @category constructors
388
+ */
389
+ export const failMessage = (message: string): Parser<never, never, never, Input> =>
390
+ described<never, never, never, Input>(
391
+ (state) => state.failMessage("Backtrack", "Fail", message),
392
+ "failMessage",
393
+ failBacktrack
394
+ )
395
+
396
+ /**
397
+ * A placeholder parser that fails with an unrecoverable error (winnow's
398
+ * `todo`).
399
+ *
400
+ * @since 0.1.0
401
+ * @category constructors
402
+ */
403
+ export const todo: Parser<never, never, never, Input> = described<never, never, never, Input>(
404
+ (state) => state.failMessage("Cut", "Assert", "`todo` parser reached"),
405
+ "todo",
406
+ { _tag: "Fail", mode: "Cut" }
407
+ )
408
+
409
+ /**
410
+ * Lazily constructs a parser, which is needed to define recursive grammars.
411
+ * The thunk is evaluated once, on first use.
412
+ *
413
+ * @example
414
+ * ```ts
415
+ * import { Parser, Token } from "@thomasfosterau/effect-parse"
416
+ *
417
+ * type Nested = ReadonlyArray<Nested>
418
+ * const nested: Parser.Parser<Nested> = Parser.delimited(
419
+ * Token.literal("["),
420
+ * Parser.repeat(Parser.suspend(() => nested)),
421
+ * Token.literal("]")
422
+ * )
423
+ * ```
424
+ *
425
+ * @since 0.1.0
426
+ * @category constructors
427
+ */
428
+ export const suspend = <A, E = never, R = never, I extends Input = string>(
429
+ evaluate: () => Parser<A, E, R, I>
430
+ ): Parser<A, E, R, I> => {
431
+ let parser: Parser<A, E, R, I> | undefined
432
+ return described((state) => (parser ??= evaluate()).step(state), "suspend", {
433
+ _tag: "Suspend",
434
+ resolve: () => parser ??= evaluate()
435
+ })
436
+ }
437
+
438
+ /**
439
+ * The current offset into the input (winnow's `Location`).
440
+ *
441
+ * @since 0.1.0
442
+ * @category constructors
443
+ */
444
+ export const location: Parser<number, never, never, Input> = described((state) => state.pos, "location", {
445
+ _tag: "Location"
446
+ })
447
+
448
+ /**
449
+ * Succeeds (with `undefined`) only at the end of the input; nom's and
450
+ * winnow's `eof`. On partial input, reaching the end of the available data
451
+ * is reported as `Incomplete` since more data may follow.
452
+ *
453
+ * @since 0.1.0
454
+ * @category constructors
455
+ */
456
+ export const eof: Parser<void, never, never, Input> = described((state) => {
457
+ if (state.pos < state.end) return state.backtrack("Eof", expectedEof)
458
+ if (state.partial) return state.incomplete(1, expectedEof)
459
+ return undefined
460
+ }, "eof", { _tag: "Eof" })
461
+
462
+ const expectedEof = ["end of input"]
463
+
464
+ // -----------------------------------------------------------------------------
465
+ // Mapping
466
+ // -----------------------------------------------------------------------------
467
+
468
+ /**
469
+ * Transforms the output of a parser.
470
+ *
471
+ * @since 0.1.0
472
+ * @category mapping
473
+ */
474
+ export const map: {
475
+ <A, B>(f: (a: A) => B): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E, R, I>
476
+ <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B, E, R, I>
477
+ } = dual(2, <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B, E, R, I> => {
478
+ const step = self.step
479
+ return described((state) => {
480
+ const a = step(state)
481
+ return a === Fail ? Fail : f(a)
482
+ }, self.name, { _tag: "Map", self, f })
483
+ })
484
+
485
+ /**
486
+ * Builds an object from the `[key, value]` entries a parser returns, as
487
+ * `Object.fromEntries` does: `fromEntries(self)` is
488
+ * `map(self, Object.fromEntries)`.
489
+ *
490
+ * Compiled parsers build the object directly when `self` repeats a pair
491
+ * (`separated` or `repeat` of `separatedPair` or a two-element `seq`,
492
+ * possibly inside `preceded`, `terminated`, `delimited` or `cut`), without
493
+ * the pairs and the array. They do the same for `map(Object.fromEntries)`.
494
+ *
495
+ * @example
496
+ * ```ts
497
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
498
+ *
499
+ * const entry = Parser.separatedPair(Ascii.alpha1, Token.literal("="), Ascii.decUint)
500
+ * const record = Parser.fromEntries(Parser.separated(entry, Token.literal(",")))
501
+ * Parser.parse(record, "a=1,b=2") // Result.succeed({ a: 1, b: 2 })
502
+ * ```
503
+ *
504
+ * @since 0.2.0
505
+ * @category mapping
506
+ */
507
+ export const fromEntries = <K extends PropertyKey, V, E, R, I extends Input>(
508
+ self: Parser<Iterable<readonly [K, V]>, E, R, I>
509
+ ): Parser<{ [P in K]: V }, E, R, I> => map(self, Entries.fromEntries)
510
+
511
+ /**
512
+ * Replaces the output of a parser with a constant (winnow's `value`, nom's
513
+ * `value`).
514
+ *
515
+ * @since 0.1.0
516
+ * @category mapping
517
+ */
518
+ export const as: {
519
+ <B>(value: B): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E, R, I>
520
+ <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, value: B): Parser<B, E, R, I>
521
+ } = dual(2, <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, value: B): Parser<B, E, R, I> => {
522
+ const step = self.step
523
+ return described((state) => step(state) === Fail ? Fail : value, self.name, { _tag: "As", self, value })
524
+ })
525
+
526
+ /**
527
+ * Discards the output of a parser (winnow's `void`).
528
+ *
529
+ * @since 0.1.0
530
+ * @category mapping
531
+ */
532
+ export const asVoid = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<void, E, R, I> =>
533
+ as(self, undefined)
534
+
535
+ /**
536
+ * Replaces the output of a parser with `Option.some(output)`.
537
+ *
538
+ * @since 0.1.0
539
+ * @category mapping
540
+ */
541
+ export const asSome = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<Option.Option<A>, E, R, I> =>
542
+ map(self, Option.some)
543
+
544
+ /**
545
+ * Runs a side effect on the output of a parser, keeping the output.
546
+ *
547
+ * @since 0.1.0
548
+ * @category mapping
549
+ */
550
+ export const tap: {
551
+ <A>(f: (a: A) => void): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E, R, I>
552
+ <A, E, R, I extends Input>(self: Parser<A, E, R, I>, f: (a: A) => void): Parser<A, E, R, I>
553
+ } = dual(2, <A, E, R, I extends Input>(self: Parser<A, E, R, I>, f: (a: A) => void): Parser<A, E, R, I> => {
554
+ const step = self.step
555
+ return described((state) => {
556
+ const a = step(state)
557
+ if (a !== Fail) f(a)
558
+ return a
559
+ }, self.name, { _tag: "Tap", self, f })
560
+ })
561
+
562
+ /**
563
+ * Uses the output of a parser to choose the parser that consumes the rest
564
+ * of the input (winnow's `flat_map`, nom's `flat_map`).
565
+ *
566
+ * @since 0.1.0
567
+ * @category sequencing
568
+ */
569
+ export const flatMap: {
570
+ <A, B, E2, R2, I2 extends Input>(
571
+ f: (a: A) => Parser<B, E2, R2, I2>
572
+ ): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E | E2, R | R2, I & I2>
573
+ <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
574
+ self: Parser<A, E, R, I>,
575
+ f: (a: A) => Parser<B, E2, R2, I2>
576
+ ): Parser<B, E | E2, R | R2, I & I2>
577
+ } = dual(2, <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
578
+ self: Parser<A, E, R, I>,
579
+ f: (a: A) => Parser<B, E2, R2, I2>
580
+ ): Parser<B, E | E2, R | R2, I & I2> => {
581
+ const step = self.step
582
+ return described((state) => {
583
+ const a = step(state)
584
+ return a === Fail ? Fail : f(a).step(state)
585
+ }, undefined, { _tag: "FlatMap", self, f })
586
+ })
587
+
588
+ /**
589
+ * Applies a second parser to the *output* of the first (winnow's
590
+ * `and_then`, nom's `map_parser`). The output of the first parser must
591
+ * itself be an input, e.g. a slice produced by {@link taken} or
592
+ * `Token.take`.
593
+ *
594
+ * Error offsets reported by the inner parser are translated relative to the
595
+ * start of the outer parser.
596
+ *
597
+ * @example
598
+ * ```ts
599
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
600
+ *
601
+ * // Take exactly 4 characters, then require them all to be digits
602
+ * const year = Token.take(4).pipe(Parser.mapParser(Parser.allConsuming(Ascii.decUint)))
603
+ * ```
604
+ *
605
+ * @since 0.1.0
606
+ * @category sequencing
607
+ */
608
+ export const mapParser: {
609
+ <A extends Input, B, E2, R2>(
610
+ inner: Parser<B, E2, R2, A>
611
+ ): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E | E2, R | R2, I>
612
+ <A extends Input, E, R, I extends Input, B, E2, R2>(
613
+ self: Parser<A, E, R, I>,
614
+ inner: Parser<B, E2, R2, A>
615
+ ): Parser<B, E | E2, R | R2, I>
616
+ } = dual(2, <A extends Input, E, R, I extends Input, B, E2, R2>(
617
+ self: Parser<A, E, R, I>,
618
+ inner: Parser<B, E2, R2, A>
619
+ ): Parser<B, E | E2, R | R2, I> => {
620
+ const step = self.step
621
+ return make((state) => {
622
+ const start = state.pos
623
+ const a = step(state)
624
+ if (a === Fail) return Fail
625
+ const sub = new State(a, { context: state.context })
626
+ sub.tracer = state.tracer
627
+ sub.depth = state.depth
628
+ sub.recovered = state.recovered
629
+ const b = inner.step(sub)
630
+ if (b === Fail) {
631
+ const f = sub.failure
632
+ if (sub.encoding !== undefined) state.encoding = sub.encoding
633
+ // The inner input is usually a slice ending where the outer parser stopped
634
+ const base = Math.max(start, state.pos - a.length)
635
+ const offset = Math.min(base + f.offset, state.pos)
636
+ return state.failWith(
637
+ sub.mode === "Incomplete" ? "Backtrack" : sub.mode,
638
+ new Failure(offset, f.kind, f.expected, f.context, f.hasCause, f.cause, f.message, 0)
639
+ )
640
+ }
641
+ return b
642
+ })
643
+ })
644
+
645
+ /**
646
+ * Transforms the output of a parser with a function that may fail with a
647
+ * `Result` (winnow's `try_map`, nom's `map_res`). On failure the parser
648
+ * backtracks to where it started and the error becomes the `cause` of the
649
+ * `ParseError`.
650
+ *
651
+ * @since 0.1.0
652
+ * @category mapping
653
+ */
654
+ export const tryMap: {
655
+ <A, B, E2>(
656
+ f: (a: A) => Result.Result<B, E2>
657
+ ): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E | E2, R, I>
658
+ <A, E, R, I extends Input, B, E2>(
659
+ self: Parser<A, E, R, I>,
660
+ f: (a: A) => Result.Result<B, E2>
661
+ ): Parser<B, E | E2, R, I>
662
+ } = dual(2, <A, E, R, I extends Input, B, E2>(
663
+ self: Parser<A, E, R, I>,
664
+ f: (a: A) => Result.Result<B, E2>
665
+ ): Parser<B, E | E2, R, I> => {
666
+ const step = self.step
667
+ return described((state) => {
668
+ const start = state.pos
669
+ const a = step(state)
670
+ if (a === Fail) return Fail
671
+ const r = f(a)
672
+ if (Result.isSuccess(r)) return r.success
673
+ state.pos = start
674
+ return state.failCause("Backtrack", "Verify", r.failure)
675
+ }, self.name, { _tag: "TryMap", self, f })
676
+ })
677
+
678
+ /**
679
+ * Like {@link tryMap}, but the function may throw. Thrown values become the
680
+ * `cause` of the error.
681
+ *
682
+ * @since 0.1.0
683
+ * @category mapping
684
+ */
685
+ export const tryMapUnknown: {
686
+ <A, B>(f: (a: A) => B): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E | unknown, R, I>
687
+ <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B, E | unknown, R, I>
688
+ } = dual(2, <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => B): Parser<B, unknown, R, I> =>
689
+ tryMap(self, (a) => {
690
+ try {
691
+ return Result.succeed(f(a))
692
+ } catch (e) {
693
+ return Result.fail(e)
694
+ }
695
+ }))
696
+
697
+ /**
698
+ * Transforms the output of a parser with a function returning an `Option`;
699
+ * `None` makes the parser backtrack (winnow's `verify_map`, nom's
700
+ * `map_opt`).
701
+ *
702
+ * @since 0.1.0
703
+ * @category filtering
704
+ */
705
+ export const filterMap: {
706
+ <A, B>(f: (a: A) => Option.Option<B>): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E, R, I>
707
+ <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, f: (a: A) => Option.Option<B>): Parser<B, E, R, I>
708
+ } = dual(2, <A, E, R, I extends Input, B>(
709
+ self: Parser<A, E, R, I>,
710
+ f: (a: A) => Option.Option<B>
711
+ ): Parser<B, E, R, I> => {
712
+ const step = self.step
713
+ return described((state) => {
714
+ const start = state.pos
715
+ const a = step(state)
716
+ if (a === Fail) return Fail
717
+ const o = f(a)
718
+ if (Option.isSome(o)) return o.value
719
+ state.pos = start
720
+ return state.backtrack("Verify")
721
+ }, self.name, { _tag: "FilterMap", self, f })
722
+ })
723
+
724
+ /**
725
+ * Keeps the output of a parser only if it satisfies a predicate, otherwise
726
+ * backtracks (winnow's and nom's `verify`).
727
+ *
728
+ * @since 0.1.0
729
+ * @category filtering
730
+ */
731
+ export const filter: {
732
+ <A, B extends A>(
733
+ refinement: (a: NoInfer<A>) => a is B
734
+ ): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E, R, I>
735
+ <A>(predicate: (a: NoInfer<A>) => boolean): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E, R, I>
736
+ <A, E, R, I extends Input, B extends A>(self: Parser<A, E, R, I>, refinement: (a: A) => a is B): Parser<B, E, R, I>
737
+ <A, E, R, I extends Input>(self: Parser<A, E, R, I>, predicate: (a: A) => boolean): Parser<A, E, R, I>
738
+ } = dual(2, <A, E, R, I extends Input>(self: Parser<A, E, R, I>, predicate: (a: A) => boolean) => {
739
+ const step = self.step
740
+ return described<A, E, R, I>((state) => {
741
+ const start = state.pos
742
+ const a = step(state)
743
+ if (a === Fail) return Fail
744
+ if (predicate(a)) return a
745
+ state.pos = start
746
+ return state.backtrack("Verify")
747
+ }, self.name, { _tag: "Filter", self, predicate })
748
+ })
749
+
750
+ /**
751
+ * Keeps the output of a parser only if it satisfies a predicate, otherwise
752
+ * backtracks with the given user error.
753
+ *
754
+ * @since 0.1.0
755
+ * @category filtering
756
+ */
757
+ export const filterOrFail: {
758
+ <A, B extends A, E2>(
759
+ refinement: (a: NoInfer<A>) => a is B,
760
+ orFailWith: (a: NoInfer<A>) => E2
761
+ ): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E | E2, R, I>
762
+ <A, E2>(
763
+ predicate: (a: NoInfer<A>) => boolean,
764
+ orFailWith: (a: NoInfer<A>) => E2
765
+ ): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E | E2, R, I>
766
+ <A, E, R, I extends Input, B extends A, E2>(
767
+ self: Parser<A, E, R, I>,
768
+ refinement: (a: A) => a is B,
769
+ orFailWith: (a: A) => E2
770
+ ): Parser<B, E | E2, R, I>
771
+ <A, E, R, I extends Input, E2>(
772
+ self: Parser<A, E, R, I>,
773
+ predicate: (a: A) => boolean,
774
+ orFailWith: (a: A) => E2
775
+ ): Parser<A, E | E2, R, I>
776
+ } = dual(3, <A, E, R, I extends Input, E2>(
777
+ self: Parser<A, E, R, I>,
778
+ predicate: (a: A) => boolean,
779
+ orFailWith: (a: A) => E2
780
+ ) => {
781
+ const step = self.step
782
+ return described<A, E | E2, R, I>((state) => {
783
+ const start = state.pos
784
+ const a = step(state)
785
+ if (a === Fail) return Fail
786
+ if (predicate(a)) return a
787
+ state.pos = start
788
+ return state.failCause("Backtrack", "Verify", orFailWith(a))
789
+ }, self.name, { _tag: "Filter", self, predicate })
790
+ })
791
+
792
+ // -----------------------------------------------------------------------------
793
+ // Consumed input
794
+ // -----------------------------------------------------------------------------
795
+
796
+ /**
797
+ * Returns the slice of input consumed by a parser instead of its output
798
+ * (winnow's `take`, nom's `recognize`).
799
+ *
800
+ * @since 0.1.0
801
+ * @category consumed input
802
+ */
803
+ export const taken = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<Slice<I>, E, R, I> => {
804
+ const step = self.step
805
+ return described((state) => {
806
+ const start = state.pos
807
+ return step(state) === Fail ? Fail : state.slice(start, state.pos)
808
+ }, self.name, { _tag: "Taken", self, encoding: undefined })
809
+ }
810
+
811
+ /**
812
+ * Returns both the output of a parser and the slice of input it consumed
813
+ * (winnow's `with_taken`; nom's `consumed` returns them in the other order).
814
+ *
815
+ * @since 0.1.0
816
+ * @category consumed input
817
+ */
818
+ export const withTaken = <A, E, R, I extends Input>(
819
+ self: Parser<A, E, R, I>
820
+ ): Parser<[output: A, taken: Slice<I>], E, R, I> => {
821
+ const step = self.step
822
+ return described((state) => {
823
+ const start = state.pos
824
+ const a = step(state)
825
+ return a === Fail ? Fail : [a, state.slice(start, state.pos)]
826
+ }, self.name, { _tag: "WithTaken", self })
827
+ }
828
+
829
+ /**
830
+ * Returns the span (`[start, end)` offsets) consumed by a parser instead of
831
+ * its output (winnow's `span`).
832
+ *
833
+ * @since 0.1.0
834
+ * @category consumed input
835
+ */
836
+ export const span = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<Span, E, R, I> => {
837
+ const step = self.step
838
+ return described((state) => {
839
+ const start = state.pos
840
+ return step(state) === Fail ? Fail : { start, end: state.pos }
841
+ }, self.name, { _tag: "Span", self })
842
+ }
843
+
844
+ /**
845
+ * Returns both the output of a parser and the span it consumed (winnow's
846
+ * `with_span`).
847
+ *
848
+ * @since 0.1.0
849
+ * @category consumed input
850
+ */
851
+ export const withSpan = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<[output: A, span: Span], E, R, I> => {
852
+ const step = self.step
853
+ return described((state) => {
854
+ const start = state.pos
855
+ const a = step(state)
856
+ return a === Fail ? Fail : [a, { start, end: state.pos }]
857
+ }, self.name, { _tag: "WithSpan", self })
858
+ }
859
+
860
+ // -----------------------------------------------------------------------------
861
+ // Error management
862
+ // -----------------------------------------------------------------------------
863
+
864
+ const changeMode = <A, E, R, I extends Input>(
865
+ self: Parser<A, E, R, I>,
866
+ from: ErrMode,
867
+ to: ErrMode
868
+ ): Parser<A, E, R, I> => {
869
+ const step = self.step
870
+ return described((state) => {
871
+ const a = step(state)
872
+ if (a === Fail && state.mode === from) state.mode = to
873
+ return a
874
+ }, self.name, { _tag: "Mode", self, from, to })
875
+ }
876
+
877
+ /**
878
+ * Commits to the current branch: recoverable failures of the parser become
879
+ * unrecoverable, so enclosing `alt`, `opt`, `repeat`, ... do not try
880
+ * alternatives (winnow's `cut_err`, nom's `cut`).
881
+ *
882
+ * @example
883
+ * ```ts
884
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
885
+ *
886
+ * // Once we have seen "-", a number must follow
887
+ * const negative = Parser.preceded(Token.literal("-"), Parser.cut(Ascii.decUint))
888
+ * ```
889
+ *
890
+ * @since 0.1.0
891
+ * @category error management
892
+ */
893
+ export const cut = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<A, E, R, I> =>
894
+ changeMode(self, "Backtrack", "Cut")
895
+
896
+ /**
897
+ * Turns unrecoverable failures back into recoverable ones (winnow's
898
+ * `backtrack_err`).
899
+ *
900
+ * @since 0.1.0
901
+ * @category error management
902
+ */
903
+ export const backtrack = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<A, E, R, I> =>
904
+ changeMode(self, "Cut", "Backtrack")
905
+
906
+ /**
907
+ * Turns `Incomplete` failures into recoverable ones, treating the input as
908
+ * complete for this parser (winnow's `complete_err`, nom's `complete`).
909
+ *
910
+ * @since 0.1.0
911
+ * @category error management
912
+ */
913
+ export const complete = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<A, E, R, I> => {
914
+ const step = self.step
915
+ return described((state) => {
916
+ const start = state.pos
917
+ const a = step(state)
918
+ if (a === Fail && state.mode === "Incomplete") {
919
+ state.pos = start
920
+ return state.backtrack("Complete")
921
+ }
922
+ return a
923
+ }, self.name, { _tag: "Mode", self, from: "Incomplete", to: "Backtrack" })
924
+ }
925
+
926
+ /**
927
+ * Adds a context label to failures of a parser (winnow's
928
+ * `context(StrContext::Label(..))`, nom's `context`). Labels are rendered as
929
+ * "while parsing <label>" in the error message.
930
+ *
931
+ * @since 0.1.0
932
+ * @category error management
933
+ */
934
+ export const context: {
935
+ (label: string): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E, R, I>
936
+ <A, E, R, I extends Input>(self: Parser<A, E, R, I>, label: string): Parser<A, E, R, I>
937
+ } = dual(2, <A, E, R, I extends Input>(self: Parser<A, E, R, I>, label: string): Parser<A, E, R, I> => {
938
+ const step = self.step
939
+ return described((state) => {
940
+ const a = step(state)
941
+ if (a === Fail && state.mode !== "Incomplete") {
942
+ const f = state.failure
943
+ state.replaceFailure(new Failure(
944
+ f.offset,
945
+ f.kind,
946
+ f.expected,
947
+ [...f.context, label],
948
+ f.hasCause,
949
+ f.cause,
950
+ f.message,
951
+ f.needed
952
+ ))
953
+ }
954
+ return a
955
+ }, self.name ?? label, { _tag: "Context", self, label })
956
+ })
957
+
958
+ /**
959
+ * Names what a parser expects. When the parser fails without consuming any
960
+ * input, its expectations are replaced with `description`, so a failure is
961
+ * reported as `expected <description>` rather than in terms of the parser's
962
+ * internals (Parsec's `<?>`, winnow's `StrContext::Expected`).
963
+ *
964
+ * @since 0.1.0
965
+ * @category error management
966
+ */
967
+ export const expected: {
968
+ (description: string): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E, R, I>
969
+ <A, E, R, I extends Input>(self: Parser<A, E, R, I>, description: string): Parser<A, E, R, I>
970
+ } = dual(2, <A, E, R, I extends Input>(self: Parser<A, E, R, I>, description: string): Parser<A, E, R, I> => {
971
+ const step = self.step
972
+ const desc = [description]
973
+ return described((state) => {
974
+ const start = state.pos
975
+ const hintOffset = state.hintOffset
976
+ const hintExpected = state.hintExpected
977
+ const a = step(state)
978
+ if (a === Fail && state.failure.offset === start) {
979
+ const f = state.failure
980
+ const expected = hintOffset === start ? mergeExpected(hintExpected, desc) : desc
981
+ state.replaceFailure(new Failure(f.offset, f.kind, expected, f.context, f.hasCause, f.cause, f.message, f.needed))
982
+ }
983
+ return a
984
+ }, description, { _tag: "Expected", self, description })
985
+ })
986
+
987
+ /**
988
+ * Transforms the user error (cause) of a parser's failures.
989
+ *
990
+ * @since 0.1.0
991
+ * @category error management
992
+ */
993
+ export const mapError: {
994
+ <E, E2>(f: (e: E) => E2): <A, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E2, R, I>
995
+ <A, E, R, I extends Input, E2>(self: Parser<A, E, R, I>, f: (e: E) => E2): Parser<A, E2, R, I>
996
+ } = dual(2, <A, E, R, I extends Input, E2>(self: Parser<A, E, R, I>, f: (e: E) => E2): Parser<A, E2, R, I> => {
997
+ const step = self.step
998
+ return described((state) => {
999
+ const a = step(state)
1000
+ if (a === Fail && state.failure.hasCause) {
1001
+ const x = state.failure
1002
+ state.replaceFailure(new Failure(x.offset, x.kind, x.expected, x.context, true, f(x.cause as E), x.message, x.needed))
1003
+ }
1004
+ return a
1005
+ }, self.name, { _tag: "MapError", self })
1006
+ })
1007
+
1008
+ /**
1009
+ * Replaces any failure of a parser with the given user error, keeping the
1010
+ * failure's position and mode.
1011
+ *
1012
+ * @since 0.1.0
1013
+ * @category error management
1014
+ */
1015
+ export const orFail: {
1016
+ <E2>(error: () => E2): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E2, R, I>
1017
+ <A, E, R, I extends Input, E2>(self: Parser<A, E, R, I>, error: () => E2): Parser<A, E2, R, I>
1018
+ } = dual(2, <A, E, R, I extends Input, E2>(self: Parser<A, E, R, I>, error: () => E2): Parser<A, E2, R, I> => {
1019
+ const step = self.step
1020
+ return described((state) => {
1021
+ const start = state.pos
1022
+ const a = step(state)
1023
+ if (a === Fail && state.mode !== "Incomplete") {
1024
+ const x = state.failure
1025
+ state.replaceFailure(new Failure(start, x.kind, [], x.context, true, error(), undefined, 0))
1026
+ }
1027
+ return a
1028
+ }, self.name, { _tag: "OrFail", self })
1029
+ })
1030
+
1031
+ /**
1032
+ * If the parser fails recoverably, tries `that` from the same position.
1033
+ * `p.pipe(Parser.orElse(() => q))` is equivalent to `Parser.alt(p, q)`.
1034
+ *
1035
+ * @since 0.1.0
1036
+ * @category error management
1037
+ */
1038
+ export const orElse: {
1039
+ <B, E2, R2, I2 extends Input>(
1040
+ that: () => Parser<B, E2, R2, I2>
1041
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A | B, E | E2, R | R2, I & I2>
1042
+ <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
1043
+ self: Parser<A, E, R, I>,
1044
+ that: () => Parser<B, E2, R2, I2>
1045
+ ): Parser<A | B, E | E2, R | R2, I & I2>
1046
+ } = dual(2, <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
1047
+ self: Parser<A, E, R, I>,
1048
+ that: () => Parser<B, E2, R2, I2>
1049
+ ): Parser<A | B, E | E2, R | R2, I & I2> => alt(self, suspend(that)))
1050
+
1051
+ /**
1052
+ * If the parser fails recoverably, succeeds with the given value without
1053
+ * consuming input.
1054
+ *
1055
+ * @since 0.1.0
1056
+ * @category error management
1057
+ */
1058
+ export const orElseSucceed: {
1059
+ <B>(value: () => B): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A | B, E, R, I>
1060
+ <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, value: () => B): Parser<A | B, E, R, I>
1061
+ } = dual(2, <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, value: () => B): Parser<A | B, E, R, I> => {
1062
+ const step = self.step
1063
+ return described((state) => {
1064
+ const start = state.pos
1065
+ const a = step(state)
1066
+ if (a !== Fail) return a
1067
+ if (state.mode !== "Backtrack") return Fail
1068
+ recover(state, start)
1069
+ return value()
1070
+ }, self.name, { _tag: "OrElseSucceed", self, value })
1071
+ })
1072
+
1073
+ /** Rewinds after a recoverable failure, remembering it as a hint. */
1074
+ const recover = (state: State<any>, start: number): void => {
1075
+ state.pos = start
1076
+ const f = state.failure
1077
+ if (f.offset === start) {
1078
+ state.hintOffset = start
1079
+ state.hintExpected = f.expected
1080
+ }
1081
+ }
1082
+
1083
+ // -----------------------------------------------------------------------------
1084
+ // Control
1085
+ // -----------------------------------------------------------------------------
1086
+
1087
+ /**
1088
+ * Makes a parser optional: returns `Option.none()` without consuming input if
1089
+ * it fails recoverably (winnow's and nom's `opt`).
1090
+ *
1091
+ * @since 0.1.0
1092
+ * @category control
1093
+ */
1094
+ export const opt = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<Option.Option<A>, E, R, I> => {
1095
+ const step = self.step
1096
+ return described((state) => {
1097
+ const start = state.pos
1098
+ const a = step(state)
1099
+ if (a !== Fail) return Option.some(a)
1100
+ if (state.mode !== "Backtrack") return Fail
1101
+ recover(state, start)
1102
+ return Option.none()
1103
+ }, self.name, { _tag: "Opt", self })
1104
+ }
1105
+
1106
+ /**
1107
+ * Like {@link opt}, but returns `undefined` instead of `None`.
1108
+ *
1109
+ * @since 0.1.0
1110
+ * @category control
1111
+ */
1112
+ export const optional = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<A | undefined, E, R, I> =>
1113
+ orElseSucceed(self, () => undefined)
1114
+
1115
+ /**
1116
+ * Runs the parser only if `condition` holds, otherwise succeeds with
1117
+ * `Option.none()` (winnow's and nom's `cond`).
1118
+ *
1119
+ * @since 0.1.0
1120
+ * @category control
1121
+ */
1122
+ export const cond = <A, E, R, I extends Input>(
1123
+ condition: boolean,
1124
+ self: Parser<A, E, R, I>
1125
+ ): Parser<Option.Option<A>, E, R, I> => condition ? asSome(self) : succeed(Option.none())
1126
+
1127
+ /**
1128
+ * Runs a parser without consuming input (winnow's and nom's `peek`).
1129
+ *
1130
+ * @since 0.1.0
1131
+ * @category control
1132
+ */
1133
+ export const peek = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<A, E, R, I> => {
1134
+ const step = self.step
1135
+ return described((state) => {
1136
+ const start = state.pos
1137
+ const hintOffset = state.hintOffset
1138
+ const hintExpected = state.hintExpected
1139
+ const a = step(state)
1140
+ if (a !== Fail) {
1141
+ state.pos = start
1142
+ state.hintOffset = hintOffset
1143
+ state.hintExpected = hintExpected
1144
+ }
1145
+ return a
1146
+ }, self.name, { _tag: "Peek", self })
1147
+ }
1148
+
1149
+ /**
1150
+ * Succeeds, without consuming input, only if the parser fails recoverably
1151
+ * (winnow's and nom's `not`).
1152
+ *
1153
+ * @since 0.1.0
1154
+ * @category control
1155
+ */
1156
+ export const not = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<void, E, R, I> => {
1157
+ const step = self.step
1158
+ return described((state) => {
1159
+ const start = state.pos
1160
+ const hintOffset = state.hintOffset
1161
+ const hintExpected = state.hintExpected
1162
+ const a = step(state)
1163
+ if (a !== Fail) {
1164
+ state.pos = start
1165
+ return state.backtrack("Not")
1166
+ }
1167
+ if (state.mode !== "Backtrack") return Fail
1168
+ state.pos = start
1169
+ state.hintOffset = hintOffset
1170
+ state.hintExpected = hintExpected
1171
+ return undefined
1172
+ }, self.name, { _tag: "Not", self })
1173
+ }
1174
+
1175
+ /**
1176
+ * Requires the parser to consume the whole input (nom's `all_consuming`).
1177
+ *
1178
+ * @since 0.1.0
1179
+ * @category control
1180
+ */
1181
+ export const allConsuming = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<A, E, R, I> =>
1182
+ terminated(self, eof)
1183
+
1184
+ // -----------------------------------------------------------------------------
1185
+ // Sequencing
1186
+ // -----------------------------------------------------------------------------
1187
+
1188
+ /**
1189
+ * Runs parsers in sequence, collecting their outputs in a tuple (winnow's
1190
+ * and nom's tuple parsers, winnow's `seq!`).
1191
+ *
1192
+ * @since 0.1.0
1193
+ * @category sequencing
1194
+ */
1195
+ export const seq = <const Ps extends ReadonlyArray<Parser.Any>>(
1196
+ ...parsers: Ps
1197
+ ): Parser<
1198
+ { -readonly [K in keyof Ps]: Parser.Success<Ps[K]> },
1199
+ Parser.Error<Ps[number]>,
1200
+ Parser.Services<Ps[number]>,
1201
+ Parser.InputOf<Ps[number]>
1202
+ > => {
1203
+ const steps = parsers.map((p) => p.step)
1204
+ const n = steps.length
1205
+ return described((state: State<any>) => {
1206
+ const out = new Array(n)
1207
+ for (let i = 0; i < n; i++) {
1208
+ const a = steps[i]!(state)
1209
+ if (a === Fail) return Fail
1210
+ out[i] = a
1211
+ }
1212
+ return out as any
1213
+ }, "seq", { _tag: "Seq", parsers, output: "tuple" })
1214
+ }
1215
+
1216
+ /**
1217
+ * Runs a record of parsers in sequence (in key order), collecting their
1218
+ * outputs in a record. This is the Effect counterpart to winnow's
1219
+ * `seq!(Struct { .. })`.
1220
+ *
1221
+ * @since 0.1.0
1222
+ * @category sequencing
1223
+ */
1224
+ export const struct = <const Fields extends Readonly<Record<string, Parser.Any>>>(
1225
+ fields: Fields
1226
+ ): Parser<
1227
+ Simplify<{ -readonly [K in keyof Fields]: Parser.Success<Fields[K]> }>,
1228
+ Parser.Error<Fields[keyof Fields]>,
1229
+ Parser.Services<Fields[keyof Fields]>,
1230
+ Parser.InputOf<Fields[keyof Fields]>
1231
+ > => {
1232
+ const keys = Object.keys(fields)
1233
+ const steps = keys.map((k) => fields[k]!.step)
1234
+ const n = keys.length
1235
+ return described((state: State<any>) => {
1236
+ const out: Record<string, unknown> = {}
1237
+ for (let i = 0; i < n; i++) {
1238
+ const a = steps[i]!(state)
1239
+ if (a === Fail) return Fail
1240
+ out[keys[i]!] = a
1241
+ }
1242
+ return out as any
1243
+ }, "struct", { _tag: "Struct", keys, parsers: keys.map((k) => fields[k]!) })
1244
+ }
1245
+
1246
+ /**
1247
+ * Runs two parsers in sequence and returns both outputs.
1248
+ *
1249
+ * @since 0.1.0
1250
+ * @category sequencing
1251
+ */
1252
+ export const zip: {
1253
+ <B, E2, R2, I2 extends Input>(
1254
+ that: Parser<B, E2, R2, I2>
1255
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<[A, B], E | E2, R | R2, I & I2>
1256
+ <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
1257
+ self: Parser<A, E, R, I>,
1258
+ that: Parser<B, E2, R2, I2>
1259
+ ): Parser<[A, B], E | E2, R | R2, I & I2>
1260
+ } = dual(2, <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
1261
+ self: Parser<A, E, R, I>,
1262
+ that: Parser<B, E2, R2, I2>
1263
+ ): Parser<[A, B], E | E2, R | R2, I & I2> => zipWith(self, that, (a, b) => [a, b]))
1264
+
1265
+ /**
1266
+ * Runs two parsers in sequence and combines their outputs.
1267
+ *
1268
+ * @since 0.1.0
1269
+ * @category sequencing
1270
+ */
1271
+ export const zipWith: {
1272
+ <B, E2, R2, I2 extends Input, A, C>(
1273
+ that: Parser<B, E2, R2, I2>,
1274
+ f: (a: A, b: B) => C
1275
+ ): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<C, E | E2, R | R2, I & I2>
1276
+ <A, E, R, I extends Input, B, E2, R2, I2 extends Input, C>(
1277
+ self: Parser<A, E, R, I>,
1278
+ that: Parser<B, E2, R2, I2>,
1279
+ f: (a: A, b: B) => C
1280
+ ): Parser<C, E | E2, R | R2, I & I2>
1281
+ } = dual(3, <A, E, R, I extends Input, B, E2, R2, I2 extends Input, C>(
1282
+ self: Parser<A, E, R, I>,
1283
+ that: Parser<B, E2, R2, I2>,
1284
+ f: (a: A, b: B) => C
1285
+ ): Parser<C, E | E2, R | R2, I & I2> => {
1286
+ const s1 = self.step
1287
+ const s2 = that.step
1288
+ return described((state) => {
1289
+ const a = s1(state)
1290
+ if (a === Fail) return Fail
1291
+ const b = s2(state)
1292
+ if (b === Fail) return Fail
1293
+ return f(a, b)
1294
+ }, undefined, { _tag: "ZipWith", self, that, f })
1295
+ })
1296
+
1297
+ /**
1298
+ * Runs two parsers in sequence, keeping the output of the first.
1299
+ *
1300
+ * @since 0.1.0
1301
+ * @category sequencing
1302
+ */
1303
+ export const zipLeft: {
1304
+ <B, E2, R2, I2 extends Input>(
1305
+ that: Parser<B, E2, R2, I2>
1306
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E | E2, R | R2, I & I2>
1307
+ <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
1308
+ self: Parser<A, E, R, I>,
1309
+ that: Parser<B, E2, R2, I2>
1310
+ ): Parser<A, E | E2, R | R2, I & I2>
1311
+ } = dual(2, <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
1312
+ self: Parser<A, E, R, I>,
1313
+ that: Parser<B, E2, R2, I2>
1314
+ ): Parser<A, E | E2, R | R2, I & I2> => terminated(self, that))
1315
+
1316
+ /**
1317
+ * Runs two parsers in sequence, keeping the output of the second.
1318
+ *
1319
+ * @since 0.1.0
1320
+ * @category sequencing
1321
+ */
1322
+ export const zipRight: {
1323
+ <B, E2, R2, I2 extends Input>(
1324
+ that: Parser<B, E2, R2, I2>
1325
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E | E2, R | R2, I & I2>
1326
+ <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
1327
+ self: Parser<A, E, R, I>,
1328
+ that: Parser<B, E2, R2, I2>
1329
+ ): Parser<B, E | E2, R | R2, I & I2>
1330
+ } = dual(2, <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
1331
+ self: Parser<A, E, R, I>,
1332
+ that: Parser<B, E2, R2, I2>
1333
+ ): Parser<B, E | E2, R | R2, I & I2> => preceded(self, that))
1334
+
1335
+ /**
1336
+ * Runs `prefix` then `parser`, keeping the output of `parser` (winnow's and
1337
+ * nom's `preceded`).
1338
+ *
1339
+ * @since 0.1.0
1340
+ * @category sequencing
1341
+ */
1342
+ export const preceded = <A1, E1, R1, I1 extends Input, A2, E2, R2, I2 extends Input>(
1343
+ prefix: Parser<A1, E1, R1, I1>,
1344
+ parser: Parser<A2, E2, R2, I2>
1345
+ ): Parser<A2, E1 | E2, R1 | R2, I1 & I2> => {
1346
+ const s1 = prefix.step
1347
+ const s2 = parser.step
1348
+ return described((state) => s1(state) === Fail ? Fail : s2(state), parser.name, {
1349
+ _tag: "Seq",
1350
+ parsers: [prefix, parser],
1351
+ output: 1
1352
+ })
1353
+ }
1354
+
1355
+ /**
1356
+ * Runs `parser` then `suffix`, keeping the output of `parser` (winnow's and
1357
+ * nom's `terminated`).
1358
+ *
1359
+ * @since 0.1.0
1360
+ * @category sequencing
1361
+ */
1362
+ export const terminated = <A1, E1, R1, I1 extends Input, A2, E2, R2, I2 extends Input>(
1363
+ parser: Parser<A1, E1, R1, I1>,
1364
+ suffix: Parser<A2, E2, R2, I2>
1365
+ ): Parser<A1, E1 | E2, R1 | R2, I1 & I2> => {
1366
+ const s1 = parser.step
1367
+ const s2 = suffix.step
1368
+ return described((state) => {
1369
+ const a = s1(state)
1370
+ if (a === Fail) return Fail
1371
+ return s2(state) === Fail ? Fail : a
1372
+ }, parser.name, { _tag: "Seq", parsers: [parser, suffix], output: 0 })
1373
+ }
1374
+
1375
+ /**
1376
+ * Runs `open`, `parser` and `close`, keeping the output of `parser`
1377
+ * (winnow's and nom's `delimited`).
1378
+ *
1379
+ * @since 0.1.0
1380
+ * @category sequencing
1381
+ */
1382
+ export const delimited = <
1383
+ A1,
1384
+ E1,
1385
+ R1,
1386
+ I1 extends Input,
1387
+ A2,
1388
+ E2,
1389
+ R2,
1390
+ I2 extends Input,
1391
+ A3,
1392
+ E3,
1393
+ R3,
1394
+ I3 extends Input
1395
+ >(
1396
+ open: Parser<A1, E1, R1, I1>,
1397
+ parser: Parser<A2, E2, R2, I2>,
1398
+ close: Parser<A3, E3, R3, I3>
1399
+ ): Parser<A2, E1 | E2 | E3, R1 | R2 | R3, I1 & I2 & I3> => {
1400
+ const s1 = open.step
1401
+ const s2 = parser.step
1402
+ const s3 = close.step
1403
+ return described((state) => {
1404
+ if (s1(state) === Fail) return Fail
1405
+ const a = s2(state)
1406
+ if (a === Fail) return Fail
1407
+ return s3(state) === Fail ? Fail : a
1408
+ }, parser.name, { _tag: "Seq", parsers: [open, parser, close], output: 1 })
1409
+ }
1410
+
1411
+ /**
1412
+ * Runs `first`, `separator` and `second`, keeping the outputs of `first` and
1413
+ * `second` (winnow's and nom's `separated_pair`).
1414
+ *
1415
+ * @since 0.1.0
1416
+ * @category sequencing
1417
+ */
1418
+ export const separatedPair = <
1419
+ A1,
1420
+ E1,
1421
+ R1,
1422
+ I1 extends Input,
1423
+ A2,
1424
+ E2,
1425
+ R2,
1426
+ I2 extends Input,
1427
+ A3,
1428
+ E3,
1429
+ R3,
1430
+ I3 extends Input
1431
+ >(
1432
+ first: Parser<A1, E1, R1, I1>,
1433
+ separator: Parser<A2, E2, R2, I2>,
1434
+ second: Parser<A3, E3, R3, I3>
1435
+ ): Parser<[A1, A3], E1 | E2 | E3, R1 | R2 | R3, I1 & I2 & I3> => {
1436
+ const s1 = first.step
1437
+ const s2 = separator.step
1438
+ const s3 = second.step
1439
+ return described((state) => {
1440
+ const a = s1(state)
1441
+ if (a === Fail) return Fail
1442
+ if (s2(state) === Fail) return Fail
1443
+ const b = s3(state)
1444
+ return b === Fail ? Fail : [a, b]
1445
+ }, undefined, { _tag: "Seq", parsers: [first, separator, second], output: [0, 2] })
1446
+ }
1447
+
1448
+ /**
1449
+ * Writes a parser in imperative style with a generator, Effect style:
1450
+ * `yield*` a parser to run it and get its output.
1451
+ *
1452
+ * @example
1453
+ * ```ts
1454
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1455
+ *
1456
+ * const keyValue = Parser.gen(function*() {
1457
+ * const key = yield* Ascii.alpha1
1458
+ * yield* Token.literal("=")
1459
+ * const value = yield* Ascii.decInt
1460
+ * return [key, value] as const
1461
+ * })
1462
+ * ```
1463
+ *
1464
+ * @since 0.1.0
1465
+ * @category sequencing
1466
+ */
1467
+ export const gen = <Eff extends Parser.Any, AEff>(
1468
+ f: () => Generator<Eff, AEff, never>
1469
+ ): Parser<AEff, Parser.Error<Eff>, Parser.Services<Eff>, Parser.InputOf<Eff>> =>
1470
+ make((state: State<any>) => {
1471
+ const iterator = f()
1472
+ let next = iterator.next()
1473
+ while (!next.done) {
1474
+ const a = next.value.step(state)
1475
+ if (a === Fail) {
1476
+ iterator.return?.(undefined as any)
1477
+ return Fail
1478
+ }
1479
+ next = iterator.next(a as never)
1480
+ }
1481
+ return next.value
1482
+ }, "gen")
1483
+
1484
+ /**
1485
+ * The starting point for Do notation.
1486
+ *
1487
+ * @example
1488
+ * ```ts
1489
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1490
+ *
1491
+ * const range = Parser.Do.pipe(
1492
+ * Parser.bind("start", () => Ascii.decUint),
1493
+ * Parser.zipLeft(Token.literal("..")),
1494
+ * Parser.bind("end", () => Ascii.decUint)
1495
+ * )
1496
+ * ```
1497
+ *
1498
+ * @since 0.1.0
1499
+ * @category do notation
1500
+ */
1501
+ export const Do: Parser<{}, never, never, Input> = succeed({})
1502
+
1503
+ /**
1504
+ * Binds the output of a parser to a field of the Do record.
1505
+ *
1506
+ * @since 0.1.0
1507
+ * @category do notation
1508
+ */
1509
+ export const bind: {
1510
+ <N extends string, A extends object, B, E2, R2, I2 extends Input>(
1511
+ name: Exclude<N, keyof A>,
1512
+ f: (a: NoInfer<A>) => Parser<B, E2, R2, I2>
1513
+ ): <E, R, I extends Input>(
1514
+ self: Parser<A, E, R, I>
1515
+ ) => Parser<Simplify<Omit<A, N> & { [K in N]: B }>, E | E2, R | R2, I & I2>
1516
+ <A extends object, E, R, I extends Input, N extends string, B, E2, R2, I2 extends Input>(
1517
+ self: Parser<A, E, R, I>,
1518
+ name: Exclude<N, keyof A>,
1519
+ f: (a: NoInfer<A>) => Parser<B, E2, R2, I2>
1520
+ ): Parser<Simplify<Omit<A, N> & { [K in N]: B }>, E | E2, R | R2, I & I2>
1521
+ } = dual(3, (self: Parser.Any, name: string, f: (a: any) => Parser.Any): Parser.Any =>
1522
+ flatMap(self, (a) => map(f(a), (b) => ({ ...a, [name]: b }))))
1523
+
1524
+ /**
1525
+ * Wraps the output of a parser in a record with a single field.
1526
+ *
1527
+ * @since 0.1.0
1528
+ * @category do notation
1529
+ */
1530
+ export const bindTo: {
1531
+ <N extends string>(name: N): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<{ [K in N]: A }, E, R, I>
1532
+ <A, E, R, I extends Input, N extends string>(self: Parser<A, E, R, I>, name: N): Parser<{ [K in N]: A }, E, R, I>
1533
+ } = dual(2, (self: Parser.Any, name: string): Parser.Any => map(self, (a) => ({ [name]: a })))
1534
+
1535
+ const let_: {
1536
+ <N extends string, A extends object, B>(
1537
+ name: Exclude<N, keyof A>,
1538
+ f: (a: NoInfer<A>) => B
1539
+ ): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<Simplify<Omit<A, N> & { [K in N]: B }>, E, R, I>
1540
+ <A extends object, E, R, I extends Input, N extends string, B>(
1541
+ self: Parser<A, E, R, I>,
1542
+ name: Exclude<N, keyof A>,
1543
+ f: (a: NoInfer<A>) => B
1544
+ ): Parser<Simplify<Omit<A, N> & { [K in N]: B }>, E, R, I>
1545
+ } = dual(3, (self: Parser.Any, name: string, f: (a: any) => unknown): Parser.Any =>
1546
+ map(self, (a) => ({ ...a, [name]: f(a) })))
1547
+
1548
+ export {
1549
+ /**
1550
+ * Adds a computed field to the Do record.
1551
+ *
1552
+ * @since 0.1.0
1553
+ * @category do notation
1554
+ */
1555
+ let_ as let
1556
+ }
1557
+
1558
+ // -----------------------------------------------------------------------------
1559
+ // Choice
1560
+ // -----------------------------------------------------------------------------
1561
+
1562
+ /**
1563
+ * Tries each parser in turn and returns the output of the first that
1564
+ * succeeds (winnow's and nom's `alt`).
1565
+ *
1566
+ * Unrecoverable (`Cut`) and `Incomplete` failures stop the search
1567
+ * immediately. If every alternative fails recoverably, the failures are
1568
+ * merged: the one that got furthest wins, and failures at the same offset
1569
+ * combine their expectations, e.g. `expected "true", "false" or "null"`.
1570
+ *
1571
+ * @since 0.1.0
1572
+ * @category choice
1573
+ */
1574
+ export const alt = <const Ps extends ReadonlyArray<Parser.Any>>(
1575
+ ...parsers: Ps
1576
+ ): Parser<
1577
+ Parser.Success<Ps[number]>,
1578
+ Parser.Error<Ps[number]>,
1579
+ Parser.Services<Ps[number]>,
1580
+ Parser.InputOf<Ps[number]>
1581
+ > => {
1582
+ const steps = parsers.map((p) => p.step)
1583
+ const n = steps.length
1584
+ if (n === 0) return reject as any
1585
+ if (n === 1) return parsers[0]!
1586
+ return described((state: State<any>) => {
1587
+ const start = state.pos
1588
+ let failure: Failure | undefined
1589
+ for (let i = 0; i < n; i++) {
1590
+ const a = steps[i]!(state)
1591
+ if (a !== Fail) {
1592
+ if (failure !== undefined && failure.offset === state.pos) state.setHint(failure.offset, failure.expected)
1593
+ return a
1594
+ }
1595
+ if (state.mode !== "Backtrack") return Fail
1596
+ failure = failure === undefined ? state.failure : mergeFailures(failure, state.failure)
1597
+ state.pos = start
1598
+ }
1599
+ return state.failWith("Backtrack", failure!)
1600
+ }, "alt", { _tag: "Alt", parsers })
1601
+ }
1602
+
1603
+ /**
1604
+ * Selects the parser to run next based on the output of `selector`, like a
1605
+ * `match` over the input (winnow's `dispatch!`). If no case matches, the
1606
+ * parser backtracks to where it started.
1607
+ *
1608
+ * Cases are given either as a record keyed by the selector's output (with
1609
+ * an optional `otherwise` parser for unlisted keys, like winnow's `_ =>`
1610
+ * arm), or as a function returning a parser (or `undefined` for no match).
1611
+ *
1612
+ * @example
1613
+ * ```ts
1614
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1615
+ *
1616
+ * const escape = Parser.preceded(
1617
+ * Token.literal("\\"),
1618
+ * Parser.dispatch(Token.any, {
1619
+ * n: Parser.succeed("\n"),
1620
+ * t: Parser.succeed("\t"),
1621
+ * u: Parser.preceded(Token.literal("{"), Ascii.hexUint).pipe(
1622
+ * Parser.zipLeft(Token.literal("}")),
1623
+ * Parser.map((cp) => String.fromCodePoint(cp))
1624
+ * )
1625
+ * })
1626
+ * )
1627
+ * ```
1628
+ *
1629
+ * @since 0.1.0
1630
+ * @category choice
1631
+ */
1632
+ export const dispatch: {
1633
+ <K, E, R, I extends Input, P extends Parser.Any>(
1634
+ selector: Parser<K, E, R, I>,
1635
+ cases: (key: K) => P | undefined
1636
+ ): Parser<Parser.Success<P>, E | Parser.Error<P>, R | Parser.Services<P>, I & Parser.InputOf<P>>
1637
+ <
1638
+ K extends PropertyKey,
1639
+ E,
1640
+ R,
1641
+ I extends Input,
1642
+ const Cases extends { readonly [key in K]?: Parser.Any },
1643
+ Otherwise extends Parser.Any = never
1644
+ >(
1645
+ selector: Parser<K, E, R, I>,
1646
+ cases: Cases,
1647
+ otherwise?: Otherwise
1648
+ ): Parser<
1649
+ Parser.Success<NonNullable<Cases[keyof Cases]> | Otherwise>,
1650
+ E | Parser.Error<NonNullable<Cases[keyof Cases]> | Otherwise>,
1651
+ R | Parser.Services<NonNullable<Cases[keyof Cases]> | Otherwise>,
1652
+ I & Parser.InputOf<NonNullable<Cases[keyof Cases]> | Otherwise>
1653
+ >
1654
+ } = (
1655
+ selector: Parser.Any,
1656
+ cases: ((key: any) => Parser.Any | undefined) | Record<PropertyKey, Parser.Any>,
1657
+ otherwise?: Parser.Any
1658
+ ) => {
1659
+ const choose = typeof cases === "function" ? cases : (key: PropertyKey) =>
1660
+ Object.prototype.hasOwnProperty.call(cases, key) ? cases[key] : otherwise
1661
+ const step = selector.step
1662
+ return described((state: State<any>) => {
1663
+ const start = state.pos
1664
+ const key = step(state)
1665
+ if (key === Fail) return Fail
1666
+ const parser = choose(key)
1667
+ if (parser === undefined) {
1668
+ state.pos = start
1669
+ return state.backtrack("Dispatch")
1670
+ }
1671
+ return parser.step(state)
1672
+ }, "dispatch", {
1673
+ _tag: "Dispatch",
1674
+ selector,
1675
+ cases: typeof cases === "function" ? undefined : cases,
1676
+ otherwise: typeof cases === "function" ? undefined : otherwise,
1677
+ choose
1678
+ })
1679
+ }
1680
+
1681
+ /**
1682
+ * Applies every parser exactly once, in whatever order they match, and
1683
+ * returns their outputs in the order the parsers were given (winnow's and
1684
+ * nom's `permutation`).
1685
+ *
1686
+ * @since 0.1.0
1687
+ * @category choice
1688
+ */
1689
+ export const permutation = <const Ps extends ReadonlyArray<Parser.Any>>(
1690
+ ...parsers: Ps
1691
+ ): Parser<
1692
+ { -readonly [K in keyof Ps]: Parser.Success<Ps[K]> },
1693
+ Parser.Error<Ps[number]>,
1694
+ Parser.Services<Ps[number]>,
1695
+ Parser.InputOf<Ps[number]>
1696
+ > => {
1697
+ const steps = parsers.map((p) => p.step)
1698
+ const n = steps.length
1699
+ return make((state: State<any>) => {
1700
+ const out = new Array(n)
1701
+ const done = new Array<boolean>(n).fill(false)
1702
+ let remaining = n
1703
+ while (remaining > 0) {
1704
+ let failure: Failure | undefined
1705
+ let matched = false
1706
+ for (let i = 0; i < n; i++) {
1707
+ if (done[i]) continue
1708
+ const start = state.pos
1709
+ const a = steps[i]!(state)
1710
+ if (a !== Fail) {
1711
+ out[i] = a
1712
+ done[i] = true
1713
+ remaining--
1714
+ matched = true
1715
+ break
1716
+ }
1717
+ if (state.mode !== "Backtrack") return Fail
1718
+ failure = failure === undefined ? state.failure : mergeFailures(failure, state.failure)
1719
+ state.pos = start
1720
+ }
1721
+ if (!matched) return state.failWith("Backtrack", failure!)
1722
+ }
1723
+ return out as any
1724
+ }, "permutation")
1725
+ }
1726
+
1727
+ // -----------------------------------------------------------------------------
1728
+ // Repetition
1729
+ // -----------------------------------------------------------------------------
1730
+
1731
+ const normalizeRange = (range: Range | undefined): [min: number, max: number] => {
1732
+ if (range === undefined) return [0, Infinity]
1733
+ if (typeof range === "number") return [range, range]
1734
+ const min = range.min ?? 0
1735
+ const max = range.max ?? Infinity
1736
+ if (min > max) throw new RangeError(`invalid range: min (${min}) > max (${max})`)
1737
+ return [min, max]
1738
+ }
1739
+
1740
+ const assertConsumes = (state: State<any>, name: string): Fail =>
1741
+ state.failMessage("Cut", "Assert", `\`${name}\` parsers must always consume`)
1742
+
1743
+ /** How far a repetition got before a streaming parse ran out of input. */
1744
+ interface FoldCheckpoint<B> {
1745
+ readonly acc: B
1746
+ readonly count: number
1747
+ /** Where the next element starts. */
1748
+ readonly pos: number
1749
+ }
1750
+
1751
+ const saveFold = <B>(state: State<any>, key: object, entry: number, acc: B, pos: number): void =>
1752
+ Resume.save(state, key, entry, { acc, count: 0, pos } satisfies FoldCheckpoint<B>)
1753
+
1754
+ const foldImpl = <A, B, E, R, I extends Input>(
1755
+ self: Parser<A, E, R, I>,
1756
+ range: Range | undefined,
1757
+ init: () => B,
1758
+ f: (acc: B, a: A) => B,
1759
+ name: string,
1760
+ node?: Node
1761
+ ): Parser<B, E, R, I> => {
1762
+ const [min, max] = normalizeRange(range)
1763
+ const step = self.step
1764
+ const checkLoop = max === Infinity
1765
+ const key = {}
1766
+ return described((state) => {
1767
+ const entry = state.pos
1768
+ let acc: B
1769
+ let count = 0
1770
+ const resumed = state.resume === undefined ? undefined : Resume.take(state, key, entry) as FoldCheckpoint<B>
1771
+ if (resumed instanceof Resume.Done) {
1772
+ state.pos = resumed.pos
1773
+ return resumed.value as B
1774
+ }
1775
+ if (resumed !== undefined) {
1776
+ acc = resumed.acc
1777
+ count = resumed.count
1778
+ state.pos = resumed.pos
1779
+ } else {
1780
+ acc = init()
1781
+ }
1782
+ for (; count < max; count++) {
1783
+ const start = state.pos
1784
+ const a = step(state)
1785
+ if (a === Fail) {
1786
+ if (state.mode === "Incomplete" && state.resume !== undefined) {
1787
+ Resume.save(state, key, entry, { acc, count, pos: start } satisfies FoldCheckpoint<B>)
1788
+ }
1789
+ if (state.mode !== "Backtrack" || count < min) return Fail
1790
+ recover(state, start)
1791
+ return Resume.done(state, key, entry, resumed !== undefined, acc)
1792
+ }
1793
+ if (checkLoop && state.pos === start) return assertConsumes(state, name)
1794
+ acc = f(acc, a)
1795
+ }
1796
+ return Resume.done(state, key, entry, resumed !== undefined, acc)
1797
+ }, name, node ?? { _tag: "Fold", self, init, f, name, ...bounds(range) })
1798
+ }
1799
+
1800
+ /**
1801
+ * Applies a parser repeatedly, collecting the outputs in an array (winnow's
1802
+ * `repeat`, nom's `many0`, `many1`, `many_m_n` and `count`).
1803
+ *
1804
+ * The range defaults to `{ min: 0 }` (zero or more). A number repeats
1805
+ * exactly that many times. With a literal positive `min`, the output is
1806
+ * typed as a non-empty array.
1807
+ *
1808
+ * As in winnow, an unbounded repetition of a parser that succeeds without
1809
+ * consuming input fails with an unrecoverable assertion instead of looping
1810
+ * forever.
1811
+ *
1812
+ * @example
1813
+ * ```ts
1814
+ * import { Parser, Token } from "@thomasfosterau/effect-parse"
1815
+ *
1816
+ * const abs = Parser.repeat(Token.literal("ab"), { min: 1 })
1817
+ * Parser.parse(abs, "ababab") // Result.succeed(["ab", "ab", "ab"])
1818
+ * ```
1819
+ *
1820
+ * @since 0.1.0
1821
+ * @category repetition
1822
+ */
1823
+ export const repeat: {
1824
+ <const Rg extends Range = { readonly min: 0 }>(
1825
+ range?: Rg
1826
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<Repeated<A, Rg>, E, R, I>
1827
+ <A, E, R, I extends Input, const Rg extends Range = { readonly min: 0 }>(
1828
+ self: Parser<A, E, R, I>,
1829
+ range?: Rg
1830
+ ): Parser<Repeated<A, Rg>, E, R, I>
1831
+ } = dual(isParserArg, <A, E, R, I extends Input>(self: Parser<A, E, R, I>, range?: Range) =>
1832
+ foldImpl<A, Array<A>, E, R, I>(self, range, () => [], (acc, a) => {
1833
+ acc.push(a)
1834
+ return acc
1835
+ }, "repeat", { _tag: "Repeat", self, ...bounds(range) }))
1836
+
1837
+ /**
1838
+ * Applies a parser repeatedly, folding the outputs into an accumulator
1839
+ * (winnow's `repeat(..).fold(..)`, nom's `fold_many0`/`fold_many1`/
1840
+ * `fold_many_m_n`).
1841
+ *
1842
+ * @since 0.1.0
1843
+ * @category repetition
1844
+ */
1845
+ export const fold: {
1846
+ <A, B>(options: {
1847
+ readonly init: () => B
1848
+ readonly f: (acc: B, a: A) => B
1849
+ readonly range?: Range | undefined
1850
+ }): <E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<B, E, R, I>
1851
+ <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, options: {
1852
+ readonly init: () => B
1853
+ readonly f: (acc: B, a: A) => B
1854
+ readonly range?: Range | undefined
1855
+ }): Parser<B, E, R, I>
1856
+ } = dual(2, <A, E, R, I extends Input, B>(self: Parser<A, E, R, I>, options: {
1857
+ readonly init: () => B
1858
+ readonly f: (acc: B, a: A) => B
1859
+ readonly range?: Range | undefined
1860
+ }): Parser<B, E, R, I> => foldImpl(self, options.range, options.init, options.f, "fold"))
1861
+
1862
+ /**
1863
+ * Applies `self` repeatedly until `end` succeeds, returning the collected
1864
+ * outputs and the output of `end` (winnow's `repeat_till`, nom's
1865
+ * `many_till`).
1866
+ *
1867
+ * @since 0.1.0
1868
+ * @category repetition
1869
+ */
1870
+ export const repeatTill: {
1871
+ <B, E2, R2, I2 extends Input, const Rg extends Range = { readonly min: 0 }>(
1872
+ end: Parser<B, E2, R2, I2>,
1873
+ range?: Rg
1874
+ ): <A, E, R, I extends Input>(
1875
+ self: Parser<A, E, R, I>
1876
+ ) => Parser<[Repeated<A, Rg>, B], E | E2, R | R2, I & I2>
1877
+ <A, E, R, I extends Input, B, E2, R2, I2 extends Input, const Rg extends Range = { readonly min: 0 }>(
1878
+ self: Parser<A, E, R, I>,
1879
+ end: Parser<B, E2, R2, I2>,
1880
+ range?: Rg
1881
+ ): Parser<[Repeated<A, Rg>, B], E | E2, R | R2, I & I2>
1882
+ } = dual(isTwoParserArgs, <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
1883
+ self: Parser<A, E, R, I>,
1884
+ end: Parser<B, E2, R2, I2>,
1885
+ range?: Range
1886
+ ): Parser<[Array<A>, B], E | E2, R | R2, I & I2> => {
1887
+ const [min, max] = normalizeRange(range)
1888
+ const step = self.step
1889
+ const endStep = end.step
1890
+ return described((state) => {
1891
+ const acc: Array<A> = []
1892
+ while (true) {
1893
+ const start = state.pos
1894
+ let endFailure: Failure | undefined
1895
+ if (acc.length >= min) {
1896
+ const b = endStep(state)
1897
+ if (b !== Fail) return [acc, b]
1898
+ if (state.mode !== "Backtrack") return Fail
1899
+ endFailure = state.failure
1900
+ state.pos = start
1901
+ if (acc.length >= max) return Fail
1902
+ }
1903
+ const a = step(state)
1904
+ if (a === Fail) {
1905
+ if (endFailure !== undefined && state.mode === "Backtrack") {
1906
+ state.failWith("Backtrack", mergeFailures(endFailure, state.failure))
1907
+ }
1908
+ return Fail
1909
+ }
1910
+ if (state.pos === start) return assertConsumes(state, "repeatTill")
1911
+ acc.push(a)
1912
+ }
1913
+ }, "repeatTill", { _tag: "RepeatTill", self, end, ...bounds(range) })
1914
+ })
1915
+
1916
+ /**
1917
+ * Applies `self` repeatedly, separated by `separator`, collecting the
1918
+ * outputs of `self` (winnow's `separated`, nom's `separated_list0` and
1919
+ * `separated_list1`). A trailing separator is not consumed.
1920
+ *
1921
+ * @example
1922
+ * ```ts
1923
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1924
+ *
1925
+ * const csv = Parser.separated(Ascii.decInt, Token.literal(","))
1926
+ * Parser.parse(csv, "1,2,3") // Result.succeed([1, 2, 3])
1927
+ * ```
1928
+ *
1929
+ * @since 0.1.0
1930
+ * @category repetition
1931
+ */
1932
+ /** How far `separated` got: the elements so far, and where the next separator starts. */
1933
+ interface SeparatedCheckpoint<A> {
1934
+ readonly acc: Array<A>
1935
+ readonly pos: number
1936
+ }
1937
+
1938
+ export const separated: {
1939
+ <S, E2, R2, I2 extends Input, const Rg extends Range = { readonly min: 0 }>(
1940
+ separator: Parser<S, E2, R2, I2>,
1941
+ range?: Rg
1942
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<Repeated<A, Rg>, E | E2, R | R2, I & I2>
1943
+ <A, E, R, I extends Input, S, E2, R2, I2 extends Input, const Rg extends Range = { readonly min: 0 }>(
1944
+ self: Parser<A, E, R, I>,
1945
+ separator: Parser<S, E2, R2, I2>,
1946
+ range?: Rg
1947
+ ): Parser<Repeated<A, Rg>, E | E2, R | R2, I & I2>
1948
+ } = dual(isTwoParserArgs, <A, E, R, I extends Input, S, E2, R2, I2 extends Input>(
1949
+ self: Parser<A, E, R, I>,
1950
+ separator: Parser<S, E2, R2, I2>,
1951
+ range?: Range
1952
+ ): Parser<Array<A>, E | E2, R | R2, I & I2> => {
1953
+ const [min, max] = normalizeRange(range)
1954
+ const step = self.step
1955
+ const sepStep = separator.step
1956
+ const checkLoop = max === Infinity
1957
+ const key = {}
1958
+ return described((state) => {
1959
+ const entry = state.pos
1960
+ let acc: Array<A>
1961
+ const resumed = state.resume === undefined ? undefined : Resume.take(state, key, entry) as SeparatedCheckpoint<A>
1962
+ if (resumed instanceof Resume.Done) {
1963
+ state.pos = resumed.pos
1964
+ return resumed.value as Array<A>
1965
+ }
1966
+ if (resumed !== undefined) {
1967
+ acc = resumed.acc
1968
+ state.pos = resumed.pos
1969
+ } else {
1970
+ acc = []
1971
+ if (max === 0) return acc
1972
+ const start = state.pos
1973
+ const first = step(state)
1974
+ if (first === Fail) {
1975
+ if (state.mode !== "Backtrack" || min > 0) return Fail
1976
+ recover(state, start)
1977
+ return acc
1978
+ }
1979
+ acc.push(first)
1980
+ }
1981
+ while (acc.length < max) {
1982
+ const start = state.pos
1983
+ if (sepStep(state) === Fail) {
1984
+ if (state.mode === "Incomplete" && state.resume !== undefined) {
1985
+ Resume.save(state, key, entry, { acc, pos: start } satisfies SeparatedCheckpoint<A>)
1986
+ }
1987
+ if (state.mode !== "Backtrack" || acc.length < min) return Fail
1988
+ recover(state, start)
1989
+ return Resume.done(state, key, entry, resumed !== undefined, acc)
1990
+ }
1991
+ if (checkLoop && state.pos === start) return assertConsumes(state, "separated")
1992
+ const a = step(state)
1993
+ if (a === Fail) {
1994
+ if (state.mode === "Incomplete" && state.resume !== undefined) {
1995
+ Resume.save(state, key, entry, { acc, pos: start } satisfies SeparatedCheckpoint<A>)
1996
+ }
1997
+ if (state.mode !== "Backtrack" || acc.length < min) return Fail
1998
+ state.pos = start
1999
+ return Resume.done(state, key, entry, resumed !== undefined, acc)
2000
+ }
2001
+ acc.push(a)
2002
+ }
2003
+ return Resume.done(state, key, entry, resumed !== undefined, acc)
2004
+ }, "separated", { _tag: "Separated", self, separator, ...bounds(range) })
2005
+ })
2006
+
2007
+ /**
2008
+ * Parses one or more `self` separated by `separator`, folding them from the
2009
+ * left with the separator's output: `((a op b) op c)` (winnow's
2010
+ * `separated_foldl1`). This is the classic way to parse left-associative
2011
+ * binary operators.
2012
+ *
2013
+ * @example
2014
+ * ```ts
2015
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
2016
+ *
2017
+ * const sum = Parser.separatedFoldl1(
2018
+ * Ascii.decInt,
2019
+ * Parser.alt(Token.literal("+"), Token.literal("-")),
2020
+ * (l, op, r) => op === "+" ? l + r : l - r
2021
+ * )
2022
+ * Parser.parse(sum, "10-3+2") // Result.succeed(9)
2023
+ * ```
2024
+ *
2025
+ * @since 0.1.0
2026
+ * @category repetition
2027
+ */
2028
+ export const separatedFoldl1 = <A, E, R, I extends Input, S, E2, R2, I2 extends Input>(
2029
+ self: Parser<A, E, R, I>,
2030
+ separator: Parser<S, E2, R2, I2>,
2031
+ op: (left: A, separator: S, right: A) => A
2032
+ ): Parser<A, E | E2, R | R2, I & I2> => {
2033
+ const step = self.step
2034
+ const sepStep = separator.step
2035
+ const key = {}
2036
+ return described((state) => {
2037
+ const entry = state.pos
2038
+ const resumed = state.resume === undefined ? undefined : Resume.take(state, key, entry) as FoldCheckpoint<A>
2039
+ let acc: A | Fail
2040
+ if (resumed instanceof Resume.Done) {
2041
+ state.pos = resumed.pos
2042
+ return resumed.value as A
2043
+ }
2044
+ if (resumed !== undefined) {
2045
+ acc = resumed.acc
2046
+ state.pos = resumed.pos
2047
+ } else {
2048
+ acc = step(state)
2049
+ if (acc === Fail) return Fail
2050
+ }
2051
+ while (true) {
2052
+ const start = state.pos
2053
+ const s = sepStep(state)
2054
+ if (s === Fail) {
2055
+ if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
2056
+ if (state.mode !== "Backtrack") return Fail
2057
+ recover(state, start)
2058
+ return Resume.done(state, key, entry, resumed !== undefined, acc as A)
2059
+ }
2060
+ if (state.pos === start) return assertConsumes(state, "separatedFoldl1")
2061
+ const a = step(state)
2062
+ if (a === Fail) {
2063
+ if (state.mode === "Incomplete" && state.resume !== undefined) saveFold(state, key, entry, acc, start)
2064
+ if (state.mode !== "Backtrack") return Fail
2065
+ state.pos = start
2066
+ return Resume.done(state, key, entry, resumed !== undefined, acc as A)
2067
+ }
2068
+ acc = op(acc, s, a)
2069
+ }
2070
+ }, "separatedFoldl1", { _tag: "SeparatedFold", self, separator, op, right: false })
2071
+ }
2072
+
2073
+ /**
2074
+ * Parses one or more `self` separated by `separator`, folding them from the
2075
+ * right with the separator's output: `(a op (b op c))` (winnow's
2076
+ * `separated_foldr1`). Useful for right-associative operators such as
2077
+ * exponentiation.
2078
+ *
2079
+ * @since 0.1.0
2080
+ * @category repetition
2081
+ */
2082
+ export const separatedFoldr1 = <A, E, R, I extends Input, S, E2, R2, I2 extends Input>(
2083
+ self: Parser<A, E, R, I>,
2084
+ separator: Parser<S, E2, R2, I2>,
2085
+ op: (left: A, separator: S, right: A) => A
2086
+ ): Parser<A, E | E2, R | R2, I & I2> => {
2087
+ const step = self.step
2088
+ const sepStep = separator.step
2089
+ return described((state) => {
2090
+ const first = step(state)
2091
+ if (first === Fail) return Fail
2092
+ const values: Array<A> = [first]
2093
+ const seps: Array<S> = []
2094
+ while (true) {
2095
+ const start = state.pos
2096
+ const s = sepStep(state)
2097
+ if (s === Fail) {
2098
+ if (state.mode !== "Backtrack") return Fail
2099
+ recover(state, start)
2100
+ break
2101
+ }
2102
+ if (state.pos === start) return assertConsumes(state, "separatedFoldr1")
2103
+ const a = step(state)
2104
+ if (a === Fail) {
2105
+ if (state.mode !== "Backtrack") return Fail
2106
+ state.pos = start
2107
+ break
2108
+ }
2109
+ seps.push(s)
2110
+ values.push(a)
2111
+ }
2112
+ let acc = values[values.length - 1] as A
2113
+ for (let i = seps.length - 1; i >= 0; i--) {
2114
+ acc = op(values[i] as A, seps[i] as S, acc)
2115
+ }
2116
+ return acc
2117
+ }, "separatedFoldr1", { _tag: "SeparatedFold", self, separator, op, right: true })
2118
+ }
2119
+
2120
+ // -----------------------------------------------------------------------------
2121
+ // Services
2122
+ // -----------------------------------------------------------------------------
2123
+
2124
+ /**
2125
+ * Accesses a service from the parser's context. Services take the place of
2126
+ * winnow's `Stateful` input: they can hold configuration, or mutable state
2127
+ * such as an indentation stack or a symbol table.
2128
+ *
2129
+ * Services are provided with {@link provideService} / {@link provide}, with
2130
+ * the `context` option of the runners, or taken from the surrounding Effect
2131
+ * by {@link parseEffect}.
2132
+ *
2133
+ * Mutations made to a service's state are not undone on backtracking (as
2134
+ * with winnow's `Stateful`).
2135
+ *
2136
+ * @example
2137
+ * ```ts
2138
+ * import { Context } from "effect"
2139
+ * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
2140
+ *
2141
+ * class Radix extends Context.Service<Radix, number>()("Radix") {}
2142
+ *
2143
+ * const number = Parser.service(Radix).pipe(
2144
+ * Parser.flatMap((radix) =>
2145
+ * Ascii.alphanumeric1.pipe(Parser.map((digits) => parseInt(digits, radix)))
2146
+ * )
2147
+ * )
2148
+ *
2149
+ * Parser.parse(Parser.provideService(number, Radix, 16), "ff") // Result.succeed(255)
2150
+ * ```
2151
+ *
2152
+ * @since 0.1.0
2153
+ * @category services
2154
+ */
2155
+ export const service = <Id, S>(key: Context.Key<Id, S>): Parser<S, never, Id, Input> =>
2156
+ described((state) => Context.getUnsafe(state.context, key), "service", serviceNode)
2157
+
2158
+ const serviceNode: Node = { _tag: "Service" }
2159
+
2160
+ /**
2161
+ * Provides a service to a parser.
2162
+ *
2163
+ * @since 0.1.0
2164
+ * @category services
2165
+ */
2166
+ export const provideService: {
2167
+ <Id, S>(
2168
+ key: Context.Key<Id, S>,
2169
+ service: NoInfer<S>
2170
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E, Exclude<R, Id>, I>
2171
+ <A, E, R, I extends Input, Id, S>(
2172
+ self: Parser<A, E, R, I>,
2173
+ key: Context.Key<Id, S>,
2174
+ service: NoInfer<S>
2175
+ ): Parser<A, E, Exclude<R, Id>, I>
2176
+ } = dual(3, <A, E, R, I extends Input, Id, S>(
2177
+ self: Parser<A, E, R, I>,
2178
+ key: Context.Key<Id, S>,
2179
+ service: S
2180
+ ): Parser<A, E, Exclude<R, Id>, I> => {
2181
+ const step = self.step
2182
+ return described((state) => {
2183
+ const prev = state.context
2184
+ state.context = Context.add(prev, key, service) as Context.Context<never>
2185
+ try {
2186
+ return step(state)
2187
+ } finally {
2188
+ state.context = prev
2189
+ }
2190
+ }, self.name, { _tag: "Provide", self })
2191
+ })
2192
+
2193
+ /**
2194
+ * Provides a context of services to a parser.
2195
+ *
2196
+ * @since 0.1.0
2197
+ * @category services
2198
+ */
2199
+ export const provide: {
2200
+ <R2>(
2201
+ context: Context.Context<R2>
2202
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E, Exclude<R, R2>, I>
2203
+ <A, E, R, I extends Input, R2>(
2204
+ self: Parser<A, E, R, I>,
2205
+ context: Context.Context<R2>
2206
+ ): Parser<A, E, Exclude<R, R2>, I>
2207
+ } = dual(2, <A, E, R, I extends Input, R2>(
2208
+ self: Parser<A, E, R, I>,
2209
+ context: Context.Context<R2>
2210
+ ): Parser<A, E, Exclude<R, R2>, I> => {
2211
+ const step = self.step
2212
+ return described((state) => {
2213
+ const prev = state.context
2214
+ state.context = Context.merge(prev, context) as Context.Context<never>
2215
+ try {
2216
+ return step(state)
2217
+ } finally {
2218
+ state.context = prev
2219
+ }
2220
+ }, self.name, { _tag: "Provide", self })
2221
+ })
2222
+
2223
+ // -----------------------------------------------------------------------------
2224
+ // Error recovery
2225
+ // -----------------------------------------------------------------------------
2226
+
2227
+ /**
2228
+ * Error recovery: if the parser fails, record the failure, skip input with
2229
+ * `recover`, and continue with `Option.none()` (winnow's `resume_after`).
2230
+ *
2231
+ * Recovery is only active when running with {@link parseRecoverable}. Under
2232
+ * the other runners the parser behaves like `Parser.asSome(self)`.
2233
+ *
2234
+ * @since 0.1.0
2235
+ * @category error recovery
2236
+ */
2237
+ export const resumeAfter: {
2238
+ <B, E2, R2, I2 extends Input>(
2239
+ recover: Parser<B, E2, R2, I2>
2240
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<Option.Option<A>, E | E2, R | R2, I & I2>
2241
+ <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
2242
+ self: Parser<A, E, R, I>,
2243
+ recover: Parser<B, E2, R2, I2>
2244
+ ): Parser<Option.Option<A>, E | E2, R | R2, I & I2>
2245
+ } = dual(2, <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
2246
+ self: Parser<A, E, R, I>,
2247
+ recover: Parser<B, E2, R2, I2>
2248
+ ): Parser<Option.Option<A>, E | E2, R | R2, I & I2> => {
2249
+ const step = self.step
2250
+ const recoverStep = recover.step
2251
+ return described((state) => {
2252
+ const start = state.pos
2253
+ const a = step(state)
2254
+ if (a !== Fail) return Option.some(a)
2255
+ if (state.recovered === undefined || state.mode === "Incomplete") return Fail
2256
+ const failure = state.failure
2257
+ const mode = state.mode
2258
+ state.pos = start
2259
+ if (recoverStep(state) === Fail) return state.failWith(mode, failure)
2260
+ state.recovered.push(failure)
2261
+ return Option.none()
2262
+ }, self.name, { _tag: "ResumeAfter", self, recover })
2263
+ })
2264
+
2265
+ /**
2266
+ * Error recovery: if the parser fails, record the failure, skip input with
2267
+ * `recover`, and try the parser again (winnow's `retry_after`).
2268
+ *
2269
+ * Recovery is only active when running with {@link parseRecoverable}. Under
2270
+ * the other runners the parser behaves like `self`.
2271
+ *
2272
+ * @since 0.1.0
2273
+ * @category error recovery
2274
+ */
2275
+ export const retryAfter: {
2276
+ <B, E2, R2, I2 extends Input>(
2277
+ recover: Parser<B, E2, R2, I2>
2278
+ ): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E | E2, R | R2, I & I2>
2279
+ <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
2280
+ self: Parser<A, E, R, I>,
2281
+ recover: Parser<B, E2, R2, I2>
2282
+ ): Parser<A, E | E2, R | R2, I & I2>
2283
+ } = dual(2, <A, E, R, I extends Input, B, E2, R2, I2 extends Input>(
2284
+ self: Parser<A, E, R, I>,
2285
+ recover: Parser<B, E2, R2, I2>
2286
+ ): Parser<A, E | E2, R | R2, I & I2> => {
2287
+ const step = self.step
2288
+ const recoverStep = recover.step
2289
+ return described((state) => {
2290
+ while (true) {
2291
+ const start = state.pos
2292
+ const a = step(state)
2293
+ if (a !== Fail) return a
2294
+ if (state.recovered === undefined || state.mode === "Incomplete") return Fail
2295
+ const failure = state.failure
2296
+ const mode = state.mode
2297
+ state.pos = start
2298
+ if (recoverStep(state) === Fail || state.pos === start) return state.failWith(mode, failure)
2299
+ state.recovered.push(failure)
2300
+ }
2301
+ }, self.name, { _tag: "RetryAfter", self, recover })
2302
+ })
2303
+
2304
+ // -----------------------------------------------------------------------------
2305
+ // Debugging
2306
+ // -----------------------------------------------------------------------------
2307
+
2308
+ /**
2309
+ * Traces the execution of a parser (winnow's `trace`). When a tracer is
2310
+ * installed (see the `trace` option of {@link parse}), `Enter` and `Exit`
2311
+ * events are reported for each traced parser; otherwise tracing has no
2312
+ * effect.
2313
+ *
2314
+ * @since 0.1.0
2315
+ * @category debugging
2316
+ */
2317
+ export const trace: {
2318
+ (name: string): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E, R, I>
2319
+ <A, E, R, I extends Input>(self: Parser<A, E, R, I>, name: string): Parser<A, E, R, I>
2320
+ } = dual(2, <A, E, R, I extends Input>(self: Parser<A, E, R, I>, name: string): Parser<A, E, R, I> => {
2321
+ const step = self.step
2322
+ return described((state) => {
2323
+ const tracer = state.tracer
2324
+ if (tracer === undefined) return step(state)
2325
+ const start = state.pos
2326
+ const depth = state.depth
2327
+ tracer({ _tag: "Enter", name, depth, offset: start, state })
2328
+ state.depth = depth + 1
2329
+ const a = step(state)
2330
+ state.depth = depth
2331
+ tracer({
2332
+ _tag: "Exit",
2333
+ name,
2334
+ depth,
2335
+ offset: start,
2336
+ endOffset: state.pos,
2337
+ result: a === Fail ? state.mode : "Ok",
2338
+ state
2339
+ })
2340
+ return a
2341
+ }, name, { _tag: "Trace", self, name })
2342
+ })
2343
+
2344
+ const preview = (state: State<any>, offset: number): string => {
2345
+ const input = state.input
2346
+ if (typeof input === "string") {
2347
+ const s = input.slice(offset, offset + 24)
2348
+ return JSON.stringify(s) + (offset + 24 < input.length ? "…" : "∅")
2349
+ }
2350
+ if (input instanceof Uint8Array) {
2351
+ return Array.from(input.subarray(offset, offset + 12), (b) => b.toString(16).padStart(2, "0")).join(" ")
2352
+ }
2353
+ return `[${offset}/${input.length}]`
2354
+ }
2355
+
2356
+ /**
2357
+ * A tracer that writes an indented trace to `console.log`, similar to
2358
+ * winnow's `debug` feature.
2359
+ *
2360
+ * @since 0.1.0
2361
+ * @category debugging
2362
+ */
2363
+ export const consoleTracer = (event: TraceEvent): void => {
2364
+ const indent = " ".repeat(event.depth)
2365
+ if (event._tag === "Enter") {
2366
+ console.log(`${indent}> ${event.name} @${event.offset} | ${preview(event.state, event.offset)}`)
2367
+ } else {
2368
+ const consumed = (event.endOffset ?? event.offset) - event.offset
2369
+ console.log(`${indent}< ${event.name} | ${event.result === "Ok" ? `+${consumed}` : event.result}`)
2370
+ }
2371
+ }
2372
+
2373
+ // -----------------------------------------------------------------------------
2374
+ // Compilation
2375
+ // -----------------------------------------------------------------------------
2376
+
2377
+ /**
2378
+ * Options for {@link compile}.
2379
+ *
2380
+ * @since 0.1.0
2381
+ * @category compilation
2382
+ */
2383
+ export interface CompileOptions {
2384
+ /**
2385
+ * Which compiler to use:
2386
+ *
2387
+ * - `"auto"` (the default): generated code (a JIT) where `new Function`
2388
+ * is allowed, otherwise specialized closures
2389
+ * - `"closure"`: specialized closures only. It never generates code, so
2390
+ * it is the choice under a Content Security Policy, where even probing
2391
+ * for `new Function` is reported as a violation.
2392
+ */
2393
+ readonly backend?: "auto" | "closure" | undefined
2394
+ }
2395
+
2396
+ /**
2397
+ * Compiles a parser into fused, specialized code, and returns the same
2398
+ * parser. Grammars are written as usual; compilation only makes
2399
+ * {@link parse}, {@link parseSync}, {@link parsePeek} and
2400
+ * {@link parseEffect} faster. Compile the root of a grammar once, where it
2401
+ * is defined:
2402
+ *
2403
+ * ```ts
2404
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
2405
+ *
2406
+ * export const list = Parser.compile(
2407
+ * Parser.separated(Ascii.decInt, Token.literal(","))
2408
+ * )
2409
+ * Parser.parse(list, "1,2,3") // Result.succeed([1, 2, 3]), compiled
2410
+ * ```
2411
+ *
2412
+ * The code is generated lazily on the first run, for each kind of input
2413
+ * (string, bytes or array), and cached with the parser.
2414
+ *
2415
+ * **Results and errors are unchanged.** Compiled code records no
2416
+ * diagnostics. When it fails, the parser runs again, interpreted, from the
2417
+ * start, so every `ParseError` (offset, expected, context, cause and
2418
+ * message) is exactly the one the interpreter reports.
2419
+ *
2420
+ * **Callbacks should be pure.** On an input that fails, the callbacks given
2421
+ * to `map`, `filter`, `tryMap`, `tap`, `fold`, ... may run twice, once
2422
+ * compiled and once interpreted. Callbacks whose results are discarded
2423
+ * (a `map` inside `taken` or `preceded`, for example) may not run at all,
2424
+ * nor those of a branch that is certain to fail, and token-set predicates
2425
+ * may be called once per character code and cached.
2426
+ *
2427
+ * Runs on partial input ({@link parsePartial}, `ParserStream`), with
2428
+ * {@link parseRecoverable} or with the `trace` option are always
2429
+ * interpreted. Parsers that can't be described, such as {@link make} and
2430
+ * {@link gen}, run interpreted inside compiled code.
2431
+ *
2432
+ * @since 0.1.0
2433
+ * @category compilation
2434
+ */
2435
+ export const compile: {
2436
+ (options?: CompileOptions): <A, E, R, I extends Input>(self: Parser<A, E, R, I>) => Parser<A, E, R, I>
2437
+ <A, E, R, I extends Input>(self: Parser<A, E, R, I>, options?: CompileOptions): Parser<A, E, R, I>
2438
+ } = dual(isParserArg, <A, E, R, I extends Input>(self: Parser<A, E, R, I>, options?: CompileOptions) => {
2439
+ Compiler.register(self, options?.backend ?? "auto")
2440
+ return self
2441
+ })
2442
+
2443
+ /** Returned by {@link runCompiled} when there is no compiled form to run. */
2444
+ const NotCompiled: unique symbol = Symbol("NotCompiled")
2445
+
2446
+ /**
2447
+ * Runs the compiled form of `self`, if it has one: returns its value, or
2448
+ * `Fail` when it failed (or, with `whole`, did not consume the whole
2449
+ * input), in which case the state must be thrown away.
2450
+ */
2451
+ const runCompiled = <I extends Input>(self: Parser.Any, state: State<I>, whole: boolean): unknown => {
2452
+ if (state.tracer !== undefined || state.recovered !== undefined) return NotCompiled
2453
+ const step = Compiler.stepFor(self, state)
2454
+ if (step === undefined) return NotCompiled
2455
+ const a = step(state)
2456
+ if (a === Fail || (whole && state.pos < state.end)) return Fail
2457
+ return a
2458
+ }
2459
+
2460
+ // -----------------------------------------------------------------------------
2461
+ // Running
2462
+ // -----------------------------------------------------------------------------
2463
+
2464
+ /**
2465
+ * Options accepted by the runners.
2466
+ *
2467
+ * @since 0.1.0
2468
+ * @category running
2469
+ */
2470
+ export interface ParseOptions {
2471
+ /**
2472
+ * Install a tracer for {@link trace}d parsers: `true` uses
2473
+ * {@link consoleTracer}.
2474
+ */
2475
+ readonly trace?: boolean | ((event: TraceEvent) => void) | undefined
2476
+ }
2477
+
2478
+ /**
2479
+ * Options accepted by the runners for parsers that require services.
2480
+ *
2481
+ * @since 0.1.0
2482
+ * @category running
2483
+ */
2484
+ export interface ParseOptionsWithContext<R> extends ParseOptions {
2485
+ readonly context: Context.Context<R>
2486
+ }
2487
+
2488
+ const makeState = <I extends Input>(
2489
+ input: I | Source<I>,
2490
+ options: (ParseOptions & { readonly context?: Context.Context<any> }) | undefined,
2491
+ partial: boolean
2492
+ ): State<I> => {
2493
+ const state = new State(input, { partial, context: options?.context })
2494
+ const t = options?.trace
2495
+ if (t !== undefined && t !== false) state.tracer = t === true ? consoleTracer : t
2496
+ return state
2497
+ }
2498
+
2499
+ const toError = <E>(state: State<any>): ParseError.ParseError<E> =>
2500
+ ParseError.fromFailure<E>(state.input, state.reportedFailure(), state.mode, state.encoding)
2501
+
2502
+ /** Runs a parser against a complete input, compiled when possible. */
2503
+ const parseComplete = <A, E, R, I extends Input>(
2504
+ self: Parser<A, E, R, I>,
2505
+ input: I | Source<I>,
2506
+ options: (ParseOptions & { readonly context?: Context.Context<any> }) | undefined
2507
+ ): Result.Result<A, ParseError.ParseError<E>> => {
2508
+ const state = makeState(input, options, false)
2509
+ const a = runCompiled(self, state, true)
2510
+ if (a === NotCompiled) return runComplete(self, state)
2511
+ if (a !== Fail) return Result.succeed(a as A)
2512
+ // Re-run interpreted for the diagnostics
2513
+ return runComplete(self, makeState(input, options, false))
2514
+ }
2515
+
2516
+ const runComplete = <A, E, R, I extends Input>(
2517
+ self: Parser<A, E, R, I>,
2518
+ state: State<I>
2519
+ ): Result.Result<A, ParseError.ParseError<E>> => {
2520
+ const a = self.step(state)
2521
+ if (a === Fail) return Result.fail(toError(state))
2522
+ if (state.pos < state.end) {
2523
+ state.backtrack("Eof", expectedEof)
2524
+ return Result.fail(toError(state))
2525
+ }
2526
+ return Result.succeed(a)
2527
+ }
2528
+
2529
+ /**
2530
+ * Runs a parser against a complete input, requiring the whole input to be
2531
+ * consumed (winnow's `Parser::parse`).
2532
+ *
2533
+ * @example
2534
+ * ```ts
2535
+ * import { Result } from "effect"
2536
+ * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
2537
+ *
2538
+ * Parser.parse(Ascii.decInt, "-42") // Result.succeed(-42)
2539
+ * Result.isFailure(Parser.parse(Ascii.decInt, "42x")) // true
2540
+ * ```
2541
+ *
2542
+ * @since 0.1.0
2543
+ * @category running
2544
+ */
2545
+ export const parse: {
2546
+ <A, E, I extends Input>(
2547
+ self: Parser<A, E, never, I>,
2548
+ input: NoInfer<Source<I>>,
2549
+ options?: ParseOptions
2550
+ ): Result.Result<A, ParseError.ParseError<E>>
2551
+ <A, E, R, I extends Input>(
2552
+ self: Parser<A, E, R, I>,
2553
+ input: NoInfer<Source<I>>,
2554
+ options: ParseOptionsWithContext<R>
2555
+ ): Result.Result<A, ParseError.ParseError<E>>
2556
+ } = <A, E, R, I extends Input>(
2557
+ self: Parser<A, E, R, I>,
2558
+ input: I | Source<I>,
2559
+ options?: ParseOptions & { readonly context?: Context.Context<R> }
2560
+ ): Result.Result<A, ParseError.ParseError<E>> => parseComplete(self, input, options)
2561
+
2562
+ /**
2563
+ * Like {@link parse}, but throws the `ParseError` on failure.
2564
+ *
2565
+ * @since 0.1.0
2566
+ * @category running
2567
+ */
2568
+ export const parseSync: {
2569
+ <A, E, I extends Input>(self: Parser<A, E, never, I>, input: NoInfer<Source<I>>, options?: ParseOptions): A
2570
+ <A, E, R, I extends Input>(self: Parser<A, E, R, I>, input: NoInfer<Source<I>>, options: ParseOptionsWithContext<R>): A
2571
+ } = <A, E, R, I extends Input>(
2572
+ self: Parser<A, E, R, I>,
2573
+ input: I | Source<I>,
2574
+ options?: ParseOptions & { readonly context?: Context.Context<R> }
2575
+ ): A => {
2576
+ const result = parseComplete(self, input, options)
2577
+ if (Result.isSuccess(result)) return result.success
2578
+ throw result.failure
2579
+ }
2580
+
2581
+ /**
2582
+ * Runs a parser against the start of an input, returning the remaining
2583
+ * input along with the output, nom-style (winnow's `parse_peek`, nom's
2584
+ * `IResult`).
2585
+ *
2586
+ * @since 0.1.0
2587
+ * @category running
2588
+ */
2589
+ export const parsePeek: {
2590
+ <A, E, I extends Input>(
2591
+ self: Parser<A, E, never, I>,
2592
+ input: NoInfer<Source<I>>,
2593
+ options?: ParseOptions
2594
+ ): Result.Result<[remaining: Slice<I>, output: A], ParseError.ParseError<E>>
2595
+ <A, E, R, I extends Input>(
2596
+ self: Parser<A, E, R, I>,
2597
+ input: NoInfer<Source<I>>,
2598
+ options: ParseOptionsWithContext<R>
2599
+ ): Result.Result<[remaining: Slice<I>, output: A], ParseError.ParseError<E>>
2600
+ } = <A, E, R, I extends Input>(
2601
+ self: Parser<A, E, R, I>,
2602
+ input: I | Source<I>,
2603
+ options?: ParseOptions & { readonly context?: Context.Context<R> }
2604
+ ): Result.Result<[remaining: Slice<I>, output: A], ParseError.ParseError<E>> => {
2605
+ let state = makeState(input, options, false)
2606
+ const compiled = runCompiled(self, state, false)
2607
+ if (compiled !== NotCompiled && compiled !== Fail) {
2608
+ return Result.succeed([state.slice(state.pos, state.end), compiled as A])
2609
+ }
2610
+ if (compiled === Fail) state = makeState(input, options, false)
2611
+ const a = self.step(state)
2612
+ if (a === Fail) return Result.fail(toError(state))
2613
+ return Result.succeed([state.slice(state.pos, state.end), a])
2614
+ }
2615
+
2616
+ /**
2617
+ * The outcome of parsing a partial (streaming) input.
2618
+ *
2619
+ * - `Done`: the parser succeeded, consuming input up to `offset`.
2620
+ * - `Incomplete`: more input is needed (`needed` tokens, if known).
2621
+ *
2622
+ * @since 0.1.0
2623
+ * @category running
2624
+ */
2625
+ export type PartialResult<A> =
2626
+ | { readonly _tag: "Done"; readonly value: A; readonly offset: number }
2627
+ | { readonly _tag: "Incomplete"; readonly needed: Option.Option<number> }
2628
+
2629
+ /**
2630
+ * Runs a parser against a *partial* input, i.e. a chunk of a stream that may
2631
+ * be followed by more data (winnow's `Partial`, nom's `streaming` parsers).
2632
+ * Token parsers that reach the end of the chunk report `Incomplete` instead
2633
+ * of failing or succeeding early.
2634
+ *
2635
+ * The parser does not have to consume the whole input; the returned
2636
+ * `offset` says how much was consumed.
2637
+ *
2638
+ * @example
2639
+ * ```ts
2640
+ * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
2641
+ *
2642
+ * Parser.parsePartial(Ascii.digit1, "123")
2643
+ * // Result.succeed({ _tag: "Incomplete", needed: Option.some(1) })
2644
+ * Parser.parsePartial(Ascii.digit1, "123;")
2645
+ * // Result.succeed({ _tag: "Done", value: "123", offset: 3 })
2646
+ * ```
2647
+ *
2648
+ * @since 0.1.0
2649
+ * @category running
2650
+ */
2651
+ export const parsePartial: {
2652
+ <A, E, I extends Input>(
2653
+ self: Parser<A, E, never, I>,
2654
+ input: NoInfer<Source<I>>,
2655
+ options?: ParseOptions
2656
+ ): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
2657
+ <A, E, R, I extends Input>(
2658
+ self: Parser<A, E, R, I>,
2659
+ input: NoInfer<Source<I>>,
2660
+ options: ParseOptionsWithContext<R>
2661
+ ): Result.Result<PartialResult<A>, ParseError.ParseError<E>>
2662
+ } = <A, E, R, I extends Input>(
2663
+ self: Parser<A, E, R, I>,
2664
+ input: I | Source<I>,
2665
+ options?: ParseOptions & { readonly context?: Context.Context<R> }
2666
+ ): Result.Result<PartialResult<A>, ParseError.ParseError<E>> => {
2667
+ const state = makeState(input, options, true)
2668
+ const a = self.step(state)
2669
+ if (a !== Fail) return Result.succeed({ _tag: "Done", value: a, offset: state.pos })
2670
+ if (state.mode === "Incomplete") {
2671
+ const needed = state.failure.needed
2672
+ return Result.succeed({ _tag: "Incomplete", needed: needed > 0 ? Option.some(needed) : Option.none() })
2673
+ }
2674
+ return Result.fail(toError(state))
2675
+ }
2676
+
2677
+ /**
2678
+ * Runs a parser against a complete input as an `Effect`. Services required
2679
+ * by the parser are taken from the Effect's context.
2680
+ *
2681
+ * @example
2682
+ * ```ts
2683
+ * import { Effect } from "effect"
2684
+ * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
2685
+ *
2686
+ * const program = Effect.gen(function*() {
2687
+ * const n = yield* Parser.parseEffect(Ascii.decInt, "42")
2688
+ * return n + 1
2689
+ * })
2690
+ * ```
2691
+ *
2692
+ * @since 0.1.0
2693
+ * @category running
2694
+ */
2695
+ export const parseEffect = <A, E, R, I extends Input>(
2696
+ self: Parser<A, E, R, I>,
2697
+ input: NoInfer<Source<I>>,
2698
+ options?: ParseOptions
2699
+ ): Effect.Effect<A, ParseError.ParseError<E>, R> =>
2700
+ Effect.flatMap(
2701
+ Effect.context<R>(),
2702
+ (context) => Effect.fromResult(parseComplete(self, input, { ...options, context }))
2703
+ )
2704
+
2705
+ /**
2706
+ * The outcome of {@link parseRecoverable}.
2707
+ *
2708
+ * @since 0.1.0
2709
+ * @category running
2710
+ */
2711
+ export interface RecoverableResult<A, E> {
2712
+ /** The output, if parsing ultimately succeeded. */
2713
+ readonly value: Option.Option<A>
2714
+ /** Every error encountered: recovered ones, then the fatal one (if any). */
2715
+ readonly errors: ReadonlyArray<ParseError.ParseError<E>>
2716
+ }
2717
+
2718
+ /**
2719
+ * Runs a parser with error recovery enabled (winnow's `Recoverable`
2720
+ * stream). Failures handled by {@link resumeAfter} and {@link retryAfter}
2721
+ * are collected instead of aborting the parse.
2722
+ *
2723
+ * @since 0.1.0
2724
+ * @category running
2725
+ */
2726
+ export const parseRecoverable: {
2727
+ <A, E, I extends Input>(
2728
+ self: Parser<A, E, never, I>,
2729
+ input: NoInfer<Source<I>>,
2730
+ options?: ParseOptions
2731
+ ): RecoverableResult<A, E>
2732
+ <A, E, R, I extends Input>(
2733
+ self: Parser<A, E, R, I>,
2734
+ input: NoInfer<Source<I>>,
2735
+ options: ParseOptionsWithContext<R>
2736
+ ): RecoverableResult<A, E>
2737
+ } = <A, E, R, I extends Input>(
2738
+ self: Parser<A, E, R, I>,
2739
+ input: I | Source<I>,
2740
+ options?: ParseOptions & { readonly context?: Context.Context<R> }
2741
+ ): RecoverableResult<A, E> => {
2742
+ const state = makeState(input, options, false)
2743
+ const recovered: Array<Failure> = []
2744
+ state.recovered = recovered
2745
+ const result = runComplete(self, state)
2746
+ const errors = recovered.map((f) => ParseError.fromFailure<E>(state.input, f, "Backtrack", state.encoding))
2747
+ if (Result.isSuccess(result)) return { value: Option.some(result.success), errors }
2748
+ errors.push(result.failure)
2749
+ return { value: Option.none(), errors }
2750
+ }
2751
+
2752
+ // -----------------------------------------------------------------------------
2753
+ // Low-level helpers
2754
+ // -----------------------------------------------------------------------------
2755
+
2756
+ /**
2757
+ * Records a failure kind for use in custom parsers; re-exported for
2758
+ * convenience.
2759
+ *
2760
+ * @since 0.1.0
2761
+ * @category models
2762
+ */
2763
+ export type { ErrorKind }