@mark1russell7/coeval 0.23.0 → 0.24.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.
package/build/index.js CHANGED
@@ -1568,7 +1568,7 @@ const MAX_TEXT_VALUES = 8;
1568
1568
  /** The most characters of one JSON value in the text of a type. */
1569
1569
  const MAX_TEXT_VALUE = 40;
1570
1570
  /** This function gives a short text of a JSON value for the text of a type. */
1571
- function valueText$1(value) {
1571
+ function valueText$2(value) {
1572
1572
  let text;
1573
1573
  try {
1574
1574
  text = JSON.stringify(value) ?? String(value);
@@ -1590,7 +1590,7 @@ function textAt(type, depth, wrap) {
1590
1590
  if (term === "one") {
1591
1591
  if (!Array.isArray(type["one"])) return "unknown";
1592
1592
  const values = type["one"];
1593
- const shown = values.slice(0, MAX_TEXT_VALUES).map(valueText$1);
1593
+ const shown = values.slice(0, MAX_TEXT_VALUES).map(valueText$2);
1594
1594
  return group(values.length > MAX_TEXT_VALUES ? [...shown, `… ${values.length - MAX_TEXT_VALUES} more`] : shown);
1595
1595
  }
1596
1596
  if (term === "fn") {
@@ -3744,7 +3744,7 @@ function intersectCaps(a, b) {
3744
3744
  /** This function gives the capability rules of a kind that cover a part and change its capabilities. */
3745
3745
  function rulesFor(rules, path) {
3746
3746
  if (rules === void 0 || rules.length === 0) return NO_RULES;
3747
- const out = rules.filter((rule) => rule.rule !== "same" && (rule.in === void 0 || rule.in.some((pattern) => pathMatches(path, pattern))));
3747
+ const out = rules.filter((rule) => rule.rule !== "same" && rule.rule !== "grant" && (rule.in === void 0 || rule.in.some((pattern) => pathMatches(path, pattern))));
3748
3748
  return out.length === 0 ? NO_RULES : out;
3749
3749
  }
3750
3750
  /** This function applies the rules that cover a part to the capabilities of its document. */
@@ -3768,6 +3768,53 @@ function capsUnder(rules, caps) {
3768
3768
  function partCaps(rules, path, caps) {
3769
3769
  return capsUnder(rulesFor(rules, path), caps);
3770
3770
  }
3771
+ /** This function collects the values at a path in a value. The key `"*"` steps into each item of a list. */
3772
+ function collectAt(value, path, index, out) {
3773
+ if (index === path.length) {
3774
+ if (typeof value === "string" || Array.isArray(value) && value.every((key) => typeof key === "string" || typeof key === "number")) out.push(value);
3775
+ return;
3776
+ }
3777
+ const key = path[index];
3778
+ if (key === "*") {
3779
+ if (Array.isArray(value)) for (const item of value) collectAt(item, path, index + 1, out);
3780
+ return;
3781
+ }
3782
+ if (typeof value === "object" && value !== null) collectAt(value[key], path, index + 1, out);
3783
+ }
3784
+ /**
3785
+ * This function gives the capabilities that the rules `grant` of a kind give one part (CAP.GRANT). The check and the
3786
+ * walk read them from the document. The kernel gets the same list from the clauses of the runner.
3787
+ *
3788
+ * @param rules - The capability rules of the kind (`Kind.caps`).
3789
+ * @param path - The path of the part in the value of the document.
3790
+ * @param value - The value of the document.
3791
+ * @returns The capabilities that the rules add, or an empty list.
3792
+ */
3793
+ function partGrants(rules, path, value) {
3794
+ if (rules === void 0) return NO_CAPS$2;
3795
+ let out;
3796
+ for (const rule of rules) {
3797
+ if (rule.rule !== "grant" || rule.in !== void 0 && !rule.in.some((pattern) => pathMatches(path, pattern))) continue;
3798
+ collectAt(value, rule.from, 0, out ??= []);
3799
+ }
3800
+ return out ?? NO_CAPS$2;
3801
+ }
3802
+ /**
3803
+ * The key of the frame of a context. Like the grant, it stays through a spread of a context, and only the kernel writes
3804
+ * it. It is not a root (D10): a document cannot read it (HANDLE.SEALED).
3805
+ */
3806
+ const FRAME = /* @__PURE__ */ Symbol("coeval.frame");
3807
+ /** This function gives the frame of a context, when it has one. */
3808
+ function frameOf(ctx) {
3809
+ return ctx[FRAME];
3810
+ }
3811
+ /** This function finds the frame and the clause that answer an effect. The innermost frame wins, then the first clause. */
3812
+ function clauseFor(start, keys) {
3813
+ for (let frame = start; frame !== void 0; frame = frame.outer) for (const clause of frame.clauses) if (permits([clause.op], keys)) return {
3814
+ frame,
3815
+ clause
3816
+ };
3817
+ }
3771
3818
  /**
3772
3819
  * The key of the grant of a context (CAP.NARROW-CODE): the capabilities that the kernel gave the document of the
3773
3820
  * context. A spread of a context keeps its grant. Thus compiled code passes the grant on in each context that it makes
@@ -4324,13 +4371,33 @@ var Session = class {
4324
4371
  done.catch(() => void 0);
4325
4372
  return effect.outcome = fail("#CALC!", `The mock gave a promise for the effect ${effect.kind}, and an evaluation does not wait.`, { place });
4326
4373
  }
4374
+ /**
4375
+ * This function finds the clause that answers an effect (D146). Without it, the effect goes on to the host. An effect
4376
+ * without a capability gets no clause, thus `record` gives its `#CAP!` (HANDLE.NARROW). A clause of a frame that has
4377
+ * ended gives `#CAP!`, because the capability came from that frame (HANDLE.ENDED).
4378
+ */
4379
+ clauseOf(given, start, kind, place) {
4380
+ const ctx = inEpoch(given, this.epoch) ? granted(given) : ungranted(given, this.epoch);
4381
+ const keys = typeof kind === "string" ? kind.split(".") : kind;
4382
+ if (!this.permitted(ctx, keys)) return void 0;
4383
+ const found = clauseFor(start, keys);
4384
+ if (found === void 0) return void 0;
4385
+ if (found.frame.ended || found.frame.owner !== this) return fail("#CAP!", `The handler of the effect ${keys.join(".")} has ended.`, { place });
4386
+ return found.clause;
4387
+ }
4327
4388
  effect(ctx, kind, payload, place) {
4328
4389
  const text = typeof kind === "string" ? kind : kind.join(".");
4329
4390
  if (this.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
4391
+ const frame = frameOf(ctx);
4392
+ const clause = frame === void 0 ? void 0 : this.clauseOf(ctx, frame, kind, place);
4393
+ if (clause !== void 0) return "ok" in clause ? clause : drive$2(clause.answer(payload), text);
4330
4394
  const made = this.record(ctx, kind, payload, place);
4331
4395
  return "ok" in made ? made : this.answer(made, place);
4332
4396
  }
4333
4397
  *runEffect(ctx, kind, payload, place) {
4398
+ const frame = frameOf(ctx);
4399
+ const clause = frame === void 0 ? void 0 : this.clauseOf(ctx, frame, kind, place);
4400
+ if (clause !== void 0) return "ok" in clause ? clause : yield* clause.answer(payload);
4334
4401
  const made = this.record(ctx, kind, payload, place);
4335
4402
  if ("ok" in made) return made;
4336
4403
  const perform = this.perform;
@@ -4741,11 +4808,12 @@ function compileNode(c, value, place, declared, depth, address = false) {
4741
4808
  if (runner !== void 0) {
4742
4809
  let inner;
4743
4810
  try {
4744
- inner = isCompiledRunner(runner) ? runner.compileRun(input) : interpretRun(runner, input, split.fill);
4811
+ inner = isCompiledRunner(runner) ? runner.compileRun(input) : interpretRun(runner, input, split.fill, kind?.caps?.some((rule) => rule.rule === "grant") === true);
4745
4812
  } catch (error) {
4746
4813
  return failingWith(c, meta, placed(thrown(error, `the runner ${name}`), place));
4747
4814
  }
4748
- const run = placedRun(inner, place, name);
4815
+ const guard = guardOf(c, place);
4816
+ const run = placedRun(guard === void 0 ? inner : guardRun(inner, guard, c.machine.domains), place, name);
4749
4817
  return finish$1(c, meta, runnerCode(run, place, kindText), run);
4750
4818
  }
4751
4819
  let inner;
@@ -4754,7 +4822,8 @@ function compileNode(c, value, place, declared, depth, address = false) {
4754
4822
  } catch (error) {
4755
4823
  return failingWith(c, meta, placed(thrown(error, `the evaluator ${name}`), place));
4756
4824
  }
4757
- const done = finish$1(c, meta, TOTAL.has(inner) ? inner : placedCode(inner, place, name), void 0);
4825
+ const guard = guardOf(c, place);
4826
+ const done = finish$1(c, meta, guard !== void 0 ? placedCode(guardCode(inner, guard, c.machine.domains), place, name) : TOTAL.has(inner) ? inner : placedCode(inner, place, name), void 0);
4758
4827
  if (frees === void 0 || kind === void 0 || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
4759
4828
  const free = freeOf(doc, kind, split.parts, frees);
4760
4829
  if (free === void 0) return done;
@@ -5150,6 +5219,29 @@ function gated(rules, part) {
5150
5219
  * This function gives the steps of a runner, with the place of each error and of each exception. The steps get the
5151
5220
  * context that its grant permits (CAP.NARROW-CODE).
5152
5221
  */
5222
+ /** This function gives the key of a place in the guards of a plan. */
5223
+ function guardKey(place) {
5224
+ return JSON.stringify(place.map(String));
5225
+ }
5226
+ /** This function gives the type of the boundary at a place, when the plan has one (PLAN.GUARD). */
5227
+ function guardOf(c, place) {
5228
+ return c.guards?.get(guardKey(place));
5229
+ }
5230
+ /** This function gives the result of a part of a boundary: a value that is not a member gives `#VALUE!` (PLAN.GUARD). */
5231
+ function guarded(result, type, domains) {
5232
+ if (!result.ok || member(result.value, type, domains)) return result;
5233
+ return fail("#VALUE!", `The value is not a member of ${typeText(type)}, the type of its boundary.`);
5234
+ }
5235
+ /** This function adds the test of a boundary to the code of a part. */
5236
+ function guardCode(inner, type, domains) {
5237
+ return (ctx, rt) => guarded(inner(ctx, rt), type, domains);
5238
+ }
5239
+ /** This function adds the test of a boundary to the steps of a part. */
5240
+ function guardRun(inner, type, domains) {
5241
+ return function* (ctx, rt) {
5242
+ return guarded(yield* inner(ctx, rt), type, domains);
5243
+ };
5244
+ }
5153
5245
  function placedRun(inner, place, name) {
5154
5246
  return function* (ctx, rt) {
5155
5247
  try {
@@ -5241,7 +5333,35 @@ function interpret(fn, input, fill) {
5241
5333
  });
5242
5334
  };
5243
5335
  }
5244
- function interpretRun(fn, input, fill) {
5336
+ /**
5337
+ * This function runs a part under a new frame (D146). The part gets the capabilities of the document and the kinds of
5338
+ * the clauses (CAP.GRANT), with the grant of the kernel. A rule around an effect narrows them as it narrows any other
5339
+ * capability (HANDLE.NARROW). The frame ends with the steps.
5340
+ */
5341
+ function* handling(given, clauses, rt, run) {
5342
+ const ctx = granted(given);
5343
+ const frame = {
5344
+ clauses,
5345
+ outer: frameOf(ctx),
5346
+ owner: rt,
5347
+ ended: false
5348
+ };
5349
+ const caps = [...ctx.caps];
5350
+ for (const clause of clauses) if (!permits(caps, clause.op)) caps.push(clause.op);
5351
+ const inner = {
5352
+ ...ctx,
5353
+ caps,
5354
+ [GRANT]: caps,
5355
+ [EPOCH]: epochOf(ctx),
5356
+ [FRAME]: frame
5357
+ };
5358
+ try {
5359
+ return yield* run(inner);
5360
+ } finally {
5361
+ frame.ended = true;
5362
+ }
5363
+ }
5364
+ function interpretRun(fn, input, fill, grants) {
5245
5365
  const { kind, typePath, doc, data, place, machine } = input;
5246
5366
  const refill = input.fill;
5247
5367
  const compiled = input.parts;
@@ -5251,7 +5371,8 @@ function interpretRun(fn, input, fill) {
5251
5371
  key: part.key,
5252
5372
  path: part.path,
5253
5373
  ...part.name === void 0 ? {} : { name: part.name },
5254
- evaluate: (override) => part.run(override === void 0 ? ctx : handed(ctx, override), rt)
5374
+ evaluate: (override) => part.run(override === void 0 ? ctx : handed(ctx, override), rt),
5375
+ ...grants ? { handled: (clauses) => handling(ctx, clauses, rt, (inner) => part.run(inner, rt)) } : {}
5255
5376
  }));
5256
5377
  return fn({
5257
5378
  kind,
@@ -5321,6 +5442,7 @@ function prepare(machine, doc, options = {}) {
5321
5442
  const traced = options.trace === true;
5322
5443
  const fold = !traced && options.fold !== false;
5323
5444
  const fixed = fold ? fixedNamesOf(options.fixed?.names) : void 0;
5445
+ const guards = options.guards === void 0 || options.guards.length === 0 ? void 0 : new Map(options.guards.map((each) => [guardKey(each.place), each.type]));
5324
5446
  const c = {
5325
5447
  machine,
5326
5448
  traced,
@@ -5328,7 +5450,8 @@ function prepare(machine, doc, options = {}) {
5328
5450
  verb,
5329
5451
  noAnswer,
5330
5452
  fold,
5331
- fixed
5453
+ fixed,
5454
+ guards
5332
5455
  };
5333
5456
  let root;
5334
5457
  try {
@@ -5441,7 +5564,8 @@ function evaluate(machine, doc, context = makeContext(), options) {
5441
5564
  const plan = prepare(machine, doc, {
5442
5565
  trace: true,
5443
5566
  ...declaredType === void 0 ? {} : { declaredType },
5444
- ...given.verb === void 0 ? {} : { verb: given.verb }
5567
+ ...given.verb === void 0 ? {} : { verb: given.verb },
5568
+ ...given.guards === void 0 ? {} : { guards: given.guards }
5445
5569
  });
5446
5570
  const ctx = rootContext(machine, context);
5447
5571
  const s = new Session(machine, ctx.caps, false, true, given.perform, void 0, void 0, readingOf(given.reads, ctx));
@@ -5462,6 +5586,7 @@ async function run(machine, doc, context = makeContext(), options = {}) {
5462
5586
  const plan = prepare(machine, doc, {
5463
5587
  trace: true,
5464
5588
  ...options.declaredType === void 0 ? {} : { declaredType: options.declaredType },
5589
+ ...options.guards === void 0 ? {} : { guards: options.guards },
5465
5590
  ...options.verb === void 0 ? {} : { verb: options.verb }
5466
5591
  });
5467
5592
  const ctx = rootContext(machine, context);
@@ -5729,6 +5854,72 @@ const STD_WORLD = {
5729
5854
  lazy: [{ after: 1 }],
5730
5855
  summary: "It evaluates its part without capabilities, thus an effect in it fails."
5731
5856
  },
5857
+ handle: {
5858
+ verbs: {
5859
+ eval: "control.handle",
5860
+ view: "view.op"
5861
+ },
5862
+ parts: {
5863
+ shape: "record",
5864
+ fields: {
5865
+ with: {
5866
+ shape: "list",
5867
+ item: {
5868
+ shape: "record",
5869
+ fields: {
5870
+ op: { shape: "none" },
5871
+ do: { shape: "term" },
5872
+ abort: { shape: "term" }
5873
+ },
5874
+ required: ["op"],
5875
+ closed: true
5876
+ }
5877
+ },
5878
+ return: { shape: "term" },
5879
+ body: { shape: "term" }
5880
+ },
5881
+ required: ["with", "body"],
5882
+ closed: true
5883
+ },
5884
+ scope: [{
5885
+ names: { fixed: ["payload"] },
5886
+ in: [[
5887
+ "with",
5888
+ "*",
5889
+ "do"
5890
+ ], [
5891
+ "with",
5892
+ "*",
5893
+ "abort"
5894
+ ]]
5895
+ }, {
5896
+ names: { fixed: ["result"] },
5897
+ in: [["return"]]
5898
+ }],
5899
+ caps: [{
5900
+ rule: "grant",
5901
+ from: [
5902
+ "with",
5903
+ "*",
5904
+ "op"
5905
+ ],
5906
+ in: [["body"]]
5907
+ }],
5908
+ lazy: [{ in: [
5909
+ [
5910
+ "with",
5911
+ "*",
5912
+ "do"
5913
+ ],
5914
+ [
5915
+ "with",
5916
+ "*",
5917
+ "abort"
5918
+ ],
5919
+ ["return"]
5920
+ ] }],
5921
+ summary: "It answers the effects of its body with its clauses, in place, thus a mock, a retry or a log is a document."
5922
+ },
5732
5923
  log: {
5733
5924
  verbs: {
5734
5925
  eval: "effect.log",
@@ -5766,7 +5957,8 @@ const STD_WORLD = {
5766
5957
  * A literal in a place that takes a document gets its data envelope (K159, D65). Such a place is a part of `if`,
5767
5958
  * `list`, `seq` or `pure`, the value of `write`, or the whole text. The document after a header name is such a place
5768
5959
  * too. Thus `list(1, "a")` evaluates to `[1, "a"]`. The forms `add`, `multiply` and `greater` write an operator document that its operator
5769
- * does not write, for example `add(5)` (K163).
5960
+ * does not write, for example `add(5)` (K163). The field `with` of `handle` is a field of data, thus each clause is a
5961
+ * record between braces in a list between brackets (D177).
5770
5962
  *
5771
5963
  * A host with more kinds spreads this profile and adds its forms to `calls.forms`. `STD_SYNTAX` stays the profile of
5772
5964
  * the formula kit.
@@ -5906,6 +6098,18 @@ const WORLD_SYNTAX = /* @__PURE__ */ Object.freeze({
5906
6098
  kind: "tree.quote",
5907
6099
  args: "literal"
5908
6100
  },
6101
+ {
6102
+ name: "handle",
6103
+ kind: "handle",
6104
+ args: "named",
6105
+ fields: [
6106
+ "with",
6107
+ "return",
6108
+ "body"
6109
+ ],
6110
+ data: ["with"],
6111
+ wrap: ["return", "body"]
6112
+ },
5909
6113
  {
5910
6114
  name: "add",
5911
6115
  kind: "add"
@@ -6362,6 +6566,7 @@ function leaderOnHost(n, key, rule, host) {
6362
6566
  /**
6363
6567
  * This function gives an attribute of a node with circular attributes, or an attribute in an open fixed point (D113).
6364
6568
  * The memo and the marks of the node do not hold the attribute. An open fixed point does not use the cells of the host.
6569
+ * With the cells of a host, only a circular attribute comes here outside a fixed point. A plain attribute takes its cell.
6365
6570
  */
6366
6571
  function circularRead(n, key) {
6367
6572
  const rule = circularRule(n.tree, key);
@@ -6776,7 +6981,7 @@ function treeThing(input, place, build, outer, host) {
6776
6981
  const text = attributeText(key, type, place);
6777
6982
  if (marks.has(key)) return busyAt(state, key);
6778
6983
  if (tree === void 0 && up?.tree === void 0 && !parts.some((item) => item.key === key)) return void 0;
6779
- if (circular || openPoint !== void 0) return circularRead(state, key);
6984
+ if (openPoint !== void 0 || circular && circularRule(tree, key) !== void 0) return circularRead(state, key);
6780
6985
  marks.set(key, BUSY);
6781
6986
  demands.push({
6782
6987
  cells: marks,
@@ -7103,6 +7308,30 @@ function* inOrder(parts) {
7103
7308
  }
7104
7309
  return { values };
7105
7310
  }
7311
+ /** This function tells if a value names a kind of effects: a text, or a list of keys. */
7312
+ function isOp(value) {
7313
+ return typeof value === "string" || Array.isArray(value) && value.every(isKey);
7314
+ }
7315
+ /**
7316
+ * This function gives the runner of an `if` with its own truth (RUN.IF-TRUTH). A branch can wait for the host, thus a
7317
+ * language with the function `truthy` of a profile does not need a copy of `if`. A missing test and a test without a
7318
+ * truth give `#VALUE!`, a failed test gives its failure, and a missing branch gives `null`.
7319
+ *
7320
+ * @param truth - The function that gives the truth of a value, or `undefined` for a value without a truth.
7321
+ * @returns The runner.
7322
+ */
7323
+ function ifRunner(truth) {
7324
+ return function* ({ parts }) {
7325
+ const test = parts.find((part) => part.key === "test");
7326
+ if (test === void 0) return fail("#VALUE!", "if needs a test.");
7327
+ const chosen = yield* test.evaluate();
7328
+ if (!chosen.ok) return chosen;
7329
+ const held = truth(chosen.value);
7330
+ if (held === void 0) return fail("#VALUE!", "The test of if must give a truth value.");
7331
+ const branch = parts.find((part) => part.key === (held ? "then" : "else"));
7332
+ return branch === void 0 ? ok(null) : yield* branch.evaluate();
7333
+ };
7334
+ }
7106
7335
  /**
7107
7336
  * The runners of the standard world, by name: the kinds that can hold effects. Each one evaluates its parts in
7108
7337
  * order. In a run, an effect ends before the next step starts.
@@ -7134,15 +7363,44 @@ const STD_RUNNERS = {
7134
7363
  if (only === void 0 || parts.length > 1) return fail("#VALUE!", "pure needs exactly one part.");
7135
7364
  return yield* only.evaluate();
7136
7365
  },
7137
- "control.if": function* ({ parts }) {
7138
- const test = parts.find((part) => part.key === "test");
7139
- if (test === void 0) return fail("#VALUE!", "if needs a test.");
7140
- const chosen = yield* test.evaluate();
7141
- if (!chosen.ok) return chosen;
7142
- if (typeof chosen.value !== "boolean") return fail("#VALUE!", "The test of if must give a truth value.");
7143
- const branch = parts.find((part) => part.key === (chosen.value === true ? "then" : "else"));
7144
- return branch === void 0 ? ok(null) : yield* branch.evaluate();
7366
+ "control.handle": function* ({ value, context }) {
7367
+ const { with: list, return: after, body } = value;
7368
+ if (body?.handled === void 0) return fail("#VALUE!", "handle needs a body.");
7369
+ let stopped;
7370
+ const clauses = [];
7371
+ for (const item of list) {
7372
+ const part = item.do ?? item.abort;
7373
+ if (!isOp(item.op)) return fail("#VALUE!", "The op of a clause of handle must be a text or a list of keys.");
7374
+ if (part === void 0 || item.do !== void 0 && item.abort !== void 0) return fail("#VALUE!", "Each clause of handle needs exactly one of do and abort.");
7375
+ const stops = item.do === void 0;
7376
+ clauses.push({
7377
+ op: item.op,
7378
+ answer: function* (payload) {
7379
+ const result = yield* part.evaluate({
7380
+ ...context,
7381
+ names: {
7382
+ ...context.names,
7383
+ payload
7384
+ }
7385
+ });
7386
+ if (!stops) return result;
7387
+ stopped ??= result;
7388
+ return fail("#CALC!", "A clause abort of handle stopped the body.");
7389
+ }
7390
+ });
7391
+ }
7392
+ const result = yield* body.handled(clauses);
7393
+ if (stopped !== void 0) return stopped;
7394
+ if (!result.ok || after === void 0) return result;
7395
+ return yield* after.evaluate({
7396
+ ...context,
7397
+ names: {
7398
+ ...context.names,
7399
+ result: result.value
7400
+ }
7401
+ });
7145
7402
  },
7403
+ "control.if": /* @__PURE__ */ ifRunner((value) => typeof value === "boolean" ? value : void 0),
7146
7404
  "list.all": function* ({ parts }) {
7147
7405
  const all = yield* inOrder(parts);
7148
7406
  return "error" in all ? all.error : ok(all.values);
@@ -7565,6 +7823,26 @@ const CONSTRUCTS = [
7565
7823
  * narrows the capabilities for each part, thus the host does not give this type.
7566
7824
  */
7567
7825
  const CAPS_TYPE = { list: { union: ["text", { list: { union: ["text", "number"] } }] } };
7826
+ /**
7827
+ * This function gives the boundaries of a check. A part with an imprecise type at a position that needs a type is a
7828
+ * boundary. A read of a known type is a boundary too, because the check trusts the root types (PLAN.GUARD-BOUNDARY).
7829
+ *
7830
+ * @param typing - The places of a check of types.
7831
+ * @param domains - The domains of the machine.
7832
+ * @returns The boundaries, in the order of the document.
7833
+ */
7834
+ function boundariesOf(typing, domains) {
7835
+ const found = [];
7836
+ for (const item of typing.places) if (item.expected !== void 0 && item.expected !== "unknown" && !isPrecise(item.type, domains)) found.push({
7837
+ place: item.place,
7838
+ type: item.expected
7839
+ });
7840
+ else if (item.read === true && item.type !== "unknown") found.push({
7841
+ place: item.place,
7842
+ type: item.type
7843
+ });
7844
+ return found;
7845
+ }
7568
7846
  /** The roots that can change, as a rule gives them in `PartType.roots`. */
7569
7847
  const CHANGING = [
7570
7848
  "self",
@@ -7584,7 +7862,7 @@ function pathKey(path) {
7584
7862
  function ruleOf(machine, name) {
7585
7863
  if (name === void 0 || machine.runners !== void 0 && Object.hasOwn(machine.runners, name)) return void 0;
7586
7864
  const evaluator = Object.hasOwn(machine.evaluators, name) ? machine.evaluators[name] : void 0;
7587
- const rule = typeof evaluator === "object" && evaluator !== null ? evaluator.type : void 0;
7865
+ const rule = typeof evaluator === "object" && evaluator !== null || typeof evaluator === "function" ? evaluator.type : void 0;
7588
7866
  return typeof rule === "object" && rule !== null && typeof rule.synth === "function" ? rule : void 0;
7589
7867
  }
7590
7868
  /** This function gives the order of the parts of a document: first the parts that give the names of a rule `keysAt`. */
@@ -7765,9 +8043,11 @@ function typePass(machine, nodes, verb, expect, rootTypes = {}) {
7765
8043
  }
7766
8044
  const places = nodes.map((node, index) => {
7767
8045
  const want = needed[index];
8046
+ const read = !node.marked && ruleOf(machine, node.evaluator)?.read === true;
7768
8047
  const entry = {
7769
8048
  type: types[index] ?? "unknown",
7770
- ...want === void 0 ? {} : { expected: want }
8049
+ ...want === void 0 ? {} : { expected: want },
8050
+ ...read ? { read } : {}
7771
8051
  };
7772
8052
  Object.defineProperty(entry, "place", {
7773
8053
  enumerable: true,
@@ -9180,11 +9460,12 @@ var Visit = class {
9180
9460
  typePath;
9181
9461
  address;
9182
9462
  caps;
9463
+ handled;
9183
9464
  #place;
9184
9465
  #at;
9185
9466
  #bound;
9186
9467
  #expect;
9187
- constructor(place, at, doc, declared, depth, names, later, slot, parent, kind, typePath, address, caps, bound, expect) {
9468
+ constructor(place, at, doc, declared, depth, names, later, slot, parent, kind, typePath, address, caps, bound, expect, handled) {
9188
9469
  this.doc = doc;
9189
9470
  this.declared = declared;
9190
9471
  this.depth = depth;
@@ -9196,6 +9477,7 @@ var Visit = class {
9196
9477
  this.typePath = typePath;
9197
9478
  this.address = address;
9198
9479
  this.caps = caps;
9480
+ this.handled = handled;
9199
9481
  this.#place = place;
9200
9482
  this.#at = at;
9201
9483
  this.#bound = bound;
@@ -9241,6 +9523,7 @@ function walk(world, doc, visit, options = {}) {
9241
9523
  slot: [],
9242
9524
  parent: void 0,
9243
9525
  caps: options.caps,
9526
+ handled: void 0,
9244
9527
  bound: [],
9245
9528
  expect: void 0
9246
9529
  }];
@@ -9257,7 +9540,7 @@ function walk(world, doc, visit, options = {}) {
9257
9540
  continue;
9258
9541
  }
9259
9542
  const { doc, kind, split } = read;
9260
- const here = new Visit(item.place, item.at, doc, read.declared, item.depth, item.names, item.later, item.slot, item.parent, kind, read.typePath, item.address ? true : void 0, item.caps, item.bound, item.expect);
9543
+ const here = new Visit(item.place, item.at, doc, read.declared, item.depth, item.names, item.later, item.slot, item.parent, kind, read.typePath, item.address ? true : void 0, item.caps, item.bound, item.expect, item.handled);
9261
9544
  if (visit(here) === "skip" || kind === void 0) continue;
9262
9545
  const base = read.declared ? item.at : {
9263
9546
  up: item.at,
@@ -9276,10 +9559,13 @@ function walk(world, doc, visit, options = {}) {
9276
9559
  continue;
9277
9560
  }
9278
9561
  const { sees, later } = scopesOf(kind.scope, doc.value, split.parts);
9562
+ const grantRules = kind.caps !== void 0 && kind.caps.some((rule) => rule.rule === "grant") ? kind.caps : void 0;
9279
9563
  for (let index = split.parts.length - 1; index >= 0; index -= 1) {
9280
9564
  const slot = split.parts[index];
9281
9565
  const extra = sees[index] ?? [];
9282
9566
  const hidden = later[index] ?? [];
9567
+ const grants = grantRules === void 0 ? void 0 : partGrants(grantRules, slot.path, doc.value);
9568
+ const granted = grants !== void 0 && grants.length > 0 ? grants : void 0;
9283
9569
  stack.push({
9284
9570
  value: slot.item,
9285
9571
  place: slot.path.length === 0 ? item.place : {
@@ -9297,7 +9583,8 @@ function walk(world, doc, visit, options = {}) {
9297
9583
  address: slot.address === true,
9298
9584
  slot: slot.path,
9299
9585
  parent: here,
9300
- caps: item.caps === void 0 || kind.caps === void 0 ? item.caps : partCaps(kind.caps, slot.path, item.caps),
9586
+ caps: item.caps === void 0 || kind.caps === void 0 ? item.caps : granted === void 0 ? partCaps(kind.caps, slot.path, item.caps) : [...partCaps(kind.caps, slot.path, item.caps), ...granted],
9587
+ handled: granted === void 0 ? item.handled : [...item.handled ?? [], ...granted],
9301
9588
  bound: extra,
9302
9589
  expect: slot.expect
9303
9590
  });
@@ -9387,6 +9674,28 @@ function mapRefs(world, doc, change, options = {}) {
9387
9674
  return out;
9388
9675
  }
9389
9676
  /**
9677
+ * This function changes the documents of a document, from the root to the parts (STATIC.MAPDOCS). The function
9678
+ * `change` reads only the original documents. It decides at each node, and it can stop at a node. The kinds of a
9679
+ * position come from the world, thus this function names no kind. The document is not changed: the function gives a
9680
+ * new document that shares each part without a change.
9681
+ *
9682
+ * @param world - The world of the kinds.
9683
+ * @param doc - The document, as JSON.
9684
+ * @param change - The function that gives the answer for each visit. See `DocChange`.
9685
+ * @param options - The type of a bare root value, and the names that the host binds at the root.
9686
+ * @returns The new document, or `doc` itself when no document changes.
9687
+ */
9688
+ function mapDocs(world, doc, change, options = {}) {
9689
+ let out = doc;
9690
+ walk(world, doc, (visit) => {
9691
+ const next = change(visit);
9692
+ if (next === void 0 || next === "skip") return next;
9693
+ out = setAt(out, visit.at, next.to);
9694
+ return next.into === true ? void 0 : "skip";
9695
+ }, options);
9696
+ return out;
9697
+ }
9698
+ /**
9390
9699
  * This function gives the parts that the code of a runner evaluates itself, without a wait (RUN.DRIVES, D119). The
9391
9700
  * answer of the hook `facts` for the code wins over the contract. The contract describes only the code of its kind for
9392
9701
  * `eval`. Without a declaration, the runner passes each wait of its parts through.
@@ -9708,6 +10017,7 @@ const KEEPS_SELF = /* @__PURE__ */ new Set([
9708
10017
  "control.seq",
9709
10018
  "control.pure",
9710
10019
  "control.if",
10020
+ "control.handle",
9711
10021
  "list.all",
9712
10022
  "effect.log",
9713
10023
  "effect.write"
@@ -10272,7 +10582,7 @@ function reaching(machine, doc, options) {
10272
10582
  const rule = options.caps === void 0 ? permits : permitsOf(machine);
10273
10583
  walk(machine.world, doc, (v) => {
10274
10584
  const caps = v.caps ?? EACH;
10275
- for (const kind of declaredEffects(v, "eval", machine)) if (rule(caps, kind)) out.push({
10585
+ for (const kind of declaredEffects(v, "eval", machine)) if (rule(caps, kind) && (v.handled === void 0 || !permits(v.handled, kind))) out.push({
10276
10586
  kind,
10277
10587
  visit: v
10278
10588
  });
@@ -10952,39 +11262,42 @@ function formulaTypes(profile) {
10952
11262
  });
10953
11263
  return {
10954
11264
  "formula.lit": { synth: (input) => valueType(input.doc.value) },
10955
- "formula.ref": { synth: (input, report) => {
10956
- const path = input.doc.value;
10957
- if (input.address || !Array.isArray(path) || path.length === 0) return "unknown";
10958
- const domains = input.machine.domains;
10959
- const plain = input.machine.plain === true;
10960
- let type;
10961
- let start;
10962
- if (path[0] === "names") {
10963
- const name = path[1];
10964
- if (!isKey(name) || !plain && HEADER_KEYS.has(String(name))) return "unknown";
10965
- type = input.nameType(String(name)) ?? "unknown";
10966
- start = 2;
10967
- } else {
10968
- type = input.rootType(String(path[0]));
10969
- start = 1;
10970
- }
10971
- for (let index = start;; index += 1) {
10972
- if (isUnknown(type, domains) || !plain && holdsEnvelope(type, domains)) return "unknown";
10973
- if (index >= path.length) return type;
10974
- const step = path[index];
10975
- if (!isKey(step)) {
10976
- type = walkType(input, step, type);
10977
- continue;
11265
+ "formula.ref": {
11266
+ read: true,
11267
+ synth: (input, report) => {
11268
+ const path = input.doc.value;
11269
+ if (input.address || !Array.isArray(path) || path.length === 0) return "unknown";
11270
+ const domains = input.machine.domains;
11271
+ const plain = input.machine.plain === true;
11272
+ let type;
11273
+ let start;
11274
+ if (path[0] === "names") {
11275
+ const name = path[1];
11276
+ if (!isKey(name) || !plain && HEADER_KEYS.has(String(name))) return "unknown";
11277
+ type = input.nameType(String(name)) ?? "unknown";
11278
+ start = 2;
11279
+ } else {
11280
+ type = input.rootType(String(path[0]));
11281
+ start = 1;
10978
11282
  }
10979
- const next = stepType(type, step, domains, input.contract);
10980
- if ("missing" in next) {
10981
- const fields = next.missing.length === 0 ? "" : ` Its fields are ${listText(next.missing)}.`;
10982
- report({ reason: `The value at ${path.slice(0, index).map(stepText).join(".")} has no field ${String(step)}.${fields}` });
10983
- return "unknown";
11283
+ for (let index = start;; index += 1) {
11284
+ if (isUnknown(type, domains) || !plain && holdsEnvelope(type, domains)) return "unknown";
11285
+ if (index >= path.length) return type;
11286
+ const step = path[index];
11287
+ if (!isKey(step)) {
11288
+ type = walkType(input, step, type);
11289
+ continue;
11290
+ }
11291
+ const next = stepType(type, step, domains, input.contract);
11292
+ if ("missing" in next) {
11293
+ const fields = next.missing.length === 0 ? "" : ` Its fields are ${listText(next.missing)}.`;
11294
+ report({ reason: `The value at ${path.slice(0, index).map(stepText).join(".")} has no field ${String(step)}.${fields}` });
11295
+ return "unknown";
11296
+ }
11297
+ type = next.type;
10984
11298
  }
10985
- type = next.type;
10986
11299
  }
10987
- } },
11300
+ },
10988
11301
  "formula.record": {
10989
11302
  part: (input, path) => {
10990
11303
  const want = fieldOf(input.expected, String(path[0]), input.machine.domains);
@@ -11937,6 +12250,28 @@ function freeIn(root, ctx, path) {
11937
12250
  const bound = Object.hasOwn(ctx.names, key);
11938
12251
  return bound === Object.hasOwn(root.names, key) && (!bound || Object.is(ctx.names[key], root.names[key]));
11939
12252
  }
12253
+ /** The key of the frame of a context. */
12254
+ const REF_FRAME = /* @__PURE__ */ Symbol("coeval.reference.frame");
12255
+ /** This function gives the frame of a context. */
12256
+ function refFrame(ctx) {
12257
+ return ctx[REF_FRAME];
12258
+ }
12259
+ /**
12260
+ * This function finds the clause for an effect (HANDLE.DO, HANDLE.FORWARD). An effect without a capability gets no clause
12261
+ * (HANDLE.NARROW). A clause of a frame that has ended gives `#CAP!` (HANDLE.ENDED).
12262
+ */
12263
+ function clauseAt(env, ctx, kind, place) {
12264
+ let frame = refFrame(ctx);
12265
+ if (frame === void 0) return void 0;
12266
+ const keys = typeof kind === "string" ? kind.split(".") : kind;
12267
+ const machine = env.machine;
12268
+ if (!(machine.permits === void 0 ? permits(ctx.caps, keys) : machine.permits(ctx.caps, keys, ctx))) return void 0;
12269
+ for (; frame !== void 0; frame = frame.outer) for (const clause of frame.clauses) {
12270
+ if (!permits([clause.op], keys)) continue;
12271
+ if (frame.ended || frame.owner !== env.effects) return fail("#CAP!", `The handler of the effect ${keys.join(".")} has ended.`, { place });
12272
+ return clause;
12273
+ }
12274
+ }
11940
12275
  /**
11941
12276
  * This function causes an effect when the capabilities permit it (RUN.CAP, CAP.HOOK). It records the effect with its
11942
12277
  * address (RUN.ADDRESS) and its outcome (RUN.OUTCOME). The mock answers the effect at once (RUN.MOCK). Without a
@@ -11955,6 +12290,8 @@ function cause(env, ctx, kind, payload, place) {
11955
12290
  function causeFromEvaluator(env, ctx, kind, payload, place) {
11956
12291
  const text = typeof kind === "string" ? kind : kind.join(".");
11957
12292
  if (env.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
12293
+ const clause = clauseAt(env, ctx, kind, place);
12294
+ if (clause !== void 0) return "ok" in clause ? clause : drive$1(clause.answer(payload), place, text);
11958
12295
  return cause(env, ctx, kind, payload, place);
11959
12296
  }
11960
12297
  /**
@@ -11963,6 +12300,8 @@ function causeFromEvaluator(env, ctx, kind, payload, place) {
11963
12300
  * (RUN.OUTCOME).
11964
12301
  */
11965
12302
  function* causeAndWait(env, ctx, kind, payload, place) {
12303
+ const clause = clauseAt(env, ctx, kind, place);
12304
+ if (clause !== void 0) return "ok" in clause ? clause : yield* clause.answer(payload);
11966
12305
  if (!env.running) return cause(env, ctx, kind, payload, place);
11967
12306
  const effect = recordEffect(env, ctx, kind, payload, place);
11968
12307
  if ("ok" in effect) return effect;
@@ -12109,6 +12448,28 @@ function evalInput(env, seen, at) {
12109
12448
  fill
12110
12449
  };
12111
12450
  }
12451
+ /** This function evaluates a part under a frame: the part also gets the kinds of the clauses (CAP.GRANT). */
12452
+ function* handledAt(env, part, clauses) {
12453
+ const frame = {
12454
+ clauses,
12455
+ outer: refFrame(part.ctx),
12456
+ owner: env.effects,
12457
+ ended: false
12458
+ };
12459
+ const ctx = {
12460
+ ...part.ctx,
12461
+ caps: [...part.ctx.caps, ...clauses.map((clause) => clause.op)],
12462
+ [REF_FRAME]: frame
12463
+ };
12464
+ try {
12465
+ return yield* stepsAt(env, {
12466
+ ...part,
12467
+ ctx
12468
+ });
12469
+ } finally {
12470
+ frame.ended = true;
12471
+ }
12472
+ }
12112
12473
  function runInput(env, seen, at) {
12113
12474
  const { split, doc } = seen;
12114
12475
  const parts = split.parts.map((slot) => {
@@ -12117,7 +12478,8 @@ function runInput(env, seen, at) {
12117
12478
  key: slot.key,
12118
12479
  path: slot.path,
12119
12480
  ...name === void 0 ? {} : { name },
12120
- evaluate: (given) => stepsAt(env, partAt(seen, at, slot, given))
12481
+ evaluate: (given) => stepsAt(env, partAt(seen, at, slot, given)),
12482
+ handled: (clauses) => handledAt(env, partAt(seen, at, slot, void 0), clauses)
12121
12483
  };
12122
12484
  });
12123
12485
  const fill = (items) => split.parts.length === 0 ? doc.value : split.fill(items);
@@ -14444,6 +14806,14 @@ function readsUnknown(reads) {
14444
14806
  function resolutions(reads) {
14445
14807
  return reads.filter((read) => read.free && read.kept !== true);
14446
14808
  }
14809
+ /** This function gives the text of a value of a trace. It does not throw for a bigint or a record with a cycle. */
14810
+ function valueText$1(value) {
14811
+ try {
14812
+ return JSON.stringify(value) ?? describe(value);
14813
+ } catch {
14814
+ return describe(value);
14815
+ }
14816
+ }
14447
14817
  function pathText(path) {
14448
14818
  if (path === void 0) return "no read";
14449
14819
  try {
@@ -14599,6 +14969,96 @@ async function agreeRun(machine, doc, context = makeContext(), options = {}) {
14599
14969
  reads: compared
14600
14970
  };
14601
14971
  }
14972
+ /** The type errors of the kernel, by the evaluator that gives them (STATIC.TYPES-AGREE). */
14973
+ const TYPE_ERRORS = {
14974
+ "formula.op": "#NAME?",
14975
+ "formula.if": "#VALUE!",
14976
+ "formula.and": "#VALUE!",
14977
+ "formula.or": "#VALUE!"
14978
+ };
14979
+ /**
14980
+ * This function examines the agreement rule for types (STATIC.TYPES-AGREE, D148). A document with no issue evaluates
14981
+ * with a trace. Each value of the trace is a member of the type of its place. A value of a precise type is also a member
14982
+ * of the type that its position needs. No document with precise parts gives a type error of the kernel.
14983
+ *
14984
+ * A language imports this function for its own property tests, with `agree`. A failure tells that a signature, a type
14985
+ * rule, a cover or a root type does not hold. A value that breaks a position of an imprecise type ends the promise, and
14986
+ * the result then has `checked: false` (TYPE.GRADUAL).
14987
+ *
14988
+ * @param machine - The world and the code of the evaluators, with their type rules.
14989
+ * @param doc - The document.
14990
+ * @param context - The context. Its roots are members of the root types.
14991
+ * @param options - The options of `check`, with the root types, and `guards`.
14992
+ * @returns The agreement, or the first place that breaks it.
14993
+ */
14994
+ function agreeTypes(machine, doc, context = makeContext(), options = {}) {
14995
+ const typing = typeCheck(machine, doc, options);
14996
+ if (typing.issues.length > 0) return {
14997
+ ok: true,
14998
+ checked: false,
14999
+ values: 0
15000
+ };
15001
+ const keyOf = (path) => JSON.stringify(path.map(String));
15002
+ const places = new Map(typing.places.map((item) => [keyOf(item.place), item]));
15003
+ const domains = machine.domains;
15004
+ const guards = options.guards === true ? boundariesOf(typing, domains) : void 0;
15005
+ const stack = [evaluate(machine, doc, context, {
15006
+ ...options.declaredType === void 0 ? {} : { declaredType: options.declaredType },
15007
+ ...guards === void 0 ? {} : { guards }
15008
+ }).trace];
15009
+ const nodes = [];
15010
+ while (stack.length > 0) {
15011
+ const node = stack.pop();
15012
+ nodes.push(node);
15013
+ stack.push(...node.children);
15014
+ }
15015
+ for (const node of nodes) {
15016
+ const place = places.get(keyOf(node.path));
15017
+ if (place?.expected === void 0 || !node.result.ok || isPrecise(place.type, domains)) continue;
15018
+ if (!member(node.result.value, place.expected, domains)) return {
15019
+ ok: true,
15020
+ checked: false,
15021
+ values: 0
15022
+ };
15023
+ }
15024
+ let values = 0;
15025
+ for (const node of nodes.reverse()) {
15026
+ const place = places.get(keyOf(node.path));
15027
+ if (place === void 0) continue;
15028
+ const at = node.path.map(String);
15029
+ if (!node.result.ok) {
15030
+ const parts = node.children.map((child) => places.get(keyOf(child.path)));
15031
+ const decided = node.children.every((child) => child.result.ok) && parts.every((part) => part?.expected !== void 0 && isPrecise(part.type, domains));
15032
+ const code = TYPE_ERRORS[node.answer.evaluator ?? ""];
15033
+ if (decided && node.result.error.code === code) return {
15034
+ ok: false,
15035
+ clause: "error",
15036
+ place: at,
15037
+ reason: `The document gives ${code}, but it checks with no issue.`
15038
+ };
15039
+ continue;
15040
+ }
15041
+ const value = node.result.value;
15042
+ values += 1;
15043
+ if (!member(value, place.type, domains)) return {
15044
+ ok: false,
15045
+ clause: "value",
15046
+ place: at,
15047
+ reason: `The value ${valueText$1(value)} is not a member of ${typeText(place.type)}.`
15048
+ };
15049
+ if (place.expected !== void 0 && !member(value, place.expected, domains)) return {
15050
+ ok: false,
15051
+ clause: "argument",
15052
+ place: at,
15053
+ reason: `The value ${valueText$1(value)} is not a member of the needed type ${typeText(place.expected)}.`
15054
+ };
15055
+ }
15056
+ return {
15057
+ ok: true,
15058
+ checked: true,
15059
+ values
15060
+ };
15061
+ }
14602
15062
  // src/codec-laws.ts
14603
15063
  /** The order of the laws in the list of findings. */
14604
15064
  const ORDER$1 = [
@@ -15022,6 +15482,13 @@ function compileConstructs(given, ops) {
15022
15482
  constructKinds
15023
15483
  };
15024
15484
  }
15485
+ /** This function examines the field `domain` of a call form (D141). A form `literal` of `ext.domain` needs a name, and each other form refuses it. */
15486
+ function compileDomain(given, args) {
15487
+ const writes = args === "literal" && given.kind === "ext.domain";
15488
+ if (writes && (typeof given.domain !== "string" || given.domain === "")) throw new TypeError(`The call form "${given.name}" of ext.domain needs the name of its domain.`);
15489
+ if (!writes && given.domain !== void 0) throw new TypeError(`The call form "${given.name}" takes no domain: only a form literal of ext.domain has one.`);
15490
+ return given.domain;
15491
+ }
15025
15492
  /** This function examines the field `wrap` of a call form (K159). A name that is not a field of the form throws. */
15026
15493
  function compileWrap(given, args) {
15027
15494
  const wrap = given.wrap;
@@ -15033,6 +15500,16 @@ function compileWrap(given, args) {
15033
15500
  return new Set(wrap);
15034
15501
  }
15035
15502
  /** This function gives the types of the data envelopes of a profile (K159), or `undefined` without them. */
15503
+ /** This function examines the field `data` of a call form (D177). Each name is a field of the form, and no name is in `wrap`. */
15504
+ function compileData(given, wrap) {
15505
+ const data = given.data ?? [];
15506
+ if (data.length > 0 && given.fields === void 0) throw new TypeError(`The call form "${given.name}" takes data only with fields.`);
15507
+ for (const name of data) {
15508
+ if (given.fields?.includes(name) !== true) throw new TypeError(`The call form "${given.name}" has no field ${name} for its data.`);
15509
+ if (wrap === true || wrap !== false && wrap.has(name)) throw new TypeError(`The call form "${given.name}" has the field ${name} in wrap and in data.`);
15510
+ }
15511
+ return new Set(data);
15512
+ }
15036
15513
  function compileEnvelopes(given) {
15037
15514
  if (given === void 0) return void 0;
15038
15515
  const out = {};
@@ -15069,17 +15546,22 @@ function compileProfile(profile) {
15069
15546
  if (!FORM_ARGS.includes(args)) throw new TypeError(`The call form "${given.name}" has the unknown arguments ${String(args)}.`);
15070
15547
  if (args === "fields" !== (given.fields !== void 0) && args !== "named") throw new TypeError(`The call form "${given.name}" with the arguments ${args} ${args === "fields" ? "needs" : "takes no"} fields.`);
15071
15548
  if (given.min !== void 0 && (args === "named" || args === "literal")) throw new TypeError(`The call form "${given.name}" with the arguments ${args} takes no min.`);
15549
+ const wrap = compileWrap(given, args);
15072
15550
  const form = {
15073
15551
  name: given.name,
15074
15552
  type,
15075
15553
  args,
15076
15554
  fields: given.fields,
15077
15555
  min: given.min ?? given.fields?.length ?? 0,
15078
- wrap: compileWrap(given, args)
15556
+ wrap,
15557
+ domain: compileDomain(given, args),
15558
+ data: compileData(given, wrap)
15079
15559
  };
15080
15560
  forms.set(form.name, form);
15081
15561
  addTo(formsByKind, key, form);
15082
15562
  }
15563
+ const brackets = [...forms.values()].some((form) => form.data.size > 0);
15564
+ for (const symbol of brackets ? [...ops.prefixSymbols, ...ops.binarySymbols] : []) if (/^[[\]{}]/.test(symbol)) throw new TypeError(`The operator "${symbol}" reads the text of a field of data.`);
15083
15565
  const compiled = {
15084
15566
  ...ops,
15085
15567
  numbers: literals.numbers ?? true,
@@ -15111,7 +15593,8 @@ function compileProfile(profile) {
15111
15593
  ...ops.opWords,
15112
15594
  ...roots
15113
15595
  ]),
15114
- maxDepth: profile.maxDepth ?? 256
15596
+ maxDepth: profile.maxDepth ?? 256,
15597
+ brackets
15115
15598
  };
15116
15599
  compiledProfiles.set(profile, compiled);
15117
15600
  return compiled;
@@ -15570,7 +16053,7 @@ var Parser = class {
15570
16053
  }
15571
16054
  /** This method stops at a token where a group or a list must end. A list can also continue with its separator. */
15572
16055
  noEnd(close, list) {
15573
- const closer = close === ")" ? "parenthesis" : "brace";
16056
+ const closer = closerName(close);
15574
16057
  const before = list ? `"${this.syn.separator}" or a ` : "";
15575
16058
  return this.stop(`A ${before}closing ${closer} is missing.`, this.pos, this.tokenLength());
15576
16059
  }
@@ -15595,16 +16078,31 @@ var Parser = class {
15595
16078
  this.pos += 1;
15596
16079
  return args;
15597
16080
  }
15598
- if (this.pos >= this.text.length) this.stop(`A closing ${close === ")" ? "parenthesis" : "brace"} is missing.`, open, 1);
16081
+ if (this.pos >= this.text.length) this.stop(`A closing ${closerName(close)} is missing.`, open, 1);
15599
16082
  this.noEnd(close, true);
15600
16083
  }
15601
16084
  }
15602
16085
  /**
15603
- * This method reads the entries `key: expr` into a record: the arguments of a form `named` (SYNTAX.RECORD), or the
15604
- * entries between braces without a form (SYNTAX.RECORD-TEXT).
16086
+ * This method reads the argument of a field of data (D177): a list between brackets, a record between braces, or a
16087
+ * document. A literal in it stays its data.
16088
+ */
16089
+ data() {
16090
+ this.skip();
16091
+ const start = this.pos;
16092
+ const c = this.text[start];
16093
+ if (c !== "[" && c !== "{") return this.bound();
16094
+ this.depth += 1;
16095
+ if (this.depth > this.syn.maxDepth) this.stop(`The text is deeper than ${String(this.syn.maxDepth)} levels.`, start, 0);
16096
+ const value = c === "[" ? this.argumentsOf(() => this.data(), "]") : this.entries(void 0, true);
16097
+ this.depth -= 1;
16098
+ return value;
16099
+ }
16100
+ /**
16101
+ * This method reads the entries `key: expr` into a record. The record is the arguments of a form `named`
16102
+ * (SYNTAX.RECORD), or the entries between braces (SYNTAX.RECORD-TEXT). It is also a record of data (D177).
15605
16103
  */
15606
- entries(form) {
15607
- const owner = form === void 0 ? "record" : `form ${form.name}`;
16104
+ entries(form, data = false) {
16105
+ const owner = data ? "record of data" : form === void 0 ? "record" : `form ${form.name}`;
15608
16106
  const value = {};
15609
16107
  const entry = () => {
15610
16108
  this.skip();
@@ -15614,9 +16112,10 @@ var Parser = class {
15614
16112
  this.skip();
15615
16113
  if (key === void 0 || this.text[this.pos] !== ":") this.stop(`Each ${form === void 0 ? "entry" : "argument"} of the ${owner} is a key, a ":" and a document.`, at, Math.max(end - at, this.tokenLength()));
15616
16114
  if (form?.fields !== void 0 && !form.fields.includes(key)) this.stop(`The form ${form.name} has no field ${key}.`, at, end - at);
16115
+ if (data && key === "type") this.stop("A record of data has no key type, because the key type makes a document.", at, end - at);
15617
16116
  if (Object.hasOwn(value, key)) this.stop(`The ${owner} has the field ${key} two times.`, at, end - at);
15618
16117
  this.pos += 1;
15619
- const arg = this.bound();
16118
+ const arg = data || form?.data.has(key) === true ? this.data() : this.bound();
15620
16119
  setOwn$1(value, key, form !== void 0 && wraps(form, key) ? envelop(this.syn, arg) : arg);
15621
16120
  };
15622
16121
  this.argumentsOf(entry, form === void 0 ? "}" : ")");
@@ -15625,11 +16124,19 @@ var Parser = class {
15625
16124
  call(name, start, nameLength) {
15626
16125
  const form = this.syn.forms.get(name);
15627
16126
  if (form?.args === "named") return opDoc(form, this.entries(form));
15628
- const args = this.argumentsOf(() => this.bound());
16127
+ let index = 0;
16128
+ const args = this.argumentsOf(() => {
16129
+ const key = form?.fields?.[index];
16130
+ index += 1;
16131
+ return key !== void 0 && form?.data.has(key) === true ? this.data() : this.bound();
16132
+ });
15629
16133
  if (form?.args === "literal") {
15630
16134
  const [only] = args;
15631
16135
  if (args.length !== 1 || !isScalar(only)) this.stop(`The form ${name} takes one literal that JSON writes.`, start, this.pos - start);
15632
- return opDoc(form, only);
16136
+ return opDoc(form, form.domain === void 0 ? only : {
16137
+ domain: form.domain,
16138
+ data: only
16139
+ });
15633
16140
  }
15634
16141
  if (form !== void 0) {
15635
16142
  const most = form.fields?.length ?? Number.POSITIVE_INFINITY;
@@ -15655,15 +16162,20 @@ var Parser = class {
15655
16162
  /** The end of a recovery. The class `Recovery` throws it, and its method `run` catches it. */
15656
16163
  var End = class {};
15657
16164
  function hasBraces(syn) {
15658
- return syn.constructs.record !== void 0 || syn.constructs.let !== void 0;
16165
+ return syn.constructs.record !== void 0 || syn.constructs.let !== void 0 || syn.brackets;
16166
+ }
16167
+ /** This function gives the name of a closer in a message. */
16168
+ function closerName(close) {
16169
+ return close === ")" ? "parenthesis" : close === "]" ? "bracket" : "brace";
15659
16170
  }
15660
16171
  /** This function gives the closer of an opening bracket of a profile, or `undefined` for each other character. */
15661
16172
  function closerOf(syn, c) {
15662
16173
  if (c === "(") return ")";
16174
+ if (c === "[" && syn.brackets) return "]";
15663
16175
  return c === "{" && hasBraces(syn) ? "}" : void 0;
15664
16176
  }
15665
16177
  function isCloser(syn, c) {
15666
- return c === ")" || c === "}" && hasBraces(syn);
16178
+ return c === ")" || c === "]" && syn.brackets || c === "}" && hasBraces(syn);
15667
16179
  }
15668
16180
  /** This function gives the position after a quoted text at `at`, or after one other character. An open quote gives `Infinity`. */
15669
16181
  function pastToken(syn, text, at) {
@@ -15906,6 +16418,7 @@ function refShape(syn, value) {
15906
16418
  }
15907
16419
  function callName(syn, keys) {
15908
16420
  if (keys.length === 0 || !keys.every((key) => typeof key === "string" && NAME.test(key))) return void 0;
16421
+ if (keys.length > 1 && !syn.fields) return void 0;
15909
16422
  const name = keys.join(".");
15910
16423
  if (syn.forms.has(name) || syn.opWords.has(keys[0])) return void 0;
15911
16424
  return syn.ops === true || syn.ops.has(name) ? name : void 0;
@@ -15928,10 +16441,16 @@ function wrappedShape(syn, doc) {
15928
16441
  const literal = literalShape(syn, doc.value);
15929
16442
  return literal.tag === "text" ? literal : void 0;
15930
16443
  }
16444
+ /** This function tells if a value of `ext.domain` is a record with this domain and its data, and no other key. */
16445
+ function holdsDomain(value, domain) {
16446
+ return isRecord(value) && value["domain"] === domain && Object.hasOwn(value, "data") && Object.keys(value).length === 2;
16447
+ }
15931
16448
  function formShape(syn, form, value) {
15932
16449
  if (form.args === "literal") {
15933
- if (!isScalar(value)) return void 0;
15934
- const literal = literalShape(syn, value);
16450
+ if (form.domain !== void 0 && !holdsDomain(value, form.domain)) return void 0;
16451
+ const data = form.domain === void 0 ? value : value["data"];
16452
+ if (!isScalar(data)) return void 0;
16453
+ const literal = literalShape(syn, data);
15935
16454
  return literal.tag === "text" ? {
15936
16455
  tag: "text",
15937
16456
  text: `${form.name}(${literal.text})`
@@ -15947,7 +16466,8 @@ function formShape(syn, form, value) {
15947
16466
  args: keys.map((key) => [
15948
16467
  key,
15949
16468
  value[key],
15950
- wraps(form, key)
16469
+ wraps(form, key),
16470
+ form.data.has(key)
15951
16471
  ]),
15952
16472
  named: true
15953
16473
  };
@@ -15974,7 +16494,8 @@ function formShape(syn, form, value) {
15974
16494
  args: present.map((key) => [
15975
16495
  key,
15976
16496
  value[key],
15977
- wraps(form, key)
16497
+ wraps(form, key),
16498
+ form.data.has(key)
15978
16499
  ])
15979
16500
  };
15980
16501
  }
@@ -16110,6 +16631,18 @@ function bracesText(syn, record, path, sub) {
16110
16631
  if (!texts.ok) return texts;
16111
16632
  return ok(`{${texts.value.map((text, index) => `${keyText(keys[index])}: ${text}`).join(`${syn.separator} `)}}`);
16112
16633
  }
16634
+ /**
16635
+ * This function writes the argument of a field of data (D177): a list between brackets, or a record between braces.
16636
+ * The function `sub` writes a literal and a document. A record that is an envelope is a document.
16637
+ */
16638
+ function dataText(syn, value, path, sub) {
16639
+ const each = (child, at) => dataText(syn, child, at, sub);
16640
+ if (Array.isArray(value)) {
16641
+ const texts = partTexts(value.map((item, index) => [index, item]), path, each);
16642
+ return texts.ok ? ok(`[${texts.value.join(`${syn.separator} `)}]`) : texts;
16643
+ }
16644
+ return isRecord(value) && !isEnvelope(value) && !Object.hasOwn(value, "type") ? bracesText(syn, value, path, each) : sub(value, path);
16645
+ }
16113
16646
  /** This function writes a construct form (K145). */
16114
16647
  function constructText(syn, shape, sub) {
16115
16648
  if (shape.tag === "record") return bracesText(syn, shape.entries, [], sub);
@@ -16152,8 +16685,8 @@ function printNode(syn, doc, sub) {
16152
16685
  return ok(texts.join("."));
16153
16686
  }
16154
16687
  const texts = [];
16155
- for (const [key, arg, wrapped] of shape.args) {
16156
- const result = sub(arg, [key], wrapped);
16688
+ for (const [key, arg, wrapped, data] of shape.args) {
16689
+ const result = data === true ? dataText(syn, arg, [key], sub) : sub(arg, [key], wrapped);
16157
16690
  if (!result.ok) return result;
16158
16691
  texts.push(shape.named === true ? `${keyText(String(key))}: ${result.value}` : result.value);
16159
16692
  }
@@ -16214,6 +16747,287 @@ function syntaxFormat(profile) {
16214
16747
  evaluators: { "syntax.format": code }
16215
16748
  };
16216
16749
  }
16750
+ /** This function tells how a document breaks `parse(print(doc))`. It gives `undefined` when the document comes back. */
16751
+ function lawBreak(profile, doc) {
16752
+ const printed = print(profile, doc);
16753
+ if (!printed.ok) return {
16754
+ text: void 0,
16755
+ why: printed.error.reason
16756
+ };
16757
+ const text = printed.value;
16758
+ const back = parse(profile, text);
16759
+ if (back.ok) return docEquals(back.doc, doc) ? void 0 : {
16760
+ text,
16761
+ why: `The text ${text} reads as ${JSON.stringify(back.doc)}.`
16762
+ };
16763
+ return {
16764
+ text,
16765
+ why: `The text ${text} gives the issue: ${back.issues[0]?.message ?? ""}`
16766
+ };
16767
+ }
16768
+ /** This function gives the index of the first pattern that `parse` uses at the start of a text, or `-1`. */
16769
+ function patternAt(syn, text) {
16770
+ return syn.patterns.findIndex(({ sticky }) => {
16771
+ sticky.lastIndex = 0;
16772
+ const match = sticky.exec(text)?.[0];
16773
+ return match !== void 0 && match !== "" && !/[A-Za-z0-9_$(]/.test(text[match.length] ?? "");
16774
+ });
16775
+ }
16776
+ /** This function gives each small document of an operator with `term` in one term. A `flat` operator also has three terms. */
16777
+ function operatorDocs(op, term, leaf) {
16778
+ if (op.fixity !== "infix") return [opDoc(op, [term])];
16779
+ const pairs = [[term, leaf], [leaf, term]];
16780
+ return (op.assoc === "flat" ? [
16781
+ ...pairs,
16782
+ [
16783
+ term,
16784
+ leaf,
16785
+ leaf
16786
+ ],
16787
+ [
16788
+ leaf,
16789
+ term,
16790
+ leaf
16791
+ ]
16792
+ ] : pairs).map((terms) => opDoc(op, terms));
16793
+ }
16794
+ /** This function gives the longer symbol that reads two postfix operators without a space, or `undefined`. */
16795
+ function gluedSymbol(syn, outer, inner) {
16796
+ if (outer.word || inner.word || outer.fixity !== "postfix" || inner.fixity !== "postfix") return void 0;
16797
+ const glued = inner.symbol + outer.symbol;
16798
+ const read = syn.binarySymbols.find((symbol) => glued.startsWith(symbol));
16799
+ return read === inner.symbol ? void 0 : read;
16800
+ }
16801
+ function checkOperators({ profile, syn, leaf, keeps }) {
16802
+ const ops = (profile.operators ?? []).map((given) => (given.fixity === "prefix" ? syn.prefix : syn.binary).get(given.symbol));
16803
+ const sound = ops.filter((op, index) => operatorDocs(op, leaf, leaf).every((doc) => keeps("operator", ["operators", index], `The operator ${op.symbol} does not come back.`, doc)));
16804
+ for (const outer of sound) {
16805
+ const place = ["operators", ops.indexOf(outer)];
16806
+ for (const inner of sound) {
16807
+ const glued = gluedSymbol(syn, outer, inner);
16808
+ const code = glued === void 0 ? "grouping" : "longest-match";
16809
+ const rule = glued === void 0 ? `The operator ${outer.symbol} with ${inner.symbol} in a term loses the tree.` : `The symbol ${glued} reads the postfix operators ${inner.symbol} and ${outer.symbol}.`;
16810
+ for (const term of operatorDocs(inner, leaf, leaf)) for (const doc of operatorDocs(outer, term, leaf)) keeps(code, place, rule, doc);
16811
+ }
16812
+ }
16813
+ }
16814
+ function checkLiterals({ profile, syn, report }) {
16815
+ for (const [word, value] of syn.words) {
16816
+ const back = parse(profile, word);
16817
+ if (!back.ok || !docEquals(back.doc, envelop(syn, literalDoc(value)))) report("word", [
16818
+ "literals",
16819
+ "words",
16820
+ word
16821
+ ], word, `The word ${word} does not read as its value.`);
16822
+ }
16823
+ for (const value of [
16824
+ 0,
16825
+ -1,
16826
+ "a"
16827
+ ]) {
16828
+ const shape = literalShape(syn, value);
16829
+ if (shape.tag === "text" && patternAt(syn, shape.text) >= 0) report("pattern", [], shape.text, `The literal ${shape.text} reads as a reference.`);
16830
+ }
16831
+ }
16832
+ function checkRoots({ profile, syn, keeps }) {
16833
+ (profile.refs?.roots ?? DEFAULT_ROOTS).forEach((root, index) => {
16834
+ for (const path of syn.fields ? [[root], [root, "x"]] : [[root]]) keeps("root", [
16835
+ "refs",
16836
+ "roots",
16837
+ index
16838
+ ], `The root ${root} does not come back.`, {
16839
+ type: "ref",
16840
+ value: path
16841
+ }, path.join("."));
16842
+ });
16843
+ }
16844
+ /** This function gives a value that a call form writes: the fewest arguments, and at least one. */
16845
+ function formValue(given, leaf) {
16846
+ const args = given.args ?? (given.fields === void 0 ? "list" : "fields");
16847
+ const least = Math.max(given.min ?? given.fields?.length ?? 0, 1);
16848
+ if (args === "literal") return 1;
16849
+ if (args === "list") return Array.from({ length: least }, () => leaf);
16850
+ const keys = (given.fields ?? ["x"]).slice(0, args === "named" ? 1 : least);
16851
+ return Object.fromEntries(keys.map((key) => [key, leaf]));
16852
+ }
16853
+ function checkCalls({ profile, syn, leaf, keeps }) {
16854
+ const calls = profile.calls ?? {};
16855
+ (calls.forms ?? []).forEach((form, index) => {
16856
+ keeps("form", [
16857
+ "calls",
16858
+ "forms",
16859
+ index
16860
+ ], `The form ${form.name} does not come back.`, {
16861
+ type: typeOfKind(form.kind).type,
16862
+ value: formValue(form, leaf)
16863
+ });
16864
+ });
16865
+ if (syn.ops === true && syn.fields) keeps("call", ["calls", "ops"], "A call name with a dot does not come back.", {
16866
+ type: [
16867
+ "ops",
16868
+ "m",
16869
+ "p"
16870
+ ],
16871
+ value: [leaf]
16872
+ });
16873
+ if (typeof calls.ops === "boolean" || calls.ops === void 0) return;
16874
+ calls.ops.forEach((name, index) => {
16875
+ keeps("call", [
16876
+ "calls",
16877
+ "ops",
16878
+ index
16879
+ ], `The call ${name} does not come back.`, {
16880
+ type: ["ops", ...name.split(".")],
16881
+ value: [leaf]
16882
+ });
16883
+ });
16884
+ }
16885
+ /** This function writes two terms with the separator. It blames the separator only when a neutral separator keeps the document. */
16886
+ function checkSeparator({ profile, syn, leaf, report }) {
16887
+ if (syn.ops !== true && syn.ops.size === 0 && syn.forms.size === 0 && syn.constructKinds.size === 0) return;
16888
+ const open = {
16889
+ ...profile,
16890
+ calls: {
16891
+ ...profile.calls,
16892
+ ops: true
16893
+ }
16894
+ };
16895
+ const neutral = {
16896
+ ...profile,
16897
+ calls: {
16898
+ ...open.calls,
16899
+ separator: ""
16900
+ }
16901
+ };
16902
+ const postfix = [...syn.binary.values()].filter((op) => op.fixity === "postfix").map((op) => opDoc(op, [leaf]));
16903
+ const docs = [
16904
+ leaf,
16905
+ {
16906
+ type: "ref",
16907
+ value: [syn.bare, "b c"]
16908
+ },
16909
+ "a",
16910
+ 1,
16911
+ ...postfix
16912
+ ].map((term) => ({
16913
+ type: ["ops", "probe__"],
16914
+ value: [term, leaf]
16915
+ }));
16916
+ const entries = {
16917
+ x: leaf,
16918
+ y: leaf
16919
+ };
16920
+ const { record, let: bind } = syn.constructs;
16921
+ if (record !== void 0) docs.push({
16922
+ type: record,
16923
+ value: entries
16924
+ });
16925
+ if (bind !== void 0) docs.push({
16926
+ type: bind,
16927
+ value: {
16928
+ bind: entries,
16929
+ body: leaf
16930
+ }
16931
+ });
16932
+ for (const doc of docs) {
16933
+ const broken = lawBreak(neutral, doc) === void 0 ? lawBreak(open, doc) : void 0;
16934
+ if (broken !== void 0) report("separator", ["calls", "separator"], "", `The separator "${syn.separator}" does not end an argument. ${broken.why}`);
16935
+ }
16936
+ }
16937
+ function checkConstructs({ syn, leaf, keeps }) {
16938
+ const entries = { x: leaf };
16939
+ const values = {
16940
+ record: entries,
16941
+ let: {
16942
+ bind: entries,
16943
+ body: leaf
16944
+ },
16945
+ fn: {
16946
+ params: ["x"],
16947
+ body: leaf
16948
+ },
16949
+ call: {
16950
+ fn: leaf,
16951
+ args: [leaf]
16952
+ }
16953
+ };
16954
+ for (const name of CONSTRUCTS$1) {
16955
+ const type = syn.constructs[name];
16956
+ if (type !== void 0) keeps("construct", ["constructs", name], `The form ${name} does not come back.`, {
16957
+ type,
16958
+ value: values[name]
16959
+ });
16960
+ }
16961
+ }
16962
+ /**
16963
+ * This function finds the faults of a syntax profile before a host uses it (SYNTAX.PROFILE-CHECK, D140). A fault is a
16964
+ * document of an entry that breaks `parse(print(doc))`, or a text of an entry that reads another way. The function
16965
+ * writes and reads one small document for each entry, for each pair of operators and for each root. Thus an empty list
16966
+ * tells that these small documents come back. It does not prove the law for each document. `parse` and `print` do not
16967
+ * call this function, thus it adds no cost to them.
16968
+ *
16969
+ * @param profile - The syntax profile.
16970
+ * @returns At most one finding for each code and place, in the order of the operators, the literals, the references, the calls and the constructs.
16971
+ * @throws An error of a `read` or a `write` function of a pattern.
16972
+ */
16973
+ function checkSyntax(profile) {
16974
+ const open = {
16975
+ ...profile,
16976
+ maxDepth: 256
16977
+ };
16978
+ let syn;
16979
+ try {
16980
+ syn = compileProfile(open);
16981
+ } catch (error) {
16982
+ if (error instanceof TypeError) return [{
16983
+ code: "invalid",
16984
+ place: [],
16985
+ reason: error.message
16986
+ }];
16987
+ throw error;
16988
+ }
16989
+ const found = /* @__PURE__ */ new Map();
16990
+ const report = (code, place, text, reason) => {
16991
+ const index = patternAt(syn, text);
16992
+ const finding = index < 0 ? {
16993
+ code,
16994
+ place,
16995
+ reason
16996
+ } : {
16997
+ code: "pattern",
16998
+ place: [
16999
+ "refs",
17000
+ "patterns",
17001
+ index
17002
+ ],
17003
+ reason: `The pattern reads the start of the text ${text}. ${reason}`
17004
+ };
17005
+ const key = JSON.stringify([finding.code, finding.place]);
17006
+ if (!found.has(key)) found.set(key, finding);
17007
+ };
17008
+ const keeps = (code, place, rule, doc, start = "") => {
17009
+ const broken = lawBreak(open, doc);
17010
+ if (broken !== void 0) report(code, place, broken.text ?? start, `${rule} ${broken.why}`);
17011
+ return broken === void 0;
17012
+ };
17013
+ const check = {
17014
+ profile: open,
17015
+ syn,
17016
+ leaf: {
17017
+ type: "ref",
17018
+ value: [syn.bare, "a"]
17019
+ },
17020
+ report,
17021
+ keeps
17022
+ };
17023
+ checkOperators(check);
17024
+ checkLiterals(check);
17025
+ checkRoots(check);
17026
+ checkCalls(check);
17027
+ checkSeparator(check);
17028
+ checkConstructs(check);
17029
+ return [...found.values()];
17030
+ }
16217
17031
  // src/generate.ts
16218
17032
  /**
16219
17033
  * This function makes a source of random numbers with a seed (the algorithm mulberry32). It needs no package.
@@ -17324,7 +18138,8 @@ function instanceOf(env, under, doc, split, sample) {
17324
18138
  const ctx = shot.context;
17325
18139
  const parts = split.parts.map((slot, index) => ({
17326
18140
  ...handleOf(slot),
17327
- evaluate: (given) => shot.steps(index, "evaluate", given ?? ctx)
18141
+ evaluate: (given) => shot.steps(index, "evaluate", given ?? ctx),
18142
+ handled: () => shot.steps(index, "evaluate", ctx)
17328
18143
  }));
17329
18144
  const input = {
17330
18145
  ...base,
@@ -18746,6 +19561,7 @@ function schedule(machine, options = {}) {
18746
19561
  let keptAt = -1;
18747
19562
  const keptNodes = [];
18748
19563
  let keptMet = false;
19564
+ let keptFrom = 0;
18749
19565
  let count = 0;
18750
19566
  let ahead = 0;
18751
19567
  let wanted;
@@ -18816,7 +19632,10 @@ function schedule(machine, options = {}) {
18816
19632
  const reader = stack[stack.length - 1];
18817
19633
  const known = epoch.values.get(id);
18818
19634
  if (known !== void 0) {
18819
- if (reader !== void 0 && onOpen.has(id)) lower(reader, order.get(id));
19635
+ if (reader !== void 0 && onOpen.has(id)) {
19636
+ if (keptAt >= 0 && order.get(id) < keptFrom) keptMet = true;
19637
+ else lower(reader, order.get(id));
19638
+ }
18820
19639
  return known;
18821
19640
  }
18822
19641
  if (busy.has(id)) {
@@ -19020,9 +19839,11 @@ function schedule(machine, options = {}) {
19020
19839
  keeping(read) {
19021
19840
  const outer = keptAt;
19022
19841
  const outerMet = keptMet;
19842
+ const outerFrom = keptFrom;
19023
19843
  const from = keptNodes.length;
19024
19844
  keptAt = stack.length;
19025
19845
  keptMet = false;
19846
+ keptFrom = count;
19026
19847
  try {
19027
19848
  return read();
19028
19849
  } finally {
@@ -19033,6 +19854,7 @@ function schedule(machine, options = {}) {
19033
19854
  }
19034
19855
  keptAt = outer;
19035
19856
  keptMet = outerMet;
19857
+ keptFrom = outerFrom;
19036
19858
  }
19037
19859
  }
19038
19860
  });
@@ -20016,6 +20838,6 @@ function samplesAt(values, closed, depth) {
20016
20838
  function typeFromSamples(values, options = {}) {
20017
20839
  return samplesAt(values, options.closed === true, 0);
20018
20840
  }
20019
- export { ATTRIBUTE_RUNGS, CAPS_TYPE, CIRCULAR_LIMIT, CONSTRUCTS, ENVELOPE_KEYS, Failure, GENERATION_LIMIT, Held, JOURNAL, MAX_SYNTAX_ISSUES, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, WORLD_SYNTAX, agree, agreeRun, agreeSchedule, argsKey, ask, atArgsOf, atRuleOf, attributeGraph, attributeTrace, attributeVertexId, capKeys, check, checkCycles, checkLatent, checkStep, checkSteps, checkWorld, circularOf, classify, codecLaws, collectOf, compilePath, components, conforms, context, cycles, dataEdges, decodeDomain, decodeLog, decodeValue, derefStep, describe, docEquals, documentKey, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeLog, encodeValue, entriesAsJson, equationType, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isStep, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partsOf, pathMatches, permits, placeOf, placed, planner, prepare, print, printReads, random, recorder, ref, reference, referenceRun, refsOf, replayer, resolve, resolveCompiled, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stepLadder, stepOf, stepRank, stored, syntaxFormat, tag, thrown, toJson, treeNodeEvaluator, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
20841
+ export { ATTRIBUTE_RUNGS, CAPS_TYPE, CIRCULAR_LIMIT, CONSTRUCTS, ENVELOPE_KEYS, Failure, GENERATION_LIMIT, Held, JOURNAL, MAX_SYNTAX_ISSUES, MergeError, PARTS, PART_INFO, ROOTS, RUNGS, STD_CAPS, STD_DEFINITIONS, STD_DOMAINS, STD_EVALUATORS, STD_EXT, STD_MACHINE, STD_RUNNERS, STD_SYNTAX, STD_WALKERS, STD_WORLD, THING, WORLD_SYNTAX, agree, agreeRun, agreeSchedule, agreeTypes, argsKey, ask, atArgsOf, atRuleOf, attributeGraph, attributeTrace, attributeVertexId, boundariesOf, capKeys, check, checkCycles, checkLatent, checkStep, checkSteps, checkSyntax, checkWorld, circularOf, classify, codecLaws, collectOf, compilePath, components, conforms, context, cycles, dataEdges, decodeDomain, decodeLog, decodeValue, derefStep, describe, docEquals, documentKey, durable, edgesOf, effectEdges, effectsOf, effectsOfVerb, encodeLog, encodeValue, entriesAsJson, equationType, evaluate, extendDomain, factsOf, fail, failures, field, fits, flatten, formulaDefinitions, formulaEvaluators, formulaFacts, formulaKinds, formulaKnown, formulaMachine, fromJson, generate, heights, ifRunner, intersectCaps, isAlias, isAllowedOrder, isClosure, isEnvelope, isHostThing, isKey, isKind, isNamespace, isRecord, isRef, isStackOverflow, isStep, isTypeRef, join, jsTruthy, kindDocument, kindNamed, kindOf, layers, looksLikeKind, lookupKind, makeContext, mapDocs, mapRefs, member, mergeMachines, mergeWorlds, narrow, nearestWalker, necessary, nodeAt, nodeLayers, ok, parse, partCaps, partGrants, partsOf, pathMatches, permits, placeOf, placed, planner, prepare, print, printReads, random, recorder, ref, reference, referenceRun, refsOf, replayer, resolve, resolveCompiled, restrictMachine, restrictWorld, run, sameResult, sameValue, schedule, splitParts, stepLadder, stepOf, stepRank, stored, syntaxFormat, tag, thrown, toJson, treeNodeEvaluator, typeCheck, typeFromSamples, typeFromSchema, typeIssues, typeName, typePath, typeText, typed, typesOf, valueOfThing, verifyReads, walk, worldThing };
20020
20842
 
20021
20843
  //# sourceMappingURL=index.js.map