@mark1russell7/coeval 0.10.0 → 0.11.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/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.10.0",
4
+ "version": "0.11.0",
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/evaluate.ts CHANGED
@@ -785,17 +785,13 @@ export interface StopSignal {
785
785
  addEventListener(type: "abort", listener: () => void, options?: { readonly once?: boolean }): void;
786
786
  }
787
787
 
788
- /** The options of a run. */
789
- export interface RunOptions {
788
+ /**
789
+ * The options of `plan.run`. A plan keeps the verb and the declared type of its `prepare`, thus these options do not
790
+ * have them (PLAN.RUN-OPTIONS).
791
+ */
792
+ export interface PlanRunOptions {
790
793
  /** The host code that does each effect. Without it, an effect gives its payload, as in an evaluation. */
791
794
  readonly perform?: Perform;
792
- /** The type of a bare root value. */
793
- readonly declaredType?: string;
794
- /**
795
- * The verb of `run`, for example `format`. The default is `eval` (VERB.PREPARE). A plan keeps the verb of its
796
- * `prepare`, thus `plan.run` does not read this option.
797
- */
798
- readonly verb?: string;
799
795
  /** A signal of the host. When it aborts, the run stops with `#ABORT`. */
800
796
  readonly signal?: StopSignal;
801
797
  /**
@@ -805,6 +801,14 @@ export interface RunOptions {
805
801
  readonly reads?: Read[];
806
802
  }
807
803
 
804
+ /** The options of `run`: the options of `plan.run`, and the type of a bare root value and the verb for `prepare`. */
805
+ export interface RunOptions extends PlanRunOptions {
806
+ /** The type of a bare root value. */
807
+ readonly declaredType?: string;
808
+ /** The verb of `run`, for example `format`. The default is `eval` (VERB.PREPARE). */
809
+ readonly verb?: string;
810
+ }
811
+
808
812
  /** The options of an evaluation of a plan. */
809
813
  export interface EvalOptions {
810
814
  /** A list that gets each effect, in its order. */
@@ -1303,7 +1307,7 @@ export interface Plan {
1303
1307
  * @param options - The host code that does the effects, a signal to stop the run and a list that gets the reads.
1304
1308
  * @returns The result, when the run ends.
1305
1309
  */
1306
- run(context?: Context, options?: RunOptions): Promise<Result>;
1310
+ run(context?: Context, options?: PlanRunOptions): Promise<Result>;
1307
1311
  }
1308
1312
 
1309
1313
  interface Compiler {
@@ -1471,7 +1475,8 @@ function compileNode(c: Compiler, value: unknown, place: readonly Key[], declare
1471
1475
  } catch (error) {
1472
1476
  return failingWith(c, meta, placed(thrown(error, `the evaluator ${name}`), place));
1473
1477
  }
1474
- const done = finish(c, meta, placedCode(inner, place, name), undefined);
1478
+ // Code that cannot fail needs no place and no catch, and it does not read the context of its grant (PLAN.TOTAL).
1479
+ const done = finish(c, meta, TOTAL.has(inner) ? inner : placedCode(inner, place, name), undefined);
1475
1480
  if (frees === undefined || kind === undefined || !pureCode(c, envelope, kind, typePath, name, quick)) return done;
1476
1481
  const free = freeOf(doc, kind, split.parts, frees);
1477
1482
  if (free === undefined) return done;
@@ -1721,6 +1726,24 @@ function copyPlain(value: unknown, memo: Map<object, unknown>): unknown {
1721
1726
  * the split of the value. Thus a plan with these helpers keeps only its code.
1722
1727
  */
1723
1728
 
1729
+ /**
1730
+ * The compiled code that cannot fail (PLAN.TOTAL). Such code gives a value and throws nothing. It does not read its
1731
+ * context or its runtime. Only the kits of the kernel mark code, thus the code of a host always gets its grant.
1732
+ */
1733
+ const TOTAL: WeakSet<Code> = /* @__PURE__ */ new WeakSet();
1734
+
1735
+ /**
1736
+ * This function marks compiled code that cannot fail (PLAN.TOTAL). Then `compileNode` does not wrap the code in
1737
+ * `placedCode`, thus a literal costs one call. The kits of the kernel use it. The package does not export it.
1738
+ *
1739
+ * @param code - The code. It gives a value and throws nothing. It does not read its context or its runtime.
1740
+ * @returns The same code.
1741
+ */
1742
+ export function totalCode(code: Code): Code {
1743
+ TOTAL.add(code);
1744
+ return code;
1745
+ }
1746
+
1724
1747
  /**
1725
1748
  * This function gives the code of a document: the code of its evaluator, with the place of each error and of each
1726
1749
  * exception. The code gets the context that its grant permits, thus a compiled parent cannot widen it (CAP.NARROW-CODE).
@@ -1952,7 +1975,7 @@ class CompiledPlan implements Plan {
1952
1975
  return this.code(ctx, s);
1953
1976
  }
1954
1977
 
1955
- run(context: Context = makeContext(), runOptions: RunOptions = {}): Promise<Result> {
1978
+ run(context: Context = makeContext(), runOptions: PlanRunOptions = {}): Promise<Result> {
1956
1979
  const machine = this.machine;
1957
1980
  const ctx = rootContext(machine, context);
1958
1981
  const s = new Session(machine, true, this.traced, runOptions.perform, runOptions.signal, undefined, readingOf(runOptions.reads, ctx));
package/src/formula.ts CHANGED
@@ -2,7 +2,7 @@ import { type Json, type Key, docEquals, isKey, isRecord } from "./doc.ts";
2
2
  import type { Kind, KindFacts, LazyRule, WorldEntry } from "./world.ts";
3
3
  import { type CompiledPath, type Context, type Resolution, compilePath, valueOfThing } from "./deref.ts";
4
4
  import { type EvalError, type Failed, type Result, Failure, fail, ok, thrown } from "./errors.ts";
5
- import { type Code, type CompileInput, type CompiledEvaluator, type CompiledPart, type Evaluator, type Machine, type Runtime, resolveCompiled } from "./evaluate.ts";
5
+ import { type Code, type CompileInput, type CompiledEvaluator, type CompiledPart, type Evaluator, type Machine, type Runtime, resolveCompiled, totalCode } from "./evaluate.ts";
6
6
  import { type MachinePart, mergeMachines } from "./merge.ts";
7
7
  import { type Domain, type ValueType, HEADER_KEYS, STD_DOMAINS, callable, fieldOf, fits, fnOf, holdsEnvelope, isPrecise, isUnknown, itemOf, join, member, stepType, typeText } from "./types.ts";
8
8
  import type { TypeInput, TypeRule } from "./typing.ts";
@@ -697,8 +697,9 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
697
697
 
698
698
  return {
699
699
  "formula.lit": compiled(({ doc }) => {
700
+ // A literal cannot fail, thus its code needs no place (PLAN.TOTAL).
700
701
  const result = ok(doc.value);
701
- return () => result;
702
+ return totalCode(() => result);
702
703
  }),
703
704
 
704
705
  "formula.ref": compiled(({ doc, parts, fill, address, machine }) => {
@@ -718,7 +719,7 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
718
719
  // A step document without parts is its own value. A step document with parts gets the result of each part.
719
720
  if (parts.length === 0) {
720
721
  const same = ok(doc);
721
- return () => same;
722
+ return totalCode(() => same);
722
723
  }
723
724
  return (ctx, rt) => {
724
725
  const values = valuesOf(parts, ctx, rt);
package/src/generate.ts CHANGED
@@ -146,9 +146,11 @@ export function generate(world: World, options: GenerateOptions): unknown {
146
146
  const keys = options.keys ?? KEYS;
147
147
  const steps = options.steps ?? 2;
148
148
  const roots: readonly Root[] = options.roots ?? ROOTS;
149
+ // A kind with the verb step walks a step document. With a default of step, each other kind walks one too (GEN.STEPS).
150
+ const defaultStep = Object.hasOwn(world.defaults, "step");
149
151
  const stepTypes = types.filter((type) => {
150
152
  const found = lookupKind(world, type);
151
- return found.ok && Object.hasOwn(found.kind.verbs, "step");
153
+ return found.ok && (defaultStep || Object.hasOwn(found.kind.verbs, "step"));
152
154
  });
153
155
  // A name under each namespace with a fallback that no evaluator knows, for example ["ops", "nope"].
154
156
  const unknownNames: readonly (readonly Key[])[] = Object.keys(world.kinds)
@@ -205,7 +207,10 @@ export function generate(world: World, options: GenerateOptions): unknown {
205
207
  return out;
206
208
  };
207
209
 
208
- /** A path from a root (D10). It uses the names in scope, and sometimes a step document. */
210
+ /**
211
+ * A path from a root (D10). It uses the names in scope, and sometimes a step document. The kind of a step document
212
+ * has the verb `step`, or the default of the world for `step` serves it.
213
+ */
209
214
  const pathFor = (pos: Position, refKind: Kind | undefined): Json => {
210
215
  if (rng.chance(malformed)) return rng.pick<Json>(["names", [], { names: "a" }, ["nowhere", "a"]]);
211
216
  const others = roots.filter((name) => name !== "names");
package/src/index.ts CHANGED
@@ -134,6 +134,7 @@ export {
134
134
  type Part,
135
135
  type Perform,
136
136
  type Plan,
137
+ type PlanRunOptions,
137
138
  type PrepareOptions,
138
139
  type Read,
139
140
  type Run,