@thomasfosterau/effect-parse 0.5.0 → 0.6.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/CHANGELOG.md +202 -0
- package/README.md +19 -1
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +5 -3
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts +5 -2
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +20 -3
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +4 -0
- package/dist/Bits.js.map +1 -1
- package/dist/Input.d.ts +21 -3
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +31 -4
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts.map +1 -1
- package/dist/ParseError.d.ts +69 -11
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js +129 -28
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +10 -4
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +10 -24
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.d.ts.map +1 -1
- package/dist/ParserSchema.js +1 -2
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +119 -12
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +282 -55
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.js +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +13 -7
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +10 -1
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +14 -5
- package/dist/Token.js.map +1 -1
- package/dist/internal/compiler/closure.d.ts +1 -1
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +7 -17
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +2 -1
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/graph.d.ts +23 -1
- package/dist/internal/compiler/graph.d.ts.map +1 -1
- package/dist/internal/compiler/graph.js +27 -5
- package/dist/internal/compiler/graph.js.map +1 -1
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +26 -13
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +1 -1
- package/dist/internal/compiler/wasm.d.ts.map +1 -1
- package/dist/internal/compiler/wasm.js +105 -34
- package/dist/internal/compiler/wasm.js.map +1 -1
- package/dist/internal/node.d.ts +19 -1
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +42 -8
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/run.d.ts +3 -1
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +4 -2
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +8 -0
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +17 -1
- package/dist/internal/stack.js.map +1 -1
- package/dist/internal/tokens.d.ts +10 -3
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +22 -9
- package/dist/internal/tokens.js.map +1 -1
- package/dist/internal/tuple.d.ts +6 -0
- package/dist/internal/tuple.d.ts.map +1 -0
- package/dist/internal/tuple.js +170 -0
- package/dist/internal/tuple.js.map +1 -0
- package/package.json +17 -10
- package/src/Ascii.ts +5 -3
- package/src/Binary.ts +24 -3
- package/src/Bits.ts +4 -0
- package/src/Input.ts +37 -4
- package/src/ParseError.ts +206 -52
- package/src/Parser.ts +19 -29
- package/src/ParserSchema.ts +1 -2
- package/src/ParserStream.ts +336 -62
- package/src/Text.ts +16 -12
- package/src/Token.ts +14 -5
- package/src/internal/compiler/closure.ts +9 -18
- package/src/internal/compiler/first.ts +6 -0
- package/src/internal/compiler/graph.ts +32 -4
- package/src/internal/compiler/jit.ts +37 -17
- package/src/internal/compiler/wasm.ts +141 -68
- package/src/internal/node.ts +49 -3
- package/src/internal/run.ts +4 -2
- package/src/internal/stack.ts +17 -0
- package/src/internal/tokens.ts +20 -10
- package/src/internal/tuple.ts +141 -0
|
@@ -2,15 +2,15 @@ import * as Predicate from "effect/Predicate";
|
|
|
2
2
|
import * as Result from "effect/Result";
|
|
3
3
|
import { Fail, State } from "../../Input.js";
|
|
4
4
|
import { floatTexts, toFloat } from "../numbers.js";
|
|
5
|
+
import { nodeOf } from "../node.js";
|
|
5
6
|
import { backend as backend$1, jitSupported } from "./jit.js";
|
|
6
7
|
import * as Option from "effect/Option";
|
|
7
8
|
import { charOf, fastLimit, latin1, string, utf16be, utf16le, utf8 } from "../text.js";
|
|
8
9
|
import * as Arr from "effect/Array";
|
|
9
10
|
import { addEntry, flushEntries, fromEntries, isPlainKey } from "../entries.js";
|
|
10
|
-
import { nodeOf } from "../node.js";
|
|
11
11
|
import { buildsEntries, isDiagnostic, pairOf } from "./entries.js";
|
|
12
12
|
import { TableSize, kindOfEncoding, textSet, tokenSet } from "./sets.js";
|
|
13
|
-
import { countRefs, isLeaf, isLetter, isSmall, lowerAscii, selectorOf } from "./graph.js";
|
|
13
|
+
import { countInlined, countRefs, isLeaf, isLetter, isSmall, lowerAscii, selectorOf } from "./graph.js";
|
|
14
14
|
import { Body, F64, I32, Op, Simd, encodeModule } from "./wasmAsm.js";
|
|
15
15
|
// src/internal/compiler/wasm.ts
|
|
16
16
|
/**
|
|
@@ -84,6 +84,32 @@ const Slack = 65536;
|
|
|
84
84
|
const MaxWrites = 4096;
|
|
85
85
|
/** Bytes left after the cursor beyond which a driver's parse of the same buffer runs the JIT (see `run`). */
|
|
86
86
|
const LargeRest = 4096;
|
|
87
|
+
/**
|
|
88
|
+
* The most linear memory a module is grown to: 2 GiB. Every offset into the
|
|
89
|
+
* memory, the trace's included, is then a non-negative `i32`. A parse that
|
|
90
|
+
* would need more (its input copied, and four to seven times as much of
|
|
91
|
+
* trace), or whose `memory.grow` the engine refuses, runs on the JIT, which
|
|
92
|
+
* needs neither (#123).
|
|
93
|
+
*/
|
|
94
|
+
const MaxMemory = 2 ** 31;
|
|
95
|
+
/**
|
|
96
|
+
* Above this much linear memory after a parse (128 MiB), the module is
|
|
97
|
+
* given a new memory: `WebAssembly.Memory` never shrinks, so one large
|
|
98
|
+
* input would otherwise hold five to ten times its size for the life of
|
|
99
|
+
* the process, per compiled parser and input kind.
|
|
100
|
+
*/
|
|
101
|
+
const ReleaseAbove = 134217728;
|
|
102
|
+
/** What `grow` throws when the trace cannot grow: the parse starts again on the JIT. */
|
|
103
|
+
var MemoryExhausted = class {};
|
|
104
|
+
/**
|
|
105
|
+
* A repetition bound as the `i32` it is compared with, unsigned. A count
|
|
106
|
+
* never reaches 2^32 - 1 (the input is in a 32-bit memory), so a larger
|
|
107
|
+
* minimum is one that cannot be met, and a larger maximum is none at all
|
|
108
|
+
* (see `unbounded`); written as given, it was truncated to its low 32 bits.
|
|
109
|
+
*/
|
|
110
|
+
const bound = (n) => n >= 4294967295 ? 4294967295 : n;
|
|
111
|
+
/** Whether a maximum can never be reached by a count (see {@link bound}). */
|
|
112
|
+
const unbounded = (max) => max >= 4294967295;
|
|
87
113
|
/** The most literals a compiled `takeUntilAny` checks at each candidate; more are searched in JavaScript. */
|
|
88
114
|
const MaxUntil = 64;
|
|
89
115
|
/** The most leading bytes of each literal a SIMD search tests at once. */
|
|
@@ -341,6 +367,8 @@ var WasmCompiler = class WasmCompiler {
|
|
|
341
367
|
rules = [/* @__PURE__ */ new WeakMap(), /* @__PURE__ */ new WeakMap()];
|
|
342
368
|
pending = [];
|
|
343
369
|
refs = /* @__PURE__ */ new WeakMap();
|
|
370
|
+
/** How many parsers have been inlined (see `countInlined`). */
|
|
371
|
+
inlined = 0;
|
|
344
372
|
decoders = /* @__PURE__ */ new WeakMap();
|
|
345
373
|
floatScanners = /* @__PURE__ */ new WeakMap();
|
|
346
374
|
/** The float scanner of `Token` / `Ascii` text, which has no encoding. */
|
|
@@ -578,6 +606,7 @@ var WasmCompiler = class WasmCompiler {
|
|
|
578
606
|
return isBuiltin(enc) && kindOfEncoding(enc) === this.kind;
|
|
579
607
|
}
|
|
580
608
|
inline(p, out, b, fail, bf, depth) {
|
|
609
|
+
this.inlined = countInlined(this.inlined);
|
|
581
610
|
const node = nodeOf(p);
|
|
582
611
|
const s = {
|
|
583
612
|
p,
|
|
@@ -1040,7 +1069,7 @@ var WasmCompiler = class WasmCompiler {
|
|
|
1040
1069
|
stop(b, fail, n, min, restore, done) {
|
|
1041
1070
|
this.unlessBacktrack(b, fail);
|
|
1042
1071
|
if (min > 0) {
|
|
1043
|
-
b.get(n).i32(min).op(Op.i32LtU);
|
|
1072
|
+
b.get(n).i32(bound(min)).op(Op.i32LtU);
|
|
1044
1073
|
b.if(() => b.br(fail));
|
|
1045
1074
|
}
|
|
1046
1075
|
restore();
|
|
@@ -1069,11 +1098,11 @@ var WasmCompiler = class WasmCompiler {
|
|
|
1069
1098
|
b.i32(0).set(n);
|
|
1070
1099
|
b.block((done) => {
|
|
1071
1100
|
this.loop(b, (again) => {
|
|
1072
|
-
if (max
|
|
1101
|
+
if (!unbounded(max)) b.get(n).i32(bound(max)).op(Op.i32GeU).brIf(done);
|
|
1073
1102
|
const saved = this.save(b);
|
|
1074
1103
|
this.attempt(b, (bad) => {
|
|
1075
1104
|
elementCode = element(b, bad, bf);
|
|
1076
|
-
|
|
1105
|
+
this.progress(b, fail, saved[0]);
|
|
1077
1106
|
this.nextElement(b, n, again);
|
|
1078
1107
|
}, () => this.stop(b, fail, n, min, () => this.restore(b, saved), done));
|
|
1079
1108
|
});
|
|
@@ -1115,10 +1144,10 @@ var WasmCompiler = class WasmCompiler {
|
|
|
1115
1144
|
b.br(done);
|
|
1116
1145
|
});
|
|
1117
1146
|
this.loop(b, (again) => {
|
|
1118
|
-
if (max
|
|
1147
|
+
if (!unbounded(max)) b.get(n).i32(bound(max)).op(Op.i32GeU).brIf(done);
|
|
1119
1148
|
mark();
|
|
1120
1149
|
this.attempt(b, (bad) => this.gen(node.separator, "", b, bad, bf, d), () => this.stop(b, fail, n, min, back, done));
|
|
1121
|
-
|
|
1150
|
+
this.progress(b, fail, st);
|
|
1122
1151
|
this.attempt(b, (bad) => {
|
|
1123
1152
|
element(b, bad, new Vars());
|
|
1124
1153
|
this.nextElement(b, n, again);
|
|
@@ -1360,14 +1389,14 @@ var WasmCompiler = class WasmCompiler {
|
|
|
1360
1389
|
b.loop((again) => {
|
|
1361
1390
|
this.atEnd(b, 0);
|
|
1362
1391
|
b.brIf(stop);
|
|
1363
|
-
if (max
|
|
1392
|
+
if (!unbounded(max)) b.get(n).i32(bound(max)).op(Op.i32GeU).brIf(stop);
|
|
1364
1393
|
this.whileStep(enc, b, set, w, wide, stop);
|
|
1365
1394
|
if (counted) b.get(n).i32(1).op(Op.i32Add).set(n);
|
|
1366
1395
|
b.br(again);
|
|
1367
1396
|
});
|
|
1368
1397
|
});
|
|
1369
1398
|
if (min > 0) {
|
|
1370
|
-
if (counted) b.get(n).i32(min).op(Op.i32LtU);
|
|
1399
|
+
if (counted) b.get(n).i32(bound(min)).op(Op.i32LtU);
|
|
1371
1400
|
else b.get(0).get(st).op(Op.i32Eq);
|
|
1372
1401
|
this.failIf(b, s.fail);
|
|
1373
1402
|
}
|
|
@@ -2600,7 +2629,7 @@ const exportedGlobal = (exports, name) => {
|
|
|
2600
2629
|
return g instanceof WebAssembly.Global ? Result.succeed(g) : Result.fail(`no global ${name}`);
|
|
2601
2630
|
};
|
|
2602
2631
|
const run = (root, c, bytes, kind, build, source) => {
|
|
2603
|
-
|
|
2632
|
+
let memory = new WebAssembly.Memory({ initial: 2 });
|
|
2604
2633
|
let size = memory.buffer.byteLength;
|
|
2605
2634
|
let i32 = new Int32Array(memory.buffer);
|
|
2606
2635
|
let f64 = new Float64Array(memory.buffer);
|
|
@@ -2610,13 +2639,20 @@ const run = (root, c, bytes, kind, build, source) => {
|
|
|
2610
2639
|
f64 = new Float64Array(memory.buffer);
|
|
2611
2640
|
u8 = new Uint8Array(memory.buffer);
|
|
2612
2641
|
};
|
|
2613
|
-
/**
|
|
2642
|
+
/**
|
|
2643
|
+
* Grows the memory to at least `bytes`, doubling it: false when that is
|
|
2644
|
+
* more than {@link MaxMemory}, or more than the engine will give.
|
|
2645
|
+
*/
|
|
2614
2646
|
const reserve = (bytes) => {
|
|
2615
2647
|
const have = size;
|
|
2616
|
-
if (have >= bytes) return;
|
|
2617
|
-
|
|
2648
|
+
if (have >= bytes) return true;
|
|
2649
|
+
if (bytes > MaxMemory) return false;
|
|
2650
|
+
const pages = Math.ceil((Math.min(Math.max(bytes, have * 2), MaxMemory) - have) / PageSize);
|
|
2651
|
+
const grown = Result.try(() => memory.grow(pages));
|
|
2652
|
+
if (Result.isFailure(grown)) return false;
|
|
2618
2653
|
size = memory.buffer.byteLength;
|
|
2619
2654
|
views();
|
|
2655
|
+
return true;
|
|
2620
2656
|
};
|
|
2621
2657
|
const steps = c.islands.map(({ parser }) => backend$1.build(parser, kind, false).step ?? parser.step);
|
|
2622
2658
|
const used = c.islands.map(({ used }) => used);
|
|
@@ -2624,8 +2660,7 @@ const run = (root, c, bytes, kind, build, source) => {
|
|
|
2624
2660
|
const checks = c.checks;
|
|
2625
2661
|
let state = idle;
|
|
2626
2662
|
const values = [];
|
|
2627
|
-
const
|
|
2628
|
-
mem: memory,
|
|
2663
|
+
const env = {
|
|
2629
2664
|
island: (id, pos) => {
|
|
2630
2665
|
const s = state;
|
|
2631
2666
|
s.pos = pos;
|
|
@@ -2635,14 +2670,14 @@ const run = (root, c, bytes, kind, build, source) => {
|
|
|
2635
2670
|
return s.pos;
|
|
2636
2671
|
},
|
|
2637
2672
|
grow: () => {
|
|
2638
|
-
reserve(size * 2);
|
|
2673
|
+
if (size >= MaxMemory || !reserve(Math.min(size * 2, MaxMemory))) throw new MemoryExhausted();
|
|
2639
2674
|
build.bind(i32, f64, state.input, state.end, values, state);
|
|
2640
2675
|
return size - Slack;
|
|
2641
2676
|
},
|
|
2642
2677
|
test: (id, cp) => tests[id](cp),
|
|
2643
2678
|
check: (id, from) => {
|
|
2644
2679
|
const s = state;
|
|
2645
|
-
const a = checks[id](build.value(id, from
|
|
2680
|
+
const a = checks[id](build.value(id, from >>> 2));
|
|
2646
2681
|
if (a === Fail) {
|
|
2647
2682
|
s.mode = "Backtrack";
|
|
2648
2683
|
return 0;
|
|
@@ -2654,17 +2689,29 @@ const run = (root, c, bytes, kind, build, source) => {
|
|
|
2654
2689
|
const input = state.input;
|
|
2655
2690
|
return toFloat(Predicate.isString(input) ? input.slice(start, end) : latin1.text(input, start, end).replace(/\0/g, ""));
|
|
2656
2691
|
}
|
|
2657
|
-
}
|
|
2658
|
-
const
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2692
|
+
};
|
|
2693
|
+
const module = new WebAssembly.Module(bytes);
|
|
2694
|
+
/** The entry points of an instance of the module over `mem`. */
|
|
2695
|
+
const instantiate = (mem) => {
|
|
2696
|
+
const exported = new WebAssembly.Instance(module, { env: {
|
|
2697
|
+
...env,
|
|
2698
|
+
mem
|
|
2699
|
+
} }).exports;
|
|
2700
|
+
const rootFn = exported.root;
|
|
2701
|
+
if (!Predicate.isFunction(rootFn)) return Result.fail("no root function");
|
|
2702
|
+
const widenFn = kind === 0 ? exported.widen : rootFn;
|
|
2703
|
+
if (!Predicate.isFunction(widenFn)) return Result.fail("no widen function");
|
|
2704
|
+
const startFn = exported.start;
|
|
2705
|
+
if (!Predicate.isFunction(startFn)) return Result.fail("no start function");
|
|
2706
|
+
return Result.map(exportedGlobal(exported, "mode"), (mode) => ({
|
|
2707
|
+
widen: widenFn,
|
|
2708
|
+
start: startFn,
|
|
2709
|
+
mode
|
|
2710
|
+
}));
|
|
2711
|
+
};
|
|
2712
|
+
const first = instantiate(memory);
|
|
2713
|
+
if (Result.isFailure(first)) return Result.fail(first.failure);
|
|
2714
|
+
let entries = first.success;
|
|
2668
2715
|
let busy = false;
|
|
2669
2716
|
/** The JIT's step for the parser, built on first use. */
|
|
2670
2717
|
let fallback = (s) => {
|
|
@@ -2680,7 +2727,7 @@ const run = (root, c, bytes, kind, build, source) => {
|
|
|
2680
2727
|
*/
|
|
2681
2728
|
const copyString = (input, staging) => {
|
|
2682
2729
|
const { written } = utf8Encoder.encodeInto(input, u8.subarray(staging, staging + 3 * input.length));
|
|
2683
|
-
const replaced =
|
|
2730
|
+
const replaced = entries.widen(staging, written);
|
|
2684
2731
|
if (replaced === 0) return;
|
|
2685
2732
|
const view = new DataView(memory.buffer);
|
|
2686
2733
|
const list = staging + written + 3 >> 2;
|
|
@@ -2696,7 +2743,7 @@ const run = (root, c, bytes, kind, build, source) => {
|
|
|
2696
2743
|
const n = input.length;
|
|
2697
2744
|
const inputBytes = kind === 0 ? 2 * n : n;
|
|
2698
2745
|
const traceStart = Input$1 + inputBytes + 15 & -16;
|
|
2699
|
-
reserve(traceStart + Math.max(Slack * 4, inputBytes * 4, kind === 0 ? 7 * n + 16 : 0));
|
|
2746
|
+
if (!reserve(traceStart + Math.max(Slack * 4, inputBytes * 4, kind === 0 ? 7 * n + 16 : 0))) throw new MemoryExhausted();
|
|
2700
2747
|
if (Predicate.isString(input)) {
|
|
2701
2748
|
if (input !== copied) {
|
|
2702
2749
|
copied = "";
|
|
@@ -2707,13 +2754,29 @@ const run = (root, c, bytes, kind, build, source) => {
|
|
|
2707
2754
|
values.length = 0;
|
|
2708
2755
|
state = s;
|
|
2709
2756
|
build.bind(i32, f64, input, s.end, values, s);
|
|
2710
|
-
const r =
|
|
2757
|
+
const r = entries.start(s.pos, s.end, n, traceStart, size - Slack);
|
|
2711
2758
|
if (r < 0) {
|
|
2712
|
-
s.mode = mode.value === Cut ? "Cut" : "Backtrack";
|
|
2759
|
+
s.mode = entries.mode.value === Cut ? "Cut" : "Backtrack";
|
|
2713
2760
|
return Fail;
|
|
2714
2761
|
}
|
|
2715
2762
|
s.pos = r;
|
|
2716
|
-
return build.root(traceStart
|
|
2763
|
+
return build.root(traceStart >>> 2);
|
|
2764
|
+
};
|
|
2765
|
+
/**
|
|
2766
|
+
* Gives the module a new memory of the initial size, dropping the one a
|
|
2767
|
+
* large parse grew (and the string it held). An instance is bound to its
|
|
2768
|
+
* memory, so the module is instantiated again; its data segments fill
|
|
2769
|
+
* the tables in.
|
|
2770
|
+
*/
|
|
2771
|
+
const release = () => {
|
|
2772
|
+
const fresh = new WebAssembly.Memory({ initial: 2 });
|
|
2773
|
+
const next = instantiate(fresh);
|
|
2774
|
+
if (Result.isFailure(next)) return;
|
|
2775
|
+
memory = fresh;
|
|
2776
|
+
size = fresh.buffer.byteLength;
|
|
2777
|
+
views();
|
|
2778
|
+
copied = "";
|
|
2779
|
+
entries = next.success;
|
|
2717
2780
|
};
|
|
2718
2781
|
/** The bytes copied by the latest parse: parsing them again from further on is a driver at work. */
|
|
2719
2782
|
let lastBytes = /* @__PURE__ */ new Uint8Array(0);
|
|
@@ -2730,13 +2793,21 @@ const run = (root, c, bytes, kind, build, source) => {
|
|
|
2730
2793
|
}
|
|
2731
2794
|
busy = true;
|
|
2732
2795
|
pending = s;
|
|
2796
|
+
const from = s.pos;
|
|
2733
2797
|
const result = Result.try(parsePending);
|
|
2734
2798
|
build.unbind();
|
|
2735
2799
|
state = idle;
|
|
2736
2800
|
pending = idle;
|
|
2737
2801
|
busy = false;
|
|
2738
2802
|
values.length = 0;
|
|
2739
|
-
if (
|
|
2803
|
+
if (size > ReleaseAbove) release();
|
|
2804
|
+
if (Result.isFailure(result)) {
|
|
2805
|
+
if (result.failure instanceof MemoryExhausted) {
|
|
2806
|
+
s.pos = from;
|
|
2807
|
+
return fallback(s);
|
|
2808
|
+
}
|
|
2809
|
+
throw result.failure;
|
|
2810
|
+
}
|
|
2740
2811
|
return result.success;
|
|
2741
2812
|
};
|
|
2742
2813
|
return Result.succeed({
|