@thomasfosterau/effect-parse 0.1.0 → 0.3.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 (161) hide show
  1. package/README.md +291 -231
  2. package/dist/Ascii.d.ts +32 -53
  3. package/dist/Ascii.d.ts.map +1 -1
  4. package/dist/Ascii.js +576 -571
  5. package/dist/Ascii.js.map +1 -1
  6. package/dist/Binary.d.ts +6 -29
  7. package/dist/Binary.d.ts.map +1 -1
  8. package/dist/Binary.js +129 -116
  9. package/dist/Binary.js.map +1 -1
  10. package/dist/Bits.d.ts +4 -2
  11. package/dist/Bits.d.ts.map +1 -1
  12. package/dist/Bits.js +121 -123
  13. package/dist/Bits.js.map +1 -1
  14. package/dist/Compiler.d.ts +39 -0
  15. package/dist/Compiler.d.ts.map +1 -0
  16. package/dist/Compiler.js +48 -0
  17. package/dist/Compiler.js.map +1 -0
  18. package/dist/Encoding.d.ts +29 -46
  19. package/dist/Encoding.d.ts.map +1 -1
  20. package/dist/Encoding.js +76 -75
  21. package/dist/Encoding.js.map +1 -1
  22. package/dist/Expression.d.ts +15 -49
  23. package/dist/Expression.d.ts.map +1 -1
  24. package/dist/Expression.js +151 -132
  25. package/dist/Expression.js.map +1 -1
  26. package/dist/Input.d.ts +251 -177
  27. package/dist/Input.d.ts.map +1 -1
  28. package/dist/Input.js +409 -299
  29. package/dist/Input.js.map +1 -1
  30. package/dist/Nom.d.ts +45 -85
  31. package/dist/Nom.d.ts.map +1 -1
  32. package/dist/Nom.js +423 -453
  33. package/dist/Nom.js.map +1 -1
  34. package/dist/ParseError.d.ts +54 -49
  35. package/dist/ParseError.d.ts.map +1 -1
  36. package/dist/ParseError.js +232 -275
  37. package/dist/ParseError.js.map +1 -1
  38. package/dist/Parser.d.ts +345 -313
  39. package/dist/Parser.d.ts.map +1 -1
  40. package/dist/Parser.js +2034 -1802
  41. package/dist/Parser.js.map +1 -1
  42. package/dist/ParserSchema.d.ts +206 -7
  43. package/dist/ParserSchema.d.ts.map +1 -1
  44. package/dist/ParserSchema.js +678 -49
  45. package/dist/ParserSchema.js.map +1 -1
  46. package/dist/ParserStream.d.ts +26 -20
  47. package/dist/ParserStream.d.ts.map +1 -1
  48. package/dist/ParserStream.js +267 -255
  49. package/dist/ParserStream.js.map +1 -1
  50. package/dist/Syntax.d.ts +401 -0
  51. package/dist/Syntax.d.ts.map +1 -0
  52. package/dist/Syntax.js +717 -0
  53. package/dist/Syntax.js.map +1 -0
  54. package/dist/Text.d.ts +98 -134
  55. package/dist/Text.d.ts.map +1 -1
  56. package/dist/Text.js +534 -496
  57. package/dist/Text.js.map +1 -1
  58. package/dist/Token.d.ts +60 -42
  59. package/dist/Token.d.ts.map +1 -1
  60. package/dist/Token.js +331 -265
  61. package/dist/Token.js.map +1 -1
  62. package/dist/index.d.ts +16 -17
  63. package/dist/index.js +16 -17
  64. package/dist/internal/compiler/closure.d.ts +14 -16
  65. package/dist/internal/compiler/closure.d.ts.map +1 -1
  66. package/dist/internal/compiler/closure.js +1458 -1637
  67. package/dist/internal/compiler/closure.js.map +1 -1
  68. package/dist/internal/compiler/entries.d.ts +11 -11
  69. package/dist/internal/compiler/entries.d.ts.map +1 -1
  70. package/dist/internal/compiler/entries.js +36 -26
  71. package/dist/internal/compiler/entries.js.map +1 -1
  72. package/dist/internal/compiler/first.d.ts +19 -19
  73. package/dist/internal/compiler/first.d.ts.map +1 -1
  74. package/dist/internal/compiler/first.js +251 -227
  75. package/dist/internal/compiler/first.js.map +1 -1
  76. package/dist/internal/compiler/jit.d.ts +6 -17
  77. package/dist/internal/compiler/jit.d.ts.map +1 -1
  78. package/dist/internal/compiler/jit.js +860 -988
  79. package/dist/internal/compiler/jit.js.map +1 -1
  80. package/dist/internal/compiler/registry.d.ts +20 -21
  81. package/dist/internal/compiler/registry.d.ts.map +1 -1
  82. package/dist/internal/compiler/registry.js +21 -41
  83. package/dist/internal/compiler/registry.js.map +1 -1
  84. package/dist/internal/compiler/sets.d.ts +9 -13
  85. package/dist/internal/compiler/sets.d.ts.map +1 -1
  86. package/dist/internal/compiler/sets.js +76 -73
  87. package/dist/internal/compiler/sets.js.map +1 -1
  88. package/dist/internal/entries.d.ts +3 -3
  89. package/dist/internal/entries.d.ts.map +1 -1
  90. package/dist/internal/entries.js +38 -30
  91. package/dist/internal/entries.js.map +1 -1
  92. package/dist/internal/node.d.ts +177 -173
  93. package/dist/internal/node.d.ts.map +1 -1
  94. package/dist/internal/node.js +77 -66
  95. package/dist/internal/node.js.map +1 -1
  96. package/dist/internal/numbers.d.ts +7 -8
  97. package/dist/internal/numbers.d.ts.map +1 -1
  98. package/dist/internal/numbers.js +246 -276
  99. package/dist/internal/numbers.js.map +1 -1
  100. package/dist/internal/resume.d.ts +6 -24
  101. package/dist/internal/resume.d.ts.map +1 -1
  102. package/dist/internal/resume.js +30 -29
  103. package/dist/internal/resume.js.map +1 -1
  104. package/dist/internal/run.d.ts +15 -0
  105. package/dist/internal/run.d.ts.map +1 -0
  106. package/dist/internal/run.js +36 -0
  107. package/dist/internal/run.js.map +1 -0
  108. package/dist/internal/stack.d.ts +16 -0
  109. package/dist/internal/stack.d.ts.map +1 -0
  110. package/dist/internal/stack.js +36 -0
  111. package/dist/internal/stack.js.map +1 -0
  112. package/dist/internal/syntax.d.ts +147 -0
  113. package/dist/internal/syntax.d.ts.map +1 -0
  114. package/dist/internal/syntax.js +309 -0
  115. package/dist/internal/syntax.js.map +1 -0
  116. package/dist/internal/text.d.ts +10 -2
  117. package/dist/internal/text.d.ts.map +1 -1
  118. package/dist/internal/text.js +295 -345
  119. package/dist/internal/text.js.map +1 -1
  120. package/dist/internal/ties.d.ts +30 -0
  121. package/dist/internal/ties.d.ts.map +1 -0
  122. package/dist/internal/ties.js +67 -0
  123. package/dist/internal/ties.js.map +1 -0
  124. package/dist/internal/tokens.d.ts +25 -30
  125. package/dist/internal/tokens.d.ts.map +1 -1
  126. package/dist/internal/tokens.js +369 -363
  127. package/dist/internal/tokens.js.map +1 -1
  128. package/package.json +42 -28
  129. package/src/Ascii.ts +278 -187
  130. package/src/Binary.ts +150 -44
  131. package/src/Bits.ts +6 -3
  132. package/src/Compiler.ts +98 -0
  133. package/src/Expression.ts +24 -16
  134. package/src/Input.ts +210 -35
  135. package/src/Nom.ts +4 -3
  136. package/src/ParseError.ts +49 -52
  137. package/src/Parser.ts +1199 -799
  138. package/src/ParserSchema.ts +982 -23
  139. package/src/ParserStream.ts +104 -80
  140. package/src/Syntax.ts +900 -0
  141. package/src/Text.ts +272 -187
  142. package/src/Token.ts +180 -87
  143. package/src/index.ts +2 -0
  144. package/src/internal/compiler/closure.ts +121 -88
  145. package/src/internal/compiler/entries.ts +21 -8
  146. package/src/internal/compiler/first.ts +17 -15
  147. package/src/internal/compiler/jit.ts +360 -157
  148. package/src/internal/compiler/registry.ts +12 -33
  149. package/src/internal/compiler/sets.ts +17 -11
  150. package/src/internal/entries.ts +4 -2
  151. package/src/internal/node.ts +51 -37
  152. package/src/internal/numbers.ts +22 -20
  153. package/src/internal/resume.ts +8 -4
  154. package/src/internal/run.ts +41 -0
  155. package/src/internal/stack.ts +41 -0
  156. package/src/internal/syntax.ts +426 -0
  157. package/src/internal/text.ts +32 -25
  158. package/src/internal/ties.ts +83 -0
  159. package/src/internal/tokens.ts +96 -51
  160. package/dist/index.d.ts.map +0 -1
  161. package/dist/index.js.map +0 -1
package/dist/Parser.js CHANGED
@@ -1,1936 +1,2168 @@
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
- */
1
+ import * as Predicate from "effect/Predicate";
2
+ import * as Result from "effect/Result";
3
+ import { Fail, Failure, State, mergeExpected, mergeFailures, mergeTiedFailures } from "./Input.js";
4
+ import * as Resume from "./internal/resume.js";
5
+ import * as Registry from "./internal/compiler/registry.js";
6
+ import * as Option from "effect/Option";
7
+ import { TiedFailures } from "./internal/ties.js";
32
8
  import * as Context from "effect/Context";
33
9
  import * as Effect from "effect/Effect";
34
10
  import { dual } from "effect/Function";
35
11
  import { NodeInspectSymbol } from "effect/Inspectable";
36
- import * as Option from "effect/Option";
37
12
  import { pipeArguments } from "effect/Pipeable";
38
- import * as Result from "effect/Result";
39
- import { Fail, Failure, mergeExpected, mergeFailures, State } from "./Input.js";
40
13
  import * as ParseError from "./ParseError.js";
41
- import * as Compiler from "./internal/compiler/registry.js";
42
14
  import * as Entries from "./internal/entries.js";
43
- import * as Resume from "./internal/resume.js";
44
- import { bounds, NodeId } from "./internal/node.js";
45
- // -----------------------------------------------------------------------------
46
- // Models
47
- // -----------------------------------------------------------------------------
48
- /**
49
- * @since 0.1.0
50
- * @category type ids
51
- */
52
- export const TypeId = "~effect-parse/Parser";
53
- // -----------------------------------------------------------------------------
54
- // Internals
55
- // -----------------------------------------------------------------------------
15
+ import * as Run from "./internal/run.js";
16
+ import * as Stack from "./internal/stack.js";
17
+ import { NodeId, bounds } from "./internal/node.js";
18
+ //#region src/Parser.ts
19
+ /**
20
+ * The `Parser` data type and its general-purpose combinators.
21
+ *
22
+ * A `Parser<A, E, R, I>` consumes input of type `I` and produces a value of
23
+ * type `A`. It may fail with a `ParseError<E>`, where `E` is the type of
24
+ * user supplied error causes, and it may require services `R` from an Effect
25
+ * `Context`.
26
+ *
27
+ * Under the hood a parser is a function over a mutable cursor (`Input.State`),
28
+ * as in winnow. Failure is cheap and backtracking is a matter of resetting an
29
+ * offset. On the surface the API follows Effect's conventions: parsers are
30
+ * pipeable, combinators are dual (data-first or data-last), and parsers can
31
+ * be written with generators:
32
+ *
33
+ * ```ts
34
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
35
+ *
36
+ * const point = Parser.gen(function*() {
37
+ * yield* Token.literal("(")
38
+ * const x = yield* Ascii.decInt
39
+ * yield* Token.literal(",")
40
+ * const y = yield* Ascii.decInt
41
+ * yield* Token.literal(")")
42
+ * return { x, y }
43
+ * })
44
+ *
45
+ * Parser.parse(point, "(1,-2)") // Result.succeed({ x: 1, y: -2 })
46
+ * ```
47
+ *
48
+ * @since 0.1.0
49
+ */
50
+ /**
51
+ * @since 0.1.0
52
+ * @category type ids
53
+ */
54
+ const TypeId = "~effect-parse/Parser";
56
55
  const variance = {
57
- _A: (_) => _,
58
- _E: (_) => _,
59
- _R: (_) => _,
60
- _I: (_) => { }
56
+ _A: (_) => _,
57
+ _E: (_) => _,
58
+ _R: (_) => _,
59
+ _I: (_) => {}
60
+ };
61
+ var SingleShotGen = class SingleShotGen {
62
+ self;
63
+ called = false;
64
+ constructor(self) {
65
+ this.self = self;
66
+ }
67
+ next(a) {
68
+ if (this.called) return {
69
+ value: a,
70
+ done: true
71
+ };
72
+ this.called = true;
73
+ return {
74
+ value: this.self,
75
+ done: false
76
+ };
77
+ }
78
+ return(a) {
79
+ return {
80
+ value: a,
81
+ done: true
82
+ };
83
+ }
84
+ throw(e) {
85
+ throw e;
86
+ }
87
+ [Symbol.iterator]() {
88
+ return new SingleShotGen(this.self);
89
+ }
61
90
  };
62
- class SingleShotGen {
63
- self;
64
- called = false;
65
- constructor(self) {
66
- this.self = self;
67
- }
68
- next(a) {
69
- if (this.called)
70
- return { value: a, done: true };
71
- this.called = true;
72
- return { value: this.self, done: false };
73
- }
74
- return(a) {
75
- return { value: a, done: true };
76
- }
77
- throw(e) {
78
- throw e;
79
- }
80
- [Symbol.iterator]() {
81
- return new SingleShotGen(this.self);
82
- }
83
- }
84
- class ParserImpl {
85
- step;
86
- name;
87
- [TypeId] = variance;
88
- /** The description used by the compiler; `undefined` for `Opaque`. */
89
- [NodeId];
90
- constructor(step, name, node) {
91
- this.step = step;
92
- this.name = name;
93
- this[NodeId] = node;
94
- }
95
- pipe() {
96
- return pipeArguments(this, arguments);
97
- }
98
- [Symbol.iterator]() {
99
- return new SingleShotGen(this);
100
- }
101
- toString() {
102
- return this.name === undefined ? "Parser" : `Parser(${this.name})`;
103
- }
104
- toJSON() {
105
- return { _id: "Parser", name: this.name };
106
- }
107
- [NodeInspectSymbol]() {
108
- return this.toJSON();
109
- }
110
- }
111
- /**
112
- * Tests whether a value is a `Parser`.
113
- *
114
- * @since 0.1.0
115
- * @category guards
116
- */
117
- export const isParser = (u) => typeof u === "object" && u !== null && TypeId in u;
91
+ var ParserImpl = class {
92
+ step;
93
+ name;
94
+ [TypeId] = variance;
95
+ /** The description used by the compiler; `undefined` for `Opaque`. */
96
+ [NodeId];
97
+ constructor(step, name, node) {
98
+ this.step = step;
99
+ this.name = name;
100
+ this[NodeId] = node;
101
+ }
102
+ pipe() {
103
+ return pipeArguments(this, arguments);
104
+ }
105
+ [Symbol.iterator]() {
106
+ return new SingleShotGen(this);
107
+ }
108
+ toString() {
109
+ return this.name === void 0 ? "Parser" : `Parser(${this.name})`;
110
+ }
111
+ toJSON() {
112
+ return {
113
+ _id: "Parser",
114
+ name: this.name
115
+ };
116
+ }
117
+ [NodeInspectSymbol]() {
118
+ return this.toJSON();
119
+ }
120
+ };
121
+ /**
122
+ * Tests whether a value is a `Parser`.
123
+ *
124
+ * @since 0.1.0
125
+ * @category guards
126
+ */
127
+ const isParser = (u) => Predicate.isObjectOrArray(u) && "~effect-parse/Parser" in u;
118
128
  const isParserArg = (args) => isParser(args[0]);
119
129
  const isTwoParserArgs = (args) => isParser(args[0]) && isParser(args[1]);
120
- // -----------------------------------------------------------------------------
121
- // Constructors
122
- // -----------------------------------------------------------------------------
123
- /**
124
- * Creates a parser from a step function over the mutable {@link State}
125
- * cursor. This is the escape hatch for writing low-level parsers, much like
126
- * passing a plain function as a parser in winnow.
127
- *
128
- * The step function must either return the parsed value, having advanced
129
- * `state.pos` past the consumed input, or return the result of one of
130
- * `state.backtrack`, `state.cut`, `state.incomplete`, ... to signal failure.
131
- *
132
- * @example
133
- * ```ts
134
- * import { Parser } from "@thomasfosterau/effect-parse"
135
- *
136
- * // Parses a single ASCII uppercase letter
137
- * const upper = Parser.make<string>((state) => {
138
- * const c = state.input.charCodeAt(state.pos)
139
- * if (state.pos < state.end && c >= 65 && c <= 90) {
140
- * state.pos++
141
- * return String.fromCharCode(c)
142
- * }
143
- * return state.pos >= state.end
144
- * ? state.eoi("Token", ["uppercase letter"], 1)
145
- * : state.backtrack("Token", ["uppercase letter"])
146
- * })
147
- * ```
148
- *
149
- * @since 0.1.0
150
- * @category constructors
151
- */
152
- export const make = (step, name) => new ParserImpl(step, name, undefined);
153
- /**
154
- * Creates a parser together with its description, used by the compiler.
155
- *
156
- * @internal
157
- */
158
- export const described = (step, name, node) => new ParserImpl(step, name, node);
130
+ /**
131
+ * Creates a parser from a step function over the mutable {@link State}
132
+ * cursor. This is the escape hatch for writing low-level parsers, much like
133
+ * passing a plain function as a parser in winnow.
134
+ *
135
+ * The step function must either return the parsed value, having advanced
136
+ * `state.pos` past the consumed input, or return the result of one of
137
+ * `state.backtrack`, `state.cut`, `state.incomplete`, ... to signal failure.
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * import { Parser } from "@thomasfosterau/effect-parse"
142
+ *
143
+ * // Parses a single ASCII uppercase letter
144
+ * const upper = Parser.make<string>((state) => {
145
+ * const c = state.input.charCodeAt(state.pos)
146
+ * if (state.pos < state.end && c >= 65 && c <= 90) {
147
+ * state.pos++
148
+ * return String.fromCharCode(c)
149
+ * }
150
+ * return state.pos >= state.end
151
+ * ? state.eoi("Token", ["uppercase letter"], 1)
152
+ * : state.backtrack("Token", ["uppercase letter"])
153
+ * })
154
+ * ```
155
+ *
156
+ * @since 0.1.0
157
+ * @category constructors
158
+ */
159
+ const make = (step, name) => new ParserImpl(step, name, void 0);
160
+ /**
161
+ * Creates a parser together with its description, used by the compiler.
162
+ *
163
+ * @internal
164
+ */
165
+ const described = (step, name, node) => new ParserImpl(step, name, node);
159
166
  const nodeOfParser = (p) => p[NodeId];
