aontu 0.44.0 → 0.46.0

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.
Files changed (84) hide show
  1. package/dist/cli.d.ts +9 -0
  2. package/dist/cli.js +203 -0
  3. package/dist/cli.js.map +1 -0
  4. package/dist/ctx.d.ts +2 -5
  5. package/dist/ctx.js +1 -2
  6. package/dist/ctx.js.map +1 -1
  7. package/dist/lang.d.ts +1 -5
  8. package/dist/lang.js +24 -100
  9. package/dist/lang.js.map +1 -1
  10. package/dist/tsconfig.tsbuildinfo +1 -1
  11. package/dist/type.d.ts +0 -5
  12. package/dist/type.js.map +1 -1
  13. package/dist/unify.js +13 -292
  14. package/dist/unify.js.map +1 -1
  15. package/dist/utility.js +2 -6
  16. package/dist/utility.js.map +1 -1
  17. package/dist/val/BagVal.d.ts +3 -0
  18. package/dist/val/BagVal.js +6 -6
  19. package/dist/val/BagVal.js.map +1 -1
  20. package/dist/val/ConjunctVal.d.ts +1 -1
  21. package/dist/val/ConjunctVal.js +14 -137
  22. package/dist/val/ConjunctVal.js.map +1 -1
  23. package/dist/val/CopyFuncVal.js +2 -3
  24. package/dist/val/CopyFuncVal.js.map +1 -1
  25. package/dist/val/DisjunctVal.js +0 -4
  26. package/dist/val/DisjunctVal.js.map +1 -1
  27. package/dist/val/ExpectVal.js +3 -16
  28. package/dist/val/ExpectVal.js.map +1 -1
  29. package/dist/val/JunctionVal.d.ts +0 -1
  30. package/dist/val/JunctionVal.js +1 -6
  31. package/dist/val/JunctionVal.js.map +1 -1
  32. package/dist/val/KeyFuncVal.js +2 -0
  33. package/dist/val/KeyFuncVal.js.map +1 -1
  34. package/dist/val/ListVal.d.ts +0 -1
  35. package/dist/val/ListVal.js +66 -33
  36. package/dist/val/ListVal.js.map +1 -1
  37. package/dist/val/MapVal.d.ts +2 -3
  38. package/dist/val/MapVal.js +95 -67
  39. package/dist/val/MapVal.js.map +1 -1
  40. package/dist/val/MoveFuncVal.d.ts +1 -2
  41. package/dist/val/MoveFuncVal.js +13 -78
  42. package/dist/val/MoveFuncVal.js.map +1 -1
  43. package/dist/val/PathFuncVal.js +4 -25
  44. package/dist/val/PathFuncVal.js.map +1 -1
  45. package/dist/val/PlusOpVal.d.ts +1 -1
  46. package/dist/val/PrefVal.js +5 -18
  47. package/dist/val/PrefVal.js.map +1 -1
  48. package/dist/val/{PathVal.d.ts → RefVal.d.ts} +3 -4
  49. package/dist/val/{PathVal.js → RefVal.js} +77 -75
  50. package/dist/val/RefVal.js.map +1 -0
  51. package/dist/val/Val.d.ts +1 -2
  52. package/dist/val/Val.js +8 -7
  53. package/dist/val/Val.js.map +1 -1
  54. package/dist/val/VarVal.js +2 -2
  55. package/dist/val/VarVal.js.map +1 -1
  56. package/package.json +9 -4
  57. package/src/cli.ts +193 -0
  58. package/src/ctx.ts +3 -16
  59. package/src/lang.ts +23 -113
  60. package/src/type.ts +0 -5
  61. package/src/unify.ts +13 -310
  62. package/src/utility.ts +2 -5
  63. package/src/val/BagVal.ts +7 -6
  64. package/src/val/ConjunctVal.ts +13 -131
  65. package/src/val/CopyFuncVal.ts +2 -3
  66. package/src/val/DisjunctVal.ts +0 -6
  67. package/src/val/ExpectVal.ts +4 -18
  68. package/src/val/JunctionVal.ts +1 -5
  69. package/src/val/KeyFuncVal.ts +3 -0
  70. package/src/val/ListVal.ts +88 -38
  71. package/src/val/MapVal.ts +124 -75
  72. package/src/val/MoveFuncVal.ts +14 -79
  73. package/src/val/PathFuncVal.ts +4 -29
  74. package/src/val/PrefVal.ts +6 -19
  75. package/src/val/{RefVal.ts.old → RefVal.ts} +19 -30
  76. package/src/val/Val.ts +9 -9
  77. package/src/val/VarVal.ts +2 -2
  78. package/README.md +0 -18
  79. package/dist/val/PathVal.js.map +0 -1
  80. package/dist/val/SpreadVal.d.ts +0 -20
  81. package/dist/val/SpreadVal.js +0 -194
  82. package/dist/val/SpreadVal.js.map +0 -1
  83. package/src/val/PathVal.ts +0 -435
  84. package/src/val/SpreadVal.ts +0 -275
