@thomasfosterau/effect-parse 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +37 -13
  2. package/dist/Ascii.d.ts +11 -4
  3. package/dist/Ascii.d.ts.map +1 -1
  4. package/dist/Ascii.js +7 -1
  5. package/dist/Ascii.js.map +1 -1
  6. package/dist/Binary.d.ts.map +1 -1
  7. package/dist/Binary.js +64 -35
  8. package/dist/Binary.js.map +1 -1
  9. package/dist/Bits.d.ts.map +1 -1
  10. package/dist/Bits.js +7 -4
  11. package/dist/Bits.js.map +1 -1
  12. package/dist/Compiler.d.ts +74 -2
  13. package/dist/Compiler.d.ts.map +1 -1
  14. package/dist/Compiler.js +78 -1
  15. package/dist/Compiler.js.map +1 -1
  16. package/dist/Expression.d.ts.map +1 -1
  17. package/dist/Expression.js +86 -74
  18. package/dist/Expression.js.map +1 -1
  19. package/dist/Input.d.ts +37 -4
  20. package/dist/Input.d.ts.map +1 -1
  21. package/dist/Input.js +42 -0
  22. package/dist/Input.js.map +1 -1
  23. package/dist/Nom.d.ts +5 -15
  24. package/dist/Nom.d.ts.map +1 -1
  25. package/dist/Nom.js.map +1 -1
  26. package/dist/ParseError.d.ts.map +1 -1
  27. package/dist/ParseError.js.map +1 -1
  28. package/dist/Parser.d.ts +366 -26
  29. package/dist/Parser.d.ts.map +1 -1
  30. package/dist/Parser.js +390 -19
  31. package/dist/Parser.js.map +1 -1
  32. package/dist/ParserSchema.js +1 -1
  33. package/dist/ParserStream.d.ts +3 -2
  34. package/dist/ParserStream.d.ts.map +1 -1
  35. package/dist/ParserStream.js +13 -9
  36. package/dist/ParserStream.js.map +1 -1
  37. package/dist/Syntax.d.ts +23 -5
  38. package/dist/Syntax.d.ts.map +1 -1
  39. package/dist/Syntax.js.map +1 -1
  40. package/dist/Text.d.ts.map +1 -1
  41. package/dist/Text.js +9 -9
  42. package/dist/Text.js.map +1 -1
  43. package/dist/Token.d.ts +55 -10
  44. package/dist/Token.d.ts.map +1 -1
  45. package/dist/Token.js +59 -21
  46. package/dist/Token.js.map +1 -1
  47. package/dist/internal/compiler/closure.d.ts.map +1 -1
  48. package/dist/internal/compiler/closure.js +257 -97
  49. package/dist/internal/compiler/closure.js.map +1 -1
  50. package/dist/internal/compiler/entries.d.ts +3 -1
  51. package/dist/internal/compiler/entries.d.ts.map +1 -1
  52. package/dist/internal/compiler/entries.js +3 -2
  53. package/dist/internal/compiler/entries.js.map +1 -1
  54. package/dist/internal/compiler/first.d.ts.map +1 -1
  55. package/dist/internal/compiler/first.js +7 -1
  56. package/dist/internal/compiler/first.js.map +1 -1
  57. package/dist/internal/compiler/graph.d.ts +17 -0
  58. package/dist/internal/compiler/graph.d.ts.map +1 -0
  59. package/dist/internal/compiler/graph.js +56 -0
  60. package/dist/internal/compiler/graph.js.map +1 -0
  61. package/dist/internal/compiler/jit.d.ts +5 -0
  62. package/dist/internal/compiler/jit.d.ts.map +1 -1
  63. package/dist/internal/compiler/jit.js +185 -96
  64. package/dist/internal/compiler/jit.js.map +1 -1
  65. package/dist/internal/compiler/registry.d.ts +10 -2
  66. package/dist/internal/compiler/registry.d.ts.map +1 -1
  67. package/dist/internal/compiler/registry.js.map +1 -1
  68. package/dist/internal/compiler/search.d.ts +32 -0
  69. package/dist/internal/compiler/search.d.ts.map +1 -0
  70. package/dist/internal/compiler/search.js +68 -0
  71. package/dist/internal/compiler/search.js.map +1 -0
  72. package/dist/internal/compiler/sets.d.ts.map +1 -1
  73. package/dist/internal/compiler/sets.js +1 -1
  74. package/dist/internal/compiler/sets.js.map +1 -1
  75. package/dist/internal/compiler/wasm.d.ts +8 -0
  76. package/dist/internal/compiler/wasm.d.ts.map +1 -0
  77. package/dist/internal/compiler/wasm.js +2769 -0
  78. package/dist/internal/compiler/wasm.js.map +1 -0
  79. package/dist/internal/compiler/wasmAsm.d.ts +199 -0
  80. package/dist/internal/compiler/wasmAsm.d.ts.map +1 -0
  81. package/dist/internal/compiler/wasmAsm.js +427 -0
  82. package/dist/internal/compiler/wasmAsm.js.map +1 -0
  83. package/dist/internal/entries.d.ts +3 -1
  84. package/dist/internal/entries.d.ts.map +1 -1
  85. package/dist/internal/entries.js.map +1 -1
  86. package/dist/internal/node.d.ts +58 -2
  87. package/dist/internal/node.d.ts.map +1 -1
  88. package/dist/internal/node.js +13 -3
  89. package/dist/internal/node.js.map +1 -1
  90. package/dist/internal/numbers.d.ts.map +1 -1
  91. package/dist/internal/numbers.js.map +1 -1
  92. package/dist/internal/run.d.ts +12 -0
  93. package/dist/internal/run.d.ts.map +1 -1
  94. package/dist/internal/run.js +51 -11
  95. package/dist/internal/run.js.map +1 -1
  96. package/dist/internal/stack.d.ts +6 -0
  97. package/dist/internal/stack.d.ts.map +1 -1
  98. package/dist/internal/stack.js +8 -2
  99. package/dist/internal/stack.js.map +1 -1
  100. package/dist/internal/syntax.d.ts +4 -4
  101. package/dist/internal/syntax.d.ts.map +1 -1
  102. package/dist/internal/syntax.js +18 -10
  103. package/dist/internal/syntax.js.map +1 -1
  104. package/dist/internal/text.d.ts +32 -0
  105. package/dist/internal/text.d.ts.map +1 -1
  106. package/dist/internal/text.js +68 -14
  107. package/dist/internal/text.js.map +1 -1
  108. package/dist/internal/tokens.d.ts +13 -0
  109. package/dist/internal/tokens.d.ts.map +1 -1
  110. package/dist/internal/tokens.js +59 -39
  111. package/dist/internal/tokens.js.map +1 -1
  112. package/package.json +5 -4
  113. package/src/Ascii.ts +42 -9
  114. package/src/Binary.ts +151 -71
  115. package/src/Bits.ts +12 -6
  116. package/src/Compiler.ts +101 -10
  117. package/src/Expression.ts +94 -81
  118. package/src/Input.ts +65 -6
  119. package/src/Nom.ts +15 -5
  120. package/src/ParseError.ts +3 -0
  121. package/src/Parser.ts +678 -127
  122. package/src/ParserStream.ts +37 -18
  123. package/src/Syntax.ts +60 -36
  124. package/src/Text.ts +16 -15
  125. package/src/Token.ts +138 -29
  126. package/src/internal/compiler/closure.ts +362 -135
  127. package/src/internal/compiler/entries.ts +4 -2
  128. package/src/internal/compiler/first.ts +10 -0
  129. package/src/internal/compiler/graph.ts +68 -0
  130. package/src/internal/compiler/jit.ts +284 -154
  131. package/src/internal/compiler/registry.ts +10 -2
  132. package/src/internal/compiler/search.ts +68 -0
  133. package/src/internal/compiler/sets.ts +7 -3
  134. package/src/internal/compiler/wasm.ts +3203 -0
  135. package/src/internal/compiler/wasmAsm.ts +484 -0
  136. package/src/internal/entries.ts +8 -2
  137. package/src/internal/node.ts +54 -4
  138. package/src/internal/numbers.ts +3 -1
  139. package/src/internal/run.ts +71 -12
  140. package/src/internal/stack.ts +8 -1
  141. package/src/internal/syntax.ts +39 -18
  142. package/src/internal/text.ts +97 -9
  143. package/src/internal/tokens.ts +96 -14
