aontu 0.43.0 → 0.45.1

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 (80) hide show
  1. package/dist/ctx.d.ts +2 -5
  2. package/dist/ctx.js +1 -2
  3. package/dist/ctx.js.map +1 -1
  4. package/dist/lang.d.ts +1 -5
  5. package/dist/lang.js +24 -100
  6. package/dist/lang.js.map +1 -1
  7. package/dist/tsconfig.tsbuildinfo +1 -1
  8. package/dist/type.d.ts +0 -5
  9. package/dist/type.js.map +1 -1
  10. package/dist/unify.js +13 -292
  11. package/dist/unify.js.map +1 -1
  12. package/dist/utility.js +2 -6
  13. package/dist/utility.js.map +1 -1
  14. package/dist/val/BagVal.d.ts +3 -0
  15. package/dist/val/BagVal.js +6 -6
  16. package/dist/val/BagVal.js.map +1 -1
  17. package/dist/val/ConjunctVal.d.ts +1 -1
  18. package/dist/val/ConjunctVal.js +14 -137
  19. package/dist/val/ConjunctVal.js.map +1 -1
  20. package/dist/val/CopyFuncVal.js +2 -3
  21. package/dist/val/CopyFuncVal.js.map +1 -1
  22. package/dist/val/DisjunctVal.js +0 -4
  23. package/dist/val/DisjunctVal.js.map +1 -1
  24. package/dist/val/ExpectVal.js +3 -16
  25. package/dist/val/ExpectVal.js.map +1 -1
  26. package/dist/val/JunctionVal.d.ts +0 -1
  27. package/dist/val/JunctionVal.js +1 -6
  28. package/dist/val/JunctionVal.js.map +1 -1
  29. package/dist/val/KeyFuncVal.js +2 -0
  30. package/dist/val/KeyFuncVal.js.map +1 -1
  31. package/dist/val/ListVal.d.ts +0 -1
  32. package/dist/val/ListVal.js +66 -33
  33. package/dist/val/ListVal.js.map +1 -1
  34. package/dist/val/MapVal.d.ts +2 -3
  35. package/dist/val/MapVal.js +95 -67
  36. package/dist/val/MapVal.js.map +1 -1
  37. package/dist/val/MoveFuncVal.d.ts +1 -2
  38. package/dist/val/MoveFuncVal.js +13 -78
  39. package/dist/val/MoveFuncVal.js.map +1 -1
  40. package/dist/val/PathFuncVal.js +4 -25
  41. package/dist/val/PathFuncVal.js.map +1 -1
  42. package/dist/val/PlusOpVal.d.ts +1 -1
  43. package/dist/val/PrefVal.js +5 -18
  44. package/dist/val/PrefVal.js.map +1 -1
  45. package/dist/val/{PathVal.d.ts → RefVal.d.ts} +3 -4
  46. package/dist/val/{PathVal.js → RefVal.js} +77 -75
  47. package/dist/val/RefVal.js.map +1 -0
  48. package/dist/val/Val.d.ts +1 -2
  49. package/dist/val/Val.js +8 -7
  50. package/dist/val/Val.js.map +1 -1
  51. package/dist/val/VarVal.js +2 -2
  52. package/dist/val/VarVal.js.map +1 -1
  53. package/package.json +6 -5
  54. package/src/ctx.ts +3 -16
  55. package/src/lang.ts +23 -113
  56. package/src/type.ts +0 -5
  57. package/src/unify.ts +13 -310
  58. package/src/utility.ts +2 -5
  59. package/src/val/BagVal.ts +7 -6
  60. package/src/val/ConjunctVal.ts +13 -131
  61. package/src/val/CopyFuncVal.ts +2 -3
  62. package/src/val/DisjunctVal.ts +0 -6
  63. package/src/val/ExpectVal.ts +4 -18
  64. package/src/val/JunctionVal.ts +1 -5
  65. package/src/val/KeyFuncVal.ts +3 -0
  66. package/src/val/ListVal.ts +88 -38
  67. package/src/val/MapVal.ts +124 -75
  68. package/src/val/MoveFuncVal.ts +14 -79
  69. package/src/val/PathFuncVal.ts +4 -29
  70. package/src/val/PrefVal.ts +6 -19
  71. package/src/val/{RefVal.ts.old → RefVal.ts} +19 -30
  72. package/src/val/Val.ts +9 -9
  73. package/src/val/VarVal.ts +2 -2
  74. package/README.md +0 -18
  75. package/dist/val/PathVal.js.map +0 -1
  76. package/dist/val/SpreadVal.d.ts +0 -20
  77. package/dist/val/SpreadVal.js +0 -194
  78. package/dist/val/SpreadVal.js.map +0 -1
  79. package/src/val/PathVal.ts +0 -435
  80. package/src/val/SpreadVal.ts +0 -275
