@thomasfosterau/effect-parse 0.0.0-stage → 0.0.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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +550 -2
  3. package/dist/Ascii.d.ts +392 -0
  4. package/dist/Ascii.d.ts.map +1 -0
  5. package/dist/Ascii.js +610 -0
  6. package/dist/Ascii.js.map +1 -0
  7. package/dist/Binary.d.ts +153 -0
  8. package/dist/Binary.d.ts.map +1 -0
  9. package/dist/Binary.js +171 -0
  10. package/dist/Binary.js.map +1 -0
  11. package/dist/Bits.d.ts +60 -0
  12. package/dist/Bits.d.ts.map +1 -0
  13. package/dist/Bits.js +144 -0
  14. package/dist/Bits.js.map +1 -0
  15. package/dist/Encoding.d.ts +116 -0
  16. package/dist/Encoding.d.ts.map +1 -0
  17. package/dist/Encoding.js +85 -0
  18. package/dist/Encoding.js.map +1 -0
  19. package/dist/Expression.d.ts +124 -0
  20. package/dist/Expression.d.ts.map +1 -0
  21. package/dist/Expression.js +139 -0
  22. package/dist/Expression.js.map +1 -0
  23. package/dist/Input.d.ts +301 -0
  24. package/dist/Input.d.ts.map +1 -0
  25. package/dist/Input.js +312 -0
  26. package/dist/Input.js.map +1 -0
  27. package/dist/Nom.d.ts +518 -0
  28. package/dist/Nom.d.ts.map +1 -0
  29. package/dist/Nom.js +519 -0
  30. package/dist/Nom.js.map +1 -0
  31. package/dist/ParseError.d.ts +120 -0
  32. package/dist/ParseError.d.ts.map +1 -0
  33. package/dist/ParseError.js +301 -0
  34. package/dist/ParseError.js.map +1 -0
  35. package/dist/Parser.d.ts +1312 -0
  36. package/dist/Parser.d.ts.map +1 -0
  37. package/dist/Parser.js +1936 -0
  38. package/dist/Parser.js.map +1 -0
  39. package/dist/ParserSchema.d.ts +31 -0
  40. package/dist/ParserSchema.d.ts.map +1 -0
  41. package/dist/ParserSchema.js +56 -0
  42. package/dist/ParserSchema.js.map +1 -0
  43. package/dist/ParserStream.d.ts +127 -0
  44. package/dist/ParserStream.d.ts.map +1 -0
  45. package/dist/ParserStream.js +270 -0
  46. package/dist/ParserStream.js.map +1 -0
  47. package/dist/Text.d.ts +330 -0
  48. package/dist/Text.d.ts.map +1 -0
  49. package/dist/Text.js +529 -0
  50. package/dist/Text.js.map +1 -0
  51. package/dist/Token.d.ts +225 -0
  52. package/dist/Token.d.ts.map +1 -0
  53. package/dist/Token.js +294 -0
  54. package/dist/Token.js.map +1 -0
  55. package/dist/index.d.ts +17 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +17 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/internal/compiler/closure.d.ts +25 -0
  60. package/dist/internal/compiler/closure.d.ts.map +1 -0
  61. package/dist/internal/compiler/closure.js +1668 -0
  62. package/dist/internal/compiler/closure.js.map +1 -0
  63. package/dist/internal/compiler/entries.d.ts +19 -0
  64. package/dist/internal/compiler/entries.d.ts.map +1 -0
  65. package/dist/internal/compiler/entries.js +32 -0
  66. package/dist/internal/compiler/entries.js.map +1 -0
  67. package/dist/internal/compiler/first.d.ts +33 -0
  68. package/dist/internal/compiler/first.d.ts.map +1 -0
  69. package/dist/internal/compiler/first.js +235 -0
  70. package/dist/internal/compiler/first.js.map +1 -0
  71. package/dist/internal/compiler/jit.d.ts +20 -0
  72. package/dist/internal/compiler/jit.d.ts.map +1 -0
  73. package/dist/internal/compiler/jit.js +1030 -0
  74. package/dist/internal/compiler/jit.js.map +1 -0
  75. package/dist/internal/compiler/registry.d.ts +34 -0
  76. package/dist/internal/compiler/registry.d.ts.map +1 -0
  77. package/dist/internal/compiler/registry.js +46 -0
  78. package/dist/internal/compiler/registry.js.map +1 -0
  79. package/dist/internal/compiler/sets.d.ts +29 -0
  80. package/dist/internal/compiler/sets.d.ts.map +1 -0
  81. package/dist/internal/compiler/sets.js +85 -0
  82. package/dist/internal/compiler/sets.js.map +1 -0
  83. package/dist/internal/entries.d.ts +27 -0
  84. package/dist/internal/entries.d.ts.map +1 -0
  85. package/dist/internal/entries.js +42 -0
  86. package/dist/internal/entries.js.map +1 -0
  87. package/dist/internal/node.d.ts +251 -0
  88. package/dist/internal/node.d.ts.map +1 -0
  89. package/dist/internal/node.js +73 -0
  90. package/dist/internal/node.js.map +1 -0
  91. package/dist/internal/numbers.d.ts +41 -0
  92. package/dist/internal/numbers.d.ts.map +1 -0
  93. package/dist/internal/numbers.js +302 -0
  94. package/dist/internal/numbers.js.map +1 -0
  95. package/dist/internal/resume.d.ts +39 -0
  96. package/dist/internal/resume.d.ts.map +1 -0
  97. package/dist/internal/resume.js +37 -0
  98. package/dist/internal/resume.js.map +1 -0
  99. package/dist/internal/text.d.ts +72 -0
  100. package/dist/internal/text.d.ts.map +1 -0
  101. package/dist/internal/text.js +391 -0
  102. package/dist/internal/text.js.map +1 -0
  103. package/dist/internal/tokens.d.ts +93 -0
  104. package/dist/internal/tokens.d.ts.map +1 -0
  105. package/dist/internal/tokens.js +400 -0
  106. package/dist/internal/tokens.js.map +1 -0
  107. package/package.json +115 -3
  108. package/src/Ascii.ts +741 -0
  109. package/src/Binary.ts +228 -0
  110. package/src/Bits.ts +192 -0
  111. package/src/Encoding.ts +128 -0
  112. package/src/Expression.ts +245 -0
  113. package/src/Input.ts +429 -0
  114. package/src/Nom.ts +642 -0
  115. package/src/ParseError.ts +378 -0
  116. package/src/Parser.ts +2763 -0
  117. package/src/ParserSchema.ts +87 -0
  118. package/src/ParserStream.ts +390 -0
  119. package/src/Text.ts +757 -0
  120. package/src/Token.ts +416 -0
  121. package/src/index.ts +16 -0
  122. package/src/internal/compiler/closure.ts +1583 -0
  123. package/src/internal/compiler/entries.ts +36 -0
  124. package/src/internal/compiler/first.ts +252 -0
  125. package/src/internal/compiler/jit.ts +1091 -0
  126. package/src/internal/compiler/registry.ts +79 -0
  127. package/src/internal/compiler/sets.ts +100 -0
  128. package/src/internal/entries.ts +51 -0
  129. package/src/internal/node.ts +207 -0
  130. package/src/internal/numbers.ts +291 -0
  131. package/src/internal/resume.ts +55 -0
  132. package/src/internal/text.ts +411 -0
  133. package/src/internal/tokens.ts +409 -0
