@thomasfosterau/effect-parse 0.4.1 → 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 +37 -13
- 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.map +1 -1
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +366 -26
- 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 +1 -1
- package/dist/ParserStream.d.ts +3 -2
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +13 -9
- 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.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 +3 -0
- package/src/Parser.ts +678 -127
- package/src/ParserStream.ts +37 -18
- 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 +8 -2
- 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
|
@@ -0,0 +1,2769 @@
|
|
|
1
|
+
import * as Predicate from "effect/Predicate";
|
|
2
|
+
import * as Result from "effect/Result";
|
|
3
|
+
import { Fail, State } from "../../Input.js";
|
|
4
|
+
import { floatTexts, toFloat } from "../numbers.js";
|
|
5
|
+
import { backend as backend$1, jitSupported } from "./jit.js";
|
|
6
|
+
import * as Option from "effect/Option";
|
|
7
|
+
import { charOf, fastLimit, latin1, string, utf16be, utf16le, utf8 } from "../text.js";
|
|
8
|
+
import * as Arr from "effect/Array";
|
|
9
|
+
import { addEntry, flushEntries, fromEntries, isPlainKey } from "../entries.js";
|
|
10
|
+
import { nodeOf } from "../node.js";
|
|
11
|
+
import { buildsEntries, isDiagnostic, pairOf } from "./entries.js";
|
|
12
|
+
import { TableSize, kindOfEncoding, textSet, tokenSet } from "./sets.js";
|
|
13
|
+
import { countRefs, isLeaf, isLetter, isSmall, lowerAscii, selectorOf } from "./graph.js";
|
|
14
|
+
import { Body, F64, I32, Op, Simd, encodeModule } from "./wasmAsm.js";
|
|
15
|
+
// src/internal/compiler/wasm.ts
|
|
16
|
+
/**
|
|
17
|
+
* The WebAssembly compiler: scans the input in WebAssembly, then builds the
|
|
18
|
+
* value in JavaScript (see `docs/DESIGN.md`, "The WebAssembly compiler").
|
|
19
|
+
*
|
|
20
|
+
* A parse runs in two passes over one description of the grammar:
|
|
21
|
+
*
|
|
22
|
+
* 1. **Scan.** Generated WebAssembly matches the input, which is copied into
|
|
23
|
+
* linear memory, with the same control flow as the JIT (labelled blocks
|
|
24
|
+
* for failure, a function per rule, `state.mode` as a global). Where a
|
|
25
|
+
* value is used, it appends what the value needs to a _trace_ in linear
|
|
26
|
+
* memory: slice bounds, which branch or case matched, repetition counts,
|
|
27
|
+
* floats. When it backtracks it rewinds the trace with the cursor, so the
|
|
28
|
+
* trace only describes the successful parse.
|
|
29
|
+
* 2. **Build.** A JavaScript function generated for the grammar reads the
|
|
30
|
+
* trace in order and builds the value, with the same callbacks and the
|
|
31
|
+
* same object shapes as the JIT, without scanning anything.
|
|
32
|
+
*
|
|
33
|
+
* Parsers with no native form are _islands_: the scan calls back into
|
|
34
|
+
* JavaScript, which runs their JIT-compiled step on the shared state, and the
|
|
35
|
+
* trace records the index of their value. Streaming (resumable) builds,
|
|
36
|
+
* array input, and environments without `WebAssembly` or `new Function` use
|
|
37
|
+
* the JIT.
|
|
38
|
+
*/
|
|
39
|
+
/** Globals. `F` holds the float of the latest scan, the others are `i32`. */
|
|
40
|
+
const G = {
|
|
41
|
+
mode: 0,
|
|
42
|
+
end: 1,
|
|
43
|
+
len: 2,
|
|
44
|
+
tp: 3,
|
|
45
|
+
tlim: 4,
|
|
46
|
+
nv: 5,
|
|
47
|
+
w: 6,
|
|
48
|
+
f: 7
|
|
49
|
+
};
|
|
50
|
+
/** Imported functions. */
|
|
51
|
+
const Imp = {
|
|
52
|
+
island: 0,
|
|
53
|
+
grow: 1,
|
|
54
|
+
test: 2,
|
|
55
|
+
float: 3,
|
|
56
|
+
check: 4
|
|
57
|
+
};
|
|
58
|
+
const importCount = 5;
|
|
59
|
+
/**
|
|
60
|
+
* Function types: rules and decoders `(i32) -> i32`, imports and helpers of
|
|
61
|
+
* two arguments `(i32, i32) -> i32`, `grow` `() -> i32`, `float` `(i32, i32) -> f64`,
|
|
62
|
+
* `start` `(i32 × 5) -> i32`.
|
|
63
|
+
*/
|
|
64
|
+
const Ty = {
|
|
65
|
+
rule: 0,
|
|
66
|
+
pair: 1,
|
|
67
|
+
grow: 2,
|
|
68
|
+
toF64: 3,
|
|
69
|
+
start: 4
|
|
70
|
+
};
|
|
71
|
+
/** Linear memory: membership tables from 0, powers of ten, then the input, then the trace. */
|
|
72
|
+
const TablesEnd = 61440;
|
|
73
|
+
const Pow10 = 61440;
|
|
74
|
+
/** The string cache (see `intern`): `StringSlots` slots of a version, a length and `MaxInterned` units. */
|
|
75
|
+
const Strings = 65536;
|
|
76
|
+
const StringSlots = 1024;
|
|
77
|
+
const MaxInterned = 16;
|
|
78
|
+
const SlotBytes = 40;
|
|
79
|
+
const Input$1 = 131072;
|
|
80
|
+
const PageSize = 65536;
|
|
81
|
+
/** Headroom kept free above the trace limit: more than the writes between two checks. */
|
|
82
|
+
const Slack = 65536;
|
|
83
|
+
/** Trace words a function may write past the last check that dominates them (see `put`). */
|
|
84
|
+
const MaxWrites = 4096;
|
|
85
|
+
/** Bytes left after the cursor beyond which a driver's parse of the same buffer runs the JIT (see `run`). */
|
|
86
|
+
const LargeRest = 4096;
|
|
87
|
+
/** The most literals a compiled `takeUntilAny` checks at each candidate; more are searched in JavaScript. */
|
|
88
|
+
const MaxUntil = 64;
|
|
89
|
+
/** The most leading bytes of each literal a SIMD search tests at once. */
|
|
90
|
+
const MaxPositions = 3;
|
|
91
|
+
/** Teddy's buckets: one bit each in a nibble table's bytes. */
|
|
92
|
+
const Buckets = 8;
|
|
93
|
+
/** For each low nibble, the high nibbles `from` to `from + 7` of the bytes `set` accepts, as bits from 0. */
|
|
94
|
+
/** Whether `set` accepts any byte. */
|
|
95
|
+
const accepts = (set) => Array.from({ length: 256 }, (_, c) => set.cs.table[c] === 1 !== set.negate).some((ok) => ok);
|
|
96
|
+
const nibbleTable = (set, from) => {
|
|
97
|
+
const table = new Array(16).fill(0);
|
|
98
|
+
for (let c = from << 4; c < from + 8 << 4; c++) if (set.cs.table[c] === 1 !== set.negate) table[c & 15] = (table[c & 15] ?? 0) | 1 << (c >> 4) - from;
|
|
99
|
+
return table;
|
|
100
|
+
};
|
|
101
|
+
/** The most rejected bytes a vectorised `takeWhile` compares with one by one. */
|
|
102
|
+
const MaxStops = 3;
|
|
103
|
+
/** For a high nibble `h` from 8 up, bit `h - 8`; none below. */
|
|
104
|
+
const UpperNibbleBits = [
|
|
105
|
+
0,
|
|
106
|
+
0,
|
|
107
|
+
0,
|
|
108
|
+
0,
|
|
109
|
+
0,
|
|
110
|
+
0,
|
|
111
|
+
0,
|
|
112
|
+
0,
|
|
113
|
+
1,
|
|
114
|
+
2,
|
|
115
|
+
4,
|
|
116
|
+
8,
|
|
117
|
+
16,
|
|
118
|
+
32,
|
|
119
|
+
64,
|
|
120
|
+
128
|
|
121
|
+
];
|
|
122
|
+
/** For a high nibble `h`, bit `h` below 8 (ASCII), none above. */
|
|
123
|
+
const HighNibbleBits = [
|
|
124
|
+
1,
|
|
125
|
+
2,
|
|
126
|
+
4,
|
|
127
|
+
8,
|
|
128
|
+
16,
|
|
129
|
+
32,
|
|
130
|
+
64,
|
|
131
|
+
128,
|
|
132
|
+
0,
|
|
133
|
+
0,
|
|
134
|
+
0,
|
|
135
|
+
0,
|
|
136
|
+
0,
|
|
137
|
+
0,
|
|
138
|
+
0,
|
|
139
|
+
0
|
|
140
|
+
];
|
|
141
|
+
const Zeros = [
|
|
142
|
+
0,
|
|
143
|
+
0,
|
|
144
|
+
0,
|
|
145
|
+
0,
|
|
146
|
+
0,
|
|
147
|
+
0,
|
|
148
|
+
0,
|
|
149
|
+
0,
|
|
150
|
+
0,
|
|
151
|
+
0,
|
|
152
|
+
0,
|
|
153
|
+
0,
|
|
154
|
+
0,
|
|
155
|
+
0,
|
|
156
|
+
0,
|
|
157
|
+
0
|
|
158
|
+
];
|
|
159
|
+
const LowNibbles = [
|
|
160
|
+
15,
|
|
161
|
+
15,
|
|
162
|
+
15,
|
|
163
|
+
15,
|
|
164
|
+
15,
|
|
165
|
+
15,
|
|
166
|
+
15,
|
|
167
|
+
15,
|
|
168
|
+
15,
|
|
169
|
+
15,
|
|
170
|
+
15,
|
|
171
|
+
15,
|
|
172
|
+
15,
|
|
173
|
+
15,
|
|
174
|
+
15,
|
|
175
|
+
15
|
|
176
|
+
];
|
|
177
|
+
/**
|
|
178
|
+
* Teddy's tables for the first `m` bytes of `literals`: for each position,
|
|
179
|
+
* the low- and high-nibble tables, literal `i` in bucket `i % 8`.
|
|
180
|
+
*/
|
|
181
|
+
const teddyTables = (literals, m) => Array.from({ length: m }, (_, j) => {
|
|
182
|
+
const lo = new Array(16).fill(0);
|
|
183
|
+
const hi = new Array(16).fill(0);
|
|
184
|
+
literals.forEach((lit, i) => {
|
|
185
|
+
const byte = lit[j] ?? 0;
|
|
186
|
+
const bit = 1 << i % Buckets;
|
|
187
|
+
lo[byte & 15] = (lo[byte & 15] ?? 0) | bit;
|
|
188
|
+
hi[byte >> 4] = (hi[byte >> 4] ?? 0) | bit;
|
|
189
|
+
});
|
|
190
|
+
return [lo, hi];
|
|
191
|
+
});
|
|
192
|
+
/**
|
|
193
|
+
* The bytes of literal units (`width` bytes each, little-endian), and the
|
|
194
|
+
* 0x20 to OR into each byte of a caseless letter, which `bytes` holds in
|
|
195
|
+
* lower case.
|
|
196
|
+
*/
|
|
197
|
+
const unitBytes = (codes, caseless, width) => {
|
|
198
|
+
const bytes = [];
|
|
199
|
+
const fold = [];
|
|
200
|
+
for (const code of codes) {
|
|
201
|
+
const letter = caseless && isLetter(code);
|
|
202
|
+
const unit = letter ? lowerAscii(code) : code;
|
|
203
|
+
for (let k = 0; k < width; k++) {
|
|
204
|
+
bytes.push(unit >> 8 * k & 255);
|
|
205
|
+
fold.push(letter && k === 0 ? 32 : 0);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
bytes,
|
|
210
|
+
fold
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
/** The widest load, of 16 (with SIMD), 8, 4, 2 or 1 bytes, that `left` bytes fill. */
|
|
214
|
+
const chunkSize = (left) => {
|
|
215
|
+
if (left >= 16 && simd()) return 16;
|
|
216
|
+
return [
|
|
217
|
+
8,
|
|
218
|
+
4,
|
|
219
|
+
2
|
|
220
|
+
].find((n) => left >= n) ?? 1;
|
|
221
|
+
};
|
|
222
|
+
/**
|
|
223
|
+
* With an address on the stack, pushes whether the 1, 2, 4, 8 or 16 bytes
|
|
224
|
+
* at it (plus `offset`), ORed with `fold`, differ from `bytes`.
|
|
225
|
+
*/
|
|
226
|
+
const compareChunk = (b, bytes, fold, offset) => {
|
|
227
|
+
const n = bytes.length;
|
|
228
|
+
const folds = fold.some((f) => f !== 0);
|
|
229
|
+
if (n === 16) {
|
|
230
|
+
b.simd(Simd.v128Load, 0, offset);
|
|
231
|
+
if (folds) b.v128(fold).simd(Simd.v128Or);
|
|
232
|
+
b.v128(bytes).simd(Simd.i8x16Eq).simd(Simd.i8x16AllTrue).op(Op.i32Eqz);
|
|
233
|
+
} else if (n === 8) {
|
|
234
|
+
b.mem(Op.i64Load, 0, offset);
|
|
235
|
+
if (folds) b.i64(littleEndian(fold, 0, 8)).op(Op.i64Or);
|
|
236
|
+
b.i64(littleEndian(bytes, 0, 8)).op(Op.i64Ne);
|
|
237
|
+
} else {
|
|
238
|
+
b.mem(n === 4 ? Op.i32Load : n === 2 ? Op.i32Load16U : Op.i32Load8U, 0, offset);
|
|
239
|
+
if (folds) b.i32(Number(littleEndian(fold, 0, n))).op(Op.i32Or);
|
|
240
|
+
b.i32(Number(BigInt.asIntN(32, littleEndian(bytes, 0, n)))).op(Op.i32Ne);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
/** `n` bytes from `bytes[i]` as a little-endian integer (as a load reads them). */
|
|
244
|
+
const littleEndian = (bytes, i, n) => {
|
|
245
|
+
let v = 0n;
|
|
246
|
+
for (let k = n - 1; k >= 0; k--) v = v << 8n | BigInt(bytes[i + k] ?? 0);
|
|
247
|
+
return BigInt.asIntN(64, v);
|
|
248
|
+
};
|
|
249
|
+
/** Values of `G.mode`. */
|
|
250
|
+
const Backtrack = 0;
|
|
251
|
+
const Cut = 1;
|
|
252
|
+
/** Whether the scan decodes an encoding itself. */
|
|
253
|
+
const isBuiltin = (enc) => enc === string || enc === utf8 || enc === utf16le || enc === utf16be || enc === latin1;
|
|
254
|
+
/** Pushes the code point of the surrogate pair in locals `hi` and `lo`. */
|
|
255
|
+
const pair = (b, hi, lo) => {
|
|
256
|
+
b.get(hi).i32(55296).op(Op.i32Sub).i32(10).op(Op.i32Shl);
|
|
257
|
+
b.get(lo).i32(56320).op(Op.i32Sub).op(Op.i32Add).i32(65536).op(Op.i32Add);
|
|
258
|
+
};
|
|
259
|
+
/** Whether every key of a record of cases is one unit below `size` (a byte, as a numeric key, for `bytes`). */
|
|
260
|
+
const closedKeys = (keys, size, bytes) => keys.every((k) => Predicate.isString(k) && (bytes ? /^\d+$/.test(k) && Number(k) < size : k.length === 1 && k.charCodeAt(0) < size));
|
|
261
|
+
/** Per builder function: its variables. */
|
|
262
|
+
var Vars = class {
|
|
263
|
+
names = [];
|
|
264
|
+
};
|
|
265
|
+
const modeOf = (mode) => mode === "Cut" ? Cut : mode === "Backtrack" ? Backtrack : 2;
|
|
266
|
+
/** The inclusive runs of accepted code points of a set, when its ranges are known. */
|
|
267
|
+
const acceptedRuns = (cs, negate, max) => {
|
|
268
|
+
if (cs.ranges === void 0) return Option.none();
|
|
269
|
+
const runs = [];
|
|
270
|
+
for (const [a, b] of cs.ranges) if (a <= max) runs.push([a, Math.min(b, max)]);
|
|
271
|
+
if (!negate) return Option.some(runs);
|
|
272
|
+
const out = [];
|
|
273
|
+
let next = 0;
|
|
274
|
+
for (const [a, b] of runs) {
|
|
275
|
+
if (a > next) out.push([next, a - 1]);
|
|
276
|
+
next = b + 1;
|
|
277
|
+
}
|
|
278
|
+
if (next <= max) out.push([next, max]);
|
|
279
|
+
return Option.some(out);
|
|
280
|
+
};
|
|
281
|
+
let simdChecked = false;
|
|
282
|
+
let simdSupported = false;
|
|
283
|
+
/**
|
|
284
|
+
* Whether WebAssembly SIMD is available, for the widener of strings (checked
|
|
285
|
+
* once): a module of one `v128` load validates.
|
|
286
|
+
*/
|
|
287
|
+
const simd = () => {
|
|
288
|
+
if (!simdChecked) {
|
|
289
|
+
simdChecked = true;
|
|
290
|
+
simdSupported = validates((probe) => probe.get(0).simd(Simd.v128Load, 0, 0).simd(Simd.i8x16Bitmask));
|
|
291
|
+
}
|
|
292
|
+
return simdSupported;
|
|
293
|
+
};
|
|
294
|
+
let relaxedChecked = false;
|
|
295
|
+
let relaxedSupported = false;
|
|
296
|
+
/**
|
|
297
|
+
* Whether relaxed SIMD is available (checked once): its `relaxed_swizzle`
|
|
298
|
+
* is `swizzle` without the fix-up for indices from 16 up, which the nibble
|
|
299
|
+
* lookups never pass.
|
|
300
|
+
*/
|
|
301
|
+
const relaxed = () => {
|
|
302
|
+
if (!relaxedChecked) {
|
|
303
|
+
relaxedChecked = true;
|
|
304
|
+
relaxedSupported = simd() && validates((probe) => probe.get(0).simd(Simd.v128Load, 0, 0).get(0).simd(Simd.v128Load, 0, 0).simd(Simd.i8x16RelaxedSwizzle).simd(Simd.i8x16Bitmask));
|
|
305
|
+
}
|
|
306
|
+
return relaxedSupported;
|
|
307
|
+
};
|
|
308
|
+
/** Pushes a swizzle of a table by nibbles (indices below 16): relaxed where it validates. */
|
|
309
|
+
const nibbleSwizzle = (b) => {
|
|
310
|
+
b.simd(relaxed() ? Simd.i8x16RelaxedSwizzle : Simd.i8x16Swizzle);
|
|
311
|
+
};
|
|
312
|
+
/** Whether a module of one function `(i32) -> i32` with the body `write` writes validates. */
|
|
313
|
+
const validates = (write) => {
|
|
314
|
+
const probe = new Body(1);
|
|
315
|
+
write(probe);
|
|
316
|
+
const bytes = encodeModule({
|
|
317
|
+
types: [{
|
|
318
|
+
params: [I32],
|
|
319
|
+
results: [I32]
|
|
320
|
+
}],
|
|
321
|
+
imports: [],
|
|
322
|
+
memoryPages: 1,
|
|
323
|
+
functions: [[0, probe]],
|
|
324
|
+
globals: [],
|
|
325
|
+
exports: [],
|
|
326
|
+
data: []
|
|
327
|
+
});
|
|
328
|
+
return WebAssembly.validate(bytes);
|
|
329
|
+
};
|
|
330
|
+
/** Whether a builder is available: it is generated with `new Function`. */
|
|
331
|
+
const supported = () => Predicate.isObject(globalThis.WebAssembly) && jitSupported();
|
|
332
|
+
var WasmCompiler = class WasmCompiler {
|
|
333
|
+
kind;
|
|
334
|
+
functions = [];
|
|
335
|
+
builders = [];
|
|
336
|
+
constants = [];
|
|
337
|
+
islands = [];
|
|
338
|
+
fallbacks = [];
|
|
339
|
+
tables = [];
|
|
340
|
+
tests = [];
|
|
341
|
+
rules = [/* @__PURE__ */ new WeakMap(), /* @__PURE__ */ new WeakMap()];
|
|
342
|
+
pending = [];
|
|
343
|
+
refs = /* @__PURE__ */ new WeakMap();
|
|
344
|
+
decoders = /* @__PURE__ */ new WeakMap();
|
|
345
|
+
floatScanners = /* @__PURE__ */ new WeakMap();
|
|
346
|
+
/** The float scanner of `Token` / `Ascii` text, which has no encoding. */
|
|
347
|
+
tokenFloatScanner = -1;
|
|
348
|
+
next = 0;
|
|
349
|
+
constructor(kind) {
|
|
350
|
+
this.kind = kind;
|
|
351
|
+
}
|
|
352
|
+
/** Counts the distinct parents of each parser. */
|
|
353
|
+
countRefs(root) {
|
|
354
|
+
this.refs = countRefs(root);
|
|
355
|
+
}
|
|
356
|
+
/** A constant of the builder, `C[i]`; the same value (by `Object.is`) is stored once. */
|
|
357
|
+
const(value) {
|
|
358
|
+
let i = this.constants.findIndex((c) => Object.is(c, value));
|
|
359
|
+
if (i < 0) i = this.constants.push(value) - 1;
|
|
360
|
+
return `C[${i}]`;
|
|
361
|
+
}
|
|
362
|
+
/** A JavaScript literal for simple values, otherwise a constant. */
|
|
363
|
+
lit(value) {
|
|
364
|
+
if (value === void 0) return "void 0";
|
|
365
|
+
if (value === null || Predicate.isBoolean(value)) return String(value);
|
|
366
|
+
if (Predicate.isNumber(value) && Number.isFinite(value) && !Object.is(value, -0)) return String(value);
|
|
367
|
+
return this.const(value);
|
|
368
|
+
}
|
|
369
|
+
v(bf) {
|
|
370
|
+
const name = `v${this.next++}`;
|
|
371
|
+
bf.names.push(name);
|
|
372
|
+
return name;
|
|
373
|
+
}
|
|
374
|
+
/** Reserves a function index; its body is filled in later. */
|
|
375
|
+
reserve(type, params = 1) {
|
|
376
|
+
const body = new Body(params);
|
|
377
|
+
this.functions.push([type, body]);
|
|
378
|
+
return [importCount + this.functions.length - 1, body];
|
|
379
|
+
}
|
|
380
|
+
/** The function of a rule, generating it later if new. With `used`, it has a builder `b<index>`. */
|
|
381
|
+
rule(p, used) {
|
|
382
|
+
const rules = this.rules[used ? 1 : 0];
|
|
383
|
+
const known = rules.get(p);
|
|
384
|
+
if (known !== void 0) return known;
|
|
385
|
+
const [index] = this.reserve(Ty.rule);
|
|
386
|
+
rules.set(p, index);
|
|
387
|
+
this.pending.push([
|
|
388
|
+
p,
|
|
389
|
+
used,
|
|
390
|
+
index
|
|
391
|
+
]);
|
|
392
|
+
return index;
|
|
393
|
+
}
|
|
394
|
+
/** Generates the pending rules (and the rules they use). */
|
|
395
|
+
flush() {
|
|
396
|
+
for (let r = this.pending.shift(); r !== void 0; r = this.pending.shift()) {
|
|
397
|
+
const [p, used, index] = r;
|
|
398
|
+
const body = this.functions[index - importCount][1];
|
|
399
|
+
const bf = new Vars();
|
|
400
|
+
const out = used ? this.v(bf) : "";
|
|
401
|
+
body.tp = body.local();
|
|
402
|
+
body.gget(G.tp).set(body.tp);
|
|
403
|
+
body.endLocal = body.local();
|
|
404
|
+
body.gget(G.end).set(body.endLocal);
|
|
405
|
+
this.check(body);
|
|
406
|
+
body.writes = 0;
|
|
407
|
+
let built = "";
|
|
408
|
+
body.block((fail) => {
|
|
409
|
+
built = this.inline(p, out, body, fail, bf, 0);
|
|
410
|
+
body.get(body.tp).gset(G.tp);
|
|
411
|
+
body.get(0).op(Op.return);
|
|
412
|
+
});
|
|
413
|
+
body.i32(-1);
|
|
414
|
+
if (used) this.builders.push(`function b${index}(i){${Arr.isArrayNonEmpty(bf.names) ? `let ${bf.names.join(",")};` : ""}${built}N=i;return ${out}}`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
/** Fails with a mode. */
|
|
418
|
+
failWith(b, fail, mode) {
|
|
419
|
+
b.i32(mode).gset(G.mode).br(fail);
|
|
420
|
+
}
|
|
421
|
+
/** Fails with `Backtrack` when the `i32` at the top of the stack is true. */
|
|
422
|
+
failIf(b, fail) {
|
|
423
|
+
b.if(() => this.failWith(b, fail, Backtrack));
|
|
424
|
+
}
|
|
425
|
+
/** Propagates a failure unless its mode is `Backtrack`. */
|
|
426
|
+
unlessBacktrack(b, fail) {
|
|
427
|
+
b.gget(G.mode).brIf(fail);
|
|
428
|
+
}
|
|
429
|
+
/** Pushes the input unit at local `pos` plus `delta` units. */
|
|
430
|
+
load(b, pos, delta = 0) {
|
|
431
|
+
if (this.kind === 0) b.get(pos).i32(1).op(Op.i32Shl).mem(Op.i32Load16U, 1, Input$1 + 2 * delta);
|
|
432
|
+
else b.get(pos).mem(Op.i32Load8U, 0, Input$1 + delta);
|
|
433
|
+
}
|
|
434
|
+
/** Pushes the end of the input: from a local in rule functions, from the global in helpers. */
|
|
435
|
+
end(b) {
|
|
436
|
+
return b.endLocal >= 0 ? b.get(b.endLocal) : b.gget(G.end);
|
|
437
|
+
}
|
|
438
|
+
/** Pushes `p >= E`. */
|
|
439
|
+
atEnd(b, pos) {
|
|
440
|
+
this.end(b.get(pos)).op(Op.i32GeU);
|
|
441
|
+
}
|
|
442
|
+
/** `p += n`. */
|
|
443
|
+
advance(b, n) {
|
|
444
|
+
b.get(0).i32(n).op(Op.i32Add).set(0);
|
|
445
|
+
}
|
|
446
|
+
/** Saves the cursor and the trace pointer. */
|
|
447
|
+
save(b) {
|
|
448
|
+
const p = b.local();
|
|
449
|
+
const t = b.local();
|
|
450
|
+
b.get(0).set(p).get(b.tp).set(t);
|
|
451
|
+
return [p, t];
|
|
452
|
+
}
|
|
453
|
+
/** Restores a saved cursor and trace pointer. */
|
|
454
|
+
restore(b, [p, t]) {
|
|
455
|
+
b.get(p).set(0).get(t).set(b.tp);
|
|
456
|
+
}
|
|
457
|
+
/** Grows the trace if it is past its limit. */
|
|
458
|
+
check(b) {
|
|
459
|
+
b.get(b.tp).gget(G.tlim).op(Op.i32GtU);
|
|
460
|
+
b.if(() => b.call(Imp.grow).gset(G.tlim));
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Appends words to the trace. Every function and loop iteration starts
|
|
464
|
+
* with a check, and the limit keeps `Slack` bytes free, so a check is
|
|
465
|
+
* only written here once a function wrote `MaxWrites` words since one.
|
|
466
|
+
*/
|
|
467
|
+
put(b, words) {
|
|
468
|
+
b.writes += words.length;
|
|
469
|
+
if (b.writes > MaxWrites) this.check(b);
|
|
470
|
+
words.forEach((push, k) => {
|
|
471
|
+
b.get(b.tp);
|
|
472
|
+
push();
|
|
473
|
+
b.mem(Op.i32Store, 2, 4 * k);
|
|
474
|
+
});
|
|
475
|
+
b.get(b.tp).i32(4 * words.length).op(Op.i32Add).set(b.tp);
|
|
476
|
+
}
|
|
477
|
+
/** Reserves a trace word, returning a local holding its address. */
|
|
478
|
+
slot(b) {
|
|
479
|
+
const s = b.local();
|
|
480
|
+
b.get(b.tp).set(s);
|
|
481
|
+
this.put(b, [() => b.i32(0)]);
|
|
482
|
+
return s;
|
|
483
|
+
}
|
|
484
|
+
/** Appends the `f64` in `G.f` to the trace, aligned to 8 bytes. */
|
|
485
|
+
putF64(b) {
|
|
486
|
+
b.writes += 4;
|
|
487
|
+
if (b.writes > MaxWrites) this.check(b);
|
|
488
|
+
b.get(b.tp).i32(7).op(Op.i32Add).i32(-8).op(Op.i32And).set(b.tp);
|
|
489
|
+
b.get(b.tp).gget(G.f).mem(Op.f64Store, 3, 0);
|
|
490
|
+
b.get(b.tp).i32(8).op(Op.i32Add).set(b.tp);
|
|
491
|
+
}
|
|
492
|
+
/** A loop with a trace check at the start of each iteration. */
|
|
493
|
+
loop(b, body) {
|
|
494
|
+
const before = b.writes;
|
|
495
|
+
b.loop((again) => {
|
|
496
|
+
this.check(b);
|
|
497
|
+
b.writes = 0;
|
|
498
|
+
body(again);
|
|
499
|
+
});
|
|
500
|
+
b.writes += before;
|
|
501
|
+
}
|
|
502
|
+
/** Builder code reading the next trace word. */
|
|
503
|
+
static word = "T[i++]";
|
|
504
|
+
/**
|
|
505
|
+
* Runs `p`'s JIT-compiled step on the shared state. With `out`, its value
|
|
506
|
+
* is kept by JavaScript and the trace records its index.
|
|
507
|
+
*/
|
|
508
|
+
island(p, out, b, fail, reason) {
|
|
509
|
+
const id = this.islands.length;
|
|
510
|
+
this.islands.push({
|
|
511
|
+
parser: p,
|
|
512
|
+
used: out !== ""
|
|
513
|
+
});
|
|
514
|
+
this.fallbacks.push({
|
|
515
|
+
name: p.name,
|
|
516
|
+
tag: nodeOf(p)._tag,
|
|
517
|
+
reason
|
|
518
|
+
});
|
|
519
|
+
const r = b.local();
|
|
520
|
+
b.i32(id).get(0).call(Imp.island).tee(r).i32(0).op(Op.i32LtS);
|
|
521
|
+
b.if(() => {
|
|
522
|
+
b.i32(-1).get(r).op(Op.i32Sub).gset(G.mode).br(fail);
|
|
523
|
+
});
|
|
524
|
+
b.get(r).set(0);
|
|
525
|
+
if (out === "") return "";
|
|
526
|
+
this.put(b, [() => b.gget(G.nv)]);
|
|
527
|
+
b.gget(G.nv).i32(1).op(Op.i32Add).gset(G.nv);
|
|
528
|
+
return `${out}=V[${WasmCompiler.word}];`;
|
|
529
|
+
}
|
|
530
|
+
/** Builder functions of the value islands, `w<id>`, and what they apply to the value. */
|
|
531
|
+
valueFns = [];
|
|
532
|
+
checks = [];
|
|
533
|
+
/**
|
|
534
|
+
* A callback that needs `self`'s value while scanning (`filter`,
|
|
535
|
+
* `tryMap`, `filterMap`, `tap`): `self` is scanned here, then JavaScript
|
|
536
|
+
* builds its value from its trace (with a builder function of its own) and
|
|
537
|
+
* applies `apply`, which returns the result or `Fail` (a failure with
|
|
538
|
+
* `Backtrack`). The trace of `self` is then replaced by the index of the
|
|
539
|
+
* result.
|
|
540
|
+
*/
|
|
541
|
+
valueIsland(self, out, b, fail, d, apply) {
|
|
542
|
+
const id = this.valueFns.length;
|
|
543
|
+
this.valueFns.push("");
|
|
544
|
+
this.checks.push(apply);
|
|
545
|
+
const ts = b.local();
|
|
546
|
+
b.get(b.tp).set(ts);
|
|
547
|
+
const vars = new Vars();
|
|
548
|
+
const x = this.v(vars);
|
|
549
|
+
const code = this.gen(self, x, b, fail, vars, d);
|
|
550
|
+
this.valueFns[id] = `function w${id}(i){let ${vars.names.join(",")};${code}return ${x}}`;
|
|
551
|
+
b.i32(id).get(ts).call(Imp.check).op(Op.i32Eqz);
|
|
552
|
+
this.failIf(b, fail);
|
|
553
|
+
b.get(ts).set(b.tp);
|
|
554
|
+
if (out !== "") this.put(b, [() => b.gget(G.nv)]);
|
|
555
|
+
b.gget(G.nv).i32(1).op(Op.i32Add).gset(G.nv);
|
|
556
|
+
return out === "" ? "" : `${out}=V[T[i++]];`;
|
|
557
|
+
}
|
|
558
|
+
call(index, out, b, fail) {
|
|
559
|
+
b.get(b.tp).gset(G.tp);
|
|
560
|
+
b.get(0).call(index).tee(0).i32(0).op(Op.i32LtS).brIf(fail);
|
|
561
|
+
b.gget(G.tp).set(b.tp);
|
|
562
|
+
return out === "" ? "" : `${out}=b${index}(i);i=N;`;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Scan code for `p`, falling through with `p` advanced, or branching to
|
|
566
|
+
* `fail` with `G.mode` set. With `out`, returns the builder code that
|
|
567
|
+
* reads its trace and sets `out` to its value.
|
|
568
|
+
*/
|
|
569
|
+
gen(p, out, b, fail, bf, depth) {
|
|
570
|
+
const node = nodeOf(p);
|
|
571
|
+
if (Predicate.isTagged(node, "Suspend")) return this.call(this.rule(node.resolve(), out !== ""), out, b, fail);
|
|
572
|
+
if (!isLeaf(node) && ((this.refs.get(p) ?? 0) > 1 && !isSmall(p) || depth > 32)) return this.call(this.rule(p, out !== ""), out, b, fail);
|
|
573
|
+
return this.inline(p, out, b, fail, bf, depth);
|
|
574
|
+
}
|
|
575
|
+
/** Whether the scan reads tokens of this encoding itself. */
|
|
576
|
+
native(enc) {
|
|
577
|
+
if (enc === void 0) return true;
|
|
578
|
+
return isBuiltin(enc) && kindOfEncoding(enc) === this.kind;
|
|
579
|
+
}
|
|
580
|
+
inline(p, out, b, fail, bf, depth) {
|
|
581
|
+
const node = nodeOf(p);
|
|
582
|
+
const s = {
|
|
583
|
+
p,
|
|
584
|
+
out,
|
|
585
|
+
b,
|
|
586
|
+
fail,
|
|
587
|
+
bf,
|
|
588
|
+
d: depth + 1
|
|
589
|
+
};
|
|
590
|
+
const families = [
|
|
591
|
+
(n, x) => this.constant(n, x),
|
|
592
|
+
(n, x) => this.slices(n, x),
|
|
593
|
+
(n, x) => this.values(n, x),
|
|
594
|
+
(n, x) => this.control(n, x),
|
|
595
|
+
(n, x) => this.structure(n, x)
|
|
596
|
+
];
|
|
597
|
+
for (const family of families) {
|
|
598
|
+
const code = family(node, s);
|
|
599
|
+
if (Option.isSome(code)) return code.value;
|
|
600
|
+
}
|
|
601
|
+
return this.island(p, out, b, fail, "no WebAssembly form");
|
|
602
|
+
}
|
|
603
|
+
/** `out = value;`, or nothing when the value is unused. */
|
|
604
|
+
set(s, value) {
|
|
605
|
+
return s.out === "" ? "" : `${s.out}=${value};`;
|
|
606
|
+
}
|
|
607
|
+
/** Constants, the end of the input, positions and rules. */
|
|
608
|
+
constant(node, s) {
|
|
609
|
+
const { b, fail } = s;
|
|
610
|
+
if (Predicate.isTagged(node, "Succeed")) return Option.some(this.set(s, this.lit(node.value)));
|
|
611
|
+
if (Predicate.isTagged(node, "Sync")) return Option.some(this.set(s, `${this.const(node.evaluate)}()`));
|
|
612
|
+
if (Predicate.isTagged(node, "Fail")) {
|
|
613
|
+
this.failWith(b, fail, modeOf(node.mode));
|
|
614
|
+
return Option.some(this.set(s, "void 0"));
|
|
615
|
+
}
|
|
616
|
+
if (Predicate.isTagged(node, "Eof")) {
|
|
617
|
+
this.atEnd(b, 0);
|
|
618
|
+
b.op(Op.i32Eqz);
|
|
619
|
+
this.failIf(b, fail);
|
|
620
|
+
return Option.some(this.set(s, "void 0"));
|
|
621
|
+
}
|
|
622
|
+
if (Predicate.isTagged(node, "Location") || Predicate.isTagged(node, "RestLen")) {
|
|
623
|
+
if (s.out !== "") this.put(b, [() => b.get(0)]);
|
|
624
|
+
return Option.some(this.set(s, Predicate.isTagged(node, "Location") ? "T[i++]" : "E-T[i++]"));
|
|
625
|
+
}
|
|
626
|
+
if (Predicate.isTagged(node, "Suspend")) return Option.some(this.call(this.rule(node.resolve(), s.out !== ""), s.out, b, fail));
|
|
627
|
+
if (Predicate.isTagged(node, "Fixed") && this.kind === 1) {
|
|
628
|
+
this.end(b.get(0).i32(node.size).op(Op.i32Add)).op(Op.i32GtU);
|
|
629
|
+
this.failIf(b, fail);
|
|
630
|
+
if (s.out !== "") this.put(b, [() => b.get(0)]);
|
|
631
|
+
this.advance(b, node.size);
|
|
632
|
+
return Option.some(this.set(s, `${this.const(node.read)}(I,T[i++],S)`));
|
|
633
|
+
}
|
|
634
|
+
return Option.none();
|
|
635
|
+
}
|
|
636
|
+
/** Tokens, `escaped`, and parsers whose value is the input they consumed. */
|
|
637
|
+
slices(node, s) {
|
|
638
|
+
if (Predicate.isTagged(node, "Any") || Predicate.isTagged(node, "OneOf") || Predicate.isTagged(node, "Literal") || Predicate.isTagged(node, "TakeWhile")) return Option.some(this.token(s, node));
|
|
639
|
+
if (Predicate.isTagged(node, "Escaped")) return Option.some(this.escaped(s, node));
|
|
640
|
+
if (Predicate.isTagged(node, "TakeUntil") || Predicate.isTagged(node, "TakeUntilAny")) return Option.some(this.takeUntil(s, node));
|
|
641
|
+
if (Predicate.isTagged(node, "Taken")) {
|
|
642
|
+
if (node.encoding !== void 0) return Option.some(this.island(s.p, s.out, s.b, s.fail, "text slices are validated in JavaScript"));
|
|
643
|
+
return Option.some(this.sliceOf(s, node.self, this.kind === 0 ? "I.slice(T[i],T[i+1])" : "I.subarray(T[i],T[i+1])"));
|
|
644
|
+
}
|
|
645
|
+
if (Predicate.isTagged(node, "Span")) return Option.some(this.sliceOf(s, node.self, "{start:T[i],end:T[i+1]}"));
|
|
646
|
+
if (Predicate.isTagged(node, "WithTaken") || Predicate.isTagged(node, "WithSpan")) return Option.some(this.withSlice(s, node));
|
|
647
|
+
return Option.none();
|
|
648
|
+
}
|
|
649
|
+
/** `self`, whose value is `value`, an expression of its start and end `T[i]`, `T[i+1]`. */
|
|
650
|
+
sliceOf(s, self, value) {
|
|
651
|
+
const { b } = s;
|
|
652
|
+
const st = b.local();
|
|
653
|
+
b.get(0).set(st);
|
|
654
|
+
this.gen(self, "", b, s.fail, s.bf, s.d);
|
|
655
|
+
if (s.out === "") return "";
|
|
656
|
+
this.put(b, [() => b.get(st), () => b.get(0)]);
|
|
657
|
+
return `${s.out}=${value};i+=2;`;
|
|
658
|
+
}
|
|
659
|
+
/** `withTaken` and `withSpan`: the value of `self` and its slice or span. */
|
|
660
|
+
withSlice(s, node) {
|
|
661
|
+
const { b, out } = s;
|
|
662
|
+
if (out === "") return this.gen(node.self, "", b, s.fail, s.bf, s.d);
|
|
663
|
+
const st = b.local();
|
|
664
|
+
b.get(0).set(st);
|
|
665
|
+
this.put(b, [() => b.get(st)]);
|
|
666
|
+
const x = this.v(s.bf);
|
|
667
|
+
const start = this.v(s.bf);
|
|
668
|
+
const code = this.gen(node.self, x, b, s.fail, s.bf, s.d);
|
|
669
|
+
this.put(b, [() => b.get(0)]);
|
|
670
|
+
return `${start}=T[i++];${code}${out}=[${x},${Predicate.isTagged(node, "WithSpan") ? `{start:${start},end:T[i++]}` : `S.slice(${start},T[i++])`}];`;
|
|
671
|
+
}
|
|
672
|
+
/** Mapping, and callbacks on values. */
|
|
673
|
+
values(node, s) {
|
|
674
|
+
const { b, fail, d } = s;
|
|
675
|
+
if (Predicate.isTagged(node, "Map")) return Option.some(this.map(node, s.out, b, fail, s.bf, d));
|
|
676
|
+
if (Predicate.isTagged(node, "As")) {
|
|
677
|
+
this.gen(node.self, "", b, fail, s.bf, d);
|
|
678
|
+
return Option.some(this.set(s, this.lit(node.value)));
|
|
679
|
+
}
|
|
680
|
+
if (Predicate.isTagged(node, "Filter")) return Option.some(this.filter(s, node));
|
|
681
|
+
if (Predicate.isTagged(node, "TryMap")) {
|
|
682
|
+
const f = node.f;
|
|
683
|
+
return Option.some(this.valueIsland(node.self, s.out, b, fail, d, (v) => Result.getOrElse(f(v), () => Fail)));
|
|
684
|
+
}
|
|
685
|
+
if (Predicate.isTagged(node, "FilterMap")) {
|
|
686
|
+
const f = node.f;
|
|
687
|
+
return Option.some(this.valueIsland(node.self, s.out, b, fail, d, (v) => Option.getOrElse(f(v), () => Fail)));
|
|
688
|
+
}
|
|
689
|
+
if (Predicate.isTagged(node, "Tap")) {
|
|
690
|
+
const f = node.f;
|
|
691
|
+
return Option.some(this.valueIsland(node.self, s.out, b, fail, d, (v) => {
|
|
692
|
+
f(v);
|
|
693
|
+
return v;
|
|
694
|
+
}));
|
|
695
|
+
}
|
|
696
|
+
return Option.none();
|
|
697
|
+
}
|
|
698
|
+
/** `filter`: on a float, a test of the double; otherwise a value island. */
|
|
699
|
+
filter(s, node) {
|
|
700
|
+
const predicate = node.predicate;
|
|
701
|
+
if (predicate === Number.isFinite) {
|
|
702
|
+
const code = this.finiteFloat(node.self, s.out, s.b, s.fail, s.bf, s.d);
|
|
703
|
+
if (Option.isSome(code)) return code.value;
|
|
704
|
+
}
|
|
705
|
+
return this.valueIsland(node.self, s.out, s.b, s.fail, s.d, (v) => predicate(v) ? v : Fail);
|
|
706
|
+
}
|
|
707
|
+
/** Modes, diagnostics and lookahead. */
|
|
708
|
+
control(node, s) {
|
|
709
|
+
const { b, fail, out, d } = s;
|
|
710
|
+
if (Predicate.isTagged(node, "Mode")) return Option.some(this.withMode(b, node, fail, (bad) => this.gen(node.self, out, b, bad, s.bf, d)));
|
|
711
|
+
if (isDiagnostic(node)) return Option.some(this.gen(node.self, out, b, fail, s.bf, d));
|
|
712
|
+
if (Predicate.isTagged(node, "ResumeAfter")) {
|
|
713
|
+
if (out === "") return Option.some(this.gen(node.self, "", b, fail, s.bf, d));
|
|
714
|
+
const x = this.v(s.bf);
|
|
715
|
+
return Option.some(this.gen(node.self, x, b, fail, s.bf, d) + `${out}=R.some(${x});`);
|
|
716
|
+
}
|
|
717
|
+
if (Predicate.isTagged(node, "Opt") || Predicate.isTagged(node, "OrElseSucceed")) return Option.some(this.opt(s, node));
|
|
718
|
+
if (Predicate.isTagged(node, "Peek")) {
|
|
719
|
+
const st = b.local();
|
|
720
|
+
b.get(0).set(st);
|
|
721
|
+
const code = this.gen(node.self, out, b, fail, s.bf, d);
|
|
722
|
+
b.get(st).set(0);
|
|
723
|
+
return Option.some(code);
|
|
724
|
+
}
|
|
725
|
+
if (Predicate.isTagged(node, "Not")) return Option.some(this.not(s, node));
|
|
726
|
+
return Option.none();
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Runs `body`, which branches to the label it is given on failure; then
|
|
730
|
+
* `failed` runs, and may fall through to the code after both.
|
|
731
|
+
*/
|
|
732
|
+
attempt(b, body, failed) {
|
|
733
|
+
b.block((ok) => {
|
|
734
|
+
b.block((bad) => {
|
|
735
|
+
body(bad);
|
|
736
|
+
b.br(ok);
|
|
737
|
+
});
|
|
738
|
+
failed();
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
/** `cut` and `complete`: a failure of `body` with the mode `from` fails with `to`. */
|
|
742
|
+
withMode(b, node, fail, body) {
|
|
743
|
+
let code = "";
|
|
744
|
+
this.attempt(b, (bad) => {
|
|
745
|
+
code = body(bad);
|
|
746
|
+
}, () => {
|
|
747
|
+
b.gget(G.mode).i32(modeOf(node.from)).op(Op.i32Eq);
|
|
748
|
+
b.if(() => b.i32(modeOf(node.to)).gset(G.mode));
|
|
749
|
+
b.br(fail);
|
|
750
|
+
});
|
|
751
|
+
return code;
|
|
752
|
+
}
|
|
753
|
+
/** `opt` and `orElseSucceed`: the trace holds 1 when `self` matched. */
|
|
754
|
+
opt(s, node) {
|
|
755
|
+
const { b, out } = s;
|
|
756
|
+
const flag = out === "" ? -1 : this.slot(b);
|
|
757
|
+
const saved = this.save(b);
|
|
758
|
+
const x = out === "" ? "" : this.v(s.bf);
|
|
759
|
+
let code = "";
|
|
760
|
+
this.attempt(b, (bad) => {
|
|
761
|
+
code = this.gen(node.self, x, b, bad, s.bf, s.d);
|
|
762
|
+
if (flag >= 0) this.putAt(b, flag, 1);
|
|
763
|
+
}, () => {
|
|
764
|
+
this.unlessBacktrack(b, s.fail);
|
|
765
|
+
this.restore(b, saved);
|
|
766
|
+
});
|
|
767
|
+
if (out === "") return "";
|
|
768
|
+
const some = Predicate.isTagged(node, "Opt") ? `R.some(${x})` : `${x}`;
|
|
769
|
+
const none = Predicate.isTagged(node, "Opt") ? "R.none()" : `${this.const(node.value)}()`;
|
|
770
|
+
return `if(T[i++]===1){${code}${out}=${some}}else ${out}=${none};`;
|
|
771
|
+
}
|
|
772
|
+
/** `not`: succeeds, consuming nothing, where `self` fails with `Backtrack`. */
|
|
773
|
+
not(s, node) {
|
|
774
|
+
const { b, fail } = s;
|
|
775
|
+
const saved = this.save(b);
|
|
776
|
+
this.attempt(b, (bad) => {
|
|
777
|
+
this.gen(node.self, "", b, bad, s.bf, s.d);
|
|
778
|
+
this.restore(b, saved);
|
|
779
|
+
this.failWith(b, fail, Backtrack);
|
|
780
|
+
}, () => {
|
|
781
|
+
this.unlessBacktrack(b, fail);
|
|
782
|
+
this.restore(b, saved);
|
|
783
|
+
});
|
|
784
|
+
return this.set(s, "void 0");
|
|
785
|
+
}
|
|
786
|
+
/** Sequences, choice and repetition. */
|
|
787
|
+
structure(node, s) {
|
|
788
|
+
const { p, out, b, fail, bf, d } = s;
|
|
789
|
+
if (Predicate.isTagged(node, "Seq")) return Option.some(this.seq(node, out, b, fail, bf, d));
|
|
790
|
+
if (Predicate.isTagged(node, "Struct")) return Option.some(this.struct(s, node));
|
|
791
|
+
if (Predicate.isTagged(node, "ZipWith")) return Option.some(this.zipWith(s, node));
|
|
792
|
+
if (Predicate.isTagged(node, "Alt")) return Option.some(this.alt(node, out, b, fail, bf, d));
|
|
793
|
+
if (Predicate.isTagged(node, "Dispatch")) return Option.some(this.dispatch(p, node, out, b, fail, bf, d));
|
|
794
|
+
if (Predicate.isTagged(node, "Repeat")) return Option.some(this.repeat(node, out, b, fail, bf, this.collect(node.self, out, d)));
|
|
795
|
+
if (Predicate.isTagged(node, "Fold")) return Option.some(node.while === void 0 ? this.fold(node, out, b, fail, bf, d) : this.island(p, out, b, fail, "the condition needs the accumulator"));
|
|
796
|
+
if (Predicate.isTagged(node, "Separated")) return Option.some(this.separated(node, out, b, fail, bf, d, this.collect(node.self, out, d)));
|
|
797
|
+
return Option.none();
|
|
798
|
+
}
|
|
799
|
+
struct(s, node) {
|
|
800
|
+
const { b, out } = s;
|
|
801
|
+
if (out === "") {
|
|
802
|
+
for (const q of node.parsers) this.gen(q, "", b, s.fail, s.bf, s.d);
|
|
803
|
+
return "";
|
|
804
|
+
}
|
|
805
|
+
const vars = node.parsers.map(() => this.v(s.bf));
|
|
806
|
+
const code = node.parsers.map((q, i) => this.gen(q, vars[i], b, s.fail, s.bf, s.d)).join("");
|
|
807
|
+
if (node.keys.includes("__proto__")) return code + `${out}={};` + node.keys.map((k, i) => `${out}[${this.const(k)}]=${vars[i]};`).join("");
|
|
808
|
+
return code + `${out}={${node.keys.map((k, i) => `[${this.const(k)}]:${vars[i]}`).join(",")}};`;
|
|
809
|
+
}
|
|
810
|
+
zipWith(s, node) {
|
|
811
|
+
const { b, out } = s;
|
|
812
|
+
if (out === "") {
|
|
813
|
+
this.gen(node.self, "", b, s.fail, s.bf, s.d);
|
|
814
|
+
this.gen(node.that, "", b, s.fail, s.bf, s.d);
|
|
815
|
+
return "";
|
|
816
|
+
}
|
|
817
|
+
const x = this.v(s.bf);
|
|
818
|
+
const y = this.v(s.bf);
|
|
819
|
+
return this.gen(node.self, x, b, s.fail, s.bf, s.d) + this.gen(node.that, y, b, s.fail, s.bf, s.d) + `${out}=${this.const(node.f)}(${x},${y});`;
|
|
820
|
+
}
|
|
821
|
+
/** Writes `value` to the trace word at the address in local `slot`. */
|
|
822
|
+
putAt(b, slot, value) {
|
|
823
|
+
b.get(slot).i32(value).mem(Op.i32Store, 2, 0);
|
|
824
|
+
}
|
|
825
|
+
map(node, out, b, fail, bf, d) {
|
|
826
|
+
if (out === "") return this.gen(node.self, "", b, fail, bf, d);
|
|
827
|
+
if (node.f === toFloat) {
|
|
828
|
+
const code = this.float(node, out, b, fail, bf, d);
|
|
829
|
+
if (Option.isSome(code)) return code.value;
|
|
830
|
+
}
|
|
831
|
+
if (node.f === fromEntries && buildsEntries(node.self)) return this.entries(node.self, out, b, fail, bf, d);
|
|
832
|
+
const x = this.v(bf);
|
|
833
|
+
return this.gen(node.self, x, b, fail, bf, d) + `${out}=${this.const(node.f)}(${x});`;
|
|
834
|
+
}
|
|
835
|
+
/** Whether `Ascii.float` / `Text.float` (`map(text, toFloat)`) can be scanned and converted in one pass. */
|
|
836
|
+
floatScans(node) {
|
|
837
|
+
return node.f === toFloat && floatTexts.has(node.self) && Option.isSome(this.floatEncoding(node.self));
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* The encoding of the text parser of `Ascii.float` / `Text.float` (`none`
|
|
841
|
+
* for `Ascii`), when the scan reads it: decimal notation is ASCII, which
|
|
842
|
+
* every built-in encoding read here encodes as one unit per character.
|
|
843
|
+
*/
|
|
844
|
+
floatEncoding(p) {
|
|
845
|
+
let taken = nodeOf(p);
|
|
846
|
+
while (Predicate.isTagged(taken, "Expected")) taken = nodeOf(taken.self);
|
|
847
|
+
if (!Predicate.isTagged(taken, "Taken")) return Option.none();
|
|
848
|
+
const enc = taken.encoding;
|
|
849
|
+
return this.native(enc) ? Option.some(enc) : Option.none();
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* `Ascii.float` and `Text.float`: decimal numbers are matched and
|
|
853
|
+
* converted in one pass (see `scanFloat`); the other forms (`nan`, `inf`)
|
|
854
|
+
* are matched by the float's text parser and converted by `toFloat`. The
|
|
855
|
+
* trace holds the double.
|
|
856
|
+
*/
|
|
857
|
+
float(node, out, b, fail, bf, d) {
|
|
858
|
+
if (!this.floatScans(node)) return Option.none();
|
|
859
|
+
return Option.some(this.scanFloatThen(node, out, b, fail, false, bf, d));
|
|
860
|
+
}
|
|
861
|
+
/** `filter(float, Number.isFinite)`: the float scan, then a test of the double. */
|
|
862
|
+
finiteFloat(self, out, b, fail, bf, d) {
|
|
863
|
+
const node = nodeOf(self);
|
|
864
|
+
if (!Predicate.isTagged(node, "Map") || !this.floatScans(node)) return Option.none();
|
|
865
|
+
return Option.some(this.scanFloatThen(node, out, b, fail, true, bf, d));
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* The float at `p`: the scan of a decimal number, or else the float's text
|
|
869
|
+
* parser, whose text `toFloat` converts in JavaScript. A failure of the
|
|
870
|
+
* scan fails with `Backtrack`, as the text parser would. With `finite`, a
|
|
871
|
+
* value that isn't finite fails.
|
|
872
|
+
*/
|
|
873
|
+
scanFloatThen(map, out, b, fail, finite, bf, d) {
|
|
874
|
+
const enc = Option.getOrUndefined(this.floatEncoding(map.self));
|
|
875
|
+
let taken = nodeOf(map.self);
|
|
876
|
+
while (Predicate.isTagged(taken, "Expected")) taken = nodeOf(taken.self);
|
|
877
|
+
const r = b.local();
|
|
878
|
+
const st = b.local();
|
|
879
|
+
b.get(0).set(st);
|
|
880
|
+
b.get(0).call(this.scanFloat(enc)).tee(r).i32(-1).op(Op.i32Eq);
|
|
881
|
+
b.if(() => {
|
|
882
|
+
if (Predicate.isTagged(taken, "Taken")) this.gen(taken.self, "", b, fail, bf, d);
|
|
883
|
+
b.get(st).get(0).call(Imp.float).gset(G.f);
|
|
884
|
+
}, () => {
|
|
885
|
+
b.get(r).i32(-2).op(Op.i32Eq);
|
|
886
|
+
this.failIf(b, fail);
|
|
887
|
+
b.get(r).set(0);
|
|
888
|
+
});
|
|
889
|
+
if (finite) {
|
|
890
|
+
b.gget(G.f).gget(G.f).op(Op.f64Sub).f64(0).op(Op.f64Ne);
|
|
891
|
+
this.failIf(b, fail);
|
|
892
|
+
}
|
|
893
|
+
if (out === "") return "";
|
|
894
|
+
this.putF64(b);
|
|
895
|
+
return `i=(i+1)&-2;${out}=F[i>>1];i+=2;`;
|
|
896
|
+
}
|
|
897
|
+
seq(node, out, b, fail, bf, d) {
|
|
898
|
+
const output = node.output;
|
|
899
|
+
if (out === "") {
|
|
900
|
+
for (const q of node.parsers) this.gen(q, "", b, fail, bf, d);
|
|
901
|
+
return "";
|
|
902
|
+
}
|
|
903
|
+
if (Predicate.isNumber(output)) return node.parsers.map((q, i) => this.gen(q, i === output ? out : "", b, fail, bf, d)).join("");
|
|
904
|
+
const keep = (i) => output === "tuple" || output.includes(i);
|
|
905
|
+
const vars = node.parsers.map((_, i) => keep(i) ? this.v(bf) : "");
|
|
906
|
+
return node.parsers.map((q, i) => this.gen(q, vars[i], b, fail, bf, d)).join("") + `${out}=[${(output === "tuple" ? vars.filter((v) => v !== "") : output.map((i) => vars[i])).join(",")}];`;
|
|
907
|
+
}
|
|
908
|
+
alt(node, out, b, fail, bf, d) {
|
|
909
|
+
const parsers = node.parsers;
|
|
910
|
+
if (Arr.isReadonlyArrayEmpty(parsers)) {
|
|
911
|
+
this.failWith(b, fail, Backtrack);
|
|
912
|
+
return "";
|
|
913
|
+
}
|
|
914
|
+
if (parsers.length === 1) return this.gen(parsers[0], out, b, fail, bf, d);
|
|
915
|
+
const saved = this.save(b);
|
|
916
|
+
const cases = [];
|
|
917
|
+
b.block((done) => {
|
|
918
|
+
parsers.forEach((q, i) => {
|
|
919
|
+
b.block((bad) => {
|
|
920
|
+
if (out !== "") this.put(b, [() => b.i32(i)]);
|
|
921
|
+
cases.push(this.gen(q, out, b, bad, bf, d));
|
|
922
|
+
b.br(done);
|
|
923
|
+
});
|
|
924
|
+
this.unlessBacktrack(b, fail);
|
|
925
|
+
this.restore(b, saved);
|
|
926
|
+
});
|
|
927
|
+
this.failWith(b, fail, Backtrack);
|
|
928
|
+
});
|
|
929
|
+
if (out === "") return "";
|
|
930
|
+
return `switch(T[i++]){${cases.map((c, i) => `case ${i}:{${c}break}`).join("")}}`;
|
|
931
|
+
}
|
|
932
|
+
dispatch(p, node, out, b, fail, bf, d) {
|
|
933
|
+
const { node: sel, peek } = selectorOf(node.selector);
|
|
934
|
+
const cases = node.cases;
|
|
935
|
+
if (cases === void 0 || !Predicate.isTagged(sel, "Any") || !this.native(sel.encoding)) return this.island(p, out, b, fail, "the selector runs in JavaScript");
|
|
936
|
+
const enc = sel.encoding;
|
|
937
|
+
const r = Option.map(Option.fromUndefinedOr(enc), (e) => this.reader(e));
|
|
938
|
+
const size = this.dispatchSize(r);
|
|
939
|
+
if (size === 0) return this.island(p, out, b, fail, "the selector runs in JavaScript");
|
|
940
|
+
const bytes = enc === void 0 && this.kind === 1;
|
|
941
|
+
const choose = (unit) => node.choose(bytes ? unit : charOf(unit));
|
|
942
|
+
const targets = [];
|
|
943
|
+
for (let unit = 0; unit < size; unit++) {
|
|
944
|
+
const q = choose(unit);
|
|
945
|
+
if (q !== void 0 && !targets.includes(q)) targets.push(q);
|
|
946
|
+
}
|
|
947
|
+
const closed = node.otherwise === void 0 && closedKeys(Reflect.ownKeys(cases), size, bytes);
|
|
948
|
+
const u = b.local();
|
|
949
|
+
const st = b.local();
|
|
950
|
+
b.get(0).set(st);
|
|
951
|
+
this.selectorUnit(b, r, size);
|
|
952
|
+
b.set(u);
|
|
953
|
+
b.get(u).i32(-1).op(Op.i32Eq);
|
|
954
|
+
this.failIf(b, fail);
|
|
955
|
+
const code = [];
|
|
956
|
+
let general = "";
|
|
957
|
+
b.block((done) => this.nested(b, targets.length + 2, (labels) => {
|
|
958
|
+
const table = Arr.makeBy(size, (unit) => {
|
|
959
|
+
const q = choose(unit);
|
|
960
|
+
return q === void 0 ? labels[targets.length] : labels[targets.indexOf(q)];
|
|
961
|
+
});
|
|
962
|
+
b.get(u).brTable(table, labels[targets.length + 1]);
|
|
963
|
+
}, (j) => {
|
|
964
|
+
if (j < targets.length) {
|
|
965
|
+
if (!peek) this.advance(b, Option.match(r, {
|
|
966
|
+
onNone: () => 1,
|
|
967
|
+
onSome: (x) => x.width
|
|
968
|
+
}));
|
|
969
|
+
if (out !== "") this.put(b, [() => b.i32(j)]);
|
|
970
|
+
code[j] = this.gen(targets[j], out, b, fail, bf, d);
|
|
971
|
+
b.br(done);
|
|
972
|
+
} else if (j === targets.length || closed) this.failWith(b, fail, Backtrack);
|
|
973
|
+
else {
|
|
974
|
+
b.get(st).set(0);
|
|
975
|
+
if (out !== "") this.put(b, [() => b.i32(targets.length)]);
|
|
976
|
+
general = this.island(p, out, b, fail, "the selector reads a code point beyond the fast units");
|
|
977
|
+
b.br(done);
|
|
978
|
+
}
|
|
979
|
+
}));
|
|
980
|
+
if (out === "") return "";
|
|
981
|
+
return `switch(T[i++]){${code.map((c, j) => `case ${j}:{${c}break}`).join("")}default:{${general}}}`;
|
|
982
|
+
}
|
|
983
|
+
/** How many units a `dispatch` switches on: the units that are whole code points, at most `TableSize`. */
|
|
984
|
+
dispatchSize(r) {
|
|
985
|
+
const limit = Option.match(r, {
|
|
986
|
+
onNone: () => this.kind === 0 ? 55296 : TableSize,
|
|
987
|
+
onSome: (x) => Math.min(x.fast, 55296)
|
|
988
|
+
});
|
|
989
|
+
return Math.min(limit, TableSize);
|
|
990
|
+
}
|
|
991
|
+
/** Pushes the unit a `dispatch` switches on: -1 at the end, `size` for a unit cut short. */
|
|
992
|
+
selectorUnit(b, r, size) {
|
|
993
|
+
if (Option.isNone(r)) {
|
|
994
|
+
this.atEnd(b, 0);
|
|
995
|
+
b.ifI32(() => b.i32(-1), () => this.load(b, 0));
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const reader = r.value;
|
|
999
|
+
reader.guard(b, 0);
|
|
1000
|
+
b.ifI32(() => reader.unit(b, 0), () => {
|
|
1001
|
+
this.atEnd(b, 0);
|
|
1002
|
+
b.ifI32(() => b.i32(-1), () => b.i32(size));
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* `n` nested blocks around `inner`, which gets their labels, outermost
|
|
1007
|
+
* first; after the end of block `j` (where a branch to its label goes),
|
|
1008
|
+
* `after(j)` runs.
|
|
1009
|
+
*/
|
|
1010
|
+
nested(b, n, inner, after) {
|
|
1011
|
+
const open = (j, labels) => {
|
|
1012
|
+
if (j === n) return inner(labels);
|
|
1013
|
+
b.block((l) => open(j + 1, [...labels, l]));
|
|
1014
|
+
after(j);
|
|
1015
|
+
};
|
|
1016
|
+
open(0, []);
|
|
1017
|
+
}
|
|
1018
|
+
/** An element that is pushed to an array (or not kept). */
|
|
1019
|
+
collect(self, out, d) {
|
|
1020
|
+
return (b, fail, bf) => {
|
|
1021
|
+
if (out === "") {
|
|
1022
|
+
this.gen(self, "", b, fail, bf, d);
|
|
1023
|
+
return "";
|
|
1024
|
+
}
|
|
1025
|
+
const x = this.v(bf);
|
|
1026
|
+
return `${this.gen(self, x, b, fail, bf, d)}${out}.push(${x});`;
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
/** The builder of a repetition: its count, then each element. */
|
|
1030
|
+
many(out, init, element, finish, bf) {
|
|
1031
|
+
if (out === "") return "";
|
|
1032
|
+
const n = this.v(bf);
|
|
1033
|
+
const k = this.v(bf);
|
|
1034
|
+
return `${init}${n}=T[i++];for(${k}=0;${k}<${n};${k}++){${element}}${finish}`;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* After a failed element or separator: fails unless it backtracked and
|
|
1038
|
+
* `n` reached `min`; otherwise restores the position and stops.
|
|
1039
|
+
*/
|
|
1040
|
+
stop(b, fail, n, min, restore, done) {
|
|
1041
|
+
this.unlessBacktrack(b, fail);
|
|
1042
|
+
if (min > 0) {
|
|
1043
|
+
b.get(n).i32(min).op(Op.i32LtU);
|
|
1044
|
+
b.if(() => b.br(fail));
|
|
1045
|
+
}
|
|
1046
|
+
restore();
|
|
1047
|
+
b.br(done);
|
|
1048
|
+
}
|
|
1049
|
+
/** Counts an element in local `n` and goes on to the next. */
|
|
1050
|
+
nextElement(b, n, again) {
|
|
1051
|
+
b.get(n).i32(1).op(Op.i32Add).set(n);
|
|
1052
|
+
b.br(again);
|
|
1053
|
+
}
|
|
1054
|
+
/** Writes the count in local `n` to the trace word reserved at `slot`, if any. Returns no builder code. */
|
|
1055
|
+
counted(b, slot, n) {
|
|
1056
|
+
if (slot >= 0) b.get(slot).get(n).mem(Op.i32Store, 2, 0);
|
|
1057
|
+
return "";
|
|
1058
|
+
}
|
|
1059
|
+
/** An unbounded repetition that consumed nothing since `from` fails with `Cut`, as the interpreter does. */
|
|
1060
|
+
progress(b, fail, from) {
|
|
1061
|
+
b.get(0).get(from).op(Op.i32Eq);
|
|
1062
|
+
b.if(() => this.failWith(b, fail, Cut));
|
|
1063
|
+
}
|
|
1064
|
+
repeat(node, out, b, fail, bf, element, init = out === "" ? "" : `${out}=[];`, finish = "") {
|
|
1065
|
+
const { max, min } = node;
|
|
1066
|
+
const n = b.local();
|
|
1067
|
+
const slot = out === "" ? -1 : this.slot(b);
|
|
1068
|
+
let elementCode = "";
|
|
1069
|
+
b.i32(0).set(n);
|
|
1070
|
+
b.block((done) => {
|
|
1071
|
+
this.loop(b, (again) => {
|
|
1072
|
+
if (max !== Infinity) b.get(n).i32(max).op(Op.i32GeU).brIf(done);
|
|
1073
|
+
const saved = this.save(b);
|
|
1074
|
+
this.attempt(b, (bad) => {
|
|
1075
|
+
elementCode = element(b, bad, bf);
|
|
1076
|
+
if (max === Infinity) this.progress(b, fail, saved[0]);
|
|
1077
|
+
this.nextElement(b, n, again);
|
|
1078
|
+
}, () => this.stop(b, fail, n, min, () => this.restore(b, saved), done));
|
|
1079
|
+
});
|
|
1080
|
+
});
|
|
1081
|
+
return this.counted(b, slot, n) + this.many(out, init, elementCode, finish, bf);
|
|
1082
|
+
}
|
|
1083
|
+
fold(node, out, b, fail, bf, d) {
|
|
1084
|
+
const f = this.const(node.f);
|
|
1085
|
+
const element = (body, bad, vars) => {
|
|
1086
|
+
if (out === "") {
|
|
1087
|
+
this.gen(node.self, "", body, bad, vars, d);
|
|
1088
|
+
return "";
|
|
1089
|
+
}
|
|
1090
|
+
const x = this.v(vars);
|
|
1091
|
+
return `${this.gen(node.self, x, body, bad, vars, d)}${out}=${f}(${out},${x});`;
|
|
1092
|
+
};
|
|
1093
|
+
return this.repeat(node, out, b, fail, bf, element, out === "" ? "" : `${out}=${this.const(node.init)}();`);
|
|
1094
|
+
}
|
|
1095
|
+
separated(node, out, b, fail, bf, d, element, init = out === "" ? "" : `${out}=[];`, finish = "") {
|
|
1096
|
+
const { max, min } = node;
|
|
1097
|
+
if (max === 0) return out === "" ? "" : `${init}${finish}`;
|
|
1098
|
+
const n = b.local();
|
|
1099
|
+
const st = b.local();
|
|
1100
|
+
const t = b.local();
|
|
1101
|
+
const slot = out === "" ? -1 : this.slot(b);
|
|
1102
|
+
let elementCode = "";
|
|
1103
|
+
const mark = () => b.get(0).set(st).get(b.tp).set(t);
|
|
1104
|
+
const back = () => b.get(st).set(0).get(t).set(b.tp);
|
|
1105
|
+
b.i32(0).set(n);
|
|
1106
|
+
b.block((done) => {
|
|
1107
|
+
mark();
|
|
1108
|
+
this.attempt(b, (bad) => {
|
|
1109
|
+
elementCode = element(b, bad, bf);
|
|
1110
|
+
b.i32(1).set(n);
|
|
1111
|
+
}, () => {
|
|
1112
|
+
this.unlessBacktrack(b, fail);
|
|
1113
|
+
if (min > 0) b.br(fail);
|
|
1114
|
+
back();
|
|
1115
|
+
b.br(done);
|
|
1116
|
+
});
|
|
1117
|
+
this.loop(b, (again) => {
|
|
1118
|
+
if (max !== Infinity) b.get(n).i32(max).op(Op.i32GeU).brIf(done);
|
|
1119
|
+
mark();
|
|
1120
|
+
this.attempt(b, (bad) => this.gen(node.separator, "", b, bad, bf, d), () => this.stop(b, fail, n, min, back, done));
|
|
1121
|
+
if (max === Infinity) this.progress(b, fail, st);
|
|
1122
|
+
this.attempt(b, (bad) => {
|
|
1123
|
+
element(b, bad, new Vars());
|
|
1124
|
+
this.nextElement(b, n, again);
|
|
1125
|
+
}, () => this.stop(b, fail, n, min, back, done));
|
|
1126
|
+
});
|
|
1127
|
+
});
|
|
1128
|
+
return this.counted(b, slot, n) + this.many(out, init, elementCode, finish, bf);
|
|
1129
|
+
}
|
|
1130
|
+
/**
|
|
1131
|
+
* `map(self, Object.fromEntries)` where `buildsEntries(self)`: the scan is
|
|
1132
|
+
* the usual one, and the builder assigns each entry as it is read, as the
|
|
1133
|
+
* JIT does (see `internal/entries.ts`).
|
|
1134
|
+
*/
|
|
1135
|
+
entries(p, out, b, fail, bf, d) {
|
|
1136
|
+
const node = nodeOf(p);
|
|
1137
|
+
if (Predicate.isTagged(node, "Mode")) return this.withMode(b, node, fail, (bad) => this.entries(node.self, out, b, bad, bf, d));
|
|
1138
|
+
if (Predicate.isTagged(node, "Seq") && Predicate.isNumber(node.output)) {
|
|
1139
|
+
const kept = node.output;
|
|
1140
|
+
return node.parsers.map((q, j) => j === kept ? this.entries(q, out, b, fail, bf, d) : this.gen(q, "", b, fail, bf, d)).join("");
|
|
1141
|
+
}
|
|
1142
|
+
if (isDiagnostic(node)) return this.entries(node.self, out, b, fail, bf, d);
|
|
1143
|
+
const pair = Predicate.isTagged(node, "Repeat") || Predicate.isTagged(node, "Separated") ? pairOf(node.self) : void 0;
|
|
1144
|
+
if ((Predicate.isTagged(node, "Repeat") || Predicate.isTagged(node, "Separated")) && pair !== void 0) {
|
|
1145
|
+
const deferred = this.v(bf);
|
|
1146
|
+
const element = (body, bad) => {
|
|
1147
|
+
const key = this.v(bf);
|
|
1148
|
+
const value = this.v(bf);
|
|
1149
|
+
return pair.parsers.map((q, j) => this.gen(q, j === pair.key ? key : j === pair.value ? value : "", body, bad, bf, d)).join("") + `if(${deferred}===void 0&&R.isPlainKey(${key}))${out}[${key}]=${value};else ${deferred}=R.addEntry(${out},${deferred},${key},${value});`;
|
|
1150
|
+
};
|
|
1151
|
+
const init = `${out}={};${deferred}=void 0;`;
|
|
1152
|
+
const finish = `if(${deferred}!==void 0)R.flushEntries(${out},${deferred});`;
|
|
1153
|
+
return Predicate.isTagged(node, "Repeat") ? this.repeat(node, out, b, fail, bf, element, init, finish) : this.separated(node, out, b, fail, bf, d, element, init, finish);
|
|
1154
|
+
}
|
|
1155
|
+
const x = this.v(bf);
|
|
1156
|
+
return this.gen(p, x, b, fail, bf, d) + `${out}=${this.const(fromEntries)}(${x});`;
|
|
1157
|
+
}
|
|
1158
|
+
/** How the scan reads an encoding's code points. */
|
|
1159
|
+
reader(enc) {
|
|
1160
|
+
if (enc === utf16le || enc === utf16be) {
|
|
1161
|
+
const [lo, hi] = enc === utf16le ? [0, 1] : [1, 0];
|
|
1162
|
+
return {
|
|
1163
|
+
width: 2,
|
|
1164
|
+
fast: 55296,
|
|
1165
|
+
guard: (b, pos) => this.end(b.get(pos).i32(1).op(Op.i32Add)).op(Op.i32LtU),
|
|
1166
|
+
unit: (b, pos) => {
|
|
1167
|
+
this.load(b, pos, lo);
|
|
1168
|
+
this.load(b, pos, hi);
|
|
1169
|
+
b.i32(8).op(Op.i32Shl, Op.i32Or);
|
|
1170
|
+
},
|
|
1171
|
+
decode: this.decoder(enc)
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
return {
|
|
1175
|
+
width: 1,
|
|
1176
|
+
fast: enc === latin1 ? TableSize : fastLimit(enc),
|
|
1177
|
+
guard: (b, pos) => this.end(b.get(pos)).op(Op.i32LtU),
|
|
1178
|
+
unit: (b, pos) => this.load(b, pos),
|
|
1179
|
+
decode: enc === latin1 ? -1 : this.decoder(enc)
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
/**
|
|
1183
|
+
* Pushes whether the code point in local `c`, known to be below `below`,
|
|
1184
|
+
* is in the set (taking `negate` into account). Known ranges compile to
|
|
1185
|
+
* comparisons, testing the accepted runs or the rejected ones, whichever
|
|
1186
|
+
* takes fewer; otherwise a table below `TableSize`, then the ranges or the
|
|
1187
|
+
* set's test above it.
|
|
1188
|
+
*/
|
|
1189
|
+
member(cs, negate, c, b, below = 1114112) {
|
|
1190
|
+
const max = Math.min(below, this.kind === 1 && this.tokenBytes ? 256 : 1114112) - 1;
|
|
1191
|
+
const runs = acceptedRuns(cs, negate, max);
|
|
1192
|
+
const cost = (rs) => rs.reduce((n, [lo, hi]) => n + (lo === hi || lo === 0 || hi >= max ? 1 : 2), 0);
|
|
1193
|
+
/** Pushes whether `c` is in one of the runs. */
|
|
1194
|
+
const compare = (rs) => {
|
|
1195
|
+
if (Arr.isReadonlyArrayEmpty(rs)) {
|
|
1196
|
+
b.i32(0);
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
rs.forEach(([lo, hi], k) => {
|
|
1200
|
+
if (lo === hi) b.get(c).i32(lo).op(Op.i32Eq);
|
|
1201
|
+
else if (lo === 0) b.get(c).i32(hi).op(Op.i32LeU);
|
|
1202
|
+
else if (hi >= max) b.get(c).i32(lo).op(Op.i32GeU);
|
|
1203
|
+
else b.get(c).i32(lo).op(Op.i32Sub).i32(hi - lo).op(Op.i32LeU);
|
|
1204
|
+
if (k > 0) b.op(Op.i32Or);
|
|
1205
|
+
});
|
|
1206
|
+
};
|
|
1207
|
+
if (Option.isSome(runs)) {
|
|
1208
|
+
const accept = runs.value;
|
|
1209
|
+
const reject = Option.getOrElse(acceptedRuns({
|
|
1210
|
+
...cs,
|
|
1211
|
+
ranges: accept
|
|
1212
|
+
}, true, max), () => []);
|
|
1213
|
+
if (Math.min(cost(accept), cost(reject)) <= 6) {
|
|
1214
|
+
if (cost(accept) <= cost(reject)) compare(accept);
|
|
1215
|
+
else {
|
|
1216
|
+
compare(reject);
|
|
1217
|
+
b.op(Op.i32Eqz);
|
|
1218
|
+
}
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
const table = new Uint8Array(TableSize);
|
|
1223
|
+
for (let x = 0; x < TableSize; x++) table[x] = cs.table[x] === 1 !== negate ? 1 : 0;
|
|
1224
|
+
const callTest = () => {
|
|
1225
|
+
const test = cs.test;
|
|
1226
|
+
this.tests.push((x) => test(x) !== negate ? 1 : 0);
|
|
1227
|
+
b.i32(this.tests.length - 1).get(c).call(Imp.test);
|
|
1228
|
+
};
|
|
1229
|
+
const offset = this.table(table);
|
|
1230
|
+
if (offset < 0) {
|
|
1231
|
+
callTest();
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
if (max < TableSize) {
|
|
1235
|
+
b.get(c).mem(Op.i32Load8U, 0, offset);
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1238
|
+
const high = Option.map(runs, (rs) => rs.filter(([, hi]) => hi >= TableSize));
|
|
1239
|
+
b.get(c).i32(TableSize).op(Op.i32LtU);
|
|
1240
|
+
b.ifI32(() => b.get(c).mem(Op.i32Load8U, 0, offset), () => {
|
|
1241
|
+
if (Option.isSome(high) && cost(high.value) <= 6) compare(high.value);
|
|
1242
|
+
else callTest();
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
/** Whether the token being compiled reads bytes as `Token` does (code points never exceed 255). */
|
|
1246
|
+
tokenBytes = false;
|
|
1247
|
+
/** Places a membership table in memory, returning its address. */
|
|
1248
|
+
table(t) {
|
|
1249
|
+
for (let i = 0; i < this.tables.length; i++) if (this.tables[i].every((x, j) => x === t[j])) return i * TableSize;
|
|
1250
|
+
if ((this.tables.length + 1) * TableSize > TablesEnd) return -1;
|
|
1251
|
+
this.tables.push(t);
|
|
1252
|
+
return (this.tables.length - 1) * TableSize;
|
|
1253
|
+
}
|
|
1254
|
+
/** Pushes whether a whole unit below `r.fast` is at `p`, leaving it in local `c`. `p < E` holds. */
|
|
1255
|
+
fastUnit(r, b, c) {
|
|
1256
|
+
if (r.width === 1) {
|
|
1257
|
+
r.unit(b, 0);
|
|
1258
|
+
b.tee(c).i32(r.fast).op(Op.i32LtU);
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
r.guard(b, 0);
|
|
1262
|
+
b.ifI32(() => {
|
|
1263
|
+
r.unit(b, 0);
|
|
1264
|
+
b.tee(c).i32(r.fast).op(Op.i32LtU);
|
|
1265
|
+
}, () => b.i32(0));
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Reads the code point at `p` into local `c` and its width into `w`,
|
|
1269
|
+
* branching to `stop` when it is malformed or cut short. `p < E` holds.
|
|
1270
|
+
*/
|
|
1271
|
+
readCodePoint(enc, b, c, w, stop) {
|
|
1272
|
+
if (enc === void 0) {
|
|
1273
|
+
this.load(b, 0);
|
|
1274
|
+
b.set(c).i32(1).set(w);
|
|
1275
|
+
if (this.kind === 0) this.lenientPair(b, c, w);
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1278
|
+
const r = this.reader(enc);
|
|
1279
|
+
if (r.decode < 0) {
|
|
1280
|
+
r.unit(b, 0);
|
|
1281
|
+
b.set(c).i32(1).set(w);
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
this.fastUnit(r, b, c);
|
|
1285
|
+
b.if(() => b.i32(r.width).set(w), () => {
|
|
1286
|
+
b.get(0).call(r.decode).tee(c).i32(0).op(Op.i32LtS).brIf(stop);
|
|
1287
|
+
b.gget(G.w).set(w);
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
/** `Token` on strings: a high surrogate in `c` followed by a low one is read as their pair. */
|
|
1291
|
+
lenientPair(b, c, w) {
|
|
1292
|
+
b.get(c).i32(64512).op(Op.i32And).i32(55296).op(Op.i32Eq);
|
|
1293
|
+
b.if(() => {
|
|
1294
|
+
b.get(0).i32(1).op(Op.i32Add).gget(G.len).op(Op.i32LtU);
|
|
1295
|
+
b.if(() => {
|
|
1296
|
+
this.load(b, 0, 1);
|
|
1297
|
+
const lo = b.local();
|
|
1298
|
+
b.tee(lo).i32(64512).op(Op.i32And).i32(56320).op(Op.i32Eq);
|
|
1299
|
+
b.if(() => {
|
|
1300
|
+
pair(b, c, lo);
|
|
1301
|
+
b.set(c);
|
|
1302
|
+
b.i32(2).set(w);
|
|
1303
|
+
});
|
|
1304
|
+
});
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
token(s, node) {
|
|
1308
|
+
const enc = node.encoding;
|
|
1309
|
+
if (!this.native(enc)) return this.island(s.p, s.out, s.b, s.fail, "the encoding is decoded in JavaScript");
|
|
1310
|
+
this.tokenBytes = enc === void 0 && this.kind === 1;
|
|
1311
|
+
if (Predicate.isTagged(node, "Literal")) return this.literal(s, node);
|
|
1312
|
+
if (Predicate.isTagged(node, "TakeWhile")) return this.takeWhile(s, node);
|
|
1313
|
+
return this.single(s, node);
|
|
1314
|
+
}
|
|
1315
|
+
/** `any` and `oneOf`: one code point. */
|
|
1316
|
+
single(s, node) {
|
|
1317
|
+
const { b } = s;
|
|
1318
|
+
const enc = node.encoding;
|
|
1319
|
+
const c = b.local();
|
|
1320
|
+
const w = b.local();
|
|
1321
|
+
const st = b.local();
|
|
1322
|
+
b.get(0).set(st);
|
|
1323
|
+
this.attempt(b, (bad) => {
|
|
1324
|
+
this.atEnd(b, 0);
|
|
1325
|
+
b.brIf(bad);
|
|
1326
|
+
this.readCodePoint(enc, b, c, w, bad);
|
|
1327
|
+
if (Predicate.isTagged(node, "OneOf")) {
|
|
1328
|
+
this.member(enc === void 0 ? tokenSet(node.set, this.kind) : textSet(node.set), node.negate, c, b);
|
|
1329
|
+
b.op(Op.i32Eqz).brIf(bad);
|
|
1330
|
+
}
|
|
1331
|
+
b.get(0).get(w).op(Op.i32Add).set(0);
|
|
1332
|
+
}, () => this.failWith(b, s.fail, Backtrack));
|
|
1333
|
+
if (s.out === "") return "";
|
|
1334
|
+
if (enc !== void 0) {
|
|
1335
|
+
this.put(b, [() => b.get(c)]);
|
|
1336
|
+
return this.set(s, "R.charOf(T[i++])");
|
|
1337
|
+
}
|
|
1338
|
+
this.put(b, [() => b.get(st), () => b.get(0)]);
|
|
1339
|
+
return this.set(s, this.kind === 0 ? "I.slice(T[i],T[i+1]);i+=2" : "I[T[i]];i+=2");
|
|
1340
|
+
}
|
|
1341
|
+
takeWhile(s, node) {
|
|
1342
|
+
const { b } = s;
|
|
1343
|
+
const enc = node.encoding;
|
|
1344
|
+
const { max, min } = node;
|
|
1345
|
+
const set = {
|
|
1346
|
+
cs: enc === void 0 ? tokenSet(node.set, this.kind) : textSet(node.set),
|
|
1347
|
+
negate: node.negate,
|
|
1348
|
+
c: b.local()
|
|
1349
|
+
};
|
|
1350
|
+
const w = b.local();
|
|
1351
|
+
const st = b.local();
|
|
1352
|
+
b.get(0).set(st);
|
|
1353
|
+
const counted = max !== Infinity || min > 1;
|
|
1354
|
+
const n = b.local();
|
|
1355
|
+
if (counted) b.i32(0).set(n);
|
|
1356
|
+
const wide = enc === utf8 && s.out !== "" ? b.local() : -1;
|
|
1357
|
+
if (wide >= 0) b.i32(0).set(wide);
|
|
1358
|
+
b.block((stop) => {
|
|
1359
|
+
if (!counted && this.vectorRun(enc, set)) this.byteRun(b, set, stop);
|
|
1360
|
+
b.loop((again) => {
|
|
1361
|
+
this.atEnd(b, 0);
|
|
1362
|
+
b.brIf(stop);
|
|
1363
|
+
if (max !== Infinity) b.get(n).i32(max).op(Op.i32GeU).brIf(stop);
|
|
1364
|
+
this.whileStep(enc, b, set, w, wide, stop);
|
|
1365
|
+
if (counted) b.get(n).i32(1).op(Op.i32Add).set(n);
|
|
1366
|
+
b.br(again);
|
|
1367
|
+
});
|
|
1368
|
+
});
|
|
1369
|
+
if (min > 0) {
|
|
1370
|
+
if (counted) b.get(n).i32(min).op(Op.i32LtU);
|
|
1371
|
+
else b.get(0).get(st).op(Op.i32Eq);
|
|
1372
|
+
this.failIf(b, s.fail);
|
|
1373
|
+
}
|
|
1374
|
+
return this.sliceValue(s, enc, st, wide);
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* The vector steps of an unbounded `takeWhile` on bytes, where every
|
|
1378
|
+
* byte is a token: sixteen bytes, up to the first that is not accepted,
|
|
1379
|
+
* which ends the run (most runs, tokens, end in the first sixteen); after
|
|
1380
|
+
* sixteen accepted, thirty-two at a time while all are. The
|
|
1381
|
+
* scalar loop takes the last fifteen bytes. Applies to `Token` and
|
|
1382
|
+
* Latin-1 text on bytes, where SIMD validates. (UTF-8, whose bytes from
|
|
1383
|
+
* 0x80 up are parts of code points, and strings, whose units are
|
|
1384
|
+
* sixteen-bit, stay scalar: on the JSON benchmark, whose runs are short,
|
|
1385
|
+
* a vector step there costs more than it saves.)
|
|
1386
|
+
*/
|
|
1387
|
+
byteRun(b, set, stop) {
|
|
1388
|
+
const x = b.localV128();
|
|
1389
|
+
const y = b.localV128();
|
|
1390
|
+
const accepted = this.acceptedBytes(b, set);
|
|
1391
|
+
b.block((done) => {
|
|
1392
|
+
b.loop((again) => {
|
|
1393
|
+
this.end(b).get(0).op(Op.i32Sub).i32(16).op(Op.i32LtS).brIf(done);
|
|
1394
|
+
b.get(0).simd(Simd.v128Load, 0, Input$1).set(x);
|
|
1395
|
+
accepted(x);
|
|
1396
|
+
b.tee(x).simd(Simd.i8x16AllTrue);
|
|
1397
|
+
b.if(() => {
|
|
1398
|
+
this.advance(b, 16);
|
|
1399
|
+
b.block((short) => {
|
|
1400
|
+
b.loop((more) => {
|
|
1401
|
+
this.end(b).get(0).op(Op.i32Sub).i32(32).op(Op.i32LtS).brIf(short);
|
|
1402
|
+
b.get(0).simd(Simd.v128Load, 0, Input$1).set(x);
|
|
1403
|
+
b.get(0).simd(Simd.v128Load, 0, 131088).set(y);
|
|
1404
|
+
accepted(x);
|
|
1405
|
+
accepted(y);
|
|
1406
|
+
b.simd(Simd.v128And).simd(Simd.i8x16AllTrue).op(Op.i32Eqz).brIf(short);
|
|
1407
|
+
this.advance(b, 32);
|
|
1408
|
+
b.br(more);
|
|
1409
|
+
});
|
|
1410
|
+
});
|
|
1411
|
+
b.br(again);
|
|
1412
|
+
});
|
|
1413
|
+
b.get(0).get(x).simd(Simd.i8x16Bitmask).i32(-1).op(Op.i32Xor).op(Op.i32Ctz).op(Op.i32Add).set(0);
|
|
1414
|
+
b.br(stop);
|
|
1415
|
+
});
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
/** Whether a `takeWhile` of `set` in `enc` has vector steps (see {@link byteRun}). */
|
|
1419
|
+
vectorRun(enc, set) {
|
|
1420
|
+
return this.kind === 1 && (enc === void 0 || enc === latin1) && simd() && accepts(set);
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Code that pushes, for the sixteen bytes in a local, 0xFF where the byte
|
|
1424
|
+
* is accepted and 0 where not: comparisons with the rejected bytes when
|
|
1425
|
+
* there are at most three of them (`takeTill` of a few stops), else two
|
|
1426
|
+
* swizzles of nibble tables for each half of the byte values. Its
|
|
1427
|
+
* constants are set up here, in locals.
|
|
1428
|
+
*/
|
|
1429
|
+
acceptedBytes(b, set) {
|
|
1430
|
+
const rejected = [];
|
|
1431
|
+
for (let c = 0; c < 256 && rejected.length <= MaxStops; c++) if (set.cs.table[c] === 1 === set.negate) rejected.push(c);
|
|
1432
|
+
if (rejected.length <= MaxStops) {
|
|
1433
|
+
const stops = rejected.map((c) => {
|
|
1434
|
+
const l = b.localV128();
|
|
1435
|
+
b.i32(c).simd(Simd.i8x16Splat).set(l);
|
|
1436
|
+
return l;
|
|
1437
|
+
});
|
|
1438
|
+
return (x) => {
|
|
1439
|
+
b.v128(Zeros);
|
|
1440
|
+
for (const l of stops) b.get(x).get(l).simd(Simd.i8x16Eq).simd(Simd.v128Or);
|
|
1441
|
+
b.simd(Simd.v128Not);
|
|
1442
|
+
};
|
|
1443
|
+
}
|
|
1444
|
+
const tables = [nibbleTable(set, 0), nibbleTable(set, 8)].map((table, k) => {
|
|
1445
|
+
const lo = b.localV128();
|
|
1446
|
+
const bits = b.localV128();
|
|
1447
|
+
b.v128(table).set(lo).v128(k === 0 ? HighNibbleBits : UpperNibbleBits).set(bits);
|
|
1448
|
+
return [lo, bits];
|
|
1449
|
+
});
|
|
1450
|
+
return (x) => {
|
|
1451
|
+
tables.forEach(([lo, bits], k) => {
|
|
1452
|
+
b.get(lo).get(x).v128(LowNibbles).simd(Simd.v128And);
|
|
1453
|
+
nibbleSwizzle(b);
|
|
1454
|
+
b.get(bits).get(x).i32(4).simd(Simd.i8x16ShrU);
|
|
1455
|
+
nibbleSwizzle(b);
|
|
1456
|
+
b.simd(Simd.v128And);
|
|
1457
|
+
if (k > 0) b.simd(Simd.v128Or);
|
|
1458
|
+
});
|
|
1459
|
+
b.v128(Zeros).simd(Simd.i8x16Ne);
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
/** One code point of a `takeWhile`: advances past it, or branches to `stop`. */
|
|
1463
|
+
whileStep(enc, b, set, w, wide, stop) {
|
|
1464
|
+
const r = Option.map(Option.fromUndefinedOr(enc), (e) => this.reader(e));
|
|
1465
|
+
if (Option.isSome(r) && r.value.decode >= 0) {
|
|
1466
|
+
const reader = r.value;
|
|
1467
|
+
this.fastUnit(reader, b, set.c);
|
|
1468
|
+
b.if(() => {
|
|
1469
|
+
this.member(set.cs, set.negate, set.c, b, reader.fast);
|
|
1470
|
+
b.op(Op.i32Eqz).brIf(stop);
|
|
1471
|
+
this.advance(b, reader.width);
|
|
1472
|
+
}, () => {
|
|
1473
|
+
b.get(0).call(reader.decode).tee(set.c).i32(0).op(Op.i32LtS).brIf(stop);
|
|
1474
|
+
if (wide >= 0) b.i32(1).set(wide);
|
|
1475
|
+
this.member(set.cs, set.negate, set.c, b);
|
|
1476
|
+
b.op(Op.i32Eqz).brIf(stop);
|
|
1477
|
+
b.get(0).gget(G.w).op(Op.i32Add).set(0);
|
|
1478
|
+
});
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
if (enc === void 0 && this.kind === 0) {
|
|
1482
|
+
this.load(b, 0);
|
|
1483
|
+
b.tee(set.c).i32(63488).op(Op.i32And).i32(55296).op(Op.i32Ne);
|
|
1484
|
+
b.if(() => {
|
|
1485
|
+
this.member(set.cs, set.negate, set.c, b, 65536);
|
|
1486
|
+
b.op(Op.i32Eqz).brIf(stop);
|
|
1487
|
+
this.advance(b, 1);
|
|
1488
|
+
}, () => this.codePointStep(enc, b, set, w, stop));
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
this.codePointStep(enc, b, set, w, stop);
|
|
1492
|
+
}
|
|
1493
|
+
/** Reads a code point and tests it, advancing past it, or branches to `stop`. */
|
|
1494
|
+
codePointStep(enc, b, set, w, stop) {
|
|
1495
|
+
this.readCodePoint(enc, b, set.c, w, stop);
|
|
1496
|
+
this.member(set.cs, set.negate, set.c, b);
|
|
1497
|
+
b.op(Op.i32Eqz).brIf(stop);
|
|
1498
|
+
b.get(0).get(w).op(Op.i32Add).set(0);
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* `takeUntil` and `takeUntilAny` on bytes: Teddy (as in ripgrep). Sixteen
|
|
1502
|
+
* starts at a time, the first `m` bytes of every literal (up to three)
|
|
1503
|
+
* are tested at once, with nibble tables: a byte belongs to bucket k at
|
|
1504
|
+
* position j when bit k is set both in the low-nibble table at its low
|
|
1505
|
+
* nibble and in the high-nibble table at its high nibble, and a start is
|
|
1506
|
+
* a candidate when one bucket holds at every position. Each candidate is
|
|
1507
|
+
* checked against the literals in full; the last starts, fewer than
|
|
1508
|
+
* sixteen, are checked one at a time. Without SIMD, with a range, or on
|
|
1509
|
+
* other input, the search runs in JavaScript.
|
|
1510
|
+
*/
|
|
1511
|
+
takeUntil(s, node) {
|
|
1512
|
+
const literals = Predicate.isTagged(node, "TakeUntil") ? [node.literal] : node.literals;
|
|
1513
|
+
const bytes = literals.filter(Predicate.isUint8Array);
|
|
1514
|
+
const shortest = Math.min(...bytes.map((lit) => lit.length));
|
|
1515
|
+
if (!(this.kind === 1 && node.encoding === void 0 && node.min === 0 && node.max === Infinity && bytes.length === literals.length && shortest > 0 && shortest !== Infinity && bytes.length <= MaxUntil && simd())) return this.island(s.p, s.out, s.b, s.fail, "the search runs in JavaScript");
|
|
1516
|
+
const { b, fail } = s;
|
|
1517
|
+
const m = Math.min(MaxPositions, shortest);
|
|
1518
|
+
const st = b.local();
|
|
1519
|
+
const c = b.local();
|
|
1520
|
+
const mask = b.local();
|
|
1521
|
+
const acc = b.localV128();
|
|
1522
|
+
const position = this.teddyPosition(b, bytes, m, acc);
|
|
1523
|
+
b.get(0).set(st);
|
|
1524
|
+
b.block((found) => {
|
|
1525
|
+
b.block((tail) => {
|
|
1526
|
+
b.loop((again) => {
|
|
1527
|
+
this.end(b).get(0).op(Op.i32Sub).i32(15 + m).op(Op.i32LtS).brIf(tail);
|
|
1528
|
+
for (let j = 0; j < m; j++) {
|
|
1529
|
+
position(j);
|
|
1530
|
+
if (j > 0) b.simd(Simd.v128And);
|
|
1531
|
+
}
|
|
1532
|
+
b.tee(acc).simd(Simd.v128AnyTrue).op(Op.i32Eqz);
|
|
1533
|
+
b.if(() => {
|
|
1534
|
+
this.advance(b, 16);
|
|
1535
|
+
b.br(again);
|
|
1536
|
+
});
|
|
1537
|
+
b.get(acc).v128(Zeros).simd(Simd.i8x16Ne).simd(Simd.i8x16Bitmask).set(mask);
|
|
1538
|
+
b.loop((bits) => {
|
|
1539
|
+
b.get(0).get(mask).op(Op.i32Ctz).op(Op.i32Add).set(c);
|
|
1540
|
+
this.literalAt(b, bytes, c, found);
|
|
1541
|
+
b.get(mask).get(mask).i32(1).op(Op.i32Sub).op(Op.i32And).tee(mask).brIf(bits);
|
|
1542
|
+
});
|
|
1543
|
+
this.advance(b, 16);
|
|
1544
|
+
b.br(again);
|
|
1545
|
+
});
|
|
1546
|
+
});
|
|
1547
|
+
b.loop((again) => {
|
|
1548
|
+
this.end(b).get(0).op(Op.i32Sub).i32(shortest).op(Op.i32LtS);
|
|
1549
|
+
this.failIf(b, fail);
|
|
1550
|
+
b.get(0).set(c);
|
|
1551
|
+
this.literalAt(b, bytes, c, found);
|
|
1552
|
+
this.advance(b, 1);
|
|
1553
|
+
b.br(again);
|
|
1554
|
+
});
|
|
1555
|
+
});
|
|
1556
|
+
return this.sliceValue(s, void 0, st, -1);
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Code that pushes, for the sixteen starts from the cursor, what position
|
|
1560
|
+
* `j` of the literals says: with at most three distinct bytes there,
|
|
1561
|
+
* 0xFF where the byte is one of them (`i8x16.eq`); otherwise Teddy's
|
|
1562
|
+
* bucket bits, from two nibble-table swizzles. Either way a start is a
|
|
1563
|
+
* candidate where the AND over the positions is not zero. The constants
|
|
1564
|
+
* are set up here, in locals; `v` is a scratch local.
|
|
1565
|
+
*/
|
|
1566
|
+
teddyPosition(b, literals, m, v) {
|
|
1567
|
+
const distinct = Array.from({ length: m }, (_, j) => Arr.dedupe(literals.map((lit) => lit[j] ?? 0)));
|
|
1568
|
+
if (distinct.every((bytes) => bytes.length <= MaxStops)) {
|
|
1569
|
+
const splats = distinct.map((bytes) => bytes.map((byte) => {
|
|
1570
|
+
const l = b.localV128();
|
|
1571
|
+
b.i32(byte).simd(Simd.i8x16Splat).set(l);
|
|
1572
|
+
return l;
|
|
1573
|
+
}));
|
|
1574
|
+
return (j) => {
|
|
1575
|
+
b.get(0).simd(Simd.v128Load, 0, Input$1 + j).set(v);
|
|
1576
|
+
b.v128(Zeros);
|
|
1577
|
+
for (const l of splats[j] ?? []) b.get(v).get(l).simd(Simd.i8x16Eq).simd(Simd.v128Or);
|
|
1578
|
+
};
|
|
1579
|
+
}
|
|
1580
|
+
const nibbles = b.localV128();
|
|
1581
|
+
b.v128(LowNibbles).set(nibbles);
|
|
1582
|
+
const tables = teddyTables(literals, m).map(([lo, hi]) => {
|
|
1583
|
+
const l = b.localV128();
|
|
1584
|
+
const h = b.localV128();
|
|
1585
|
+
b.v128(lo).set(l).v128(hi).set(h);
|
|
1586
|
+
return [l, h];
|
|
1587
|
+
});
|
|
1588
|
+
return (j) => {
|
|
1589
|
+
const [lo, hi] = tables[j] ?? [-1, -1];
|
|
1590
|
+
b.get(0).simd(Simd.v128Load, 0, Input$1 + j).set(v);
|
|
1591
|
+
b.get(lo).get(v).get(nibbles).simd(Simd.v128And);
|
|
1592
|
+
nibbleSwizzle(b);
|
|
1593
|
+
b.get(hi).get(v).i32(4).simd(Simd.i8x16ShrU);
|
|
1594
|
+
nibbleSwizzle(b);
|
|
1595
|
+
b.simd(Simd.v128And);
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
/** Moves the cursor to `c` and branches to `found` when one of `literals` starts at `c`. */
|
|
1599
|
+
literalAt(b, literals, c, found) {
|
|
1600
|
+
for (const lit of literals) b.block((next) => {
|
|
1601
|
+
this.end(b).get(c).op(Op.i32Sub).i32(lit.length).op(Op.i32LtS).brIf(next);
|
|
1602
|
+
const bytes = Array.from(lit);
|
|
1603
|
+
for (let i = 0; i < bytes.length;) {
|
|
1604
|
+
const n = chunkSize(bytes.length - i);
|
|
1605
|
+
b.get(c);
|
|
1606
|
+
compareChunk(b, bytes.slice(i, i + n), [], Input$1 + i);
|
|
1607
|
+
b.brIf(next);
|
|
1608
|
+
i += n;
|
|
1609
|
+
}
|
|
1610
|
+
b.get(c).set(0).br(found);
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
/** The trace and builder code of a `takeWhile`'s slice `[st, p)`. */
|
|
1614
|
+
sliceValue(s, enc, st, wide) {
|
|
1615
|
+
const { b } = s;
|
|
1616
|
+
if (s.out === "") return "";
|
|
1617
|
+
if (enc === void 0 && this.kind === 1) {
|
|
1618
|
+
this.put(b, [() => b.get(st), () => b.get(0)]);
|
|
1619
|
+
return this.set(s, "I.subarray(T[i],T[i+1]);i+=2");
|
|
1620
|
+
}
|
|
1621
|
+
const start = () => {
|
|
1622
|
+
b.get(st);
|
|
1623
|
+
if (wide >= 0) b.get(wide).i32(31).op(Op.i32Shl).op(Op.i32Or);
|
|
1624
|
+
};
|
|
1625
|
+
if (this.plainStrings) {
|
|
1626
|
+
this.put(b, [start, () => b.get(0)]);
|
|
1627
|
+
return this.set(s, `${this.decodeSlice(enc)};i+=2`);
|
|
1628
|
+
}
|
|
1629
|
+
this.put(b, [
|
|
1630
|
+
start,
|
|
1631
|
+
() => b.get(0),
|
|
1632
|
+
() => this.internCall(b, st),
|
|
1633
|
+
() => b.gget(G.w)
|
|
1634
|
+
]);
|
|
1635
|
+
return this.interned(s.out, this.decodeSlice(enc));
|
|
1636
|
+
}
|
|
1637
|
+
/** Pushes the cache slot of `[st, p)` (see `intern`), testing the length inline: most long slices aren't hashed. */
|
|
1638
|
+
internCall(b, st) {
|
|
1639
|
+
b.get(0).get(st).op(Op.i32Sub).i32(MaxInterned).op(Op.i32GtU);
|
|
1640
|
+
b.ifI32(() => b.i32(-1), () => b.get(st).get(0).call(this.intern()));
|
|
1641
|
+
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Builder code for the string of the slice of a `takeWhile` at `T[i]`,
|
|
1644
|
+
* `T[i+1]`. A UTF-8 slice whose start is not negative is ASCII, which
|
|
1645
|
+
* `R.ascii` decodes without looking for longer sequences.
|
|
1646
|
+
*/
|
|
1647
|
+
decodeSlice(enc) {
|
|
1648
|
+
if (enc === utf8 && this.kind === 1) return `(T[i]<0?${this.const(enc.text)}(I,T[i]&2147483647,T[i+1]):R.ascii(I,T[i],T[i+1]))`;
|
|
1649
|
+
return enc !== void 0 && this.kind === 1 ? `${this.const(enc.text)}(I,T[i],T[i+1])` : "I.slice(T[i],T[i+1])";
|
|
1650
|
+
}
|
|
1651
|
+
/** While generating the pieces of an `escaped` whose single-piece strings are interned. */
|
|
1652
|
+
plainStrings = false;
|
|
1653
|
+
literal(s, node) {
|
|
1654
|
+
const { b, fail } = s;
|
|
1655
|
+
const units = this.literalUnits(node);
|
|
1656
|
+
if (Result.isFailure(units)) return this.island(s.p, s.out, b, fail, units.failure);
|
|
1657
|
+
const { codes, constant } = units.success;
|
|
1658
|
+
const len = codes.length;
|
|
1659
|
+
const st = b.local();
|
|
1660
|
+
b.get(0).set(st);
|
|
1661
|
+
if (len > 0) {
|
|
1662
|
+
this.end(b).get(0).op(Op.i32Sub).i32(len).op(Op.i32LtS);
|
|
1663
|
+
this.failIf(b, fail);
|
|
1664
|
+
this.compareUnits(b, codes, node.caseless, fail);
|
|
1665
|
+
this.advance(b, len);
|
|
1666
|
+
}
|
|
1667
|
+
if (s.out === "") return "";
|
|
1668
|
+
if (constant) return this.set(s, this.lit(node.literal));
|
|
1669
|
+
this.put(b, [() => b.get(st)]);
|
|
1670
|
+
return this.set(s, this.kind === 0 ? `I.slice(T[i],T[i]+${len});i++` : `I.subarray(T[i],T[i]+${len});i++`);
|
|
1671
|
+
}
|
|
1672
|
+
/**
|
|
1673
|
+
* The units a literal matches, and whether its value is the literal itself
|
|
1674
|
+
* (rather than the input it matched, as a caseless one returns), or why it
|
|
1675
|
+
* runs in JavaScript.
|
|
1676
|
+
*/
|
|
1677
|
+
/**
|
|
1678
|
+
* Fails unless the input at the cursor holds `codes` (folding ASCII
|
|
1679
|
+
* letters with `caseless`): the units' bytes compared sixteen, eight,
|
|
1680
|
+
* four, two and one at a time, with `v128`, `i64` and `i32` loads, a
|
|
1681
|
+
* caseless letter's byte ORed with 0x20 first.
|
|
1682
|
+
*/
|
|
1683
|
+
compareUnits(b, codes, caseless, fail) {
|
|
1684
|
+
const { bytes, fold } = unitBytes(codes, caseless, this.kind === 0 ? 2 : 1);
|
|
1685
|
+
const at = b.local();
|
|
1686
|
+
if (this.kind === 0) b.get(0).i32(1).op(Op.i32Shl).set(at);
|
|
1687
|
+
else b.get(0).set(at);
|
|
1688
|
+
for (let i = 0; i < bytes.length;) {
|
|
1689
|
+
const n = chunkSize(bytes.length - i);
|
|
1690
|
+
b.get(at);
|
|
1691
|
+
compareChunk(b, bytes.slice(i, i + n), fold.slice(i, i + n), Input$1 + i);
|
|
1692
|
+
this.failIf(b, fail);
|
|
1693
|
+
i += n;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
literalUnits(node) {
|
|
1697
|
+
const lit = node.literal;
|
|
1698
|
+
const enc = node.encoding;
|
|
1699
|
+
if (enc !== void 0) {
|
|
1700
|
+
if (node.caseless || !Predicate.isString(lit)) return Result.fail("caseless text literal");
|
|
1701
|
+
const encoded = enc.encode(lit);
|
|
1702
|
+
return Result.succeed({
|
|
1703
|
+
codes: Predicate.isString(encoded) ? unitsOf(encoded) : Array.from(encoded),
|
|
1704
|
+
constant: true
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
const constant = !node.caseless;
|
|
1708
|
+
if (Predicate.isString(lit)) return Result.succeed(this.kind === 0 ? {
|
|
1709
|
+
codes: unitsOf(lit),
|
|
1710
|
+
constant
|
|
1711
|
+
} : {
|
|
1712
|
+
codes: Array.from(utf8Encoder.encode(lit)),
|
|
1713
|
+
constant: false
|
|
1714
|
+
});
|
|
1715
|
+
if (lit instanceof Uint8Array && this.kind === 1) return Result.succeed({
|
|
1716
|
+
codes: Array.from(lit),
|
|
1717
|
+
constant
|
|
1718
|
+
});
|
|
1719
|
+
return Result.fail("the literal is not of the input's kind");
|
|
1720
|
+
}
|
|
1721
|
+
/** `escaped`: segments of `normal` and escapes after the control character. */
|
|
1722
|
+
escaped(s, node) {
|
|
1723
|
+
const { b } = s;
|
|
1724
|
+
const enc = node.encoding;
|
|
1725
|
+
if (!this.native(enc)) return this.island(s.p, s.out, b, s.fail, "the encoding is decoded in JavaScript");
|
|
1726
|
+
const r = Option.map(Option.fromUndefinedOr(enc), (e) => this.reader(e));
|
|
1727
|
+
if (Option.isSome(r) && !(node.control < r.value.fast)) return this.island(s.p, s.out, b, s.fail, "the control character is decoded");
|
|
1728
|
+
const pieces = !node.raw && s.out !== "";
|
|
1729
|
+
const e = {
|
|
1730
|
+
r,
|
|
1731
|
+
pieces,
|
|
1732
|
+
internsWhole: pieces && Predicate.isTagged(nodeOf(node.normal), "TakeWhile") && (enc !== void 0 || this.kind === 0),
|
|
1733
|
+
start: b.local(),
|
|
1734
|
+
seg: b.local(),
|
|
1735
|
+
ok: b.local(),
|
|
1736
|
+
ts: b.local(),
|
|
1737
|
+
count: b.local(),
|
|
1738
|
+
pieceStart: b.local(),
|
|
1739
|
+
slot: pieces ? this.slot(b) : -1,
|
|
1740
|
+
x: pieces ? this.v(s.bf) : ""
|
|
1741
|
+
};
|
|
1742
|
+
let normal = "";
|
|
1743
|
+
let escape = "";
|
|
1744
|
+
b.get(0).set(e.start);
|
|
1745
|
+
b.i32(0).set(e.count);
|
|
1746
|
+
b.block((exit) => {
|
|
1747
|
+
this.loop(b, (again) => {
|
|
1748
|
+
this.atEnd(b, 0);
|
|
1749
|
+
b.brIf(exit);
|
|
1750
|
+
normal = this.escapedNormal(s, node, e, again);
|
|
1751
|
+
if (Option.isNone(r)) {
|
|
1752
|
+
this.atEnd(b, 0);
|
|
1753
|
+
b.brIf(exit);
|
|
1754
|
+
this.load(b, 0);
|
|
1755
|
+
} else {
|
|
1756
|
+
r.value.guard(b, 0);
|
|
1757
|
+
b.op(Op.i32Eqz).brIf(exit);
|
|
1758
|
+
r.value.unit(b, 0);
|
|
1759
|
+
}
|
|
1760
|
+
b.i32(node.control).op(Op.i32Ne).brIf(exit);
|
|
1761
|
+
this.advance(b, Option.match(r, {
|
|
1762
|
+
onNone: () => 1,
|
|
1763
|
+
onSome: (x) => x.width
|
|
1764
|
+
}));
|
|
1765
|
+
if (pieces) this.put(b, [() => b.i32(1)]);
|
|
1766
|
+
escape = this.gen(node.escape, e.x, b, s.fail, s.bf, s.d);
|
|
1767
|
+
b.get(e.count).i32(1).op(Op.i32Add).set(e.count);
|
|
1768
|
+
b.br(again);
|
|
1769
|
+
});
|
|
1770
|
+
});
|
|
1771
|
+
return this.escapedValue(s, node, e, normal, escape);
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* A segment of `normal`: one that consumed input continues the loop; one
|
|
1775
|
+
* that matched nothing, or failed with `Backtrack`, is dropped (with its
|
|
1776
|
+
* trace) for the control character to be tested.
|
|
1777
|
+
*/
|
|
1778
|
+
escapedNormal(s, node, e, again) {
|
|
1779
|
+
const { b } = s;
|
|
1780
|
+
let code = "";
|
|
1781
|
+
b.get(0).set(e.seg).get(b.tp).set(e.ts).i32(0).set(e.ok);
|
|
1782
|
+
b.block((bad) => {
|
|
1783
|
+
if (e.pieces) this.put(b, [() => b.i32(0)]);
|
|
1784
|
+
const plain = this.plainStrings;
|
|
1785
|
+
this.plainStrings = e.internsWhole;
|
|
1786
|
+
code = this.gen(node.normal, e.x, b, bad, s.bf, s.d);
|
|
1787
|
+
this.plainStrings = plain;
|
|
1788
|
+
b.i32(1).set(e.ok);
|
|
1789
|
+
});
|
|
1790
|
+
b.get(e.ok);
|
|
1791
|
+
b.if(() => {
|
|
1792
|
+
b.get(0).get(e.seg).op(node.raw ? Op.i32Eq : Op.i32Ne);
|
|
1793
|
+
if (node.raw) {
|
|
1794
|
+
b.if(() => this.failWith(b, s.fail, Cut));
|
|
1795
|
+
b.br(again);
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
b.if(() => {
|
|
1799
|
+
b.get(e.count).i32(1).op(Op.i32Add).set(e.count);
|
|
1800
|
+
b.get(e.seg).set(e.pieceStart);
|
|
1801
|
+
b.br(again);
|
|
1802
|
+
});
|
|
1803
|
+
b.get(e.ts).set(b.tp);
|
|
1804
|
+
}, () => {
|
|
1805
|
+
this.unlessBacktrack(b, s.fail);
|
|
1806
|
+
b.get(e.seg).set(0).get(e.ts).set(b.tp);
|
|
1807
|
+
});
|
|
1808
|
+
return code;
|
|
1809
|
+
}
|
|
1810
|
+
/** The trace and builder code of an `escaped`'s value, from its pieces' code. */
|
|
1811
|
+
escapedValue(s, node, e, normal, escape) {
|
|
1812
|
+
const { b, out } = s;
|
|
1813
|
+
const enc = node.encoding;
|
|
1814
|
+
if (out === "") return "";
|
|
1815
|
+
if (node.raw) {
|
|
1816
|
+
this.put(b, [() => b.get(e.start), () => b.get(0)]);
|
|
1817
|
+
if (enc !== void 0 && this.kind === 1) return `${out}=${this.const(enc.text)}(I,T[i],T[i+1]);i+=2;`;
|
|
1818
|
+
return `${out}=${this.kind === 0 ? "I.slice(T[i],T[i+1])" : "I.subarray(T[i],T[i+1])"};i+=2;`;
|
|
1819
|
+
}
|
|
1820
|
+
b.get(e.slot).get(e.count).mem(Op.i32Store, 2, 0);
|
|
1821
|
+
const n = this.v(s.bf);
|
|
1822
|
+
const k = this.v(s.bf);
|
|
1823
|
+
const loop = `${out}="";for(${k}=0;${k}<${n};${k}++){if(T[i++]===0){${normal}}else{${escape}}${out}+=${e.x};}`;
|
|
1824
|
+
if (!e.internsWhole) return `${n}=T[i++];if(${n}===1&&T[i]===0){i++;${normal}${out}=${e.x}}else{${loop}}`;
|
|
1825
|
+
b.get(e.count).i32(1).op(Op.i32Eq);
|
|
1826
|
+
b.if(() => {
|
|
1827
|
+
b.get(e.slot).mem(Op.i32Load, 2, 4).op(Op.i32Eqz);
|
|
1828
|
+
b.if(() => this.put(b, [() => this.internCall(b, e.pieceStart), () => b.gget(G.w)]));
|
|
1829
|
+
});
|
|
1830
|
+
return `${n}=T[i++];if(${n}===1&&T[i]===0){i++;${this.interned(out, this.decodeSlice(enc))}}else{${loop}}`;
|
|
1831
|
+
}
|
|
1832
|
+
/** The strings of the cache slots, and the version of the slot each was decoded from. */
|
|
1833
|
+
interning = false;
|
|
1834
|
+
internedStrings = [];
|
|
1835
|
+
internedVersions = /*#__PURE__*/ new Int32Array(0);
|
|
1836
|
+
internIndex = -1;
|
|
1837
|
+
/**
|
|
1838
|
+
* Builder code for a string slice traced as start, end, slot and version
|
|
1839
|
+
* (see `intern`): the string decoded from that slot's version if there is
|
|
1840
|
+
* one, else `decode`, which is then kept for the slot.
|
|
1841
|
+
*/
|
|
1842
|
+
interned(out, decode) {
|
|
1843
|
+
if (!this.interning) {
|
|
1844
|
+
this.interning = true;
|
|
1845
|
+
this.internedStrings = new Array(StringSlots).fill("");
|
|
1846
|
+
this.internedVersions = new Int32Array(StringSlots).fill(-1);
|
|
1847
|
+
}
|
|
1848
|
+
const ks = this.const(this.internedStrings);
|
|
1849
|
+
const kv = this.const(this.internedVersions);
|
|
1850
|
+
return `${out}=T[i+2];if(${out}>=0&&${kv}[${out}]===T[i+3])${out}=${ks}[${out}];else if(${out}>=0){${kv}[${out}]=T[i+3];${out}=${ks}[${out}]=${decode}}else ${out}=${decode};i+=4;`;
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* `(start, end) -> slot`: the cache slot of the units of `[start, end)`,
|
|
1854
|
+
* or -1 if there are more than `MaxInterned`, with the slot's version in
|
|
1855
|
+
* `G.w`. A slot holds the units of the latest slice hashed to it, and its
|
|
1856
|
+
* version changes whenever they do, so every slice traced with a slot and
|
|
1857
|
+
* a version has the same units, and the same string: the builder decodes
|
|
1858
|
+
* it once per version. The cache outlives a parse, and backtracking can't
|
|
1859
|
+
* invalidate it.
|
|
1860
|
+
*/
|
|
1861
|
+
intern() {
|
|
1862
|
+
if (this.internIndex >= 0) return this.internIndex;
|
|
1863
|
+
const [index, b] = this.reserve(Ty.pair, 2);
|
|
1864
|
+
this.internIndex = index;
|
|
1865
|
+
const start = 0;
|
|
1866
|
+
const end = 1;
|
|
1867
|
+
const len = b.local();
|
|
1868
|
+
const h = b.local();
|
|
1869
|
+
const k = b.local();
|
|
1870
|
+
const addr = b.local();
|
|
1871
|
+
const ver = b.local();
|
|
1872
|
+
const width = this.kind === 0 ? 2 : 1;
|
|
1873
|
+
const unitAt = (pos) => this.kind === 0 ? (pos(), b.i32(1).op(Op.i32Shl).mem(Op.i32Load16U, 1, Input$1)) : (pos(), b.mem(Op.i32Load8U, 0, Input$1));
|
|
1874
|
+
const cached = (j) => {
|
|
1875
|
+
b.get(addr);
|
|
1876
|
+
j();
|
|
1877
|
+
if (width === 2) b.i32(1).op(Op.i32Shl);
|
|
1878
|
+
b.op(Op.i32Add);
|
|
1879
|
+
b.mem(width === 2 ? Op.i32Load16U : Op.i32Load8U, width === 2 ? 1 : 0, 8);
|
|
1880
|
+
};
|
|
1881
|
+
b.get(end).get(start).op(Op.i32Sub).tee(len).i32(MaxInterned).op(Op.i32GtU);
|
|
1882
|
+
b.if(() => b.i32(-1).op(Op.return));
|
|
1883
|
+
b.get(len).set(h);
|
|
1884
|
+
b.get(start).set(k);
|
|
1885
|
+
b.block((done) => b.loop((again) => {
|
|
1886
|
+
b.get(k).get(end).op(Op.i32GeU).brIf(done);
|
|
1887
|
+
b.get(h).i32(31).op(Op.i32Mul);
|
|
1888
|
+
unitAt(() => b.get(k));
|
|
1889
|
+
b.op(Op.i32Add).set(h);
|
|
1890
|
+
b.get(k).i32(1).op(Op.i32Add).set(k);
|
|
1891
|
+
b.br(again);
|
|
1892
|
+
}));
|
|
1893
|
+
b.get(h).get(h).i32(10).op(Op.i32ShrU).op(Op.i32Xor).i32(1023).op(Op.i32And).set(h);
|
|
1894
|
+
b.get(h).i32(SlotBytes).op(Op.i32Mul).i32(Strings).op(Op.i32Add).set(addr);
|
|
1895
|
+
b.block((differ) => {
|
|
1896
|
+
b.get(addr).mem(Op.i32Load, 2, 4).get(len).op(Op.i32Ne).brIf(differ);
|
|
1897
|
+
b.i32(0).set(k);
|
|
1898
|
+
b.block((same) => b.loop((again) => {
|
|
1899
|
+
b.get(k).get(len).op(Op.i32GeU).brIf(same);
|
|
1900
|
+
cached(() => b.get(k));
|
|
1901
|
+
unitAt(() => b.get(start).get(k).op(Op.i32Add));
|
|
1902
|
+
b.op(Op.i32Ne).brIf(differ);
|
|
1903
|
+
b.get(k).i32(1).op(Op.i32Add).set(k);
|
|
1904
|
+
b.br(again);
|
|
1905
|
+
}));
|
|
1906
|
+
b.get(addr).mem(Op.i32Load, 2, 0).gset(G.w);
|
|
1907
|
+
b.get(h).op(Op.return);
|
|
1908
|
+
});
|
|
1909
|
+
b.get(addr).mem(Op.i32Load, 2, 0).i32(1).op(Op.i32Add).set(ver);
|
|
1910
|
+
b.get(addr).get(ver).mem(Op.i32Store, 2, 0);
|
|
1911
|
+
b.get(addr).get(len).mem(Op.i32Store, 2, 4);
|
|
1912
|
+
b.i32(0).set(k);
|
|
1913
|
+
b.block((done) => b.loop((again) => {
|
|
1914
|
+
b.get(k).get(len).op(Op.i32GeU).brIf(done);
|
|
1915
|
+
b.get(addr).get(k);
|
|
1916
|
+
if (width === 2) b.i32(1).op(Op.i32Shl);
|
|
1917
|
+
b.op(Op.i32Add);
|
|
1918
|
+
unitAt(() => b.get(start).get(k).op(Op.i32Add));
|
|
1919
|
+
b.mem(width === 2 ? Op.i32Store16 : Op.i32Store8, width === 2 ? 1 : 0, 8);
|
|
1920
|
+
b.get(k).i32(1).op(Op.i32Add).set(k);
|
|
1921
|
+
b.br(again);
|
|
1922
|
+
}));
|
|
1923
|
+
b.get(ver).gset(G.w);
|
|
1924
|
+
b.get(h);
|
|
1925
|
+
return index;
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* `(pos, end, len, tp, tlim) -> p | -1`: sets up the globals of a parse
|
|
1929
|
+
* and runs the root, in one call from JavaScript (setting a global from
|
|
1930
|
+
* JavaScript costs about as much as a call).
|
|
1931
|
+
*/
|
|
1932
|
+
starter(root) {
|
|
1933
|
+
const [index, b] = this.reserve(Ty.start, 5);
|
|
1934
|
+
b.get(1).gset(G.end).get(2).gset(G.len).get(3).gset(G.tp).get(4).gset(G.tlim);
|
|
1935
|
+
b.i32(0).gset(G.nv).i32(0).gset(G.mode);
|
|
1936
|
+
b.get(0).call(root);
|
|
1937
|
+
return index;
|
|
1938
|
+
}
|
|
1939
|
+
/**
|
|
1940
|
+
* `(src, n) -> count`, for string input: widens the `n` bytes of UTF-8 at
|
|
1941
|
+
* `src`, which `TextEncoder#encodeInto` wrote, to the string's UTF-16
|
|
1942
|
+
* units at `Input`, sixteen ASCII bytes at a time with SIMD (where it is
|
|
1943
|
+
* available) and eight without. `encodeInto` writes each
|
|
1944
|
+
* lone surrogate as U+FFFD, which is one unit as well, so every unit is in
|
|
1945
|
+
* place; the units that are U+FFFD (or were a lone surrogate) are listed
|
|
1946
|
+
* as 32-bit indices from `(src + n + 3) & -4`, for JavaScript to copy from
|
|
1947
|
+
* the string. Returns their number.
|
|
1948
|
+
*/
|
|
1949
|
+
widener() {
|
|
1950
|
+
const [index, b] = this.reserve(Ty.pair, 2);
|
|
1951
|
+
const src = 0;
|
|
1952
|
+
const n = 1;
|
|
1953
|
+
const end = b.local();
|
|
1954
|
+
const k = b.local();
|
|
1955
|
+
const c = b.local();
|
|
1956
|
+
const cp = b.local();
|
|
1957
|
+
const count = b.local();
|
|
1958
|
+
const list = b.local();
|
|
1959
|
+
const x = b.localI64();
|
|
1960
|
+
const byte = (at) => b.get(src).mem(Op.i32Load8U, 0, at);
|
|
1961
|
+
const cont = (at, shift) => {
|
|
1962
|
+
byte(at);
|
|
1963
|
+
b.i32(63).op(Op.i32And);
|
|
1964
|
+
if (shift > 0) b.i32(shift).op(Op.i32Shl);
|
|
1965
|
+
b.op(Op.i32Or);
|
|
1966
|
+
};
|
|
1967
|
+
const unit = (delta, push) => {
|
|
1968
|
+
b.get(k).i32(1).op(Op.i32Shl);
|
|
1969
|
+
push();
|
|
1970
|
+
b.mem(Op.i32Store16, 1, Input$1 + 2 * delta);
|
|
1971
|
+
};
|
|
1972
|
+
const step = (bytes, units, again) => {
|
|
1973
|
+
b.get(src).i32(bytes).op(Op.i32Add).set(src);
|
|
1974
|
+
b.get(k).i32(units).op(Op.i32Add).set(k);
|
|
1975
|
+
b.br(again);
|
|
1976
|
+
};
|
|
1977
|
+
/** Spreads four bytes (the low 32 bits of `x`, shifted right by `shift`) to four 16-bit units. */
|
|
1978
|
+
const spread = (shift) => {
|
|
1979
|
+
b.get(x);
|
|
1980
|
+
if (shift > 0) b.i64(BigInt(shift)).op(Op.i64ShrU);
|
|
1981
|
+
b.i64(4294967295n).op(Op.i64And);
|
|
1982
|
+
b.tee(x).get(x).i64(16n).op(Op.i64Shl).op(Op.i64Or).i64(281470681808895n).op(Op.i64And);
|
|
1983
|
+
b.tee(x).get(x).i64(8n).op(Op.i64Shl).op(Op.i64Or).i64(71777214294589695n).op(Op.i64And);
|
|
1984
|
+
};
|
|
1985
|
+
b.get(src).get(n).op(Op.i32Add).tee(end).i32(3).op(Op.i32Add).i32(-4).op(Op.i32And).set(list);
|
|
1986
|
+
b.block((done) => b.loop((again) => {
|
|
1987
|
+
b.get(src).get(end).op(Op.i32GeU).brIf(done);
|
|
1988
|
+
if (simd()) {
|
|
1989
|
+
b.get(src).i32(16).op(Op.i32Add).get(end).op(Op.i32LeU);
|
|
1990
|
+
b.if(() => {
|
|
1991
|
+
b.get(src).simd(Simd.v128Load, 0, 0).simd(Simd.i8x16Bitmask).op(Op.i32Eqz);
|
|
1992
|
+
b.if(() => {
|
|
1993
|
+
b.get(k).i32(1).op(Op.i32Shl);
|
|
1994
|
+
b.get(src).simd(Simd.v128Load, 0, 0).simd(Simd.i16x8ExtendLowI8x16U).simd(Simd.v128Store, 0, Input$1);
|
|
1995
|
+
b.get(k).i32(1).op(Op.i32Shl);
|
|
1996
|
+
b.get(src).simd(Simd.v128Load, 0, 0).simd(Simd.i16x8ExtendHighI8x16U).simd(Simd.v128Store, 0, 131088);
|
|
1997
|
+
step(16, 16, again);
|
|
1998
|
+
});
|
|
1999
|
+
});
|
|
2000
|
+
}
|
|
2001
|
+
b.get(src).i32(8).op(Op.i32Add).get(end).op(Op.i32LeU);
|
|
2002
|
+
b.if(() => {
|
|
2003
|
+
b.get(src).mem(Op.i64Load, 0, 0).tee(x).i64(-9187201950435737472n).op(Op.i64And).op(Op.i64Eqz);
|
|
2004
|
+
b.if(() => {
|
|
2005
|
+
const word = b.localI64();
|
|
2006
|
+
b.get(x).set(word);
|
|
2007
|
+
b.get(k).i32(1).op(Op.i32Shl);
|
|
2008
|
+
spread(0);
|
|
2009
|
+
b.mem(Op.i64Store, 0, Input$1);
|
|
2010
|
+
b.get(word).set(x);
|
|
2011
|
+
b.get(k).i32(1).op(Op.i32Shl);
|
|
2012
|
+
spread(32);
|
|
2013
|
+
b.mem(Op.i64Store, 0, 131080);
|
|
2014
|
+
step(8, 8, again);
|
|
2015
|
+
});
|
|
2016
|
+
});
|
|
2017
|
+
byte(0);
|
|
2018
|
+
b.tee(c).i32(128).op(Op.i32LtU);
|
|
2019
|
+
b.if(() => {
|
|
2020
|
+
unit(0, () => b.get(c));
|
|
2021
|
+
step(1, 1, again);
|
|
2022
|
+
});
|
|
2023
|
+
b.get(c).i32(224).op(Op.i32LtU);
|
|
2024
|
+
b.if(() => {
|
|
2025
|
+
unit(0, () => {
|
|
2026
|
+
b.get(c).i32(31).op(Op.i32And).i32(6).op(Op.i32Shl);
|
|
2027
|
+
cont(1, 0);
|
|
2028
|
+
});
|
|
2029
|
+
step(2, 1, again);
|
|
2030
|
+
});
|
|
2031
|
+
b.get(c).i32(240).op(Op.i32LtU);
|
|
2032
|
+
b.if(() => {
|
|
2033
|
+
b.get(c).i32(15).op(Op.i32And).i32(12).op(Op.i32Shl);
|
|
2034
|
+
cont(1, 6);
|
|
2035
|
+
cont(2, 0);
|
|
2036
|
+
b.tee(cp).i32(65533).op(Op.i32Eq);
|
|
2037
|
+
b.if(() => {
|
|
2038
|
+
b.get(count).i32(2).op(Op.i32Shl).get(list).op(Op.i32Add).get(k).mem(Op.i32Store, 2, 0);
|
|
2039
|
+
b.get(count).i32(1).op(Op.i32Add).set(count);
|
|
2040
|
+
});
|
|
2041
|
+
unit(0, () => b.get(cp));
|
|
2042
|
+
step(3, 1, again);
|
|
2043
|
+
});
|
|
2044
|
+
b.get(c).i32(7).op(Op.i32And).i32(18).op(Op.i32Shl);
|
|
2045
|
+
cont(1, 12);
|
|
2046
|
+
cont(2, 6);
|
|
2047
|
+
cont(3, 0);
|
|
2048
|
+
b.i32(65536).op(Op.i32Sub).set(cp);
|
|
2049
|
+
unit(0, () => b.get(cp).i32(10).op(Op.i32ShrU).i32(55296).op(Op.i32Or));
|
|
2050
|
+
unit(1, () => b.get(cp).i32(1023).op(Op.i32And).i32(56320).op(Op.i32Or));
|
|
2051
|
+
step(4, 2, again);
|
|
2052
|
+
}));
|
|
2053
|
+
b.get(count);
|
|
2054
|
+
return index;
|
|
2055
|
+
}
|
|
2056
|
+
/** The decoder of a built-in encoding: `(p) -> code point | -1 | -2`, the width in `G.w`. */
|
|
2057
|
+
decoder(enc) {
|
|
2058
|
+
const known = this.decoders.get(enc);
|
|
2059
|
+
if (known !== void 0) return known;
|
|
2060
|
+
const [index, b] = this.reserve(Ty.rule);
|
|
2061
|
+
this.decoders.set(enc, index);
|
|
2062
|
+
const c = b.local();
|
|
2063
|
+
const d = b.local();
|
|
2064
|
+
const ret = (code, width) => {
|
|
2065
|
+
b.i32(width).gset(G.w);
|
|
2066
|
+
code();
|
|
2067
|
+
b.op(Op.return);
|
|
2068
|
+
};
|
|
2069
|
+
const fail = (code) => b.i32(code).op(Op.return);
|
|
2070
|
+
const at = (k) => this.load(b, 0, k);
|
|
2071
|
+
const inRange = (k) => this.end(b.get(0).i32(k).op(Op.i32Add)).op(Op.i32GeU);
|
|
2072
|
+
if (enc === string) {
|
|
2073
|
+
at(0);
|
|
2074
|
+
b.tee(c).i32(63488).op(Op.i32And).i32(55296).op(Op.i32Ne);
|
|
2075
|
+
b.if(() => ret(() => b.get(c), 1));
|
|
2076
|
+
b.get(c).i32(56319).op(Op.i32LeU);
|
|
2077
|
+
b.if(() => {
|
|
2078
|
+
inRange(1);
|
|
2079
|
+
b.if(() => fail(-2));
|
|
2080
|
+
at(1);
|
|
2081
|
+
b.tee(d).i32(64512).op(Op.i32And).i32(56320).op(Op.i32Eq);
|
|
2082
|
+
b.if(() => ret(() => pair(b, c, d), 2));
|
|
2083
|
+
});
|
|
2084
|
+
fail(-1);
|
|
2085
|
+
} else if (enc === utf16le || enc === utf16be) {
|
|
2086
|
+
const [lo, hi] = enc === utf16le ? [0, 1] : [1, 0];
|
|
2087
|
+
const unitAt = (k) => {
|
|
2088
|
+
at(k + lo);
|
|
2089
|
+
at(k + hi);
|
|
2090
|
+
b.i32(8).op(Op.i32Shl, Op.i32Or);
|
|
2091
|
+
};
|
|
2092
|
+
inRange(1);
|
|
2093
|
+
b.if(() => fail(-2));
|
|
2094
|
+
unitAt(0);
|
|
2095
|
+
b.tee(c).i32(63488).op(Op.i32And).i32(55296).op(Op.i32Ne);
|
|
2096
|
+
b.if(() => ret(() => b.get(c), 2));
|
|
2097
|
+
b.get(c).i32(56319).op(Op.i32GtU);
|
|
2098
|
+
b.if(() => fail(-1));
|
|
2099
|
+
inRange(3);
|
|
2100
|
+
b.if(() => fail(-2));
|
|
2101
|
+
unitAt(2);
|
|
2102
|
+
b.tee(d).i32(64512).op(Op.i32And).i32(56320).op(Op.i32Eq);
|
|
2103
|
+
b.if(() => ret(() => pair(b, c, d), 4));
|
|
2104
|
+
fail(-1);
|
|
2105
|
+
} else {
|
|
2106
|
+
const e = b.local();
|
|
2107
|
+
const f = b.local();
|
|
2108
|
+
const cont = (l) => b.get(l).i32(192).op(Op.i32And).i32(128).op(Op.i32Ne);
|
|
2109
|
+
/** Fails unless the second byte, in `d`, may follow the lead byte: `a` and `b` are the leads with a range of their own. */
|
|
2110
|
+
const second = (a, z) => {
|
|
2111
|
+
b.get(c).i32(a[0]).op(Op.i32Eq);
|
|
2112
|
+
b.ifI32(() => b.get(d).i32(a[1]).op(Op.i32Sub).i32(a[2]).op(Op.i32GtU), () => {
|
|
2113
|
+
b.get(c).i32(z[0]).op(Op.i32Eq);
|
|
2114
|
+
b.ifI32(() => b.get(d).i32(z[1]).op(Op.i32Sub).i32(z[2]).op(Op.i32GtU), () => cont(d));
|
|
2115
|
+
});
|
|
2116
|
+
b.if(() => fail(-1));
|
|
2117
|
+
};
|
|
2118
|
+
/** Reads the continuation byte `k` into local `l`, failing if it is missing or malformed. */
|
|
2119
|
+
const next = (k, l) => {
|
|
2120
|
+
inRange(k);
|
|
2121
|
+
b.if(() => fail(-2));
|
|
2122
|
+
at(k);
|
|
2123
|
+
b.set(l);
|
|
2124
|
+
cont(l);
|
|
2125
|
+
b.if(() => fail(-1));
|
|
2126
|
+
};
|
|
2127
|
+
at(0);
|
|
2128
|
+
b.tee(c).i32(128).op(Op.i32LtU);
|
|
2129
|
+
b.if(() => ret(() => b.get(c), 1));
|
|
2130
|
+
b.get(c).i32(194).op(Op.i32LtU);
|
|
2131
|
+
b.if(() => fail(-1));
|
|
2132
|
+
inRange(1);
|
|
2133
|
+
b.if(() => fail(-2));
|
|
2134
|
+
at(1);
|
|
2135
|
+
b.set(d);
|
|
2136
|
+
b.get(c).i32(224).op(Op.i32LtU);
|
|
2137
|
+
b.if(() => {
|
|
2138
|
+
cont(d);
|
|
2139
|
+
b.if(() => fail(-1));
|
|
2140
|
+
ret(() => {
|
|
2141
|
+
b.get(c).i32(31).op(Op.i32And).i32(6).op(Op.i32Shl);
|
|
2142
|
+
b.get(d).i32(63).op(Op.i32And).op(Op.i32Or);
|
|
2143
|
+
}, 2);
|
|
2144
|
+
});
|
|
2145
|
+
b.get(c).i32(240).op(Op.i32LtU);
|
|
2146
|
+
b.if(() => {
|
|
2147
|
+
second([
|
|
2148
|
+
224,
|
|
2149
|
+
160,
|
|
2150
|
+
31
|
|
2151
|
+
], [
|
|
2152
|
+
237,
|
|
2153
|
+
128,
|
|
2154
|
+
31
|
|
2155
|
+
]);
|
|
2156
|
+
next(2, e);
|
|
2157
|
+
ret(() => {
|
|
2158
|
+
b.get(c).i32(15).op(Op.i32And).i32(12).op(Op.i32Shl);
|
|
2159
|
+
b.get(d).i32(63).op(Op.i32And).i32(6).op(Op.i32Shl).op(Op.i32Or);
|
|
2160
|
+
b.get(e).i32(63).op(Op.i32And).op(Op.i32Or);
|
|
2161
|
+
}, 3);
|
|
2162
|
+
});
|
|
2163
|
+
b.get(c).i32(245).op(Op.i32LtU);
|
|
2164
|
+
b.if(() => {
|
|
2165
|
+
second([
|
|
2166
|
+
240,
|
|
2167
|
+
144,
|
|
2168
|
+
47
|
|
2169
|
+
], [
|
|
2170
|
+
244,
|
|
2171
|
+
128,
|
|
2172
|
+
15
|
|
2173
|
+
]);
|
|
2174
|
+
next(2, e);
|
|
2175
|
+
next(3, f);
|
|
2176
|
+
ret(() => {
|
|
2177
|
+
b.get(c).i32(7).op(Op.i32And).i32(18).op(Op.i32Shl);
|
|
2178
|
+
b.get(d).i32(63).op(Op.i32And).i32(12).op(Op.i32Shl).op(Op.i32Or);
|
|
2179
|
+
b.get(e).i32(63).op(Op.i32And).i32(6).op(Op.i32Shl).op(Op.i32Or);
|
|
2180
|
+
b.get(f).i32(63).op(Op.i32And).op(Op.i32Or);
|
|
2181
|
+
}, 4);
|
|
2182
|
+
});
|
|
2183
|
+
fail(-1);
|
|
2184
|
+
}
|
|
2185
|
+
return index;
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* `scanFloatString` / `scanFloatBytes` at `p`: returns where the number
|
|
2189
|
+
* ends, with its value in `G.f`; -1 when the text doesn't start like a
|
|
2190
|
+
* decimal number; -2 when the float parser fails there. Numbers with more
|
|
2191
|
+
* than 15 significant digits or an exponent beyond ±22 are converted by
|
|
2192
|
+
* the JavaScript scan, so the value is always `Number`'s.
|
|
2193
|
+
*/
|
|
2194
|
+
scanFloat(enc) {
|
|
2195
|
+
const known = enc === void 0 ? this.tokenFloatScanner : this.floatScanners.get(enc) ?? -1;
|
|
2196
|
+
if (known >= 0) return known;
|
|
2197
|
+
const [index, b] = this.reserve(Ty.rule);
|
|
2198
|
+
if (enc === void 0) this.tokenFloatScanner = index;
|
|
2199
|
+
else this.floatScanners.set(enc, index);
|
|
2200
|
+
const r = enc === void 0 ? void 0 : this.reader(enc);
|
|
2201
|
+
const width = r?.width ?? 1;
|
|
2202
|
+
const unit = (k) => r === void 0 ? this.load(b, k) : r.unit(b, k);
|
|
2203
|
+
const i = 0;
|
|
2204
|
+
const c = b.local();
|
|
2205
|
+
const digits = b.local();
|
|
2206
|
+
const e10 = b.local();
|
|
2207
|
+
const e = b.local();
|
|
2208
|
+
const j = b.local();
|
|
2209
|
+
const neg = b.local();
|
|
2210
|
+
const start = b.local();
|
|
2211
|
+
const high = b.localF64();
|
|
2212
|
+
const low = b.localF64();
|
|
2213
|
+
const v = b.localF64();
|
|
2214
|
+
const isDigit = () => b.get(c).i32(48).op(Op.i32Sub).i32(9).op(Op.i32LeU);
|
|
2215
|
+
/** `c = unit(k)`, or -1 if there is no whole unit at `k` */
|
|
2216
|
+
const read = (k) => {
|
|
2217
|
+
if (r === void 0) this.end(b.get(k)).op(Op.i32LtU);
|
|
2218
|
+
else r.guard(b, k);
|
|
2219
|
+
b.ifI32(() => unit(k), () => b.i32(-1));
|
|
2220
|
+
b.set(c);
|
|
2221
|
+
};
|
|
2222
|
+
const adv = (l) => b.get(l).i32(width).op(Op.i32Add).set(l);
|
|
2223
|
+
const inc = (l) => b.get(l).i32(1).op(Op.i32Add).set(l);
|
|
2224
|
+
const digit = (frac) => {
|
|
2225
|
+
b.get(digits).i32(15).op(Op.i32LtU);
|
|
2226
|
+
b.if(() => {
|
|
2227
|
+
b.get(c).i32(48).op(Op.i32Ne).get(high).f64(0).op(Op.f64Ne).op(Op.i32Or);
|
|
2228
|
+
b.if(() => {
|
|
2229
|
+
b.get(high).f64(10).op(Op.f64Mul).get(c).i32(48).op(Op.i32Sub).op(Op.f64ConvertI32U).op(Op.f64Add);
|
|
2230
|
+
b.set(high);
|
|
2231
|
+
inc(digits);
|
|
2232
|
+
});
|
|
2233
|
+
if (frac) b.get(e10).i32(1).op(Op.i32Sub).set(e10);
|
|
2234
|
+
}, () => {
|
|
2235
|
+
const addLow = () => b.get(low).f64(10).op(Op.f64Mul).get(c).i32(48).op(Op.i32Sub).op(Op.f64ConvertI32U).op(Op.f64Add).set(low);
|
|
2236
|
+
b.get(digits).i32(19).op(Op.i32LtU);
|
|
2237
|
+
if (frac) b.if(() => {
|
|
2238
|
+
addLow();
|
|
2239
|
+
b.get(e10).i32(1).op(Op.i32Sub).set(e10);
|
|
2240
|
+
});
|
|
2241
|
+
else b.if(addLow, () => inc(e10));
|
|
2242
|
+
inc(digits);
|
|
2243
|
+
});
|
|
2244
|
+
};
|
|
2245
|
+
b.get(i).set(start);
|
|
2246
|
+
read(i);
|
|
2247
|
+
b.get(c).i32(45).op(Op.i32Eq).get(c).i32(43).op(Op.i32Eq).op(Op.i32Or);
|
|
2248
|
+
b.if(() => {
|
|
2249
|
+
b.get(i).i32(width).op(Op.i32Add).set(j);
|
|
2250
|
+
read(j);
|
|
2251
|
+
isDigit();
|
|
2252
|
+
b.get(c).i32(46).op(Op.i32Eq).op(Op.i32Or).op(Op.i32Eqz);
|
|
2253
|
+
b.if(() => b.i32(-1).op(Op.return));
|
|
2254
|
+
adv(i);
|
|
2255
|
+
}, () => {
|
|
2256
|
+
isDigit();
|
|
2257
|
+
b.get(c).i32(46).op(Op.i32Eq).op(Op.i32Or).op(Op.i32Eqz);
|
|
2258
|
+
b.if(() => b.i32(-1).op(Op.return));
|
|
2259
|
+
});
|
|
2260
|
+
b.get(c).i32(46).op(Op.i32Ne);
|
|
2261
|
+
b.if(() => {
|
|
2262
|
+
b.loop((more) => {
|
|
2263
|
+
digit(false);
|
|
2264
|
+
adv(i);
|
|
2265
|
+
read(i);
|
|
2266
|
+
isDigit();
|
|
2267
|
+
b.brIf(more);
|
|
2268
|
+
});
|
|
2269
|
+
b.get(c).i32(46).op(Op.i32Eq);
|
|
2270
|
+
b.if(() => {
|
|
2271
|
+
adv(i);
|
|
2272
|
+
read(i);
|
|
2273
|
+
});
|
|
2274
|
+
}, () => {
|
|
2275
|
+
adv(i);
|
|
2276
|
+
read(i);
|
|
2277
|
+
isDigit();
|
|
2278
|
+
b.op(Op.i32Eqz);
|
|
2279
|
+
b.if(() => b.i32(-2).op(Op.return));
|
|
2280
|
+
});
|
|
2281
|
+
b.block((done) => b.loop((more) => {
|
|
2282
|
+
isDigit();
|
|
2283
|
+
b.op(Op.i32Eqz).brIf(done);
|
|
2284
|
+
digit(true);
|
|
2285
|
+
adv(i);
|
|
2286
|
+
read(i);
|
|
2287
|
+
b.br(more);
|
|
2288
|
+
}));
|
|
2289
|
+
b.get(c).i32(32).op(Op.i32Or).i32(101).op(Op.i32Eq);
|
|
2290
|
+
b.if(() => {
|
|
2291
|
+
b.get(i).i32(width).op(Op.i32Add).set(j);
|
|
2292
|
+
read(j);
|
|
2293
|
+
b.get(c).i32(45).op(Op.i32Eq).set(neg);
|
|
2294
|
+
b.get(neg).get(c).i32(43).op(Op.i32Eq).op(Op.i32Or);
|
|
2295
|
+
b.if(() => {
|
|
2296
|
+
adv(j);
|
|
2297
|
+
read(j);
|
|
2298
|
+
});
|
|
2299
|
+
isDigit();
|
|
2300
|
+
b.if(() => {
|
|
2301
|
+
b.i32(0).set(e);
|
|
2302
|
+
b.loop((more) => {
|
|
2303
|
+
b.get(e).i32(1e6).op(Op.i32LtS);
|
|
2304
|
+
b.if(() => b.get(e).i32(10).op(Op.i32Mul).get(c).i32(48).op(Op.i32Sub).op(Op.i32Add).set(e));
|
|
2305
|
+
adv(j);
|
|
2306
|
+
read(j);
|
|
2307
|
+
isDigit();
|
|
2308
|
+
b.brIf(more);
|
|
2309
|
+
});
|
|
2310
|
+
b.get(e10).i32(0).get(e).op(Op.i32Sub).get(e).get(neg).op(Op.select).op(Op.i32Add).set(e10);
|
|
2311
|
+
b.get(j).set(i);
|
|
2312
|
+
});
|
|
2313
|
+
});
|
|
2314
|
+
unit(start);
|
|
2315
|
+
b.i32(45).op(Op.i32Eq).set(neg);
|
|
2316
|
+
b.get(high).f64(0).op(Op.f64Eq);
|
|
2317
|
+
b.if(() => {
|
|
2318
|
+
b.f64(-0).f64(0).get(neg).op(Op.select).gset(G.f);
|
|
2319
|
+
b.get(i).op(Op.return);
|
|
2320
|
+
});
|
|
2321
|
+
/** `exact[k]`, for `k` in local `l` (or `-l` when `negate`). */
|
|
2322
|
+
const pow = (l, negate = false) => {
|
|
2323
|
+
if (negate) b.i32(0).get(l).op(Op.i32Sub);
|
|
2324
|
+
else b.get(l);
|
|
2325
|
+
b.i32(3).op(Op.i32Shl).mem(Op.f64Load, 3, Pow10);
|
|
2326
|
+
};
|
|
2327
|
+
const [ph, pl, t, ah, al, bh, bl, wh, wl, xh, xl, pp, err] = Array.from({ length: 13 }, () => b.localF64());
|
|
2328
|
+
const lowDigits = b.local();
|
|
2329
|
+
/** `twoProduct(a, b)`: `ph + pl` is exactly `a * b`. */
|
|
2330
|
+
const twoProduct = (x, y) => {
|
|
2331
|
+
x();
|
|
2332
|
+
y();
|
|
2333
|
+
b.op(Op.f64Mul).set(ph);
|
|
2334
|
+
b.f64(134217729);
|
|
2335
|
+
x();
|
|
2336
|
+
b.op(Op.f64Mul).set(t);
|
|
2337
|
+
b.get(t).get(t);
|
|
2338
|
+
x();
|
|
2339
|
+
b.op(Op.f64Sub).op(Op.f64Sub).set(ah);
|
|
2340
|
+
x();
|
|
2341
|
+
b.get(ah).op(Op.f64Sub).set(al);
|
|
2342
|
+
b.f64(134217729);
|
|
2343
|
+
y();
|
|
2344
|
+
b.op(Op.f64Mul).set(t);
|
|
2345
|
+
b.get(t).get(t);
|
|
2346
|
+
y();
|
|
2347
|
+
b.op(Op.f64Sub).op(Op.f64Sub).set(bh);
|
|
2348
|
+
y();
|
|
2349
|
+
b.get(bh).op(Op.f64Sub).set(bl);
|
|
2350
|
+
b.get(ah).get(bh).op(Op.f64Mul).get(ph).op(Op.f64Sub);
|
|
2351
|
+
b.get(ah).get(bl).op(Op.f64Mul).op(Op.f64Add);
|
|
2352
|
+
b.get(al).get(bh).op(Op.f64Mul).op(Op.f64Add);
|
|
2353
|
+
b.get(al).get(bl).op(Op.f64Mul).op(Op.f64Add).set(pl);
|
|
2354
|
+
};
|
|
2355
|
+
b.block((slow) => {
|
|
2356
|
+
b.get(digits).i32(19).op(Op.i32GtU).brIf(slow);
|
|
2357
|
+
b.get(e10).i32(22).op(Op.i32Add).i32(44).op(Op.i32GtU).brIf(slow);
|
|
2358
|
+
b.get(digits).i32(15).op(Op.i32LeU);
|
|
2359
|
+
b.if(() => {
|
|
2360
|
+
b.get(e10).i32(0).op(Op.i32Eq);
|
|
2361
|
+
b.if(() => b.get(high).set(v), () => {
|
|
2362
|
+
b.get(e10).i32(0).op(Op.i32GtS);
|
|
2363
|
+
b.if(() => {
|
|
2364
|
+
b.get(high);
|
|
2365
|
+
pow(e10);
|
|
2366
|
+
b.op(Op.f64Mul).set(v);
|
|
2367
|
+
}, () => {
|
|
2368
|
+
b.get(high);
|
|
2369
|
+
pow(e10, true);
|
|
2370
|
+
b.op(Op.f64Div).set(v);
|
|
2371
|
+
});
|
|
2372
|
+
});
|
|
2373
|
+
}, () => {
|
|
2374
|
+
b.get(digits).i32(15).op(Op.i32Sub).set(lowDigits);
|
|
2375
|
+
twoProduct(() => b.get(high), () => pow(lowDigits));
|
|
2376
|
+
b.get(ph).get(low).op(Op.f64Add).set(wh);
|
|
2377
|
+
b.get(pl).get(low).get(wh).get(ph).op(Op.f64Sub).op(Op.f64Sub).op(Op.f64Add).set(wl);
|
|
2378
|
+
b.get(e10).i32(0).op(Op.i32GeS);
|
|
2379
|
+
b.if(() => {
|
|
2380
|
+
pow(e10);
|
|
2381
|
+
b.set(pp);
|
|
2382
|
+
twoProduct(() => b.get(wh), () => b.get(pp));
|
|
2383
|
+
b.get(ph).set(xh);
|
|
2384
|
+
b.get(pl).get(wl).get(pp).op(Op.f64Mul).op(Op.f64Add).set(xl);
|
|
2385
|
+
}, () => {
|
|
2386
|
+
pow(e10, true);
|
|
2387
|
+
b.set(pp);
|
|
2388
|
+
b.get(wh).get(pp).op(Op.f64Div).set(xh);
|
|
2389
|
+
twoProduct(() => b.get(xh), () => b.get(pp));
|
|
2390
|
+
b.get(wh).get(ph).op(Op.f64Sub).get(pl).op(Op.f64Sub).get(wl).op(Op.f64Add).get(pp).op(Op.f64Div).set(xl);
|
|
2391
|
+
});
|
|
2392
|
+
b.get(xh).get(xl).op(Op.f64Add).set(v);
|
|
2393
|
+
b.get(xh).op(Op.f64Abs).f64(1 / 2 ** 96).op(Op.f64Mul).set(err);
|
|
2394
|
+
b.get(xh).get(xl).get(err).op(Op.f64Add).op(Op.f64Add).get(v).op(Op.f64Ne).brIf(slow);
|
|
2395
|
+
b.get(xh).get(xl).get(err).op(Op.f64Sub).op(Op.f64Add).get(v).op(Op.f64Ne).brIf(slow);
|
|
2396
|
+
});
|
|
2397
|
+
b.get(v).op(Op.f64Neg).get(v).get(neg).op(Op.select).gset(G.f);
|
|
2398
|
+
b.get(i).op(Op.return);
|
|
2399
|
+
});
|
|
2400
|
+
b.get(start).get(i).call(Imp.float).gset(G.f);
|
|
2401
|
+
b.get(i);
|
|
2402
|
+
return index;
|
|
2403
|
+
}
|
|
2404
|
+
};
|
|
2405
|
+
const unitsOf = (s) => Array.from({ length: s.length }, (_, i) => s.charCodeAt(i));
|
|
2406
|
+
const utf8Encoder = /*#__PURE__*/ new TextEncoder();
|
|
2407
|
+
/** The powers of ten that doubles represent exactly, as stored at `Pow10`. */
|
|
2408
|
+
const powersOfTen = /*#__PURE__*/ new Uint8Array((/*#__PURE__*/ new Float64Array(/*#__PURE__*/ Array.from({ length: 23 }, (_, k) => 10 ** k))).buffer);
|
|
2409
|
+
/** Runtime helpers of the builder (`R`). */
|
|
2410
|
+
const fromCharCode = String.fromCharCode;
|
|
2411
|
+
/**
|
|
2412
|
+
* The text of ASCII bytes. Short ones are built from their codes, about a
|
|
2413
|
+
* third faster than `utf8.text`, which tests every byte for a longer
|
|
2414
|
+
* sequence; long ones are decoded by `TextDecoder`.
|
|
2415
|
+
*/
|
|
2416
|
+
const ascii = (b, start, end) => {
|
|
2417
|
+
const n = end - start;
|
|
2418
|
+
if (n > 24) return utf8.text(b, start, end);
|
|
2419
|
+
if (n <= 4) {
|
|
2420
|
+
if (n === 0) return "";
|
|
2421
|
+
if (n === 1) return charOf(b[start]);
|
|
2422
|
+
if (n === 2) return fromCharCode(b[start], b[start + 1]);
|
|
2423
|
+
if (n === 3) return fromCharCode(b[start], b[start + 1], b[start + 2]);
|
|
2424
|
+
return fromCharCode(b[start], b[start + 1], b[start + 2], b[start + 3]);
|
|
2425
|
+
}
|
|
2426
|
+
let s = fromCharCode(b[start], b[start + 1], b[start + 2], b[start + 3]);
|
|
2427
|
+
for (let i = start + 4; i < end; i++) s += charOf(b[i]);
|
|
2428
|
+
return s;
|
|
2429
|
+
};
|
|
2430
|
+
const runtime = {
|
|
2431
|
+
some: Option.some,
|
|
2432
|
+
none: Option.none,
|
|
2433
|
+
charOf,
|
|
2434
|
+
isPlainKey,
|
|
2435
|
+
addEntry,
|
|
2436
|
+
flushEntries,
|
|
2437
|
+
ascii
|
|
2438
|
+
};
|
|
2439
|
+
/** The state outside a parse: islands only run during one. */
|
|
2440
|
+
const idle = /*#__PURE__*/ new State("");
|
|
2441
|
+
const compileWasm = (root, kind) => {
|
|
2442
|
+
const c = new WasmCompiler(kind);
|
|
2443
|
+
c.countRefs(root);
|
|
2444
|
+
const rootIndex = c.rule(root, true);
|
|
2445
|
+
c.flush();
|
|
2446
|
+
const widen = kind === 0 ? c.widener() : -1;
|
|
2447
|
+
const start = c.starter(rootIndex);
|
|
2448
|
+
if (c.functions.some(([, body]) => body.invalid)) return Result.fail("a branch to a label out of scope");
|
|
2449
|
+
const globals = [
|
|
2450
|
+
{
|
|
2451
|
+
type: I32,
|
|
2452
|
+
init: 0
|
|
2453
|
+
},
|
|
2454
|
+
{
|
|
2455
|
+
type: I32,
|
|
2456
|
+
init: 0
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
type: I32,
|
|
2460
|
+
init: 0
|
|
2461
|
+
},
|
|
2462
|
+
{
|
|
2463
|
+
type: I32,
|
|
2464
|
+
init: 0
|
|
2465
|
+
},
|
|
2466
|
+
{
|
|
2467
|
+
type: I32,
|
|
2468
|
+
init: 0
|
|
2469
|
+
},
|
|
2470
|
+
{
|
|
2471
|
+
type: I32,
|
|
2472
|
+
init: 0
|
|
2473
|
+
},
|
|
2474
|
+
{
|
|
2475
|
+
type: I32,
|
|
2476
|
+
init: 0
|
|
2477
|
+
},
|
|
2478
|
+
{
|
|
2479
|
+
type: F64,
|
|
2480
|
+
init: 0
|
|
2481
|
+
}
|
|
2482
|
+
];
|
|
2483
|
+
const tables = new Uint8Array(c.tables.length * TableSize);
|
|
2484
|
+
c.tables.forEach((t, i) => tables.set(t, i * TableSize));
|
|
2485
|
+
const bytes = encodeModule({
|
|
2486
|
+
types: [
|
|
2487
|
+
{
|
|
2488
|
+
params: [I32],
|
|
2489
|
+
results: [I32]
|
|
2490
|
+
},
|
|
2491
|
+
{
|
|
2492
|
+
params: [I32, I32],
|
|
2493
|
+
results: [I32]
|
|
2494
|
+
},
|
|
2495
|
+
{
|
|
2496
|
+
params: [],
|
|
2497
|
+
results: [I32]
|
|
2498
|
+
},
|
|
2499
|
+
{
|
|
2500
|
+
params: [I32, I32],
|
|
2501
|
+
results: [F64]
|
|
2502
|
+
},
|
|
2503
|
+
{
|
|
2504
|
+
params: [
|
|
2505
|
+
I32,
|
|
2506
|
+
I32,
|
|
2507
|
+
I32,
|
|
2508
|
+
I32,
|
|
2509
|
+
I32
|
|
2510
|
+
],
|
|
2511
|
+
results: [I32]
|
|
2512
|
+
}
|
|
2513
|
+
],
|
|
2514
|
+
imports: [
|
|
2515
|
+
[
|
|
2516
|
+
"env",
|
|
2517
|
+
"island",
|
|
2518
|
+
Ty.pair
|
|
2519
|
+
],
|
|
2520
|
+
[
|
|
2521
|
+
"env",
|
|
2522
|
+
"grow",
|
|
2523
|
+
Ty.grow
|
|
2524
|
+
],
|
|
2525
|
+
[
|
|
2526
|
+
"env",
|
|
2527
|
+
"test",
|
|
2528
|
+
Ty.pair
|
|
2529
|
+
],
|
|
2530
|
+
[
|
|
2531
|
+
"env",
|
|
2532
|
+
"float",
|
|
2533
|
+
Ty.toF64
|
|
2534
|
+
],
|
|
2535
|
+
[
|
|
2536
|
+
"env",
|
|
2537
|
+
"check",
|
|
2538
|
+
Ty.pair
|
|
2539
|
+
]
|
|
2540
|
+
],
|
|
2541
|
+
memoryPages: 2,
|
|
2542
|
+
functions: c.functions,
|
|
2543
|
+
globals,
|
|
2544
|
+
exports: [
|
|
2545
|
+
[
|
|
2546
|
+
"root",
|
|
2547
|
+
0,
|
|
2548
|
+
rootIndex
|
|
2549
|
+
],
|
|
2550
|
+
...widen < 0 ? [] : [[
|
|
2551
|
+
"widen",
|
|
2552
|
+
0,
|
|
2553
|
+
widen
|
|
2554
|
+
]],
|
|
2555
|
+
[
|
|
2556
|
+
"start",
|
|
2557
|
+
0,
|
|
2558
|
+
start
|
|
2559
|
+
],
|
|
2560
|
+
[
|
|
2561
|
+
"mode",
|
|
2562
|
+
3,
|
|
2563
|
+
G.mode
|
|
2564
|
+
],
|
|
2565
|
+
[
|
|
2566
|
+
"end",
|
|
2567
|
+
3,
|
|
2568
|
+
G.end
|
|
2569
|
+
],
|
|
2570
|
+
[
|
|
2571
|
+
"len",
|
|
2572
|
+
3,
|
|
2573
|
+
G.len
|
|
2574
|
+
],
|
|
2575
|
+
[
|
|
2576
|
+
"tp",
|
|
2577
|
+
3,
|
|
2578
|
+
G.tp
|
|
2579
|
+
],
|
|
2580
|
+
[
|
|
2581
|
+
"tlim",
|
|
2582
|
+
3,
|
|
2583
|
+
G.tlim
|
|
2584
|
+
],
|
|
2585
|
+
[
|
|
2586
|
+
"nv",
|
|
2587
|
+
3,
|
|
2588
|
+
G.nv
|
|
2589
|
+
]
|
|
2590
|
+
],
|
|
2591
|
+
data: [[0, tables], [Pow10, powersOfTen]]
|
|
2592
|
+
});
|
|
2593
|
+
const source = `"use strict";let T,F,I,E,V,S,N=0;${c.builders.join("")}${c.valueFns.join("")}const W=[${c.valueFns.map((_, k) => `w${k}`).join(",")}];return{bind(t,f,inp,e,vals,s){T=t;F=f;I=inp;E=e;V=vals;S=s},unbind(){T=F=I=V=S=void 0},root(i){return b${rootIndex}(i)},value(w,i){return W[w](i)}}`;
|
|
2594
|
+
const build = new globalThis.Function("C", "R", source)(c.constants, runtime);
|
|
2595
|
+
return run(root, c, bytes, kind, build, source);
|
|
2596
|
+
};
|
|
2597
|
+
/** A global of a module's exports. */
|
|
2598
|
+
const exportedGlobal = (exports, name) => {
|
|
2599
|
+
const g = exports[name];
|
|
2600
|
+
return g instanceof WebAssembly.Global ? Result.succeed(g) : Result.fail(`no global ${name}`);
|
|
2601
|
+
};
|
|
2602
|
+
const run = (root, c, bytes, kind, build, source) => {
|
|
2603
|
+
const memory = new WebAssembly.Memory({ initial: 2 });
|
|
2604
|
+
let size = memory.buffer.byteLength;
|
|
2605
|
+
let i32 = new Int32Array(memory.buffer);
|
|
2606
|
+
let f64 = new Float64Array(memory.buffer);
|
|
2607
|
+
let u8 = new Uint8Array(memory.buffer);
|
|
2608
|
+
const views = () => {
|
|
2609
|
+
i32 = new Int32Array(memory.buffer);
|
|
2610
|
+
f64 = new Float64Array(memory.buffer);
|
|
2611
|
+
u8 = new Uint8Array(memory.buffer);
|
|
2612
|
+
};
|
|
2613
|
+
/** Grows the memory to at least `bytes`, doubling it. */
|
|
2614
|
+
const reserve = (bytes) => {
|
|
2615
|
+
const have = size;
|
|
2616
|
+
if (have >= bytes) return;
|
|
2617
|
+
memory.grow(Math.ceil((Math.max(bytes, have * 2) - have) / PageSize));
|
|
2618
|
+
size = memory.buffer.byteLength;
|
|
2619
|
+
views();
|
|
2620
|
+
};
|
|
2621
|
+
const steps = c.islands.map(({ parser }) => backend$1.build(parser, kind, false).step ?? parser.step);
|
|
2622
|
+
const used = c.islands.map(({ used }) => used);
|
|
2623
|
+
const tests = c.tests;
|
|
2624
|
+
const checks = c.checks;
|
|
2625
|
+
let state = idle;
|
|
2626
|
+
const values = [];
|
|
2627
|
+
const imports = { env: {
|
|
2628
|
+
mem: memory,
|
|
2629
|
+
island: (id, pos) => {
|
|
2630
|
+
const s = state;
|
|
2631
|
+
s.pos = pos;
|
|
2632
|
+
const a = steps[id](s);
|
|
2633
|
+
if (a === Fail) return s.mode === "Cut" ? -2 : -1;
|
|
2634
|
+
if (used[id]) values.push(a);
|
|
2635
|
+
return s.pos;
|
|
2636
|
+
},
|
|
2637
|
+
grow: () => {
|
|
2638
|
+
reserve(size * 2);
|
|
2639
|
+
build.bind(i32, f64, state.input, state.end, values, state);
|
|
2640
|
+
return size - Slack;
|
|
2641
|
+
},
|
|
2642
|
+
test: (id, cp) => tests[id](cp),
|
|
2643
|
+
check: (id, from) => {
|
|
2644
|
+
const s = state;
|
|
2645
|
+
const a = checks[id](build.value(id, from >> 2));
|
|
2646
|
+
if (a === Fail) {
|
|
2647
|
+
s.mode = "Backtrack";
|
|
2648
|
+
return 0;
|
|
2649
|
+
}
|
|
2650
|
+
values.push(a);
|
|
2651
|
+
return 1;
|
|
2652
|
+
},
|
|
2653
|
+
float: (start, end) => {
|
|
2654
|
+
const input = state.input;
|
|
2655
|
+
return toFloat(Predicate.isString(input) ? input.slice(start, end) : latin1.text(input, start, end).replace(/\0/g, ""));
|
|
2656
|
+
}
|
|
2657
|
+
} };
|
|
2658
|
+
const exported = new WebAssembly.Instance(new WebAssembly.Module(bytes), imports).exports;
|
|
2659
|
+
const rootFn = exported.root;
|
|
2660
|
+
if (!Predicate.isFunction(rootFn)) return Result.fail("no root function");
|
|
2661
|
+
const widenFn = kind === 0 ? exported.widen : rootFn;
|
|
2662
|
+
if (!Predicate.isFunction(widenFn)) return Result.fail("no widen function");
|
|
2663
|
+
const startFn = exported.start;
|
|
2664
|
+
if (!Predicate.isFunction(startFn)) return Result.fail("no start function");
|
|
2665
|
+
const globals = Result.all(["mode"].map((name) => exportedGlobal(exported, name)));
|
|
2666
|
+
if (Result.isFailure(globals)) return Result.fail(globals.failure);
|
|
2667
|
+
const [mode] = globals.success;
|
|
2668
|
+
let busy = false;
|
|
2669
|
+
/** The JIT's step for the parser, built on first use. */
|
|
2670
|
+
let fallback = (s) => {
|
|
2671
|
+
fallback = backend$1.build(root, kind, false).step ?? root.step;
|
|
2672
|
+
return fallback(s);
|
|
2673
|
+
};
|
|
2674
|
+
/** Copies the input into memory, scans it and builds the value. */
|
|
2675
|
+
/**
|
|
2676
|
+
* Copies a string's UTF-16 units into memory, at `staging` for its UTF-8
|
|
2677
|
+
* (see `widener`): standard `ArrayBuffer` views and `TextEncoder`, so the
|
|
2678
|
+
* same code runs everywhere, and a memory backed by a `SharedArrayBuffer`
|
|
2679
|
+
* would take it as well.
|
|
2680
|
+
*/
|
|
2681
|
+
const copyString = (input, staging) => {
|
|
2682
|
+
const { written } = utf8Encoder.encodeInto(input, u8.subarray(staging, staging + 3 * input.length));
|
|
2683
|
+
const replaced = widenFn(staging, written);
|
|
2684
|
+
if (replaced === 0) return;
|
|
2685
|
+
const view = new DataView(memory.buffer);
|
|
2686
|
+
const list = staging + written + 3 >> 2;
|
|
2687
|
+
for (let r = 0; r < replaced; r++) {
|
|
2688
|
+
const at = i32[list + r];
|
|
2689
|
+
view.setUint16(Input$1 + 2 * at, input.charCodeAt(at), true);
|
|
2690
|
+
}
|
|
2691
|
+
};
|
|
2692
|
+
/** The string whose units are in memory, if any (`""`: none). */
|
|
2693
|
+
let copied = "";
|
|
2694
|
+
const parse = (s) => {
|
|
2695
|
+
const input = s.input;
|
|
2696
|
+
const n = input.length;
|
|
2697
|
+
const inputBytes = kind === 0 ? 2 * n : n;
|
|
2698
|
+
const traceStart = Input$1 + inputBytes + 15 & -16;
|
|
2699
|
+
reserve(traceStart + Math.max(Slack * 4, inputBytes * 4, kind === 0 ? 7 * n + 16 : 0));
|
|
2700
|
+
if (Predicate.isString(input)) {
|
|
2701
|
+
if (input !== copied) {
|
|
2702
|
+
copied = "";
|
|
2703
|
+
copyString(input, traceStart);
|
|
2704
|
+
copied = input;
|
|
2705
|
+
}
|
|
2706
|
+
} else u8.set(s.pos === 0 && s.end === n ? input : input.subarray(s.pos, s.end), Input$1 + s.pos);
|
|
2707
|
+
values.length = 0;
|
|
2708
|
+
state = s;
|
|
2709
|
+
build.bind(i32, f64, input, s.end, values, s);
|
|
2710
|
+
const r = startFn(s.pos, s.end, n, traceStart, size - Slack);
|
|
2711
|
+
if (r < 0) {
|
|
2712
|
+
s.mode = mode.value === Cut ? "Cut" : "Backtrack";
|
|
2713
|
+
return Fail;
|
|
2714
|
+
}
|
|
2715
|
+
s.pos = r;
|
|
2716
|
+
return build.root(traceStart >> 2);
|
|
2717
|
+
};
|
|
2718
|
+
/** The bytes copied by the latest parse: parsing them again from further on is a driver at work. */
|
|
2719
|
+
let lastBytes = /* @__PURE__ */ new Uint8Array(0);
|
|
2720
|
+
/** The state of the parse `parsePending` runs: `Result.try` takes a function without arguments. */
|
|
2721
|
+
let pending = idle;
|
|
2722
|
+
const parsePending = () => parse(pending);
|
|
2723
|
+
const step = (s) => {
|
|
2724
|
+
if (busy) return fallback(s);
|
|
2725
|
+
const input = s.input;
|
|
2726
|
+
if (!Predicate.isString(input)) {
|
|
2727
|
+
const again = input === lastBytes && s.pos > 0 && s.end - s.pos > LargeRest;
|
|
2728
|
+
lastBytes = input;
|
|
2729
|
+
if (again) return fallback(s);
|
|
2730
|
+
}
|
|
2731
|
+
busy = true;
|
|
2732
|
+
pending = s;
|
|
2733
|
+
const result = Result.try(parsePending);
|
|
2734
|
+
build.unbind();
|
|
2735
|
+
state = idle;
|
|
2736
|
+
pending = idle;
|
|
2737
|
+
busy = false;
|
|
2738
|
+
values.length = 0;
|
|
2739
|
+
if (Result.isFailure(result)) throw result.failure;
|
|
2740
|
+
return result.success;
|
|
2741
|
+
};
|
|
2742
|
+
return Result.succeed({
|
|
2743
|
+
backend: "wasm",
|
|
2744
|
+
step,
|
|
2745
|
+
guarded: false,
|
|
2746
|
+
fallbacks: c.fallbacks,
|
|
2747
|
+
error: void 0,
|
|
2748
|
+
source
|
|
2749
|
+
});
|
|
2750
|
+
};
|
|
2751
|
+
/**
|
|
2752
|
+
* The WebAssembly compiler as a backend. Streaming builds, array input and
|
|
2753
|
+
* environments without `WebAssembly` or `new Function` use the JIT, as does
|
|
2754
|
+
* a parser whose module fails to build.
|
|
2755
|
+
*/
|
|
2756
|
+
const backend = { build: (p, kind, resumable) => {
|
|
2757
|
+
if (resumable || kind === 2 || !supported()) return backend$1.build(p, kind, resumable);
|
|
2758
|
+
const built = Result.flatMap(Result.try(() => compileWasm(p, kind)), (r) => r);
|
|
2759
|
+
return Result.match(built, {
|
|
2760
|
+
onSuccess: (compiled) => compiled,
|
|
2761
|
+
onFailure: (error) => ({
|
|
2762
|
+
...backend$1.build(p, kind, resumable),
|
|
2763
|
+
wasmError: error
|
|
2764
|
+
})
|
|
2765
|
+
});
|
|
2766
|
+
} };
|
|
2767
|
+
export { backend };
|
|
2768
|
+
|
|
2769
|
+
//# sourceMappingURL=wasm.js.map
|