package/src/lang.ts CHANGED
@@ -25,10 +25,6 @@ import {
25
25
  makeFileResolver
26
26
  } from '@jsonic/multisource/resolver/file'
27
27
 
28
- import {
29
- preloadFiles
30
- } from '@jsonic/multisource'
31
-
32
28
  import {
33
29
  makePkgResolver
34
30
  } from '@jsonic/multisource/resolver/pkg'
@@ -79,8 +75,7 @@ import { NilVal } from './val/NilVal'
79
75
  import { NullVal } from './val/NullVal'
80
76
  import { NumberVal } from './val/NumberVal'
81
77
  import { PrefVal } from './val/PrefVal'
82
- import { PathVal } from './val/PathVal'
83
- // RefVal replaced by PathVal — see RefVal.ts.old
78
+ import { RefVal } from './val/RefVal'
84
79
  import { StringVal } from './val/StringVal'
85
80
  import { VarVal } from './val/VarVal'
86
81
  import { PlusOpVal } from './val/PlusOpVal'
@@ -96,7 +91,6 @@ import { PrefFuncVal } from './val/PrefFuncVal'
96
91
  import { CloseFuncVal } from './val/CloseFuncVal'
97
92
  import { OpenFuncVal } from './val/OpenFuncVal'
98
93
  import { SuperFuncVal } from './val/SuperFuncVal'
99
- import { SpreadVal } from './val/SpreadVal'
100
94
 
101
95
 
102
96
  let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
@@ -116,17 +110,6 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
116
110
  }
117
111
 
118
112
 
119
- // Helper: create a SpreadVal from the raw SPREAD symbol data.
120
- function makeSpreadVal(rawSpread: any): SpreadVal {
121
- const constraint = Array.isArray(rawSpread.v)
122
- ? (rawSpread.v.length > 1
123
- ? new ConjunctVal({ peg: rawSpread.v })
124
- : rawSpread.v[0])
125
- : rawSpread.v
126
- return new SpreadVal({ peg: constraint })
127
- }
128
-
129
-
130
113
  jsonic.options({
131
114
  hint: {
132
115
  unknown: `
@@ -240,25 +223,12 @@ help isolate the syntax error.`,
240
223
  addsite(new DisjunctVal({ peg: terms }), r, ctx),
241
224
 
242
225
  'dot-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
243
- const pv = addsite(new PathVal({ peg: terms, prefix: true }), r, ctx)
244
- if (ctx.meta.paths) { ctx.meta.paths.push(pv) }
245
- return pv
226
+ return addsite(new RefVal({ peg: terms, prefix: true }), r, ctx)
246
227
  },
247
228
 
248
229
  'dot-infix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
249
- const paths = ctx.meta.paths
250
- if (paths) {
251
- // Remove consumed intermediates
252
- for (const t of terms) {
253
- if (t instanceof PathVal) {
254
- const idx = paths.indexOf(t)
255
- if (idx >= 0) paths.splice(idx, 1)
256
- }
257
- }
258
- }
259
- const pv = addsite(new PathVal({ peg: terms }), r, ctx)
260
- if (paths) { paths.push(pv) }
261
- return pv
230
+ // // console.log('DOT-INFIX-OP', terms)
231
+ return addsite(new RefVal({ peg: terms }), r, ctx)
262
232
  },
263
233
 
264
234
  'star-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) =>
@@ -266,7 +236,7 @@ help isolate the syntax error.`,
266
236
 
267
237
  'dollar-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
268
238
  // $.a.b absolute path
269
- if (terms[0] instanceof PathVal) {
239
+ if (terms[0] instanceof RefVal) {
270
240
  terms[0].absolute = true
271
241
  return terms[0]
272
242
  }
@@ -301,9 +271,6 @@ help isolate the syntax error.`,
301
271
  })
