@mark1russell7/coeval 0.26.1 → 0.27.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@mark1russell7/coeval",
4
- "version": "0.26.1",
4
+ "version": "0.27.1",
5
5
  "description": "coeval: the kernel of declarative languages. Documents in envelopes, a ladder that finds the evaluator, kind contracts, compiled plans, a static kit, a formula kit and a run side with capabilities.",
6
6
  "type": "module",
7
7
  "main": "./build/index.js",
package/src/errors.ts CHANGED
@@ -16,6 +16,7 @@ export type KernelCode =
16
16
  | "#CAP!"
17
17
  | "#CALC!"
18
18
  | "#DEPTH!"
19
+ | "#WORK!"
19
20
  | "#ABORT"
20
21
  | "#REPLAY!"
21
22
  | "#TYPE!";
package/src/evaluate.ts CHANGED
@@ -541,6 +541,25 @@ export function placeText(place: readonly Key[]): string {
541
541
  /** An effect while the kernel waits for its outcome. */
542
542
  type Recorded = { -readonly [K in keyof Effect]: Effect[K] };
543
543
 
544
+ /**
545
+ * The most work of one evaluation for a machine without its own `maxWork` (WORK.LIMIT, D134). One application of a
546
+ * closure is one point.
547
+ */
548
+ export const WORK_LIMIT = 200000;
549
+
550
+ /** The work of one node that an equation makes (WORK.NODE, D134). A node costs more than an application. */
551
+ export const NODE_WORK = 8;
552
+
553
+ /**
554
+ * This function gives the error of the limit of work. Each tier gives this error (WORK.AGREES).
555
+ *
556
+ * @param limit - The limit of the machine.
557
+ * @returns The error.
558
+ */
559
+ export function workFailure(limit: number): EvalError {
560
+ return fail("#WORK!", `The work of one evaluation is more than ${limit} points. The field maxWork of the machine sets the limit.`).error;
561
+ }
562
+
544
563
  /**
545
564
  * The runtime of one evaluation or run. Compiled code gets it with the context, and asks it to resolve paths and
546
565
  * to cause effects.
@@ -549,6 +568,15 @@ export interface Runtime {
549
568
  readonly machine: Machine;
550
569
  /** `true` in a run: the host does each effect, and a part that waits needs a parent that can wait. */
551
570
  readonly running: boolean;
571
+ /** `true` when a node that this runtime makes costs work: in an equation of a tree (WORK.NODE) and in a fold. */
572
+ readonly nested?: boolean;
573
+ /**
574
+ * This optional function adds work to the count of the evaluation (WORK.LIMIT). A runtime without it counts nothing.
575
+ *
576
+ * @param points - The work.
577
+ * @throws {Failure} The error `#WORK!`, when the count is over the limit of the machine.
578
+ */
579
+ spend?(points: number): void;
552
580
  /**
553
581
  * This function resolves a path from a context.
554
582
  *
@@ -819,6 +847,11 @@ export interface CompiledEvaluator {
819
847
  * (TYPE.RULE). Only a check reads it. An evaluator without a rule gives `unknown`.
820
848
  */
821
849
  readonly type?: TypeRule;
850
+ /**
851
+ * `true` when a value that the code gives can use its runtime after the end of the evaluation (WORK.KEEPS). The plan
852
+ * then marks that end. A compiled evaluator without this field gives no such value.
853
+ */
854
+ readonly keeps?: boolean;
822
855
  }
823
856
 
824
857
  /** An evaluator: a function of its input, or a compiler. */
@@ -848,6 +881,7 @@ export type Runner = RunFn | CompiledRunner;
848
881
  * A machine: a world of kinds, and the code of each evaluator name, of each `deref` and of each walker.
849
882
  *
850
883
  * - `maxDepth`: the deepest nesting of documents. The default is 1000.
884
+ * - `maxWork`: the most work of one evaluation (WORK.LIMIT, D134). The default is `WORK_LIMIT`.
851
885
  * - `permits`: the rule of the host for an effect. The default is the function `permits` on the capabilities. A run,
852
886
  * `check` and `effectsOf` ask it (CAP.PERMITS). In a check, its context has only the capabilities and the world.
853
887
  * - `known`: the names that the evaluators know, for `check`. Without it, `check` does not check such names.