@@ -0,0 +1,1583 @@
1
+ /**
2
+ * The closure compiler: turns a parser's description into specialized
3
+ * closures with the step signature. Compiled steps return a value, or
4
+ * `Fail` with only `state.mode` set: no diagnostics are recorded.
5
+ *
6
+ * Nothing here generates code, so it works under a Content Security
7
+ * Policy. Parsers that can't be compiled run their own (interpreted) steps
8
+ * on the same state.
9
+ */
10
+ import type { Encoding } from "../../Encoding.js"
11
+ import type { ErrMode, State } from "../../Input.js"
12
+ import { Fail } from "../../Input.js"
13
+ import * as Option from "effect/Option"
14
+ import type { Node, NodeOf, ParserLike } from "../node.js"
15
+ import { addEntry, flushEntries, fromEntries, isPlainKey } from "../entries.js"
16
+ import { nodeOf } from "../node.js"
17
+ import { floatScan, floatTexts, scanFloatBytes, scanFloatString, toFloat } from "../numbers.js"
18
+ import { charOf, fastLimit, fold, validate } from "../text.js"
19
+ import { countTokens, indexOf } from "../tokens.js"
20
+ import type { AltPlan } from "./first.js"
21
+ import * as Resume from "../resume.js"
22
+ import { entriesShape, pairOf } from "./entries.js"
23
+ import { FirstSets, planAlt } from "./first.js"
24
+ import type { CharSet } from "./sets.js"
25
+ import { kindOfEncoding, TableSize, textSet, tokenSet } from "./sets.js"
26
+
27
+ export type Step = (state: State<any>) => any
28
+
29
+ /** A parser that the compiler left to its interpreted step. */
30
+ export interface Fallback {
31
+ readonly name: string | undefined
32
+ readonly tag: Node["_tag"]
33
+ readonly reason: string
34
+ }
35
+
36
+ /** The maximum number of distinct parsers compiled for one root. */
37
+ export const maxNodes = 2048
38
+
39
+ const BT: ErrMode = "Backtrack"
40
+ const CUT: ErrMode = "Cut"
41
+ const INC: ErrMode = "Incomplete"
42
+
43
+ /**
44
+ * How many units past the cursor a single token can depend on (a code
45
+ * point is at most 4 units). In a partial parse, a token parser that read
46
+ * no closer than this to the end of the input behaves exactly as on
47
+ * complete input.
48
+ */
49
+ const Margin = 4
50
+
51
+ const lowerAscii = (c: number): number => c >= 65 && c <= 90 ? c + 32 : c
52
+
53
+ const isSuccess = (r: any): boolean => r._tag === "Success"
54
+ const isSome = (o: any): boolean => o._tag === "Some"
55
+
56
+ interface Slot {
57
+ fn: Step | undefined
58
+ }
59
+
60
+ class Compiler {
61
+ readonly used = new Map<ParserLike, Slot>()
62
+ readonly unused = new Map<ParserLike, Slot>()
63
+ readonly firsts: FirstSets
64
+ readonly fallbacks: Array<Fallback> = []
65
+ private readonly fellBack = new Set<ParserLike>()
66
+ private budget = maxNodes
67
+
68
+ /**
69
+ * `resumable`: compile for streaming parses, which may be partial (see
70
+ * `state.partial`) and keep checkpoints (see `internal/resume.ts`).
71
+ */
72
+ constructor(readonly kind: 0 | 1 | 2, readonly resumable = false) {
73
+ this.firsts = new FirstSets(kind)
74
+ }
75
+
76
+ /**
77
+ * In a resumable build, runs the compiled token parser `step`, and falls
78
+ * back to the interpreted one when it read within `lookahead` units of
79
+ * the end of a partial input, where it might have to report `Incomplete`.
80
+ * Compiled token parsers leave `state.pos` where they stopped reading,
81
+ * even when they fail.
82
+ */
83
+ private partialSafe(p: ParserLike, step: Step, lookahead: number): Step {
84
+ if (!this.resumable) return step
85
+ const interpreted = p.step
86
+ const ahead = Math.max(lookahead, Margin)
87
+ return (s) => {
88
+ if (!s.partial) return step(s)
89
+ const start = s.pos
90
+ const r = step(s)
91
+ if (s.pos + ahead <= s.end) return r
92
+ s.pos = start
93
+ return interpreted(s)
94
+ }
95
+ }
96
+
97
+ /** Compiles `p`; when `used` is false its output is not needed. */
98
+ compile(p: ParserLike, used: boolean): Step {
99
+ const memo = used ? this.used : this.unused
100
+ const slot = memo.get(p)
101
+ if (slot !== undefined) {
102
+ if (slot.fn !== undefined) return slot.fn
103
+ return (s) => slot.fn!(s) // a cycle through `suspend`
104
+ }
105
+ const fresh: Slot = { fn: undefined }
106
+ memo.set(p, fresh)
107
+ const node = nodeOf(p)
108
+ const fn = --this.budget < 0 ? this.fallback(p, node, "node budget exceeded") : this.build(p, node, used)
109
+ fresh.fn = fn
110
+ return fn
111
+ }
112
+
113
+ fallback(p: ParserLike, node: Node, reason: string): Step {
114
+ this.note(p, node, reason)
115
+ return p.step
116
+ }
117
+
118
+ /** Records that (part of) `p` runs interpreted. */
119
+ note(p: ParserLike, node: Node, reason: string): void {
120
+ if (!this.fellBack.has(p)) {
121
+ this.fellBack.add(p)
122
+ this.fallbacks.push({ name: p.name, tag: node._tag, reason })
123
+ }
124
+ }
125
+
126
+ /** Whether a token node reads this input kind natively. */
127
+ private native(enc: Encoding<any> | undefined): boolean {
128
+ return enc === undefined ? this.kind !== 2 : kindOfEncoding(enc) === this.kind
129
+ }
130
+
131
+ build(p: ParserLike, node: Node, used: boolean): Step {
132
+ switch (node._tag) {
133
+ case "Opaque":
134
+ return this.fallback(p, node, "not describable (Parser.make, gen, ...)")
135
+ case "Succeed": {
136
+ const value = node.value
137
+ return () => value
138
+ }
139
+ case "Sync": {
140
+ const evaluate = node.evaluate
141
+ return used ? () => evaluate() : () => undefined
142
+ }
143
+ case "Fail": {
144
+ const mode = node.mode
145
+ return (s) => {
146
+ s.mode = mode
147
+ return Fail
148
+ }
149
+ }
150
+ case "Eof":
151
+ return (s) => {
152
+ if (s.pos < s.end) {
153
+ s.mode = BT
154
+ return Fail
155
+ }
156
+ if (s.partial) {
157
+ s.mode = INC
158
+ return Fail
159
+ }
160
+ return undefined
161
+ }
162
+ case "Location":
163
+ return (s) => s.pos
164
+ case "RestLen":
165
+ return (s) => s.end - s.pos
166
+ case "Suspend":
167
+ return this.compile(node.resolve(), used)
168
+ case "Any":
169
+ case "Literal":
170
+ case "OneOf":
171
+ case "Take":
172
+ case "TakeWhile":
173
+ case "TakeUntil":
174
+ case "Rest":
175
+ if (!this.native(node.encoding)) return this.fallback(p, node, "token parser on array input")
176
+ return this.partialSafe(
177
+ p,
178
+ node.encoding === undefined ? this.token(p, node, used) : this.text(p, node, node.encoding, used),
179
+ node._tag === "Literal" || node._tag === "TakeUntil" ? node.literal.length * 4 : Margin
180
+ )
181
+ case "TillLineEnding":
182
+ case "Identifier":
183
+ case "Bom":
184
+ return this.fallback(p, node, "leaf parser without a compiled form")
185
+ case "Escaped":
186
+ if (!this.native(node.encoding)) return this.fallback(p, node, "token parser on array input")
187
+ return this.escaped(node)
188
+ case "Map": {
189
+ if (!used) return this.compile(node.self, false)
190
+ if (node.f === toFloat) {
191
+ const step = this.float(node)
192
+ if (step !== undefined) return step
193
+ }
194
+ // Streaming builds keep the array, which their checkpoints hold
195
+ if (node.f === fromEntries && !this.resumable && entriesShape(node.self)) return this.entries(node.self)
196
+ const c = this.compile(node.self, true)
197
+ const f = node.f
198
+ return (s) => {
199
+ const a = c(s)
200
+ return a === Fail ? Fail : f(a)
201
+ }
202
+ }
203
+ case "As": {
204
+ const c = this.compile(node.self, false)
205
+ if (!used) return c
206
+ const value = node.value
207
+ return (s) => c(s) === Fail ? Fail : value
208
+ }
209
+ case "Tap": {
210
+ const c = this.compile(node.self, true)
211
+ const f = node.f
212
+ return (s) => {
213
+ const a = c(s)
214
+ if (a !== Fail) f(a)
215
+ return a
216
+ }
217
+ }
218
+ case "TryMap": {
219
+ const c = this.compile(node.self, true)
220
+ const f = node.f
221
+ return (s) => {
222
+ const a = c(s)
223
+ if (a === Fail) return Fail
224
+ const r = f(a) as any
225
+ if (isSuccess(r)) return r.success
226
+ s.mode = BT
227
+ return Fail
228
+ }
229
+ }
230
+ case "FilterMap": {
231
+ const c = this.compile(node.self, true)
232
+ const f = node.f
233
+ return (s) => {
234
+ const a = c(s)
235
+ if (a === Fail) return Fail
236
+ const o = f(a) as any
237
+ if (isSome(o)) return o.value
238
+ s.mode = BT
239
+ return Fail
240
+ }
241
+ }
242
+ case "Filter": {
243
+ const c = this.compile(node.self, true)
244
+ const predicate = node.predicate
245
+ return (s) => {
246
+ const a = c(s)
247
+ if (a === Fail) return Fail
248
+ if (predicate(a)) return a
249
+ s.mode = BT
250
+ return Fail
251
+ }
252
+ }
253
+ case "FlatMap": {
254
+ // The parser returned by `f` runs interpreted
255
+ const c = this.compile(node.self, true)
256
+ const f = node.f
257
+ this.note(p, node, "the parser returned by the function runs interpreted")
258
+ return (s) => {
259
+ const a = c(s)
260
+ return a === Fail ? Fail : f(a).step(s)
261
+ }
262
+ }
263
+ case "Taken":
264
+ if (node.encoding !== undefined && !this.native(node.encoding)) {
265
+ return this.fallback(p, node, "Text parser on another input kind")
266
+ }
267
+ return this.taken(node, used)
268
+ case "WithTaken": {
269
+ const c = this.compile(node.self, true)
270
+ return (s) => {
271
+ const start = s.pos
272
+ const a = c(s)
273
+ return a === Fail ? Fail : [a, s.slice(start, s.pos)]
274
+ }
275
+ }
276
+ case "Span": {
277
+ const c = this.compile(node.self, false)
278
+ if (!used) return c
279
+ return (s) => {
280
+ const start = s.pos
281
+ return c(s) === Fail ? Fail : { start, end: s.pos }
282
+ }
283
+ }
284
+ case "WithSpan": {
285
+ const c = this.compile(node.self, true)
286
+ return (s) => {
287
+ const start = s.pos
288
+ const a = c(s)
289
+ return a === Fail ? Fail : [a, { start, end: s.pos }]
290
+ }
291
+ }
292
+ case "Mode": {
293
+ const c = this.compile(node.self, used)
294
+ const from = node.from
295
+ const to = node.to
296
+ return (s) => {
297
+ const a = c(s)
298
+ if (a === Fail && s.mode === from) s.mode = to
299
+ return a
300
+ }
301
+ }
302
+ case "Context":
303
+ case "Expected":
304
+ case "MapError":
305
+ case "OrFail":
306
+ case "Trace":
307
+ case "RetryAfter":
308
+ // Diagnostics only, or inactive outside parseRecoverable / tracing
309
+ return this.compile(node.self, used)
310
+ case "ResumeAfter": {
311
+ const c = this.compile(node.self, used)
312
+ if (!used) return c
313
+ return (s) => {
314
+ const a = c(s)
315
+ return a === Fail ? Fail : Option.some(a)
316
+ }
317
+ }
318
+ case "Opt":
319
+ case "OrElseSucceed": {
320
+ const c = this.compile(node.self, used)
321
+ const orElse = !used ? undefined : node._tag === "Opt" ? Option.none : node.value
322
+ const some = used && node._tag === "Opt"
323
+ return (s) => {
324
+ const start = s.pos
325
+ const a = c(s)
326
+ if (a !== Fail) return some ? Option.some(a) : a
327
+ if (s.mode !== BT) return Fail
328
+ s.pos = start
329
+ return orElse === undefined ? undefined : orElse()
330
+ }
331
+ }
332
+ case "Peek": {
333
+ const c = this.compile(node.self, used)
334
+ return (s) => {
335
+ const start = s.pos
336
+ const a = c(s)
337
+ if (a !== Fail) s.pos = start
338
+ return a
339
+ }
340
+ }
341
+ case "Not": {
342
+ const c = this.compile(node.self, false)
343
+ return (s) => {
344
+ const start = s.pos
345
+ if (c(s) !== Fail) {
346
+ s.pos = start
347
+ s.mode = BT
348
+ return Fail
349
+ }
350
+ if (s.mode !== BT) return Fail
351
+ s.pos = start
352
+ return undefined
353
+ }
354
+ }
355
+ case "Seq":
356
+ return this.seq(node, used)
357
+ case "Struct": {
358
+ const keys = node.keys
359
+ const steps = node.parsers.map((q) => this.compile(q, used))
360
+ const n = steps.length
361
+ if (!used) return this.chain(steps)
362
+ return (s) => {
363
+ const out: Record<string, unknown> = {}
364
+ for (let i = 0; i < n; i++) {
365
+ const a = steps[i]!(s)
366
+ if (a === Fail) return Fail
367
+ out[keys[i]!] = a
368
+ }
369
+ return out
370
+ }
371
+ }
372
+ case "ZipWith": {
373
+ const c1 = this.compile(node.self, used)
374
+ const c2 = this.compile(node.that, used)
375
+ if (!used) return this.chain([c1, c2])
376
+ const f = node.f
377
+ return (s) => {
378
+ const a = c1(s)
379
+ if (a === Fail) return Fail
380
+ const b = c2(s)
381
+ return b === Fail ? Fail : f(a, b)
382
+ }
383
+ }
384
+ case "Alt":
385
+ return this.alt(node, used)
386
+ case "Dispatch":
387
+ return this.dispatch(p, node, used)
388
+ case "Repeat":
389
+ case "Fold":
390
+ return this.repeat(node, used)
391
+ case "RepeatTill":
392
+ return this.repeatTill(node)
393
+ case "Separated":
394
+ return this.separated(node, used)
395
+ case "SeparatedFold":
396
+ return this.separatedFold(node)
397
+ case "Service":
398
+ case "Provide":
399
+ return this.fallback(p, node, "services stay interpreted")
400
+ }
401
+ }
402
+
403
+ /** Runs steps in order, for their effect on the cursor only. */
404
+ chain(steps: ReadonlyArray<Step>): Step {
405
+ const n = steps.length
406
+ if (n === 0) return () => undefined
407
+ if (n === 1) return steps[0]!
408
+ if (n === 2) {
409
+ const [a, b] = steps as [Step, Step]
410
+ return (s) => a(s) === Fail ? Fail : b(s)
411
+ }
412
+ return (s) => {
413
+ for (let i = 0; i < n; i++) if (steps[i]!(s) === Fail) return Fail
414
+ return undefined
415
+ }
416
+ }
417
+
418
+ seq(node: NodeOf<"Seq">, used: boolean): Step {
419
+ const output = node.output
420
+ const keep = (i: number) =>
421
+ used && (output === "tuple" || output === i || (typeof output === "object" && output.includes(i)))
422
+ const steps = node.parsers.map((q, i) => this.compile(q, keep(i)))
423
+ const n = steps.length
424
+ if (!used) return this.chain(steps)
425
+ if (output === "tuple") {
426
+ return (s) => {
427
+ const out = new Array(n)
428
+ for (let i = 0; i < n; i++) {
429
+ const a = steps[i]!(s)
430
+ if (a === Fail) return Fail
431
+ out[i] = a
432
+ }
433
+ return out
434
+ }
435
+ }
436
+ if (typeof output === "number") {
437
+ if (n === 2) {
438
+ const [a, b] = steps as [Step, Step]
439
+ return output === 0
440
+ ? (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)
446
+ }
447
+ return (s) => {
448
+ let out: unknown
449
+ for (let i = 0; i < n; i++) {
450
+ const a = steps[i]!(s)
451
+ if (a === Fail) return Fail
452
+ if (i === output) out = a
453
+ }
454
+ return out
455
+ }
456
+ }
457
+ const [i0, i1] = output
458
+ return (s) => {
459
+ let x: unknown
460
+ let y: unknown
461
+ for (let i = 0; i < n; i++) {
462
+ const a = steps[i]!(s)
463
+ if (a === Fail) return Fail
464
+ if (i === i0) x = a
465
+ else if (i === i1) y = a
466
+ }
467
+ return [x, y]
468
+ }
469
+ }
470
+
471
+ /** The unit at the cursor, or -1 at the end of the input. */
472
+ private unitReader(): (s: State<any>) => number {
473
+ 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
476
+ }
477
+
478
+ alt(node: NodeOf<"Alt">, used: boolean): Step {
479
+ const steps = node.parsers.map((q) => this.compile(q, used))
480
+ const n = steps.length
481
+ if (n === 0) {
482
+ return (s) => {
483
+ s.mode = BT
484
+ return Fail
485
+ }
486
+ }
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)))
491
+ if (plan === undefined) {
492
+ return (s) => {
493
+ const start = s.pos
494
+ for (let i = 0; i < n; i++) {
495
+ const a = steps[i]!(s)
496
+ if (a !== Fail) return a
497
+ if (s.mode !== BT) return Fail
498
+ s.pos = start
499
+ }
500
+ s.mode = BT
501
+ return Fail
502
+ }
503
+ }
504
+ const lists = plan.lists.map((l) => l.map((i) => steps[i]!))
505
+ const { byUnit, end, high } = plan
506
+ const unit = this.unitReader()
507
+ const resumable = this.resumable
508
+ return (s) => {
509
+ // Near the end of a partial input, branches may be incomplete rather than fail
510
+ 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]!
512
+ const start = s.pos
513
+ for (let i = 0; i < candidates.length; i++) {
514
+ const a = candidates[i]!(s)
515
+ if (a !== Fail) return a
516
+ if (s.mode !== BT) return Fail
517
+ s.pos = start
518
+ }
519
+ s.mode = BT
520
+ return Fail
521
+ }
522
+ }
523
+
524
+ dispatch(p: ParserLike, node: NodeOf<"Dispatch">, used: boolean): Step {
525
+ const selector = this.compile(node.selector, true)
526
+ const choose = node.choose
527
+ const compiled = new Map<ParserLike, Step>()
528
+ if (node.cases !== undefined) {
529
+ for (const key of Reflect.ownKeys(node.cases)) {
530
+ const q = node.cases[key as any]!
531
+ if (!compiled.has(q)) compiled.set(q, this.compile(q, used))
532
+ }
533
+ }
534
+ if (node.otherwise !== undefined && !compiled.has(node.otherwise)) {
535
+ compiled.set(node.otherwise, this.compile(node.otherwise, used))
536
+ }
537
+ const general: Step = (s) => {
538
+ const start = s.pos
539
+ const key = selector(s)
540
+ if (key === Fail) return Fail
541
+ const q = choose(key)
542
+ if (q === undefined) {
543
+ s.pos = start
544
+ s.mode = BT
545
+ return Fail
546
+ }
547
+ return (compiled.get(q) ?? q.step)(s)
548
+ }
549
+ if (node.cases === undefined) {
550
+ this.note(p, node, "the parser returned by the function of cases runs interpreted")
551
+ return general
552
+ }
553
+ // Switch on the next unit when the selector is `any` or `peek(any)`
554
+ let sel = nodeOf(node.selector)
555
+ const peek = sel._tag === "Peek"
556
+ if (peek) sel = nodeOf((sel as NodeOf<"Peek">).self)
557
+ if (sel._tag !== "Any" || !this.native(sel.encoding)) return general
558
+ const enc = sel.encoding
559
+ // Units below `limit` are whole tokens, so the key is known in advance
560
+ const limit = enc === undefined ? (this.kind === 0 ? 0xd800 : TableSize) : Math.min(fastLimit(enc), 0xd800)
561
+ const size = Math.min(limit, TableSize)
562
+ if (size === 0) return general
563
+ const table: Array<Step | null> = []
564
+ for (let u = 0; u < size; u++) {
565
+ const key = enc === undefined && this.kind === 1 ? u : charOf(u)
566
+ const q = choose(key)
567
+ table.push(q === undefined ? null : compiled.get(q) ?? q.step)
568
+ }
569
+ const unit = this.unitReader()
570
+ const advance = peek ? 0 : 1
571
+ const resumable = this.resumable
572
+ return (s) => {
573
+ if (resumable && s.partial && s.pos + Margin > s.end) return general(s)
574
+ const u = unit(s)
575
+ if (u < 0) {
576
+ s.mode = BT
577
+ return Fail
578
+ }
579
+ if (u >= size) return general(s)
580
+ const q = table[u]!
581
+ if (q === null) {
582
+ s.mode = BT
583
+ return Fail
584
+ }
585
+ s.pos += advance
586
+ return q(s)
587
+ }
588
+ }
589
+
590
+ repeat(node: NodeOf<"Repeat"> | NodeOf<"Fold">, used: boolean): Step {
591
+ const { max, min } = node
592
+ const checkLoop = max === Infinity
593
+ const isFold = node._tag === "Fold"
594
+ const c = this.compile(node.self, used)
595
+ const resumable = this.resumable
596
+ 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
600
+ ? (acc) => acc
601
+ : isFold
602
+ ? (node as NodeOf<"Fold">).f
603
+ : (acc, a) => {
604
+ acc.push(a)
605
+ return acc
606
+ }
607
+ return (s) => {
608
+ const entry = s.pos
609
+ let acc: unknown
610
+ let count = 0
611
+ let resumed = false
612
+ if (resumable && s.resume !== undefined) {
613
+ const cp = Resume.take(s, key, entry) as Resume.Done | { acc: unknown; count: number; pos: number } | undefined
614
+ if (cp instanceof Resume.Done) {
615
+ s.pos = cp.pos
616
+ return cp.value
617
+ }
618
+ if (cp !== undefined) {
619
+ acc = cp.acc
620
+ count = cp.count
621
+ s.pos = cp.pos
622
+ resumed = true
623
+ }
624
+ }
625
+ if (!resumed) acc = init()
626
+ for (; count < max; count++) {
627
+ const start = s.pos
628
+ const a = c(s)
629
+ if (a === Fail) {
630
+ if (s.mode === INC && s.resume !== undefined) Resume.save(s, key, entry, { acc, count, pos: start })
631
+ if (s.mode !== BT || count < min) return Fail
632
+ s.pos = start
633
+ return resumed ? Resume.done(s, key, entry, true, acc) : acc
634
+ }
635
+ if (checkLoop && s.pos === start) {
636
+ s.mode = CUT
637
+ return Fail
638
+ }
639
+ acc = add(acc, a)
640
+ }
641
+ return resumed ? Resume.done(s, key, entry, true, acc) : acc
642
+ }
643
+ }
644
+
645
+ /**
646
+ * `map(self, Object.fromEntries)`, where `entriesShape(self)`: builds the
647
+ * object directly (see the JIT).
648
+ */
649
+ private entries(p: ParserLike): Step {
650
+ const node = nodeOf(p)
651
+ switch (node._tag) {
652
+ case "Mode": {
653
+ const c = this.entries(node.self)
654
+ const from = node.from
655
+ const to = node.to
656
+ return (s) => {
657
+ const a = c(s)
658
+ if (a === Fail && s.mode === from) s.mode = to
659
+ return a
660
+ }
661
+ }
662
+ case "Seq": {
663
+ const i = node.output as number
664
+ const steps = node.parsers.map((q, j) => j === i ? this.entries(q) : this.compile(q, false))
665
+ return (s) => {
666
+ let out: unknown
667
+ for (let j = 0; j < steps.length; j++) {
668
+ const a = steps[j]!(s)
669
+ if (a === Fail) return Fail
670
+ if (j === i) out = a
671
+ }
672
+ return out
673
+ }
674
+ }
675
+ case "Repeat":
676
+ case "Separated":
677
+ return this.entriesLoop(node)
678
+ default:
679
+ return this.entries((node as { readonly self: ParserLike }).self)
680
+ }
681
+ }
682
+
683
+ private entriesLoop(node: NodeOf<"Repeat"> | NodeOf<"Separated">): Step {
684
+ const pair = pairOf(node.self)!
685
+ const parts = pair.parsers.map((q, j) => this.compile(q, j === pair.key || j === pair.value))
686
+ const { key: ki, value: vi } = pair
687
+ // Parses an entry and adds it to `acc`
688
+ const entry = (s: State<any>, acc: EntriesAcc): boolean => {
689
+ let key: unknown
690
+ let value: unknown
691
+ for (let j = 0; j < parts.length; j++) {
692
+ const a = parts[j]!(s)
693
+ if (a === Fail) return false
694
+ if (j === ki) key = a
695
+ else if (j === vi) value = a
696
+ }
697
+ acc.add(key, value)
698
+ return true
699
+ }
700
+ const { max, min } = node
701
+ const checkLoop = max === Infinity
702
+ if (node._tag === "Repeat") {
703
+ return (s) => {
704
+ const acc = new EntriesAcc()
705
+ for (let count = 0; count < max; count++) {
706
+ const start = s.pos
707
+ let key: unknown
708
+ let value: unknown
709
+ let ok = true
710
+ for (let j = 0; j < parts.length; j++) {
711
+ const a = parts[j]!(s)
712
+ if (a === Fail) {
713
+ ok = false
714
+ break
715
+ }
716
+ if (j === ki) key = a
717
+ else if (j === vi) value = a
718
+ }
719
+ if (!ok) {
720
+ if (s.mode !== BT || count < min) return Fail
721
+ s.pos = start
722
+ return acc.result()
723
+ }
724
+ if (checkLoop && s.pos === start) {
725
+ s.mode = CUT
726
+ return Fail
727
+ }
728
+ acc.add(key, value)
729
+ }
730
+ return acc.result()
731
+ }
732
+ }
733
+ const sep = this.compile(node.separator, false)
734
+ return (s) => {
735
+ const acc = new EntriesAcc()
736
+ let count = 0
737
+ if (max === 0) return acc.result()
738
+ let start = s.pos
739
+ if (!entry(s, acc)) {
740
+ if (s.mode !== BT || min > 0) return Fail
741
+ s.pos = start
742
+ return acc.result()
743
+ }
744
+ count++
745
+ while (count < max) {
746
+ start = s.pos
747
+ if (sep(s) === Fail) {
748
+ if (s.mode !== BT || count < min) return Fail
749
+ s.pos = start
750
+ return acc.result()
751
+ }
752
+ if (checkLoop && s.pos === start) {
753
+ s.mode = CUT
754
+ return Fail
755
+ }
756
+ if (!entry(s, acc)) {
757
+ if (s.mode !== BT || count < min) return Fail
758
+ s.pos = start
759
+ return acc.result()
760
+ }
761
+ count++
762
+ }
763
+ return acc.result()
764
+ }
765
+ }
766
+
767
+ repeatTill(node: NodeOf<"RepeatTill">): Step {
768
+ const { max, min } = node
769
+ const c = this.compile(node.self, true)
770
+ const end = this.compile(node.end, true)
771
+ return (s) => {
772
+ const acc: Array<unknown> = []
773
+ while (true) {
774
+ const start = s.pos
775
+ if (acc.length >= min) {
776
+ const b = end(s)
777
+ if (b !== Fail) return [acc, b]
778
+ if (s.mode !== BT) return Fail
779
+ s.pos = start
780
+ if (acc.length >= max) return Fail
781
+ }
782
+ const a = c(s)
783
+ if (a === Fail) return Fail
784
+ if (s.pos === start) {
785
+ s.mode = CUT
786
+ return Fail
787
+ }
788
+ acc.push(a)
789
+ }
790
+ }
791
+ }
792
+
793
+ separated(node: NodeOf<"Separated">, used: boolean): Step {
794
+ const { max, min } = node
795
+ const checkLoop = max === Infinity
796
+ const c = this.compile(node.self, used)
797
+ const sep = this.compile(node.separator, false)
798
+ const resumable = this.resumable
799
+ const key = {}
800
+ return (s) => {
801
+ const entry = s.pos
802
+ let acc: Array<unknown> | undefined
803
+ let count = 0
804
+ let resumed = false
805
+ if (resumable && s.resume !== undefined) {
806
+ const cp = Resume.take(s, key, entry) as Resume.Done | { acc: Array<unknown>; count: number; pos: number } | undefined
807
+ if (cp instanceof Resume.Done) {
808
+ s.pos = cp.pos
809
+ return cp.value
810
+ }
811
+ if (cp !== undefined) {
812
+ acc = cp.acc
813
+ count = cp.count
814
+ s.pos = cp.pos
815
+ resumed = true
816
+ }
817
+ }
818
+ if (!resumed) {
819
+ acc = used ? [] : undefined
820
+ if (max === 0) return acc
821
+ const start = s.pos
822
+ const first = c(s)
823
+ if (first === Fail) {
824
+ if (s.mode !== BT || min > 0) return Fail
825
+ s.pos = start
826
+ return acc
827
+ }
828
+ acc?.push(first)
829
+ count = 1
830
+ }
831
+ while (count < max) {
832
+ const start = s.pos
833
+ if (sep(s) === Fail) {
834
+ if (s.mode === INC && s.resume !== undefined) Resume.save(s, key, entry, { acc, count, pos: start })
835
+ if (s.mode !== BT || count < min) return Fail
836
+ s.pos = start
837
+ return resumed ? Resume.done(s, key, entry, true, acc) : acc
838
+ }
839
+ if (checkLoop && s.pos === start) {
840
+ s.mode = CUT
841
+ return Fail
842
+ }
843
+ const a = c(s)
844
+ if (a === Fail) {
845
+ if (s.mode === INC && s.resume !== undefined) Resume.save(s, key, entry, { acc, count, pos: start })
846
+ if (s.mode !== BT || count < min) return Fail
847
+ s.pos = start
848
+ return resumed ? Resume.done(s, key, entry, true, acc) : acc
849
+ }
850
+ acc?.push(a)
851
+ count++
852
+ }
853
+ return resumed ? Resume.done(s, key, entry, true, acc) : acc
854
+ }
855
+ }
856
+
857
+ separatedFold(node: NodeOf<"SeparatedFold">): Step {
858
+ const c = this.compile(node.self, true)
859
+ const sep = this.compile(node.separator, true)
860
+ const op = node.op
861
+ const resumable = this.resumable
862
+ const key = {}
863
+ if (!node.right) {
864
+ return (s) => {
865
+ const entry = s.pos
866
+ let acc: unknown
867
+ let resumed = false
868
+ if (resumable && s.resume !== undefined) {
869
+ const cp = Resume.take(s, key, entry) as Resume.Done | { acc: unknown; pos: number } | undefined
870
+ if (cp instanceof Resume.Done) {
871
+ s.pos = cp.pos
872
+ return cp.value
873
+ }
874
+ if (cp !== undefined) {
875
+ acc = cp.acc
876
+ s.pos = cp.pos
877
+ resumed = true
878
+ }
879
+ }
880
+ if (!resumed) {
881
+ acc = c(s)
882
+ if (acc === Fail) return Fail
883
+ }
884
+ while (true) {
885
+ const start = s.pos
886
+ const x = sep(s)
887
+ if (x === Fail) {
888
+ if (s.mode === INC && s.resume !== undefined) Resume.save(s, key, entry, { acc, pos: start })
889
+ if (s.mode !== BT) return Fail
890
+ s.pos = start
891
+ return resumed ? Resume.done(s, key, entry, true, acc) : acc
892
+ }
893
+ if (s.pos === start) {
894
+ s.mode = CUT
895
+ return Fail
896
+ }
897
+ const a = c(s)
898
+ if (a === Fail) {
899
+ if (s.mode === INC && s.resume !== undefined) Resume.save(s, key, entry, { acc, pos: start })
900
+ if (s.mode !== BT) return Fail
901
+ s.pos = start
902
+ return resumed ? Resume.done(s, key, entry, true, acc) : acc
903
+ }
904
+ acc = op(acc, x, a)
905
+ }
906
+ }
907
+ }
908
+ return (s) => {
909
+ const first = c(s)
910
+ if (first === Fail) return Fail
911
+ const values: Array<unknown> = [first]
912
+ const seps: Array<unknown> = []
913
+ while (true) {
914
+ const start = s.pos
915
+ const x = sep(s)
916
+ if (x === Fail) {
917
+ if (s.mode !== BT) return Fail
918
+ s.pos = start
919
+ break
920
+ }
921
+ if (s.pos === start) {
922
+ s.mode = CUT
923
+ return Fail
924
+ }
925
+ const a = c(s)
926
+ if (a === Fail) {
927
+ if (s.mode !== BT) return Fail
928
+ s.pos = start
929
+ break
930
+ }
931
+ seps.push(x)
932
+ values.push(a)
933
+ }
934
+ let acc = values[values.length - 1]
935
+ for (let i = seps.length - 1; i >= 0; i--) acc = op(values[i], seps[i], acc)
936
+ return acc
937
+ }
938
+ }
939
+
940
+ // ---------------------------------------------------------------------------
941
+ // Token parsers (`Token`, `Ascii`)
942
+ // ---------------------------------------------------------------------------
943
+
944
+ token(p: ParserLike, node: TokenNode, used: boolean): Step {
945
+ const kind = this.kind as 0 | 1
946
+ switch (node._tag) {
947
+ case "Any":
948
+ if (kind === 1) {
949
+ return (s) => {
950
+ const pos = s.pos
951
+ if (pos >= s.end) return fail(s)
952
+ s.pos = pos + 1
953
+ return (s.input as Uint8Array)[pos]
954
+ }
955
+ }
956
+ return (s) => {
957
+ const pos = s.pos
958
+ if (pos >= s.end) return fail(s)
959
+ const str = s.input as string
960
+ const c = str.charCodeAt(pos)
961
+ if (c >= 0xd800 && c <= 0xdbff && pos + 1 < str.length && (str.charCodeAt(pos + 1) & 0xfc00) === 0xdc00) {
962
+ s.pos = pos + 2
963
+ return used ? str.slice(pos, pos + 2) : undefined
964
+ }
965
+ s.pos = pos + 1
966
+ return used ? str[pos] : undefined
967
+ }
968
+ case "Literal":
969
+ return this.tokenLiteral(p, node)
970
+ case "OneOf": {
971
+ const set = tokenSet(node.set, kind)
972
+ const negate = node.negate
973
+ const accept = acceptTable(set, negate)
974
+ const test = set.test
975
+ if (kind === 1) {
976
+ return (s) => {
977
+ const pos = s.pos
978
+ if (pos >= s.end) return fail(s)
979
+ const b = (s.input as Uint8Array)[pos]!
980
+ if (accept[b] === 0) return fail(s)
981
+ s.pos = pos + 1
982
+ return b
983
+ }
984
+ }
985
+ return (s) => {
986
+ const pos = s.pos
987
+ if (pos >= s.end) return fail(s)
988
+ const str = s.input as string
989
+ const c = str.charCodeAt(pos)
990
+ if (c < TableSize) {
991
+ if (accept[c] === 0) return fail(s)
992
+ s.pos = pos + 1
993
+ return used ? str[pos] : undefined
994
+ }
995
+ let cp = c
996
+ if (c >= 0xd800 && c <= 0xdbff && pos + 1 < str.length) {
997
+ const d = str.charCodeAt(pos + 1)
998
+ if ((d & 0xfc00) === 0xdc00) cp = ((c - 0xd800) << 10) + (d - 0xdc00) + 0x10000
999
+ }
1000
+ if (test(cp) === negate) return fail(s)
1001
+ const len = cp > 0xffff ? 2 : 1
1002
+ s.pos = pos + len
1003
+ return !used ? undefined : len === 1 ? str[pos] : str.slice(pos, pos + 2)
1004
+ }
1005
+ }
1006
+ case "Take": {
1007
+ const count = node.count
1008
+ if (kind === 1) {
1009
+ return (s) => {
1010
+ const start = s.pos
1011
+ if (s.end - start < count) return failAt(s, s.end)
1012
+ s.pos = start + count
1013
+ return used ? (s.input as Uint8Array).subarray(start, start + count) : undefined
1014
+ }
1015
+ }
1016
+ return (s) => {
1017
+ const str = s.input as string
1018
+ const start = s.pos
1019
+ const end = s.end
1020
+ let i = start
1021
+ let got = 0
1022
+ while (got < count && i < end) {
1023
+ const c = str.charCodeAt(i)
1024
+ i += c >= 0xd800 && c <= 0xdbff && i + 1 < str.length && (str.charCodeAt(i + 1) & 0xfc00) === 0xdc00 ? 2 : 1
1025
+ got++
1026
+ }
1027
+ if (got < count) return failAt(s, i)
1028
+ s.pos = i
1029
+ return used ? str.slice(start, i) : undefined
1030
+ }
1031
+ }
1032
+ case "TakeWhile":
1033
+ return this.tokenTakeWhile(node, used)
1034
+ case "TakeUntil": {
1035
+ const { literal, max, min } = node
1036
+ const bounded = min > 0 || max !== Infinity
1037
+ return (s) => {
1038
+ const start = s.pos
1039
+ const idx = indexOf(s, literal, start)
1040
+ if (idx < 0) return failAt(s, s.end)
1041
+ if (bounded) {
1042
+ const count = countTokens(s, start, idx)
1043
+ if (count < min || count > max) return failAt(s, idx)
1044
+ }
1045
+ s.pos = idx
1046
+ return used ? s.slice(start, idx) : undefined
1047
+ }
1048
+ }
1049
+ case "Rest":
1050
+ return (s) => {
1051
+ const start = s.pos
1052
+ s.pos = s.end
1053
+ return used ? s.slice(start, s.end) : undefined
1054
+ }
1055
+ }
1056
+ }
1057
+
1058
+ tokenLiteral(p: ParserLike, node: NodeOf<"Literal">): Step {
1059
+ const lit = node.literal
1060
+ const literalKind = typeof lit === "string" ? 0 : lit instanceof Uint8Array ? 1 : 2
1061
+ if (this.kind === 0) {
1062
+ if (literalKind !== 0) return this.fallback(p, node, "byte or array literal on string input")
1063
+ const str = lit as string
1064
+ const len = str.length
1065
+ if (node.caseless) {
1066
+ const lowered = Array.from({ length: len }, (_, i) => lowerAscii(str.charCodeAt(i)))
1067
+ return (s) => {
1068
+ const pos = s.pos
1069
+ if (s.end - pos < len) return fail(s)
1070
+ const input = s.input as string
1071
+ for (let i = 0; i < len; i++) {
1072
+ if (lowerAscii(input.charCodeAt(pos + i)) !== lowered[i]) return fail(s)
1073
+ }
1074
+ s.pos = pos + len
1075
+ return input.slice(pos, pos + len)
1076
+ }
1077
+ }
1078
+ return stringLiteral(str, str)
1079
+ }
1080
+ 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
1082
+ const len = bytes.length
1083
+ const sameKind = literalKind === 1 && !node.caseless
1084
+ const expect = node.caseless ? Array.from(bytes, lowerAscii) : Array.from(bytes)
1085
+ const caseless = node.caseless
1086
+ return (s) => {
1087
+ const pos = s.pos
1088
+ if (s.end - pos < len) return fail(s)
1089
+ const input = s.input as Uint8Array
1090
+ for (let i = 0; i < len; i++) {
1091
+ const b = input[pos + i]!
1092
+ if ((caseless ? lowerAscii(b) : b) !== expect[i]) return fail(s)
1093
+ }
1094
+ s.pos = pos + len
1095
+ return sameKind ? lit : input.subarray(pos, pos + len)
1096
+ }
1097
+ }
1098
+
1099
+ tokenTakeWhile(node: NodeOf<"TakeWhile">, used: boolean): Step {
1100
+ const kind = this.kind as 0 | 1
1101
+ const { max, min, negate } = node
1102
+ const set = tokenSet(node.set, kind)
1103
+ const accept = acceptTable(set, negate)
1104
+ if (kind === 1) {
1105
+ return (s) => {
1106
+ const bytes = s.input as Uint8Array
1107
+ const start = s.pos
1108
+ const limit = max === Infinity ? s.end : Math.min(s.end, start + max)
1109
+ let i = start
1110
+ while (i < limit && accept[bytes[i]!] === 1) i++
1111
+ if (i - start < min) return failAt(s, i)
1112
+ s.pos = i
1113
+ return used ? bytes.subarray(start, i) : undefined
1114
+ }
1115
+ }
1116
+ const test = set.test
1117
+ return (s) => {
1118
+ const str = s.input as string
1119
+ const start = s.pos
1120
+ const end = s.end
1121
+ let i = start
1122
+ let count = 0
1123
+ while (count < max && i < end) {
1124
+ const c = str.charCodeAt(i)
1125
+ if (c < TableSize) {
1126
+ if (accept[c] === 0) break
1127
+ i++
1128
+ } else if (c < 0xd800 || c > 0xdbff) {
1129
+ if (test(c) === negate) break
1130
+ i++
1131
+ } else {
1132
+ let cp = c
1133
+ if (i + 1 < str.length) {
1134
+ const d = str.charCodeAt(i + 1)
1135
+ if ((d & 0xfc00) === 0xdc00) cp = ((c - 0xd800) << 10) + (d - 0xdc00) + 0x10000
1136
+ }
1137
+ if (test(cp) === negate) break
1138
+ i += cp > 0xffff ? 2 : 1
1139
+ }
1140
+ count++
1141
+ }
1142
+ if (count < min) return failAt(s, i)
1143
+ s.pos = i
1144
+ return used ? str.slice(start, i) : undefined
1145
+ }
1146
+ }
1147
+
1148
+ // ---------------------------------------------------------------------------
1149
+ // Text parsers, specialized per encoding
1150
+ // ---------------------------------------------------------------------------
1151
+
1152
+ text(p: ParserLike, node: TokenNode, enc: Encoding<any>, used: boolean): Step {
1153
+ const kind = this.kind as 0 | 1
1154
+ const fast = fastLimit(enc)
1155
+ const decode = enc.decode
1156
+ const width = enc.width
1157
+ 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]!
1161
+ switch (node._tag) {
1162
+ case "Any":
1163
+ return (s) => {
1164
+ const pos = s.pos
1165
+ const end = s.end
1166
+ if (pos >= end) return fail(s)
1167
+ const c = unitAt(s.input, pos)
1168
+ if (c < fast) {
1169
+ s.pos = pos + 1
1170
+ return used ? charOf(c) : undefined
1171
+ }
1172
+ const cp = decode(s.input, pos, end)
1173
+ if (cp < 0) return fail(s)
1174
+ s.pos = pos + width(cp)
1175
+ return used ? charOf(cp) : undefined
1176
+ }
1177
+ case "Literal": {
1178
+ const lit = node.literal as string
1179
+ if (node.caseless) {
1180
+ const folded = Array.from(lit, (c) => fold(c.codePointAt(0)!))
1181
+ const n = folded.length
1182
+ return (s) => {
1183
+ const start = s.pos
1184
+ const end = s.end
1185
+ const input = s.input
1186
+ let pos = start
1187
+ for (let i = 0; i < n; i++) {
1188
+ if (pos >= end) return failAt(s, pos)
1189
+ const c = unitAt(input, pos)
1190
+ let cp: number
1191
+ if (c < fast) {
1192
+ cp = c
1193
+ pos++
1194
+ } else {
1195
+ cp = decode(input, pos, end)
1196
+ if (cp < 0) return failAt(s, pos)
1197
+ pos += width(cp)
1198
+ }
1199
+ if (fold(cp) !== folded[i]) return failAt(s, pos)
1200
+ }
1201
+ s.pos = pos
1202
+ return used ? toText(input, start, pos) : undefined
1203
+ }
1204
+ }
1205
+ const encoded = enc.encode(lit)
1206
+ if (typeof encoded === "string") return stringLiteral(encoded, lit)
1207
+ const bytes = encoded as Uint8Array
1208
+ const len = bytes.length
1209
+ if (len === 1) {
1210
+ const b0 = bytes[0]!
1211
+ return (s) => {
1212
+ const pos = s.pos
1213
+ if (pos < s.end && (s.input as Uint8Array)[pos] === b0) {
1214
+ s.pos = pos + 1
1215
+ return lit
1216
+ }
1217
+ return fail(s)
1218
+ }
1219
+ }
1220
+ return (s) => {
1221
+ const pos = s.pos
1222
+ if (s.end - pos < len) return fail(s)
1223
+ const input = s.input as Uint8Array
1224
+ for (let i = 0; i < len; i++) if (input[pos + i] !== bytes[i]) return fail(s)
1225
+ s.pos = pos + len
1226
+ return lit
1227
+ }
1228
+ }
1229
+ case "OneOf": {
1230
+ const set = textSet(node.set)
1231
+ const negate = node.negate
1232
+ const accept = acceptTable(set, negate)
1233
+ const test = set.test
1234
+ return (s) => {
1235
+ const pos = s.pos
1236
+ const end = s.end
1237
+ if (pos >= end) return fail(s)
1238
+ const c = unitAt(s.input, pos)
1239
+ if (c < fast && c < TableSize) {
1240
+ if (accept[c] === 0) return fail(s)
1241
+ s.pos = pos + 1
1242
+ return used ? charOf(c) : undefined
1243
+ }
1244
+ const cp = c < fast ? c : decode(s.input, pos, end)
1245
+ if (cp < 0 || test(cp) === negate) return fail(s)
1246
+ s.pos = pos + (c < fast ? 1 : width(cp))
1247
+ return used ? charOf(cp) : undefined
1248
+ }
1249
+ }
1250
+ case "Take": {
1251
+ const count = node.count
1252
+ return (s) => {
1253
+ const start = s.pos
1254
+ const end = s.end
1255
+ const input = s.input
1256
+ let pos = start
1257
+ for (let got = 0; got < count; got++) {
1258
+ if (pos >= end) return failAt(s, pos)
1259
+ const c = unitAt(input, pos)
1260
+ if (c < fast) pos++
1261
+ else {
1262
+ const cp = decode(input, pos, end)
1263
+ if (cp < 0) return failAt(s, pos)
1264
+ pos += width(cp)
1265
+ }
1266
+ }
1267
+ s.pos = pos
1268
+ return used ? toText(input, start, pos) : undefined
1269
+ }
1270
+ }
1271
+ case "TakeWhile": {
1272
+ const { max, min, negate } = node
1273
+ const set = textSet(node.set)
1274
+ const accept = acceptTable(set, negate)
1275
+ const test = set.test
1276
+ const small = Math.min(fast, TableSize)
1277
+ return (s) => {
1278
+ const input = s.input
1279
+ const start = s.pos
1280
+ const end = s.end
1281
+ let i = start
1282
+ let count = 0
1283
+ while (count < max && i < end) {
1284
+ const c = unitAt(input, i)
1285
+ if (c < small) {
1286
+ if (accept[c] === 0) break
1287
+ i++
1288
+ } else if (c < fast) {
1289
+ if (test(c) === negate) break
1290
+ i++
1291
+ } else {
1292
+ const cp = decode(input, i, end)
1293
+ if (cp < 0 || test(cp) === negate) break
1294
+ i += width(cp)
1295
+ }
1296
+ count++
1297
+ }
1298
+ if (count < min) return failAt(s, i)
1299
+ s.pos = i
1300
+ return used ? toText(input, start, i) : undefined
1301
+ }
1302
+ }
1303
+ case "TakeUntil":
1304
+ case "Rest":
1305
+ return this.fallback(p, node, "Text parser without a compiled form")
1306
+ }
1307
+ }
1308
+
1309
+ taken(node: NodeOf<"Taken">, used: boolean): Step {
1310
+ const c = this.compile(node.self, false)
1311
+ const enc = node.encoding
1312
+ if (enc === undefined) {
1313
+ if (!used) return c
1314
+ if (this.kind === 0) {
1315
+ return (s) => {
1316
+ const start = s.pos
1317
+ return c(s) === Fail ? Fail : (s.input as string).slice(start, s.pos)
1318
+ }
1319
+ }
1320
+ return (s) => {
1321
+ const start = s.pos
1322
+ return c(s) === Fail ? Fail : s.slice(start, s.pos)
1323
+ }
1324
+ }
1325
+ // `Text.taken` checks that the text is well formed, then decodes it
1326
+ const fast = fastLimit(enc)
1327
+ 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]!
1331
+ return (s) => {
1332
+ const start = s.pos
1333
+ if (c(s) === Fail) return Fail
1334
+ const end = s.pos
1335
+ const input = s.input
1336
+ let i = start
1337
+ while (i < end && unitAt(input, i) < fast) i++
1338
+ if (i < end && validate(enc, s, i, end) < 0) return fail(s)
1339
+ return used ? toText(input, start, end) : undefined
1340
+ }
1341
+ }
1342
+
1343
+ /**
1344
+ * `Ascii.float` and `Text.float` (`map(floatText, toFloat)`): numbers in
1345
+ * decimal notation are matched and converted in one pass, without
1346
+ * slicing the input; the rest runs the compiled parser (see the JIT).
1347
+ */
1348
+ private float(node: NodeOf<"Map">): Step | undefined {
1349
+ if (!floatTexts.has(node.self) || this.kind === 2) return undefined
1350
+ let taken = nodeOf(node.self)
1351
+ while (taken._tag === "Expected") taken = nodeOf(taken.self)
1352
+ if (taken._tag !== "Taken") return undefined
1353
+ const enc = taken.encoding
1354
+ if (enc !== undefined && !(this.native(enc) && enc.unit === 1 && enc.asciiCompatible)) return undefined
1355
+ const c = this.compile(node.self, true)
1356
+ const scan = (this.kind === 0 ? scanFloatString : scanFloatBytes) as (input: unknown, i: number, end: number) => unknown
1357
+ const resumable = this.resumable
1358
+ return (s) => {
1359
+ const v = scan(s.input, s.pos, s.end)
1360
+ // Near the end of a partial input, the parser may have to report `Incomplete`
1361
+ if (v === undefined || (resumable && s.partial && floatScan.read + Margin > s.end)) {
1362
+ const t = c(s)
1363
+ return t === Fail ? Fail : toFloat(t)
1364
+ }
1365
+ if (v === Fail) return fail(s)
1366
+ s.pos = floatScan.end
1367
+ return v
1368
+ }
1369
+ }
1370
+
1371
+ escaped(node: NodeOf<"Escaped">): Step {
1372
+ const raw = node.raw
1373
+ const normal = this.compile(node.normal, !raw)
1374
+ const escape = this.compile(node.escape, !raw)
1375
+ const enc = node.encoding
1376
+ const ctrl = node.control
1377
+ const kind = this.kind
1378
+ // Matches the control character, advancing past it
1379
+ let control: (s: State<any>) => boolean
1380
+ 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
+ }
1396
+ } else {
1397
+ control = (s) => {
1398
+ if (s.pos >= s.end) return false
1399
+ const cp = enc.decode(s.input, s.pos, s.end)
1400
+ if (cp !== ctrl) return false
1401
+ s.pos += enc.width(cp)
1402
+ return true
1403
+ }
1404
+ }
1405
+ const resumable = this.resumable
1406
+ const key = {}
1407
+ /** Fails as incomplete, keeping a checkpoint in a streaming parse. */
1408
+ const incomplete = (s: State<any>, start: number, checkpoint: unknown): typeof Fail => {
1409
+ if (s.resume !== undefined) Resume.save(s, key, start, checkpoint)
1410
+ s.mode = INC
1411
+ return Fail
1412
+ }
1413
+ 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)
1417
+ return (s) => {
1418
+ const start = s.pos
1419
+ let resumed = false
1420
+ if (resumable && s.resume !== undefined) {
1421
+ const cp = Resume.take(s, key, start) as Resume.Done | number | undefined
1422
+ if (cp instanceof Resume.Done) {
1423
+ s.pos = cp.pos
1424
+ return cp.value
1425
+ }
1426
+ if (cp !== undefined) {
1427
+ s.pos = cp
1428
+ resumed = true
1429
+ }
1430
+ }
1431
+ while (s.pos < s.end) {
1432
+ const before = s.pos
1433
+ if (normal(s) !== Fail) {
1434
+ if (s.pos === before) {
1435
+ s.mode = CUT
1436
+ return Fail
1437
+ }
1438
+ continue
1439
+ }
1440
+ if (s.mode === INC) return incomplete(s, start, before)
1441
+ if (s.mode !== BT) return Fail
1442
+ s.pos = before
1443
+ if (control(s)) {
1444
+ if (escape(s) === Fail) return s.mode === INC ? incomplete(s, start, before) : Fail
1445
+ } else {
1446
+ return resumed ? Resume.done(s, key, start, true, slice(s, start)) : slice(s, start)
1447
+ }
1448
+ }
1449
+ if (s.partial) return incomplete(s, start, s.pos)
1450
+ return resumed ? Resume.done(s, key, start, true, slice(s, start)) : slice(s, start)
1451
+ }
1452
+ }
1453
+ return (s) => {
1454
+ const start = s.pos
1455
+ let out = ""
1456
+ let resumed = false
1457
+ if (resumable && s.resume !== undefined) {
1458
+ const cp = Resume.take(s, key, start) as Resume.Done | { out: string; pos: number } | undefined
1459
+ if (cp instanceof Resume.Done) {
1460
+ s.pos = cp.pos
1461
+ return cp.value
1462
+ }
1463
+ if (cp !== undefined) {
1464
+ out = cp.out
1465
+ s.pos = cp.pos
1466
+ resumed = true
1467
+ }
1468
+ }
1469
+ while (s.pos < s.end) {
1470
+ const before = s.pos
1471
+ const a = normal(s)
1472
+ if (a !== Fail) {
1473
+ if (s.pos !== before) {
1474
+ out += a
1475
+ continue
1476
+ }
1477
+ } else {
1478
+ if (s.mode === INC) return incomplete(s, start, { out, pos: before })
1479
+ if (s.mode !== BT) return Fail
1480
+ s.pos = before
1481
+ }
1482
+ if (control(s)) {
1483
+ const b = escape(s)
1484
+ if (b === Fail) return s.mode === INC ? incomplete(s, start, { out, pos: before }) : Fail
1485
+ out += b
1486
+ } else {
1487
+ return resumed ? Resume.done(s, key, start, true, out) : out
1488
+ }
1489
+ }
1490
+ if (s.partial) return incomplete(s, start, { out, pos: s.pos })
1491
+ return resumed ? Resume.done(s, key, start, true, out) : out
1492
+ }
1493
+ }
1494
+ }
1495
+
1496
+ type TokenNode = NodeOf<"Any" | "Literal" | "OneOf" | "Take" | "TakeWhile" | "TakeUntil" | "Rest">
1497
+
1498
+ const fail = (s: State<any>): typeof Fail => {
1499
+ s.mode = BT
1500
+ return Fail
1501
+ }
1502
+
1503
+ /** Fails, leaving the cursor at `pos`: how far a token parser read (see `partialSafe`). */
1504
+ const failAt = (s: State<any>, pos: number): typeof Fail => {
1505
+ s.pos = pos
1506
+ s.mode = BT
1507
+ return Fail
1508
+ }
1509
+
1510
+ /** 1 where a small code is accepted (taking `negate` into account). */
1511
+ const acceptTable = (set: CharSet, negate: boolean): Uint8Array => {
1512
+ const t = new Uint8Array(TableSize)
1513
+ for (let c = 0; c < TableSize; c++) t[c] = (set.table[c] === 1) !== negate ? 1 : 0
1514
+ return t
1515
+ }
1516
+
1517
+ /** A literal matched against a string input, returning `value`. */
1518
+ const stringLiteral = (str: string, value: unknown): Step => {
1519
+ const len = str.length
1520
+ if (len === 0) return () => value
1521
+ if (len === 1) {
1522
+ const c0 = str.charCodeAt(0)
1523
+ return (s) => {
1524
+ const pos = s.pos
1525
+ if (pos < s.end && (s.input as string).charCodeAt(pos) === c0) {
1526
+ s.pos = pos + 1
1527
+ return value
1528
+ }
1529
+ return fail(s)
1530
+ }
1531
+ }
1532
+ return (s) => {
1533
+ const pos = s.pos
1534
+ if (s.end - pos >= len && (s.input as string).startsWith(str, pos)) {
1535
+ s.pos = pos + len
1536
+ return value
1537
+ }
1538
+ return fail(s)
1539
+ }
1540
+ }
1541
+
1542
+ /** A closure compiler for one input kind; it memoizes what it compiles. */
1543
+ /** The object of `Object.fromEntries`, built an entry at a time (see `internal/entries.ts`). */
1544
+ class EntriesAcc {
1545
+ readonly o: Record<string, unknown> = {}
1546
+ deferred: Array<unknown> | undefined = undefined
1547
+ add(key: unknown, value: unknown): void {
1548
+ if (this.deferred === undefined && isPlainKey(key)) this.o[key] = value
1549
+ else this.deferred = addEntry(this.o, this.deferred, key, value)
1550
+ }
1551
+ result(): object {
1552
+ if (this.deferred !== undefined) flushEntries(this.o, this.deferred)
1553
+ return this.o
1554
+ }
1555
+ }
1556
+
1557
+ export interface ClosureCompiler {
1558
+ readonly compile: (p: ParserLike, used: boolean) => Step
1559
+ readonly note: (p: ParserLike, node: Node, reason: string) => void
1560
+ readonly firsts: FirstSets
1561
+ readonly fallbacks: ReadonlyArray<Fallback>
1562
+ }
1563
+
1564
+ export const createClosureCompiler = (kind: 0 | 1 | 2, resumable = false): ClosureCompiler => {
1565
+ const compiler = new Compiler(kind, resumable)
1566
+ return {
1567
+ compile: (p, used) => compiler.compile(p, used),
1568
+ note: (p, node, reason) => compiler.note(p, node, reason),
1569
+ firsts: compiler.firsts,
1570
+ fallbacks: compiler.fallbacks
1571
+ }
1572
+ }
1573
+
1574
+ /** Compiles a root parser for one input kind. */
1575
+ export const compileClosure = (
1576
+ root: ParserLike,
1577
+ kind: 0 | 1 | 2,
1578
+ resumable = false
1579
+ ): { readonly step: Step; readonly fallbacks: ReadonlyArray<Fallback> } => {
1580
+ const compiler = new Compiler(kind, resumable)
1581
+ const step = compiler.compile(root, true)
1582
+ return { step, fallbacks: compiler.fallbacks }
1583
+ }