package/src/unify.ts CHANGED
@@ -10,9 +10,6 @@ import { DONE } from './type'
10
10
  import { makeNilErr } from './err'
11
11
 
12
12
  import { NilVal } from './val/NilVal'
13
- import { StringVal } from './val/StringVal'
14
- import { PathVal } from './val/PathVal'
15
- import { KeyFuncVal } from './val/KeyFuncVal'
16
13
 
17
14
  import {
18
15
  Lang
@@ -29,9 +26,8 @@ import {
29
26
  } from './val/top'
30
27
 
31
28
 
32
-
33
29
  // TODO: FIX: false positive when too many top unifications
34
- const MAXCYCLE = 9999
30
+ const MAXCYCLE = 999
35
31
 
36
32
  // Vals should only have to unify downwards (in .unify) over Vals they understand.
37
33
  // and for complex Vals, TOP, which means self unify if not yet done
@@ -54,19 +50,12 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
54
50
  if (a.done && b.done) {
55
51
  if (a.id === b.id) return a
56
52
  if (a.constructor === b.constructor && a.peg === b.peg
57
- && !a.isNil && !b.isNil
58
- && !a.isConjunct && !a.isDisjunct
59
- && !a.isPath && !a.isPref && !a.isFunc && !a.isExpect) {
53
+ && !a.isNil && !b.isNil
54
+ && !a.isMap && !a.isList
55
+ && !a.isConjunct && !a.isDisjunct
56
+ && !a.isRef && !a.isPref && !a.isFunc && !a.isExpect) {
60
57
  return a
61
58
  }
62
-
63
- // Id-keyed cache: reuse results for the exact same Val pair.
64
- const uc = ctx._uniteCache
65
- if (uc !== undefined) {
66
- const ucKey = a.id + '|' + b.id
67
- const ucHit = uc.get(ucKey)
68
- if (ucHit !== undefined) return ucHit
69
- }
70
59
  }
71
60
  }
72
61
  }
@@ -78,25 +67,21 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
78
67
 
79
68
  // Cycle-detection key. Use numeric path index for speed; fall back to
80
69
  // full string key when debug is enabled so the saw value is human-readable.
81
- let saw: string
82
- if (ctx.opts.debug) {
83
- saw = (a ? a.id + (a.done ? '' : '*') : '') + '~' +
70
+ const saw = ctx.opts.debug
71
+ ? (a ? a.id + (a.done ? '' : '*') : '') + '~' +
84
72
  (b ? b.id + (b.done ? '' : '*') : '') + '@' + ctx.pathstr
85
- }
86
- else {
87
- saw = (a ? a.id + (a.done ? 'd' : '') : 0) + '~' +
73
+ : (a ? a.id + (a.done ? 'd' : '') : 0) + '~' +
88
74
  (b ? b.id + (b.done ? 'd' : '') : 0) + '~' + ctx.pathidx
89
- }
90
75
 
91
76
  // NOTE: if this error occurs "unreasonably", attemp to avoid unnecesary unification
92
77
  // See for example PrefVal peg.id equality inspection.