@@ -859,6 +893,7 @@ export interface Machine extends Navigator {
859
893
  /** The runners, by evaluator name. A runner wins over an evaluator of the same name. */
860
894
  readonly runners?: Readonly<Record<string, Runner>>;
861
895
  readonly maxDepth?: number;
896
+ readonly maxWork?: number;
862
897
  readonly permits?: (caps: readonly Cap[], kind: readonly Key[], context: Context) => boolean;
863
898
  /**
864
899
  * This function tells if an evaluator knows the name in the path of a kind. An example is the operation
@@ -1157,6 +1192,18 @@ function readingOf(list: Read[] | undefined, root: Context): Reading | undefined
1157
1192
 
1158
1193
  /** This class holds the state of one evaluation or run: the machine, the effects, the clock and the trace. */
1159
1194
  class Session implements Runtime {
1195
+ /**
1196
+ * The count of the work, on the runtime of the root of the evaluation (WORK.LIMIT). The first count adds the field,
1197
+ * thus an evaluation without a counted point has no field.
1198
+ */
1199
+ declare work?: number;
1200
+ /** The count of the applications of closures that run at this time. `formula.fn` writes it (FORMULA.CALL-DEPTH). */
1201
+ declare applying?: number;
1202
+ /**
1203
+ * The count of the applications of closures that run at this time, on the root and on each runtime of an equation
1204
+ * (WORK.AFTER). `formula.fn` writes it on the runtime of the root. The first application adds the field.
1205
+ */
1206
+ declare active?: number;
1160
1207
  readonly machine: Machine;
1161
1208
  /**
1162
1209
  * The epoch of the evaluation: the list of capabilities that the host gave the root (CAP.NARROW-KEPT). A context
@@ -1166,7 +1213,12 @@ class Session implements Runtime {
1166
1213
  readonly running: boolean;
1167
1214
  readonly traced: boolean;
1168
1215
  readonly perform: Perform | undefined;
1169
- readonly signal: StopSignal | undefined;
1216
+ /**
1217
+ * The runtime of the root, on the runtime of an equation of a tree (WORK.EQUATION). The runtime of a root holds
1218
+ * itself after the end of its evaluation (WORK.AFTER, WORK.KEEPS), thus the end mark adds no field to the object.
1219
+ * Each runtime has this field, thus a tree and its equations have one object shape.
1220
+ */
1221
+ top: Session | undefined;
1170
1222
  /**
1171
1223
  * The record of the reads. Without it, a resolution records nothing. A nested evaluation of an equation of a tree
1172
1224
  * shares the record of the evaluation around it (READS.EQUATION).
@@ -1196,7 +1248,7 @@ class Session implements Runtime {
1196
1248
  running: boolean,
1197
1249
  traced: boolean,
1198
1250
  perform: Perform | undefined,
1199
- signal: StopSignal | undefined,
1251
+ top: Session | undefined,
1200
1252
  effects: Effect[] | undefined,
1201
1253
  reading: Reading | undefined = undefined,
1202
1254
  ) {
@@ -1205,13 +1257,36 @@ class Session implements Runtime {
1205
1257
  this.running = running;
1206
1258
  this.traced = traced;
1207
1259
  this.perform = perform;
1208
- this.signal = signal;
1260
+ this.top = top;
1209
1261
  this.reading = reading;
1210
1262
  this.list = effects;
1211
1263
  this.holder = traced ? holderNode() : undefined;
1212
1264
  this.current = this.holder;
1213
1265
  }
1214
1266
 
1267
+ /** This getter gives `true` for the runtime of an equation of a tree (WORK.NODE). */
1268
+ get nested(): boolean {
1269
+ const top = this.top;
1270
+ return top !== undefined && top !== this;
1271
+ }
1272
+
1273
+ /**
1274
+ * This function adds work to the count of the evaluation (WORK.LIMIT). One count is on the runtime of the root, thus
1275
+ * each equation of a tree adds to it. After the end of the evaluation, each entry from the host starts a new count
1276
+ * (WORK.AFTER). An entry is a point with no application of a closure that runs, on any runtime of the evaluation.
1277
+ *
1278
+ * @param points - The work.
1279
+ * @throws {Failure} The error `#WORK!`, when the count is over the limit of the machine.
1280
+ */
1281
+ spend(points: number): void {
1282
+ const root = this.top ?? this;
1283
+ const entry = root.top === root && (root.active ?? 0) === 0;
1284
+ const work = (entry ? 0 : (root.work ?? 0)) + points;
1285
+ root.work = work;
1286
+ const limit = this.machine.maxWork ?? WORK_LIMIT;
1287
+ if (work > limit) throw new Failure(workFailure(limit));
1288
+ }
1289
+
1215
1290
  /** The effects of the evaluation, in their order. */
1216
1291
  get effects(): Effect[] {
1217
1292
  return (this.list ??= []);
@@ -1753,6 +1828,8 @@ interface Compiler {
1753
1828
  readonly guards: ReadonlyMap<string, ValueType> | undefined;
1754
1829
  /** The runtime of the folds of this compilation. The first fold makes it. */
1755
1830
  probe?: Probe;
1831
+ /** `true` when a document of this compilation can use its runtime after the end of the evaluation (WORK.KEEPS). */
1832
+ keeps?: boolean;
1756
1833
  }
1757
1834
 
1758
1835
  function failing(c: Compiler, meta: Meta, place: readonly Key[], code: ErrorCode, reason: string): Compiled {
@@ -1890,6 +1967,7 @@ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declare
1890
1967
  };
1891
1968
  // A compiler that throws fails its own document when that document evaluates, with its place (PLAN.FAIL-LATE).
1892
1969
  if (runner !== undefined) {
1970
+ c.keeps = true;
1893
1971
  let inner: RunCode;
1894
1972
  try {
1895
1973
  inner = isCompiledRunner(runner) ? runner.compileRun(input) : interpretRun(runner, input, split.fill, kind?.caps?.some((rule) => rule.rule === "grant") === true);
@@ -1901,6 +1979,7 @@ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declare
1901
1979
  return finish(c, meta, runnerCode(run, place, kindText), run);
1902
1980
  }
1903
1981
 
1982
+ if (!isCompiledEvaluator(evaluator as Evaluator) || (evaluator as CompiledEvaluator).keeps === true) c.keeps = true;
1904
1983
  let inner: Code;
1905
1984
  try {
1906
1985
  inner = isCompiledEvaluator(evaluator as Evaluator) ? (evaluator as CompiledEvaluator).compile(input) : interpret(evaluator as EvalFn, input, split.fill);
@@ -1990,6 +2069,8 @@ class Probe implements Runtime {
1990
2069
  readonly running = false;
1991
2070
  /** `true` after the part asked for something that a fold cannot give. */
1992
2071
  refused = false;
2072
+ /** A node in a fold costs work, thus a tree stops the fold before it evaluates an equation (WORK.PREPARE). */
2073
+ readonly nested = true;
1993
2074
  /** The count of the names that the part read. */
1994
2075
  reads = 0;
1995
2076
  /**
@@ -2058,6 +2139,12 @@ class Probe implements Runtime {
2058
2139
  keepsContext(): void {
2059
2140
  this.refused = true;
2060
2141
  }
2142
+
2143
+ /** This function stops the fold at the first point of work, thus `prepare` does no such work (WORK.PREPARE). */
2144
+ spend(): void {
2145
+ this.refused = true;
2146
+ throw new Failure(workFailure(0));
2147
+ }
2061
2148
  }
2062
2149
 
2063
2150
  /**
@@ -2418,8 +2505,8 @@ export interface InputWithRuntime extends EvalInput {
2418
2505
  /**
2419
2506
  * This function evaluates a plan inside the evaluation of another runtime, for example an equation of a tree inside
2420
2507
  * the evaluation of its tree (READS.EQUATION). The plan gets its own runtime, as in `plan.evaluate`. The reads of the
2421
- * plan go in the record of the reads of `outer`. The context at the root of `outer` tells which reads are free
2422
- * (READS.FREE). Without such a record, the function gives `plan.evaluate(context)`.
2508
+ * plan go in the record of the reads of `outer`, and its work goes in the count of `outer` (WORK.EQUATION). The context
2509
+ * at the root of `outer` tells which reads are free (READS.FREE). Without a session, the function gives `plan.evaluate(context)`.
2423
2510
  *
2424
2511
  * @param plan - The plan.
2425
2512
  * @param context - The context of the plan.
@@ -2427,11 +2514,10 @@ export interface InputWithRuntime extends EvalInput {
2427
2514
  * @returns The result.
2428
2515
  */
2429
2516
  export function evaluateWithin(plan: Plan, context: Context, outer: Runtime | undefined): Result {
2430
- const reading = outer instanceof Session ? outer.reading : undefined;
2431
- if (reading === undefined || !(plan instanceof CompiledPlan)) return plan.evaluate(context);
2517
+ if (!(outer instanceof Session) || !(plan instanceof CompiledPlan)) return plan.evaluate(context);
2432
2518
  const machine = plan.machine;
2433
2519
  const ctx = rootContext(machine, context);
2434
- return plan.code(ctx, new Session(machine, ctx.caps, false, plan.traced, undefined, undefined, undefined, reading));
2520
+ return plan.code(ctx, new Session(machine, ctx.caps, false, plan.traced, undefined, outer.top ?? outer, undefined, outer.reading));
2435
2521
  }
2436
2522
 
2437
2523
  function interpret(fn: EvalFn, input: CompileInput, fill: <P>(handles: readonly P[]) => unknown): Code {
@@ -2570,7 +2656,23 @@ export function prepare(machine: Machine, doc: unknown, options: PrepareOptions
2570
2656
  const result = placed(thrown(error, "the compilation"), []);
2571
2657
  root = { code: () => result, run: undefined };
2572
2658
  }
2573
- return new CompiledPlan(machine, c.traced, root.code, root.run);
2659
+ return new CompiledPlan(machine, c.traced, c.keeps === true ? endingCode(root.code) : root.code, root.run);
2660
+ }
2661
+
2662
+ /**
2663
+ * This function gives the code of a plan with a document that can use its runtime after the end of the evaluation. The
2664
+ * code marks that end (WORK.KEEPS). Each other plan has no such step, thus the count adds nothing to a read.
2665
+ *
2666
+ * @param code - The code of the root.
2667
+ * @returns The code that marks the end.
2668
+ */
2669
+ function endingCode(code: Code): Code {
2670
+ return (ctx, rt) => {
2671
+ const result = code(ctx, rt);
2672
+ const session = rt as Session;
2673
+ session.top ??= session;
2674
+ return result;
2675
+ };
2574
2676
  }
2575
2677
 
2576
2678
  /**
@@ -2619,8 +2721,10 @@ class CompiledPlan implements Plan {
2619
2721
  run(context: Context = makeContext(), runOptions: PlanRunOptions = {}): Promise<Result> {
2620
2722
  const machine = this.machine;
2621
2723
  const ctx = rootContext(machine, context);
2622
- const s = new Session(machine, ctx.caps, true, this.traced, runOptions.perform, runOptions.signal, undefined, readingOf(runOptions.reads, ctx));
2623
- return driveWaiting(this.runCode()(ctx, s), runOptions.signal);
2724
+ const s = new Session(machine, ctx.caps, true, this.traced, runOptions.perform, undefined, undefined, readingOf(runOptions.reads, ctx));
2725
+ return driveWaiting(this.runCode()(ctx, s), runOptions.signal).finally(() => {
2726
+ s.top = s;
2727
+ });
2624
2728
  }
2625
2729
  }
2626
2730
 
@@ -2689,8 +2793,7 @@ export function evaluate(machine: Machine, doc: unknown, context: Context = make
2689
2793
  });
2690
2794
  const ctx = rootContext(machine, context);
2691
2795
  const s = new Session(machine, ctx.caps, false, true, given.perform, undefined, undefined, readingOf(given.reads, ctx));
2692
- const result = (plan as CompiledPlan).code(ctx, s);
2693
- return record(s, result);
2796
+ return record(s, (plan as CompiledPlan).code(ctx, s));
2694
2797
  }
2695
2798
 
2696
2799
  /**
@@ -2712,8 +2815,9 @@ export async function run(machine: Machine, doc: unknown, context: Context = mak
2712
2815
  ...(options.verb === undefined ? {} : { verb: options.verb }),
2713
2816
  });
2714
2817
  const ctx = rootContext(machine, context);
2715
- const s = new Session(machine, ctx.caps, true, true, options.perform, options.signal, undefined, readingOf(options.reads, ctx));
2818
+ const s = new Session(machine, ctx.caps, true, true, options.perform, undefined, undefined, readingOf(options.reads, ctx));
2716
2819
  const result = await driveWaiting((plan as CompiledPlan).runCode()(ctx, s), options.signal);
2820
+ s.top = s;
2717
2821
  return record(s, result);
2718
2822
  }
2719
2823
 
package/src/formula.ts CHANGED
@@ -139,7 +139,7 @@ export function isClosure(value: unknown): value is Closure {
139
139
  * The runtime of an evaluation with the clock of `call` (FORMULA.CALL-DEPTH, D125): the count of the applications of
140
140
  * closures that run at this time. The first application adds the field, thus an evaluation without one has no field.
141
141
  */
142
- type Clocked = Runtime & { applying?: number };
142
+ type Clocked = Runtime & { applying?: number; active?: number; top?: Clocked | undefined };
143
143
 
144
144
  /**
145
145
  * The standard extensions: the values that JSON cannot write. `ext.domain` reads a member of a domain through the
@@ -175,8 +175,8 @@ function setOwn(target: Record<string, unknown>, key: string, value: unknown): v
175
175
  else target[key] = value;
176
176
  }
177
177
 
178
- function compiled(compile: (input: CompileInput) => Code): CompiledEvaluator {
179
- return { compile };
178
+ function compiled(compile: (input: CompileInput) => Code, keeps?: true): CompiledEvaluator {
179
+ return keeps === undefined ? { compile } : { compile, keeps };
180
180
  }
181
181
 
182
182
  /** This function evaluates parts in order. It gives the value of each part, or the first error. */
@@ -756,7 +756,7 @@ function makeEvaluators(profile: FormulaProfile): Readonly<Record<string, Evalua
756
756
  for (const name of Object.keys(evaluators)) {
757
757
  const evaluator = evaluators[name] as CompiledEvaluator;
758
758
  const type = Object.hasOwn(types, name) ? types[name] : undefined;
759
- const made = Object.freeze(type === undefined ? evaluator : { compile: evaluator.compile, type });
759
+ const made = Object.freeze(type === undefined ? evaluator : { ...evaluator, type });
760
760
  if (truth !== undefined && TRUTH_READERS.includes(name)) truthByCode.set(made, truth);
761
761
  if (PROFILE_READERS.includes(name)) readerByCode.set(made, { name, profile });
762
762
  out[name] = made;
@@ -1045,7 +1045,13 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
1045
1045
  // Each application counts one level on the runtime of its evaluation, and the count goes down after it (FORMULA.CALL-DEPTH).
1046
1046
  const level = (clock.applying ?? 0) + 1;
1047
1047
  if (level > limit) throw new Failure(fail("#DEPTH!", `The applications of functions nest deeper than ${limit} levels.`).error);
1048
+ // WORK.LIMIT: an application is a place where work can multiply, thus each one counts one point (D134).
1049
+ rt.spend?.(1);
1050
+ // WORK.AFTER: the runtime of the root counts the applications of each runtime of the evaluation. An equation of
1051
+ // a tree in the body has its own runtime, and its work does not start a new count.
1052
+ const root = clock.top ?? clock;
1048
1053
  clock.applying = level;
1054
+ root.active = (root.active ?? 0) + 1;
1049
1055
  try {
1050
1056
  const scope: Record<string, unknown> = { ...ctx.names };
1051
1057
  names.forEach((name, index) => setOwn(scope, name, args[index]));
@@ -1054,12 +1060,14 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
1054
1060
  return result.value;
1055
1061
  } finally {
1056
1062
  clock.applying = level - 1;
1063
+ root.active = (root.active ?? 1) - 1;
1057
1064
  }
1058
1065
  };
1059
1066
  Object.defineProperty(closure, CLOSURE, { value: { params: names } });
1060
1067
  return ok(closure);
1061
1068
  };
1062
- }),
1069
+ // A closure uses its runtime after the end of the evaluation (WORK.KEEPS).
1070
+ }, true),
1063
1071
 
