@thomasfosterau/effect-parse 0.0.0 → 0.2.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 +247 -230
- package/dist/Ascii.d.ts +32 -53
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +576 -571
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts +6 -29
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +129 -116
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts +4 -2
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +121 -123
- package/dist/Bits.js.map +1 -1
- package/dist/Compiler.d.ts +39 -0
- package/dist/Compiler.d.ts.map +1 -0
- package/dist/Compiler.js +48 -0
- package/dist/Compiler.js.map +1 -0
- package/dist/Encoding.d.ts +29 -46
- package/dist/Encoding.d.ts.map +1 -1
- package/dist/Encoding.js +76 -75
- package/dist/Encoding.js.map +1 -1
- package/dist/Expression.d.ts +15 -49
- package/dist/Expression.d.ts.map +1 -1
- package/dist/Expression.js +143 -131
- package/dist/Expression.js.map +1 -1
- package/dist/Input.d.ts +206 -177
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +317 -299
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts +44 -84
- package/dist/Nom.d.ts.map +1 -1
- package/dist/Nom.js +423 -453
- package/dist/Nom.js.map +1 -1
- package/dist/ParseError.d.ts +54 -49
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js +233 -275
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +345 -313
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +2010 -1802
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.d.ts +8 -7
- package/dist/ParserSchema.d.ts.map +1 -1
- package/dist/ParserSchema.js +49 -49
- package/dist/ParserSchema.js.map +1 -1
- package/dist/ParserStream.d.ts +26 -20
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +267 -255
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.d.ts +401 -0
- package/dist/Syntax.d.ts.map +1 -0
- package/dist/Syntax.js +717 -0
- package/dist/Syntax.js.map +1 -0
- package/dist/Text.d.ts +98 -134
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +534 -496
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +60 -42
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +331 -265
- package/dist/Token.js.map +1 -1
- package/dist/index.d.ts +16 -17
- package/dist/index.js +16 -17
- package/dist/internal/compiler/closure.d.ts +14 -16
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +1458 -1637
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/entries.d.ts +11 -11
- package/dist/internal/compiler/entries.d.ts.map +1 -1
- package/dist/internal/compiler/entries.js +36 -26
- package/dist/internal/compiler/entries.js.map +1 -1
- package/dist/internal/compiler/first.d.ts +19 -19
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +251 -227
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/jit.d.ts +6 -17
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +860 -988
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +20 -21
- package/dist/internal/compiler/registry.d.ts.map +1 -1
- package/dist/internal/compiler/registry.js +21 -41
- package/dist/internal/compiler/registry.js.map +1 -1
- package/dist/internal/compiler/sets.d.ts +9 -13
- package/dist/internal/compiler/sets.d.ts.map +1 -1
- package/dist/internal/compiler/sets.js +76 -73
- package/dist/internal/compiler/sets.js.map +1 -1
- package/dist/internal/entries.d.ts +3 -3
- package/dist/internal/entries.d.ts.map +1 -1
- package/dist/internal/entries.js +38 -30
- package/dist/internal/entries.js.map +1 -1
- package/dist/internal/node.d.ts +177 -173
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +77 -66
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/numbers.d.ts +7 -8
- package/dist/internal/numbers.d.ts.map +1 -1
- package/dist/internal/numbers.js +246 -276
- package/dist/internal/numbers.js.map +1 -1
- package/dist/internal/resume.d.ts +6 -24
- package/dist/internal/resume.d.ts.map +1 -1
- package/dist/internal/resume.js +30 -29
- package/dist/internal/resume.js.map +1 -1
- package/dist/internal/run.d.ts +15 -0
- package/dist/internal/run.d.ts.map +1 -0
- package/dist/internal/run.js +36 -0
- package/dist/internal/run.js.map +1 -0
- package/dist/internal/stack.d.ts +16 -0
- package/dist/internal/stack.d.ts.map +1 -0
- package/dist/internal/stack.js +26 -0
- package/dist/internal/stack.js.map +1 -0
- package/dist/internal/syntax.d.ts +146 -0
- package/dist/internal/syntax.d.ts.map +1 -0
- package/dist/internal/syntax.js +305 -0
- package/dist/internal/syntax.js.map +1 -0
- package/dist/internal/text.d.ts +10 -2
- package/dist/internal/text.d.ts.map +1 -1
- package/dist/internal/text.js +295 -345
- package/dist/internal/text.js.map +1 -1
- package/dist/internal/tokens.d.ts +25 -30
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +369 -363
- package/dist/internal/tokens.js.map +1 -1
- package/package.json +42 -28
- package/src/Ascii.ts +278 -187
- package/src/Binary.ts +150 -44
- package/src/Bits.ts +6 -3
- package/src/Compiler.ts +98 -0
- package/src/Expression.ts +10 -5
- package/src/Input.ts +106 -28
- package/src/Nom.ts +4 -3
- package/src/ParseError.ts +48 -51
- package/src/Parser.ts +1163 -795
- package/src/ParserStream.ts +104 -80
- package/src/Syntax.ts +900 -0
- package/src/Text.ts +272 -187
- package/src/Token.ts +180 -87
- package/src/index.ts +2 -0
- package/src/internal/compiler/closure.ts +121 -88
- package/src/internal/compiler/entries.ts +21 -8
- package/src/internal/compiler/first.ts +17 -15
- package/src/internal/compiler/jit.ts +360 -157
- package/src/internal/compiler/registry.ts +12 -33
- package/src/internal/compiler/sets.ts +17 -11
- package/src/internal/entries.ts +4 -2
- package/src/internal/node.ts +51 -37
- package/src/internal/numbers.ts +22 -20
- package/src/internal/resume.ts +8 -4
- package/src/internal/run.ts +41 -0
- package/src/internal/stack.ts +36 -0
- package/src/internal/syntax.ts +421 -0
- package/src/internal/text.ts +32 -25
- package/src/internal/tokens.ts +96 -51
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// fallow-ignore-file duplicate-export -- every compiler module exports its `backend` under the same name,
|
|
2
|
+
// which the Compiler module imports by path; no barrel re-exports both.
|
|
1
3
|
/**
|
|
2
4
|
* The JIT: emits JavaScript source for a parser's description and builds
|
|
3
5
|
* it with `new Function`.
|
|
@@ -14,6 +16,7 @@
|
|
|
14
16
|
* the interpreter for what they can't compile) on the shared state, with
|
|
15
17
|
* `state.pos` kept in sync.
|
|
16
18
|
*/
|
|
19
|
+
import * as Predicate from "effect/Predicate"
|
|
17
20
|
import * as Option from "effect/Option"
|
|
18
21
|
import type { Encoding } from "../../Encoding.js"
|
|
19
22
|
import type { State } from "../../Input.js"
|
|
@@ -22,11 +25,13 @@ import type { Node, NodeOf, ParserLike } from "../node.js"
|
|
|
22
25
|
import { addEntry, flushEntries, fromEntries, isPlainKey } from "../entries.js"
|
|
23
26
|
import { children, nodeOf } from "../node.js"
|
|
24
27
|
import { floatScan, floatTexts, scanFloatBytes, scanFloatString, toFloat } from "../numbers.js"
|
|
28
|
+
import { compileAny } from "../tokens.js"
|
|
25
29
|
import * as Resume from "../resume.js"
|
|
26
30
|
import { charOf, fastLimit, latin1, string, utf16be, utf16le, utf8, validate } from "../text.js"
|
|
27
31
|
import type { ClosureCompiler, Fallback, Step } from "./closure.js"
|
|
28
|
-
import { createClosureCompiler } from "./closure.js"
|
|
29
|
-
import {
|
|
32
|
+
import { backend as closureBackend, createClosureCompiler } from "./closure.js"
|
|
33
|
+
import { buildsEntries, pairOf } from "./entries.js"
|
|
34
|
+
import type { Backend } from "./registry.js"
|
|
30
35
|
import type { First } from "./first.js"
|
|
31
36
|
import { planAlt, Strict } from "./first.js"
|
|
32
37
|
import type { CharSet } from "./sets.js"
|
|
@@ -53,7 +58,7 @@ const runtime = {
|
|
|
53
58
|
const input = s.input
|
|
54
59
|
let i = start
|
|
55
60
|
if (s.inputKind === 0) while (i < end && (input as string).charCodeAt(i) < fast) i++
|
|
56
|
-
else while (i < end && (input as Uint8Array)[i]
|
|
61
|
+
else while (i < end && (input as Uint8Array)[i] < fast) i++
|
|
57
62
|
if (i < end && validate(enc, s, i, end) < 0) {
|
|
58
63
|
s.mode = "Backtrack"
|
|
59
64
|
return Fail
|
|
@@ -69,7 +74,7 @@ let supported = false
|
|
|
69
74
|
* Whether `new Function` works here (it doesn't under a Content Security
|
|
70
75
|
* Policy). Checked once, and again if `globalThis.Function` is replaced.
|
|
71
76
|
*/
|
|
72
|
-
|
|
77
|
+
const jitSupported = (): boolean => {
|
|
73
78
|
if (checked !== globalThis.Function) {
|
|
74
79
|
checked = globalThis.Function
|
|
75
80
|
try {
|
|
@@ -83,7 +88,7 @@ export const jitSupported = (): boolean => {
|
|
|
83
88
|
|
|
84
89
|
const BT = `$S.mode="Backtrack";`
|
|
85
90
|
|
|
86
|
-
const builtinEncodings: ReadonlySet<Encoding<any>> = new Set([string, utf8, utf16le, utf16be, latin1])
|
|
91
|
+
const builtinEncodings: ReadonlySet<Encoding<any>> = /*#__PURE__*/ new Set([string, utf8, utf16le, utf16be, latin1])
|
|
87
92
|
|
|
88
93
|
/** How many units past the cursor a single token can depend on (see the closure compiler). */
|
|
89
94
|
const Margin = 4
|
|
@@ -118,7 +123,7 @@ const isLeaf = (node: Node): boolean => {
|
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
|
|
121
|
-
const lowerAscii = (c: number): number => c >= 65 && c <= 90 ? c + 32 : c
|
|
126
|
+
const lowerAscii = (c: number): number => (c >= 65 && c <= 90 ? c + 32 : c)
|
|
122
127
|
const isLetter = (c: number): boolean => (c >= 65 && c <= 90) || (c >= 97 && c <= 122)
|
|
123
128
|
|
|
124
129
|
/** The inclusive ranges of 1s in a table. */
|
|
@@ -138,7 +143,7 @@ const runsOf = (table: Uint8Array): Array<[number, number]> => {
|
|
|
138
143
|
const rangeTest = (runs: ReadonlyArray<readonly [number, number]>, v: string): string | undefined => {
|
|
139
144
|
if (runs.length === 0) return "false"
|
|
140
145
|
if (runs.length > 4) return undefined
|
|
141
|
-
return runs.map(([a, b]) => a === b ? `${v}===${a}` : `(${v}>=${a}&&${v}<=${b})`).join("||")
|
|
146
|
+
return runs.map(([a, b]) => (a === b ? `${v}===${a}` : `(${v}>=${a}&&${v}<=${b})`)).join("||")
|
|
142
147
|
}
|
|
143
148
|
|
|
144
149
|
/** The inclusive ranges of `[0, size)` where `table[c] === value`. */
|
|
@@ -173,13 +178,24 @@ const tableTest = (table: Uint8Array, v: string, size: number): string | undefin
|
|
|
173
178
|
const acceptCost = cost(accept)
|
|
174
179
|
const rejectCost = cost(reject)
|
|
175
180
|
if (Math.min(acceptCost, rejectCost) > 6) return undefined
|
|
176
|
-
const terms =
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
const terms =
|
|
182
|
+
acceptCost <= rejectCost
|
|
183
|
+
? accept
|
|
184
|
+
.map(([a, b]) =>
|
|
185
|
+
a === b
|
|
186
|
+
? `${v}===${a}`
|
|
187
|
+
: a === 0
|
|
188
|
+
? `${v}<=${b}`
|
|
189
|
+
: b === size - 1
|
|
190
|
+
? `${v}>=${a}`
|
|
191
|
+
: `(${v}>=${a}&&${v}<=${b})`
|
|
192
|
+
)
|
|
193
|
+
.join("||")
|
|
194
|
+
: reject
|
|
195
|
+
.map(([a, b]) =>
|
|
196
|
+
a === b ? `${v}!==${a}` : a === 0 ? `${v}>${b}` : b === size - 1 ? `${v}<${a}` : `(${v}<${a}||${v}>${b})`
|
|
197
|
+
)
|
|
198
|
+
.join("&&")
|
|
183
199
|
return `(${terms})`
|
|
184
200
|
}
|
|
185
201
|
|
|
@@ -203,7 +219,11 @@ class Emitter {
|
|
|
203
219
|
private readonly closure: ClosureCompiler
|
|
204
220
|
private next = 0
|
|
205
221
|
|
|
206
|
-
constructor(
|
|
222
|
+
constructor(
|
|
223
|
+
readonly root: ParserLike,
|
|
224
|
+
readonly kind: 0 | 1 | 2,
|
|
225
|
+
readonly resumable = false
|
|
226
|
+
) {
|
|
207
227
|
this.closure = createClosureCompiler(kind, resumable)
|
|
208
228
|
this.countRefs(root)
|
|
209
229
|
}
|
|
@@ -216,8 +236,7 @@ class Emitter {
|
|
|
216
236
|
private countRefs(root: ParserLike): void {
|
|
217
237
|
const seen = new Set<ParserLike>()
|
|
218
238
|
const stack = [root]
|
|
219
|
-
|
|
220
|
-
const p = stack.pop()!
|
|
239
|
+
for (let p = stack.pop(); p !== undefined; p = stack.pop()) {
|
|
221
240
|
if (seen.has(p)) continue
|
|
222
241
|
seen.add(p)
|
|
223
242
|
for (const c of new Set(children(nodeOf(p)))) {
|
|
@@ -240,9 +259,9 @@ class Emitter {
|
|
|
240
259
|
/** A JavaScript literal for simple values, otherwise a constant. */
|
|
241
260
|
lit(value: unknown): string {
|
|
242
261
|
if (value === undefined) return "void 0"
|
|
243
|
-
if (value === null ||
|
|
244
|
-
if (
|
|
245
|
-
if (
|
|
262
|
+
if (value === null || Predicate.isBoolean(value)) return String(value)
|
|
263
|
+
if (Predicate.isNumber(value) && Number.isFinite(value) && !Object.is(value, -0)) return String(value)
|
|
264
|
+
if (Predicate.isString(value) && value.length < 64) return JSON.stringify(value)
|
|
246
265
|
return this.const(value)
|
|
247
266
|
}
|
|
248
267
|
|
|
@@ -280,7 +299,7 @@ class Emitter {
|
|
|
280
299
|
|
|
281
300
|
/** The function implementing a rule, generating it on first use. */
|
|
282
301
|
rule(p: ParserLike, used: boolean): string {
|
|
283
|
-
const rules = this.rules[used ? 1 : 0]
|
|
302
|
+
const rules = this.rules[used ? 1 : 0]
|
|
284
303
|
let name = rules.get(p)
|
|
285
304
|
if (name !== undefined) return name
|
|
286
305
|
name = `f${this.next++}`
|
|
@@ -340,9 +359,70 @@ class Emitter {
|
|
|
340
359
|
const ok = this.v(fn)
|
|
341
360
|
const t = this.v(fn)
|
|
342
361
|
const leaf = this.label()
|
|
343
|
-
return
|
|
362
|
+
return (
|
|
363
|
+
`${st}=p;${ok}=false;${leaf}:{${bad}:{${code}${ok}=true;}` +
|
|
344
364
|
`if(!$S.partial||p+${ahead}<=E){if(${ok})break ${leaf};${fail}}` +
|
|
345
365
|
`p=${st};$S.pos=p;${t}=${this.const(p.step)}($S);if(${t}===F){${fail}}p=$S.pos;${out === null ? "" : `${out}=${t};`}}`
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Token code for string and byte input, or the parser's step for input
|
|
371
|
+
* the code can't read. In a resumable build, code that read within
|
|
372
|
+
* `lookahead` units of the end of a partial input re-runs interpreted.
|
|
373
|
+
*/
|
|
374
|
+
private leaf(
|
|
375
|
+
p: ParserLike,
|
|
376
|
+
node: NodeOf<"Any" | "Literal" | "OneOf" | "TakeWhile">,
|
|
377
|
+
used: boolean,
|
|
378
|
+
out: string | null,
|
|
379
|
+
fail: string,
|
|
380
|
+
fn: Fn,
|
|
381
|
+
lookahead: number = Margin
|
|
382
|
+
): string {
|
|
383
|
+
if (!this.native(node.encoding)) return this.delegate(p, used, out, fail, fn)
|
|
384
|
+
if (!this.resumable) return this.token(node, out, fail, fn) ?? this.delegate(p, used, out, fail, fn)
|
|
385
|
+
const bad = this.label()
|
|
386
|
+
const code = this.token(node, out, `break ${bad};`, fn)
|
|
387
|
+
if (code === undefined) return this.delegate(p, used, out, fail, fn)
|
|
388
|
+
return this.partialSafe(p, code, bad, lookahead, out, fail, fn)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** Whether a token parser reads array input as such (`Token`'s, on an array). */
|
|
392
|
+
private array(enc: Encoding<any> | undefined): boolean {
|
|
393
|
+
return this.kind === 2 && enc === undefined
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Token code for array input, built by `code` with its failure code. Its
|
|
398
|
+
* set's predicate (or membership test) is called on the element, as the
|
|
399
|
+
* interpreter calls it. In a resumable build, code that read the last
|
|
400
|
+
* element of a partial input re-runs interpreted.
|
|
401
|
+
*/
|
|
402
|
+
private arrayLeaf(p: ParserLike, code: (fail: string) => string, out: string | null, fail: string, fn: Fn): string {
|
|
403
|
+
if (!this.resumable) return code(fail)
|
|
404
|
+
const bad = this.label()
|
|
405
|
+
return this.partialSafe(p, code(`break ${bad};`), bad, 1, out, fail, fn)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private arrayAny(out: string | null, fail: string): string {
|
|
409
|
+
return `if(p>=E){${BT}${fail}}${out === null ? "" : `${out}=I[p];`}p++;`
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private arrayOneOf(node: NodeOf<"OneOf">, out: string | null, fail: string): string {
|
|
413
|
+
const m = this.const(compileAny(node.set).forCode.token)
|
|
414
|
+
return `if(p>=E||${m}(I[p])===${node.negate}){${BT}${fail}}${out === null ? "" : `${out}=I[p];`}p++;`
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private arrayTakeWhile(node: NodeOf<"TakeWhile">, out: string | null, fail: string, fn: Fn): string {
|
|
418
|
+
const m = this.const(compileAny(node.set).forCode.token)
|
|
419
|
+
const st = this.v(fn)
|
|
420
|
+
const max = node.max === Infinity ? "" : `&&p-${st}<${node.max}`
|
|
421
|
+
return (
|
|
422
|
+
`${st}=p;while(p<E${max}&&${m}(I[p])!==${node.negate})p++;` +
|
|
423
|
+
(node.min > 0 ? `if(p-${st}<${node.min}){${BT}${fail}}` : "") +
|
|
424
|
+
(out === null ? "" : `${out}=I.slice(${st},p);`)
|
|
425
|
+
)
|
|
346
426
|
}
|
|
347
427
|
|
|
348
428
|
call(name: string, out: string | null, fail: string): string {
|
|
@@ -363,7 +443,7 @@ class Emitter {
|
|
|
363
443
|
|
|
364
444
|
inline(p: ParserLike, used: boolean, out: string | null, fail: string, fn: Fn, depth: number): string {
|
|
365
445
|
const node = nodeOf(p)
|
|
366
|
-
const set = (value: string) => out === null ? "" : `${out}=${value};`
|
|
446
|
+
const set = (value: string) => (out === null ? "" : `${out}=${value};`)
|
|
367
447
|
const d = depth + 1
|
|
368
448
|
switch (node._tag) {
|
|
369
449
|
case "Succeed":
|
|
@@ -373,8 +453,11 @@ class Emitter {
|
|
|
373
453
|
case "Fail":
|
|
374
454
|
return `$S.mode=${JSON.stringify(node.mode)};${fail}`
|
|
375
455
|
case "Eof":
|
|
376
|
-
return
|
|
456
|
+
return (
|
|
457
|
+
`if(p<E){${BT}${fail}}` +
|
|
458
|
+
(this.resumable ? `if($S.partial){$S.mode="Incomplete";${fail}}` : "") +
|
|
377
459
|
set("void 0")
|
|
460
|
+
)
|
|
378
461
|
case "Location":
|
|
379
462
|
return set("p")
|
|
380
463
|
case "RestLen":
|
|
@@ -382,16 +465,17 @@ class Emitter {
|
|
|
382
465
|
case "Suspend":
|
|
383
466
|
return this.call(this.rule(node.resolve(), used), out, fail)
|
|
384
467
|
case "Any":
|
|
385
|
-
|
|
468
|
+
if (this.array(node.encoding)) return this.arrayLeaf(p, (f) => this.arrayAny(out, f), out, fail, fn)
|
|
469
|
+
return this.leaf(p, node, used, out, fail, fn)
|
|
386
470
|
case "OneOf":
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
471
|
+
if (this.array(node.encoding)) return this.arrayLeaf(p, (f) => this.arrayOneOf(node, out, f), out, fail, fn)
|
|
472
|
+
return this.leaf(p, node, used, out, fail, fn)
|
|
473
|
+
case "TakeWhile":
|
|
474
|
+
if (this.array(node.encoding))
|
|
475
|
+
return this.arrayLeaf(p, (f) => this.arrayTakeWhile(node, out, f, fn), out, fail, fn)
|
|
476
|
+
return this.leaf(p, node, used, out, fail, fn)
|
|
477
|
+
case "Literal":
|
|
478
|
+
return this.leaf(p, node, used, out, fail, fn, node.literal.length * 4)
|
|
395
479
|
case "Escaped": {
|
|
396
480
|
// Streaming builds use the closure compiler's escapes, which keep checkpoints
|
|
397
481
|
if (!this.native(node.encoding) || this.resumable) return this.delegate(p, used, out, fail, fn)
|
|
@@ -404,7 +488,7 @@ class Emitter {
|
|
|
404
488
|
if (code !== undefined) return code
|
|
405
489
|
}
|
|
406
490
|
// Streaming builds keep the array, which their checkpoints hold
|
|
407
|
-
if (node.f === fromEntries && out !== null && !this.resumable &&
|
|
491
|
+
if (node.f === fromEntries && out !== null && !this.resumable && buildsEntries(node.self)) {
|
|
408
492
|
return this.entries(node.self, out, fail, fn, d)
|
|
409
493
|
}
|
|
410
494
|
const t = this.v(fn)
|
|
@@ -421,8 +505,10 @@ class Emitter {
|
|
|
421
505
|
const t = this.v(fn)
|
|
422
506
|
const r = this.v(fn)
|
|
423
507
|
const [tag, field] = node._tag === "TryMap" ? ["Success", "success"] : ["Some", "value"]
|
|
424
|
-
return
|
|
508
|
+
return (
|
|
509
|
+
this.gen(node.self, true, t, fail, fn, d) +
|
|
425
510
|
`${r}=${this.const(node.f)}(${t});if(${r}._tag!=="${tag}"){${BT}${fail}}${set(`${r}.${field}`)}`
|
|
511
|
+
)
|
|
426
512
|
}
|
|
427
513
|
case "Filter": {
|
|
428
514
|
const t = out ?? this.v(fn)
|
|
@@ -432,8 +518,10 @@ class Emitter {
|
|
|
432
518
|
const t = this.v(fn)
|
|
433
519
|
const r = out ?? this.v(fn)
|
|
434
520
|
this.closure.note(p, node, "the parser returned by the function runs interpreted")
|
|
435
|
-
return
|
|
521
|
+
return (
|
|
522
|
+
this.gen(node.self, true, t, fail, fn, d) +
|
|
436
523
|
`$S.pos=p;${r}=${this.const(node.f)}(${t}).step($S);if(${r}===F){${fail}}p=$S.pos;`
|
|
524
|
+
)
|
|
437
525
|
}
|
|
438
526
|
case "Taken": {
|
|
439
527
|
if (node.encoding !== undefined && !this.native(node.encoding)) return this.delegate(p, used, out, fail, fn)
|
|
@@ -441,7 +529,10 @@ class Emitter {
|
|
|
441
529
|
const body = `${st}=p;` + this.gen(node.self, false, null, fail, fn, d)
|
|
442
530
|
if (out === null && node.encoding === undefined) return body
|
|
443
531
|
if (node.encoding === undefined) {
|
|
444
|
-
return
|
|
532
|
+
return (
|
|
533
|
+
body +
|
|
534
|
+
set(this.kind === 0 ? `I.slice(${st},p)` : this.kind === 1 ? `I.subarray(${st},p)` : `$S.slice(${st},p)`)
|
|
535
|
+
)
|
|
445
536
|
}
|
|
446
537
|
const t = out ?? this.v(fn)
|
|
447
538
|
const enc = this.const(node.encoding)
|
|
@@ -461,14 +552,17 @@ class Emitter {
|
|
|
461
552
|
case "Mode": {
|
|
462
553
|
const ok = this.label()
|
|
463
554
|
const bad = this.label()
|
|
464
|
-
return
|
|
555
|
+
return (
|
|
556
|
+
`${ok}:{${bad}:{${this.gen(node.self, used, out, `break ${bad};`, fn, d)}break ${ok};}` +
|
|
465
557
|
`if($S.mode===${JSON.stringify(node.from)})$S.mode=${JSON.stringify(node.to)};${fail}}`
|
|
558
|
+
)
|
|
466
559
|
}
|
|
467
560
|
case "Context":
|
|
468
561
|
case "Expected":
|
|
469
562
|
case "MapError":
|
|
470
563
|
case "OrFail":
|
|
471
564
|
case "Trace":
|
|
565
|
+
case "Syntax":
|
|
472
566
|
case "RetryAfter":
|
|
473
567
|
return this.gen(node.self, used, out, fail, fn, d)
|
|
474
568
|
case "ResumeAfter": {
|
|
@@ -482,10 +576,12 @@ class Emitter {
|
|
|
482
576
|
const ok = this.label()
|
|
483
577
|
const bad = this.label()
|
|
484
578
|
const t = out === null ? null : this.v(fn)
|
|
485
|
-
const some = node._tag === "Opt" ? `R.some(${t})` : t
|
|
579
|
+
const some = node._tag === "Opt" ? `R.some(${t})` : `${t}`
|
|
486
580
|
const orElse = node._tag === "Opt" ? "R.none()" : `${this.const(node.value)}()`
|
|
487
|
-
return
|
|
581
|
+
return (
|
|
582
|
+
`${st}=p;${ok}:{${bad}:{${this.gen(node.self, used, t, `break ${bad};`, fn, d)}${set(some)}break ${ok};}` +
|
|
488
583
|
`if($S.mode!=="Backtrack"){${fail}}p=${st};${set(orElse)}}`
|
|
584
|
+
)
|
|
489
585
|
}
|
|
490
586
|
case "Peek": {
|
|
491
587
|
const st = this.v(fn)
|
|
@@ -495,26 +591,32 @@ class Emitter {
|
|
|
495
591
|
const st = this.v(fn)
|
|
496
592
|
const ok = this.label()
|
|
497
593
|
const bad = this.label()
|
|
498
|
-
return
|
|
594
|
+
return (
|
|
595
|
+
`${st}=p;${ok}:{${bad}:{${this.gen(node.self, false, null, `break ${bad};`, fn, d)}p=${st};${BT}${fail}}` +
|
|
499
596
|
`if($S.mode!=="Backtrack"){${fail}}p=${st};}${set("void 0")}`
|
|
597
|
+
)
|
|
500
598
|
}
|
|
501
599
|
case "Seq":
|
|
502
600
|
return this.seq(node, used, out, fail, fn, d)
|
|
503
601
|
case "Struct": {
|
|
504
602
|
if (out === null) return node.parsers.map((q) => this.gen(q, false, null, fail, fn, d)).join("")
|
|
505
603
|
const vars = node.parsers.map(() => this.v(fn))
|
|
506
|
-
const code = node.parsers.map((q, i) => this.gen(q, true, vars[i]
|
|
604
|
+
const code = node.parsers.map((q, i) => this.gen(q, true, vars[i], fail, fn, d)).join("")
|
|
507
605
|
if (node.keys.includes("__proto__")) {
|
|
508
606
|
return code + `${out}={};` + node.keys.map((k, i) => `${out}[${JSON.stringify(k)}]=${vars[i]};`).join("")
|
|
509
607
|
}
|
|
510
608
|
return code + `${out}={${node.keys.map((k, i) => `${JSON.stringify(k)}:${vars[i]}`).join(",")}};`
|
|
511
609
|
}
|
|
512
610
|
case "ZipWith": {
|
|
513
|
-
if (out === null)
|
|
611
|
+
if (out === null)
|
|
612
|
+
return this.gen(node.self, false, null, fail, fn, d) + this.gen(node.that, false, null, fail, fn, d)
|
|
514
613
|
const x = this.v(fn)
|
|
515
614
|
const y = this.v(fn)
|
|
516
|
-
return
|
|
615
|
+
return (
|
|
616
|
+
this.gen(node.self, true, x, fail, fn, d) +
|
|
617
|
+
this.gen(node.that, true, y, fail, fn, d) +
|
|
517
618
|
`${out}=${this.const(node.f)}(${x},${y});`
|
|
619
|
+
)
|
|
518
620
|
}
|
|
519
621
|
case "Alt":
|
|
520
622
|
return this.alt(node, used, out, fail, fn, d)
|
|
@@ -549,20 +651,22 @@ class Emitter {
|
|
|
549
651
|
const scan = this.const(floatScan)
|
|
550
652
|
// Near the end of a partial input, the parser may have to report `Incomplete`
|
|
551
653
|
const near = this.resumable ? `||$S.partial&&${scan}.read+${Margin}>E` : ""
|
|
552
|
-
return
|
|
654
|
+
return (
|
|
655
|
+
`${out}=${this.kind === 0 ? "R.scanFloatString" : "R.scanFloatBytes"}(I,p,E);` +
|
|
553
656
|
`if(${out}===void 0${near}){${this.gen(node.self, true, t, fail, fn, d)}${out}=${this.const(toFloat)}(${t})}` +
|
|
554
657
|
`else if(${out}===F){${BT}${fail}}else p=${scan}.end;`
|
|
658
|
+
)
|
|
555
659
|
}
|
|
556
660
|
|
|
557
661
|
seq(node: NodeOf<"Seq">, used: boolean, out: string | null, fail: string, fn: Fn, d: number): string {
|
|
558
662
|
const output = node.output
|
|
559
663
|
if (out === null || !used) return node.parsers.map((q) => this.gen(q, false, null, fail, fn, d)).join("")
|
|
560
|
-
if (
|
|
664
|
+
if (Predicate.isNumber(output)) {
|
|
561
665
|
return node.parsers.map((q, i) => this.gen(q, i === output, i === output ? out : null, fail, fn, d)).join("")
|
|
562
666
|
}
|
|
563
667
|
const keep = (i: number) => output === "tuple" || output.includes(i)
|
|
564
|
-
const vars = node.parsers.map((_, i) => keep(i) ? this.v(fn) : null)
|
|
565
|
-
const code = node.parsers.map((q, i) => this.gen(q, keep(i), vars[i]
|
|
668
|
+
const vars = node.parsers.map((_, i) => (keep(i) ? this.v(fn) : null))
|
|
669
|
+
const code = node.parsers.map((q, i) => this.gen(q, keep(i), vars[i], fail, fn, d)).join("")
|
|
566
670
|
return code + `${out}=[${vars.filter((v) => v !== null).join(",")}];`
|
|
567
671
|
}
|
|
568
672
|
|
|
@@ -578,7 +682,7 @@ class Emitter {
|
|
|
578
682
|
alt(node: NodeOf<"Alt">, used: boolean, out: string | null, fail: string, fn: Fn, d: number): string {
|
|
579
683
|
const parsers = node.parsers
|
|
580
684
|
if (parsers.length === 0) return `${BT}${fail}`
|
|
581
|
-
if (parsers.length === 1) return this.gen(parsers[0]
|
|
685
|
+
if (parsers.length === 1) return this.gen(parsers[0], used, out, fail, fn, d)
|
|
582
686
|
const firsts = this.kind === 2 ? undefined : parsers.map((q) => this.closure.firsts.of(q))
|
|
583
687
|
const prune = firsts !== undefined && planAlt(firsts) !== undefined
|
|
584
688
|
const st = this.v(fn)
|
|
@@ -589,75 +693,98 @@ class Emitter {
|
|
|
589
693
|
let code = `${st}=p;${prune ? `${u}=${read};` : ""}${done}:{`
|
|
590
694
|
parsers.forEach((q, i) => {
|
|
591
695
|
const bad = this.label()
|
|
592
|
-
const branch =
|
|
696
|
+
const branch =
|
|
697
|
+
`${bad}:{${this.gen(q, used, out, `break ${bad};`, fn, d)}break ${done};}` +
|
|
593
698
|
`if($S.mode!=="Backtrack"){${fail}}p=${st};`
|
|
594
699
|
const f = firsts?.[i]
|
|
595
|
-
code +=
|
|
596
|
-
|
|
597
|
-
|
|
700
|
+
code +=
|
|
701
|
+
prune && f !== undefined && f.mode === Strict
|
|
702
|
+
? `if(${this.resumable ? `${u}===-2||` : ""}${this.startTest(f, u)}){${branch}}`
|
|
703
|
+
: branch
|
|
598
704
|
})
|
|
599
705
|
return code + `${BT}${fail}}`
|
|
600
706
|
}
|
|
601
707
|
|
|
602
|
-
dispatch(
|
|
708
|
+
dispatch(
|
|
709
|
+
p: ParserLike,
|
|
710
|
+
node: NodeOf<"Dispatch">,
|
|
711
|
+
used: boolean,
|
|
712
|
+
out: string | null,
|
|
713
|
+
fail: string,
|
|
714
|
+
fn: Fn,
|
|
715
|
+
d: number
|
|
716
|
+
): string {
|
|
603
717
|
const choose = this.const(node.choose)
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
let s = cases.get(q)
|
|
608
|
-
if (s === undefined) cases.set(q, s = this.closure.compile(q, used))
|
|
609
|
-
return s
|
|
610
|
-
}
|
|
611
|
-
if (node.cases !== undefined) {
|
|
612
|
-
for (const key of Reflect.ownKeys(node.cases)) caseStep(node.cases[key as any]!)
|
|
613
|
-
if (node.otherwise !== undefined) caseStep(node.otherwise)
|
|
614
|
-
}
|
|
615
|
-
const table = this.const(cases)
|
|
616
|
-
const general = (): string => {
|
|
718
|
+
if (node.cases === undefined) {
|
|
719
|
+
// The general path: run the selector, choose, then run the case's step
|
|
720
|
+
this.closure.note(p, node, "the parser returned by the function of cases runs interpreted")
|
|
617
721
|
const st = this.v(fn)
|
|
618
722
|
const k = this.v(fn)
|
|
619
723
|
const q = this.v(fn)
|
|
620
724
|
const r = out ?? this.v(fn)
|
|
621
|
-
return
|
|
622
|
-
|
|
725
|
+
return (
|
|
726
|
+
`${st}=p;${this.gen(node.selector, true, k, fail, fn, d)}${q}=${choose}(${k});` +
|
|
727
|
+
`if(${q}===void 0){p=${st};${BT}${fail}}$S.pos=p;${r}=${q}.step($S);` +
|
|
623
728
|
`if(${r}===F){${fail}}p=$S.pos;`
|
|
729
|
+
)
|
|
624
730
|
}
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
731
|
+
// The cases are known: number them, and run the chosen one inline
|
|
732
|
+
const cases = new Map<ParserLike, number>()
|
|
733
|
+
for (const key of Reflect.ownKeys(node.cases)) {
|
|
734
|
+
const q = node.cases[key as any]
|
|
735
|
+
if (!cases.has(q)) cases.set(q, cases.size)
|
|
736
|
+
}
|
|
737
|
+
if (node.otherwise !== undefined && !cases.has(node.otherwise)) cases.set(node.otherwise, cases.size)
|
|
738
|
+
const index = this.const(cases)
|
|
739
|
+
// `inline`: the cases' code goes here; otherwise (a fallback next to the
|
|
740
|
+
// switch on units below, which inlines them already) they are called as rules
|
|
741
|
+
const general = (inline: boolean): string => {
|
|
742
|
+
const st = this.v(fn)
|
|
743
|
+
const k = this.v(fn)
|
|
744
|
+
const q = this.v(fn)
|
|
745
|
+
const sw = this.label()
|
|
746
|
+
let code =
|
|
747
|
+
`${st}=p;${this.gen(node.selector, true, k, fail, fn, d)}${q}=${choose}(${k});` +
|
|
748
|
+
`if(${q}===void 0){p=${st};${BT}${fail}}${sw}:switch(${index}.get(${q})){`
|
|
749
|
+
for (const [c, i] of cases) {
|
|
750
|
+
const body = inline ? this.gen(c, used, out, fail, fn, d) : this.call(this.rule(c, used), out, fail)
|
|
751
|
+
code += `case ${i}:{${body}break ${sw};}`
|
|
752
|
+
}
|
|
753
|
+
// Unreachable unless the record of cases changed after `dispatch` read it
|
|
754
|
+
const r = out ?? this.v(fn)
|
|
755
|
+
return code + `default:{$S.pos=p;${r}=${q}.step($S);if(${r}===F){${fail}}p=$S.pos;}}`
|
|
628
756
|
}
|
|
629
757
|
let sel = nodeOf(node.selector)
|
|
630
758
|
const peek = sel._tag === "Peek"
|
|
631
759
|
if (peek) sel = nodeOf((sel as NodeOf<"Peek">).self)
|
|
632
|
-
if (sel._tag !== "Any" || !this.native(sel.encoding)) return general()
|
|
760
|
+
if (sel._tag !== "Any" || !this.native(sel.encoding)) return general(true)
|
|
633
761
|
const enc = sel.encoding
|
|
634
762
|
const r = enc === undefined ? undefined : this.reader(enc)
|
|
635
763
|
const limit = r === undefined ? (this.kind === 0 ? 0xd800 : TableSize) : Math.min(r.fast, 0xd800)
|
|
636
764
|
const size = Math.min(limit, TableSize)
|
|
637
|
-
if (size === 0) return general()
|
|
765
|
+
if (size === 0) return general(true)
|
|
638
766
|
// Group the units by the case they select
|
|
639
767
|
const groups = new Map<ParserLike, Array<number>>()
|
|
640
768
|
for (let unit = 0; unit < size; unit++) {
|
|
641
|
-
const q =
|
|
769
|
+
const q = node.choose(enc === undefined && this.kind === 1 ? unit : charOf(unit))
|
|
642
770
|
if (q === undefined) continue
|
|
643
771
|
let g = groups.get(q)
|
|
644
|
-
if (g === undefined) groups.set(q, g = [])
|
|
772
|
+
if (g === undefined) groups.set(q, (g = []))
|
|
645
773
|
g.push(unit)
|
|
646
774
|
}
|
|
647
775
|
const u = this.v(fn)
|
|
648
776
|
const sw = this.label()
|
|
649
777
|
// A unit cut short by the end of the input takes the general path
|
|
650
|
-
const read = r === undefined
|
|
651
|
-
? `p<E?${this.unit("p")}:-1`
|
|
652
|
-
: `${r.guard("p")}?${r.unit("p")}:p<E?${size}:-1`
|
|
778
|
+
const read = r === undefined ? `p<E?${this.unit("p")}:-1` : `${r.guard("p")}?${r.unit("p")}:p<E?${size}:-1`
|
|
653
779
|
// Near the end of a partial input, the general path handles incomplete units
|
|
654
780
|
const near = this.resumable ? `$S.partial&&p+${Margin}>E?${size}:` : ""
|
|
655
781
|
let code = `${u}=${near}${read};${sw}:switch(${u}){`
|
|
656
782
|
for (const [q, units] of groups) {
|
|
657
|
-
code +=
|
|
783
|
+
code +=
|
|
784
|
+
units.map((x) => `case ${x}:`).join("") +
|
|
658
785
|
`{${peek ? "" : `p+=${r?.width ?? 1};`}${this.gen(q, used, out, fail, fn, d)}break ${sw};}`
|
|
659
786
|
}
|
|
660
|
-
code += `case -1:{${BT}${fail}}default:{if(${u}<${size}){${BT}${fail}}${general()}}}`
|
|
787
|
+
code += `case -1:{${BT}${fail}}default:{if(${u}<${size}){${BT}${fail}}${general(false)}}}`
|
|
661
788
|
return code
|
|
662
789
|
}
|
|
663
790
|
|
|
@@ -676,12 +803,15 @@ class Emitter {
|
|
|
676
803
|
const n = this.v(fn)
|
|
677
804
|
const st = this.v(fn)
|
|
678
805
|
const isFold = node._tag === "Fold"
|
|
806
|
+
const cond = isFold ? (node as NodeOf<"Fold">).while : undefined
|
|
679
807
|
const keep = out !== null && used && collect === undefined
|
|
680
|
-
|
|
681
|
-
const
|
|
808
|
+
// `while` needs the accumulator even when the output is unused
|
|
809
|
+
const tracked = keep || cond !== undefined
|
|
810
|
+
const x = tracked ? this.v(fn) : null
|
|
811
|
+
const acc = out !== null && keep ? out : this.v(fn)
|
|
682
812
|
let init = collect?.init ?? ""
|
|
683
813
|
let add = ""
|
|
684
|
-
if (
|
|
814
|
+
if (tracked) {
|
|
685
815
|
if (isFold) {
|
|
686
816
|
init = `${acc}=${this.const((node as NodeOf<"Fold">).init)}();`
|
|
687
817
|
add = `${acc}=${this.const((node as NodeOf<"Fold">).f)}(${acc},${x});`
|
|
@@ -693,14 +823,19 @@ class Emitter {
|
|
|
693
823
|
const check = max === Infinity ? `if(p===${st}){$S.mode="Cut";${fail}}` : ""
|
|
694
824
|
const bound = max === Infinity ? "" : `${n}<${max}`
|
|
695
825
|
const r = this.resumable ? this.resumePoint(fn) : undefined
|
|
696
|
-
const save =
|
|
697
|
-
|
|
698
|
-
|
|
826
|
+
const save =
|
|
827
|
+
r === undefined
|
|
828
|
+
? ""
|
|
829
|
+
: `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${acc},count:${n},pos:${st}});`
|
|
699
830
|
// With `collect` (never in resumable builds), the element adds itself once the loop check passed
|
|
700
|
-
const element =
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
831
|
+
const element =
|
|
832
|
+
collect === undefined
|
|
833
|
+
? `${this.gen(node.self, tracked, x, `break ${bad};`, fn, d)}${check}${add}`
|
|
834
|
+
: collect.element(`break ${bad};`, check)
|
|
835
|
+
const stop =
|
|
836
|
+
cond === undefined ? "" : `if(${min > 0 ? `${n}>=${min}&&` : ""}!${this.const(cond)}(${acc}))break ${loop};`
|
|
837
|
+
const body =
|
|
838
|
+
`${loop}:for(;${bound};${n}++){${stop}${st}=p;` +
|
|
704
839
|
`${bad}:{${element}continue ${loop};}` +
|
|
705
840
|
`${save}if($S.mode!=="Backtrack"${min > 0 ? `||${n}<${min}` : ""}){${fail}}p=${st};break ${loop};}`
|
|
706
841
|
if (r === undefined) return `${init}${n}=0;${body}${collect?.finish ?? ""}`
|
|
@@ -742,29 +877,37 @@ class Emitter {
|
|
|
742
877
|
const keep = out !== null && used && collect === undefined
|
|
743
878
|
if (max === 0) return collect !== undefined ? collect.init + collect.finish : keep ? `${out}=[];` : ""
|
|
744
879
|
const x = keep ? this.v(fn) : null
|
|
745
|
-
const acc = keep ? out
|
|
880
|
+
const acc = out !== null && keep ? out : this.v(fn)
|
|
746
881
|
const n = this.v(fn)
|
|
747
882
|
const st = this.v(fn)
|
|
748
883
|
const done = this.label()
|
|
749
884
|
const loop = this.label()
|
|
750
885
|
const push = keep ? `${acc}.push(${x});` : ""
|
|
751
886
|
const r = this.resumable ? this.resumePoint(fn) : undefined
|
|
752
|
-
const save =
|
|
753
|
-
|
|
754
|
-
|
|
887
|
+
const save =
|
|
888
|
+
r === undefined
|
|
889
|
+
? ""
|
|
890
|
+
: `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${acc},count:${n},pos:${st}});`
|
|
755
891
|
// Runs `body`; on its failure, stops the repetition (or fails if `fatal`)
|
|
756
892
|
const attempt = (body: (fail: string) => string, success: string, fatal: string, checkpoint: string) => {
|
|
757
893
|
const ok = this.label()
|
|
758
894
|
const bad = this.label()
|
|
759
|
-
return
|
|
895
|
+
return (
|
|
896
|
+
`${ok}:{${bad}:{${body(`break ${bad};`)}${success}break ${ok};}` +
|
|
760
897
|
`${checkpoint}if($S.mode!=="Backtrack"${fatal}){${fail}}p=${st};break ${done};}`
|
|
898
|
+
)
|
|
761
899
|
}
|
|
762
|
-
const self = (f: string) =>
|
|
900
|
+
const self = (f: string) =>
|
|
901
|
+
collect === undefined ? this.gen(node.self, keep, x, f, fn, d) : collect.element(f, "")
|
|
763
902
|
const sep = (f: string) => this.gen(node.separator, false, null, f, fn, d)
|
|
764
903
|
const check = max === Infinity ? `if(p===${st}){$S.mode="Cut";${fail}}` : ""
|
|
765
|
-
const first =
|
|
766
|
-
|
|
767
|
-
attempt(
|
|
904
|
+
const first =
|
|
905
|
+
`${collect?.init ?? (keep ? `${acc}=[];` : "")}${n}=0;${st}=p;` +
|
|
906
|
+
attempt(self, `${push}${n}++;`, min > 0 ? "||true" : "", "")
|
|
907
|
+
const rest =
|
|
908
|
+
`${loop}:while(${max === Infinity ? "true" : `${n}<${max}`}){${st}=p;` +
|
|
909
|
+
attempt(sep, "", `||${n}<${min}`, save) +
|
|
910
|
+
check +
|
|
768
911
|
attempt(self, `${push}${n}++;continue ${loop};`, `||${n}<${min}`, save) +
|
|
769
912
|
`}`
|
|
770
913
|
if (r === undefined) return `${done}:{${first}${rest}}${collect?.finish ?? ""}`
|
|
@@ -773,7 +916,7 @@ class Emitter {
|
|
|
773
916
|
}
|
|
774
917
|
|
|
775
918
|
/**
|
|
776
|
-
* `map(self, Object.fromEntries)`, where `
|
|
919
|
+
* `map(self, Object.fromEntries)`, where `buildsEntries(self)`: builds the
|
|
777
920
|
* object directly, assigning each entry as it is parsed (see
|
|
778
921
|
* `internal/entries.ts`), without the pairs and their array. `cut`,
|
|
779
922
|
* `preceded`, `terminated` and diagnostics around the repetition are
|
|
@@ -785,27 +928,44 @@ class Emitter {
|
|
|
785
928
|
case "Mode": {
|
|
786
929
|
const ok = this.label()
|
|
787
930
|
const bad = this.label()
|
|
788
|
-
return
|
|
931
|
+
return (
|
|
932
|
+
`${ok}:{${bad}:{${this.entries(node.self, out, `break ${bad};`, fn, d)}break ${ok};}` +
|
|
789
933
|
`if($S.mode===${JSON.stringify(node.from)})$S.mode=${JSON.stringify(node.to)};${fail}}`
|
|
934
|
+
)
|
|
790
935
|
}
|
|
791
936
|
case "Seq": {
|
|
792
937
|
const i = node.output as number
|
|
793
|
-
return node.parsers
|
|
794
|
-
j === i ? this.entries(q, out, fail, fn, d) : this.gen(q, false, null, fail, fn, d)
|
|
795
|
-
|
|
938
|
+
return node.parsers
|
|
939
|
+
.map((q, j) => (j === i ? this.entries(q, out, fail, fn, d) : this.gen(q, false, null, fail, fn, d)))
|
|
940
|
+
.join("")
|
|
796
941
|
}
|
|
797
942
|
case "Repeat":
|
|
798
943
|
case "Separated": {
|
|
799
|
-
const pair = pairOf(node.self)
|
|
944
|
+
const pair = pairOf(node.self)
|
|
945
|
+
if (pair === undefined) {
|
|
946
|
+
// Not reached (`buildsEntries` found the pair): build the pairs, then the object
|
|
947
|
+
const t = this.v(fn)
|
|
948
|
+
return this.gen(p, true, t, fail, fn, d) + `${out}=${this.const(fromEntries)}(${t});`
|
|
949
|
+
}
|
|
800
950
|
const deferred = this.v(fn)
|
|
801
951
|
const key = this.v(fn)
|
|
802
952
|
const value = this.v(fn)
|
|
803
953
|
const collect: Collect = {
|
|
804
954
|
init: `${out}={};${deferred}=void 0;`,
|
|
805
955
|
element: (f, check) =>
|
|
806
|
-
pair.parsers
|
|
807
|
-
|
|
808
|
-
|
|
956
|
+
pair.parsers
|
|
957
|
+
.map((q, j) =>
|
|
958
|
+
this.gen(
|
|
959
|
+
q,
|
|
960
|
+
j === pair.key || j === pair.value,
|
|
961
|
+
j === pair.key ? key : j === pair.value ? value : null,
|
|
962
|
+
f,
|
|
963
|
+
fn,
|
|
964
|
+
d
|
|
965
|
+
)
|
|
966
|
+
)
|
|
967
|
+
.join("") +
|
|
968
|
+
check +
|
|
809
969
|
`if(${deferred}===void 0&&R.isPlainKey(${key}))${out}[${key}]=${value};` +
|
|
810
970
|
`else ${deferred}=R.addEntry(${out},${deferred},${key},${value});`,
|
|
811
971
|
finish: `if(${deferred}!==void 0)R.flushEntries(${out},${deferred});`
|
|
@@ -862,7 +1022,7 @@ class Emitter {
|
|
|
862
1022
|
fail: string,
|
|
863
1023
|
fn: Fn
|
|
864
1024
|
): string | undefined {
|
|
865
|
-
const set = (value: string) => out === null ? "" : `${out}=${value};`
|
|
1025
|
+
const set = (value: string) => (out === null ? "" : `${out}=${value};`)
|
|
866
1026
|
const enc = node.encoding
|
|
867
1027
|
const kind = this.kind as 0 | 1
|
|
868
1028
|
const failBT = `{${BT}${fail}}`
|
|
@@ -871,40 +1031,47 @@ class Emitter {
|
|
|
871
1031
|
if (enc === undefined) {
|
|
872
1032
|
if (kind === 1) return `if(p>=E)${failBT}${set("I[p]")}p++;`
|
|
873
1033
|
const c = this.v(fn)
|
|
874
|
-
return
|
|
1034
|
+
return (
|
|
1035
|
+
`if(p>=E)${failBT}${c}=I.charCodeAt(p);` +
|
|
875
1036
|
`if(${c}>=55296&&${c}<=56319&&p+1<I.length&&(I.charCodeAt(p+1)&64512)===56320){${set("I.slice(p,p+2)")}p+=2}` +
|
|
876
1037
|
`else{${set("I[p]")}p++}`
|
|
1038
|
+
)
|
|
877
1039
|
}
|
|
878
1040
|
const r = this.reader(enc)
|
|
879
1041
|
const c = this.v(fn)
|
|
880
1042
|
const dec = `${this.const(enc.decode)}(I,p,E)`
|
|
881
|
-
return
|
|
882
|
-
|
|
1043
|
+
return (
|
|
1044
|
+
`if(p>=E)${failBT}` +
|
|
1045
|
+
(r.fast > 0
|
|
1046
|
+
? `if(${r.guard("p")}&&(${c}=${r.unit("p")})<${r.fast}){${set(`R.charOf(${c})`)}p+=${r.width}}else`
|
|
1047
|
+
: "") +
|
|
883
1048
|
`{${c}=${dec};if(${c}<0)${failBT}${set(`R.charOf(${c})`)}p+=${this.const(enc.width)}(${c})}`
|
|
1049
|
+
)
|
|
884
1050
|
}
|
|
885
1051
|
case "Literal": {
|
|
886
1052
|
const lit = node.literal
|
|
887
1053
|
if (enc !== undefined) {
|
|
888
1054
|
if (node.caseless) return undefined
|
|
889
1055
|
const encoded = enc.encode(lit as string)
|
|
890
|
-
return
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
) + set(this.lit(lit))
|
|
1056
|
+
return (
|
|
1057
|
+
this.literalUnits(Predicate.isString(encoded) ? unitsOf(encoded) : Array.from(encoded), false, fail) +
|
|
1058
|
+
set(this.lit(lit))
|
|
1059
|
+
)
|
|
895
1060
|
}
|
|
896
|
-
const literalKind =
|
|
1061
|
+
const literalKind = Predicate.isString(lit) ? 0 : lit instanceof Uint8Array ? 1 : 2
|
|
897
1062
|
if (kind === 0 && literalKind !== 0) return undefined
|
|
898
1063
|
if (literalKind === 2) return undefined
|
|
899
|
-
const codes =
|
|
900
|
-
|
|
901
|
-
|
|
1064
|
+
const codes =
|
|
1065
|
+
kind === 0
|
|
1066
|
+
? unitsOf(lit as string)
|
|
1067
|
+
: Array.from(literalKind === 0 ? new TextEncoder().encode(lit as string) : (lit as Uint8Array))
|
|
902
1068
|
const len = codes.length
|
|
903
|
-
const value =
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
1069
|
+
const value =
|
|
1070
|
+
!node.caseless && literalKind === kind
|
|
1071
|
+
? this.lit(lit)
|
|
1072
|
+
: kind === 0
|
|
1073
|
+
? `I.slice(p-${len},p)`
|
|
1074
|
+
: `I.subarray(p-${len},p)`
|
|
908
1075
|
return this.literalUnits(codes, node.caseless, fail) + set(value)
|
|
909
1076
|
}
|
|
910
1077
|
case "OneOf": {
|
|
@@ -912,27 +1079,32 @@ class Emitter {
|
|
|
912
1079
|
const cs = enc === undefined ? tokenSet(node.set, kind) : textSet(node.set)
|
|
913
1080
|
const accept = acceptTable(cs, negate)
|
|
914
1081
|
const c = this.v(fn)
|
|
915
|
-
const value = (len: string) => this.kind === 0 ? `I.slice(p,p+${len})` : `I[p]`
|
|
1082
|
+
const value = (len: string) => (this.kind === 0 ? `I.slice(p,p+${len})` : `I[p]`)
|
|
916
1083
|
if (enc === undefined) {
|
|
917
|
-
if (kind === 1)
|
|
1084
|
+
if (kind === 1)
|
|
1085
|
+
return `if(p>=E)${failBT}${c}=I[p];if(!(${this.member(accept, c)}))${failBT}${set("I[p]")}p++;`
|
|
918
1086
|
const cp = this.v(fn)
|
|
919
1087
|
if (!negate && this.memberHigh(cs, negate, cp) === "false") {
|
|
920
1088
|
return `if(p>=E)${failBT}${c}=I.charCodeAt(p);if(${c}>=${TableSize}||!(${this.member(accept, c)}))${failBT}${set("I[p]")}p++;`
|
|
921
1089
|
}
|
|
922
|
-
return
|
|
1090
|
+
return (
|
|
1091
|
+
`if(p>=E)${failBT}${c}=I.charCodeAt(p);` +
|
|
923
1092
|
`if(${c}<${TableSize}){if(!(${this.member(accept, c)}))${failBT}${set("I[p]")}p++}` +
|
|
924
1093
|
`else{${cp}=${c};if(${c}>=55296&&${c}<=56319&&p+1<I.length){const d=I.charCodeAt(p+1);` +
|
|
925
1094
|
`if((d&64512)===56320)${cp}=((${c}-55296)<<10)+(d-56320)+65536}` +
|
|
926
1095
|
`if(!(${this.memberHigh(cs, negate, cp)}))${failBT}` +
|
|
927
1096
|
`if(${cp}>65535){${set(value("2"))}p+=2}else{${set("I[p]")}p++}}`
|
|
1097
|
+
)
|
|
928
1098
|
}
|
|
929
1099
|
const r = this.reader(enc)
|
|
930
|
-
return
|
|
1100
|
+
return (
|
|
1101
|
+
`if(p>=E)${failBT}` +
|
|
931
1102
|
(r.fast > 0
|
|
932
1103
|
? `if(${r.guard("p")}&&(${c}=${r.unit("p")})<${r.fast}){if(!${this.textAccepts(r, cs, accept, negate, c)})${failBT}${set(`R.charOf(${c})`)}p+=${r.width}}else`
|
|
933
1104
|
: "") +
|
|
934
1105
|
`{${c}=${this.const(enc.decode)}(I,p,E);if(${c}<0||${this.textRejectsDecoded(enc, r, cs, negate, c)})${failBT}` +
|
|
935
1106
|
`${set(`R.charOf(${c})`)}p+=${this.const(enc.width)}(${c})}`
|
|
1107
|
+
)
|
|
936
1108
|
}
|
|
937
1109
|
case "TakeWhile":
|
|
938
1110
|
return this.takeWhile(node, out, fail, fn)
|
|
@@ -974,13 +1146,10 @@ class Emitter {
|
|
|
974
1146
|
const counted = max !== Infinity || min > 1
|
|
975
1147
|
const count = counted ? `${n}++;` : ""
|
|
976
1148
|
const bound = max !== Infinity ? `&&${n}<${max}` : ""
|
|
977
|
-
const slice =
|
|
978
|
-
? `${this.const(enc.text)}(I,${st},p)`
|
|
979
|
-
|
|
980
|
-
? `
|
|
981
|
-
: `I.subarray(${st},p)`
|
|
982
|
-
const finish = (min > 0 ? `if(${counted ? n : `p-${st}`}<${min}){${BT}${fail}}` : "") +
|
|
983
|
-
(out === null ? "" : `${out}=${slice};`)
|
|
1149
|
+
const slice =
|
|
1150
|
+
enc !== undefined ? `${this.const(enc.text)}(I,${st},p)` : kind === 0 ? `I.slice(${st},p)` : `I.subarray(${st},p)`
|
|
1151
|
+
const finish =
|
|
1152
|
+
(min > 0 ? `if(${counted ? n : `p-${st}`}<${min}){${BT}${fail}}` : "") + (out === null ? "" : `${out}=${slice};`)
|
|
984
1153
|
const head = `${st}=p;${counted ? `${n}=0;` : ""}${loop}:while(p<E${bound}){${c}=${this.unit("p")};`
|
|
985
1154
|
const member = this.member(accept, c)
|
|
986
1155
|
if (kind === 1 && (enc === undefined || fastLimit(enc) >= TableSize)) {
|
|
@@ -992,12 +1161,14 @@ class Emitter {
|
|
|
992
1161
|
const r = this.reader(enc)
|
|
993
1162
|
// The loop condition already checks a unit of width 1
|
|
994
1163
|
const guard = r.width === 1 ? "" : `${r.guard("p")}&&`
|
|
995
|
-
return
|
|
1164
|
+
return (
|
|
1165
|
+
`${st}=p;${counted ? `${n}=0;` : ""}${loop}:while(p<E${bound}){` +
|
|
996
1166
|
(r.fast > 0
|
|
997
1167
|
? `if(${guard}(${c}=${r.unit("p")})<${r.fast}){if(!${this.textAccepts(r, cs, accept, negate, c)})break ${loop};p+=${r.width}}else`
|
|
998
1168
|
: "") +
|
|
999
1169
|
`{${c}=${this.const(enc.decode)}(I,p,E);if(${c}<0||${this.textRejectsDecoded(enc, r, cs, negate, c)})break ${loop};` +
|
|
1000
1170
|
`p+=${this.const(enc.width)}(${c})}${count}}${finish}`
|
|
1171
|
+
)
|
|
1001
1172
|
}
|
|
1002
1173
|
const fast = 0xd800
|
|
1003
1174
|
const small = TableSize
|
|
@@ -1008,13 +1179,14 @@ class Emitter {
|
|
|
1008
1179
|
// Every code point from `TableSize` up is accepted: without a count,
|
|
1009
1180
|
// surrogate pairs can be skipped a unit at a time
|
|
1010
1181
|
const always = highTest(c) === "true"
|
|
1011
|
-
const rest =
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1182
|
+
const rest =
|
|
1183
|
+
never && !negate
|
|
1184
|
+
? `break;`
|
|
1185
|
+
: always && !counted
|
|
1186
|
+
? `p++;`
|
|
1187
|
+
: `if(${c}<${fast}||${c}>56319){if(!(${highTest(c)}))break ${loop};p++}` +
|
|
1188
|
+
`else{${cp}=${c};if(p+1<I.length){const d=I.charCodeAt(p+1);if((d&64512)===56320)${cp}=((${c}-55296)<<10)+(d-56320)+65536}` +
|
|
1189
|
+
`if(!(${highTest(cp)}))break ${loop};p+=${cp}>65535?2:1}`
|
|
1018
1190
|
return `${head}if(${c}<${small}){if(!(${member}))break;p++}else ${rest}${count}}${finish}`
|
|
1019
1191
|
}
|
|
1020
1192
|
|
|
@@ -1024,7 +1196,7 @@ class Emitter {
|
|
|
1024
1196
|
const r = enc === undefined ? undefined : this.reader(enc)
|
|
1025
1197
|
if (r !== undefined && !(ctrl < r.fast)) return this.delegate(p, !node.raw, out, fail, fn)
|
|
1026
1198
|
const raw = node.raw
|
|
1027
|
-
const acc = raw ? null : out ?? this.v(fn)
|
|
1199
|
+
const acc = raw ? null : (out ?? this.v(fn))
|
|
1028
1200
|
const x = raw ? null : this.v(fn)
|
|
1029
1201
|
const b = this.v(fn)
|
|
1030
1202
|
const st = this.v(fn)
|
|
@@ -1036,17 +1208,20 @@ class Emitter {
|
|
|
1036
1208
|
const onNormal = raw
|
|
1037
1209
|
? `if(p===${b}){$S.mode="Cut";${fail}}continue ${loop};`
|
|
1038
1210
|
: `if(p!==${b}){${acc}+=${x};continue ${loop}}`
|
|
1039
|
-
const slice =
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1211
|
+
const slice =
|
|
1212
|
+
enc !== undefined
|
|
1213
|
+
? `${this.const(enc.text)}(I,${st},p)`
|
|
1214
|
+
: this.kind === 0
|
|
1215
|
+
? `I.slice(${st},p)`
|
|
1216
|
+
: `I.subarray(${st},p)`
|
|
1044
1217
|
const ctrlTest = r === undefined ? `p<E&&${this.unit("p")}===${ctrl}` : `${r.guard("p")}&&${r.unit("p")}===${ctrl}`
|
|
1045
|
-
return
|
|
1218
|
+
return (
|
|
1219
|
+
`${raw ? `${st}=p;` : `${acc}="";`}${loop}:while(p<E){${b}=p;${okv}=false;` +
|
|
1046
1220
|
`${bad}:{${normal}${okv}=true;}` +
|
|
1047
1221
|
`if(${okv}){${onNormal}}else{if($S.mode!=="Backtrack"){${fail}}p=${b}}` +
|
|
1048
1222
|
`if(${ctrlTest}){p+=${r?.width ?? 1};${escape}${raw ? "" : `${acc}+=${x};`}}else break ${loop};}` +
|
|
1049
1223
|
(raw && out !== null ? `${out}=${slice};` : "")
|
|
1224
|
+
)
|
|
1050
1225
|
}
|
|
1051
1226
|
}
|
|
1052
1227
|
|
|
@@ -1061,21 +1236,30 @@ const acceptTable = (set: CharSet, negate: boolean): Uint8Array => {
|
|
|
1061
1236
|
}
|
|
1062
1237
|
|
|
1063
1238
|
/** Generates the source of a compiled parser, without building it. */
|
|
1064
|
-
|
|
1239
|
+
const generate = (
|
|
1240
|
+
root: ParserLike,
|
|
1241
|
+
kind: 0 | 1 | 2,
|
|
1242
|
+
resumable = false
|
|
1243
|
+
): {
|
|
1065
1244
|
readonly source: string
|
|
1066
1245
|
readonly constants: ReadonlyArray<unknown>
|
|
1067
1246
|
readonly fallbacks: ReadonlyArray<Fallback>
|
|
1068
1247
|
} => {
|
|
1069
1248
|
const e = new Emitter(root, kind, resumable)
|
|
1070
1249
|
const main = e.rule(root, true)
|
|
1071
|
-
const source =
|
|
1250
|
+
const source =
|
|
1251
|
+
`"use strict";let $I,$E,$S,P;${e.functions.join("\n")}\n` +
|
|
1072
1252
|
`return function run(state){const sI=$I,sE=$E,sS=$S,sP=P;$I=state.input;$E=state.end;$S=state;` +
|
|
1073
1253
|
`try{const v=${main}(state.pos);if(v!==F)state.pos=P;return v}finally{$I=sI;$E=sE;$S=sS;P=sP}}`
|
|
1074
1254
|
return { source, constants: e.constants, fallbacks: e.fallbacks }
|
|
1075
1255
|
}
|
|
1076
1256
|
|
|
1077
1257
|
/** Compiles a root parser for one input kind with `new Function`. */
|
|
1078
|
-
|
|
1258
|
+
const compileJit = (
|
|
1259
|
+
root: ParserLike,
|
|
1260
|
+
kind: 0 | 1 | 2,
|
|
1261
|
+
resumable = false
|
|
1262
|
+
): {
|
|
1079
1263
|
readonly step: Step
|
|
1080
1264
|
readonly fallbacks: ReadonlyArray<Fallback>
|
|
1081
1265
|
readonly source: string
|
|
@@ -1086,6 +1270,25 @@ export const compileJit = (root: ParserLike, kind: 0 | 1 | 2, resumable = false)
|
|
|
1086
1270
|
// holds when `end` is the input's length, as it is in the runners.
|
|
1087
1271
|
let closure: Step | undefined
|
|
1088
1272
|
const step: Step = (state) =>
|
|
1089
|
-
state.end === state.input.length
|
|
1273
|
+
state.end === state.input.length
|
|
1274
|
+
? run(state)
|
|
1275
|
+
: (closure ??= createClosureCompiler(kind, resumable).compile(root, true))(state)
|
|
1090
1276
|
return { step, fallbacks, source }
|
|
1091
1277
|
}
|
|
1278
|
+
|
|
1279
|
+
/**
|
|
1280
|
+
* The JIT as a backend: generated code where `new Function` is allowed,
|
|
1281
|
+
* otherwise (or if generating fails) the closure compiler.
|
|
1282
|
+
*/
|
|
1283
|
+
export const backend: Backend = {
|
|
1284
|
+
build: (p, kind, resumable) => {
|
|
1285
|
+
if (!jitSupported()) return closureBackend.build(p, kind, resumable)
|
|
1286
|
+
try {
|
|
1287
|
+
const { fallbacks, source, step } = compileJit(p, kind, resumable)
|
|
1288
|
+
return { backend: "jit", step, fallbacks, error: undefined, source }
|
|
1289
|
+
} catch (error) {
|
|
1290
|
+
// Fall back to the closure compiler, remembering why
|
|
1291
|
+
return { ...closureBackend.build(p, kind, resumable), jitError: error }
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|