@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.
- package/README.md +291 -231
- package/dist/Ascii.d.ts +32 -53
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +576 -571
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts +6 -29
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +129 -116
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts +4 -2
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +121 -123
- package/dist/Bits.js.map +1 -1
- package/dist/Compiler.d.ts +39 -0
- package/dist/Compiler.d.ts.map +1 -0
- package/dist/Compiler.js +48 -0
- package/dist/Compiler.js.map +1 -0
- package/dist/Encoding.d.ts +29 -46
- package/dist/Encoding.d.ts.map +1 -1
- package/dist/Encoding.js +76 -75
- package/dist/Encoding.js.map +1 -1
- package/dist/Expression.d.ts +15 -49
- package/dist/Expression.d.ts.map +1 -1
- package/dist/Expression.js +151 -132
- package/dist/Expression.js.map +1 -1
- package/dist/Input.d.ts +251 -177
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +409 -299
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts +45 -85
- package/dist/Nom.d.ts.map +1 -1
- package/dist/Nom.js +423 -453
- package/dist/Nom.js.map +1 -1
- package/dist/ParseError.d.ts +54 -49
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js +232 -275
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +345 -313
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +2034 -1802
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.d.ts +206 -7
- package/dist/ParserSchema.d.ts.map +1 -1
- package/dist/ParserSchema.js +678 -49
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +26 -20
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +267 -255
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.d.ts +401 -0
- package/dist/Syntax.d.ts.map +1 -0
- package/dist/Syntax.js +717 -0
- package/dist/Syntax.js.map +1 -0
- package/dist/Text.d.ts +98 -134
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +534 -496
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +60 -42
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +331 -265
- package/dist/Token.js.map +1 -1
- package/dist/index.d.ts +16 -17
- package/dist/index.js +16 -17
- package/dist/internal/compiler/closure.d.ts +14 -16
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +1458 -1637
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/entries.d.ts +11 -11
- package/dist/internal/compiler/entries.d.ts.map +1 -1
- package/dist/internal/compiler/entries.js +36 -26
- package/dist/internal/compiler/entries.js.map +1 -1
- package/dist/internal/compiler/first.d.ts +19 -19
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +251 -227
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/jit.d.ts +6 -17
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +860 -988
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +20 -21
- package/dist/internal/compiler/registry.d.ts.map +1 -1
- package/dist/internal/compiler/registry.js +21 -41
- package/dist/internal/compiler/registry.js.map +1 -1
- package/dist/internal/compiler/sets.d.ts +9 -13
- package/dist/internal/compiler/sets.d.ts.map +1 -1
- package/dist/internal/compiler/sets.js +76 -73
- package/dist/internal/compiler/sets.js.map +1 -1
- package/dist/internal/entries.d.ts +3 -3
- package/dist/internal/entries.d.ts.map +1 -1
- package/dist/internal/entries.js +38 -30
- package/dist/internal/entries.js.map +1 -1
- package/dist/internal/node.d.ts +177 -173
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +77 -66
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/numbers.d.ts +7 -8
- package/dist/internal/numbers.d.ts.map +1 -1
- package/dist/internal/numbers.js +246 -276
- package/dist/internal/numbers.js.map +1 -1
- package/dist/internal/resume.d.ts +6 -24
- package/dist/internal/resume.d.ts.map +1 -1
- package/dist/internal/resume.js +30 -29
- package/dist/internal/resume.js.map +1 -1
- package/dist/internal/run.d.ts +15 -0
- package/dist/internal/run.d.ts.map +1 -0
- package/dist/internal/run.js +36 -0
- package/dist/internal/run.js.map +1 -0
- package/dist/internal/stack.d.ts +16 -0
- package/dist/internal/stack.d.ts.map +1 -0
- package/dist/internal/stack.js +36 -0
- package/dist/internal/stack.js.map +1 -0
- package/dist/internal/syntax.d.ts +147 -0
- package/dist/internal/syntax.d.ts.map +1 -0
- package/dist/internal/syntax.js +309 -0
- package/dist/internal/syntax.js.map +1 -0
- package/dist/internal/text.d.ts +10 -2
- package/dist/internal/text.d.ts.map +1 -1
- package/dist/internal/text.js +295 -345
- package/dist/internal/text.js.map +1 -1
- package/dist/internal/ties.d.ts +30 -0
- package/dist/internal/ties.d.ts.map +1 -0
- package/dist/internal/ties.js +67 -0
- package/dist/internal/ties.js.map +1 -0
- package/dist/internal/tokens.d.ts +25 -30
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +369 -363
- package/dist/internal/tokens.js.map +1 -1
- package/package.json +42 -28
- package/src/Ascii.ts +278 -187
- package/src/Binary.ts +150 -44
- package/src/Bits.ts +6 -3
- package/src/Compiler.ts +98 -0
- package/src/Expression.ts +24 -16
- package/src/Input.ts +210 -35
- package/src/Nom.ts +4 -3
- package/src/ParseError.ts +49 -52
- package/src/Parser.ts +1199 -799
- package/src/ParserSchema.ts +982 -23
- package/src/ParserStream.ts +104 -80
- package/src/Syntax.ts +900 -0
- package/src/Text.ts +272 -187
- package/src/Token.ts +180 -87
- package/src/index.ts +2 -0
- package/src/internal/compiler/closure.ts +121 -88
- package/src/internal/compiler/entries.ts +21 -8
- package/src/internal/compiler/first.ts +17 -15
- package/src/internal/compiler/jit.ts +360 -157
- package/src/internal/compiler/registry.ts +12 -33
- package/src/internal/compiler/sets.ts +17 -11
- package/src/internal/entries.ts +4 -2
- package/src/internal/node.ts +51 -37
- package/src/internal/numbers.ts +22 -20
- package/src/internal/resume.ts +8 -4
- package/src/internal/run.ts +41 -0
- package/src/internal/stack.ts +41 -0
- package/src/internal/syntax.ts +426 -0
- package/src/internal/text.ts +32 -25
- package/src/internal/ties.ts +83 -0
- package/src/internal/tokens.ts +96 -51
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
package/dist/Parser.js
CHANGED
|
@@ -1,1936 +1,2168 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
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
|
|
44
|
-
import
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
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
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
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
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
/**
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
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
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
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
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
/**
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
/**
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
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
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
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
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
/**
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
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
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
/**
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
/**
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
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
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
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
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
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
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
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
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
/**
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
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
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
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
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
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
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
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, {
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
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
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
}, "repeat", {
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
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
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
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
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
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
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
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
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
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
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
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
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
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
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
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
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
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
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
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
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
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
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
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
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
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
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
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
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
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
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
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
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
/**
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
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
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
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
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
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
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
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
|