@calcit/procs 0.29.0-alpha.2 → 0.29.0-alpha.20
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/lib/calcit-data.mjs +14 -5
- package/lib/calcit.procs.d.mts +5 -2
- package/lib/calcit.procs.mjs +24 -5
- package/lib/js-primes.d.mts +2 -0
- package/lib/js-primes.mjs +88 -1
- package/lib/js-ref.d.mts +3 -0
- package/lib/js-ref.mjs +4 -1
- package/lib/js-struct-def.d.mts +1 -1
- package/lib/js-struct-def.mjs +8 -1
- package/lib/js-struct-value.mjs +12 -0
- package/lib/js-type-form.d.mts +2 -0
- package/lib/js-type-form.mjs +167 -0
- package/lib/package.json +4 -3
- package/package.json +4 -3
package/lib/calcit-data.mjs
CHANGED
|
@@ -208,6 +208,7 @@ export var refsRegistry = new Map();
|
|
|
208
208
|
let defaultHash_nil = valueHash("nil:");
|
|
209
209
|
let defaultHash_unit = valueHash("unit:");
|
|
210
210
|
let defaultHash_number = valueHash("number:");
|
|
211
|
+
let defaultHash_nan = valueHash("number:NaN");
|
|
211
212
|
let defaultHash_string = valueHash("string:");
|
|
212
213
|
let defaultHash_tag = valueHash("tag:");
|
|
213
214
|
let defaultHash_true = valueHash("bool:true");
|
|
@@ -237,7 +238,10 @@ export let hashFunction = (x) => {
|
|
|
237
238
|
if (x === undefined)
|
|
238
239
|
return defaultHash_unit;
|
|
239
240
|
if (typeof x === "number") {
|
|
240
|
-
|
|
241
|
+
// NaN equals NaN, so it needs a hash that equals itself; `0` and `-0` share one hash
|
|
242
|
+
if (Number.isNaN(x))
|
|
243
|
+
return defaultHash_nan;
|
|
244
|
+
return mergeValueHash(defaultHash_number, x === 0 ? 0 : x);
|
|
241
245
|
}
|
|
242
246
|
if (typeof x === "string") {
|
|
243
247
|
return mergeValueHash(defaultHash_string, x);
|
|
@@ -369,7 +373,8 @@ export let hashFunction = (x) => {
|
|
|
369
373
|
let base = defaultHash_impl;
|
|
370
374
|
base = mergeValueHash(base, hashFunction(x.name));
|
|
371
375
|
if (x.origin != null) {
|
|
372
|
-
|
|
376
|
+
// Impl equality compares origin names, not trait object identities.
|
|
377
|
+
base = mergeValueHash(base, hashFunction(x.origin.name.value));
|
|
373
378
|
}
|
|
374
379
|
for (let idx = 0; idx < x.fields.length; idx++) {
|
|
375
380
|
base = mergeValueHash(base, hashFunction(x.fields[idx]));
|
|
@@ -656,8 +661,8 @@ export let _$n__$e_ = (x, y) => {
|
|
|
656
661
|
return false;
|
|
657
662
|
}
|
|
658
663
|
if (tx === "number") {
|
|
659
|
-
// already
|
|
660
|
-
return
|
|
664
|
+
// value equality: NaN equals NaN (`0 === -0` already held above)
|
|
665
|
+
return Number.isNaN(x) && Number.isNaN(y);
|
|
661
666
|
}
|
|
662
667
|
if (tx === "function") {
|
|
663
668
|
// method values are closures created on the fly (see invoke_method_closure),
|
|
@@ -837,7 +842,11 @@ export let _$n__$e_ = (x, y) => {
|
|
|
837
842
|
}
|
|
838
843
|
if (x instanceof CalcitEnumDef) {
|
|
839
844
|
if (y instanceof CalcitEnumDef) {
|
|
840
|
-
|
|
845
|
+
// The prototype carries the nominal origin, variant tags and payload
|
|
846
|
+
// schemas. Comparing name methods compares one shared function object.
|
|
847
|
+
return _$n__$e_(x.prototype, y.prototype)
|
|
848
|
+
&& x.impls.length === y.impls.length
|
|
849
|
+
&& x.impls.every((impl, index) => _$n__$e_(impl, y.impls[index]));
|
|
841
850
|
}
|
|
842
851
|
return false;
|
|
843
852
|
}
|
package/lib/calcit.procs.d.mts
CHANGED
|
@@ -31,6 +31,7 @@ export declare const calcit_package_json: {
|
|
|
31
31
|
"check-smooth": string;
|
|
32
32
|
"check-all": string;
|
|
33
33
|
"check-strict-default": string;
|
|
34
|
+
"check-core-tests": string;
|
|
34
35
|
"check-agent-interface": string;
|
|
35
36
|
"try-all": string;
|
|
36
37
|
"try-core-tests": string;
|
|
@@ -111,7 +112,7 @@ export declare let _$L_: (...xs: CalcitValue[]) => CalcitSliceList;
|
|
|
111
112
|
export declare let _SQUO_: (...xs: CalcitValue[]) => CalcitSliceList;
|
|
112
113
|
export declare let _$n__$M_: (...xs: CalcitValue[]) => CalcitSliceMap;
|
|
113
114
|
export declare let defatom: (path: string, x: CalcitValue) => CalcitValue;
|
|
114
|
-
export { atom } from "./js-ref.mjs";
|
|
115
|
+
export { atom, ref } from "./js-ref.mjs";
|
|
115
116
|
export declare let peekDefatom: (path: string) => CalcitRef;
|
|
116
117
|
export declare let _$n_atom_$o_deref: (x: CalcitRef) => CalcitValue;
|
|
117
118
|
export declare let _$n__ADD_: (x: number, y: number) => number;
|
|
@@ -188,7 +189,8 @@ export declare let _$n__GT_: (a: number, b: number) => boolean;
|
|
|
188
189
|
export declare let _$n__LT_: (a: number, b: number) => boolean;
|
|
189
190
|
export declare let _$n__: (a: number, b: number) => number;
|
|
190
191
|
export declare let _$n__SLSH_: (a: number, b: number) => number;
|
|
191
|
-
/** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`.
|
|
192
|
+
/** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`.
|
|
193
|
+
* Operands in the error use the native number text (`inf`, expanded exponents) so messages match native. */
|
|
192
194
|
export declare let _$n_number_$o_rem: (a: number, b: number) => number;
|
|
193
195
|
export declare let _$n_number_$o_fits_$q_: (value: number, target: CalcitTag) => boolean;
|
|
194
196
|
export declare let round_$q_: (a: number) => boolean;
|
|
@@ -361,6 +363,7 @@ export declare function _$n_methods_of(obj: CalcitValue): CalcitSliceList;
|
|
|
361
363
|
export declare function _$n_inspect_methods(obj: CalcitValue, note: CalcitValue): CalcitValue;
|
|
362
364
|
/** Dispatch through one nominal origin; duplicate implementations never select by order. */
|
|
363
365
|
export declare function _$n_trait_call(traitDef: CalcitValue, method: CalcitValue, obj: CalcitValue, ...args: CalcitValue[]): CalcitValue;
|
|
366
|
+
export declare let _$n_map_$o_fold_kv: (m: CalcitValue, initial: CalcitValue, reducer: CalcitFn) => CalcitValue;
|
|
364
367
|
export declare let _$n_map_$o_to_list: (m: CalcitValue) => CalcitSliceList;
|
|
365
368
|
export declare let _$n_map_$o_keys: (m: CalcitValue) => CalcitSet;
|
|
366
369
|
export declare let _$n_map_$o_vals: (m: CalcitValue) => CalcitSliceList;
|
package/lib/calcit.procs.mjs
CHANGED
|
@@ -224,7 +224,7 @@ export let _$n_trait_$o__$o_new = function (name, methods, parents) {
|
|
|
224
224
|
const requires = parents == null ? [] : list_items(parents);
|
|
225
225
|
for (const parent of requires) {
|
|
226
226
|
if (!(parent instanceof CalcitTrait)) {
|
|
227
|
-
throw new Error(`trait ${toString(name, false)}
|
|
227
|
+
throw new Error(`trait ${toString(name, false)}: each requires entry must be a trait, but received: ${toString(parent, true)}`);
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
230
|
const traitDef = new CalcitTrait(name, methodNames, methodTypes, requires);
|
|
@@ -508,7 +508,7 @@ export let defatom = (path, x) => {
|
|
|
508
508
|
refsRegistry.set(path, v);
|
|
509
509
|
return v;
|
|
510
510
|
};
|
|
511
|
-
export { atom } from "./js-ref.mjs";
|
|
511
|
+
export { atom, ref } from "./js-ref.mjs";
|
|
512
512
|
export let peekDefatom = (path) => {
|
|
513
513
|
return refsRegistry.get(path);
|
|
514
514
|
};
|
|
@@ -1200,10 +1200,11 @@ export let _$n__ = (a, b) => {
|
|
|
1200
1200
|
export let _$n__SLSH_ = (a, b) => {
|
|
1201
1201
|
return a / b;
|
|
1202
1202
|
};
|
|
1203
|
-
/** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`.
|
|
1203
|
+
/** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`.
|
|
1204
|
+
* Operands in the error use the native number text (`inf`, expanded exponents) so messages match native. */
|
|
1204
1205
|
export let _$n_number_$o_rem = (a, b) => {
|
|
1205
1206
|
if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b)) {
|
|
1206
|
-
throw new Error(`&number:rem requires safe integers, but received: ${
|
|
1207
|
+
throw new Error(`&number:rem requires safe integers, but received: ${format_calcit_number(a)} ${format_calcit_number(b)}`);
|
|
1207
1208
|
}
|
|
1208
1209
|
if (b === 0) {
|
|
1209
1210
|
throw new Error("&number:rem divisor must not be zero");
|
|
@@ -1418,7 +1419,12 @@ export let _$n_str_$o_replace = (x, y, z) => {
|
|
|
1418
1419
|
return x.replace(new RegExp(literalPattern, "gu"), () => z);
|
|
1419
1420
|
};
|
|
1420
1421
|
export let split = (xs, x) => {
|
|
1421
|
-
|
|
1422
|
+
// An empty delimiter splits by code point, matching native for astral characters.
|
|
1423
|
+
if (x === "") {
|
|
1424
|
+
return new CalcitSliceList(Array.from(xs));
|
|
1425
|
+
}
|
|
1426
|
+
// Drop empty pieces to match native and WASM: `split | |,` is an empty list.
|
|
1427
|
+
return new CalcitSliceList(xs.split(x).filter((piece) => piece !== ""));
|
|
1422
1428
|
};
|
|
1423
1429
|
export let split_lines = (xs) => {
|
|
1424
1430
|
return new CalcitSliceList(xs.split("\n"));
|
|
@@ -2808,6 +2814,19 @@ export function _$n_trait_call(traitDef, method, obj, ...args) {
|
|
|
2808
2814
|
}
|
|
2809
2815
|
throw new Error(`&trait-call: cannot find impl for trait ${traitDef.name.toString()} on ${toString(obj, true)}. Hint: use defimpl to create impls tagged by trait.`);
|
|
2810
2816
|
}
|
|
2817
|
+
export let _$n_map_$o_fold_kv = (m, initial, reducer) => {
|
|
2818
|
+
if (!(m instanceof CalcitMap || m instanceof CalcitSliceMap)) {
|
|
2819
|
+
throw new Error("&map:fold-kv expected a Map");
|
|
2820
|
+
}
|
|
2821
|
+
if (typeof reducer !== "function") {
|
|
2822
|
+
throw new Error("&map:fold-kv expected a callback function");
|
|
2823
|
+
}
|
|
2824
|
+
let accumulator = initial;
|
|
2825
|
+
for (const [key, value] of m.pairs()) {
|
|
2826
|
+
accumulator = reducer(accumulator, key, value);
|
|
2827
|
+
}
|
|
2828
|
+
return accumulator;
|
|
2829
|
+
};
|
|
2811
2830
|
export let _$n_map_$o_to_list = (m) => {
|
|
2812
2831
|
if (m instanceof CalcitMap || m instanceof CalcitSliceMap) {
|
|
2813
2832
|
let ys = [];
|
package/lib/js-primes.d.mts
CHANGED
|
@@ -12,4 +12,6 @@ import { CalcitCirruQuote } from "./js-cirru.mjs";
|
|
|
12
12
|
import { CalcitTrait } from "./js-trait.mjs";
|
|
13
13
|
export type CalcitValue = string | number | boolean | CalcitMap | CalcitSliceMap | CalcitList | CalcitSliceList | CalcitSet | CalcitTag | CalcitSymbol | CalcitRef | CalcitEnumValue | CalcitFn | CalcitRecur | CalcitStructValue | CalcitImpl | CalcitTrait | CalcitStructDef | CalcitEnumDef | CalcitCirruQuote | Uint8Array | undefined | null;
|
|
14
14
|
export declare let isLiteral: (x: CalcitValue) => boolean;
|
|
15
|
+
/** total order over numbers: `-0` equals `0`, NaN equals NaN and sorts after every number */
|
|
16
|
+
export declare let compareNumbers: (x: number, y: number) => number;
|
|
15
17
|
export declare let _$n_compare: (a: CalcitValue, b: CalcitValue) => number;
|
package/lib/js-primes.mjs
CHANGED
|
@@ -101,6 +101,14 @@ let rawCompare = (x, y) => {
|
|
|
101
101
|
return 0;
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
|
+
/** total order over numbers: `-0` equals `0`, NaN equals NaN and sorts after every number */
|
|
105
|
+
export let compareNumbers = (x, y) => {
|
|
106
|
+
if (x < y)
|
|
107
|
+
return -1;
|
|
108
|
+
if (x > y)
|
|
109
|
+
return 1;
|
|
110
|
+
return (Number.isNaN(x) ? 1 : 0) - (Number.isNaN(y) ? 1 : 0);
|
|
111
|
+
};
|
|
104
112
|
/** lexicographic order over two sequences of values, a shorter prefix sorts first (same as Rust `Vec::cmp`) */
|
|
105
113
|
let compareSequences = (xs, ys) => {
|
|
106
114
|
let n = Math.min(xs.length, ys.length);
|
|
@@ -112,6 +120,18 @@ let compareSequences = (xs, ys) => {
|
|
|
112
120
|
return rawCompare(xs.length, ys.length);
|
|
113
121
|
};
|
|
114
122
|
let listToArray = (x) => Array.from(x.items());
|
|
123
|
+
// Plain functions compare by identity. IDs are process-local, never serialized,
|
|
124
|
+
// and kept weakly so ordering does not retain closures or mutate frozen hosts.
|
|
125
|
+
const functionOrderIds = new WeakMap();
|
|
126
|
+
let nextFunctionOrderId = 0;
|
|
127
|
+
const functionOrderId = (fn) => {
|
|
128
|
+
let id = functionOrderIds.get(fn);
|
|
129
|
+
if (id === undefined) {
|
|
130
|
+
id = ++nextFunctionOrderId;
|
|
131
|
+
functionOrderIds.set(fn, id);
|
|
132
|
+
}
|
|
133
|
+
return id;
|
|
134
|
+
};
|
|
115
135
|
export let _$n_compare = (a, b) => {
|
|
116
136
|
if (a === b)
|
|
117
137
|
return 0;
|
|
@@ -125,7 +145,7 @@ export let _$n_compare = (a, b) => {
|
|
|
125
145
|
case PseudoTypeIndex.bool:
|
|
126
146
|
return rawCompare(a, b);
|
|
127
147
|
case PseudoTypeIndex.number:
|
|
128
|
-
return
|
|
148
|
+
return compareNumbers(a, b);
|
|
129
149
|
case PseudoTypeIndex.tag:
|
|
130
150
|
return rawCompare(a.value, b.value);
|
|
131
151
|
case PseudoTypeIndex.symbol:
|
|
@@ -134,6 +154,36 @@ export let _$n_compare = (a, b) => {
|
|
|
134
154
|
return rawCompare(a, b);
|
|
135
155
|
case PseudoTypeIndex.ref:
|
|
136
156
|
return rawCompare(a.path, b.path);
|
|
157
|
+
case PseudoTypeIndex.fn: {
|
|
158
|
+
const left = a;
|
|
159
|
+
const right = b;
|
|
160
|
+
// Method closures use name equality, unlike ordinary function values.
|
|
161
|
+
if (left.__calcitMethodName != null || right.__calcitMethodName != null) {
|
|
162
|
+
if (left.__calcitMethodName == null)
|
|
163
|
+
return -1;
|
|
164
|
+
if (right.__calcitMethodName == null)
|
|
165
|
+
return 1;
|
|
166
|
+
return compareUnicodeStrings(left.__calcitMethodName, right.__calcitMethodName);
|
|
167
|
+
}
|
|
168
|
+
return rawCompare(functionOrderId(left), functionOrderId(right));
|
|
169
|
+
}
|
|
170
|
+
case PseudoTypeIndex.impl: {
|
|
171
|
+
const left = a;
|
|
172
|
+
const right = b;
|
|
173
|
+
const nameOrder = compareUnicodeStrings(left.name.value, right.name.value);
|
|
174
|
+
if (nameOrder !== 0)
|
|
175
|
+
return nameOrder;
|
|
176
|
+
if (left.origin == null && right.origin != null)
|
|
177
|
+
return -1;
|
|
178
|
+
if (left.origin != null && right.origin == null)
|
|
179
|
+
return 1;
|
|
180
|
+
if (left.origin != null && right.origin != null) {
|
|
181
|
+
const originOrder = compareUnicodeStrings(left.origin.name.value, right.origin.name.value);
|
|
182
|
+
if (originOrder !== 0)
|
|
183
|
+
return originOrder;
|
|
184
|
+
}
|
|
185
|
+
return compareSequences(left.fields, right.fields) || compareSequences(left.values, right.values);
|
|
186
|
+
}
|
|
137
187
|
case PseudoTypeIndex.struct_value: {
|
|
138
188
|
const left = a;
|
|
139
189
|
const right = b;
|
|
@@ -176,6 +226,43 @@ export let _$n_compare = (a, b) => {
|
|
|
176
226
|
return tagOrder;
|
|
177
227
|
return compareSequences(left.extra, right.extra);
|
|
178
228
|
}
|
|
229
|
+
case PseudoTypeIndex.enum_def: {
|
|
230
|
+
const left = a;
|
|
231
|
+
const right = b;
|
|
232
|
+
const leftRef = left.prototype.structRef.definitionRef;
|
|
233
|
+
const rightRef = right.prototype.structRef.definitionRef;
|
|
234
|
+
if (leftRef !== rightRef) {
|
|
235
|
+
if (leftRef == null)
|
|
236
|
+
return -1;
|
|
237
|
+
if (rightRef == null)
|
|
238
|
+
return 1;
|
|
239
|
+
return compareUnicodeStrings(leftRef, rightRef);
|
|
240
|
+
}
|
|
241
|
+
const nameOrder = compareUnicodeStrings(left.name(), right.name());
|
|
242
|
+
if (nameOrder !== 0)
|
|
243
|
+
return nameOrder;
|
|
244
|
+
const variantCountOrder = rawCompare(left.prototype.fields.length, right.prototype.fields.length);
|
|
245
|
+
if (variantCountOrder !== 0)
|
|
246
|
+
return variantCountOrder;
|
|
247
|
+
for (let index = 0; index < left.prototype.fields.length; index++) {
|
|
248
|
+
const tagOrder = _$n_compare(left.prototype.fields[index], right.prototype.fields[index]);
|
|
249
|
+
if (tagOrder !== 0)
|
|
250
|
+
return tagOrder;
|
|
251
|
+
const leftPayload = left.prototype.values[index];
|
|
252
|
+
const rightPayload = right.prototype.values[index];
|
|
253
|
+
// Native orders variant payloads by arity before comparing types.
|
|
254
|
+
if ((leftPayload instanceof CalcitList || leftPayload instanceof CalcitSliceList)
|
|
255
|
+
&& (rightPayload instanceof CalcitList || rightPayload instanceof CalcitSliceList)) {
|
|
256
|
+
const arityOrder = rawCompare(leftPayload.len(), rightPayload.len());
|
|
257
|
+
if (arityOrder !== 0)
|
|
258
|
+
return arityOrder;
|
|
259
|
+
}
|
|
260
|
+
const payloadOrder = _$n_compare(leftPayload, rightPayload);
|
|
261
|
+
if (payloadOrder !== 0)
|
|
262
|
+
return payloadOrder;
|
|
263
|
+
}
|
|
264
|
+
return rawCompare(left.impls.length, right.impls.length) || compareSequences(left.impls, right.impls);
|
|
265
|
+
}
|
|
179
266
|
case PseudoTypeIndex.set: {
|
|
180
267
|
// like native: smaller sets first, then the sorted elements
|
|
181
268
|
const left = Array.from(a.values());
|
package/lib/js-ref.d.mts
CHANGED
|
@@ -9,4 +9,7 @@ export declare class CalcitRef {
|
|
|
9
9
|
constructor(x: CalcitValue, path: string);
|
|
10
10
|
toString(): string;
|
|
11
11
|
}
|
|
12
|
+
/** Creates a local `Ref<T>`; generated code calls this for both `ref` and the compatibility spelling `atom`. */
|
|
13
|
+
export declare let ref: (x: CalcitValue) => CalcitValue;
|
|
14
|
+
/** Compatibility export for JavaScript callers written before `ref`; it is the same function. */
|
|
12
15
|
export declare let atom: (x: CalcitValue) => CalcitValue;
|
package/lib/js-ref.mjs
CHANGED
|
@@ -10,8 +10,11 @@ export class CalcitRef {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
var atomCounter = 0;
|
|
13
|
-
|
|
13
|
+
/** Creates a local `Ref<T>`; generated code calls this for both `ref` and the compatibility spelling `atom`. */
|
|
14
|
+
export let ref = (x) => {
|
|
14
15
|
atomCounter = atomCounter + 1;
|
|
15
16
|
let v = new CalcitRef(x, `atom-${atomCounter}`);
|
|
16
17
|
return v;
|
|
17
18
|
};
|
|
19
|
+
/** Compatibility export for JavaScript callers written before `ref`; it is the same function. */
|
|
20
|
+
export let atom = ref;
|
package/lib/js-struct-def.d.mts
CHANGED
|
@@ -12,5 +12,5 @@ export declare class CalcitStructDef {
|
|
|
12
12
|
withImpls(impls: CalcitImpl | CalcitImpl[]): CalcitStructDef;
|
|
13
13
|
toString(disableJsDataWarning?: boolean): string;
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
15
|
+
/** Bind nominal definitions like native, preserving an alias's existing identity. */
|
|
16
16
|
export declare function bind_struct_definition(value: CalcitValue, definitionRef: string): CalcitValue;
|
package/lib/js-struct-def.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { canonicalizeTagPairs, toString } from "./calcit-data.mjs";
|
|
2
2
|
import { CalcitImpl } from "./js-impl.mjs";
|
|
3
|
+
import { CalcitEnumDef } from "./js-enum-def.mjs";
|
|
4
|
+
import { CalcitStructValue } from "./js-struct-value.mjs";
|
|
3
5
|
export class CalcitStructDef {
|
|
4
6
|
constructor(name, fields, fieldTypes, impls = [], definitionRef = null) {
|
|
5
7
|
const [canonicalFields, canonicalTypes] = canonicalizeTagPairs(fields, fieldTypes, "CalcitStructDef");
|
|
@@ -33,10 +35,15 @@ export class CalcitStructDef {
|
|
|
33
35
|
return parts.join("");
|
|
34
36
|
}
|
|
35
37
|
}
|
|
36
|
-
/**
|
|
38
|
+
/** Bind nominal definitions like native, preserving an alias's existing identity. */
|
|
37
39
|
export function bind_struct_definition(value, definitionRef) {
|
|
38
40
|
if (value instanceof CalcitStructDef && value.definitionRef == null) {
|
|
39
41
|
return new CalcitStructDef(value.name, value.fields, value.fieldTypes, value.impls, definitionRef);
|
|
40
42
|
}
|
|
43
|
+
if (value instanceof CalcitEnumDef && value.prototype.structRef.definitionRef == null) {
|
|
44
|
+
const prototype = value.prototype;
|
|
45
|
+
const structRef = bind_struct_definition(prototype.structRef, definitionRef);
|
|
46
|
+
return new CalcitEnumDef(new CalcitStructValue(prototype.name, prototype.fields, prototype.values, structRef));
|
|
47
|
+
}
|
|
41
48
|
return value;
|
|
42
49
|
}
|
package/lib/js-struct-value.mjs
CHANGED
|
@@ -2,6 +2,14 @@ import { CalcitImpl } from "./js-impl.mjs";
|
|
|
2
2
|
import { newTag, castTag, toString, getStringName, findInFields, compareTagNames, canonicalizeTagPairs, } from "./calcit-data.mjs";
|
|
3
3
|
import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
|
|
4
4
|
import { CalcitStructDef } from "./js-struct-def.mjs";
|
|
5
|
+
import { valueMatchesTypeForm } from "./js-type-form.mjs";
|
|
6
|
+
/// Field writes check the declared field type, like the native runtime.
|
|
7
|
+
let assertFieldValue = (structRef, idx, value, operation) => {
|
|
8
|
+
const fieldType = structRef?.fieldTypes?.[idx];
|
|
9
|
+
if (fieldType != null && !valueMatchesTypeForm(value, fieldType)) {
|
|
10
|
+
throw new Error(`${operation} field :${structRef.fields[idx].value} expects type ${toString(fieldType, true)}, but received ${toString(value, true)}`);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
5
13
|
export class CalcitStructValue {
|
|
6
14
|
constructor(name, fields, values, structRef) {
|
|
7
15
|
this.name = name;
|
|
@@ -44,6 +52,7 @@ export class CalcitStructValue {
|
|
|
44
52
|
let k_id = castTag(k);
|
|
45
53
|
for (let idx = 0; idx < this.fields.length; idx++) {
|
|
46
54
|
if (this.fields[idx] === k_id) {
|
|
55
|
+
assertFieldValue(this.structRef, idx, v, "&struct:assoc");
|
|
47
56
|
values[idx] = v;
|
|
48
57
|
}
|
|
49
58
|
else {
|
|
@@ -60,6 +69,7 @@ export class CalcitStructValue {
|
|
|
60
69
|
assocAt(index, field, value) {
|
|
61
70
|
const idx = checkedStructIndex(index, "&struct:assoc-at");
|
|
62
71
|
this.assertFieldAt(idx, field, "&struct:assoc-at");
|
|
72
|
+
assertFieldValue(this.structRef, idx, value, "&struct:assoc-at");
|
|
63
73
|
const values = this.values.slice();
|
|
64
74
|
values[idx] = value;
|
|
65
75
|
return new CalcitStructValue(this.name, this.fields, values, this.structRef);
|
|
@@ -72,6 +82,7 @@ export class CalcitStructValue {
|
|
|
72
82
|
for (let base = 0; base < triples.length; base += 3) {
|
|
73
83
|
const idx = checkedStructIndex(triples[base], "&struct:with-at");
|
|
74
84
|
this.assertFieldAt(idx, triples[base + 1], "&struct:with-at");
|
|
85
|
+
assertFieldValue(this.structRef, idx, triples[base + 2], "&struct:with-at");
|
|
75
86
|
values[idx] = triples[base + 2];
|
|
76
87
|
}
|
|
77
88
|
return new CalcitStructValue(this.name, this.fields, values, this.structRef);
|
|
@@ -234,6 +245,7 @@ export let _$n_struct_$o_with = (proto, ...xs) => {
|
|
|
234
245
|
if (idx < 0) {
|
|
235
246
|
throw new Error(`Cannot find field ${k} among ${proto.fields}`);
|
|
236
247
|
}
|
|
248
|
+
assertFieldValue(proto.structRef, idx, v, "&struct:with");
|
|
237
249
|
values[idx] = v;
|
|
238
250
|
}
|
|
239
251
|
return new CalcitStructValue(proto.name, proto.fields, values, proto.structRef);
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { CalcitSymbol, CalcitTag } from "./calcit-data.mjs";
|
|
2
|
+
import { CalcitList, CalcitSliceList } from "./js-list.mjs";
|
|
3
|
+
import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
|
|
4
|
+
import { CalcitSet } from "./js-set.mjs";
|
|
5
|
+
import { CalcitRef } from "./js-ref.mjs";
|
|
6
|
+
import { CalcitEnumValue } from "./js-enum-value.mjs";
|
|
7
|
+
/// Shallow runtime check of a value against a struct field type form, matching
|
|
8
|
+
/// the native `value_matches_type_annotation`: container kinds are checked,
|
|
9
|
+
/// members are not. Names that cannot be resolved here (aliases, traits, type
|
|
10
|
+
/// variables) stay permissive, as unresolved forms are on the native side.
|
|
11
|
+
export let valueMatchesTypeForm = (value, form) => {
|
|
12
|
+
if (form instanceof CalcitSliceList || form instanceof CalcitList) {
|
|
13
|
+
const items = form.toArray();
|
|
14
|
+
const head = items[0];
|
|
15
|
+
if (head instanceof CalcitSymbol && head.value === "quote") {
|
|
16
|
+
return valueMatchesTypeForm(value, items[1]);
|
|
17
|
+
}
|
|
18
|
+
if (head instanceof CalcitSymbol && head.value === "::") {
|
|
19
|
+
const name = typeFormName(items[1]);
|
|
20
|
+
// Nil is `null` here; `undefined` is Unit. Only the JS host boundary
|
|
21
|
+
// type also admits `undefined`.
|
|
22
|
+
if (name === "Optional") {
|
|
23
|
+
return value === null || valueMatchesTypeForm(value, items[2]);
|
|
24
|
+
}
|
|
25
|
+
if (name === "JsNullish") {
|
|
26
|
+
return value == null || valueMatchesTypeForm(value, items[2]);
|
|
27
|
+
}
|
|
28
|
+
return name == null || valueMatchesTypeName(value, name);
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
const name = typeFormName(form);
|
|
33
|
+
return name == null || valueMatchesTypeName(value, name);
|
|
34
|
+
};
|
|
35
|
+
let typeFormName = (form) => {
|
|
36
|
+
if (form instanceof CalcitSymbol)
|
|
37
|
+
return form.value;
|
|
38
|
+
if (form instanceof CalcitTag)
|
|
39
|
+
return form.value;
|
|
40
|
+
if (form instanceof CalcitSliceList || form instanceof CalcitList) {
|
|
41
|
+
const items = form.toArray();
|
|
42
|
+
if (items[0] instanceof CalcitSymbol && items[0].value === "quote")
|
|
43
|
+
return typeFormName(items[1]);
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
/// Numeric refinements check range and integrality like the native `CalcitNumericRefinement::accepts`.
|
|
48
|
+
let isIntegerIn = (value, min, max) => typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
|
|
49
|
+
let isList = (value) => value instanceof CalcitList || value instanceof CalcitSliceList;
|
|
50
|
+
let isMap = (value) => value instanceof CalcitMap || value instanceof CalcitSliceMap;
|
|
51
|
+
let valueMatchesTypeName = (value, rawName) => {
|
|
52
|
+
const name = rawName.startsWith("calcit.core/") ? rawName.slice("calcit.core/".length) : rawName;
|
|
53
|
+
switch (name) {
|
|
54
|
+
case "Dynamic":
|
|
55
|
+
case "dynamic":
|
|
56
|
+
case "any":
|
|
57
|
+
case "JsObject":
|
|
58
|
+
return true;
|
|
59
|
+
case "Number":
|
|
60
|
+
case "number":
|
|
61
|
+
case "Float64":
|
|
62
|
+
case "float64":
|
|
63
|
+
return typeof value === "number";
|
|
64
|
+
case "Float32":
|
|
65
|
+
case "float32":
|
|
66
|
+
return typeof value === "number" && Math.fround(value) === value;
|
|
67
|
+
case "Int8":
|
|
68
|
+
case "int8":
|
|
69
|
+
return isIntegerIn(value, -128, 127);
|
|
70
|
+
case "UInt8":
|
|
71
|
+
case "uint8":
|
|
72
|
+
return isIntegerIn(value, 0, 255);
|
|
73
|
+
case "Int16":
|
|
74
|
+
case "int16":
|
|
75
|
+
return isIntegerIn(value, -32768, 32767);
|
|
76
|
+
case "UInt16":
|
|
77
|
+
case "uint16":
|
|
78
|
+
return isIntegerIn(value, 0, 65535);
|
|
79
|
+
case "Int32":
|
|
80
|
+
case "int32":
|
|
81
|
+
return isIntegerIn(value, -2147483648, 2147483647);
|
|
82
|
+
case "UInt32":
|
|
83
|
+
case "uint32":
|
|
84
|
+
return isIntegerIn(value, 0, 4294967295);
|
|
85
|
+
case "Int64":
|
|
86
|
+
case "int64":
|
|
87
|
+
return isIntegerIn(value, -Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
|
|
88
|
+
case "UInt64":
|
|
89
|
+
case "uint64":
|
|
90
|
+
return isIntegerIn(value, 0, Number.MAX_SAFE_INTEGER);
|
|
91
|
+
case "String":
|
|
92
|
+
case "string":
|
|
93
|
+
return typeof value === "string";
|
|
94
|
+
case "Bool":
|
|
95
|
+
case "bool":
|
|
96
|
+
return typeof value === "boolean";
|
|
97
|
+
case "Tag":
|
|
98
|
+
case "tag":
|
|
99
|
+
return value instanceof CalcitTag;
|
|
100
|
+
case "Symbol":
|
|
101
|
+
case "symbol":
|
|
102
|
+
return value instanceof CalcitSymbol;
|
|
103
|
+
case "Nil":
|
|
104
|
+
case "nil":
|
|
105
|
+
return value === null;
|
|
106
|
+
case "Unit":
|
|
107
|
+
case "unit":
|
|
108
|
+
return value === undefined;
|
|
109
|
+
case "List":
|
|
110
|
+
case "list":
|
|
111
|
+
return isList(value);
|
|
112
|
+
case "Map":
|
|
113
|
+
case "map":
|
|
114
|
+
return isMap(value);
|
|
115
|
+
case "Set":
|
|
116
|
+
case "set":
|
|
117
|
+
return value instanceof CalcitSet;
|
|
118
|
+
case "Ref":
|
|
119
|
+
case "ref":
|
|
120
|
+
return value instanceof CalcitRef;
|
|
121
|
+
case "Fn":
|
|
122
|
+
case "fn":
|
|
123
|
+
return typeof value === "function";
|
|
124
|
+
case "Struct":
|
|
125
|
+
case "struct":
|
|
126
|
+
case "record":
|
|
127
|
+
return isStructValue(value);
|
|
128
|
+
case "Enum":
|
|
129
|
+
case "enum":
|
|
130
|
+
case "tuple":
|
|
131
|
+
return value instanceof CalcitEnumValue;
|
|
132
|
+
}
|
|
133
|
+
// Core Struct and Enum definitions (bare names resolve to them through the
|
|
134
|
+
// implicit core import) are known here, so a reference to one admits only
|
|
135
|
+
// values of that definition, as the native matcher does.
|
|
136
|
+
if (CORE_NOMINAL_DEFS.has(name)) {
|
|
137
|
+
if (isStructValue(value))
|
|
138
|
+
return value.structRef.definitionRef === `calcit.core/${name}`;
|
|
139
|
+
return value instanceof CalcitEnumValue && value.enumPrototype?.prototype.structRef.definitionRef === `calcit.core/${name}`;
|
|
140
|
+
}
|
|
141
|
+
// Other names may be type variables, aliases or traits that cannot be
|
|
142
|
+
// resolved here. A qualified reference is checked against the value's own
|
|
143
|
+
// Struct or Enum name; other values are left to the static checker like
|
|
144
|
+
// unresolved forms.
|
|
145
|
+
const shortName = name.includes("/") ? name.slice(name.lastIndexOf("/") + 1) : name;
|
|
146
|
+
if (isStructValue(value))
|
|
147
|
+
return value.name.value === shortName || !name.includes("/");
|
|
148
|
+
if (value instanceof CalcitEnumValue && value.enumPrototype != null) {
|
|
149
|
+
return value.enumPrototype.name() === shortName || !name.includes("/");
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
};
|
|
153
|
+
/// Struct and Enum definitions of `calcit.core` usable as field types.
|
|
154
|
+
const CORE_NOMINAL_DEFS = new Set([
|
|
155
|
+
"Option",
|
|
156
|
+
"Result",
|
|
157
|
+
"Data",
|
|
158
|
+
"ListDestruct",
|
|
159
|
+
"MapDestruct",
|
|
160
|
+
"SetDestruct",
|
|
161
|
+
"StringDestruct",
|
|
162
|
+
"MapEntry",
|
|
163
|
+
"RuntimeMapMeta",
|
|
164
|
+
"RuntimeMapResponse",
|
|
165
|
+
]);
|
|
166
|
+
// Duck-typed to avoid a module cycle with js-struct-value.
|
|
167
|
+
let isStructValue = (value) => value != null && typeof value === "object" && "structRef" in value && "fields" in value;
|
package/lib/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calcit/procs",
|
|
3
|
-
"version": "0.29.0-alpha.
|
|
3
|
+
"version": "0.29.0-alpha.20",
|
|
4
4
|
"main": "./lib/calcit.procs.mjs",
|
|
5
5
|
"files": [
|
|
6
6
|
"lib/",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"test-rs": "cargo test -q",
|
|
22
22
|
"test-fail": "cargo test -q --bin calcit type_fail_",
|
|
23
23
|
"test-snippets": "cargo test -q snippets::tests",
|
|
24
|
-
"check-js-runtime": "node scripts/check-js-runtime-identity.mjs && cargo build --bin calcit && node scripts/check-string-unicode.mjs && node scripts/check-
|
|
24
|
+
"check-js-runtime": "node scripts/check-js-runtime-identity.mjs && cargo build --bin calcit && node scripts/check-string-unicode.mjs && node scripts/check-map-add-trait-boundary.mjs && node scripts/check-typed-string-conversions.mjs && node scripts/check-identity-conversion-fix.mjs && node scripts/check-nil-predicate.mjs && node scripts/check-monotonic-clock-js.mjs && node scripts/check-helper-inference.mjs",
|
|
25
25
|
"check-js-ffi-source": "cargo build --bin calcit && node scripts/check-js-ffi-source.mjs",
|
|
26
26
|
"check-literal-paths": "cargo run --bin calcit -- calcit/bench-literal-paths.cirru && cargo run --bin calcit -- --emit-path target/literal-path-js calcit/bench-literal-paths.cirru js && node scripts/bench-literal-paths.mjs",
|
|
27
27
|
"check-typed-method-rem": "cargo run --bin calcit -- calcit/fibo.cirru && cargo run --bin calcit -- --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
|
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
"bench-typed-method-rem": "cargo build --release --bin calcit && yarn compile && target/release/calcit calcit/fibo.cirru && node scripts/bench-typed-method-rem-native.mjs && target/release/calcit --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
|
|
32
32
|
"bench-struct-index": "yarn compile && node scripts/bench-struct-index.mjs",
|
|
33
33
|
"check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
|
|
34
|
-
"check-all": "yarn compile && yarn check-js-runtime && node scripts/check-file-read-names.mjs && node scripts/check-enum-impl-cycle.mjs && node scripts/check-typed-source-alias.mjs && node scripts/check-parse-boundary.mjs && yarn check-js-ffi-source && yarn check-strict-default && yarn check-agent-interface && node --test scripts/core-api-contract.test.mjs && node scripts/core-api-contract.mjs && yarn try-core-tests && yarn check-core-dynamic-classification && yarn try-rs && yarn try-js && node scripts/check-struct-field-js.mjs && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
|
|
34
|
+
"check-all": "yarn compile && yarn check-js-runtime && node scripts/check-file-read-names.mjs && node scripts/check-enum-impl-cycle.mjs && node scripts/check-typed-source-alias.mjs && node scripts/check-parse-boundary.mjs && yarn check-js-ffi-source && yarn check-strict-default && yarn check-agent-interface && node --test scripts/core-api-contract.test.mjs && node scripts/core-api-contract.mjs && yarn try-core-tests && yarn check-core-tests && yarn check-core-dynamic-classification && yarn try-rs && yarn try-js && node scripts/check-struct-field-js.mjs && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
|
|
35
35
|
"check-strict-default": "cargo build --bin calcit && node scripts/check-strict-default.mjs && node scripts/check-known-assertion.mjs && node scripts/check-spread-boundary.mjs",
|
|
36
|
+
"check-core-tests": "node --test scripts/core-test-protocol.test.mjs && cargo build --bin calcit && node scripts/run-core-tests.mjs && node scripts/run-core-tests.mjs --snapshot calcit/test-traits.cirru --target test-traits.main/test-qualified-contains-boundary --backend native,js",
|
|
36
37
|
"check-agent-interface": "cargo build --bin calcit && node scripts/check-agent-interface.mjs",
|
|
37
38
|
"try-all": "yarn check-all",
|
|
38
39
|
"try-core-tests": "cargo run --bin calcit -- src/cirru/calcit-core.cirru test --tag unit --summary-only --require-match",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@calcit/procs",
|
|
3
|
-
"version": "0.29.0-alpha.
|
|
3
|
+
"version": "0.29.0-alpha.20",
|
|
4
4
|
"main": "./lib/calcit.procs.mjs",
|
|
5
5
|
"files": [
|
|
6
6
|
"lib/",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"test-rs": "cargo test -q",
|
|
22
22
|
"test-fail": "cargo test -q --bin calcit type_fail_",
|
|
23
23
|
"test-snippets": "cargo test -q snippets::tests",
|
|
24
|
-
"check-js-runtime": "node scripts/check-js-runtime-identity.mjs && cargo build --bin calcit && node scripts/check-string-unicode.mjs && node scripts/check-
|
|
24
|
+
"check-js-runtime": "node scripts/check-js-runtime-identity.mjs && cargo build --bin calcit && node scripts/check-string-unicode.mjs && node scripts/check-map-add-trait-boundary.mjs && node scripts/check-typed-string-conversions.mjs && node scripts/check-identity-conversion-fix.mjs && node scripts/check-nil-predicate.mjs && node scripts/check-monotonic-clock-js.mjs && node scripts/check-helper-inference.mjs",
|
|
25
25
|
"check-js-ffi-source": "cargo build --bin calcit && node scripts/check-js-ffi-source.mjs",
|
|
26
26
|
"check-literal-paths": "cargo run --bin calcit -- calcit/bench-literal-paths.cirru && cargo run --bin calcit -- --emit-path target/literal-path-js calcit/bench-literal-paths.cirru js && node scripts/bench-literal-paths.mjs",
|
|
27
27
|
"check-typed-method-rem": "cargo run --bin calcit -- calcit/fibo.cirru && cargo run --bin calcit -- --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
|
|
@@ -31,8 +31,9 @@
|
|
|
31
31
|
"bench-typed-method-rem": "cargo build --release --bin calcit && yarn compile && target/release/calcit calcit/fibo.cirru && node scripts/bench-typed-method-rem-native.mjs && target/release/calcit --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
|
|
32
32
|
"bench-struct-index": "yarn compile && node scripts/bench-struct-index.mjs",
|
|
33
33
|
"check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
|
|
34
|
-
"check-all": "yarn compile && yarn check-js-runtime && node scripts/check-file-read-names.mjs && node scripts/check-enum-impl-cycle.mjs && node scripts/check-typed-source-alias.mjs && node scripts/check-parse-boundary.mjs && yarn check-js-ffi-source && yarn check-strict-default && yarn check-agent-interface && node --test scripts/core-api-contract.test.mjs && node scripts/core-api-contract.mjs && yarn try-core-tests && yarn check-core-dynamic-classification && yarn try-rs && yarn try-js && node scripts/check-struct-field-js.mjs && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
|
|
34
|
+
"check-all": "yarn compile && yarn check-js-runtime && node scripts/check-file-read-names.mjs && node scripts/check-enum-impl-cycle.mjs && node scripts/check-typed-source-alias.mjs && node scripts/check-parse-boundary.mjs && yarn check-js-ffi-source && yarn check-strict-default && yarn check-agent-interface && node --test scripts/core-api-contract.test.mjs && node scripts/core-api-contract.mjs && yarn try-core-tests && yarn check-core-tests && yarn check-core-dynamic-classification && yarn try-rs && yarn try-js && node scripts/check-struct-field-js.mjs && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
|
|
35
35
|
"check-strict-default": "cargo build --bin calcit && node scripts/check-strict-default.mjs && node scripts/check-known-assertion.mjs && node scripts/check-spread-boundary.mjs",
|
|
36
|
+
"check-core-tests": "node --test scripts/core-test-protocol.test.mjs && cargo build --bin calcit && node scripts/run-core-tests.mjs && node scripts/run-core-tests.mjs --snapshot calcit/test-traits.cirru --target test-traits.main/test-qualified-contains-boundary --backend native,js",
|
|
36
37
|
"check-agent-interface": "cargo build --bin calcit && node scripts/check-agent-interface.mjs",
|
|
37
38
|
"try-all": "yarn check-all",
|
|
38
39
|
"try-core-tests": "cargo run --bin calcit -- src/cirru/calcit-core.cirru test --tag unit --summary-only --require-match",
|