squint-cljs 0.12.192 → 0.13.194

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.
@@ -1,6 +1,6 @@
1
1
  /*eslint no-unused-vars: ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_"}]*/
2
2
 
3
- // __toFn is not public API the leading underscores mark it as an
3
+ // __toFn is not public API - the leading underscores mark it as an
4
4
  // implementation helper shared with other squint runtime modules
5
5
  // (e.g. multi.js). Signature and semantics may change without notice.
6
6
  export function __toFn(x) {
@@ -24,6 +24,8 @@ function dequal(foo, bar) {
24
24
  // supports primitives, Array, Set, Map and plain objects
25
25
  // like CLJS: does not support NaN
26
26
  if (foo === bar) return true;
27
+ // null and undefined are both nil in CLJS, so they compare equal
28
+ if (foo == null) return bar == null;
27
29
  var ctor, len, tmp;
28
30
 
29
31
  if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
@@ -66,7 +68,9 @@ function dequal(foo, bar) {
66
68
  return true;
67
69
  }
68
70
 
69
- if (!ctor || typeof foo === 'object') {
71
+ // LazyIterable falls through to the sequential-equality path below; it is an
72
+ // object but must compare element-wise, not by enumerable properties
73
+ if ((!ctor || typeof foo === 'object') && !(foo instanceof LazyIterable)) {
70
74
  len = 0;
71
75
  for (const k in foo) {
72
76
  if (has.call(foo, k) && ++len && !has.call(bar, k)) return false;
@@ -75,6 +79,26 @@ function dequal(foo, bar) {
75
79
  return Object.keys(bar).length === len;
76
80
  }
77
81
  }
82
+
83
+ // Cross-type sequential equality, like CLJS `(= '(1 2) [1 2])`: vectors,
84
+ // lists and lazy seqs compare element-wise regardless of concrete type. Only
85
+ // reached when the same-constructor paths above did not apply, so equal-typed
86
+ // collections keep their fast paths.
87
+ if (
88
+ foo && bar &&
89
+ (Array.isArray(foo) || foo instanceof LazyIterable) &&
90
+ (Array.isArray(bar) || bar instanceof LazyIterable)
91
+ ) {
92
+ const fi = foo[Symbol.iterator]();
93
+ const bi = bar[Symbol.iterator]();
94
+ while (true) {
95
+ const a = fi.next();
96
+ const b = bi.next();
97
+ if (a.done || b.done) return !!(a.done && b.done);
98
+ if (!dequal(a.value, b.value)) return false;
99
+ }
100
+ }
101
+
78
102
  return false;
79
103
  }
80
104
  // end inlined version of dequals
@@ -179,16 +203,31 @@ export function assoc_BANG_(m, k, v, ...kvs) {
179
203
  return m;
180
204
  }
181
205
 
206
+ const _metaSym = Symbol('meta');
207
+
208
+ // Copies metadata from `from` onto `to` (when present) and returns `to`. Used
209
+ // to make value-producing operations (copy, empty, conj, into) carry metadata
210
+ // like Clojure does, instead of dropping it on the freshly built structure.
211
+ // Kept branch-light for the hot path: reads an absent symbol property (cheap,
212
+ // no hidden-class change) and only writes when metadata is actually present.
213
+ function copyMeta(from, to) {
214
+ const m = from?.[_metaSym];
215
+ if (m !== undefined) to[_metaSym] = m;
216
+ return to;
217
+ }
218
+
182
219
  function copy(o) {
183
220
  switch (typeConst(o)) {
184
221
  case MAP_TYPE:
185
- return new Map(o);
222
+ return copyMeta(o, new Map(o));
186
223
  case SET_TYPE:
187
- return new o.constructor(o);
224
+ return copyMeta(o, new o.constructor(o));
188
225
  case ARRAY_TYPE:
189
- return [...o];
226
+ return copyMeta(o, [...o]);
190
227
  case OBJECT_TYPE:
191
- return { ...o };
228
+ return copyMeta(o, { ...o });
229
+ case LIST_TYPE:
230
+ return copyMeta(o, new List(...o));
192
231
  default:
193
232
  throw new Error(`Don't know how to copy object of type ${typeof o}.`);
194
233
  }
@@ -399,14 +438,14 @@ export function conj(...xs) {
399
438
  case SET_TYPE:
400
439
  if (o instanceof SortedSet) {
401
440
  // prevent re-sorting of collection
402
- return conj_BANG_set(new o.constructor(o), rest);
441
+ return copyMeta(o, conj_BANG_set(new o.constructor(o), rest));
403
442
  } else {
404
- return new o.constructor([...o, ...rest]);
443
+ return copyMeta(o, new o.constructor([...o, ...rest]));
405
444
  }
406
445
  case LIST_TYPE:
407
- return new List(...rest.reverse(), ...o);
446
+ return copyMeta(o, new List(...rest.reverse(), ...o));
408
447
  case ARRAY_TYPE:
409
- return [...o, ...rest];
448
+ return copyMeta(o, [...o, ...rest]);
410
449
  case MAP_TYPE:
411
450
  m = new Map(o);
412
451
  for (const x of rest) {
@@ -417,7 +456,7 @@ export function conj(...xs) {
417
456
  else m.set(x[0], x[1]);
418
457
  }
419
458
 
420
- return m;
459
+ return copyMeta(o, m);
421
460
  case LAZY_ITERABLE_TYPE:
422
461
  return lazy(function* () {
423
462
  yield* rest;
@@ -431,7 +470,7 @@ export function conj(...xs) {
431
470
  else o2[x[0]] = x[1];
432
471
  }
433
472
 
434
- return o2;
473
+ return copyMeta(o, o2);
435
474
  default:
436
475
  throw new Error(
437
476
  'Illegal argument: conj expects a Set, Array, List, Map, or Object as the first argument.'
@@ -447,6 +486,7 @@ export function disj_BANG_(s, ...xs) {
447
486
  }
448
487
 
449
488
  export function disj(s, ...xs) {
489
+ if (s == null) return s;
450
490
  const s1 = new s.constructor([...s]);
451
491
  return disj_BANG_(s1, ...xs);
452
492
  }
@@ -503,25 +543,29 @@ export function println(...args) {
503
543
  }
504
544
 
505
545
  export function nth(coll, idx, orElse) {
506
- if (coll) {
507
- var elt = undefined;
508
- if (Array.isArray(coll)) {
509
- elt = coll[idx];
510
- } else {
511
- const iter = iterable(coll);
512
- let i = 0;
513
- for (const value of iter) {
514
- if (i++ == idx) {
515
- elt = value;
516
- break;
517
- }
518
- }
546
+ const hasDefault = arguments.length > 2;
547
+ // nil coll puns to nil, like Clojure
548
+ if (coll == null) return hasDefault ? orElse : null;
549
+ // "found" is decided by the index bound, not the value. An in-bounds element
550
+ // that happens to be undefined is still found.
551
+ if (Array.isArray(coll)) {
552
+ if (idx >= 0 && idx < coll.length) {
553
+ return coll[idx];
519
554
  }
520
- if (elt !== undefined) {
521
- return elt;
555
+ } else if (idx >= 0) {
556
+ // non-array: skip whole chunks instead of counting elements (handles
557
+ // infinite seqs since it stops once idx is reached)
558
+ const next = chunkCursor(coll);
559
+ let base = 0;
560
+ let ch;
561
+ while ((ch = next()) !== null) {
562
+ if (idx < base + ch.length) return ch[idx - base];
563
+ base += ch.length;
522
564
  }
523
565
  }
524
- return orElse;
566
+ // out of bounds. With a default return it, otherwise throw like Clojure
567
+ if (hasDefault) return orElse;
568
+ throw new Error('Index out of bounds: ' + idx);
525
569
  }
526
570
 
527
571
  export function get(coll, key, otherwise = undefined) {
@@ -576,21 +620,42 @@ export function seqable_QMARK_(x) {
576
620
  return (
577
621
  x === null ||
578
622
  x === undefined ||
623
+ // plain objects (squint maps) are seqable via Object.entries in `iterable`,
624
+ // even though they lack Symbol.iterator.
625
+ object_QMARK_(x) ||
579
626
  // we used to check instanceof Object but this returns false for TC39 Records
580
627
  // also we used to write `Symbol.iterator in` but this does not work for strings and some other types
581
628
  !!x[Symbol.iterator]
582
629
  );
583
630
  }
584
631
 
632
+ // squint has no distinct MapEntry type (map entries are plain 2-element
633
+ // arrays). We tag entries produced from a map with this marker symbol so
634
+ // map-entry? can tell them apart from ordinary vectors. Symbol-keyed props are
635
+ // invisible to =, into, iteration and JSON, so the effect is contained.
636
+ const MAP_ENTRY = Symbol('squint.lang.map-entry');
637
+
638
+ function tagMapEntry(e) {
639
+ e[MAP_ENTRY] = true;
640
+ return e;
641
+ }
642
+
643
+ export function map_entry_QMARK_(x) {
644
+ return Array.isArray(x) && x[MAP_ENTRY] === true;
645
+ }
646
+
585
647
  export function iterable(x) {
586
648
  // nil puns to empty iterable, support passing nil to first/rest/reduce, etc.
587
649
  if (x === null || x === undefined) {
588
650
  return [];
589
651
  }
590
- if (seqable_QMARK_(x)) {
652
+ // fast path: anything with Symbol.iterator (arrays, strings, sets, maps,
653
+ // lazy seqs). Inlined rather than calling seqable?, which also reports plain
654
+ // objects as seqable; those are handled by the Object.entries branch below.
655
+ if (x[Symbol.iterator]) {
591
656
  return x;
592
657
  }
593
- if (x instanceof Object) return Object.entries(x);
658
+ if (x instanceof Object) return Object.entries(x).map(tagMapEntry);
594
659
  throw new TypeError(`${x} is not iterable`);
595
660
  }
596
661
 
@@ -617,12 +682,24 @@ export function seq(x) {
617
682
  }
618
683
 
619
684
  export function first(coll) {
685
+ if (coll == null) return undefined;
686
+ if (Array.isArray(coll)) return coll[0];
687
+ if (coll instanceof LazyIterable) {
688
+ coll.force();
689
+ return coll.chunk === null ? undefined : coll.chunk[0];
690
+ }
620
691
  // destructuring uses iterable protocol
621
692
  const [first] = iterable(coll);
622
693
  return first;
623
694
  }
624
695
 
625
696
  export function second(coll) {
697
+ if (coll instanceof LazyIterable) {
698
+ coll.force();
699
+ const ch = coll.chunk;
700
+ if (ch === null) return undefined;
701
+ return ch.length > 1 ? ch[1] : first(coll._rest);
702
+ }
626
703
  const [_, v] = iterable(coll);
627
704
  return v;
628
705
  }
@@ -632,13 +709,20 @@ export function ffirst(coll) {
632
709
  }
633
710
 
634
711
  export function rest(coll) {
635
- return lazy(function* () {
636
- let first = true;
637
- for (const x of iterable(coll)) {
638
- if (first) first = false;
639
- else yield x;
640
- }
641
- });
712
+ // chunk-aware: drop the first element of the first chunk, keep the rest of
713
+ // the chain chunked (preserves chunkedness, unlike re-iterating element-wise)
714
+ const cell = chunkCells(coll);
715
+ cell.force();
716
+ const ch = cell.chunk;
717
+ if (ch === null) return cell; // (rest ()) is ()
718
+ if (ch.length > 1) {
719
+ const c = new LazyIterable(null);
720
+ c.realized = true;
721
+ c.chunk = ch.slice(1);
722
+ c._rest = cell._rest;
723
+ return c;
724
+ }
725
+ return cell._rest; // first chunk had one element; the next cell is the rest
642
726
  }
643
727
 
644
728
  class Reduced {
@@ -653,16 +737,15 @@ class Reduced {
653
737
 
654
738
  export function last(coll) {
655
739
  coll = iterable(coll);
656
- let lastEl;
657
- switch (typeConst(coll)) {
658
- case ARRAY_TYPE:
659
- return coll[coll.length - 1];
660
- default:
661
- for (const x of coll) {
662
- lastEl = x;
663
- }
664
- return lastEl;
740
+ if (Array.isArray(coll)) {
741
+ return coll[coll.length - 1];
665
742
  }
743
+ // non-array: walk chunks, keep the last chunk's last element
744
+ const next = chunkCursor(coll);
745
+ let lastEl;
746
+ let ch;
747
+ while ((ch = next()) !== null) lastEl = ch[ch.length - 1];
748
+ return lastEl;
666
749
  }
667
750
 
668
751
  export function reduced(x) {
@@ -675,31 +758,43 @@ export function reduced_QMARK_(x) {
675
758
 
676
759
  export function reduce(f, arg1, arg2) {
677
760
  f = __toFn(f);
678
- let coll, val;
679
- if (arguments.length === 2) {
680
- // (reduce f coll)
681
- const iter = iterable(arg1)[Symbol.iterator]();
682
- const vd = iter.next();
683
- if (vd.done) {
684
- val = f();
685
- } else {
686
- val = vd.value;
761
+ const hasInit = arguments.length !== 2;
762
+ const coll = hasInit ? arg2 : arg1;
763
+ let val = hasInit ? arg1 : undefined;
764
+
765
+ // fast path: index loop over an array
766
+ if (Array.isArray(coll)) {
767
+ let i = 0;
768
+ if (!hasInit) {
769
+ if (coll.length === 0) return f();
770
+ val = coll[0];
771
+ i = 1;
687
772
  }
688
- coll = iter;
689
- } else {
690
- // (reduce f val coll)
691
- val = arg1;
692
- coll = iterable(arg2);
773
+ if (val instanceof Reduced) return val.value;
774
+ for (; i < coll.length; i++) {
775
+ val = f(val, coll[i]);
776
+ if (val instanceof Reduced) return val.value;
777
+ }
778
+ return val;
693
779
  }
694
- if (val instanceof Reduced) {
695
- return val.value;
780
+
781
+ // non-array: walk chunks (chunked cell or any other seqable)
782
+ const next = chunkCursor(coll);
783
+ let ch = next();
784
+ let i = 0;
785
+ if (!hasInit) {
786
+ if (ch === null) return f();
787
+ val = ch[0];
788
+ i = 1;
696
789
  }
697
- for (const x of coll) {
698
- val = f(val, x);
699
- if (val instanceof Reduced) {
700
- val = val.value;
701
- break;
790
+ if (val instanceof Reduced) return val.value;
791
+ while (ch !== null) {
792
+ for (; i < ch.length; i++) {
793
+ val = f(val, ch[i]);
794
+ if (val instanceof Reduced) return val.value;
702
795
  }
796
+ ch = next();
797
+ i = 0;
703
798
  }
704
799
  return val;
705
800
  }
@@ -732,42 +827,152 @@ function* _reductions3(f, init, coll) {
732
827
  export function reductions(f, arg1, arg2) {
733
828
  f = __toFn(f);
734
829
  if (arguments.length === 2) {
830
+ const it = es6_iterator(iterable(arg1));
735
831
  return lazy(function* () {
736
- yield* _reductions2(f, iterable(arg1)[Symbol.iterator]());
832
+ yield* _reductions2(f, it);
737
833
  });
738
834
  }
835
+ const it = es6_iterator(iterable(arg2));
739
836
  return lazy(function* () {
740
- yield* _reductions3(f, arg1, iterable(arg2)[Symbol.iterator]());
837
+ yield* _reductions3(f, arg1, it);
741
838
  });
742
839
  }
743
840
 
744
- var tolr = false;
841
+ // Deprecated: lazy values are now cached, so reuse no longer recomputes. Kept
842
+ // for API compatibility; remove in a future release.
745
843
  export function warn_on_lazy_reusage_BANG_() {
746
- tolr = true;
844
+ console.warn(
845
+ 'warn-on-lazy-reusage! is deprecated and does nothing: lazy values are now cached.',
846
+ );
747
847
  }
748
848
 
849
+ const CHUNK_SIZE = 32;
850
+
851
+ // One cell of a self-caching chunked seq. `step` is a thunk returning
852
+ // [nonEmptyChunkArray, nextStep] or null at the end. See doc/dev/lazy-seqs.md.
749
853
  class LazyIterable {
750
- constructor(gen) {
751
- this.gen = gen;
752
- this.usages = 0;
753
- }
754
- [Symbol.iterator]() {
755
- this.usages++;
756
- if (this.usages >= 2 && tolr) {
757
- try {
758
- throw new Error();
759
- } catch (e) {
760
- console.warn('Re-use of lazy value', e.stack);
854
+ constructor(step) {
855
+ this.step = step;
856
+ this.realized = false;
857
+ this.chunk = null; // array, or null when this is the terminal (empty) cell
858
+ this._rest = null;
859
+ }
860
+ force() {
861
+ if (!this.realized) {
862
+ this.realized = true;
863
+ const r = this.step();
864
+ this.step = null;
865
+ if (r !== null && r !== undefined) {
866
+ this.chunk = r[0];
867
+ this._rest = new LazyIterable(r[1]);
761
868
  }
762
869
  }
763
- return this.gen();
870
+ return this;
871
+ }
872
+ [Symbol.iterator]() {
873
+ let cell = this;
874
+ let i = 0;
875
+ return {
876
+ next() {
877
+ for (;;) {
878
+ cell.force();
879
+ const ch = cell.chunk;
880
+ if (ch === null) return { value: undefined, done: true };
881
+ if (i < ch.length) return { value: ch[i++], done: false };
882
+ cell = cell._rest;
883
+ i = 0;
884
+ }
885
+ },
886
+ [Symbol.iterator]() {
887
+ return this;
888
+ },
889
+ };
764
890
  }
765
891
  }
766
892
 
767
893
  LazyIterable.prototype[IIterable] = true; // Closure compatibility
768
894
 
895
+ // One-element chunks: an unchunked seq, realized one element at a time.
896
+ function unchunkedSteps(iter) {
897
+ const step = () => {
898
+ const r = iter.next();
899
+ return r.done ? null : [[r.value], step];
900
+ };
901
+ return step;
902
+ }
903
+
904
+ // f is a zero-arg generator function; its result is an unchunked lazy seq.
769
905
  export function lazy(f) {
770
- return new LazyIterable(f);
906
+ return new LazyIterable(unchunkedSteps(f()));
907
+ }
908
+
909
+ // gen(it) over a hoisted iterator: keeps the input head unpinned (streaming)
910
+ function lazyIter(coll, gen) {
911
+ const it = es6_iterator(iterable(coll));
912
+ return lazy(() => gen(it));
913
+ }
914
+
915
+ // A chunked view of any seqable for chunk-aware ops, preserving chunkedness:
916
+ // cells pass through, arrays slice into CHUNK_SIZE batches, others stay unchunked.
917
+ function chunkCells(coll) {
918
+ if (coll instanceof LazyIterable) return coll;
919
+ if (Array.isArray(coll)) {
920
+ const step = (pos) => () => {
921
+ if (pos >= coll.length) return null;
922
+ const end = Math.min(pos + CHUNK_SIZE, coll.length);
923
+ return [coll.slice(pos, end), step(end)];
924
+ };
925
+ return new LazyIterable(step(0));
926
+ }
927
+ return new LazyIterable(unchunkedSteps(es6_iterator(iterable(coll))));
928
+ }
929
+
930
+ // A cursor over a seq's chunks for realizers: returns a function yielding the
931
+ // next chunk array or null. Drive it with an inline loop (keeps an accumulator a
932
+ // plain local, unlike a callback). Callers keep their own array shortcut.
933
+ function chunkCursor(coll) {
934
+ if (coll instanceof LazyIterable) {
935
+ let cell = coll;
936
+ return () => {
937
+ if (cell === null) return null;
938
+ cell.force();
939
+ const ch = cell.chunk;
940
+ cell = ch === null ? null : cell._rest;
941
+ return ch;
942
+ };
943
+ }
944
+ const it = es6_iterator(iterable(coll));
945
+ return () => {
946
+ const b = [];
947
+ for (let i = 0; i < CHUNK_SIZE; i++) {
948
+ const r = it.next();
949
+ if (r.done) break;
950
+ b.push(r.value);
951
+ }
952
+ return b.length === 0 ? null : b;
953
+ };
954
+ }
955
+
956
+ // Build a lazy seq transforming each input chunk via xf(chunk, baseIndex) -> new
957
+ // chunk array, preserving chunkedness. Empty results are skipped (e.g. filter).
958
+ // baseIndex is the input element count before the chunk, for indexed ops.
959
+ function mapChunks(coll, xf) {
960
+ const src = chunkCells(coll);
961
+ const step = (cell, base) => () => {
962
+ let c = cell;
963
+ let b = base;
964
+ for (;;) {
965
+ c.force();
966
+ const ch = c.chunk;
967
+ if (ch === null) return null;
968
+ const out = xf(ch, b);
969
+ const rest = c._rest;
970
+ b += ch.length;
971
+ if (out.length !== 0) return [out, step(rest, b)];
972
+ c = rest;
973
+ }
974
+ };
975
+ return new LazyIterable(step(src, 0));
771
976
  }
772
977
 
773
978
  export class Cons {
@@ -775,24 +980,35 @@ export class Cons {
775
980
  this.x = x;
776
981
  this.coll = coll;
777
982
  }
778
- *[Symbol.iterator]() {
779
- yield this.x;
780
- yield* iterable(this.coll);
983
+ [Symbol.iterator]() {
984
+ const x = this.x;
985
+ let coll = this.coll;
986
+ let started = false;
987
+ let it = null;
988
+ return {
989
+ next() {
990
+ if (!started) {
991
+ started = true;
992
+ return { value: x, done: false };
993
+ }
994
+ if (!it) {
995
+ it = es6_iterator(iterable(coll));
996
+ coll = null; // release the tail head so a single pass streams
997
+ }
998
+ return it.next();
999
+ },
1000
+ [Symbol.iterator]() {
1001
+ return this;
1002
+ },
1003
+ };
781
1004
  }
782
1005
  }
783
1006
 
784
1007
  export function cons(x, coll) {
785
1008
  return new Cons(x, coll);
786
- // return lazy(function* () {
787
- // yield x;
788
- // yield* iterable(coll);
789
- // });
790
1009
  }
791
1010
 
792
1011
  export function map(f, ...colls) {
793
- // if (! (f instanceof Function)) {
794
- // throw new Error(`Argument f must be a function but is ${typeof(f)}`);
795
- // }
796
1012
  f = __toFn(f);
797
1013
  switch (colls.length) {
798
1014
  case 0:
@@ -815,14 +1031,14 @@ export function map(f, ...colls) {
815
1031
  };
816
1032
  };
817
1033
  case 1:
818
- return lazy(function* () {
819
- for (const x of iterable(colls[0])) {
820
- yield f(x);
821
- }
1034
+ return mapChunks(colls[0], (ch) => {
1035
+ const out = new Array(ch.length);
1036
+ for (let i = 0; i < ch.length; i++) out[i] = f(ch[i]);
1037
+ return out;
822
1038
  });
823
- default:
1039
+ default: {
1040
+ const iters = colls.map((coll) => es6_iterator(iterable(coll)));
824
1041
  return lazy(function* () {
825
- const iters = colls.map((coll) => es6_iterator(iterable(coll)));
826
1042
  while (true) {
827
1043
  const args = [];
828
1044
  for (const i of iters) {
@@ -835,45 +1051,41 @@ export function map(f, ...colls) {
835
1051
  yield f(...args);
836
1052
  }
837
1053
  });
1054
+ }
838
1055
  }
839
1056
  }
840
1057
 
841
- function filter1(pred) {
1058
+ // 0/1 arities pass through to rf; step(rf) is the 2-arity reducer
1059
+ function transducer(step) {
842
1060
  return (rf) => {
843
- return (...args) => {
844
- switch (args.length) {
845
- case 0:
846
- return rf();
847
- case 1:
848
- return rf(args[0]);
849
- case 2: {
850
- const result = args[0];
851
- const input = args[1];
852
- if (truth_(pred(input))) {
853
- return rf(result, input);
854
- } else return result;
855
- }
856
- }
857
- };
1061
+ const s = step(rf);
1062
+ return (...args) =>
1063
+ args.length === 0 ? rf() : args.length === 1 ? rf(args[0]) : s(args[0], args[1]);
858
1064
  };
859
1065
  }
860
1066
 
1067
+ function filter1(pred) {
1068
+ return transducer((rf) => (r, x) => (truth_(pred(x)) ? rf(r, x) : r));
1069
+ }
1070
+
861
1071
  export function filter(pred, coll) {
862
1072
  if (arguments.length === 1) {
863
1073
  return filter1(pred);
864
1074
  }
865
1075
  pred = __toFn(pred);
866
- return lazy(function* () {
867
- for (const x of iterable(coll)) {
868
- if (truth_(pred(x))) {
869
- yield x;
870
- }
1076
+ return mapChunks(coll, (ch) => {
1077
+ const out = [];
1078
+ for (let i = 0; i < ch.length; i++) {
1079
+ const x = ch[i];
1080
+ if (truth_(pred(x))) out.push(x);
871
1081
  }
1082
+ return out;
872
1083
  });
873
1084
  }
874
1085
 
875
1086
  export function filterv(pred, coll) {
876
- return [...filter(pred, coll)];
1087
+ // filter is chunked; vec bulk-appends its chunks
1088
+ return pushAll([], filter(pred, coll));
877
1089
  }
878
1090
 
879
1091
  export function remove(pred, coll) {
@@ -884,19 +1096,10 @@ export function remove(pred, coll) {
884
1096
  }
885
1097
 
886
1098
  function map_indexed1(f) {
887
- return (rf) => {
1099
+ return transducer((rf) => {
888
1100
  let i = -1;
889
- return (...args) => {
890
- switch (args.length) {
891
- case 0:
892
- return rf();
893
- case 1:
894
- return rf(args[0]);
895
- case 2:
896
- return rf(args[0], f(((i = i + 1), i), args[1]));
897
- }
898
- };
899
- };
1101
+ return (r, x) => rf(r, f(++i, x));
1102
+ });
900
1103
  }
901
1104
 
902
1105
  export function map_indexed(f, coll) {
@@ -904,50 +1107,33 @@ export function map_indexed(f, coll) {
904
1107
  if (arguments.length === 1) {
905
1108
  return map_indexed1(f);
906
1109
  }
907
- return lazy(function* () {
908
- let idx = 0;
909
- for (const i of iterable(coll)) {
910
- yield f(idx, i);
911
- idx++;
912
- }
1110
+ return mapChunks(coll, (ch, base) => {
1111
+ const out = new Array(ch.length);
1112
+ for (let i = 0; i < ch.length; i++) out[i] = f(base + i, ch[i]);
1113
+ return out;
913
1114
  });
914
1115
  }
915
1116
 
916
1117
  function keep_indexed2(f, coll) {
917
1118
  f = __toFn(f);
918
- return lazy(function* () {
919
- let idx = 0;
920
- for (const i of iterable(coll)) {
921
- const v = f(idx, i);
922
- if (truth_(v)) yield v;
923
- idx++;
1119
+ return mapChunks(coll, (ch, base) => {
1120
+ const out = [];
1121
+ for (let i = 0; i < ch.length; i++) {
1122
+ const v = f(base + i, ch[i]);
1123
+ if (truth_(v)) out.push(v);
924
1124
  }
1125
+ return out;
925
1126
  });
926
1127
  }
927
1128
 
928
1129
  function keep_indexed1(f) {
929
- return (rf) => {
1130
+ return transducer((rf) => {
930
1131
  let ia = -1;
931
- return (...args) => {
932
- const al = args.length;
933
- if (al === 0) {
934
- return rf();
935
- }
936
- if (al === 1) {
937
- return rf(args[0]);
938
- }
939
- if (al === 2) {
940
- const result = args[0];
941
- const input = args[1];
942
- ia++;
943
- const v = f(ia, input);
944
- if (v == null) {
945
- return result;
946
- }
947
- return rf(result, v);
948
- }
1132
+ return (r, x) => {
1133
+ const v = f(++ia, x);
1134
+ return v == null ? r : rf(r, v);
949
1135
  };
950
- };
1136
+ });
951
1137
  }
952
1138
 
953
1139
  export function keep_indexed(f, coll) {
@@ -963,7 +1149,12 @@ export function str(...xs) {
963
1149
  }
964
1150
 
965
1151
  export function name(x) {
966
- if (typeof x === 'string') return x;
1152
+ if (typeof x === 'string') {
1153
+ // keywords/symbols are strings in squint; name is the part after the "/"
1154
+ // ns separator (consistent with `namespace`, which returns the part before)
1155
+ const i = x.indexOf('/');
1156
+ return i >= 1 ? x.slice(i + 1) : x;
1157
+ }
967
1158
  throw new Error("Doesn't support name: " + typeof x);
968
1159
  }
969
1160
 
@@ -1048,22 +1239,29 @@ export function compare_and_set_BANG_(atm, oldv, newv) {
1048
1239
  }
1049
1240
 
1050
1241
  export function range(begin, end, step) {
1051
- return lazy(function* () {
1052
- let b = begin,
1053
- e = end,
1054
- s = step;
1055
- if (end === undefined) {
1056
- b = 0;
1057
- e = begin;
1058
- }
1059
- let i = b || 0;
1060
- s = step ?? 1;
1061
- const ascending = s >= 0;
1062
- while (e === undefined || (ascending && i < e) || (!ascending && e < i)) {
1063
- yield i;
1242
+ // range is a chunked source: it realizes CHUNK_SIZE values at a time
1243
+ let b = begin,
1244
+ e = end,
1245
+ s = step;
1246
+ if (end === undefined) {
1247
+ b = 0;
1248
+ e = begin;
1249
+ }
1250
+ const start = b || 0;
1251
+ s = step ?? 1;
1252
+ const ascending = s >= 0;
1253
+ const more = (i) => e === undefined || (ascending && i < e) || (!ascending && e < i);
1254
+ const mkStep = (from) => () => {
1255
+ if (!more(from)) return null;
1256
+ const out = [];
1257
+ let i = from;
1258
+ while (out.length < CHUNK_SIZE && more(i)) {
1259
+ out.push(i);
1064
1260
  i += s;
1065
1261
  }
1066
- });
1262
+ return [out, mkStep(i)];
1263
+ };
1264
+ return new LazyIterable(mkStep(start));
1067
1265
  }
1068
1266
 
1069
1267
  export function re_matches(re, s) {
@@ -1136,9 +1334,12 @@ export function mapv(...args) {
1136
1334
  }
1137
1335
  return ret;
1138
1336
  } else {
1139
- var ret = [];
1140
- for (const x of iter) {
1141
- ret.push(f(x));
1337
+ // non-array: map whole chunks straight into the result array
1338
+ const ret = [];
1339
+ const next = chunkCursor(iter);
1340
+ let ch;
1341
+ while ((ch = next()) !== null) {
1342
+ for (let i = 0; i < ch.length; i++) ret.push(f(ch[i]));
1142
1343
  }
1143
1344
  return ret;
1144
1345
  }
@@ -1146,12 +1347,32 @@ export function mapv(...args) {
1146
1347
  return [...map(...args)];
1147
1348
  }
1148
1349
 
1149
- export function vec(x) {
1150
- if (array_QMARK_(x)) {
1151
- // return original, no need to clone the entire thing
1152
- return x;
1350
+ // Append every element of `from` to array `out`, bulk-appending whole chunks
1351
+ // for a chunked seq. Returns out.
1352
+ function pushAll(out, from) {
1353
+ if (from instanceof LazyIterable) {
1354
+ let cell = from;
1355
+ for (;;) {
1356
+ cell.force();
1357
+ const ch = cell.chunk;
1358
+ if (ch === null) return out;
1359
+ Array.prototype.push.apply(out, ch);
1360
+ cell = cell._rest;
1361
+ }
1153
1362
  }
1154
- return [...iterable(x)];
1363
+ for (const x of iterable(from)) out.push(x);
1364
+ return out;
1365
+ }
1366
+
1367
+ // Materialize a seq into a fresh, mutable array (bulk-copies chunked seqs).
1368
+ function toArray(coll) {
1369
+ if (coll instanceof LazyIterable) return pushAll([], coll);
1370
+ return [...iterable(coll)];
1371
+ }
1372
+
1373
+ export function vec(x) {
1374
+ if (array_QMARK_(x)) return x;
1375
+ return pushAll([], x);
1155
1376
  }
1156
1377
 
1157
1378
  export function set(coll) {
@@ -1216,12 +1437,54 @@ export function array_QMARK_(x) {
1216
1437
  return Array.isArray(x);
1217
1438
  }
1218
1439
 
1440
+ const CONCAT_DONE = Symbol('concat-done');
1441
+
1219
1442
  function concat1(colls) {
1220
- return lazy(function* () {
1221
- for (const coll of colls) {
1222
- yield* iterable(coll);
1443
+ // chunk-aware: pass each coll's chunks through, preserving chunkedness. Each
1444
+ // coll head is released once consumed so a single pass streams.
1445
+ const isArr = Array.isArray(colls);
1446
+ const arr = isArr ? colls.slice() : null;
1447
+ const collIter = isArr ? null : es6_iterator(iterable(colls));
1448
+ let idx = 0;
1449
+ const nextColl = () => {
1450
+ if (isArr) {
1451
+ if (idx >= arr.length) return CONCAT_DONE;
1452
+ const c = arr[idx];
1453
+ arr[idx] = null;
1454
+ idx++;
1455
+ return c;
1223
1456
  }
1224
- });
1457
+ const r = collIter.next();
1458
+ return r.done ? CONCAT_DONE : r.value;
1459
+ };
1460
+ // src is null, a chunked cell, or {a, pos} for an array (raw-array fast path:
1461
+ // no per-coll wrapper). Continuations carry immutable state, so a single pass
1462
+ // streams.
1463
+ const step = (src) => () => {
1464
+ for (;;) {
1465
+ if (src !== null) {
1466
+ if (src instanceof LazyIterable) {
1467
+ src.force();
1468
+ if (src.chunk !== null) return [src.chunk, step(src._rest)];
1469
+ src = null;
1470
+ } else {
1471
+ const a = src.a;
1472
+ const pos = src.pos;
1473
+ if (pos < a.length) {
1474
+ const end = Math.min(pos + CHUNK_SIZE, a.length);
1475
+ // slice (copy) so a cached chunk never aliases the caller's array
1476
+ return [a.slice(pos, end), step({ a, pos: end })];
1477
+ }
1478
+ src = null;
1479
+ }
1480
+ }
1481
+ const nc = nextColl();
1482
+ if (nc === CONCAT_DONE) return null;
1483
+ src =
1484
+ nc instanceof LazyIterable ? nc : Array.isArray(nc) ? { a: nc, pos: 0 } : chunkCells(nc);
1485
+ }
1486
+ };
1487
+ return new LazyIterable(step(null));
1225
1488
  }
1226
1489
 
1227
1490
  export function concat(...colls) {
@@ -1236,10 +1499,8 @@ concat[IApply__apply] = (colls) => {
1236
1499
  export function mapcat(f, ...colls) {
1237
1500
  if (colls.length === 0) {
1238
1501
  return comp(map(f), cat);
1239
- } else {
1240
- const mapped = map(f, ...colls);
1241
- return concat1(mapped);
1242
1502
  }
1503
+ return concat1(map(f, ...colls));
1243
1504
  }
1244
1505
 
1245
1506
  export function identity(x) {
@@ -1247,8 +1508,8 @@ export function identity(x) {
1247
1508
  }
1248
1509
 
1249
1510
  export function interleave(...colls) {
1511
+ const iters = colls.map((coll) => es6_iterator(iterable(coll)));
1250
1512
  return lazy(function* () {
1251
- const iters = colls.map((coll) => es6_iterator(iterable(coll)));
1252
1513
  while (true) {
1253
1514
  const res = [];
1254
1515
  for (const i of iters) {
@@ -1264,30 +1525,17 @@ export function interleave(...colls) {
1264
1525
  }
1265
1526
 
1266
1527
  function interpose1(sep) {
1267
- return (rf) => {
1528
+ return transducer((rf) => {
1268
1529
  let started = false;
1269
- return (...args) => {
1270
- switch (args.length) {
1271
- case 0:
1272
- return rf();
1273
- case 1:
1274
- return rf(args[0]);
1275
- case 2: {
1276
- if (started) {
1277
- const sepr = rf(args[0], sep);
1278
- if (reduced_QMARK_(sepr)) {
1279
- return sepr;
1280
- } else {
1281
- return rf(sepr, args[1]);
1282
- }
1283
- } else {
1284
- started = true;
1285
- return rf(args[0], args[1]);
1286
- }
1287
- }
1530
+ return (r, x) => {
1531
+ if (!started) {
1532
+ started = true;
1533
+ return rf(r, x);
1288
1534
  }
1535
+ const sepr = rf(r, sep);
1536
+ return reduced_QMARK_(sepr) ? sepr : rf(sepr, x);
1289
1537
  };
1290
- };
1538
+ });
1291
1539
  }
1292
1540
 
1293
1541
  export function interpose(sep, coll) {
@@ -1301,7 +1549,7 @@ export function select_keys(o, ks) {
1301
1549
  const ret = emptyOfType(type) || {};
1302
1550
  for (const k of ks) {
1303
1551
  const v = get(o, k);
1304
- if (v != undefined) {
1552
+ if (v !== undefined) {
1305
1553
  assoc_BANG_(ret, k, v);
1306
1554
  }
1307
1555
  }
@@ -1379,10 +1627,10 @@ export const partitionv = partition; // partition already returns a lazy of arra
1379
1627
  export const partitionv_all = partition_all;
1380
1628
 
1381
1629
  function partitionInternal(n, step, pad, coll, all) {
1382
- return lazy(function* () {
1630
+ return lazyIter(coll, function* (it) {
1383
1631
  let p = [];
1384
1632
  let i = 0;
1385
- for (const x of iterable(coll)) {
1633
+ for (const x of it) {
1386
1634
  if (i < n) {
1387
1635
  p.push(x);
1388
1636
  if (p.length === n) {
@@ -1454,8 +1702,8 @@ export function partition_by(f, coll) {
1454
1702
  if (arguments.length === 1) {
1455
1703
  return partition_by1(f);
1456
1704
  }
1705
+ const iter = es6_iterator(coll);
1457
1706
  return lazy(function* () {
1458
- const iter = es6_iterator(coll);
1459
1707
  const _fst = iter.next();
1460
1708
  if (_fst.done) {
1461
1709
  yield* null;
@@ -1488,7 +1736,7 @@ export function partition_by(f, coll) {
1488
1736
  export function empty(coll) {
1489
1737
  const type = typeConst(coll);
1490
1738
  if (type != null) {
1491
- return emptyOfType(type);
1739
+ return copyMeta(coll, emptyOfType(type));
1492
1740
  } else {
1493
1741
  throw new Error(`Can't create empty of ${typeof coll}`);
1494
1742
  }
@@ -1554,7 +1802,13 @@ export function into(...args) {
1554
1802
  case 1:
1555
1803
  return args[0];
1556
1804
  case 2:
1557
- return conj(args[0] ?? [], ...iterable(args[1]));
1805
+ // vector target bulk-appends chunks (copy preserves metadata); lists conj
1806
+ // at the head, other targets need conj!
1807
+ to = args[0] ?? [];
1808
+ if (Array.isArray(to) && !(to instanceof List)) {
1809
+ return pushAll(copy(to), args[1]);
1810
+ }
1811
+ return reduce(conj_BANG_, copy(to), args[1]);
1558
1812
  case 3:
1559
1813
  to = args[0];
1560
1814
  xform = args[1];
@@ -1605,42 +1859,23 @@ export function ensure_reduced(x) {
1605
1859
  }
1606
1860
 
1607
1861
  function take1(n) {
1608
- return (rf) => {
1862
+ return transducer((rf) => {
1609
1863
  let na = n;
1610
- return (...args) => {
1611
- const al = args.length;
1612
- if (al === 0) {
1613
- return rf();
1614
- }
1615
- if (al === 1) {
1616
- const result = args[0];
1617
- return rf(result);
1618
- }
1619
- if (al === 2) {
1620
- let result = args[0];
1621
- const input = args[1];
1622
- const n = na;
1623
- const nn = ((na = na - 1), na);
1624
- if (n > 0) {
1625
- result = rf(result, input);
1626
- }
1627
- if (!(nn > 0)) {
1628
- return ensure_reduced(result);
1629
- } else {
1630
- return result;
1631
- }
1632
- }
1864
+ return (r, x) => {
1865
+ const nn = --na + 1;
1866
+ if (nn > 0) r = rf(r, x);
1867
+ return nn > 1 ? r : ensure_reduced(r);
1633
1868
  };
1634
- };
1869
+ });
1635
1870
  }
1636
1871
 
1637
1872
  export function take(n, coll) {
1638
1873
  if (arguments.length === 1) {
1639
1874
  return take1(n);
1640
1875
  }
1641
- return lazy(function* () {
1876
+ return lazyIter(coll, function* (it) {
1642
1877
  let i = n - 1;
1643
- for (const x of iterable(coll)) {
1878
+ for (const x of it) {
1644
1879
  if (i-- >= 0) {
1645
1880
  yield x;
1646
1881
  }
@@ -1674,22 +1909,7 @@ export function take_last(n, coll) {
1674
1909
  }
1675
1910
 
1676
1911
  function take_while1(pred) {
1677
- return (rf) => {
1678
- return (...args) => {
1679
- const al = args.length;
1680
- if (al === 0) return rf();
1681
- if (al === 1) return rf(args[0]);
1682
- if (al === 2) {
1683
- const result = args[0];
1684
- const input = args[1];
1685
- if (truth_(pred(input))) {
1686
- return rf(result, input);
1687
- } else {
1688
- return reduced(result);
1689
- }
1690
- }
1691
- };
1692
- };
1912
+ return transducer((rf) => (r, x) => (truth_(pred(x)) ? rf(r, x) : reduced(r)));
1693
1913
  }
1694
1914
 
1695
1915
  export function take_while(pred, coll) {
@@ -1697,8 +1917,8 @@ export function take_while(pred, coll) {
1697
1917
  if (arguments.length === 1) {
1698
1918
  return take_while1(pred);
1699
1919
  }
1700
- return lazy(function* () {
1701
- for (const o of iterable(coll)) {
1920
+ return lazyIter(coll, function* (it) {
1921
+ for (const o of it) {
1702
1922
  if (truth_(pred(o))) yield o;
1703
1923
  else return;
1704
1924
  }
@@ -1706,23 +1926,10 @@ export function take_while(pred, coll) {
1706
1926
  }
1707
1927
 
1708
1928
  function take_nth1(n) {
1709
- return (rf) => {
1929
+ return transducer((rf) => {
1710
1930
  let ia = -1;
1711
- return (...args) => {
1712
- const al = args.length;
1713
- if (al === 0) return rf();
1714
- if (al === 1) return rf(args[0]);
1715
- if (al === 2) {
1716
- const result = args[0];
1717
- const input = args[1];
1718
- ia++;
1719
- const i = ia;
1720
- if (rem(i, n) === 0) {
1721
- return rf(result, input);
1722
- } else return result;
1723
- }
1724
- };
1725
- };
1931
+ return (r, x) => (rem(++ia, n) === 0 ? rf(r, x) : r);
1932
+ });
1726
1933
  }
1727
1934
 
1728
1935
  export function take_nth(n, coll) {
@@ -1731,9 +1938,9 @@ export function take_nth(n, coll) {
1731
1938
  return repeat(first(coll));
1732
1939
  }
1733
1940
 
1734
- return lazy(function* () {
1941
+ return lazyIter(coll, function* (it) {
1735
1942
  let i = 0;
1736
- for (const x of iterable(coll)) {
1943
+ for (const x of it) {
1737
1944
  if (i % n === 0) {
1738
1945
  yield x;
1739
1946
  }
@@ -1756,33 +1963,15 @@ export function cycle(coll) {
1756
1963
  }
1757
1964
 
1758
1965
  function drop1(n) {
1759
- return (rf) => {
1966
+ return transducer((rf) => {
1760
1967
  let na = n;
1761
- return (...args) => {
1762
- const al = args.length;
1763
- if (al === 0) {
1764
- return rf();
1765
- }
1766
- if (al === 1) {
1767
- return rf(args[0]);
1768
- }
1769
- if (al === 2) {
1770
- const result = args[0];
1771
- const input = args[1];
1772
- const n = na;
1773
- na--;
1774
- if (n > 0) {
1775
- return result;
1776
- } else return rf(result, input);
1777
- }
1778
- };
1779
- };
1968
+ return (r, x) => (na-- > 0 ? r : rf(r, x));
1969
+ });
1780
1970
  }
1781
1971
 
1782
1972
  export function drop(n, xs) {
1783
1973
  if (arguments.length === 1) return drop1(n);
1784
- return lazy(function* () {
1785
- const iter = _iterator(iterable(xs));
1974
+ return lazyIter(xs, function* (iter) {
1786
1975
  for (let x = 0; x < n; x++) {
1787
1976
  iter.next();
1788
1977
  }
@@ -1791,36 +1980,20 @@ export function drop(n, xs) {
1791
1980
  }
1792
1981
 
1793
1982
  function drop_while1(pred) {
1794
- return (rf) => {
1983
+ return transducer((rf) => {
1795
1984
  let da = true;
1796
- return (...args) => {
1797
- const al = args.length;
1798
- if (al === 0) {
1799
- return rf();
1800
- }
1801
- if (al === 1) {
1802
- return rf(args[0]);
1803
- }
1804
- if (al === 2) {
1805
- const isDrop = da;
1806
- const result = args[0];
1807
- const input = args[1];
1808
- if (isDrop && truth_(pred(input))) {
1809
- return result;
1810
- } else {
1811
- da = null;
1812
- return rf(result, input);
1813
- }
1814
- }
1985
+ return (r, x) => {
1986
+ if (da && truth_(pred(x))) return r;
1987
+ da = false;
1988
+ return rf(r, x);
1815
1989
  };
1816
- };
1990
+ });
1817
1991
  }
1818
1992
 
1819
1993
  export function drop_while(pred, xs) {
1820
1994
  pred = __toFn(pred);
1821
1995
  if (arguments.length === 1) return drop_while1(pred);
1822
- return lazy(function* () {
1823
- const iter = _iterator(iterable(xs));
1996
+ return lazyIter(xs, function* (iter) {
1824
1997
  while (true) {
1825
1998
  const nextItem = iter.next();
1826
1999
  if (nextItem.done) {
@@ -1837,28 +2010,21 @@ export function drop_while(pred, xs) {
1837
2010
  }
1838
2011
 
1839
2012
  function distinct1() {
1840
- return (rf) => {
2013
+ return transducer((rf) => {
1841
2014
  const seen = new Set();
1842
- return (...args) => {
1843
- const al = args.length;
1844
- if (al === 0) return rf();
1845
- if (al === 1) return rf(args[0]);
1846
- if (al === 2) {
1847
- const result = args[0];
1848
- const input = args[1];
1849
- if (seen.has(input)) return result;
1850
- seen.add(input);
1851
- return rf(result, input);
1852
- }
2015
+ return (r, x) => {
2016
+ if (seen.has(x)) return r;
2017
+ seen.add(x);
2018
+ return rf(r, x);
1853
2019
  };
1854
- };
2020
+ });
1855
2021
  }
1856
2022
 
1857
2023
  export function distinct(coll) {
1858
2024
  if (arguments.length === 0) return distinct1();
1859
- return lazy(function* () {
2025
+ return lazyIter(coll, function* (it) {
1860
2026
  const seen = new Set();
1861
- for (const x of iterable(coll)) {
2027
+ for (const x of it) {
1862
2028
  if (!seen.has(x)) yield x;
1863
2029
  seen.add(x);
1864
2030
  }
@@ -1869,28 +2035,21 @@ export function distinct(coll) {
1869
2035
  const DEDUPE_NONE = Symbol('dedupe-none');
1870
2036
 
1871
2037
  function dedupe1() {
1872
- return (rf) => {
2038
+ return transducer((rf) => {
1873
2039
  let prev = DEDUPE_NONE;
1874
- return (...args) => {
1875
- const al = args.length;
1876
- if (al === 0) return rf();
1877
- if (al === 1) return rf(args[0]);
1878
- if (al === 2) {
1879
- const result = args[0];
1880
- const input = args[1];
1881
- const skip = prev !== DEDUPE_NONE && truth_(_EQ_(prev, input));
1882
- prev = input;
1883
- return skip ? result : rf(result, input);
1884
- }
2040
+ return (r, x) => {
2041
+ const skip = prev !== DEDUPE_NONE && truth_(_EQ_(prev, x));
2042
+ prev = x;
2043
+ return skip ? r : rf(r, x);
1885
2044
  };
1886
- };
2045
+ });
1887
2046
  }
1888
2047
 
1889
2048
  export function dedupe(coll) {
1890
2049
  if (arguments.length === 0) return dedupe1();
1891
- return lazy(function* () {
2050
+ return lazyIter(coll, function* (it) {
1892
2051
  let prev = DEDUPE_NONE;
1893
- for (const x of iterable(coll)) {
2052
+ for (const x of it) {
1894
2053
  if (prev === DEDUPE_NONE || !truth_(_EQ_(prev, x))) yield x;
1895
2054
  prev = x;
1896
2055
  }
@@ -1930,8 +2089,14 @@ export function fnil(f, x, ...xs) {
1930
2089
 
1931
2090
  export function every_QMARK_(pred, coll) {
1932
2091
  pred = __toFn(pred);
1933
- for (const x of iterable(coll)) {
1934
- if (!pred(x)) return false;
2092
+ if (Array.isArray(coll)) {
2093
+ for (let i = 0; i < coll.length; i++) if (!pred(coll[i])) return false;
2094
+ return true;
2095
+ }
2096
+ const next = chunkCursor(coll);
2097
+ let ch;
2098
+ while ((ch = next()) !== null) {
2099
+ for (let i = 0; i < ch.length; i++) if (!pred(ch[i])) return false;
1935
2100
  }
1936
2101
  return true;
1937
2102
  }
@@ -1941,36 +2106,27 @@ export function not_every_QMARK_(pred, coll) {
1941
2106
  }
1942
2107
 
1943
2108
  function keep1(pred) {
1944
- return (rf) => {
1945
- return (...args) => {
1946
- const al = args.length;
1947
- if (al === 0) return rf();
1948
- if (al === 1) return rf(args[0]);
1949
- if (al === 2) {
1950
- const result = args[0];
1951
- const input = args[1];
1952
- const v = pred(input);
1953
- if (v == null) return result;
1954
- return rf(result, v);
1955
- }
1956
- };
1957
- };
2109
+ return transducer((rf) => (r, x) => {
2110
+ const v = pred(x);
2111
+ return v == null ? r : rf(r, v);
2112
+ });
1958
2113
  }
1959
2114
 
1960
2115
  export function keep(pred, coll) {
1961
2116
  pred = __toFn(pred);
1962
2117
  if (arguments.length === 1) return keep1(pred);
1963
- return lazy(function* () {
1964
- for (const o of iterable(coll)) {
1965
- const res = pred(o);
1966
- if (truth_(res)) yield res;
2118
+ return mapChunks(coll, (ch) => {
2119
+ const out = [];
2120
+ for (let i = 0; i < ch.length; i++) {
2121
+ const res = pred(ch[i]);
2122
+ if (truth_(res)) out.push(res);
1967
2123
  }
2124
+ return out;
1968
2125
  });
1969
2126
  }
1970
2127
 
1971
2128
  export function reverse(coll) {
1972
- coll = iterable(coll);
1973
- return [...coll].reverse();
2129
+ return toArray(coll).reverse();
1974
2130
  }
1975
2131
 
1976
2132
  export function sort(f, coll) {
@@ -1979,9 +2135,8 @@ export function sort(f, coll) {
1979
2135
  f = undefined;
1980
2136
  }
1981
2137
  f = __toFn(f);
1982
- coll = iterable(coll);
1983
- // we need to clone coll since .sort works in place and .toSorted isn't available on Node < 20
1984
- const clone = [...coll];
2138
+ // need a copy since .sort works in place and .toSorted isn't on Node < 20
2139
+ const clone = toArray(coll);
1985
2140
  // result is guaranteed to be stable since ES2019, like CLJS
1986
2141
  return clone.sort(f || compare);
1987
2142
  }
@@ -2021,7 +2176,7 @@ export function sort_by(keyfn, comp, coll) {
2021
2176
  }
2022
2177
 
2023
2178
  export function shuffle(coll) {
2024
- const result = [...coll];
2179
+ const result = toArray(coll);
2025
2180
  let remaining = result.length;
2026
2181
  while (remaining) {
2027
2182
  const i = Math.floor(Math.random() * remaining--);
@@ -2035,9 +2190,20 @@ export function shuffle(coll) {
2035
2190
 
2036
2191
  export function some(pred, coll) {
2037
2192
  pred = __toFn(pred);
2038
- for (const o of iterable(coll)) {
2039
- const res = pred(o);
2040
- if (truth_(res)) return res;
2193
+ if (Array.isArray(coll)) {
2194
+ for (let i = 0; i < coll.length; i++) {
2195
+ const res = pred(coll[i]);
2196
+ if (truth_(res)) return res;
2197
+ }
2198
+ return undefined;
2199
+ }
2200
+ const next = chunkCursor(coll);
2201
+ let ch;
2202
+ while ((ch = next()) !== null) {
2203
+ for (let i = 0; i < ch.length; i++) {
2204
+ const res = pred(ch[i]);
2205
+ if (truth_(res)) return res;
2206
+ }
2041
2207
  }
2042
2208
  return undefined;
2043
2209
  }
@@ -2123,22 +2289,16 @@ export function frequencies(coll) {
2123
2289
  return res;
2124
2290
  }
2125
2291
 
2126
- export class LazySeq {
2292
+ // The lazy-seq macro emits `new LazySeq(() => body)`: a LazyIterable whose step
2293
+ // evaluates the body thunk on first force and reads it unchunked.
2294
+ export class LazySeq extends LazyIterable {
2127
2295
  constructor(f) {
2128
- this.f = f;
2129
- this.res = undefined;
2130
- }
2131
- *[Symbol.iterator]() {
2132
- if (this.res === undefined) {
2133
- this.res = this.f();
2134
- this.f = null;
2135
- }
2136
- yield* iterable(this.res);
2296
+ super(() => unchunkedSteps(es6_iterator(iterable(f())))());
2137
2297
  }
2138
2298
  }
2139
2299
 
2140
2300
  export function butlast(coll) {
2141
- const x = [...iterable(coll)];
2301
+ const x = toArray(coll);
2142
2302
  x.pop();
2143
2303
  return x.length > 0 ? x : null;
2144
2304
  }
@@ -2162,10 +2322,11 @@ export function count(coll) {
2162
2322
  if (typeof len === 'number') {
2163
2323
  return len;
2164
2324
  }
2325
+ // sum chunk lengths instead of counting elements one by one
2326
+ const next = chunkCursor(coll);
2165
2327
  let ret = 0;
2166
- for (const _ of iterable(coll)) {
2167
- ret++;
2168
- }
2328
+ let ch;
2329
+ while ((ch = next()) !== null) ret += ch.length;
2169
2330
  return ret;
2170
2331
  }
2171
2332
 
@@ -2233,6 +2394,12 @@ export function aset(arr, idx, val, ...more) {
2233
2394
  }
2234
2395
 
2235
2396
  export function dorun(x) {
2397
+ // only a lazy seq needs forcing; realized colls are walked natively
2398
+ if (x instanceof LazyIterable) {
2399
+ const next = chunkCursor(x);
2400
+ while (next() !== null);
2401
+ return null;
2402
+ }
2236
2403
  for (const _ of iterable(x)) {
2237
2404
  // nothing here, just consume for side effects
2238
2405
  }
@@ -2372,7 +2539,8 @@ export function compare(x, y) {
2372
2539
  }
2373
2540
  const tx = typeof x;
2374
2541
  const ty = typeof y;
2375
- if ((tx === 'number' && ty === 'number') || (tx === 'string' && ty === 'string')) {
2542
+ if ((tx === 'number' && ty === 'number') || (tx === 'string' && ty === 'string') ||
2543
+ (tx === 'boolean' && ty === 'boolean')) {
2376
2544
  if (x === y) {
2377
2545
  return 0;
2378
2546
  }
@@ -2493,6 +2661,62 @@ export function string_QMARK_(s) {
2493
2661
  return typeof s === 'string';
2494
2662
  }
2495
2663
 
2664
+ export function unchecked_int(x) {
2665
+ return Math.trunc(x);
2666
+ }
2667
+
2668
+ export function unchecked_inc_int(x) {
2669
+ return x + 1;
2670
+ }
2671
+
2672
+ export function unchecked_dec_int(x) {
2673
+ return x - 1;
2674
+ }
2675
+
2676
+ export function unchecked_add_int(x, y) {
2677
+ return x + y;
2678
+ }
2679
+
2680
+ // keywords/symbols are strings in squint; namespace is the part before "/"
2681
+ export function namespace(x) {
2682
+ // keywords/symbols are strings in squint; namespace is the part before the
2683
+ // "/" ns separator (consistent with `name`, which returns the part after).
2684
+ // i >= 1 so a leading "/" yields a nil namespace rather than an empty one.
2685
+ const i = x.indexOf('/');
2686
+ return i >= 1 ? x.slice(0, i) : null;
2687
+ }
2688
+
2689
+ // squint has no keyword type; keywords are strings, so keyword/keyword? are
2690
+ // string-based. (keyword name) or (keyword ns name).
2691
+ export function keyword(arg1, arg2) {
2692
+ if (arg2 !== undefined) {
2693
+ return (arg1 != null ? arg1 + '/' : '') + arg2;
2694
+ }
2695
+ return arg1;
2696
+ }
2697
+
2698
+ export function keyword_QMARK_(x) {
2699
+ return typeof x === 'string';
2700
+ }
2701
+
2702
+ // squint has no symbol type either; symbols are strings
2703
+ export function symbol_QMARK_(x) {
2704
+ return typeof x === 'string';
2705
+ }
2706
+
2707
+ // squint has no first-class Var objects (#'x emits the value), so var? is false
2708
+ export function var_QMARK_(_x) {
2709
+ return false;
2710
+ }
2711
+
2712
+ export function simple_keyword_QMARK_(x) {
2713
+ return typeof x === 'string' && !x.includes('/');
2714
+ }
2715
+
2716
+ export function qualified_keyword_QMARK_(x) {
2717
+ return typeof x === 'string' && x.includes('/');
2718
+ }
2719
+
2496
2720
  export function coll_QMARK_(coll) {
2497
2721
  return typeConst(coll) != undefined;
2498
2722
  }
@@ -2546,8 +2770,6 @@ export function neg_int_QMARK_(x) {
2546
2770
  return int_QMARK_(x) && x < 0;
2547
2771
  }
2548
2772
 
2549
- const _metaSym = Symbol('meta');
2550
-
2551
2773
  export function meta(x) {
2552
2774
  if (x instanceof Object) {
2553
2775
  return x[_metaSym];
@@ -2557,7 +2779,7 @@ export function meta(x) {
2557
2779
  export function with_meta(x, m) {
2558
2780
  // For functions, wrap in a new callable that forwards to the original
2559
2781
  // so fn? stays true and the original isn't mutated. copy() can't handle
2560
- // functions a {...x} spread loses the call signature.
2782
+ // functions - a {...x} spread loses the call signature.
2561
2783
  if (typeof x === 'function') {
2562
2784
  const wrapped = function (...args) { return x.apply(this, args); };
2563
2785
  wrapped[_metaSym] = m;
@@ -2596,7 +2818,7 @@ export function bounded_count(n, coll) {
2596
2818
  export function find(m, k) {
2597
2819
  const v = get(m, k);
2598
2820
  if (v !== undefined) {
2599
- return [k, v];
2821
+ return tagMapEntry([k, v]);
2600
2822
  }
2601
2823
  }
2602
2824
 
@@ -2644,7 +2866,7 @@ export function parse_long(s) {
2644
2866
  if (string_QMARK_(s)) {
2645
2867
  if (/^[+-]?\d+$/.test(s)) {
2646
2868
  const i = parseInt(s);
2647
- if (Number.MIN_SAFE_INTEGER <= i <= Number.MAX_SAFE_INTEGER) {
2869
+ if (Number.MIN_SAFE_INTEGER <= i && i <= Number.MAX_SAFE_INTEGER) {
2648
2870
  return i;
2649
2871
  }
2650
2872
  }
@@ -2655,10 +2877,10 @@ export function parse_long(s) {
2655
2877
 
2656
2878
  export function parse_double(s) {
2657
2879
  if (string_QMARK_(s)) {
2658
- if (/^[\\x00-\\x20]*[+-]?NaN[\\x00-\\x20]*$/.test(s)) {
2880
+ if (/^[\x00-\x20]*[+-]?NaN[\x00-\x20]*$/.test(s)) {
2659
2881
  return NaN;
2660
2882
  } else if (
2661
- /^[\\x00-\\x20]*[+-]?(Infinity|((\d+\.?\d*|\.\d+)([eE][+-]?\d+)?)[dDfF]?)[\\x00-\\x20]*$/.test(
2883
+ /^[\x00-\x20]*[+-]?(Infinity|((\d+\.?\d*|\.\d+)([eE][+-]?\d+)?)[dDfF]?)[\x00-\x20]*$/.test(
2662
2884
  s
2663
2885
  )
2664
2886
  ) {
@@ -2667,7 +2889,7 @@ export function parse_double(s) {
2667
2889
  return null;
2668
2890
  }
2669
2891
  } else {
2670
- throw new parsing_err(s);
2892
+ return parsing_err(s);
2671
2893
  }
2672
2894
  }
2673
2895
 
@@ -2765,7 +2987,9 @@ export function transient$(x) {
2765
2987
  }
2766
2988
 
2767
2989
  export function persistent_BANG_(x) {
2768
- return Object.freeze(x);
2990
+ // no Object.freeze: persistent structures stay extensible so symbol-keyed
2991
+ // metadata can be attached
2992
+ return x;
2769
2993
  }
2770
2994
 
2771
2995
  class SortedSet {
@@ -3052,11 +3276,18 @@ export function volatile_BANG_(x) {
3052
3276
 
3053
3277
  export function vreset_BANG_(vol, v) {
3054
3278
  vol.v = v;
3279
+ return v;
3055
3280
  }
3056
3281
 
3057
3282
  function toEDN(value, seen = new WeakSet()) {
3058
3283
  if (value == null) return 'nil';
3059
- if (typeof value === 'number' || typeof value === 'boolean') return String(value);
3284
+ if (typeof value === 'number') {
3285
+ if (value === Infinity) return '##Inf';
3286
+ if (value === -Infinity) return '##-Inf';
3287
+ if (Number.isNaN(value)) return '##NaN';
3288
+ return String(value);
3289
+ }
3290
+ if (typeof value === 'boolean') return String(value);
3060
3291
  if (typeof value === 'string') return JSON.stringify(value);
3061
3292
  if (typeof value === 'bigint') return `${value}N`;
3062
3293