@calcit/procs 0.29.0-alpha.8 → 0.29.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/lib/calcit-data.d.mts +1 -0
- package/lib/calcit-data.mjs +47 -6
- package/lib/calcit.procs.d.mts +12 -7
- package/lib/calcit.procs.mjs +214 -101
- package/lib/js-cirru.d.mts +1 -1
- package/lib/js-cirru.mjs +9 -2
- 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 +5 -3
- package/lib/js-struct-def.mjs +14 -6
- package/lib/js-struct-value.mjs +55 -88
- package/lib/js-type-form.d.mts +2 -0
- package/lib/js-type-form.mjs +188 -0
- package/lib/package.json +4 -3
- package/package.json +4 -3
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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CalcitTag } from "./calcit-data.mjs";
|
|
2
2
|
import { CalcitValue } from "./js-primes.mjs";
|
|
3
3
|
import { CalcitImpl } from "./js-impl.mjs";
|
|
4
|
+
export type FieldValidator = ((value: CalcitValue, requireEvidence?: boolean) => boolean) | null;
|
|
4
5
|
export declare class CalcitStructDef {
|
|
5
6
|
name: CalcitTag;
|
|
6
7
|
fields: CalcitTag[];
|
|
@@ -8,9 +9,10 @@ export declare class CalcitStructDef {
|
|
|
8
9
|
impls: CalcitImpl[];
|
|
9
10
|
cachedHash: number;
|
|
10
11
|
readonly definitionRef: string | null;
|
|
11
|
-
|
|
12
|
+
readonly fieldValidators: FieldValidator[] | null;
|
|
13
|
+
constructor(name: CalcitTag, fields: CalcitTag[], fieldTypes: CalcitValue[], impls?: CalcitImpl[], definitionRef?: string | null, fieldValidators?: FieldValidator[] | null);
|
|
12
14
|
withImpls(impls: CalcitImpl | CalcitImpl[]): CalcitStructDef;
|
|
13
15
|
toString(disableJsDataWarning?: boolean): string;
|
|
14
16
|
}
|
|
15
|
-
/**
|
|
16
|
-
export declare function bind_struct_definition(value: CalcitValue, definitionRef: string): CalcitValue;
|
|
17
|
+
/** Bind nominal definitions like native, preserving an alias's existing identity. */
|
|
18
|
+
export declare function bind_struct_definition(value: CalcitValue, definitionRef: string, fieldValidators?: FieldValidator[] | null): CalcitValue;
|
package/lib/js-struct-def.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
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
|
-
constructor(name, fields, fieldTypes, impls = [], definitionRef = null) {
|
|
6
|
+
constructor(name, fields, fieldTypes, impls = [], definitionRef = null, fieldValidators = null) {
|
|
5
7
|
const [canonicalFields, canonicalTypes] = canonicalizeTagPairs(fields, fieldTypes, "CalcitStructDef");
|
|
6
8
|
this.name = name;
|
|
7
9
|
this.fields = canonicalFields;
|
|
@@ -9,13 +11,14 @@ export class CalcitStructDef {
|
|
|
9
11
|
this.impls = impls ?? [];
|
|
10
12
|
this.cachedHash = null;
|
|
11
13
|
this.definitionRef = definitionRef;
|
|
14
|
+
this.fieldValidators = fieldValidators == null ? null : canonicalizeTagPairs(fields, fieldValidators, "CalcitStructDef validators")[1];
|
|
12
15
|
}
|
|
13
16
|
withImpls(impls) {
|
|
14
17
|
if (impls instanceof CalcitImpl) {
|
|
15
|
-
return new CalcitStructDef(this.name, this.fields, this.fieldTypes, [impls], this.definitionRef);
|
|
18
|
+
return new CalcitStructDef(this.name, this.fields, this.fieldTypes, [impls], this.definitionRef, this.fieldValidators);
|
|
16
19
|
}
|
|
17
20
|
else if (Array.isArray(impls)) {
|
|
18
|
-
return new CalcitStructDef(this.name, this.fields, this.fieldTypes, impls, this.definitionRef);
|
|
21
|
+
return new CalcitStructDef(this.name, this.fields, this.fieldTypes, impls, this.definitionRef, this.fieldValidators);
|
|
19
22
|
}
|
|
20
23
|
throw new Error("Expected an impl as implementation");
|
|
21
24
|
}
|
|
@@ -33,10 +36,15 @@ export class CalcitStructDef {
|
|
|
33
36
|
return parts.join("");
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
|
-
/**
|
|
37
|
-
export function bind_struct_definition(value, definitionRef) {
|
|
39
|
+
/** Bind nominal definitions like native, preserving an alias's existing identity. */
|
|
40
|
+
export function bind_struct_definition(value, definitionRef, fieldValidators = null) {
|
|
38
41
|
if (value instanceof CalcitStructDef && value.definitionRef == null) {
|
|
39
|
-
return new CalcitStructDef(value.name, value.fields, value.fieldTypes, value.impls, definitionRef);
|
|
42
|
+
return new CalcitStructDef(value.name, value.fields, value.fieldTypes, value.impls, definitionRef, fieldValidators);
|
|
43
|
+
}
|
|
44
|
+
if (value instanceof CalcitEnumDef && value.prototype.structRef.definitionRef == null) {
|
|
45
|
+
const prototype = value.prototype;
|
|
46
|
+
const structRef = bind_struct_definition(prototype.structRef, definitionRef);
|
|
47
|
+
return new CalcitEnumDef(new CalcitStructValue(prototype.name, prototype.fields, prototype.values, structRef));
|
|
40
48
|
}
|
|
41
49
|
return value;
|
|
42
50
|
}
|
package/lib/js-struct-value.mjs
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { CalcitImpl } from "./js-impl.mjs";
|
|
2
|
-
import { newTag, castTag, toString, getStringName, findInFields, compareTagNames, canonicalizeTagPairs, } from "./calcit-data.mjs";
|
|
2
|
+
import { newTag, castTag, toString, CalcitTag, CalcitSymbol, 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, requireEvidence = false) => {
|
|
8
|
+
const fieldType = structRef?.fieldTypes?.[idx];
|
|
9
|
+
const validator = structRef?.fieldValidators?.[idx];
|
|
10
|
+
if (validator != null ? !validator(value, requireEvidence)
|
|
11
|
+
: fieldType != null && !valueMatchesTypeForm(value, fieldType, requireEvidence)) {
|
|
12
|
+
throw new Error(`${operation} field :${structRef.fields[idx].value} expects type ${toString(fieldType, true)}, but received ${toString(value, true)}`);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
5
15
|
export class CalcitStructValue {
|
|
6
16
|
constructor(name, fields, values, structRef) {
|
|
7
17
|
this.name = name;
|
|
@@ -40,16 +50,14 @@ export class CalcitStructValue {
|
|
|
40
50
|
}
|
|
41
51
|
}
|
|
42
52
|
assoc(k, v) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
values[idx] = v;
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
values[idx] = this.values[idx];
|
|
51
|
-
}
|
|
53
|
+
const field = castTag(k);
|
|
54
|
+
const idx = findInFields(this.fields, field);
|
|
55
|
+
if (idx < 0) {
|
|
56
|
+
throw new Error(`&struct:assoc invalid field ${field.toString()} for struct '${this.name.value}'`);
|
|
52
57
|
}
|
|
58
|
+
assertFieldValue(this.structRef, idx, v, "&struct:assoc", true);
|
|
59
|
+
const values = this.values.slice();
|
|
60
|
+
values[idx] = v;
|
|
53
61
|
return new CalcitStructValue(this.name, this.fields, values, this.structRef);
|
|
54
62
|
}
|
|
55
63
|
nthAt(index, field) {
|
|
@@ -60,6 +68,7 @@ export class CalcitStructValue {
|
|
|
60
68
|
assocAt(index, field, value) {
|
|
61
69
|
const idx = checkedStructIndex(index, "&struct:assoc-at");
|
|
62
70
|
this.assertFieldAt(idx, field, "&struct:assoc-at");
|
|
71
|
+
assertFieldValue(this.structRef, idx, value, "&struct:assoc-at");
|
|
63
72
|
const values = this.values.slice();
|
|
64
73
|
values[idx] = value;
|
|
65
74
|
return new CalcitStructValue(this.name, this.fields, values, this.structRef);
|
|
@@ -72,6 +81,7 @@ export class CalcitStructValue {
|
|
|
72
81
|
for (let base = 0; base < triples.length; base += 3) {
|
|
73
82
|
const idx = checkedStructIndex(triples[base], "&struct:with-at");
|
|
74
83
|
this.assertFieldAt(idx, triples[base + 1], "&struct:with-at");
|
|
84
|
+
assertFieldValue(this.structRef, idx, triples[base + 2], "&struct:with-at");
|
|
75
85
|
values[idx] = triples[base + 2];
|
|
76
86
|
}
|
|
77
87
|
return new CalcitStructValue(this.name, this.fields, values, this.structRef);
|
|
@@ -116,7 +126,7 @@ export class CalcitStructValue {
|
|
|
116
126
|
else {
|
|
117
127
|
throw new Error("Expected an impl or array of impls");
|
|
118
128
|
}
|
|
119
|
-
let nextStruct = new CalcitStructDef(this.name, this.fields, this.structRef.fieldTypes, this.structRef.impls.concat(nextImpls), this.structRef.definitionRef);
|
|
129
|
+
let nextStruct = new CalcitStructDef(this.name, this.fields, this.structRef.fieldTypes, this.structRef.impls.concat(nextImpls), this.structRef.definitionRef, this.structRef.fieldValidators);
|
|
120
130
|
return new CalcitStructValue(this.name, this.fields, this.values, nextStruct);
|
|
121
131
|
}
|
|
122
132
|
}
|
|
@@ -181,45 +191,38 @@ export let fieldsEqual = (xs, ys) => {
|
|
|
181
191
|
}
|
|
182
192
|
return true;
|
|
183
193
|
};
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (proto instanceof
|
|
187
|
-
|
|
194
|
+
// Share complete construction without spreading map contents into call arguments.
|
|
195
|
+
const constructStruct = (proto, xs) => {
|
|
196
|
+
if (!(proto instanceof CalcitStructDef)) {
|
|
197
|
+
throw new Error("Expected prototype to be a StructDef");
|
|
188
198
|
}
|
|
189
|
-
|
|
190
|
-
|
|
199
|
+
if (xs.length % 2 !== 0) {
|
|
200
|
+
throw new Error("Expected even number of key/value");
|
|
191
201
|
}
|
|
192
|
-
|
|
193
|
-
throw new Error("
|
|
202
|
+
if (xs.length !== proto.fields.length * 2) {
|
|
203
|
+
throw new Error("fields size does not match");
|
|
194
204
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
205
|
+
const values = new Array(proto.fields.length);
|
|
206
|
+
const seen = new Set();
|
|
207
|
+
for (let i = 0; i < xs.length; i += 2) {
|
|
208
|
+
const key = xs[i];
|
|
209
|
+
if (!(key instanceof CalcitTag) && !(key instanceof CalcitSymbol) && typeof key !== "string") {
|
|
210
|
+
throw new Error("&%{} requires field in string/tag/symbol");
|
|
198
211
|
}
|
|
199
|
-
|
|
200
|
-
|
|
212
|
+
const idx = findInFields(proto.fields, castTag(key));
|
|
213
|
+
if (idx < 0) {
|
|
214
|
+
throw new Error("invalid field name for this struct");
|
|
201
215
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
let idx = -1;
|
|
205
|
-
let k = recordProto.fields[i];
|
|
206
|
-
for (let j = 0; j < recordProto.fields.length; j++) {
|
|
207
|
-
if (k === castTag(xs[j * 2])) {
|
|
208
|
-
idx = j;
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (idx < 0) {
|
|
213
|
-
throw new Error("invalid field name for this struct");
|
|
214
|
-
}
|
|
215
|
-
if (values[i] != null) {
|
|
216
|
-
throw new Error("struct field already has value, probably duplicated key");
|
|
217
|
-
}
|
|
218
|
-
values[i] = xs[idx * 2 + 1];
|
|
216
|
+
if (seen.has(idx)) {
|
|
217
|
+
throw new Error("struct field already has value, probably duplicated key");
|
|
219
218
|
}
|
|
220
|
-
|
|
219
|
+
seen.add(idx);
|
|
220
|
+
assertFieldValue(proto, idx, xs[i + 1], "&%{}");
|
|
221
|
+
values[idx] = xs[i + 1];
|
|
221
222
|
}
|
|
223
|
+
return new CalcitStructValue(proto.name, proto.fields, values, proto);
|
|
222
224
|
};
|
|
225
|
+
export let _$n__PCT__$M_ = (proto, ...xs) => constructStruct(proto, xs);
|
|
223
226
|
/// update record with new values
|
|
224
227
|
export let _$n_struct_$o_with = (proto, ...xs) => {
|
|
225
228
|
if (proto instanceof CalcitStructValue) {
|
|
@@ -234,6 +237,7 @@ export let _$n_struct_$o_with = (proto, ...xs) => {
|
|
|
234
237
|
if (idx < 0) {
|
|
235
238
|
throw new Error(`Cannot find field ${k} among ${proto.fields}`);
|
|
236
239
|
}
|
|
240
|
+
assertFieldValue(proto.structRef, idx, v, "&struct:with", true);
|
|
237
241
|
values[idx] = v;
|
|
238
242
|
}
|
|
239
243
|
return new CalcitStructValue(proto.name, proto.fields, values, proto.structRef);
|
|
@@ -261,57 +265,20 @@ export let _$n_struct_$o_definition = (x) => {
|
|
|
261
265
|
}
|
|
262
266
|
};
|
|
263
267
|
export let _$n_struct_$o_from_map = (proto, data) => {
|
|
264
|
-
|
|
265
|
-
if (proto instanceof CalcitStructDef) {
|
|
266
|
-
recordProto = new CalcitStructValue(proto.name, proto.fields, new Array(proto.fields.length).fill(null), proto);
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
268
|
+
if (!(proto instanceof CalcitStructDef)) {
|
|
269
269
|
throw new Error("Expected prototype to be struct");
|
|
270
270
|
}
|
|
271
|
-
if (data instanceof
|
|
272
|
-
|
|
273
|
-
return new CalcitStructValue(recordProto.name, recordProto.fields, data.values, recordProto.structRef);
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
let values = [];
|
|
277
|
-
for (let i = 0; i < recordProto.fields.length; i++) {
|
|
278
|
-
let field = recordProto.fields[i];
|
|
279
|
-
let idx = findInFields(data.fields, field);
|
|
280
|
-
if (idx < 0) {
|
|
281
|
-
throw new Error(`Cannot find field ${field} among ${data.fields}`);
|
|
282
|
-
}
|
|
283
|
-
values.push(data.values[idx]);
|
|
284
|
-
}
|
|
285
|
-
return new CalcitStructValue(recordProto.name, recordProto.fields, values, recordProto.structRef);
|
|
286
|
-
}
|
|
271
|
+
if (!(data instanceof CalcitMap) && !(data instanceof CalcitSliceMap)) {
|
|
272
|
+
throw new Error("&struct:from-map requires a Map");
|
|
287
273
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
let k = pairs[i][0];
|
|
293
|
-
let v = pairs[i][1];
|
|
294
|
-
pairs_buffer.push([castTag(k), v]);
|
|
274
|
+
const fields = [];
|
|
275
|
+
for (const [key, value] of data.pairs()) {
|
|
276
|
+
if (!(key instanceof CalcitTag) && typeof key !== "string") {
|
|
277
|
+
throw new Error("&struct:from-map requires field in string/tag");
|
|
295
278
|
}
|
|
296
|
-
|
|
297
|
-
pairs_buffer.sort((pair1, pair2) => pair1[0].cmp(pair2[0]));
|
|
298
|
-
let values = [];
|
|
299
|
-
outerLoop: for (let i = 0; i < recordProto.fields.length; i++) {
|
|
300
|
-
let field = recordProto.fields[i];
|
|
301
|
-
for (let idx = 0; idx < pairs_buffer.length; idx++) {
|
|
302
|
-
let pair = pairs_buffer[idx];
|
|
303
|
-
if (pair[0] === field) {
|
|
304
|
-
values.push(pair[1]);
|
|
305
|
-
continue outerLoop; // dirty code for performance
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
throw new Error(`Cannot find field ${field} among ${pairs_buffer}`);
|
|
309
|
-
}
|
|
310
|
-
return new CalcitStructValue(recordProto.name, recordProto.fields, values, recordProto.structRef);
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
throw new Error("Expected a struct value or data for making a struct");
|
|
279
|
+
fields.push(key, value);
|
|
314
280
|
}
|
|
281
|
+
return constructStruct(proto, fields);
|
|
315
282
|
};
|
|
316
283
|
export let _$n_struct_$o_to_map = (x) => {
|
|
317
284
|
if (x instanceof CalcitStructValue) {
|
|
@@ -0,0 +1,188 @@
|
|
|
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
|
+
import { CalcitCirruQuote } from "./js-cirru.mjs";
|
|
8
|
+
/// Shallow runtime check of a value against a struct field type form, matching
|
|
9
|
+
/// the native `value_matches_type_annotation`: container kinds are checked,
|
|
10
|
+
/// members are not. Names that cannot be resolved here (aliases, traits, type
|
|
11
|
+
/// variables) stay permissive only for the legacy, statically checked path.
|
|
12
|
+
/// Dynamic writes require evidence: deep contracts use generated validators,
|
|
13
|
+
/// while this fallback accepts only completely checkable scalar/open forms.
|
|
14
|
+
export let valueMatchesTypeForm = (value, form, requireEvidence = false) => matchTypeForm(value, form, requireEvidence) === true;
|
|
15
|
+
// null means that the contract cannot be checked here, not a mismatched value.
|
|
16
|
+
// Nullable absence proves its own branch; present payloads still require
|
|
17
|
+
// evidence for the inner contract.
|
|
18
|
+
let matchTypeForm = (value, form, requireEvidence) => {
|
|
19
|
+
if (form instanceof CalcitSliceList || form instanceof CalcitList) {
|
|
20
|
+
const items = form.toArray();
|
|
21
|
+
const head = items[0];
|
|
22
|
+
if (head instanceof CalcitSymbol && head.value === "quote") {
|
|
23
|
+
return matchTypeForm(value, items[1], requireEvidence);
|
|
24
|
+
}
|
|
25
|
+
if (head instanceof CalcitSymbol && head.value === "::") {
|
|
26
|
+
const name = typeFormName(items[1]);
|
|
27
|
+
// Nil is `null` here; `undefined` is Unit. Only the JS host boundary
|
|
28
|
+
// type also admits `undefined`.
|
|
29
|
+
if (name === "Optional") {
|
|
30
|
+
return value === null ? true : matchTypeForm(value, items[2], requireEvidence);
|
|
31
|
+
}
|
|
32
|
+
if (name === "JsNullish") {
|
|
33
|
+
return value == null ? true : matchTypeForm(value, items[2], requireEvidence);
|
|
34
|
+
}
|
|
35
|
+
if (name === "Fn" && requireEvidence)
|
|
36
|
+
return null;
|
|
37
|
+
return name == null ? (requireEvidence ? null : true) : valueMatchesTypeName(value, name, requireEvidence);
|
|
38
|
+
}
|
|
39
|
+
return requireEvidence ? null : true;
|
|
40
|
+
}
|
|
41
|
+
const name = typeFormName(form);
|
|
42
|
+
return name == null ? (requireEvidence ? null : true) : valueMatchesTypeName(value, name, requireEvidence);
|
|
43
|
+
};
|
|
44
|
+
let typeFormName = (form) => {
|
|
45
|
+
if (form instanceof CalcitSymbol)
|
|
46
|
+
return form.value;
|
|
47
|
+
if (form instanceof CalcitTag)
|
|
48
|
+
return form.value;
|
|
49
|
+
if (form instanceof CalcitSliceList || form instanceof CalcitList) {
|
|
50
|
+
const items = form.toArray();
|
|
51
|
+
if (items[0] instanceof CalcitSymbol && items[0].value === "quote")
|
|
52
|
+
return typeFormName(items[1]);
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
};
|
|
56
|
+
/// Numeric refinements check range and integrality like the native `CalcitNumericRefinement::accepts`.
|
|
57
|
+
let isIntegerIn = (value, min, max) => typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
|
|
58
|
+
let isList = (value) => value instanceof CalcitList || value instanceof CalcitSliceList;
|
|
59
|
+
let isMap = (value) => value instanceof CalcitMap || value instanceof CalcitSliceMap;
|
|
60
|
+
let valueMatchesTypeName = (value, rawName, requireEvidence) => {
|
|
61
|
+
const name = rawName.startsWith("calcit.core/") ? rawName.slice("calcit.core/".length) : rawName;
|
|
62
|
+
switch (name) {
|
|
63
|
+
case "Dynamic":
|
|
64
|
+
case "dynamic":
|
|
65
|
+
case "any":
|
|
66
|
+
case "JsObject":
|
|
67
|
+
return true;
|
|
68
|
+
case "Number":
|
|
69
|
+
case "number":
|
|
70
|
+
case "Float64":
|
|
71
|
+
case "float64":
|
|
72
|
+
return typeof value === "number";
|
|
73
|
+
case "Float32":
|
|
74
|
+
case "float32":
|
|
75
|
+
return typeof value === "number" && Math.fround(value) === value;
|
|
76
|
+
case "Int8":
|
|
77
|
+
case "int8":
|
|
78
|
+
return isIntegerIn(value, -128, 127);
|
|
79
|
+
case "UInt8":
|
|
80
|
+
case "uint8":
|
|
81
|
+
return isIntegerIn(value, 0, 255);
|
|
82
|
+
case "Int16":
|
|
83
|
+
case "int16":
|
|
84
|
+
return isIntegerIn(value, -32768, 32767);
|
|
85
|
+
case "UInt16":
|
|
86
|
+
case "uint16":
|
|
87
|
+
return isIntegerIn(value, 0, 65535);
|
|
88
|
+
case "Int32":
|
|
89
|
+
case "int32":
|
|
90
|
+
return isIntegerIn(value, -2147483648, 2147483647);
|
|
91
|
+
case "UInt32":
|
|
92
|
+
case "uint32":
|
|
93
|
+
return isIntegerIn(value, 0, 4294967295);
|
|
94
|
+
case "Int64":
|
|
95
|
+
case "int64":
|
|
96
|
+
return isIntegerIn(value, -Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
|
|
97
|
+
case "UInt64":
|
|
98
|
+
case "uint64":
|
|
99
|
+
return isIntegerIn(value, 0, Number.MAX_SAFE_INTEGER);
|
|
100
|
+
case "String":
|
|
101
|
+
case "string":
|
|
102
|
+
return typeof value === "string";
|
|
103
|
+
case "Bool":
|
|
104
|
+
case "bool":
|
|
105
|
+
return typeof value === "boolean";
|
|
106
|
+
case "Tag":
|
|
107
|
+
case "tag":
|
|
108
|
+
return value instanceof CalcitTag;
|
|
109
|
+
case "Symbol":
|
|
110
|
+
case "symbol":
|
|
111
|
+
return value instanceof CalcitSymbol;
|
|
112
|
+
case "Nil":
|
|
113
|
+
case "nil":
|
|
114
|
+
return value === null;
|
|
115
|
+
case "Unit":
|
|
116
|
+
case "unit":
|
|
117
|
+
return value === undefined;
|
|
118
|
+
case "Never":
|
|
119
|
+
return false;
|
|
120
|
+
case "Buffer":
|
|
121
|
+
case "buffer":
|
|
122
|
+
return value instanceof Uint8Array;
|
|
123
|
+
case "CirruQuote":
|
|
124
|
+
case "cirru-quote":
|
|
125
|
+
return value instanceof CalcitCirruQuote;
|
|
126
|
+
case "List":
|
|
127
|
+
case "list":
|
|
128
|
+
return requireEvidence ? null : isList(value);
|
|
129
|
+
case "Map":
|
|
130
|
+
case "map":
|
|
131
|
+
return requireEvidence ? null : isMap(value);
|
|
132
|
+
case "Set":
|
|
133
|
+
case "set":
|
|
134
|
+
return requireEvidence ? null : value instanceof CalcitSet;
|
|
135
|
+
case "Ref":
|
|
136
|
+
case "ref":
|
|
137
|
+
return requireEvidence ? null : value instanceof CalcitRef;
|
|
138
|
+
case "Fn":
|
|
139
|
+
case "fn":
|
|
140
|
+
return typeof value === "function";
|
|
141
|
+
case "Struct":
|
|
142
|
+
case "struct":
|
|
143
|
+
case "record":
|
|
144
|
+
return requireEvidence ? null : isStructValue(value);
|
|
145
|
+
case "Enum":
|
|
146
|
+
case "enum":
|
|
147
|
+
case "tuple":
|
|
148
|
+
return requireEvidence ? null : value instanceof CalcitEnumValue;
|
|
149
|
+
}
|
|
150
|
+
// Deep/nominal contracts must use the compiler's resolved validator. A
|
|
151
|
+
// short-name match or an erased generic cannot establish runtime evidence.
|
|
152
|
+
if (requireEvidence)
|
|
153
|
+
return null;
|
|
154
|
+
// Core Struct and Enum definitions (bare names resolve to them through the
|
|
155
|
+
// implicit core import) are known here, so a reference to one admits only
|
|
156
|
+
// values of that definition, as the native matcher does.
|
|
157
|
+
if (CORE_NOMINAL_DEFS.has(name)) {
|
|
158
|
+
if (isStructValue(value))
|
|
159
|
+
return value.structRef.definitionRef === `calcit.core/${name}`;
|
|
160
|
+
return value instanceof CalcitEnumValue && value.enumPrototype?.prototype.structRef.definitionRef === `calcit.core/${name}`;
|
|
161
|
+
}
|
|
162
|
+
// Other names may be type variables, aliases or traits that cannot be
|
|
163
|
+
// resolved here. A qualified reference is checked against the value's own
|
|
164
|
+
// Struct or Enum name; other values are left to the static checker like
|
|
165
|
+
// unresolved forms.
|
|
166
|
+
const shortName = name.includes("/") ? name.slice(name.lastIndexOf("/") + 1) : name;
|
|
167
|
+
if (isStructValue(value))
|
|
168
|
+
return value.name.value === shortName || !name.includes("/");
|
|
169
|
+
if (value instanceof CalcitEnumValue && value.enumPrototype != null) {
|
|
170
|
+
return value.enumPrototype.name() === shortName || !name.includes("/");
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
};
|
|
174
|
+
/// Struct and Enum definitions of `calcit.core` usable as field types.
|
|
175
|
+
const CORE_NOMINAL_DEFS = new Set([
|
|
176
|
+
"Option",
|
|
177
|
+
"Result",
|
|
178
|
+
"Data",
|
|
179
|
+
"ListDestruct",
|
|
180
|
+
"MapDestruct",
|
|
181
|
+
"SetDestruct",
|
|
182
|
+
"StringDestruct",
|
|
183
|
+
"MapEntry",
|
|
184
|
+
"RuntimeMapMeta",
|
|
185
|
+
"RuntimeMapResponse",
|
|
186
|
+
]);
|
|
187
|
+
// Duck-typed to avoid a module cycle with js-struct-value.
|
|
188
|
+
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
|
|
3
|
+
"version": "0.29.0",
|
|
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",
|