302
272
  }
303
273
  const out = addsite(val, r, ctx)
304
- if (out.isKeyFunc && ctx.meta.keys) {
305
- ctx.meta.keys.push(out)
306
- }
307
274
  return out
308
275
  },
309
276
  }
@@ -314,11 +281,11 @@ help isolate the syntax error.`,
314
281
  op: {
315
282
  // disjunct < conjunct: c & b | a -> (c & b) | a
316
283
  'conjunct': {
317
- infix: true, src: '&', left: 16_000_000, right: 17_000_000,
284
+ infix: true, src: '&', left: 16_000_000, right: 17_000_000
318
285
  },
319
286
 
320
287
  'disjunct': {
321
- infix: true, src: '|', left: 14_000_000, right: 15_000_000,
288
+ infix: true, src: '|', left: 14_000_000, right: 15_000_000
322
289
  },
323
290
 
324
291
  'plus-infix': {
@@ -357,6 +324,7 @@ help isolate the syntax error.`,
357
324
  paren: true,
358
325
  preval: {
359
326
  active: true,
327
+ // allow: ['floor'], //Object.keys(funcMap)
360
328
  },
361
329
  osrc: '(',
362
330
  csrc: ')',
@@ -477,46 +445,26 @@ help isolate the syntax error.`,
477
445
  { s: [OPTKEY, QM], p: 'pair', b: 2, g: 'pair,list,val,imp,jsonic,aontu-optional' },
478
446
  ])
479
447
 
480
-
481
448
  .bc((r: Rule, ctx: JsonicContext) => {
482
449
  const optionalKeys = r.u.aontu_optional_keys ?? []
483
450
 
484
451
  let mo = r.node
485
452
 
486
- // Extract spread constraint before creating MapVal.
487
- // If a spread exists, wrap: ConjunctVal([MapVal, SpreadVal])
488
- const rawSpread = (mo as any)[SPREAD]
489
- if (rawSpread) {
490
- delete (mo as any)[SPREAD]
491
- }
492
-
493
453
  // Handle defered conjuncts, e.g. `{x:1 @"foo"}`
494
454
  if (mo.___merge) {
495
455
  let mop = { ...mo }
496
456
  delete mop.___merge
497
457
 
458
+ // TODO: needs addpath?
498
459
  let mopv = new MapVal({ peg: mop })
499
460
  mopv.optionalKeys = optionalKeys
500
461
 
501
- let terms = [mopv, ...mo.___merge]
502
- if (rawSpread && '&' === rawSpread.o) {
503
- terms.push(makeSpreadVal(rawSpread))
504
- }
505
-
506
- r.node = addsite(new ConjunctVal({ peg: terms }), r, ctx)
462
+ r.node =
463
+ addsite(new ConjunctVal({ peg: [mopv, ...mo.___merge] }), r, ctx)
507
464
  }
508
465
  else {
509
- let mapVal = new MapVal({ peg: mo })
510
- mapVal.optionalKeys = optionalKeys
511
-
512
- if (rawSpread && '&' === rawSpread.o) {
513
- const spreadVal = makeSpreadVal(rawSpread)
514
- r.node = addsite(
515
- new ConjunctVal({ peg: [mapVal, spreadVal] }), r, ctx)
516
- }
517
- else {
518
- r.node = addsite(mapVal, r, ctx)
519
- }
466
+ r.node = addsite(new MapVal({ peg: mo }), r, ctx)
467
+ r.node.optionalKeys = optionalKeys
520
468
  }
521
469
 
522
470
  return undefined
@@ -538,39 +486,20 @@ help isolate the syntax error.`,
538
486
 
539
487
  let ao = r.node
540
488
 
541
- // Extract spread from list before creating ListVal.
542
- const rawSpread = (ao as any)[SPREAD]
543
- if (rawSpread) {
544
- delete (ao as any)[SPREAD]
545
- }
546
-
547
489
  if (ao.___merge) {
548
490
  let aop = [...ao]
549
491
  delete (aop as any).___merge
550
492
 
493
+ // TODO: needs addpath?
551
494
  let aopv = new ListVal({ peg: aop })
552
495
  aopv.optionalKeys = optionalKeys
553
496
 
554
- let terms = [aopv, ...ao.___merge]
555
- if (rawSpread && '&' === rawSpread.o) {
556
- terms.push(makeSpreadVal(rawSpread))
557
- }
558
-
559
497
  r.node =
560
- addsite(new ConjunctVal({ peg: terms }), r, ctx)
498
+ addsite(new ConjunctVal({ peg: [aopv, ...ao.___merge] }), r, ctx)
561
499
  }
