@thomasfosterau/effect-parse 0.1.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.
Files changed (155) hide show
  1. package/README.md +247 -230
  2. package/dist/Ascii.d.ts +32 -53
  3. package/dist/Ascii.d.ts.map +1 -1
  4. package/dist/Ascii.js +576 -571
  5. package/dist/Ascii.js.map +1 -1
  6. package/dist/Binary.d.ts +6 -29
  7. package/dist/Binary.d.ts.map +1 -1
  8. package/dist/Binary.js +129 -116
  9. package/dist/Binary.js.map +1 -1
  10. package/dist/Bits.d.ts +4 -2
  11. package/dist/Bits.d.ts.map +1 -1
  12. package/dist/Bits.js +121 -123
  13. package/dist/Bits.js.map +1 -1
  14. package/dist/Compiler.d.ts +39 -0
  15. package/dist/Compiler.d.ts.map +1 -0
  16. package/dist/Compiler.js +48 -0
  17. package/dist/Compiler.js.map +1 -0
  18. package/dist/Encoding.d.ts +29 -46
  19. package/dist/Encoding.d.ts.map +1 -1
  20. package/dist/Encoding.js +76 -75
  21. package/dist/Encoding.js.map +1 -1
  22. package/dist/Expression.d.ts +15 -49
  23. package/dist/Expression.d.ts.map +1 -1
  24. package/dist/Expression.js +143 -131
  25. package/dist/Expression.js.map +1 -1
  26. package/dist/Input.d.ts +206 -177
  27. package/dist/Input.d.ts.map +1 -1
  28. package/dist/Input.js +317 -299
  29. package/dist/Input.js.map +1 -1
  30. package/dist/Nom.d.ts +44 -84
  31. package/dist/Nom.d.ts.map +1 -1
  32. package/dist/Nom.js +423 -453
  33. package/dist/Nom.js.map +1 -1
  34. package/dist/ParseError.d.ts +54 -49
  35. package/dist/ParseError.d.ts.map +1 -1
  36. package/dist/ParseError.js +233 -275
  37. package/dist/ParseError.js.map +1 -1
  38. package/dist/Parser.d.ts +345 -313
  39. package/dist/Parser.d.ts.map +1 -1
  40. package/dist/Parser.js +2010 -1802
  41. package/dist/Parser.js.map +1 -1
  42. package/dist/ParserSchema.d.ts +8 -7
  43. package/dist/ParserSchema.d.ts.map +1 -1
  44. package/dist/ParserSchema.js +49 -49
  45. package/dist/ParserSchema.js.map +1 -1
  46. package/dist/ParserStream.d.ts +26 -20
  47. package/dist/ParserStream.d.ts.map +1 -1
  48. package/dist/ParserStream.js +267 -255
  49. package/dist/ParserStream.js.map +1 -1
  50. package/dist/Syntax.d.ts +401 -0
  51. package/dist/Syntax.d.ts.map +1 -0
  52. package/dist/Syntax.js +717 -0
  53. package/dist/Syntax.js.map +1 -0
  54. package/dist/Text.d.ts +98 -134
  55. package/dist/Text.d.ts.map +1 -1
  56. package/dist/Text.js +534 -496
  57. package/dist/Text.js.map +1 -1
  58. package/dist/Token.d.ts +60 -42
  59. package/dist/Token.d.ts.map +1 -1
  60. package/dist/Token.js +331 -265
  61. package/dist/Token.js.map +1 -1
  62. package/dist/index.d.ts +16 -17
  63. package/dist/index.js +16 -17
  64. package/dist/internal/compiler/closure.d.ts +14 -16
  65. package/dist/internal/compiler/closure.d.ts.map +1 -1
  66. package/dist/internal/compiler/closure.js +1458 -1637
  67. package/dist/internal/compiler/closure.js.map +1 -1
  68. package/dist/internal/compiler/entries.d.ts +11 -11
  69. package/dist/internal/compiler/entries.d.ts.map +1 -1
  70. package/dist/internal/compiler/entries.js +36 -26
  71. package/dist/internal/compiler/entries.js.map +1 -1
  72. package/dist/internal/compiler/first.d.ts +19 -19
  73. package/dist/internal/compiler/first.d.ts.map +1 -1
  74. package/dist/internal/compiler/first.js +251 -227
  75. package/dist/internal/compiler/first.js.map +1 -1
  76. package/dist/internal/compiler/jit.d.ts +6 -17
  77. package/dist/internal/compiler/jit.d.ts.map +1 -1
  78. package/dist/internal/compiler/jit.js +860 -988
  79. package/dist/internal/compiler/jit.js.map +1 -1
  80. package/dist/internal/compiler/registry.d.ts +20 -21
  81. package/dist/internal/compiler/registry.d.ts.map +1 -1
  82. package/dist/internal/compiler/registry.js +21 -41
  83. package/dist/internal/compiler/registry.js.map +1 -1
  84. package/dist/internal/compiler/sets.d.ts +9 -13
  85. package/dist/internal/compiler/sets.d.ts.map +1 -1
  86. package/dist/internal/compiler/sets.js +76 -73
  87. package/dist/internal/compiler/sets.js.map +1 -1
  88. package/dist/internal/entries.d.ts +3 -3
  89. package/dist/internal/entries.d.ts.map +1 -1
  90. package/dist/internal/entries.js +38 -30
  91. package/dist/internal/entries.js.map +1 -1
  92. package/dist/internal/node.d.ts +177 -173
  93. package/dist/internal/node.d.ts.map +1 -1
  94. package/dist/internal/node.js +77 -66
  95. package/dist/internal/node.js.map +1 -1
  96. package/dist/internal/numbers.d.ts +7 -8
  97. package/dist/internal/numbers.d.ts.map +1 -1
  98. package/dist/internal/numbers.js +246 -276
  99. package/dist/internal/numbers.js.map +1 -1
  100. package/dist/internal/resume.d.ts +6 -24
  101. package/dist/internal/resume.d.ts.map +1 -1
  102. package/dist/internal/resume.js +30 -29
  103. package/dist/internal/resume.js.map +1 -1
  104. package/dist/internal/run.d.ts +15 -0
  105. package/dist/internal/run.d.ts.map +1 -0
  106. package/dist/internal/run.js +36 -0
  107. package/dist/internal/run.js.map +1 -0
  108. package/dist/internal/stack.d.ts +16 -0
  109. package/dist/internal/stack.d.ts.map +1 -0
  110. package/dist/internal/stack.js +26 -0
  111. package/dist/internal/stack.js.map +1 -0
  112. package/dist/internal/syntax.d.ts +146 -0
  113. package/dist/internal/syntax.d.ts.map +1 -0
  114. package/dist/internal/syntax.js +305 -0
  115. package/dist/internal/syntax.js.map +1 -0
  116. package/dist/internal/text.d.ts +10 -2
  117. package/dist/internal/text.d.ts.map +1 -1
  118. package/dist/internal/text.js +295 -345
  119. package/dist/internal/text.js.map +1 -1
  120. package/dist/internal/tokens.d.ts +25 -30
  121. package/dist/internal/tokens.d.ts.map +1 -1
  122. package/dist/internal/tokens.js +369 -363
  123. package/dist/internal/tokens.js.map +1 -1
  124. package/package.json +42 -28
  125. package/src/Ascii.ts +278 -187
  126. package/src/Binary.ts +150 -44
  127. package/src/Bits.ts +6 -3
  128. package/src/Compiler.ts +98 -0
  129. package/src/Expression.ts +10 -5
  130. package/src/Input.ts +106 -28
  131. package/src/Nom.ts +4 -3
  132. package/src/ParseError.ts +48 -51
  133. package/src/Parser.ts +1163 -795
  134. package/src/ParserStream.ts +104 -80
  135. package/src/Syntax.ts +900 -0
  136. package/src/Text.ts +272 -187
  137. package/src/Token.ts +180 -87
  138. package/src/index.ts +2 -0
  139. package/src/internal/compiler/closure.ts +121 -88
  140. package/src/internal/compiler/entries.ts +21 -8
  141. package/src/internal/compiler/first.ts +17 -15
  142. package/src/internal/compiler/jit.ts +360 -157
  143. package/src/internal/compiler/registry.ts +12 -33
  144. package/src/internal/compiler/sets.ts +17 -11
  145. package/src/internal/entries.ts +4 -2
  146. package/src/internal/node.ts +51 -37
  147. package/src/internal/numbers.ts +22 -20
  148. package/src/internal/resume.ts +8 -4
  149. package/src/internal/run.ts +41 -0
  150. package/src/internal/stack.ts +36 -0
  151. package/src/internal/syntax.ts +421 -0
  152. package/src/internal/text.ts +32 -25
  153. package/src/internal/tokens.ts +96 -51
  154. package/dist/index.d.ts.map +0 -1
  155. 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 { entriesShape, pairOf } from "./entries.js"
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]! < fast) 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
- export const jitSupported = (): boolean => {
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 = acceptCost <= rejectCost
177
- ? accept.map(([a, b]) =>
178
- a === b ? `${v}===${a}` : a === 0 ? `${v}<=${b}` : b === size - 1 ? `${v}>=${a}` : `(${v}>=${a}&&${v}<=${b})`
179
- ).join("||")
180
- : reject.map(([a, b]) =>
181
- a === b ? `${v}!==${a}` : a === 0 ? `${v}>${b}` : b === size - 1 ? `${v}<${a}` : `(${v}<${a}||${v}>${b})`
182
- ).join("&&")
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(readonly root: ParserLike, readonly kind: 0 | 1 | 2, readonly resumable = false) {
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
- while (stack.length > 0) {
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 || typeof value === "boolean") return String(value)
244
- if (typeof value === "number" && Number.isFinite(value) && !Object.is(value, -0)) return String(value)
245
- if (typeof value === "string" && value.length < 64) return JSON.stringify(value)
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 `${st}=p;${ok}=false;${leaf}:{${bad}:{${code}${ok}=true;}` +
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 `if(p<E){${BT}${fail}}` + (this.resumable ? `if($S.partial){$S.mode="Incomplete";${fail}}` : "") +
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
- case "Literal":
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
- case "TakeWhile": {
388
- if (!this.native(node.encoding)) return this.delegate(p, used, out, fail, fn)
389
- if (!this.resumable) return this.token(node, out, fail, fn) ?? this.delegate(p, used, out, fail, fn)
390
- const bad = this.label()
391
- const code = this.token(node, out, `break ${bad};`, fn)
392
- if (code === undefined) return this.delegate(p, used, out, fail, fn)
393
- return this.partialSafe(p, code, bad, node._tag === "Literal" ? node.literal.length * 4 : Margin, out, fail, fn)
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 && entriesShape(node.self)) {
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 this.gen(node.self, true, t, fail, fn, d) +
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 this.gen(node.self, true, t, fail, fn, d) +
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 body + set(this.kind === 0 ? `I.slice(${st},p)` : this.kind === 1 ? `I.subarray(${st},p)` : `$S.slice(${st},p)`)
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 `${ok}:{${bad}:{${this.gen(node.self, used, out, `break ${bad};`, fn, d)}break ${ok};}` +
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 `${st}=p;${ok}:{${bad}:{${this.gen(node.self, used, t, `break ${bad};`, fn, d)}${set(some)}break ${ok};}` +
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 `${st}=p;${ok}:{${bad}:{${this.gen(node.self, false, null, `break ${bad};`, fn, d)}p=${st};${BT}${fail}}` +
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]!, fail, fn, d)).join("")
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) return this.gen(node.self, false, null, fail, fn, d) + this.gen(node.that, false, null, fail, fn, d)
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 this.gen(node.self, true, x, fail, fn, d) + this.gen(node.that, true, y, fail, fn, d) +
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 `${out}=${this.kind === 0 ? "R.scanFloatString" : "R.scanFloatBytes"}(I,p,E);` +
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 (typeof output === "number") {
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]!, fail, fn, d)).join("")
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]!, used, out, fail, fn, d)
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 = `${bad}:{${this.gen(q, used, out, `break ${bad};`, fn, d)}break ${done};}` +
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 += prune && f!.mode === Strict
596
- ? `if(${this.resumable ? `${u}===-2||` : ""}${this.startTest(f!, u)}){${branch}}`
597
- : branch
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(p: ParserLike, node: NodeOf<"Dispatch">, used: boolean, out: string | null, fail: string, fn: Fn, d: number): string {
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
- // The general path: run the selector, choose, then run the case's compiled step
605
- const cases = new Map<ParserLike, Step>()
606
- const caseStep = (q: ParserLike) => {
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 `${st}=p;${this.gen(node.selector, true, k, fail, fn, d)}${q}=${choose}(${k});` +
622
- `if(${q}===void 0){p=${st};${BT}${fail}}$S.pos=p;${r}=(${table}.get(${q})??${q}.step)($S);` +
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
- if (node.cases === undefined) {
626
- this.closure.note(p, node, "the parser returned by the function of cases runs interpreted")
627
- return general()
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 = (node.choose)(enc === undefined && this.kind === 1 ? unit : charOf(unit))
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 += units.map((x) => `case ${x}:`).join("") +
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
- const x = keep ? this.v(fn) : null
681
- const acc = keep ? out! : this.v(fn)
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 (keep) {
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 = r === undefined
697
- ? ""
698
- : `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${acc},count:${n},pos:${st}});`
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 = collect === undefined
701
- ? `${this.gen(node.self, keep, x, `break ${bad};`, fn, d)}${check}${add}`
702
- : collect.element(`break ${bad};`, check)
703
- const body = `${loop}:for(;${bound};${n}++){${st}=p;` +
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! : this.v(fn)
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 = r === undefined
753
- ? ""
754
- : `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${acc},count:${n},pos:${st}});`
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 `${ok}:{${bad}:{${body(`break ${bad};`)}${success}break ${ok};}` +
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) => collect === undefined ? this.gen(node.self, keep, x, f, fn, d) : collect.element(f, "")
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 = `${collect?.init ?? (keep ? `${acc}=[];` : "")}${n}=0;${st}=p;` + attempt(self, `${push}${n}++;`, min > 0 ? "||true" : "", "")
766
- const rest = `${loop}:while(${max === Infinity ? "true" : `${n}<${max}`}){${st}=p;` +
767
- attempt(sep, "", `||${n}<${min}`, save) + check +
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 `entriesShape(self)`: builds the
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 `${ok}:{${bad}:{${this.entries(node.self, out, `break ${bad};`, fn, d)}break ${ok};}` +
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.map((q, j) =>
794
- j === i ? this.entries(q, out, fail, fn, d) : this.gen(q, false, null, fail, fn, d)
795
- ).join("")
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.map((q, j) =>
807
- this.gen(q, j === pair.key || j === pair.value, j === pair.key ? key : j === pair.value ? value : null, f, fn, d)
808
- ).join("") + check +
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 `if(p>=E)${failBT}${c}=I.charCodeAt(p);` +
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 `if(p>=E)${failBT}` +
882
- (r.fast > 0 ? `if(${r.guard("p")}&&(${c}=${r.unit("p")})<${r.fast}){${set(`R.charOf(${c})`)}p+=${r.width}}else` : "") +
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 this.literalUnits(
891
- typeof encoded === "string" ? unitsOf(encoded) : Array.from(encoded),
892
- false,
893
- fail
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 = typeof lit === "string" ? 0 : lit instanceof Uint8Array ? 1 : 2
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 = kind === 0
900
- ? unitsOf(lit as string)
901
- : Array.from(literalKind === 0 ? new TextEncoder().encode(lit as string) : lit as Uint8Array)
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 = !node.caseless && literalKind === kind
904
- ? this.lit(lit)
905
- : kind === 0
906
- ? `I.slice(p-${len},p)`
907
- : `I.subarray(p-${len},p)`
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) return `if(p>=E)${failBT}${c}=I[p];if(!(${this.member(accept, c)}))${failBT}${set("I[p]")}p++;`
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 `if(p>=E)${failBT}${c}=I.charCodeAt(p);` +
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 `if(p>=E)${failBT}` +
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 = enc !== undefined
978
- ? `${this.const(enc.text)}(I,${st},p)`
979
- : kind === 0
980
- ? `I.slice(${st},p)`
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 `${st}=p;${counted ? `${n}=0;` : ""}${loop}:while(p<E${bound}){` +
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 = never && !negate
1012
- ? `break;`
1013
- : always && !counted
1014
- ? `p++;`
1015
- : `if(${c}<${fast}||${c}>56319){if(!(${highTest(c)}))break ${loop};p++}` +
1016
- `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}` +
1017
- `if(!(${highTest(cp)}))break ${loop};p+=${cp}>65535?2:1}`
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 = enc !== undefined
1040
- ? `${this.const(enc.text)}(I,${st},p)`
1041
- : this.kind === 0
1042
- ? `I.slice(${st},p)`
1043
- : `I.subarray(${st},p)`
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 `${raw ? `${st}=p;` : `${acc}="";`}${loop}:while(p<E){${b}=p;${okv}=false;` +
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
- export const generate = (root: ParserLike, kind: 0 | 1 | 2, resumable = false): {
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 = `"use strict";let $I,$E,$S,P;${e.functions.join("\n")}\n` +
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
- export const compileJit = (root: ParserLike, kind: 0 | 1 | 2, resumable = false): {
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 ? run(state) : (closure ??= createClosureCompiler(kind, resumable).compile(root, true))(state)
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
+ }