squint-cljs 0.14.200 → 0.14.202

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,6 +46,46 @@ function gh() {
46
46
  return _globalHierarchy ?? (_globalHierarchy = make_hierarchy());
47
47
  }
48
48
 
49
+ // A hierarchy may also be a plain map literal like
50
+ // {:parents {} :ancestors {} :descendants {}} with set values, as in CLJS.
51
+ // Normalize such a rep to the Map-based one the internals expect.
52
+ function fieldMap(m) {
53
+ if (m instanceof Map) return m;
54
+ const out = new Map();
55
+ if (m) for (const k of Object.keys(m)) out.set(k, new Set(m[k]));
56
+ return out;
57
+ }
58
+ function toHierarchy(h) {
59
+ if (h.parents instanceof Map && h.ancestors instanceof Map && h.descendants instanceof Map) {
60
+ return h;
61
+ }
62
+ return {
63
+ parents: fieldMap(h.parents),
64
+ ancestors: fieldMap(h.ancestors),
65
+ descendants: fieldMap(h.descendants),
66
+ };
67
+ }
68
+
69
+ // derive/underive with an explicit hierarchy throw on a malformed one,
70
+ // like CLJS. A field is a map: a js/Map or a plain object.
71
+ function checkHierarchy(h) {
72
+ const ok =
73
+ h != null &&
74
+ typeof h === 'object' &&
75
+ ['parents', 'ancestors', 'descendants'].every((f) => {
76
+ const m = h[f];
77
+ return m instanceof Map || (m != null && typeof m === 'object' && m.constructor === Object);
78
+ });
79
+ if (!ok) throw new Error('Invalid hierarchy: expected map with :parents, :ancestors and :descendants');
80
+ return h;
81
+ }
82
+
83
+ // keywords and symbols are strings in squint; namespaced means a '/' past
84
+ // position 0, the same threshold as `namespace`
85
+ function namespaced(x) {
86
+ return typeof x === 'string' && x.indexOf('/') > 0;
87
+ }
88
+
49
89
  function _isa(h, child, parent) {
50
90
  if (_EQ_(child, parent)) return true;
51
91
  if (typeof child === 'function' && typeof parent === 'function') {
@@ -64,7 +104,7 @@ function _isa(h, child, parent) {
64
104
 
65
105
  export function isa_QMARK_(a, b, c) {
66
106
  if (c === undefined) return _isa(gh(), a, b);
67
- return _isa(a, b, c);
107
+ return _isa(toHierarchy(a), b, c);
68
108
  }
69
109
 
70
110
  function _deriveInto(h, tag, parent) {
@@ -102,6 +142,11 @@ function _deriveInto(h, tag, parent) {
102
142
 
103
143
  export function derive(a, b, c) {
104
144
  if (c === undefined) {
145
+ // validation matches the CLJS asserts
146
+ if (!namespaced(b)) throw new Error('Parent must be a namespace-qualified keyword or symbol');
147
+ if (!(typeof a === 'function' || namespaced(a))) {
148
+ throw new Error('Tag must be a namespace-qualified keyword or symbol, or a class');
149
+ }
105
150
  // Rebuild-and-swap the global hierarchy so its identity changes;
106
151
  // MultiFn's cache compares hierarchy identity to decide when to
107
152
  // invalidate, so in-place mutation would leave stale cached
@@ -111,6 +156,14 @@ export function derive(a, b, c) {
111
156
  _deriveInto(_globalHierarchy, a, b);
112
157
  return null;
113
158
  }
159
+ checkHierarchy(a);
160
+ // vectors are allowed as compound dispatch tags, beyond CLJS
161
+ if (!(typeof b === 'function' || typeof b === 'string' || Array.isArray(b))) {
162
+ throw new Error('Tag must be a keyword or symbol, or a class');
163
+ }
164
+ if (!(typeof c === 'string' || Array.isArray(c))) {
165
+ throw new Error('Parent must be a keyword or symbol');
166
+ }
114
167
  const next = cloneHierarchy(a);
115
168
  _deriveInto(next, b, c);
116
169
  return next;
@@ -125,15 +178,15 @@ function rebuildFromPairs(pairs) {
125
178
  function cloneHierarchy(h) {
126
179
  const out = make_hierarchy();
127
180
  for (const f of ['parents', 'ancestors', 'descendants']) {
128
- for (const [k, s] of h[f]) out[f].set(k, new Set(s));
181
+ for (const [k, s] of fieldMap(h[f])) out[f].set(k, new Set(s));
129
182
  }
130
183
  return out;
131
184
  }
132
185
 
133
186
  export function underive(a, b, c) {
134
- const [h, tag, parent] = c === undefined ? [gh(), a, b] : [a, b, c];
187
+ const [h, tag, parent] = c === undefined ? [gh(), a, b] : [checkHierarchy(a), b, c];
135
188
  const pairs = [];
136
- for (const [child, parents] of h.parents) {
189
+ for (const [child, parents] of fieldMap(h.parents)) {
137
190
  for (const p of parents) {
138
191
  if (!(_EQ_(child, tag) && _EQ_(p, parent))) pairs.push([child, p]);
139
192
  }
@@ -155,9 +208,12 @@ export function underive(a, b, c) {
155
208
  // internally holds the entry but .get misses on reference equality.
156
209
  function hAnd(a, b, field) {
157
210
  const [h, tag] = b === undefined ? [gh(), a] : [a, b];
158
- const key = findKeyByEquiv(h[field], tag);
211
+ // an invalid hierarchy gives nil instead of throwing
212
+ if (h == null || typeof h !== 'object') return null;
213
+ const m = fieldMap(h[field]);
214
+ const key = findKeyByEquiv(m, tag);
159
215
  if (key === undefined) return null;
160
- const s = h[field].get(key);
216
+ const s = m.get(key);
161
217
  return s && s.size ? new Set(s) : null;
162
218
  }
163
219
  export function parents(a, b) { return hAnd(a, b, 'parents'); }
@@ -220,7 +276,8 @@ class MultiFn {
220
276
  this.resetCache();
221
277
  }
222
278
  findBest(val) {
223
- const h = this.hierarchy.deref();
279
+ // an atom-held hierarchy can hold the plain-map rep
280
+ const h = toHierarchy(this.hierarchy.deref());
224
281
  let best = null;
225
282
  for (const [dv, fn] of this.methodTable) {
226
283
  if (_isa(h, val, dv)) {
@@ -279,7 +336,7 @@ export function defmulti(name, dispatchFn, opts) {
279
336
  let hierarchy;
280
337
  if (opts.hierarchy == null) hierarchy = { deref: gh };
281
338
  else if (typeof opts.hierarchy.deref === 'function') hierarchy = opts.hierarchy;
282
- else { const h = opts.hierarchy; hierarchy = { deref: () => h }; }
339
+ else { const h = toHierarchy(opts.hierarchy); hierarchy = { deref: () => h }; }
283
340
  const mf = new MultiFn(name, dispatchFn, defaultVal, hierarchy);
284
341
  const call = function (...args) { return mf.invoke(args); };
285
342
  call.multiFn = mf;
@@ -1,14 +1,27 @@
1
1
  /*eslint no-unused-vars: ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_"}]*/
2
2
 
3
- import { iterable, string_QMARK_ } from './core.js';
3
+ import { get, iterable, string_QMARK_ } from './core.js';
4
4
 
5
5
  export function blank_QMARK_(s) {
6
- if (!s) return true;
6
+ if (s == null) return true;
7
+ // a non-string is not blank, like CLJS
8
+ if (typeof s !== 'string') return false;
7
9
  if (s.length === 0) return true;
8
- if (s.trimLeft().length === 0) return true;
10
+ if (s.trimStart().length === 0) return true;
9
11
  return false;
10
12
  }
11
13
 
14
+ export function escape(s, cmap) {
15
+ let buffer = '';
16
+ const length = s.length;
17
+ for (let i = 0; i < length; i++) {
18
+ const ch = s.charAt(i);
19
+ const replacement = get(cmap, ch);
20
+ buffer += replacement != null ? String(replacement) : ch;
21
+ }
22
+ return buffer;
23
+ }
24
+
12
25
  export function join(sep, coll) {
13
26
  if (coll === undefined) {
14
27
  coll = sep;
@@ -32,11 +45,11 @@ export function trim(s) {
32
45
  }
33
46
 
34
47
  export function triml(s) {
35
- return s.trimLeft();
48
+ return s.trimStart();
36
49
  }
37
50
 
38
51
  export function trimr(s) {
39
- return s.trimRight();
52
+ return s.trimEnd();
40
53
  }
41
54
 
42
55
  function discardTrailingIfNeeded(limit, v) {
@@ -162,6 +175,10 @@ export function capitalize(s) {
162
175
  return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
163
176
  }
164
177
 
178
+ export function reverse(s) {
179
+ return [...s].reverse().join('');
180
+ }
181
+
165
182
  export function includes_QMARK_(s, substr) {
166
183
  return s.indexOf(substr) != -1;
167
184
  }