@calcit/procs 0.29.0-alpha.26 → 0.29.0-alpha.28

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.
@@ -37,6 +37,7 @@ export declare let newTag: (content: string) => CalcitTag;
37
37
  export declare let castTag: (x: CalcitValue) => CalcitTag;
38
38
  export declare var refsRegistry: Map<string, CalcitRef>;
39
39
  export declare let hashFunction: (x: CalcitValue) => Hash;
40
+ export declare let format_calcit_number: (x: number) => string;
40
41
  export declare let toString: (x: CalcitValue, escaped: boolean, disableJsDataWarning?: boolean) => string;
41
42
  export declare let to_js_data: (x: CalcitValue, options?: CalcitValue) => any;
42
43
  export declare let _$n_map_$o_get: (xs: CalcitValue, k: CalcitValue) => CalcitValue;
@@ -447,6 +447,38 @@ let hashCirru = (base, x) => {
447
447
  };
448
448
  // Dirty code to change ternary-tree behavior
449
449
  overwriteHashGenerator(hashFunction);
450
+ // Calcit number text, shared by `turn-string`, `str` and printing (see docs/data/number.md).
451
+ export let format_calcit_number = (x) => {
452
+ // Match native f64 Display, which expands finite exponents into decimal notation.
453
+ if (Object.is(x, -0)) {
454
+ return "-0";
455
+ }
456
+ if (x === Infinity) {
457
+ return "inf";
458
+ }
459
+ if (x === -Infinity) {
460
+ return "-inf";
461
+ }
462
+ const text = x.toString();
463
+ const exponentIndex = text.indexOf("e");
464
+ if (exponentIndex < 0) {
465
+ return text;
466
+ }
467
+ const mantissa = text.slice(0, exponentIndex);
468
+ const exponent = Number(text.slice(exponentIndex + 1));
469
+ const sign = mantissa.startsWith("-") ? "-" : "";
470
+ const unsigned = sign ? mantissa.slice(1) : mantissa;
471
+ const decimalPoint = unsigned.indexOf(".");
472
+ const decimalIndex = (decimalPoint < 0 ? unsigned.length : decimalPoint) + exponent;
473
+ const digits = unsigned.replace(".", "");
474
+ if (decimalIndex <= 0) {
475
+ return `${sign}0.${"0".repeat(-decimalIndex)}${digits}`;
476
+ }
477
+ if (decimalIndex >= digits.length) {
478
+ return `${sign}${digits}${"0".repeat(decimalIndex - digits.length)}`;
479
+ }
480
+ return `${sign}${digits.slice(0, decimalIndex)}.${digits.slice(decimalIndex)}`;
481
+ };
450
482
  export let toString = (x, escaped, disableJsDataWarning = false) => {
451
483
  if (x === null) {
452
484
  return "nil";
@@ -468,7 +500,7 @@ export let toString = (x, escaped, disableJsDataWarning = false) => {
468
500
  }
469
501
  }
470
502
  if (typeof x === "number") {
471
- return x.toString();
503
+ return format_calcit_number(x);
472
504
  }
473
505
  if (typeof x === "boolean") {
474
506
  return x.toString();
@@ -3,7 +3,7 @@ export const calcit_version = pkg.version;
3
3
  export const calcit_package_json = pkg;
4
4
  import { parse } from "@cirru/parser.ts";
5
5
  import { writeCirruCode } from "@cirru/writer.ts";
6
- import { CalcitSymbol, CalcitTag, CalcitRecur, castTag, newTag, refsRegistry, toString, getStringName, compareTagNames, _$n__$e_, hashFunction, } from "./calcit-data.mjs";
6
+ import { CalcitSymbol, CalcitTag, CalcitRecur, castTag, newTag, refsRegistry, toString, format_calcit_number, getStringName, compareTagNames, _$n__$e_, hashFunction, } from "./calcit-data.mjs";
7
7
  import { CalcitRef, atom } from "./js-ref.mjs";
8
8
  import { CalcitStructValue } from "./js-struct-value.mjs";
9
9
  import { CalcitImpl } from "./js-impl.mjs";
@@ -930,14 +930,25 @@ export let add_watch_$x_ = (a, k, f) => {
930
930
  if (!(typeof f === "function")) {
931
931
  throw new Error("Expected watcher function");
932
932
  }
933
+ for (const listenerKey of a.listeners.keys()) {
934
+ if (listenerKey instanceof CalcitTag && listenerKey.value === k.value) {
935
+ throw new Error(`add-watch! failed: listener with key \`${k.value}\` already existed`);
936
+ }
937
+ }
933
938
  a.listeners.set(k, f);
934
939
  };
935
940
  export let remove_watch = (a, k) => {
936
- if (!a.listeners.delete(k)) {
937
- throw new Error(`remove-watch failed: listener with key \`${k.value}\` not found`);
941
+ for (const listenerKey of a.listeners.keys()) {
942
+ if (listenerKey instanceof CalcitTag && listenerKey.value === k.value) {
943
+ a.listeners.delete(listenerKey);
944
+ return;
945
+ }
938
946
  }
947
+ throw new Error(`remove-watch failed: listener with key \`${k.value}\` not found`);
939
948
  };
940
- const MAX_RANGE_LENGTH = 4294967295;
949
+ // Same eager range limit as native `MAX_RANGE_LEN` and WASM, 2^24 elements (#1852).
950
+ const MAX_RANGE_LENGTH = 2 ** 24;
951
+ const rangeTooLarge = () => new Error(`&list:range result is too large: more than ${MAX_RANGE_LENGTH} elements`);
941
952
  export let range = (n, m, step = 1) => {
942
953
  const base = m == null ? 0 : n;
943
954
  const bound = m == null ? n : m;
@@ -955,11 +966,13 @@ export let range = (n, m, step = 1) => {
955
966
  ? Math.ceil(Math.abs(base) / Math.abs(step) + Math.abs(bound) / Math.abs(step))
956
967
  : Math.ceil((bound - base) / step);
957
968
  if (!Number.isFinite(estimatedLength) || estimatedLength > MAX_RANGE_LENGTH) {
958
- throw new Error("&list:range result is too large");
969
+ throw rangeTooLarge();
959
970
  }
960
971
  const result = [];
961
972
  if (step > 0) {
962
973
  for (let value = base; value < bound;) {
974
+ if (result.length >= MAX_RANGE_LENGTH)
975
+ throw rangeTooLarge();
963
976
  result.push(value);
964
977
  const next = value + step;
965
978
  if (next === value) {
@@ -970,6 +983,8 @@ export let range = (n, m, step = 1) => {
970
983
  }
971
984
  else {
972
985
  for (let value = base; value > bound;) {
986
+ if (result.length >= MAX_RANGE_LENGTH)
987
+ throw rangeTooLarge();
973
988
  result.push(value);
974
989
  const next = value + step;
975
990
  if (next === value) {
@@ -1149,11 +1164,12 @@ export let _$n_list_$o_slice = (xs, from, to) => {
1149
1164
  if (to == null) {
1150
1165
  to = size;
1151
1166
  }
1152
- else if (to <= from) {
1153
- return new CalcitSliceList([]);
1167
+ // Bounds are integers with 0 <= from <= to <= size, as in native (docs/data/number.md).
1168
+ if (!Number.isInteger(from) || !Number.isInteger(to) || from < 0 || from > to || to > size) {
1169
+ throw new Error(`&list:slice expected integer bounds within 0..${size}, but received: ${from} ${to}`);
1154
1170
  }
1155
- else if (to > size) {
1156
- to = size;
1171
+ if (from === to) {
1172
+ return new CalcitSliceList([]);
1157
1173
  }
1158
1174
  return xs.slice(from, to);
1159
1175
  };
@@ -1351,13 +1367,19 @@ export let cos = (n) => {
1351
1367
  return Math.cos(n);
1352
1368
  };
1353
1369
  export let pow = (n, m) => {
1370
+ // IEEE 754 pow, which Math.pow breaks: pow(1, y) is 1 for any y, pow(-1, ±Infinity) is 1.
1371
+ if (n === 1 || (n === -1 && (m === Infinity || m === -Infinity))) {
1372
+ return 1;
1373
+ }
1354
1374
  return Math.pow(n, m);
1355
1375
  };
1356
1376
  export let ceil = (n) => {
1357
1377
  return Math.ceil(n);
1358
1378
  };
1359
1379
  export let round = (n) => {
1360
- return Math.round(n);
1380
+ // Ties round away from zero; `Math.round` alone rounds them toward +Infinity.
1381
+ // Negating keeps `-0` for inputs such as -0.4.
1382
+ return n < 0 ? -Math.round(-n) : Math.round(n);
1361
1383
  };
1362
1384
  export let _$n_number_$o_fract = (n) => {
1363
1385
  return n - Math.floor(n);
@@ -1510,6 +1532,10 @@ export let _$n_number_$o_format = (x, n) => {
1510
1532
  return x.toFixed(n);
1511
1533
  };
1512
1534
  export let _$n_number_$o_display_by = (x, n) => {
1535
+ // a non-negative safe integer, printed exactly (docs/data/number.md)
1536
+ if (typeof x !== "number" || !Number.isSafeInteger(x) || x < 0) {
1537
+ throw new Error(`&number:display-by expected a non-negative safe integer, but received: ${x}`);
1538
+ }
1513
1539
  switch (n) {
1514
1540
  case 2:
1515
1541
  return `0b${x.toString(2)}`;
@@ -1681,37 +1707,6 @@ export let quit_$x_ = (code) => {
1681
1707
  throw new Error(`quit!(${code})`);
1682
1708
  }
1683
1709
  };
1684
- let format_calcit_number = (x) => {
1685
- // Match native f64 Display, which expands finite exponents into decimal notation.
1686
- if (Object.is(x, -0)) {
1687
- return "-0";
1688
- }
1689
- if (x === Infinity) {
1690
- return "inf";
1691
- }
1692
- if (x === -Infinity) {
1693
- return "-inf";
1694
- }
1695
- const text = x.toString();
1696
- const exponentIndex = text.indexOf("e");
1697
- if (exponentIndex < 0) {
1698
- return text;
1699
- }
1700
- const mantissa = text.slice(0, exponentIndex);
1701
- const exponent = Number(text.slice(exponentIndex + 1));
1702
- const sign = mantissa.startsWith("-") ? "-" : "";
1703
- const unsigned = sign ? mantissa.slice(1) : mantissa;
1704
- const decimalPoint = unsigned.indexOf(".");
1705
- const decimalIndex = (decimalPoint < 0 ? unsigned.length : decimalPoint) + exponent;
1706
- const digits = unsigned.replace(".", "");
1707
- if (decimalIndex <= 0) {
1708
- return `${sign}0.${"0".repeat(-decimalIndex)}${digits}`;
1709
- }
1710
- if (decimalIndex >= digits.length) {
1711
- return `${sign}${digits}${"0".repeat(decimalIndex - digits.length)}`;
1712
- }
1713
- return `${sign}${digits.slice(0, decimalIndex)}.${digits.slice(decimalIndex)}`;
1714
- };
1715
1710
  export let turn_string = (x) => {
1716
1711
  if (x === null) {
1717
1712
  return "";
@@ -2971,23 +2966,30 @@ export let _$n_map_$o_diff_triple = (a, b) => {
2971
2966
  throw new Error("&map:diff-triple expected 2 maps");
2972
2967
  }
2973
2968
  };
2969
+ // Bitwise operands are integers within i32 (docs/data/number.md); JS would otherwise wrap or truncate them.
2970
+ let bit_operand = (name, x) => {
2971
+ if (typeof x !== "number" || !Number.isInteger(x) || x < -2147483648 || x > 2147483647) {
2972
+ throw new Error(`&math:${name} operand expected an integer within i32, but got: ${x}`);
2973
+ }
2974
+ return x;
2975
+ };
2974
2976
  export let bit_shr = (base, step) => {
2975
- return base >> step;
2977
+ return bit_operand("bit-shr", base) >> bit_operand("bit-shr", step);
2976
2978
  };
2977
2979
  export let bit_shl = (base, step) => {
2978
- return base << step;
2980
+ return bit_operand("bit-shl", base) << bit_operand("bit-shl", step);
2979
2981
  };
2980
2982
  export let bit_and = (a, b) => {
2981
- return a & b;
2983
+ return bit_operand("bit-and", a) & bit_operand("bit-and", b);
2982
2984
  };
2983
2985
  export let bit_or = (a, b) => {
2984
- return a | b;
2986
+ return bit_operand("bit-or", a) | bit_operand("bit-or", b);
2985
2987
  };
2986
2988
  export let bit_xor = (a, b) => {
2987
- return a ^ b;
2989
+ return bit_operand("bit-xor", a) ^ bit_operand("bit-xor", b);
2988
2990
  };
2989
2991
  export let bit_not = (a) => {
2990
- return ~a;
2992
+ return ~bit_operand("bit-not", a);
2991
2993
  };
2992
2994
  export let _$n_list_$o_to_set = (xs) => {
2993
2995
  var result = [];
@@ -3010,11 +3012,41 @@ export let _$n_list_$o_distinct = (xs) => {
3010
3012
  }
3011
3013
  return new CalcitSliceList(result);
3012
3014
  };
3015
+ // Longest padded string, the V8 string length limit, so every backend fails at the same size.
3016
+ const MAX_PADDED_LEN = 536870888;
3017
+ // Padding for `&str:pad-left` / `&str:pad-right`, measured in Unicode scalars like `&str:count`
3018
+ // (not UTF-16 units, so astral characters are never split). NaN and negative lengths pad nothing,
3019
+ // and so does an empty pattern.
3020
+ const strPadding = (procName, s, size, pattern) => {
3021
+ if (size > MAX_PADDED_LEN) {
3022
+ throw new Error(`${procName} expected a finite length up to ${MAX_PADDED_LEN}, but received: ${format_calcit_number(size)}`);
3023
+ }
3024
+ if (!(size > 0) || pattern === "")
3025
+ return "";
3026
+ let missing = Math.floor(size);
3027
+ for (const _scalar of s) {
3028
+ missing -= 1;
3029
+ if (missing <= 0)
3030
+ return "";
3031
+ }
3032
+ let patternCount = 0;
3033
+ for (const _scalar of pattern)
3034
+ patternCount += 1;
3035
+ let padding = pattern.repeat(Math.floor(missing / patternCount));
3036
+ let rest = missing % patternCount;
3037
+ for (const scalar of pattern) {
3038
+ if (rest <= 0)
3039
+ break;
3040
+ padding += scalar;
3041
+ rest -= 1;
3042
+ }
3043
+ return padding;
3044
+ };
3013
3045
  export let _$n_str_$o_pad_left = (s, size, pattern) => {
3014
- return s.padStart(size, pattern);
3046
+ return strPadding("&str:pad-left", s, size, pattern) + s;
3015
3047
  };
3016
3048
  export let _$n_str_$o_pad_right = (s, size, pattern) => {
3017
- return s.padEnd(size, pattern);
3049
+ return s + strPadding("&str:pad-right", s, size, pattern);
3018
3050
  };
3019
3051
  export let _$n_get_os = () => {
3020
3052
  return newTag("js-engine");
@@ -12,7 +12,7 @@ export declare class CalcitCirruQuote {
12
12
  /** provide a simple text representation in Console or std out, with indentations */
13
13
  textForm(): string;
14
14
  }
15
- export declare let format_cirru: (data: CalcitCirruQuote | CalcitList, useInline: boolean) => string;
15
+ export declare let format_cirru: (data: CalcitCirruQuote | CalcitList, useInline?: boolean) => string;
16
16
  export declare let format_cirru_one_liner: (data: CalcitCirruQuote | CalcitList) => string;
17
17
  /** better use string version of Cirru EDN in future */
18
18
  export declare let to_cirru_edn: (x: CalcitValue) => CirruEdnFormat;
package/lib/js-cirru.mjs CHANGED
@@ -160,7 +160,12 @@ export class CalcitCirruQuote {
160
160
  }
161
161
  }
162
162
  }
163
- export let format_cirru = (data, useInline) => {
163
+ export let format_cirru = function (data, useInline) {
164
+ if (arguments.length < 2)
165
+ useInline = false;
166
+ if (typeof useInline !== "boolean") {
167
+ throw new Error("format-cirru requires a boolean inline option");
168
+ }
164
169
  if (data instanceof CalcitCirruQuote) {
165
170
  return writeCirruCode(data.value, { useInline });
166
171
  }
@@ -563,7 +568,9 @@ export let extract_cirru_edn = (x, options) => {
563
568
  export let extract_cirru_edn_for_typed = (x, options) => {
564
569
  return extract_cirru_edn_inner(x, options, true);
565
570
  };
566
- export let format_cirru_edn = (data, useInline = true) => {
571
+ export let format_cirru_edn = function (data, useInline) {
572
+ if (arguments.length < 2)
573
+ useInline = true;
567
574
  if (typeof useInline !== "boolean") {
568
575
  throw new Error("format-cirru-edn requires a boolean inline option");
569
576
  }
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.29.0-alpha.26",
3
+ "version": "0.29.0-alpha.28",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "files": [
6
6
  "lib/",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.29.0-alpha.26",
3
+ "version": "0.29.0-alpha.28",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "files": [
6
6
  "lib/",