squint-cljs
Version:
<img src="./logo/logo.svg" width="100%">
1,699 lines (1,531 loc) • 126 kB
JavaScript
/*eslint no-unused-vars: ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_"}]*/
// __toFn is not public API - the leading underscores mark it as an
// implementation helper shared with other squint runtime modules
// (e.g. multi.js). Signature and semantics may change without notice.
export function __toFn(x) {
if (x == null || typeof x === 'function') return x;
if (typeof x === 'string') return (coll, d) => get(coll, x, d);
// a value is callable as a lookup only if it is a collection or a custom
// type implementing ILookup; a seq, list or opaque object throws when
// called, like a non-IFn in CLJS
switch (typeConst(x)) {
case MAP_TYPE:
case ARRAY_TYPE:
case OBJECT_TYPE:
case SET_TYPE:
return (k, d) => get(x, k, d);
case INSTANCE_TYPE:
if (x[ILookup__lookup] !== undefined) return (k, d) => get(x, k, d);
}
return x;
}
// inlined and modified version of https://github.com/lukeed/dequal
var has = Object.prototype.hasOwnProperty;
function findKey(iter, tar, key) {
for (key of iter.keys()) {
if (dequal(key, tar)) return key;
}
}
function isSortedMap(m) {
return m != null && m[SORTED_TAG] === true && m[TYPE_TAG] === MAP_TYPE;
}
function isSetLike(s) {
return s != null && (s instanceof Set || s[TYPE_TAG] === SET_TYPE);
}
function isMapLike(m) {
return (
m != null &&
typeof m === 'object' &&
(m.constructor === Object || m instanceof Map || m[TYPE_TAG] === MAP_TYPE)
);
}
function mapHas(m, k) {
return m instanceof Map || m[TYPE_TAG] === MAP_TYPE ? m.has(k) : has.call(m, k);
}
function mapGet(m, k) {
return m instanceof Map || m[TYPE_TAG] === MAP_TYPE ? m.get(k) : m[k];
}
function mapCount(m) {
return m instanceof Map || m[TYPE_TAG] === MAP_TYPE
? m.size
: Object.keys(m).length;
}
// shared by dequal's fast path and fall-through. element-wise for arrays, own
// enumerable keys otherwise; callers guarantee a shared ctor, no brand, no -equiv
function dequalSameCtor(foo, bar, ctor) {
var len;
if (ctor === Array) {
if ((len = foo.length) === bar.length) {
while (len-- && dequal(foo[len], bar[len]));
}
return len === -1;
}
len = 0;
for (const k in foo) {
if (has.call(foo, k) && ++len && !has.call(bar, k)) return false;
if (!(k in bar) || !dequal(foo[k], bar[k])) return false;
}
return Object.keys(bar).length === len;
}
function dequal(foo, bar) {
// supports primitives, Array, Set, Map and plain objects
// like CLJS: does not support NaN
if (foo === bar) return true;
// null and undefined are both nil in CLJS, so they compare equal
if (foo == null) return bar == null;
if (bar == null) return false;
// a primitive is only equal by identity, checked above; bail before the
// property checks below box it
if (typeof foo !== 'object' || typeof bar !== 'object') return false;
var ctor = foo.constructor, tmp;
// same-constructor plain objects and arrays skip the protocol, sorted and
// cross-type checks below; neither type can carry -equiv or a brand
if (ctor === bar.constructor && (ctor === Object || ctor === Array)) {
return dequalSameCtor(foo, bar, ctor);
}
// -equiv dispatches on the left argument, like CLJS =
if (typeof foo === 'object' && foo[IEquiv__equiv] !== undefined) return !!foo[IEquiv__equiv](foo, bar);
// when only the right side has -equiv (the left is e.g. a plain object),
// dispatch on it so = stays symmetric
if (typeof bar === 'object' && bar[IEquiv__equiv] !== undefined) return !!bar[IEquiv__equiv](bar, foo);
// A sorted map compares by entries against any map type (object, Map, sorted).
const fooSorted = isSortedMap(foo);
if (fooSorted || isSortedMap(bar)) {
const sm = fooSorted ? foo : bar;
const other = sm === foo ? bar : foo;
if (!isMapLike(other) || mapCount(sm) !== mapCount(other)) return false;
for (const k of sm.keys()) {
if (!mapHas(other, k) || !dequal(sm.get(k), mapGet(other, k))) return false;
}
return true;
}
// A plain object and a js/Map are both map reps; compare by entries.
// Same-type pairs skip this and keep their fast paths below.
if (isMapLike(foo) && isMapLike(bar) && foo.constructor !== bar.constructor) {
if (mapCount(foo) !== mapCount(bar)) return false;
for (const k of foo instanceof Map ? foo.keys() : Object.keys(foo)) {
if (!mapHas(bar, k) || !dequal(mapGet(foo, k), mapGet(bar, k))) return false;
}
return true;
}
// Sets (hash or sorted) compare by elements, across concrete types.
if (isSetLike(foo) || isSetLike(bar)) {
if (!isSetLike(foo) || !isSetLike(bar) || foo.size !== bar.size) return false;
for (let e of foo) {
if (e && typeof e === 'object') {
e = findKey(bar, e);
if (!e) return false;
}
if (!bar.has(e)) return false;
}
return true;
}
if (foo && bar && ctor === bar.constructor) {
if (ctor === Date) return foo.getTime() === bar.getTime();
// regexes only compare by identity, like CLJS
if (ctor === RegExp) return false;
// no Array branch: same-ctor arrays already returned via the fast path
if (ctor === Map) {
if (foo.size !== bar.size) {
return false;
}
for (const kv of foo) {
tmp = kv[0];
if (tmp && typeof tmp === 'object') {
tmp = findKey(bar, tmp);
if (!tmp) return false;
}
if (!dequal(kv[1], bar.get(tmp))) {
return false;
}
}
return true;
}
// LazyIterable falls through to the sequential-equality path below; it is an
// object but must compare element-wise, not by enumerable properties
if ((!ctor || typeof foo === 'object') && foo[TYPE_TAG] !== LAZY_ITERABLE_TYPE) {
return dequalSameCtor(foo, bar, Object);
}
}
// Cross-type sequential equality, like CLJS `(= '(1 2) [1 2])`: vectors,
// lists and lazy seqs compare element-wise regardless of concrete type. Only
// reached when the same-constructor paths above did not apply, so equal-typed
// collections keep their fast paths.
if (
foo && bar &&
(Array.isArray(foo) || foo[TYPE_TAG] === LAZY_ITERABLE_TYPE) &&
(Array.isArray(bar) || bar[TYPE_TAG] === LAZY_ITERABLE_TYPE)
) {
const fi = foo[Symbol.iterator]();
const bi = bar[Symbol.iterator]();
for (;;) {
const a = fi.next();
const b = bi.next();
if (a.done || b.done) return !!(a.done && b.done);
if (!dequal(a.value, b.value)) return false;
}
}
return false;
}
// end inlined version of dequals
function walkArray(arr, comp) {
return arr.every(function (x, i) {
return i === 0 || comp(arr[i - 1], x);
});
}
export function _EQ_(...xs) {
return walkArray(xs, (x, y) => dequal(x, y));
}
export function _GT_(...xs) {
return walkArray(xs, (x, y) => x > y);
}
export function _GT__EQ_(...xs) {
return walkArray(xs, (x, y) => x >= y);
}
export function _LT_(...xs) {
return walkArray(xs, (x, y) => x < y);
}
export function _LT__EQ_(...xs) {
return walkArray(xs, (x, y) => x <= y);
}
export function _PLUS_(...xs) {
return xs.reduce((x, y) => x + y, 0);
}
export function _STAR_(...xs) {
return xs.reduce((x, y) => x * y, 1);
}
export function _(...xs) {
if (xs.length == 1) {
return 0 - xs[0];
}
return xs.reduce((x, y) => x - y);
}
export function _SLASH_(...xs) {
if (xs.length === 1) {
return 1 / xs[0];
}
return xs.reduce((x, y) => x / y);
}
export const __protocol_satisfies = {};
export function satisfies_QMARK_(protocol, x) {
if (x == null) {
return protocol[null];
}
if (typeof protocol == 'symbol') return x[protocol];
return x[protocol.__sym];
}
function mapAssocMut(m, k, v) {
m.set(k, v);
return m;
}
function objAssocMut(m, k, v) {
m[k] = v;
return m;
}
function getAssocMut(m) {
switch (typeConst(m)) {
case MAP_TYPE:
return mapAssocMut;
case ARRAY_TYPE:
case OBJECT_TYPE:
case INSTANCE_TYPE:
return objAssocMut;
}
}
function validateArrayKeys(o, k, kvs) {
// like CLJS: a vector key is an index in [0, count], count appends
let len = o.length;
for (let i = 0; i < kvs.length + 2; i += 2) {
const key = i === 0 ? k : kvs[i - 2];
if (!Number.isInteger(key)) {
throw new Error("Vector's key for assoc must be a number.");
}
if (key < 0 || key > len) {
throw new Error(`Index ${key} out of bounds [0,${len}]`);
}
if (key === len) len++;
}
}
export function assoc_BANG_(m, k, v, ...kvs) {
if (arguments.length < 3 || kvs.length % 2 !== 0) {
throw new Error('Illegal argument: assoc expects an odd number of arguments.');
}
switch (typeConst(m)) {
case MAP_TYPE:
m.set(k, v);
for (let i = 0; i < kvs.length; i += 2) {
m.set(kvs[i], kvs[i + 1]);
}
break;
case ARRAY_TYPE:
validateArrayKeys(m, k, kvs);
m[k] = v;
for (let i = 0; i < kvs.length; i += 2) {
m[kvs[i]] = kvs[i + 1];
}
break;
case INSTANCE_TYPE:
if (m[ITransientAssociative__assoc_BANG_] !== undefined) {
// re-read the slot off the current value: an -assoc! impl may return a
// different handle
let ret = m[ITransientAssociative__assoc_BANG_](m, k, v);
for (let i = 0; i < kvs.length; i += 2) {
ret = ret[ITransientAssociative__assoc_BANG_](ret, kvs[i], kvs[i + 1]);
}
return ret;
}
// fall through: an instance without -assoc! keeps the object behavior
case OBJECT_TYPE:
m[k] = v;
for (let i = 0; i < kvs.length; i += 2) {
m[kvs[i]] = kvs[i + 1];
}
break;
default:
throw new Error(
`Illegal argument: assoc! expects a Map, Array, or Object as the first argument, but got ${typeof m}.`
);
}
return m;
}
// value-producing ops (copy, empty, conj, into) carry metadata by
// forwarding the instance-level meta slots onto the fresh structure
function copyMeta(from, to) {
const f = from?.[IMeta__meta];
if (f !== undefined) {
to[IMeta__meta] = f;
to[IWithMeta__with_meta] = from[IWithMeta__with_meta];
}
return to;
}
function copy(o) {
switch (typeConst(o)) {
case MAP_TYPE:
// new o.constructor(o) preserves a SortedMap; for a plain Map it is new Map(o)
return copyMeta(o, new o.constructor(o));
case SET_TYPE:
return copyMeta(o, new o.constructor(o));
case ARRAY_TYPE:
return copyMeta(o, [...o]);
case INSTANCE_TYPE:
case OBJECT_TYPE:
return copyMeta(o, { ...o });
case LIST_TYPE:
return copyMeta(o, new List(...o));
default:
throw new Error(`Don't know how to copy object of type ${typeof o}.`);
}
}
export function assoc(o, k, v, ...kvs) {
if (arguments.length < 3 || kvs.length % 2 !== 0) {
throw new Error('Illegal argument: assoc expects an odd number of arguments.');
}
// only nil puns to an empty map; assoc on false throws, like CLJS
if (o == null) {
o = {};
}
// plain objects and arrays never carry the slot: skip the lookup, like get
if (!isObj(o) && !Array.isArray(o) && o[IAssociative__assoc] !== undefined) {
let ret = o[IAssociative__assoc](o, k, v);
for (let i = 0; i < kvs.length; i += 2) {
ret = ret[IAssociative__assoc](ret, kvs[i], kvs[i + 1]);
}
return ret;
}
const ret = copy(o);
assoc_BANG_(ret, k, v, ...kvs);
return ret;
}
// squint has no distinct hash-map or array-map type; both build a plain object.
export function hash_map(...kvs) {
if (kvs.length === 0) return {};
if (kvs.length % 2 !== 0) {
throw new Error('No value supplied for key: ' + kvs[kvs.length - 1]);
}
return assoc({}, ...kvs);
}
export const array_map = hash_map;
// req! and some-vals are clojure.core fns added for 1.13 destructuring.
// Ported from Clojure (clojure/core.clj), Copyright (c) Rich Hickey and
// contributors, Eclipse Public License 1.0.
const REQ_NOT_FOUND = {};
// Like arity-2 get, but throws if key not present.
export function req_BANG_(m, k) {
const v = get(m, k, REQ_NOT_FOUND);
if (v === REQ_NOT_FOUND) throw new Error('Missing required key: ' + k);
return v;
}
// Returns a map with only the non-nil values of m, or nil if there are none.
export function some_vals(m) {
if (m == null) return null;
let ret = null;
for (const [k, v] of iterable(m)) {
if (v !== null && v !== undefined) {
if (ret === null) ret = {};
ret[k] = v;
}
}
return ret;
}
// https://clojure.org/reference/special_forms#keyword-arguments
export function seq_to_map_for_destructuring(s) {
const arr = Array.isArray(s) ? s : [...iterable(s)];
const n = arr.length;
if (n < 2) return n ? arr[0] : {};
const m = {};
for (let i = 0; i < n; i += 2) {
// an odd count leaves a trailing map
if (i === n - 1) for (const [k, v] of iterable(arr[i])) m[k] = v;
else m[arr[i]] = arr[i + 1];
}
return m;
}
const MAP_TYPE = 1;
const ARRAY_TYPE = 2;
const OBJECT_TYPE = 3;
const LIST_TYPE = 4;
const SET_TYPE = 5;
const LAZY_ITERABLE_TYPE = 6;
// a class instance or null-prototype object: the extension point for the
// map-facing protocols. Plain objects keep the OBJECT_TYPE fast path.
const INSTANCE_TYPE = 7;
// type tag set in each collection ctor, read by typeConst (DCE: no instanceof).
const TYPE_TAG = /* @__PURE__ */ Symbol('squint.lang.type');
const SORTED_TAG = /* @__PURE__ */ Symbol('squint.lang.sorted');
// @__NO_SIDE_EFFECTS__ lets a bundler drop unused defclass/withApply calls; see doc/dev/dce.md
// @__NO_SIDE_EFFECTS__
function defclass(c) {
return c;
}
// @__NO_SIDE_EFFECTS__
function withApply(f, applyFn) {
f.squint$lang$variadic = applyFn;
return f;
}
function emptyOfType(type) {
switch (type) {
case MAP_TYPE:
return new Map();
case ARRAY_TYPE:
return [];
case OBJECT_TYPE:
return {}; // Object.create?
case LIST_TYPE:
return new List();
case SET_TYPE:
return new Set();
case LAZY_ITERABLE_TYPE:
return lazy(function* () {
return;
});
}
return undefined;
}
function isObj(coll) {
return coll.constructor === Object;
}
function isVectorArray(x) {
return Array.isArray(x) && x[TYPE_TAG] !== LIST_TYPE;
}
export function object_QMARK_(coll) {
return coll != null && isObj(coll);
}
function typeConst(obj) {
if (obj == null) {
return undefined;
}
// optimize for object
if (isObj(obj)) {
return OBJECT_TYPE;
}
if (obj instanceof Map) return MAP_TYPE;
if (obj instanceof Set) return SET_TYPE;
// brand, not instanceof, so dispatch does not reference the classes
const tag = obj[TYPE_TAG];
if (tag !== undefined) return tag;
if (isVectorArray(obj)) return ARRAY_TYPE;
// any remaining object (class instance, null-proto) is associative
if (typeof obj === 'object') return INSTANCE_TYPE;
return undefined;
}
function assoc_in_with(f, fname, o, keys, value) {
keys = vec(keys);
o = o || {}; // default nil behavior is JS object
const baseType = typeConst(o);
if (baseType !== MAP_TYPE && baseType !== ARRAY_TYPE && baseType !== OBJECT_TYPE && baseType !== INSTANCE_TYPE)
throw new Error(
`Illegal argument: ${fname} expects the first argument to be a Map, Array, or Object.`
);
const chain = [o];
let lastInChain = o;
for (let i = 0; i < keys.length - 1; i += 1) {
const k = keys[i];
let chainValue;
if (lastInChain instanceof Map) chainValue = lastInChain.get(k);
else if (lastInChain != null && lastInChain[ILookup__lookup] !== undefined) {
chainValue = lastInChain[ILookup__lookup](lastInChain, k, undefined);
} else chainValue = lastInChain[k];
if (!chainValue) {
// an instance root has no empty-of-type: missing levels become plain maps
chainValue = emptyOfType(baseType) ?? {};
}
chain.push(chainValue);
lastInChain = chainValue;
}
chain.push(value);
for (let i = chain.length - 2; i >= 0; i -= 1) {
chain[i] = f(chain[i], keys[i], chain[i + 1]);
}
return chain[0];
}
export function assoc_in(o, keys, value) {
return assoc_in_with(assoc, 'assoc-in', o, keys, value);
}
export function assoc_in_BANG_(o, keys, value) {
keys = vec(keys);
var currObj = o;
const baseType = typeConst(o);
for (const k of keys.splice(0, keys.length - 1)) {
let v = get(currObj, k);
if (v === undefined) {
v = emptyOfType(baseType);
assoc_BANG_(currObj, k, v);
}
currObj = v;
}
assoc_BANG_(currObj, keys[keys.length - 1], value);
return o;
}
export function comp(...fs) {
fs = fs.map(__toFn);
if (fs.length === 0) {
return identity;
} else if (fs.length === 1) {
return fs[0];
}
const [f, ...more] = fs.slice().reverse();
return function (...args) {
let x = f(...args);
for (const g of more) {
x = g(x);
}
return x;
};
}
function conj_BANG_set(o, rest) {
for (const x of rest) {
o.add(x);
}
return o;
}
export function conj_BANG_(...xs) {
const n = xs.length;
if (n === 0) {
return vector();
}
// single arg: return the coll unchanged, including nil, like CLJS
if (n === 1) {
return xs[0];
}
let o = xs[0];
if (o === null || o === undefined) {
o = [];
}
// Fast path for the common single-element conj! onto an array or set,
// avoiding the rest-array allocation and spread.
if (n === 2) {
switch (typeConst(o)) {
case ARRAY_TYPE:
o.push(xs[1]);
return o;
case SET_TYPE:
o.add(xs[1]);
return o;
}
}
const rest = xs.slice(1);
switch (typeConst(o)) {
case SET_TYPE:
conj_BANG_set(o, rest);
break;
case LIST_TYPE:
o.unshift(...rest.reverse());
break;
case ARRAY_TYPE:
o.push(...rest);
break;
case MAP_TYPE:
for (const x of rest) {
if (isVectorArray(x)) { asMapEntry(x); o.set(x[0], x[1]); }
else for (const kv of mapEntriesOf(x)) o.set(kv[0], kv[1]);
}
break;
case INSTANCE_TYPE:
if (o[ITransientCollection__conj_BANG_] !== undefined) {
// re-dispatch per element: a -conj! impl may return a different handle
let acc = o[ITransientCollection__conj_BANG_](o, rest[0]);
for (let i = 1; i < rest.length; i++) acc = conj_BANG_(acc, rest[i]);
return acc;
}
// fall through: an instance without -conj! keeps the object behavior
case OBJECT_TYPE:
for (const x of rest) {
if (isVectorArray(x)) { asMapEntry(x); o[x[0]] = x[1]; }
else for (const kv of mapEntriesOf(x)) o[kv[0]] = kv[1];
}
break;
default:
throw new Error(
'Illegal argument: conj! expects a Set, Array, List, Map, or Object as the first argument.'
);
}
return o;
}
// entries carried by a non-entry conj arg onto a map: a map merges, a
// seqable must contain entry vectors, like CLJS
function* mapEntriesOf(x) {
if (isMapLike(x)) {
yield* iterable(x);
return;
}
for (const kv of iterable(x)) {
if (!isVectorArray(kv)) {
throw new Error('conj on a map takes map entries or seqables of map entries');
}
yield kv;
}
}
function asMapEntry(x) {
if (x.length < 2) {
throw new Error('Vector arg to map conj must be a pair');
}
return x;
}
export function conj(...xs) {
if (xs.length === 0) {
return vector();
}
const [_o, ...rest] = xs;
// (conj coll) with nothing to add returns coll unchanged, including nil.
if (rest.length === 0) return _o;
let o = _o;
if (o === null || o === undefined) {
o = list();
}
let m, o2;
switch (typeConst(o)) {
case SET_TYPE:
// brand, not instanceof, so conj does not pin SortedSet
if (o[SORTED_TAG]) {
// prevent re-sorting of collection
return copyMeta(o, conj_BANG_set(new o.constructor(o), rest));
} else {
return copyMeta(o, new o.constructor([...o, ...rest]));
}
case LIST_TYPE:
return copyMeta(o, new List(...rest.reverse(), ...o));
case ARRAY_TYPE:
return copyMeta(o, [...o, ...rest]);
case MAP_TYPE:
m = new Map(o);
for (const x of rest) {
if (isVectorArray(x)) { asMapEntry(x); m.set(x[0], x[1]); }
else for (const kv of mapEntriesOf(x)) m.set(kv[0], kv[1]);
}
return copyMeta(o, m);
case LAZY_ITERABLE_TYPE:
return lazy(function* () {
yield* rest;
yield* o;
});
case INSTANCE_TYPE:
if (o[ICollection__conj] !== undefined) {
// re-dispatch per element: a -conj impl may return a different type
o2 = o[ICollection__conj](o, rest[0]);
for (let i = 1; i < rest.length; i++) o2 = conj(o2, rest[i]);
return o2;
}
// fall through: an instance without -conj keeps the object behavior
case OBJECT_TYPE:
o2 = { ...o };
for (const x of rest) {
if (isVectorArray(x)) { asMapEntry(x); o2[x[0]] = x[1]; }
else for (const kv of mapEntriesOf(x)) o2[kv[0]] = kv[1];
}
return copyMeta(o, o2);
default:
throw new Error(
'Illegal argument: conj expects a Set, Array, List, Map, or Object as the first argument.'
);
}
}
export function disj_BANG_(s, ...xs) {
if (s != null && s[ITransientSet__disjoin_BANG_] !== undefined) {
let ret = s;
for (const x of xs) {
ret = ret != null && ret[ITransientSet__disjoin_BANG_] !== undefined ? ret[ITransientSet__disjoin_BANG_](ret, x) : disj_BANG_(ret, x);
}
return ret;
}
for (const x of xs) {
s.delete(x);
}
return s;
}
export function disj(s, ...xs) {
if (s == null) return s;
if (xs.length === 0) return s;
if (s[ISet__disjoin] !== undefined) {
let ret = s[ISet__disjoin](s, xs[0]);
for (let i = 1; i < xs.length; i++) ret = disj(ret, xs[i]);
return ret;
}
// pass s itself (not a spread) so a SortedSet keeps its comparator
const s1 = new s.constructor(s);
return copyMeta(s, disj_BANG_(s1, ...xs));
}
export function contains_QMARK_(coll, v) {
if (typeof coll === 'string') {
return int_QMARK_(v) && v >= 0 && v < coll.length;
}
switch (typeConst(coll)) {
case SET_TYPE:
case MAP_TYPE:
return coll.has(v);
case undefined:
return false;
case INSTANCE_TYPE:
if (coll[IAssociative__contains_key_QMARK_] !== undefined) {
return coll[IAssociative__contains_key_QMARK_](coll, v);
}
// fall through
default:
return v in coll;
}
}
export function dissoc_BANG_(m, ...ks) {
if (m != null && m[ITransientMap__dissoc_BANG_] !== undefined) {
let ret = m;
for (const k of ks) ret = ret != null && ret[ITransientMap__dissoc_BANG_] !== undefined ? ret[ITransientMap__dissoc_BANG_](ret, k) : dissoc_BANG_(ret, k);
return ret;
}
for (const k of ks) {
delete m[k];
}
return m;
}
export function dissoc(m, ...ks) {
if (!m) return;
if (ks.length === 0) return m;
const tc = typeConst(m);
if (tc !== MAP_TYPE && tc !== OBJECT_TYPE && tc !== INSTANCE_TYPE) {
throw new Error('dissoc expects a map, got: ' + typeof m);
}
if (tc === INSTANCE_TYPE && m[IMap__dissoc] !== undefined) {
let ret = m;
// re-dispatch per key: a -dissoc impl may return a different type
for (const k of ks) {
if (ret == null) return ret;
ret = ret[IMap__dissoc] !== undefined ? ret[IMap__dissoc](ret, k) : dissoc(ret, k);
}
return ret;
}
if (tc === MAP_TYPE) {
let present = false;
for (const k of ks) if (m.has(k)) { present = true; break; }
if (!present) return m;
const m2 = copy(m);
for (const k of ks) m2.delete(k);
return m2;
}
let present = false;
for (const k of ks) if (k in m) { present = true; break; }
if (!present) return m;
const m2 = copy(m);
for (const k of ks) delete m2[k];
return m2;
}
export function inc(n) {
return n + 1;
}
export function dec(n) {
return n - 1;
}
export const _STAR_print_newline_STAR_ = { val: false };
export const _STAR_print_fn_STAR_ = { val: (s) => console.log(s) };
export const _STAR_print_err_fn_STAR_ = { val: (s) => console.error(s) };
export function print(...args) {
_STAR_print_fn_STAR_.val(args.map((v) => toEDN(v, undefined, false)).join(' '));
}
export function println(...args) {
print(...args);
if (_STAR_print_newline_STAR_.val) _STAR_print_fn_STAR_.val('\n');
}
export function print_str(...args) {
return args.map((v) => toEDN(v, undefined, false)).join(' ');
}
export function println_str(...args) {
return print_str(...args) + '\n';
}
export function pr(...xs) {
_STAR_print_fn_STAR_.val(pr_str(...xs));
}
export function nth(coll, idx, orElse) {
if (typeof idx !== 'number') {
throw new Error('Index argument to nth must be a number');
}
const hasDefault = arguments.length > 2;
// nil coll puns to nil, like Clojure
if (coll == null) return hasDefault ? orElse : null;
// "found" is decided by the index bound, not the value. An in-bounds element
// that happens to be undefined is still found.
if (Array.isArray(coll)) {
if (idx >= 0 && idx < coll.length) {
return coll[idx];
}
} else if (coll[IIndexed__nth] !== undefined) {
return hasDefault ? coll[IIndexed__nth](coll, idx, orElse) : coll[IIndexed__nth](coll, idx);
} else if (idx >= 0) {
// non-array: skip whole chunks instead of counting elements (handles
// infinite seqs since it stops once idx is reached)
const next = chunkCursor(coll);
let base = 0;
let ch;
while ((ch = next()) !== null) {
if (idx < base + ch.length) return ch[idx - base];
base += ch.length;
}
}
// out of bounds. With a default return it, otherwise throw like Clojure
if (hasDefault) return orElse;
throw new Error('Index out of bounds: ' + idx);
}
export function get(coll, key, otherwise = undefined) {
if (coll == null) {
return otherwise;
}
let v;
// optimize for getting values out of objects
if (isObj(coll)) {
v = coll[key];
if (v === undefined) {
return otherwise;
} else {
return v;
}
}
let g;
switch (typeConst(coll)) {
case SET_TYPE:
if (coll.has(key)) v = key;
break;
case MAP_TYPE:
v = coll.get(key);
break;
case ARRAY_TYPE:
v = coll[key];
break;
default:
if (coll[ILookup__lookup] !== undefined) {
v = coll[ILookup__lookup](coll, key, otherwise);
return v === undefined ? otherwise : v;
}
// we choose .get as the default implementation, e.g. fetch Headers are not Maps, but do implement a .get method
g = coll['get'];
if (typeof g === 'function') {
try {
v = coll.get(key);
break;
} catch (e) {
// ignore error
}
}
v = coll[key];
break;
}
return v !== undefined ? v : otherwise;
}
export function seq_QMARK_(x) {
return x != null && !!x[Symbol.iterator];
}
export function sequential_QMARK_(x) {
// vectors and lists are arrays; lazy seqs and cons carry the lazy brand.
// Sets, maps and strings are iterable but not sequential.
return Array.isArray(x) || x?.[TYPE_TAG] === LAZY_ITERABLE_TYPE || (x != null && x[IVector.__sym] !== undefined);
}
export function seqable_QMARK_(x) {
return (
x === null ||
x === undefined ||
// plain objects (squint maps) are seqable via Object.entries in `iterable`,
// even though they lack Symbol.iterator.
object_QMARK_(x) ||
// we used to check instanceof Object but this returns false for TC39 Records
// also we used to write `Symbol.iterator in` but this does not work for strings and some other types
!!x[Symbol.iterator] ||
!!x[ISEQABLE_SYM]
);
}
// squint has no distinct MapEntry type (map entries are plain 2-element
// arrays). We tag entries produced from a map with this marker symbol so
// map-entry? can tell them apart from ordinary vectors. Symbol-keyed props are
// invisible to =, into, iteration and JSON, so the effect is contained.
const MAP_ENTRY = /* @__PURE__ */ Symbol('squint.lang.map-entry');
function tagMapEntry(e) {
e[MAP_ENTRY] = true;
return e;
}
export function map_entry_QMARK_(x) {
return Array.isArray(x) && x[MAP_ENTRY] === true;
}
export function iterable(x) {
// nil puns to empty iterable, support passing nil to first/rest/reduce, etc.
if (x === null || x === undefined) {
return [];
}
// fast path: anything with Symbol.iterator (arrays, strings, sets, maps,
// lazy seqs). Inlined rather than calling seqable?, which also reports plain
// objects as seqable; those are handled by the Object.entries branch below.
if (x[Symbol.iterator]) {
return x;
}
// a type extended to ISeqable seqs through its -seq method
if (x[ISeqable__seq] !== undefined) return iterable(x[ISeqable__seq](x));
// only a plain object is a squint map; a class instance without a native
// iterator or ISeqable is not iterable, matching seqable? and CLJS, and
// never leaks its internal fields
if (isObj(x)) return Object.entries(x).map(tagMapEntry);
throw new TypeError(`${x} is not iterable`);
}
export const IIterable = /* @__PURE__ */ Symbol('Iterable');
export const IIterable__iterator = Symbol.iterator;
export function _iterator(coll) {
return coll[Symbol.iterator]();
}
export const es6_iterator = _iterator;
export function seq(x) {
if (x == null) return x;
if (!seqable_QMARK_(x)) throw new TypeError(x + ' is not ISeqable');
// a string seqs into its characters, like CLJS.
if (typeof x === 'string') return x.length ? [...x] : null;
const iter = iterable(x);
// return nil for terminal checking
if (iter.length === 0 || iter.size === 0) {
return null;
}
// a set or map is iterable but not a sequence; materialize its entries
// into a distinct seq so the result is not itself a set or map, like CLJS
if (iter instanceof Set || iter[TYPE_TAG] === SET_TYPE) {
return [...iter];
}
if (iter instanceof Map || iter[TYPE_TAG] === MAP_TYPE) {
return [...iter].map(tagMapEntry);
}
// an instance with -dissoc is a map rep: same distinct entry seq
if (iter[IMap__dissoc] !== undefined) {
const entries = [...iter].map(tagMapEntry);
return entries.length === 0 ? null : entries;
}
const _i = iter[Symbol.iterator]();
if (_i.next().done) return null;
return iter;
}
export function first(coll) {
if (coll == null) return undefined;
if (Array.isArray(coll)) return coll[0];
if (coll instanceof LazyIterable) {
coll.force();
return coll.chunk === null ? undefined : coll.chunk[0];
}
// destructuring uses iterable protocol
const [first] = iterable(coll);
return first;
}
export function second(coll) {
if (coll instanceof LazyIterable) {
coll.force();
const ch = coll.chunk;
if (ch === null) return undefined;
return ch.length > 1 ? ch[1] : first(coll._rest);
}
const [_, v] = iterable(coll);
return v;
}
export function ffirst(coll) {
return first(first(coll));
}
export function fnext(coll) {
return first(next(coll));
}
export function nfirst(coll) {
return next(first(coll));
}
export function rest(coll) {
// chunk-aware: drop the first element of the first chunk, keep the rest of
// the chain chunked (preserves chunkedness, unlike re-iterating element-wise)
const cell = chunkCells(coll);
cell.force();
const ch = cell.chunk;
if (ch === null) return cell; // (rest ()) is ()
if (ch.length > 1) {
const c = new LazyIterable(null);
c.realized = true;
c.chunk = ch.slice(1);
c._rest = cell._rest;
return c;
}
return cell._rest; // first chunk had one element; the next cell is the rest
}
const REDUCED_DEREF = (self) => self.value;
class Reduced {
value;
constructor(x) {
this.value = x;
this[IDeref__deref] = REDUCED_DEREF;
}
}
export function last(coll) {
coll = iterable(coll);
if (Array.isArray(coll)) {
return coll[coll.length - 1];
}
// non-array: walk chunks, keep the last chunk's last element
const next = chunkCursor(coll);
let lastEl;
let ch;
while ((ch = next()) !== null) lastEl = ch[ch.length - 1];
return lastEl;
}
export function reduced(x) {
return new Reduced(x);
}
export function reduced_QMARK_(x) {
return x instanceof Reduced;
}
export function reduce(f, arg1, arg2) {
f = __toFn(f);
const hasInit = arguments.length !== 2;
const coll = hasInit ? arg2 : arg1;
let val = hasInit ? arg1 : undefined;
// fast path: index loop over an array
if (Array.isArray(coll)) {
let i = 0;
if (!hasInit) {
if (coll.length === 0) return f();
val = coll[0];
i = 1;
}
if (val instanceof Reduced) return val.value;
for (; i < coll.length; i++) {
val = f(val, coll[i]);
if (val instanceof Reduced) return val.value;
}
return val;
}
// non-array: walk chunks (chunked cell or any other seqable)
const next = chunkCursor(coll);
let ch = next();
let i = 0;
if (!hasInit) {
if (ch === null) return f();
val = ch[0];
i = 1;
}
if (val instanceof Reduced) return val.value;
while (ch !== null) {
for (; i < ch.length; i++) {
val = f(val, ch[i]);
if (val instanceof Reduced) return val.value;
}
ch = next();
i = 0;
}
return val;
}
function* _reductions2(f, s) {
const vd = s.next();
if (vd.done) {
yield f();
} else {
yield* _reductions3(f, vd.value, s);
}
}
function* _reductions3(f, init, coll) {
let i = init;
const rst = coll;
while (true) {
if (reduced_QMARK_(i)) {
yield i.value;
return;
} else yield i;
const vd = rst.next();
if (vd.done) {
break;
}
i = f(i, vd.value);
}
}
export function reductions(f, arg1, arg2) {
f = __toFn(f);
if (arguments.length === 2) {
const it = es6_iterator(iterable(arg1));
return lazy(function* () {
yield* _reductions2(f, it);
});
}
const it = es6_iterator(iterable(arg2));
return lazy(function* () {
yield* _reductions3(f, arg1, it);
});
}
// Deprecated: lazy values are now cached, so reuse no longer recomputes. Kept
// for API compatibility; remove in a future release.
export function warn_on_lazy_reusage_BANG_() {
console.warn(
'warn-on-lazy-reusage! is deprecated and does nothing: lazy values are now cached.',
);
}
const CHUNK_SIZE = 32;
// One cell of a self-caching chunked seq. `step` is a thunk returning
// [nonEmptyChunkArray, nextStep] or null at the end. See doc/dev/lazy-seqs.md.
const LazyIterable = defclass(
class LazyIterable {
constructor(step) {
this[TYPE_TAG] = LAZY_ITERABLE_TYPE;
this[IIterable] = true; // Closure compatibility
this.step = step;
this.realized = false;
this.chunk = null; // array, or null when this is the terminal (empty) cell
this._rest = null;
}
force() {
if (!this.realized) {
this.realized = true;
const r = this.step();
this.step = null;
if (r !== null && r !== undefined) {
this.chunk = r[0];
this._rest = new LazyIterable(r[1]);
}
}
return this;
}
[Symbol.iterator]() {
let cell = this;
let i = 0;
return {
next() {
for (;;) {
cell.force();
const ch = cell.chunk;
if (ch === null) return { value: undefined, done: true };
if (i < ch.length) return { value: ch[i++], done: false };
cell = cell._rest;
i = 0;
}
},
[Symbol.iterator]() {
return this;
},
};
}
// Mirrors Array.prototype.indexOf so lazy seqs support (.indexOf coll x):
// reference equality, returns -1 when absent. Unlike cljs.core, not by value.
indexOf(x, fromIndex = 0) {
let i = 0;
for (const v of this) {
if (i >= fromIndex && v === x) return i;
i++;
}
return -1;
}
}
);
// One-element chunks: an unchunked seq, realized one element at a time.
function unchunkedSteps(iter) {
const step = () => {
const r = iter.next();
return r.done ? null : [[r.value], step];
};
return step;
}
// f is a zero-arg generator function; its result is an unchunked lazy seq.
export function lazy(f) {
return new LazyIterable(unchunkedSteps(f()));
}
// gen(it) over a hoisted iterator: keeps the input head unpinned (streaming)
function lazyIter(coll, gen) {
const it = es6_iterator(iterable(coll));
return lazy(() => gen(it));
}
// A chunked view of any seqable for chunk-aware ops, preserving chunkedness:
// cells pass through, arrays slice into CHUNK_SIZE batches, others stay unchunked.
function chunkCells(coll) {
if (coll instanceof LazyIterable) return coll;
if (Array.isArray(coll)) {
const step = (pos) => () => {
if (pos >= coll.length) return null;
const end = Math.min(pos + CHUNK_SIZE, coll.length);
return [coll.slice(pos, end), step(end)];
};
return new LazyIterable(step(0));
}
return new LazyIterable(unchunkedSteps(es6_iterator(iterable(coll))));
}
// A cursor over a seq's chunks for realizers: returns a function yielding the
// next chunk array or null. Drive it with an inline loop (keeps an accumulator a
// plain local, unlike a callback). Callers keep their own array shortcut.
function chunkCursor(coll) {
if (coll instanceof LazyIterable) {
let cell = coll;
return () => {
if (cell === null) return null;
cell.force();
const ch = cell.chunk;
cell = ch === null ? null : cell._rest;
return ch;
};
}
const it = es6_iterator(iterable(coll));
return () => {
const b = [];
for (let i = 0; i < CHUNK_SIZE; i++) {
const r = it.next();
if (r.done) break;
b.push(r.value);
}
return b.length === 0 ? null : b;
};
}
// Build a lazy seq transforming each input chunk via xf(chunk, baseIndex) -> new
// chunk array, preserving chunkedness. Empty results are skipped (e.g. filter).
// baseIndex is the input element count before the chunk, for indexed ops.
function mapChunks(coll, xf) {
const src = chunkCells(coll);
const step = (cell, base) => () => {
let c = cell;
let b = base;
for (;;) {
c.force();
const ch = c.chunk;
if (ch === null) return null;
const out = xf(ch, b);
const rest = c._rest;
b += ch.length;
if (out.length !== 0) return [out, step(rest, b)];
c = rest;
}
};
return new LazyIterable(step(src, 0));
}
export const Cons = defclass(
class Cons {
constructor(x, coll) {
this[TYPE_TAG] = LAZY_ITERABLE_TYPE;
this.x = x;
this.coll = coll;
}
[Symbol.iterator]() {
const x = this.x;
let coll = this.coll;
let started = false;
let it = null;
return {
next() {
if (!started) {
started = true;
return { value: x, done: false };
}
if (!it) {
it = es6_iterator(iterable(coll));
coll = null; // release the tail head so a single pass streams
}
return it.next();
},
[Symbol.iterator]() {
return this;
},
};
}
}
);
export function cons(x, coll) {
// like CLJS cons, which seqs a non-ISeq tail
if (!seqable_QMARK_(coll)) throw new TypeError(coll + ' is not ISeqable');
return new Cons(x, coll);
}
export function map(f, ...colls) {
f = __toFn(f);
switch (colls.length) {
case 0:
return (rf) => {
return (...args) => {
switch (args.length) {
case 0: {
return rf();
}
case 1: {
return rf(args[0]);
}
case 2: {
return rf(args[0], f(args[1]));
}
default: {
return rf(args[0], f(...args.slice(1)));
}
}
};
};
case 1:
return mapChunks(colls[0], (ch) => {
const out = new Array(ch.length);
for (let i = 0; i < ch.length; i++) out[i] = f(ch[i]);
return out;
});
default: {
const iters = colls.map((coll) => es6_iterator(iterable(coll)));
return lazy(function* () {
while (true) {
const args = [];
for (const i of iters) {
const nextVal = i.next();
if (nextVal.done) {
return;
}
args.push(nextVal.value);
}
yield f(...args);
}
});
}
}
}
// 0/1 arities pass through to rf; step(rf) is the 2-arity reducer
function transducer(step) {
return (rf) => {
const s = step(rf);
return (...args) =>
args.length === 0 ? rf() : args.length === 1 ? rf(args[0]) : s(args[0], args[1]);
};
}
function filter1(pred) {
return transducer((rf) => (r, x) => (truth_(pred(x)) ? rf(r, x) : r));
}
export function filter(pred, coll) {
if (arguments.length === 1) {
return filter1(pred);
}
pred = __toFn(pred);
return mapChunks(coll, (ch) => {
const out = [];
for (let i = 0; i < ch.length; i++) {
const x = ch[i];
if (truth_(pred(x))) out.push(x);
}
return out;
});
}
export function filterv(pred, coll) {
// filter is chunked; vec bulk-appends its chunks
return pushAll([], filter(pred, coll));
}
export function random_sample(prob, coll) {
if (arguments.length === 1) {
return filter((_) => rand() < prob);
}
return filter((_) => rand() < prob, coll);
}
export function remove(pred, coll) {
if (arguments.length === 1) {
return filter1(complement(pred));
}
return filter(complement(pred), coll);
}
function map_indexed1(f) {
return transducer((rf) => {
let i = -1;
return (r, x) => rf(r, f(++i, x));
});
}
export function map_indexed(f, coll) {
f = __toFn(f);
if (arguments.length === 1) {
return map_indexed1(f);
}
return mapChunks(coll, (ch, base) => {
const out = new Array(ch.length);
for (let i = 0; i < ch.length; i++) out[i] = f(base + i, ch[i]);
return out;
});
}
function keep_indexed2(f, coll) {
f = __toFn(f);
return mapChunks(coll, (ch, base) => {
const out = [];
for (let i = 0; i < ch.length; i++) {
const v = f(base + i, ch[i]);
if (truth_(v)) out.push(v);
}
return out;
});
}
function keep_indexed1(f) {
return transducer((rf) => {
let ia = -1;
return (r, x) => {
const v = f(++ia, x);
return v == null ? r : rf(r, v);
};
});
}
export function keep_indexed(f, coll) {
if (arguments.length === 1) {
return keep_indexed1(f);
} else {
return keep_indexed2(f, coll);
}
}
export function str(...xs) {
return xs.join('');
}
export function name(x) {
if (typeof x === 'string') {
// keywords/symbols are strings in squint; name is the part after the "/"
// ns separator (consistent with `namespace`, which returns the part before)
const i = x.indexOf('/');
return i >= 1 ? x.slice(i + 1) : x;
}
throw new Error("Doesn't support name: " + typeof x);
}
export function not(expr) {
return !truth_(expr);
}
export function nil_QMARK_(v) {
return v == null;
}
export const PROTOCOL_SENTINEL = {};
// marker protocols so (satisfies? IAtom x) works, like CLJS. Marked in the
// constructor, not on the prototype, so no top-level mutation pins Atom
// into bundles that do not use it.
const IATOM_SYM = /* @__PURE__ */ Symbol('squint.core.IAtom');
const IDEREF_SYM = /* @__PURE__ */ Symbol('squint.core.IDeref');
const ISEQABLE_SYM = /* @__PURE__ */ Symbol('squint.core.ISeqable');
export const IAtom = { __sym: IATOM_SYM };
export const IDeref = { __sym: IDEREF_SYM };
// method slot for (-deref x), named like the defprotocol emission so
// (extend-type T IDeref (-deref [x] ...)) fills it
export const IDeref__deref = /* @__PURE__ */ Symbol('IDeref_-deref');
export function _deref(o) {
if (o != null && o[IDeref__deref] !== undefined) return o[IDeref__deref](o);
return nilImpl(_deref, 'IDeref.-deref', o)(o);
}
export const ISeqable = { __sym: ISEQABLE_SYM };
export const ISeqable__seq = /* @__PURE__ */ Symbol('ISeqable_-seq');
// map-facing protocols. Each dispatches through its slot in the extension
// path (INSTANCE_TYPE) of the corresponding core fn, so plain objects and
// arrays never pay for them. Slot symbols are separate consts so a bundle
// using only e.g. conj pulls one symbol, not the whole protocol set.
export const ILookup = { __sym: /* @__PURE__ */ Symbol('squint.core.ILookup') };
export const ILookup__lookup = /* @__PURE__ */ Symbol('ILookup_-lookup');
export const IAssociative = { __sym: /* @__PURE__ */ Symbol('squint.core.IAssociative') };
export const IAssociative__assoc = /* @__PURE__ */ Symbol('IAssociative_-assoc');
export const IAssociative__contains_key_QMARK_ = /* @__PURE__ */ Symbol('IAssociative_-contains-key?');
export const IMap = { __sym: /* @__PURE__ */ Symbol('squint.core.IMap') };
export const IMap__dissoc = /* @__PURE__ */ Symbol('IMap_-dissoc');
export const ICounted = { __sym: /* @__PURE__ */ Symbol('squint.core.ICounted') };
export const ICounted__count = /* @__PURE__ */ Symbol('ICounted_-count');
export const IKVReduce = { __sym: /* @__PURE__ */ Symbol('squint.core.IKVReduce') };
export const IKVReduce__kv_reduce = /* @__PURE__ */ Symbol('IKVReduce_-kv-reduce');
export const ICollection = { __sym: /* @__PURE__ */ Symbol('squint.core.ICollection') };
export const ICollection__conj = /* @__PURE__ */ Symbol('ICollection_-conj');
export const IEmptyableCollection = { __sym: /* @__PURE__ */ Symbol('squint.core.IEmptyableCollection') };
export const IEmptyableCollection__empty = /* @__PURE__ */ Symbol('IEmptyableCollection_-empty');
export const IEquiv = { __sym: /* @__PURE__ */ Symbol('squint.core.IEquiv') };
export const IEquiv__equiv = /* @__PURE__ */ Symbol('IEquiv_-equiv');
// set and transient protocols, same extension-path dispatch as the map-facing
// protocols above
export const ISet = { __sym: /* @__PURE__ */ Symbol('squint.core.ISet') };
export const ISet__disjoin = /* @__PURE__ */ Symbol('ISet_-disjoin');
export const IEditableCollection = { __sym: /* @__PURE__ */ Symbol('squint.core.IEditableCollection') };
export const IEditableCollection__as_transient = /* @__PURE__ */ Symbol('IEditableCollection_-as-transient');
export const ITransientCollection = { __sym: /* @__PURE__ */ Symbol('squint.core.ITransientCollection') };
export const ITransientCollection__conj_BANG_ = /* @__PURE__ */ Symbol('ITransientCollection_-conj!');
export const ITransientCollection__persistent_BANG_ = /* @__PURE__ */ Symbol('ITransientCollection_-persistent!');
export const ITransientAssociative = { __sym: /* @__PURE__ */ Symbol('squint.core.ITransientAssociative') };
export const ITransientAssociative__assoc_BANG_ = /* @__PURE__ */ Symbol('ITransientAssociative_-assoc!');
export const ITransientMap = { __sym: /* @__PURE__ */ Symbol('squint.core.ITransientMap') };
export const ITransientMap__dissoc_BANG_ = /* @__PURE__ */ Symbol('ITransientMap_-dissoc!');
export const ITransientSet = { __sym: /* @__PURE__ */ Symbol('squint.core.ITransientSet') };
export const ITransientSet__disjoin_BANG_ = /* @__PURE__ */ Symbol('ITransientSet_-disjoin!');
// metadata protocols, like CLJS: types implement the slots, plain values
// get instance-level impls installed by with-meta
export const IMeta = { __sym: /* @__PURE__ */ Symbol('squint.core.IMeta') };
export const IMeta__meta = /* @__PURE__ */ Symbol('IMeta_-meta');
export const IWithMeta = { __sym: /* @__PURE__ */ Symbol('squint.core.IWithMeta') };
export const IWithMeta__with_meta = /* @__PURE__ */ Symbol('IWithMeta_-with-meta');
// hashing (Murmur3, like CLJS). The contract: (= a b) implies (hash a) ===
// (hash b), where = is dequal. None of this is referenced by =, so bundles
// that only compare pay nothing.
// Ported from ClojureScript (cljs/core.cljs), Copyright (c) Rich Hickey and
// contributors, Eclipse Public License 1.0. MurmurHash3 by Austin Appleby
// (public domain).
// IHash: a custom type opts into value hashing
export const IHash = { __sym: /* @__PURE__ */ Symbol('squint.core.IHash') };
export const IHash__hash = /* @__PURE__ */ Symbol('IHash_-hash');
// the equality hashed collections key by: identical or -equiv, never a
// deep compare like =
export function equiv(x, y) {
if (x === y) return true;
if (x == null) return y == null;
if (y == null) return false;
// a primitive is only equal by identity, checked above; bail before the
// slot checks below box it
if (typeof x !== 'object' || typeof y !== 'object') return false;
if (x[IEquiv__equiv] !== undefined) return !!x[IEquiv__equiv](x, y);
if (y[IEquiv__equiv] !== undefined) return !!y[IEquiv__equiv](y, x);
if (x instanceof Date && y instanceof Date) return x.getTime() === y.getTime();
return false;
}
// identity uid for reference-keyed values, like CLJS goog/getUid
const UIDS = /* @__PURE__ */ new WeakMap();
let uidCounter = 0;
function uid(o) {
let id = UIDS.get(o);
if (id === undefined) {
id = ++uidCounter;
UIDS.set(o, id);
}
return id;
}
const imul = Math.imul;
const M3_C1 = 0xcc9e2d51 | 0;
const M3_C2 = 0x1b873593 | 0;
function rotl(x, n) {
return (x << n) | (x >>> (32 - n));
}
function m3MixK1(k1) {
return imul(rotl(imul(k1 | 0, M3_C1), 15), M3_C2);
}
function m3MixH1(h1, k1) {
return (imul(rotl((h1 | 0) ^ (k1 | 0), 13), 5) + (0xe6546b64 | 0)) | 0;
}
function m3Fmix(h1, len) {
h1 = (h1 ^ len) | 0;
h1 = (h1 ^ (h1 >>> 16)) | 0;
h1 = imul(h1, 0x85ebca6b | 0);
h1 = (h1 ^ (h1 >>> 13)) | 0;
h1 = imul(h1, 0xc2b2ae35 | 0);
return (h1 ^ (h1 >>> 16)) | 0;
}
function m3HashInt(x) {
return x === 0 ? 0 : m3Fmix(m3MixH1(0, m3MixK1(x)), 4);
}
let stringHashCache = /* @__PURE__ */ Object.create(null);
let stringHashCacheCount = 0;
function hashStringRaw(s) {
let h = 0;
for (let i = 0; i < s.length; i++) h = (imul(31, h) + s.charCodeAt(i)) | 0;
return h;
}
function hashString(s) {
if (stringHas