@mark1russell7/coeval 0.7.0 → 0.7.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/README.md +31 -0
- package/build/index.d.ts +225 -5
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1038 -27
- package/build/index.js.map +1 -1
- package/package.json +1 -1
- package/src/codec.ts +185 -0
- package/src/conform.ts +1 -1
- package/src/evaluate.ts +8 -2
- package/src/formula.ts +19 -8
- package/src/index.ts +19 -1
- package/src/reference.ts +4 -2
- package/src/syntax.ts +846 -0
- package/src/types.ts +28 -1
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.7.
|
|
4
|
+
"version": "0.7.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/codec.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { type Json, isEnvelope, isRecord } from "./doc.ts";
|
|
2
|
+
import { type Result, describe, fail, ok } from "./errors.ts";
|
|
3
|
+
import { type Machine, makeContext, prepare } from "./evaluate.ts";
|
|
4
|
+
import { type Domain, type DomainCodec, domainOf, member } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* The codec of the values that JSON cannot write (K140, D40). A document holds such a value as a standard extension:
|
|
8
|
+
* `ext.number` for `NaN`, an infinity and `-0`, `ext.undefined` for `undefined`, and `ext.domain` for a member of a
|
|
9
|
+
* domain with a codec. Thus the same value has the same document in each language (D12).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** The domains of a machine, by their names. */
|
|
13
|
+
type Domains = Readonly<Record<string, Domain>> | undefined;
|
|
14
|
+
|
|
15
|
+
/** This function gives the codec of a domain, or `undefined` when the domain has no codec. */
|
|
16
|
+
function codecOf(domain: Domain): DomainCodec | undefined {
|
|
17
|
+
const codec: unknown = domain.codec;
|
|
18
|
+
if (typeof codec !== "object" || codec === null) return undefined;
|
|
19
|
+
const { encode, decode } = codec as Partial<DomainCodec>;
|
|
20
|
+
return typeof encode === "function" && typeof decode === "function" ? (codec as DomainCodec) : undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* This function reads the value of an extension `ext.domain` (EXT.DOMAIN). The value is a record with only the text
|
|
25
|
+
* `domain` and the JSON `data`. The codec of the named domain reads the data, and the result must be a member of the
|
|
26
|
+
* domain.
|
|
27
|
+
*
|
|
28
|
+
* @param data - The value of the extension document.
|
|
29
|
+
* @param domains - The domains of the machine.
|
|
30
|
+
* @returns The member, `#NAME?` for a domain without a codec, or `#VALUE!` for data that the codec cannot read.
|
|
31
|
+
*/
|
|
32
|
+
export function decodeDomain(data: Json, domains: Domains): Result {
|
|
33
|
+
if (!isRecord(data) || typeof data["domain"] !== "string" || !Object.hasOwn(data, "data") || Object.keys(data).length !== 2) {
|
|
34
|
+
return fail("#VALUE!", "The value of ext.domain is a record with only the text domain and the data.");
|
|
35
|
+
}
|
|
36
|
+
const name = data["domain"];
|
|
37
|
+
const domain = domainOf(name, domains);
|
|
38
|
+
if (domain === undefined) return fail("#NAME?", `The machine has no domain ${name}.`);
|
|
39
|
+
const codec = codecOf(domain);
|
|
40
|
+
if (codec === undefined) return fail("#NAME?", `The domain ${name} has no codec.`);
|
|
41
|
+
let value: unknown;
|
|
42
|
+
try {
|
|
43
|
+
value = codec.decode(data["data"] as Json);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
return fail("#VALUE!", `The codec of the domain ${name} cannot read the data: ${describe(error)}`, { cause: error });
|
|
46
|
+
}
|
|
47
|
+
if (!member(value, name, domains)) return fail("#VALUE!", `The codec of the domain ${name} gives a value that is not a member of the domain.`);
|
|
48
|
+
return ok(value);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* This function gives the static type of an extension `ext.domain`: the name of its domain when that domain has a
|
|
53
|
+
* codec. A run then gives a member of the domain or an error, because `decodeDomain` tests the membership.
|
|
54
|
+
*
|
|
55
|
+
* @param data - The value of the extension document.
|
|
56
|
+
* @param domains - The domains of the machine.
|
|
57
|
+
* @returns The name of the domain, or `unknown`.
|
|
58
|
+
*/
|
|
59
|
+
export function domainTypeOf(data: unknown, domains: Domains): string {
|
|
60
|
+
if (!isRecord(data) || typeof data["domain"] !== "string") return "unknown";
|
|
61
|
+
const domain = domainOf(data["domain"], domains);
|
|
62
|
+
return domain !== undefined && codecOf(domain) !== undefined ? data["domain"] : "unknown";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** This function sets an own data property. Only the key `__proto__` needs `defineProperty`. */
|
|
66
|
+
function setOwn(target: Record<string, Json>, key: string, value: Json): void {
|
|
67
|
+
if (key === "__proto__") Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
|
|
68
|
+
else target[key] = value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The state of one call of `encodeValue`. */
|
|
72
|
+
interface Encoder {
|
|
73
|
+
readonly domains: Domains;
|
|
74
|
+
/** The domains with a codec, in the order of their names. */
|
|
75
|
+
readonly codecs: readonly (readonly [string, DomainCodec])[];
|
|
76
|
+
/** The answer of `isJson` for each container that it examined. */
|
|
77
|
+
readonly json: WeakMap<object, boolean>;
|
|
78
|
+
/** The containers on the way from the root to the value. A container on the way again is a cycle. */
|
|
79
|
+
readonly open: Set<object>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* This function tells if JSON writes a value exactly. Such a value is a finite number other than `-0`, a text, a truth
|
|
84
|
+
* value, `null`, or a container of such values.
|
|
85
|
+
*/
|
|
86
|
+
function isJson(value: unknown, encoder: Encoder): boolean {
|
|
87
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
88
|
+
if (typeof value === "number") return Number.isFinite(value) && !Object.is(value, -0);
|
|
89
|
+
const list = Array.isArray(value);
|
|
90
|
+
if (!list && !isRecord(value)) return false;
|
|
91
|
+
const container = value as object;
|
|
92
|
+
const known = encoder.json.get(container);
|
|
93
|
+
if (known !== undefined) return known;
|
|
94
|
+
// A cycle is not JSON. The mark stays false until each item is JSON.
|
|
95
|
+
encoder.json.set(container, false);
|
|
96
|
+
let all = true;
|
|
97
|
+
if (list) {
|
|
98
|
+
const items = value as readonly unknown[];
|
|
99
|
+
for (let index = 0; index < items.length && all; index += 1) all = isJson(items[index], encoder);
|
|
100
|
+
} else {
|
|
101
|
+
const record = value as Readonly<Record<string, unknown>>;
|
|
102
|
+
for (const key of Object.keys(record)) {
|
|
103
|
+
if (!isJson(record[key], encoder)) {
|
|
104
|
+
all = false;
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
encoder.json.set(container, all);
|
|
110
|
+
return all;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** This function gives the text of a number that JSON cannot write, for `ext.number`. */
|
|
114
|
+
function numberText(value: number): string {
|
|
115
|
+
if (Number.isNaN(value)) return "NaN";
|
|
116
|
+
if (value === Number.POSITIVE_INFINITY) return "Infinity";
|
|
117
|
+
if (value === Number.NEGATIVE_INFINITY) return "-Infinity";
|
|
118
|
+
return "-0";
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** This function gives the term document of a value in one call of `encodeValue`. */
|
|
122
|
+
function encodeTerm(value: unknown, encoder: Encoder): Json {
|
|
123
|
+
if (isJson(value, encoder)) return isEnvelope(value) ? { type: "lit", value: value as Json } : (value as Json);
|
|
124
|
+
if (value === undefined) return { type: ["ext", "undefined"], value: null };
|
|
125
|
+
if (typeof value === "number") return { type: ["ext", "number"], value: numberText(value) };
|
|
126
|
+
for (const [name, codec] of encoder.codecs) {
|
|
127
|
+
if (member(value, name, encoder.domains)) return { type: ["ext", "domain"], value: { domain: name, data: codec.encode(value) } };
|
|
128
|
+
}
|
|
129
|
+
if (!Array.isArray(value) && !isRecord(value)) {
|
|
130
|
+
throw new TypeError(`encodeValue cannot write the value ${describe(value)}: no domain of the machine with a codec holds it.`);
|
|
131
|
+
}
|
|
132
|
+
if (encoder.open.has(value)) throw new TypeError("encodeValue cannot write a value with a cycle.");
|
|
133
|
+
encoder.open.add(value);
|
|
134
|
+
let out: Json;
|
|
135
|
+
if (Array.isArray(value)) {
|
|
136
|
+
// `Array.from` visits each hole of a sparse list as `undefined`.
|
|
137
|
+
out = { type: "list", value: Array.from(value as readonly unknown[], (item) => encodeTerm(item, encoder)) };
|
|
138
|
+
} else {
|
|
139
|
+
const fields: Record<string, Json> = {};
|
|
140
|
+
const record = value as Readonly<Record<string, unknown>>;
|
|
141
|
+
for (const key of Object.keys(record)) setOwn(fields, key, encodeTerm(record[key], encoder));
|
|
142
|
+
out = { type: "record", value: fields };
|
|
143
|
+
}
|
|
144
|
+
encoder.open.delete(value);
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* This function writes a host value as a term document of the formula kit (EXT.ENCODE, D40). Thus the same value has
|
|
150
|
+
* the same document in each language (D12).
|
|
151
|
+
*
|
|
152
|
+
* - A value that JSON writes exactly stays as it is. A literal with the shape of an envelope goes into `lit`.
|
|
153
|
+
* - `NaN`, an infinity and `-0` become `ext.number`, and `undefined` becomes `ext.undefined`.
|
|
154
|
+
* - A member of a domain with a codec becomes `ext.domain`. The domains go in the order of their names.
|
|
155
|
+
* - A list or a plain record with such a value becomes the construct `list` or `record`.
|
|
156
|
+
*
|
|
157
|
+
* @param machine - The machine, or only its domains.
|
|
158
|
+
* @param value - The host value.
|
|
159
|
+
* @returns The document. `decodeValue` gives the value again (EXT.ROUNDTRIP).
|
|
160
|
+
* @throws {TypeError} The value has a cycle, or it is not JSON and no domain with a codec holds it.
|
|
161
|
+
*/
|
|
162
|
+
export function encodeValue(machine: Pick<Machine, "domains">, value: unknown): Json {
|
|
163
|
+
const domains = machine.domains;
|
|
164
|
+
const codecs: (readonly [string, DomainCodec])[] = [];
|
|
165
|
+
if (domains !== undefined) {
|
|
166
|
+
for (const name of Object.keys(domains).sort()) {
|
|
167
|
+
const domain = domainOf(name, domains);
|
|
168
|
+
const codec = domain === undefined ? undefined : codecOf(domain);
|
|
169
|
+
if (codec !== undefined) codecs.push([name, codec]);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return encodeTerm(value, { domains, codecs, json: new WeakMap(), open: new Set() });
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* This function reads a document of `encodeValue` again: it evaluates the document as a term, in an empty context.
|
|
177
|
+
* A bare root value is a literal.
|
|
178
|
+
*
|
|
179
|
+
* @param machine - A machine of the formula kit, with the domains of the document.
|
|
180
|
+
* @param doc - The document.
|
|
181
|
+
* @returns The value, or the error of the evaluation.
|
|
182
|
+
*/
|
|
183
|
+
export function decodeValue(machine: Machine, doc: unknown): Result {
|
|
184
|
+
return prepare(machine, doc, { declaredType: "lit" }).evaluate(makeContext());
|
|
185
|
+
}
|
package/src/conform.ts
CHANGED
|
@@ -692,7 +692,7 @@ function finish(shot: Shot, start: () => Result, who: string): Result {
|
|
|
692
692
|
*/
|
|
693
693
|
function instanceOf(env: Env, under: Under, doc: Envelope, split: SplitOk, sample: Sample): Instance {
|
|
694
694
|
const fill = (items: readonly unknown[]): unknown => (split.parts.length === 0 ? doc.value : split.fill(items));
|
|
695
|
-
const base = { kind: sample.type, typePath: sample.typePath, doc, data: split.data, place: PLACE, fill };
|
|
695
|
+
const base = { kind: sample.type, typePath: sample.typePath, doc, data: split.data, place: PLACE, machine: env.machine, fill };
|
|
696
696
|
const who = `the ${env.runner ? "runner" : "evaluator"} ${env.name}`;
|
|
697
697
|
if (under.form === "eval" || under.form === "run") {
|
|
698
698
|
if (under.form === "eval") {
|
package/src/evaluate.ts
CHANGED
|
@@ -523,6 +523,8 @@ export interface EvalInput {
|
|
|
523
523
|
readonly context: Context;
|
|
524
524
|
/** The keys from the root document to this document. */
|
|
525
525
|
readonly place: readonly Key[];
|
|
526
|
+
/** The machine of the evaluation, for example for its domains (D40). The kernel sets it. */
|
|
527
|
+
readonly machine?: Machine;
|
|
526
528
|
/** `true` when the position of the document declares an address. */
|
|
527
529
|
readonly address?: boolean;
|
|
528
530
|
/**
|
|
@@ -580,6 +582,8 @@ export interface RunInput {
|
|
|
580
582
|
readonly parts: readonly RunPart[];
|
|
581
583
|
readonly context: Context;
|
|
582
584
|
readonly place: readonly Key[];
|
|
585
|
+
/** The machine of the evaluation, for example for its domains (D40). The kernel sets it. */
|
|
586
|
+
readonly machine?: Machine;
|
|
583
587
|
resolve(path: readonly unknown[], options?: ResolveOptions): Resolution;
|
|
584
588
|
/**
|
|
585
589
|
* This function causes an effect. In a run, the host does the effect, and the steps wait when the host waits. In
|
|
@@ -1534,7 +1538,7 @@ function fillOf(split: Extract<Split, { readonly ok: true }>, doc: Envelope): <P
|
|
|
1534
1538
|
|
|
1535
1539
|
|
|
1536
1540
|
function interpret(fn: EvalFn, input: CompileInput, fill: <P>(handles: readonly P[]) => unknown): Code {
|
|
1537
|
-
const { kind, typePath, doc, data, place } = input;
|
|
1541
|
+
const { kind, typePath, doc, data, place, machine } = input;
|
|
1538
1542
|
const address = input.address === true ? { address: true } : {};
|
|
1539
1543
|
// The code keeps the fill and not the whole input, thus it does not keep the compiled value of the document (PLAN.LEAN).
|
|
1540
1544
|
const refill = input.fill;
|
|
@@ -1556,6 +1560,7 @@ function interpret(fn: EvalFn, input: CompileInput, fill: <P>(handles: readonly
|
|
|
1556
1560
|
parts,
|
|
1557
1561
|
context: ctx,
|
|
1558
1562
|
place,
|
|
1563
|
+
machine,
|
|
1559
1564
|
...address,
|
|
1560
1565
|
resolve: (path, options) => rt.resolve(ctx, path, options),
|
|
1561
1566
|
effect: (effectKind, payload) => rt.effect(ctx, effectKind, payload, place),
|
|
@@ -1565,7 +1570,7 @@ function interpret(fn: EvalFn, input: CompileInput, fill: <P>(handles: readonly
|
|
|
1565
1570
|
}
|
|
1566
1571
|
|
|
1567
1572
|
function interpretRun(fn: RunFn, input: CompileInput, fill: <P>(handles: readonly P[]) => unknown): RunCode {
|
|
1568
|
-
const { kind, typePath, doc, data, place } = input;
|
|
1573
|
+
const { kind, typePath, doc, data, place, machine } = input;
|
|
1569
1574
|
const refill = input.fill;
|
|
1570
1575
|
const compiled = input.parts;
|
|
1571
1576
|
const hasParts = compiled.length > 0;
|
|
@@ -1585,6 +1590,7 @@ function interpretRun(fn: RunFn, input: CompileInput, fill: <P>(handles: readonl
|
|
|
1585
1590
|
parts,
|
|
1586
1591
|
context: ctx,
|
|
1587
1592
|
place,
|
|
1593
|
+
machine,
|
|
1588
1594
|
resolve: (path, options) => rt.resolve(ctx, path, options),
|
|
1589
1595
|
effect: (effectKind, payload) => rt.runEffect(ctx, effectKind, payload, place),
|
|
1590
1596
|
all: (list, options) => rt.all(list.map((part) => () => part.evaluate()), options),
|
package/src/formula.ts
CHANGED
|
@@ -4,8 +4,9 @@ import { type CompiledPath, type Context, type Resolution, compilePath, valueOfT
|
|
|
4
4
|
import { type EvalError, type Failed, type Result, Failure, fail, ok, thrown } from "./errors.ts";
|
|
5
5
|
import { type Code, type CompileInput, type CompiledEvaluator, type CompiledPart, type Evaluator, type Machine, type Runtime, resolveCompiled } from "./evaluate.ts";
|
|
6
6
|
import { type MachinePart, mergeMachines } from "./merge.ts";
|
|
7
|
-
import { type ValueType, HEADER_KEYS, STD_DOMAINS, callable, fieldOf, fits, fnOf, holdsEnvelope, isPrecise, isUnknown, itemOf, join, stepType, typeText } from "./types.ts";
|
|
7
|
+
import { type Domain, type ValueType, HEADER_KEYS, STD_DOMAINS, callable, fieldOf, fits, fnOf, holdsEnvelope, isPrecise, isUnknown, itemOf, join, stepType, typeText } from "./types.ts";
|
|
8
8
|
import type { TypeInput, TypeRule } from "./typing.ts";
|
|
9
|
+
import { decodeDomain, domainTypeOf } from "./codec.ts";
|
|
9
10
|
|
|
10
11
|
/** An operation of a formula language: a function of the values of its arguments. */
|
|
11
12
|
export type OpFn = (...args: never[]) => unknown;
|
|
@@ -16,15 +17,19 @@ export type OpFn = (...args: never[]) => unknown;
|
|
|
16
17
|
*/
|
|
17
18
|
export type LazyOp = (args: readonly (() => Result)[]) => Result;
|
|
18
19
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The code of an extension: it gives the value of the data of an extension document, in the context of the document.
|
|
22
|
+
* It also gets the domains of the machine, for the codecs of `ext.domain` (D40).
|
|
23
|
+
*/
|
|
24
|
+
export type ExtFn = (data: Json, context: Context, domains?: Readonly<Record<string, Domain>>) => Result;
|
|
21
25
|
|
|
22
26
|
/**
|
|
23
27
|
* The rules of one formula language: its operations, and the rules that differ between languages.
|
|
24
28
|
*
|
|
25
29
|
* - `ops`: the operations of the namespace `ops`, by their name (the keys after `ops`, with dots between them).
|
|
26
30
|
* - `lazy`: the operations that evaluate their arguments themselves.
|
|
27
|
-
* - `ext`: the extensions of the namespace `ext`, by their name. The kit has `ext.undefined
|
|
31
|
+
* - `ext`: the extensions of the namespace `ext`, by their name. The kit has `ext.undefined`, `ext.number` and
|
|
32
|
+
* `ext.domain`.
|
|
28
33
|
* - `truthy`: the truth of a value for `if`, `and` and `or`. `undefined` is an error `#VALUE!`. The default
|
|
29
34
|
* accepts only `true` and `false`.
|
|
30
35
|
* - `otherwise`: the value of an `if` without `else`. The default is `null`. A profile with this key gives its value,
|
|
@@ -97,7 +102,10 @@ export function isClosure(value: unknown): value is Closure {
|
|
|
97
102
|
return typeof value === "function" && (value as Partial<Closure>)[CLOSURE] !== undefined;
|
|
98
103
|
}
|
|
99
104
|
|
|
100
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* The standard extensions: the values that JSON cannot write. `ext.domain` reads a member of a domain through the
|
|
107
|
+
* codec of the domain (EXT.DOMAIN, D40).
|
|
108
|
+
*/
|
|
101
109
|
export const STD_EXT: Readonly<Record<string, ExtFn>> = {
|
|
102
110
|
undefined: () => ok(undefined),
|
|
103
111
|
number: (data) => {
|
|
@@ -107,6 +115,7 @@ export const STD_EXT: Readonly<Record<string, ExtFn>> = {
|
|
|
107
115
|
if (data === "-0") return ok(-0);
|
|
108
116
|
return fail("#VALUE!", "The value of ext.number is NaN, Infinity, -Infinity or -0.");
|
|
109
117
|
},
|
|
118
|
+
domain: (data, _context, domains) => decodeDomain(data, domains),
|
|
110
119
|
};
|
|
111
120
|
|
|
112
121
|
/** The truth of a value in the rules of JavaScript: `0`, `""`, `null`, `undefined` and `NaN` are false. */
|
|
@@ -477,7 +486,9 @@ function formulaTypes(profile: FormulaProfile): Readonly<Record<string, TypeRule
|
|
|
477
486
|
synth: (input) => {
|
|
478
487
|
const name = nameOf(input);
|
|
479
488
|
const own = profile.ext !== undefined && Object.hasOwn(profile.ext, name);
|
|
480
|
-
|
|
489
|
+
if (own) return "unknown";
|
|
490
|
+
if (name === "number") return "number";
|
|
491
|
+
return name === "domain" ? domainTypeOf(input.doc.value, input.machine.domains) : "unknown";
|
|
481
492
|
},
|
|
482
493
|
},
|
|
483
494
|
};
|
|
@@ -811,7 +822,7 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
|
|
|
811
822
|
};
|
|
812
823
|
}),
|
|
813
824
|
|
|
814
|
-
"formula.ext": compiled(({ typePath, doc }) => {
|
|
825
|
+
"formula.ext": compiled(({ typePath, doc, machine }) => {
|
|
815
826
|
const name = typePath.slice(1).join(".");
|
|
816
827
|
const own = profile.ext !== undefined && Object.hasOwn(profile.ext, name) ? profile.ext[name] : undefined;
|
|
817
828
|
const handler = own ?? (Object.hasOwn(STD_EXT, name) ? STD_EXT[name] : undefined);
|
|
@@ -821,7 +832,7 @@ function formulaCode(profile: FormulaProfile): Readonly<Record<string, CompiledE
|
|
|
821
832
|
}
|
|
822
833
|
return (ctx) => {
|
|
823
834
|
try {
|
|
824
|
-
return handler(doc.value, ctx);
|
|
835
|
+
return handler(doc.value, ctx, machine.domains);
|
|
825
836
|
} catch (error) {
|
|
826
837
|
return thrown(error, `the extension ${name}`);
|
|
827
838
|
}
|
package/src/index.ts
CHANGED
|
@@ -211,6 +211,24 @@ export {
|
|
|
211
211
|
isClosure,
|
|
212
212
|
jsTruthy,
|
|
213
213
|
} from "./formula.ts";
|
|
214
|
+
export { decodeDomain, decodeValue, encodeValue } from "./codec.ts";
|
|
215
|
+
export {
|
|
216
|
+
type ParseResult,
|
|
217
|
+
type SyntaxAssoc,
|
|
218
|
+
type SyntaxCalls,
|
|
219
|
+
type SyntaxFixity,
|
|
220
|
+
type SyntaxForm,
|
|
221
|
+
type SyntaxIssue,
|
|
222
|
+
type SyntaxLiterals,
|
|
223
|
+
type SyntaxOperator,
|
|
224
|
+
type SyntaxProfile,
|
|
225
|
+
type SyntaxRefPattern,
|
|
226
|
+
type SyntaxRefs,
|
|
227
|
+
STD_SYNTAX,
|
|
228
|
+
parse,
|
|
229
|
+
print,
|
|
230
|
+
syntaxFormat,
|
|
231
|
+
} from "./syntax.ts";
|
|
214
232
|
export { type Definitions, type ReferenceOptions, STD_DEFINITIONS, formulaDefinitions, reference } from "./reference.ts";
|
|
215
233
|
export { type AgreeOptions, type Agreement, type Tier, type TierInput, type TierResult, agree, sameValue } from "./agree.ts";
|
|
216
234
|
export { type ConformOptions, type ConformProbe, type Finding, type Obligation, conforms } from "./conform.ts";
|
|
@@ -230,6 +248,6 @@ export {
|
|
|
230
248
|
export { type GraphCycle, type GraphEdge, type GraphNode, type GraphOptions, type NodeGraph, components, cycles, heights, necessary, nodeLayers } from "./graph.ts";
|
|
231
249
|
export { type Recorder, type ReplayEntry, type ReplayLog, type ReplayOptions, type ReplaySettle, recorder, replayer } from "./replay.ts";
|
|
232
250
|
export { type GenerateOptions, type Random, generate, random, typesOf } from "./generate.ts";
|
|
233
|
-
export { type Domain, type TypeIssue, type ValueType, STD_DOMAINS, fits, join, member, typeIssues, typeText } from "./types.ts";
|
|
251
|
+
export { type Domain, type DomainCodec, type TypeIssue, type ValueType, STD_DOMAINS, fits, join, member, typeIssues, typeText } from "./types.ts";
|
|
234
252
|
export { type PartType, type RootTypes, type TypeInput, type TypeProblem, type TypeRule, type Typed, CAPS_TYPE } from "./typing.ts";
|
|
235
253
|
export { type SampleOptions, type TypeReading, typeFromSamples, typeFromSchema } from "./providers.ts";
|
package/src/reference.ts
CHANGED
|
@@ -307,6 +307,7 @@ function evalInput(env: Env, seen: Seen, at: At): EvalInput {
|
|
|
307
307
|
parts,
|
|
308
308
|
context: at.ctx,
|
|
309
309
|
place: at.place,
|
|
310
|
+
machine: env.machine,
|
|
310
311
|
...(at.address ? { address: true } : {}),
|
|
311
312
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
312
313
|
effect: (kind, payload) => cause(env, at.ctx, kind, payload, at.place),
|
|
@@ -335,6 +336,7 @@ function runInput(env: Env, seen: Seen, at: At): RunInput {
|
|
|
335
336
|
parts,
|
|
336
337
|
context: at.ctx,
|
|
337
338
|
place: at.place,
|
|
339
|
+
machine: env.machine,
|
|
338
340
|
resolve: (path, options) => resolveFully(env.machine, at.ctx, path, options),
|
|
339
341
|
effect: function* (kind, payload) {
|
|
340
342
|
return cause(env, at.ctx, kind, payload, at.place);
|
|
@@ -748,12 +750,12 @@ export function formulaDefinitions(profile: FormulaProfile = {}): Definitions {
|
|
|
748
750
|
}
|
|
749
751
|
};
|
|
750
752
|
|
|
751
|
-
const ext: EvalFn = ({ typePath, doc, context }) => {
|
|
753
|
+
const ext: EvalFn = ({ typePath, doc, context, machine }) => {
|
|
752
754
|
const name = typePath.slice(1).join(".");
|
|
753
755
|
const handler = own(profile.ext, name) ?? own(STD_EXT, name);
|
|
754
756
|
if (handler === undefined) return fail("#NAME?", `The language has no extension ${name}.`);
|
|
755
757
|
try {
|
|
756
|
-
return handler(doc.value, context);
|
|
758
|
+
return handler(doc.value, context, machine?.domains);
|
|
757
759
|
} catch (error) {
|
|
758
760
|
return thrown(error, `the extension ${name}`);
|
|
759
761
|
}
|