squint-cljs 0.14.199 → 0.14.201

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.
@@ -23,6 +23,9 @@ function findKey(iter, tar, key) {
23
23
  function isSortedMap(m) {
24
24
  return m != null && m[SORTED_TAG] === true && m[TYPE_TAG] === MAP_TYPE;
25
25
  }
26
+ function isSetLike(s) {
27
+ return s != null && (s instanceof Set || s[TYPE_TAG] === SET_TYPE);
28
+ }
26
29
  function isMapLike(m) {
27
30
  return (
28
31
  m != null &&
@@ -62,8 +65,33 @@ function dequal(foo, bar) {
62
65
  return true;
63
66
  }
64
67
 
68
+ // A plain object and a js/Map are both map reps; compare by entries.
69
+ // Same-type pairs skip this and keep their fast paths below.
70
+ if (isMapLike(foo) && isMapLike(bar) && foo.constructor !== bar.constructor) {
71
+ if (mapCount(foo) !== mapCount(bar)) return false;
72
+ for (const k of foo instanceof Map ? foo.keys() : Object.keys(foo)) {
73
+ if (!mapHas(bar, k) || !dequal(mapGet(foo, k), mapGet(bar, k))) return false;
74
+ }
75
+ return true;
76
+ }
77
+
78
+ // Sets (hash or sorted) compare by elements, across concrete types.
79
+ if (isSetLike(foo) || isSetLike(bar)) {
80
+ if (!isSetLike(foo) || !isSetLike(bar) || foo.size !== bar.size) return false;
81
+ for (let e of foo) {
82
+ if (e && typeof e === 'object') {
83
+ e = findKey(bar, e);
84
+ if (!e) return false;
85
+ }
86
+ if (!bar.has(e)) return false;
87
+ }
88
+ return true;
89
+ }
90
+
65
91
  if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
66
92
  if (ctor === Date) return foo.getTime() === bar.getTime();
93
+ // regexes only compare by identity, like CLJS
94
+ if (ctor === RegExp) return false;
67
95
 
68
96
  if (ctor === Array) {
69
97
  if ((len = foo.length) === bar.length) {
@@ -72,21 +100,6 @@ function dequal(foo, bar) {
72
100
  return len === -1;
73
101
  }
74
102
 
75
- if (ctor === Set) {
76
- if (foo.size !== bar.size) {
77
- return false;
78
- }
79
- for (const elt of foo) {
80
- tmp = elt;
81
- if (tmp && typeof tmp === 'object') {
82
- tmp = findKey(bar, tmp);
83
- if (!tmp) return false;
84
- }
85
- if (!bar.has(tmp)) return false;
86
- }
87
- return true;
88
- }
89
-
90
103
  if (ctor === Map) {
91
104
  if (foo.size !== bar.size) {
92
105
  return false;
@@ -271,9 +284,24 @@ function copy(o) {
271
284
  }
272
285
 
273
286
  export function assoc(o, k, v, ...kvs) {
274
- if (!o) {
287
+ // only nil puns to an empty map; assoc on false throws, like CLJS
288
+ if (o == null) {
275
289
  o = {};
276
290
  }
291
+ if (isVectorArray(o)) {
292
+ // like CLJS: a vector key is an index in [0, count], count appends
293
+ let len = o.length;
294
+ for (let i = 0; i < kvs.length + 2; i += 2) {
295
+ const key = i === 0 ? k : kvs[i - 2];
296
+ if (!Number.isInteger(key)) {
297
+ throw new Error("Vector's key for assoc must be a number.");
298
+ }
299
+ if (key < 0 || key > len) {
300
+ throw new Error(`Index ${key} out of bounds [0,${len}]`);
301
+ }
302
+ if (key === len) len++;
303
+ }
304
+ }
277
305
  const ret = copy(o);
278
306
  assoc_BANG_(ret, k, v, ...kvs);
279
307
  return ret;
@@ -282,6 +310,9 @@ export function assoc(o, k, v, ...kvs) {
282
310
  // squint has no distinct hash-map or array-map type; both build a plain object.
283
311
  export function hash_map(...kvs) {
284
312
  if (kvs.length === 0) return {};
313
+ if (kvs.length % 2 !== 0) {
314
+ throw new Error('No value supplied for key: ' + kvs[kvs.length - 1]);
315
+ }
285
316
  return assoc({}, ...kvs);
286
317
  }
287
318
 
@@ -355,6 +386,8 @@ function typeConst(obj) {
355
386
  const tag = obj[TYPE_TAG];
356
387
  if (tag !== undefined) return tag;
357
388
  if (isVectorArray(obj)) return ARRAY_TYPE;
389
+ // any remaining object (class instance, null-proto) is associative
390
+ if (typeof obj === 'object') return OBJECT_TYPE;
358
391
 
359
392
  return undefined;
360
393
  }
@@ -441,6 +474,10 @@ export function conj_BANG_(...xs) {
441
474
  if (n === 0) {
442
475
  return vector();
443
476
  }
477
+ // single arg: return the coll unchanged, including nil, like CLJS
478
+ if (n === 1) {
479
+ return xs[0];
480
+ }
444
481
 
445
482
  let o = xs[0];
446
483
  if (o === null || o === undefined) {
@@ -474,17 +511,14 @@ export function conj_BANG_(...xs) {
474
511
  break;
475
512
  case MAP_TYPE:
476
513
  for (const x of rest) {
477
- if (!Array.isArray(x))
478
- iterable(x).forEach((kv) => {
479
- o.set(kv[0], kv[1]);
480
- });
481
- else o.set(x[0], x[1]);
514
+ if (isVectorArray(x)) { asMapEntry(x); o.set(x[0], x[1]); }
515
+ else for (const kv of mapEntriesOf(x)) o.set(kv[0], kv[1]);
482
516
  }
483
517
  break;
484
518
  case OBJECT_TYPE:
485
519
  for (const x of rest) {
486
- if (!Array.isArray(x)) Object.assign(o, x);
487
- else o[x[0]] = x[1];
520
+ if (isVectorArray(x)) { asMapEntry(x); o[x[0]] = x[1]; }
521
+ else for (const kv of mapEntriesOf(x)) o[kv[0]] = kv[1];
488
522
  }
489
523
  break;
490
524
  default:
@@ -496,6 +530,28 @@ export function conj_BANG_(...xs) {
496
530
  return o;
497
531
  }
498
532
 
533
+ // entries carried by a non-entry conj arg onto a map: a map merges, a
534
+ // seqable must contain entry vectors, like CLJS
535
+ function* mapEntriesOf(x) {
536
+ if (isMapLike(x)) {
537
+ yield* iterable(x);
538
+ return;
539
+ }
540
+ for (const kv of iterable(x)) {
541
+ if (!isVectorArray(kv)) {
542
+ throw new Error('conj on a map takes map entries or seqables of map entries');
543
+ }
544
+ yield kv;
545
+ }
546
+ }
547
+
548
+ function asMapEntry(x) {
549
+ if (x.length < 2) {
550
+ throw new Error('Vector arg to map conj must be a pair');
551
+ }
552
+ return x;
553
+ }
554
+
499
555
  export function conj(...xs) {
500
556
  if (xs.length === 0) {
501
557
  return vector();
@@ -527,11 +583,8 @@ export function conj(...xs) {
527
583
  case MAP_TYPE:
528
584
  m = new Map(o);
529
585
  for (const x of rest) {
530
- if (!Array.isArray(x))
531
- iterable(x).forEach((kv) => {
532
- m.set(kv[0], kv[1]);
533
- });
534
- else m.set(x[0], x[1]);
586
+ if (isVectorArray(x)) { asMapEntry(x); m.set(x[0], x[1]); }
587
+ else for (const kv of mapEntriesOf(x)) m.set(kv[0], kv[1]);
535
588
  }
536
589
 
537
590
  return copyMeta(o, m);
@@ -544,8 +597,8 @@ export function conj(...xs) {
544
597
  o2 = { ...o };
545
598
 
546
599
  for (const x of rest) {
547
- if (!Array.isArray(x)) Object.assign(o2, x);
548
- else o2[x[0]] = x[1];
600
+ if (isVectorArray(x)) { asMapEntry(x); o2[x[0]] = x[1]; }
601
+ else for (const kv of mapEntriesOf(x)) o2[kv[0]] = kv[1];
549
602
  }
550
603
 
551
604
  return copyMeta(o, o2);
@@ -567,7 +620,7 @@ export function disj(s, ...xs) {
567
620
  if (s == null) return s;
568
621
  // pass s itself (not a spread) so a SortedSet keeps its comparator
569
622
  const s1 = new s.constructor(s);
570
- return disj_BANG_(s1, ...xs);
623
+ return copyMeta(s, disj_BANG_(s1, ...xs));
571
624
  }
572
625
 
573
626
  export function contains_QMARK_(coll, v) {
@@ -596,7 +649,11 @@ export function dissoc_BANG_(m, ...ks) {
596
649
  export function dissoc(m, ...ks) {
597
650
  if (!m) return;
598
651
  if (ks.length === 0) return m;
599
- if (typeConst(m) === MAP_TYPE) {
652
+ const tc = typeConst(m);
653
+ if (tc !== MAP_TYPE && tc !== OBJECT_TYPE) {
654
+ throw new Error('dissoc expects a map, got: ' + typeof m);
655
+ }
656
+ if (tc === MAP_TYPE) {
600
657
  let present = false;
601
658
  for (const k of ks) if (m.has(k)) { present = true; break; }
602
659
  if (!present) return m;
@@ -638,6 +695,9 @@ export function pr(...xs) {
638
695
  }
639
696
 
640
697
  export function nth(coll, idx, orElse) {
698
+ if (typeof idx !== 'number') {
699
+ throw new Error('Index argument to nth must be a number');
700
+ }
641
701
  const hasDefault = arguments.length > 2;
642
702
  // nil coll puns to nil, like Clojure
643
703
  if (coll == null) return hasDefault ? orElse : null;
@@ -724,7 +784,8 @@ export function seqable_QMARK_(x) {
724
784
  object_QMARK_(x) ||
725
785
  // we used to check instanceof Object but this returns false for TC39 Records
726
786
  // also we used to write `Symbol.iterator in` but this does not work for strings and some other types
727
- !!x[Symbol.iterator]
787
+ !!x[Symbol.iterator] ||
788
+ !!x[ISEQABLE_SYM]
728
789
  );
729
790
  }
730
791
 
@@ -754,6 +815,8 @@ export function iterable(x) {
754
815
  if (x[Symbol.iterator]) {
755
816
  return x;
756
817
  }
818
+ // a type extended to ISeqable seqs through its -seq method
819
+ if (x[ISeqable__seq] !== undefined) return iterable(x[ISeqable__seq](x));
757
820
  if (x instanceof Object) return Object.entries(x).map(tagMapEntry);
758
821
  throw new TypeError(`${x} is not iterable`);
759
822
  }
@@ -770,6 +833,7 @@ export const es6_iterator = _iterator;
770
833
 
771
834
  export function seq(x) {
772
835
  if (x == null) return x;
836
+ if (!seqable_QMARK_(x)) throw new TypeError(x + ' is not ISeqable');
773
837
  // a string seqs into its characters, like CLJS.
774
838
  if (typeof x === 'string') return x.length ? [...x] : null;
775
839
  const iter = iterable(x);
@@ -830,13 +894,13 @@ export function rest(coll) {
830
894
  return cell._rest; // first chunk had one element; the next cell is the rest
831
895
  }
832
896
 
897
+ const REDUCED_DEREF = (self) => self.value;
898
+
833
899
  class Reduced {
834
900
  value;
835
901
  constructor(x) {
836
902
  this.value = x;
837
- }
838
- _deref() {
839
- return this.value;
903
+ this[IDeref__deref] = REDUCED_DEREF;
840
904
  }
841
905
  }
842
906
 
@@ -996,12 +1060,12 @@ class LazyIterable {
996
1060
  },
997
1061
  };
998
1062
  }
999
- // CLJS supports (.indexOf coll x) on seqs; mirror it so lazy seqs work like
1000
- // arrays. Uses value equality, like cljs.core, and returns -1 when absent.
1063
+ // Mirrors Array.prototype.indexOf so lazy seqs support (.indexOf coll x):
1064
+ // reference equality, returns -1 when absent. Unlike cljs.core, not by value.
1001
1065
  indexOf(x, fromIndex = 0) {
1002
1066
  let i = 0;
1003
1067
  for (const v of this) {
1004
- if (i >= fromIndex && dequal(v, x)) return i;
1068
+ if (i >= fromIndex && v === x) return i;
1005
1069
  i++;
1006
1070
  }
1007
1071
  return -1;
@@ -1125,6 +1189,8 @@ class Cons {
1125
1189
  );
1126
1190
 
1127
1191
  export function cons(x, coll) {
1192
+ // like CLJS cons, which seqs a non-ISeq tail
1193
+ if (!seqable_QMARK_(coll)) throw new TypeError(coll + ' is not ISeqable');
1128
1194
  return new Cons(x, coll);
1129
1195
  }
1130
1196
 
@@ -1288,48 +1354,167 @@ export function nil_QMARK_(v) {
1288
1354
 
1289
1355
  export const PROTOCOL_SENTINEL = {};
1290
1356
 
1357
+ // marker protocols so (satisfies? IAtom x) works, like CLJS. Marked in the
1358
+ // constructor, not on the prototype, so no top-level mutation pins Atom
1359
+ // into bundles that do not use it.
1360
+ const IATOM_SYM = Symbol('squint.core.IAtom');
1361
+ const IDEREF_SYM = Symbol('squint.core.IDeref');
1362
+ const ISEQABLE_SYM = Symbol('squint.core.ISeqable');
1363
+ export const IAtom = { __sym: IATOM_SYM };
1364
+ export const IDeref = { __sym: IDEREF_SYM };
1365
+ // method slot for (-deref x), named like the defprotocol emission so
1366
+ // (extend-type T IDeref (-deref [x] ...)) fills it
1367
+ export const IDeref__deref = Symbol('IDeref_-deref');
1368
+ export function _deref(o) {
1369
+ if (o != null && o[IDeref__deref] !== undefined) return o[IDeref__deref](o);
1370
+ return nilImpl(_deref, 'IDeref.-deref', o)(o);
1371
+ }
1372
+ export const ISeqable = { __sym: ISEQABLE_SYM };
1373
+ export const ISeqable__seq = Symbol('ISeqable_-seq');
1374
+ export function _seq(o) {
1375
+ if (o != null && o[ISeqable__seq] !== undefined) return o[ISeqable__seq](o);
1376
+ return nilImpl(_seq, 'ISeqable.-seq', o)(o);
1377
+ }
1378
+
1379
+ // an extend-type on nil stores the impl on the dispatch fn under null,
1380
+ // the same convention the generated protocol dispatch fns use
1381
+ function nilImpl(dispatchFn, protoMethod, o) {
1382
+ const f = dispatchFn[null];
1383
+ if (f === undefined) throw missing_protocol(protoMethod, o);
1384
+ return f;
1385
+ }
1386
+ export const IReset = { __sym: Symbol('squint.core.IReset') };
1387
+ export const IReset__reset_BANG_ = Symbol('IReset_-reset!');
1388
+ export function _reset_BANG_(o, v) {
1389
+ if (o != null && o[IReset__reset_BANG_] !== undefined) return o[IReset__reset_BANG_](o, v);
1390
+ return nilImpl(_reset_BANG_, 'IReset.-reset!', o)(o, v);
1391
+ }
1392
+ export const ISwap = { __sym: Symbol('squint.core.ISwap') };
1393
+ export const ISwap__swap_BANG_ = Symbol('ISwap_-swap!');
1394
+ export function _swap_BANG_(o, f, ...args) {
1395
+ if (o != null && o[ISwap__swap_BANG_] !== undefined) return o[ISwap__swap_BANG_](o, f, ...args);
1396
+ return nilImpl(_swap_BANG_, 'ISwap.-swap!', o)(o, f, ...args);
1397
+ }
1398
+ export const IWatchable = { __sym: Symbol('squint.core.IWatchable') };
1399
+ export const IWatchable__add_watch = Symbol('IWatchable_-add-watch');
1400
+ export const IWatchable__remove_watch = Symbol('IWatchable_-remove-watch');
1401
+ export const IWatchable__notify_watches = Symbol('IWatchable_-notify-watches');
1402
+ export function _add_watch(o, k, f) {
1403
+ if (o != null && o[IWatchable__add_watch] !== undefined) return o[IWatchable__add_watch](o, k, f);
1404
+ return nilImpl(_add_watch, 'IWatchable.-add-watch', o)(o, k, f);
1405
+ }
1406
+ export function _remove_watch(o, k) {
1407
+ if (o != null && o[IWatchable__remove_watch] !== undefined) return o[IWatchable__remove_watch](o, k);
1408
+ return nilImpl(_remove_watch, 'IWatchable.-remove-watch', o)(o, k);
1409
+ }
1410
+ export function _notify_watches(o, oldv, newv) {
1411
+ if (o != null && o[IWatchable__notify_watches] !== undefined) return o[IWatchable__notify_watches](o, oldv, newv);
1412
+ return nilImpl(_notify_watches, 'IWatchable.-notify-watches', o)(o, oldv, newv);
1413
+ }
1414
+
1415
+ // shared protocol impls: one fn per operation, a pointer per instance
1416
+ const ATOM_DEREF = (self) => self.val;
1417
+ const ATOM_RESET = (self, x) => {
1418
+ if (self._validator && !truth_(self._validator(x))) {
1419
+ throw new Error('Validator rejected reference state');
1420
+ }
1421
+ const old_val = self.val;
1422
+ self.val = x;
1423
+ if (self._hasWatches) {
1424
+ for (const [k, f] of Object.entries(self._watches)) f(k, self, old_val, x);
1425
+ }
1426
+ return x;
1427
+ };
1428
+ const ATOM_SWAP = function (self, f, a, b, xs) {
1429
+ switch (arguments.length) {
1430
+ case 2:
1431
+ return ATOM_RESET(self, f(self.val));
1432
+ case 3:
1433
+ return ATOM_RESET(self, f(self.val, a));
1434
+ case 4:
1435
+ return ATOM_RESET(self, f(self.val, a, b));
1436
+ default:
1437
+ return ATOM_RESET(self, f(self.val, a, b, ...xs));
1438
+ }
1439
+ };
1440
+ const ATOM_ADD_WATCH = (self, k, f) => {
1441
+ self._watches[k] = f;
1442
+ self._hasWatches = true;
1443
+ };
1444
+ const ATOM_REMOVE_WATCH = (self, k) => {
1445
+ delete self._watches[k];
1446
+ };
1447
+ const ATOM_NOTIFY = (self, oldv, newv) => {
1448
+ for (const [k, f] of Object.entries(self._watches)) f(k, self, oldv, newv);
1449
+ };
1450
+
1291
1451
  export class Atom {
1292
1452
  constructor(init) {
1293
1453
  this.val = init;
1294
1454
  this._watches = {};
1295
- this._deref = () => this.val;
1296
1455
  this._hasWatches = false;
1297
- this._reset_BANG_ = (x) => {
1298
- const old_val = this.val;
1299
- this.val = x;
1300
- if (this._hasWatches) {
1301
- for (const entry of Object.entries(this._watches)) {
1302
- const k = entry[0];
1303
- const f = entry[1];
1304
- f(k, this, old_val, x);
1305
- }
1306
- }
1307
- return x;
1308
- };
1309
- this._add_watch = (k, fn) => {
1310
- this._watches[k] = fn;
1311
- this._hasWatches = true;
1312
- };
1313
- this._remove_watch = (k) => {
1314
- delete this._watches[k];
1315
- };
1316
- }
1317
- }
1318
-
1319
- export function atom(init) {
1320
- return new Atom(init);
1456
+ this[IATOM_SYM] = true;
1457
+ this[IDEREF_SYM] = true;
1458
+ this[IDeref__deref] = ATOM_DEREF;
1459
+ this[IReset.__sym] = true;
1460
+ this[IReset__reset_BANG_] = ATOM_RESET;
1461
+ this[ISwap.__sym] = true;
1462
+ this[ISwap__swap_BANG_] = ATOM_SWAP;
1463
+ this[IWatchable.__sym] = true;
1464
+ this[IWatchable__add_watch] = ATOM_ADD_WATCH;
1465
+ this[IWatchable__remove_watch] = ATOM_REMOVE_WATCH;
1466
+ this[IWatchable__notify_watches] = ATOM_NOTIFY;
1467
+ }
1468
+ }
1469
+
1470
+ export function atom(init, ...opts) {
1471
+ const a = new Atom(init);
1472
+ for (let i = 0; i < opts.length; i += 2) {
1473
+ if (opts[i] === 'meta') a[_metaSym] = opts[i + 1];
1474
+ else if (opts[i] === 'validator') a._validator = opts[i + 1];
1475
+ }
1476
+ return a;
1477
+ }
1478
+
1479
+ // the CLJS missing-protocol error, used by every protocol dispatch miss
1480
+ export function missing_protocol(proto, obj) {
1481
+ let ty;
1482
+ if (obj === null) ty = 'null';
1483
+ else if (obj === undefined) ty = 'undefined';
1484
+ else if (Array.isArray(obj)) ty = 'array';
1485
+ else if (typeof obj === 'object' && obj.constructor && obj.constructor !== Object) {
1486
+ ty = obj.constructor.name;
1487
+ } else ty = typeof obj;
1488
+ return new Error(
1489
+ `No protocol method ${proto} defined for type ${ty}: ${obj ?? ''}`
1490
+ );
1321
1491
  }
1322
1492
 
1323
1493
  export function deref(ref) {
1324
- return ref._deref();
1494
+ if (ref?.[IDeref__deref] !== undefined) return ref[IDeref__deref](ref);
1495
+ return nilImpl(_deref, 'IDeref.-deref', ref)(ref);
1325
1496
  }
1326
1497
 
1327
1498
  export function reset_BANG_(atm, v) {
1328
- atm._reset_BANG_(v);
1499
+ if (atm?.[IReset__reset_BANG_] !== undefined) return atm[IReset__reset_BANG_](atm, v);
1500
+ return nilImpl(_reset_BANG_, 'IReset.-reset!', atm)(atm, v);
1329
1501
  }
1330
1502
 
1331
1503
  export function swap_BANG_(atm, f, ...args) {
1332
1504
  f = __toFn(f);
1505
+ if (atm?.[ISwap__swap_BANG_] !== undefined) {
1506
+ // the CLJS -swap! contract: up to two positional args, the rest packed
1507
+ switch (args.length) {
1508
+ case 0:
1509
+ return atm[ISwap__swap_BANG_](atm, f);
1510
+ case 1:
1511
+ return atm[ISwap__swap_BANG_](atm, f, args[0]);
1512
+ case 2:
1513
+ return atm[ISwap__swap_BANG_](atm, f, args[0], args[1]);
1514
+ default:
1515
+ return atm[ISwap__swap_BANG_](atm, f, args[0], args[1], args.slice(2));
1516
+ }
1517
+ }
1333
1518
  const v = f(deref(atm), ...args);
1334
1519
  reset_BANG_(atm, v);
1335
1520
  return v;
@@ -1339,19 +1524,19 @@ export function swap_vals_BANG_(atm, f, ...args) {
1339
1524
  const oldv = deref(atm);
1340
1525
  f = __toFn(f);
1341
1526
  const newv = f(oldv, ...args);
1342
- atm._reset_BANG_(newv);
1527
+ reset_BANG_(atm, newv);
1343
1528
  return [oldv, newv];
1344
1529
  }
1345
1530
 
1346
1531
  export function reset_vals_BANG_(atm, newv) {
1347
1532
  const oldv = deref(atm);
1348
- atm._reset_BANG_(newv);
1533
+ reset_BANG_(atm, newv);
1349
1534
  return [oldv, newv];
1350
1535
  }
1351
1536
 
1352
1537
  export function compare_and_set_BANG_(atm, oldv, newv) {
1353
1538
  if (deref(atm) === oldv) {
1354
- atm._reset_BANG_(newv);
1539
+ reset_BANG_(atm, newv);
1355
1540
  return true;
1356
1541
  } else {
1357
1542
  return false;
@@ -1516,11 +1701,23 @@ export function set_QMARK_(x) {
1516
1701
  return typeConst(x) === SET_TYPE;
1517
1702
  }
1518
1703
 
1704
+ export function hash_set(...xs) {
1705
+ return new Set(xs);
1706
+ }
1707
+
1708
+ export function sorted_QMARK_(x) {
1709
+ return x != null && x[SORTED_TAG] === true;
1710
+ }
1711
+
1712
+ export function char_QMARK_(x) {
1713
+ return typeof x === 'string' && x.length === 1;
1714
+ }
1715
+
1519
1716
  export function apply(f, ...args) {
1520
1717
  f = __toFn(f);
1521
1718
  const xs = args.slice(0, args.length - 1);
1522
1719
  const last = args[args.length - 1];
1523
- // variadic impl hint; see doc/adr/0001
1720
+ // variadic impl hint; see doc/ai/adr/0001
1524
1721
  const v = f.squint$lang$variadic;
1525
1722
  if (v) {
1526
1723
  // pull maxfa fixed args (bounded, lazy-safe); more left -> variadic, else fixed
@@ -1694,9 +1891,10 @@ export function interpose(sep, coll) {
1694
1891
 
1695
1892
  export function select_keys(o, ks) {
1696
1893
  const type = typeConst(o);
1697
- // ret could be object or array, but in the future, maybe we'll have an IEmpty protocol
1698
- const ret = emptyOfType(type) || {};
1699
- for (const k of ks) {
1894
+ // always a map, like CLJS; a js/Map source keeps its rep
1895
+ const ret = type === MAP_TYPE ? new Map() : {};
1896
+ // iterable puns nil to no keys and a map to its entries, like CLJS seq
1897
+ for (const k of iterable(ks)) {
1700
1898
  const v = get(o, k);
1701
1899
  if (v !== undefined) {
1702
1900
  assoc_BANG_(ret, k, v);
@@ -1903,6 +2101,9 @@ export function merge(...args) {
1903
2101
  let obj;
1904
2102
  if (firstArg === null || firstArg === undefined) {
1905
2103
  obj = {};
2104
+ } else if (typeConst(firstArg) === undefined) {
2105
+ // a non-collection passes through; conj! throws when maps follow, like CLJS
2106
+ obj = firstArg;
1906
2107
  } else {
1907
2108
  obj = into(empty(firstArg), firstArg);
1908
2109
  }
@@ -2020,7 +2221,15 @@ function take1(n) {
2020
2221
  });
2021
2222
  }
2022
2223
 
2224
+ function assertNumber(n) {
2225
+ if (typeof n !== 'number') {
2226
+ throw new Error('Assert failed: (number? n)');
2227
+ }
2228
+ return n;
2229
+ }
2230
+
2023
2231
  export function take(n, coll) {
2232
+ assertNumber(n);
2024
2233
  if (arguments.length === 1) {
2025
2234
  return take1(n);
2026
2235
  }
@@ -2038,6 +2247,7 @@ export function take(n, coll) {
2038
2247
  }
2039
2248
 
2040
2249
  export function take_last(n, coll) {
2250
+ assertNumber(n);
2041
2251
  if (n <= 0) {
2042
2252
  return null;
2043
2253
  }
@@ -2110,8 +2320,18 @@ export function partial(f, ...xs) {
2110
2320
  }
2111
2321
 
2112
2322
  export function cycle(coll) {
2323
+ // seq the coll eagerly: nil or empty cycles to an empty seq and a
2324
+ // non-iterable throws, like the seq call in CLJS cycle. The first lap
2325
+ // is cached and replayed, like the retained seq in CLJS Cycle, so a
2326
+ // one-shot iterator source cycles instead of ending after one lap.
2327
+ const it = iterable(coll);
2113
2328
  return lazy(function* () {
2114
- while (true) yield* coll;
2329
+ const cache = [];
2330
+ for (const x of it) {
2331
+ cache.push(x);
2332
+ yield x;
2333
+ }
2334
+ while (cache.length) yield* cache;
2115
2335
  });
2116
2336
  }
2117
2337
 
@@ -2123,6 +2343,7 @@ function drop1(n) {
2123
2343
  }
2124
2344
 
2125
2345
  export function drop(n, xs) {
2346
+ assertNumber(n);
2126
2347
  if (arguments.length === 1) return drop1(n);
2127
2348
  return lazyIter(xs, function* (iter) {
2128
2349
  for (let x = 0; x < n; x++) {
@@ -2217,7 +2438,8 @@ export function update(coll, k, f, ...args) {
2217
2438
 
2218
2439
  export function get_in(coll, path, orElse) {
2219
2440
  let entry = coll;
2220
- for (const item of path) {
2441
+ // iterable puns a nil path to an empty path, like CLJS
2442
+ for (const item of iterable(path)) {
2221
2443
  entry = get(entry, item);
2222
2444
  }
2223
2445
  if (entry === undefined) return orElse;
@@ -2282,6 +2504,18 @@ export function reverse(coll) {
2282
2504
  return toArray(coll).reverse();
2283
2505
  }
2284
2506
 
2507
+ export function rseq(x) {
2508
+ // vectors and sorted maps/sets are reversible, like CLJS
2509
+ if (isVectorArray(x)) {
2510
+ return x.length === 0 ? null : [...x].reverse();
2511
+ }
2512
+ if (x != null && x[SORTED_TAG] === true) {
2513
+ const xs = [...x].reverse();
2514
+ return xs.length === 0 ? null : xs;
2515
+ }
2516
+ throw new Error('rseq not supported on: ' + typeof x);
2517
+ }
2518
+
2285
2519
  export function sort(f, coll) {
2286
2520
  if (arguments.length === 1) {
2287
2521
  coll = f;
@@ -2568,8 +2802,10 @@ export function dorun(x) {
2568
2802
  }
2569
2803
 
2570
2804
  export function doall(x) {
2571
- // realize as concrete array
2572
- return vec(x);
2805
+ // realize a lazy seq and return it, like CLJS; anything else is
2806
+ // already concrete
2807
+ if (x != null && x[TYPE_TAG] === LAZY_ITERABLE_TYPE) for (const _ of x);
2808
+ return x;
2573
2809
  }
2574
2810
 
2575
2811
  export function aclone(arr) {
@@ -2577,13 +2813,53 @@ export function aclone(arr) {
2577
2813
  return cloned;
2578
2814
  }
2579
2815
 
2816
+ function typed_array(sizeOrSeq, initValOrSeq) {
2817
+ if (initValOrSeq !== undefined) {
2818
+ const a = new Array(sizeOrSeq);
2819
+ if (typeof initValOrSeq === 'number') return a.fill(initValOrSeq);
2820
+ let i = 0;
2821
+ for (const x of iterable(initValOrSeq)) {
2822
+ if (i >= sizeOrSeq) break;
2823
+ a[i++] = x;
2824
+ }
2825
+ return a;
2826
+ }
2827
+ if (typeof sizeOrSeq === 'number') return new Array(sizeOrSeq).fill(null);
2828
+ return [...iterable(sizeOrSeq)];
2829
+ }
2830
+
2831
+ // like CLJS: provided for compatibility, all make a plain array
2832
+ export function int_array(sizeOrSeq, initValOrSeq) {
2833
+ return typed_array(sizeOrSeq, initValOrSeq);
2834
+ }
2835
+ export function long_array(sizeOrSeq, initValOrSeq) {
2836
+ return typed_array(sizeOrSeq, initValOrSeq);
2837
+ }
2838
+ export function float_array(sizeOrSeq, initValOrSeq) {
2839
+ return typed_array(sizeOrSeq, initValOrSeq);
2840
+ }
2841
+ export function double_array(sizeOrSeq, initValOrSeq) {
2842
+ return typed_array(sizeOrSeq, initValOrSeq);
2843
+ }
2844
+ export function object_array(sizeOrSeq, initValOrSeq) {
2845
+ return typed_array(sizeOrSeq, initValOrSeq);
2846
+ }
2847
+
2580
2848
  export function add_watch(ref, key, fn) {
2581
- ref._add_watch(key, fn);
2849
+ if (ref?.[IWatchable__add_watch] !== undefined) {
2850
+ ref[IWatchable__add_watch](ref, key, fn);
2851
+ return ref;
2852
+ }
2853
+ nilImpl(_add_watch, 'IWatchable.-add-watch', ref)(ref, key, fn);
2582
2854
  return ref;
2583
2855
  }
2584
2856
 
2585
2857
  export function remove_watch(ref, key) {
2586
- ref._remove_watch(key);
2858
+ if (ref?.[IWatchable__remove_watch] !== undefined) {
2859
+ ref[IWatchable__remove_watch](ref, key);
2860
+ return ref;
2861
+ }
2862
+ nilImpl(_remove_watch, 'IWatchable.-remove-watch', ref)(ref, key);
2587
2863
  return ref;
2588
2864
  }
2589
2865
 
@@ -2598,17 +2874,17 @@ export function reduce_kv(f, init, m) {
2598
2874
  return ret;
2599
2875
  }
2600
2876
 
2877
+ // CLJS reduce of (cond (NaN? x) x (NaN? y) y (> x y) x :else y): returns one of
2878
+ // the values, so nil acts like zero, and a NaN propagates. NaN? is js/isNaN.
2601
2879
  export function max(x, ...more) {
2602
- // (if (> a b) a b) reduce, like CLJS: returns one of the values, so nil acts
2603
- // like zero and NaN propagates, instead of Math.max coercing nil to 0.
2604
2880
  let m = x;
2605
- for (const y of more) m = m > y ? m : y;
2881
+ for (const y of more) m = isNaN(m) ? m : isNaN(y) ? y : m > y ? m : y;
2606
2882
  return m;
2607
2883
  }
2608
2884
 
2609
2885
  export function min(x, ...more) {
2610
2886
  let m = x;
2611
- for (const y of more) m = m < y ? m : y;
2887
+ for (const y of more) m = isNaN(m) ? m : isNaN(y) ? y : m < y ? m : y;
2612
2888
  return m;
2613
2889
  }
2614
2890
 
@@ -2636,15 +2912,15 @@ export function every_pred(...preds) {
2636
2912
 
2637
2913
  export function some_fn(...fns) {
2638
2914
  return (...args) => {
2915
+ let res;
2639
2916
  for (const f of fns) {
2640
2917
  for (const a of args) {
2641
- const res = f(a);
2642
- if (res) {
2643
- return res;
2644
- }
2918
+ res = f(a);
2919
+ // truth_, not JS truthiness: 0 and "" are truthy in CLJS
2920
+ if (truth_(res)) return res;
2645
2921
  }
2646
2922
  }
2647
- return undefined;
2923
+ return res;
2648
2924
  };
2649
2925
  }
2650
2926
 
@@ -2788,7 +3064,8 @@ export function re_seq(re, s) {
2788
3064
  }
2789
3065
 
2790
3066
  export function NaN_QMARK_(x) {
2791
- return Number.isNaN(x);
3067
+ // coercing, like CLJS js/isNaN: (NaN? "foo") is true
3068
+ return isNaN(x);
2792
3069
  }
2793
3070
 
2794
3071
  export function number_QMARK_(x) {
@@ -2796,7 +3073,7 @@ export function number_QMARK_(x) {
2796
3073
  }
2797
3074
 
2798
3075
  export function keys(obj) {
2799
- if (obj == null) return;
3076
+ if (obj == null) return null;
2800
3077
  const t = typeConst(obj);
2801
3078
  switch (t) {
2802
3079
  case OBJECT_TYPE: {
@@ -2808,6 +3085,11 @@ export function keys(obj) {
2808
3085
  if (obj.size) return Array.from(obj.keys());
2809
3086
  return;
2810
3087
  }
3088
+ // not a map: nil when empty, like seq in CLJS, else throw
3089
+ for (const _ of iterable(obj)) {
3090
+ throw new TypeError(obj + ' is not a map');
3091
+ }
3092
+ return null;
2811
3093
  }
2812
3094
 
2813
3095
  export function js_keys(obj) {
@@ -2815,7 +3097,7 @@ export function js_keys(obj) {
2815
3097
  }
2816
3098
 
2817
3099
  export function vals(obj) {
2818
- if (obj == null) return;
3100
+ if (obj == null) return null;
2819
3101
  const t = typeConst(obj);
2820
3102
  switch (t) {
2821
3103
  case OBJECT_TYPE: {
@@ -2827,6 +3109,11 @@ export function vals(obj) {
2827
3109
  if (obj.size) return Array.from(obj.values());
2828
3110
  return;
2829
3111
  }
3112
+ // not a map: nil when empty, like seq in CLJS, else throw
3113
+ for (const _ of iterable(obj)) {
3114
+ throw new TypeError(obj + ' is not a map');
3115
+ }
3116
+ return null;
2830
3117
  }
2831
3118
 
2832
3119
  export function string_QMARK_(s) {
@@ -3021,34 +3308,22 @@ export function mod(x, y) {
3021
3308
  }
3022
3309
 
3023
3310
  export function min_key(k, x, ...more) {
3024
- if (more.length == 0) {
3025
- return x;
3311
+ k = __toFn(k);
3312
+ // pairwise (if (< (k x) (k y)) x y), like CLJS, so NaN propagates right
3313
+ let min = x;
3314
+ for (const y of more) {
3315
+ if (!(k(min) < k(y))) min = y;
3026
3316
  }
3027
- var kx = k(x);
3028
- var min = x;
3029
- more.forEach((y) => {
3030
- var ky = k(y);
3031
- if (ky <= kx) {
3032
- kx = ky;
3033
- min = y;
3034
- }
3035
- });
3036
3317
  return min;
3037
3318
  }
3038
3319
 
3039
3320
  export function max_key(k, x, ...more) {
3040
- if (more.length == 0) {
3041
- return x;
3321
+ k = __toFn(k);
3322
+ // pairwise (if (> (k x) (k y)) x y), like CLJS, so NaN propagates right
3323
+ let max = x;
3324
+ for (const y of more) {
3325
+ if (!(k(max) > k(y))) max = y;
3042
3326
  }
3043
- var kx = k(x);
3044
- var max = x;
3045
- more.forEach((y) => {
3046
- var ky = k(y);
3047
- if (ky >= kx) {
3048
- kx = ky;
3049
- max = y;
3050
- }
3051
- });
3052
3327
  return max;
3053
3328
  }
3054
3329
 
@@ -3202,6 +3477,7 @@ class SortedSet {
3202
3477
  // we don't re-use xs since xs can contain duplicates
3203
3478
  this._elts = [...s];
3204
3479
  this._set = s;
3480
+ this.size = this._elts.length;
3205
3481
  }
3206
3482
  add(x) {
3207
3483
  if (this._set.has(x)) return this;
@@ -3461,6 +3737,7 @@ export function memoize(f) {
3461
3737
  }
3462
3738
 
3463
3739
  export function peek(vec) {
3740
+ if (vec == null) return null;
3464
3741
  // A list peeks at its front; squint lists are array-backed, so check list
3465
3742
  // before array to avoid returning the last element.
3466
3743
  if (list_QMARK_(vec)) {
@@ -3468,7 +3745,7 @@ export function peek(vec) {
3468
3745
  } else if (array_QMARK_(vec)) {
3469
3746
  return vec[vec.length - 1];
3470
3747
  } else {
3471
- return first(vec);
3748
+ throw missing_protocol('IStack.-peek', vec);
3472
3749
  }
3473
3750
  }
3474
3751
 
@@ -3483,7 +3760,7 @@ export function pop(vec) {
3483
3760
  ret.pop();
3484
3761
  return ret;
3485
3762
  } else {
3486
- return rest(vec);
3763
+ throw missing_protocol('IStack.-pop', vec);
3487
3764
  }
3488
3765
  }
3489
3766
 
@@ -3514,7 +3791,7 @@ export function update_vals(m, f) {
3514
3791
  }
3515
3792
 
3516
3793
  export function random_uuid() {
3517
- return crypto.randomUUID();
3794
+ return new UUID(crypto.randomUUID());
3518
3795
  }
3519
3796
 
3520
3797
  export class UUID {
@@ -3527,20 +3804,34 @@ export class UUID {
3527
3804
  }
3528
3805
 
3529
3806
  export function uuid(s) {
3530
- return new UUID(s);
3807
+ // lowercased, like CLJS
3808
+ return new UUID(s.toLowerCase());
3531
3809
  }
3532
3810
 
3533
3811
  export function uuid_QMARK_(x) {
3534
3812
  return x instanceof UUID;
3535
3813
  }
3536
3814
 
3815
+ const UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
3816
+
3817
+ export function parse_uuid(s) {
3818
+ if (typeof s !== 'string') {
3819
+ throw new Error('Expected string, got: ' + (s === null ? 'nil' : typeof s));
3820
+ }
3821
+ return UUID_REGEX.test(s) ? uuid(s) : null;
3822
+ }
3823
+
3537
3824
  export function inst_QMARK_(x) {
3538
3825
  return x instanceof Date;
3539
3826
  }
3540
3827
 
3828
+ const DELAY_DEREF = (self) => self._deref();
3829
+
3541
3830
  export class Delay {
3542
3831
  constructor(f) {
3543
3832
  this.f = f;
3833
+ this[IDEREF_SYM] = true;
3834
+ this[IDeref__deref] = DELAY_DEREF;
3544
3835
  }
3545
3836
  _deref() {
3546
3837
  if (this.realized) {
@@ -3560,6 +3851,10 @@ export function realized_QMARK_(x) {
3560
3851
  throw new Error('realized? not supported on: ' + str(x));
3561
3852
  }
3562
3853
 
3854
+ export function force(x) {
3855
+ return x instanceof Delay ? x._deref() : x;
3856
+ }
3857
+
3563
3858
  function clj__GT_js_(x, seen) {
3564
3859
  // we need to protect against circular objects
3565
3860
  if (seen.has(x)) return x;
@@ -3587,12 +3882,12 @@ export function not_EQ_(...more) {
3587
3882
  return not(_EQ_(...more));
3588
3883
  }
3589
3884
 
3885
+ const VOLATILE_DEREF = (self) => self.v;
3886
+
3590
3887
  class Volatile {
3591
3888
  constructor(v) {
3592
3889
  this.v = v;
3593
- }
3594
- _deref() {
3595
- return this.v;
3890
+ this[IDeref__deref] = VOLATILE_DEREF;
3596
3891
  }
3597
3892
  }
3598
3893