@thomasfosterau/effect-parse 0.4.0 → 0.5.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 +40 -14
- package/dist/Ascii.d.ts +11 -4
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +7 -1
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +64 -35
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +7 -4
- package/dist/Bits.js.map +1 -1
- package/dist/Compiler.d.ts +74 -2
- package/dist/Compiler.d.ts.map +1 -1
- package/dist/Compiler.js +78 -1
- package/dist/Compiler.js.map +1 -1
- package/dist/Expression.d.ts.map +1 -1
- package/dist/Expression.js +86 -74
- package/dist/Expression.js.map +1 -1
- package/dist/Input.d.ts +37 -4
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +42 -0
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts +5 -15
- package/dist/Nom.d.ts.map +1 -1
- package/dist/Nom.js.map +1 -1
- package/dist/ParseError.d.ts +2 -2
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js +1 -1
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +370 -28
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +390 -19
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.js +2 -2
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +3 -2
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +15 -10
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.d.ts +23 -5
- package/dist/Syntax.d.ts.map +1 -1
- package/dist/Syntax.js.map +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +9 -9
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +55 -10
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +59 -21
- package/dist/Token.js.map +1 -1
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +257 -97
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/entries.d.ts +3 -1
- package/dist/internal/compiler/entries.d.ts.map +1 -1
- package/dist/internal/compiler/entries.js +3 -2
- package/dist/internal/compiler/entries.js.map +1 -1
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +7 -1
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/graph.d.ts +17 -0
- package/dist/internal/compiler/graph.d.ts.map +1 -0
- package/dist/internal/compiler/graph.js +56 -0
- package/dist/internal/compiler/graph.js.map +1 -0
- package/dist/internal/compiler/jit.d.ts +5 -0
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +185 -96
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +10 -2
- package/dist/internal/compiler/registry.d.ts.map +1 -1
- package/dist/internal/compiler/registry.js.map +1 -1
- package/dist/internal/compiler/search.d.ts +32 -0
- package/dist/internal/compiler/search.d.ts.map +1 -0
- package/dist/internal/compiler/search.js +68 -0
- package/dist/internal/compiler/search.js.map +1 -0
- package/dist/internal/compiler/sets.d.ts.map +1 -1
- package/dist/internal/compiler/sets.js +1 -1
- package/dist/internal/compiler/sets.js.map +1 -1
- package/dist/internal/compiler/wasm.d.ts +8 -0
- package/dist/internal/compiler/wasm.d.ts.map +1 -0
- package/dist/internal/compiler/wasm.js +2769 -0
- package/dist/internal/compiler/wasm.js.map +1 -0
- package/dist/internal/compiler/wasmAsm.d.ts +199 -0
- package/dist/internal/compiler/wasmAsm.d.ts.map +1 -0
- package/dist/internal/compiler/wasmAsm.js +427 -0
- package/dist/internal/compiler/wasmAsm.js.map +1 -0
- package/dist/internal/entries.d.ts +3 -1
- package/dist/internal/entries.d.ts.map +1 -1
- package/dist/internal/entries.js +1 -2
- package/dist/internal/entries.js.map +1 -1
- package/dist/internal/node.d.ts +58 -2
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +13 -3
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/numbers.d.ts.map +1 -1
- package/dist/internal/numbers.js.map +1 -1
- package/dist/internal/run.d.ts +12 -0
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +51 -11
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +6 -0
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +8 -2
- package/dist/internal/stack.js.map +1 -1
- package/dist/internal/syntax.d.ts +4 -4
- package/dist/internal/syntax.d.ts.map +1 -1
- package/dist/internal/syntax.js +18 -10
- package/dist/internal/syntax.js.map +1 -1
- package/dist/internal/text.d.ts +32 -0
- package/dist/internal/text.d.ts.map +1 -1
- package/dist/internal/text.js +68 -14
- package/dist/internal/text.js.map +1 -1
- package/dist/internal/tokens.d.ts +13 -0
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +59 -39
- package/dist/internal/tokens.js.map +1 -1
- package/package.json +5 -4
- package/src/Ascii.ts +42 -9
- package/src/Binary.ts +151 -71
- package/src/Bits.ts +12 -6
- package/src/Compiler.ts +101 -10
- package/src/Expression.ts +94 -81
- package/src/Input.ts +65 -6
- package/src/Nom.ts +15 -5
- package/src/ParseError.ts +9 -4
- package/src/Parser.ts +682 -129
- package/src/ParserSchema.ts +1 -1
- package/src/ParserStream.ts +40 -19
- package/src/Syntax.ts +60 -36
- package/src/Text.ts +16 -15
- package/src/Token.ts +138 -29
- package/src/internal/compiler/closure.ts +362 -135
- package/src/internal/compiler/entries.ts +4 -2
- package/src/internal/compiler/first.ts +10 -0
- package/src/internal/compiler/graph.ts +68 -0
- package/src/internal/compiler/jit.ts +284 -154
- package/src/internal/compiler/registry.ts +10 -2
- package/src/internal/compiler/search.ts +68 -0
- package/src/internal/compiler/sets.ts +7 -3
- package/src/internal/compiler/wasm.ts +3203 -0
- package/src/internal/compiler/wasmAsm.ts +484 -0
- package/src/internal/entries.ts +10 -4
- package/src/internal/node.ts +54 -4
- package/src/internal/numbers.ts +3 -1
- package/src/internal/run.ts +71 -12
- package/src/internal/stack.ts +8 -1
- package/src/internal/syntax.ts +39 -18
- package/src/internal/text.ts +97 -9
- package/src/internal/tokens.ts +96 -14
|
@@ -7,13 +7,16 @@ import * as Resume from "../resume.js";
|
|
|
7
7
|
import { compileAny } from "../tokens.js";
|
|
8
8
|
import { backend as backend$1, createClosureCompiler } from "./closure.js";
|
|
9
9
|
import * as Option from "effect/Option";
|
|
10
|
-
import { charOf, fastLimit, latin1, string, utf16be, utf16le, utf8, validate } from "../text.js";
|
|
10
|
+
import { ShortText, charOf, fastLimit, latin1, string, textIn, utf16be, utf16le, utf8, utf8Text, validate } from "../text.js";
|
|
11
11
|
import * as Arr from "effect/Array";
|
|
12
12
|
import { addEntry, flushEntries, fromEntries, isPlainKey } from "../entries.js";
|
|
13
|
-
import
|
|
13
|
+
import * as Stack from "../stack.js";
|
|
14
|
+
import { Defer, NodeId, nodeOf, scansFloat } from "../node.js";
|
|
14
15
|
import { buildsEntries, pairOf } from "./entries.js";
|
|
15
16
|
import { Strict, planAlt } from "./first.js";
|
|
17
|
+
import { SearchHead, StopCache, nextStop, stopChars } from "./search.js";
|
|
16
18
|
import { TableSize, acceptTable, kindOfEncoding, reachesAbove, textSet, tokenSet } from "./sets.js";
|
|
19
|
+
import { countRefs, isLeaf, isLetter, isSmall, lowerAscii, selectorOf } from "./graph.js";
|
|
17
20
|
// src/internal/compiler/jit.ts
|
|
18
21
|
/**
|
|
19
22
|
* The JIT: emits JavaScript source for a parser's description and builds
|
|
@@ -33,11 +36,15 @@ import { TableSize, acceptTable, kindOfEncoding, reachesAbove, textSet, tokenSet
|
|
|
33
36
|
*/
|
|
34
37
|
/** Helpers available to generated code as `R`. */
|
|
35
38
|
const runtime = {
|
|
39
|
+
StopCache,
|
|
40
|
+
nextStop,
|
|
36
41
|
take: Resume.take,
|
|
37
42
|
save: Resume.save,
|
|
38
43
|
Done: Resume.Done,
|
|
39
44
|
/** Records the result of a resumed repetition (see `Resume.done`). */
|
|
40
45
|
done: (state, key, entry, value, pos) => Resume.save(state, key, entry, new Resume.Done(value, pos)),
|
|
46
|
+
isStackOverflow: Stack.isStackOverflow,
|
|
47
|
+
tooDeep: Stack.tooDeep,
|
|
41
48
|
some: Option.some,
|
|
42
49
|
none: Option.none,
|
|
43
50
|
charOf,
|
|
@@ -56,8 +63,9 @@ const runtime = {
|
|
|
56
63
|
s.mode = "Backtrack";
|
|
57
64
|
return Fail;
|
|
58
65
|
}
|
|
59
|
-
return enc
|
|
60
|
-
}
|
|
66
|
+
return textIn(enc, s, start, end);
|
|
67
|
+
},
|
|
68
|
+
utf8Text
|
|
61
69
|
};
|
|
62
70
|
let checked;
|
|
63
71
|
let supported = false;
|
|
@@ -87,22 +95,6 @@ const Margin = 4;
|
|
|
87
95
|
var Fn = class {
|
|
88
96
|
vars = [];
|
|
89
97
|
};
|
|
90
|
-
const isLeaf = (node) => {
|
|
91
|
-
switch (node._tag) {
|
|
92
|
-
case "Succeed":
|
|
93
|
-
case "Fail":
|
|
94
|
-
case "Eof":
|
|
95
|
-
case "Location":
|
|
96
|
-
case "RestLen":
|
|
97
|
-
case "Any":
|
|
98
|
-
case "Literal":
|
|
99
|
-
case "OneOf":
|
|
100
|
-
case "TakeWhile": return true;
|
|
101
|
-
default: return false;
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
const lowerAscii = (c) => c >= 65 && c <= 90 ? c + 32 : c;
|
|
105
|
-
const isLetter = (c) => c >= 65 && c <= 90 || c >= 97 && c <= 122;
|
|
106
98
|
/** The inclusive ranges of 1s in a table. */
|
|
107
99
|
const runsOf = (table) => {
|
|
108
100
|
const out = [];
|
|
@@ -160,7 +152,7 @@ var Emitter = class {
|
|
|
160
152
|
constantIndex = /* @__PURE__ */ new Map();
|
|
161
153
|
functions = [];
|
|
162
154
|
rules = [/* @__PURE__ */ new Map(), /* @__PURE__ */ new Map()];
|
|
163
|
-
refs
|
|
155
|
+
refs;
|
|
164
156
|
closure;
|
|
165
157
|
next = 0;
|
|
166
158
|
constructor(root, kind, resumable = false) {
|
|
@@ -168,24 +160,11 @@ var Emitter = class {
|
|
|
168
160
|
this.kind = kind;
|
|
169
161
|
this.resumable = resumable;
|
|
170
162
|
this.closure = createClosureCompiler(kind, resumable);
|
|
171
|
-
this.countRefs(root);
|
|
163
|
+
this.refs = countRefs(root);
|
|
172
164
|
}
|
|
173
165
|
get fallbacks() {
|
|
174
166
|
return this.closure.fallbacks;
|
|
175
167
|
}
|
|
176
|
-
/** Counts the distinct parents of each parser. */
|
|
177
|
-
countRefs(root) {
|
|
178
|
-
const seen = /* @__PURE__ */ new Set();
|
|
179
|
-
const stack = [root];
|
|
180
|
-
for (let p = stack.pop(); p !== void 0; p = stack.pop()) {
|
|
181
|
-
if (seen.has(p)) continue;
|
|
182
|
-
seen.add(p);
|
|
183
|
-
for (const c of new Set(children(nodeOf(p)))) {
|
|
184
|
-
this.refs.set(c, (this.refs.get(c) ?? 0) + 1);
|
|
185
|
-
stack.push(c);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
168
|
const(value) {
|
|
190
169
|
let i = this.constantIndex.get(value);
|
|
191
170
|
if (i === void 0) {
|
|
@@ -195,6 +174,11 @@ var Emitter = class {
|
|
|
195
174
|
}
|
|
196
175
|
return `C[${i}]`;
|
|
197
176
|
}
|
|
177
|
+
/** The text of `[start, p)` in `enc`: UTF-8 decodes through the state's ASCII run (see `utf8Text`). */
|
|
178
|
+
text(enc, start) {
|
|
179
|
+
const text = `${this.const(enc.text)}(I,${start},p)`;
|
|
180
|
+
return enc === utf8 ? `(p-${start}>${ShortText}?R.utf8Text($S,${start},p):${text})` : text;
|
|
181
|
+
}
|
|
198
182
|
/** A JavaScript literal for simple values, otherwise a constant. */
|
|
199
183
|
lit(value) {
|
|
200
184
|
if (value === void 0) return "void 0";
|
|
@@ -257,19 +241,9 @@ var Emitter = class {
|
|
|
257
241
|
gen(p, used, out, fail, fn, depth) {
|
|
258
242
|
const node = nodeOf(p);
|
|
259
243
|
if (Predicate.isTagged(node, "Suspend")) return this.call(this.rule(node.resolve(), used), out, fail);
|
|
260
|
-
if (!isLeaf(node) && ((this.refs.get(p) ?? 0) > 1 && !
|
|
244
|
+
if (!isLeaf(node) && ((this.refs.get(p) ?? 0) > 1 && !isSmall(p) || depth > 32)) return this.call(this.rule(p, used), out, fail);
|
|
261
245
|
return this.inline(p, used, out, fail, fn, depth);
|
|
262
246
|
}
|
|
263
|
-
/** Whether a parser is small enough to inline wherever it is used. */
|
|
264
|
-
small(p) {
|
|
265
|
-
let budget = 6;
|
|
266
|
-
const visit = (q) => {
|
|
267
|
-
const node = nodeOf(q);
|
|
268
|
-
if (--budget < 0 || Predicate.isTagged(node, "Suspend") || Predicate.isTagged(node, "Escaped") || Predicate.isTagged(node, "Dispatch")) return false;
|
|
269
|
-
return children(node).every(visit);
|
|
270
|
-
};
|
|
271
|
-
return visit(p);
|
|
272
|
-
}
|
|
273
247
|
/**
|
|
274
248
|
* In a resumable build: token code (which fails by breaking out of `bad`)
|
|
275
249
|
* that read within `lookahead` units of the end of a partial input re-runs
|
|
@@ -344,7 +318,7 @@ var Emitter = class {
|
|
|
344
318
|
case "Succeed": return set(this.lit(node.value));
|
|
345
319
|
case "Sync": return used ? set(`${this.const(node.evaluate)}()`) : "";
|
|
346
320
|
case "Fail": return `$S.mode=${JSON.stringify(node.mode)};${fail}`;
|
|
347
|
-
case "Eof": return `if(p<E){${BT}${fail}}` + (this.resumable ? `if($S.partial){$S.
|
|
321
|
+
case "Eof": return `if(p<E){${BT}${fail}}` + (this.resumable ? `if($S.partial){$S.incomplete(1);${fail}}` : "") + set("void 0");
|
|
348
322
|
case "Location": return set("p");
|
|
349
323
|
case "RestLen": return set("E-p");
|
|
350
324
|
case "Suspend": return this.call(this.rule(node.resolve(), used), out, fail);
|
|
@@ -367,7 +341,7 @@ var Emitter = class {
|
|
|
367
341
|
const code = this.float(node, out, fail, fn, d);
|
|
368
342
|
if (code !== void 0) return code;
|
|
369
343
|
}
|
|
370
|
-
if (node.f === fromEntries && out !== null &&
|
|
344
|
+
if (node.f === fromEntries && out !== null && buildsEntries(node.self)) return this.entries(node.self, out, fail, fn, d);
|
|
371
345
|
const t = this.v(fn);
|
|
372
346
|
return this.gen(node.self, true, t, fail, fn, d) + set(`${this.const(node.f)}(${t})`);
|
|
373
347
|
}
|
|
@@ -383,6 +357,17 @@ var Emitter = class {
|
|
|
383
357
|
const [tag, field] = Predicate.isTagged(node, "TryMap") ? ["Success", "success"] : ["Some", "value"];
|
|
384
358
|
return this.gen(node.self, true, t, fail, fn, d) + `${r}=${this.const(node.f)}(${t});if(${r}._tag!=="${tag}"){${BT}${fail}}${set(`${r}.${field}`)}`;
|
|
385
359
|
}
|
|
360
|
+
case "FastPath": {
|
|
361
|
+
const st = this.v(fn);
|
|
362
|
+
const t = out ?? this.v(fn);
|
|
363
|
+
const done = this.label();
|
|
364
|
+
return `${st}=p;$S.pos=p;${t}=${this.const(node.fast)}($S);${done}:{if(${t}!==${this.const(Defer)}){p=$S.pos;break ${done}}p=${st};${this.gen(node.self, used, out, fail, fn, d)}}`;
|
|
365
|
+
}
|
|
366
|
+
case "MapOrFail": {
|
|
367
|
+
const t = this.v(fn);
|
|
368
|
+
const r = out ?? this.v(fn);
|
|
369
|
+
return this.gen(node.self, true, t, fail, fn, d) + `${r}=${this.const(node.f)}(${t});if(${r}===F){${BT}${fail}}`;
|
|
370
|
+
}
|
|
386
371
|
case "Filter": {
|
|
387
372
|
const t = out ?? this.v(fn);
|
|
388
373
|
return this.gen(node.self, true, t, fail, fn, d) + `if(!${this.const(node.predicate)}(${t})){${BT}${fail}}`;
|
|
@@ -409,6 +394,12 @@ var Emitter = class {
|
|
|
409
394
|
const extra = Predicate.isTagged(node, "WithSpan") ? `{start:${st},end:p}` : `$S.slice(${st},p)`;
|
|
410
395
|
return `${st}=p;` + this.gen(node.self, true, t, fail, fn, d) + set(`[${t},${extra}]`);
|
|
411
396
|
}
|
|
397
|
+
case "MapSpan": {
|
|
398
|
+
if (!used) return this.gen(node.self, false, null, fail, fn, d);
|
|
399
|
+
const st = this.v(fn);
|
|
400
|
+
const t = this.v(fn);
|
|
401
|
+
return `${st}=p;` + this.gen(node.self, true, t, fail, fn, d) + set(`${this.const(node.f)}(${t},${st},p)`);
|
|
402
|
+
}
|
|
412
403
|
case "Span": {
|
|
413
404
|
const st = this.v(fn);
|
|
414
405
|
return `${st}=p;` + this.gen(node.self, false, null, fail, fn, d) + set(`{start:${st},end:p}`);
|
|
@@ -420,6 +411,7 @@ var Emitter = class {
|
|
|
420
411
|
}
|
|
421
412
|
case "Context":
|
|
422
413
|
case "Expected":
|
|
414
|
+
case "Quiet":
|
|
423
415
|
case "MapError":
|
|
424
416
|
case "OrFail":
|
|
425
417
|
case "Trace":
|
|
@@ -458,6 +450,11 @@ var Emitter = class {
|
|
|
458
450
|
if (node.keys.includes("__proto__")) return code + `${out}={};` + node.keys.map((k, i) => `${out}[${JSON.stringify(k)}]=${vars[i]};`).join("");
|
|
459
451
|
return code + `${out}={${node.keys.map((k, i) => `${JSON.stringify(k)}:${vars[i]}`).join(",")}};`;
|
|
460
452
|
}
|
|
453
|
+
case "SeqWith": {
|
|
454
|
+
if (out === null) return node.parsers.map((q) => this.gen(q, false, null, fail, fn, d)).join("");
|
|
455
|
+
const vars = node.parsers.map(() => this.v(fn));
|
|
456
|
+
return node.parsers.map((q, i) => this.gen(q, true, vars[i], fail, fn, d)).join("") + `${out}=${this.const(node.f)}(${vars.join(",")});`;
|
|
457
|
+
}
|
|
461
458
|
case "ZipWith": {
|
|
462
459
|
if (out === null) return this.gen(node.self, false, null, fail, fn, d) + this.gen(node.that, false, null, fail, fn, d);
|
|
463
460
|
const x = this.v(fn);
|
|
@@ -467,8 +464,13 @@ var Emitter = class {
|
|
|
467
464
|
case "Alt": return this.alt(node, used, out, fail, fn, d);
|
|
468
465
|
case "Dispatch": return this.dispatch(p, node, used, out, fail, fn, d);
|
|
469
466
|
case "Repeat":
|
|
470
|
-
case "Fold": return this.repeat(node, used, out, fail, fn, d);
|
|
467
|
+
case "Fold": return (out === null && Predicate.isTagged(node, "Repeat") ? this.repeatRun(p, node, fail, fn, d) : void 0) ?? this.repeat(node, used, out, fail, fn, d);
|
|
471
468
|
case "Separated": return this.separated(node, used, out, fail, fn, d);
|
|
469
|
+
case "Fixed": {
|
|
470
|
+
if (this.kind !== 1) return this.delegate(p, used, out, fail, fn);
|
|
471
|
+
const read = used ? `${out ?? this.v(fn)}=${this.const(node.read)}(I,p,$S);` : "";
|
|
472
|
+
return `if(E-p>=${node.size}){${read}p+=${node.size}}else{${this.delegate(p, used, out, fail, fn)}}`;
|
|
473
|
+
}
|
|
472
474
|
default: return this.delegate(p, used, out, fail, fn);
|
|
473
475
|
}
|
|
474
476
|
}
|
|
@@ -552,9 +554,7 @@ var Emitter = class {
|
|
|
552
554
|
const r = out ?? this.v(fn);
|
|
553
555
|
return code + `default:{$S.pos=p;${r}=${q}.step($S);if(${r}===F){${fail}}p=$S.pos;}}`;
|
|
554
556
|
};
|
|
555
|
-
|
|
556
|
-
const peek = Predicate.isTagged(sel, "Peek");
|
|
557
|
-
if (peek) sel = nodeOf(sel.self);
|
|
557
|
+
const { node: sel, peek } = selectorOf(node.selector);
|
|
558
558
|
if (!Predicate.isTagged(sel, "Any") || !this.native(sel.encoding)) return general(true);
|
|
559
559
|
const enc = sel.encoding;
|
|
560
560
|
const r = enc === void 0 ? void 0 : this.reader(enc);
|
|
@@ -577,6 +577,57 @@ var Emitter = class {
|
|
|
577
577
|
code += `case -1:{${BT}${fail}}default:{if(${u}<${size}){${BT}${fail}}${general(false)}}}`;
|
|
578
578
|
return code;
|
|
579
579
|
}
|
|
580
|
+
/**
|
|
581
|
+
* A repetition whose elements are not kept, rewritten to scan a run of a
|
|
582
|
+
* character set in one loop instead of one element at a time (#79):
|
|
583
|
+
*
|
|
584
|
+
* - `repeat(oneOf(S), range)` matches what `takeWhile(S, range)` matches,
|
|
585
|
+
* stopping, failing and reporting `Incomplete` where it does, so it
|
|
586
|
+
* compiles as that `takeWhile`, with the repetition's own step as the
|
|
587
|
+
* fallback near the end of a partial input.
|
|
588
|
+
* - `repeat(alt(oneOf(S), ...rest))` with `min` at most 1 and no `max`
|
|
589
|
+
* matches what `repeat(alt(takeWhile(S, { min: 1 }), ...rest))` matches:
|
|
590
|
+
* a run of `S` is a run of one-character elements, and the counts that
|
|
591
|
+
* differ only matter for other ranges. The first branch is a synthetic
|
|
592
|
+
* `takeWhile` node; outside resumable builds, which never run its step.
|
|
593
|
+
*
|
|
594
|
+
* Returns `undefined` when neither applies.
|
|
595
|
+
*/
|
|
596
|
+
repeatRun(p, node, fail, fn, d) {
|
|
597
|
+
if (this.kind === 2) return void 0;
|
|
598
|
+
const element = nodeOf(node.self);
|
|
599
|
+
if (Predicate.isTagged(element, "OneOf") && this.native(element.encoding)) {
|
|
600
|
+
const { encoding, negate, set } = element;
|
|
601
|
+
return this.leaf(p, {
|
|
602
|
+
_tag: "TakeWhile",
|
|
603
|
+
set,
|
|
604
|
+
negate,
|
|
605
|
+
encoding,
|
|
606
|
+
min: node.min,
|
|
607
|
+
max: node.max
|
|
608
|
+
}, false, null, fail, fn);
|
|
609
|
+
}
|
|
610
|
+
if (this.resumable || node.min > 1 || node.max !== Infinity || !Predicate.isTagged(element, "Alt")) return void 0;
|
|
611
|
+
const [first, ...rest] = element.parsers;
|
|
612
|
+
const head = first === void 0 ? void 0 : nodeOf(first);
|
|
613
|
+
if (head === void 0 || !Predicate.isTagged(head, "OneOf") || !this.native(head.encoding) || Arr.isReadonlyArrayEmpty(rest)) return;
|
|
614
|
+
const run = synthetic({
|
|
615
|
+
_tag: "TakeWhile",
|
|
616
|
+
set: head.set,
|
|
617
|
+
negate: head.negate,
|
|
618
|
+
encoding: head.encoding,
|
|
619
|
+
min: 1,
|
|
620
|
+
max: Infinity
|
|
621
|
+
}, node.self.step);
|
|
622
|
+
const alt = synthetic({
|
|
623
|
+
_tag: "Alt",
|
|
624
|
+
parsers: [run, ...rest]
|
|
625
|
+
}, node.self.step);
|
|
626
|
+
return this.repeat({
|
|
627
|
+
...node,
|
|
628
|
+
self: alt
|
|
629
|
+
}, false, null, fail, fn, d);
|
|
630
|
+
}
|
|
580
631
|
repeat(node, used, out, fail, fn, d, collect) {
|
|
581
632
|
const { max, min } = node;
|
|
582
633
|
const loop = this.label();
|
|
@@ -603,11 +654,12 @@ var Emitter = class {
|
|
|
603
654
|
const check = max === Infinity ? `if(p===${st}){$S.mode="Cut";${fail}}` : "";
|
|
604
655
|
const bound = max === Infinity ? "" : `${n}<${max}`;
|
|
605
656
|
const r = this.resumable ? this.resumePoint(fn) : void 0;
|
|
606
|
-
const
|
|
657
|
+
const held = collect?.value ?? acc;
|
|
658
|
+
const save = r === void 0 ? "" : `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${held},${collect?.saved ?? ""}count:${n},pos:${st}});`;
|
|
607
659
|
const element = collect === void 0 ? `${this.gen(node.self, tracked, x, `break ${bad};`, fn, d)}${check}${add}` : collect.element(`break ${bad};`, check);
|
|
608
660
|
const body = `${loop}:for(;${bound};${n}++){${cond === void 0 ? "" : `if(${min > 0 ? `${n}>=${min}&&` : ""}!${this.const(cond)}(${acc}))break ${loop};`}${st}=p;${bad}:{${element}continue ${loop};}${save}if($S.mode!=="Backtrack"${min > 0 ? `||${n}<${min}` : ""}){${fail}}p=${st};break ${loop};}`;
|
|
609
661
|
if (r === void 0) return `${init}${n}=0;${body}${collect?.finish ?? ""}`;
|
|
610
|
-
return r.wrap(
|
|
662
|
+
return r.wrap(held, `${held}=cp.acc;${collect?.restore ?? ""}${n}=cp.count;p=cp.pos;`, `${init}${n}=0;`, `${body}${collect?.finish ?? ""}`);
|
|
611
663
|
}
|
|
612
664
|
/**
|
|
613
665
|
* In a resumable build, a repetition entered at `entry` may restart from a
|
|
@@ -638,7 +690,8 @@ var Emitter = class {
|
|
|
638
690
|
const loop = this.label();
|
|
639
691
|
const push = keep ? `${acc}.push(${x});` : "";
|
|
640
692
|
const r = this.resumable ? this.resumePoint(fn) : void 0;
|
|
641
|
-
const
|
|
693
|
+
const held = collect?.value ?? acc;
|
|
694
|
+
const save = r === void 0 ? "" : `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${held},${collect?.saved ?? ""}count:${n},pos:${st}});`;
|
|
642
695
|
const attempt = (body, success, fatal, checkpoint) => {
|
|
643
696
|
const ok = this.label();
|
|
644
697
|
const bad = this.label();
|
|
@@ -650,7 +703,7 @@ var Emitter = class {
|
|
|
650
703
|
const first = `${collect?.init ?? (keep ? `${acc}=[];` : "")}${n}=0;${st}=p;` + attempt(self, `${push}${n}++;`, min > 0 ? "||true" : "", "");
|
|
651
704
|
const rest = `${loop}:while(${max === Infinity ? "true" : `${n}<${max}`}){${st}=p;` + attempt(sep, "", `||${n}<${min}`, save) + check + attempt(self, `${push}${n}++;continue ${loop};`, `||${n}<${min}`, save) + `}`;
|
|
652
705
|
if (r === void 0) return `${done}:{${first}${rest}}${collect?.finish ?? ""}`;
|
|
653
|
-
return r.wrap(
|
|
706
|
+
return r.wrap(held, `${held}=cp.acc;${collect?.restore ?? ""}${n}=cp.count;p=cp.pos;`, "", `${done}:{if(!${r.resumed}){${first}}${rest}}${collect?.finish ?? ""}`);
|
|
654
707
|
}
|
|
655
708
|
/**
|
|
656
709
|
* `map(self, Object.fromEntries)`, where `buildsEntries(self)`: builds the
|
|
@@ -661,35 +714,35 @@ var Emitter = class {
|
|
|
661
714
|
*/
|
|
662
715
|
entries(p, out, fail, fn, d) {
|
|
663
716
|
const node = nodeOf(p);
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
const t = this.v(fn);
|
|
679
|
-
return this.gen(p, true, t, fail, fn, d) + `${out}=${this.const(fromEntries)}(${t});`;
|
|
680
|
-
}
|
|
681
|
-
const deferred = this.v(fn);
|
|
682
|
-
const key = this.v(fn);
|
|
683
|
-
const value = this.v(fn);
|
|
684
|
-
const collect = {
|
|
685
|
-
init: `${out}={};${deferred}=void 0;`,
|
|
686
|
-
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 + `if(${deferred}===void 0&&R.isPlainKey(${key}))${out}[${key}]=${value};else ${deferred}=R.addEntry(${out},${deferred},${key},${value});`,
|
|
687
|
-
finish: `if(${deferred}!==void 0)R.flushEntries(${out},${deferred});`
|
|
688
|
-
};
|
|
689
|
-
return Predicate.isTagged(node, "Repeat") ? this.repeat(node, true, out, fail, fn, d, collect) : this.separated(node, true, out, fail, fn, d, collect);
|
|
717
|
+
if (Predicate.isTagged(node, "Mode")) {
|
|
718
|
+
const ok = this.label();
|
|
719
|
+
const bad = this.label();
|
|
720
|
+
return `${ok}:{${bad}:{${this.entries(node.self, out, `break ${bad};`, fn, d)}break ${ok};}if($S.mode===${JSON.stringify(node.from)})$S.mode=${JSON.stringify(node.to)};${fail}}`;
|
|
721
|
+
}
|
|
722
|
+
if (Predicate.isTagged(node, "Seq")) {
|
|
723
|
+
const i = node.output;
|
|
724
|
+
return node.parsers.map((q, j) => j === i ? this.entries(q, out, fail, fn, d) : this.gen(q, false, null, fail, fn, d)).join("");
|
|
725
|
+
}
|
|
726
|
+
if (Predicate.isTagged(node, "Repeat") || Predicate.isTagged(node, "Separated")) {
|
|
727
|
+
const pair = pairOf(node.self);
|
|
728
|
+
if (pair === void 0) {
|
|
729
|
+
const t = this.v(fn);
|
|
730
|
+
return this.gen(p, true, t, fail, fn, d) + `${out}=${this.const(fromEntries)}(${t});`;
|
|
690
731
|
}
|
|
691
|
-
|
|
732
|
+
const deferred = this.v(fn);
|
|
733
|
+
const key = this.v(fn);
|
|
734
|
+
const value = this.v(fn);
|
|
735
|
+
const collect = {
|
|
736
|
+
init: `${out}={};${deferred}=void 0;`,
|
|
737
|
+
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 + `if(${deferred}===void 0&&R.isPlainKey(${key}))${out}[${key}]=${value};else ${deferred}=R.addEntry(${out},${deferred},${key},${value});`,
|
|
738
|
+
finish: `if(${deferred}!==void 0)R.flushEntries(${out},${deferred});`,
|
|
739
|
+
value: out,
|
|
740
|
+
saved: `def:${deferred},`,
|
|
741
|
+
restore: `${deferred}=cp.def;`
|
|
742
|
+
};
|
|
743
|
+
return Predicate.isTagged(node, "Repeat") ? this.repeat(node, true, out, fail, fn, d, collect) : this.separated(node, true, out, fail, fn, d, collect);
|
|
692
744
|
}
|
|
745
|
+
return this.entries(node.self, out, fail, fn, d);
|
|
693
746
|
}
|
|
694
747
|
/** An expression: is the unit `c`, known to be below `size` (at most `TableSize`), accepted? */
|
|
695
748
|
member(accept, c, size = TableSize) {
|
|
@@ -745,9 +798,10 @@ var Emitter = class {
|
|
|
745
798
|
return this.literalUnits(Predicate.isString(encoded) ? unitsOf(encoded) : Array.from(encoded), false, fail) + set(this.lit(lit));
|
|
746
799
|
}
|
|
747
800
|
const literalKind = Predicate.isString(lit) ? 0 : lit instanceof Uint8Array ? 1 : 2;
|
|
748
|
-
|
|
749
|
-
if (
|
|
750
|
-
|
|
801
|
+
let codes;
|
|
802
|
+
if (Predicate.isString(lit)) codes = kind === 0 ? unitsOf(lit) : Array.from(new TextEncoder().encode(lit));
|
|
803
|
+
else if (kind === 1 && lit instanceof Uint8Array) codes = Array.from(lit);
|
|
804
|
+
else return void 0;
|
|
751
805
|
const len = codes.length;
|
|
752
806
|
const value = !node.caseless && literalKind === kind ? this.lit(lit) : kind === 0 ? `I.slice(p-${len},p)` : `I.subarray(p-${len},p)`;
|
|
753
807
|
return this.literalUnits(codes, node.caseless, fail) + set(value);
|
|
@@ -801,7 +855,7 @@ var Emitter = class {
|
|
|
801
855
|
const counted = max !== Infinity || min > 1;
|
|
802
856
|
const count = counted ? `${n}++;` : "";
|
|
803
857
|
const bound = max !== Infinity ? `&&${n}<${max}` : "";
|
|
804
|
-
const slice = enc !== void 0 ?
|
|
858
|
+
const slice = enc !== void 0 ? this.text(enc, st) : kind === 0 ? `I.slice(${st},p)` : `I.subarray(${st},p)`;
|
|
805
859
|
const finish = (min > 0 ? `if(${counted ? n : `p-${st}`}<${min}){${BT}${fail}}` : "") + (out === null ? "" : `${out}=${slice};`);
|
|
806
860
|
const head = `${st}=p;${counted ? `${n}=0;` : ""}${loop}:while(p<E${bound}){${c}=${this.unit("p")};`;
|
|
807
861
|
const member = this.member(accept, c);
|
|
@@ -811,6 +865,8 @@ var Emitter = class {
|
|
|
811
865
|
const guard = r.width === 1 ? "" : `${r.guard("p")}&&`;
|
|
812
866
|
return `${st}=p;${counted ? `${n}=0;` : ""}${loop}:while(p<E${bound}){` + (r.fast > 0 ? `if(${guard}(${c}=${r.unit("p")})<${r.fast}){if(!${this.textAccepts(r, cs, accept, negate, c)})break ${loop};p+=${r.width}}else` : "") + `{${c}=${this.const(enc.decode)}(I,p,E);if(${c}<0||${this.textRejectsDecoded(enc, r, cs, negate, c)})break ${loop};p+=${this.const(enc.width)}(${c})}${count}}${finish}`;
|
|
813
867
|
}
|
|
868
|
+
const stops = negate && !counted ? stopChars(cs) : void 0;
|
|
869
|
+
if (stops !== void 0) return this.stopSearch(stops, st, c, finish, fn);
|
|
814
870
|
const fast = 55296;
|
|
815
871
|
const small = TableSize;
|
|
816
872
|
const highTest = (v) => this.memberHigh(cs, negate, v);
|
|
@@ -820,6 +876,21 @@ var Emitter = class {
|
|
|
820
876
|
return `${head}if(${c}<${small}){if(!(${member}))break;p++}else ${never && !negate ? `break;` : always && !counted ? `p++;` : `if(${c}<${fast}||${c}>56319){if(!(${highTest(c)}))break ${loop};p++}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}if(!(${highTest(cp)}))break ${loop};p+=${cp}>65535?2:1}`}${count}}${finish}`;
|
|
821
877
|
}
|
|
822
878
|
/**
|
|
879
|
+
* `Token.takeTill` on a string, of one to `MaxStops` characters that are
|
|
880
|
+
* whole code points: the set accepts every other unit, surrogates
|
|
881
|
+
* included, so the run ends at the first stop character. The first
|
|
882
|
+
* `SearchHead` units are tested one at a time, which beats a native
|
|
883
|
+
* search on short runs; a longer run ends at the next occurrence that
|
|
884
|
+
* `nextStop` finds with `indexOf` (#75).
|
|
885
|
+
*/
|
|
886
|
+
stopSearch(stops, st, c, finish, fn) {
|
|
887
|
+
const h = this.v(fn);
|
|
888
|
+
const test = stops.map((u) => `${c}!==${u}`).join("&&");
|
|
889
|
+
const search = stops.length === 1 ? `{p=I.indexOf(${JSON.stringify(String.fromCharCode(stops[0]))},p);if(p<0)p=E}` : `p=R.nextStop(${this.const(new StopCache(stops.map((u) => String.fromCharCode(u))))},$S,p);`;
|
|
890
|
+
const head = stops.length === 1 ? SearchHead : 2 * SearchHead;
|
|
891
|
+
return `${st}=p;${h}=p+${head}<E?p+${head}:E;while(p<${h}&&(${c}=I.charCodeAt(p),${test}))p++;if(p===${h}&&p<E&&(${c}=I.charCodeAt(p),${test}))${search}${finish}`;
|
|
892
|
+
}
|
|
893
|
+
/**
|
|
823
894
|
* `escaped`. In a resumable build it keeps the closure compiler's
|
|
824
895
|
* checkpoints: where the next segment starts and, unless raw, the text
|
|
825
896
|
* so far, saved when a segment or an escape runs out of input.
|
|
@@ -843,37 +914,54 @@ var Emitter = class {
|
|
|
843
914
|
const normal = this.gen(node.normal, !raw, x, `break ${bad};`, fn, d);
|
|
844
915
|
const escape = this.gen(node.escape, !raw, x, `${save(b)}${fail}`, fn, d);
|
|
845
916
|
const onNormal = raw ? `if(p===${b}){$S.mode="Cut";${fail}}continue ${loop};` : `if(p!==${b}){${acc}+=${x};continue ${loop}}`;
|
|
846
|
-
const slice = enc !== void 0 ?
|
|
917
|
+
const slice = enc !== void 0 ? this.text(enc, st) : this.kind === 0 ? `I.slice(${st},p)` : `I.subarray(${st},p)`;
|
|
847
918
|
const ctrlTest = rd === void 0 ? `p<E&&${this.unit("p")}===${ctrl}` : `${rd.guard("p")}&&${rd.unit("p")}===${ctrl}`;
|
|
848
919
|
const stop = r === void 0 ? loop : this.label();
|
|
849
920
|
const loopCode = `${loop}:while(p<E){${b}=p;${okv}=false;${bad}:{${normal}${okv}=true;}if(${okv}){${onNormal}}else{if($S.mode!=="Backtrack"){${save(b)}${fail}}p=${b}}if(${ctrlTest}){p+=${rd?.width ?? 1};${escape}${raw ? "" : `${acc}+=${x};`}}else break ${stop};}`;
|
|
850
|
-
const body = r === void 0 ? loopCode : `${stop}:{${loopCode}if($S.partial){$S.
|
|
921
|
+
const body = r === void 0 ? loopCode : `${stop}:{${loopCode}if($S.partial){$S.incomplete(0);${save("p")}${fail}}}`;
|
|
851
922
|
if (r === void 0) return `${raw ? `${st}=p;` : `${acc}="";`}${body}${raw && out !== null ? `${out}=${slice};` : ""}`;
|
|
852
923
|
if (acc !== null) return r.wrap(acc, `${acc}=cp.out;p=cp.pos;`, `${acc}="";`, body);
|
|
853
924
|
const value = out ?? this.v(fn);
|
|
854
925
|
return r.wrap(value, "p=cp;", "", `${body}${value}=${out === null ? "void 0" : slice};`);
|
|
855
926
|
}
|
|
856
927
|
};
|
|
928
|
+
/**
|
|
929
|
+
* A parser that exists only as a description, for the rewrites of
|
|
930
|
+
* `Emitter.repeatRun`. `step` is never run on the paths they compile (the
|
|
931
|
+
* token code of a native `takeWhile`, and alternatives inline), so it is
|
|
932
|
+
* the step of the repetition's element, which describes the same language
|
|
933
|
+
* when its value is unused.
|
|
934
|
+
*/
|
|
935
|
+
const synthetic = (node, step) => {
|
|
936
|
+
return {
|
|
937
|
+
step,
|
|
938
|
+
name: void 0,
|
|
939
|
+
[NodeId]: node
|
|
940
|
+
};
|
|
941
|
+
};
|
|
857
942
|
/** The UTF-16 code units of a string. */
|
|
858
943
|
const unitsOf = (s) => Array.from({ length: s.length }, (_, i) => s.charCodeAt(i));
|
|
859
944
|
/** Generates the source of a compiled parser, without building it. */
|
|
860
945
|
const generate = (root, kind, resumable = false) => {
|
|
861
946
|
const e = new Emitter(root, kind, resumable);
|
|
862
947
|
const main = e.rule(root, true);
|
|
948
|
+
let closure;
|
|
949
|
+
const short = e.const((state) => (closure ??= createClosureCompiler(kind, resumable).compile(root, true))(state));
|
|
863
950
|
return {
|
|
864
|
-
source: `"use strict";let $I,$E,$S,P;${e.functions.join("\n")}\nreturn function run(state){const sI=$I,sE=$E,sS=$S,sP=P;$I=state.input;$E=state.end;$S=state;try{const v=${main}(state.pos);if(v!==F)state.pos=P;return v}finally{$I=sI;$E=sE;$S=sS;P=sP}}`,
|
|
951
|
+
source: `"use strict";let $I,$E,$S,P;${e.functions.join("\n")}\nreturn function run(state){if(state.end!==state.input.length)return ${short}(state);const sI=$I,sE=$E,sS=$S,sP=P;$I=state.input;$E=state.end;$S=state;try{const v=${main}(state.pos);if(v!==F)state.pos=P;return v}catch(e){if(!R.isStackOverflow(e))throw e;return R.tooDeep(state)}finally{$I=sI;$E=sE;$S=sS;P=sP}}`,
|
|
865
952
|
constants: e.constants,
|
|
866
953
|
fallbacks: e.fallbacks
|
|
867
954
|
};
|
|
868
955
|
};
|
|
869
|
-
/**
|
|
956
|
+
/**
|
|
957
|
+
* Compiles a root parser for one input kind with `new Function`. The step
|
|
958
|
+
* is the generated entry, which handles every state and reports call-stack
|
|
959
|
+
* overflows itself.
|
|
960
|
+
*/
|
|
870
961
|
const compileJit = (root, kind, resumable = false) => {
|
|
871
962
|
const { constants, fallbacks, source } = generate(root, kind, resumable);
|
|
872
|
-
const run = globalThis.Function("C", "F", "R", source)(constants, Fail, runtime);
|
|
873
|
-
let closure;
|
|
874
|
-
const step = (state) => state.end === state.input.length ? run(state) : (closure ??= createClosureCompiler(kind, resumable).compile(root, true))(state);
|
|
875
963
|
return {
|
|
876
|
-
step,
|
|
964
|
+
step: globalThis.Function("C", "F", "R", source)(constants, Fail, runtime),
|
|
877
965
|
fallbacks,
|
|
878
966
|
source
|
|
879
967
|
};
|
|
@@ -888,6 +976,7 @@ const backend = { build: (p, kind, resumable) => {
|
|
|
888
976
|
onSuccess: ({ fallbacks, source, step }) => ({
|
|
889
977
|
backend: "jit",
|
|
890
978
|
step,
|
|
979
|
+
guarded: true,
|
|
891
980
|
fallbacks,
|
|
892
981
|
error: void 0,
|
|
893
982
|
source
|
|
@@ -898,6 +987,6 @@ const backend = { build: (p, kind, resumable) => {
|
|
|
898
987
|
})
|
|
899
988
|
});
|
|
900
989
|
} };
|
|
901
|
-
export { backend };
|
|
990
|
+
export { backend, jitSupported };
|
|
902
991
|
|
|
903
992
|
//# sourceMappingURL=jit.js.map
|