parseman 0.47.0 → 0.47.1

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 (57) hide show
  1. package/dist/analysis/diagnostics.cjs +133 -45
  2. package/dist/analysis/diagnostics.cjs.map +2 -2
  3. package/dist/analysis/diagnostics.js +133 -45
  4. package/dist/analysis/diagnostics.js.map +2 -2
  5. package/dist/cli/index.cjs +133 -45
  6. package/dist/cli/index.cjs.map +2 -2
  7. package/dist/cli/index.js +133 -45
  8. package/dist/cli/index.js.map +2 -2
  9. package/dist/combinators/first-set.d.ts.map +1 -1
  10. package/dist/combinators/sequence.d.ts.map +1 -1
  11. package/dist/combinators/trivia-skip.d.ts +5 -0
  12. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  13. package/dist/index.cjs +134 -46
  14. package/dist/index.cjs.map +2 -2
  15. package/dist/index.js +134 -46
  16. package/dist/index.js.map +2 -2
  17. package/dist/language-service/index.cjs +3 -1
  18. package/dist/language-service/index.cjs.map +1 -1
  19. package/dist/language-service/index.js +3 -1
  20. package/dist/language-service/index.js.map +1 -1
  21. package/dist/plugin/index.cjs +134 -46
  22. package/dist/plugin/index.cjs.map +2 -2
  23. package/dist/plugin/index.js +134 -46
  24. package/dist/plugin/index.js.map +2 -2
  25. package/dist/spec/index.cjs +55 -7
  26. package/dist/spec/index.cjs.map +1 -1
  27. package/dist/spec/index.js +55 -7
  28. package/dist/spec/index.js.map +1 -1
  29. package/dist/table/assemble.d.ts.map +1 -1
  30. package/dist/table/emit-assembly.d.ts +2 -2
  31. package/dist/table/emit-assembly.d.ts.map +1 -1
  32. package/dist/table/emit.d.ts.map +1 -1
  33. package/dist/table/exec.d.ts.map +1 -1
  34. package/dist/table/index.cjs +1566 -1478
  35. package/dist/table/index.cjs.map +3 -3
  36. package/dist/table/index.js +1566 -1478
  37. package/dist/table/index.js.map +3 -3
  38. package/dist/table/ops.d.ts +8 -1
  39. package/dist/table/ops.d.ts.map +1 -1
  40. package/dist/table/program.d.ts +4 -1
  41. package/dist/table/program.d.ts.map +1 -1
  42. package/dist/version.d.ts +1 -1
  43. package/package.json +2 -1
  44. package/src/combinators/first-set.ts +29 -3
  45. package/src/combinators/sequence.ts +23 -7
  46. package/src/combinators/trivia-skip.ts +31 -0
  47. package/src/table/assemble.ts +30 -18
  48. package/src/table/child-slots.ts +2 -2
  49. package/src/table/emit-assembly.ts +36 -25
  50. package/src/table/emit.ts +12 -1
  51. package/src/table/encode.ts +4 -4
  52. package/src/table/exec.ts +20 -12
  53. package/src/table/ops.ts +9 -1
  54. package/src/table/program.ts +22 -1
  55. package/src/table/site-labels.ts +2 -2
  56. package/src/table/stamp.ts +2 -2
  57. package/src/version.ts +1 -1