@@ -7,13 +7,16 @@ import * as Resume from "../resume.js";
7
7
  import { compileAny } from "../tokens.js";
8
8
  import { backend as backend$1, createClosureCompiler } from "./closure.js";
9
9
  import * as Option from "effect/Option";
10
- import { charOf, fastLimit, latin1, string, utf16be, utf16le, utf8, validate } from "../text.js";
10
+ import { ShortText, charOf, fastLimit, latin1, string, textIn, utf16be, utf16le, utf8, utf8Text, validate } from "../text.js";
11
11
  import * as Arr from "effect/Array";
12
12
  import { addEntry, flushEntries, fromEntries, isPlainKey } from "../entries.js";
13
- import { children, nodeOf, scansFloat } from "../node.js";
13
+ import * as Stack from "../stack.js";
14
+ import { Defer, NodeId, nodeOf, scansFloat } from "../node.js";
14
15
  import { buildsEntries, pairOf } from "./entries.js";
15
16
  import { Strict, planAlt } from "./first.js";
17
+ import { SearchHead, StopCache, nextStop, stopChars } from "./search.js";
16
18
  import { TableSize, acceptTable, kindOfEncoding, reachesAbove, textSet, tokenSet } from "./sets.js";
19
+ import { countRefs, isLeaf, isLetter, isSmall, lowerAscii, selectorOf } from "./graph.js";
17
20
  // src/internal/compiler/jit.ts
18
21
  /**
19
22
  * The JIT: emits JavaScript source for a parser's description and builds
@@ -33,11 +36,15 @@ import { TableSize, acceptTable, kindOfEncoding, reachesAbove, textSet, tokenSet
33
36
  */
34
37
  /** Helpers available to generated code as `R`. */
35
38
  const runtime = {
39
+ StopCache,
40
+ nextStop,
36
41
  take: Resume.take,
37
42
  save: Resume.save,
38
43
  Done: Resume.Done,
39
44
  /** Records the result of a resumed repetition (see `Resume.done`). */
40
45
  done: (state, key, entry, value, pos) => Resume.save(state, key, entry, new Resume.Done(value, pos)),
46
+ isStackOverflow: Stack.isStackOverflow,
47
+ tooDeep: Stack.tooDeep,
41
48
  some: Option.some,
42
49
  none: Option.none,
43
50
  charOf,
@@ -56,8 +63,9 @@ const runtime = {
56
63
  s.mode = "Backtrack";
57
64
  return Fail;
58
65
  }
59
- return enc.text(input, start, end);
60
- }
66
+ return textIn(enc, s, start, end);
67
+ },
68
+ utf8Text
61
69
  };
62
70
  let checked;
63
71
  let supported = false;