160
167
  /**
161
- * Gives a parser a name, used by `toString`.
162
- *
163
- * @since 0.1.0
164
- * @category utils
165
- */
166
- export const named = dual(2, (self, name) => new ParserImpl(self.step, name, nodeOfParser(self)));
167
- /**
168
- * A parser that consumes nothing and succeeds with the given value.
169
- *
170
- * @since 0.1.0
171
- * @category constructors
172
- */
173
- export const succeed = (value) => described(() => value, "succeed", { _tag: "Succeed", value });
174
- /**
175
- * A parser that consumes nothing and succeeds with the result of a thunk.
176
- *
177
- * @since 0.1.0
178
- * @category constructors
179
- */
180
- export const sync = (evaluate) => described(() => evaluate(), "sync", { _tag: "Sync", evaluate });
181
- /**
182
- * A parser that consumes nothing and succeeds with `undefined` (winnow's
183
- * `empty`, nom's `success(())`).
184
- *
185
- * @since 0.1.0
186
- * @category constructors
187
- */
188
- export const empty = described(() => undefined, "empty", {
189
- _tag: "Succeed",
190
- value: undefined
168
+ * Gives a parser a name, used by `toString`.
169
+ *
170
+ * @since 0.1.0
171
+ * @category utils
172
+ */
173
+ const named = /*#__PURE__*/ dual(2, (self, name) => new ParserImpl(self.step, name, nodeOfParser(self)));
174
+ /**
175
+ * A parser that consumes nothing and succeeds with the given value.
176
+ *
177
+ * @since 0.1.0
178
+ * @category constructors
179
+ */
180
+ const succeed = (value) => described(() => value, "succeed", {
181
+ _tag: "Succeed",
182
+ value
183
+ });
184
+ /**
185
+ * A parser that consumes nothing and succeeds with the result of a thunk.
186
+ *
187
+ * @since 0.1.0
188
+ * @category constructors
189
+ */
190
+ const sync = (evaluate) => described(() => evaluate(), "sync", {
191
+ _tag: "Sync",
192
+ evaluate
193
+ });
194
+ /**
195
+ * A parser that consumes nothing and succeeds with `undefined` (winnow's
196
+ * `empty`, nom's `success(())`).
197
+ *
198
+ * @since 0.1.0
199
+ * @category constructors
200
+ */
201
+ const empty = /*#__PURE__*/ described(() => void 0, "empty", {
202
+ _tag: "Succeed",
203
+ value: void 0
191
204
  });
192
205
  /**
193
- * Alias of {@link empty}, following Effect's `Effect.void`.
194
- *
195
- * @since 0.1.0
196
- * @category constructors
197
- */
198
- export const void_ = empty;
199
- export {
200
- /**
201
- * @since 0.1.0
202
- * @category constructors
203
- */
204
- void_ as void };
205
- /**
206
- * A parser that always fails (backtracks) with the given user error as its
207
- * cause.
208
- *
209
- * @since 0.1.0
210
- * @category constructors
211
- */
212
- export const fail = (error) => described((state) => state.failCause("Backtrack", "Fail", error), "fail", failBacktrack);
213
- const failBacktrack = { _tag: "Fail", mode: "Backtrack" };
214
- /**
215
- * A parser that always fails (backtracks) without a cause. This is winnow's
216
- * `fail` and nom's `fail()`.
217
- *
218
- * @since 0.1.0
219
- * @category constructors
220
- */
221
- export const reject = described((state) => state.backtrack("Fail"), "reject", failBacktrack);
222
- /**
223
- * A parser that always fails with a message.
224
- *
225
- * @since 0.1.0
226
- * @category constructors
227
- */
228
- export const failMessage = (message) => described((state) => state.failMessage("Backtrack", "Fail", message), "failMessage", failBacktrack);
229
- /**
230
- * A placeholder parser that fails with an unrecoverable error (winnow's
231
- * `todo`).
232
- *
233
- * @since 0.1.0
234
- * @category constructors
235
- */
236
- export const todo = described((state) => state.failMessage("Cut", "Assert", "`todo` parser reached"), "todo", { _tag: "Fail", mode: "Cut" });
237
- /**
238
- * Lazily constructs a parser, which is needed to define recursive grammars.
239
- * The thunk is evaluated once, on first use.
240
- *
241
- * @example
242
- * ```ts
243
- * import { Parser, Token } from "@thomasfosterau/effect-parse"
244
- *
245
- * type Nested = ReadonlyArray<Nested>
246
- * const nested: Parser.Parser<Nested> = Parser.delimited(
247
- * Token.literal("["),
248
- * Parser.repeat(Parser.suspend(() => nested)),
249
- * Token.literal("]")
250
- * )
251
- * ```
252
- *
253
- * @since 0.1.0
254
- * @category constructors
255
- */
256
- export const suspend = (evaluate) => {
257
- let parser;
258
- return described((state) => (parser ??= evaluate()).step(state), "suspend", {
259
- _tag: "Suspend",
260
- resolve: () => parser ??= evaluate()
261
- });
206
+ * Alias of {@link empty}, following Effect's `Effect.void`.
207
+ *
208
+ * @since 0.1.0
209
+ * @category constructors
210
+ */
211
+ const void_ = empty;
212
+ /**
213
+ * A parser that always fails (backtracks) with the given user error as its
214
+ * cause.
215
+ *
216
+ * @since 0.1.0
217
+ * @category constructors
218
+ */
219
+ const fail = (error) => described((state) => state.failCause("Backtrack", "Fail", error), "fail", failBacktrack);
220
+ const failBacktrack = {
221
+ _tag: "Fail",
222
+ mode: "Backtrack"
262
223
  };
263
224
  /**
264
- * The current offset into the input (winnow's `Location`).
265
- *
266
- * @since 0.1.0
267
- * @category constructors
268
- */
269
- export const location = described((state) => state.pos, "location", {
270
- _tag: "Location"
225
+ * A parser that always fails (backtracks) without a cause. This is winnow's
226
+ * `fail` and nom's `fail()`.
227
+ *
228
+ * @since 0.1.0
229
+ * @category constructors
230
+ */
231
+ const reject = /*#__PURE__*/ described((state) => state.backtrack("Fail"), "reject", failBacktrack);
232
+ /**
233
+ * A parser that always fails with a message.
234
+ *
235
+ * @since 0.1.0
236
+ * @category constructors
237
+ */
238
+ const failMessage = (message) => described((state) => state.failMessage("Backtrack", "Fail", message), "failMessage", failBacktrack);
239
+ /**
240
+ * A placeholder parser that fails with an unrecoverable error (winnow's
241
+ * `todo`).
242
+ *
243
+ * @since 0.1.0
244
+ * @category constructors
245
+ */
246
+ const todo = /*#__PURE__*/ described((state) => state.failMessage("Cut", "Assert", "`todo` parser reached"), "todo", {
247
+ _tag: "Fail",
248
+ mode: "Cut"
271
249
  });
272
250
  /**
273
- * Succeeds (with `undefined`) only at the end of the input; nom's and
274
- * winnow's `eof`. On partial input, reaching the end of the available data
275
- * is reported as `Incomplete` since more data may follow.
276
- *
277
- * @since 0.1.0
278
- * @category constructors
279
- */
280
- export const eof = described((state) => {
281
- if (state.pos < state.end)
282
- return state.backtrack("Eof", expectedEof);
283
- if (state.partial)
284
- return state.incomplete(1, expectedEof);
285
- return undefined;
251
+ * Lazily constructs a parser, which is needed to define recursive grammars.
252
+ * The thunk is evaluated once, on first use.
253
+ *
254
+ * Each level of recursion uses some of the call stack, interpreted or
255
+ * compiled, so input nested a few thousand levels deep exhausts it. The
256
+ * runners catch the overflow and fail with an unrecoverable `ParseError` of
257
+ * kind `"TooDeep"` at the offset reached, instead of throwing.
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * import { Parser, Token } from "@thomasfosterau/effect-parse"
262
+ *
263
+ * type Nested = ReadonlyArray<Nested>
264
+ * const nested: Parser.Parser<Nested> = Parser.delimited(
265
+ * Token.literal("["),
266
+ * Parser.repeat(Parser.suspend(() => nested)),
267
+ * Token.literal("]")
268
+ * )
269
+ * ```
270
+ *
271
+ * @since 0.1.0
272
+ * @category constructors
273
+ */
274
+ const suspend = (evaluate) => {
275
+ let parser;
276
+ return described((state) => (parser ??= evaluate()).step(state), "suspend", {
277
+ _tag: "Suspend",
278
+ resolve: () => parser ??= evaluate()
279
+ });
280
+ };
281
+ /**
282
+ * The current offset into the input (winnow's `Location`).
283
+ *
284
+ * @since 0.1.0
285
+ * @category constructors
286
+ */
287
+ const location = /*#__PURE__*/ described((state) => state.pos, "location", { _tag: "Location" });
288
+ /**
289
+ * Succeeds (with `undefined`) only at the end of the input; nom's and
290
+ * winnow's `eof`. On partial input, reaching the end of the available data
291
+ * is reported as `Incomplete` since more data may follow.
292
+ *
293
+ * @since 0.1.0
294
+ * @category constructors
295
+ */
296
+ const eof = /*#__PURE__*/ described((state) => {
297
+ if (state.pos < state.end) return state.backtrack("Eof", expectedEof);
298
+ if (state.partial) return state.incomplete(1, expectedEof);
286
299
  }, "eof", { _tag: "Eof" });
287
300
  const expectedEof = ["end of input"];
288
- // -----------------------------------------------------------------------------
289
- // Mapping
290
- // -----------------------------------------------------------------------------
291
- /**
292
- * Transforms the output of a parser.
293
- *
294
- * @since 0.1.0
295
- * @category mapping
296
- */
297
- export const map = dual(2, (self, f) => {
298
- const step = self.step;
299
- return described((state) => {
300
- const a = step(state);
301
- return a === Fail ? Fail : f(a);
302
- }, self.name, { _tag: "Map", self, f });
301
+ /**
302
+ * Transforms the output of a parser.
303
+ *
304
+ * @since 0.1.0
305
+ * @category mapping
306
+ */
307
+ const map = /*#__PURE__*/ dual(2, (self, f) => {
308
+ const step = self.step;
309
+ return described((state) => {
310
+ const a = step(state);
311
+ return a === Fail ? Fail : f(a);
312
+ }, self.name, {
313
+ _tag: "Map",
314
+ self,
315
+ f
316
+ });
303
317
  });
304
318
  /**
305
- * Builds an object from the `[key, value]` entries a parser returns, as
306
- * `Object.fromEntries` does: `fromEntries(self)` is
307
- * `map(self, Object.fromEntries)`.
308
- *
309
- * Compiled parsers build the object directly when `self` repeats a pair
310
- * (`separated` or `repeat` of `separatedPair` or a two-element `seq`,
311
- * possibly inside `preceded`, `terminated`, `delimited` or `cut`), without
312
- * the pairs and the array. They do the same for `map(Object.fromEntries)`.
313
- *
314
- * @example
315
- * ```ts
316
- * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
317
- *
318
- * const entry = Parser.separatedPair(Ascii.alpha1, Token.literal("="), Ascii.decUint)
319
- * const record = Parser.fromEntries(Parser.separated(entry, Token.literal(",")))
320
- * Parser.parse(record, "a=1,b=2") // Result.succeed({ a: 1, b: 2 })
321
- * ```
322
- *
323
- * @since 0.2.0
324
- * @category mapping
325
- */
326
- export const fromEntries = (self) => map(self, Entries.fromEntries);
327
- /**
328
- * Replaces the output of a parser with a constant (winnow's `value`, nom's
329
- * `value`).
330
- *
331
- * @since 0.1.0
332
- * @category mapping
333
- */
334
- export const as = dual(2, (self, value) => {
335
- const step = self.step;
336
- return described((state) => step(state) === Fail ? Fail : value, self.name, { _tag: "As", self, value });
319
+ * Builds an object from the `[key, value]` entries a parser returns, as
320
+ * `Object.fromEntries` does: `fromEntries(self)` is
321
+ * `map(self, Object.fromEntries)`.
322
+ *
323
+ * Compiled parsers build the object directly when `self` repeats a pair
324
+ * (`separated` or `repeat` of `separatedPair` or a two-element `seq`,
325
+ * possibly inside `preceded`, `terminated`, `delimited` or `cut`), without
326
+ * the pairs and the array. They do the same for `map(Object.fromEntries)`.
327
+ *
328
+ * @example
329
+ * ```ts
330
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
331
+ *
332
+ * const entry = Parser.separatedPair(Ascii.alpha1, Token.literal("="), Ascii.decUint)
333
+ * const record = Parser.fromEntries(Parser.separated(entry, Token.literal(",")))
334
+ * Parser.parse(record, "a=1,b=2") // Result.succeed({ a: 1, b: 2 })
335
+ * ```
336
+ *
337
+ * @since 0.2.0
338
+ * @category mapping
339
+ */
340
+ const fromEntries = (self) => map(self, Entries.fromEntries);
341
+ /**
342
+ * Replaces the output of a parser with a constant (winnow's `value`, nom's
343
+ * `value`).
344
+ *
345
+ * @since 0.1.0
346
+ * @category mapping
347
+ */
348
+ const as = /*#__PURE__*/ dual(2, (self, value) => {
349
+ const step = self.step;
350
+ return described((state) => step(state) === Fail ? Fail : value, self.name, {
351
+ _tag: "As",
352
+ self,
353
+ value
354
+ });
337
355
  });
338
356
  /**
339
- * Discards the output of a parser (winnow's `void`).
340
- *
341
- * @since 0.1.0
342
- * @category mapping
343
- */
344
- export const asVoid = (self) => as(self, undefined);
345
- /**
346
- * Replaces the output of a parser with `Option.some(output)`.
347
- *
348
- * @since 0.1.0
349
- * @category mapping
350
- */
351
- export const asSome = (self) => map(self, Option.some);
352
- /**
353
- * Runs a side effect on the output of a parser, keeping the output.
354
- *
355
- * @since 0.1.0
356
- * @category mapping
357
- */
358
- export const tap = dual(2, (self, f) => {
359
- const step = self.step;
360
- return described((state) => {
361
- const a = step(state);
362
- if (a !== Fail)
363
- f(a);
364
- return a;
365
- }, self.name, { _tag: "Tap", self, f });
357
+ * Discards the output of a parser (winnow's `void`).
358
+ *
359
+ * @since 0.1.0
360
+ * @category mapping
361
+ */
362
+ const asVoid = (self) => as(self, void 0);
363
+ /**
364
+ * Replaces the output of a parser with `Option.some(output)`.
365
+ *
366
+ * @since 0.1.0
367
+ * @category mapping
368
+ */
369
+ const asSome = (self) => map(self, Option.some);
370
+ /**
371
+ * Runs a side effect on the output of a parser, keeping the output.
372
+ *
373
+ * @since 0.1.0
374
+ * @category mapping
375
+ */
376
+ const tap = /*#__PURE__*/ dual(2, (self, f) => {
377
+ const step = self.step;
378
+ return described((state) => {
379
+ const a = step(state);
380
+ if (a !== Fail) f(a);
381
+ return a;
382
+ }, self.name, {
383
+ _tag: "Tap",
384
+ self,
385
+ f
386
+ });
366
387
  });
367
388
  /**
368
- * Uses the output of a parser to choose the parser that consumes the rest
369
- * of the input (winnow's `flat_map`, nom's `flat_map`).
370
- *
371
- * @since 0.1.0
372
- * @category sequencing
373
- */
374
- export const flatMap = dual(2, (self, f) => {
375
- const step = self.step;
376
- return described((state) => {
377
- const a = step(state);
378
- return a === Fail ? Fail : f(a).step(state);
379
- }, undefined, { _tag: "FlatMap", self, f });
389
+ * Uses the output of a parser to choose the parser that consumes the rest
390
+ * of the input (winnow's `flat_map`, nom's `flat_map`).
391
+ *
392
+ * @since 0.1.0
393
+ * @category sequencing
394
+ */
395
+ const flatMap = /*#__PURE__*/ dual(2, (self, f) => {
396
+ const step = self.step;
397
+ return described((state) => {
398
+ const a = step(state);
399
+ return a === Fail ? Fail : f(a).step(state);
400
+ }, void 0, {
401
+ _tag: "FlatMap",
402
+ self,
403
+ f
404
+ });
380
405
  });
381
406
  /**
382
- * Applies a second parser to the *output* of the first (winnow's
383
- * `and_then`, nom's `map_parser`). The output of the first parser must
384
- * itself be an input, e.g. a slice produced by {@link taken} or
385
- * `Token.take`.
386
- *
387
- * Error offsets reported by the inner parser are translated relative to the
388
- * start of the outer parser.
389
- *
390
- * @example
391
- * ```ts
392
- * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
393
- *
394
- * // Take exactly 4 characters, then require them all to be digits
395
- * const year = Token.take(4).pipe(Parser.mapParser(Parser.allConsuming(Ascii.decUint)))
396
- * ```
397
- *
398
- * @since 0.1.0
399
- * @category sequencing
400
- */
401
- export const mapParser = dual(2, (self, inner) => {
402
- const step = self.step;
403
- return make((state) => {
404
- const start = state.pos;
405
- const a = step(state);
406
- if (a === Fail)
407
- return Fail;
408
- const sub = new State(a, { context: state.context });
409
- sub.tracer = state.tracer;
410
- sub.depth = state.depth;
411
- sub.recovered = state.recovered;
412
- const b = inner.step(sub);
413
- if (b === Fail) {
414
- const f = sub.failure;
415
- if (sub.encoding !== undefined)
416
- state.encoding = sub.encoding;
417
- // The inner input is usually a slice ending where the outer parser stopped
418
- const base = Math.max(start, state.pos - a.length);
419
- const offset = Math.min(base + f.offset, state.pos);
420
- return state.failWith(sub.mode === "Incomplete" ? "Backtrack" : sub.mode, new Failure(offset, f.kind, f.expected, f.context, f.hasCause, f.cause, f.message, 0));
421
- }
422
- return b;
423
- });
407
+ * Applies a second parser to the *output* of the first (winnow's
408
+ * `and_then`, nom's `map_parser`). The output of the first parser must
409
+ * itself be an input, e.g. a slice produced by {@link taken} or
410
+ * `Token.take`.
411
+ *
412
+ * Error offsets reported by the inner parser are translated relative to the
413
+ * start of the outer parser.
414
+ *
415
+ * @example
416
+ * ```ts
417
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
418
+ *
419
+ * // Take exactly 4 characters, then require them all to be digits
420
+ * const year = Token.take(4).pipe(Parser.mapParser(Parser.allConsuming(Ascii.decUint)))
421
+ * ```
422
+ *
423
+ * @since 0.1.0
424
+ * @category sequencing
425
+ */
426
+ const mapParser = /*#__PURE__*/ dual(2, (self, inner) => {
427
+ const step = self.step;
428
+ return make((state) => {
429
+ const start = state.pos;
430
+ const a = step(state);
431
+ if (a === Fail) return Fail;
432
+ const sub = new State(a, { context: state.context });
433
+ sub.tracer = state.tracer;
434
+ sub.depth = state.depth;
435
+ sub.recovered = state.recovered;
436
+ const b = inner.step(sub);
437
+ if (b === Fail) {
438
+ const f = sub.failure;
439
+ if (sub.encoding !== void 0) state.encoding = sub.encoding;
440
+ const base = Math.max(start, state.pos - a.length);
441
+ const offset = Math.min(base + f.offset, state.pos);
442
+ return state.failWith(sub.mode === "Incomplete" ? "Backtrack" : sub.mode, new Failure(offset, f.kind, f.expected, f.context, f.hasCause, f.cause, f.message, 0));
443
+ }
444
+ return b;
445
+ });
424
446
  });
425
447
  /**
426
- * Transforms the output of a parser with a function that may fail with a
427
- * `Result` (winnow's `try_map`, nom's `map_res`). On failure the parser
428
- * backtracks to where it started and the error becomes the `cause` of the
429
- * `ParseError`.
430
- *
431
- * @since 0.1.0
432
- * @category mapping
433
- */
434
- export const tryMap = dual(2, (self, f) => {
435
- const step = self.step;
436
- return described((state) => {
437
- const start = state.pos;
438
- const a = step(state);
439
- if (a === Fail)
440
- return Fail;
441
- const r = f(a);
442
- if (Result.isSuccess(r))
443
- return r.success;
444
- state.pos = start;
445
- return state.failCause("Backtrack", "Verify", r.failure);
446
- }, self.name, { _tag: "TryMap", self, f });
448
+ * Transforms the output of a parser with a function that may fail with a
449
+ * `Result` (winnow's `try_map`, nom's `map_res`). On failure the parser
450
+ * backtracks to where it started and the error becomes the `cause` of the
451
+ * `ParseError`.
452
+ *
453
+ * @since 0.1.0
454
+ * @category mapping
455
+ */
456
+ const tryMap = /*#__PURE__*/ dual(2, (self, f) => {
457
+ const step = self.step;
458
+ return described((state) => {
459
+ const start = state.pos;
460
+ const a = step(state);
461
+ if (a === Fail) return Fail;
462
+ const r = f(a);
463
+ if (Result.isSuccess(r)) return r.success;
464
+ state.pos = start;
465
+ return state.failCause("Backtrack", "Verify", r.failure);
466
+ }, self.name, {
467
+ _tag: "TryMap",
468
+ self,
469
+ f
470
+ });
447
471
  });
448
472
  /**
449
- * Like {@link tryMap}, but the function may throw. Thrown values become the
450
- * `cause` of the error.
451
- *
452
- * @since 0.1.0
453
- * @category mapping
454
- */
455
- export const tryMapUnknown = dual(2, (self, f) => tryMap(self, (a) => {
456
- try {
457
- return Result.succeed(f(a));
458
- }
459
- catch (e) {
460
- return Result.fail(e);
461
- }
473
+ * Like {@link tryMap}, but the function may throw. Thrown values become the
474
+ * `cause` of the error.
475
+ *
476
+ * @since 0.1.0
477
+ * @category mapping
478
+ */
479
+ const tryMapUnknown = /*#__PURE__*/ dual(2, (self, f) => tryMap(self, (a) => {
480
+ try {
481
+ return Result.succeed(f(a));
482
+ } catch (e) {
483
+ if (Stack.isStackOverflow(e)) throw e;
484
+ return Result.fail(e);
485
+ }
462
486
  }));
463
487
  /**
464
- * Transforms the output of a parser with a function returning an `Option`;
465
- * `None` makes the parser backtrack (winnow's `verify_map`, nom's
466
- * `map_opt`).
467
- *
468
- * @since 0.1.0
469
- * @category filtering
470
- */
471
- export const filterMap = dual(2, (self, f) => {
472
- const step = self.step;
473
- return described((state) => {
474
- const start = state.pos;
475
- const a = step(state);
476
- if (a === Fail)
477
- return Fail;
478
- const o = f(a);
479
- if (Option.isSome(o))
480
- return o.value;
481
- state.pos = start;
482
- return state.backtrack("Verify");
483
- }, self.name, { _tag: "FilterMap", self, f });
488
+ * Transforms the output of a parser with a function returning an `Option`;
489
+ * `None` makes the parser backtrack (winnow's `verify_map`, nom's
490
+ * `map_opt`).
491
+ *
492
+ * @since 0.1.0
493
+ * @category filtering
494
+ */
495
+ const filterMap = /*#__PURE__*/ dual(2, (self, f) => {
496
+ const step = self.step;
497
+ return described((state) => {
498
+ const start = state.pos;
499
+ const a = step(state);
500
+ if (a === Fail) return Fail;
501
+ const o = f(a);
502
+ if (Option.isSome(o)) return o.value;
503
+ state.pos = start;
504
+ return state.backtrack("Verify");
505
+ }, self.name, {
506
+ _tag: "FilterMap",
507
+ self,
508
+ f
509
+ });
484
510
  });
485
511
  /**
486
- * Keeps the output of a parser only if it satisfies a predicate, otherwise
487
- * backtracks (winnow's and nom's `verify`).
488
- *
489
- * @since 0.1.0
490
- * @category filtering
491
- */
492
- export const filter = dual(2, (self, predicate) => {
493
- const step = self.step;
494
- return described((state) => {
495
- const start = state.pos;
496
- const a = step(state);
497
- if (a === Fail)
498
- return Fail;
499
- if (predicate(a))
500
- return a;
501
- state.pos = start;
502
- return state.backtrack("Verify");
503
- }, self.name, { _tag: "Filter", self, predicate });
512
+ * Keeps the output of a parser only if it satisfies a predicate, otherwise
513
+ * backtracks (winnow's and nom's `verify`).
514
+ *
515
+ * @since 0.1.0
516
+ * @category filtering
517
+ */
518
+ const filter = /*#__PURE__*/ dual(2, (self, predicate) => {
519
+ const step = self.step;
520
+ return described((state) => {
521
+ const start = state.pos;
522
+ const a = step(state);
523
+ if (a === Fail) return Fail;
524
+ if (predicate(a)) return a;
525
+ state.pos = start;
526
+ return state.backtrack("Verify");
527
+ }, self.name, {
528
+ _tag: "Filter",
529
+ self,
530
+ predicate
531
+ });
504
532
  });
505
533
  /**
506
- * Keeps the output of a parser only if it satisfies a predicate, otherwise
507
- * backtracks with the given user error.
508
- *
509
- * @since 0.1.0
510
- * @category filtering
511
- */
512
- export const filterOrFail = dual(3, (self, predicate, orFailWith) => {
513
- const step = self.step;
514
- return described((state) => {
515
- const start = state.pos;
516
- const a = step(state);
517
- if (a === Fail)
518
- return Fail;
519
- if (predicate(a))
520
- return a;
521
- state.pos = start;
522
- return state.failCause("Backtrack", "Verify", orFailWith(a));
523
- }, self.name, { _tag: "Filter", self, predicate });
534
+ * Keeps the output of a parser only if it satisfies a predicate, otherwise
535
+ * backtracks with the given user error.
536
+ *
537
+ * @since 0.1.0
538
+ * @category filtering
539
+ */
540
+ const filterOrFail = /*#__PURE__*/ dual(3, (self, predicate, orFailWith) => {
541
+ const step = self.step;
542
+ return described((state) => {
543
+ const start = state.pos;
544
+ const a = step(state);
545
+ if (a === Fail) return Fail;
546
+ if (predicate(a)) return a;
547
+ state.pos = start;
548
+ return state.failCause("Backtrack", "Verify", orFailWith(a));
549
+ }, self.name, {
550
+ _tag: "Filter",
551
+ self,
552
+ predicate
553
+ });
524
554
  });
525
- // -----------------------------------------------------------------------------
526
- // Consumed input
527
- // -----------------------------------------------------------------------------
528
- /**
529
- * Returns the slice of input consumed by a parser instead of its output
530
- * (winnow's `take`, nom's `recognize`).
531
- *
532
- * @since 0.1.0
533
- * @category consumed input
534
- */
535
- export const taken = (self) => {
536
- const step = self.step;
537
- return described((state) => {
538
- const start = state.pos;
539
- return step(state) === Fail ? Fail : state.slice(start, state.pos);
540
- }, self.name, { _tag: "Taken", self, encoding: undefined });
555
+ /**
556
+ * Returns the slice of input consumed by a parser instead of its output
557
+ * (winnow's `take`, nom's `recognize`).
558
+ *
559
+ * @since 0.1.0
560
+ * @category consumed input
561
+ */
562
+ const taken = (self) => {
563
+ const step = self.step;
564
+ return described((state) => {
565
+ const start = state.pos;
566
+ return step(state) === Fail ? Fail : state.slice(start, state.pos);
567
+ }, self.name, {
568
+ _tag: "Taken",
569
+ self,
570
+ encoding: void 0
571
+ });
541
572
  };
542
573
  /**
543
- * Returns both the output of a parser and the slice of input it consumed
544
- * (winnow's `with_taken`; nom's `consumed` returns them in the other order).
545
- *
546
- * @since 0.1.0
547
- * @category consumed input
548
- */
549
- export const withTaken = (self) => {
550
- const step = self.step;
551
- return described((state) => {
552
- const start = state.pos;
553
- const a = step(state);
554
- return a === Fail ? Fail : [a, state.slice(start, state.pos)];
555
- }, self.name, { _tag: "WithTaken", self });
574
+ * Returns both the output of a parser and the slice of input it consumed
575
+ * (winnow's `with_taken`; nom's `consumed` returns them in the other order).
576
+ *
577
+ * @since 0.1.0
578
+ * @category consumed input
579
+ */
580
+ const withTaken = (self) => {
581
+ const step = self.step;
582
+ return described((state) => {
583
+ const start = state.pos;
584
+ const a = step(state);
585
+ return a === Fail ? Fail : [a, state.slice(start, state.pos)];
586
+ }, self.name, {
587
+ _tag: "WithTaken",
588
+ self
589
+ });
556
590
  };
557
591
  /**
558
- * Returns the span (`[start, end)` offsets) consumed by a parser instead of
559
- * its output (winnow's `span`).
560
- *
561
- * @since 0.1.0
562
- * @category consumed input
563
- */
564
- export const span = (self) => {
565
- const step = self.step;
566
- return described((state) => {
567
- const start = state.pos;
568
- return step(state) === Fail ? Fail : { start, end: state.pos };
569
- }, self.name, { _tag: "Span", self });
592
+ * Returns the span (`[start, end)` offsets) consumed by a parser instead of
593
+ * its output (winnow's `span`).
594
+ *
595
+ * @since 0.1.0
596
+ * @category consumed input
597
+ */
598
+ const span = (self) => {
599
+ const step = self.step;
600
+ return described((state) => {
601
+ const start = state.pos;
602
+ return step(state) === Fail ? Fail : {
603
+ start,
604
+ end: state.pos
605
+ };
606
+ }, self.name, {
607
+ _tag: "Span",
608
+ self
609
+ });
570
610
  };
571
611
  /**
572
- * Returns both the output of a parser and the span it consumed (winnow's
573
- * `with_span`).
574
- *
575
- * @since 0.1.0
576
- * @category consumed input
577
- */
578
- export const withSpan = (self) => {
579
- const step = self.step;
580
- return described((state) => {
581
- const start = state.pos;
582
- const a = step(state);
583
- return a === Fail ? Fail : [a, { start, end: state.pos }];
584
- }, self.name, { _tag: "WithSpan", self });
612
+ * Returns both the output of a parser and the span it consumed (winnow's
613
+ * `with_span`).
614
+ *
615
+ * @since 0.1.0
616
+ * @category consumed input
617
+ */
618
+ const withSpan = (self) => {
619
+ const step = self.step;
620
+ return described((state) => {
621
+ const start = state.pos;
622
+ const a = step(state);
623
+ return a === Fail ? Fail : [a, {
624
+ start,
625
+ end: state.pos
626
+ }];
627
+ }, self.name, {
628
+ _tag: "WithSpan",
629
+ self
630
+ });
585
631
  };
586
- // -----------------------------------------------------------------------------
587
- // Error management
588
- // -----------------------------------------------------------------------------
589
632
  const changeMode = (self, from, to) => {
590
- const step = self.step;
591
- return described((state) => {
592
- const a = step(state);
593
- if (a === Fail && state.mode === from)
594
- state.mode = to;
595
- return a;
596
- }, self.name, { _tag: "Mode", self, from, to });
633
+ const step = self.step;
634
+ return described((state) => {
635
+ const a = step(state);
636
+ if (a === Fail && state.mode === from) state.mode = to;
637
+ return a;
638
+ }, self.name, {
639
+ _tag: "Mode",
640
+ self,
641
+ from,
642
+ to
643
+ });
644
+ };
645
+ /**
646
+ * Commits to the current branch: recoverable failures of the parser become
647
+ * unrecoverable, so enclosing `alt`, `opt`, `repeat`, ... do not try
648
+ * alternatives (winnow's `cut_err`, nom's `cut`).
649
+ *
650
+ * @example
651
+ * ```ts
652
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
653
+ *
654
+ * // Once we have seen "-", a number must follow
655
+ * const negative = Parser.preceded(Token.literal("-"), Parser.cut(Ascii.decUint))
656
+ * ```
657
+ *
658
+ * @since 0.1.0
659
+ * @category error management
660
+ */
661
+ const cut = (self) => {
662
+ const step = self.step;
663
+ return described((state) => {
664
+ const deepest = state.deepest;
665
+ state.deepest = void 0;
666
+ const a = step(state);
667
+ if (a === Fail && state.mode !== "Incomplete") {
668
+ state.mode = "Cut";
669
+ return a;
670
+ }
671
+ const inner = state.deepest;
672
+ if (deepest !== void 0 && (inner === void 0 || deepest.offset > inner.offset)) state.deepest = deepest;
673
+ return a;
674
+ }, self.name, {
675
+ _tag: "Mode",
676
+ self,
677
+ from: "Backtrack",
678
+ to: "Cut"
679
+ });
597
680
  };
598
681
  /**
599
- * Commits to the current branch: recoverable failures of the parser become
600
- * unrecoverable, so enclosing `alt`, `opt`, `repeat`, ... do not try
601
- * alternatives (winnow's `cut_err`, nom's `cut`).
602
- *
603
- * @example
604
- * ```ts
605
- * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
606
- *
607
- * // Once we have seen "-", a number must follow
608
- * const negative = Parser.preceded(Token.literal("-"), Parser.cut(Ascii.decUint))
609
- * ```
610
- *
611
- * @since 0.1.0
612
- * @category error management
613
- */
614
- export const cut = (self) => changeMode(self, "Backtrack", "Cut");
615
- /**
616
- * Turns unrecoverable failures back into recoverable ones (winnow's
617
- * `backtrack_err`).
618
- *
619
- * @since 0.1.0
620
- * @category error management
621
- */
622
- export const backtrack = (self) => changeMode(self, "Cut", "Backtrack");
623
- /**
624
- * Turns `Incomplete` failures into recoverable ones, treating the input as
625
- * complete for this parser (winnow's `complete_err`, nom's `complete`).
626
- *
627
- * @since 0.1.0
628
- * @category error management
629
- */
630
- export const complete = (self) => {
631
- const step = self.step;
632
- return described((state) => {
633
- const start = state.pos;
634
- const a = step(state);
635
- if (a === Fail && state.mode === "Incomplete") {
636
- state.pos = start;
637
- return state.backtrack("Complete");
638
- }
639
- return a;
640
- }, self.name, { _tag: "Mode", self, from: "Incomplete", to: "Backtrack" });
682
+ * Turns unrecoverable failures back into recoverable ones (winnow's
683
+ * `backtrack_err`).
684
+ *
685
+ * @since 0.1.0
686
+ * @category error management
687
+ */
688
+ const backtrack = (self) => changeMode(self, "Cut", "Backtrack");
689
+ /**
690
+ * Turns `Incomplete` failures into recoverable ones, treating the input as
691
+ * complete for this parser (winnow's `complete_err`, nom's `complete`).
692
+ *
693
+ * @since 0.1.0
694
+ * @category error management
695
+ */
696
+ const complete = (self) => {
697
+ const step = self.step;
698
+ return described((state) => {
699
+ const start = state.pos;
700
+ const a = step(state);
701
+ if (a === Fail && state.mode === "Incomplete") {
702
+ state.pos = start;
703
+ return state.backtrack("Complete");
704
+ }
705
+ return a;
706
+ }, self.name, {
707
+ _tag: "Mode",
708
+ self,
709
+ from: "Incomplete",
710
+ to: "Backtrack"
711
+ });
641
712
  };
642
713
  /**
643
- * Adds a context label to failures of a parser (winnow's
644
- * `context(StrContext::Label(..))`, nom's `context`). Labels are rendered as
645
- * "while parsing <label>" in the error message.
646
- *
647
- * @since 0.1.0
648
- * @category error management
649
- */
650
- export const context = dual(2, (self, label) => {
651
- const step = self.step;
652
- return described((state) => {
653
- const a = step(state);
654
- if (a === Fail && state.mode !== "Incomplete") {
655
- const f = state.failure;
656
- state.replaceFailure(new Failure(f.offset, f.kind, f.expected, [...f.context, label], f.hasCause, f.cause, f.message, f.needed));
657
- }
658
- return a;
659
- }, self.name ?? label, { _tag: "Context", self, label });
714
+ * Adds a context label to failures of a parser (winnow's
715
+ * `context(StrContext::Label(..))`, nom's `context`). Labels are rendered as
716
+ * "while parsing <label>" in the error message.
717
+ *
718
+ * @since 0.1.0
719
+ * @category error management
720
+ */
721
+ const context = /*#__PURE__*/ dual(2, (self, label) => {
722
+ const step = self.step;
723
+ return described((state) => {
724
+ const a = step(state);
725
+ if (a === Fail && state.mode !== "Incomplete") {
726
+ const f = state.failure;
727
+ state.replaceFailure(new Failure(f.offset, f.kind, f.expected, [...f.context, label], f.hasCause, f.cause, f.message, f.needed));
728
+ }
729
+ return a;
730
+ }, self.name ?? label, {
731
+ _tag: "Context",
732
+ self,
733
+ label
734
+ });
660
735
  });
661
736
  /**
662
- * Names what a parser expects. When the parser fails without consuming any
663
- * input, its expectations are replaced with `description`, so a failure is
664
- * reported as `expected <description>` rather than in terms of the parser's
665
- * internals (Parsec's `<?>`, winnow's `StrContext::Expected`).
666
- *
667
- * @since 0.1.0
668
- * @category error management
669
- */
670
- export const expected = dual(2, (self, description) => {
671
- const step = self.step;
672
- const desc = [description];
673
- return described((state) => {
674
- const start = state.pos;
675
- const hintOffset = state.hintOffset;
676
- const hintExpected = state.hintExpected;
677
- const a = step(state);
678
- if (a === Fail && state.failure.offset === start) {
679
- const f = state.failure;
680
- const expected = hintOffset === start ? mergeExpected(hintExpected, desc) : desc;
681
- state.replaceFailure(new Failure(f.offset, f.kind, expected, f.context, f.hasCause, f.cause, f.message, f.needed));
682
- }
683
- return a;
684
- }, description, { _tag: "Expected", self, description });
737
+ * Names what a parser expects. When the parser fails without consuming any
738
+ * input, its expectations are replaced with `description`, so a failure is
739
+ * reported as `expected <description>` rather than in terms of the parser's
740
+ * internals (Parsec's `<?>`, winnow's `StrContext::Expected`).
741
+ *
742
+ * @since 0.1.0
743
+ * @category error management
744
+ */
745
+ const expected = /*#__PURE__*/ dual(2, (self, description) => {
746
+ const step = self.step;
747
+ const desc = [description];
748
+ return described((state) => {
749
+ const start = state.pos;
750
+ const hintOffset = state.hintOffset;
751
+ const hintExpected = state.hintExpected;
752
+ const a = step(state);
753
+ if (a === Fail && state.failure.offset === start) {
754
+ const f = state.failure;
755
+ const expected = hintOffset === start ? mergeExpected(hintExpected, desc) : desc;
756
+ state.replaceFailure(new Failure(f.offset, f.kind, expected, f.context, f.hasCause, f.cause, f.message, f.needed));
757
+ }
758
+ return a;
759
+ }, description, {
760
+ _tag: "Expected",
761
+ self,
762
+ description
763
+ });
685
764
  });
686
765
  /**
687
- * Transforms the user error (cause) of a parser's failures.
688
- *
689
- * @since 0.1.0
690
- * @category error management
691
- */
692
- export const mapError = dual(2, (self, f) => {
693
- const step = self.step;
694
- return described((state) => {
695
- const a = step(state);
696
- if (a === Fail && state.failure.hasCause) {
697
- const x = state.failure;
698
- state.replaceFailure(new Failure(x.offset, x.kind, x.expected, x.context, true, f(x.cause), x.message, x.needed));
699
- }
700
- return a;
701
- }, self.name, { _tag: "MapError", self });
766
+ * Transforms the user error (cause) of a parser's failures.
767
+ *
768
+ * @since 0.1.0
769
+ * @category error management
770
+ */
771
+ const mapError = /*#__PURE__*/ dual(2, (self, f) => {
772
+ const step = self.step;
773
+ return described((state) => {
774
+ const a = step(state);
775
+ if (a === Fail && state.failure.hasCause) {
776
+ const x = state.failure;
777
+ state.replaceFailure(new Failure(x.offset, x.kind, x.expected, x.context, true, f(x.cause), x.message, x.needed));
778
+ }
779
+ return a;
780
+ }, self.name, {
781
+ _tag: "MapError",
782
+ self
783
+ });
702
784
  });
703
785
  /**
704
- * Replaces any failure of a parser with the given user error, keeping the
705
- * failure's position and mode.
706
- *
707
- * @since 0.1.0
708
- * @category error management
709
- */
710
- export const orFail = dual(2, (self, error) => {
711
- const step = self.step;
712
- return described((state) => {
713
- const start = state.pos;
714
- const a = step(state);
715
- if (a === Fail && state.mode !== "Incomplete") {
716
- const x = state.failure;
717
- state.replaceFailure(new Failure(start, x.kind, [], x.context, true, error(), undefined, 0));
718
- }
719
- return a;
720
- }, self.name, { _tag: "OrFail", self });
786
+ * Replaces any failure of a parser with the given user error, keeping the
787
+ * failure's position and mode.
788
+ *
789
+ * @since 0.1.0
790
+ * @category error management
791
+ */
792
+ const orFail = /*#__PURE__*/ dual(2, (self, error) => {
793
+ const step = self.step;
794
+ return described((state) => {
795
+ const start = state.pos;
796
+ const a = step(state);
797
+ if (a === Fail && state.mode !== "Incomplete") {
798
+ const x = state.failure;
799
+ state.replaceFailure(new Failure(start, x.kind, [], x.context, true, error(), void 0, 0));
800
+ }
801
+ return a;
802
+ }, self.name, {
803
+ _tag: "OrFail",
804
+ self
805
+ });
721
806
  });
722
807
  /**
723
- * If the parser fails recoverably, tries `that` from the same position.
724
- * `p.pipe(Parser.orElse(() => q))` is equivalent to `Parser.alt(p, q)`.
725
- *
726
- * @since 0.1.0
727
- * @category error management
728
- */
729
- export const orElse = dual(2, (self, that) => alt(self, suspend(that)));
730
- /**
731
- * If the parser fails recoverably, succeeds with the given value without
732
- * consuming input.
733
- *
734
- * @since 0.1.0
735
- * @category error management
736
- */
737
- export const orElseSucceed = dual(2, (self, value) => {
738
- const step = self.step;
739
- return described((state) => {
740
- const start = state.pos;
741
- const a = step(state);
742
- if (a !== Fail)
743
- return a;
744
- if (state.mode !== "Backtrack")
745
- return Fail;
746
- recover(state, start);
747
- return value();
748
- }, self.name, { _tag: "OrElseSucceed", self, value });
808
+ * If the parser fails recoverably, tries `that` from the same position.
809
+ * `p.pipe(Parser.orElse(() => q))` is equivalent to `Parser.alt(p, q)`.
810
+ *
811
+ * @since 0.1.0
812
+ * @category error management
813
+ */
814
+ const orElse = /*#__PURE__*/ dual(2, (self, that) => alt(self, suspend(that)));
815
+ /**
816
+ * If the parser fails recoverably, succeeds with the given value without
817
+ * consuming input.
818
+ *
819
+ * @since 0.1.0
820
+ * @category error management
821
+ */
822
+ const orElseSucceed = /*#__PURE__*/ dual(2, (self, value) => {
823
+ const step = self.step;
824
+ return described((state) => {
825
+ const start = state.pos;
826
+ const a = step(state);
827
+ if (a !== Fail) return a;
828
+ if (state.mode !== "Backtrack") return Fail;
829
+ recover(state, start);
830
+ return value();
831
+ }, self.name, {
832
+ _tag: "OrElseSucceed",
833
+ self,
834
+ value
835
+ });
749
836
  });
750
837
  /** Rewinds after a recoverable failure, remembering it as a hint. */
751
838
  const recover = (state, start) => {
752
- state.pos = start;
753
- const f = state.failure;
754
- if (f.offset === start) {
755
- state.hintOffset = start;
756
- state.hintExpected = f.expected;
757
- }
839
+ state.pos = start;
840
+ const f = state.failure;
841
+ if (f.offset === start) {
842
+ state.hintOffset = start;
843
+ state.hintExpected = f.expected;
844
+ }
758
845
  };
759
- // -----------------------------------------------------------------------------
760
- // Control
761
- // -----------------------------------------------------------------------------
762
- /**
763
- * Makes a parser optional: returns `Option.none()` without consuming input if
764
- * it fails recoverably (winnow's and nom's `opt`).
765
- *
766
- * @since 0.1.0
767
- * @category control
768
- */
769
- export const opt = (self) => {
770
- const step = self.step;
771
- return described((state) => {
772
- const start = state.pos;
773
- const a = step(state);
774
- if (a !== Fail)
775
- return Option.some(a);
776
- if (state.mode !== "Backtrack")
777
- return Fail;
778
- recover(state, start);
779
- return Option.none();
780
- }, self.name, { _tag: "Opt", self });
846
+ /**
847
+ * Makes a parser optional: returns `Option.none()` without consuming input if
848
+ * it fails recoverably (winnow's and nom's `opt`).
849
+ *
850
+ * @since 0.1.0
851
+ * @category control
852
+ */
853
+ const opt = (self) => {
854
+ const step = self.step;
855
+ return described((state) => {
856
+ const start = state.pos;
857
+ const a = step(state);
858
+ if (a !== Fail) return Option.some(a);
859
+ if (state.mode !== "Backtrack") return Fail;
860
+ recover(state, start);
861
+ return Option.none();
862
+ }, self.name, {
863
+ _tag: "Opt",
864
+ self
865
+ });
781
866
  };
782
867
  /**
783
- * Like {@link opt}, but returns `undefined` instead of `None`.
784
- *
785
- * @since 0.1.0
786
- * @category control
787
- */
788
- export const optional = (self) => orElseSucceed(self, () => undefined);
789
- /**
790
- * Runs the parser only if `condition` holds, otherwise succeeds with
791
- * `Option.none()` (winnow's and nom's `cond`).
792
- *
793
- * @since 0.1.0
794
- * @category control
795
- */
796
- export const cond = (condition, self) => condition ? asSome(self) : succeed(Option.none());
797
- /**
798
- * Runs a parser without consuming input (winnow's and nom's `peek`).
799
- *
800
- * @since 0.1.0
801
- * @category control
802
- */
803
- export const peek = (self) => {
804
- const step = self.step;
805
- return described((state) => {
806
- const start = state.pos;
807
- const hintOffset = state.hintOffset;
808
- const hintExpected = state.hintExpected;
809
- const a = step(state);
810
- if (a !== Fail) {
811
- state.pos = start;
812
- state.hintOffset = hintOffset;
813
- state.hintExpected = hintExpected;
814
- }
815
- return a;
816
- }, self.name, { _tag: "Peek", self });
868
+ * Like {@link opt}, but returns `undefined` instead of `None`.
869
+ *
870
+ * @since 0.1.0
871
+ * @category control
872
+ */
873
+ const optional = (self) => orElseSucceed(self, () => void 0);
874
+ /**
875
+ * Runs the parser only if `condition` holds, otherwise succeeds with
876
+ * `Option.none()` (winnow's and nom's `cond`).
877
+ *
878
+ * @since 0.1.0
879
+ * @category control
880
+ */
881
+ const cond = (condition, self) => condition ? asSome(self) : succeed(Option.none());
882
+ /**
883
+ * Runs a parser without consuming input (winnow's and nom's `peek`).
884
+ *
885
+ * @since 0.1.0
886
+ * @category control
887
+ */
888
+ const peek = (self) => {
889
+ const step = self.step;
890
+ return described((state) => {
891
+ const start = state.pos;
892
+ const hintOffset = state.hintOffset;
893
+ const hintExpected = state.hintExpected;
894
+ const deepest = state.deepest;
895
+ const a = step(state);
896
+ if (a !== Fail) {
897
+ state.pos = start;
898
+ state.hintOffset = hintOffset;
899
+ state.hintExpected = hintExpected;
900
+ state.deepest = deepest;
901
+ }
902
+ return a;
903
+ }, self.name, {
904
+ _tag: "Peek",
905
+ self
906
+ });
817
907
  };
818
908
  /**
819
- * Succeeds, without consuming input, only if the parser fails recoverably
820
- * (winnow's and nom's `not`).
821
- *
822
- * @since 0.1.0
823
- * @category control
824
- */
825
- export const not = (self) => {
826
- const step = self.step;
827
- return described((state) => {
828
- const start = state.pos;
829
- const hintOffset = state.hintOffset;
830
- const hintExpected = state.hintExpected;
831
- const a = step(state);
832
- if (a !== Fail) {
833
- state.pos = start;
834
- return state.backtrack("Not");
835
- }
836
- if (state.mode !== "Backtrack")
837
- return Fail;
838
- state.pos = start;
839
- state.hintOffset = hintOffset;
840
- state.hintExpected = hintExpected;
841
- return undefined;
842
- }, self.name, { _tag: "Not", self });
909
+ * Succeeds, without consuming input, only if the parser fails recoverably
910
+ * (winnow's and nom's `not`).
911
+ *
912
+ * @since 0.1.0
913
+ * @category control
914
+ */
915
+ const not = (self) => {
916
+ const step = self.step;
917
+ return described((state) => {
918
+ const start = state.pos;
919
+ const hintOffset = state.hintOffset;
920
+ const hintExpected = state.hintExpected;
921
+ const deepest = state.deepest;
922
+ if (step(state) !== Fail) {
923
+ state.pos = start;
924
+ state.deepest = deepest;
925
+ return state.backtrack("Not");
926
+ }
927
+ if (state.mode !== "Backtrack") return Fail;
928
+ state.pos = start;
929
+ state.hintOffset = hintOffset;
930
+ state.hintExpected = hintExpected;
931
+ state.deepest = deepest;
932
+ }, self.name, {
933
+ _tag: "Not",
934
+ self
935
+ });
843
936
  };
844
937
  /**
845
- * Requires the parser to consume the whole input (nom's `all_consuming`).
846
- *
847
- * @since 0.1.0
848
- * @category control
849
- */
850
- export const allConsuming = (self) => terminated(self, eof);
851
- // -----------------------------------------------------------------------------
852
- // Sequencing
853
- // -----------------------------------------------------------------------------
854
- /**
855
- * Runs parsers in sequence, collecting their outputs in a tuple (winnow's
856
- * and nom's tuple parsers, winnow's `seq!`).
857
- *
858
- * @since 0.1.0
859
- * @category sequencing
860
- */
861
- export const seq = (...parsers) => {
862
- const steps = parsers.map((p) => p.step);
863
- const n = steps.length;
864
- return described((state) => {
865
- const out = new Array(n);
866
- for (let i = 0; i < n; i++) {
867
- const a = steps[i](state);
868
- if (a === Fail)
869
- return Fail;
870
- out[i] = a;
871
- }
872
- return out;
873
- }, "seq", { _tag: "Seq", parsers, output: "tuple" });
938
+ * Requires the parser to consume the whole input (nom's `all_consuming`).
939
+ *
940
+ * @since 0.1.0
941
+ * @category control
942
+ */
943
+ const allConsuming = (self) => terminated(self, eof);
944
+ /**
945
+ * Runs parsers in sequence, collecting their outputs in a tuple (winnow's
946
+ * and nom's tuple parsers, winnow's `seq!`).
947
+ *
948
+ * @since 0.1.0
949
+ * @category sequencing
950
+ */
951
+ const seq = (...parsers) => {
952
+ const steps = parsers.map((p) => p.step);
953
+ const n = steps.length;
954
+ return described((state) => {
955
+ const out = new Array(n);
956
+ for (let i = 0; i < n; i++) {
957
+ const a = steps[i](state);
958
+ if (a === Fail) return Fail;
959
+ out[i] = a;
960
+ }
961
+ return out;
962
+ }, "seq", {
963
+ _tag: "Seq",
964
+ parsers,
965
+ output: "tuple"
966
+ });
874
967
  };
875
968
  /**
876
- * Runs a record of parsers in sequence (in key order), collecting their
877
- * outputs in a record. This is the Effect counterpart to winnow's
878
- * `seq!(Struct { .. })`.
879
- *
880
- * @since 0.1.0
881
- * @category sequencing
882
- */
883
- export const struct = (fields) => {
884
- const keys = Object.keys(fields);
885
- const steps = keys.map((k) => fields[k].step);
886
- const n = keys.length;
887
- return described((state) => {
888
- const out = {};
889
- for (let i = 0; i < n; i++) {
890
- const a = steps[i](state);
891
- if (a === Fail)
892
- return Fail;
893
- out[keys[i]] = a;
894
- }
895
- return out;
896
- }, "struct", { _tag: "Struct", keys, parsers: keys.map((k) => fields[k]) });
969
+ * Runs a record of parsers in sequence (in key order), collecting their
970
+ * outputs in a record. This is the Effect counterpart to winnow's
971
+ * `seq!(Struct { .. })`.
972
+ *
973
+ * @since 0.1.0
974
+ * @category sequencing
975
+ */
976
+ const struct = (fields) => {
977
+ const keys = Object.keys(fields);
978
+ const steps = keys.map((k) => fields[k].step);
979
+ const n = keys.length;
980
+ return described((state) => {
981
+ const out = {};
982
+ for (let i = 0; i < n; i++) {
983
+ const a = steps[i](state);
984
+ if (a === Fail) return Fail;
985
+ out[keys[i]] = a;
986
+ }
987
+ return out;
988
+ }, "struct", {
989
+ _tag: "Struct",
990
+ keys,
991
+ parsers: keys.map((k) => fields[k])
992
+ });
897
993
  };
898
994
  /**
899
- * Runs two parsers in sequence and returns both outputs.
900
- *
901
- * @since 0.1.0
902
- * @category sequencing
903
- */
904
- export const zip = dual(2, (self, that) => zipWith(self, that, (a, b) => [a, b]));
905
- /**
906
- * Runs two parsers in sequence and combines their outputs.
907
- *
908
- * @since 0.1.0
909
- * @category sequencing
910
- */
911
- export const zipWith = dual(3, (self, that, f) => {
912
- const s1 = self.step;
913
- const s2 = that.step;
914
- return described((state) => {
915
- const a = s1(state);
916
- if (a === Fail)
917
- return Fail;
918
- const b = s2(state);
919
- if (b === Fail)
920
- return Fail;
921
- return f(a, b);
922
- }, undefined, { _tag: "ZipWith", self, that, f });
995
+ * Runs two parsers in sequence and returns both outputs.
996
+ *
997
+ * @since 0.1.0
998
+ * @category sequencing
999
+ */
1000
+ const zip = /*#__PURE__*/ dual(2, (self, that) => zipWith(self, that, (a, b) => [a, b]));
1001
+ /**
1002
+ * Runs two parsers in sequence and combines their outputs.
1003
+ *
1004
+ * @since 0.1.0
1005
+ * @category sequencing
1006
+ */
1007
+ const zipWith = /*#__PURE__*/ dual(3, (self, that, f) => {
1008
+ const s1 = self.step;
1009
+ const s2 = that.step;
1010
+ return described((state) => {
1011
+ const a = s1(state);
1012
+ if (a === Fail) return Fail;
1013
+ const b = s2(state);
1014
+ if (b === Fail) return Fail;
1015
+ return f(a, b);
1016
+ }, void 0, {
1017
+ _tag: "ZipWith",
1018
+ self,
1019
+ that,
1020
+ f
1021
+ });
923
1022
  });
924
1023
  /**
925
- * Runs two parsers in sequence, keeping the output of the first.
926
- *
927
- * @since 0.1.0
928
- * @category sequencing
929
- */
930
- export const zipLeft = dual(2, (self, that) => terminated(self, that));
931
- /**
932
- * Runs two parsers in sequence, keeping the output of the second.
933
- *
934
- * @since 0.1.0
935
- * @category sequencing
936
- */
937
- export const zipRight = dual(2, (self, that) => preceded(self, that));
938
- /**
939
- * Runs `prefix` then `parser`, keeping the output of `parser` (winnow's and
940
- * nom's `preceded`).
941
- *
942
- * @since 0.1.0
943
- * @category sequencing
944
- */
945
- export const preceded = (prefix, parser) => {
946
- const s1 = prefix.step;
947
- const s2 = parser.step;
948
- return described((state) => s1(state) === Fail ? Fail : s2(state), parser.name, {
949
- _tag: "Seq",
950
- parsers: [prefix, parser],
951
- output: 1
952
- });
1024
+ * Runs two parsers in sequence, keeping the output of the first.
1025
+ *
1026
+ * @since 0.1.0
1027
+ * @category sequencing
1028
+ */
1029
+ const zipLeft = /*#__PURE__*/ dual(2, (self, that) => terminated(self, that));
1030
+ /**
1031
+ * Runs two parsers in sequence, keeping the output of the second.
1032
+ *
1033
+ * @since 0.1.0
1034
+ * @category sequencing
1035
+ */
1036
+ const zipRight = /*#__PURE__*/ dual(2, (self, that) => preceded(self, that));
1037
+ /**
1038
+ * Runs `prefix` then `parser`, keeping the output of `parser` (winnow's and
1039
+ * nom's `preceded`).
1040
+ *
1041
+ * @since 0.1.0
1042
+ * @category sequencing
1043
+ */
1044
+ const preceded = (prefix, parser) => {
1045
+ const s1 = prefix.step;
1046
+ const s2 = parser.step;
1047
+ return described((state) => s1(state) === Fail ? Fail : s2(state), parser.name, {
1048
+ _tag: "Seq",
1049
+ parsers: [prefix, parser],
1050
+ output: 1
1051
+ });
953
1052
  };
954
1053
  /**
955
- * Runs `parser` then `suffix`, keeping the output of `parser` (winnow's and
956
- * nom's `terminated`).
957
- *
958
- * @since 0.1.0
959
- * @category sequencing
960
- */
961
- export const terminated = (parser, suffix) => {
962
- const s1 = parser.step;
963
- const s2 = suffix.step;
964
- return described((state) => {
965
- const a = s1(state);
966
- if (a === Fail)
967
- return Fail;
968
- return s2(state) === Fail ? Fail : a;
969
- }, parser.name, { _tag: "Seq", parsers: [parser, suffix], output: 0 });
1054
+ * Runs `parser` then `suffix`, keeping the output of `parser` (winnow's and
1055
+ * nom's `terminated`).
1056
+ *
1057
+ * @since 0.1.0
1058
+ * @category sequencing
1059
+ */
1060
+ const terminated = (parser, suffix) => {
1061
+ const s1 = parser.step;
1062
+ const s2 = suffix.step;
1063
+ return described((state) => {
1064
+ const a = s1(state);
1065
+ if (a === Fail) return Fail;
1066
+ return s2(state) === Fail ? Fail : a;
1067
+ }, parser.name, {
1068
+ _tag: "Seq",
1069
+ parsers: [parser, suffix],
1070
+ output: 0
1071
+ });
970
1072
  };
971
1073
  /**
972
- * Runs `open`, `parser` and `close`, keeping the output of `parser`
973
- * (winnow's and nom's `delimited`).
974
- *
975
- * @since 0.1.0
976
- * @category sequencing
977
- */
978
- export const delimited = (open, parser, close) => {
979
- const s1 = open.step;
980
- const s2 = parser.step;
981
- const s3 = close.step;
982
- return described((state) => {
983
- if (s1(state) === Fail)
984
- return Fail;
985
- const a = s2(state);
986
- if (a === Fail)
987
- return Fail;
988
- return s3(state) === Fail ? Fail : a;
989
- }, parser.name, { _tag: "Seq", parsers: [open, parser, close], output: 1 });
1074
+ * Runs `open`, `parser` and `close`, keeping the output of `parser`
1075
+ * (winnow's and nom's `delimited`).
1076
+ *
1077
+ * @since 0.1.0
1078
+ * @category sequencing
1079
+ */
1080
+ const delimited = (open, parser, close) => {
1081
+ const s1 = open.step;
1082
+ const s2 = parser.step;
1083
+ const s3 = close.step;
1084
+ return described((state) => {
1085
+ if (s1(state) === Fail) return Fail;
1086
+ const a = s2(state);
1087
+ if (a === Fail) return Fail;
1088
+ return s3(state) === Fail ? Fail : a;
1089
+ }, parser.name, {
1090
+ _tag: "Seq",
1091
+ parsers: [
1092
+ open,
1093
+ parser,
1094
+ close
1095
+ ],
1096
+ output: 1
1097
+ });
990
1098
  };
991
1099
  /**
992
- * Runs `first`, `separator` and `second`, keeping the outputs of `first` and
993
- * `second` (winnow's and nom's `separated_pair`).
994
- *
995
- * @since 0.1.0
996
- * @category sequencing
997
- */
998
- export const separatedPair = (first, separator, second) => {
999
- const s1 = first.step;
1000
- const s2 = separator.step;
1001
- const s3 = second.step;
1002
- return described((state) => {
1003
- const a = s1(state);
1004
- if (a === Fail)
1005
- return Fail;
1006
- if (s2(state) === Fail)
1007
- return Fail;
1008
- const b = s3(state);
1009
- return b === Fail ? Fail : [a, b];
1010
- }, undefined, { _tag: "Seq", parsers: [first, separator, second], output: [0, 2] });
1100
+ * Runs `first`, `separator` and `second`, keeping the outputs of `first` and
1101
+ * `second` (winnow's and nom's `separated_pair`).
1102
+ *
1103
+ * @since 0.1.0
1104
+ * @category sequencing
1105
+ */
1106
+ const separatedPair = (first, separator, second) => {
1107
+ const s1 = first.step;
1108
+ const s2 = separator.step;
1109
+ const s3 = second.step;
1110
+ return described((state) => {
1111
+ const a = s1(state);
1112
+ if (a === Fail) return Fail;
1113
+ if (s2(state) === Fail) return Fail;
1114
+ const b = s3(state);
1115
+ return b === Fail ? Fail : [a, b];
1116
+ }, void 0, {
1117
+ _tag: "Seq",
1118
+ parsers: [
1119
+ first,
1120
+ separator,
1121
+ second
1122
+ ],
1123
+ output: [0, 2]
1124
+ });
1011
1125
  };
1012
1126
  /**
1013
- * Writes a parser in imperative style with a generator, Effect style:
1014
- * `yield*` a parser to run it and get its output.
1015
- *
1016
- * @example
1017
- * ```ts
1018
- * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1019
- *
1020
- * const keyValue = Parser.gen(function*() {
1021
- * const key = yield* Ascii.alpha1
1022
- * yield* Token.literal("=")
1023
- * const value = yield* Ascii.decInt
1024
- * return [key, value] as const
1025
- * })
1026
- * ```
1027
- *
1028
- * @since 0.1.0
1029
- * @category sequencing
1030
- */
1031
- export const gen = (f) => make((state) => {
1032
- const iterator = f();
1033
- let next = iterator.next();
1034
- while (!next.done) {
1035
- const a = next.value.step(state);
1036
- if (a === Fail) {
1037
- iterator.return?.(undefined);
1038
- return Fail;
1039
- }
1040
- next = iterator.next(a);
1041
- }
1042
- return next.value;
1127
+ * Writes a parser in imperative style with a generator, Effect style:
1128
+ * `yield*` a parser to run it and get its output.
1129
+ *
1130
+ * @example
1131
+ * ```ts
1132
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1133
+ *
1134
+ * const keyValue = Parser.gen(function*() {
1135
+ * const key = yield* Ascii.alpha1
1136
+ * yield* Token.literal("=")
1137
+ * const value = yield* Ascii.decInt
1138
+ * return [key, value] as const
1139
+ * })
1140
+ * ```
1141
+ *
1142
+ * The body runs on every parse, so parsers created inside it, like
1143
+ * `Token.literal("=")` above, are rebuilt on every run. On hot paths, build
1144
+ * them once outside the body:
1145
+ *
1146
+ * ```ts
1147
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1148
+ *
1149
+ * const equals = Token.literal("=")
1150
+ *
1151
+ * const keyValue = Parser.gen(function*() {
1152
+ * const key = yield* Ascii.alpha1
1153
+ * yield* equals
1154
+ * const value = yield* Ascii.decInt
1155
+ * return [key, value] as const
1156
+ * })
1157
+ * ```
1158
+ *
1159
+ * @since 0.1.0
1160
+ * @category sequencing
1161
+ */
1162
+ const gen = (f) => make((state) => {
1163
+ const iterator = f();
1164
+ let next = iterator.next();
1165
+ while (!next.done) {
1166
+ const a = next.value.step(state);
1167
+ if (a === Fail) {
1168
+ iterator.return?.(void 0);
1169
+ return Fail;
1170
+ }
1171
+ next = iterator.next(a);
1172
+ }
1173
+ return next.value;
1043
1174
  }, "gen");
1044
1175
  /**
1045
- * The starting point for Do notation.
1046
- *
1047
- * @example
1048
- * ```ts
1049
- * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1050
- *
1051
- * const range = Parser.Do.pipe(
1052
- * Parser.bind("start", () => Ascii.decUint),
1053
- * Parser.zipLeft(Token.literal("..")),
1054
- * Parser.bind("end", () => Ascii.decUint)
1055
- * )
1056
- * ```
1057
- *
1058
- * @since 0.1.0
1059
- * @category do notation
1060
- */
1061
- export const Do = succeed({});
1062
- /**
1063
- * Binds the output of a parser to a field of the Do record.
1064
- *
1065
- * @since 0.1.0
1066
- * @category do notation
1067
- */
1068
- export const bind = dual(3, (self, name, f) => flatMap(self, (a) => map(f(a), (b) => ({ ...a, [name]: b }))));
1069
- /**
1070
- * Wraps the output of a parser in a record with a single field.
1071
- *
1072
- * @since 0.1.0
1073
- * @category do notation
1074
- */
1075
- export const bindTo = dual(2, (self, name) => map(self, (a) => ({ [name]: a })));
1076
- const let_ = dual(3, (self, name, f) => map(self, (a) => ({ ...a, [name]: f(a) })));
1077
- export {
1078
- /**
1079
- * Adds a computed field to the Do record.
1080
- *
1081
- * @since 0.1.0
1082
- * @category do notation
1083
- */
1084
- let_ as let };
1085
- // -----------------------------------------------------------------------------
1086
- // Choice
1087
- // -----------------------------------------------------------------------------
1088
- /**
1089
- * Tries each parser in turn and returns the output of the first that
1090
- * succeeds (winnow's and nom's `alt`).
1091
- *
1092
- * Unrecoverable (`Cut`) and `Incomplete` failures stop the search
1093
- * immediately. If every alternative fails recoverably, the failures are
1094
- * merged: the one that got furthest wins, and failures at the same offset
1095
- * combine their expectations, e.g. `expected "true", "false" or "null"`.
1096
- *
1097
- * @since 0.1.0
1098
- * @category choice
1099
- */
1100
- export const alt = (...parsers) => {
1101
- const steps = parsers.map((p) => p.step);
1102
- const n = steps.length;
1103
- if (n === 0)
1104
- return reject;
1105
- if (n === 1)
1106
- return parsers[0];
1107
- return described((state) => {
1108
- const start = state.pos;
1109
- let failure;
1110
- for (let i = 0; i < n; i++) {
1111
- const a = steps[i](state);
1112
- if (a !== Fail) {
1113
- if (failure !== undefined && failure.offset === state.pos)
1114
- state.setHint(failure.offset, failure.expected);
1115
- return a;
1116
- }
1117
- if (state.mode !== "Backtrack")
1118
- return Fail;
1119
- failure = failure === undefined ? state.failure : mergeFailures(failure, state.failure);
1120
- state.pos = start;
1121
- }
1122
- return state.failWith("Backtrack", failure);
1123
- }, "alt", { _tag: "Alt", parsers });
1176
+ * The starting point for Do notation.
1177
+ *
1178
+ * @example
1179
+ * ```ts
1180
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1181
+ *
1182
+ * const range = Parser.Do.pipe(
1183
+ * Parser.bind("start", () => Ascii.decUint),
1184
+ * Parser.zipLeft(Token.literal("..")),
1185
+ * Parser.bind("end", () => Ascii.decUint)
1186
+ * )
1187
+ * ```
1188
+ *
1189
+ * @since 0.1.0
1190
+ * @category do notation
1191
+ */
1192
+ const Do = /*#__PURE__*/ succeed({});
1193
+ /**
1194
+ * Binds the output of a parser to a field of the Do record.
1195
+ *
1196
+ * @since 0.1.0
1197
+ * @category do notation
1198
+ */
1199
+ const bind = /*#__PURE__*/ dual(3, (self, name, f) => flatMap(self, (a) => map(f(a), (b) => ({
1200
+ ...a,
1201
+ [name]: b
1202
+ }))));
1203
+ /**
1204
+ * Wraps the output of a parser in a record with a single field.
1205
+ *
1206
+ * @since 0.1.0
1207
+ * @category do notation
1208
+ */
1209
+ const bindTo = /*#__PURE__*/ dual(2, (self, name) => map(self, (a) => ({ [name]: a })));
1210
+ const let_ = /*#__PURE__*/ dual(3, (self, name, f) => map(self, (a) => ({
1211
+ ...a,
1212
+ [name]: f(a)
1213
+ })));
1214
+ /**
1215
+ * Tries each parser in turn and returns the output of the first that
1216
+ * succeeds (winnow's and nom's `alt`).
1217
+ *
1218
+ * Unrecoverable (`Cut`) and `Incomplete` failures stop the search
1219
+ * immediately. If every alternative fails recoverably, the failures are
1220
+ * merged: the one that got furthest wins, and failures at the same offset
1221
+ * combine their expectations, e.g. `expected "true", "false" or "null"`.
1222
+ *
1223
+ * @since 0.1.0
1224
+ * @category choice
1225
+ */
1226
+ const alt = (...parsers) => {
1227
+ const steps = parsers.map((p) => p.step);
1228
+ const n = steps.length;
1229
+ if (n === 0) return reject;
1230
+ if (n === 1) return parsers[0];
1231
+ return described((state) => {
1232
+ const start = state.pos;
1233
+ let failure;
1234
+ let tie1;
1235
+ let tie2;
1236
+ for (let i = 0; i < n; i++) {
1237
+ const a = steps[i](state);
1238
+ if (a !== Fail) {
1239
+ if (failure !== void 0 && failure.offset === state.pos) {
1240
+ let expected = failure.expected;
1241
+ if (tie1 !== void 0) expected = mergeExpected(expected, tie1.expected);
1242
+ if (tie2 !== void 0) expected = mergeExpected(expected, tie2.expected);
1243
+ state.setHint(failure.offset, expected);
1244
+ }
1245
+ return a;
1246
+ }
1247
+ if (state.mode !== "Backtrack") return Fail;
1248
+ state.pos = start;
1249
+ const head = state.failureHead;
1250
+ if (failure === void 0 || head.offset > failure.offset) {
1251
+ failure = head;
1252
+ tie1 = state.failureTie1;
1253
+ tie2 = state.failureTie2;
1254
+ } else if (head.offset === failure.offset) {
1255
+ const f = state.failure;
1256
+ if (tie1 === void 0) tie1 = f;
1257
+ else if (tie2 === void 0) tie2 = f;
1258
+ else {
1259
+ failure = mergeTiedFailures(failure, tie1, mergeExpected(failure.expected, tie1.expected));
1260
+ tie1 = tie2;
1261
+ tie2 = f;
1262
+ }
1263
+ }
1264
+ }
1265
+ if (failure === void 0) return state.failWith("Backtrack", state.failure);
1266
+ return tie1 === void 0 ? state.failWith("Backtrack", failure) : state.failTies(failure, tie1, tie2);
1267
+ }, "alt", {
1268
+ _tag: "Alt",
1269
+ parsers
1270
+ });
1124
1271
  };
1125
1272
  /**
1126
- * Selects the parser to run next based on the output of `selector`, like a
1127
- * `match` over the input (winnow's `dispatch!`). If no case matches, the
1128
- * parser backtracks to where it started.
1129
- *
1130
- * Cases are given either as a record keyed by the selector's output (with
1131
- * an optional `otherwise` parser for unlisted keys, like winnow's `_ =>`
1132
- * arm), or as a function returning a parser (or `undefined` for no match).
1133
- *
1134
- * @example
1135
- * ```ts
1136
- * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1137
- *
1138
- * const escape = Parser.preceded(
1139
- * Token.literal("\\"),
1140
- * Parser.dispatch(Token.any, {
1141
- * n: Parser.succeed("\n"),
1142
- * t: Parser.succeed("\t"),
1143
- * u: Parser.preceded(Token.literal("{"), Ascii.hexUint).pipe(
1144
- * Parser.zipLeft(Token.literal("}")),
1145
- * Parser.map((cp) => String.fromCodePoint(cp))
1146
- * )
1147
- * })
1148
- * )
1149
- * ```
1150
- *
1151
- * @since 0.1.0
1152
- * @category choice
1153
- */
1154
- export const dispatch = (selector, cases, otherwise) => {
1155
- const choose = typeof cases === "function" ? cases : (key) => Object.prototype.hasOwnProperty.call(cases, key) ? cases[key] : otherwise;
1156
- const step = selector.step;
1157
- return described((state) => {
1158
- const start = state.pos;
1159
- const key = step(state);
1160
- if (key === Fail)
1161
- return Fail;
1162
- const parser = choose(key);
1163
- if (parser === undefined) {
1164
- state.pos = start;
1165
- return state.backtrack("Dispatch");
1166
- }
1167
- return parser.step(state);
1168
- }, "dispatch", {
1169
- _tag: "Dispatch",
1170
- selector,
1171
- cases: typeof cases === "function" ? undefined : cases,
1172
- otherwise: typeof cases === "function" ? undefined : otherwise,
1173
- choose
1174
- });
1273
+ * Selects the parser to run next based on the output of `selector`, like a
1274
+ * `match` over the input (winnow's `dispatch!`). If no case matches, the
1275
+ * parser backtracks to where it started.
1276
+ *
1277
+ * Cases are given either as a record keyed by the selector's output (with
1278
+ * an optional `otherwise` parser for unlisted keys, like winnow's `_ =>`
1279
+ * arm), or as a function returning a parser (or `undefined` for no match).
1280
+ *
1281
+ * Prefer a record where the keys are known. Compiled code (see the
1282
+ * `Compiler` module) switches on the key and runs every case compiled; the
1283
+ * parser a function returns always runs interpreted, and one the function
1284
+ * builds (`(c) => Parser.cut(p)`) is rebuilt on every run.
1285
+ *
1286
+ * @example
1287
+ * ```ts
1288
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1289
+ *
1290
+ * const escape = Parser.preceded(
1291
+ * Token.literal("\\"),
1292
+ * Parser.dispatch(Token.any, {
1293
+ * n: Parser.succeed("\n"),
1294
+ * t: Parser.succeed("\t"),
1295
+ * u: Parser.preceded(Token.literal("{"), Ascii.hexUint).pipe(
1296
+ * Parser.zipLeft(Token.literal("}")),
1297
+ * Parser.map((cp) => String.fromCodePoint(cp))
1298
+ * )
1299
+ * })
1300
+ * )
1301
+ * ```
1302
+ *
1303
+ * @since 0.1.0
1304
+ * @category choice
1305
+ */
1306
+ const dispatch = (selector, cases, otherwise) => {
1307
+ const choose = Predicate.isFunction(cases) ? cases : (key) => Object.prototype.hasOwnProperty.call(cases, key) ? cases[key] : otherwise;
1308
+ const step = selector.step;
1309
+ return described((state) => {
1310
+ const start = state.pos;
1311
+ const key = step(state);
1312
+ if (key === Fail) return Fail;
1313
+ const parser = choose(key);
1314
+ if (parser === void 0) {
1315
+ state.pos = start;
1316
+ return state.backtrack("Dispatch");
1317
+ }
1318
+ return parser.step(state);
1319
+ }, "dispatch", {
1320
+ _tag: "Dispatch",
1321
+ selector,
1322
+ cases: Predicate.isFunction(cases) ? void 0 : cases,
1323
+ otherwise: Predicate.isFunction(cases) ? void 0 : otherwise,
1324
+ choose
1325
+ });
1175
1326
  };
1176
1327
  /**
1177
- * Applies every parser exactly once, in whatever order they match, and
1178
- * returns their outputs in the order the parsers were given (winnow's and
1179
- * nom's `permutation`).
1180
- *
1181
- * @since 0.1.0
1182
- * @category choice
1183
- */
1184
- export const permutation = (...parsers) => {
1185
- const steps = parsers.map((p) => p.step);
1186
- const n = steps.length;
1187
- return make((state) => {
1188
- const out = new Array(n);
1189
- const done = new Array(n).fill(false);
1190
- let remaining = n;
1191
- while (remaining > 0) {
1192
- let failure;
1193
- let matched = false;
1194
- for (let i = 0; i < n; i++) {
1195
- if (done[i])
1196
- continue;
1197
- const start = state.pos;
1198
- const a = steps[i](state);
1199
- if (a !== Fail) {
1200
- out[i] = a;
1201
- done[i] = true;
1202
- remaining--;
1203
- matched = true;
1204
- break;
1205
- }
1206
- if (state.mode !== "Backtrack")
1207
- return Fail;
1208
- failure = failure === undefined ? state.failure : mergeFailures(failure, state.failure);
1209
- state.pos = start;
1210
- }
1211
- if (!matched)
1212
- return state.failWith("Backtrack", failure);
1213
- }
1214
- return out;
1215
- }, "permutation");
1328
+ * Applies every parser exactly once, in whatever order they match, and
1329
+ * returns their outputs in the order the parsers were given (winnow's and
1330
+ * nom's `permutation`).
1331
+ *
1332
+ * @since 0.1.0
1333
+ * @category choice
1334
+ */
1335
+ const permutation = (...parsers) => {
1336
+ const steps = parsers.map((p) => p.step);
1337
+ const n = steps.length;
1338
+ return make((state) => {
1339
+ const ties = new TiedFailures();
1340
+ const done = new Array(n).fill(false);
1341
+ const out = new Array(n);
1342
+ let remaining = n;
1343
+ while (remaining > 0) {
1344
+ ties.clear();
1345
+ let matched = false;
1346
+ for (let i = 0; i < n; i++) {
1347
+ if (done[i]) continue;
1348
+ const start = state.pos;
1349
+ const a = steps[i](state);
1350
+ if (a !== Fail) {
1351
+ out[i] = a;
1352
+ done[i] = true;
1353
+ remaining--;
1354
+ matched = true;
1355
+ break;
1356
+ }
1357
+ if (state.mode !== "Backtrack") return Fail;
1358
+ state.pos = start;
1359
+ ties.add(state);
1360
+ }
1361
+ if (!matched) return ties.fail(state);
1362
+ }
1363
+ return out;
1364
+ }, "permutation");
1216
1365
  };
1217
- // -----------------------------------------------------------------------------
1218
- // Repetition
1219
- // -----------------------------------------------------------------------------
1220
1366
  const normalizeRange = (range) => {
1221
- if (range === undefined)
1222
- return [0, Infinity];
1223
- if (typeof range === "number")
1224
- return [range, range];
1225
- const min = range.min ?? 0;
1226
- const max = range.max ?? Infinity;
1227
- if (min > max)
1228
- throw new RangeError(`invalid range: min (${min}) > max (${max})`);
1229
- return [min, max];
1367
+ if (range === void 0) return [0, Infinity];
1368
+ if (Predicate.isNumber(range)) return [range, range];
1369
+ const min = range.min ?? 0;
1370
+ const max = range.max ?? Infinity;
1371
+ if (min > max) throw new RangeError(`invalid range: min (${min}) > max (${max})`);
1372
+ return [min, max];
1230
1373
  };
1231
1374
  const assertConsumes = (state, name) => state.failMessage("Cut", "Assert", `\`${name}\` parsers must always consume`);
1232
- const saveFold = (state, key, entry, acc, pos) => Resume.save(state, key, entry, { acc, count: 0, pos });
1233
- const foldImpl = (self, range, init, f, name, node) => {
1234
- const [min, max] = normalizeRange(range);
1235
- const step = self.step;
1236
- const checkLoop = max === Infinity;
1237
- const key = {};
1238
- return described((state) => {
1239
- const entry = state.pos;
1240
- let acc;
1241
- let count = 0;
1242
- const resumed = state.resume === undefined ? undefined : Resume.take(state, key, entry);
1243
- if (resumed instanceof Resume.Done) {
1244
- state.pos = resumed.pos;
1245
- return resumed.value;
1246
- }
1247
- if (resumed !== undefined) {
1248
- acc = resumed.acc;
1249
- count = resumed.count;
1250
- state.pos = resumed.pos;
1251
- }
1252
- else {
1253
- acc = init();
1254
- }
1255
- for (; count < max; count++) {
1256
- const start = state.pos;
1257
- const a = step(state);
1258
- if (a === Fail) {
1259
- if (state.mode === "Incomplete" && state.resume !== undefined) {
1260
- Resume.save(state, key, entry, { acc, count, pos: start });
1261
- }
1262
- if (state.mode !== "Backtrack" || count < min)
1263
- return Fail;
1264
- recover(state, start);
1265
- return Resume.done(state, key, entry, resumed !== undefined, acc);
1266
- }
1267
- if (checkLoop && state.pos === start)
1268
- return assertConsumes(state, name);
1269
- acc = f(acc, a);
1270
- }
1271
- return Resume.done(state, key, entry, resumed !== undefined, acc);
1272
- }, name, node ?? { _tag: "Fold", self, init, f, name, ...bounds(range) });
1375
+ const saveFold = (state, key, entry, acc, pos) => Resume.save(state, key, entry, {
1376
+ acc,
1377
+ count: 0,
1378
+ pos
1379
+ });
1380
+ const foldImpl = (self, range, init, f, name, node, cond) => {
1381
+ const [min, max] = normalizeRange(range);
1382
+ const step = self.step;
1383
+ const checkLoop = max === Infinity;
1384
+ const key = {};
1385
+ return described((state) => {
1386
+ const entry = state.pos;
1387
+ let acc;
1388
+ let count = 0;
1389
+ const resumed = state.resume === void 0 ? void 0 : Resume.take(state, key, entry);
1390
+ if (resumed instanceof Resume.Done) {
1391
+ state.pos = resumed.pos;
1392
+ return resumed.value;
1393
+ }
1394
+ if (resumed !== void 0) {
1395
+ acc = resumed.acc;
1396
+ count = resumed.count;
1397
+ state.pos = resumed.pos;
1398
+ } else acc = init();
1399
+ for (; count < max; count++) {
1400
+ if (cond !== void 0 && count >= min && !cond(acc)) break;
1401
+ const start = state.pos;
1402
+ const a = step(state);
1403
+ if (a === Fail) {
1404
+ if (state.mode === "Incomplete" && state.resume !== void 0) Resume.save(state, key, entry, {
1405
+ acc,
1406
+ count,
1407
+ pos: start
1408
+ });
1409
+ if (state.mode !== "Backtrack" || count < min) return Fail;
1410
+ recover(state, start);
1411
+ return Resume.done(state, key, entry, resumed !== void 0, acc);
1412
+ }
1413
+ if (checkLoop && state.pos === start) return assertConsumes(state, name);
1414
+ acc = f(acc, a);
1415
+ }
1416
+ return Resume.done(state, key, entry, resumed !== void 0, acc);
1417
+ }, name, node ?? {
1418
+ _tag: "Fold",
1419
+ self,
1420
+ init,
1421
+ f,
1422
+ while: cond,
1423
+ name,
1424
+ ...bounds(range)
1425
+ });
1273
1426
  };
1274
1427
  /**
1275
- * Applies a parser repeatedly, collecting the outputs in an array (winnow's
1276
- * `repeat`, nom's `many0`, `many1`, `many_m_n` and `count`).
1277
- *
1278
- * The range defaults to `{ min: 0 }` (zero or more). A number repeats
1279
- * exactly that many times. With a literal positive `min`, the output is
1280
- * typed as a non-empty array.
1281
- *
1282
- * As in winnow, an unbounded repetition of a parser that succeeds without
1283
- * consuming input fails with an unrecoverable assertion instead of looping
1284
- * forever.
1285
- *
1286
- * @example
1287
- * ```ts
1288
- * import { Parser, Token } from "@thomasfosterau/effect-parse"
1289
- *
1290
- * const abs = Parser.repeat(Token.literal("ab"), { min: 1 })
1291
- * Parser.parse(abs, "ababab") // Result.succeed(["ab", "ab", "ab"])
1292
- * ```
1293
- *
1294
- * @since 0.1.0
1295
- * @category repetition
1296
- */
1297
- export const repeat = dual(isParserArg, (self, range) => foldImpl(self, range, () => [], (acc, a) => {
1298
- acc.push(a);
1299
- return acc;
1300
- }, "repeat", { _tag: "Repeat", self, ...bounds(range) }));
1301
- /**
1302
- * Applies a parser repeatedly, folding the outputs into an accumulator
1303
- * (winnow's `repeat(..).fold(..)`, nom's `fold_many0`/`fold_many1`/
1304
- * `fold_many_m_n`).
1305
- *
1306
- * @since 0.1.0
1307
- * @category repetition
1308
- */
1309
- export const fold = dual(2, (self, options) => foldImpl(self, options.range, options.init, options.f, "fold"));
1310
- /**
1311
- * Applies `self` repeatedly until `end` succeeds, returning the collected
1312
- * outputs and the output of `end` (winnow's `repeat_till`, nom's
1313
- * `many_till`).
1314
- *
1315
- * @since 0.1.0
1316
- * @category repetition
1317
- */
1318
- export const repeatTill = dual(isTwoParserArgs, (self, end, range) => {
1319
- const [min, max] = normalizeRange(range);
1320
- const step = self.step;
1321
- const endStep = end.step;
1322
- return described((state) => {
1323
- const acc = [];
1324
- while (true) {
1325
- const start = state.pos;
1326
- let endFailure;
1327
- if (acc.length >= min) {
1328
- const b = endStep(state);
1329
- if (b !== Fail)
1330
- return [acc, b];
1331
- if (state.mode !== "Backtrack")
1332
- return Fail;
1333
- endFailure = state.failure;
1334
- state.pos = start;
1335
- if (acc.length >= max)
1336
- return Fail;
1337
- }
1338
- const a = step(state);
1339
- if (a === Fail) {
1340
- if (endFailure !== undefined && state.mode === "Backtrack") {
1341
- state.failWith("Backtrack", mergeFailures(endFailure, state.failure));
1342
- }
1343
- return Fail;
1344
- }
1345
- if (state.pos === start)
1346
- return assertConsumes(state, "repeatTill");
1347
- acc.push(a);
1348
- }
1349
- }, "repeatTill", { _tag: "RepeatTill", self, end, ...bounds(range) });
1428
+ * Applies a parser repeatedly, collecting the outputs in an array (winnow's
1429
+ * `repeat`, nom's `many0`, `many1`, `many_m_n` and `count`).
1430
+ *
1431
+ * The range defaults to `{ min: 0 }` (zero or more). A number repeats
1432
+ * exactly that many times. With a literal positive `min`, the output is
1433
+ * typed as a non-empty array.
1434
+ *
1435
+ * As in winnow, an unbounded repetition of a parser that succeeds without
1436
+ * consuming input fails with an unrecoverable assertion instead of looping
1437
+ * forever.
1438
+ *
1439
+ * @example
1440
+ * ```ts
1441
+ * import { Parser, Token } from "@thomasfosterau/effect-parse"
1442
+ *
1443
+ * const abs = Parser.repeat(Token.literal("ab"), { min: 1 })
1444
+ * Parser.parse(abs, "ababab") // Result.succeed(["ab", "ab", "ab"])
1445
+ * ```
1446
+ *
1447
+ * @since 0.1.0
1448
+ * @category repetition
1449
+ */
1450
+ const repeat = /*#__PURE__*/ dual(isParserArg, (self, range) => foldImpl(self, range, () => [], (acc, a) => {
1451
+ acc.push(a);
1452
+ return acc;
1453
+ }, "repeat", {
1454
+ _tag: "Repeat",
1455
+ self,
1456
+ ...bounds(range)
1457
+ }));
1458
+ /**
1459
+ * Applies a parser repeatedly, folding the outputs into an accumulator
1460
+ * (winnow's `repeat(..).fold(..)`, nom's `fold_many0`/`fold_many1`/
1461
+ * `fold_many_m_n`).
1462
+ *
1463
+ * With `while`, the repetition also stops, successfully and without
1464
+ * consuming more input, as soon as the predicate is false of the
1465
+ * accumulator. It is checked before each element once `range.min` elements
1466
+ * have been folded, so `min` still holds.
1467
+ *
1468
+ * @example
1469
+ * ```ts
1470
+ * import { Parser, Token } from "@thomasfosterau/effect-parse"
1471
+ *
1472
+ * // Keeps the first two digits, leaving the rest unparsed
1473
+ * const two = Parser.fold(Token.oneOf("0123456789"), {
1474
+ * init: () => "",
1475
+ * f: (acc, d) => acc + d,
1476
+ * while: (acc) => acc.length < 2
1477
+ * })
1478
+ * Parser.parsePeek(two, "1234") // Result.succeed(["34", "12"])
1479
+ * ```
1480
+ *
1481
+ * @since 0.1.0
1482
+ * @category repetition
1483
+ */
1484
+ const fold = /*#__PURE__*/ dual(2, (self, options) => foldImpl(self, options.range, options.init, options.f, "fold", void 0, options.while));
1485
+ /**
1486
+ * Applies `self` repeatedly until `end` succeeds, returning the collected
1487
+ * outputs and the output of `end` (winnow's `repeat_till`, nom's
1488
+ * `many_till`).
1489
+ *
1490
+ * @since 0.1.0
1491
+ * @category repetition
1492
+ */
1493
+ const repeatTill = /*#__PURE__*/ dual(isTwoParserArgs, (self, end, range) => {
1494
+ const [min, max] = normalizeRange(range);
1495
+ const step = self.step;
1496
+ const endStep = end.step;
1497
+ return described((state) => {
1498
+ const acc = [];
1499
+ while (true) {
1500
+ const start = state.pos;
1501
+ let endFailure;
1502
+ if (acc.length >= min) {
1503
+ const b = endStep(state);
1504
+ if (b !== Fail) return [acc, b];
1505
+ if (state.mode !== "Backtrack") return Fail;
1506
+ endFailure = state.failure;
1507
+ state.pos = start;
1508
+ if (acc.length >= max) return Fail;
1509
+ }
1510
+ const a = step(state);
1511
+ if (a === Fail) {
1512
+ if (endFailure !== void 0 && state.mode === "Backtrack") state.failWith("Backtrack", mergeFailures(endFailure, state.failure));
1513
+ return Fail;
1514
+ }
1515
+ if (state.pos === start) return assertConsumes(state, "repeatTill");
1516
+ acc.push(a);
1517
+ }
1518
+ }, "repeatTill", {
1519
+ _tag: "RepeatTill",
1520
+ self,
1521
+ end,
1522
+ ...bounds(range)
1523
+ });
1350
1524
  });
1351
- export const separated = dual(isTwoParserArgs, (self, separator, range) => {
1352
- const [min, max] = normalizeRange(range);
1353
- const step = self.step;
1354
- const sepStep = separator.step;
1355
- const checkLoop = max === Infinity;
1356
- const key = {};
1357
- return described((state) => {
1358
- const entry = state.pos;
1359
- let acc;
1360
- const resumed = state.resume === undefined ? undefined : Resume.take(state, key, entry);
1361
- if (resumed instanceof Resume.Done) {
1362
- state.pos = resumed.pos;
1363
- return resumed.value;
1364
- }
1365
- if (resumed !== undefined) {
1366
- acc = resumed.acc;
1367
- state.pos = resumed.pos;
1368
- }
1369
- else {
1370
- acc = [];
1371
- if (max === 0)
1372
- return acc;
1373
- const start = state.pos;
1374
- const first = step(state);
1375
- if (first === Fail) {
1376
- if (state.mode !== "Backtrack" || min > 0)
1377
- return Fail;
1378
- recover(state, start);
1379
- return acc;
1380
- }
1381
- acc.push(first);
1382
- }
1383
- while (acc.length < max) {
1384
- const start = state.pos;
1385
- if (sepStep(state) === Fail) {
1386
- if (state.mode === "Incomplete" && state.resume !== undefined) {
1387
- Resume.save(state, key, entry, { acc, pos: start });
1388
- }
1389
- if (state.mode !== "Backtrack" || acc.length < min)
1390
- return Fail;
1391
- recover(state, start);
1392
- return Resume.done(state, key, entry, resumed !== undefined, acc);
1393
- }
1394
- if (checkLoop && state.pos === start)
1395
- return assertConsumes(state, "separated");
1396
- const a = step(state);
1397
- if (a === Fail) {
1398
- if (state.mode === "Incomplete" && state.resume !== undefined) {
1399
- Resume.save(state, key, entry, { acc, pos: start });
1400
- }
1401
- if (state.mode !== "Backtrack" || acc.length < min)
1402
- return Fail;
1403
- state.pos = start;
1404
- return Resume.done(state, key, entry, resumed !== undefined, acc);
1405
- }
1406
- acc.push(a);
1407
- }
1408
- return Resume.done(state, key, entry, resumed !== undefined, acc);
1409
- }, "separated", { _tag: "Separated", self, separator, ...bounds(range) });
1525
+ /**
1526
+ * Applies `self` repeatedly, separated by `separator`, collecting the
1527
+ * outputs of `self` (winnow's `separated`, nom's `separated_list0` and
1528
+ * `separated_list1`). A trailing separator is not consumed.
1529
+ *
1530
+ * @example
1531
+ * ```ts
1532
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1533
+ *
1534
+ * const csv = Parser.separated(Ascii.decInt, Token.literal(","))
1535
+ * Parser.parse(csv, "1,2,3") // Result.succeed([1, 2, 3])
1536
+ * ```
1537
+ *
1538
+ * @since 0.1.0
1539
+ * @category repetition
1540
+ */
1541
+ const separated = /*#__PURE__*/ dual(isTwoParserArgs, (self, separator, range) => {
1542
+ const [min, max] = normalizeRange(range);
1543
+ const step = self.step;
1544
+ const sepStep = separator.step;
1545
+ const checkLoop = max === Infinity;
1546
+ const key = {};
1547
+ return described((state) => {
1548
+ const entry = state.pos;
1549
+ let acc;
1550
+ const resumed = state.resume === void 0 ? void 0 : Resume.take(state, key, entry);
1551
+ if (resumed instanceof Resume.Done) {
1552
+ state.pos = resumed.pos;
1553
+ return resumed.value;
1554
+ }
1555
+ if (resumed !== void 0) {
1556
+ acc = resumed.acc;
1557
+ state.pos = resumed.pos;
1558
+ } else {
1559
+ acc = [];
1560
+ if (max === 0) return acc;
1561
+ const start = state.pos;
1562
+ const first = step(state);
1563
+ if (first === Fail) {
1564
+ if (state.mode !== "Backtrack" || min > 0) return Fail;
1565
+ recover(state, start);
1566
+ return acc;
1567
+ }
1568
+ acc.push(first);
1569
+ }
1570
+ while (acc.length < max) {
1571
+ const start = state.pos;
1572
+ if (sepStep(state) === Fail) {
1573
+ if (state.mode === "Incomplete" && state.resume !== void 0) Resume.save(state, key, entry, {
1574
+ acc,
1575
+ pos: start
1576
+ });
1577
+ if (state.mode !== "Backtrack" || acc.length < min) return Fail;
1578
+ recover(state, start);
1579
+ return Resume.done(state, key, entry, resumed !== void 0, acc);
1580
+ }
1581
+ if (checkLoop && state.pos === start) return assertConsumes(state, "separated");
1582
+ const a = step(state);
1583
+ if (a === Fail) {
1584
+ if (state.mode === "Incomplete" && state.resume !== void 0) Resume.save(state, key, entry, {
1585
+ acc,
1586
+ pos: start
1587
+ });
1588
+ if (state.mode !== "Backtrack" || acc.length < min) return Fail;
1589
+ state.pos = start;
1590
+ return Resume.done(state, key, entry, resumed !== void 0, acc);
1591
+ }
1592
+ acc.push(a);
1593
+ }
1594
+ return Resume.done(state, key, entry, resumed !== void 0, acc);
1595
+ }, "separated", {
1596
+ _tag: "Separated",
1597
+ self,
1598
+ separator,
1599
+ ...bounds(range)
1600
+ });
1410
1601
  });
1411
1602
  /**
1412
- * Parses one or more `self` separated by `separator`, folding them from the
1413
- * left with the separator's output: `((a op b) op c)` (winnow's
1414
- * `separated_foldl1`). This is the classic way to parse left-associative
1415
- * binary operators.
1416
- *
1417
- * @example
1418
- * ```ts
1419
- * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1420
- *
1421
- * const sum = Parser.separatedFoldl1(
1422
- * Ascii.decInt,
1423
- * Parser.alt(Token.literal("+"), Token.literal("-")),
1424
- * (l, op, r) => op === "+" ? l + r : l - r
1425
- * )
1426
- * Parser.parse(sum, "10-3+2") // Result.succeed(9)
1427
- * ```
1428
- *
1429
- * @since 0.1.0
1430
- * @category repetition
1431
- */
1432
- export const separatedFoldl1 = (self, separator, op) => {
1433
- const step = self.step;
1434
- const sepStep = separator.step;
1435
- const key = {};
1436
- return described((state) => {
1437
- const entry = state.pos;
1438
- const resumed = state.resume === undefined ? undefined : Resume.take(state, key, entry);
1439
- let acc;
1440
- if (resumed instanceof Resume.Done) {
1441
- state.pos = resumed.pos;
1442
- return resumed.value;
1443
- }
1444
- if (resumed !== undefined) {
1445
- acc = resumed.acc;
1446
- state.pos = resumed.pos;
1447
- }
1448
- else {
1449
- acc = step(state);
1450
- if (acc === Fail)
1451
- return Fail;
1452
- }
1453
- while (true) {
1454
- const start = state.pos;
1455
- const s = sepStep(state);
1456
- if (s === Fail) {
1457
- if (state.mode === "Incomplete" && state.resume !== undefined)
1458
- saveFold(state, key, entry, acc, start);
1459
- if (state.mode !== "Backtrack")
1460
- return Fail;
1461
- recover(state, start);
1462
- return Resume.done(state, key, entry, resumed !== undefined, acc);
1463
- }
1464
- if (state.pos === start)
1465
- return assertConsumes(state, "separatedFoldl1");
1466
- const a = step(state);
1467
- if (a === Fail) {
1468
- if (state.mode === "Incomplete" && state.resume !== undefined)
1469
- saveFold(state, key, entry, acc, start);
1470
- if (state.mode !== "Backtrack")
1471
- return Fail;
1472
- state.pos = start;
1473
- return Resume.done(state, key, entry, resumed !== undefined, acc);
1474
- }
1475
- acc = op(acc, s, a);
1476
- }
1477
- }, "separatedFoldl1", { _tag: "SeparatedFold", self, separator, op, right: false });
1603
+ * Parses one or more `self` separated by `separator`, folding them from the
1604
+ * left with the separator's output: `((a op b) op c)` (winnow's
1605
+ * `separated_foldl1`). This is the classic way to parse left-associative
1606
+ * binary operators.
1607
+ *
1608
+ * @example
1609
+ * ```ts
1610
+ * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1611
+ *
1612
+ * const sum = Parser.separatedFoldl1(
1613
+ * Ascii.decInt,
1614
+ * Parser.alt(Token.literal("+"), Token.literal("-")),
1615
+ * (l, op, r) => op === "+" ? l + r : l - r
1616
+ * )
1617
+ * Parser.parse(sum, "10-3+2") // Result.succeed(9)
1618
+ * ```
1619
+ *
1620
+ * @since 0.1.0
1621
+ * @category repetition
1622
+ */
1623
+ const separatedFoldl1 = (self, separator, op) => {
1624
+ const step = self.step;
1625
+ const sepStep = separator.step;
1626
+ const key = {};
1627
+ return described((state) => {
1628
+ const entry = state.pos;
1629
+ const resumed = state.resume === void 0 ? void 0 : Resume.take(state, key, entry);
1630
+ let acc;
1631
+ if (resumed instanceof Resume.Done) {
1632
+ state.pos = resumed.pos;
1633
+ return resumed.value;
1634
+ }
1635
+ if (resumed !== void 0) {
1636
+ acc = resumed.acc;
1637
+ state.pos = resumed.pos;
1638
+ } else {
1639
+ acc = step(state);
1640
+ if (acc === Fail) return Fail;
1641
+ }
1642
+ while (true) {
1643
+ const start = state.pos;
1644
+ const s = sepStep(state);
1645
+ if (s === Fail) {
1646
+ if (state.mode === "Incomplete" && state.resume !== void 0) saveFold(state, key, entry, acc, start);
1647
+ if (state.mode !== "Backtrack") return Fail;
1648
+ recover(state, start);
1649
+ return Resume.done(state, key, entry, resumed !== void 0, acc);
1650
+ }
1651
+ if (state.pos === start) return assertConsumes(state, "separatedFoldl1");
1652
+ const a = step(state);
1653
+ if (a === Fail) {
1654
+ if (state.mode === "Incomplete" && state.resume !== void 0) saveFold(state, key, entry, acc, start);
1655
+ if (state.mode !== "Backtrack") return Fail;
1656
+ state.pos = start;
1657
+ return Resume.done(state, key, entry, resumed !== void 0, acc);
1658
+ }
1659
+ acc = op(acc, s, a);
1660
+ }
1661
+ }, "separatedFoldl1", {
1662
+ _tag: "SeparatedFold",
1663
+ self,
1664
+ separator,
1665
+ op,
1666
+ right: false
1667
+ });
1478
1668
  };
1479
1669
  /**
1480
- * Parses one or more `self` separated by `separator`, folding them from the
1481
- * right with the separator's output: `(a op (b op c))` (winnow's
1482
- * `separated_foldr1`). Useful for right-associative operators such as
1483
- * exponentiation.
1484
- *
1485
- * @since 0.1.0
1486
- * @category repetition
1487
- */
1488
- export const separatedFoldr1 = (self, separator, op) => {
1489
- const step = self.step;
1490
- const sepStep = separator.step;
1491
- return described((state) => {
1492
- const first = step(state);
1493
- if (first === Fail)
1494
- return Fail;
1495
- const values = [first];
1496
- const seps = [];
1497
- while (true) {
1498
- const start = state.pos;
1499
- const s = sepStep(state);
1500
- if (s === Fail) {
1501
- if (state.mode !== "Backtrack")
1502
- return Fail;
1503
- recover(state, start);
1504
- break;
1505
- }
1506
- if (state.pos === start)
1507
- return assertConsumes(state, "separatedFoldr1");
1508
- const a = step(state);
1509
- if (a === Fail) {
1510
- if (state.mode !== "Backtrack")
1511
- return Fail;
1512
- state.pos = start;
1513
- break;
1514
- }
1515
- seps.push(s);
1516
- values.push(a);
1517
- }
1518
- let acc = values[values.length - 1];
1519
- for (let i = seps.length - 1; i >= 0; i--) {
1520
- acc = op(values[i], seps[i], acc);
1521
- }
1522
- return acc;
1523
- }, "separatedFoldr1", { _tag: "SeparatedFold", self, separator, op, right: true });
1670
+ * Parses one or more `self` separated by `separator`, folding them from the
1671
+ * right with the separator's output: `(a op (b op c))` (winnow's
1672
+ * `separated_foldr1`). Useful for right-associative operators such as
1673
+ * exponentiation.
1674
+ *
1675
+ * @since 0.1.0
1676
+ * @category repetition
1677
+ */
1678
+ const separatedFoldr1 = (self, separator, op) => {
1679
+ const step = self.step;
1680
+ const sepStep = separator.step;
1681
+ return described((state) => {
1682
+ const first = step(state);
1683
+ if (first === Fail) return Fail;
1684
+ const values = [first];
1685
+ const seps = [];
1686
+ while (true) {
1687
+ const start = state.pos;
1688
+ const s = sepStep(state);
1689
+ if (s === Fail) {
1690
+ if (state.mode !== "Backtrack") return Fail;
1691
+ recover(state, start);
1692
+ break;
1693
+ }
1694
+ if (state.pos === start) return assertConsumes(state, "separatedFoldr1");
1695
+ const a = step(state);
1696
+ if (a === Fail) {
1697
+ if (state.mode !== "Backtrack") return Fail;
1698
+ state.pos = start;
1699
+ break;
1700
+ }
1701
+ seps.push(s);
1702
+ values.push(a);
1703
+ }
1704
+ let acc = values[values.length - 1];
1705
+ for (let i = seps.length - 1; i >= 0; i--) acc = op(values[i], seps[i], acc);
1706
+ return acc;
1707
+ }, "separatedFoldr1", {
1708
+ _tag: "SeparatedFold",
1709
+ self,
1710
+ separator,
1711
+ op,
1712
+ right: true
1713
+ });
1524
1714
  };
1525
- // -----------------------------------------------------------------------------
1526
- // Services
1527
- // -----------------------------------------------------------------------------
1528
- /**
1529
- * Accesses a service from the parser's context. Services take the place of
1530
- * winnow's `Stateful` input: they can hold configuration, or mutable state
1531
- * such as an indentation stack or a symbol table.
1532
- *
1533
- * Services are provided with {@link provideService} / {@link provide}, with
1534
- * the `context` option of the runners, or taken from the surrounding Effect
1535
- * by {@link parseEffect}.
1536
- *
1537
- * Mutations made to a service's state are not undone on backtracking (as
1538
- * with winnow's `Stateful`).
1539
- *
1540
- * @example
1541
- * ```ts
1542
- * import { Context } from "effect"
1543
- * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
1544
- *
1545
- * class Radix extends Context.Service<Radix, number>()("Radix") {}
1546
- *
1547
- * const number = Parser.service(Radix).pipe(
1548
- * Parser.flatMap((radix) =>
1549
- * Ascii.alphanumeric1.pipe(Parser.map((digits) => parseInt(digits, radix)))
1550
- * )
1551
- * )
1552
- *
1553
- * Parser.parse(Parser.provideService(number, Radix, 16), "ff") // Result.succeed(255)
1554
- * ```
1555
- *
1556
- * @since 0.1.0
1557
- * @category services
1558
- */
1559
- export const service = (key) => described((state) => Context.getUnsafe(state.context, key), "service", serviceNode);
1715
+ /**
1716
+ * Accesses a service from the parser's context. Services take the place of
1717
+ * winnow's `Stateful` input: they can hold configuration, or mutable state
1718
+ * such as an indentation stack or a symbol table.
1719
+ *
1720
+ * Services are provided with {@link provideService} / {@link provide}, with
1721
+ * the `context` option of the runners, or taken from the surrounding Effect
1722
+ * by {@link parseEffect}.
1723
+ *
1724
+ * Mutations made to a service's state are not undone on backtracking (as
1725
+ * with winnow's `Stateful`).
1726
+ *
1727
+ * @example
1728
+ * ```ts
1729
+ * import { Context } from "effect"
1730
+ * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
1731
+ *
1732
+ * class Radix extends Context.Service<Radix, number>()("Radix") {}
1733
+ *
1734
+ * const number = Parser.service(Radix).pipe(
1735
+ * Parser.flatMap((radix) =>
1736
+ * Ascii.alphanumeric1.pipe(Parser.map((digits) => parseInt(digits, radix)))
1737
+ * )
1738
+ * )
1739
+ *
1740
+ * Parser.parse(Parser.provideService(number, Radix, 16), "ff") // Result.succeed(255)
1741
+ * ```
1742
+ *
1743
+ * @since 0.1.0
1744
+ * @category services
1745
+ */
1746
+ const service = (key) => described((state) => Context.getUnsafe(state.context, key), "service", serviceNode);
1560
1747
  const serviceNode = { _tag: "Service" };
1561
1748
  /**
1562
- * Provides a service to a parser.
1563
- *
1564
- * @since 0.1.0
1565
- * @category services
1566
- */
1567
- export const provideService = dual(3, (self, key, service) => {
1568
- const step = self.step;
1569
- return described((state) => {
1570
- const prev = state.context;
1571
- state.context = Context.add(prev, key, service);
1572
- try {
1573
- return step(state);
1574
- }
1575
- finally {
1576
- state.context = prev;
1577
- }
1578
- }, self.name, { _tag: "Provide", self });
1749
+ * Provides a service to a parser.
1750
+ *
1751
+ * @since 0.1.0
1752
+ * @category services
1753
+ */
1754
+ const provideService = /*#__PURE__*/ dual(3, (self, key, service) => {
1755
+ const step = self.step;
1756
+ return described((state) => {
1757
+ const prev = state.context;
1758
+ state.context = Context.add(prev, key, service);
1759
+ try {
1760
+ return step(state);
1761
+ } finally {
1762
+ state.context = prev;
1763
+ }
1764
+ }, self.name, {
1765
+ _tag: "Provide",
1766
+ self
1767
+ });
1579
1768
  });
1580
1769
  /**
1581
- * Provides a context of services to a parser.
1582
- *
1583
- * @since 0.1.0
1584
- * @category services
1585
- */
1586
- export const provide = dual(2, (self, context) => {
1587
- const step = self.step;
1588
- return described((state) => {
1589
- const prev = state.context;
1590
- state.context = Context.merge(prev, context);
1591
- try {
1592
- return step(state);
1593
- }
1594
- finally {
1595
- state.context = prev;
1596
- }
1597
- }, self.name, { _tag: "Provide", self });
1770
+ * Provides a context of services to a parser.
1771
+ *
1772
+ * @since 0.1.0
1773
+ * @category services
1774
+ */
1775
+ const provide = /*#__PURE__*/ dual(2, (self, context) => {
1776
+ const step = self.step;
1777
+ return described((state) => {
1778
+ const prev = state.context;
1779
+ state.context = Context.merge(prev, context);
1780
+ try {
1781
+ return step(state);
1782
+ } finally {
1783
+ state.context = prev;
1784
+ }
1785
+ }, self.name, {
1786
+ _tag: "Provide",
1787
+ self
1788
+ });
1598
1789
  });
1599
- // -----------------------------------------------------------------------------
1600
- // Error recovery
1601
- // -----------------------------------------------------------------------------
1602
- /**
1603
- * Error recovery: if the parser fails, record the failure, skip input with
1604
- * `recover`, and continue with `Option.none()` (winnow's `resume_after`).
1605
- *
1606
- * Recovery is only active when running with {@link parseRecoverable}. Under
1607
- * the other runners the parser behaves like `Parser.asSome(self)`.
1608
- *
1609
- * @since 0.1.0
1610
- * @category error recovery
1611
- */
1612
- export const resumeAfter = dual(2, (self, recover) => {
1613
- const step = self.step;
1614
- const recoverStep = recover.step;
1615
- return described((state) => {
1616
- const start = state.pos;
1617
- const a = step(state);
1618
- if (a !== Fail)
1619
- return Option.some(a);
1620
- if (state.recovered === undefined || state.mode === "Incomplete")
1621
- return Fail;
1622
- const failure = state.failure;
1623
- const mode = state.mode;
1624
- state.pos = start;
1625
- if (recoverStep(state) === Fail)
1626
- return state.failWith(mode, failure);
1627
- state.recovered.push(failure);
1628
- return Option.none();
1629
- }, self.name, { _tag: "ResumeAfter", self, recover });
1790
+ /**
1791
+ * Error recovery: if the parser fails, record the failure, skip input with
1792
+ * `recover`, and continue with `Option.none()` (winnow's `resume_after`).
1793
+ *
1794
+ * Recovery is only active when running with {@link parseRecoverable}. Under
1795
+ * the other runners the parser behaves like `Parser.asSome(self)`.
1796
+ *
1797
+ * @since 0.1.0
1798
+ * @category error recovery
1799
+ */
1800
+ const resumeAfter = /*#__PURE__*/ dual(2, (self, recover) => {
1801
+ const step = self.step;
1802
+ const recoverStep = recover.step;
1803
+ return described((state) => {
1804
+ const start = state.pos;
1805
+ const a = step(state);
1806
+ if (a !== Fail) return Option.some(a);
1807
+ if (state.recovered === void 0 || state.mode === "Incomplete") return Fail;
1808
+ const failure = state.failure;
1809
+ const mode = state.mode;
1810
+ state.pos = start;
1811
+ if (recoverStep(state) === Fail) return state.failWith(mode, failure);
1812
+ state.recovered.push(failure);
1813
+ state.syntax?.skipped(start, failure, mode);
1814
+ return Option.none();
1815
+ }, self.name, {
1816
+ _tag: "ResumeAfter",
1817
+ self,
1818
+ recover
1819
+ });
1630
1820
  });
1631
1821
  /**
1632
- * Error recovery: if the parser fails, record the failure, skip input with
1633
- * `recover`, and try the parser again (winnow's `retry_after`).
1634
- *
1635
- * Recovery is only active when running with {@link parseRecoverable}. Under
1636
- * the other runners the parser behaves like `self`.
1637
- *
1638
- * @since 0.1.0
1639
- * @category error recovery
1640
- */
1641
- export const retryAfter = dual(2, (self, recover) => {
1642
- const step = self.step;
1643
- const recoverStep = recover.step;
1644
- return described((state) => {
1645
- while (true) {
1646
- const start = state.pos;
1647
- const a = step(state);
1648
- if (a !== Fail)
1649
- return a;
1650
- if (state.recovered === undefined || state.mode === "Incomplete")
1651
- return Fail;
1652
- const failure = state.failure;
1653
- const mode = state.mode;
1654
- state.pos = start;
1655
- if (recoverStep(state) === Fail || state.pos === start)
1656
- return state.failWith(mode, failure);
1657
- state.recovered.push(failure);
1658
- }
1659
- }, self.name, { _tag: "RetryAfter", self, recover });
1822
+ * Error recovery: if the parser fails, record the failure, skip input with
1823
+ * `recover`, and try the parser again (winnow's `retry_after`).
1824
+ *
1825
+ * Recovery is only active when running with {@link parseRecoverable}. Under
1826
+ * the other runners the parser behaves like `self`.
1827
+ *
1828
+ * @since 0.1.0
1829
+ * @category error recovery
1830
+ */
1831
+ const retryAfter = /*#__PURE__*/ dual(2, (self, recover) => {
1832
+ const step = self.step;
1833
+ const recoverStep = recover.step;
1834
+ return described((state) => {
1835
+ while (true) {
1836
+ const start = state.pos;
1837
+ const a = step(state);
1838
+ if (a !== Fail) return a;
1839
+ if (state.recovered === void 0 || state.mode === "Incomplete") return Fail;
1840
+ const failure = state.failure;
1841
+ const mode = state.mode;
1842
+ state.pos = start;
1843
+ if (recoverStep(state) === Fail || state.pos === start) return state.failWith(mode, failure);
1844
+ state.recovered.push(failure);
1845
+ state.syntax?.skipped(start, failure, mode);
1846
+ }
1847
+ }, self.name, {
1848
+ _tag: "RetryAfter",
1849
+ self,
1850
+ recover
1851
+ });
1660
1852
  });
1661
- // -----------------------------------------------------------------------------
1662
- // Debugging
1663
- // -----------------------------------------------------------------------------
1664
- /**
1665
- * Traces the execution of a parser (winnow's `trace`). When a tracer is
1666
- * installed (see the `trace` option of {@link parse}), `Enter` and `Exit`
1667
- * events are reported for each traced parser; otherwise tracing has no
1668
- * effect.
1669
- *
1670
- * @since 0.1.0
1671
- * @category debugging
1672
- */
1673
- export const trace = dual(2, (self, name) => {
1674
- const step = self.step;
1675
- return described((state) => {
1676
- const tracer = state.tracer;
1677
- if (tracer === undefined)
1678
- return step(state);
1679
- const start = state.pos;
1680
- const depth = state.depth;
1681
- tracer({ _tag: "Enter", name, depth, offset: start, state });
1682
- state.depth = depth + 1;
1683
- const a = step(state);
1684
- state.depth = depth;
1685
- tracer({
1686
- _tag: "Exit",
1687
- name,
1688
- depth,
1689
- offset: start,
1690
- endOffset: state.pos,
1691
- result: a === Fail ? state.mode : "Ok",
1692
- state
1693
- });
1694
- return a;
1695
- }, name, { _tag: "Trace", self, name });
1853
+ /**
1854
+ * Traces the execution of a parser (winnow's `trace`). When a tracer is
1855
+ * installed (see the `trace` option of {@link parse}), `Enter` and `Exit`
1856
+ * events are reported for each traced parser; otherwise tracing has no
1857
+ * effect.
1858
+ *
1859
+ * @since 0.1.0
1860
+ * @category debugging
1861
+ */
1862
+ const trace = /*#__PURE__*/ dual(2, (self, name) => {
1863
+ const step = self.step;
1864
+ return described((state) => {
1865
+ const tracer = state.tracer;
1866
+ if (tracer === void 0) return step(state);
1867
+ const start = state.pos;
1868
+ const depth = state.depth;
1869
+ tracer({
1870
+ _tag: "Enter",
1871
+ name,
1872
+ depth,
1873
+ offset: start,
1874
+ state
1875
+ });
1876
+ state.depth = depth + 1;
1877
+ const a = step(state);
1878
+ state.depth = depth;
1879
+ tracer({
1880
+ _tag: "Exit",
1881
+ name,
1882
+ depth,
1883
+ offset: start,
1884
+ endOffset: state.pos,
1885
+ result: a === Fail ? state.mode : "Ok",
1886
+ state
1887
+ });
1888
+ return a;
1889
+ }, name, {
1890
+ _tag: "Trace",
1891
+ self,
1892
+ name
1893
+ });
1696
1894
  });
1697
1895
  const preview = (state, offset) => {
1698
- const input = state.input;
1699
- if (typeof input === "string") {
1700
- const s = input.slice(offset, offset + 24);
1701
- return JSON.stringify(s) + (offset + 24 < input.length ? "…" : "∅");
1702
- }
1703
- if (input instanceof Uint8Array) {
1704
- return Array.from(input.subarray(offset, offset + 12), (b) => b.toString(16).padStart(2, "0")).join(" ");
1705
- }
1706
- return `[${offset}/${input.length}]`;
1896
+ const input = state.input;
1897
+ if (Predicate.isString(input)) {
1898
+ const s = input.slice(offset, offset + 24);
1899
+ return JSON.stringify(s) + (offset + 24 < input.length ? "…" : "∅");
1900
+ }
1901
+ if (input instanceof Uint8Array) return Array.from(input.subarray(offset, offset + 12), (b) => b.toString(16).padStart(2, "0")).join(" ");
1902
+ return `[${offset}/${input.length}]`;
1707
1903
  };
1708
1904
  /**
1709
- * A tracer that writes an indented trace to `console.log`, similar to
1710
- * winnow's `debug` feature.
1711
- *
1712
- * @since 0.1.0
1713
- * @category debugging
1714
- */
1715
- export const consoleTracer = (event) => {
1716
- const indent = " ".repeat(event.depth);
1717
- if (event._tag === "Enter") {
1718
- console.log(`${indent}> ${event.name} @${event.offset} | ${preview(event.state, event.offset)}`);
1719
- }
1720
- else {
1721
- const consumed = (event.endOffset ?? event.offset) - event.offset;
1722
- console.log(`${indent}< ${event.name} | ${event.result === "Ok" ? `+${consumed}` : event.result}`);
1723
- }
1905
+ * A tracer that writes an indented trace to `console.log`, similar to
1906
+ * winnow's `debug` feature.
1907
+ *
1908
+ * @since 0.1.0
1909
+ * @category debugging
1910
+ */
1911
+ const consoleTracer = (event) => {
1912
+ const indent = " ".repeat(event.depth);
1913
+ if (event._tag === "Enter") console.log(`${indent}> ${event.name} @${event.offset} | ${preview(event.state, event.offset)}`);
1914
+ else {
1915
+ const consumed = (event.endOffset ?? event.offset) - event.offset;
1916
+ console.log(`${indent}< ${event.name} | ${event.result === "Ok" ? `+${consumed}` : event.result}`);
1917
+ }
1724
1918
  };
1725
- /**
1726
- * Compiles a parser into fused, specialized code, and returns the same
1727
- * parser. Grammars are written as usual; compilation only makes
1728
- * {@link parse}, {@link parseSync}, {@link parsePeek} and
1729
- * {@link parseEffect} faster. Compile the root of a grammar once, where it
1730
- * is defined:
1731
- *
1732
- * ```ts
1733
- * import { Ascii, Parser, Token } from "@thomasfosterau/effect-parse"
1734
- *
1735
- * export const list = Parser.compile(
1736
- * Parser.separated(Ascii.decInt, Token.literal(","))
1737
- * )
1738
- * Parser.parse(list, "1,2,3") // Result.succeed([1, 2, 3]), compiled
1739
- * ```
1740
- *
1741
- * The code is generated lazily on the first run, for each kind of input
1742
- * (string, bytes or array), and cached with the parser.
1743
- *
1744
- * **Results and errors are unchanged.** Compiled code records no
1745
- * diagnostics. When it fails, the parser runs again, interpreted, from the
1746
- * start, so every `ParseError` (offset, expected, context, cause and
1747
- * message) is exactly the one the interpreter reports.
1748
- *
1749
- * **Callbacks should be pure.** On an input that fails, the callbacks given
1750
- * to `map`, `filter`, `tryMap`, `tap`, `fold`, ... may run twice, once
1751
- * compiled and once interpreted. Callbacks whose results are discarded
1752
- * (a `map` inside `taken` or `preceded`, for example) may not run at all,
1753
- * nor those of a branch that is certain to fail, and token-set predicates
1754
- * may be called once per character code and cached.
1755
- *
1756
- * Runs on partial input ({@link parsePartial}, `ParserStream`), with
1757
- * {@link parseRecoverable} or with the `trace` option are always
1758
- * interpreted. Parsers that can't be described, such as {@link make} and
1759
- * {@link gen}, run interpreted inside compiled code.
1760
- *
1761
- * @since 0.1.0
1762
- * @category compilation
1763
- */
1764
- export const compile = dual(isParserArg, (self, options) => {
1765
- Compiler.register(self, options?.backend ?? "auto");
1766
- return self;
1767
- });
1768
1919
  /** Returned by {@link runCompiled} when there is no compiled form to run. */
1769
- const NotCompiled = Symbol("NotCompiled");
1770
- /**
1771
- * Runs the compiled form of `self`, if it has one: returns its value, or
1772
- * `Fail` when it failed (or, with `whole`, did not consume the whole
1773
- * input), in which case the state must be thrown away.
1774
- */
1775
- const runCompiled = (self, state, whole) => {
1776
- if (state.tracer !== undefined || state.recovered !== undefined)
1777
- return NotCompiled;
1778
- const step = Compiler.stepFor(self, state);
1779
- if (step === undefined)
1780
- return NotCompiled;
1781
- const a = step(state);
1782
- if (a === Fail || (whole && state.pos < state.end))
1783
- return Fail;
1784
- return a;
1920
+ const NotCompiled = /*#__PURE__*/ Symbol("NotCompiled");
1921
+ /**
1922
+ * Runs the compiled form of `self`, if it has one: returns its value, or
1923
+ * `Fail` when it failed (or, with `whole`, did not consume the whole
1924
+ * input), in which case the state must be thrown away. `resumable` selects
1925
+ * the build that handles partial input.
1926
+ */
1927
+ const runCompiled = (self, state, whole, resumable = false) => {
1928
+ if (state.tracer !== void 0 || state.recovered !== void 0) return NotCompiled;
1929
+ const step = Registry.stepFor(self, state, resumable);
1930
+ if (step === void 0) return NotCompiled;
1931
+ const a = Stack.run(step, state);
1932
+ if (a === Fail || whole && state.pos < state.end) return Fail;
1933
+ return a;
1785
1934
  };
1786
1935
  const makeState = (input, options, partial) => {
1787
- const state = new State(input, { partial, context: options?.context });
1788
- const t = options?.trace;
1789
- if (t !== undefined && t !== false)
1790
- state.tracer = t === true ? consoleTracer : t;
1791
- return state;
1936
+ const offset = options?.offset;
1937
+ const state = new State(input, {
1938
+ partial,
1939
+ context: options?.context,
1940
+ offset
1941
+ });
1942
+ if (offset !== void 0 && !(Number.isInteger(offset) && offset >= 0 && offset <= state.end)) throw new RangeError(`Invalid offset ${offset} for an input of length ${state.end}`);
1943
+ const t = options?.trace;
1944
+ if (t !== void 0 && t !== false) state.tracer = t === true ? consoleTracer : t;
1945
+ return state;
1792
1946
  };
1793
1947
  const toError = (state) => ParseError.fromFailure(state.input, state.reportedFailure(), state.mode, state.encoding);
1794
1948
  /** Runs a parser against a complete input, compiled when possible. */
1795
1949
  const parseComplete = (self, input, options) => {
1796
- const state = makeState(input, options, false);
1797
- const a = runCompiled(self, state, true);
1798
- if (a === NotCompiled)
1799
- return runComplete(self, state);
1800
- if (a !== Fail)
1801
- return Result.succeed(a);
1802
- // Re-run interpreted for the diagnostics
1803
- return runComplete(self, makeState(input, options, false));
1950
+ const state = makeState(input, options, false);
1951
+ const a = runCompiled(self, state, true);
1952
+ if (a === NotCompiled) return runComplete(self, state);
1953
+ if (a !== Fail) return Result.succeed(a);
1954
+ return runComplete(self, makeState(input, options, false));
1804
1955
  };
1805
1956
  const runComplete = (self, state) => {
1806
- const a = self.step(state);
1807
- if (a === Fail)
1808
- return Result.fail(toError(state));
1809
- if (state.pos < state.end) {
1810
- state.backtrack("Eof", expectedEof);
1811
- return Result.fail(toError(state));
1812
- }
1813
- return Result.succeed(a);
1957
+ const a = Stack.run(self.step, state);
1958
+ if (a === Fail) return Result.fail(toError(state));
1959
+ if (state.pos < state.end) {
1960
+ state.backtrack("Eof", expectedEof);
1961
+ return Result.fail(toError(state));
1962
+ }
1963
+ return Result.succeed(a);
1814
1964
  };
1815
1965
  /**
1816
- * Runs a parser against a complete input, requiring the whole input to be
1817
- * consumed (winnow's `Parser::parse`).
1818
- *
1819
- * @example
1820
- * ```ts
1821
- * import { Result } from "effect"
1822
- * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
1823
- *
1824
- * Parser.parse(Ascii.decInt, "-42") // Result.succeed(-42)
1825
- * Result.isFailure(Parser.parse(Ascii.decInt, "42x")) // true
1826
- * ```
1827
- *
1828
- * @since 0.1.0
1829
- * @category running
1830
- */
1831
- export const parse = (self, input, options) => parseComplete(self, input, options);
1832
- /**
1833
- * Like {@link parse}, but throws the `ParseError` on failure.
1834
- *
1835
- * @since 0.1.0
1836
- * @category running
1837
- */
1838
- export const parseSync = (self, input, options) => {
1839
- const result = parseComplete(self, input, options);
1840
- if (Result.isSuccess(result))
1841
- return result.success;
1842
- throw result.failure;
1966
+ * Runs a parser against a complete input, requiring the whole input to be
1967
+ * consumed (winnow's `Parser::parse`).
1968
+ *
1969
+ * @example
1970
+ * ```ts
1971
+ * import { Result } from "effect"
1972
+ * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
1973
+ *
1974
+ * Parser.parse(Ascii.decInt, "-42") // Result.succeed(-42)
1975
+ * Result.isFailure(Parser.parse(Ascii.decInt, "42x")) // true
1976
+ * ```
1977
+ *
1978
+ * @since 0.1.0
1979
+ * @category running
1980
+ */
1981
+ const parse = (self, input, options) => parseComplete(self, input, options);
1982
+ /**
1983
+ * Like {@link parse}, but throws the `ParseError` on failure.
1984
+ *
1985
+ * @since 0.1.0
1986
+ * @category running
1987
+ */
1988
+ const parseSync = (self, input, options) => {
1989
+ const result = parseComplete(self, input, options);
1990
+ if (Result.isSuccess(result)) return result.success;
1991
+ throw result.failure;
1843
1992
  };
1844
1993
  /**
1845
- * Runs a parser against the start of an input, returning the remaining
1846
- * input along with the output, nom-style (winnow's `parse_peek`, nom's
1847
- * `IResult`).
1848
- *
1849
- * @since 0.1.0
1850
- * @category running
1851
- */
1852
- export const parsePeek = (self, input, options) => {
1853
- let state = makeState(input, options, false);
1854
- const compiled = runCompiled(self, state, false);
1855
- if (compiled !== NotCompiled && compiled !== Fail) {
1856
- return Result.succeed([state.slice(state.pos, state.end), compiled]);
1857
- }
1858
- if (compiled === Fail)
1859
- state = makeState(input, options, false);
1860
- const a = self.step(state);
1861
- if (a === Fail)
1862
- return Result.fail(toError(state));
1863
- return Result.succeed([state.slice(state.pos, state.end), a]);
1994
+ * Runs a parser against the start of an input, returning the remaining
1995
+ * input along with the output, nom-style (winnow's `parse_peek`, nom's
1996
+ * `IResult`).
1997
+ *
1998
+ * The parser ended at offset `input.length - remaining.length`, which is
1999
+ * how a caller running from an `offset` (see {@link ParseOptions}) finds
2000
+ * where to continue.
2001
+ *
2002
+ * @since 0.1.0
2003
+ * @category running
2004
+ */
2005
+ const parsePeek = (self, input, options) => {
2006
+ let state = makeState(input, options, false);
2007
+ const compiled = runCompiled(self, state, false);
2008
+ if (compiled !== NotCompiled && compiled !== Fail) return Result.succeed([state.slice(state.pos, state.end), compiled]);
2009
+ if (compiled === Fail) state = makeState(input, options, false);
2010
+ const a = Stack.run(self.step, state);
2011
+ if (a === Fail) return Result.fail(toError(state));
2012
+ return Result.succeed([state.slice(state.pos, state.end), a]);
1864
2013
  };
1865
2014
  /**
1866
- * Runs a parser against a *partial* input, i.e. a chunk of a stream that may
1867
- * be followed by more data (winnow's `Partial`, nom's `streaming` parsers).
1868
- * Token parsers that reach the end of the chunk report `Incomplete` instead
1869
- * of failing or succeeding early.
1870
- *
1871
- * The parser does not have to consume the whole input; the returned
1872
- * `offset` says how much was consumed.
1873
- *
1874
- * @example
1875
- * ```ts
1876
- * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
1877
- *
1878
- * Parser.parsePartial(Ascii.digit1, "123")
1879
- * // Result.succeed({ _tag: "Incomplete", needed: Option.some(1) })
1880
- * Parser.parsePartial(Ascii.digit1, "123;")
1881
- * // Result.succeed({ _tag: "Done", value: "123", offset: 3 })
1882
- * ```
1883
- *
1884
- * @since 0.1.0
1885
- * @category running
1886
- */
1887
- export const parsePartial = (self, input, options) => {
1888
- const state = makeState(input, options, true);
1889
- const a = self.step(state);
1890
- if (a !== Fail)
1891
- return Result.succeed({ _tag: "Done", value: a, offset: state.pos });
1892
- if (state.mode === "Incomplete") {
1893
- const needed = state.failure.needed;
1894
- return Result.succeed({ _tag: "Incomplete", needed: needed > 0 ? Option.some(needed) : Option.none() });
1895
- }
1896
- return Result.fail(toError(state));
2015
+ * Runs a parser against a *partial* input, i.e. a chunk of a stream that may
2016
+ * be followed by more data (winnow's `Partial`, nom's `streaming` parsers).
2017
+ * Token parsers that reach the end of the chunk report `Incomplete` instead
2018
+ * of failing or succeeding early.
2019
+ *
2020
+ * The parser does not have to consume the whole input; the returned
2021
+ * `offset` says where it stopped. It is a position in the whole input, so
2022
+ * with the `offset` option (see {@link ParseOptions}) it is where the next
2023
+ * parse starts.
2024
+ *
2025
+ * A parser compiled with the `Compiler` module runs compiled, with a build
2026
+ * that handles partial input. When it fails or needs more input, it runs again
2027
+ * interpreted, so `Incomplete`'s `needed` and every `ParseError` are the
2028
+ * interpreter's.
2029
+ *
2030
+ * @example
2031
+ * ```ts
2032
+ * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
2033
+ *
2034
+ * Parser.parsePartial(Ascii.digit1, "123")
2035
+ * // Result.succeed({ _tag: "Incomplete", needed: Option.some(1) })
2036
+ * Parser.parsePartial(Ascii.digit1, "123;")
2037
+ * // Result.succeed({ _tag: "Done", value: "123", offset: 3 })
2038
+ * ```
2039
+ *
2040
+ * @since 0.1.0
2041
+ * @category running
2042
+ */
2043
+ const parsePartial = (self, input, options) => {
2044
+ let state = makeState(input, options, true);
2045
+ const compiled = runCompiled(self, state, false, true);
2046
+ if (compiled !== NotCompiled && compiled !== Fail) return Result.succeed({
2047
+ _tag: "Done",
2048
+ value: compiled,
2049
+ offset: state.pos
2050
+ });
2051
+ if (compiled === Fail) state = makeState(input, options, true);
2052
+ const a = Stack.run(self.step, state);
2053
+ if (a !== Fail) return Result.succeed({
2054
+ _tag: "Done",
2055
+ value: a,
2056
+ offset: state.pos
2057
+ });
2058
+ if (state.mode === "Incomplete") {
2059
+ const needed = state.failure.needed;
2060
+ return Result.succeed({
2061
+ _tag: "Incomplete",
2062
+ needed: needed > 0 ? Option.some(needed) : Option.none()
2063
+ });
2064
+ }
2065
+ return Result.fail(toError(state));
1897
2066
  };
1898
2067
  /**
1899
- * Runs a parser against a complete input as an `Effect`. Services required
1900
- * by the parser are taken from the Effect's context.
1901
- *
1902
- * @example
1903
- * ```ts
1904
- * import { Effect } from "effect"
1905
- * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
1906
- *
1907
- * const program = Effect.gen(function*() {
1908
- * const n = yield* Parser.parseEffect(Ascii.decInt, "42")
1909
- * return n + 1
1910
- * })
1911
- * ```
1912
- *
1913
- * @since 0.1.0
1914
- * @category running
1915
- */
1916
- export const parseEffect = (self, input, options) => Effect.flatMap(Effect.context(), (context) => Effect.fromResult(parseComplete(self, input, { ...options, context })));
1917
- /**
1918
- * Runs a parser with error recovery enabled (winnow's `Recoverable`
1919
- * stream). Failures handled by {@link resumeAfter} and {@link retryAfter}
1920
- * are collected instead of aborting the parse.
1921
- *
1922
- * @since 0.1.0
1923
- * @category running
1924
- */
1925
- export const parseRecoverable = (self, input, options) => {
1926
- const state = makeState(input, options, false);
1927
- const recovered = [];
1928
- state.recovered = recovered;
1929
- const result = runComplete(self, state);
1930
- const errors = recovered.map((f) => ParseError.fromFailure(state.input, f, "Backtrack", state.encoding));
1931
- if (Result.isSuccess(result))
1932
- return { value: Option.some(result.success), errors };
1933
- errors.push(result.failure);
1934
- return { value: Option.none(), errors };
2068
+ * Runs a parser against a complete input as an `Effect`. Services required
2069
+ * by the parser are taken from the Effect's context.
2070
+ *
2071
+ * @example
2072
+ * ```ts
2073
+ * import { Effect } from "effect"
2074
+ * import { Ascii, Parser } from "@thomasfosterau/effect-parse"
2075
+ *
2076
+ * const program = Effect.gen(function*() {
2077
+ * const n = yield* Parser.parseEffect(Ascii.decInt, "42")
2078
+ * return n + 1
2079
+ * })
2080
+ * ```
2081
+ *
2082
+ * @since 0.1.0
2083
+ * @category running
2084
+ */
2085
+ const parseEffect = (self, input, options) => Effect.flatMap(Effect.context(), (context) => Effect.fromResult(parseComplete(self, input, {
2086
+ ...options,
2087
+ context
2088
+ })));
2089
+ /**
2090
+ * Runs a parser on a state you own, from `state.pos`, and returns its output
2091
+ * or {@link Fail}: like calling `self.step(state)`, but using the parser's
2092
+ * compiled form when it has one (see the `Compiler` module). This is the
2093
+ * runner for drivers that make many small runs over one buffer, such as a
2094
+ * lexer reading one token at a time: keep one `Input.State` and
2095
+ * `reset` it (`Input.State.reset`) before each run, and no run allocates a
2096
+ * state, a `Result` or a slice.
2097
+ *
2098
+ * On success the cursor is where the parser stopped. On failure the state
2099
+ * holds the interpreter's failure (`mode`, `failure`, and what
2100
+ * `reportedFailure()` returns): a compiled run that fails is thrown away and
2101
+ * the parser's step runs again from the same position. A partial state
2102
+ * (`partial: true`) runs the compiled build that handles partial input, so
2103
+ * `Incomplete` is reported as the step would report it. Under a tracer,
2104
+ * with recovery on or inside a `Syntax` parse, the step runs interpreted.
2105
+ *
2106
+ * A driver that only needs to know *that* a parser failed (it tries
2107
+ * another, or skips a character) can pass `{ diagnostics: false }` to skip
2108
+ * the interpreted re-run; see {@link RunOptions}.
2109
+ *
2110
+ * Compiled code records no hints or furthest failure when it succeeds, so
2111
+ * a later failure on the same state can list fewer expectations than one
2112
+ * after interpreted runs would: reset the state between runs (as above) and
2113
+ * its errors are the ones {@link parse} reports. Deep nesting fails with an
2114
+ * unrecoverable `TooDeep` failure instead of throwing, as in the other
2115
+ * runners.
2116
+ *
2117
+ * @example
2118
+ * ```ts
2119
+ * import { Ascii, Compiler, Input, Parser, Token } from "@thomasfosterau/effect-parse"
2120
+ *
2121
+ * const token = Compiler.jit(Parser.preceded(Ascii.space0, Parser.alt(Ascii.alpha1, Token.oneOf("+-"))))
2122
+ *
2123
+ * const tokens = (text: string): Array<string> => {
2124
+ * const state = new Input.State(text)
2125
+ * const out: Array<string> = []
2126
+ * while (state.pos < state.end) {
2127
+ * const t = Parser.run(token, state.reset(state.pos))
2128
+ * if (t === Input.Fail) break
2129
+ * out.push(t)
2130
+ * }
2131
+ * return out
2132
+ * }
2133
+ *
2134
+ * tokens("a + b") // ["a", "+", "b"]
2135
+ * ```
2136
+ *
2137
+ * @since 0.2.0
2138
+ * @category running
2139
+ */
2140
+ const run = (self, state, options) => Run.runOn(self, self.step, state, options?.diagnostics !== false);
2141
+ /**
2142
+ * Runs a parser with error recovery enabled (winnow's `Recoverable`
2143
+ * stream). Failures handled by {@link resumeAfter} and {@link retryAfter}
2144
+ * are collected instead of aborting the parse.
2145
+ *
2146
+ * @since 0.1.0
2147
+ * @category running
2148
+ */
2149
+ const parseRecoverable = (self, input, options) => {
2150
+ const state = makeState(input, options, false);
2151
+ const recovered = [];
2152
+ state.recovered = recovered;
2153
+ const result = runComplete(self, state);
2154
+ const errors = recovered.map((f) => ParseError.fromFailure(state.input, f, "Backtrack", state.encoding));
2155
+ if (Result.isSuccess(result)) return {
2156
+ value: Option.some(result.success),
2157
+ errors
2158
+ };
2159
+ errors.push(result.failure);
2160
+ return {
2161
+ value: Option.none(),
2162
+ errors
2163
+ };
1935
2164
  };
2165
+ //#endregion
2166
+ export { Do, TypeId, allConsuming, alt, as, asSome, asVoid, backtrack, bind, bindTo, complete, cond, consoleTracer, context, cut, delimited, described, dispatch, empty, eof, expected, fail, failMessage, filter, filterMap, filterOrFail, flatMap, fold, fromEntries, gen, isParser, let_ as let, location, make, map, mapError, mapParser, named, not, opt, optional, orElse, orElseSucceed, orFail, parse, parseEffect, parsePartial, parsePeek, parseRecoverable, parseSync, peek, permutation, preceded, provide, provideService, reject, repeat, repeatTill, resumeAfter, retryAfter, run, separated, separatedFoldl1, separatedFoldr1, separatedPair, seq, service, span, struct, succeed, suspend, sync, taken, tap, terminated, todo, trace, tryMap, tryMapUnknown, void_ as void, void_, withSpan, withTaken, zip, zipLeft, zipRight, zipWith };
2167
+
1936
2168
  //# sourceMappingURL=Parser.js.map