@thomasfosterau/effect-parse 0.2.0 → 0.4.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 +44 -1
- package/dist/Ascii.d.ts +0 -2
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +6 -6
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts +0 -2
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +1 -2
- package/dist/Bits.d.ts +0 -2
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +2 -3
- package/dist/Bits.js.map +1 -1
- package/dist/Compiler.d.ts +0 -2
- package/dist/Compiler.d.ts.map +1 -1
- package/dist/Compiler.js +1 -2
- package/dist/Encoding.d.ts +0 -2
- package/dist/Encoding.d.ts.map +1 -1
- package/dist/Encoding.js +1 -2
- package/dist/Expression.d.ts +0 -2
- package/dist/Expression.d.ts.map +1 -1
- package/dist/Expression.js +61 -18
- package/dist/Expression.js.map +1 -1
- package/dist/Input.d.ts +80 -7
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +149 -18
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts +1 -3
- package/dist/Nom.d.ts.map +1 -1
- package/dist/Nom.js +1 -2
- package/dist/ParseError.d.ts +0 -2
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js +6 -10
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +2 -2
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +58 -32
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.d.ts +198 -2
- package/dist/ParserSchema.d.ts.map +1 -1
- package/dist/ParserSchema.js +646 -18
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +0 -2
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +15 -9
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.d.ts +0 -2
- package/dist/Syntax.d.ts.map +1 -1
- package/dist/Syntax.js +13 -11
- package/dist/Syntax.js.map +1 -1
- package/dist/Text.d.ts +0 -2
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +1 -2
- package/dist/Token.d.ts +0 -2
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +1 -2
- package/dist/internal/compiler/closure.d.ts +5 -2
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +160 -92
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/entries.d.ts +0 -2
- package/dist/internal/compiler/entries.d.ts.map +1 -1
- package/dist/internal/compiler/entries.js +6 -7
- package/dist/internal/compiler/entries.js.map +1 -1
- package/dist/internal/compiler/first.d.ts +0 -2
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +10 -10
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/jit.d.ts +0 -2
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +52 -51
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +0 -2
- package/dist/internal/compiler/registry.d.ts.map +1 -1
- package/dist/internal/compiler/registry.js +1 -2
- package/dist/internal/compiler/sets.d.ts +2 -2
- package/dist/internal/compiler/sets.d.ts.map +1 -1
- package/dist/internal/compiler/sets.js +8 -3
- package/dist/internal/compiler/sets.js.map +1 -1
- package/dist/internal/entries.d.ts +0 -2
- package/dist/internal/entries.d.ts.map +1 -1
- package/dist/internal/entries.js +1 -2
- package/dist/internal/key.d.ts +10 -0
- package/dist/internal/key.d.ts.map +1 -0
- package/dist/internal/key.js +11 -0
- package/dist/internal/key.js.map +1 -0
- package/dist/internal/node.d.ts +11 -4
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +15 -3
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/numbers.d.ts +0 -2
- package/dist/internal/numbers.d.ts.map +1 -1
- package/dist/internal/numbers.js +1 -2
- package/dist/internal/resume.d.ts +5 -6
- package/dist/internal/resume.d.ts.map +1 -1
- package/dist/internal/resume.js +1 -2
- package/dist/internal/resume.js.map +1 -1
- package/dist/internal/run.d.ts +0 -2
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +2 -3
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +0 -2
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +12 -3
- package/dist/internal/stack.js.map +1 -1
- package/dist/internal/syntax.d.ts +30 -19
- package/dist/internal/syntax.d.ts.map +1 -1
- package/dist/internal/syntax.js +74 -63
- package/dist/internal/syntax.js.map +1 -1
- package/dist/internal/text.d.ts +5 -2
- package/dist/internal/text.d.ts.map +1 -1
- package/dist/internal/text.js +14 -5
- package/dist/internal/text.js.map +1 -1
- package/dist/internal/ties.d.ts +28 -0
- package/dist/internal/ties.d.ts.map +1 -0
- package/dist/internal/ties.js +66 -0
- package/dist/internal/ties.js.map +1 -0
- package/dist/internal/tokens.d.ts +10 -9
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +4 -8
- package/dist/internal/tokens.js.map +1 -1
- package/package.json +6 -6
- package/src/Ascii.ts +6 -5
- package/src/Bits.ts +1 -1
- package/src/Expression.ts +27 -23
- package/src/Input.ts +157 -21
- package/src/ParseError.ts +10 -8
- package/src/Parser.ts +67 -32
- package/src/ParserSchema.ts +982 -23
- package/src/ParserStream.ts +20 -8
- package/src/Syntax.ts +12 -9
- package/src/internal/compiler/closure.ts +202 -90
- package/src/internal/compiler/entries.ts +14 -11
- package/src/internal/compiler/first.ts +9 -8
- package/src/internal/compiler/jit.ts +95 -74
- package/src/internal/compiler/sets.ts +7 -0
- package/src/internal/key.ts +11 -0
- package/src/internal/node.ts +19 -2
- package/src/internal/resume.ts +5 -4
- package/src/internal/run.ts +1 -1
- package/src/internal/stack.ts +6 -1
- package/src/internal/syntax.ts +80 -84
- package/src/internal/text.ts +24 -4
- package/src/internal/ties.ts +83 -0
- package/src/internal/tokens.ts +20 -13
package/dist/Expression.js
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as Predicate from "effect/Predicate";
|
|
2
|
+
import { Fail } from "./Input.js";
|
|
2
3
|
import { make as make$1 } from "./Parser.js";
|
|
3
|
-
|
|
4
|
+
import { TiedFailures } from "./internal/ties.js";
|
|
5
|
+
// src/Expression.ts
|
|
6
|
+
/**
|
|
7
|
+
* Operator-precedence expression parsing with a Pratt parser (from
|
|
8
|
+
* `nom-language`'s `precedence`, and winnow's expression parser).
|
|
9
|
+
*
|
|
10
|
+
* Describe the operand parser and a table of prefix, postfix and infix
|
|
11
|
+
* operators with their precedence (higher binds tighter) and associativity.
|
|
12
|
+
* Each operator carries a function that folds its operands into a result.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { Ascii, Expression, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
17
|
+
*
|
|
18
|
+
* const op = (s: string) => Parser.delimited(Ascii.space0, Token.literal(s), Ascii.space0)
|
|
19
|
+
*
|
|
20
|
+
* const arithmetic: Parser.Parser<number, string> = Expression.make({
|
|
21
|
+
* operand: Parser.alt(
|
|
22
|
+
* Ascii.decInt,
|
|
23
|
+
* Parser.delimited(op("("), Parser.suspend(() => arithmetic), op(")"))
|
|
24
|
+
* ),
|
|
25
|
+
* operators: [
|
|
26
|
+
* Expression.prefix(op("-"), 4, (_, x) => -x),
|
|
27
|
+
* Expression.postfix(op("!"), 5, (x) => factorial(x)),
|
|
28
|
+
* Expression.infixRight(op("^"), 3, (l, _, r) => l ** r),
|
|
29
|
+
* Expression.infixLeft(op("*"), 2, (l, _, r) => l * r),
|
|
30
|
+
* Expression.infixLeft(op("/"), 2, (l, _, r) => l / r),
|
|
31
|
+
* Expression.infixLeft(op("+"), 1, (l, _, r) => l + r),
|
|
32
|
+
* Expression.infixLeft(op("-"), 1, (l, _, r) => l - r)
|
|
33
|
+
* ]
|
|
34
|
+
* })
|
|
35
|
+
*
|
|
36
|
+
* declare const factorial: (n: number) => number
|
|
37
|
+
* Parser.parse(arithmetic, "1 + 2 * 3 ^ 2") // Result.succeed(19)
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @since 0.1.0
|
|
41
|
+
*/
|
|
4
42
|
/**
|
|
5
43
|
* A prefix operator, e.g. unary minus.
|
|
6
44
|
*
|
|
@@ -66,11 +104,11 @@ const infixRight = (parser, precedence, fold) => ({
|
|
|
66
104
|
*/
|
|
67
105
|
const make = (options) => {
|
|
68
106
|
const operand = options.operand.step;
|
|
69
|
-
const prefixes = options.operators.filter((o) => o
|
|
70
|
-
const postfixes = options.operators.filter((o) => o
|
|
71
|
-
const infixes = options.operators.filter((o) => o
|
|
107
|
+
const prefixes = options.operators.filter((o) => Predicate.isTagged(o, "Prefix"));
|
|
108
|
+
const postfixes = options.operators.filter((o) => Predicate.isTagged(o, "Postfix"));
|
|
109
|
+
const infixes = options.operators.filter((o) => Predicate.isTagged(o, "Infix"));
|
|
72
110
|
let matched;
|
|
73
|
-
|
|
111
|
+
const failures = new TiedFailures();
|
|
74
112
|
const tryOps = (state, ops, mustConsume) => {
|
|
75
113
|
const start = state.pos;
|
|
76
114
|
for (let i = 0; i < ops.length; i++) {
|
|
@@ -84,14 +122,14 @@ const make = (options) => {
|
|
|
84
122
|
return i;
|
|
85
123
|
}
|
|
86
124
|
if (state.mode !== "Backtrack") return -2;
|
|
87
|
-
|
|
88
|
-
state
|
|
125
|
+
state.rewind(start);
|
|
126
|
+
failures.add(state);
|
|
89
127
|
}
|
|
90
128
|
return -1;
|
|
91
129
|
};
|
|
92
130
|
const expr = (state, minPower) => {
|
|
93
131
|
let lhs;
|
|
94
|
-
|
|
132
|
+
failures.clear();
|
|
95
133
|
const p = tryOps(state, prefixes, true);
|
|
96
134
|
if (p === -2) return Fail;
|
|
97
135
|
if (p >= 0) {
|
|
@@ -101,39 +139,45 @@ const make = (options) => {
|
|
|
101
139
|
if (operandValue === Fail) return Fail;
|
|
102
140
|
lhs = op.fold(opOut, operandValue);
|
|
103
141
|
} else {
|
|
104
|
-
const prefixFailure = failure;
|
|
142
|
+
const prefixFailure = failures.failure;
|
|
143
|
+
const prefixTie1 = failures.tie1;
|
|
144
|
+
const prefixTie2 = failures.tie2;
|
|
105
145
|
lhs = operand(state);
|
|
106
146
|
if (lhs === Fail) {
|
|
107
|
-
if (prefixFailure !== void 0 && state.mode === "Backtrack")
|
|
147
|
+
if (prefixFailure !== void 0 && state.mode === "Backtrack") {
|
|
148
|
+
failures.failure = prefixFailure;
|
|
149
|
+
failures.tie1 = prefixTie1;
|
|
150
|
+
failures.tie2 = prefixTie2;
|
|
151
|
+
failures.add(state);
|
|
152
|
+
failures.fail(state);
|
|
153
|
+
}
|
|
108
154
|
return Fail;
|
|
109
155
|
}
|
|
110
156
|
}
|
|
111
157
|
while (true) {
|
|
112
158
|
const start = state.pos;
|
|
113
|
-
|
|
159
|
+
failures.clear();
|
|
114
160
|
const q = tryOps(state, postfixes, true);
|
|
115
161
|
if (q === -2) return Fail;
|
|
116
162
|
if (q >= 0) {
|
|
117
163
|
const op = postfixes[q];
|
|
118
164
|
if (op.precedence * 2 < minPower) {
|
|
119
|
-
state.
|
|
165
|
+
state.rewind(start);
|
|
120
166
|
break;
|
|
121
167
|
}
|
|
122
168
|
lhs = op.fold(lhs, matched);
|
|
123
169
|
continue;
|
|
124
170
|
}
|
|
125
|
-
const postfixFailure = failure;
|
|
126
171
|
const i = tryOps(state, infixes, false);
|
|
127
172
|
if (i === -2) return Fail;
|
|
128
173
|
if (i < 0) {
|
|
129
|
-
|
|
130
|
-
if (f !== void 0 && f.offset === state.pos) state.setHint(f.offset, f.expected);
|
|
174
|
+
failures.hint(state);
|
|
131
175
|
break;
|
|
132
176
|
}
|
|
133
177
|
const op = infixes[i];
|
|
134
178
|
const leftPower = op.precedence * 2;
|
|
135
179
|
if (leftPower < minPower) {
|
|
136
|
-
state.
|
|
180
|
+
state.rewind(start);
|
|
137
181
|
break;
|
|
138
182
|
}
|
|
139
183
|
const opOut = matched;
|
|
@@ -145,7 +189,6 @@ const make = (options) => {
|
|
|
145
189
|
};
|
|
146
190
|
return make$1((state) => expr(state, 0), "expression");
|
|
147
191
|
};
|
|
148
|
-
//#endregion
|
|
149
192
|
export { infixLeft, infixRight, make, postfix, prefix };
|
|
150
193
|
|
|
151
194
|
//# sourceMappingURL=Expression.js.map
|
package/dist/Expression.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Expression.js","names":["makeParser"],"sources":["../src/Expression.ts"],"sourcesContent":["/**\n * Operator-precedence expression parsing with a Pratt parser (from\n * `nom-language`'s `precedence`, and winnow's expression parser).\n *\n * Describe the operand parser and a table of prefix, postfix and infix\n * operators with their precedence (higher binds tighter) and associativity.\n * Each operator carries a function that folds its operands into a result.\n *\n * @example\n * ```ts\n * import { Ascii, Expression, Parser, Token } from \"@thomasfosterau/effect-parse\"\n *\n * const op = (s: string) => Parser.delimited(Ascii.space0, Token.literal(s), Ascii.space0)\n *\n * const arithmetic: Parser.Parser<number, string> = Expression.make({\n * operand: Parser.alt(\n * Ascii.decInt,\n * Parser.delimited(op(\"(\"), Parser.suspend(() => arithmetic), op(\")\"))\n * ),\n * operators: [\n * Expression.prefix(op(\"-\"), 4, (_, x) => -x),\n * Expression.postfix(op(\"!\"), 5, (x) => factorial(x)),\n * Expression.infixRight(op(\"^\"), 3, (l, _, r) => l ** r),\n * Expression.infixLeft(op(\"*\"), 2, (l, _, r) => l * r),\n * Expression.infixLeft(op(\"/\"), 2, (l, _, r) => l / r),\n * Expression.infixLeft(op(\"+\"), 1, (l, _, r) => l + r),\n * Expression.infixLeft(op(\"-\"), 1, (l, _, r) => l - r)\n * ]\n * })\n *\n * declare const factorial: (n: number) => number\n * Parser.parse(arithmetic, \"1 + 2 * 3 ^ 2\") // Result.succeed(19)\n * ```\n *\n * @since 0.1.0\n */\nimport type { Input, State } from \"./Input.js\"\nimport { Fail, mergeFailures } from \"./Input.js\"\nimport type { Failure } from \"./Input.js\"\nimport type { Parser } from \"./Parser.js\"\nimport { make as makeParser } from \"./Parser.js\"\n\n/**\n * An operator in an expression grammar.\n *\n * @since 0.1.0\n * @category models\n */\nexport type Operator<A, E = never, R = never, I extends Input = string> =\n | Prefix<A, E, R, I>\n | Postfix<A, E, R, I>\n | Infix<A, E, R, I>\n\ninterface OperatorBase<E, R, I extends Input> {\n readonly parser: Parser<unknown, E, R, I>\n readonly precedence: number\n}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport interface Prefix<A, E = never, R = never, I extends Input = string> extends OperatorBase<E, R, I> {\n readonly _tag: \"Prefix\"\n readonly fold: (op: any, operand: A) => A\n}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport interface Postfix<A, E = never, R = never, I extends Input = string> extends OperatorBase<E, R, I> {\n readonly _tag: \"Postfix\"\n readonly fold: (operand: A, op: any) => A\n}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport interface Infix<A, E = never, R = never, I extends Input = string> extends OperatorBase<E, R, I> {\n readonly _tag: \"Infix\"\n readonly associativity: \"left\" | \"right\"\n readonly fold: (left: A, op: any, right: A) => A\n}\n\n/**\n * A prefix operator, e.g. unary minus.\n *\n * @since 0.1.0\n * @category operators\n */\nexport const prefix = <O, E, R, I extends Input, A>(\n parser: Parser<O, E, R, I>,\n precedence: number,\n fold: (op: O, operand: A) => A\n): Prefix<A, E, R, I> => ({ _tag: \"Prefix\", parser, precedence, fold })\n\n/**\n * A postfix operator, e.g. factorial or a function call suffix.\n *\n * @since 0.1.0\n * @category operators\n */\nexport const postfix = <O, E, R, I extends Input, A>(\n parser: Parser<O, E, R, I>,\n precedence: number,\n fold: (operand: A, op: O) => A\n): Postfix<A, E, R, I> => ({ _tag: \"Postfix\", parser, precedence, fold })\n\n/**\n * A left-associative infix operator: `a - b - c` is `(a - b) - c`.\n *\n * @since 0.1.0\n * @category operators\n */\nexport const infixLeft = <O, E, R, I extends Input, A>(\n parser: Parser<O, E, R, I>,\n precedence: number,\n fold: (left: A, op: O, right: A) => A\n): Infix<A, E, R, I> => ({ _tag: \"Infix\", associativity: \"left\", parser, precedence, fold })\n\n/**\n * A right-associative infix operator: `a ^ b ^ c` is `a ^ (b ^ c)`.\n *\n * @since 0.1.0\n * @category operators\n */\nexport const infixRight = <O, E, R, I extends Input, A>(\n parser: Parser<O, E, R, I>,\n precedence: number,\n fold: (left: A, op: O, right: A) => A\n): Infix<A, E, R, I> => ({ _tag: \"Infix\", associativity: \"right\", parser, precedence, fold })\n\ntype OpError<Op> = Op extends OperatorBase<infer E, infer _R, infer _I> ? E : never\ntype OpServices<Op> = Op extends OperatorBase<infer _E, infer R, infer _I> ? R : never\ntype OpInput<Op> = [Op] extends [never] ? Input : [Op] extends [OperatorBase<infer _E, infer _R, infer I>] ? I : never\n\n/**\n * Builds an expression parser from an operand parser and an operator table.\n *\n * Precedences are compared numerically: higher binds tighter. A prefix\n * operator applies to everything that binds tighter than it, so with `-`\n * at precedence 4 and `^` at 3, `-2 ^ 2` parses as `(-2) ^ 2`.\n *\n * When several operators of the same kind match, the first one in the\n * table wins, so list longer operators (`<=`) before their prefixes (`<`).\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const make = <A, E, R, I extends Input, const Ops extends ReadonlyArray<Operator<A, any, any, any>>>(options: {\n readonly operand: Parser<A, E, R, I>\n readonly operators: Ops\n}): Parser<A, E | OpError<Ops[number]>, R | OpServices<Ops[number]>, I & OpInput<Ops[number]>> => {\n const operand = options.operand.step\n const prefixes = options.operators.filter((o): o is Prefix<A, any, any, any> => o._tag === \"Prefix\")\n const postfixes = options.operators.filter((o): o is Postfix<A, any, any, any> => o._tag === \"Postfix\")\n const infixes = options.operators.filter((o): o is Infix<A, any, any, any> => o._tag === \"Infix\")\n\n // Attempts each operator in turn. Returns the index of the first match\n // (with its output in `matched`), -1 if none matched, or -2 on a fatal\n // failure. Expectations of failed attempts are accumulated in `failure`.\n let matched: unknown\n let failure: Failure | undefined\n const tryOps = (state: State<any>, ops: ReadonlyArray<OperatorBase<any, any, any>>, mustConsume: boolean): number => {\n const start = state.pos\n for (let i = 0; i < ops.length; i++) {\n const out = ops[i].parser.step(state)\n if (out !== Fail) {\n // A prefix or postfix operator matching nothing would apply forever\n if (mustConsume && state.pos === start) {\n state.failMessage(\"Cut\", \"Assert\", \"prefix and postfix operators must always consume\")\n return -2\n }\n matched = out\n return i\n }\n if (state.mode !== \"Backtrack\") return -2\n failure = failure === undefined ? state.failure : mergeFailures(failure, state.failure)\n state.pos = start\n }\n return -1\n }\n\n const expr = (state: State<any>, minPower: number): A | Fail => {\n let lhs: A | Fail\n failure = undefined\n const p = tryOps(state, prefixes, true)\n if (p === -2) return Fail\n if (p >= 0) {\n const op = prefixes[p]\n const opOut = matched\n const operandValue = expr(state, op.precedence * 2)\n if (operandValue === Fail) return Fail\n lhs = op.fold(opOut, operandValue)\n } else {\n const prefixFailure = failure\n lhs = operand(state)\n if (lhs === Fail) {\n if (prefixFailure !== undefined && state.mode === \"Backtrack\") {\n state.failWith(\"Backtrack\", mergeFailures(prefixFailure, state.failure))\n }\n return Fail\n }\n }\n\n while (true) {\n const start = state.pos\n failure = undefined\n const q = tryOps(state, postfixes, true)\n if (q === -2) return Fail\n if (q >= 0) {\n const op = postfixes[q]\n if (op.precedence * 2 < minPower) {\n state.pos = start\n break\n }\n lhs = op.fold(lhs, matched)\n continue\n }\n const postfixFailure = failure\n const i = tryOps(state, infixes, false)\n if (i === -2) return Fail\n if (i < 0) {\n const f =\n postfixFailure === undefined\n ? failure\n : failure === undefined\n ? postfixFailure\n : mergeFailures(postfixFailure, failure)\n if (f !== undefined && f.offset === state.pos) state.setHint(f.offset, f.expected)\n break\n }\n const op = infixes[i]\n const leftPower = op.precedence * 2\n if (leftPower < minPower) {\n state.pos = start\n break\n }\n const opOut = matched\n const rhs = expr(state, op.associativity === \"left\" ? leftPower + 1 : leftPower)\n if (rhs === Fail) return Fail\n lhs = op.fold(lhs, opOut, rhs)\n }\n return lhs\n }\n\n return makeParser((state: State<any>) => expr(state, 0), \"expression\")\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Expression.js","names":["makeParser"],"sources":["../src/Expression.ts"],"sourcesContent":["/**\n * Operator-precedence expression parsing with a Pratt parser (from\n * `nom-language`'s `precedence`, and winnow's expression parser).\n *\n * Describe the operand parser and a table of prefix, postfix and infix\n * operators with their precedence (higher binds tighter) and associativity.\n * Each operator carries a function that folds its operands into a result.\n *\n * @example\n * ```ts\n * import { Ascii, Expression, Parser, Token } from \"@thomasfosterau/effect-parse\"\n *\n * const op = (s: string) => Parser.delimited(Ascii.space0, Token.literal(s), Ascii.space0)\n *\n * const arithmetic: Parser.Parser<number, string> = Expression.make({\n * operand: Parser.alt(\n * Ascii.decInt,\n * Parser.delimited(op(\"(\"), Parser.suspend(() => arithmetic), op(\")\"))\n * ),\n * operators: [\n * Expression.prefix(op(\"-\"), 4, (_, x) => -x),\n * Expression.postfix(op(\"!\"), 5, (x) => factorial(x)),\n * Expression.infixRight(op(\"^\"), 3, (l, _, r) => l ** r),\n * Expression.infixLeft(op(\"*\"), 2, (l, _, r) => l * r),\n * Expression.infixLeft(op(\"/\"), 2, (l, _, r) => l / r),\n * Expression.infixLeft(op(\"+\"), 1, (l, _, r) => l + r),\n * Expression.infixLeft(op(\"-\"), 1, (l, _, r) => l - r)\n * ]\n * })\n *\n * declare const factorial: (n: number) => number\n * Parser.parse(arithmetic, \"1 + 2 * 3 ^ 2\") // Result.succeed(19)\n * ```\n *\n * @since 0.1.0\n */\nimport * as Predicate from \"effect/Predicate\"\nimport type { Input, State } from \"./Input.js\"\nimport { Fail } from \"./Input.js\"\nimport type { Parser } from \"./Parser.js\"\nimport { make as makeParser } from \"./Parser.js\"\nimport { TiedFailures } from \"./internal/ties.js\"\n\n/**\n * An operator in an expression grammar.\n *\n * @since 0.1.0\n * @category models\n */\nexport type Operator<A, E = never, R = never, I extends Input = string> =\n | Prefix<A, E, R, I>\n | Postfix<A, E, R, I>\n | Infix<A, E, R, I>\n\ninterface OperatorBase<E, R, I extends Input> {\n readonly parser: Parser<unknown, E, R, I>\n readonly precedence: number\n}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport interface Prefix<A, E = never, R = never, I extends Input = string> extends OperatorBase<E, R, I> {\n readonly _tag: \"Prefix\"\n readonly fold: (op: any, operand: A) => A\n}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport interface Postfix<A, E = never, R = never, I extends Input = string> extends OperatorBase<E, R, I> {\n readonly _tag: \"Postfix\"\n readonly fold: (operand: A, op: any) => A\n}\n\n/**\n * @since 0.1.0\n * @category models\n */\nexport interface Infix<A, E = never, R = never, I extends Input = string> extends OperatorBase<E, R, I> {\n readonly _tag: \"Infix\"\n readonly associativity: \"left\" | \"right\"\n readonly fold: (left: A, op: any, right: A) => A\n}\n\n/**\n * A prefix operator, e.g. unary minus.\n *\n * @since 0.1.0\n * @category operators\n */\nexport const prefix = <O, E, R, I extends Input, A>(\n parser: Parser<O, E, R, I>,\n precedence: number,\n fold: (op: O, operand: A) => A\n): Prefix<A, E, R, I> => ({ _tag: \"Prefix\", parser, precedence, fold })\n\n/**\n * A postfix operator, e.g. factorial or a function call suffix.\n *\n * @since 0.1.0\n * @category operators\n */\nexport const postfix = <O, E, R, I extends Input, A>(\n parser: Parser<O, E, R, I>,\n precedence: number,\n fold: (operand: A, op: O) => A\n): Postfix<A, E, R, I> => ({ _tag: \"Postfix\", parser, precedence, fold })\n\n/**\n * A left-associative infix operator: `a - b - c` is `(a - b) - c`.\n *\n * @since 0.1.0\n * @category operators\n */\nexport const infixLeft = <O, E, R, I extends Input, A>(\n parser: Parser<O, E, R, I>,\n precedence: number,\n fold: (left: A, op: O, right: A) => A\n): Infix<A, E, R, I> => ({ _tag: \"Infix\", associativity: \"left\", parser, precedence, fold })\n\n/**\n * A right-associative infix operator: `a ^ b ^ c` is `a ^ (b ^ c)`.\n *\n * @since 0.1.0\n * @category operators\n */\nexport const infixRight = <O, E, R, I extends Input, A>(\n parser: Parser<O, E, R, I>,\n precedence: number,\n fold: (left: A, op: O, right: A) => A\n): Infix<A, E, R, I> => ({ _tag: \"Infix\", associativity: \"right\", parser, precedence, fold })\n\ntype OpError<Op> = Op extends OperatorBase<infer E, infer _R, infer _I> ? E : never\ntype OpServices<Op> = Op extends OperatorBase<infer _E, infer R, infer _I> ? R : never\ntype OpInput<Op> = [Op] extends [never] ? Input : [Op] extends [OperatorBase<infer _E, infer _R, infer I>] ? I : never\n\n/**\n * Builds an expression parser from an operand parser and an operator table.\n *\n * Precedences are compared numerically: higher binds tighter. A prefix\n * operator applies to everything that binds tighter than it, so with `-`\n * at precedence 4 and `^` at 3, `-2 ^ 2` parses as `(-2) ^ 2`.\n *\n * When several operators of the same kind match, the first one in the\n * table wins, so list longer operators (`<=`) before their prefixes (`<`).\n *\n * @since 0.1.0\n * @category constructors\n */\nexport const make = <A, E, R, I extends Input, const Ops extends ReadonlyArray<Operator<A, any, any, any>>>(options: {\n readonly operand: Parser<A, E, R, I>\n readonly operators: Ops\n}): Parser<A, E | OpError<Ops[number]>, R | OpServices<Ops[number]>, I & OpInput<Ops[number]>> => {\n const operand = options.operand.step\n const prefixes = options.operators.filter((o): o is Prefix<A, any, any, any> => Predicate.isTagged(o, \"Prefix\"))\n const postfixes = options.operators.filter((o): o is Postfix<A, any, any, any> => Predicate.isTagged(o, \"Postfix\"))\n const infixes = options.operators.filter((o): o is Infix<A, any, any, any> => Predicate.isTagged(o, \"Infix\"))\n\n // Attempts each operator in turn. Returns the index of the first match\n // (with its output in `matched`), -1 if none matched, or -2 on a fatal\n // failure. Failed attempts are folded into `failures`, merged only when\n // read (#44): a round in which an operator matches discards them unmerged.\n // Like `matched`, it is shared by nested expressions, which clear it.\n let matched: unknown\n const failures = new TiedFailures()\n const tryOps = (state: State<any>, ops: ReadonlyArray<OperatorBase<any, any, any>>, mustConsume: boolean): number => {\n const start = state.pos\n for (let i = 0; i < ops.length; i++) {\n const out = ops[i].parser.step(state)\n if (out !== Fail) {\n // A prefix or postfix operator matching nothing would apply forever\n if (mustConsume && state.pos === start) {\n state.failMessage(\"Cut\", \"Assert\", \"prefix and postfix operators must always consume\")\n return -2\n }\n matched = out\n return i\n }\n if (state.mode !== \"Backtrack\") return -2\n state.rewind(start)\n failures.add(state)\n }\n return -1\n }\n\n const expr = (state: State<any>, minPower: number): A | Fail => {\n let lhs: A | Fail\n failures.clear()\n const p = tryOps(state, prefixes, true)\n if (p === -2) return Fail\n if (p >= 0) {\n const op = prefixes[p]\n const opOut = matched\n const operandValue = expr(state, op.precedence * 2)\n if (operandValue === Fail) return Fail\n lhs = op.fold(opOut, operandValue)\n } else {\n const prefixFailure = failures.failure\n const prefixTie1 = failures.tie1\n const prefixTie2 = failures.tie2\n lhs = operand(state)\n if (lhs === Fail) {\n if (prefixFailure !== undefined && state.mode === \"Backtrack\") {\n // The operand may have parsed nested expressions, which reuse `failures`\n failures.failure = prefixFailure\n failures.tie1 = prefixTie1\n failures.tie2 = prefixTie2\n failures.add(state)\n failures.fail(state)\n }\n return Fail\n }\n }\n\n while (true) {\n const start = state.pos\n failures.clear()\n const q = tryOps(state, postfixes, true)\n if (q === -2) return Fail\n if (q >= 0) {\n const op = postfixes[q]\n if (op.precedence * 2 < minPower) {\n state.rewind(start)\n break\n }\n lhs = op.fold(lhs, matched)\n continue\n }\n // The infix attempts are folded on top of the postfix ones\n const i = tryOps(state, infixes, false)\n if (i === -2) return Fail\n if (i < 0) {\n failures.hint(state)\n break\n }\n const op = infixes[i]\n const leftPower = op.precedence * 2\n if (leftPower < minPower) {\n state.rewind(start)\n break\n }\n const opOut = matched\n const rhs = expr(state, op.associativity === \"left\" ? leftPower + 1 : leftPower)\n if (rhs === Fail) return Fail\n lhs = op.fold(lhs, opOut, rhs)\n }\n return lhs\n }\n\n return makeParser((state: State<any>) => expr(state, 0), \"expression\")\n}\n"],"mappings":"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"}
|
package/dist/Input.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import * as Context from "effect/Context";
|
|
|
2
2
|
import { ErrorKind } from "./ParseError.js";
|
|
3
3
|
import { Builder } from "./internal/syntax.js";
|
|
4
4
|
import { Encoding } from "./Encoding.js";
|
|
5
|
-
//#region src/Input.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* The kinds of input a parser can consume.
|
|
8
7
|
*
|
|
@@ -161,7 +160,10 @@ export interface TraceEvent {
|
|
|
161
160
|
export declare class State<I extends Input = Input> {
|
|
162
161
|
/** The full input being parsed. */
|
|
163
162
|
readonly input: I;
|
|
164
|
-
/**
|
|
163
|
+
/**
|
|
164
|
+
* The current offset. Parsers advance it by assigning it; moving it back
|
|
165
|
+
* (backtracking, or after a lookahead) goes through {@link rewind}.
|
|
166
|
+
*/
|
|
165
167
|
pos: number;
|
|
166
168
|
/** The end offset (the input's length; in bits inside `Bits.bits`). */
|
|
167
169
|
end: number;
|
|
@@ -173,8 +175,31 @@ export declare class State<I extends Input = Input> {
|
|
|
173
175
|
context: Context.Context<never>;
|
|
174
176
|
/** Mode of the latest failure. Only meaningful after a step returned {@link Fail}. */
|
|
175
177
|
mode: ErrMode;
|
|
176
|
-
/**
|
|
177
|
-
|
|
178
|
+
/**
|
|
179
|
+
* The latest failure, kept unmerged: the failure is `failureHead` merged
|
|
180
|
+
* (as `mergeFailures` does) with `failureTie1` and then `failureTie2`, all
|
|
181
|
+
* at the same offset. `Parser.alt` records its tied branch failures here
|
|
182
|
+
* with {@link failTies} instead of merging them, so that an enclosing `alt`
|
|
183
|
+
* whose next branch succeeds never pays for the merge. Reading
|
|
184
|
+
* {@link failure} performs the merge, once.
|
|
185
|
+
*
|
|
186
|
+
* @internal
|
|
187
|
+
*/
|
|
188
|
+
failureHead: Failure;
|
|
189
|
+
/** @internal */
|
|
190
|
+
failureTie1: Failure | undefined;
|
|
191
|
+
/** @internal */
|
|
192
|
+
failureTie2: Failure | undefined;
|
|
193
|
+
/** Backs {@link deepest}. @internal */
|
|
194
|
+
deepestFailure: Failure | undefined;
|
|
195
|
+
/**
|
|
196
|
+
* Whether the furthest failure is the latest failure, still unmerged, so
|
|
197
|
+
* that {@link deepest} is that failure once merged. Only set while
|
|
198
|
+
* `failureTie1` is.
|
|
199
|
+
*
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
deepestIsLatest: boolean;
|
|
178
203
|
/**
|
|
179
204
|
* The offset of a recoverable failure that occurred without consuming
|
|
180
205
|
* input (for example the last attempt of a `repeat`). Its
|
|
@@ -193,7 +218,8 @@ export declare class State<I extends Input = Input> {
|
|
|
193
218
|
* recorded inside it when it commits, and `Parser.peek` and `Parser.not`
|
|
194
219
|
* forget the failures of a lookahead that succeeded.
|
|
195
220
|
*/
|
|
196
|
-
deepest: Failure | undefined;
|
|
221
|
+
get deepest(): Failure | undefined;
|
|
222
|
+
set deepest(failure: Failure | undefined);
|
|
197
223
|
/** When defined, `Parser.resumeAfter`/`retryAfter` record failures here. */
|
|
198
224
|
recovered: Array<Failure> | undefined;
|
|
199
225
|
/** When defined, `Parser.trace` reports events here. */
|
|
@@ -213,7 +239,8 @@ export declare class State<I extends Input = Input> {
|
|
|
213
239
|
*/
|
|
214
240
|
resume: Map<object, Map<number, unknown>> | undefined;
|
|
215
241
|
/**
|
|
216
|
-
* The tree builder of a `Syntax` parse; `undefined` otherwise.
|
|
242
|
+
* The tree builder of a `Syntax` parse; `undefined` otherwise. A builder
|
|
243
|
+
* rather than a subclass of `State`, so that every state has one shape.
|
|
217
244
|
*
|
|
218
245
|
* @internal
|
|
219
246
|
*/
|
|
@@ -223,6 +250,11 @@ export declare class State<I extends Input = Input> {
|
|
|
223
250
|
readonly context?: Context.Context<never> | undefined;
|
|
224
251
|
readonly offset?: number | undefined;
|
|
225
252
|
});
|
|
253
|
+
/** Details of the latest failure. Only meaningful after a step returned {@link Fail}. */
|
|
254
|
+
get failure(): Failure;
|
|
255
|
+
set failure(failure: Failure);
|
|
256
|
+
/** Merges the latest failure's ties into it (see {@link failureHead}). */
|
|
257
|
+
private settleFailure;
|
|
226
258
|
/**
|
|
227
259
|
* Prepares the state for another run over the same input, starting at
|
|
228
260
|
* `offset`: afterwards it behaves exactly like
|
|
@@ -255,6 +287,32 @@ export declare class State<I extends Input = Input> {
|
|
|
255
287
|
readonly partial?: boolean | undefined;
|
|
256
288
|
readonly context?: Context.Context<never> | undefined;
|
|
257
289
|
}): this;
|
|
290
|
+
/**
|
|
291
|
+
* Moves the cursor back to `offset`, abandoning what was parsed after it:
|
|
292
|
+
* how a parser backtracks, or returns to where a lookahead started. A step
|
|
293
|
+
* that moves the cursor backwards must use this rather than assigning
|
|
294
|
+
* {@link pos}, so that a `Syntax` parse drops the nodes the abandoned
|
|
295
|
+
* attempt built. Moving forward is an assignment.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```ts
|
|
299
|
+
* import { Input, Parser } from "@thomasfosterau/effect-parse"
|
|
300
|
+
*
|
|
301
|
+
* // Like `Parser.opt`, for one parser
|
|
302
|
+
* const optional = <A>(p: Parser.Parser<A>) =>
|
|
303
|
+
* Parser.make<A | undefined>((state) => {
|
|
304
|
+
* const start = state.pos
|
|
305
|
+
* const a = p.step(state)
|
|
306
|
+
* if (a !== Input.Fail) return a
|
|
307
|
+
* if (state.mode !== "Backtrack") return Input.Fail
|
|
308
|
+
* state.rewind(start)
|
|
309
|
+
* return undefined
|
|
310
|
+
* })
|
|
311
|
+
* ```
|
|
312
|
+
*
|
|
313
|
+
* @since 0.4.0
|
|
314
|
+
*/
|
|
315
|
+
rewind(offset: number): void;
|
|
258
316
|
/**
|
|
259
317
|
* The number of input units (code units, bytes or elements) remaining.
|
|
260
318
|
*/
|
|
@@ -297,6 +355,14 @@ export declare class State<I extends Input = Input> {
|
|
|
297
355
|
* Records the given failure and returns {@link Fail}.
|
|
298
356
|
*/
|
|
299
357
|
failWith(mode: ErrMode, failure: Failure): Fail;
|
|
358
|
+
/**
|
|
359
|
+
* Records a recoverable failure made of tied failures, merged only when
|
|
360
|
+
* read: `failWith("Backtrack", f)` where `f` merges `head` with `tie1`
|
|
361
|
+
* and then `tie2` (all at the same offset), as `mergeFailures` would.
|
|
362
|
+
*
|
|
363
|
+
* @internal
|
|
364
|
+
*/
|
|
365
|
+
failTies(head: Failure, tie1: Failure, tie2: Failure | undefined): Fail;
|
|
300
366
|
/**
|
|
301
367
|
* Replaces the details of the current failure (for example to add context),
|
|
302
368
|
* keeping the record of the furthest failure consistent.
|
|
@@ -326,5 +392,12 @@ export declare const mergeExpected: (a: ReadonlyArray<string>, b: ReadonlyArray<
|
|
|
326
392
|
* @internal
|
|
327
393
|
*/
|
|
328
394
|
export declare const mergeFailures: (a: Failure, b: Failure) => Failure;
|
|
329
|
-
|
|
395
|
+
/**
|
|
396
|
+
* {@link mergeFailures} for two failures at the same offset, given their
|
|
397
|
+
* merged expectations (`mergeExpected(a.expected, b.expected)`, possibly
|
|
398
|
+
* memoised by the caller).
|
|
399
|
+
*
|
|
400
|
+
* @internal
|
|
401
|
+
*/
|
|
402
|
+
export declare const mergeTiedFailures: (a: Failure, b: Failure, expected: ReadonlyArray<string>) => Failure;
|
|
330
403
|
//# sourceMappingURL=Input.d.ts.map
|
package/dist/Input.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.d.ts","names":["SyntaxBuilder"],"sources":["../src/Input.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"Input.d.ts","names":["SyntaxBuilder"],"sources":["../src/Input.ts"],"mappings":";;;;;;;;;;;;;;;YAiCY,iBAAiB,aAAa;;;;;;;;;;;;;YAc9B,cAAc,kBAAkB;;;;;;;;YAShC,OAAO,KAAK,MAAM,YAAY,cAAc;;;;;;;;;;;;;;;qBAgB3C;GACV,QAAQ,cAAc;GACtB,UAAU,OAAO,QAAQ,OAAO,KAAK;;;;;;;;YAkB5B,MAAM,KAAK,4BAEnB,UAAU,sBAER,UAAU,oBAAoB,KAC5B;;;;;;;YASI,MAAM,KAAK,4BAEnB,UAAU,aACR,aACA,UAAU,oBAAoB,KAC5B,cAAc;;;;;;;;qBAUT;;;;;YAMD,cAAc;;;;;;;;;;;YAYd;;;;;;;qBAQC;;WAGA;;WAEA,MAAM;;WAEN,UAAU;;WAEV,SAAS;;WAET;;WAEA;;WAEA;;WAEA;;;EAdA;;EAEA,MAAM;;EAEN,UAAU;;EAEV,SAAS;;EAET;;EAEA;;EAEA;;EAEA;;;qBAUA;;;;;;;iBAQI;WACN;WACA;WACA;WACA;;WAEA,gBAAgB;;WAEhB;WACA,OAAO;;;;;;;;qBASL,MAAM,UAAU,QAAQ;;WAE1B,OAAO;;;;;EAKhB;;EAEA;;WAES;;EAET;;EAEA,SAAS,QAAQ;;EAEjB,MAAM;;;;;;;;;;;EAWN,aAAa;;EAEb,aAAa;;EAEb,aAAa;;EAEb,gBAAgB;;;;;;;;EAQhB;;;;;;;EAOA;;EAEA,cAAc;;;;;;;;;;MAUV,WAAW;MAIX,QAAQ,SAAS;;EAKrB,WAAW,MAAM;;EAEjB,UAAU,OAAO;;EAEjB;;;;;EAKA,UAAU;;;;;;;EAOV,QAAQ,YAAY;;;;;;;EAOpB,QAAQA;EAGN,YAAA,QAAQ,IAAI,OAAO,IACnB;aACW;aACA,UAAU,QAAQ;aAClB;;;MAcT,WAAW;MAIX,QAAQ,SAAS;;UAQb;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6CR,MACE,iBACA;aACW;aACA,UAAU,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgD/B,OAAO;;;;EASP;;;;EAOA;;;;EAOA,MAAM,eAAe,cAAc,MAAM;;;;EAezC,UAAU,MAAM,WAAW,WAAU,uBAA+B,kBAA4B;;;;EAOhG,IAAI,MAAM,WAAW,WAAU,uBAA+B,kBAA4B;;;;;EAQ1F,WAAW,gBAAgB,WAAU,wBAAgC;;;;;EAWrE,IAAI,MAAM,WAAW,UAAU,uBAAuB,iBAAiB;;;;EAOvE,UAAU,MAAM,SAAS,MAAM,WAAW,gBAAgB,kBAA4B;;;;EAOtF,YAAY,MAAM,SAAS,MAAM,WAAW,iBAAiB,kBAA4B;;;;EAOzF,SAAS,MAAM,SAAS,SAAS,UAAU;;;;;;;;EAwB3C,SAAS,MAAM,SAAS,MAAM,SAAS,MAAM,sBAAsB;;;;;EAenE,eAAe,SAAS;;;;;;EAUxB,mBAAmB;;;;;;EAwBnB,QAAQ,gBAAgB,UAAU;;EAWlC,YAAY,gBAAgB,MAAM,WAAW,UAAU,wBAAwB;;;qBASpE,gBAAiB,GAAG,uBAAuB,GAAG,0BAAwB;;;;;;;qBAgBtE,gBAAiB,GAAG,SAAS,GAAG,YAAU;;;;;;;;qBAa1C,oBAAqB,GAAG,SAAS,GAAG,SAAS,UAAU,0BAAwB"}
|
package/dist/Input.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as Predicate from "effect/Predicate";
|
|
2
|
+
import * as Arr from "effect/Array";
|
|
2
3
|
import * as Context from "effect/Context";
|
|
3
|
-
|
|
4
|
+
// src/Input.ts
|
|
4
5
|
/**
|
|
5
6
|
* Input types and the mutable parse {@link State} cursor.
|
|
6
7
|
*
|
|
@@ -85,6 +86,11 @@ const emptyStrings = empty;
|
|
|
85
86
|
var State = class {
|
|
86
87
|
/** The full input being parsed. */
|
|
87
88
|
input;
|
|
89
|
+
/**
|
|
90
|
+
* The current offset. Parsers advance it by assigning it; moving it back
|
|
91
|
+
* (backtracking, or after a lookahead) goes through {@link rewind}.
|
|
92
|
+
*/
|
|
93
|
+
pos;
|
|
88
94
|
/** The end offset (the input's length; in bits inside `Bits.bits`). */
|
|
89
95
|
end;
|
|
90
96
|
/** 0 = string, 1 = Uint8Array, 2 = array. */
|
|
@@ -95,8 +101,31 @@ var State = class {
|
|
|
95
101
|
context;
|
|
96
102
|
/** Mode of the latest failure. Only meaningful after a step returned {@link Fail}. */
|
|
97
103
|
mode = "Backtrack";
|
|
98
|
-
/**
|
|
99
|
-
failure
|
|
104
|
+
/**
|
|
105
|
+
* The latest failure, kept unmerged: the failure is `failureHead` merged
|
|
106
|
+
* (as `mergeFailures` does) with `failureTie1` and then `failureTie2`, all
|
|
107
|
+
* at the same offset. `Parser.alt` records its tied branch failures here
|
|
108
|
+
* with {@link failTies} instead of merging them, so that an enclosing `alt`
|
|
109
|
+
* whose next branch succeeds never pays for the merge. Reading
|
|
110
|
+
* {@link failure} performs the merge, once.
|
|
111
|
+
*
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
failureHead;
|
|
115
|
+
/** @internal */
|
|
116
|
+
failureTie1 = void 0;
|
|
117
|
+
/** @internal */
|
|
118
|
+
failureTie2 = void 0;
|
|
119
|
+
/** Backs {@link deepest}. @internal */
|
|
120
|
+
deepestFailure = void 0;
|
|
121
|
+
/**
|
|
122
|
+
* Whether the furthest failure is the latest failure, still unmerged, so
|
|
123
|
+
* that {@link deepest} is that failure once merged. Only set while
|
|
124
|
+
* `failureTie1` is.
|
|
125
|
+
*
|
|
126
|
+
* @internal
|
|
127
|
+
*/
|
|
128
|
+
deepestIsLatest = false;
|
|
100
129
|
/**
|
|
101
130
|
* The offset of a recoverable failure that occurred without consuming
|
|
102
131
|
* input (for example the last attempt of a `repeat`). Its
|
|
@@ -115,7 +144,14 @@ var State = class {
|
|
|
115
144
|
* recorded inside it when it commits, and `Parser.peek` and `Parser.not`
|
|
116
145
|
* forget the failures of a lookahead that succeeded.
|
|
117
146
|
*/
|
|
118
|
-
deepest
|
|
147
|
+
get deepest() {
|
|
148
|
+
if (this.deepestIsLatest) this.settleFailure();
|
|
149
|
+
return this.deepestFailure;
|
|
150
|
+
}
|
|
151
|
+
set deepest(failure) {
|
|
152
|
+
this.deepestIsLatest = false;
|
|
153
|
+
this.deepestFailure = failure;
|
|
154
|
+
}
|
|
119
155
|
/** When defined, `Parser.resumeAfter`/`retryAfter` record failures here. */
|
|
120
156
|
recovered = void 0;
|
|
121
157
|
/** When defined, `Parser.trace` reports events here. */
|
|
@@ -135,7 +171,8 @@ var State = class {
|
|
|
135
171
|
*/
|
|
136
172
|
resume = void 0;
|
|
137
173
|
/**
|
|
138
|
-
* The tree builder of a `Syntax` parse; `undefined` otherwise.
|
|
174
|
+
* The tree builder of a `Syntax` parse; `undefined` otherwise. A builder
|
|
175
|
+
* rather than a subclass of `State`, so that every state has one shape.
|
|
139
176
|
*
|
|
140
177
|
* @internal
|
|
141
178
|
*/
|
|
@@ -148,7 +185,35 @@ var State = class {
|
|
|
148
185
|
this.partial = options?.partial ?? false;
|
|
149
186
|
this.context = options?.context ?? Context.empty();
|
|
150
187
|
this.inputKind = Predicate.isString(input) ? 0 : input instanceof Uint8Array ? 1 : 2;
|
|
151
|
-
this.
|
|
188
|
+
this.failureHead = initialFailure;
|
|
189
|
+
}
|
|
190
|
+
/** Details of the latest failure. Only meaningful after a step returned {@link Fail}. */
|
|
191
|
+
get failure() {
|
|
192
|
+
if (this.failureTie1 !== void 0) this.settleFailure();
|
|
193
|
+
return this.failureHead;
|
|
194
|
+
}
|
|
195
|
+
set failure(failure) {
|
|
196
|
+
if (this.deepestIsLatest) this.settleFailure();
|
|
197
|
+
this.failureHead = failure;
|
|
198
|
+
this.failureTie1 = void 0;
|
|
199
|
+
this.failureTie2 = void 0;
|
|
200
|
+
}
|
|
201
|
+
/** Merges the latest failure's ties into it (see {@link failureHead}). */
|
|
202
|
+
settleFailure() {
|
|
203
|
+
let failure = this.failureHead;
|
|
204
|
+
const tie1 = this.failureTie1;
|
|
205
|
+
if (tie1 !== void 0) {
|
|
206
|
+
failure = mergeTiedFailures(failure, tie1, mergeExpected(failure.expected, tie1.expected));
|
|
207
|
+
const tie2 = this.failureTie2;
|
|
208
|
+
if (tie2 !== void 0) failure = mergeTiedFailures(failure, tie2, mergeExpected(failure.expected, tie2.expected));
|
|
209
|
+
this.failureHead = failure;
|
|
210
|
+
this.failureTie1 = void 0;
|
|
211
|
+
this.failureTie2 = void 0;
|
|
212
|
+
}
|
|
213
|
+
if (this.deepestIsLatest) {
|
|
214
|
+
this.deepestFailure = failure;
|
|
215
|
+
this.deepestIsLatest = false;
|
|
216
|
+
}
|
|
152
217
|
}
|
|
153
218
|
/**
|
|
154
219
|
* Prepares the state for another run over the same input, starting at
|
|
@@ -184,10 +249,13 @@ var State = class {
|
|
|
184
249
|
this.partial = options?.partial ?? false;
|
|
185
250
|
this.context = options?.context ?? Context.empty();
|
|
186
251
|
this.mode = "Backtrack";
|
|
187
|
-
this.
|
|
252
|
+
this.failureHead = initialFailure;
|
|
253
|
+
this.failureTie1 = void 0;
|
|
254
|
+
this.failureTie2 = void 0;
|
|
188
255
|
this.hintOffset = -1;
|
|
189
256
|
this.hintExpected = empty;
|
|
190
|
-
this.
|
|
257
|
+
this.deepestFailure = void 0;
|
|
258
|
+
this.deepestIsLatest = false;
|
|
191
259
|
this.recovered = void 0;
|
|
192
260
|
this.tracer = void 0;
|
|
193
261
|
this.depth = 0;
|
|
@@ -196,6 +264,36 @@ var State = class {
|
|
|
196
264
|
return this;
|
|
197
265
|
}
|
|
198
266
|
/**
|
|
267
|
+
* Moves the cursor back to `offset`, abandoning what was parsed after it:
|
|
268
|
+
* how a parser backtracks, or returns to where a lookahead started. A step
|
|
269
|
+
* that moves the cursor backwards must use this rather than assigning
|
|
270
|
+
* {@link pos}, so that a `Syntax` parse drops the nodes the abandoned
|
|
271
|
+
* attempt built. Moving forward is an assignment.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```ts
|
|
275
|
+
* import { Input, Parser } from "@thomasfosterau/effect-parse"
|
|
276
|
+
*
|
|
277
|
+
* // Like `Parser.opt`, for one parser
|
|
278
|
+
* const optional = <A>(p: Parser.Parser<A>) =>
|
|
279
|
+
* Parser.make<A | undefined>((state) => {
|
|
280
|
+
* const start = state.pos
|
|
281
|
+
* const a = p.step(state)
|
|
282
|
+
* if (a !== Input.Fail) return a
|
|
283
|
+
* if (state.mode !== "Backtrack") return Input.Fail
|
|
284
|
+
* state.rewind(start)
|
|
285
|
+
* return undefined
|
|
286
|
+
* })
|
|
287
|
+
* ```
|
|
288
|
+
*
|
|
289
|
+
* @since 0.4.0
|
|
290
|
+
*/
|
|
291
|
+
rewind(offset) {
|
|
292
|
+
const syntax = this.syntax;
|
|
293
|
+
if (syntax !== void 0 && offset < this.pos) syntax.rewound(this.pos, offset);
|
|
294
|
+
this.pos = offset;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
199
297
|
* The number of input units (code units, bytes or elements) remaining.
|
|
200
298
|
*/
|
|
201
299
|
remaining() {
|
|
@@ -261,8 +359,32 @@ var State = class {
|
|
|
261
359
|
*/
|
|
262
360
|
failWith(mode, failure) {
|
|
263
361
|
this.mode = mode;
|
|
264
|
-
this.
|
|
265
|
-
|
|
362
|
+
if (this.deepestIsLatest) {
|
|
363
|
+
if (mode === "Backtrack" && failure.offset >= this.failureHead.offset) this.deepestIsLatest = false;
|
|
364
|
+
else this.settleFailure();
|
|
365
|
+
}
|
|
366
|
+
this.failureHead = failure;
|
|
367
|
+
this.failureTie1 = void 0;
|
|
368
|
+
this.failureTie2 = void 0;
|
|
369
|
+
if (mode === "Backtrack" && (this.deepestFailure === void 0 || failure.offset >= this.deepestFailure.offset)) this.deepestFailure = failure;
|
|
370
|
+
if (this.syntax !== void 0) this.syntax.reach(failure.offset);
|
|
371
|
+
return Fail;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Records a recoverable failure made of tied failures, merged only when
|
|
375
|
+
* read: `failWith("Backtrack", f)` where `f` merges `head` with `tie1`
|
|
376
|
+
* and then `tie2` (all at the same offset), as `mergeFailures` would.
|
|
377
|
+
*
|
|
378
|
+
* @internal
|
|
379
|
+
*/
|
|
380
|
+
failTies(head, tie1, tie2) {
|
|
381
|
+
this.mode = "Backtrack";
|
|
382
|
+
if (this.deepestIsLatest && head.offset < this.failureHead.offset) this.settleFailure();
|
|
383
|
+
this.failureHead = head;
|
|
384
|
+
this.failureTie1 = tie1;
|
|
385
|
+
this.failureTie2 = tie2;
|
|
386
|
+
this.deepestIsLatest = this.deepestFailure === void 0 || head.offset >= this.deepestFailure.offset;
|
|
387
|
+
if (this.syntax !== void 0) this.syntax.reach(head.offset);
|
|
266
388
|
return Fail;
|
|
267
389
|
}
|
|
268
390
|
/**
|
|
@@ -282,7 +404,7 @@ var State = class {
|
|
|
282
404
|
const f = this.failure;
|
|
283
405
|
const d = this.deepest;
|
|
284
406
|
if (d === void 0 || d === f || d.offset <= f.offset || this.mode === "Incomplete" || f.hasCause || f.message !== void 0) return f;
|
|
285
|
-
const context = d.context
|
|
407
|
+
const context = Arr.isReadonlyArrayEmpty(d.context) ? f.context : [...d.context, ...f.context.filter((l) => !d.context.includes(l))];
|
|
286
408
|
return new Failure(d.offset, d.kind, d.expected, context, d.hasCause, d.cause, d.message, d.needed);
|
|
287
409
|
}
|
|
288
410
|
/**
|
|
@@ -291,6 +413,7 @@ var State = class {
|
|
|
291
413
|
* `repeat` and `Ascii.digit1`.
|
|
292
414
|
*/
|
|
293
415
|
setHint(offset, expected) {
|
|
416
|
+
if (this.syntax !== void 0) this.syntax.reach(offset);
|
|
294
417
|
if (this.hintOffset === offset) this.hintExpected = mergeExpected(this.hintExpected, expected);
|
|
295
418
|
else {
|
|
296
419
|
this.hintOffset = offset;
|
|
@@ -299,14 +422,14 @@ var State = class {
|
|
|
299
422
|
}
|
|
300
423
|
/** @internal */
|
|
301
424
|
makeFailure(offset, kind, expected) {
|
|
302
|
-
if (this.hintOffset === offset && this.hintExpected
|
|
425
|
+
if (this.hintOffset === offset && Arr.isReadonlyArrayNonEmpty(this.hintExpected)) expected = mergeExpected(this.hintExpected, expected);
|
|
303
426
|
return new Failure(offset, kind, expected, empty, false, void 0, void 0, 0);
|
|
304
427
|
}
|
|
305
428
|
};
|
|
306
429
|
/** @internal */
|
|
307
430
|
const mergeExpected = (a, b) => {
|
|
308
|
-
if (a
|
|
309
|
-
if (b
|
|
431
|
+
if (Arr.isReadonlyArrayEmpty(a)) return b;
|
|
432
|
+
if (Arr.isReadonlyArrayEmpty(b)) return a;
|
|
310
433
|
const out = a.slice();
|
|
311
434
|
for (const e of b) if (!out.includes(e)) out.push(e);
|
|
312
435
|
return out;
|
|
@@ -320,11 +443,19 @@ const mergeExpected = (a, b) => {
|
|
|
320
443
|
const mergeFailures = (a, b) => {
|
|
321
444
|
if (a.offset > b.offset) return a;
|
|
322
445
|
if (b.offset > a.offset) return b;
|
|
323
|
-
|
|
446
|
+
return mergeTiedFailures(a, b, mergeExpected(a.expected, b.expected));
|
|
447
|
+
};
|
|
448
|
+
/**
|
|
449
|
+
* {@link mergeFailures} for two failures at the same offset, given their
|
|
450
|
+
* merged expectations (`mergeExpected(a.expected, b.expected)`, possibly
|
|
451
|
+
* memoised by the caller).
|
|
452
|
+
*
|
|
453
|
+
* @internal
|
|
454
|
+
*/
|
|
455
|
+
const mergeTiedFailures = (a, b, expected) => {
|
|
324
456
|
if (expected === b.expected) return b;
|
|
325
|
-
return new Failure(b.offset, b.kind, expected, b.context
|
|
457
|
+
return new Failure(b.offset, b.kind, expected, Arr.isReadonlyArrayNonEmpty(b.context) ? b.context : a.context, b.hasCause || a.hasCause, b.hasCause ? b.cause : a.cause, b.message ?? a.message, b.needed);
|
|
326
458
|
};
|
|
327
|
-
|
|
328
|
-
export { Fail, Failure, State, emptyStrings, fromSource, mergeExpected, mergeFailures };
|
|
459
|
+
export { Fail, Failure, State, emptyStrings, fromSource, mergeExpected, mergeFailures, mergeTiedFailures };
|
|
329
460
|
|
|
330
461
|
//# sourceMappingURL=Input.js.map
|