562
500
  else {
563
- let listVal = new ListVal({ peg: ao })
564
- listVal.optionalKeys = optionalKeys
565
-
566
- if (rawSpread && '&' === rawSpread.o) {
567
- const spreadVal = makeSpreadVal(rawSpread)
568
- r.node = addsite(
569
- new ConjunctVal({ peg: [listVal, spreadVal] }), r, ctx)
570
- }
571
- else {
572
- r.node = addsite(listVal, r, ctx)
573
- }
501
+ r.node = addsite(new ListVal({ peg: ao }), r, ctx)
502
+ r.node.optionalKeys = optionalKeys
574
503
  }
575
504
 
576
505
  return undefined
@@ -626,13 +555,11 @@ help isolate the syntax error.`,
626
555
  }
627
556
  ])
628
557
 
629
- // Spread children inherit the parent path directly.
630
- // The path gets adjusted correctly when SpreadVal applies
631
- // the constraint via spreadClone(keyctx).
558
+ // NOTE: manually adjust path - @jsonic/path ignores as not pair:true
632
559
  .ao((r) => {
633
560
  if (0 < r.d && r.u.spread) {
634
- r.child.k.path = r.k.path
635
- r.child.k.key = r.k.key
561
+ r.child.k.path = [...r.k.path, '&']
562
+ r.child.k.key = '&'
636
563
  }
637
564
  })
638
565
 
@@ -720,18 +647,10 @@ function makeModelResolver(options: any) {
720
647
  ...(options.resolver?.mem || {})
721
648
  })
722
649
 
723
- const pathfinder = (spec: any) => {
650
+ // TODO: make this consistent with other resolvers
651
+ let fileResolver = makeFileResolver((spec: any) => {
724
652
  return 'string' === typeof spec ? spec : spec?.peg
725
- }
726
-
727
- let preload: { [fullpath: string]: string } | undefined
728
- if (options.preload) {
729
- preload = preloadFiles(options.preload)
730
- }
731
-
732
- let fileResolver = makeFileResolver(
733
- preload ? { pathfinder, preload } : pathfinder
734
- )
653
+ })
735
654
 
736
655
  let pkgResolver = makePkgResolver({
737
656
  require: useRequire,
@@ -780,8 +699,6 @@ class Lang {
780
699
  jsonic: Jsonic
781
700
  opts: AontuOptions
782
701
  idcount: number | undefined
783
- paths: PathVal[] = []
784
- keys: KeyFuncVal[] = []
785
702
 
786
703
 
787
704
  constructor(options?: Partial<AontuOptions>) {
@@ -815,14 +732,9 @@ class Lang {
815
732
  // const start = performance.now()
816
733
 
817
734
  // JSONIC-UPDATE - check meta
818
- this.paths = []
819
- this.keys = []
820
-
821
735
  let jm: any = {
822
736
  fs: opts?.fs,
823
737
  fileName: opts?.path ?? this.opts.path,
824
- paths: this.paths,
825
- keys: this.keys,
826
738
  multisource: {
827
739
  path: opts?.path ?? this.opts.path,
828
740
  deps: (opts && opts.deps) || undefined
@@ -867,7 +779,5 @@ class Lang {
867
779
 
868
780
  export {
869
781
  Lang,
870
- PathVal,
871
- KeyFuncVal,
872
782
  Site,
873
783
  }
package/src/type.ts CHANGED
@@ -30,11 +30,6 @@ type AontuOptions = {
30
30
  collect?: boolean // Collect errors into an errs property, rather than throw them.
31
31
  err?: any[]
32
32
  explain?: any[]
33
- preload?: { // Preload files into memory before parsing.
34
- folders: string[] // Folders to scan.
35
- ext?: string[] // Extensions (default: ['.jsonic', '.json']).
36
- recursive?: boolean // Recurse into subfolders (default: false).
37
- }
38
33
  }
39
34
 
40
35
 
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) {