1064
1072
  "formula.call": compiled(({ value }) => {
1065
1073
  const record = partsOf(value);
package/src/generate.ts CHANGED
@@ -198,7 +198,12 @@ export function generate(world: World, options: GenerateOptions): unknown {
198
198
  const envelope = (type: readonly Key[], value: Json, header?: string): Json => {
199
199
  const out: Record<string, Json> = { type: renderType(type), value };
200
200
  if (header !== undefined) out["name"] = header;
201
- if (options.own !== undefined && options.own.length > 0 && rng.chance(0.03)) out["own"] = { eval: rng.pick(options.own) };
201
+ if (options.own !== undefined && options.own.length > 0 && rng.chance(0.03)) {
202
+ const code = rng.pick(options.own);
203
+ // The code of a kind binds the names of its scope rules (C7). An own entry of other code does not, thus a kind
204
+ // with scope rules gets no own entry (GEN.CONTRACTS). The draws stay, thus each other document stays the same.
205
+ if ((kindOf(type)?.scope ?? []).length === 0) out["own"] = { eval: code };
206
+ }
202
207
  return out;
203
208
  };
204
209
 
package/src/index.ts CHANGED
@@ -91,7 +91,7 @@ export {
91
91
  restrictMachine,
92
92
  restrictWorld,
93
93
  } from "./merge.ts";
94
- export { type Language, type LanguageCode, type LanguageDefinition, compose, defineLanguage } from "./language.ts";
94
+ export { type Language, type LanguageCode, type LanguageDefinition, boundary, compose, defineLanguage } from "./language.ts";
95
95
  export {
96
96
  type Answer,
97
97
  type AskOptions,
@@ -186,6 +186,7 @@ export {
186
186
  prepare,
187
187
  resolveCompiled,
188
188
  run,
189
+ WORK_LIMIT,
189
190
  } from "./evaluate.ts";
190
191
  export {
191
192
  type AttributeAnswer,
package/src/language.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type Key, isRecord } from "./doc.ts";
2
- import type { Machine } from "./evaluate.ts";
2
+ import { type InputWithRuntime, type Machine, type Plan, RUNTIME, evaluateWithin, makeContext, planner } from "./evaluate.ts";
3
+ import { fail } from "./errors.ts";
3
4
  import { type MachinePart, type MergeConflict, type WorldPart, MergeError, mergeMachines } from "./merge.ts";
4
5
 
5
6
  /** The code half of a language: each field of a machine part, without the world. */
@@ -145,3 +146,52 @@ export function compose(...languages: readonly Language[]): Machine {
145
146
  if (conflicts.length > 0) throw new MergeError(conflicts);
146
147
  return mergeMachines(...parts);
147
148
  }
149
+
150
+ /**
151
+ * This function makes the language of the boundary kind `lang` (K38, D193). A document of the kind holds the name of a
152
+ * language and a document in that language: `{ type: "lang", value: { language, doc } }`. The machine of the named
153
+ * language prepares and evaluates the inner document, as a module of another language (COMPOSE.BOUNDARY).
154
+ *
155
+ * The boundary is closed. The inner document gets no names, no capabilities and no node as `self`. Only its result
156
+ * crosses the boundary. Thus an effect in the inner document fails with `#CAP!`, and the inner machine cannot read the
157
+ * names of the outer document.
158
+ *
159
+ * @param machines - The machine of each language, by the name that a document gives.
160
+ * @returns The language, for `compose`.
161
+ */
162
+ export function boundary(machines: Readonly<Record<string, Machine>>): Language {
163
+ const plans = new Map<string, (doc: unknown) => Plan>();
164
+ const planOf = (name: string): ((doc: unknown) => Plan) | undefined => {
165
+ let found = plans.get(name);
166
+ if (found === undefined && Object.hasOwn(machines, name)) {
167
+ found = planner(machines[name] as Machine);
168
+ plans.set(name, found);
169
+ }
170
+ return found;
171
+ };
172
+ return defineLanguage({
173
+ name: "boundary",
174
+ version: "1.0.0",
175
+ world: {
176
+ kinds: {
177
+ lang: {
178
+ verbs: { eval: "boundary.lang" },
179
+ summary: "A document in another language. The machine of that language evaluates it, with no names and no capabilities.",
180
+ },
181
+ },
182
+ },
183
+ code: {
184
+ evaluators: {
185
+ "boundary.lang": (input) => {
186
+ const { data, place } = input;
187
+ if (!isRecord(data) || typeof data["language"] !== "string" || !("doc" in data)) {
188
+ return fail("#VALUE!", "A document of the kind lang has the value { language, doc }.", { place });
189
+ }
190
+ const plans = planOf(data["language"]);
191
+ if (plans === undefined) return fail("#NAME?", `The boundary knows no language ${JSON.stringify(data["language"])}.`, { place });
192
+ return evaluateWithin(plans(data["doc"]), makeContext(), (input as InputWithRuntime)[RUNTIME]);
193
+ },
194
+ },
195
+ },
196
+ });
197
+ }
package/src/merge.ts CHANGED
@@ -581,7 +581,7 @@ function checkHidden(m: Merger, draft: MachineDraft): void {
581
581
  * - A runner hides the evaluator of its name. Thus one part must give both, or the merge gives a conflict.
582
582
  * - The hooks `known` compose in the order of the parts. A hook does not answer for a name that a later part replaces.
583
583
  * The hooks `facts` compose the same way, and the first answer that is not `undefined` wins.
584
- * - Each other field, for example `permits`, `adopt`, `roots`, `plain` or `maxDepth`, is one value. Two different
584
+ * - Each other field, for example `permits`, `adopt`, `roots`, `plain`, `maxDepth` or `maxWork`, is one value. Two different
585
585
  * values are a conflict. An absent field has no value.
586
586
  *
587
587
  * A conflict stops the merge, unless the later part names its path in `replaces`. A part can be a function of the