aontu 0.40.0 → 0.42.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 (99) hide show
  1. package/dist/aontu.js +7 -0
  2. package/dist/aontu.js.map +1 -1
  3. package/dist/ctx.d.ts +14 -2
  4. package/dist/ctx.js +56 -9
  5. package/dist/ctx.js.map +1 -1
  6. package/dist/err.js +36 -15
  7. package/dist/err.js.map +1 -1
  8. package/dist/lang.d.ts +5 -1
  9. package/dist/lang.js +100 -24
  10. package/dist/lang.js.map +1 -1
  11. package/dist/tsconfig.tsbuildinfo +1 -1
  12. package/dist/type.d.ts +5 -0
  13. package/dist/type.js.map +1 -1
  14. package/dist/unify.js +372 -55
  15. package/dist/unify.js.map +1 -1
  16. package/dist/utility.js +6 -2
  17. package/dist/utility.js.map +1 -1
  18. package/dist/val/BagVal.d.ts +0 -3
  19. package/dist/val/BagVal.js +6 -6
  20. package/dist/val/BagVal.js.map +1 -1
  21. package/dist/val/ConjunctVal.d.ts +1 -1
  22. package/dist/val/ConjunctVal.js +138 -15
  23. package/dist/val/ConjunctVal.js.map +1 -1
  24. package/dist/val/CopyFuncVal.js +3 -2
  25. package/dist/val/CopyFuncVal.js.map +1 -1
  26. package/dist/val/DisjunctVal.js +40 -10
  27. package/dist/val/DisjunctVal.js.map +1 -1
  28. package/dist/val/ExpectVal.js +17 -4
  29. package/dist/val/ExpectVal.js.map +1 -1
  30. package/dist/val/FuncBaseVal.js +2 -2
  31. package/dist/val/FuncBaseVal.js.map +1 -1
  32. package/dist/val/IntegerVal.js +1 -1
  33. package/dist/val/IntegerVal.js.map +1 -1
  34. package/dist/val/JunctionVal.d.ts +1 -0
  35. package/dist/val/JunctionVal.js +6 -1
  36. package/dist/val/JunctionVal.js.map +1 -1
  37. package/dist/val/KeyFuncVal.js +0 -2
  38. package/dist/val/KeyFuncVal.js.map +1 -1
  39. package/dist/val/ListVal.d.ts +1 -0
  40. package/dist/val/ListVal.js +36 -65
  41. package/dist/val/ListVal.js.map +1 -1
  42. package/dist/val/MapVal.d.ts +3 -2
  43. package/dist/val/MapVal.js +71 -91
  44. package/dist/val/MapVal.js.map +1 -1
  45. package/dist/val/MoveFuncVal.d.ts +2 -1
  46. package/dist/val/MoveFuncVal.js +78 -13
  47. package/dist/val/MoveFuncVal.js.map +1 -1
  48. package/dist/val/NilVal.d.ts +2 -1
  49. package/dist/val/NilVal.js +15 -1
  50. package/dist/val/NilVal.js.map +1 -1
  51. package/dist/val/OpBaseVal.js +1 -1
  52. package/dist/val/OpBaseVal.js.map +1 -1
  53. package/dist/val/PathFuncVal.js +25 -4
  54. package/dist/val/PathFuncVal.js.map +1 -1
  55. package/dist/val/{RefVal.d.ts → PathVal.d.ts} +4 -3
  56. package/dist/val/{RefVal.js → PathVal.js} +75 -77
  57. package/dist/val/PathVal.js.map +1 -0
  58. package/dist/val/PlusOpVal.d.ts +1 -1
  59. package/dist/val/PrefVal.js +20 -7
  60. package/dist/val/PrefVal.js.map +1 -1
  61. package/dist/val/SpreadVal.d.ts +20 -0
  62. package/dist/val/SpreadVal.js +194 -0
  63. package/dist/val/SpreadVal.js.map +1 -0
  64. package/dist/val/Val.d.ts +4 -1
  65. package/dist/val/Val.js +88 -44
  66. package/dist/val/Val.js.map +1 -1
  67. package/dist/val/VarVal.js +3 -3
  68. package/dist/val/VarVal.js.map +1 -1
  69. package/package.json +6 -7
  70. package/src/aontu.ts +9 -1
  71. package/src/ctx.ts +95 -11
  72. package/src/err.ts +37 -17
  73. package/src/lang.ts +113 -23
  74. package/src/tsconfig.json +2 -1
  75. package/src/type.ts +5 -0
  76. package/src/unify.ts +390 -64
  77. package/src/utility.ts +5 -2
  78. package/src/val/BagVal.ts +6 -7
  79. package/src/val/ConjunctVal.ts +133 -15
  80. package/src/val/CopyFuncVal.ts +3 -2
  81. package/src/val/DisjunctVal.ts +43 -11
  82. package/src/val/ExpectVal.ts +19 -5
  83. package/src/val/FuncBaseVal.ts +2 -2
  84. package/src/val/IntegerVal.ts +1 -1
  85. package/src/val/JunctionVal.ts +5 -1
  86. package/src/val/KeyFuncVal.ts +0 -3
  87. package/src/val/ListVal.ts +40 -86
  88. package/src/val/MapVal.ts +78 -119
  89. package/src/val/MoveFuncVal.ts +79 -14
  90. package/src/val/NilVal.ts +17 -1
  91. package/src/val/OpBaseVal.ts +1 -1
  92. package/src/val/PathFuncVal.ts +29 -4
  93. package/src/val/PathVal.ts +435 -0
  94. package/src/val/PrefVal.ts +21 -8
  95. package/src/val/{RefVal.ts → RefVal.ts.old} +31 -20
  96. package/src/val/SpreadVal.ts +275 -0
  97. package/src/val/Val.ts +141 -50
  98. package/src/val/VarVal.ts +3 -3
  99. package/dist/val/RefVal.js.map +0 -1
