@mark1russell7/coeval 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -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,7 +4808,7 @@ 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
  }
@@ -5241,7 +5308,35 @@ function interpret(fn, input, fill) {
5241
5308
  });
5242
5309
  };
5243
5310
  }
5244
- function interpretRun(fn, input, fill) {
5311
+ /**
5312
+ * This function runs a part under a new frame (D146). The part gets the capabilities of the document and the kinds of
5313
+ * the clauses (CAP.GRANT), with the grant of the kernel. A rule around an effect narrows them as it narrows any other
5314
+ * capability (HANDLE.NARROW). The frame ends with the steps.
5315
+ */
5316
+ function* handling(given, clauses, rt, run) {
5317
+ const ctx = granted(given);
5318
+ const frame = {
5319
+ clauses,
5320
+ outer: frameOf(ctx),
5321
+ owner: rt,
5322
+ ended: false
5323
+ };
5324
+ const caps = [...ctx.caps];
5325
+ for (const clause of clauses) if (!permits(caps, clause.op)) caps.push(clause.op);
5326
+ const inner = {
5327
+ ...ctx,
5328
+ caps,
5329
+ [GRANT]: caps,
5330
+ [EPOCH]: epochOf(ctx),
5331
+ [FRAME]: frame
5332
+ };
5333
+ try {
5334
+ return yield* run(inner);
5335
+ } finally {
5336
+ frame.ended = true;
5337
+ }
5338
+ }
5339
+ function interpretRun(fn, input, fill, grants) {
5245
5340
  const { kind, typePath, doc, data, place, machine } = input;
5246
5341
  const refill = input.fill;
5247
5342
  const compiled = input.parts;
@@ -5251,7 +5346,8 @@ function interpretRun(fn, input, fill) {
5251
5346
  key: part.key,
5252
5347
  path: part.path,
5253
5348
  ...part.name === void 0 ? {} : { name: part.name },
5254
- evaluate: (override) => part.run(override === void 0 ? ctx : handed(ctx, override), rt)
5349
+ evaluate: (override) => part.run(override === void 0 ? ctx : handed(ctx, override), rt),
5350
+ ...grants ? { handled: (clauses) => handling(ctx, clauses, rt, (inner) => part.run(inner, rt)) } : {}
5255
5351
  }));
