@thomasfosterau/effect-parse 0.1.0 → 0.3.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 (161) hide show
  1. package/README.md +291 -231
  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 +151 -132
  25. package/dist/Expression.js.map +1 -1
  26. package/dist/Input.d.ts +251 -177
  27. package/dist/Input.d.ts.map +1 -1
  28. package/dist/Input.js +409 -299
  29. package/dist/Input.js.map +1 -1
  30. package/dist/Nom.d.ts +45 -85
  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 +232 -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 +2034 -1802
  41. package/dist/Parser.js.map +1 -1
  42. package/dist/ParserSchema.d.ts +206 -7
  43. package/dist/ParserSchema.d.ts.map +1 -1
  44. package/dist/ParserSchema.js +678 -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 +36 -0
  111. package/dist/internal/stack.js.map +1 -0
  112. package/dist/internal/syntax.d.ts +147 -0
  113. package/dist/internal/syntax.d.ts.map +1 -0
  114. package/dist/internal/syntax.js +309 -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/ties.d.ts +30 -0
  121. package/dist/internal/ties.d.ts.map +1 -0
  122. package/dist/internal/ties.js +67 -0
  123. package/dist/internal/ties.js.map +1 -0
  124. package/dist/internal/tokens.d.ts +25 -30
  125. package/dist/internal/tokens.d.ts.map +1 -1
  126. package/dist/internal/tokens.js +369 -363
  127. package/dist/internal/tokens.js.map +1 -1
  128. package/package.json +42 -28
  129. package/src/Ascii.ts +278 -187
  130. package/src/Binary.ts +150 -44
  131. package/src/Bits.ts +6 -3
  132. package/src/Compiler.ts +98 -0
  133. package/src/Expression.ts +24 -16
  134. package/src/Input.ts +210 -35
  135. package/src/Nom.ts +4 -3
  136. package/src/ParseError.ts +49 -52
  137. package/src/Parser.ts +1199 -799
  138. package/src/ParserSchema.ts +982 -23
  139. package/src/ParserStream.ts +104 -80
  140. package/src/Syntax.ts +900 -0
  141. package/src/Text.ts +272 -187
  142. package/src/Token.ts +180 -87
  143. package/src/index.ts +2 -0
  144. package/src/internal/compiler/closure.ts +121 -88
  145. package/src/internal/compiler/entries.ts +21 -8
  146. package/src/internal/compiler/first.ts +17 -15
  147. package/src/internal/compiler/jit.ts +360 -157
  148. package/src/internal/compiler/registry.ts +12 -33
  149. package/src/internal/compiler/sets.ts +17 -11
  150. package/src/internal/entries.ts +4 -2
  151. package/src/internal/node.ts +51 -37
  152. package/src/internal/numbers.ts +22 -20
  153. package/src/internal/resume.ts +8 -4
  154. package/src/internal/run.ts +41 -0
  155. package/src/internal/stack.ts +41 -0
  156. package/src/internal/syntax.ts +426 -0
  157. package/src/internal/text.ts +32 -25
  158. package/src/internal/ties.ts +83 -0
  159. package/src/internal/tokens.ts +96 -51
  160. package/dist/index.d.ts.map +0 -1
  161. package/dist/index.js.map +0 -1
@@ -7,6 +7,7 @@
7
7
  * Policy. Parsers that can't be compiled run their own (interpreted) steps
8
8
  * on the same state.
9
9
  */
10
+ import * as Predicate from "effect/Predicate"
10
11
  import type { Encoding } from "../../Encoding.js"
11
12
  import type { ErrMode, State } from "../../Input.js"
12
13
  import { Fail } from "../../Input.js"
@@ -15,11 +16,13 @@ import type { Node, NodeOf, ParserLike } from "../node.js"
15
16
  import { addEntry, flushEntries, fromEntries, isPlainKey } from "../entries.js"
16
17
  import { nodeOf } from "../node.js"
17
18
  import { floatScan, floatTexts, scanFloatBytes, scanFloatString, toFloat } from "../numbers.js"
18
- import { charOf, fastLimit, fold, validate } from "../text.js"
19
+ import { charOf, fastLimit, firstCodePoint, fold, validate } from "../text.js"
19
20
  import { countTokens, indexOf } from "../tokens.js"
20
21
  import type { AltPlan } from "./first.js"