package/src/val/MapVal.ts CHANGED
@@ -8,7 +8,6 @@ import type {
8
8
 
9
9
  import {
10
10
  DONE,
11
- SPREAD,
12
11
  } from '../type'
13
12
 
14
13
  import { AontuContext } from '../ctx'
@@ -28,13 +27,13 @@ import {
28
27
  } from './top'
29
28
 
30
29
 
31
- import { ConjunctVal } from './ConjunctVal'
32
30
  import { NilVal } from './NilVal'
33
31
  import { BagVal } from './BagVal'
34
32
 
35
33
 
36
34
  class MapVal extends BagVal {
37
35
  isMap = true
36
+ _canonCache?: string
38
37
 
39
38
  constructor(
40
39
  spec: ValSpec,
@@ -48,23 +47,6 @@ class MapVal extends BagVal {
48
47
 
49
48
  this.mark.type = !!spec.mark?.type
50
49
  this.mark.hide = !!spec.mark?.hide
51
-
52
- let spread = (this.peg as any)[SPREAD]
53
- delete (this.peg as any)[SPREAD]
54
-
55
- if (spread) {
56
- if ('&' === spread.o) {
57
- // TODO: handle existing spread!
58
- this.spread.cj =
59
- Array.isArray(spread.v) ?
60
- 1 < spread.v.length ?
61
- new ConjunctVal({ peg: spread.v }, ctx) :
62
- spread.v[0] :
63
- spread.v
64
- }
65
- }
66
-
67
- // console.log('MAPVAL-ctor', this.type, spec)
68
50
  }
69
51
 
70
52
 
@@ -75,6 +57,23 @@ class MapVal extends BagVal {
75
57
 
76
58
  const TOP = top()
77
59
  peer = peer ?? TOP
60
+
61
+ // Fast path: both maps done, no spreads, peer's keys are a
62
+ // subset of this's keys with the same child references, and
63
+ // neither side has type/hide marks. No new information.
64
+ if (this.done && peer instanceof MapVal && peer.done
65
+ && !this.mark.type && !this.mark.hide
66
+ && !peer.mark.type && !peer.mark.hide) {
67
+ let canSkip = true
68
+ for (const k in peer.peg) {
69
+ if (this.peg[k] !== peer.peg[k]) {
70
+ canSkip = false
71
+ break
72
+ }
73
+ }
74
+ if (canSkip) return this
75
+ }
76
+
78
77
  const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Map', this, peer)
79
78
 
80
79
  let done: boolean = true
@@ -84,13 +83,12 @@ class MapVal extends BagVal {
84
83
  let out: MapVal | NilVal = (peer.isTop ? this : new MapVal({ peg: {} }, ctx))
85
84
 
86
85
  out.closed = this.closed
87
- out.optionalKeys = [...this.optionalKeys]
88
- out.spread.cj = this.spread.cj
86
+ out.optionalKeys = 0 < this.optionalKeys.length ? [...this.optionalKeys] : this.optionalKeys
89
87
  out.site = this.site
90
88
 
91
89
  if (peer instanceof MapVal) {
92
90
  if (!this.closed && peer.closed) {
93
- out = peer.unify(this, ctx.clone({ explain: ec(te, 'PMC') })) as MapVal
91
+ out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'PMC') }) : ctx) as MapVal
94
92
  exit = true
95
93
  }
96
94
 
@@ -105,56 +103,50 @@ class MapVal extends BagVal {
105
103
  && peerkeys.join('~') < selfkeys.join('~')
106
104
  )
107
105
  ) {
108
- out = peer.unify(this, ctx.clone({ explain: ec(te, 'SPC') })) as MapVal
106
+ out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'SPC') }) : ctx) as MapVal
109
107
  exit = true