5256
5352
  return fn({
5257
5353
  kind,
@@ -5729,6 +5825,72 @@ const STD_WORLD = {
5729
5825
  lazy: [{ after: 1 }],
5730
5826
  summary: "It evaluates its part without capabilities, thus an effect in it fails."
5731
5827
  },
5828
+ handle: {
5829
+ verbs: {
5830
+ eval: "control.handle",
5831
+ view: "view.op"
5832
+ },
5833
+ parts: {
5834
+ shape: "record",
5835
+ fields: {
5836
+ with: {
5837
+ shape: "list",
5838
+ item: {
5839
+ shape: "record",
5840
+ fields: {
5841
+ op: { shape: "none" },
5842
+ do: { shape: "term" },
5843
+ abort: { shape: "term" }
5844
+ },
5845
+ required: ["op"],
5846
+ closed: true
5847
+ }
5848
+ },
5849
+ return: { shape: "term" },
5850
+ body: { shape: "term" }
5851
+ },
5852
+ required: ["with", "body"],
5853
+ closed: true
5854
+ },
5855
+ scope: [{
5856
+ names: { fixed: ["payload"] },
5857
+ in: [[
5858
+ "with",
5859
+ "*",
5860
+ "do"
5861
+ ], [
5862
+ "with",
5863
+ "*",
5864
+ "abort"
5865
+ ]]
5866
+ }, {
5867
+ names: { fixed: ["result"] },
5868
+ in: [["return"]]
5869
+ }],
5870
+ caps: [{
5871
+ rule: "grant",
5872
+ from: [
5873
+ "with",
5874
+ "*",
5875
+ "op"
5876
+ ],
5877
+ in: [["body"]]
5878
+ }],
5879
+ lazy: [{ in: [
5880
+ [
5881
+ "with",
5882
+ "*",
5883
+ "do"
5884
+ ],
5885
+ [
5886
+ "with",
5887
+ "*",
5888
+ "abort"
5889
+ ],
5890
+ ["return"]
5891
+ ] }],
5892
+ summary: "It answers the effects of its body with its clauses, in place, thus a mock, a retry or a log is a document."
5893
+ },
5732
5894
  log: {
5733
5895
  verbs: {
5734
5896
  eval: "effect.log",
@@ -7103,6 +7265,10 @@ function* inOrder(parts) {
7103
7265
  }
7104
7266
  return { values };
7105
7267
  }
7268
+ /** This function tells if a value names a kind of effects: a text, or a list of keys. */
7269
+ function isOp(value) {
7270
+ return typeof value === "string" || Array.isArray(value) && value.every(isKey);
7271
+ }
7106
7272
  /**
7107
7273
  * The runners of the standard world, by name: the kinds that can hold effects. Each one evaluates its parts in
7108
7274
  * order. In a run, an effect ends before the next step starts.
@@ -7134,6 +7300,43 @@ const STD_RUNNERS = {
7134
7300
  if (only === void 0 || parts.length > 1) return fail("#VALUE!", "pure needs exactly one part.");
7135
7301
  return yield* only.evaluate();
7136
7302
  },
7303
+ "control.handle": function* ({ value, context }) {
7304
+ const { with: list, return: after, body } = value;
7305
+ if (body?.handled === void 0) return fail("#VALUE!", "handle needs a body.");
7306
+ let stopped;
7307
+ const clauses = [];
7308
+ for (const item of list) {
7309
+ const part = item.do ?? item.abort;
7310
+ if (!isOp(item.op)) return fail("#VALUE!", "The op of a clause of handle must be a text or a list of keys.");
7311
+ 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.");
7312
+ const stops = item.do === void 0;
7313
+ clauses.push({
7314
+ op: item.op,
7315
+ answer: function* (payload) {
7316
+ const result = yield* part.evaluate({
7317
+ ...context,
7318
+ names: {
7319
+ ...context.names,
7320
+ payload
7321
+ }
7322
+ });
7323
+ if (!stops) return result;
7324
+ stopped ??= result;
7325
+ return fail("#CALC!", "A clause abort of handle stopped the body.");
7326
+ }
7327
+ });
7328
+ }
7329
+ const result = yield* body.handled(clauses);
7330
+ if (stopped !== void 0) return stopped;
7331
+ if (!result.ok || after === void 0) return result;
7332
+ return yield* after.evaluate({
7333
+ ...context,
7334
+ names: {
7335
+ ...context.names,
7336
+ result: result.value
7337
+ }
7338
+ });
7339
+ },
7137
7340
  "control.if": function* ({ parts }) {
7138
7341
  const test = parts.find((part) => part.key === "test");
7139
7342
  if (test === void 0) return fail("#VALUE!", "if needs a test.");
@@ -9180,11 +9383,12 @@ var Visit = class {
9180
9383
  typePath;
9181
9384
  address;
9182
9385
  caps;
9386
+ handled;
9183
9387
  #place;
9184
9388
  #at;
9185
9389
  #bound;
9186
9390
  #expect;
9187
- constructor(place, at, doc, declared, depth, names, later, slot, parent, kind, typePath, address, caps, bound, expect) {
9391
+ constructor(place, at, doc, declared, depth, names, later, slot, parent, kind, typePath, address, caps, bound, expect, handled) {
9188
9392
  this.doc = doc;
9189
9393
  this.declared = declared;
9190
9394
  this.depth = depth;
@@ -9196,6 +9400,7 @@ var Visit = class {
9196
9400
  this.typePath = typePath;
9197
9401
  this.address = address;
9198
9402
  this.caps = caps;
9403
+ this.handled = handled;
9199
9404
  this.#place = place;
9200
9405
  this.#at = at;
9201
9406
  this.#bound = bound;
@@ -9241,6 +9446,7 @@ function walk(world, doc, visit, options = {}) {
9241
9446
  slot: [],
9242
9447
  parent: void 0,
9243
9448
  caps: options.caps,
9449
+ handled: void 0,
9244
9450
  bound: [],
9245
9451
  expect: void 0
9246
9452
  }];
@@ -9257,7 +9463,7 @@ function walk(world, doc, visit, options = {}) {
9257
9463
  continue;
9258
9464
  }
9259
9465
  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);
9466
+ 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
9467
  if (visit(here) === "skip" || kind === void 0) continue;
9262
9468
  const base = read.declared ? item.at : {
9263
9469
  up: item.at,
@@ -9280,6 +9486,7 @@ function walk(world, doc, visit, options = {}) {
9280
9486
  const slot = split.parts[index];
9281
9487
  const extra = sees[index] ?? [];
9282
9488
  const hidden = later[index] ?? [];
9489
+ const grants = partGrants(kind.caps, slot.path, doc.value);
9283
9490
  stack.push({
9284
9491
  value: slot.item,
9285
9492
  place: slot.path.length === 0 ? item.place : {
@@ -9297,7 +9504,8 @@ function walk(world, doc, visit, options = {}) {
9297
9504
  address: slot.address === true,
9298
9505
  slot: slot.path,
9299
9506
  parent: here,
9300
- caps: item.caps === void 0 || kind.caps === void 0 ? item.caps : partCaps(kind.caps, slot.path, item.caps),
9507
+ caps: item.caps === void 0 || kind.caps === void 0 ? item.caps : [...partCaps(kind.caps, slot.path, item.caps), ...grants],
9508
+ handled: grants.length === 0 ? item.handled : [...item.handled ?? [], ...grants],
9301
9509
  bound: extra,
9302
9510
  expect: slot.expect
9303
9511
  });
@@ -9708,6 +9916,7 @@ const KEEPS_SELF = /* @__PURE__ */ new Set([
9708
9916
  "control.seq",
9709
9917
  "control.pure",
9710
9918
  "control.if",
9919
+ "control.handle",
9711
9920
  "list.all",
9712
9921
  "effect.log",
9713
9922
  "effect.write"
@@ -10272,7 +10481,7 @@ function reaching(machine, doc, options) {
10272
10481
  const rule = options.caps === void 0 ? permits : permitsOf(machine);
10273
10482
  walk(machine.world, doc, (v) => {
10274
10483
  const caps = v.caps ?? EACH;
10275
- for (const kind of declaredEffects(v, "eval", machine)) if (rule(caps, kind)) out.push({
10484
+ for (const kind of declaredEffects(v, "eval", machine)) if (rule(caps, kind) && (v.handled === void 0 || !permits(v.handled, kind))) out.push({
10276
10485
  kind,
10277
10486
  visit: v
10278
10487
  });
@@ -11937,6 +12146,28 @@ function freeIn(root, ctx, path) {
11937
12146
  const bound = Object.hasOwn(ctx.names, key);
11938
12147
  return bound === Object.hasOwn(root.names, key) && (!bound || Object.is(ctx.names[key], root.names[key]));
11939
12148
  }
12149
+ /** The key of the frame of a context. */
12150
+ const REF_FRAME = /* @__PURE__ */ Symbol("coeval.reference.frame");
12151
+ /** This function gives the frame of a context. */
12152
+ function refFrame(ctx) {
12153
+ return ctx[REF_FRAME];
12154
+ }
12155
+ /**
12156
+ * This function finds the clause for an effect (HANDLE.DO, HANDLE.FORWARD). An effect without a capability gets no clause
12157
+ * (HANDLE.NARROW). A clause of a frame that has ended gives `#CAP!` (HANDLE.ENDED).
12158
+ */
12159
+ function clauseAt(env, ctx, kind, place) {
12160
+ let frame = refFrame(ctx);
12161
+ if (frame === void 0) return void 0;
12162
+ const keys = typeof kind === "string" ? kind.split(".") : kind;
12163
+ const machine = env.machine;
12164
+ if (!(machine.permits === void 0 ? permits(ctx.caps, keys) : machine.permits(ctx.caps, keys, ctx))) return void 0;
12165
+ for (; frame !== void 0; frame = frame.outer) for (const clause of frame.clauses) {
12166
+ if (!permits([clause.op], keys)) continue;
12167
+ if (frame.ended || frame.owner !== env.effects) return fail("#CAP!", `The handler of the effect ${keys.join(".")} has ended.`, { place });
12168
+ return clause;
12169
+ }
12170
+ }
11940
12171
  /**
11941
12172
  * This function causes an effect when the capabilities permit it (RUN.CAP, CAP.HOOK). It records the effect with its
11942
12173
  * address (RUN.ADDRESS) and its outcome (RUN.OUTCOME). The mock answers the effect at once (RUN.MOCK). Without a
@@ -11955,6 +12186,8 @@ function cause(env, ctx, kind, payload, place) {
11955
12186
  function causeFromEvaluator(env, ctx, kind, payload, place) {
11956
12187
  const text = typeof kind === "string" ? kind : kind.join(".");
11957
12188
  if (env.running) return fail("#CALC!", `The effect ${text} needs a runner: in a run, the host does each effect.`, { place });
12189
+ const clause = clauseAt(env, ctx, kind, place);
12190
+ if (clause !== void 0) return "ok" in clause ? clause : drive$1(clause.answer(payload), place, text);
11958
12191
  return cause(env, ctx, kind, payload, place);
11959
12192
  }
11960
12193
  /**
@@ -11963,6 +12196,8 @@ function causeFromEvaluator(env, ctx, kind, payload, place) {
11963
12196
  * (RUN.OUTCOME).
11964
12197
  */
11965
12198
  function* causeAndWait(env, ctx, kind, payload, place) {
12199
+ const clause = clauseAt(env, ctx, kind, place);
12200
+ if (clause !== void 0) return "ok" in clause ? clause : yield* clause.answer(payload);
11966
12201
  if (!env.running) return cause(env, ctx, kind, payload, place);
11967
12202
  const effect = recordEffect(env, ctx, kind, payload, place);
11968
12203
  if ("ok" in effect) return effect;
@@ -12109,6 +12344,28 @@ function evalInput(env, seen, at) {
12109
12344
  fill
12110
12345
  };
12111
12346
  }
12347
+ /** This function evaluates a part under a frame: the part also gets the kinds of the clauses (CAP.GRANT). */
12348
+ function* handledAt(env, part, clauses) {
12349
+ const frame = {
12350
+ clauses,
12351
+ outer: refFrame(part.ctx),
12352
+ owner: env.effects,
12353
+ ended: false
12354
+ };
12355
+ const ctx = {
12356
+ ...part.ctx,
12357
+ caps: [...part.ctx.caps, ...clauses.map((clause) => clause.op)],
12358
+ [REF_FRAME]: frame
12359
+ };
12360
+ try {
12361
+ return yield* stepsAt(env, {
12362
+ ...part,
12363
+ ctx
12364
+ });
12365
+ } finally {
12366
+ frame.ended = true;
12367
+ }
12368
+ }
12112
12369
  function runInput(env, seen, at) {
12113
12370
  const { split, doc } = seen;
12114
12371
  const parts = split.parts.map((slot) => {
@@ -12117,7 +12374,8 @@ function runInput(env, seen, at) {
12117
12374
  key: slot.key,
12118
12375
  path: slot.path,
12119
12376
  ...name === void 0 ? {} : { name },
12120
- evaluate: (given) => stepsAt(env, partAt(seen, at, slot, given))
12377
+ evaluate: (given) => stepsAt(env, partAt(seen, at, slot, given)),
12378
+ handled: (clauses) => handledAt(env, partAt(seen, at, slot, void 0), clauses)
12121
12379
  };
12122
12380
  });
12123
12381
  const fill = (items) => split.parts.length === 0 ? doc.value : split.fill(items);
@@ -16214,6 +16472,287 @@ function syntaxFormat(profile) {
16214
16472
  evaluators: { "syntax.format": code }
16215
16473
  };
16216
16474
  }
16475
+ /** This function tells how a document breaks `parse(print(doc))`. It gives `undefined` when the document comes back. */
16476
+ function lawBreak(profile, doc) {
16477
+ const printed = print(profile, doc);
16478
+ if (!printed.ok) return {
16479
+ text: void 0,
16480
+ why: printed.error.reason
16481
+ };
16482
+ const text = printed.value;
16483
+ const back = parse(profile, text);
16484
+ if (back.ok) return docEquals(back.doc, doc) ? void 0 : {
16485
+ text,
16486
+ why: `The text ${text} reads as ${JSON.stringify(back.doc)}.`
16487
+ };
16488
+ return {
16489
+ text,
16490
+ why: `The text ${text} gives the issue: ${back.issues[0]?.message ?? ""}`
16491
+ };
16492
+ }
16493
+ /** This function gives the index of the first pattern that `parse` uses at the start of a text, or `-1`. */
16494
+ function patternAt(syn, text) {
16495
+ return syn.patterns.findIndex(({ sticky }) => {
16496
+ sticky.lastIndex = 0;
16497
+ const match = sticky.exec(text)?.[0];
16498
+ return match !== void 0 && match !== "" && !/[A-Za-z0-9_$(]/.test(text[match.length] ?? "");
16499
+ });
16500
+ }
16501
+ /** This function gives each small document of an operator with `term` in one term. A `flat` operator also has three terms. */
16502
+ function operatorDocs(op, term, leaf) {
16503
+ if (op.fixity !== "infix") return [opDoc(op, [term])];
16504
+ const pairs = [[term, leaf], [leaf, term]];
16505
+ return (op.assoc === "flat" ? [
16506
+ ...pairs,
16507
+ [
16508
+ term,
16509
+ leaf,
16510
+ leaf
16511
+ ],
16512
+ [
16513
+ leaf,
16514
+ term,
16515
+ leaf
16516
+ ]
16517
+ ] : pairs).map((terms) => opDoc(op, terms));
16518
+ }
16519
+ /** This function gives the longer symbol that reads two postfix operators without a space, or `undefined`. */
16520
+ function gluedSymbol(syn, outer, inner) {
16521
+ if (outer.word || inner.word || outer.fixity !== "postfix" || inner.fixity !== "postfix") return void 0;
16522
+ const glued = inner.symbol + outer.symbol;
16523
+ const read = syn.binarySymbols.find((symbol) => glued.startsWith(symbol));
16524
+ return read === inner.symbol ? void 0 : read;
16525
+ }
16526
+ function checkOperators({ profile, syn, leaf, keeps }) {
16527
+ const ops = (profile.operators ?? []).map((given) => (given.fixity === "prefix" ? syn.prefix : syn.binary).get(given.symbol));
16528
+ 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)));
16529
+ for (const outer of sound) {
16530
+ const place = ["operators", ops.indexOf(outer)];
16531
+ for (const inner of sound) {
16532
+ const glued = gluedSymbol(syn, outer, inner);
16533
+ const code = glued === void 0 ? "grouping" : "longest-match";
16534
+ 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}.`;
16535
+ for (const term of operatorDocs(inner, leaf, leaf)) for (const doc of operatorDocs(outer, term, leaf)) keeps(code, place, rule, doc);
16536
+ }
16537
+ }
16538
+ }
16539
+ function checkLiterals({ profile, syn, report }) {
16540
+ for (const [word, value] of syn.words) {
16541
+ const back = parse(profile, word);
16542
+ if (!back.ok || !docEquals(back.doc, envelop(syn, literalDoc(value)))) report("word", [
16543
+ "literals",
16544
+ "words",
16545
+ word
16546
+ ], word, `The word ${word} does not read as its value.`);
16547
+ }
16548
+ for (const value of [
16549
+ 0,
16550
+ -1,
16551
+ "a"
16552
+ ]) {
16553
+ const shape = literalShape(syn, value);
16554
+ if (shape.tag === "text" && patternAt(syn, shape.text) >= 0) report("pattern", [], shape.text, `The literal ${shape.text} reads as a reference.`);
16555
+ }
16556
+ }
16557
+ function checkRoots({ profile, syn, keeps }) {
16558
+ (profile.refs?.roots ?? DEFAULT_ROOTS).forEach((root, index) => {
16559
+ for (const path of syn.fields ? [[root], [root, "x"]] : [[root]]) keeps("root", [
16560
+ "refs",
16561
+ "roots",
16562
+ index
16563
+ ], `The root ${root} does not come back.`, {
16564
+ type: "ref",
16565
+ value: path
16566
+ }, path.join("."));
16567
+ });
16568
+ }
16569
+ /** This function gives a value that a call form writes: the fewest arguments, and at least one. */
16570
+ function formValue(given, leaf) {
16571
+ const args = given.args ?? (given.fields === void 0 ? "list" : "fields");
16572
+ const least = Math.max(given.min ?? given.fields?.length ?? 0, 1);
16573
+ if (args === "literal") return 1;
16574
+ if (args === "list") return Array.from({ length: least }, () => leaf);
16575
+ const keys = (given.fields ?? ["x"]).slice(0, args === "named" ? 1 : least);
16576
+ return Object.fromEntries(keys.map((key) => [key, leaf]));
16577
+ }
16578
+ function checkCalls({ profile, syn, leaf, keeps }) {
16579
+ const calls = profile.calls ?? {};
16580
+ (calls.forms ?? []).forEach((form, index) => {
16581
+ keeps("form", [
16582
+ "calls",
16583
+ "forms",
16584
+ index
16585
+ ], `The form ${form.name} does not come back.`, {
16586
+ type: typeOfKind(form.kind).type,
16587
+ value: formValue(form, leaf)
16588
+ });
16589
+ });
16590
+ if (syn.ops === true && syn.fields) keeps("call", ["calls", "ops"], "A call name with a dot does not come back.", {
16591
+ type: [
16592
+ "ops",
16593
+ "m",
16594
+ "p"
16595
+ ],
16596
+ value: [leaf]
16597
+ });
16598
+ if (typeof calls.ops === "boolean" || calls.ops === void 0) return;
16599
+ calls.ops.forEach((name, index) => {
16600
+ keeps("call", [
16601
+ "calls",
16602
+ "ops",
16603
+ index
16604
+ ], `The call ${name} does not come back.`, {
16605
+ type: ["ops", ...name.split(".")],
16606
+ value: [leaf]
16607
+ });
16608
+ });
16609
+ }
16610
+ /** This function writes two terms with the separator. It blames the separator only when a neutral separator keeps the document. */
16611
+ function checkSeparator({ profile, syn, leaf, report }) {
16612
+ if (syn.ops !== true && syn.ops.size === 0 && syn.forms.size === 0 && syn.constructKinds.size === 0) return;
16613
+ const open = {
16614
+ ...profile,
16615
+ calls: {
16616
+ ...profile.calls,
16617
+ ops: true
16618
+ }
16619
+ };
16620
+ const neutral = {
16621
+ ...profile,
16622
+ calls: {
16623
+ ...open.calls,
16624
+ separator: ""
16625
+ }
16626
+ };
16627
+ const postfix = [...syn.binary.values()].filter((op) => op.fixity === "postfix").map((op) => opDoc(op, [leaf]));
16628
+ const docs = [
16629
+ leaf,
16630
+ {
16631
+ type: "ref",
16632
+ value: [syn.bare, "b c"]
16633
+ },
16634
+ "a",
16635
+ 1,
16636
+ ...postfix
16637
+ ].map((term) => ({
16638
+ type: ["ops", "probe__"],
16639
+ value: [term, leaf]
16640
+ }));
16641
+ const entries = {
16642
+ x: leaf,
16643
+ y: leaf
16644
+ };
16645
+ const { record, let: bind } = syn.constructs;
16646
+ if (record !== void 0) docs.push({
16647
+ type: record,
16648
+ value: entries
16649
+ });
16650
+ if (bind !== void 0) docs.push({
16651
+ type: bind,
16652
+ value: {
16653
+ bind: entries,
16654
+ body: leaf
16655
+ }
16656
+ });
16657
+ for (const doc of docs) {
16658
+ const broken = lawBreak(neutral, doc) === void 0 ? lawBreak(open, doc) : void 0;
16659
+ if (broken !== void 0) report("separator", ["calls", "separator"], "", `The separator "${syn.separator}" does not end an argument. ${broken.why}`);
16660
+ }
16661
+ }
16662
+ function checkConstructs({ syn, leaf, keeps }) {
16663
+ const entries = { x: leaf };
16664
+ const values = {
16665
+ record: entries,
16666
+ let: {
16667
+ bind: entries,
16668
+ body: leaf
16669
+ },
16670
+ fn: {
16671
+ params: ["x"],
16672
+ body: leaf
16673
+ },
16674
+ call: {
16675
+ fn: leaf,
16676
+ args: [leaf]
16677
+ }
16678
+ };
16679
+ for (const name of CONSTRUCTS$1) {
16680
+ const type = syn.constructs[name];
16681
+ if (type !== void 0) keeps("construct", ["constructs", name], `The form ${name} does not come back.`, {
16682
+ type,
16683
+ value: values[name]
16684
+ });
16685
+ }
16686
+ }
16687
+ /**
16688
+ * This function finds the faults of a syntax profile before a host uses it (SYNTAX.PROFILE-CHECK, D140). A fault is a
16689
+ * document of an entry that breaks `parse(print(doc))`, or a text of an entry that reads another way. The function
16690
+ * writes and reads one small document for each entry, for each pair of operators and for each root. Thus an empty list
16691
+ * tells that these small documents come back. It does not prove the law for each document. `parse` and `print` do not
16692
+ * call this function, thus it adds no cost to them.
16693
+ *
16694
+ * @param profile - The syntax profile.
16695
+ * @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.
16696
+ * @throws An error of a `read` or a `write` function of a pattern.
16697
+ */
16698
+ function checkSyntax(profile) {
16699
+ const open = {
16700
+ ...profile,
16701
+ maxDepth: 256
16702
+ };
16703
+ let syn;
16704
+ try {
16705
+ syn = compileProfile(open);
16706
+ } catch (error) {
16707
+ if (error instanceof TypeError) return [{
16708
+ code: "invalid",
16709
+ place: [],
16710
+ reason: error.message
16711
+ }];
16712
+ throw error;
16713
+ }
16714
+ const found = /* @__PURE__ */ new Map();
16715
+ const report = (code, place, text, reason) => {
16716
+ const index = patternAt(syn, text);
16717
+ const finding = index < 0 ? {
16718
+ code,
16719
+ place,
16720
+ reason
16721
+ } : {
16722
+ code: "pattern",
16723
+ place: [
16724
+ "refs",
16725
+ "patterns",
16726
+ index
16727
+ ],
16728
+ reason: `The pattern reads the start of the text ${text}. ${reason}`
16729
+ };
16730
+ const key = JSON.stringify([finding.code, finding.place]);
16731
+ if (!found.has(key)) found.set(key, finding);
16732
+ };
16733
+ const keeps = (code, place, rule, doc, start = "") => {
16734
+ const broken = lawBreak(open, doc);
16735
+ if (broken !== void 0) report(code, place, broken.text ?? start, `${rule} ${broken.why}`);
16736
+ return broken === void 0;
16737
+ };
16738
+ const check = {
16739
+ profile: open,
16740
+ syn,
16741
+ leaf: {
16742
+ type: "ref",
16743
+ value: [syn.bare, "a"]
16744
+ },
16745
+ report,
16746
+ keeps
16747
+ };
16748
+ checkOperators(check);
16749
+ checkLiterals(check);
16750
+ checkRoots(check);
16751
+ checkCalls(check);
16752
+ checkSeparator(check);
16753
+ checkConstructs(check);
16754
+ return [...found.values()];
16755
+ }
16217
16756
  // src/generate.ts
16218
16757
  /**
16219
16758
  * This function makes a source of random numbers with a seed (the algorithm mulberry32). It needs no package.
@@ -17324,7 +17863,8 @@ function instanceOf(env, under, doc, split, sample) {
17324
17863
  const ctx = shot.context;
17325
17864
  const parts = split.parts.map((slot, index) => ({
17326
17865
  ...handleOf(slot),
17327
- evaluate: (given) => shot.steps(index, "evaluate", given ?? ctx)
17866
+ evaluate: (given) => shot.steps(index, "evaluate", given ?? ctx),
17867
+ handled: () => shot.steps(index, "evaluate", ctx)
17328
17868
  }));
17329
17869
  const input = {
17330
17870
  ...base,
@@ -20016,6 +20556,6 @@ function samplesAt(values, closed, depth) {
20016
20556
  function typeFromSamples(values, options = {}) {
20017
20557
  return samplesAt(values, options.closed === true, 0);
20018
20558
  }
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 };
20559
+ 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, 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, 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, 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
20560
 
20021
20561
  //# sourceMappingURL=index.js.map