21
22
  import * as Resume from "../resume.js"
22
- import { entriesShape, pairOf } from "./entries.js"
23
+ import type { Backend } from "./registry.js"
24
+ import type { EntryPair } from "./entries.js"
25
+ import { buildsEntries, pairOf } from "./entries.js"
23
26
  import { FirstSets, planAlt } from "./first.js"
24
27
  import type { CharSet } from "./sets.js"
25
28
  import { kindOfEncoding, TableSize, textSet, tokenSet } from "./sets.js"
@@ -34,7 +37,7 @@ export interface Fallback {
34
37
  }
35
38
 
36
39
  /** The maximum number of distinct parsers compiled for one root. */
37
- export const maxNodes = 2048
40
+ const maxNodes = 2048
38
41
 
39
42
  const BT: ErrMode = "Backtrack"
40
43
  const CUT: ErrMode = "Cut"
@@ -48,7 +51,7 @@ const INC: ErrMode = "Incomplete"
48
51
  */
49
52
  const Margin = 4
50
53
 
51
- const lowerAscii = (c: number): number => c >= 65 && c <= 90 ? c + 32 : c
54
+ const lowerAscii = (c: number): number => (c >= 65 && c <= 90 ? c + 32 : c)
52
55
 
53
56
  const isSuccess = (r: any): boolean => r._tag === "Success"
54
57
  const isSome = (o: any): boolean => o._tag === "Some"