93
- const sawCount = ctx.seen.get(saw) ?? 0
78
+ const sawCount = ctx.seen[saw] ?? 0
94
79
  if (MAXCYCLE < sawCount) {
95
80
  // console.log('SAW', sawCount, saw, a?.id, a?.canon, b?.id, b?.canon, ctx.cc)
96
81
  out = makeNilErr(ctx, 'unify_cycle', a, b)
97
82
  }
98
83
  else {
99
- ctx.seen.set(saw, sawCount + 1)
84
+ ctx.seen[saw] = sawCount + 1
100
85
 
101
86
  try {
102
87
  let unified = false
@@ -133,7 +118,7 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
133
118
  out = update(b, a)
134
119
  why = 'bn'
135
120
  }
136
- else if (a.isConjunct || a.isExpect || a.isSpread) {
121
+ else if (a.isConjunct || a.isExpect) {
137
122
  out = a.unify(b, te ? ctx.clone({ explain: ec(te, 'AC') }) : ctx)
138
123
  unified = true
139
124
  why = 'a*'
@@ -141,11 +126,10 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
141
126
  else if (
142
127
  b.isConjunct
143
128
  || b.isDisjunct
144
- || b.isPath
129
+ || b.isRef
145
130
  || b.isPref
146
131
  || b.isFunc
147
132
  || b.isExpect
148
- || b.isSpread
149
133
  ) {
150
134
  out = b.unify(a, te ? ctx.clone({ explain: ec(te, 'BW') }) : ctx)
151
135
  unified = true
@@ -185,11 +169,6 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
185
169
 
186
170
  ctx.explain && explainClose(te, out)
187
171
 
188
- // Store in id-keyed cache when both operands were done.
189
- if (a?.done && b?.done && out?.done && ctx._uniteCache !== undefined) {
190
- ctx._uniteCache.set(a.id + '|' + b.id, out)
191
- }
192
-
193
172
  return out
194
173
  }
195
174
 
@@ -200,269 +179,6 @@ function update(x: Val, _y: Val) {
200
179
  }
201
180
 
202
181
 
203
- // Resolve all PathVals using the pre-collected paths list.
204
- // Mutates the tree in place, replacing each PathVal with its cloned target.
205
- function resolvePaths(root: Val, ctx: AontuContext, paths: PathVal[]) {
206
- for (const pv of paths) {
207
- if (pv.done) continue
208
-
209
- // Resolve: find target, following chains.
210
- // Suppress errors during pre-resolution — intermediate PathVals
211
- // (from multi-dot expressions like ..a.q) may fail here but the
212
- // final PathVal resolves correctly during unification.
213
- const savedErr = ctx.err
214
- ctx.err = []
215
- let found: any = pv.find(ctx)
216
- ctx.err = savedErr
217
- if (found == null || found.isNil) continue
218
-
219
- // Skip if target or container contains a mark-setting function
220
- // (type/hide/move) — let unification resolve it first.
221
- if (hasMarkFunc(found)) continue
222
- if (hasMarkFuncAtPath(root, pv.path)) continue
223
-
224
- while (found instanceof PathVal) {
225
- if (found.done && found._resolved) {
226
- found = found._resolved
227
- break
228
- }
229
- const next = found.find(ctx)
230
- if (next == null || next.isNil) break
231
- found.dc = DONE
232
- found._resolved = next
233
- found = next
234
- }
235
-
236
- // If found value is a key() function, set its path to the
237
- // destination so it evaluates at the right position.
238
- if (found.isKeyFunc) {
239
- found.path = pv.path
240
- }
241
-
242
- pv.dc = DONE
243
- pv._resolved = found
244
-
245
- // Replace PathVal in tree using its path
246
- replaceAtPath(root, pv.path, pv, found)
247
-
248
- // Walk the placed value to resolve any PathVals cloned into it
249
- resolveNestedPaths(found, ctx)
250
- }
251
- }
252
-
253
-
254
- // Check if a value contains a type() or hide() function.
255
- // Check if the value AT the path position is inside a mark-setting function.
256
- function hasMarkFuncAtPath(root: Val, path: string[]): boolean {
257
- let node: any = root
258
- for (let i = 0; i < path.length; i++) {
259
- const part = path[i]
260
- if (node.isMap || node.isList) {
261
- node = node.peg[part]
262
- }
263
- else if (node.isConjunct || node.isDisjunct) {
264
- let found = null
265
- const stack = [...node.peg]
266
- while (stack.length > 0) {
267
- const term = stack.pop()
268
- if (term?.isConjunct || term?.isDisjunct) {
269
- stack.push(...term.peg)
270
- }
271
- else if ((term?.isMap || term?.isList) && term.peg[part] != null) {
272
- found = term.peg[part]
273
- break
274
- }
275
- }
276
- node = found
277
- }
278
- else {
279
- return false
280
- }
281
- if (node == null) return false
282
- }
283
- return hasMarkFunc(node)
284
- }
285
-
286
-
287
- function hasMarkFunc(val: any): boolean {
288
- if (val == null || !val.isVal) return false
289
- if (val.isTypeFunc || val.isHideFunc || val.isMoveFunc) return true
290
- if (val.isConjunct || val.isDisjunct) {
291
- for (const t of val.peg) {
292
- if (hasMarkFunc(t)) return true
293
- }
294
- }
295
- return false
296
- }
297
-
298
-
299
- // Resolve any PathVals found inside a value (e.g. cloned subtrees).
300
- // Iterative stack-based walk — no recursion.
301
- function resolveNestedPaths(val: any, ctx: AontuContext) {
302
- const stack: any[] = [val]
303
- while (stack.length > 0) {
304
- const v = stack.pop()
305
- if (v == null || !v.isVal) continue
306
-
307
- if (v.isMap || v.isList) {
308
- for (const k in v.peg) {
309
- const child = v.peg[k]
310
- if (child instanceof PathVal && !child.done) {
311
- const found = child.find(ctx)
312
- if (found != null && !found.isNil) {
313
- v.peg[k] = found
314
- child.dc = DONE
315
- child._resolved = found
316
- stack.push(found)
317
- }
318
- }
319
- else if (child?.isVal) {
320
- stack.push(child)
321
- }
322
- }
323
- }
324
- else if (v.isConjunct || v.isDisjunct) {
325
- for (let i = 0; i < v.peg.length; i++) {
326
- const child = v.peg[i]
327
- if (child instanceof PathVal && !child.done) {
328
- const found = child.find(ctx)
329
- if (found != null && !found.isNil) {
330
- v.peg[i] = found
331
- child.dc = DONE
332
- child._resolved = found
333
- stack.push(found)
334
- }
335
- }
336
- else if (child?.isVal) {
337
- stack.push(child)
338
- }
339
- }
340
- }
341
- else if (v.isFeature) {
342
- if (v.peg instanceof PathVal && !v.peg.done) {
343
- const found = v.peg.find(ctx)
344
- if (found != null && !found.isNil) {
345
- v.peg.dc = DONE
346
- v.peg._resolved = found
347
- v.peg = found
348
- stack.push(found)
349
- }
350
- }
351
- else if (Array.isArray(v.peg)) {
352
- for (let i = 0; i < v.peg.length; i++) {
353
- if (v.peg[i]?.isVal) stack.push(v.peg[i])
354
- }
355
- }
356
- else if (v.peg?.isVal) {
357
- stack.push(v.peg)
358
- }
359
- }
360
- }
361
- }
362
-
363
-
364
- // Replace target Val in the tree, using path to navigate and a stack
365
- // to search through junctions, features, and nested structures.
366
- // No recursion — uses a single loop with an explicit stack.
367
- function replaceAtPath(root: Val, path: string[], target: Val, replacement: Val,
368
- intoSpreads: boolean = true): boolean {
369
- let node: any = root
370
-
371
- // Descend through map/list using path segments.
372
- // When a non-map/list is encountered (junction, feature),
373
- // push its children onto the search stack and stop descending.
374
- let pi = 0
375
- for (; pi < path.length; pi++) {
376
- if (node.isMap || node.isList) {
377
- const child = node.peg[path[pi]]
378
- if (child == null) break
379
-
380
- // Last segment: check for direct replacement
381
- if (pi === path.length - 1) {
382
- if (child === target) {
383
- node.peg[path[pi]] = replacement
384
- return true
385
- }
386
- // Target not at this position — search within child
387
- node = child
388
- pi++
389
- break
390
- }
391
-
392
- node = child
393
- }
394
- else {
395
- // Hit a non-navigable node — search it
396
- break
397
- }
398
- }
399
-
400
- // If path fully consumed with no match, or hit a junction/feature,
401
- // search the current node using a stack.
402
- const stack: any[] = [node]
403
-
404
- while (stack.length > 0) {
405
- const val = stack.pop()
406
- if (val == null || !val.isVal) continue
407
-
408
- if (val.isMap || val.isList) {
409
- for (const k in val.peg) {
410
- if (val.peg[k] === target) {
411
- val.peg[k] = replacement
412
- return true
413
- }
414
- if (val.peg[k]?.isVal) stack.push(val.peg[k])
415
- }
416
- }
417
- else if (val.isConjunct || val.isDisjunct) {
418
- for (let i = 0; i < val.peg.length; i++) {
419
- if (val.peg[i] === target) {
420
- val.peg[i] = replacement
421
- return true
422
- }
423
- stack.push(val.peg[i])
424
- }
425
- }
426
- else if (val.isFeature) {
427
- if (val.peg === target) {
428
- val.peg = replacement
429
- return true
430
- }
431
- if (intoSpreads || !val.isSpread) {
432
- if (Array.isArray(val.peg)) {
433
- for (let i = 0; i < val.peg.length; i++) {
434
- if (val.peg[i] === target) {
435
- val.peg[i] = replacement
436
- return true
437
- }
438
- if (val.peg[i]?.isVal) stack.push(val.peg[i])
439
- }
440
- }
441
- else if (val.peg?.isVal) {
442
- stack.push(val.peg)
443
- }
444
- }
445
- }
446
- }
447
-
448
- return false
449
- }
450
-
451
-
452
- // Resolve all KeyFuncVals (not inside spreads) to StringVals.
453
- // Uses the KeyFuncVal's path to determine the key name.
454
- function resolveKeys(root: Val, keys: KeyFuncVal[]) {
455
- for (const kv of keys) {
456
- const resolved = kv.resolve(null as any, kv.peg)
457
- if (resolved instanceof StringVal) {
458
- resolved.dc = DONE
459
- resolved.path = kv.path
460
- replaceAtPath(root, kv.path, kv, resolved, false)
461
- }
462
- }
463
- }
464
-
465
-
466
182
  class Unify {
467
183
  root: Val
468
184
  res: Val
@@ -513,28 +229,15 @@ class Unify {
513
229
  uctx.err = this.err
514
230
  uctx.explain = this.explain
515
231
 
516
- // Path resolution phase: replace all PathVals with cloned targets.
517
- // Pure structural replacement — no unification.
518
- resolvePaths(res, uctx, this.lang.paths)
519
- uctx = uctx.clone({ root: res })
520
-
521
- // Key resolution phase: replace key() functions (not in spreads)
522
- // with their resolved StringVal.
523
- resolveKeys(res, this.lang.keys)
524
-
525
232
  const explain = null == ctx?.explain ? undefined : ctx?.explain
526
233
  const te = explain && explainOpen(uctx, explain, 'root', res)
527
234
 
528
235
  // NOTE: if true === res.done already, then this loop never needs to run.
529
- // RefVals defer on cc=0 and while ctx.sc > 0.
530
- // SpreadVal.unify maintains ctx.sc via increment/decrement.
531
236
  let maxcc = 9 // 99
532
237
  for (; this.cc < maxcc && DONE !== res.dc; this.cc++) {
533
238
  // console.log('CC', this.cc, res.canon)
534
239
  uctx.cc = this.cc
535
- uctx.seen = new Map()
536
- uctx._refCloneCache = new Map()
537
- uctx._uniteCache = new Map()
240
+ uctx.seen = {}
538
241
  res = unite(te ? uctx.clone({ explain: ec(te, 'run') }) : uctx, res, top(), 'unify')
539
242
 
540
243
  if (0 < uctx.err.length) {
package/src/utility.ts CHANGED
@@ -11,11 +11,8 @@ function propagateMarks(source: Val, target: Val): void {
11
11
  if (source.isTop || target.isTop) {
12
12
  return
13
13
  }
14
- const sm = source.mark as any
15
- if (!sm.type && !sm.hide) return
16
- const tm = target.mark as any
17
- for (let name in sm) {
18
- tm[name] = tm[name] || sm[name]
14
+ for (let name in source.mark) {
15
+ (target.mark as any)[name] = (target.mark as any)[name] || (source.mark as any)[name]
19
16
  }
20
17
  }
21
18
 
package/src/val/BagVal.ts CHANGED
@@ -29,6 +29,10 @@ abstract class BagVal extends FeatureVal {
29
29
  closed: boolean = false
30
30
  optionalKeys: string[] = []
31
31
 
32
+ spread = {
33
+ cj: (undefined as Val | undefined),
34
+ }
35
+
32
36
  constructor(
33
37
  spec: ValSpec,
34
38
  ctx?: AontuContext
@@ -38,6 +42,7 @@ abstract class BagVal extends FeatureVal {
38
42
 
39
43
  clone(ctx: AontuContext, spec?: ValSpec): Val {
40
44
  const bag = super.clone(ctx, spec) as BagVal
45
+ bag.spread = this.spread
41
46
  return bag
42
47
  }
43
48
 
@@ -87,15 +92,11 @@ abstract class BagVal extends FeatureVal {
87
92
  || child.isMap
88
93
  || child.isList
89
94
  || child.isPref
90
- || child.isPath
95
+ || child.isRef
91
96
  || child.isDisjunct
92
- || child.isConjunct
93
97
  || child.isNil
94
98
  ) {
95
- // Optional keys: use separate error context so gen errors
96
- // don't propagate when the key is dropped.
97
- const genctx = optional ? ctx.clone({ err: [], collect: true }) : ctx
98
- let cval = child.gen(genctx)
99
+ let cval = child.gen(ctx)
99
100
 
100
101
  if (optional && (undefined === cval || empty(cval))) {
101
102
  continue
@@ -36,57 +36,6 @@ import {
36
36
  top
37
37
  } from './top'
38
38
 
39
- import { MapVal } from './MapVal'
40
-
41
-
42
- // Shallow merge two MapVals: combine keys without deep unification.
43
- // For shared keys, merge child terms from both maps into one ConjunctVal
44
- // so spreads at that level see all children from both terms.
45
- function shallowMerge(a: MapVal, b: MapVal, ctx: AontuContext): MapVal {
46
- const out = new MapVal({ peg: {} }, ctx)
47
- out.site = a.site
48
- out.closed = a.closed || b.closed
49
- out.optionalKeys = [...a.optionalKeys]
50
- for (const k of b.optionalKeys) {
51
- if (!out.optionalKeys.includes(k)) out.optionalKeys.push(k)
52
- }
53
-
54
- let done = true
55
-
56
- // Copy all keys from a
57
- for (const k in a.peg) {
58
- out.peg[k] = a.peg[k]
59
- }
60
-
61
- // Merge keys from b
62
- for (const k in b.peg) {
63
- if (k in out.peg) {
64
- const av = out.peg[k]
65
- const bv = b.peg[k]
66
-
67
- // Flatten both sides' terms into a single ConjunctVal
68
- const terms: Val[] = []
69
- if (av.isConjunct) { terms.push(...av.peg) } else { terms.push(av) }
70
- if (bv.isConjunct) { terms.push(...bv.peg) } else { terms.push(bv) }
71
-
72
- out.peg[k] = new ConjunctVal({ peg: terms }, ctx)
73
- done = false
74
- }
75
- else {
76
- out.peg[k] = b.peg[k]
77
- }
78
- }
79
-
80
- for (const k in out.peg) {
81
- done = done && (DONE === out.peg[k]?.dc)
82
- }
83
-
84
- out.dc = done ? DONE : 1
85
- propagateMarks(a, out)
86
- propagateMarks(b, out)
87
- return out
88
- }
89
-
90
39
 
91
40
 
92
41
  // TODO: move main logic to op/conjunct
@@ -122,11 +71,6 @@ class ConjunctVal extends JunctionVal {
122
71
  unify(peer: Val, ctx: AontuContext): Val {
123
72
  peer = peer ?? top()
124
73
 
125
- // Fast path: done conjunct self-unifying with TOP.
126
- if (this.done && peer.isTop) {
127
- return this
128
- }
129
-
130
74
  const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Conjunct', this, peer)
131
75
 
132
76
  let done = true
@@ -152,12 +96,7 @@ class ConjunctVal extends JunctionVal {
152
96
  this.peg[vI].mark.hide = newhide
153
97
  // console.log('CONJUNCT-TERM', this.id, vI, this.peg[vI].canon)
154
98
 
155
- // Defer unify-with-TOP for map terms with spreads when other
156
- // map terms exist — the shallow merge fold handles them instead.
157
- const hasMapPeers = this.peg.length > 1 && this.peg[vI].isMap &&
158
- this.peg.some((p: Val, i: number) => i !== vI && p.isMap)
159
99
  upeer[vI] = (this.peg[vI].done && peer.isTop) ? this.peg[vI] :
160
- (hasMapPeers && hasSpreads(this.peg[vI])) ? this.peg[vI] :
161
100
  unite(te ? ctx.clone({ explain: ec(te, 'OWN') }) : ctx, this.peg[vI], peer, 'cj-own')
162
101
 
163
102
  upeer[vI].mark.type = newtype = newtype || upeer[vI].mark.type
@@ -178,22 +117,7 @@ class ConjunctVal extends JunctionVal {
178
117
  }
179
118
 
180
119
  upeer = norm(upeer)
181
-
182
- // Stable partition: fold pure terms first, spread-bearing terms
183
- // last. Pure terms (no spreads, no path-dependent functions)
184
- // unify cheaply. Dynamic terms are then folded in, applying
185
- // spread constraints once to the merged result rather than
186
- // incrementally at every intermediate fold step.
187
- const pure: Val[] = []
188
- const dyn: Val[] = []
189
- for (const term of upeer) {
190
- if (term.isPathDependent || hasSpreads(term)) {
191
- dyn.push(term)
192
- } else {
193
- pure.push(term)
194
- }
195
- }
196
- upeer = pure.concat(dyn)
120
+ // console.log('CONJUNCT-UPEER', this.id, upeer.map((v: Val) => v.canon))
197
121
 
198
122
  // Unify terms against each other
199
123
 
@@ -217,28 +141,17 @@ class ConjunctVal extends JunctionVal {
217
141
 
218
142
  // Can't unite with a RefVal, unless also a RefVal with same path.
219
143
  // else if (t0 instanceof RefVal && !(t1 instanceof RefVal)) {
220
- else if (t0.isPath && !(t1.isPath)) {
144
+ else if (t0.isRef && !(t1.isRef)) {
221
145
  outvals.push(t0)
222
146
  t0 = t1
223
147
  }
224
148
 
225
- else if (t1.isPath && !(t0.isPath)) {
149
+ else if (t1.isRef && !(t0.isRef)) {
226
150
  outvals.push(t0)
227
151
  t0 = t1
228
152
  }
229
153
 
230
154
 
231
- // Shallow merge: when both are maps AND the conjunct contains spreads,
232
- // combine keys into inner ConjunctVals instead of deep unification.
233
- // This ensures spreads see ALL children across terms before being applied.
234
- else if (t0.isMap && t1.isMap && (hasSpreads(t0) || hasSpreads(t1))) {
235
- const merged = shallowMerge(t0 as any, t1 as any, ctx)
236
- done = done && DONE === merged.dc
237
- newtype = this.mark.type || merged.mark.type
238
- newhide = this.mark.hide || merged.mark.hide
239
- t0 = merged
240
- }
241
-
242
155
  else {
243
156
  val = unite(te ? ctx.clone({ explain: ec(te, 'DEF') }) : ctx, t0, t1, 'cj-peer-t0t1')
244
157
  // console.log('CONJUNCT-T', t0.canon, t1?.canon, '->', val.canon)
@@ -246,7 +159,7 @@ class ConjunctVal extends JunctionVal {
246
159
  newtype = this.mark.type || val.mark.type
247
160
  newhide = this.mark.hide || val.mark.hide
248
161
 
249
- // Unite was just a conjunct anyway, so discard.
162
+ // Unite was just a conjunt anyway, so discard.
250
163
  if (val.isConjunct) {
251
164
  outvals.push(t0)
252
165
  t0 = t1
@@ -302,15 +215,6 @@ class ConjunctVal extends JunctionVal {
302
215
 
303
216
 
304
217
  gen(ctx?: AontuContext) {
305
- // If this conjunct contains a SpreadVal + a generable Val,
306
- // gen the generable Val (spread is an unresolved constraint
307
- // that's a no-op at gen time — e.g., empty map with spread).
308
- if (this.peg.length === 2) {
309
- const [a, b] = this.peg
310
- if (a.isSpread && b.isGenable) return b.gen(ctx as any)
311
- if (b.isSpread && a.isGenable) return a.gen(ctx as any)
312
- }
313
-
314
218
  // Unresolved conjunct cannot be generated, so always an error.
315
219
  let nil = makeNilErr(
316
220
  ctx,
@@ -343,17 +247,18 @@ class ConjunctVal extends JunctionVal {
343
247
  function norm(terms: Val[]): Val[] {
344
248
 
345
249
  let expand: Val[] = []
346
- function flattenTerms(terms: Val[]) {
347
- for (let tI = 0; tI < terms.length; tI++) {
348
- if (terms[tI].isConjunct) {
349
- flattenTerms(terms[tI].peg)
350
- }
351
- else {
352
- expand.push(terms[tI])
250
+ for (let tI = 0, pI = 0; tI < terms.length; tI++, pI++) {
251
+ if (terms[tI].isConjunct) {
252
+ const cterms = terms[tI].peg
253
+ for (let cI = 0; cI < cterms.length; cI++) {
254
+ expand[pI + cI] = cterms[cI]
353
255
  }
256
+ pI += cterms.length - 1
257
+ }
258
+ else {
259
+ expand[pI] = terms[tI]
354
260
  }
355
261
  }
356
- flattenTerms(terms)
357
262
 
358
263
 
359
264
  // Consistent ordering ensures order independent unification.
@@ -377,29 +282,6 @@ function norm(terms: Val[]): Val[] {
377
282
  }
378
283
 
379
284
 
380
- // Check if a Val tree contains any SpreadVal constraints.
381
- // Cached on _hasSpreads for performance.
382
- function hasSpreads(v: any): boolean {
383
- if (v._hasSpreads !== undefined) return v._hasSpreads
384
- let result = false
385
- if (v.isSpread) {
386
- result = true
387
- }
388
- else if (v.isMap || v.isList) {
389
- for (const k in v.peg) {
390
- if (v.peg[k]?.isVal && hasSpreads(v.peg[k])) { result = true; break }
391
- }
392
- }
393
- else if (v.isJunction) {
394
- for (const p of v.peg) {
395
- if (p?.isVal && hasSpreads(p)) { result = true; break }
396
- }
397
- }
398
- v._hasSpreads = result
399
- return result
400
- }
401
-
402
-
403
285
  export {
404
286
  norm,
405
287
  ConjunctVal,
@@ -61,10 +61,9 @@ class CopyFuncVal extends FuncBaseVal {
61
61
 
62
62
  // console.log('CR', out)
63
63
 
64
- if (!out.isPath) {
65
- out.mark.type = false
66
- out.mark.hide = false
64
+ if (!out.isRef) {
67
65
  walk(out, (_key: string | number | undefined, val: Val) => {
66
+ // console.log('WALK', val)
68
67
  val.mark.type = false
69
68
  val.mark.hide = false
70
69
  return val