@calcit/procs 0.28.0-alpha.3 → 0.29.0-alpha.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.
@@ -27,6 +27,8 @@ export type CalcitFn = (...xs: CalcitValue[]) => CalcitValue;
27
27
  export declare let getStringName: (x: CalcitValue) => string;
28
28
  /** Compare tag names by Unicode scalar value, matching Rust `str` ordering. */
29
29
  export declare function compareTagNames(x: CalcitTag, y: CalcitTag): number;
30
+ /** Compare scalar values, matching Rust string ordering rather than UTF-16. */
31
+ export declare function compareUnicodeStrings(x: string, y: string): number;
30
32
  export declare function tagNamesAreCanonical(fields: CalcitTag[]): boolean;
31
33
  export declare function canonicalizeTagPairs<T>(fields: CalcitTag[], values: T[], context: string): [CalcitTag[], T[]];
32
34
  /** returns -1 when not found */
@@ -107,11 +107,15 @@ export let getStringName = (x) => {
107
107
  };
108
108
  /** Compare tag names by Unicode scalar value, matching Rust `str` ordering. */
109
109
  export function compareTagNames(x, y) {
110
+ return compareUnicodeStrings(x.value, y.value);
111
+ }
112
+ /** Compare scalar values, matching Rust string ordering rather than UTF-16. */
113
+ export function compareUnicodeStrings(x, y) {
110
114
  let xIdx = 0;
111
115
  let yIdx = 0;
112
- while (xIdx < x.value.length && yIdx < y.value.length) {
113
- const xCode = x.value.codePointAt(xIdx);
114
- const yCode = y.value.codePointAt(yIdx);
116
+ while (xIdx < x.length && yIdx < y.length) {
117
+ const xCode = x.codePointAt(xIdx);
118
+ const yCode = y.codePointAt(yIdx);
115
119
  if (xCode < yCode)
116
120
  return -1;
117
121
  if (xCode > yCode)
@@ -119,9 +123,9 @@ export function compareTagNames(x, y) {
119
123
  xIdx += xCode > 0xffff ? 2 : 1;
120
124
  yIdx += yCode > 0xffff ? 2 : 1;
121
125
  }
122
- if (xIdx < x.value.length)
126
+ if (xIdx < x.length)
123
127
  return 1;
124
- if (yIdx < y.value.length)
128
+ if (yIdx < y.length)
125
129
  return -1;
126
130
  return 0;
127
131
  }
@@ -221,6 +225,9 @@ let defaultHash_struct = valueHash("struct:");
221
225
  let defaultHash_enum = valueHash("enum:");
222
226
  let defaultHash_cirru_quote = valueHash("cirru-quote:");
223
227
  let defaultHash_unknown = valueHash("unknown:");
228
+ let defaultHash_buffer = valueHash("buffer:");
229
+ /** Identity hashes for host values, kept outside the objects so frozen or DOM objects are never mutated. */
230
+ let hostValueHashes = new WeakMap();
224
231
  let fnHashCounter = 0;
225
232
  let jsObjectHashCounter = 0;
226
233
  export let hashFunction = (x) => {
@@ -261,15 +268,20 @@ export let hashFunction = (x) => {
261
268
  if (typeof x === "function") {
262
269
  // method values are closures created on the fly (see invoke_method_closure);
263
270
  // hash by method name so equal methods share the same hash, matching isEqual
271
+ // cache in a WeakMap instead of writing onto the function, so frozen functions hash safely
272
+ const cachedFn = hostValueHashes.get(x);
273
+ if (cachedFn != null) {
274
+ return cachedFn;
275
+ }
264
276
  const methodName = x.__calcitMethodName;
265
277
  if (methodName != null) {
266
278
  let h = mergeValueHash(defaultHash_fn, methodName);
267
- x[calcit_dirty_hash_key] = h;
279
+ hostValueHashes.set(x, h);
268
280
  return h;
269
281
  }
270
282
  fnHashCounter = fnHashCounter + 1;
271
283
  let h = mergeValueHash(defaultHash_fn, fnHashCounter);
272
- x[calcit_dirty_hash_key] = h;
284
+ hostValueHashes.set(x, h);
273
285
  return h;
274
286
  }
275
287
  if (x instanceof CalcitRef) {
@@ -344,6 +356,8 @@ export let hashFunction = (x) => {
344
356
  }
345
357
  if (x instanceof CalcitStructValue) {
346
358
  let base = defaultHash_record;
359
+ base = mergeValueHash(base, hashFunction(x.name));
360
+ base = mergeValueHash(base, hashFunction(x.structRef.definitionRef));
347
361
  for (let idx = 0; idx < x.fields.length; idx++) {
348
362
  base = mergeValueHash(base, hashFunction(x.fields[idx]));
349
363
  base = mergeValueHash(base, hashFunction(x.values[idx]));
@@ -391,15 +405,28 @@ export let hashFunction = (x) => {
391
405
  base = hashCirru(base, x.value);
392
406
  return base;
393
407
  }
394
- console.warn(`[warn] calcit-js has no method for hashing this: ${x}`);
395
- // currently we use dirty solution here to generate a custom hash
396
- // probably happening in .to-pairs of maps, putting a js object into a set
397
- // better forbid this, use .to-list instead
398
- let hashJsObject = defaultHash_unknown;
408
+ if (x instanceof Uint8Array) {
409
+ // Buffers are values: equal bytes must hash equally, matching native Buffer equality.
410
+ let base = mergeValueHash(defaultHash_buffer, x.length);
411
+ for (let idx = 0; idx < x.length; idx++) {
412
+ base = mergeValueHash(base, x[idx]);
413
+ }
414
+ return base;
415
+ }
416
+ if (typeof x === "object" || typeof x === "function") {
417
+ // Host values compare by identity, so they hash by identity as well.
418
+ let cached = hostValueHashes.get(x);
419
+ if (cached != null) {
420
+ return cached;
421
+ }
422
+ jsObjectHashCounter = jsObjectHashCounter + 1;
423
+ let hashHostValue = mergeValueHash(defaultHash_unknown, jsObjectHashCounter);
424
+ hostValueHashes.set(x, hashHostValue);
425
+ return hashHostValue;
426
+ }
427
+ console.warn(`[warn] calcit-js has no method for hashing this: ${String(x)}`);
399
428
  jsObjectHashCounter = jsObjectHashCounter + 1;
400
- hashJsObject = mergeValueHash(hashJsObject, jsObjectHashCounter);
401
- x[calcit_dirty_hash_key] = hashJsObject;
402
- return hashJsObject;
429
+ return mergeValueHash(defaultHash_unknown, jsObjectHashCounter);
403
430
  };
404
431
  /// traverse Cirru tree to make unique hash
405
432
  let hashCirru = (base, x) => {
@@ -758,6 +785,9 @@ export let _$n__$e_ = (x, y) => {
758
785
  if (x.name !== y.name) {
759
786
  return false;
760
787
  }
788
+ if (x.structRef.definitionRef !== y.structRef.definitionRef) {
789
+ return false;
790
+ }
761
791
  if (!fieldsEqual(x.fields, y.fields)) {
762
792
  return false;
763
793
  }
@@ -817,6 +847,22 @@ export let _$n__$e_ = (x, y) => {
817
847
  }
818
848
  return false;
819
849
  }
850
+ if (x instanceof Uint8Array) {
851
+ if (!(y instanceof Uint8Array) || x.length !== y.length) {
852
+ return false;
853
+ }
854
+ for (let idx = 0; idx < x.length; idx++) {
855
+ if (x[idx] !== y[idx]) {
856
+ return false;
857
+ }
858
+ }
859
+ return true;
860
+ }
861
+ if (tx === "object") {
862
+ // Host objects (DOM nodes, JS class instances, BufList) compare by identity;
863
+ // identical references were accepted at the top of this function.
864
+ return false;
865
+ }
820
866
  throw new Error("Missing handler for this type");
821
867
  };
822
868
  // overwrite internary comparator of ternary-tree
@@ -48,6 +48,7 @@ export declare const calcit_package_json: {
48
48
  url: string;
49
49
  };
50
50
  dependencies: {
51
+ "@calcit/finger-vec": string;
51
52
  "@calcit/ternary-tree": string;
52
53
  "@cirru/parser.ts": string;
53
54
  "@cirru/writer.ts": string;
@@ -86,7 +87,9 @@ import { CalcitSet } from "./js-set.mjs";
86
87
  import { CalcitEnumValue } from "./js-enum-value.mjs";
87
88
  import { CalcitCirruQuote } from "./js-cirru.mjs";
88
89
  export declare let type_of: (x: any) => CalcitTag;
89
- export declare let _$n_trait_$o__$o_new: (name: CalcitValue, methods: CalcitValue) => CalcitTrait;
90
+ /** Check host member shape without invoking methods or claiming their signature is verified. */
91
+ export declare const js_cast: (value: unknown, trait: string, fields: readonly string[], methods: readonly string[]) => object;
92
+ export declare let _$n_trait_$o__$o_new: (name: CalcitValue, methods: CalcitValue, parents?: CalcitValue) => CalcitTrait;
90
93
  export declare let _$n_assert_traits: (value: CalcitValue, traitDef: CalcitValue) => CalcitValue;
91
94
  export declare let defstruct: (name: CalcitValue, ...entries: CalcitValue[]) => CalcitStructDef;
92
95
  export declare let defenum: (name: CalcitValue, ...variants: CalcitValue[]) => CalcitEnumDef;
@@ -185,6 +188,7 @@ export declare let _$n__GT_: (a: number, b: number) => boolean;
185
188
  export declare let _$n__LT_: (a: number, b: number) => boolean;
186
189
  export declare let _$n__: (a: number, b: number) => number;
187
190
  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`. */
188
192
  export declare let _$n_number_$o_rem: (a: number, b: number) => number;
189
193
  export declare let _$n_number_$o_fits_$q_: (value: number, target: CalcitTag) => boolean;
190
194
  export declare let round_$q_: (a: number) => boolean;
@@ -355,6 +359,7 @@ export declare function invoke_method_closure(p: string): (obj: CalcitValue, ...
355
359
  export declare function invoke_method(p: string, obj: CalcitValue, ...args: CalcitValue[]): CalcitValue;
356
360
  export declare function _$n_methods_of(obj: CalcitValue): CalcitSliceList;
357
361
  export declare function _$n_inspect_methods(obj: CalcitValue, note: CalcitValue): CalcitValue;
362
+ /** Dispatch through one nominal origin; duplicate implementations never select by order. */
358
363
  export declare function _$n_trait_call(traitDef: CalcitValue, method: CalcitValue, obj: CalcitValue, ...args: CalcitValue[]): CalcitValue;
359
364
  export declare let _$n_map_$o_to_list: (m: CalcitValue) => CalcitSliceList;
360
365
  export declare let _$n_map_$o_keys: (m: CalcitValue) => CalcitSet;
@@ -33,7 +33,7 @@ import { CalcitSet } from "./js-set.mjs";
33
33
  import { CalcitEnumValue } from "./js-enum-value.mjs";
34
34
  import { to_calcit_data, extract_cirru_edn, extract_cirru_edn_for_typed, CalcitCirruQuote } from "./js-cirru.mjs";
35
35
  import { TypedEdnSetView } from "./typed-edn.mjs";
36
- import { initTernaryTreeList } from "@calcit/ternary-tree";
36
+ import { FingerVec } from "@calcit/finger-vec";
37
37
  let inNodeJs = typeof process !== "undefined" && process?.release?.name === "node";
38
38
  export let type_of = (x) => {
39
39
  if (typeof x === "string") {
@@ -104,6 +104,52 @@ export let type_of = (x) => {
104
104
  }
105
105
  throw new Error(`Unknown data ${x}`);
106
106
  };
107
+ // The intrinsic getter reads typed-array slots across realms, ignoring a forged Symbol.toStringTag.
108
+ const typedArrayTag = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint8Array.prototype), Symbol.toStringTag).get;
109
+ /** Check host member shape without invoking methods or claiming their signature is verified. */
110
+ export const js_cast = (value, trait, fields, methods) => {
111
+ const fail = (detail, cause) => {
112
+ const error = new TypeError(`js-cast ${trait}: ${detail}`);
113
+ Object.defineProperty(error, "cause", { value: cause, configurable: true });
114
+ throw error;
115
+ };
116
+ // Calcit objects (including Buffers) are not interchangeable with external handles.
117
+ // Keep the runtime's own value classification authoritative instead of another registry.
118
+ let host;
119
+ try {
120
+ host = value !== null && typeof value === "object"
121
+ && !(ArrayBuffer.isView(value) && typedArrayTag.call(value) === "Uint8Array")
122
+ && !(value instanceof CalcitRecur) && type_of(value).value === "js-object";
123
+ }
124
+ catch (cause) {
125
+ return fail("host classification failed", cause);
126
+ }
127
+ if (!host || value === null || typeof value !== "object")
128
+ return fail("expected a JavaScript host object");
129
+ for (const field of fields) {
130
+ let present;
131
+ try {
132
+ present = field in value;
133
+ }
134
+ catch (cause) {
135
+ return fail(`field ${JSON.stringify(field)} lookup failed`, cause);
136
+ }
137
+ if (!present)
138
+ return fail(`missing field ${JSON.stringify(field)}`);
139
+ }
140
+ for (const method of methods) {
141
+ let member;
142
+ try {
143
+ member = Reflect.get(value, method);
144
+ }
145
+ catch (cause) {
146
+ return fail(`method ${JSON.stringify(method)} lookup failed`, cause);
147
+ }
148
+ if (typeof member !== "function")
149
+ return fail(`method ${JSON.stringify(method)} is not callable`);
150
+ }
151
+ return value;
152
+ };
107
153
  const list_items = (item) => {
108
154
  if (item instanceof CalcitList || item instanceof CalcitSliceList) {
109
155
  return Array.from(item.items());
@@ -161,9 +207,9 @@ const trimDataDefinitionEntries = (entries) => {
161
207
  }
162
208
  return entries.slice(start);
163
209
  };
164
- export let _$n_trait_$o__$o_new = function (name, methods) {
165
- if (arguments.length !== 2)
166
- throw new Error("&trait::new expected 2 arguments");
210
+ export let _$n_trait_$o__$o_new = function (name, methods, parents) {
211
+ if (arguments.length !== 2 && arguments.length !== 3)
212
+ throw new Error("&trait::new expected 2 or 3 arguments");
167
213
  const items = list_items(methods);
168
214
  const methodNames = [];
169
215
  const methodTypes = [];
@@ -175,7 +221,39 @@ export let _$n_trait_$o__$o_new = function (name, methods) {
175
221
  methodNames.push(pair[0]);
176
222
  methodTypes.push(pair[1]);
177
223
  }
178
- return new CalcitTrait(name, methodNames, methodTypes);
224
+ const requires = parents == null ? [] : list_items(parents);
225
+ for (const parent of requires) {
226
+ if (!(parent instanceof CalcitTrait)) {
227
+ throw new Error(`trait ${toString(name, false)} 'requires expects a trait, but received: ${toString(parent, true)}`);
228
+ }
229
+ }
230
+ const traitDef = new CalcitTrait(name, methodNames, methodTypes, requires);
231
+ const owners = new Map();
232
+ for (const reachable of traitDef.reachable()) {
233
+ for (const member of reachable.methods) {
234
+ const owner = owners.get(member.value);
235
+ if (owner != null && owner !== reachable) {
236
+ throw new Error(`member \`${member.value}\` is declared by both ${owner.name.toString()} and ${reachable.name.toString()}; keep it on one trait`);
237
+ }
238
+ owners.set(member.value, reachable);
239
+ }
240
+ }
241
+ return traitDef;
242
+ };
243
+ /** Every attached impl needs impls of all traits its origin requires on the same type. */
244
+ let checkRequiredTraitImpls = (impls, procName) => {
245
+ for (const impl of impls) {
246
+ const origin = impl?.origin;
247
+ if (origin == null)
248
+ continue;
249
+ for (const required of origin.reachable()) {
250
+ if (required === origin)
251
+ continue;
252
+ if (!impls.some((candidate) => candidate?.origin === required)) {
253
+ throw new Error(`${procName}: an impl of trait ${origin.name.toString()} requires an impl of trait ${required.name.toString()} on the same type; attach one before or with it`);
254
+ }
255
+ }
256
+ }
179
257
  };
180
258
  export let _$n_assert_traits = function (value, traitDef) {
181
259
  if (arguments.length !== 2)
@@ -205,6 +283,10 @@ export let _$n_assert_traits = function (value, traitDef) {
205
283
  if (missing.length > 0) {
206
284
  throw new Error(`assert-traits failed: impl ${selected.name.toString()} for trait ${traitDef.name.toString()} is incomplete. Missing: ${missing.join(" ")}`);
207
285
  }
286
+ const unmet = traitDef.reachable().find((required) => required !== traitDef && !impls.some((impl) => impl?.origin === required));
287
+ if (unmet != null) {
288
+ throw new Error(`assert-traits failed: ${toString(value, true)} implements ${traitDef.name.toString()} but not its required trait ${unmet.name.toString()}`);
289
+ }
208
290
  return value;
209
291
  };
210
292
  export let defstruct = (name, ...entries) => {
@@ -597,6 +679,7 @@ export let _$n_enum_$o_impl_traits = function (x, ...traits) {
597
679
  if (!(x instanceof CalcitEnumValue))
598
680
  throw new Error("&enum:impl-traits expects an enum value");
599
681
  const impls = traits.map((trait) => coerce_impl(trait, "&enum:impl-traits"));
682
+ checkRequiredTraitImpls([...(x.enumPrototype?.impls ?? []), ...impls], "&enum:impl-traits");
600
683
  let proto = x.enumPrototype;
601
684
  if (proto == null) {
602
685
  const tagName = x.tag instanceof CalcitTag ? x.tag : newTag("tag");
@@ -733,7 +816,8 @@ export let _$n_struct_$o_impl_traits = function (xs, ...traits) {
733
816
  if (!(xs instanceof CalcitStructValue))
734
817
  throw new Error("&struct:impl-traits expected a struct value");
735
818
  const impls = traits.map((trait) => coerce_impl(trait, "&struct:impl-traits"));
736
- const nextStruct = new CalcitStructDef(xs.name, xs.fields, xs.structRef.fieldTypes, xs.structRef.impls.concat(impls));
819
+ checkRequiredTraitImpls(xs.structRef.impls.concat(impls), "&struct:impl-traits");
820
+ const nextStruct = new CalcitStructDef(xs.name, xs.fields, xs.structRef.fieldTypes, xs.structRef.impls.concat(impls), xs.structRef.definitionRef);
737
821
  return new CalcitStructValue(xs.name, xs.fields, xs.values, nextStruct);
738
822
  };
739
823
  export let _$n_struct_def_$o_impl_traits = function (xs, ...traits) {
@@ -743,13 +827,15 @@ export let _$n_struct_def_$o_impl_traits = function (xs, ...traits) {
743
827
  throw new Error("&struct-def:impl-traits expected a struct definition");
744
828
  const addedImpls = traits.map((trait) => coerce_impl(trait, "&struct-def:impl-traits"));
745
829
  const baseImpls = xs.impls ?? [];
746
- return new CalcitStructDef(xs.name, xs.fields, xs.fieldTypes, baseImpls.concat(addedImpls));
830
+ checkRequiredTraitImpls(baseImpls.concat(addedImpls), "&struct-def:impl-traits");
831
+ return new CalcitStructDef(xs.name, xs.fields, xs.fieldTypes, baseImpls.concat(addedImpls), xs.definitionRef);
747
832
  };
748
833
  export let _$n_enum_def_$o_impl_traits = function (xs, ...traits) {
749
834
  if (traits.length < 1)
750
835
  throw new Error("&enum-def:impl-traits takes 2+ arguments");
751
836
  const addedImpls = traits.map((trait) => coerce_impl(trait, "&enum-def:impl-traits"));
752
837
  if (xs instanceof CalcitEnumDef) {
838
+ checkRequiredTraitImpls([...(xs.impls ?? []), ...addedImpls], "&enum-def:impl-traits");
753
839
  return xs.withImpls(addedImpls);
754
840
  }
755
841
  throw new Error("&enum-def:impl-traits expected an enum definition");
@@ -1114,8 +1200,15 @@ export let _$n__ = (a, b) => {
1114
1200
  export let _$n__SLSH_ = (a, b) => {
1115
1201
  return a / b;
1116
1202
  };
1203
+ /** Truncated remainder over safe integers, matching native and WASM; the result is never `-0`. */
1117
1204
  export let _$n_number_$o_rem = (a, b) => {
1118
- return a % b;
1205
+ if (!Number.isSafeInteger(a) || !Number.isSafeInteger(b)) {
1206
+ throw new Error(`&number:rem requires safe integers, but received: ${toString(a, false)} ${toString(b, false)}`);
1207
+ }
1208
+ if (b === 0) {
1209
+ throw new Error("&number:rem divisor must not be zero");
1210
+ }
1211
+ return (a % b) + 0;
1119
1212
  };
1120
1213
  export let _$n_number_$o_fits_$q_ = (value, target) => {
1121
1214
  if (!(target instanceof CalcitTag)) {
@@ -1476,7 +1569,7 @@ export let _$n_get_env = (name, v0) => {
1476
1569
  export let get_env = _$n_get_env;
1477
1570
  export let _$n_get_args = () => {
1478
1571
  const args = inNodeJs ? process.argv : [];
1479
- return new CalcitList(initTernaryTreeList(args));
1572
+ return new CalcitList(FingerVec.from(args));
1480
1573
  };
1481
1574
  export let get_args = _$n_get_args;
1482
1575
  /** Block for an integral millisecond duration and preserve host failures as Result values. */
@@ -2679,6 +2772,7 @@ export function _$n_inspect_methods(obj, note) {
2679
2772
  console.log("\n");
2680
2773
  return obj;
2681
2774
  }
2775
+ /** Dispatch through one nominal origin; duplicate implementations never select by order. */
2682
2776
  export function _$n_trait_call(traitDef, method, obj, ...args) {
2683
2777
  if (arguments.length < 3) {
2684
2778
  throw new Error("&trait-call expected 3+ arguments (trait, method, receiver, & args)");
@@ -2697,20 +2791,20 @@ export function _$n_trait_call(traitDef, method, obj, ...args) {
2697
2791
  throw new Error(`&trait-call cannot resolve impls for: ${toString(obj, true)}`);
2698
2792
  }
2699
2793
  const impls = pair[0];
2700
- const reverse = obj instanceof CalcitStructValue || obj instanceof CalcitEnumValue || obj instanceof CalcitStructDef || obj instanceof CalcitEnumDef;
2701
- let idx = reverse ? impls.length - 1 : 0;
2702
- while (reverse ? idx >= 0 : idx < impls.length) {
2703
- const impl = impls[idx];
2704
- if (impl != null && impl.origin === traitDef) {
2705
- const fn = impl.getOrNil(methodName);
2706
- if (fn != null) {
2707
- if (typeof fn !== "function") {
2708
- throw new Error(`&trait-call: method :${methodName} for trait ${traitDef.name.toString()} is not a function: ${toString(fn, true)}`);
2709
- }
2710
- return fn(obj, ...args);
2794
+ // Explicit trait selection disambiguates origins, not implementations of
2795
+ // one origin. Check all candidates before invoking any method.
2796
+ const matchingImpls = impls.filter((impl) => impl != null && impl.origin === traitDef);
2797
+ if (matchingImpls.length > 1) {
2798
+ throw new Error(`[E_DUPLICATE_TRAIT_IMPL] &trait-call: trait ${traitDef.name.toString()} has ${matchingImpls.length} implementations attached to the same receiver; dispatch cannot choose between duplicate impls`);
2799
+ }
2800
+ if (matchingImpls.length === 1) {
2801
+ const fn = matchingImpls[0].getOrNil(methodName);
2802
+ if (fn != null) {
2803
+ if (typeof fn !== "function") {
2804
+ throw new Error(`&trait-call: method :${methodName} for trait ${traitDef.name.toString()} is not a function: ${toString(fn, true)}`);
2711
2805
  }
2806
+ return fn(obj, ...args);
2712
2807
  }
2713
- idx += reverse ? -1 : 1;
2714
2808
  }
2715
2809
  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.`);
2716
2810
  }
package/lib/js-list.d.mts CHANGED
@@ -1,10 +1,11 @@
1
+ import { FingerVec } from "@calcit/finger-vec";
2
+ import { Hash } from "@calcit/ternary-tree";
1
3
  import { CalcitValue } from "./js-primes.mjs";
2
- import { TernaryTreeList, Hash } from "@calcit/ternary-tree";
3
4
  import { CalcitFn } from "./calcit-data.mjs";
4
5
  export declare class CalcitList {
5
- value: TernaryTreeList<CalcitValue>;
6
+ value: FingerVec<CalcitValue>;
6
7
  cachedHash: Hash;
7
- constructor(value: TernaryTreeList<CalcitValue>);
8
+ constructor(value: FingerVec<CalcitValue>);
8
9
  len(): number;
9
10
  get(idx: number): CalcitValue;
10
11
  assoc(idx: number, v: CalcitValue): CalcitList;
package/lib/js-list.mjs CHANGED
@@ -1,6 +1,5 @@
1
- import * as ternaryTree from "@calcit/ternary-tree";
1
+ import { FingerVec } from "@calcit/finger-vec";
2
2
  import { isLiteral } from "./js-primes.mjs";
3
- import { initTernaryTreeList, initTernaryTreeListFromRange, listLen, listGet, assocList, listToItems, dissocList, assocBefore, assocAfter, } from "@calcit/ternary-tree";
4
3
  import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
5
4
  import { CalcitSet } from "./js-set.mjs";
6
5
  import { CalcitEnumValue } from "./js-enum-value.mjs";
@@ -10,35 +9,38 @@ export class CalcitList {
10
9
  constructor(value) {
11
10
  this.cachedHash = null;
12
11
  if (value == null) {
13
- this.value = initTernaryTreeList([]);
12
+ this.value = FingerVec.empty();
14
13
  }
15
14
  else {
16
15
  this.value = value;
17
16
  }
18
17
  }
19
18
  len() {
20
- return listLen(this.value);
19
+ return this.value.size;
21
20
  }
22
21
  get(idx) {
23
- if (this.len() === 0) {
22
+ if (this.value.size === 0) {
24
23
  return null;
25
24
  }
26
- return listGet(this.value, idx);
25
+ if (idx < 0 || idx >= this.value.size) {
26
+ throw new Error(`Index ${idx} out of range for list of size ${this.value.size}`);
27
+ }
28
+ return this.value.get(idx);
27
29
  }
28
30
  assoc(idx, v) {
29
- return new CalcitList(assocList(this.value, idx, v));
31
+ return new CalcitList(this.value.assoc(idx, v));
30
32
  }
31
33
  assocBefore(idx, v) {
32
- return new CalcitList(assocBefore(this.value, idx, v));
34
+ return new CalcitList(this.value.assocBefore(idx, v));
33
35
  }
34
36
  assocAfter(idx, v) {
35
- return new CalcitList(assocAfter(this.value, idx, v));
37
+ return new CalcitList(this.value.assocAfter(idx, v));
36
38
  }
37
39
  dissoc(idx) {
38
- return new CalcitList(dissocList(this.value, idx));
40
+ return new CalcitList(this.value.dissoc(idx));
39
41
  }
40
42
  slice(from, to) {
41
- return new CalcitList(ternaryTree.slice(this.value, from, to));
43
+ return new CalcitList(this.value.slice(from, to));
42
44
  }
43
45
  toString(shorter = false, disableJsDataWarning = false) {
44
46
  let result = "";
@@ -53,43 +55,46 @@ export class CalcitList {
53
55
  return `([]${result})`;
54
56
  }
55
57
  isEmpty() {
56
- return this.len() === 0;
58
+ return this.value.size === 0;
57
59
  }
58
60
  /** usage: `for of` */
59
61
  items() {
60
- return listToItems(this.value);
62
+ return this.value.items();
61
63
  }
62
64
  append(v) {
63
- return new CalcitList(ternaryTree.append(this.value, v));
65
+ return new CalcitList(this.value.pushRight(v));
64
66
  }
65
67
  prepend(v) {
66
- return new CalcitList(ternaryTree.prepend(this.value, v));
68
+ return new CalcitList(this.value.pushLeft(v));
67
69
  }
68
70
  first() {
69
- return ternaryTree.first(this.value);
71
+ if (this.value.size === 0) {
72
+ throw new Error("Cannot get from empty list");
73
+ }
74
+ return this.value.first();
70
75
  }
71
76
  rest() {
72
- return new CalcitList(ternaryTree.rest(this.value));
77
+ return new CalcitList(this.value.rest());
73
78
  }
74
79
  concat(ys) {
75
80
  if (ys instanceof CalcitSliceList) {
76
- return new CalcitList(ternaryTree.concat(this.value, ys.turnListMode().value));
81
+ return new CalcitList(this.value.concat(ys.turnListMode().value));
77
82
  }
78
83
  else if (ys instanceof CalcitList) {
79
- return new CalcitList(ternaryTree.concat(this.value, ys.value));
84
+ return new CalcitList(this.value.concat(ys.value));
80
85
  }
81
86
  else {
82
87
  throw new Error(`Unknown data to concat: ${ys}`);
83
88
  }
84
89
  }
85
90
  map(f) {
86
- return new CalcitList(ternaryTree.listMapValues(this.value, f));
91
+ return new CalcitList(this.value.map(f));
87
92
  }
88
93
  toArray() {
89
- return [...ternaryTree.listToItems(this.value)];
94
+ return this.value.toArray();
90
95
  }
91
96
  reverse() {
92
- return new CalcitList(ternaryTree.reverse(this.value));
97
+ return new CalcitList(this.value.reverse());
93
98
  }
94
99
  nestedDataInChildren() {
95
100
  let size = this.len();
@@ -98,6 +103,7 @@ export class CalcitList {
98
103
  return true;
99
104
  }
100
105
  }
106
+ return false;
101
107
  }
102
108
  }
103
109
  // represent append-only immutable list in Array slices
@@ -115,7 +121,7 @@ export class CalcitSliceList {
115
121
  if (this.cachedTreeListRef != null) {
116
122
  return this.cachedTreeListRef;
117
123
  }
118
- let ret = new CalcitList(initTernaryTreeListFromRange(this.value, this.start, this.end));
124
+ let ret = new CalcitList(FingerVec.fromRange(this.value, this.start, this.end));
119
125
  this.cachedTreeListRef = ret;
120
126
  return ret;
121
127
  }
@@ -241,8 +247,8 @@ export class CalcitSliceList {
241
247
  }
242
248
  map(f) {
243
249
  let ys = [];
244
- for (let x in sliceGenerator(this.value, this.start, this.end)) {
245
- ys.push(f(x));
250
+ for (let idx = this.start; idx < this.end; idx++) {
251
+ ys.push(f(this.value[idx]));
246
252
  }
247
253
  return new CalcitSliceList(ys);
248
254
  }
package/lib/js-primes.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { CalcitTag, CalcitSymbol, CalcitRecur } from "./calcit-data.mjs";
1
+ import { CalcitTag, CalcitSymbol, CalcitRecur, compareUnicodeStrings } from "./calcit-data.mjs";
2
2
  import { CalcitRef } from "./js-ref.mjs";
3
3
  import { CalcitList, CalcitSliceList } from "./js-list.mjs";
4
4
  import { CalcitStructValue } from "./js-struct-value.mjs";
@@ -101,6 +101,17 @@ let rawCompare = (x, y) => {
101
101
  return 0;
102
102
  }
103
103
  };
104
+ /** lexicographic order over two sequences of values, a shorter prefix sorts first (same as Rust `Vec::cmp`) */
105
+ let compareSequences = (xs, ys) => {
106
+ let n = Math.min(xs.length, ys.length);
107
+ for (let i = 0; i < n; i++) {
108
+ let order = _$n_compare(xs[i], ys[i]);
109
+ if (order !== 0)
110
+ return order;
111
+ }
112
+ return rawCompare(xs.length, ys.length);
113
+ };
114
+ let listToArray = (x) => Array.from(x.items());
104
115
  export let _$n_compare = (a, b) => {
105
116
  if (a === b)
106
117
  return 0;
@@ -123,8 +134,72 @@ export let _$n_compare = (a, b) => {
123
134
  return rawCompare(a, b);
124
135
  case PseudoTypeIndex.ref:
125
136
  return rawCompare(a.path, b.path);
137
+ case PseudoTypeIndex.struct_value: {
138
+ const left = a;
139
+ const right = b;
140
+ const nameOrder = compareUnicodeStrings(left.name.value, right.name.value);
141
+ if (nameOrder !== 0)
142
+ return nameOrder;
143
+ const leftRef = left.structRef.definitionRef;
144
+ const rightRef = right.structRef.definitionRef;
145
+ if (leftRef !== rightRef) {
146
+ if (leftRef == null)
147
+ return -1;
148
+ if (rightRef == null)
149
+ return 1;
150
+ return compareUnicodeStrings(leftRef, rightRef);
151
+ }
152
+ for (let index = 0; index < Math.min(left.fields.length, right.fields.length); index++) {
153
+ const fieldOrder = compareUnicodeStrings(left.fields[index].value, right.fields[index].value);
154
+ if (fieldOrder !== 0)
155
+ return fieldOrder;
156
+ }
157
+ const fieldCountOrder = rawCompare(left.fields.length, right.fields.length);
158
+ if (fieldCountOrder !== 0)
159
+ return fieldCountOrder;
160
+ for (let index = 0; index < Math.min(left.values.length, right.values.length); index++) {
161
+ const valueOrder = _$n_compare(left.values[index], right.values[index]);
162
+ if (valueOrder !== 0)
163
+ return valueOrder;
164
+ }
165
+ return rawCompare(left.values.length, right.values.length);
166
+ }
126
167
  case PseudoTypeIndex.cirru_quote:
127
168
  return rawCompare(a, b); // TODO not stable
169
+ case PseudoTypeIndex.list:
170
+ return compareSequences(listToArray(a), listToArray(b));
171
+ case PseudoTypeIndex.enum_value: {
172
+ const left = a;
173
+ const right = b;
174
+ const tagOrder = _$n_compare(left.tag, right.tag);
175
+ if (tagOrder !== 0)
176
+ return tagOrder;
177
+ return compareSequences(left.extra, right.extra);
178
+ }
179
+ case PseudoTypeIndex.set: {
180
+ // like native: smaller sets first, then the sorted elements
181
+ const left = Array.from(a.values());
182
+ const right = Array.from(b.values());
183
+ const sizeOrder = rawCompare(left.length, right.length);
184
+ if (sizeOrder !== 0)
185
+ return sizeOrder;
186
+ return compareSequences(left.sort(_$n_compare), right.sort(_$n_compare));
187
+ }
188
+ case PseudoTypeIndex.map: {
189
+ // like native: smaller maps first, then the (key, value) pairs sorted by key and value
190
+ const sortPairs = (pairs) => pairs.sort((x, y) => _$n_compare(x[0], y[0]) || _$n_compare(x[1], y[1]));
191
+ const left = sortPairs(a.pairs());
192
+ const right = sortPairs(b.pairs());
193
+ const sizeOrder = rawCompare(left.length, right.length);
194
+ if (sizeOrder !== 0)
195
+ return sizeOrder;
196
+ for (let i = 0; i < left.length; i++) {
197
+ const order = _$n_compare(left[i][0], right[i][0]) || _$n_compare(left[i][1], right[i][1]);
198
+ if (order !== 0)
199
+ return order;
200
+ }
201
+ return 0;
202
+ }
128
203
  default:
129
204
  // TODO, need more accurate solution
130
205
  if (a < b) {
@@ -7,7 +7,10 @@ export declare class CalcitStructDef {
7
7
  fieldTypes: CalcitValue[];
8
8
  impls: CalcitImpl[];
9
9
  cachedHash: number;
10
- constructor(name: CalcitTag, fields: CalcitTag[], fieldTypes: CalcitValue[], impls?: CalcitImpl[]);
10
+ readonly definitionRef: string | null;
11
+ constructor(name: CalcitTag, fields: CalcitTag[], fieldTypes: CalcitValue[], impls?: CalcitImpl[], definitionRef?: string | null);
11
12
  withImpls(impls: CalcitImpl | CalcitImpl[]): CalcitStructDef;
12
13
  toString(disableJsDataWarning?: boolean): string;
13
14
  }
15
+ /** Match native definition binding without replacing an alias's existing identity. */
16
+ export declare function bind_struct_definition(value: CalcitValue, definitionRef: string): CalcitValue;
@@ -1,20 +1,21 @@
1
1
  import { canonicalizeTagPairs, toString } from "./calcit-data.mjs";
2
2
  import { CalcitImpl } from "./js-impl.mjs";
3
3
  export class CalcitStructDef {
4
- constructor(name, fields, fieldTypes, impls = []) {
4
+ constructor(name, fields, fieldTypes, impls = [], definitionRef = null) {
5
5
  const [canonicalFields, canonicalTypes] = canonicalizeTagPairs(fields, fieldTypes, "CalcitStructDef");
6
6
  this.name = name;
7
7
  this.fields = canonicalFields;
8
8
  this.fieldTypes = canonicalTypes;
9
- this.impls = impls;
9
+ this.impls = impls ?? [];
10
10
  this.cachedHash = null;
11
+ this.definitionRef = definitionRef;
11
12
  }
12
13
  withImpls(impls) {
13
14
  if (impls instanceof CalcitImpl) {
14
- return new CalcitStructDef(this.name, this.fields, this.fieldTypes, [impls]);
15
+ return new CalcitStructDef(this.name, this.fields, this.fieldTypes, [impls], this.definitionRef);
15
16
  }
16
17
  else if (Array.isArray(impls)) {
17
- return new CalcitStructDef(this.name, this.fields, this.fieldTypes, impls);
18
+ return new CalcitStructDef(this.name, this.fields, this.fieldTypes, impls, this.definitionRef);
18
19
  }
19
20
  throw new Error("Expected an impl as implementation");
20
21
  }
@@ -32,3 +33,10 @@ export class CalcitStructDef {
32
33
  return parts.join("");
33
34
  }
34
35
  }
36
+ /** Match native definition binding without replacing an alias's existing identity. */
37
+ export function bind_struct_definition(value, definitionRef) {
38
+ if (value instanceof CalcitStructDef && value.definitionRef == null) {
39
+ return new CalcitStructDef(value.name, value.fields, value.fieldTypes, value.impls, definitionRef);
40
+ }
41
+ return value;
42
+ }
@@ -116,7 +116,7 @@ export class CalcitStructValue {
116
116
  else {
117
117
  throw new Error("Expected an impl or array of impls");
118
118
  }
119
- let nextStruct = new CalcitStructDef(this.name, this.fields, this.structRef.fieldTypes, this.structRef.impls.concat(nextImpls));
119
+ let nextStruct = new CalcitStructDef(this.name, this.fields, this.structRef.fieldTypes, this.structRef.impls.concat(nextImpls), this.structRef.definitionRef);
120
120
  return new CalcitStructValue(this.name, this.fields, this.values, nextStruct);
121
121
  }
122
122
  }
@@ -4,6 +4,9 @@ export declare class CalcitTrait {
4
4
  name: CalcitTag;
5
5
  methods: CalcitTag[];
6
6
  methodTypes: CalcitValue[];
7
- constructor(name: CalcitValue, methods: CalcitValue[], methodTypes: CalcitValue[]);
7
+ requires: CalcitTrait[];
8
+ constructor(name: CalcitValue, methods: CalcitValue[], methodTypes: CalcitValue[], requires?: CalcitTrait[]);
9
+ /** This trait and every transitively required trait, each origin once. */
10
+ reachable(): CalcitTrait[];
8
11
  toString(disableJsDataWarning?: boolean): string;
9
12
  }
package/lib/js-trait.mjs CHANGED
@@ -1,9 +1,26 @@
1
1
  import { castTag } from "./calcit-data.mjs";
2
2
  export class CalcitTrait {
3
- constructor(name, methods, methodTypes) {
3
+ constructor(name, methods, methodTypes, requires = []) {
4
4
  this.name = castTag(name);
5
5
  this.methods = methods.map(castTag);
6
6
  this.methodTypes = methodTypes;
7
+ this.requires = requires;
8
+ }
9
+ /** This trait and every transitively required trait, each origin once. */
10
+ reachable() {
11
+ const output = [];
12
+ const visit = (current, active) => {
13
+ if (active.includes(current)) {
14
+ throw new Error(`trait requires cycle: ${[...active, current].map((item) => item.name.toString()).join(" -> ")}`);
15
+ }
16
+ if (output.includes(current))
17
+ return;
18
+ for (const required of current.requires)
19
+ visit(required, [...active, current]);
20
+ output.push(current);
21
+ };
22
+ visit(this, []);
23
+ return output;
7
24
  }
8
25
  toString(disableJsDataWarning = false) {
9
26
  const parts = ["(trait ", this.name.toString()];
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.28.0-alpha.3",
3
+ "version": "0.29.0-alpha.1",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "files": [
6
6
  "lib/",
@@ -32,7 +32,7 @@
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
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",
35
- "check-strict-default": "cargo build --bin calcit && node scripts/check-strict-default.mjs && node scripts/check-known-assertion.mjs",
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
36
  "check-agent-interface": "cargo build --bin calcit && node scripts/check-agent-interface.mjs",
37
37
  "try-all": "yarn check-all",
38
38
  "try-core-tests": "cargo run --bin calcit -- src/cirru/calcit-core.cirru test --tag unit --summary-only --require-match",
@@ -50,8 +50,9 @@
50
50
  "url": "git+https://github.com/calcit-lang/calcit.git"
51
51
  },
52
52
  "dependencies": {
53
+ "@calcit/finger-vec": "0.1.1",
53
54
  "@calcit/ternary-tree": "0.0.26",
54
- "@cirru/parser.ts": "^0.0.9",
55
+ "@cirru/parser.ts": "0.0.10",
55
56
  "@cirru/writer.ts": "^0.1.9"
56
57
  },
57
58
  "packageManager": "yarn@4.12.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.28.0-alpha.3",
3
+ "version": "0.29.0-alpha.1",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "files": [
6
6
  "lib/",
@@ -32,7 +32,7 @@
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
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",
35
- "check-strict-default": "cargo build --bin calcit && node scripts/check-strict-default.mjs && node scripts/check-known-assertion.mjs",
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
36
  "check-agent-interface": "cargo build --bin calcit && node scripts/check-agent-interface.mjs",
37
37
  "try-all": "yarn check-all",
38
38
  "try-core-tests": "cargo run --bin calcit -- src/cirru/calcit-core.cirru test --tag unit --summary-only --require-match",
@@ -50,8 +50,9 @@
50
50
  "url": "git+https://github.com/calcit-lang/calcit.git"
51
51
  },
52
52
  "dependencies": {
53
+ "@calcit/finger-vec": "0.1.1",
53
54
  "@calcit/ternary-tree": "0.0.26",
54
- "@cirru/parser.ts": "^0.0.9",
55
+ "@cirru/parser.ts": "0.0.10",
55
56
  "@cirru/writer.ts": "^0.1.9"
56
57
  },
57
58
  "packageManager": "yarn@4.12.0"