@@ -87,22 +95,6 @@ const Margin = 4;
87
95
  var Fn = class {
88
96
  vars = [];
89
97
  };
90
- const isLeaf = (node) => {
91
- switch (node._tag) {
92
- case "Succeed":
93
- case "Fail":
94
- case "Eof":
95
- case "Location":
96
- case "RestLen":
97
- case "Any":
98
- case "Literal":
99
- case "OneOf":
100
- case "TakeWhile": return true;
101
- default: return false;
102
- }
103
- };
104
- const lowerAscii = (c) => c >= 65 && c <= 90 ? c + 32 : c;
105
- const isLetter = (c) => c >= 65 && c <= 90 || c >= 97 && c <= 122;
106
98
  /** The inclusive ranges of 1s in a table. */
107
99
  const runsOf = (table) => {
108
100
  const out = [];
@@ -160,7 +152,7 @@ var Emitter = class {
160
152
  constantIndex = /* @__PURE__ */ new Map();
161
153
  functions = [];
162
154
  rules = [/* @__PURE__ */ new Map(), /* @__PURE__ */ new Map()];
163
- refs = /* @__PURE__ */ new Map();
155
+ refs;
164
156
  closure;
165
157
  next = 0;
166
158
  constructor(root, kind, resumable = false) {
@@ -168,24 +160,11 @@ var Emitter = class {
168
160
  this.kind = kind;
169
161
  this.resumable = resumable;
170
162
  this.closure = createClosureCompiler(kind, resumable);
171
- this.countRefs(root);
163
+ this.refs = countRefs(root);
172
164
  }
173
165
  get fallbacks() {
174
166
  return this.closure.fallbacks;
175
167
  }
176
- /** Counts the distinct parents of each parser. */
177
- countRefs(root) {
178
- const seen = /* @__PURE__ */ new Set();
179
- const stack = [root];
180
- for (let p = stack.pop(); p !== void 0; p = stack.pop()) {
181
- if (seen.has(p)) continue;
182
- seen.add(p);
183
- for (const c of new Set(children(nodeOf(p)))) {
184
- this.refs.set(c, (this.refs.get(c) ?? 0) + 1);
185
- stack.push(c);
186
- }
187
- }
188
- }
189
168
  const(value) {
190
169
  let i = this.constantIndex.get(value);
191
170
  if (i === void 0) {
@@ -195,6 +174,11 @@ var Emitter = class {
195
174
  }
196
175
  return `C[${i}]`;
197
176
  }
177
+ /** The text of `[start, p)` in `enc`: UTF-8 decodes through the state's ASCII run (see `utf8Text`). */
178
+ text(enc, start) {
179
+ const text = `${this.const(enc.text)}(I,${start},p)`;
180
+ return enc === utf8 ? `(p-${start}>${ShortText}?R.utf8Text($S,${start},p):${text})` : text;
181
+ }
198
182
  /** A JavaScript literal for simple values, otherwise a constant. */
199
183
  lit(value) {
200
184
  if (value === void 0) return "void 0";
@@ -257,19 +241,9 @@ var Emitter = class {
257
241
  gen(p, used, out, fail, fn, depth) {
258
242
  const node = nodeOf(p);
259
243
  if (Predicate.isTagged(node, "Suspend")) return this.call(this.rule(node.resolve(), used), out, fail);
260
- if (!isLeaf(node) && ((this.refs.get(p) ?? 0) > 1 && !this.small(p) || depth > 32)) return this.call(this.rule(p, used), out, fail);
244
+ if (!isLeaf(node) && ((this.refs.get(p) ?? 0) > 1 && !isSmall(p) || depth > 32)) return this.call(this.rule(p, used), out, fail);
261
245
  return this.inline(p, used, out, fail, fn, depth);
262
246
  }
263
- /** Whether a parser is small enough to inline wherever it is used. */
264
- small(p) {
265
- let budget = 6;
266
- const visit = (q) => {
267
- const node = nodeOf(q);
268
- if (--budget < 0 || Predicate.isTagged(node, "Suspend") || Predicate.isTagged(node, "Escaped") || Predicate.isTagged(node, "Dispatch")) return false;
269
- return children(node).every(visit);
270
- };
271
- return visit(p);
272
- }
273
247
  /**
274
248
  * In a resumable build: token code (which fails by breaking out of `bad`)
275
249
  * that read within `lookahead` units of the end of a partial input re-runs
@@ -344,7 +318,7 @@ var Emitter = class {
344
318
  case "Succeed": return set(this.lit(node.value));
345
319
  case "Sync": return used ? set(`${this.const(node.evaluate)}()`) : "";
346
320
  case "Fail": return `$S.mode=${JSON.stringify(node.mode)};${fail}`;
347
- case "Eof": return `if(p<E){${BT}${fail}}` + (this.resumable ? `if($S.partial){$S.mode="Incomplete";${fail}}` : "") + set("void 0");
321
+ case "Eof": return `if(p<E){${BT}${fail}}` + (this.resumable ? `if($S.partial){$S.incomplete(1);${fail}}` : "") + set("void 0");
348
322
  case "Location": return set("p");
349
323
  case "RestLen": return set("E-p");
350
324
  case "Suspend": return this.call(this.rule(node.resolve(), used), out, fail);
@@ -367,7 +341,7 @@ var Emitter = class {
367
341
  const code = this.float(node, out, fail, fn, d);
368
342
  if (code !== void 0) return code;
369
343
  }
370
- if (node.f === fromEntries && out !== null && !this.resumable && buildsEntries(node.self)) return this.entries(node.self, out, fail, fn, d);
344
+ if (node.f === fromEntries && out !== null && buildsEntries(node.self)) return this.entries(node.self, out, fail, fn, d);
371
345
  const t = this.v(fn);
372
346
  return this.gen(node.self, true, t, fail, fn, d) + set(`${this.const(node.f)}(${t})`);
373
347
  }
@@ -383,6 +357,17 @@ var Emitter = class {
383
357
  const [tag, field] = Predicate.isTagged(node, "TryMap") ? ["Success", "success"] : ["Some", "value"];
384
358
  return this.gen(node.self, true, t, fail, fn, d) + `${r}=${this.const(node.f)}(${t});if(${r}._tag!=="${tag}"){${BT}${fail}}${set(`${r}.${field}`)}`;
385
359
  }
360
+ case "FastPath": {
361
+ const st = this.v(fn);
362
+ const t = out ?? this.v(fn);
363
+ const done = this.label();
364
+ return `${st}=p;$S.pos=p;${t}=${this.const(node.fast)}($S);${done}:{if(${t}!==${this.const(Defer)}){p=$S.pos;break ${done}}p=${st};${this.gen(node.self, used, out, fail, fn, d)}}`;
365
+ }
366
+ case "MapOrFail": {
367
+ const t = this.v(fn);
368
+ const r = out ?? this.v(fn);
369
+ return this.gen(node.self, true, t, fail, fn, d) + `${r}=${this.const(node.f)}(${t});if(${r}===F){${BT}${fail}}`;
370
+ }
386
371
  case "Filter": {
387
372
  const t = out ?? this.v(fn);
388
373
  return this.gen(node.self, true, t, fail, fn, d) + `if(!${this.const(node.predicate)}(${t})){${BT}${fail}}`;
@@ -409,6 +394,12 @@ var Emitter = class {
409
394
  const extra = Predicate.isTagged(node, "WithSpan") ? `{start:${st},end:p}` : `$S.slice(${st},p)`;
410
395
  return `${st}=p;` + this.gen(node.self, true, t, fail, fn, d) + set(`[${t},${extra}]`);
411
396
  }
397
+ case "MapSpan": {
398
+ if (!used) return this.gen(node.self, false, null, fail, fn, d);
399
+ const st = this.v(fn);
400
+ const t = this.v(fn);
401
+ return `${st}=p;` + this.gen(node.self, true, t, fail, fn, d) + set(`${this.const(node.f)}(${t},${st},p)`);
402
+ }
412
403
  case "Span": {
413
404
  const st = this.v(fn);
414
405
  return `${st}=p;` + this.gen(node.self, false, null, fail, fn, d) + set(`{start:${st},end:p}`);
@@ -420,6 +411,7 @@ var Emitter = class {
420
411
  }
421
412
  case "Context":
422
413
  case "Expected":
414
+ case "Quiet":
423
415
  case "MapError":
424
416
  case "OrFail":
425
417
  case "Trace":
@@ -458,6 +450,11 @@ var Emitter = class {
458
450
  if (node.keys.includes("__proto__")) return code + `${out}={};` + node.keys.map((k, i) => `${out}[${JSON.stringify(k)}]=${vars[i]};`).join("");
459
451
  return code + `${out}={${node.keys.map((k, i) => `${JSON.stringify(k)}:${vars[i]}`).join(",")}};`;
460
452
  }
453
+ case "SeqWith": {
454
+ if (out === null) return node.parsers.map((q) => this.gen(q, false, null, fail, fn, d)).join("");
455
+ const vars = node.parsers.map(() => this.v(fn));
456
+ return node.parsers.map((q, i) => this.gen(q, true, vars[i], fail, fn, d)).join("") + `${out}=${this.const(node.f)}(${vars.join(",")});`;
457
+ }
461
458
  case "ZipWith": {
462
459
  if (out === null) return this.gen(node.self, false, null, fail, fn, d) + this.gen(node.that, false, null, fail, fn, d);
463
460
  const x = this.v(fn);
@@ -467,8 +464,13 @@ var Emitter = class {
467
464
  case "Alt": return this.alt(node, used, out, fail, fn, d);
468
465
  case "Dispatch": return this.dispatch(p, node, used, out, fail, fn, d);
469
466
  case "Repeat":
470
- case "Fold": return this.repeat(node, used, out, fail, fn, d);
467
+ case "Fold": return (out === null && Predicate.isTagged(node, "Repeat") ? this.repeatRun(p, node, fail, fn, d) : void 0) ?? this.repeat(node, used, out, fail, fn, d);
471
468
  case "Separated": return this.separated(node, used, out, fail, fn, d);
469
+ case "Fixed": {
470
+ if (this.kind !== 1) return this.delegate(p, used, out, fail, fn);
471
+ const read = used ? `${out ?? this.v(fn)}=${this.const(node.read)}(I,p,$S);` : "";
472
+ return `if(E-p>=${node.size}){${read}p+=${node.size}}else{${this.delegate(p, used, out, fail, fn)}}`;
473
+ }
472
474
  default: return this.delegate(p, used, out, fail, fn);
473
475
  }
474
476
  }
@@ -552,9 +554,7 @@ var Emitter = class {
552
554
  const r = out ?? this.v(fn);
553
555
  return code + `default:{$S.pos=p;${r}=${q}.step($S);if(${r}===F){${fail}}p=$S.pos;}}`;
554
556
  };
555
- let sel = nodeOf(node.selector);
556
- const peek = Predicate.isTagged(sel, "Peek");
557
- if (peek) sel = nodeOf(sel.self);
557
+ const { node: sel, peek } = selectorOf(node.selector);
558
558
  if (!Predicate.isTagged(sel, "Any") || !this.native(sel.encoding)) return general(true);
559
559
  const enc = sel.encoding;
560
560
  const r = enc === void 0 ? void 0 : this.reader(enc);
@@ -577,6 +577,57 @@ var Emitter = class {
577
577
  code += `case -1:{${BT}${fail}}default:{if(${u}<${size}){${BT}${fail}}${general(false)}}}`;
578
578
  return code;
579
579
  }
580
+ /**
581
+ * A repetition whose elements are not kept, rewritten to scan a run of a
582
+ * character set in one loop instead of one element at a time (#79):
583
+ *
584
+ * - `repeat(oneOf(S), range)` matches what `takeWhile(S, range)` matches,
585
+ * stopping, failing and reporting `Incomplete` where it does, so it
586
+ * compiles as that `takeWhile`, with the repetition's own step as the
587
+ * fallback near the end of a partial input.
588
+ * - `repeat(alt(oneOf(S), ...rest))` with `min` at most 1 and no `max`
589
+ * matches what `repeat(alt(takeWhile(S, { min: 1 }), ...rest))` matches:
590
+ * a run of `S` is a run of one-character elements, and the counts that
591
+ * differ only matter for other ranges. The first branch is a synthetic
592
+ * `takeWhile` node; outside resumable builds, which never run its step.
593
+ *
594
+ * Returns `undefined` when neither applies.
595
+ */
596
+ repeatRun(p, node, fail, fn, d) {
597
+ if (this.kind === 2) return void 0;
598
+ const element = nodeOf(node.self);
599
+ if (Predicate.isTagged(element, "OneOf") && this.native(element.encoding)) {
600
+ const { encoding, negate, set } = element;
601
+ return this.leaf(p, {
602
+ _tag: "TakeWhile",
603
+ set,
604
+ negate,
605
+ encoding,
606
+ min: node.min,
607
+ max: node.max
608
+ }, false, null, fail, fn);
609
+ }
610
+ if (this.resumable || node.min > 1 || node.max !== Infinity || !Predicate.isTagged(element, "Alt")) return void 0;
611
+ const [first, ...rest] = element.parsers;
612
+ const head = first === void 0 ? void 0 : nodeOf(first);
613
+ if (head === void 0 || !Predicate.isTagged(head, "OneOf") || !this.native(head.encoding) || Arr.isReadonlyArrayEmpty(rest)) return;
614
+ const run = synthetic({
615
+ _tag: "TakeWhile",
616
+ set: head.set,
617
+ negate: head.negate,
618
+ encoding: head.encoding,
619
+ min: 1,
620
+ max: Infinity
621
+ }, node.self.step);
622
+ const alt = synthetic({
623
+ _tag: "Alt",
624
+ parsers: [run, ...rest]
625
+ }, node.self.step);
626
+ return this.repeat({
627
+ ...node,
628
+ self: alt
629
+ }, false, null, fail, fn, d);
630
+ }
580
631
  repeat(node, used, out, fail, fn, d, collect) {
581
632
  const { max, min } = node;
582
633
  const loop = this.label();
@@ -603,11 +654,12 @@ var Emitter = class {
603
654
  const check = max === Infinity ? `if(p===${st}){$S.mode="Cut";${fail}}` : "";
604
655
  const bound = max === Infinity ? "" : `${n}<${max}`;
605
656
  const r = this.resumable ? this.resumePoint(fn) : void 0;
606
- const save = r === void 0 ? "" : `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${acc},count:${n},pos:${st}});`;
657
+ const held = collect?.value ?? acc;
658
+ const save = r === void 0 ? "" : `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${held},${collect?.saved ?? ""}count:${n},pos:${st}});`;
607
659
  const element = collect === void 0 ? `${this.gen(node.self, tracked, x, `break ${bad};`, fn, d)}${check}${add}` : collect.element(`break ${bad};`, check);
608
660
  const body = `${loop}:for(;${bound};${n}++){${cond === void 0 ? "" : `if(${min > 0 ? `${n}>=${min}&&` : ""}!${this.const(cond)}(${acc}))break ${loop};`}${st}=p;${bad}:{${element}continue ${loop};}${save}if($S.mode!=="Backtrack"${min > 0 ? `||${n}<${min}` : ""}){${fail}}p=${st};break ${loop};}`;
609
661
  if (r === void 0) return `${init}${n}=0;${body}${collect?.finish ?? ""}`;
610
- return r.wrap(acc, `${acc}=cp.acc;${n}=cp.count;p=cp.pos;`, `${init}${n}=0;`, body);
662
+ return r.wrap(held, `${held}=cp.acc;${collect?.restore ?? ""}${n}=cp.count;p=cp.pos;`, `${init}${n}=0;`, `${body}${collect?.finish ?? ""}`);
611
663
  }
612
664
  /**
613
665
  * In a resumable build, a repetition entered at `entry` may restart from a
@@ -638,7 +690,8 @@ var Emitter = class {
638
690
  const loop = this.label();
639
691
  const push = keep ? `${acc}.push(${x});` : "";
640
692
  const r = this.resumable ? this.resumePoint(fn) : void 0;
641
- const save = r === void 0 ? "" : `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${acc},count:${n},pos:${st}});`;
693
+ const held = collect?.value ?? acc;
694
+ const save = r === void 0 ? "" : `if($S.mode==="Incomplete"&&$S.resume!==void 0)R.save($S,${r.key},${r.entry},{acc:${held},${collect?.saved ?? ""}count:${n},pos:${st}});`;
642
695
  const attempt = (body, success, fatal, checkpoint) => {
643
696
  const ok = this.label();
644
697
  const bad = this.label();
@@ -650,7 +703,7 @@ var Emitter = class {
650
703
  const first = `${collect?.init ?? (keep ? `${acc}=[];` : "")}${n}=0;${st}=p;` + attempt(self, `${push}${n}++;`, min > 0 ? "||true" : "", "");
651
704
  const rest = `${loop}:while(${max === Infinity ? "true" : `${n}<${max}`}){${st}=p;` + attempt(sep, "", `||${n}<${min}`, save) + check + attempt(self, `${push}${n}++;continue ${loop};`, `||${n}<${min}`, save) + `}`;
652
705
  if (r === void 0) return `${done}:{${first}${rest}}${collect?.finish ?? ""}`;
653
- return r.wrap(acc, `${acc}=cp.acc;${n}=cp.count;p=cp.pos;`, "", `${done}:{if(!${r.resumed}){${first}}${rest}}`);
706
+ return r.wrap(held, `${held}=cp.acc;${collect?.restore ?? ""}${n}=cp.count;p=cp.pos;`, "", `${done}:{if(!${r.resumed}){${first}}${rest}}${collect?.finish ?? ""}`);
654
707
  }
655
708
  /**
656
709
  * `map(self, Object.fromEntries)`, where `buildsEntries(self)`: builds the
@@ -661,35 +714,35 @@ var Emitter = class {
661
714
  */
662
715
  entries(p, out, fail, fn, d) {
663
716
  const node = nodeOf(p);
664
- switch (node._tag) {
665
- case "Mode": {
666
- const ok = this.label();
667
- const bad = this.label();
668
- return `${ok}:{${bad}:{${this.entries(node.self, out, `break ${bad};`, fn, d)}break ${ok};}if($S.mode===${JSON.stringify(node.from)})$S.mode=${JSON.stringify(node.to)};${fail}}`;
669
- }
670
- case "Seq": {
671
- const i = node.output;
672
- return node.parsers.map((q, j) => j === i ? this.entries(q, out, fail, fn, d) : this.gen(q, false, null, fail, fn, d)).join("");
673
- }
674
- case "Repeat":
675
- case "Separated": {
676
- const pair = pairOf(node.self);
677
- if (pair === void 0) {
678
- const t = this.v(fn);
679
- return this.gen(p, true, t, fail, fn, d) + `${out}=${this.const(fromEntries)}(${t});`;
680
- }
681
- const deferred = this.v(fn);
682
- const key = this.v(fn);
683
- const value = this.v(fn);
684
- const collect = {
685
- init: `${out}={};${deferred}=void 0;`,
686
- element: (f, check) => pair.parsers.map((q, j) => this.gen(q, j === pair.key || j === pair.value, j === pair.key ? key : j === pair.value ? value : null, f, fn, d)).join("") + check + `if(${deferred}===void 0&&R.isPlainKey(${key}))${out}[${key}]=${value};else ${deferred}=R.addEntry(${out},${deferred},${key},${value});`,
687
- finish: `if(${deferred}!==void 0)R.flushEntries(${out},${deferred});`
688
- };
689
- return Predicate.isTagged(node, "Repeat") ? this.repeat(node, true, out, fail, fn, d, collect) : this.separated(node, true, out, fail, fn, d, collect);
717
+ if (Predicate.isTagged(node, "Mode")) {
718
+ const ok = this.label();
719
+ const bad = this.label();
720
+ return `${ok}:{${bad}:{${this.entries(node.self, out, `break ${bad};`, fn, d)}break ${ok};}if($S.mode===${JSON.stringify(node.from)})$S.mode=${JSON.stringify(node.to)};${fail}}`;
721
+ }
722
+ if (Predicate.isTagged(node, "Seq")) {
723
+ const i = node.output;
724
+ return node.parsers.map((q, j) => j === i ? this.entries(q, out, fail, fn, d) : this.gen(q, false, null, fail, fn, d)).join("");
725
+ }
726
+ if (Predicate.isTagged(node, "Repeat") || Predicate.isTagged(node, "Separated")) {
727
+ const pair = pairOf(node.self);
728
+ if (pair === void 0) {
729
+ const t = this.v(fn);
730
+ return this.gen(p, true, t, fail, fn, d) + `${out}=${this.const(fromEntries)}(${t});`;
690
731
  }
691
- default: return this.entries(node.self, out, fail, fn, d);
732
+ const deferred = this.v(fn);
733
+ const key = this.v(fn);
734
+ const value = this.v(fn);
735
+ const collect = {
736
+ init: `${out}={};${deferred}=void 0;`,
737
+ element: (f, check) => pair.parsers.map((q, j) => this.gen(q, j === pair.key || j === pair.value, j === pair.key ? key : j === pair.value ? value : null, f, fn, d)).join("") + check + `if(${deferred}===void 0&&R.isPlainKey(${key}))${out}[${key}]=${value};else ${deferred}=R.addEntry(${out},${deferred},${key},${value});`,
738
+ finish: `if(${deferred}!==void 0)R.flushEntries(${out},${deferred});`,
739
+ value: out,
740
+ saved: `def:${deferred},`,
741
+ restore: `${deferred}=cp.def;`
742
+ };
743
+ return Predicate.isTagged(node, "Repeat") ? this.repeat(node, true, out, fail, fn, d, collect) : this.separated(node, true, out, fail, fn, d, collect);
692
744
  }
745
+ return this.entries(node.self, out, fail, fn, d);
693
746
  }
694
747
  /** An expression: is the unit `c`, known to be below `size` (at most `TableSize`), accepted? */
695
748
  member(accept, c, size = TableSize) {
@@ -745,9 +798,10 @@ var Emitter = class {
745
798
  return this.literalUnits(Predicate.isString(encoded) ? unitsOf(encoded) : Array.from(encoded), false, fail) + set(this.lit(lit));
746
799
  }
747
800
  const literalKind = Predicate.isString(lit) ? 0 : lit instanceof Uint8Array ? 1 : 2;
748
- if (kind === 0 && literalKind !== 0) return void 0;
749
- if (literalKind === 2) return void 0;
750
- const codes = kind === 0 ? unitsOf(lit) : Array.from(literalKind === 0 ? new TextEncoder().encode(lit) : lit);
801
+ let codes;
802
+ if (Predicate.isString(lit)) codes = kind === 0 ? unitsOf(lit) : Array.from(new TextEncoder().encode(lit));
803
+ else if (kind === 1 && lit instanceof Uint8Array) codes = Array.from(lit);
804
+ else return void 0;
751
805
  const len = codes.length;
752
806
  const value = !node.caseless && literalKind === kind ? this.lit(lit) : kind === 0 ? `I.slice(p-${len},p)` : `I.subarray(p-${len},p)`;
753
807
  return this.literalUnits(codes, node.caseless, fail) + set(value);
@@ -801,7 +855,7 @@ var Emitter = class {
801
855
  const counted = max !== Infinity || min > 1;
802
856
  const count = counted ? `${n}++;` : "";
803
857
  const bound = max !== Infinity ? `&&${n}<${max}` : "";
804
- const slice = enc !== void 0 ? `${this.const(enc.text)}(I,${st},p)` : kind === 0 ? `I.slice(${st},p)` : `I.subarray(${st},p)`;
858
+ const slice = enc !== void 0 ? this.text(enc, st) : kind === 0 ? `I.slice(${st},p)` : `I.subarray(${st},p)`;
805
859
  const finish = (min > 0 ? `if(${counted ? n : `p-${st}`}<${min}){${BT}${fail}}` : "") + (out === null ? "" : `${out}=${slice};`);
806
860
  const head = `${st}=p;${counted ? `${n}=0;` : ""}${loop}:while(p<E${bound}){${c}=${this.unit("p")};`;
807
861
  const member = this.member(accept, c);
@@ -811,6 +865,8 @@ var Emitter = class {
811
865
  const guard = r.width === 1 ? "" : `${r.guard("p")}&&`;
812
866
  return `${st}=p;${counted ? `${n}=0;` : ""}${loop}:while(p<E${bound}){` + (r.fast > 0 ? `if(${guard}(${c}=${r.unit("p")})<${r.fast}){if(!${this.textAccepts(r, cs, accept, negate, c)})break ${loop};p+=${r.width}}else` : "") + `{${c}=${this.const(enc.decode)}(I,p,E);if(${c}<0||${this.textRejectsDecoded(enc, r, cs, negate, c)})break ${loop};p+=${this.const(enc.width)}(${c})}${count}}${finish}`;
813
867
  }
868
+ const stops = negate && !counted ? stopChars(cs) : void 0;
869
+ if (stops !== void 0) return this.stopSearch(stops, st, c, finish, fn);
814
870
  const fast = 55296;
815
871
  const small = TableSize;
816
872
  const highTest = (v) => this.memberHigh(cs, negate, v);
@@ -820,6 +876,21 @@ var Emitter = class {
820
876
  return `${head}if(${c}<${small}){if(!(${member}))break;p++}else ${never && !negate ? `break;` : always && !counted ? `p++;` : `if(${c}<${fast}||${c}>56319){if(!(${highTest(c)}))break ${loop};p++}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}if(!(${highTest(cp)}))break ${loop};p+=${cp}>65535?2:1}`}${count}}${finish}`;
821
877
  }
822
878
  /**
879
+ * `Token.takeTill` on a string, of one to `MaxStops` characters that are
880
+ * whole code points: the set accepts every other unit, surrogates
881
+ * included, so the run ends at the first stop character. The first
882
+ * `SearchHead` units are tested one at a time, which beats a native
883
+ * search on short runs; a longer run ends at the next occurrence that
884
+ * `nextStop` finds with `indexOf` (#75).
885
+ */
886
+ stopSearch(stops, st, c, finish, fn) {
887
+ const h = this.v(fn);
888
+ const test = stops.map((u) => `${c}!==${u}`).join("&&");
889
+ const search = stops.length === 1 ? `{p=I.indexOf(${JSON.stringify(String.fromCharCode(stops[0]))},p);if(p<0)p=E}` : `p=R.nextStop(${this.const(new StopCache(stops.map((u) => String.fromCharCode(u))))},$S,p);`;
890
+ const head = stops.length === 1 ? SearchHead : 2 * SearchHead;
891
+ return `${st}=p;${h}=p+${head}<E?p+${head}:E;while(p<${h}&&(${c}=I.charCodeAt(p),${test}))p++;if(p===${h}&&p<E&&(${c}=I.charCodeAt(p),${test}))${search}${finish}`;
892
+ }
893
+ /**
823
894
  * `escaped`. In a resumable build it keeps the closure compiler's
824
895
  * checkpoints: where the next segment starts and, unless raw, the text
825
896
  * so far, saved when a segment or an escape runs out of input.
@@ -843,37 +914,54 @@ var Emitter = class {
843
914
  const normal = this.gen(node.normal, !raw, x, `break ${bad};`, fn, d);
844
915
  const escape = this.gen(node.escape, !raw, x, `${save(b)}${fail}`, fn, d);
845
916
  const onNormal = raw ? `if(p===${b}){$S.mode="Cut";${fail}}continue ${loop};` : `if(p!==${b}){${acc}+=${x};continue ${loop}}`;
846
- const slice = enc !== void 0 ? `${this.const(enc.text)}(I,${st},p)` : this.kind === 0 ? `I.slice(${st},p)` : `I.subarray(${st},p)`;
917
+ const slice = enc !== void 0 ? this.text(enc, st) : this.kind === 0 ? `I.slice(${st},p)` : `I.subarray(${st},p)`;
847
918
  const ctrlTest = rd === void 0 ? `p<E&&${this.unit("p")}===${ctrl}` : `${rd.guard("p")}&&${rd.unit("p")}===${ctrl}`;
848
919
  const stop = r === void 0 ? loop : this.label();
849
920
  const loopCode = `${loop}:while(p<E){${b}=p;${okv}=false;${bad}:{${normal}${okv}=true;}if(${okv}){${onNormal}}else{if($S.mode!=="Backtrack"){${save(b)}${fail}}p=${b}}if(${ctrlTest}){p+=${rd?.width ?? 1};${escape}${raw ? "" : `${acc}+=${x};`}}else break ${stop};}`;
850
- const body = r === void 0 ? loopCode : `${stop}:{${loopCode}if($S.partial){$S.mode="Incomplete";${save("p")}${fail}}}`;
921
+ const body = r === void 0 ? loopCode : `${stop}:{${loopCode}if($S.partial){$S.incomplete(0);${save("p")}${fail}}}`;
851
922
  if (r === void 0) return `${raw ? `${st}=p;` : `${acc}="";`}${body}${raw && out !== null ? `${out}=${slice};` : ""}`;
852
923
  if (acc !== null) return r.wrap(acc, `${acc}=cp.out;p=cp.pos;`, `${acc}="";`, body);
853
924
  const value = out ?? this.v(fn);
854
925
  return r.wrap(value, "p=cp;", "", `${body}${value}=${out === null ? "void 0" : slice};`);
855
926
  }
856
927
  };
928
+ /**
929
+ * A parser that exists only as a description, for the rewrites of
930
+ * `Emitter.repeatRun`. `step` is never run on the paths they compile (the
931
+ * token code of a native `takeWhile`, and alternatives inline), so it is
932
+ * the step of the repetition's element, which describes the same language
933
+ * when its value is unused.
934
+ */
935
+ const synthetic = (node, step) => {
936
+ return {
937
+ step,
938
+ name: void 0,
939
+ [NodeId]: node
940
+ };
941
+ };
857
942
  /** The UTF-16 code units of a string. */
858
943
  const unitsOf = (s) => Array.from({ length: s.length }, (_, i) => s.charCodeAt(i));
859
944
  /** Generates the source of a compiled parser, without building it. */
860
945
  const generate = (root, kind, resumable = false) => {
861
946
  const e = new Emitter(root, kind, resumable);
862
947
  const main = e.rule(root, true);
948
+ let closure;
949
+ const short = e.const((state) => (closure ??= createClosureCompiler(kind, resumable).compile(root, true))(state));
863
950
  return {
864
- source: `"use strict";let $I,$E,$S,P;${e.functions.join("\n")}\nreturn function run(state){const sI=$I,sE=$E,sS=$S,sP=P;$I=state.input;$E=state.end;$S=state;try{const v=${main}(state.pos);if(v!==F)state.pos=P;return v}finally{$I=sI;$E=sE;$S=sS;P=sP}}`,
951
+ source: `"use strict";let $I,$E,$S,P;${e.functions.join("\n")}\nreturn function run(state){if(state.end!==state.input.length)return ${short}(state);const sI=$I,sE=$E,sS=$S,sP=P;$I=state.input;$E=state.end;$S=state;try{const v=${main}(state.pos);if(v!==F)state.pos=P;return v}catch(e){if(!R.isStackOverflow(e))throw e;return R.tooDeep(state)}finally{$I=sI;$E=sE;$S=sS;P=sP}}`,
865
952
  constants: e.constants,
866
953
  fallbacks: e.fallbacks
867
954
  };
868
955
  };
869
- /** Compiles a root parser for one input kind with `new Function`. */
956
+ /**
957
+ * Compiles a root parser for one input kind with `new Function`. The step
958
+ * is the generated entry, which handles every state and reports call-stack
959
+ * overflows itself.
960
+ */
870
961
  const compileJit = (root, kind, resumable = false) => {
871
962
  const { constants, fallbacks, source } = generate(root, kind, resumable);
872
- const run = globalThis.Function("C", "F", "R", source)(constants, Fail, runtime);
873
- let closure;
874
- const step = (state) => state.end === state.input.length ? run(state) : (closure ??= createClosureCompiler(kind, resumable).compile(root, true))(state);
875
963
  return {
876
- step,
964
+ step: globalThis.Function("C", "F", "R", source)(constants, Fail, runtime),
877
965
  fallbacks,
878
966
  source
879
967
  };
@@ -888,6 +976,7 @@ const backend = { build: (p, kind, resumable) => {
888
976
  onSuccess: ({ fallbacks, source, step }) => ({
889
977
  backend: "jit",
890
978
  step,
979
+ guarded: true,
891
980
  fallbacks,
892
981
  error: void 0,
893
982
  source
@@ -898,6 +987,6 @@ const backend = { build: (p, kind, resumable) => {
898
987
  })
899
988
  });
900
989
  } };
901
- export { backend };
990
+ export { backend, jitSupported };
902
991
 
903
992
  //# sourceMappingURL=jit.js.map