squint-cljs 0.14.200 → 0.14.202

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.
@@ -65,6 +65,16 @@ function dequal(foo, bar) {
65
65
  return true;
66
66
  }
67
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
+
68
78
  // Sets (hash or sorted) compare by elements, across concrete types.
69
79
  if (isSetLike(foo) || isSetLike(bar)) {
70
80
  if (!isSetLike(foo) || !isSetLike(bar) || foo.size !== bar.size) return false;
@@ -80,6 +90,8 @@ function dequal(foo, bar) {
80
90
 
81
91
  if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
82
92
  if (ctor === Date) return foo.getTime() === bar.getTime();
93
+ // regexes only compare by identity, like CLJS
94
+ if (ctor === RegExp) return false;
83
95
 
84
96
  if (ctor === Array) {
85
97
  if ((len = foo.length) === bar.length) {
@@ -211,8 +223,23 @@ function getAssocMut(m) {
211
223
  }
212
224
  }
213
225
 
226
+ function validateArrayKeys(o, k, kvs) {
227
+ // like CLJS: a vector key is an index in [0, count], count appends
228
+ let len = o.length;
229
+ for (let i = 0; i < kvs.length + 2; i += 2) {
230
+ const key = i === 0 ? k : kvs[i - 2];
231
+ if (!Number.isInteger(key)) {
232
+ throw new Error("Vector's key for assoc must be a number.");
233
+ }
234
+ if (key < 0 || key > len) {
235
+ throw new Error(`Index ${key} out of bounds [0,${len}]`);
236
+ }
237
+ if (key === len) len++;
238
+ }
239
+ }
240
+
214
241
  export function assoc_BANG_(m, k, v, ...kvs) {
215
- if (kvs.length % 2 !== 0) {
242
+ if (arguments.length < 3 || kvs.length % 2 !== 0) {
216
243
  throw new Error('Illegal argument: assoc expects an odd number of arguments.');
217
244
  }
218
245
  switch (typeConst(m)) {
@@ -224,6 +251,13 @@ export function assoc_BANG_(m, k, v, ...kvs) {
224
251
  }
225
252
  break;
226
253
  case ARRAY_TYPE:
254
+ validateArrayKeys(m, k, kvs);
255
+ m[k] = v;
256
+
257
+ for (let i = 0; i < kvs.length; i += 2) {
258
+ m[kvs[i]] = kvs[i + 1];
259
+ }
260
+ break;
227
261
  case OBJECT_TYPE:
228
262
  m[k] = v;
229
263
 
@@ -272,7 +306,11 @@ function copy(o) {
272
306
  }
273
307
 
274
308
  export function assoc(o, k, v, ...kvs) {
275
- if (!o) {
309
+ if (arguments.length < 3 || kvs.length % 2 !== 0) {
310
+ throw new Error('Illegal argument: assoc expects an odd number of arguments.');
311
+ }
312
+ // only nil puns to an empty map; assoc on false throws, like CLJS
313
+ if (o == null) {
276
314
  o = {};
277
315
  }
278
316
  const ret = copy(o);
@@ -283,6 +321,9 @@ export function assoc(o, k, v, ...kvs) {
283
321
  // squint has no distinct hash-map or array-map type; both build a plain object.
284
322
  export function hash_map(...kvs) {
285
323
  if (kvs.length === 0) return {};
324
+ if (kvs.length % 2 !== 0) {
325
+ throw new Error('No value supplied for key: ' + kvs[kvs.length - 1]);
326
+ }
286
327
  return assoc({}, ...kvs);
287
328
  }
288
329
 
@@ -444,6 +485,10 @@ export function conj_BANG_(...xs) {
444
485
  if (n === 0) {
445
486
  return vector();
446
487
  }
488
+ // single arg: return the coll unchanged, including nil, like CLJS
489
+ if (n === 1) {
490
+ return xs[0];
491
+ }
447
492
 
448
493
  let o = xs[0];
449
494
  if (o === null || o === undefined) {
@@ -477,17 +522,14 @@ export function conj_BANG_(...xs) {
477
522
  break;
478
523
  case MAP_TYPE:
479
524
  for (const x of rest) {
480
- if (!Array.isArray(x))
481
- iterable(x).forEach((kv) => {
482
- o.set(kv[0], kv[1]);
483
- });
484
- else o.set(x[0], x[1]);
525
+ if (isVectorArray(x)) { asMapEntry(x); o.set(x[0], x[1]); }
526
+ else for (const kv of mapEntriesOf(x)) o.set(kv[0], kv[1]);
485
527
  }
486
528
  break;
487
529
  case OBJECT_TYPE:
488
530
  for (const x of rest) {
489
- if (!Array.isArray(x)) Object.assign(o, x);
490
- else o[x[0]] = x[1];
531
+ if (isVectorArray(x)) { asMapEntry(x); o[x[0]] = x[1]; }
532
+ else for (const kv of mapEntriesOf(x)) o[kv[0]] = kv[1];
491
533
  }
492
534
  break;
493
535
  default:
@@ -499,6 +541,28 @@ export function conj_BANG_(...xs) {
499
541
  return o;
500
542
  }
501
543
 
544
+ // entries carried by a non-entry conj arg onto a map: a map merges, a
545
+ // seqable must contain entry vectors, like CLJS
546
+ function* mapEntriesOf(x) {
547
+ if (isMapLike(x)) {
548
+ yield* iterable(x);
549
+ return;
550
+ }
551
+ for (const kv of iterable(x)) {
552
+ if (!isVectorArray(kv)) {
553
+ throw new Error('conj on a map takes map entries or seqables of map entries');
554
+ }
555
+ yield kv;
556
+ }
557
+ }
558
+
559
+ function asMapEntry(x) {
560
+ if (x.length < 2) {
561
+ throw new Error('Vector arg to map conj must be a pair');
562
+ }
563
+ return x;
564
+ }
565
+
502
566
  export function conj(...xs) {
503
567
  if (xs.length === 0) {
504
568
  return vector();
@@ -530,11 +594,8 @@ export function conj(...xs) {
530
594
  case MAP_TYPE:
531
595
  m = new Map(o);
532
596
  for (const x of rest) {
533
- if (!Array.isArray(x))
534
- iterable(x).forEach((kv) => {
535
- m.set(kv[0], kv[1]);
536
- });
537
- else m.set(x[0], x[1]);
597
+ if (isVectorArray(x)) { asMapEntry(x); m.set(x[0], x[1]); }
598
+ else for (const kv of mapEntriesOf(x)) m.set(kv[0], kv[1]);
538
599
  }
539
600
 
540
601
  return copyMeta(o, m);
@@ -547,8 +608,8 @@ export function conj(...xs) {
547
608
  o2 = { ...o };
548
609
 
549
610
  for (const x of rest) {
550
- if (!Array.isArray(x)) Object.assign(o2, x);
551
- else o2[x[0]] = x[1];
611
+ if (isVectorArray(x)) { asMapEntry(x); o2[x[0]] = x[1]; }
612
+ else for (const kv of mapEntriesOf(x)) o2[kv[0]] = kv[1];
552
613
  }
553
614
 
554
615
  return copyMeta(o, o2);
@@ -570,7 +631,7 @@ export function disj(s, ...xs) {
570
631
  if (s == null) return s;
571
632
  // pass s itself (not a spread) so a SortedSet keeps its comparator
572
633
  const s1 = new s.constructor(s);
573
- return disj_BANG_(s1, ...xs);
634
+ return copyMeta(s, disj_BANG_(s1, ...xs));
574
635
  }
575
636
 
576
637
  export function contains_QMARK_(coll, v) {
@@ -599,7 +660,11 @@ export function dissoc_BANG_(m, ...ks) {
599
660
  export function dissoc(m, ...ks) {
600
661
  if (!m) return;
601
662
  if (ks.length === 0) return m;
602
- if (typeConst(m) === MAP_TYPE) {
663
+ const tc = typeConst(m);
664
+ if (tc !== MAP_TYPE && tc !== OBJECT_TYPE) {
665
+ throw new Error('dissoc expects a map, got: ' + typeof m);
666
+ }
667
+ if (tc === MAP_TYPE) {
603
668
  let present = false;
604
669
  for (const k of ks) if (m.has(k)) { present = true; break; }
605
670
  if (!present) return m;
@@ -641,6 +706,9 @@ export function pr(...xs) {
641
706
  }
642
707
 
643
708
  export function nth(coll, idx, orElse) {
709
+ if (typeof idx !== 'number') {
710
+ throw new Error('Index argument to nth must be a number');
711
+ }
644
712
  const hasDefault = arguments.length > 2;
645
713
  // nil coll puns to nil, like Clojure
646
714
  if (coll == null) return hasDefault ? orElse : null;
@@ -727,7 +795,8 @@ export function seqable_QMARK_(x) {
727
795
  object_QMARK_(x) ||
728
796
  // we used to check instanceof Object but this returns false for TC39 Records
729
797
  // also we used to write `Symbol.iterator in` but this does not work for strings and some other types
730
- !!x[Symbol.iterator]
798
+ !!x[Symbol.iterator] ||
799
+ !!x[ISEQABLE_SYM]
731
800
  );
732
801
  }
733
802
 
@@ -757,6 +826,8 @@ export function iterable(x) {
757
826
  if (x[Symbol.iterator]) {
758
827
  return x;
759
828
  }
829
+ // a type extended to ISeqable seqs through its -seq method
830
+ if (x[ISeqable__seq] !== undefined) return iterable(x[ISeqable__seq](x));
760
831
  if (x instanceof Object) return Object.entries(x).map(tagMapEntry);
761
832
  throw new TypeError(`${x} is not iterable`);
762
833
  }
@@ -773,6 +844,7 @@ export const es6_iterator = _iterator;
773
844
 
774
845
  export function seq(x) {
775
846
  if (x == null) return x;
847
+ if (!seqable_QMARK_(x)) throw new TypeError(x + ' is not ISeqable');
776
848
  // a string seqs into its characters, like CLJS.
777
849
  if (typeof x === 'string') return x.length ? [...x] : null;
778
850
  const iter = iterable(x);
@@ -833,13 +905,13 @@ export function rest(coll) {
833
905
  return cell._rest; // first chunk had one element; the next cell is the rest
834
906
  }
835
907
 
908
+ const REDUCED_DEREF = (self) => self.value;
909
+
836
910
  class Reduced {
837
911
  value;
838
912
  constructor(x) {
839
913
  this.value = x;
840
- }
841
- _deref() {
842
- return this.value;
914
+ this[IDeref__deref] = REDUCED_DEREF;
843
915
  }
844
916
  }
845
917
 
@@ -1128,6 +1200,8 @@ class Cons {
1128
1200
  );
1129
1201
 
1130
1202
  export function cons(x, coll) {
1203
+ // like CLJS cons, which seqs a non-ISeq tail
1204
+ if (!seqable_QMARK_(coll)) throw new TypeError(coll + ' is not ISeqable');
1131
1205
  return new Cons(x, coll);
1132
1206
  }
1133
1207
 
@@ -1291,48 +1365,179 @@ export function nil_QMARK_(v) {
1291
1365
 
1292
1366
  export const PROTOCOL_SENTINEL = {};
1293
1367
 
1368
+ // marker protocols so (satisfies? IAtom x) works, like CLJS. Marked in the
1369
+ // constructor, not on the prototype, so no top-level mutation pins Atom
1370
+ // into bundles that do not use it.
1371
+ const IATOM_SYM = Symbol('squint.core.IAtom');
1372
+ const IDEREF_SYM = Symbol('squint.core.IDeref');
1373
+ const ISEQABLE_SYM = Symbol('squint.core.ISeqable');
1374
+ export const IAtom = { __sym: IATOM_SYM };
1375
+ export const IDeref = { __sym: IDEREF_SYM };
1376
+ // method slot for (-deref x), named like the defprotocol emission so
1377
+ // (extend-type T IDeref (-deref [x] ...)) fills it
1378
+ export const IDeref__deref = Symbol('IDeref_-deref');
1379
+ export function _deref(o) {
1380
+ if (o != null && o[IDeref__deref] !== undefined) return o[IDeref__deref](o);
1381
+ return nilImpl(_deref, 'IDeref.-deref', o)(o);
1382
+ }
1383
+ export const ISeqable = { __sym: ISEQABLE_SYM };
1384
+ export const ISeqable__seq = Symbol('ISeqable_-seq');
1385
+ export function _seq(o) {
1386
+ if (o != null && o[ISeqable__seq] !== undefined) return o[ISeqable__seq](o);
1387
+ return nilImpl(_seq, 'ISeqable.-seq', o)(o);
1388
+ }
1389
+
1390
+ // an extend-type on nil stores the impl on the dispatch fn under null,
1391
+ // the same convention the generated protocol dispatch fns use
1392
+ function nilImpl(dispatchFn, protoMethod, o) {
1393
+ const f = dispatchFn[null];
1394
+ if (f === undefined) throw missing_protocol(protoMethod, o);
1395
+ return f;
1396
+ }
1397
+ export const IReset = { __sym: Symbol('squint.core.IReset') };
1398
+ export const IReset__reset_BANG_ = Symbol('IReset_-reset!');
1399
+ export function _reset_BANG_(o, v) {
1400
+ if (o != null && o[IReset__reset_BANG_] !== undefined) return o[IReset__reset_BANG_](o, v);
1401
+ return nilImpl(_reset_BANG_, 'IReset.-reset!', o)(o, v);
1402
+ }
1403
+ export const ISwap = { __sym: Symbol('squint.core.ISwap') };
1404
+ export const ISwap__swap_BANG_ = Symbol('ISwap_-swap!');
1405
+ export function _swap_BANG_(o, f, ...args) {
1406
+ if (o != null && o[ISwap__swap_BANG_] !== undefined) return o[ISwap__swap_BANG_](o, f, ...args);
1407
+ return nilImpl(_swap_BANG_, 'ISwap.-swap!', o)(o, f, ...args);
1408
+ }
1409
+ export const IWatchable = { __sym: Symbol('squint.core.IWatchable') };
1410
+ export const IWatchable__add_watch = Symbol('IWatchable_-add-watch');
1411
+ export const IWatchable__remove_watch = Symbol('IWatchable_-remove-watch');
1412
+ export const IWatchable__notify_watches = Symbol('IWatchable_-notify-watches');
1413
+ export function _add_watch(o, k, f) {
1414
+ if (o != null && o[IWatchable__add_watch] !== undefined) return o[IWatchable__add_watch](o, k, f);
1415
+ return nilImpl(_add_watch, 'IWatchable.-add-watch', o)(o, k, f);
1416
+ }
1417
+ export function _remove_watch(o, k) {
1418
+ if (o != null && o[IWatchable__remove_watch] !== undefined) return o[IWatchable__remove_watch](o, k);
1419
+ return nilImpl(_remove_watch, 'IWatchable.-remove-watch', o)(o, k);
1420
+ }
1421
+ export function _notify_watches(o, oldv, newv) {
1422
+ if (o != null && o[IWatchable__notify_watches] !== undefined) return o[IWatchable__notify_watches](o, oldv, newv);
1423
+ return nilImpl(_notify_watches, 'IWatchable.-notify-watches', o)(o, oldv, newv);
1424
+ }
1425
+
1426
+ // shared protocol impls: one fn per operation, a pointer per instance
1427
+ const ATOM_DEREF = (self) => self.val;
1428
+ const ATOM_RESET = (self, x) => {
1429
+ if (self._validator && !truth_(self._validator(x))) {
1430
+ throw new Error('Validator rejected reference state');
1431
+ }
1432
+ const old_val = self.val;
1433
+ self.val = x;
1434
+ if (self._hasWatches) {
1435
+ for (const [k, f] of Object.entries(self._watches)) f(k, self, old_val, x);
1436
+ }
1437
+ return x;
1438
+ };
1439
+ const ATOM_SWAP = function (self, f, a, b, xs) {
1440
+ switch (arguments.length) {
1441
+ case 2:
1442
+ return ATOM_RESET(self, f(self.val));
1443
+ case 3:
1444
+ return ATOM_RESET(self, f(self.val, a));
1445
+ case 4:
1446
+ return ATOM_RESET(self, f(self.val, a, b));
1447
+ default:
1448
+ return ATOM_RESET(self, f(self.val, a, b, ...xs));
1449
+ }
1450
+ };
1451
+ const ATOM_ADD_WATCH = (self, k, f) => {
1452
+ self._watches[k] = f;
1453
+ self._hasWatches = true;
1454
+ };
1455
+ const ATOM_REMOVE_WATCH = (self, k) => {
1456
+ delete self._watches[k];
1457
+ };
1458
+ const ATOM_NOTIFY = (self, oldv, newv) => {
1459
+ for (const [k, f] of Object.entries(self._watches)) f(k, self, oldv, newv);
1460
+ };
1461
+
1294
1462
  export class Atom {
1295
1463
  constructor(init) {
1296
1464
  this.val = init;
1297
1465
  this._watches = {};
1298
- this._deref = () => this.val;
1299
1466
  this._hasWatches = false;
1300
- this._reset_BANG_ = (x) => {
1301
- const old_val = this.val;
1302
- this.val = x;
1303
- if (this._hasWatches) {
1304
- for (const entry of Object.entries(this._watches)) {
1305
- const k = entry[0];
1306
- const f = entry[1];
1307
- f(k, this, old_val, x);
1308
- }
1309
- }
1310
- return x;
1311
- };
1312
- this._add_watch = (k, fn) => {
1313
- this._watches[k] = fn;
1314
- this._hasWatches = true;
1315
- };
1316
- this._remove_watch = (k) => {
1317
- delete this._watches[k];
1318
- };
1467
+ this[IATOM_SYM] = true;
1468
+ this[IDEREF_SYM] = true;
1469
+ this[IDeref__deref] = ATOM_DEREF;
1470
+ this[IReset.__sym] = true;
1471
+ this[IReset__reset_BANG_] = ATOM_RESET;
1472
+ this[ISwap.__sym] = true;
1473
+ this[ISwap__swap_BANG_] = ATOM_SWAP;
1474
+ this[IWatchable.__sym] = true;
1475
+ this[IWatchable__add_watch] = ATOM_ADD_WATCH;
1476
+ this[IWatchable__remove_watch] = ATOM_REMOVE_WATCH;
1477
+ this[IWatchable__notify_watches] = ATOM_NOTIFY;
1478
+ }
1479
+ }
1480
+
1481
+ export function atom(init, ...opts) {
1482
+ const a = new Atom(init);
1483
+ for (let i = 0; i < opts.length; i += 2) {
1484
+ if (opts[i] === 'meta') a[_metaSym] = opts[i + 1];
1485
+ else if (opts[i] === 'validator') a._validator = opts[i + 1];
1486
+ }
1487
+ return a;
1488
+ }
1489
+
1490
+ export function get_validator(ref) {
1491
+ return ref._validator ?? null;
1492
+ }
1493
+
1494
+ export function set_validator_BANG_(ref, f) {
1495
+ if (f != null && !truth_(f(deref(ref)))) {
1496
+ throw new Error('Validator rejected reference state');
1319
1497
  }
1498
+ ref._validator = f;
1499
+ return null;
1320
1500
  }
1321
1501
 
1322
- export function atom(init) {
1323
- return new Atom(init);
1502
+ // the CLJS missing-protocol error, used by every protocol dispatch miss
1503
+ export function missing_protocol(proto, obj) {
1504
+ let ty;
1505
+ if (obj === null) ty = 'null';
1506
+ else if (obj === undefined) ty = 'undefined';
1507
+ else if (Array.isArray(obj)) ty = 'array';
1508
+ else if (typeof obj === 'object' && obj.constructor && obj.constructor !== Object) {
1509
+ ty = obj.constructor.name;
1510
+ } else ty = typeof obj;
1511
+ return new Error(
1512
+ `No protocol method ${proto} defined for type ${ty}: ${obj ?? ''}`
1513
+ );
1324
1514
  }
1325
1515
 
1326
1516
  export function deref(ref) {
1327
- return ref._deref();
1517
+ if (ref?.[IDeref__deref] !== undefined) return ref[IDeref__deref](ref);
1518
+ return nilImpl(_deref, 'IDeref.-deref', ref)(ref);
1328
1519
  }
1329
1520
 
1330
1521
  export function reset_BANG_(atm, v) {
1331
- atm._reset_BANG_(v);
1522
+ if (atm?.[IReset__reset_BANG_] !== undefined) return atm[IReset__reset_BANG_](atm, v);
1523
+ return nilImpl(_reset_BANG_, 'IReset.-reset!', atm)(atm, v);
1332
1524
  }
1333
1525
 
1334
1526
  export function swap_BANG_(atm, f, ...args) {
1335
1527
  f = __toFn(f);
1528
+ if (atm?.[ISwap__swap_BANG_] !== undefined) {
1529
+ // the CLJS -swap! contract: up to two positional args, the rest packed
1530
+ switch (args.length) {
1531
+ case 0:
1532
+ return atm[ISwap__swap_BANG_](atm, f);
1533
+ case 1:
1534
+ return atm[ISwap__swap_BANG_](atm, f, args[0]);
1535
+ case 2:
1536
+ return atm[ISwap__swap_BANG_](atm, f, args[0], args[1]);
1537
+ default:
1538
+ return atm[ISwap__swap_BANG_](atm, f, args[0], args[1], args.slice(2));
1539
+ }
1540
+ }
1336
1541
  const v = f(deref(atm), ...args);
1337
1542
  reset_BANG_(atm, v);
1338
1543
  return v;
@@ -1342,19 +1547,19 @@ export function swap_vals_BANG_(atm, f, ...args) {
1342
1547
  const oldv = deref(atm);
1343
1548
  f = __toFn(f);
1344
1549
  const newv = f(oldv, ...args);
1345
- atm._reset_BANG_(newv);
1550
+ reset_BANG_(atm, newv);
1346
1551
  return [oldv, newv];
1347
1552
  }
1348
1553
 
1349
1554
  export function reset_vals_BANG_(atm, newv) {
1350
1555
  const oldv = deref(atm);
1351
- atm._reset_BANG_(newv);
1556
+ reset_BANG_(atm, newv);
1352
1557
  return [oldv, newv];
1353
1558
  }
1354
1559
 
1355
1560
  export function compare_and_set_BANG_(atm, oldv, newv) {
1356
1561
  if (deref(atm) === oldv) {
1357
- atm._reset_BANG_(newv);
1562
+ reset_BANG_(atm, newv);
1358
1563
  return true;
1359
1564
  } else {
1360
1565
  return false;
@@ -1519,6 +1724,18 @@ export function set_QMARK_(x) {
1519
1724
  return typeConst(x) === SET_TYPE;
1520
1725
  }
1521
1726
 
1727
+ export function hash_set(...xs) {
1728
+ return new Set(xs);
1729
+ }
1730
+
1731
+ export function sorted_QMARK_(x) {
1732
+ return x != null && x[SORTED_TAG] === true;
1733
+ }
1734
+
1735
+ export function char_QMARK_(x) {
1736
+ return typeof x === 'string' && x.length === 1;
1737
+ }
1738
+
1522
1739
  export function apply(f, ...args) {
1523
1740
  f = __toFn(f);
1524
1741
  const xs = args.slice(0, args.length - 1);
@@ -1697,9 +1914,10 @@ export function interpose(sep, coll) {
1697
1914
 
1698
1915
  export function select_keys(o, ks) {
1699
1916
  const type = typeConst(o);
1700
- // ret could be object or array, but in the future, maybe we'll have an IEmpty protocol
1701
- const ret = emptyOfType(type) || {};
1702
- for (const k of ks) {
1917
+ // always a map, like CLJS; a js/Map source keeps its rep
1918
+ const ret = type === MAP_TYPE ? new Map() : {};
1919
+ // iterable puns nil to no keys and a map to its entries, like CLJS seq
1920
+ for (const k of iterable(ks)) {
1703
1921
  const v = get(o, k);
1704
1922
  if (v !== undefined) {
1705
1923
  assoc_BANG_(ret, k, v);
@@ -1892,10 +2110,13 @@ export function partition_by(f, coll) {
1892
2110
  export function empty(coll) {
1893
2111
  const type = typeConst(coll);
1894
2112
  if (type != null) {
2113
+ // a class instance is not an emptyable collection, like CLJS;
2114
+ // a plain or null-prototype object still empties to {}
2115
+ if (type === OBJECT_TYPE && coll.constructor !== undefined && coll.constructor !== Object) return null;
1895
2116
  return copyMeta(coll, emptyOfType(type));
1896
- } else {
1897
- throw new Error(`Can't create empty of ${typeof coll}`);
1898
2117
  }
2118
+ // non-collections give nil, like CLJS
2119
+ return null;
1899
2120
  }
1900
2121
 
1901
2122
  export function merge(...args) {
@@ -1906,6 +2127,9 @@ export function merge(...args) {
1906
2127
  let obj;
1907
2128
  if (firstArg === null || firstArg === undefined) {
1908
2129
  obj = {};
2130
+ } else if (typeConst(firstArg) === undefined) {
2131
+ // a non-collection passes through; conj! throws when maps follow, like CLJS
2132
+ obj = firstArg;
1909
2133
  } else {
1910
2134
  obj = into(empty(firstArg), firstArg);
1911
2135
  }
@@ -2023,7 +2247,15 @@ function take1(n) {
2023
2247
  });
2024
2248
  }
2025
2249
 
2250
+ function assertNumber(n) {
2251
+ if (typeof n !== 'number') {
2252
+ throw new Error('Assert failed: (number? n)');
2253
+ }
2254
+ return n;
2255
+ }
2256
+
2026
2257
  export function take(n, coll) {
2258
+ assertNumber(n);
2027
2259
  if (arguments.length === 1) {
2028
2260
  return take1(n);
2029
2261
  }
@@ -2041,6 +2273,7 @@ export function take(n, coll) {
2041
2273
  }
2042
2274
 
2043
2275
  export function take_last(n, coll) {
2276
+ assertNumber(n);
2044
2277
  if (n <= 0) {
2045
2278
  return null;
2046
2279
  }
@@ -2089,6 +2322,7 @@ function take_nth1(n) {
2089
2322
  }
2090
2323
 
2091
2324
  export function take_nth(n, coll) {
2325
+ assertNumber(n);
2092
2326
  if (arguments.length === 1) return take_nth1(n);
2093
2327
  if (n <= 0) {
2094
2328
  return repeat(first(coll));
@@ -2113,8 +2347,18 @@ export function partial(f, ...xs) {
2113
2347
  }
2114
2348
 
2115
2349
  export function cycle(coll) {
2350
+ // seq the coll eagerly: nil or empty cycles to an empty seq and a
2351
+ // non-iterable throws, like the seq call in CLJS cycle. The first lap
2352
+ // is cached and replayed, like the retained seq in CLJS Cycle, so a
2353
+ // one-shot iterator source cycles instead of ending after one lap.
2354
+ const it = iterable(coll);
2116
2355
  return lazy(function* () {
2117
- while (true) yield* coll;
2356
+ const cache = [];
2357
+ for (const x of it) {
2358
+ cache.push(x);
2359
+ yield x;
2360
+ }
2361
+ while (cache.length) yield* cache;
2118
2362
  });
2119
2363
  }
2120
2364
 
@@ -2126,6 +2370,7 @@ function drop1(n) {
2126
2370
  }
2127
2371
 
2128
2372
  export function drop(n, xs) {
2373
+ assertNumber(n);
2129
2374
  if (arguments.length === 1) return drop1(n);
2130
2375
  return lazyIter(xs, function* (iter) {
2131
2376
  for (let x = 0; x < n; x++) {
@@ -2220,7 +2465,8 @@ export function update(coll, k, f, ...args) {
2220
2465
 
2221
2466
  export function get_in(coll, path, orElse) {
2222
2467
  let entry = coll;
2223
- for (const item of path) {
2468
+ // iterable puns a nil path to an empty path, like CLJS
2469
+ for (const item of iterable(path)) {
2224
2470
  entry = get(entry, item);
2225
2471
  }
2226
2472
  if (entry === undefined) return orElse;
@@ -2285,6 +2531,18 @@ export function reverse(coll) {
2285
2531
  return toArray(coll).reverse();
2286
2532
  }
2287
2533
 
2534
+ export function rseq(x) {
2535
+ // vectors and sorted maps/sets are reversible, like CLJS
2536
+ if (isVectorArray(x)) {
2537
+ return x.length === 0 ? null : [...x].reverse();
2538
+ }
2539
+ if (x != null && x[SORTED_TAG] === true) {
2540
+ const xs = [...x].reverse();
2541
+ return xs.length === 0 ? null : xs;
2542
+ }
2543
+ throw new Error('rseq not supported on: ' + typeof x);
2544
+ }
2545
+
2288
2546
  export function sort(f, coll) {
2289
2547
  if (arguments.length === 1) {
2290
2548
  coll = f;
@@ -2571,8 +2829,10 @@ export function dorun(x) {
2571
2829
  }
2572
2830
 
2573
2831
  export function doall(x) {
2574
- // realize as concrete array
2575
- return vec(x);
2832
+ // realize a lazy seq and return it, like CLJS; anything else is
2833
+ // already concrete
2834
+ if (x != null && x[TYPE_TAG] === LAZY_ITERABLE_TYPE) for (const _ of x);
2835
+ return x;
2576
2836
  }
2577
2837
 
2578
2838
  export function aclone(arr) {
@@ -2580,13 +2840,53 @@ export function aclone(arr) {
2580
2840
  return cloned;
2581
2841
  }
2582
2842
 
2843
+ function typed_array(sizeOrSeq, initValOrSeq) {
2844
+ if (initValOrSeq !== undefined) {
2845
+ const a = new Array(sizeOrSeq);
2846
+ if (typeof initValOrSeq === 'number') return a.fill(initValOrSeq);
2847
+ let i = 0;
2848
+ for (const x of iterable(initValOrSeq)) {
2849
+ if (i >= sizeOrSeq) break;
2850
+ a[i++] = x;
2851
+ }
2852
+ return a;
2853
+ }
2854
+ if (typeof sizeOrSeq === 'number') return new Array(sizeOrSeq).fill(null);
2855
+ return [...iterable(sizeOrSeq)];
2856
+ }
2857
+
2858
+ // like CLJS: provided for compatibility, all make a plain array
2859
+ export function int_array(sizeOrSeq, initValOrSeq) {
2860
+ return typed_array(sizeOrSeq, initValOrSeq);
2861
+ }
2862
+ export function long_array(sizeOrSeq, initValOrSeq) {
2863
+ return typed_array(sizeOrSeq, initValOrSeq);
2864
+ }
2865
+ export function float_array(sizeOrSeq, initValOrSeq) {
2866
+ return typed_array(sizeOrSeq, initValOrSeq);
2867
+ }
2868
+ export function double_array(sizeOrSeq, initValOrSeq) {
2869
+ return typed_array(sizeOrSeq, initValOrSeq);
2870
+ }
2871
+ export function object_array(sizeOrSeq, initValOrSeq) {
2872
+ return typed_array(sizeOrSeq, initValOrSeq);
2873
+ }
2874
+
2583
2875
  export function add_watch(ref, key, fn) {
2584
- ref._add_watch(key, fn);
2876
+ if (ref?.[IWatchable__add_watch] !== undefined) {
2877
+ ref[IWatchable__add_watch](ref, key, fn);
2878
+ return ref;
2879
+ }
2880
+ nilImpl(_add_watch, 'IWatchable.-add-watch', ref)(ref, key, fn);
2585
2881
  return ref;
2586
2882
  }
2587
2883
 
2588
2884
  export function remove_watch(ref, key) {
2589
- ref._remove_watch(key);
2885
+ if (ref?.[IWatchable__remove_watch] !== undefined) {
2886
+ ref[IWatchable__remove_watch](ref, key);
2887
+ return ref;
2888
+ }
2889
+ nilImpl(_remove_watch, 'IWatchable.-remove-watch', ref)(ref, key);
2590
2890
  return ref;
2591
2891
  }
2592
2892
 
@@ -2639,15 +2939,15 @@ export function every_pred(...preds) {
2639
2939
 
2640
2940
  export function some_fn(...fns) {
2641
2941
  return (...args) => {
2942
+ let res;
2642
2943
  for (const f of fns) {
2643
2944
  for (const a of args) {
2644
- const res = f(a);
2645
- if (res) {
2646
- return res;
2647
- }
2945
+ res = f(a);
2946
+ // truth_, not JS truthiness: 0 and "" are truthy in CLJS
2947
+ if (truth_(res)) return res;
2648
2948
  }
2649
2949
  }
2650
- return undefined;
2950
+ return res;
2651
2951
  };
2652
2952
  }
2653
2953
 
@@ -2791,7 +3091,8 @@ export function re_seq(re, s) {
2791
3091
  }
2792
3092
 
2793
3093
  export function NaN_QMARK_(x) {
2794
- return Number.isNaN(x);
3094
+ // coercing, like CLJS js/isNaN: (NaN? "foo") is true
3095
+ return isNaN(x);
2795
3096
  }
2796
3097
 
2797
3098
  export function number_QMARK_(x) {
@@ -2799,7 +3100,7 @@ export function number_QMARK_(x) {
2799
3100
  }
2800
3101
 
2801
3102
  export function keys(obj) {
2802
- if (obj == null) return;
3103
+ if (obj == null) return null;
2803
3104
  const t = typeConst(obj);
2804
3105
  switch (t) {
2805
3106
  case OBJECT_TYPE: {
@@ -2811,6 +3112,11 @@ export function keys(obj) {
2811
3112
  if (obj.size) return Array.from(obj.keys());
2812
3113
  return;
2813
3114
  }
3115
+ // not a map: nil when empty, like seq in CLJS, else throw
3116
+ for (const _ of iterable(obj)) {
3117
+ throw new TypeError(obj + ' is not a map');
3118
+ }
3119
+ return null;
2814
3120
  }
2815
3121
 
2816
3122
  export function js_keys(obj) {
@@ -2818,7 +3124,7 @@ export function js_keys(obj) {
2818
3124
  }
2819
3125
 
2820
3126
  export function vals(obj) {
2821
- if (obj == null) return;
3127
+ if (obj == null) return null;
2822
3128
  const t = typeConst(obj);
2823
3129
  switch (t) {
2824
3130
  case OBJECT_TYPE: {
@@ -2830,6 +3136,11 @@ export function vals(obj) {
2830
3136
  if (obj.size) return Array.from(obj.values());
2831
3137
  return;
2832
3138
  }
3139
+ // not a map: nil when empty, like seq in CLJS, else throw
3140
+ for (const _ of iterable(obj)) {
3141
+ throw new TypeError(obj + ' is not a map');
3142
+ }
3143
+ return null;
2833
3144
  }
2834
3145
 
2835
3146
  export function string_QMARK_(s) {
@@ -3024,34 +3335,22 @@ export function mod(x, y) {
3024
3335
  }
3025
3336
 
3026
3337
  export function min_key(k, x, ...more) {
3027
- if (more.length == 0) {
3028
- return x;
3338
+ k = __toFn(k);
3339
+ // pairwise (if (< (k x) (k y)) x y), like CLJS, so NaN propagates right
3340
+ let min = x;
3341
+ for (const y of more) {
3342
+ if (!(k(min) < k(y))) min = y;
3029
3343
  }
3030
- var kx = k(x);
3031
- var min = x;
3032
- more.forEach((y) => {
3033
- var ky = k(y);
3034
- if (ky <= kx) {
3035
- kx = ky;
3036
- min = y;
3037
- }
3038
- });
3039
3344
  return min;
3040
3345
  }
3041
3346
 
3042
3347
  export function max_key(k, x, ...more) {
3043
- if (more.length == 0) {
3044
- return x;
3348
+ k = __toFn(k);
3349
+ // pairwise (if (> (k x) (k y)) x y), like CLJS, so NaN propagates right
3350
+ let max = x;
3351
+ for (const y of more) {
3352
+ if (!(k(max) > k(y))) max = y;
3045
3353
  }
3046
- var kx = k(x);
3047
- var max = x;
3048
- more.forEach((y) => {
3049
- var ky = k(y);
3050
- if (ky >= kx) {
3051
- kx = ky;
3052
- max = y;
3053
- }
3054
- });
3055
3354
  return max;
3056
3355
  }
3057
3356
 
@@ -3465,6 +3764,7 @@ export function memoize(f) {
3465
3764
  }
3466
3765
 
3467
3766
  export function peek(vec) {
3767
+ if (vec == null) return null;
3468
3768
  // A list peeks at its front; squint lists are array-backed, so check list
3469
3769
  // before array to avoid returning the last element.
3470
3770
  if (list_QMARK_(vec)) {
@@ -3472,7 +3772,7 @@ export function peek(vec) {
3472
3772
  } else if (array_QMARK_(vec)) {
3473
3773
  return vec[vec.length - 1];
3474
3774
  } else {
3475
- return first(vec);
3775
+ throw missing_protocol('IStack.-peek', vec);
3476
3776
  }
3477
3777
  }
3478
3778
 
@@ -3487,7 +3787,7 @@ export function pop(vec) {
3487
3787
  ret.pop();
3488
3788
  return ret;
3489
3789
  } else {
3490
- return rest(vec);
3790
+ throw missing_protocol('IStack.-pop', vec);
3491
3791
  }
3492
3792
  }
3493
3793
 
@@ -3518,7 +3818,7 @@ export function update_vals(m, f) {
3518
3818
  }
3519
3819
 
3520
3820
  export function random_uuid() {
3521
- return crypto.randomUUID();
3821
+ return new UUID(crypto.randomUUID());
3522
3822
  }
3523
3823
 
3524
3824
  export class UUID {
@@ -3531,20 +3831,34 @@ export class UUID {
3531
3831
  }
3532
3832
 
3533
3833
  export function uuid(s) {
3534
- return new UUID(s);
3834
+ // lowercased, like CLJS
3835
+ return new UUID(s.toLowerCase());
3535
3836
  }
3536
3837
 
3537
3838
  export function uuid_QMARK_(x) {
3538
3839
  return x instanceof UUID;
3539
3840
  }
3540
3841
 
3842
+ 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}$/;
3843
+
3844
+ export function parse_uuid(s) {
3845
+ if (typeof s !== 'string') {
3846
+ throw new Error('Expected string, got: ' + (s === null ? 'nil' : typeof s));
3847
+ }
3848
+ return UUID_REGEX.test(s) ? uuid(s) : null;
3849
+ }
3850
+
3541
3851
  export function inst_QMARK_(x) {
3542
3852
  return x instanceof Date;
3543
3853
  }
3544
3854
 
3855
+ const DELAY_DEREF = (self) => self._deref();
3856
+
3545
3857
  export class Delay {
3546
3858
  constructor(f) {
3547
3859
  this.f = f;
3860
+ this[IDEREF_SYM] = true;
3861
+ this[IDeref__deref] = DELAY_DEREF;
3548
3862
  }
3549
3863
  _deref() {
3550
3864
  if (this.realized) {
@@ -3564,6 +3878,10 @@ export function realized_QMARK_(x) {
3564
3878
  throw new Error('realized? not supported on: ' + str(x));
3565
3879
  }
3566
3880
 
3881
+ export function force(x) {
3882
+ return x instanceof Delay ? x._deref() : x;
3883
+ }
3884
+
3567
3885
  function clj__GT_js_(x, seen) {
3568
3886
  // we need to protect against circular objects
3569
3887
  if (seen.has(x)) return x;
@@ -3591,12 +3909,12 @@ export function not_EQ_(...more) {
3591
3909
  return not(_EQ_(...more));
3592
3910
  }
3593
3911
 
3912
+ const VOLATILE_DEREF = (self) => self.v;
3913
+
3594
3914
  class Volatile {
3595
3915
  constructor(v) {
3596
3916
  this.v = v;
3597
- }
3598
- _deref() {
3599
- return this.v;
3917
+ this[IDeref__deref] = VOLATILE_DEREF;
3600
3918
  }
3601
3919
  }
3602
3920