@@ -44,6 +44,13 @@ function empty() {
44
44
  function matchesEmpty(p, seen = /* @__PURE__ */ new Set(), resolve) {
45
45
  if (seen.has(p)) return true;
46
46
  seen.add(p);
47
+ try {
48
+ return matchesEmptyBody(p, seen, resolve);
49
+ } finally {
50
+ seen.delete(p);
51
+ }
52
+ }
53
+ function matchesEmptyBody(p, seen, resolve) {
47
54
  const me = (c) => matchesEmpty(c, seen, resolve);
48
55
  const d = p._def;
49
56
  switch (d.tag) {
@@ -145,6 +152,13 @@ function sequenceFirstSet(parsers) {
145
152
  function firstSetOf(p, seen = /* @__PURE__ */ new Set(), resolve) {
146
153
  if (seen.has(p)) return any();
147
154
  seen.add(p);
155
+ try {
156
+ return firstSetBody(p, seen, resolve);
157
+ } finally {
158
+ seen.delete(p);
159
+ }
160
+ }
161
+ function firstSetBody(p, seen, resolve) {
148
162
  const fs = (c) => firstSetOf(c, seen, resolve);
149
163
  const empties = (c) => matchesEmpty(c, /* @__PURE__ */ new Set(), resolve);
150
164
  const d = p._def;
@@ -1185,6 +1199,23 @@ function rollbackTrivia(ctx, mark) {
1185
1199
  if (ctx._triviaLog && ctx._triviaLog.length !== mark.log) ctx._triviaLog.length = mark.log;
1186
1200
  if (ctx._rootTriviaLog && ctx._rootTriviaLog.length !== mark.rootLog) ctx._rootTriviaLog.length = mark.rootLog;
1187
1201
  }
1202
+ function removeNumberRange(values, start, end) {
1203
+ if (values === void 0 || end <= start) return;
1204
+ if (values.length > end) values.copyWithin(start, end);
1205
+ values.length -= end - start;
1206
+ }
1207
+ function rollbackScannedTriviaAt(ctx, tlogStart, tlogEnd, logStart, logEnd, rootLogStart, rootLogEnd) {
1208
+ const b = ctx._cstBuf;
1209
+ if (b !== void 0) {
1210
+ const tlog = b.tl;
1211
+ removeNumberRange(tlog, tlogStart, tlogEnd);
1212
+ if (tlog !== void 0 && tlog.length === 0) b.tl = void 0;
1213
+ } else {
1214
+ removeNumberRange(ctx._cstTriviaLog, tlogStart, tlogEnd);
1215
+ }
1216
+ removeNumberRange(ctx._triviaLog, logStart, logEnd);
1217
+ removeNumberRange(ctx._rootTriviaLog, rootLogStart, rootLogEnd);
1218
+ }
1188
1219
  function parseTriviaNoCapture(triviaP, input, cur, ctx) {
1189
1220
  const probeCtx = createDetachedParseContext(ctx.trackLines, ctx.state);
1190
1221
  probeCtx._lineIndex = ctx._lineIndex;
@@ -3213,6 +3244,7 @@ var OP_LIVE = 37;
3213
3244
  var OP_ATTEMPT = 38;
3214
3245
  var OP_LABEL = 39;
3215
3246
  var OP_COV = 40;
3247
+ var OP_SCOPE_PLAIN = 41;
3216
3248
  var OP_DISPATCH = 25;
3217
3249
  var OP_ROUTED = 26;
3218
3250
  var OP_NAMES = {
@@ -3255,7 +3287,8 @@ var OP_NAMES = {
3255
3287
  [OP_LIVE]: "LIVE",
3256
3288
  [OP_ATTEMPT]: "ATTEMPT",
3257
3289
  [OP_LABEL]: "LABEL",
3258
- [OP_COV]: "COV"
3290
+ [OP_COV]: "COV",
3291
+ [OP_SCOPE_PLAIN]: "SCOPE_PLAIN"
3259
3292
  };
3260
3293
 
3261
3294
  // src/combinators/adjacency.ts
@@ -4238,6 +4271,15 @@ function expandCompact(p) {
4238
4271
  ...p.a === void 0 ? {} : { asm: p.a }
4239
4272
  });
4240
4273
  }
4274
+ function validateDispatchSpec(spec, arity, fallbackRouted) {
4275
+ const invalid = (arms) => {
4276
+ for (const arm of arms) if (!Number.isInteger(arm) || arm < 0 || arm >= arity) return true;
4277
+ return false;
4278
+ };
4279
+ if (spec === void 0 || !Number.isInteger(arity) || arity < 0 || fallbackRouted !== 0 && fallbackRouted !== 1 || spec.routed.length !== arity || spec.routed.some((value) => value !== 0 && value !== 1) || invalid(spec.byKey.values()) || invalid(spec.byFold.values()) || spec.match.some((matcher) => invalid([matcher[3]]))) {
4280
+ throw new TypeError("table: malformed dispatch");
4281
+ }
4282
+ }
4241
4283
  var TABLE_RUNTIME = /* @__PURE__ */ Symbol("parseman.tableRuntime");
4242
4284
  function ownTableProgram(prog, resolved) {
4243
4285
  return { ...prog, [TABLE_RUNTIME]: { resolved } };
@@ -4796,7 +4838,7 @@ var Encoder = class {
4796
4838
  this.carryTriviaMeta(amb);
4797
4839
  this.carryTriviaMeta(p);
4798
4840
  if (p._def.tag === "grammar") this.carryTriviaMeta(p._def.triviaParser);
4799
- this.rules[name] = amb === void 0 ? body : this.emit(OP_SCOPE, this.triviaSlot(amb), body);
4841
+ this.rules[name] = amb === void 0 ? body : this.emit(OP_SCOPE_PLAIN, this.triviaSlot(amb), body);
4800
4842
  }
4801
4843
  /**
4802
4844
  * The row a cross-rule `g.X` reference jumps to — the target's body, RE-WRAPPED
@@ -4839,7 +4881,7 @@ var Encoder = class {
4839
4881
  const amb = p._meta.grammarTrivia ?? target._meta.grammarTrivia;
4840
4882
  if (amb === void 0 || this.activeTrivia !== void 0) return ip;
4841
4883
  if (!hasOwnTriviaBoundary(target)) return ip;
4842
- return this.emit(OP_SCOPE, this.triviaSlot(amb), ip);
4884
+ return this.emit(OP_SCOPE_PLAIN, this.triviaSlot(amb), ip);
4843
4885
  }
4844
4886
  node(p) {
4845
4887
  const lazyRef = p._def.tag === "lazy";
@@ -5244,6 +5286,7 @@ var Encoder = class {
5244
5286
  return [ip + 1];
5245
5287
  case OP_SCOPE:
5246
5288
  case OP_SCOPE_CAP:
5289
+ case OP_SCOPE_PLAIN:
5247
5290
  case OP_WITHCTX:
5248
5291
  case OP_XFORM:
5249
5292
  case OP_LEAF:
@@ -6434,6 +6477,7 @@ function childSlots(code, ip, out) {
6434
6477
  case OP_GATE:
6435
6478
  case OP_SCOPE:
6436
6479
  case OP_SCOPE_CAP:
6480
+ case OP_SCOPE_PLAIN:
6437
6481
  case OP_WITHCTX:
6438
6482
  case OP_XFORM:
6439
6483
  case OP_NODE:
@@ -6522,7 +6566,7 @@ function reachableSites(code, roots) {
6522
6566
  }
6523
6567
  function transfer(code, ip, at, hostCst) {
6524
6568
  const op = code[ip];
6525
- if (op === OP_SCOPE || op === OP_SCOPE_CAP) {
6569
+ if (op === OP_SCOPE || op === OP_SCOPE_CAP || op === OP_SCOPE_PLAIN) {
6526
6570
  const ki = code[ip + 1];
6527
6571
  const tri = ki < 0 ? TRI_NONE : ki;
6528
6572
  const cap = op === OP_SCOPE_CAP ? CAP_ON : at.cap;
@@ -6603,6 +6647,7 @@ var EMITTED_PARAMS = [
6603
6647
  "pushCstLeaf",
6604
6648
  "pushCstChild",
6605
6649
  "rollbackTriviaAt",
6650
+ "rollbackScannedTriviaAt",
6606
6651
  "failAt",
6607
6652
  "classHas",
6608
6653
  "consumeTrivia",
@@ -6746,19 +6791,24 @@ if(ctx._errors!==undefined&&ctx._errors.length!==${t2}er)ctx._errors.length=${t2
6746
6791
  if (!s.fd && !s.er) return `if(${t2}n)rollbackTriviaAt(ctx,${t2}raw,${t2}tl,${t2}lv,0,undefined,${t2}lg,${t2}rt)`;
6747
6792
  return `if(${t2}n){rollbackTriviaAt(ctx,${t2}raw,${t2}tl,${t2}lv,${s.fd ? `${t2}fd` : "0"},${s.er ? `${t2}er` : "undefined"},${t2}lg,${t2}rt)}`;
6748
6793
  }
6749
- function emitTerm(callee, dst, t2, l, skip, s) {
6794
+ function emitTerm(callee, dst, t2, l, skip) {
6750
6795
  const fast = `const ${t2}v=${callee}(input,cur,ctx)
6751
6796
  if(${t2}v===FAIL)return FAIL
6752
6797
  ${dst}=${t2}v
6753
6798
  cur=EC.e`;
6754
6799
  if (l.tri === TRI_NONE) return fast;
6755
- const scanned = `${emitMark(t2, l.buf, s)}
6800
+ const scanned = `const ${t2}tl=ctx._cstBuf!==undefined?(ctx._cstBuf.tl!==undefined?ctx._cstBuf.tl.length:0):(ctx._cstTriviaLog!==undefined?ctx._cstTriviaLog.length:0)
6801
+ const ${t2}lg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
6802
+ const ${t2}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0
6756
6803
  const ${t2}s=${skip}(input,cur,ctx)
6804
+ const ${t2}stl=ctx._cstBuf!==undefined?(ctx._cstBuf.tl!==undefined?ctx._cstBuf.tl.length:0):(ctx._cstTriviaLog!==undefined?ctx._cstTriviaLog.length:0)
6805
+ const ${t2}slg=ctx._triviaLog!==undefined?ctx._triviaLog.length:0
6806
+ const ${t2}srt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0
6757
6807
  const ${t2}v=${callee}(input,${t2}s,ctx)
6758
6808
  if(${t2}v===FAIL)return FAIL
6759
6809
  ${dst}=${t2}v
6760
6810
  if(EC.e>${t2}s)cur=EC.e
6761
- else{${emitRollback(t2, l.buf, s)}}`;
6811
+ else{rollbackScannedTriviaAt(ctx,${t2}tl,${t2}stl,${t2}lg,${t2}slg,${t2}rt,${t2}srt)}`;
6762
6812
  if (l.tri !== TRI_UNKNOWN) return scanned;
6763
6813
  return `if(ctx.trivia===undefined){
6764
6814
  ${fast}
@@ -6921,7 +6971,7 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)
6921
6971
  const op = code[ip];
6922
6972
  if (op === OP_GATE && (cfg.tolerant || cfg.probe)) return code[ip + 2];
6923
6973
  if (op === OP_RULE) return code[ip + 1];
6924
- if (op === OP_SCOPE && code[ip + 3] === 0 && code[ip + 1] >= 0 && labels.at(ip).tri === code[ip + 1]) {
6974
+ if ((op === OP_SCOPE_PLAIN || op === OP_SCOPE && code[ip + 3] === 0) && code[ip + 1] >= 0 && labels.at(ip).tri === code[ip + 1]) {
6925
6975
  return code[ip + 2];
6926
6976
  }
6927
6977
  return void 0;
@@ -7089,10 +7139,11 @@ return r.value
7089
7139
  }`;
7090
7140
  }
7091
7141
  case OP_SCOPE:
7092
- case OP_SCOPE_CAP: {
7142
+ case OP_SCOPE_CAP:
7143
+ case OP_SCOPE_PLAIN: {
7093
7144
  const ki = code[ip + 1];
7094
7145
  const cap = op === OP_SCOPE_CAP;
7095
- const flags = code[ip + 3];
7146
+ const flags = op === OP_SCOPE_PLAIN ? 0 : code[ip + 3];
7096
7147
  const child = link(code[ip + 2]);
7097
7148
  const scanFor = swapLegal && ki >= 0 && !triviaLabelled[ki] ? hoist("ts", `TRIVIASCAN[${ki}]`) : "null";
7098
7149
  const tri = ki < 0 ? "undefined" : hoist("tv", `TRIVIA[${ki}]`);
@@ -7246,7 +7297,7 @@ return v
7246
7297
  const vn = `v${i}`;
7247
7298
  parts.push(`let ${vn}`);
7248
7299
  if (REC) parts.push(pub(i));
7249
- parts.push(emitTerm(kids[i], vn, tmp(), L, skipFor(L), sinks));
7300
+ parts.push(emitTerm(kids[i], vn, tmp(), L, skipFor(L)));
7250
7301
  names.push(vn);
7251
7302
  }
7252
7303
  parts.push("EC.e=cur");
@@ -7347,11 +7398,11 @@ return FAIL
7347
7398
  const other = otherIp >= 0 ? link(otherIp) : void 0;
7348
7399
  const otherRouted = code[ip + 4] === 1;
7349
7400
  const n = code[ip + 5];
7401
+ validateDispatchSpec(spec, n, code[ip + 4]);
7350
7402
  const armBase = ip + 6;
7351
7403
  const arms = [];
7352
7404
  for (let i = 0; i < n; i++) arms.push(link(code[armBase + i]));
7353
7405
  const bk = hoist("bk", `DSP[${di}].byKey`);
7354
- const rt = hoist("rt", `DSP[${di}].routed`);
7355
7406
  const dx = hoist("dx", `DSP[${di}].expected`);
7356
7407
  const claims = (m) => {
7357
7408
  switch (m[0]) {
@@ -7375,6 +7426,15 @@ ${spec.match.map((m, i) => `${i === 0 ? "" : "else "}if(${claims(m)})arm=${m[3]}
7375
7426
  ` : "";
7376
7427
  const m1 = tmp();
7377
7428
  const m2 = tmp();
7429
+ const routedCall = (target) => `{const savedRouted=ctx._routed
7430
+ ${emitRollback(m1, L.buf, sinks)}
7431
+ ${emitMark(m2, L.buf, sinks, false)}
7432
+ ctx._routed={value:key,span:{start:pos,end:selEnd}}
7433
+ try{v=${target}(input,pos,ctx)}finally{ctx._routed=savedRouted}
7434
+ break}`;
7435
+ const plainCall = (target) => `v=${target}(input,selEnd,ctx);break`;
7436
+ const armCases = arms.map((target, i) => `case ${i}:${spec.routed[i] === 1 ? routedCall(target) : plainCall(target)}`).join("\n");
7437
+ const fallbackCase = other === void 0 ? "" : `default:${otherRouted ? routedCall(other) : plainCall(other)}`;
7378
7438
  return `${head}
7379
7439
  ${emitMark(m1, L.buf, sinks)}
7380
7440
  const sv=${selector}(input,pos,ctx)
@@ -7382,24 +7442,16 @@ if(sv===FAIL)return FAIL
7382
7442
  const selEnd=EC.e
7383
7443
  const key=sv
7384
7444
  let arm=${bk}.get(key)
7385
- ${fold}${chain}let ur
7445
+ ${fold}${chain}
7386
7446
  if(arm===undefined){
7387
- ${other === void 0 ? `ctx._fe=selEnd;ctx._fx=${dx};return FAIL` : `ur=${String(otherRouted)}`}
7388
- }else ur=${rt}[arm]===1
7389
- const savedRouted=ctx._routed
7390
- ${emitMark(m2, L.buf, sinks)}
7391
- if(ur){
7392
- ${emitRollback(m1, L.buf, sinks)}
7393
- ${emitMark(m2, L.buf, sinks, false)}
7394
- ctx._routed={value:key,span:{start:pos,end:selEnd}}
7447
+ ${other === void 0 ? `ctx._fe=selEnd;ctx._fx=${dx};return FAIL` : ""}
7395
7448
  }
7396
- const at=ur?pos:selEnd
7449
+ ${emitMark(m2, L.buf, sinks)}
7397
7450
  let v
7398
7451
  switch(arm){
7399
- ${arms.map((a, i) => `case ${i}:v=${a}(input,at,ctx);break`).join("\n")}
7400
- ${other === void 0 ? "" : `default:v=${other}(input,at,ctx)`}
7452
+ ${armCases}
7453
+ ${fallbackCase}
7401
7454
  }
7402
- if(ur)ctx._routed=savedRouted
7403
7455
  if(v===FAIL){
7404
7456
  ${emitRollback(m2, L.buf, sinks)}
7405
7457
  ctx._fc=true
@@ -7774,7 +7826,9 @@ function sequence(...parsers) {
7774
7826
  continue;
7775
7827
  }
7776
7828
  if (ctx.trivia && i > 0) {
7777
- const mark = saveTriviaMark(ctx);
7829
+ const mTlog = cstTlLen(ctx);
7830
+ const mLog = ctx._triviaLog?.length ?? 0;
7831
+ const mRootLog = ctx._rootTriviaLog?.length ?? 0;
7778
7832
  let scanEnd;
7779
7833
  if (needsDeferredTriviaCommit(ctx)) {
7780
7834
  const scan = scanTrivia(input, cur, ctx);
@@ -7783,10 +7837,13 @@ function sequence(...parsers) {
7783
7837
  } else {
7784
7838
  scanEnd = advanceTrivia(input, cur, ctx);
7785
7839
  }
7840
+ const scanTlog = cstTlLen(ctx);
7841
+ const scanLog = ctx._triviaLog?.length ?? 0;
7842
+ const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
7786
7843
  const result2 = parsers[i].parse(input, scanEnd, ctx);
7787
7844
  if (!result2.ok) return result2;
7788
7845
  if (result2.span.end > scanEnd) cur = result2.span.end;
7789
- else rollbackTrivia(ctx, mark);
7846
+ else rollbackScannedTriviaAt(ctx, mTlog, scanTlog, mLog, scanLog, mRootLog, scanRootLog);
7790
7847
  if (values !== void 0) values.push(result2.value);
7791
7848
  continue;
7792
7849
  }
@@ -7817,7 +7874,9 @@ function sequence(...parsers) {
7817
7874
  }
7818
7875
  if (ctx.trivia && i > 0) {
7819
7876
  let scanEnd;
7820
- const mark = saveTriviaMark(ctx);
7877
+ const mTlog = cstTlLen(ctx);
7878
+ const mLog = ctx._triviaLog?.length ?? 0;
7879
+ const mRootLog = ctx._rootTriviaLog?.length ?? 0;
7821
7880
  if (needsDeferredTriviaCommit(ctx)) {
7822
7881
  const scan = scanTrivia(input, cur, ctx);
7823
7882
  scan.commit();
@@ -7825,10 +7884,13 @@ function sequence(...parsers) {
7825
7884
  } else {
7826
7885
  scanEnd = advanceTrivia(input, cur, ctx);
7827
7886
  }
7887
+ const scanTlog = cstTlLen(ctx);
7888
+ const scanLog = ctx._triviaLog?.length ?? 0;
7889
+ const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
7828
7890
  const result2 = parsers[i].parse(input, scanEnd, ctx);
7829
7891
  if (!result2.ok) return result2;
7830
7892
  if (result2.span.end > scanEnd) cur = result2.span.end;
7831
- else rollbackTrivia(ctx, mark);
7893
+ else rollbackScannedTriviaAt(ctx, mTlog, scanTlog, mLog, scanLog, mRootLog, scanRootLog);
7832
7894
  if (values !== void 0) values.push(result2.value);
7833
7895
  continue;
7834
7896
  }
@@ -7856,7 +7918,9 @@ function sequence(...parsers) {
7856
7918
  for (let i = 0; i < parsers.length; i++) {
7857
7919
  if (ctx.trivia && i > 0) {
7858
7920
  let scanEnd;
7859
- let mark = saveTriviaMark(ctx);
7921
+ const mTlog = cstTlLen(ctx);
7922
+ const mLog = ctx._triviaLog?.length ?? 0;
7923
+ const mRootLog = ctx._rootTriviaLog?.length ?? 0;
7860
7924
  if (needsDeferredTriviaCommit(ctx)) {
7861
7925
  const scan = scanTrivia(input, cur, ctx);
7862
7926
  scan.commit();
@@ -7864,12 +7928,15 @@ function sequence(...parsers) {
7864
7928
  } else {
7865
7929
  scanEnd = advanceTrivia(input, cur, ctx);
7866
7930
  }
7931
+ const scanTlog = cstTlLen(ctx);
7932
+ const scanLog = ctx._triviaLog?.length ?? 0;
7933
+ const scanRootLog = ctx._rootTriviaLog?.length ?? 0;
7867
7934
  const result2 = parsers[i].parse(input, scanEnd, ctx);
7868
7935
  if (!result2.ok) return result2;
7869
7936
  if (result2.span.end > scanEnd) {
7870
7937
  cur = result2.span.end;
7871
7938
  } else {
7872
- rollbackTrivia(ctx, mark);
7939
+ rollbackScannedTriviaAt(ctx, mTlog, scanTlog, mLog, scanLog, mRootLog, scanRootLog);
7873
7940
  }
7874
7941
  if (values !== void 0) values.push(result2.value);
7875
7942
  continue;
@@ -8234,7 +8301,7 @@ function committed(c) {
8234
8301
  }
8235
8302
  function stampRuleMap(prog, d, artifactMetadata = {}, resolved = resolveTable(prog)) {
8236
8303
  const triviaOfRule = (ip) => {
8237
- if (prog.code[ip] !== OP_SCOPE) return void 0;
8304
+ if (prog.code[ip] !== OP_SCOPE_PLAIN && prog.code[ip] !== OP_SCOPE) return void 0;
8238
8305
  const slot = prog.code[ip + 1];
8239
8306
  return slot < 0 ? void 0 : resolved.trivia[slot];
8240
8307
  };
@@ -8448,20 +8515,19 @@ function assemble(t2, prog, cfg) {
8448
8515
  TERMV = v2;
8449
8516
  return EC.e > scanEnd2 ? EC.e : cur;
8450
8517
  }
8451
- const need = markCst(ctx);
8452
- const mRaw = MRAW;
8453
- const mTl = MTL;
8454
- const mLv = MLV;
8455
- const mFl = need ? ctx._fields?.length ?? 0 : 0;
8456
- const mEr = need ? ctx._errors?.length ?? 0 : 0;
8518
+ const need = rollbackNeeded(ctx);
8519
+ const mTl = need ? cstTlLen(ctx) : 0;
8457
8520
  const mLog = need ? ctx._triviaLog?.length ?? 0 : 0;
8458
8521
  const mRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
8459
8522
  const scanEnd = skipTrivia(input, cur, ctx);
8523
+ const scanTl = need ? cstTlLen(ctx) : 0;
8524
+ const scanLog = need ? ctx._triviaLog?.length ?? 0 : 0;
8525
+ const scanRoot = need ? ctx._rootTriviaLog?.length ?? 0 : 0;
8460
8526
  const v = child(input, scanEnd, ctx);
8461
8527
  if (v === FAIL) return -1;
8462
8528
  TERMV = v;
8463
8529
  if (EC.e > scanEnd) return EC.e;
8464
- if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot);
8530
+ if (need) rollbackScannedTriviaAt(ctx, mTl, scanTl, mLog, scanLog, mRoot, scanRoot);
8465
8531
  return cur;
8466
8532
  }
8467
8533
  function committed2(c) {
@@ -8875,7 +8941,8 @@ function assemble(t2, prog, cfg) {
8875
8941
  };
8876
8942
  }
8877
8943
  case OP_SCOPE:
8878
- case OP_SCOPE_CAP: {
8944
+ case OP_SCOPE_CAP:
8945
+ case OP_SCOPE_PLAIN: {
8879
8946
  const ki = code[ip + 1];
8880
8947
  const scopeTrivia = ki < 0 ? void 0 : trivia2[ki];
8881
8948
  const scopeLabels = scopeTrivia?._meta.triviaKindLabels;
@@ -8909,7 +8976,7 @@ function assemble(t2, prog, cfg) {
8909
8976
  SCAN = savedScan;
8910
8977
  return v;
8911
8978
  };
8912
- return scopeRootPolicy(scopePiece, code[ip + 3]);
8979
+ return scopeRootPolicy(scopePiece, code[ip] === OP_SCOPE_PLAIN ? 0 : code[ip + 3]);
8913
8980
  }
8914
8981
  case OP_ROUTED: {
8915
8982
  const fb = code[ip + 1];
@@ -9825,15 +9892,21 @@ function assemble(t2, prog, cfg) {
9825
9892
  target = arms[arm];
9826
9893
  usesRouted = routed2[arm] === 1;
9827
9894
  }
9828
- const savedRouted = ctx._routed;
9829
9895
  let mark = saveTriviaMark(ctx);
9896
+ let v;
9830
9897
  if (usesRouted) {
9898
+ const savedRouted = ctx._routed;
9831
9899
  rollbackTrivia(ctx, selectorMark);
9832
9900
  mark = saveTriviaMark(ctx);
9833
9901
  ctx._routed = { value: key, span: { start: pos, end: selEnd } };
9902
+ try {
9903
+ v = target(input, pos, ctx);
9904
+ } finally {
9905
+ ctx._routed = savedRouted;
9906
+ }
9907
+ } else {
9908
+ v = target(input, selEnd, ctx);
9834
9909
  }
9835
- const v = target(input, usesRouted ? pos : selEnd, ctx);
9836
- if (usesRouted) ctx._routed = savedRouted;
9837
9910
  if (v === FAIL) {
9838
9911
  rollbackTrivia(ctx, mark);
9839
9912
  ctx._fc = true;
@@ -9979,6 +10052,11 @@ function assemble(t2, prog, cfg) {
9979
10052
  if (s.sentinel !== void 0) extraIps.push(s.sentinel[0]);
9980
10053
  }
9981
10054
  for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0]);
10055
+ const validationRoots = [...Object.values(prog.rules), ...extraIps];
10056
+ for (const ip of reachableSites(code, validationRoots)) {
10057
+ if (code[ip] !== OP_DISPATCH) continue;
10058
+ validateDispatchSpec(dsp[code[ip + 2]], code[ip + 5], code[ip + 4]);
10059
+ }
9982
10060
  const pre = cfg.hostCaptureTrivia === void 0 ? prog.asm?.find((a) => a.key === cfgKey(cfg)) : void 0;
9983
10061
  if (pre !== void 0) {
9984
10062
  const pools = rebuildPools(t2.cc, t2.fx, pre.plan);
@@ -10005,6 +10083,7 @@ function assemble(t2, prog, cfg) {
10005
10083
  pushCstLeaf,
10006
10084
  pushCstChild,
10007
10085
  rollbackTriviaAt,
10086
+ rollbackScannedTriviaAt,
10008
10087
  failAt,
10009
10088
  classHas,
10010
10089
  consumeTrivia,
@@ -10075,6 +10154,7 @@ function assemble(t2, prog, cfg) {
10075
10154
  pushCstLeaf,
10076
10155
  pushCstChild,
10077
10156
  rollbackTriviaAt,
10157
+ rollbackScannedTriviaAt,
10078
10158
  failAt,
10079
10159
  classHas,
10080
10160
  consumeTrivia,
@@ -10307,6 +10387,14 @@ function tableRules(source, artifactMetadata = {}) {
10307
10387
  function jsString(s) {
10308
10388
  return JSON.stringify(s);
10309
10389
  }
10390
+ function emitClassPool(classes) {
10391
+ const array = `[${classes.map(jsString).join(",")}]`;
10392
+ if (classes.length < 2) return array;
10393
+ const delimiter = "|/~?^`".split("").find((ch2) => classes.every((spec) => !spec.includes(ch2)));
10394
+ if (delimiter === void 0) return array;
10395
+ const split = `${jsString(classes.join(delimiter))}.split(${jsString(delimiter)})`;
10396
+ return split.length < array.length ? split : array;
10397
+ }
10310
10398
  function emitConst(v) {
10311
10399
  if (v instanceof RegExp) return `/${v.source}/${v.flags}`;
10312
10400
  if (typeof v === "string") return jsString(v);
@@ -10385,7 +10473,7 @@ function programFields(prog, fns, opts = {}) {
10385
10473
  ...emitAssemblies(prog, opts.assemblies ?? []),
10386
10474
  `c:[${prog.code.join(",")}],`,
10387
10475
  `k:[${prog.k.map(emitConst).join(",")}],`,
10388
- `x:[${prog.cc.map(jsString).join(",")}],`,
10476
+ `x:${emitClassPool(prog.cc)},`,
10389
10477
  `e:[${prog.fx.map((f) => `[${f.map(jsString).join(",")}]`).join(",")}],`,
10390
10478
  `d:[${prog.disp.map((a) => `[${a.join(",")}]`).join(",")}],`,
10391
10479
  `r:${JSON.stringify(prog.rules)},`,