@thomasfosterau/effect-parse 0.0.0-stage → 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +550 -2
- package/dist/Ascii.d.ts +392 -0
- package/dist/Ascii.d.ts.map +1 -0
- package/dist/Ascii.js +610 -0
- package/dist/Ascii.js.map +1 -0
- package/dist/Binary.d.ts +153 -0
- package/dist/Binary.d.ts.map +1 -0
- package/dist/Binary.js +171 -0
- package/dist/Binary.js.map +1 -0
- package/dist/Bits.d.ts +60 -0
- package/dist/Bits.d.ts.map +1 -0
- package/dist/Bits.js +144 -0
- package/dist/Bits.js.map +1 -0
- package/dist/Encoding.d.ts +116 -0
- package/dist/Encoding.d.ts.map +1 -0
- package/dist/Encoding.js +85 -0
- package/dist/Encoding.js.map +1 -0
- package/dist/Expression.d.ts +124 -0
- package/dist/Expression.d.ts.map +1 -0
- package/dist/Expression.js +139 -0
- package/dist/Expression.js.map +1 -0
- package/dist/Input.d.ts +301 -0
- package/dist/Input.d.ts.map +1 -0
- package/dist/Input.js +312 -0
- package/dist/Input.js.map +1 -0
- package/dist/Nom.d.ts +518 -0
- package/dist/Nom.d.ts.map +1 -0
- package/dist/Nom.js +519 -0
- package/dist/Nom.js.map +1 -0
- package/dist/ParseError.d.ts +120 -0
- package/dist/ParseError.d.ts.map +1 -0
- package/dist/ParseError.js +301 -0
- package/dist/ParseError.js.map +1 -0
- package/dist/Parser.d.ts +1312 -0
- package/dist/Parser.d.ts.map +1 -0
- package/dist/Parser.js +1936 -0
- package/dist/Parser.js.map +1 -0
- package/dist/ParserSchema.d.ts +31 -0
- package/dist/ParserSchema.d.ts.map +1 -0
- package/dist/ParserSchema.js +56 -0
- package/dist/ParserSchema.js.map +1 -0
- package/dist/ParserStream.d.ts +127 -0
- package/dist/ParserStream.d.ts.map +1 -0
- package/dist/ParserStream.js +270 -0
- package/dist/ParserStream.js.map +1 -0
- package/dist/Text.d.ts +330 -0
- package/dist/Text.d.ts.map +1 -0
- package/dist/Text.js +529 -0
- package/dist/Text.js.map +1 -0
- package/dist/Token.d.ts +225 -0
- package/dist/Token.d.ts.map +1 -0
- package/dist/Token.js +294 -0
- package/dist/Token.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/compiler/closure.d.ts +25 -0
- package/dist/internal/compiler/closure.d.ts.map +1 -0
- package/dist/internal/compiler/closure.js +1668 -0
- package/dist/internal/compiler/closure.js.map +1 -0
- package/dist/internal/compiler/entries.d.ts +19 -0
- package/dist/internal/compiler/entries.d.ts.map +1 -0
- package/dist/internal/compiler/entries.js +32 -0
- package/dist/internal/compiler/entries.js.map +1 -0
- package/dist/internal/compiler/first.d.ts +33 -0
- package/dist/internal/compiler/first.d.ts.map +1 -0
- package/dist/internal/compiler/first.js +235 -0
- package/dist/internal/compiler/first.js.map +1 -0
- package/dist/internal/compiler/jit.d.ts +20 -0
- package/dist/internal/compiler/jit.d.ts.map +1 -0
- package/dist/internal/compiler/jit.js +1030 -0
- package/dist/internal/compiler/jit.js.map +1 -0
- package/dist/internal/compiler/registry.d.ts +34 -0
- package/dist/internal/compiler/registry.d.ts.map +1 -0
- package/dist/internal/compiler/registry.js +46 -0
- package/dist/internal/compiler/registry.js.map +1 -0
- package/dist/internal/compiler/sets.d.ts +29 -0
- package/dist/internal/compiler/sets.d.ts.map +1 -0
- package/dist/internal/compiler/sets.js +85 -0
- package/dist/internal/compiler/sets.js.map +1 -0
- package/dist/internal/entries.d.ts +27 -0
- package/dist/internal/entries.d.ts.map +1 -0
- package/dist/internal/entries.js +42 -0
- package/dist/internal/entries.js.map +1 -0
- package/dist/internal/node.d.ts +251 -0
- package/dist/internal/node.d.ts.map +1 -0
- package/dist/internal/node.js +73 -0
- package/dist/internal/node.js.map +1 -0
- package/dist/internal/numbers.d.ts +41 -0
- package/dist/internal/numbers.d.ts.map +1 -0
- package/dist/internal/numbers.js +302 -0
- package/dist/internal/numbers.js.map +1 -0
- package/dist/internal/resume.d.ts +39 -0
- package/dist/internal/resume.d.ts.map +1 -0
- package/dist/internal/resume.js +37 -0
- package/dist/internal/resume.js.map +1 -0
- package/dist/internal/text.d.ts +72 -0
- package/dist/internal/text.d.ts.map +1 -0
- package/dist/internal/text.js +391 -0
- package/dist/internal/text.js.map +1 -0
- package/dist/internal/tokens.d.ts +93 -0
- package/dist/internal/tokens.d.ts.map +1 -0
- package/dist/internal/tokens.js +400 -0
- package/dist/internal/tokens.js.map +1 -0
- package/package.json +115 -3
- package/src/Ascii.ts +741 -0
- package/src/Binary.ts +228 -0
- package/src/Bits.ts +192 -0
- package/src/Encoding.ts +128 -0
- package/src/Expression.ts +245 -0
- package/src/Input.ts +429 -0
- package/src/Nom.ts +642 -0
- package/src/ParseError.ts +378 -0
- package/src/Parser.ts +2763 -0
- package/src/ParserSchema.ts +87 -0
- package/src/ParserStream.ts +390 -0
- package/src/Text.ts +757 -0
- package/src/Token.ts +416 -0
- package/src/index.ts +16 -0
- package/src/internal/compiler/closure.ts +1583 -0
- package/src/internal/compiler/entries.ts +36 -0
- package/src/internal/compiler/first.ts +252 -0
- package/src/internal/compiler/jit.ts +1091 -0
- package/src/internal/compiler/registry.ts +79 -0
- package/src/internal/compiler/sets.ts +100 -0
- package/src/internal/entries.ts +51 -0
- package/src/internal/node.ts +207 -0
- package/src/internal/numbers.ts +291 -0
- package/src/internal/resume.ts +55 -0
- package/src/internal/text.ts +411 -0
- package/src/internal/tokens.ts +409 -0
|
@@ -0,0 +1,1030 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The JIT: emits JavaScript source for a parser's description and builds
|
|
3
|
+
* it with `new Function`.
|
|
4
|
+
*
|
|
5
|
+
* Every rule (the root, every `suspend` target and every non-leaf parser
|
|
6
|
+
* with several parents) becomes a function `f(p)` taking the cursor and
|
|
7
|
+
* returning the value, or `Fail`, leaving the new cursor in the register
|
|
8
|
+
* `P`. Everything else is inlined as straight-line code, with labelled
|
|
9
|
+
* blocks for failure. The cursor is a local; the input and its end are
|
|
10
|
+
* hoisted into locals. Failures only set `state.mode`, as in the closure
|
|
11
|
+
* compiler.
|
|
12
|
+
*
|
|
13
|
+
* Nodes without an inline form run their closure-compiled steps (which run
|
|
14
|
+
* the interpreter for what they can't compile) on the shared state, with
|
|
15
|
+
* `state.pos` kept in sync.
|
|
16
|
+
*/
|
|
17
|
+
import * as Option from "effect/Option";
|
|
18
|
+
import { Fail } from "../../Input.js";
|
|
19
|
+
import { addEntry, flushEntries, fromEntries, isPlainKey } from "../entries.js";
|
|
20
|
+
import { children, nodeOf } from "../node.js";
|
|
21
|
+
import { floatScan, floatTexts, scanFloatBytes, scanFloatString, toFloat } from "../numbers.js";
|
|
22
|
+
import * as Resume from "../resume.js";
|
|
23
|
+
import { charOf, fastLimit, latin1, string, utf16be, utf16le, utf8, validate } from "../text.js";
|
|
24
|
+
import { createClosureCompiler } from "./closure.js";
|
|
25
|
+
import { entriesShape, pairOf } from "./entries.js";
|
|
26
|
+
import { planAlt, Strict } from "./first.js";
|
|
27
|
+
import { kindOfEncoding, reachesAbove, TableSize, textSet, tokenSet } from "./sets.js";
|
|
28
|
+
/** Helpers available to generated code as `R`. */
|
|
29
|
+
const runtime = {
|
|
30
|
+
take: Resume.take,
|
|
31
|
+
save: Resume.save,
|
|
32
|
+
Done: Resume.Done,
|
|
33
|
+
/** Records the result of a resumed repetition (see `Resume.done`). */
|
|
34
|
+
done: (state, key, entry, value, pos) => Resume.save(state, key, entry, new Resume.Done(value, pos)),
|
|
35
|
+
some: Option.some,
|
|
36
|
+
none: Option.none,
|
|
37
|
+
charOf,
|
|
38
|
+
scanFloatString,
|
|
39
|
+
scanFloatBytes,
|
|
40
|
+
isPlainKey,
|
|
41
|
+
addEntry,
|
|
42
|
+
flushEntries,
|
|
43
|
+
/** `Text.taken`: the text of `[start, end)`, or `Fail` if it is malformed. */
|
|
44
|
+
textOf: (enc, fast, s, start, end) => {
|
|
45
|
+
const input = s.input;
|
|
46
|
+
let i = start;
|
|
47
|
+
if (s.inputKind === 0)
|
|
48
|
+
while (i < end && input.charCodeAt(i) < fast)
|
|
49
|
+
i++;
|
|
50
|
+
else
|
|
51
|
+
while (i < end && input[i] < fast)
|
|
52
|
+
i++;
|
|
53
|
+
if (i < end && validate(enc, s, i, end) < 0) {
|
|
54
|
+
s.mode = "Backtrack";
|
|
55
|
+
return Fail;
|
|
56
|
+
}
|
|
57
|
+
return enc.text(input, start, end);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
let checked;
|
|
61
|
+
let supported = false;
|
|
62
|
+
/**
|
|
63
|
+
* Whether `new Function` works here (it doesn't under a Content Security
|
|
64
|
+
* Policy). Checked once, and again if `globalThis.Function` is replaced.
|
|
65
|
+
*/
|
|
66
|
+
export const jitSupported = () => {
|
|
67
|
+
if (checked !== globalThis.Function) {
|
|
68
|
+
checked = globalThis.Function;
|
|
69
|
+
try {
|
|
70
|
+
supported = checked("return true")() === true;
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
supported = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return supported;
|
|
77
|
+
};
|
|
78
|
+
const BT = `$S.mode="Backtrack";`;
|
|
79
|
+
const builtinEncodings = new Set([string, utf8, utf16le, utf16be, latin1]);
|
|
80
|
+
/** How many units past the cursor a single token can depend on (see the closure compiler). */
|
|
81
|
+
const Margin = 4;
|
|
82
|
+
/** Per generated function: its local variables. */
|
|
83
|
+
class Fn {
|
|
84
|
+
vars = [];
|
|
85
|
+
}
|
|
86
|
+
const isLeaf = (node) => {
|
|
87
|
+
switch (node._tag) {
|
|
88
|
+
case "Succeed":
|
|
89
|
+
case "Fail":
|
|
90
|
+
case "Eof":
|
|
91
|
+
case "Location":
|
|
92
|
+
case "RestLen":
|
|
93
|
+
case "Any":
|
|
94
|
+
case "Literal":
|
|
95
|
+
case "OneOf":
|
|
96
|
+
case "TakeWhile":
|
|
97
|
+
return true;
|
|
98
|
+
default:
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const lowerAscii = (c) => c >= 65 && c <= 90 ? c + 32 : c;
|
|
103
|
+
const isLetter = (c) => (c >= 65 && c <= 90) || (c >= 97 && c <= 122);
|
|
104
|
+
/** The inclusive ranges of 1s in a table. */
|
|
105
|
+
const runsOf = (table) => {
|
|
106
|
+
const out = [];
|
|
107
|
+
for (let i = 0; i < table.length; i++) {
|
|
108
|
+
if (table[i] !== 1)
|
|
109
|
+
continue;
|
|
110
|
+
let j = i;
|
|
111
|
+
while (j + 1 < table.length && table[j + 1] === 1)
|
|
112
|
+
j++;
|
|
113
|
+
out.push([i, j]);
|
|
114
|
+
i = j;
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
};
|
|
118
|
+
/** A JavaScript expression testing `v` against ranges, or `undefined` if too long. */
|
|
119
|
+
const rangeTest = (runs, v) => {
|
|
120
|
+
if (runs.length === 0)
|
|
121
|
+
return "false";
|
|
122
|
+
if (runs.length > 4)
|
|
123
|
+
return undefined;
|
|
124
|
+
return runs.map(([a, b]) => a === b ? `${v}===${a}` : `(${v}>=${a}&&${v}<=${b})`).join("||");
|
|
125
|
+
};
|
|
126
|
+
/** The inclusive ranges of `[0, size)` where `table[c] === value`. */
|
|
127
|
+
const runsWhere = (table, size, value) => {
|
|
128
|
+
const out = [];
|
|
129
|
+
for (let i = 0; i < size; i++) {
|
|
130
|
+
if (table[i] !== value)
|
|
131
|
+
continue;
|
|
132
|
+
let j = i;
|
|
133
|
+
while (j + 1 < size && table[j + 1] === value)
|
|
134
|
+
j++;
|
|
135
|
+
out.push([i, j]);
|
|
136
|
+
i = j;
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
};
|
|
140
|
+
/** How many comparisons it takes to test a run of `[0, size)`. */
|
|
141
|
+
const runCost = ([a, b], size) => a === b || a === 0 || b === size - 1 ? 1 : 2;
|
|
142
|
+
/**
|
|
143
|
+
* A JavaScript expression testing whether `v`, known to be in `[0, size)`,
|
|
144
|
+
* is accepted by `table`, or `undefined` if that takes too many comparisons.
|
|
145
|
+
* It tests the accepted runs or the rejected ones, whichever is cheaper:
|
|
146
|
+
* `c!==34&&c!==92&&c>=32` rather than three two-sided ranges.
|
|
147
|
+
*/
|
|
148
|
+
const tableTest = (table, v, size) => {
|
|
149
|
+
const accept = runsWhere(table, size, 1);
|
|
150
|
+
const reject = runsWhere(table, size, 0);
|
|
151
|
+
if (accept.length === 0)
|
|
152
|
+
return "false";
|
|
153
|
+
if (reject.length === 0)
|
|
154
|
+
return "true";
|
|
155
|
+
const cost = (runs) => runs.reduce((n, r) => n + runCost(r, size), 0);
|
|
156
|
+
const acceptCost = cost(accept);
|
|
157
|
+
const rejectCost = cost(reject);
|
|
158
|
+
if (Math.min(acceptCost, rejectCost) > 6)
|
|
159
|
+
return undefined;
|
|
160
|
+
const terms = acceptCost <= rejectCost
|
|
161
|
+
? accept.map(([a, b]) => a === b ? `${v}===${a}` : a === 0 ? `${v}<=${b}` : b === size - 1 ? `${v}>=${a}` : `(${v}>=${a}&&${v}<=${b})`).join("||")
|
|
162
|
+
: reject.map(([a, b]) => a === b ? `${v}!==${a}` : a === 0 ? `${v}>${b}` : b === size - 1 ? `${v}<${a}` : `(${v}<${a}||${v}>${b})`).join("&&");
|
|
163
|
+
return `(${terms})`;
|
|
164
|
+
};
|
|
165
|
+
class Emitter {
|
|
166
|
+
root;
|
|
167
|
+
kind;
|
|
168
|
+
resumable;
|
|
169
|
+
constants = [];
|
|
170
|
+
constantIndex = new Map();
|
|
171
|
+
functions = [];
|
|
172
|
+
rules = [new Map(), new Map()];
|
|
173
|
+
refs = new Map();
|
|
174
|
+
closure;
|
|
175
|
+
next = 0;
|
|
176
|
+
constructor(root, kind, resumable = false) {
|
|
177
|
+
this.root = root;
|
|
178
|
+
this.kind = kind;
|
|
179
|
+
this.resumable = resumable;
|
|
180
|
+
this.closure = createClosureCompiler(kind, resumable);
|
|
181
|
+
this.countRefs(root);
|
|
182
|
+
}
|
|
183
|
+
get fallbacks() {
|
|
184
|
+
return this.closure.fallbacks;
|
|
185
|
+
}
|
|
186
|
+
/** Counts the distinct parents of each parser. */
|
|
187
|
+
countRefs(root) {
|
|
188
|
+
const seen = new Set();
|
|
189
|
+
const stack = [root];
|
|
190
|
+
while (stack.length > 0) {
|
|
191
|
+
const p = stack.pop();
|
|
192
|
+
if (seen.has(p))
|
|
193
|
+
continue;
|
|
194
|
+
seen.add(p);
|
|
195
|
+
for (const c of new Set(children(nodeOf(p)))) {
|
|
196
|
+
this.refs.set(c, (this.refs.get(c) ?? 0) + 1);
|
|
197
|
+
stack.push(c);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const(value) {
|
|
202
|
+
let i = this.constantIndex.get(value);
|
|
203
|
+
if (i === undefined) {
|
|
204
|
+
i = this.constants.length;
|
|
205
|
+
this.constants.push(value);
|
|
206
|
+
this.constantIndex.set(value, i);
|
|
207
|
+
}
|
|
208
|
+
return `C[${i}]`;
|
|
209
|
+
}
|
|
210
|
+
/** A JavaScript literal for simple values, otherwise a constant. */
|
|
211
|
+
lit(value) {
|
|
212
|
+
if (value === undefined)
|
|
213
|
+
return "void 0";
|
|
214
|
+
if (value === null || typeof value === "boolean")
|
|
215
|
+
return String(value);
|
|
216
|
+
if (typeof value === "number" && Number.isFinite(value) && !Object.is(value, -0))
|
|
217
|
+
return String(value);
|
|
218
|
+
if (typeof value === "string" && value.length < 64)
|
|
219
|
+
return JSON.stringify(value);
|
|
220
|
+
return this.const(value);
|
|
221
|
+
}
|
|
222
|
+
v(fn) {
|
|
223
|
+
const name = `v${this.next++}`;
|
|
224
|
+
fn.vars.push(name);
|
|
225
|
+
return name;
|
|
226
|
+
}
|
|
227
|
+
label() {
|
|
228
|
+
return `L${this.next++}`;
|
|
229
|
+
}
|
|
230
|
+
unit(i) {
|
|
231
|
+
return this.kind === 0 ? `I.charCodeAt(${i})` : `I[${i}]`;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* How generated code reads an encoding's code units: units below `fast`
|
|
235
|
+
* are whole code points, `width` input units long. `guard(i)` says that a
|
|
236
|
+
* whole unit is available at `i`.
|
|
237
|
+
*/
|
|
238
|
+
reader(enc) {
|
|
239
|
+
if (enc === utf16le || enc === utf16be) {
|
|
240
|
+
const [lo, hi] = enc === utf16le ? ["", "+1"] : ["+1", ""];
|
|
241
|
+
return {
|
|
242
|
+
width: 2,
|
|
243
|
+
fast: 0xd800,
|
|
244
|
+
guard: (i) => `${i}+1<E`,
|
|
245
|
+
unit: (i) => `(I[${i}${lo}]|I[${i}${hi}]<<8)`
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return { width: 1, fast: enc.unit === 1 ? fastLimit(enc) : 0, guard: (i) => `${i}<E`, unit: (i) => this.unit(i) };
|
|
249
|
+
}
|
|
250
|
+
/** The function implementing a rule, generating it on first use. */
|
|
251
|
+
rule(p, used) {
|
|
252
|
+
const rules = this.rules[used ? 1 : 0];
|
|
253
|
+
let name = rules.get(p);
|
|
254
|
+
if (name !== undefined)
|
|
255
|
+
return name;
|
|
256
|
+
name = `f${this.next++}`;
|
|
257
|
+
rules.set(p, name);
|
|
258
|
+
const fn = new Fn();
|
|
259
|
+
const out = used ? this.v(fn) : null;
|
|
260
|
+
const done = this.label();
|
|
261
|
+
const body = this.inline(p, used, out, `break ${done};`, fn, 0);
|
|
262
|
+
this.functions.push(`function ${name}(p){const I=$I,E=$E;${fn.vars.length > 0 ? `let ${fn.vars.join(",")};` : ""}` +
|
|
263
|
+
`${done}:{${body}P=p;return ${out ?? "void 0"}}return F}`);
|
|
264
|
+
return name;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Code for `p`: on success it falls through with `p` advanced and, when
|
|
268
|
+
* `out` is given, the value in `out`; on failure it runs `fail`.
|
|
269
|
+
*/
|
|
270
|
+
gen(p, used, out, fail, fn, depth) {
|
|
271
|
+
const node = nodeOf(p);
|
|
272
|
+
if (node._tag === "Suspend")
|
|
273
|
+
return this.call(this.rule(node.resolve(), used), out, fail);
|
|
274
|
+
if (!isLeaf(node) && (((this.refs.get(p) ?? 0) > 1 && !this.small(p)) || depth > 32)) {
|
|
275
|
+
return this.call(this.rule(p, used), out, fail);
|
|
276
|
+
}
|
|
277
|
+
return this.inline(p, used, out, fail, fn, depth);
|
|
278
|
+
}
|
|
279
|
+
/** Whether a parser is small enough to inline wherever it is used. */
|
|
280
|
+
small(p) {
|
|
281
|
+
let budget = 6;
|
|
282
|
+
const visit = (q) => {
|
|
283
|
+
const node = nodeOf(q);
|
|
284
|
+
if (--budget < 0 || node._tag === "Suspend" || node._tag === "Escaped" || node._tag === "Dispatch")
|
|
285
|
+
return false;
|
|
286
|
+
return children(node).every(visit);
|
|
287
|
+
};
|
|
288
|
+
return visit(p);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* In a resumable build: token code (which fails by breaking out of `bad`)
|
|
292
|
+
* that read within `lookahead` units of the end of a partial input re-runs
|
|
293
|
+
* interpreted, since it may have to report `Incomplete` there. Token code
|
|
294
|
+
* leaves `p` where it stopped reading, even when it fails.
|
|
295
|
+
*/
|
|
296
|
+
partialSafe(p, code, bad, lookahead, out, fail, fn) {
|
|
297
|
+
const ahead = Math.max(lookahead, Margin);
|
|
298
|
+
const st = this.v(fn);
|
|
299
|
+
const ok = this.v(fn);
|
|
300
|
+
const t = this.v(fn);
|
|
301
|
+
const leaf = this.label();
|
|
302
|
+
return `${st}=p;${ok}=false;${leaf}:{${bad}:{${code}${ok}=true;}` +
|
|
303
|
+
`if(!$S.partial||p+${ahead}<=E){if(${ok})break ${leaf};${fail}}` +
|
|
304
|
+
`p=${st};$S.pos=p;${t}=${this.const(p.step)}($S);if(${t}===F){${fail}}p=$S.pos;${out === null ? "" : `${out}=${t};`}}`;
|
|
305
|
+
}
|
|
306
|
+
call(name, out, fail) {
|
|
307
|
+
if (out === null)
|
|
308
|
+
return `if(${name}(p)===F){${fail}}p=P;`;
|
|
309
|
+
return `${out}=${name}(p);if(${out}===F){${fail}}p=P;`;
|
|
310
|
+
}
|
|
311
|
+
/** Runs the closure-compiled step of `p` on the shared state. */
|
|
312
|
+
delegate(p, used, out, fail, fn) {
|
|
313
|
+
const step = this.const(this.closure.compile(p, used));
|
|
314
|
+
const t = out ?? this.v(fn);
|
|
315
|
+
return `$S.pos=p;${t}=${step}($S);if(${t}===F){${fail}}p=$S.pos;`;
|
|
316
|
+
}
|
|
317
|
+
native(enc) {
|
|
318
|
+
return enc === undefined ? this.kind !== 2 : kindOfEncoding(enc) === this.kind;
|
|
319
|
+
}
|
|
320
|
+
inline(p, used, out, fail, fn, depth) {
|
|
321
|
+
const node = nodeOf(p);
|
|
322
|
+
const set = (value) => out === null ? "" : `${out}=${value};`;
|
|
323
|
+
const d = depth + 1;
|
|
324
|
+
switch (node._tag) {
|
|
325
|
+
case "Succeed":
|
|
326
|
+
return set(this.lit(node.value));
|
|
327
|
+
case "Sync":
|
|
328
|
+
return used ? set(`${this.const(node.evaluate)}()`) : "";
|
|
329
|
+
case "Fail":
|
|
330
|
+
return `$S.mode=${JSON.stringify(node.mode)};${fail}`;
|
|
331
|
+
case "Eof":
|
|
332
|
+
return `if(p<E){${BT}${fail}}` + (this.resumable ? `if($S.partial){$S.mode="Incomplete";${fail}}` : "") +
|
|
333
|
+
set("void 0");
|
|
334
|
+
case "Location":
|
|
335
|
+
return set("p");
|
|
336
|
+
case "RestLen":
|
|
337
|
+
return set("E-p");
|
|
338
|
+
case "Suspend":
|
|
339
|
+
return this.call(this.rule(node.resolve(), used), out, fail);
|
|
340
|
+
case "Any":
|
|
341
|
+
case "Literal":
|
|
342
|
+
case "OneOf":
|
|
343
|
+
case "TakeWhile": {
|
|
344
|
+
if (!this.native(node.encoding))
|
|
345
|
+
return this.delegate(p, used, out, fail, fn);
|
|
346
|
+
if (!this.resumable)
|
|
347
|
+
return this.token(node, out, fail, fn) ?? this.delegate(p, used, out, fail, fn);
|
|
348
|
+
const bad = this.label();
|
|
349
|
+
const code = this.token(node, out, `break ${bad};`, fn);
|
|
350
|
+
if (code === undefined)
|
|
351
|
+
return this.delegate(p, used, out, fail, fn);
|
|
352
|
+
return this.partialSafe(p, code, bad, node._tag === "Literal" ? node.literal.length * 4 : Margin, out, fail, fn);
|
|
353
|
+
}
|
|
354
|
+
case "Escaped": {
|
|
355
|
+
// Streaming builds use the closure compiler's escapes, which keep checkpoints
|
|
356
|
+
if (!this.native(node.encoding) || this.resumable)
|
|
357
|
+
return this.delegate(p, used, out, fail, fn);
|
|
358
|
+
return this.escaped(p, node, out, fail, fn, d);
|
|
359
|
+
}
|
|
360
|
+
case "Map": {
|
|
361
|
+
if (!used)
|
|
362
|
+
return this.gen(node.self, false, null, fail, fn, d);
|
|
363
|
+
if (node.f === toFloat && out !== null) {
|
|
364
|
+
const code = this.float(node, out, fail, fn, d);
|
|
365
|
+
if (code !== undefined)
|
|
366
|
+
return code;
|
|
367
|
+
}
|
|
368
|
+
// Streaming builds keep the array, which their checkpoints hold
|
|
369
|
+
if (node.f === fromEntries && out !== null && !this.resumable && entriesShape(node.self)) {
|
|
370
|
+
return this.entries(node.self, out, fail, fn, d);
|
|
371
|
+
}
|
|
372
|
+
const t = this.v(fn);
|
|
373
|
+
return this.gen(node.self, true, t, fail, fn, d) + set(`${this.const(node.f)}(${t})`);
|
|
374
|
+
}
|
|
375
|
+
case "As":
|
|
376
|
+
return this.gen(node.self, false, null, fail, fn, d) + (used ? set(this.lit(node.value)) : "");
|
|
377
|
+
case "Tap": {
|
|
378
|
+
const t = out ?? this.v(fn);
|
|
379
|
+
return this.gen(node.self, true, t, fail, fn, d) + `${this.const(node.f)}(${t});`;
|
|
380
|
+
}
|
|
381
|
+
case "TryMap":
|
|
382
|
+
case "FilterMap": {
|
|
383
|
+
const t = this.v(fn);
|
|
384
|
+
const r = this.v(fn);
|
|
385
|
+
const [tag, field] = node._tag === "TryMap" ? ["Success", "success"] : ["Some", "value"];
|
|
386
|
+
return this.gen(node.self, true, t, fail, fn, d) +
|
|
387
|
+
`${r}=${this.const(node.f)}(${t});if(${r}._tag!=="${tag}"){${BT}${fail}}${set(`${r}.${field}`)}`;
|
|
388
|
+
}
|
|
389
|
+
case "Filter": {
|
|
390
|
+
const t = out ?? this.v(fn);
|
|
391
|
+
return this.gen(node.self, true, t, fail, fn, d) + `if(!${this.const(node.predicate)}(${t})){${BT}${fail}}`;
|
|
392
|
+
}
|
|
393
|
+
case "FlatMap": {
|
|
394
|
+
const t = this.v(fn);
|
|
395
|
+
const r = out ?? this.v(fn);
|
|
396
|
+
this.closure.note(p, node, "the parser returned by the function runs interpreted");
|
|
397
|
+
return this.gen(node.self, true, t, fail, fn, d) +
|
|
398
|
+
`$S.pos=p;${r}=${this.const(node.f)}(${t}).step($S);if(${r}===F){${fail}}p=$S.pos;`;
|
|
399
|
+
}
|
|
400
|
+
case "Taken": {
|
|
401
|
+
if (node.encoding !== undefined && !this.native(node.encoding))
|
|
402
|
+
return this.delegate(p, used, out, fail, fn);
|
|
403
|
+
const st = this.v(fn);
|
|
404
|
+
const body = `${st}=p;` + this.gen(node.self, false, null, fail, fn, d);
|
|
405
|
+
if (out === null && node.encoding === undefined)
|
|
406
|
+
return body;
|
|
407
|
+
if (node.encoding === undefined) {
|
|
408
|
+
return body + set(this.kind === 0 ? `I.slice(${st},p)` : this.kind === 1 ? `I.subarray(${st},p)` : `$S.slice(${st},p)`);
|
|
409
|
+
}
|
|
410
|
+
const t = out ?? this.v(fn);
|
|
411
|
+
const enc = this.const(node.encoding);
|
|
412
|
+
return body + `${t}=R.textOf(${enc},${fastLimit(node.encoding)},$S,${st},p);if(${t}===F){${fail}}`;
|
|
413
|
+
}
|
|
414
|
+
case "WithTaken":
|
|
415
|
+
case "WithSpan": {
|
|
416
|
+
const st = this.v(fn);
|
|
417
|
+
const t = this.v(fn);
|
|
418
|
+
const extra = node._tag === "WithSpan" ? `{start:${st},end:p}` : `$S.slice(${st},p)`;
|
|
419
|
+
return `${st}=p;` + this.gen(node.self, true, t, fail, fn, d) + set(`[${t},${extra}]`);
|
|
420
|
+
}
|
|
421
|
+
case "Span": {
|
|
422
|
+
const st = this.v(fn);
|
|
423
|
+
return `${st}=p;` + this.gen(node.self, false, null, fail, fn, d) + set(`{start:${st},end:p}`);
|
|
424
|
+
}
|
|
425
|
+
case "Mode": {
|
|
426
|
+
const ok = this.label();
|
|
427
|
+
const bad = this.label();
|
|
428
|
+
return `${ok}:{${bad}:{${this.gen(node.self, used, out, `break ${bad};`, fn, d)}break ${ok};}` +
|
|
429
|
+
`if($S.mode===${JSON.stringify(node.from)})$S.mode=${JSON.stringify(node.to)};${fail}}`;
|
|
430
|
+
}
|
|
431
|
+
case "Context":
|
|
432
|
+
case "Expected":
|
|
433
|
+
case "MapError":
|
|
434
|
+
case "OrFail":
|
|
435
|
+
case "Trace":
|
|
436
|
+
case "RetryAfter":
|
|
437
|
+
return this.gen(node.self, used, out, fail, fn, d);
|
|
438
|
+
case "ResumeAfter": {
|
|
439
|
+
if (!used)
|
|
440
|
+
return this.gen(node.self, false, null, fail, fn, d);
|
|
441
|
+
const t = this.v(fn);
|
|
442
|
+
return this.gen(node.self, true, t, fail, fn, d) + set(`R.some(${t})`);
|
|
443
|
+
}
|
|
444
|
+
case "Opt":
|
|
445
|
+
case "OrElseSucceed": {
|
|
446
|
+
const st = this.v(fn);
|
|
447
|
+
const ok = this.label();
|
|
448
|
+
const bad = this.label();
|
|
449
|
+
const t = out === null ? null : this.v(fn);
|
|
450
|
+
const some = node._tag === "Opt" ? `R.some(${t})` : t;
|
|
451
|
+
const orElse = node._tag === "Opt" ? "R.none()" : `${this.const(node.value)}()`;
|
|
452
|
+
return `${st}=p;${ok}:{${bad}:{${this.gen(node.self, used, t, `break ${bad};`, fn, d)}${set(some)}break ${ok};}` +
|
|
453
|
+
`if($S.mode!=="Backtrack"){${fail}}p=${st};${set(orElse)}}`;
|
|
454
|
+
}
|
|
455
|
+
case "Peek": {
|
|
456
|
+
const st = this.v(fn);
|
|
457
|
+
return `${st}=p;${this.gen(node.self, used, out, fail, fn, d)}p=${st};`;
|
|
458
|
+
}
|
|
459
|
+
case "Not": {
|
|
460
|
+
const st = this.v(fn);
|
|
461
|
+
const ok = this.label();
|
|
462
|
+
const bad = this.label();
|
|
463
|
+
return `${st}=p;${ok}:{${bad}:{${this.gen(node.self, false, null, `break ${bad};`, fn, d)}p=${st};${BT}${fail}}` +
|
|
464
|
+
`if($S.mode!=="Backtrack"){${fail}}p=${st};}${set("void 0")}`;
|
|
465
|
+
}
|
|
466
|
+
case "Seq":
|
|
467
|
+
return this.seq(node, used, out, fail, fn, d);
|
|
468
|
+
case "Struct": {
|
|
469
|
+
if (out === null)
|
|
470
|
+
return node.parsers.map((q) => this.gen(q, false, null, fail, fn, d)).join("");
|
|
471
|
+
const vars = node.parsers.map(() => this.v(fn));
|
|
472
|
+
const code = node.parsers.map((q, i) => this.gen(q, true, vars[i], fail, fn, d)).join("");
|
|
473
|
+
if (node.keys.includes("__proto__")) {
|
|
474
|
+
return code + `${out}={};` + node.keys.map((k, i) => `${out}[${JSON.stringify(k)}]=${vars[i]};`).join("");
|
|
475
|
+
}
|
|
476
|
+
return code + `${out}={${node.keys.map((k, i) => `${JSON.stringify(k)}:${vars[i]}`).join(",")}};`;
|
|
477
|
+
}
|
|
478
|
+
case "ZipWith": {
|
|
479
|
+
if (out === null)
|
|
480
|
+
return this.gen(node.self, false, null, fail, fn, d) + this.gen(node.that, false, null, fail, fn, d);
|
|
481
|
+
const x = this.v(fn);
|
|
482
|
+
const y = this.v(fn);
|
|
483
|
+
return this.gen(node.self, true, x, fail, fn, d) + this.gen(node.that, true, y, fail, fn, d) +
|
|
484
|
+
`${out}=${this.const(node.f)}(${x},${y});`;
|
|
485
|
+
}
|
|
486
|
+
case "Alt":
|
|
487
|
+
return this.alt(node, used, out, fail, fn, d);
|
|
488
|
+
case "Dispatch":
|
|
489
|
+
return this.dispatch(p, node, used, out, fail, fn, d);
|
|
490
|
+
case "Repeat":
|
|
491
|
+
case "Fold":
|
|
492
|
+
return this.repeat(node, used, out, fail, fn, d);
|
|
493
|
+
case "Separated":
|
|
494
|
+
return this.separated(node, used, out, fail, fn, d);
|
|
495
|
+
default:
|
|
496
|
+
return this.delegate(p, used, out, fail, fn);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* `Ascii.float` and `Text.float` (`map(floatText, toFloat)`): numbers in
|
|
501
|
+
* decimal notation are matched and converted in one pass by
|
|
502
|
+
* `scanFloatString` or `scanFloatBytes`, without slicing the input; the
|
|
503
|
+
* rest (`nan`, `inf`, failures before a digit) runs the parser. Returns
|
|
504
|
+
* `undefined` when this does not apply.
|
|
505
|
+
*/
|
|
506
|
+
float(node, out, fail, fn, d) {
|
|
507
|
+
if (!floatTexts.has(node.self) || this.kind === 2)
|
|
508
|
+
return undefined;
|
|
509
|
+
let taken = nodeOf(node.self);
|
|
510
|
+
while (taken._tag === "Expected")
|
|
511
|
+
taken = nodeOf(taken.self);
|
|
512
|
+
if (taken._tag !== "Taken")
|
|
513
|
+
return undefined;
|
|
514
|
+
const enc = taken.encoding;
|
|
515
|
+
// Decimal notation is ASCII, which these encodings read unit by unit
|
|
516
|
+
if (enc !== undefined && !(this.native(enc) && enc.unit === 1 && enc.asciiCompatible))
|
|
517
|
+
return undefined;
|
|
518
|
+
const t = this.v(fn);
|
|
519
|
+
const scan = this.const(floatScan);
|
|
520
|
+
// Near the end of a partial input, the parser may have to report `Incomplete`
|
|
521
|
+
const near = this.resumable ? `||$S.partial&&${scan}.read+${Margin}>E` : "";
|
|
522
|
+
return `${out}=${this.kind === 0 ? "R.scanFloatString" : "R.scanFloatBytes"}(I,p,E);` +
|
|
523
|
+
`if(${out}===void 0${near}){${this.gen(node.self, true, t, fail, fn, d)}${out}=${this.const(toFloat)}(${t})}` +
|
|
524
|
+
`else if(${out}===F){${BT}${fail}}else p=${scan}.end;`;
|
|
525
|
+
}
|
|
526
|
+
seq(node, used, out, fail, fn, d) {
|
|
527
|
+
const output = node.output;
|
|
528
|
+
if (out === null || !used)
|
|
529
|
+
return node.parsers.map((q) => this.gen(q, false, null, fail, fn, d)).join("");
|
|
530
|
+
if (typeof output === "number") {
|
|
531
|
+
return node.parsers.map((q, i) => this.gen(q, i === output, i === output ? out : null, fail, fn, d)).join("");
|
|
532
|
+
}
|
|
533
|
+
const keep = (i) => output === "tuple" || output.includes(i);
|
|
534
|
+
const vars = node.parsers.map((_, i) => keep(i) ? this.v(fn) : null);
|
|
535
|
+
const code = node.parsers.map((q, i) => this.gen(q, keep(i), vars[i], fail, fn, d)).join("");
|
|
536
|
+
return code + `${out}=[${vars.filter((v) => v !== null).join(",")}];`;
|
|
537
|
+
}
|
|
538
|
+
/** A test that the unit `u` may start `first` (false for -1, the end of the input). */
|
|
539
|
+
startTest(first, u) {
|
|
540
|
+
const runs = runsOf(first.units);
|
|
541
|
+
const small = rangeTest(runs, u);
|
|
542
|
+
const low = small ?? `${this.const(first.units)}[${u}]===1`;
|
|
543
|
+
if (!first.high)
|
|
544
|
+
return small !== undefined ? `(${low})` : `(${u}<${TableSize}&&${low})`;
|
|
545
|
+
return `(${u}>=${TableSize}||${low})`;
|
|
546
|
+
}
|
|
547
|
+
alt(node, used, out, fail, fn, d) {
|
|
548
|
+
const parsers = node.parsers;
|
|
549
|
+
if (parsers.length === 0)
|
|
550
|
+
return `${BT}${fail}`;
|
|
551
|
+
if (parsers.length === 1)
|
|
552
|
+
return this.gen(parsers[0], used, out, fail, fn, d);
|
|
553
|
+
const firsts = this.kind === 2 ? undefined : parsers.map((q) => this.closure.firsts.of(q));
|
|
554
|
+
const prune = firsts !== undefined && planAlt(firsts) !== undefined;
|
|
555
|
+
const st = this.v(fn);
|
|
556
|
+
const u = prune ? this.v(fn) : "";
|
|
557
|
+
const done = this.label();
|
|
558
|
+
// Near the end of a partial input, no branch is skipped: it may be incomplete rather than fail
|
|
559
|
+
const read = this.resumable ? `$S.partial&&p+${Margin}>E?-2:p<E?${this.unit("p")}:-1` : `p<E?${this.unit("p")}:-1`;
|
|
560
|
+
let code = `${st}=p;${prune ? `${u}=${read};` : ""}${done}:{`;
|
|
561
|
+
parsers.forEach((q, i) => {
|
|
562
|
+
const bad = this.label();
|
|
563
|
+
const branch = `${bad}:{${this.gen(q, used, out, `break ${bad};`, fn, d)}break ${done};}` +
|
|
564
|
+
`if($S.mode!=="Backtrack"){${fail}}p=${st};`;
|
|
565
|
+
const f = firsts?.[i];
|
|
566
|
+
code += prune && f.mode === Strict
|
|
567
|
+
? `if(${this.resumable ? `${u}===-2||` : ""}${this.startTest(f, u)}){${branch}}`
|
|
568
|
+
: branch;
|
|
569
|
+
});
|
|
570
|
+
return code + `${BT}${fail}}`;
|
|
571
|
+
}
|
|
572
|
+
dispatch(p, node, used, out, fail, fn, d) {
|
|
573
|
+
const choose = this.const(node.choose);
|
|
574
|
+
// The general path: run the selector, choose, then run the case's compiled step
|
|
575
|
+
const cases = new Map();
|
|
576
|
+
const caseStep = (q) => {
|
|
577
|
+
let s = cases.get(q);
|
|
578
|
+
if (s === undefined)
|
|
579
|
+
cases.set(q, s = this.closure.compile(q, used));
|
|
580
|
+
return s;
|
|
581
|
+
};
|
|
582
|
+
if (node.cases !== undefined) {
|
|
583
|
+
for (const key of Reflect.ownKeys(node.cases))
|
|
584
|
+
caseStep(node.cases[key]);
|
|
585
|
+
if (node.otherwise !== undefined)
|
|
586
|
+
caseStep(node.otherwise);
|
|
587
|
+
}
|
|
588
|
+
const table = this.const(cases);
|
|
589
|
+
const general = () => {
|
|
590
|
+
const st = this.v(fn);
|
|
591
|
+
const k = this.v(fn);
|
|
592
|
+
const q = this.v(fn);
|
|
593
|
+
const r = out ?? this.v(fn);
|
|
594
|
+
return `${st}=p;${this.gen(node.selector, true, k, fail, fn, d)}${q}=${choose}(${k});` +
|
|
595
|
+
`if(${q}===void 0){p=${st};${BT}${fail}}$S.pos=p;${r}=(${table}.get(${q})??${q}.step)($S);` +
|
|
596
|
+
`if(${r}===F){${fail}}p=$S.pos;`;
|
|
597
|
+
};
|
|
598
|
+
if (node.cases === undefined) {
|
|
599
|
+
this.closure.note(p, node, "the parser returned by the function of cases runs interpreted");
|
|
600
|
+
return general();
|
|
601
|
+
}
|
|
602
|
+
let sel = nodeOf(node.selector);
|
|
603
|
+
const peek = sel._tag === "Peek";
|
|
604
|
+
if (peek)
|
|
605
|
+
sel = nodeOf(sel.self);
|
|
606
|
+
if (sel._tag !== "Any" || !this.native(sel.encoding))
|
|
607
|
+
return general();
|
|
608
|
+
const enc = sel.encoding;
|
|
609
|
+
const r = enc === undefined ? undefined : this.reader(enc);
|
|
610
|
+
const limit = r === undefined ? (this.kind === 0 ? 0xd800 : TableSize) : Math.min(r.fast, 0xd800);
|
|
611
|
+
const size = Math.min(limit, TableSize);
|
|
612
|
+
if (size === 0)
|
|
613
|
+
return general();
|
|
614
|
+
// Group the units by the case they select
|
|
615
|
+
const groups = new Map();
|
|
616
|
+
for (let unit = 0; unit < size; unit++) {
|
|
617
|
+
const q = (node.choose)(enc === undefined && this.kind === 1 ? unit : charOf(unit));
|
|
618
|
+
if (q === undefined)
|
|
619
|
+
continue;
|
|
620
|
+
let g = groups.get(q);
|
|
621
|
+
if (g === undefined)
|
|
622
|
+
groups.set(q, g = []);
|
|
623
|
+
g.push(unit);
|
|
624
|
+
}
|
|
625
|
+
const u = this.v(fn);
|
|
626
|
+
const sw = this.label();
|
|
627
|
+
// A unit cut short by the end of the input takes the general path
|
|
628
|
+
const read = r === undefined
|
|
629
|
+
? `p<E?${this.unit("p")}:-1`
|
|
630
|
+
: `${r.guard("p")}?${r.unit("p")}:p<E?${size}:-1`;
|
|
631
|
+
// Near the end of a partial input, the general path handles incomplete units
|
|
632
|
+
const near = this.resumable ? `$S.partial&&p+${Margin}>E?${size}:` : "";
|
|
633
|
+
let code = `${u}=${near}${read};${sw}:switch(${u}){`;
|
|
634
|
+
for (const [q, units] of groups) {
|
|
635
|
+
code += units.map((x) => `case ${x}:`).join("") +
|
|
636
|
+
`{${peek ? "" : `p+=${r?.width ?? 1};`}${this.gen(q, used, out, fail, fn, d)}break ${sw};}`;
|
|
637
|
+
}
|
|
638
|
+
code += `case -1:{${BT}${fail}}default:{if(${u}<${size}){${BT}${fail}}${general()}}}`;
|
|
639
|
+
return code;
|
|
640
|
+
}
|
|
641
|
+
repeat(node, used, out, fail, fn, d, collect) {
|
|
642
|
+
const { max, min } = node;
|
|
643
|
+
const loop = this.label();
|
|
644
|
+
const bad = this.label();
|
|
645
|
+
const n = this.v(fn);
|
|
646
|
+
const st = this.v(fn);
|
|
647
|
+
const isFold = node._tag === "Fold";
|
|
648
|
+
const keep = out !== null && used && collect === undefined;
|
|
649
|
+
const x = keep ? this.v(fn) : null;
|
|
650
|
+
const acc = keep ? out : this.v(fn);
|
|
651
|
+
let init = collect?.init ?? "";
|
|
652
|
+
let add = "";
|
|
653
|
+
if (keep) {
|
|
654
|
+
if (isFold) {
|
|
655
|
+
init = `${acc}=${this.const(node.init)}();`;
|
|
656
|
+
add = `${acc}=${this.const(node.f)}(${acc},${x});`;
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
init = `${acc}=[];`;
|
|
660
|
+
add = `${acc}.push(${x});`;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
const check = max === Infinity ? `if(p===${st}){$S.mode="Cut";${fail}}` : "";
|
|
664
|
+
const bound = max === Infinity ? "" : `${n}<${max}`;
|
|
665
|
+
const r = this.resumable ? this.resumePoint(fn) : undefined;
|
|
666
|
+
const save = r === undefined
|
|
667
|
+
? ""
|
|
668
|
+
: `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${acc},count:${n},pos:${st}});`;
|
|
669
|
+
// With `collect` (never in resumable builds), the element adds itself once the loop check passed
|
|
670
|
+
const element = collect === undefined
|
|
671
|
+
? `${this.gen(node.self, keep, x, `break ${bad};`, fn, d)}${check}${add}`
|
|
672
|
+
: collect.element(`break ${bad};`, check);
|
|
673
|
+
const body = `${loop}:for(;${bound};${n}++){${st}=p;` +
|
|
674
|
+
`${bad}:{${element}continue ${loop};}` +
|
|
675
|
+
`${save}if($S.mode!=="Backtrack"${min > 0 ? `||${n}<${min}` : ""}){${fail}}p=${st};break ${loop};}`;
|
|
676
|
+
if (r === undefined)
|
|
677
|
+
return `${init}${n}=0;${body}${collect?.finish ?? ""}`;
|
|
678
|
+
return r.wrap(acc, `${acc}=cp.acc;${n}=cp.count;p=cp.pos;`, `${init}${n}=0;`, body);
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* In a resumable build, a repetition entered at `entry` may restart from a
|
|
682
|
+
* checkpoint (`restore`, with the checkpoint in `cp`), or return the result
|
|
683
|
+
* of a `Done` one; a resumed repetition records its result when it ends.
|
|
684
|
+
*/
|
|
685
|
+
resumePoint(fn) {
|
|
686
|
+
const key = this.const({});
|
|
687
|
+
const entry = this.v(fn);
|
|
688
|
+
const resumed = this.v(fn);
|
|
689
|
+
const done = this.label();
|
|
690
|
+
return {
|
|
691
|
+
key,
|
|
692
|
+
entry,
|
|
693
|
+
resumed,
|
|
694
|
+
wrap: (value, restore, fresh, body) => `${entry}=p;${resumed}=false;${done}:{if($S.resume!==void 0){const cp=R.take($S,${key},p);` +
|
|
695
|
+
`if(cp instanceof R.Done){p=cp.pos;${value}=cp.value;break ${done};}` +
|
|
696
|
+
`if(cp!==void 0){${restore}${resumed}=true;}}if(!${resumed}){${fresh}}${body}` +
|
|
697
|
+
`if(${resumed})R.done($S,${key},${entry},${value},p);}`
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
separated(node, used, out, fail, fn, d, collect) {
|
|
701
|
+
const { max, min } = node;
|
|
702
|
+
const keep = out !== null && used && collect === undefined;
|
|
703
|
+
if (max === 0)
|
|
704
|
+
return collect !== undefined ? collect.init + collect.finish : keep ? `${out}=[];` : "";
|
|
705
|
+
const x = keep ? this.v(fn) : null;
|
|
706
|
+
const acc = keep ? out : this.v(fn);
|
|
707
|
+
const n = this.v(fn);
|
|
708
|
+
const st = this.v(fn);
|
|
709
|
+
const done = this.label();
|
|
710
|
+
const loop = this.label();
|
|
711
|
+
const push = keep ? `${acc}.push(${x});` : "";
|
|
712
|
+
const r = this.resumable ? this.resumePoint(fn) : undefined;
|
|
713
|
+
const save = r === undefined
|
|
714
|
+
? ""
|
|
715
|
+
: `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${acc},count:${n},pos:${st}});`;
|
|
716
|
+
// Runs `body`; on its failure, stops the repetition (or fails if `fatal`)
|
|
717
|
+
const attempt = (body, success, fatal, checkpoint) => {
|
|
718
|
+
const ok = this.label();
|
|
719
|
+
const bad = this.label();
|
|
720
|
+
return `${ok}:{${bad}:{${body(`break ${bad};`)}${success}break ${ok};}` +
|
|
721
|
+
`${checkpoint}if($S.mode!=="Backtrack"${fatal}){${fail}}p=${st};break ${done};}`;
|
|
722
|
+
};
|
|
723
|
+
const self = (f) => collect === undefined ? this.gen(node.self, keep, x, f, fn, d) : collect.element(f, "");
|
|
724
|
+
const sep = (f) => this.gen(node.separator, false, null, f, fn, d);
|
|
725
|
+
const check = max === Infinity ? `if(p===${st}){$S.mode="Cut";${fail}}` : "";
|
|
726
|
+
const first = `${collect?.init ?? (keep ? `${acc}=[];` : "")}${n}=0;${st}=p;` + attempt(self, `${push}${n}++;`, min > 0 ? "||true" : "", "");
|
|
727
|
+
const rest = `${loop}:while(${max === Infinity ? "true" : `${n}<${max}`}){${st}=p;` +
|
|
728
|
+
attempt(sep, "", `||${n}<${min}`, save) + check +
|
|
729
|
+
attempt(self, `${push}${n}++;continue ${loop};`, `||${n}<${min}`, save) +
|
|
730
|
+
`}`;
|
|
731
|
+
if (r === undefined)
|
|
732
|
+
return `${done}:{${first}${rest}}${collect?.finish ?? ""}`;
|
|
733
|
+
// `done` ends the repetition; the checkpoint wrapper records the result after it
|
|
734
|
+
return r.wrap(acc, `${acc}=cp.acc;${n}=cp.count;p=cp.pos;`, "", `${done}:{if(!${r.resumed}){${first}}${rest}}`);
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* `map(self, Object.fromEntries)`, where `entriesShape(self)`: builds the
|
|
738
|
+
* object directly, assigning each entry as it is parsed (see
|
|
739
|
+
* `internal/entries.ts`), without the pairs and their array. `cut`,
|
|
740
|
+
* `preceded`, `terminated` and diagnostics around the repetition are
|
|
741
|
+
* compiled as usual around it.
|
|
742
|
+
*/
|
|
743
|
+
entries(p, out, fail, fn, d) {
|
|
744
|
+
const node = nodeOf(p);
|
|
745
|
+
switch (node._tag) {
|
|
746
|
+
case "Mode": {
|
|
747
|
+
const ok = this.label();
|
|
748
|
+
const bad = this.label();
|
|
749
|
+
return `${ok}:{${bad}:{${this.entries(node.self, out, `break ${bad};`, fn, d)}break ${ok};}` +
|
|
750
|
+
`if($S.mode===${JSON.stringify(node.from)})$S.mode=${JSON.stringify(node.to)};${fail}}`;
|
|
751
|
+
}
|
|
752
|
+
case "Seq": {
|
|
753
|
+
const i = node.output;
|
|
754
|
+
return node.parsers.map((q, j) => j === i ? this.entries(q, out, fail, fn, d) : this.gen(q, false, null, fail, fn, d)).join("");
|
|
755
|
+
}
|
|
756
|
+
case "Repeat":
|
|
757
|
+
case "Separated": {
|
|
758
|
+
const pair = pairOf(node.self);
|
|
759
|
+
const deferred = this.v(fn);
|
|
760
|
+
const key = this.v(fn);
|
|
761
|
+
const value = this.v(fn);
|
|
762
|
+
const collect = {
|
|
763
|
+
init: `${out}={};${deferred}=void 0;`,
|
|
764
|
+
element: (f, check) => pair.parsers.map((q, j) => this.gen(q, j === pair.key || j === pair.value, j === pair.key ? key : j === pair.value ? value : null, f, fn, d)).join("") + check +
|
|
765
|
+
`if(${deferred}===void 0&&R.isPlainKey(${key}))${out}[${key}]=${value};` +
|
|
766
|
+
`else ${deferred}=R.addEntry(${out},${deferred},${key},${value});`,
|
|
767
|
+
finish: `if(${deferred}!==void 0)R.flushEntries(${out},${deferred});`
|
|
768
|
+
};
|
|
769
|
+
return node._tag === "Repeat"
|
|
770
|
+
? this.repeat(node, true, out, fail, fn, d, collect)
|
|
771
|
+
: this.separated(node, true, out, fail, fn, d, collect);
|
|
772
|
+
}
|
|
773
|
+
default:
|
|
774
|
+
return this.entries(node.self, out, fail, fn, d);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
// ---------------------------------------------------------------------------
|
|
778
|
+
// Tokens
|
|
779
|
+
// ---------------------------------------------------------------------------
|
|
780
|
+
/** An expression: is the unit `c`, known to be below `size` (at most `TableSize`), accepted? */
|
|
781
|
+
member(accept, c, size = TableSize) {
|
|
782
|
+
return tableTest(accept, c, size) ?? `${this.const(accept)}[${c}]===1`;
|
|
783
|
+
}
|
|
784
|
+
/** An expression: is the code point `c` (at least `TableSize`) accepted? */
|
|
785
|
+
memberHigh(set, negate, c) {
|
|
786
|
+
if (set.ranges !== undefined && !set.ranges.some(([, b]) => b >= TableSize))
|
|
787
|
+
return negate ? "true" : "false";
|
|
788
|
+
return `${this.const(set.test)}(${c})!==${negate}`;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* An expression: does a `Text` set accept the unit `c`, known to be below
|
|
792
|
+
* `r.fast`? Code points from `TableSize` up are tested by the set's
|
|
793
|
+
* function, unless its ranges show they are all rejected.
|
|
794
|
+
*/
|
|
795
|
+
textAccepts(r, cs, accept, negate, c) {
|
|
796
|
+
if (r.fast <= TableSize)
|
|
797
|
+
return `(${this.member(accept, c, r.fast)})`;
|
|
798
|
+
const member = this.member(accept, c);
|
|
799
|
+
if (reachesAbove(cs, TableSize))
|
|
800
|
+
return `(${c}<${TableSize}?${member}:${this.const(cs.test)}(${c})!==${negate})`;
|
|
801
|
+
return negate ? `(${c}>=${TableSize}||${member})` : `(${c}<${TableSize}&&${member})`;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* An expression: does a `Text` set reject `c`, a code point that `decode`
|
|
805
|
+
* returned? The built-in encodings only decode code points from `r.fast`
|
|
806
|
+
* up there.
|
|
807
|
+
*/
|
|
808
|
+
textRejectsDecoded(enc, r, cs, negate, c) {
|
|
809
|
+
if (r.fast > 0 && builtinEncodings.has(enc) && !reachesAbove(cs, r.fast))
|
|
810
|
+
return String(!negate);
|
|
811
|
+
return `${this.const(cs.test)}(${c})===${negate}`;
|
|
812
|
+
}
|
|
813
|
+
token(node, out, fail, fn) {
|
|
814
|
+
const set = (value) => out === null ? "" : `${out}=${value};`;
|
|
815
|
+
const enc = node.encoding;
|
|
816
|
+
const kind = this.kind;
|
|
817
|
+
const failBT = `{${BT}${fail}}`;
|
|
818
|
+
switch (node._tag) {
|
|
819
|
+
case "Any": {
|
|
820
|
+
if (enc === undefined) {
|
|
821
|
+
if (kind === 1)
|
|
822
|
+
return `if(p>=E)${failBT}${set("I[p]")}p++;`;
|
|
823
|
+
const c = this.v(fn);
|
|
824
|
+
return `if(p>=E)${failBT}${c}=I.charCodeAt(p);` +
|
|
825
|
+
`if(${c}>=55296&&${c}<=56319&&p+1<I.length&&(I.charCodeAt(p+1)&64512)===56320){${set("I.slice(p,p+2)")}p+=2}` +
|
|
826
|
+
`else{${set("I[p]")}p++}`;
|
|
827
|
+
}
|
|
828
|
+
const r = this.reader(enc);
|
|
829
|
+
const c = this.v(fn);
|
|
830
|
+
const dec = `${this.const(enc.decode)}(I,p,E)`;
|
|
831
|
+
return `if(p>=E)${failBT}` +
|
|
832
|
+
(r.fast > 0 ? `if(${r.guard("p")}&&(${c}=${r.unit("p")})<${r.fast}){${set(`R.charOf(${c})`)}p+=${r.width}}else` : "") +
|
|
833
|
+
`{${c}=${dec};if(${c}<0)${failBT}${set(`R.charOf(${c})`)}p+=${this.const(enc.width)}(${c})}`;
|
|
834
|
+
}
|
|
835
|
+
case "Literal": {
|
|
836
|
+
const lit = node.literal;
|
|
837
|
+
if (enc !== undefined) {
|
|
838
|
+
if (node.caseless)
|
|
839
|
+
return undefined;
|
|
840
|
+
const encoded = enc.encode(lit);
|
|
841
|
+
return this.literalUnits(typeof encoded === "string" ? unitsOf(encoded) : Array.from(encoded), false, fail) + set(this.lit(lit));
|
|
842
|
+
}
|
|
843
|
+
const literalKind = typeof lit === "string" ? 0 : lit instanceof Uint8Array ? 1 : 2;
|
|
844
|
+
if (kind === 0 && literalKind !== 0)
|
|
845
|
+
return undefined;
|
|
846
|
+
if (literalKind === 2)
|
|
847
|
+
return undefined;
|
|
848
|
+
const codes = kind === 0
|
|
849
|
+
? unitsOf(lit)
|
|
850
|
+
: Array.from(literalKind === 0 ? new TextEncoder().encode(lit) : lit);
|
|
851
|
+
const len = codes.length;
|
|
852
|
+
const value = !node.caseless && literalKind === kind
|
|
853
|
+
? this.lit(lit)
|
|
854
|
+
: kind === 0
|
|
855
|
+
? `I.slice(p-${len},p)`
|
|
856
|
+
: `I.subarray(p-${len},p)`;
|
|
857
|
+
return this.literalUnits(codes, node.caseless, fail) + set(value);
|
|
858
|
+
}
|
|
859
|
+
case "OneOf": {
|
|
860
|
+
const negate = node.negate;
|
|
861
|
+
const cs = enc === undefined ? tokenSet(node.set, kind) : textSet(node.set);
|
|
862
|
+
const accept = acceptTable(cs, negate);
|
|
863
|
+
const c = this.v(fn);
|
|
864
|
+
const value = (len) => this.kind === 0 ? `I.slice(p,p+${len})` : `I[p]`;
|
|
865
|
+
if (enc === undefined) {
|
|
866
|
+
if (kind === 1)
|
|
867
|
+
return `if(p>=E)${failBT}${c}=I[p];if(!(${this.member(accept, c)}))${failBT}${set("I[p]")}p++;`;
|
|
868
|
+
const cp = this.v(fn);
|
|
869
|
+
if (!negate && this.memberHigh(cs, negate, cp) === "false") {
|
|
870
|
+
return `if(p>=E)${failBT}${c}=I.charCodeAt(p);if(${c}>=${TableSize}||!(${this.member(accept, c)}))${failBT}${set("I[p]")}p++;`;
|
|
871
|
+
}
|
|
872
|
+
return `if(p>=E)${failBT}${c}=I.charCodeAt(p);` +
|
|
873
|
+
`if(${c}<${TableSize}){if(!(${this.member(accept, c)}))${failBT}${set("I[p]")}p++}` +
|
|
874
|
+
`else{${cp}=${c};if(${c}>=55296&&${c}<=56319&&p+1<I.length){const d=I.charCodeAt(p+1);` +
|
|
875
|
+
`if((d&64512)===56320)${cp}=((${c}-55296)<<10)+(d-56320)+65536}` +
|
|
876
|
+
`if(!(${this.memberHigh(cs, negate, cp)}))${failBT}` +
|
|
877
|
+
`if(${cp}>65535){${set(value("2"))}p+=2}else{${set("I[p]")}p++}}`;
|
|
878
|
+
}
|
|
879
|
+
const r = this.reader(enc);
|
|
880
|
+
return `if(p>=E)${failBT}` +
|
|
881
|
+
(r.fast > 0
|
|
882
|
+
? `if(${r.guard("p")}&&(${c}=${r.unit("p")})<${r.fast}){if(!${this.textAccepts(r, cs, accept, negate, c)})${failBT}${set(`R.charOf(${c})`)}p+=${r.width}}else`
|
|
883
|
+
: "") +
|
|
884
|
+
`{${c}=${this.const(enc.decode)}(I,p,E);if(${c}<0||${this.textRejectsDecoded(enc, r, cs, negate, c)})${failBT}` +
|
|
885
|
+
`${set(`R.charOf(${c})`)}p+=${this.const(enc.width)}(${c})}`;
|
|
886
|
+
}
|
|
887
|
+
case "TakeWhile":
|
|
888
|
+
return this.takeWhile(node, out, fail, fn);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
/** Matches literal units at `p`, advancing past them. */
|
|
892
|
+
literalUnits(codes, caseless, fail) {
|
|
893
|
+
const len = codes.length;
|
|
894
|
+
if (len === 0)
|
|
895
|
+
return "";
|
|
896
|
+
const test = (c, i) => {
|
|
897
|
+
const at = this.unit(i === 0 ? "p" : `p+${i}`);
|
|
898
|
+
if (caseless && isLetter(c))
|
|
899
|
+
return `(${at}|32)===${lowerAscii(c)}`;
|
|
900
|
+
return `${at}===${c}`;
|
|
901
|
+
};
|
|
902
|
+
if (len <= 8) {
|
|
903
|
+
// Reading past the end gives NaN or undefined, which matches nothing
|
|
904
|
+
return `if(!(${codes.map(test).join("&&")})){${BT}${fail}}p+=${len};`;
|
|
905
|
+
}
|
|
906
|
+
if (this.kind === 0 && !caseless) {
|
|
907
|
+
const str = String.fromCharCode(...codes);
|
|
908
|
+
return `if(!I.startsWith(${JSON.stringify(str)},p)){${BT}${fail}}p+=${len};`;
|
|
909
|
+
}
|
|
910
|
+
const expect = this.const(Uint8Array.from(caseless ? codes.map(lowerAscii) : codes));
|
|
911
|
+
const lower = caseless ? `${this.const(lowerAscii)}` : "";
|
|
912
|
+
return `if(E-p<${len}){${BT}${fail}}for(let i=0;i<${len};i++)if(${lower}(${this.unit("p+i")})!==${expect}[i]){${BT}${fail}}p+=${len};`;
|
|
913
|
+
}
|
|
914
|
+
takeWhile(node, out, fail, fn) {
|
|
915
|
+
const enc = node.encoding;
|
|
916
|
+
const { max, min, negate } = node;
|
|
917
|
+
const kind = this.kind;
|
|
918
|
+
const cs = enc === undefined ? tokenSet(node.set, kind) : textSet(node.set);
|
|
919
|
+
const accept = acceptTable(cs, negate);
|
|
920
|
+
const st = this.v(fn);
|
|
921
|
+
const c = this.v(fn);
|
|
922
|
+
const n = this.v(fn);
|
|
923
|
+
const loop = this.label();
|
|
924
|
+
const counted = max !== Infinity || min > 1;
|
|
925
|
+
const count = counted ? `${n}++;` : "";
|
|
926
|
+
const bound = max !== Infinity ? `&&${n}<${max}` : "";
|
|
927
|
+
const slice = enc !== undefined
|
|
928
|
+
? `${this.const(enc.text)}(I,${st},p)`
|
|
929
|
+
: kind === 0
|
|
930
|
+
? `I.slice(${st},p)`
|
|
931
|
+
: `I.subarray(${st},p)`;
|
|
932
|
+
const finish = (min > 0 ? `if(${counted ? n : `p-${st}`}<${min}){${BT}${fail}}` : "") +
|
|
933
|
+
(out === null ? "" : `${out}=${slice};`);
|
|
934
|
+
const head = `${st}=p;${counted ? `${n}=0;` : ""}${loop}:while(p<E${bound}){${c}=${this.unit("p")};`;
|
|
935
|
+
const member = this.member(accept, c);
|
|
936
|
+
if (kind === 1 && (enc === undefined || fastLimit(enc) >= TableSize)) {
|
|
937
|
+
// Bytes, one token each
|
|
938
|
+
return `${head}if(!(${member}))break;p++;${count}}${finish}`;
|
|
939
|
+
}
|
|
940
|
+
if (enc !== undefined) {
|
|
941
|
+
// Text: inline the encoding's single-unit code points, decode the rest
|
|
942
|
+
const r = this.reader(enc);
|
|
943
|
+
// The loop condition already checks a unit of width 1
|
|
944
|
+
const guard = r.width === 1 ? "" : `${r.guard("p")}&&`;
|
|
945
|
+
return `${st}=p;${counted ? `${n}=0;` : ""}${loop}:while(p<E${bound}){` +
|
|
946
|
+
(r.fast > 0
|
|
947
|
+
? `if(${guard}(${c}=${r.unit("p")})<${r.fast}){if(!${this.textAccepts(r, cs, accept, negate, c)})break ${loop};p+=${r.width}}else`
|
|
948
|
+
: "") +
|
|
949
|
+
`{${c}=${this.const(enc.decode)}(I,p,E);if(${c}<0||${this.textRejectsDecoded(enc, r, cs, negate, c)})break ${loop};` +
|
|
950
|
+
`p+=${this.const(enc.width)}(${c})}${count}}${finish}`;
|
|
951
|
+
}
|
|
952
|
+
const fast = 0xd800;
|
|
953
|
+
const small = TableSize;
|
|
954
|
+
const highTest = (v) => this.memberHigh(cs, negate, v);
|
|
955
|
+
// Token on strings: code points, with lenient surrogates
|
|
956
|
+
const cp = this.v(fn);
|
|
957
|
+
const never = highTest(c) === "false";
|
|
958
|
+
// Every code point from `TableSize` up is accepted: without a count,
|
|
959
|
+
// surrogate pairs can be skipped a unit at a time
|
|
960
|
+
const always = highTest(c) === "true";
|
|
961
|
+
const rest = never && !negate
|
|
962
|
+
? `break;`
|
|
963
|
+
: always && !counted
|
|
964
|
+
? `p++;`
|
|
965
|
+
: `if(${c}<${fast}||${c}>56319){if(!(${highTest(c)}))break ${loop};p++}` +
|
|
966
|
+
`else{${cp}=${c};if(p+1<I.length){const d=I.charCodeAt(p+1);if((d&64512)===56320)${cp}=((${c}-55296)<<10)+(d-56320)+65536}` +
|
|
967
|
+
`if(!(${highTest(cp)}))break ${loop};p+=${cp}>65535?2:1}`;
|
|
968
|
+
return `${head}if(${c}<${small}){if(!(${member}))break;p++}else ${rest}${count}}${finish}`;
|
|
969
|
+
}
|
|
970
|
+
escaped(p, node, out, fail, fn, d) {
|
|
971
|
+
const enc = node.encoding;
|
|
972
|
+
const ctrl = node.control;
|
|
973
|
+
const r = enc === undefined ? undefined : this.reader(enc);
|
|
974
|
+
if (r !== undefined && !(ctrl < r.fast))
|
|
975
|
+
return this.delegate(p, !node.raw, out, fail, fn);
|
|
976
|
+
const raw = node.raw;
|
|
977
|
+
const acc = raw ? null : out ?? this.v(fn);
|
|
978
|
+
const x = raw ? null : this.v(fn);
|
|
979
|
+
const b = this.v(fn);
|
|
980
|
+
const st = this.v(fn);
|
|
981
|
+
const okv = this.v(fn);
|
|
982
|
+
const loop = this.label();
|
|
983
|
+
const bad = this.label();
|
|
984
|
+
const normal = this.gen(node.normal, !raw, x, `break ${bad};`, fn, d);
|
|
985
|
+
const escape = this.gen(node.escape, !raw, x, fail, fn, d);
|
|
986
|
+
const onNormal = raw
|
|
987
|
+
? `if(p===${b}){$S.mode="Cut";${fail}}continue ${loop};`
|
|
988
|
+
: `if(p!==${b}){${acc}+=${x};continue ${loop}}`;
|
|
989
|
+
const slice = enc !== undefined
|
|
990
|
+
? `${this.const(enc.text)}(I,${st},p)`
|
|
991
|
+
: this.kind === 0
|
|
992
|
+
? `I.slice(${st},p)`
|
|
993
|
+
: `I.subarray(${st},p)`;
|
|
994
|
+
const ctrlTest = r === undefined ? `p<E&&${this.unit("p")}===${ctrl}` : `${r.guard("p")}&&${r.unit("p")}===${ctrl}`;
|
|
995
|
+
return `${raw ? `${st}=p;` : `${acc}="";`}${loop}:while(p<E){${b}=p;${okv}=false;` +
|
|
996
|
+
`${bad}:{${normal}${okv}=true;}` +
|
|
997
|
+
`if(${okv}){${onNormal}}else{if($S.mode!=="Backtrack"){${fail}}p=${b}}` +
|
|
998
|
+
`if(${ctrlTest}){p+=${r?.width ?? 1};${escape}${raw ? "" : `${acc}+=${x};`}}else break ${loop};}` +
|
|
999
|
+
(raw && out !== null ? `${out}=${slice};` : "");
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
/** The UTF-16 code units of a string. */
|
|
1003
|
+
const unitsOf = (s) => Array.from({ length: s.length }, (_, i) => s.charCodeAt(i));
|
|
1004
|
+
/** 1 where a small code is accepted (taking `negate` into account). */
|
|
1005
|
+
const acceptTable = (set, negate) => {
|
|
1006
|
+
const t = new Uint8Array(TableSize);
|
|
1007
|
+
for (let c = 0; c < TableSize; c++)
|
|
1008
|
+
t[c] = (set.table[c] === 1) !== negate ? 1 : 0;
|
|
1009
|
+
return t;
|
|
1010
|
+
};
|
|
1011
|
+
/** Generates the source of a compiled parser, without building it. */
|
|
1012
|
+
export const generate = (root, kind, resumable = false) => {
|
|
1013
|
+
const e = new Emitter(root, kind, resumable);
|
|
1014
|
+
const main = e.rule(root, true);
|
|
1015
|
+
const source = `"use strict";let $I,$E,$S,P;${e.functions.join("\n")}\n` +
|
|
1016
|
+
`return function run(state){const sI=$I,sE=$E,sS=$S,sP=P;$I=state.input;$E=state.end;$S=state;` +
|
|
1017
|
+
`try{const v=${main}(state.pos);if(v!==F)state.pos=P;return v}finally{$I=sI;$E=sE;$S=sS;P=sP}}`;
|
|
1018
|
+
return { source, constants: e.constants, fallbacks: e.fallbacks };
|
|
1019
|
+
};
|
|
1020
|
+
/** Compiles a root parser for one input kind with `new Function`. */
|
|
1021
|
+
export const compileJit = (root, kind, resumable = false) => {
|
|
1022
|
+
const { constants, fallbacks, source } = generate(root, kind, resumable);
|
|
1023
|
+
const run = globalThis.Function("C", "F", "R", source)(constants, Fail, runtime);
|
|
1024
|
+
// Generated code reads past `end` expecting nothing to match there, which
|
|
1025
|
+
// holds when `end` is the input's length, as it is in the runners.
|
|
1026
|
+
let closure;
|
|
1027
|
+
const step = (state) => state.end === state.input.length ? run(state) : (closure ??= createClosureCompiler(kind, resumable).compile(root, true))(state);
|
|
1028
|
+
return { step, fallbacks, source };
|
|
1029
|
+
};
|
|
1030
|
+
//# sourceMappingURL=jit.js.map
|