@thomasfosterau/effect-parse 0.0.0-stage → 0.1.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
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The registry of compiled parsers, keyed by parser (see `Parser.compile`).
3
+ * Compiled steps are built lazily, once per input kind.
4
+ */
5
+ import type { State } from "../../Input.js"
6
+ import type { ParserLike } from "../node.js"
7
+ import type { Fallback, Step } from "./closure.js"
8
+ import { compileClosure } from "./closure.js"
9
+ import { compileJit, jitSupported } from "./jit.js"
10
+
11
+ /** Which compiler to use. */
12
+ export type Backend = "auto" | "closure"
13
+
14
+ /** What was compiled for a parser and an input kind. */
15
+ export interface Compiled {
16
+ /** The compiler actually used. */
17
+ readonly backend: "jit" | "closure" | undefined
18
+ readonly step: Step | undefined
19
+ /** Parts of the grammar that run interpreted, and why. */
20
+ readonly fallbacks: ReadonlyArray<Fallback>
21
+ /** Why compilation failed, if it did (the parser then runs interpreted). */
22
+ readonly error: unknown
23
+ /** Why the JIT failed, if it did (the closure compiler is then used). */
24
+ readonly jitError?: unknown
25
+ /** The generated source, for the JIT. */
26
+ readonly source?: string
27
+ }
28
+
29
+ interface Entry {
30
+ readonly backend: Backend
31
+ readonly byKind: Array<Compiled | undefined>
32
+ }
33
+
34
+ const registry = new WeakMap<ParserLike, Entry>()
35
+
36
+ export const register = (p: ParserLike, backend: Backend): void => {
37
+ const entry = registry.get(p)
38
+ if (entry === undefined || entry.backend !== backend) registry.set(p, { backend, byKind: [] })
39
+ }
40
+
41
+ export const isRegistered = (p: ParserLike): boolean => registry.has(p)
42
+
43
+ const build = (p: ParserLike, kind: 0 | 1 | 2, backend: Backend, resumable: boolean): Compiled => {
44
+ if (backend === "auto" && jitSupported()) {
45
+ try {
46
+ const { fallbacks, source, step } = compileJit(p, kind, resumable)
47
+ return { backend: "jit", step, fallbacks, error: undefined, source }
48
+ } catch (error) {
49
+ // Fall back to the closure compiler, remembering why
50
+ return { ...buildClosure(p, kind, resumable), jitError: error }
51
+ }
52
+ }
53
+ return buildClosure(p, kind, resumable)
54
+ }
55
+
56
+ const buildClosure = (p: ParserLike, kind: 0 | 1 | 2, resumable: boolean): Compiled => {
57
+ try {
58
+ const { fallbacks, step } = compileClosure(p, kind, resumable)
59
+ return { backend: "closure", step, fallbacks, error: undefined }
60
+ } catch (error) {
61
+ return { backend: undefined, step: undefined, fallbacks: [], error }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * The compiled form of a registered parser for an input kind, compiling it
67
+ * if needed. `resumable` selects the build used by streaming parses, which
68
+ * supports partial input and checkpoints.
69
+ */
70
+ export const compiled = (p: ParserLike, kind: 0 | 1 | 2, resumable = false): Compiled | undefined => {
71
+ const entry = registry.get(p)
72
+ if (entry === undefined) return undefined
73
+ const index = resumable ? kind + 3 : kind
74
+ return entry.byKind[index] ??= build(p, kind, entry.backend, resumable)
75
+ }
76
+
77
+ /** The compiled step for running `p` on `state`, if it is registered. */
78
+ export const stepFor = (p: ParserLike, state: State<any>, resumable = false): Step | undefined =>
79
+ compiled(p, state.inputKind, resumable)?.step
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Token sets for compiled parsers: membership tables for small units, the
3
+ * exact test for the rest, and the code point ranges when they are known.
4
+ */
5
+ import type { Encoding } from "../../Encoding.js"
6
+ import { compile, compileAny, PredicateRanges } from "../tokens.js"
7
+
8
+ /** Size of the membership table: every byte, and the Latin-1 code points. */
9
+ export const TableSize = 256
10
+
11
+ export interface CharSet {
12
+ /** Tests a code point (or byte, for `Token` on bytes). */
13
+ readonly test: (code: number) => boolean
14
+ /** `table[c] === 1` iff `test(c)`, for `c < TableSize`. */
15
+ readonly table: Uint8Array
16
+ /** Sorted inclusive ranges of accepted code points, when known. */
17
+ readonly ranges: ReadonlyArray<readonly [number, number]> | undefined
18
+ }
19
+
20
+ const isRange = (u: unknown): u is { readonly from: string | number; readonly to: string | number } =>
21
+ typeof u === "object" && u !== null && (u as any)._tag === "TokenRange"
22
+
23
+ const toCode = (u: string | number): number => typeof u === "number" ? u : u.codePointAt(0)!
24
+
25
+ /** The code point ranges of a token set, or `undefined` if it has an unknown predicate. */
26
+ export const rangesOf = (set: unknown): Array<[number, number]> | undefined => {
27
+ const out: Array<[number, number]> = []
28
+ const add = (u: unknown): boolean => {
29
+ if (typeof u === "string") {
30
+ for (const c of u) {
31
+ const cp = c.codePointAt(0)!
32
+ out.push([cp, cp])
33
+ }
34
+ return true
35
+ }
36
+ if (typeof u === "number") {
37
+ out.push([u, u])
38
+ return true
39
+ }
40
+ if (u instanceof Uint8Array) {
41
+ for (const b of u) out.push([b, b])
42
+ return true
43
+ }
44
+ if (isRange(u)) {
45
+ out.push([toCode(u.from), toCode(u.to)])
46
+ return true
47
+ }
48
+ if (typeof u === "function") {
49
+ const r = (u as any)[PredicateRanges] as ReadonlyArray<readonly [number, number]> | undefined
50
+ if (r === undefined) return false
51
+ for (const [a, b] of r) out.push([a, b])
52
+ return true
53
+ }
54
+ if (Array.isArray(u)) return u.every(add)
55
+ return false
56
+ }
57
+ if (!add(set)) return undefined
58
+ out.sort((a, b) => a[0] - b[0])
59
+ const merged: Array<[number, number]> = []
60
+ for (const [a, b] of out) {
61
+ if (b < a) continue
62
+ const last = merged[merged.length - 1]
63
+ if (last !== undefined && a <= last[1] + 1) last[1] = Math.max(last[1], b)
64
+ else merged.push([a, b])
65
+ }
66
+ return merged
67
+ }
68
+
69
+ const makeSet = (test: (code: number) => boolean, set: unknown): CharSet => {
70
+ // The table calls the test once per small code: token-set predicates are
71
+ // assumed to be pure (see the design notes).
72
+ const table = new Uint8Array(TableSize)
73
+ for (let c = 0; c < TableSize; c++) table[c] = test(c) ? 1 : 0
74
+ return { test, table, ranges: rangesOf(set) }
75
+ }
76
+
77
+ /**
78
+ * The set as a `Token` parser sees it: string predicates receive strings on
79
+ * string input, everything receives numbers on byte input.
80
+ */
81
+ export const tokenSet = (set: unknown, kind: 0 | 1): CharSet => {
82
+ const m = compileAny(set)
83
+ return makeSet(kind === 0 ? m.forString.code : m.forCode.code, set)
84
+ }
85
+
86
+ /** The set as a `Text` parser sees it: code points, for any encoding. */
87
+ export const textSet = (set: unknown): CharSet => makeSet(compile(set, "string").code, set)
88
+
89
+ /** Whether some accepted code point is at least `limit` (true when unknown). */
90
+ export const reachesAbove = (s: CharSet, limit: number): boolean =>
91
+ s.ranges === undefined || s.ranges.some(([, b]) => b >= limit)
92
+
93
+ /** The input kind a `Text` encoding reads: 0 for strings, 1 for bytes. */
94
+ export const kindOfEncoding = (enc: Encoding<any>): 0 | 1 => {
95
+ let k = kinds.get(enc)
96
+ if (k === undefined) kinds.set(enc, k = typeof enc.encode("") === "string" ? 0 : 1)
97
+ return k
98
+ }
99
+
100
+ const kinds = new WeakMap<Encoding<any>, 0 | 1>()
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Building objects as `Object.fromEntries` does, without its array of
3
+ * `[key, value]` pairs: the compilers use these when a parser maps a
4
+ * repetition of pairs with `Object.fromEntries` (see `Parser.fromEntries`).
5
+ */
6
+
7
+ /** The built-in `Object.fromEntries`, which compiled parsers recognize. */
8
+ export const fromEntries: <K extends PropertyKey, V>(entries: Iterable<readonly [K, V]>) => { [P in K]: V } =
9
+ Object.fromEntries as any
10
+
11
+ const objectPrototype = Object.prototype
12
+ const hasOwn = objectPrototype.hasOwnProperty
13
+
14
+ /**
15
+ * Whether `o[key] = value` on a new plain object creates the same property
16
+ * as `Object.fromEntries` (a writable, enumerable, configurable data
17
+ * property): `key` is a string that `Object.prototype` has no property for,
18
+ * so there is no setter (`__proto__`) or read-only property to run into.
19
+ */
20
+ export const isPlainKey = (key: unknown): key is string => typeof key === "string" && !hasOwn.call(objectPrototype, key)
21
+
22
+ const define = (o: object, key: unknown, value: unknown): void => {
23
+ Object.defineProperty(o, key as PropertyKey, { value, writable: true, enumerable: true, configurable: true })
24
+ }
25
+
26
+ /**
27
+ * Adds an entry whose key is not plain to `o`, given the entries already
28
+ * deferred. Keys that are objects may run code when converted to property
29
+ * keys, which `Object.fromEntries` only does once every entry is parsed:
30
+ * from the first such key on, entries are deferred to {@link flushEntries}.
31
+ * Returns the deferred entries, if any.
32
+ */
33
+ export const addEntry = (
34
+ o: object,
35
+ deferred: Array<unknown> | undefined,
36
+ key: unknown,
37
+ value: unknown
38
+ ): Array<unknown> | undefined => {
39
+ if (deferred === undefined && (typeof key !== "object" || key === null) && typeof key !== "function") {
40
+ define(o, key, value)
41
+ return undefined
42
+ }
43
+ if (deferred === undefined) deferred = []
44
+ deferred.push(key, value)
45
+ return deferred
46
+ }
47
+
48
+ /** Adds the deferred entries of {@link addEntry} to `o`, in order. */
49
+ export const flushEntries = (o: object, deferred: ReadonlyArray<unknown>): void => {
50
+ for (let i = 0; i < deferred.length; i += 2) define(o, deferred[i], deferred[i + 1])
51
+ }
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Descriptions of parsers, recorded next to their steps so that parsers can
3
+ * be compiled (see `docs/DESIGN.md`, "Compiled parsers").
4
+ *
5
+ * Nodes refer to their child parsers rather than to child nodes, so a
6
+ * child's own step is always at hand for fallback, and recursion through
7
+ * `Suspend` is just a reference.
8
+ */
9
+ import type { Encoding } from "../Encoding.js"
10
+ import type { ErrMode, Input, State } from "../Input.js"
11
+
12
+ /** The parts of a parser the compiler needs. */
13
+ export interface ParserLike {
14
+ readonly step: (state: State<any>) => unknown
15
+ readonly name: string | undefined
16
+ }
17
+
18
+ type P = ParserLike
19
+ type Step = (state: State<any>) => unknown
20
+
21
+ /** A repetition range, normalized: `max` may be `Infinity`. */
22
+ interface Bounds {
23
+ readonly min: number
24
+ readonly max: number
25
+ }
26
+
27
+ /**
28
+ * For token nodes: `undefined` for the `Token`/`Ascii` parsers (code points
29
+ * of strings with lenient surrogates, bytes, or array elements), or the
30
+ * encoding of a `Text` parser.
31
+ */
32
+ type Enc = Encoding<any> | undefined
33
+
34
+ export type Node =
35
+ // Constants
36
+ | { readonly _tag: "Opaque"; readonly step: Step }
37
+ | { readonly _tag: "Succeed"; readonly value: unknown }
38
+ | { readonly _tag: "Sync"; readonly evaluate: () => unknown }
39
+ | { readonly _tag: "Fail"; readonly mode: ErrMode }
40
+ | { readonly _tag: "Eof" }
41
+ | { readonly _tag: "Location" }
42
+ | { readonly _tag: "Suspend"; readonly resolve: () => P }
43
+ // Tokens
44
+ | { readonly _tag: "Any"; readonly encoding: Enc }
45
+ | { readonly _tag: "Literal"; readonly literal: Input; readonly caseless: boolean; readonly encoding: Enc }
46
+ | { readonly _tag: "OneOf"; readonly set: unknown; readonly negate: boolean; readonly encoding: Enc }
47
+ | { readonly _tag: "Take"; readonly count: number; readonly encoding: Enc }
48
+ | ({ readonly _tag: "TakeWhile"; readonly set: unknown; readonly negate: boolean; readonly encoding: Enc } & Bounds)
49
+ | ({ readonly _tag: "TakeUntil"; readonly literal: Input; readonly encoding: Enc } & Bounds)
50
+ | { readonly _tag: "Rest"; readonly encoding: Enc }
51
+ | { readonly _tag: "RestLen" }
52
+ | { readonly _tag: "TillLineEnding"; readonly encoding: Enc }
53
+ | { readonly _tag: "Identifier"; readonly encoding: Enc }
54
+ | { readonly _tag: "Bom"; readonly encoding: Enc }
55
+ | {
56
+ readonly _tag: "Escaped"
57
+ readonly normal: P
58
+ /** The control character, as a code point (or a byte / unit for `Ascii`). */
59
+ readonly control: number
60
+ readonly escape: P
61
+ /** `takeEscaped`: return the raw slice instead of the unescaped text. */
62
+ readonly raw: boolean
63
+ readonly encoding: Enc
64
+ }
65
+ // Mapping
66
+ | { readonly _tag: "Map"; readonly self: P; readonly f: (a: any) => unknown }
67
+ | { readonly _tag: "As"; readonly self: P; readonly value: unknown }
68
+ | { readonly _tag: "Tap"; readonly self: P; readonly f: (a: any) => void }
69
+ | { readonly _tag: "TryMap"; readonly self: P; readonly f: (a: any) => unknown }
70
+ | { readonly _tag: "FilterMap"; readonly self: P; readonly f: (a: any) => unknown }
71
+ | { readonly _tag: "Filter"; readonly self: P; readonly predicate: (a: any) => boolean }
72
+ | { readonly _tag: "FlatMap"; readonly self: P; readonly f: (a: any) => P }
73
+ // Consumed input
74
+ | { readonly _tag: "Taken"; readonly self: P; readonly encoding: Enc }
75
+ | { readonly _tag: "WithTaken"; readonly self: P }
76
+ | { readonly _tag: "Span"; readonly self: P }
77
+ | { readonly _tag: "WithSpan"; readonly self: P }
78
+ // Errors
79
+ | { readonly _tag: "Mode"; readonly self: P; readonly from: ErrMode; readonly to: ErrMode }
80
+ | { readonly _tag: "Context"; readonly self: P; readonly label: string }
81
+ | { readonly _tag: "Expected"; readonly self: P; readonly description: string }
82
+ | { readonly _tag: "MapError"; readonly self: P }
83
+ | { readonly _tag: "OrFail"; readonly self: P }
84
+ // Control
85
+ | { readonly _tag: "Opt"; readonly self: P }
86
+ | { readonly _tag: "OrElseSucceed"; readonly self: P; readonly value: () => unknown }
87
+ | { readonly _tag: "Peek"; readonly self: P }
88
+ | { readonly _tag: "Not"; readonly self: P }
89
+ // Sequencing
90
+ | {
91
+ readonly _tag: "Seq"
92
+ readonly parsers: ReadonlyArray<P>
93
+ /** Which outputs are kept: all (a tuple), one index, or a pair of indices. */
94
+ readonly output: "tuple" | number | readonly [number, number]
95
+ }
96
+ | { readonly _tag: "Struct"; readonly keys: ReadonlyArray<string>; readonly parsers: ReadonlyArray<P> }
97
+ | { readonly _tag: "ZipWith"; readonly self: P; readonly that: P; readonly f: (a: any, b: any) => unknown }
98
+ // Choice
99
+ | { readonly _tag: "Alt"; readonly parsers: ReadonlyArray<P> }
100
+ | {
101
+ readonly _tag: "Dispatch"
102
+ readonly selector: P
103
+ /** A record of cases, when `dispatch` was given one. */
104
+ readonly cases: Readonly<Record<PropertyKey, P>> | undefined
105
+ readonly otherwise: P | undefined
106
+ readonly choose: (key: any) => P | undefined
107
+ }
108
+ // Repetition
109
+ | ({ readonly _tag: "Repeat"; readonly self: P } & Bounds)
110
+ | ({
111
+ readonly _tag: "Fold"
112
+ readonly self: P
113
+ readonly init: () => unknown
114
+ readonly f: (acc: any, a: any) => unknown
115
+ readonly name: string
116
+ } & Bounds)
117
+ | ({ readonly _tag: "RepeatTill"; readonly self: P; readonly end: P } & Bounds)
118
+ | ({ readonly _tag: "Separated"; readonly self: P; readonly separator: P } & Bounds)
119
+ | {
120
+ readonly _tag: "SeparatedFold"
121
+ readonly self: P
122
+ readonly separator: P
123
+ readonly op: (l: any, s: any, r: any) => unknown
124
+ readonly right: boolean
125
+ }
126
+ // Services, recovery, debugging
127
+ | { readonly _tag: "Service" }
128
+ | { readonly _tag: "Provide"; readonly self: P }
129
+ | { readonly _tag: "ResumeAfter"; readonly self: P; readonly recover: P }
130
+ | { readonly _tag: "RetryAfter"; readonly self: P; readonly recover: P }
131
+ | { readonly _tag: "Trace"; readonly self: P; readonly name: string }
132
+
133
+ export type Tag = Node["_tag"]
134
+
135
+ export type NodeOf<T extends Tag> = Extract<Node, { readonly _tag: T }>
136
+
137
+ /** Where a parser's description is stored. */
138
+ export const NodeId: unique symbol = Symbol.for("effect-parse/Parser/node")
139
+
140
+ /** The description of a parser: `Opaque` when it has none. */
141
+ export const nodeOf = (p: ParserLike): Node => (p as any)[NodeId] ?? { _tag: "Opaque", step: p.step }
142
+
143
+ /** Normalizes a range, as `repeat`, `takeWhile`, ... do. */
144
+ export const bounds = (range: number | { readonly min?: number | undefined; readonly max?: number | undefined } | undefined): Bounds => {
145
+ if (range === undefined) return { min: 0, max: Infinity }
146
+ if (typeof range === "number") return { min: range, max: range }
147
+ return { min: range.min ?? 0, max: range.max ?? Infinity }
148
+ }
149
+
150
+ /** The child parsers of a node, in evaluation order. */
151
+ export const children = (node: Node): ReadonlyArray<P> => {
152
+ switch (node._tag) {
153
+ case "Escaped":
154
+ return [node.normal, node.escape]
155
+ case "Map":
156
+ case "As":
157
+ case "Tap":
158
+ case "TryMap":
159
+ case "FilterMap":
160
+ case "Filter":
161
+ case "FlatMap":
162
+ case "Taken":
163
+ case "WithTaken":
164
+ case "Span":
165
+ case "WithSpan":
166
+ case "Mode":
167
+ case "Context":
168
+ case "Expected":
169
+ case "MapError":
170
+ case "OrFail":
171
+ case "Opt":
172
+ case "OrElseSucceed":
173
+ case "Peek":
174
+ case "Not":
175
+ case "Repeat":
176
+ case "Fold":
177
+ case "Provide":
178
+ case "Trace":
179
+ return [node.self]
180
+ case "ResumeAfter":
181
+ case "RetryAfter":
182
+ return [node.self, node.recover]
183
+ case "Seq":
184
+ case "Struct":
185
+ case "Alt":
186
+ return node.parsers
187
+ case "ZipWith":
188
+ return [node.self, node.that]
189
+ case "Dispatch": {
190
+ const out: Array<P> = [node.selector]
191
+ if (node.cases !== undefined) {
192
+ for (const key of Reflect.ownKeys(node.cases)) out.push(node.cases[key as any]!)
193
+ }
194
+ if (node.otherwise !== undefined) out.push(node.otherwise)
195
+ return out
196
+ }
197
+ case "RepeatTill":
198
+ return [node.self, node.end]
199
+ case "Separated":
200
+ case "SeparatedFold":
201
+ return [node.self, node.separator]
202
+ case "Suspend":
203
+ return [node.resolve()]
204
+ default:
205
+ return []
206
+ }
207
+ }