110
108
  }
111
-
112
109
  }
113
-
114
- if (!exit) {
115
- out.spread.cj = null == out.spread.cj ? peer.spread.cj : (
116
- null == peer.spread.cj ? out.spread.cj : (
117
- out.spread.cj =
118
- unite(ctx.clone({ explain: ec(te, 'SPR') }),
119
- out.spread.cj, peer.spread.cj, 'map-self')
120
- )
121
- )
122
- }
123
- }
124
- else {
125
- // console.log('MAPVAL-PEER-OTHER', this.id, this.canon, this.done, peer.id, peer.canon, peer.done)
126
110
  }
127
111
 
128
112
 
129
113
  if (!exit) {
130
114
  out.dc = this.dc + 1
131
115
 
132
- // let newtype = this.type || peer.type
133
-
134
- let spread_cj = out.spread.cj ?? TOP
116
+ // Fast path: self-unify with TOP.
117
+ // If all children are already done, the map is fully converged.
118
+ if (peer.isTop) {
119
+ let allChildrenDone = true
120
+ for (let key in this.peg) {
121
+ if (DONE !== this.peg[key]?.dc) {
122
+ allChildrenDone = false
123
+ break
124
+ }
125
+ }
126
+ if (allChildrenDone) {
127
+ out.dc = DONE
128
+ ctx.explain && explainClose(te, out)
129
+ return out
130
+ }
131
+ }
135
132
 
136
- // Always unify own children first
133
+ // Unify own children
137
134
  for (let key in this.peg) {
138
- const keyctx = ctx.descend(key)
139
-
140
- const key_spread_cj = spread_cj.spreadClone(keyctx)
141
135
  const child = this.peg[key]
136
+ const keyctx = ctx.descend(key)
142
137
 
143
138
  propagateMarks(this, child)
144
139
 
145
140
  out.peg[key] =
146
- undefined === child ? key_spread_cj :
141
+ undefined === child ? top() :
147
142
  child.isNil ? child :
148
- key_spread_cj.isNil ? key_spread_cj :
149
- key_spread_cj.isTop && child.done ? child :
150
- child.isTop && key_spread_cj.done ? key_spread_cj :
151
- unite(keyctx.clone({ explain: ec(te, 'KEY:' + key) }),
152
- child, key_spread_cj, 'map-own')
143
+ child.done ? child :
144
+ unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
145
+ child, top(), 'map-own')
153
146
 
154
147
  done = (done && DONE === out.peg[key].dc)
155
148
  }
156
149
 
157
- const allowedKeys: string[] = this.closed ? Object.keys(this.peg) : []
158
150
  let bad: NilVal | undefined = undefined
159
151
 