@@ -69,7 +72,10 @@ class Compiler {
69
72
  * `resumable`: compile for streaming parses, which may be partial (see
70
73
  * `state.partial`) and keep checkpoints (see `internal/resume.ts`).
71
74
  */
72
- constructor(readonly kind: 0 | 1 | 2, readonly resumable = false) {
75
+ constructor(
76
+ readonly kind: 0 | 1 | 2,
77
+ readonly resumable = false
78
+ ) {
73
79
  this.firsts = new FirstSets(kind)
74
80
  }
75
81
 
@@ -100,7 +106,8 @@ class Compiler {
100
106
  const slot = memo.get(p)
101
107
  if (slot !== undefined) {
102
108
  if (slot.fn !== undefined) return slot.fn
103
- return (s) => slot.fn!(s) // a cycle through `suspend`
109
+ // A cycle through `suspend`: the slot is filled once the cycle is compiled, before any parse runs
110
+ return (s) => (slot.fn ?? p.step)(s)
104
111
  }
105
112
  const fresh: Slot = { fn: undefined }
106
113
  memo.set(p, fresh)
@@ -192,7 +199,7 @@ class Compiler {
192
199
  if (step !== undefined) return step
193
200
  }
194
201
  // Streaming builds keep the array, which their checkpoints hold
195
- if (node.f === fromEntries && !this.resumable && entriesShape(node.self)) return this.entries(node.self)
202
+ if (node.f === fromEntries && !this.resumable && buildsEntries(node.self)) return this.entries(node.self)
196
203
  const c = this.compile(node.self, true)
197
204
  const f = node.f
198
205
  return (s) => {
@@ -204,7 +211,7 @@ class Compiler {
204
211
  const c = this.compile(node.self, false)
205
212
  if (!used) return c
206
213
  const value = node.value
207
- return (s) => c(s) === Fail ? Fail : value
214
+ return (s) => (c(s) === Fail ? Fail : value)
208
215
  }
209
216
  case "Tap": {
210
217
  const c = this.compile(node.self, true)
@@ -304,6 +311,7 @@ class Compiler {
304
311
  case "MapError":
305
312
  case "OrFail":
306
313
  case "Trace":
314
+ case "Syntax":
307
315
  case "RetryAfter":
308
316
  // Diagnostics only, or inactive outside parseRecoverable / tracing
309
317
  return this.compile(node.self, used)
@@ -362,9 +370,9 @@ class Compiler {
362
370
  return (s) => {
363
371
  const out: Record<string, unknown> = {}
364
372
  for (let i = 0; i < n; i++) {
365
- const a = steps[i]!(s)
373
+ const a = steps[i](s)
366
374
  if (a === Fail) return Fail
367
- out[keys[i]!] = a
375
+ out[keys[i]] = a
368
376
  }
369
377
  return out
370
378
  }
@@ -404,13 +412,13 @@ class Compiler {
404
412
  chain(steps: ReadonlyArray<Step>): Step {
405
413
  const n = steps.length
406
414
  if (n === 0) return () => undefined
407
- if (n === 1) return steps[0]!
415
+ if (n === 1) return steps[0]
408
416
  if (n === 2) {
409
417
  const [a, b] = steps as [Step, Step]
410
- return (s) => a(s) === Fail ? Fail : b(s)
418
+ return (s) => (a(s) === Fail ? Fail : b(s))
411
419
  }
412
420
  return (s) => {
413
- for (let i = 0; i < n; i++) if (steps[i]!(s) === Fail) return Fail
421
+ for (let i = 0; i < n; i++) if (steps[i](s) === Fail) return Fail
414
422
  return undefined
415
423
  }
416
424
  }
@@ -418,7 +426,7 @@ class Compiler {
418
426
  seq(node: NodeOf<"Seq">, used: boolean): Step {
419
427
  const output = node.output
420
428
  const keep = (i: number) =>
421
- used && (output === "tuple" || output === i || (typeof output === "object" && output.includes(i)))
429
+ used && (output === "tuple" || output === i || (Array.isArray(output) && output.includes(i)))
422
430
  const steps = node.parsers.map((q, i) => this.compile(q, keep(i)))
423
431
  const n = steps.length
424
432
  if (!used) return this.chain(steps)
@@ -426,28 +434,28 @@ class Compiler {
426
434
  return (s) => {
427
435
  const out = new Array(n)
428
436
  for (let i = 0; i < n; i++) {
429
- const a = steps[i]!(s)
437
+ const a = steps[i](s)
430
438
  if (a === Fail) return Fail
431
439
  out[i] = a
432
440
  }
433
441
  return out
434
442
  }
435
443
  }
436
- if (typeof output === "number") {
444
+ if (Predicate.isNumber(output)) {
437
445
  if (n === 2) {
438
446
  const [a, b] = steps as [Step, Step]
439
447
  return output === 0
440
448
  ? (s) => {
441
- const x = a(s)
442
- if (x === Fail) return Fail
443
- return b(s) === Fail ? Fail : x
444
- }
445
- : (s) => a(s) === Fail ? Fail : b(s)
449
+ const x = a(s)
450
+ if (x === Fail) return Fail
451
+ return b(s) === Fail ? Fail : x
452
+ }
453
+ : (s) => (a(s) === Fail ? Fail : b(s))
446
454
  }
447
455
  return (s) => {
448
456
  let out: unknown
449
457
  for (let i = 0; i < n; i++) {
450
- const a = steps[i]!(s)
458
+ const a = steps[i](s)
451
459
  if (a === Fail) return Fail
452
460
  if (i === output) out = a
453
461
  }
@@ -459,7 +467,7 @@ class Compiler {
459
467
  let x: unknown
460
468
  let y: unknown
461
469
  for (let i = 0; i < n; i++) {
462
- const a = steps[i]!(s)
470
+ const a = steps[i](s)
463
471
  if (a === Fail) return Fail
464
472
  if (i === i0) x = a
465
473
  else if (i === i1) y = a
@@ -471,8 +479,8 @@ class Compiler {
471
479
  /** The unit at the cursor, or -1 at the end of the input. */
472
480
  private unitReader(): (s: State<any>) => number {
473
481
  return this.kind === 0
474
- ? (s) => s.pos < s.end ? (s.input as string).charCodeAt(s.pos) : -1
475
- : (s) => s.pos < s.end ? (s.input as Uint8Array)[s.pos]! : -1
482
+ ? (s) => (s.pos < s.end ? (s.input as string).charCodeAt(s.pos) : -1)
483
+ : (s) => (s.pos < s.end ? (s.input as Uint8Array)[s.pos] : -1)
476
484
  }
477
485
 
478
486
  alt(node: NodeOf<"Alt">, used: boolean): Step {
@@ -484,15 +492,13 @@ class Compiler {
484
492
  return Fail
485
493
  }
486
494
  }
487
- if (n === 1) return steps[0]!
488
- const plan: AltPlan | undefined = this.kind === 2
489
- ? undefined
490
- : planAlt(node.parsers.map((q) => this.firsts.of(q)))
495
+ if (n === 1) return steps[0]
496
+ const plan: AltPlan | undefined = this.kind === 2 ? undefined : planAlt(node.parsers.map((q) => this.firsts.of(q)))
491
497
  if (plan === undefined) {
492
498
  return (s) => {
493
499
  const start = s.pos
494
500
  for (let i = 0; i < n; i++) {
495
- const a = steps[i]!(s)
501
+ const a = steps[i](s)
496
502
  if (a !== Fail) return a
497
503
  if (s.mode !== BT) return Fail
498
504
  s.pos = start
@@ -501,17 +507,17 @@ class Compiler {
501
507
  return Fail
502
508
  }
503
509
  }
504
- const lists = plan.lists.map((l) => l.map((i) => steps[i]!))
510
+ const lists = plan.lists.map((l) => l.map((i) => steps[i]))
505
511
  const { byUnit, end, high } = plan
506
512
  const unit = this.unitReader()
507
513
  const resumable = this.resumable
508
514
  return (s) => {
509
515
  // Near the end of a partial input, branches may be incomplete rather than fail
510
516
  const u = resumable && s.partial && s.pos + Margin > s.end ? -2 : unit(s)
511
- const candidates = u === -2 ? steps : lists[u < 0 ? end : u < TableSize ? byUnit[u]! : high]!
517
+ const candidates = u === -2 ? steps : lists[u < 0 ? end : u < TableSize ? byUnit[u] : high]
512
518
  const start = s.pos
513
519
  for (let i = 0; i < candidates.length; i++) {
514
- const a = candidates[i]!(s)
520
+ const a = candidates[i](s)
515
521
  if (a !== Fail) return a
516
522
  if (s.mode !== BT) return Fail
517
523
  s.pos = start
@@ -527,7 +533,7 @@ class Compiler {
527
533
  const compiled = new Map<ParserLike, Step>()
528
534
  if (node.cases !== undefined) {
529
535
  for (const key of Reflect.ownKeys(node.cases)) {
530
- const q = node.cases[key as any]!
536
+ const q = node.cases[key as any]
531
537
  if (!compiled.has(q)) compiled.set(q, this.compile(q, used))
532
538
  }
533
539
  }
@@ -564,7 +570,7 @@ class Compiler {
564
570
  for (let u = 0; u < size; u++) {
565
571
  const key = enc === undefined && this.kind === 1 ? u : charOf(u)
566
572
  const q = choose(key)
567
- table.push(q === undefined ? null : compiled.get(q) ?? q.step)
573
+ table.push(q === undefined ? null : (compiled.get(q) ?? q.step))
568
574
  }
569
575
  const unit = this.unitReader()
570
576
  const advance = peek ? 0 : 1
@@ -577,7 +583,7 @@ class Compiler {
577
583
  return Fail
578
584
  }
579
585
  if (u >= size) return general(s)
580
- const q = table[u]!
586
+ const q = table[u]
581
587
  if (q === null) {
582
588
  s.mode = BT
583
589
  return Fail
@@ -591,19 +597,21 @@ class Compiler {
591
597
  const { max, min } = node
592
598
  const checkLoop = max === Infinity
593
599
  const isFold = node._tag === "Fold"
600
+ const cond = isFold ? (node as NodeOf<"Fold">).while : undefined
594
601
  const c = this.compile(node.self, used)
595
602
  const resumable = this.resumable
596
603
  const key = {}
597
- // The accumulator: an array, the fold's value, or nothing when unused
598
- const init: () => unknown = !used ? () => undefined : isFold ? (node as NodeOf<"Fold">).init : () => []
599
- const add: (acc: any, a: unknown) => unknown = !used
604
+ // The accumulator: an array, the fold's value, or nothing when unused (and unneeded by `while`)
605
+ const tracked = used || cond !== undefined
606
+ const init: () => unknown = !tracked ? () => undefined : isFold ? (node as NodeOf<"Fold">).init : () => []
607
+ const add: (acc: any, a: unknown) => unknown = !tracked
600
608
  ? (acc) => acc
601
609
  : isFold
602
- ? (node as NodeOf<"Fold">).f
603
- : (acc, a) => {
604
- acc.push(a)
605
- return acc
606
- }
610
+ ? (node as NodeOf<"Fold">).f
611
+ : (acc, a) => {
612
+ acc.push(a)
613
+ return acc
614
+ }
607
615
  return (s) => {
608
616
  const entry = s.pos
609
617
  let acc: unknown
@@ -624,6 +632,7 @@ class Compiler {
624
632
  }
625
633
  if (!resumed) acc = init()
626
634
  for (; count < max; count++) {
635
+ if (cond !== undefined && count >= min && !cond(acc)) break
627
636
  const start = s.pos
628
637
  const a = c(s)
629
638
  if (a === Fail) {
@@ -643,7 +652,7 @@ class Compiler {
643
652
  }
644
653
 
645
654
  /**
646
- * `map(self, Object.fromEntries)`, where `entriesShape(self)`: builds the
655
+ * `map(self, Object.fromEntries)`, where `buildsEntries(self)`: builds the
647
656
  * object directly (see the JIT).
648
657
  */
649
658
  private entries(p: ParserLike): Step {
@@ -661,11 +670,11 @@ class Compiler {
661
670
  }
662
671
  case "Seq": {
663
672
  const i = node.output as number
664
- const steps = node.parsers.map((q, j) => j === i ? this.entries(q) : this.compile(q, false))
673
+ const steps = node.parsers.map((q, j) => (j === i ? this.entries(q) : this.compile(q, false)))
665
674
  return (s) => {
666
675
  let out: unknown
667
676
  for (let j = 0; j < steps.length; j++) {
668
- const a = steps[j]!(s)
677
+ const a = steps[j](s)
669
678
  if (a === Fail) return Fail
670
679
  if (j === i) out = a
671
680
  }
@@ -673,15 +682,22 @@ class Compiler {
673
682
  }
674
683
  }
675
684
  case "Repeat":
676
- case "Separated":
677
- return this.entriesLoop(node)
685
+ case "Separated": {
686
+ const pair = pairOf(node.self)
687
+ if (pair !== undefined) return this.entriesLoop(node, pair)
688
+ // Not reached (`buildsEntries` found the pair): build the pairs, then the object
689
+ const c = this.compile(p, true)
690
+ return (s) => {
691
+ const a = c(s)
692
+ return a === Fail ? Fail : fromEntries(a as Iterable<readonly [PropertyKey, unknown]>)
693
+ }
694
+ }
678
695
  default:
679
696
  return this.entries((node as { readonly self: ParserLike }).self)
680
697
  }
681
698
  }
682
699
 
683
- private entriesLoop(node: NodeOf<"Repeat"> | NodeOf<"Separated">): Step {
684
- const pair = pairOf(node.self)!
700
+ private entriesLoop(node: NodeOf<"Repeat"> | NodeOf<"Separated">, pair: EntryPair): Step {
685
701
  const parts = pair.parsers.map((q, j) => this.compile(q, j === pair.key || j === pair.value))
686
702
  const { key: ki, value: vi } = pair
687
703
  // Parses an entry and adds it to `acc`
@@ -689,7 +705,7 @@ class Compiler {
689
705
  let key: unknown
690
706
  let value: unknown
691
707
  for (let j = 0; j < parts.length; j++) {
692
- const a = parts[j]!(s)
708
+ const a = parts[j](s)
693
709
  if (a === Fail) return false
694
710
  if (j === ki) key = a
695
711
  else if (j === vi) value = a
@@ -708,7 +724,7 @@ class Compiler {
708
724
  let value: unknown
709
725
  let ok = true
710
726
  for (let j = 0; j < parts.length; j++) {
711
- const a = parts[j]!(s)
727
+ const a = parts[j](s)
712
728
  if (a === Fail) {
713
729
  ok = false
714
730
  break
@@ -803,7 +819,10 @@ class Compiler {
803
819
  let count = 0
804
820
  let resumed = false
805
821
  if (resumable && s.resume !== undefined) {
806
- const cp = Resume.take(s, key, entry) as Resume.Done | { acc: Array<unknown>; count: number; pos: number } | undefined
822
+ const cp = Resume.take(s, key, entry) as
823
+ | Resume.Done
824
+ | { acc: Array<unknown>; count: number; pos: number }
825
+ | undefined
807
826
  if (cp instanceof Resume.Done) {
808
827
  s.pos = cp.pos
809
828
  return cp.value
@@ -976,7 +995,7 @@ class Compiler {
976
995
  return (s) => {
977
996
  const pos = s.pos
978
997
  if (pos >= s.end) return fail(s)
979
- const b = (s.input as Uint8Array)[pos]!
998
+ const b = (s.input as Uint8Array)[pos]
980
999
  if (accept[b] === 0) return fail(s)
981
1000
  s.pos = pos + 1
982
1001
  return b
@@ -1057,7 +1076,7 @@ class Compiler {
1057
1076
 
1058
1077
  tokenLiteral(p: ParserLike, node: NodeOf<"Literal">): Step {
1059
1078
  const lit = node.literal
1060
- const literalKind = typeof lit === "string" ? 0 : lit instanceof Uint8Array ? 1 : 2
1079
+ const literalKind = Predicate.isString(lit) ? 0 : lit instanceof Uint8Array ? 1 : 2
1061
1080
  if (this.kind === 0) {
1062
1081
  if (literalKind !== 0) return this.fallback(p, node, "byte or array literal on string input")
1063
1082
  const str = lit as string
@@ -1078,7 +1097,7 @@ class Compiler {
1078
1097
  return stringLiteral(str, str)
1079
1098
  }
1080
1099
  if (literalKind === 2) return this.fallback(p, node, "array literal on byte input")
1081
- const bytes = literalKind === 0 ? new TextEncoder().encode(lit as string) : lit as Uint8Array
1100
+ const bytes = literalKind === 0 ? new TextEncoder().encode(lit as string) : (lit as Uint8Array)
1082
1101
  const len = bytes.length
1083
1102
  const sameKind = literalKind === 1 && !node.caseless
1084
1103
  const expect = node.caseless ? Array.from(bytes, lowerAscii) : Array.from(bytes)
@@ -1088,7 +1107,7 @@ class Compiler {
1088
1107
  if (s.end - pos < len) return fail(s)
1089
1108
  const input = s.input as Uint8Array
1090
1109
  for (let i = 0; i < len; i++) {
1091
- const b = input[pos + i]!
1110
+ const b = input[pos + i]
1092
1111
  if ((caseless ? lowerAscii(b) : b) !== expect[i]) return fail(s)
1093
1112
  }
1094
1113
  s.pos = pos + len
@@ -1107,7 +1126,7 @@ class Compiler {
1107
1126
  const start = s.pos
1108
1127
  const limit = max === Infinity ? s.end : Math.min(s.end, start + max)
1109
1128
  let i = start
1110
- while (i < limit && accept[bytes[i]!] === 1) i++
1129
+ while (i < limit && accept[bytes[i]] === 1) i++
1111
1130
  if (i - start < min) return failAt(s, i)
1112
1131
  s.pos = i
1113
1132
  return used ? bytes.subarray(start, i) : undefined
@@ -1155,9 +1174,8 @@ class Compiler {
1155
1174
  const decode = enc.decode
1156
1175
  const width = enc.width
1157
1176
  const toText = enc.text
1158
- const unitAt: (input: any, i: number) => number = kind === 0
1159
- ? (input: string, i) => input.charCodeAt(i)
1160
- : (input: Uint8Array, i) => input[i]!
1177
+ const unitAt: (input: any, i: number) => number =
1178
+ kind === 0 ? (input: string, i) => input.charCodeAt(i) : (input: Uint8Array, i) => input[i]
1161
1179
  switch (node._tag) {
1162
1180
  case "Any":
1163
1181
  return (s) => {
@@ -1177,7 +1195,7 @@ class Compiler {
1177
1195
  case "Literal": {
1178
1196
  const lit = node.literal as string
1179
1197
  if (node.caseless) {
1180
- const folded = Array.from(lit, (c) => fold(c.codePointAt(0)!))
1198
+ const folded = Array.from(lit, (c) => fold(firstCodePoint(c)))
1181
1199
  const n = folded.length
1182
1200
  return (s) => {
1183
1201
  const start = s.pos
@@ -1203,11 +1221,11 @@ class Compiler {
1203
1221
  }
1204
1222
  }
1205
1223
  const encoded = enc.encode(lit)
1206
- if (typeof encoded === "string") return stringLiteral(encoded, lit)
1224
+ if (Predicate.isString(encoded)) return stringLiteral(encoded, lit)
1207
1225
  const bytes = encoded as Uint8Array
1208
1226
  const len = bytes.length
1209
1227
  if (len === 1) {
1210
- const b0 = bytes[0]!
1228
+ const b0 = bytes[0]
1211
1229
  return (s) => {
1212
1230
  const pos = s.pos
1213
1231
  if (pos < s.end && (s.input as Uint8Array)[pos] === b0) {
@@ -1325,9 +1343,8 @@ class Compiler {
1325
1343
  // `Text.taken` checks that the text is well formed, then decodes it
1326
1344
  const fast = fastLimit(enc)
1327
1345
  const toText = enc.text
1328
- const unitAt: (input: any, i: number) => number = this.kind === 0
1329
- ? (input: string, i) => input.charCodeAt(i)
1330
- : (input: Uint8Array, i) => input[i]!
1346
+ const unitAt: (input: any, i: number) => number =
1347
+ this.kind === 0 ? (input: string, i) => input.charCodeAt(i) : (input: Uint8Array, i) => input[i]
1331
1348
  return (s) => {
1332
1349
  const start = s.pos
1333
1350
  if (c(s) === Fail) return Fail
@@ -1353,7 +1370,11 @@ class Compiler {
1353
1370
  const enc = taken.encoding
1354
1371
  if (enc !== undefined && !(this.native(enc) && enc.unit === 1 && enc.asciiCompatible)) return undefined
1355
1372
  const c = this.compile(node.self, true)
1356
- const scan = (this.kind === 0 ? scanFloatString : scanFloatBytes) as (input: unknown, i: number, end: number) => unknown
1373
+ const scan = (this.kind === 0 ? scanFloatString : scanFloatBytes) as (
1374
+ input: unknown,
1375
+ i: number,
1376
+ end: number
1377
+ ) => unknown
1357
1378
  const resumable = this.resumable
1358
1379
  return (s) => {
1359
1380
  const v = scan(s.input, s.pos, s.end)
@@ -1378,21 +1399,22 @@ class Compiler {
1378
1399
  // Matches the control character, advancing past it
1379
1400
  let control: (s: State<any>) => boolean
1380
1401
  if (enc === undefined || (ctrl < fastLimit(enc) && ctrl < TableSize)) {
1381
- control = kind === 0
1382
- ? (s) => {
1383
- if (s.pos < s.end && (s.input as string).charCodeAt(s.pos) === ctrl) {
1384
- s.pos++
1385
- return true
1386
- }
1387
- return false
1388
- }
1389
- : (s) => {
1390
- if (s.pos < s.end && (s.input as Uint8Array)[s.pos] === ctrl) {
1391
- s.pos++
1392
- return true
1393
- }
1394
- return false
1395
- }
1402
+ control =
1403
+ kind === 0
1404
+ ? (s) => {
1405
+ if (s.pos < s.end && (s.input as string).charCodeAt(s.pos) === ctrl) {
1406
+ s.pos++
1407
+ return true
1408
+ }
1409
+ return false
1410
+ }
1411
+ : (s) => {
1412
+ if (s.pos < s.end && (s.input as Uint8Array)[s.pos] === ctrl) {
1413
+ s.pos++
1414
+ return true
1415
+ }
1416
+ return false
1417
+ }
1396
1418
  } else {
1397
1419
  control = (s) => {
1398
1420
  if (s.pos >= s.end) return false
@@ -1411,9 +1433,8 @@ class Compiler {
1411
1433
  return Fail
1412
1434
  }
1413
1435
  if (raw) {
1414
- const slice: (s: State<any>, start: number) => unknown = enc === undefined
1415
- ? (s, start) => s.slice(start, s.pos)
1416
- : (s, start) => enc.text(s.input, start, s.pos)
1436
+ const slice: (s: State<any>, start: number) => unknown =
1437
+ enc === undefined ? (s, start) => s.slice(start, s.pos) : (s, start) => enc.text(s.input, start, s.pos)
1417
1438
  return (s) => {
1418
1439
  const start = s.pos
1419
1440
  let resumed = false
@@ -1572,7 +1593,7 @@ export const createClosureCompiler = (kind: 0 | 1 | 2, resumable = false): Closu
1572
1593
  }
1573
1594
 
1574
1595
  /** Compiles a root parser for one input kind. */
1575
- export const compileClosure = (
1596
+ const compileClosure = (
1576
1597
  root: ParserLike,
1577
1598
  kind: 0 | 1 | 2,
1578
1599
  resumable = false
@@ -1581,3 +1602,15 @@ export const compileClosure = (
1581
1602
  const step = compiler.compile(root, true)
1582
1603
  return { step, fallbacks: compiler.fallbacks }
1583
1604
  }
1605
+
1606
+ /** The closure compiler as a backend. */
1607
+ export const backend: Backend = {
1608
+ build: (p, kind, resumable) => {
1609
+ try {
1610
+ const { fallbacks, step } = compileClosure(p, kind, resumable)
1611
+ return { backend: "closure", step, fallbacks, error: undefined }
1612
+ } catch (error) {
1613
+ return { backend: undefined, step: undefined, fallbacks: [], error }
1614
+ }
1615
+ }
1616
+ }
@@ -2,25 +2,38 @@
2
2
  * The shapes of `map(p, Object.fromEntries)` that the compilers build
3
3
  * directly (see `internal/entries.ts`).
4
4
  */
5
+ import * as Predicate from "effect/Predicate"
5
6
  import type { Node, NodeOf, ParserLike } from "../node.js"
6
7
  import { nodeOf } from "../node.js"
7
8
 
9
+ /** A sequence that yields an entry: its parsers, and which of them produce the key and the value. */
10
+ export interface EntryPair {
11
+ readonly parsers: ReadonlyArray<ParserLike>
12
+ readonly key: number
13
+ readonly value: number
14
+ }
15
+
8
16
  /** A repeated pair for `Object.fromEntries`: the sequence and the indices of its key and value. */
9
- export const pairOf = (p: ParserLike): { readonly parsers: ReadonlyArray<ParserLike>; readonly key: number; readonly value: number } | undefined => {
17
+ export const pairOf = (p: ParserLike): EntryPair | undefined => {
10
18
  let node = nodeOf(p)
11
19
  while (isDiagnostic(node)) node = nodeOf(node.self)
12
20
  if (node._tag !== "Seq") return undefined
13
21
  const output = node.output
14
22
  if (output === "tuple") return node.parsers.length === 2 ? { parsers: node.parsers, key: 0, value: 1 } : undefined
15
- if (typeof output === "number" || output[0] === output[1]) return undefined
23
+ if (Predicate.isNumber(output) || output[0] === output[1]) return undefined
16
24
  return { parsers: node.parsers, key: output[0], value: output[1] }
17
25
  }
18
26
 
19
27
  const isDiagnostic = (
20
28
  node: Node
21
- ): node is NodeOf<"Context" | "Expected" | "MapError" | "OrFail" | "Trace" | "RetryAfter"> =>
22
- node._tag === "Context" || node._tag === "Expected" || node._tag === "MapError" || node._tag === "OrFail" ||
23
- node._tag === "Trace" || node._tag === "RetryAfter"
29
+ ): node is NodeOf<"Context" | "Expected" | "MapError" | "OrFail" | "Trace" | "Syntax" | "RetryAfter"> =>
30
+ node._tag === "Context" ||
31
+ node._tag === "Expected" ||
32
+ node._tag === "MapError" ||
33
+ node._tag === "OrFail" ||
34
+ node._tag === "Trace" ||
35
+ node._tag === "Syntax" ||
36
+ node._tag === "RetryAfter"
24
37
 
25
38
  /**
26
39
  * Whether `map(p, Object.fromEntries)` can build its object directly: `p`
@@ -28,9 +41,9 @@ const isDiagnostic = (
28
41
  * element of a sequence (`preceded`, `terminated`, `delimited`) and
29
42
  * diagnostics.
30
43
  */
31
- export const entriesShape = (p: ParserLike): boolean => {
44
+ export const buildsEntries = (p: ParserLike): boolean => {
32
45
  const node = nodeOf(p)
33
- if (isDiagnostic(node) || node._tag === "Mode") return entriesShape(node.self)
34
- if (node._tag === "Seq") return typeof node.output === "number" && entriesShape(node.parsers[node.output]!)
46
+ if (isDiagnostic(node) || node._tag === "Mode") return buildsEntries(node.self)
47
+ if (node._tag === "Seq") return Predicate.isNumber(node.output) && buildsEntries(node.parsers[node.output])
35
48
  return (node._tag === "Repeat" || node._tag === "Separated") && pairOf(node.self) !== undefined
36
49
  }