160
152
  if (peer instanceof MapVal) {
@@ -165,12 +157,14 @@ class MapVal extends BagVal {
165
157
  for (let peerkey in upeer.peg) {
166
158
  let peerchild = upeer.peg[peerkey]
167
159
 
168
- if (this.closed && !allowedKeys.includes(peerkey)) {
160
+ if (this.closed && !(peerkey in this.peg)) {
169
161
  bad = makeNilErr(ctx, 'closed', peerchild, undefined)
170
162
  }
171
163
 
172
164
  // key optionality is additive
173
- if (upeer.optionalKeys.includes(peerkey) && !out.optionalKeys.includes(peerkey)) {
165
+ if (0 < upeer.optionalKeys.length
166
+ && upeer.optionalKeys.includes(peerkey)
167
+ && !out.optionalKeys.includes(peerkey)) {
174
168
  out.optionalKeys.push(peerkey)
175
169
  }
176
170
 
@@ -180,19 +174,11 @@ class MapVal extends BagVal {
180
174
 
181
175
  let oval = out.peg[peerkey] =
182
176
  undefined === child ? this.handleExpectedVal(peerkey, peerchild, this, ctx) :
183
- child.isTop && peerchild.done ? peerchild :
184
- child.isNil ? child :
185
- peerchild.isNil ? peerchild :
186
- unite(te ? peerctx.clone({ explain: ec(te, 'CHD') }) : peerctx,
187
- child, peerchild, 'map-peer')
188
-
189
- if (this.spread.cj) {
190
- let key_spread_cj = spread_cj.spreadClone(peerctx)
191
-
192
- oval = out.peg[peerkey] =
193
- unite(te ? peerctx.clone({ explain: ec(te, 'PSP:' + peerkey) }) : peerctx,
194
- oval, key_spread_cj, 'map-peer-spread')
195
- }
177
+ child.isTop && peerchild.done ? peerchild :
178
+ child.isNil ? child :
179
+ peerchild.isNil ? peerchild :
180
+ unite(te ? peerctx.clone({ explain: ec(te, 'CHD') }) : peerctx,
181
+ child, peerchild, 'map-peer')
196
182
 
197
183
  propagateMarks(this, oval)
198
184
 
@@ -213,58 +199,41 @@ class MapVal extends BagVal {
213
199
  out.dc = done ? DONE : out.dc
214
200
  propagateMarks(peer, out)
215
201
  propagateMarks(this, out)
202
+
216
203
  }
217
204
  }
218
205
 
219
- // console.log(
220
- // 'MAPVAL-OUT', out.canon,
221
- // '\n SELF', this,
222
- // '\n PEER', peer,
223
- // '\n OUT', out,
224
- // '\n FROM', (out as any).spread.cj
225
- // )
226
-
227
206
  ctx.explain && explainClose(te, out)
228
207
 
229
208
  return out
230
209
  }
231
210
 
232
211
 
233
- // Spread clone: only deep-clone children that are path-dependent
234
- // (isFunc, isRef). Share all other children directly to avoid
235
- // N x M allocations for maps with N keys and M spread fields.
236
- // Spread clone: when all children are ScalarKindVal (simple type
237
- // constraints like `string`, `number`), share them directly to avoid
238
- // N x M allocations. ScalarKindVal is safe to share: it is immutable,
239
- // always done, never path-dependent, and never has marks mutated.
240
- // For anything more complex, fall back to full deep clone.
212
+ // Optimized clone for use as a spread constraint: share
213
+ // path-independent children, clone only path-dependent ones.
241
214
  spreadClone(ctx: AontuContext): Val {
242
- let allScalarKind = true
243
- for (let key in this.peg) {
244
- if (!(this.peg[key] as any)?.isScalarKind) {
245
- allScalarKind = false
246
- break
247
- }
248
- }
249
-
250
- if (!allScalarKind) {
251
- return this.clone(ctx)
252
- }
215
+ if (!this.isPathDependent) return this
253
216
 
254
217
  let out = (super.clone(ctx) as MapVal)
255
218
  out.peg = {}
256
219
 
257
220
  for (let entry of Object.entries(this.peg)) {
258
- out.peg[entry[0]] = entry[1]
259
- }
260
-
261
- // Must create a new spread object to avoid mutating the original.
262
- out.spread = {
263
- cj: this.spread.cj ? this.spread.cj.spreadClone(ctx) : undefined,
221
+ const child = entry[1] as Val
222
+ if (child?.isVal && child.isPathDependent) {
223
+ out.peg[entry[0]] = child.clone(ctx, { path: [...out.path, entry[0]] })
224
+ }
225
+ else if (child?.isVal) {
226
+ const wrapper = Object.create(child)
227
+ wrapper.mark = { ...child.mark }
228
+ out.peg[entry[0]] = wrapper
229
+ }
230
+ else {
231
+ out.peg[entry[0]] = child
232
+ }
264
233
  }
265
234
 
266
235
  out.closed = this.closed
267
- out.optionalKeys = [...this.optionalKeys]
236
+ out.optionalKeys = 0 < this.optionalKeys.length ? [...this.optionalKeys] : this.optionalKeys
268
237
 
269
238
  return out
270
239
  }
@@ -277,36 +246,25 @@ class MapVal extends BagVal {
277
246
  for (let entry of Object.entries(this.peg)) {
278
247
  out.peg[entry[0]] =
279
248
  (entry[1] as any)?.isVal ?
280
- // (entry[1] as Val).clone(ctx, spec?.mark ? { mark: spec.mark } : {}) :
281
249
  (entry[1] as Val).clone(ctx, {
282
250
  mark: spec?.mark ?? {},
283
251
  path: [...out.path, entry[0]]
284
252
  }) :
285
253
  entry[1]
286
254
  }
287
- if (this.spread.cj) {
288
- out.spread.cj = this.spread.cj.clone(ctx, spec?.mark ? { mark: spec.mark } : {})
289
- }
290
255
 
291
256
  out.closed = this.closed
292
257
  out.optionalKeys = [...this.optionalKeys]
293
258
 
294
- // out.from = this.from
295
-
296
- // console.log('MAPVAL-CLONE', this.canon, '->', out.canon)
297
259
  return out
298
260
  }
299
261
 
300
262
 
301
263
  get canon() {
302
- let keys = Object.keys(this.peg)
303
- return '' +
304
- // this.errcanon() +
305
- // (this.mark.type ? '<type>' : '') +
306
- // (this.id + '=') +
264
+ if (this._canonCache !== undefined) return this._canonCache
265
+ let keys = Object.keys(this.peg).sort()
266
+ const c = '' +
307
267
  '{' +
308
- (this.spread.cj ? '&:' + this.spread.cj.canon +
309
- (0 < keys.length ? ',' : '') : '') +
310
268
  keys
311
269
  .map(k => [
312
270
  JSON.stringify(k) +
@@ -315,18 +273,19 @@ class MapVal extends BagVal {
315
273
  (this.peg[k]?.canon ?? this.peg[k])
316
274
  ])
317
275
  .join(',') +
318
- '}' // + '<' + (this.mark.hide ? 'H' : '') + '>'
319
-
276
+ '}'
277
+ if (this.done) this._canonCache = c
278
+ return c
320
279
  }
321
280
 
322
281
 
323
- inspection(d?: number) {
324
- return this.spread.cj ? '&:' + this.spread.cj.inspect(null == d ? 0 : d + 1) : ''
282
+ inspection(_d?: number) {
283
+ return ''
325
284
  }
326
285
 
327
286
  }
328
287
 
329
288
 
330
289
  export {
331
- MapVal
290
+ MapVal,
332
291
  }
@@ -13,6 +13,7 @@ import {
13
13
  import { makeNilErr } from '../err'
14
14
 
15
15
  import { NilVal } from '../val/NilVal'
16
+ import { PathVal } from '../val/PathVal'
16
17
 
17
18
  import {
18
19
  walk
@@ -26,6 +27,60 @@ import { PrefFuncVal } from './PrefFuncVal'
26
27
 
27
28
 
28
29
 
30
+ // Navigate the tree using a PathVal's resolved path and hide the source.
31
+ function hideAtPath(root: Val, pv: PathVal) {
32
+ // Compute the refpath the same way PathVal.find does
33
+ const parts: string[] = []
34
+ for (const part of pv.peg) {
35
+ if ('string' === typeof part) parts.push(part)
36
+ }
37
+
38
+ let refpath: string[]
39
+ if (pv.absolute) {
40
+ refpath = parts
41
+ }
42
+ else {
43
+ refpath = pv.path.slice(0, -1).concat(parts)
44
+ }
45
+
46
+ // Walk to the source, handling conjuncts/disjuncts
47
+ let node: any = root
48
+ for (let i = 0; i < refpath.length; i++) {
49
+ const part = refpath[i]
50
+ if (node?.isMap || node?.isList) {
51
+ node = node.peg[part]
52
+ }
53
+ else if (node?.isConjunct || node?.isDisjunct) {
54
+ // Search junction terms for the key
55
+ let found = null
56
+ const stack = [...node.peg]
57
+ while (stack.length > 0) {
58
+ const term = stack.pop()
59
+ if (term?.isConjunct || term?.isDisjunct) {
60
+ stack.push(...term.peg)
61
+ }
62
+ else if ((term?.isMap || term?.isList) && term.peg[part] != null) {
63
+ found = term.peg[part]
64
+ break
65
+ }
66
+ }
67
+ node = found
68
+ }
69
+ else {
70
+ return
71
+ }
72
+ if (node == null) return
73
+ }
74
+
75
+ // Mark the source value hidden
76
+ node.mark.hide = true
77
+ walk(node, (_key: string | number | undefined, val: Val) => {
78
+ val.mark.hide = true
79
+ return val
80
+ })
81
+ }
82
+
83
+
29
84
  class MoveFuncVal extends FuncBaseVal {
30
85
  isMoveFunc = true
31
86
 
@@ -50,30 +105,40 @@ class MoveFuncVal extends FuncBaseVal {
50
105
  }
51
106
 
52
107
  resolve(ctx: AontuContext, args: Val[]) {
53
- let out = args[0] ?? makeNilErr(ctx, 'arg', this)
108
+ let arg = args[0] ?? makeNilErr(ctx, 'arg', this)
109
+ if (arg.isNil) return arg
54
110
 
55
- const orig = out
111
+ let src: Val
56
112
 
57
- if (!orig.isNil) {
58
- const src = orig.clone(ctx)
113
+ if (arg instanceof PathVal) {
114
+ // Get clone for the destination
115
+ src = arg.find(ctx) as Val
116
+ if (src == null || src.isNil) return makeNilErr(ctx, 'arg', this)
59
117
 
60
- if (src.isRef) {
61
- src.mark._hide_found = true
62
- }
118
+ // Hide the source in the tree by navigating to it
119
+ hideAtPath(ctx.root as Val, arg)
120
+ }
121
+ else {
122
+ src = arg.clone(ctx)
63
123
 
64
- walk(orig, (_key: string | number | undefined, val: Val) => {
124
+ // Hide the original
125
+ arg.mark.hide = true
126
+ walk(arg, (_key: string | number | undefined, val: Val) => {
65
127
  val.mark.hide = true
66
128
  return val
67
129
  })
68
-
69
- // out = new CopyFuncVal({ peg: [src] }, ctx)
70
- out = new PrefFuncVal({ peg: [src] }, ctx)
71
- // out = src
72
130
  }
73
131
 
74
- // console.log('MOVE-resolve', orig, out)
132
+ // Clear marks on the clone (like copy)
133
+ src.mark.type = false
134
+ src.mark.hide = false
135
+ walk(src, (_key: string | number | undefined, val: Val) => {
136
+ val.mark.type = false
137
+ val.mark.hide = false
138
+ return val
139
+ })
75
140
 
76
- return out
141
+ return new PrefFuncVal({ peg: [src] }, ctx)
77
142
  }
78
143
  }
79
144
 
package/src/val/NilVal.ts CHANGED
@@ -16,7 +16,7 @@ import {
16
16
 
17
17
  import { Val, EMPTY_ERR } from './Val'
18
18
 
19
- import { AontuError } from '../err'
19
+ import { AontuError, descErr } from '../err'
20
20
 
21
21
 
22
22
  class NilVal extends Val {
@@ -142,6 +142,9 @@ class NilVal extends Val {
142
142
  ctx.adderr(this)
143
143
 
144
144
  if (!ctx.collect) {
145
+ if (null == this.msg || '' === this.msg) {
146
+ descErr(this, ctx)
147
+ }
145
148
  const err = new AontuError(this.msg, [this])
146
149
  throw err
147
150
  }
@@ -162,6 +165,19 @@ class NilVal extends Val {
162
165
  }
163
166
 
164
167
 
168
+ // Shared sentinel for transient "this unification branch failed"
169
+ // markers. Used by DisjunctVal.unify to flag failed member trials
170
+ // (and to dedup results) without allocating a fresh NilVal per
171
+ // failure. The sentinel is filtered out before the disjunct result
172
+ // is constructed, so its .why / .site / .primary fields are never
173
+ // inspected by user-visible code.
174
+ //
175
+ // Do NOT use this sentinel for errors that may surface: those need
176
+ // real NilVals with proper site/path info for descErr formatting.
177
+ const TRIAL_NIL = new NilVal({ why: '|:trial-nil' })
178
+
179
+
165
180
  export {
166
181
  NilVal,
182
+ TRIAL_NIL,
167
183
  }
@@ -117,7 +117,7 @@ class OpBaseVal extends FeatureVal {
117
117
  }
118
118
  else {
119
119
  out = result.done && peer.isTop ? result :
120
- unite(ctx.clone({ explain: ec(te, 'RES') }), result, peer, 'op')
120
+ unite(te ? ctx.clone({ explain: ec(te, 'RES') }) : ctx, result, peer, 'op')
121
121
  }
122
122
 
123
123
  out.dc = DONE === out.dc ? DONE : this.dc + 1
@@ -6,6 +6,10 @@ import type {
6
6
  ValSpec,
7
7
  } from '../type'
8
8
 
9
+ import {
10
+ DONE,
11
+ } from '../type'
12
+
9
13
  import {
10
14
  AontuContext,
11
15
  } from '../ctx'
@@ -13,7 +17,7 @@ import {
13
17
  import { makeNilErr } from '../err'
14
18
 
15
19
  import { NilVal } from '../val/NilVal'
16
- import { RefVal } from '../val/RefVal'
20
+ import { PathVal } from '../val/PathVal'
17
21
  import { FuncBaseVal } from './FuncBaseVal'
18
22
 
19
23
 
@@ -45,12 +49,26 @@ class PathFuncVal extends FuncBaseVal {
45
49
  let arg = args[0]
46
50
 
47
51
  if (0 === this.prepared) {
48
- if (arg.isScalar) {
49
- arg = this.place(new RefVal({ peg: [arg], absolute: false }))
52
+ if (arg instanceof PathVal) {
53
+ // PathVal from dotted arg resolve via find().
54
+ const found = arg.find(ctx)
55
+ if (found != null && !found.isNil) {
56
+ arg = found
57
+ }
58
+ else {
59
+ arg.dc = DONE
60
+ }
61
+ }
62
+ else if (arg.isScalar && arg.peg != null && arg.peg !== ''
63
+ && ('string' === typeof arg.peg || arg.isNumber)) {
64
+ // String or number arg like path("foo") or path(0.2) — create a path value
65
+ arg = this.place(new PathVal({ peg: [arg], absolute: false })) as PathVal
66
+ arg.dc = DONE
50
67
  }
51
- else if (!arg.isRef) {
68
+ else if (arg.isNil || (arg.isScalar && (arg.peg === '' || arg.peg == null))) {
52
69
  arg = makeNilErr(ctx, 'invalid-arg', this)
53
70
  }
71
+ // else: already resolved by preprocessing — pass through
54
72
  }
55
73
 
56
74
  args[0] = arg
@@ -64,6 +82,13 @@ class PathFuncVal extends FuncBaseVal {
64
82
  resolve(ctx: AontuContext, args: Val[]) {
65
83
  let out = args[0] ?? makeNilErr(ctx, 'arg', this)
66
84
 
85
+ if (out instanceof PathVal) {
86
+ const found = out.find(ctx)
87
+ if (found != null && !found.isNil) {
88
+ out = found
89
+ }
90
+ }
91
+
67
92
  return out
68
93
  }
69
94