aontu 0.53.0 → 0.55.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 (197) hide show
  1. package/dist/aontu.d.ts +3 -2
  2. package/dist/aontu.js +36 -7
  3. package/dist/aontu.js.map +1 -1
  4. package/dist/cli.d.ts +2 -1
  5. package/dist/cli.js +500 -8
  6. package/dist/cli.js.map +1 -1
  7. package/dist/ctx.d.ts +5 -0
  8. package/dist/ctx.js +1 -0
  9. package/dist/ctx.js.map +1 -1
  10. package/dist/diff.js.map +1 -1
  11. package/dist/err.js +7 -1
  12. package/dist/err.js.map +1 -1
  13. package/dist/graph.d.ts +2 -5
  14. package/dist/graph.js +83 -46
  15. package/dist/graph.js.map +1 -1
  16. package/dist/hcanon.js +9 -10
  17. package/dist/hcanon.js.map +1 -1
  18. package/dist/hints.js +94 -26
  19. package/dist/hints.js.map +1 -1
  20. package/dist/jsonschema.js +34 -0
  21. package/dist/jsonschema.js.map +1 -1
  22. package/dist/lang.js +593 -191
  23. package/dist/lang.js.map +1 -1
  24. package/dist/lsp.d.ts +1 -1
  25. package/dist/lsp.js +86 -6
  26. package/dist/lsp.js.map +1 -1
  27. package/dist/mcp.js +153 -6
  28. package/dist/mcp.js.map +1 -1
  29. package/dist/mod-tool.js +42 -8
  30. package/dist/mod-tool.js.map +1 -1
  31. package/dist/mod.d.ts +4 -0
  32. package/dist/mod.js +97 -2
  33. package/dist/mod.js.map +1 -1
  34. package/dist/patch.d.ts +5 -0
  35. package/dist/patch.js +25 -25
  36. package/dist/patch.js.map +1 -1
  37. package/dist/provenance.d.ts +1 -0
  38. package/dist/provenance.js +2 -1
  39. package/dist/provenance.js.map +1 -1
  40. package/dist/query.js.map +1 -1
  41. package/dist/reach.d.ts +1 -0
  42. package/dist/reach.js +49 -21
  43. package/dist/reach.js.map +1 -1
  44. package/dist/relation.d.ts +4 -0
  45. package/dist/relation.js +125 -200
  46. package/dist/relation.js.map +1 -1
  47. package/dist/sig.d.ts +25 -0
  48. package/dist/sig.js +277 -0
  49. package/dist/sig.js.map +1 -0
  50. package/dist/sigdecl.d.ts +2 -0
  51. package/dist/sigdecl.js +11 -0
  52. package/dist/sigdecl.js.map +1 -0
  53. package/dist/siggate.d.ts +4 -0
  54. package/dist/siggate.js +90 -0
  55. package/dist/siggate.js.map +1 -0
  56. package/dist/std.js +75 -16
  57. package/dist/std.js.map +1 -1
  58. package/dist/subsume.js +57 -10
  59. package/dist/subsume.js.map +1 -1
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/dist/unify.d.ts +2 -2
  62. package/dist/unify.js +93 -114
  63. package/dist/unify.js.map +1 -1
  64. package/dist/utility.d.ts +1 -2
  65. package/dist/utility.js +7 -61
  66. package/dist/utility.js.map +1 -1
  67. package/dist/val/AggFuncVal.d.ts +12 -1
  68. package/dist/val/AggFuncVal.js +165 -3
  69. package/dist/val/AggFuncVal.js.map +1 -1
  70. package/dist/val/BagVal.d.ts +2 -0
  71. package/dist/val/BagVal.js +56 -8
  72. package/dist/val/BagVal.js.map +1 -1
  73. package/dist/val/ConstraintVal.js +33 -5
  74. package/dist/val/ConstraintVal.js.map +1 -1
  75. package/dist/val/ContainerKindVal.d.ts +35 -0
  76. package/dist/val/ContainerKindVal.js +99 -0
  77. package/dist/val/ContainerKindVal.js.map +1 -0
  78. package/dist/val/CopyFuncVal.js +0 -7
  79. package/dist/val/CopyFuncVal.js.map +1 -1
  80. package/dist/val/DisjunctVal.d.ts +1 -2
  81. package/dist/val/DisjunctVal.js +146 -39
  82. package/dist/val/DisjunctVal.js.map +1 -1
  83. package/dist/val/ExpectVal.js +37 -2
  84. package/dist/val/ExpectVal.js.map +1 -1
  85. package/dist/val/FuncBaseVal.d.ts +1 -0
  86. package/dist/val/FuncBaseVal.js +19 -8
  87. package/dist/val/FuncBaseVal.js.map +1 -1
  88. package/dist/val/GraphAtomVal.d.ts +39 -0
  89. package/dist/val/GraphAtomVal.js +184 -0
  90. package/dist/val/GraphAtomVal.js.map +1 -0
  91. package/dist/val/JunctionVal.js +22 -5
  92. package/dist/val/JunctionVal.js.map +1 -1
  93. package/dist/val/ListVal.js +27 -34
  94. package/dist/val/ListVal.js.map +1 -1
  95. package/dist/val/MapVal.d.ts +1 -0
  96. package/dist/val/MapVal.js +82 -35
  97. package/dist/val/MapVal.js.map +1 -1
  98. package/dist/val/PathFuncVal.d.ts +2 -2
  99. package/dist/val/PathFuncVal.js +75 -16
  100. package/dist/val/PathFuncVal.js.map +1 -1
  101. package/dist/val/PathVal.d.ts +25 -0
  102. package/dist/val/PathVal.js +150 -0
  103. package/dist/val/PathVal.js.map +1 -0
  104. package/dist/val/PlusOpVal.d.ts +2 -1
  105. package/dist/val/PlusOpVal.js +50 -35
  106. package/dist/val/PlusOpVal.js.map +1 -1
  107. package/dist/val/PrefVal.d.ts +2 -0
  108. package/dist/val/PrefVal.js +159 -32
  109. package/dist/val/PrefVal.js.map +1 -1
  110. package/dist/val/RecurseVal.d.ts +19 -0
  111. package/dist/val/RecurseVal.js +217 -0
  112. package/dist/val/RecurseVal.js.map +1 -0
  113. package/dist/val/RefVal.d.ts +2 -1
  114. package/dist/val/RefVal.js +105 -72
  115. package/dist/val/RefVal.js.map +1 -1
  116. package/dist/val/ReferFuncVal.d.ts +30 -7
  117. package/dist/val/ReferFuncVal.js +395 -94
  118. package/dist/val/ReferFuncVal.js.map +1 -1
  119. package/dist/val/ScalarKindVal.d.ts +4 -2
  120. package/dist/val/ScalarKindVal.js +12 -1
  121. package/dist/val/ScalarKindVal.js.map +1 -1
  122. package/dist/val/SuperFuncVal.d.ts +4 -2
  123. package/dist/val/SuperFuncVal.js +118 -14
  124. package/dist/val/SuperFuncVal.js.map +1 -1
  125. package/dist/val/TopVal.d.ts +1 -1
  126. package/dist/val/Val.d.ts +2 -3
  127. package/dist/val/Val.js +39 -20
  128. package/dist/val/Val.js.map +1 -1
  129. package/dist/val/arith.js +4 -1
  130. package/dist/val/arith.js.map +1 -1
  131. package/dist/vet.d.ts +1 -0
  132. package/dist/vet.js +32 -1
  133. package/dist/vet.js.map +1 -1
  134. package/dist/view.d.ts +90 -0
  135. package/dist/view.js +2168 -0
  136. package/dist/view.js.map +1 -0
  137. package/grammar/aontu.gbnf +18 -10
  138. package/grammar/aontu.lark +15 -10
  139. package/grammar/aontu.tmLanguage.json +184 -0
  140. package/package.json +10 -3
  141. package/skill/grammar-card.md +1 -2
  142. package/src/aontu.ts +37 -7
  143. package/src/cli.ts +553 -8
  144. package/src/ctx.ts +20 -0
  145. package/src/diff.ts +4 -2
  146. package/src/err.ts +8 -1
  147. package/src/graph.ts +125 -75
  148. package/src/hcanon.ts +9 -11
  149. package/src/hints.ts +112 -29
  150. package/src/jsonschema.ts +41 -0
  151. package/src/lang.ts +642 -203
  152. package/src/lsp.ts +75 -6
  153. package/src/mcp.ts +164 -6
  154. package/src/mod-tool.ts +48 -9
  155. package/src/mod.ts +110 -1
  156. package/src/patch.ts +31 -27
  157. package/src/provenance.ts +8 -1
  158. package/src/query.ts +4 -2
  159. package/src/reach.ts +52 -23
  160. package/src/relation.ts +139 -236
  161. package/src/sig.ts +345 -0
  162. package/src/sigdecl.ts +11 -0
  163. package/src/siggate.ts +144 -0
  164. package/src/std.ts +77 -16
  165. package/src/subsume.ts +59 -10
  166. package/src/unify.ts +102 -125
  167. package/src/utility.ts +7 -67
  168. package/src/val/AggFuncVal.ts +231 -4
  169. package/src/val/BagVal.ts +58 -9
  170. package/src/val/ConstraintVal.ts +34 -5
  171. package/src/val/ContainerKindVal.ts +158 -0
  172. package/src/val/CopyFuncVal.ts +0 -7
  173. package/src/val/DisjunctVal.ts +152 -40
  174. package/src/val/ExpectVal.ts +39 -4
  175. package/src/val/FuncBaseVal.ts +21 -8
  176. package/src/val/GraphAtomVal.ts +264 -0
  177. package/src/val/JunctionVal.ts +23 -6
  178. package/src/val/ListVal.ts +30 -37
  179. package/src/val/MapVal.ts +87 -39
  180. package/src/val/PathFuncVal.ts +107 -19
  181. package/src/val/PathVal.ts +221 -0
  182. package/src/val/PlusOpVal.ts +56 -37
  183. package/src/val/PrefVal.ts +186 -38
  184. package/src/val/RecurseVal.ts +285 -0
  185. package/src/val/RefVal.ts +105 -83
  186. package/src/val/ReferFuncVal.ts +445 -100
  187. package/src/val/ScalarKindVal.ts +12 -0
  188. package/src/val/SuperFuncVal.ts +137 -13
  189. package/src/val/TopVal.ts +1 -1
  190. package/src/val/Val.ts +44 -35
  191. package/src/val/arith.ts +4 -1
  192. package/src/vet.ts +41 -4
  193. package/src/view.ts +2882 -0
  194. package/dist/val/IdFuncVal.d.ts +0 -13
  195. package/dist/val/IdFuncVal.js +0 -54
  196. package/dist/val/IdFuncVal.js.map +0 -1
  197. package/src/val/IdFuncVal.ts +0 -91
@@ -33,12 +33,29 @@ abstract class JunctionVal extends FeatureVal {
33
33
 
34
34
  clone(ctx: AontuContext, spec?: ValSpec): Val {
35
35
  let out = (super.clone(ctx, spec) as JunctionVal)
36
- // The instantiation flag descends with the mark (ADR-005): a
37
- // junction inside a template clones its members as instances (the
38
- // instantiation sites re-path the whole clone afterwards — see
39
- // repathInstance in Val.ts).
40
- const childspec = spec?.mark || spec?.dup ?
41
- { mark: spec?.mark, dup: spec?.dup } : {}
36
+ // A JUNCTION'S MEMBERS SIT AT THE JUNCTION'S OWN POSITION. That is
37
+ // the rule `repathInstance` (Val.ts) states for the instance walk
38
+ // -- a conjunct's members occupy the conjunct's position, which is
39
+ // what `A & B` means -- and it holds for every clone, not only for
40
+ // marked ones.
41
+ //
42
+ // Val.clone's ctx cut cannot derive it: MapVal.clone hands a child
43
+ // its path through the SPEC rather than by descending the ctx, so
44
+ // a member left to the cut takes the DRIVING context's path -- the
45
+ // map's -- and loses the field key it sits under. A conjunct
46
+ // member reached through a reference into a position at least as
47
+ // deep as its definition then carried the entity's path, and every
48
+ // reader of `Val.path` was told the wrong position: a rel()-minted
49
+ // link took the ENTITY's key as its predicate, so `inverse(n)`
50
+ // looked for a mirror under a relation nobody declared and
51
+ // reported a written mirror missing.
52
+ //
53
+ // The instantiation flag still descends with the mark (ADR-005).
54
+ // Go states the same invariant directly (go/clone.go, cloneAt), so
55
+ // this restores parity rather than inventing a behaviour.
56
+ const childspec: any = spec?.mark || spec?.dup ?
57
+ { mark: spec?.mark, dup: spec?.dup, path: out.path }
58
+ : { path: out.path }
42
59
  out.peg = this.peg.map((entry: Val) => entry.clone(ctx, childspec))
43
60
  return out
44
61
  }
@@ -18,7 +18,6 @@ import { unite } from '../unify'
18
18
  import {
19
19
  propagateMarks,
20
20
  canonRiders,
21
- constantIdFunc,
22
21
  explainOpen,
23
22
  ec,
24
23
  explainClose,
@@ -34,7 +33,7 @@ import {
34
33
  import { ConjunctVal } from './ConjunctVal'
35
34
  import { NilVal } from './NilVal'
36
35
  import { BagVal } from './BagVal'
37
- import { repathInstance } from './Val'
36
+ import { repathInstance, spreadId } from './Val'
38
37
  import { markSpread } from '../provenance'
39
38
 
40
39
 
@@ -70,22 +69,6 @@ class ListVal extends BagVal {
70
69
  spread.v[0] :
71
70
  spread.v
72
71
 
73
- // Clearing rule 3 (G4 phase 1): a CONSTANT id in the template
74
- // would declare every child to be one entity. The refusal
75
- // replaces the template, so it reaches every child and the
76
- // bag itself (see the isNil arm where the spread is applied)
77
- // as ONE nil identity — made here, once, rather than per
78
- // pass, so the report names it once.
79
- const idfn: any = constantIdFunc(this.spread.cj)
80
- if (undefined !== idfn) {
81
- const nil: any = new NilVal({ why: 'id_spread' }, ctx)
82
- nil.site.row = idfn.site.row
83
- nil.site.col = idfn.site.col
84
- nil.site.url = idfn.site.url
85
- nil.primary = idfn
86
- this.spread.cj = nil
87
- }
88
-
89
72
  // let tmv = Array.isArray(spread.v) ? spread.v : [spread.v]
90
73
  // this.spread.cj = new ConjunctVal({ peg: tmv }, ctx)
91
74
  }
@@ -96,6 +79,12 @@ class ListVal extends BagVal {
96
79
  // NOTE: order of keys is not preserved!
97
80
  // not possible in any case - consider {a,b} unify {b,a}
98
81
  unify(peer: Val, ctx: AontuContext): Val {
82
+ // A rel() peer drives: the relation constraint rewrites this list
83
+ // leaf by leaf (RELATIONS.0.md §3.2); see the twin arm in MapVal.
84
+ if (true === (peer as any)?.isRel) {
85
+ return (peer as any).unify(this, ctx)
86
+ }
87
+
99
88
  const TOP = top()
100
89
  peer = peer ?? TOP
101
90
 
@@ -109,6 +98,19 @@ class ListVal extends BagVal {
109
98
  return peer.unify(this, ctx)
110
99
  }
111
100
 
101
+ // A DISJUNCT ALTERNATIVE MATCHES ITS OWN LENGTH (BUGS.md §52
102
+ // regime 4, the X-C3 adjudication): in a trial, a literal list
103
+ // with no spread admits only a peer list of the same length -- a
104
+ // spread makes it variadic. Outside trials the ordinary
105
+ // elementwise merge stands (two statements of one list are one
106
+ // list), so `[] | [&: T]` stops admitting every list through the
107
+ // empty arm while `a: [] a: [1]` still merges.
108
+ if (true === ctx._trialMode && true === (peer as any).isList
109
+ && null == this.spread.cj && null == (peer as any).spread.cj
110
+ && this.peg.length !== (peer as any).peg.length) {
111
+ return makeNilErr(ctx, 'list_length', this, peer)
112
+ }
113
+
112
114
  const te = ctx.explain && explainOpen(ctx, ctx.explain, 'List', this, peer)
113
115
  let done: boolean = true
114
116
  let exit = false
@@ -144,21 +146,6 @@ class ListVal extends BagVal {
144
146
 
145
147
  let spread_cj = out.spread.cj || TOP
146
148
 
147
- // The template REFUSED at construction (clearing rule 3, G4
148
- // phase 1): the bag itself is that refusal. Returning the nil
149
- // here rather than only letting it reach the children is what
150
- // makes an EMPTY bag with a bad template an error too — there
151
- // are no children to carry it.
152
- //
153
- // Narrow to THIS code on purpose. A nil spread from any other
154
- // cause keeps its existing behaviour of driving every key
155
- // (coverage3 `nil-spread-drives-every-key`): a template that has
156
- // merely not resolved yet must not permanently kill the bag that
157
- // holds it.
158
- if ('id_spread' === (spread_cj as any).why) {
159
- return spread_cj
160
- }
161
-
162
149
  // Always unify children first
163
150
  for (let key in this.peg) {
164
151
  const keyctx = ctx.descend(key)
@@ -172,7 +159,7 @@ class ListVal extends BagVal {
172
159
  // instead of having the template met into it a second time.
173
160
  // Re-applying is the identity for a template that has already
174
161
  // RESOLVED, which is why the missing guard went unnoticed here
175
- // — but a template that residuates (`&: id(key(1))`, G8 phase
162
+ // — but a template that residuates (`&: {k: key(1)}`, G8 phase
176
163
  // 0) is not yet a value to be idempotent about, so each pass
177
164
  // conjoined another copy and the element's canon DOUBLED per
178
165
  // pass. The old `ctx.cc < 3` key delay hid it by ending the
@@ -181,11 +168,11 @@ class ListVal extends BagVal {
181
168
  // does.
182
169
  let oval: Val
183
170
  if (!spread_cj.isTop
184
- && (child as any)._spr === (spread_cj as any).id) {
171
+ && (child as any)._spr === spreadId(spread_cj)) {
185
172
  oval = child.done ? child :
186
173
  unite(te ? keyctx.clone({ explain: ec(te, 'PEG:' + key) }) : keyctx,
187
174
  child, TOP, 'list-own')
188
- ; (oval as any)._spr = (spread_cj as any).id
175
+ ; (oval as any)._spr = spreadId(spread_cj)
189
176
  }
190
177
  else {
191
178
  const key_spread_cj = spread_cj.spreadClone(keyctx)
@@ -212,7 +199,7 @@ class ListVal extends BagVal {
212
199
  child, key_spread_cj, 'list-own')
213
200
 
214
201
  if (!spread_cj.isTop && !oval.isNil) {
215
- ; (oval as any)._spr = (spread_cj as any).id
202
+ ; (oval as any)._spr = spreadId(spread_cj)
216
203
  }
217
204
  }
218
205
 
@@ -265,6 +252,12 @@ class ListVal extends BagVal {
265
252
  done = (done && DONE === oval.dc)
266
253
  }
267
254
  }
255
+ else if (true === (peer as any).isContainerKind) {
256
+ // The container KIND delegates to its own arm, exactly as a
257
+ // scalar delegates to a ScalarKindVal peer (MapVal has the
258
+ // same arm).
259
+ out = peer.unify(this, ctx) as any
260
+ }
268
261
  else if (!peer.isTop) {
269
262
  out = makeNilErr(ctx, 'list', this, peer)
270
263
  }
package/src/val/MapVal.ts CHANGED
@@ -17,7 +17,6 @@ import { unite } from '../unify'
17
17
  import {
18
18
  propagateMarks,
19
19
  canonRiders,
20
- constantIdFunc,
21
20
  walk,
22
21
  explainOpen,
23
22
  ec,
@@ -29,12 +28,13 @@ import { makeNilErr, AontuError } from '../err'
29
28
  import {
30
29
  top
31
30
  } from './top'
31
+ import { pendingMarkWrapper } from './RefVal'
32
32
 
33
33
 
34
34
  import { ConjunctVal } from './ConjunctVal'
35
35
  import { NilVal } from './NilVal'
36
36
  import { BagVal } from './BagVal'
37
- import { repathInstance } from './Val'
37
+ import { repathInstance, spreadId } from './Val'
38
38
  import { cmpCodePoint } from '../keyorder'
39
39
  import { markSpread } from '../provenance'
40
40
 
@@ -70,6 +70,15 @@ function snapshotRefSpread(cj: any, ctx: AontuContext): Val | undefined {
70
70
  // A ref to a type() resolves to its inner template — snapshot that,
71
71
  // so a type-wrapped ref behaves like a plain-map ref spread.
72
72
  if (tgt && (tgt as any).isTypeFunc) tgt = (tgt as any).peg?.[0]
73
+ // A pending type()/hide() CALL is not yet a value to snapshot
74
+ // (ADR-005, the same rule find's non-snap path defers on): cached
75
+ // here it resolves at every destination and STAMPS marks the
76
+ // clearing walk ran too early to clear -- a mutual recursive
77
+ // schema's members vanished from generation this way. No cache;
78
+ // retry once the wrapper has resolved at its own field.
79
+ if (tgt && pendingMarkWrapper(tgt)) {
80
+ return undefined
81
+ }
73
82
  // Only snapshot a found, path-dependent target. If the target is not
74
83
  // present yet (it may be introduced by a later conjunct/merge), do
75
84
  // NOT cache — retry on the next fixpoint pass.
@@ -122,22 +131,6 @@ class MapVal extends BagVal {
122
131
  new ConjunctVal({ peg: spread.v }, ctx) :
123
132
  spread.v[0] :
124
133
  spread.v
125
-
126
- // Clearing rule 3 (G4 phase 1): a CONSTANT id in the template
127
- // would declare every child to be one entity. The refusal
128
- // replaces the template, so it reaches every child and the
129
- // bag itself (see the isNil arm where the spread is applied)
130
- // as ONE nil identity — made here, once, rather than per
131
- // pass, so the report names it once.
132
- const idfn: any = constantIdFunc(this.spread.cj)
133
- if (undefined !== idfn) {
134
- const nil: any = new NilVal({ why: 'id_spread' }, ctx)
135
- nil.site.row = idfn.site.row
136
- nil.site.col = idfn.site.col
137
- nil.site.url = idfn.site.url
138
- nil.primary = idfn
139
- this.spread.cj = nil
140
- }
141
134
  }
142
135
  }
143
136
 
@@ -147,7 +140,34 @@ class MapVal extends BagVal {
147
140
 
148
141
  // NOTE: order of keys is not preserved!
149
142
  // not possible in any case - consider {a,b} unify {b,a}
143
+ // ALIAS DECLARATIONS MUST SIT AT THE DOCUMENT ROOT. Stated on the
144
+ // VALUE rather than at the parse, because the parse cannot see it: an
145
+ // INCLUDED file's declarations are at the root of their own text, and
146
+ // only once the loaded map is placed does it become apparent that
147
+ // root is not the document's. `%name` is spelled as a reference from
148
+ // the document root, so an included file's own `%b` would otherwise
149
+ // reach the INCLUDER's `%b` -- cross-file capture, the hazard the
150
+ // sigil exists to prevent, one level up.
151
+ //
152
+ // P1 is single-file by construction; carrying a name ACROSS files is
153
+ // what `export` and the destructure are for (P2, not built), and this
154
+ // refusal is what keeps the two from being confused meanwhile. Pathed
155
+ // at the DECLARATION, which is what is wrong, not at the map.
156
+ aliasDeclarationsAreRooted(ctx: AontuContext): Val | undefined {
157
+ if (0 === this.aliasKeys.length || 0 === this.path.length) {
158
+ return undefined
159
+ }
160
+ const nv: any = new NilVal({ why: 'alias_not_toplevel' }, ctx)
161
+ nv.site = this.site
162
+ nv.path = [...this.path, this.aliasKeys[0]]
163
+ return nv
164
+ }
165
+
150
166
  unify(peer: Val, ctx: AontuContext): Val {
167
+ const arooted = this.aliasDeclarationsAreRooted(ctx)
168
+ if (undefined !== arooted) {
169
+ return arooted
170
+ }
151
171
  // console.log('MAPVAL-UNIFY', this.id, this.canon, peer.id, peer.canon)
152
172
 
153
173
  const TOP = top()
@@ -173,9 +193,18 @@ class MapVal extends BagVal {
173
193
 
174
194
  out.closed = this.closed
175
195
  out.optionalKeys = [...this.optionalKeys]
196
+ out.aliasKeys = [...this.aliasKeys]
176
197
  out.spread.cj = this.spread.cj
177
198
  out.site = this.site
178
199
 
200
+ // A rel() peer DRIVES whichever side the fold hands it on: the
201
+ // relation constraint rewrites this container leaf by leaf
202
+ // (RELATIONS.0.md §3.2), exactly as a sizing residual takes the
203
+ // driver's seat above.
204
+ if (true === (peer as any)?.isRel) {
205
+ return peer.unify(this, te ? ctx.clone({ explain: ec(te, 'REL') }) : ctx)
206
+ }
207
+
179
208
  if (peer instanceof MapVal) {
180
209
  if (!this.closed && peer.closed) {
181
210
  out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'PMC') }) : ctx) as MapVal
@@ -238,21 +267,6 @@ class MapVal extends BagVal {
238
267
 
239
268
  let spread_cj = out.spread.cj ?? TOP
240
269
 
241
- // The template REFUSED at construction (clearing rule 3, G4
242
- // phase 1): the bag itself is that refusal. Returning the nil
243
- // here rather than only letting it reach the children is what
244
- // makes an EMPTY bag with a bad template an error too — there
245
- // are no children to carry it.
246
- //
247
- // Narrow to THIS code on purpose. A nil spread from any other
248
- // cause keeps its existing behaviour of driving every key
249
- // (coverage3 `nil-spread-drives-every-key`): a template that has
250
- // merely not resolved yet must not permanently kill the bag that
251
- // holds it.
252
- if ('id_spread' === (spread_cj as any).why) {
253
- return spread_cj
254
- }
255
-
256
270
  // Snapshot a path-dependent *ref* spread to its structural target
257
271
  // once (while inner key()/path() funcs are still unresolved), so
258
272
  // later fixpoint passes don't re-resolve the ref against the mutated
@@ -292,11 +306,11 @@ class MapVal extends BagVal {
292
306
  // No `undefined !== child` here: propagateMarks above already
293
307
  // dereferenced it, so a missing child would have thrown there.
294
308
  if (!spread_cj.isTop
295
- && (child as any)._spr === (spread_cj as any).id) {
309
+ && (child as any)._spr === spreadId(spread_cj)) {
296
310
  oval = child.done ? child :
297
311
  unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
298
312
  child, TOP, 'map-own')
299
- ; (oval as any)._spr = (spread_cj as any).id
313
+ ; (oval as any)._spr = spreadId(spread_cj)
300
314
  }
301
315
  else {
302
316
  const key_spread_cj = spread_cj.spreadClone(keyctx)
@@ -325,7 +339,7 @@ class MapVal extends BagVal {
325
339
  child, key_spread_cj, 'map-own')
326
340
 
327
341
  if (!spread_cj.isTop && !oval.isNil) {
328
- ; (oval as any)._spr = (spread_cj as any).id
342
+ ; (oval as any)._spr = spreadId(spread_cj)
329
343
  }
330
344
  }
331
345
 
@@ -355,6 +369,13 @@ class MapVal extends BagVal {
355
369
  out.optionalKeys.push(peerkey)
356
370
  }
357
371
 
372
+ // ... and so is an alias declaration, for the same reason:
373
+ // two statements for one map are one map, and a name declared
374
+ // in either is declared in the result.
375
+ if (upeer.aliasKeys.includes(peerkey) && !out.aliasKeys.includes(peerkey)) {
376
+ out.aliasKeys.push(peerkey)
377
+ }
378
+
358
379
  let child = out.peg[peerkey]
359
380
 
360
381
  const peerctx = ctx.descend(peerkey)
@@ -383,15 +404,27 @@ class MapVal extends BagVal {
383
404
  // Same apply-once discipline as the own-key loop: once the
384
405
  // constraint is merged into the value (marked with the
385
406
  // constraint's id), later passes only self-unify.
386
- if ((oval as any)._spr !== (spread_cj as any).id) {
407
+ if ((oval as any)._spr !== spreadId(spread_cj)) {
387
408
  let key_spread_cj = spread_cj.spreadClone(peerctx)
388
409
 
410
+ // A SPREAD IS A SPREAD FROM EITHER SIDE. The own-key loop
411
+ // above marks its template so `why` can say the
412
+ // contribution came from `&:` rather than from the key;
413
+ // this arm did not, so the SAME document reported the
414
+ // role or dropped it depending on whether the template
415
+ // and the keys were written in one statement or two --
416
+ // `services: &: {..}` plus `services: {web:{}}` took the
417
+ // peer path and lost `(spread)` (use-cases/BUGS.md §55).
418
+ if (undefined !== peerctx.prov) {
419
+ markSpread(key_spread_cj)
420
+ }
421
+
389
422
  oval = out.peg[peerkey] =
390
423
  unite(te ? peerctx.clone({ explain: ec(te, 'PSP:' + peerkey) }) : peerctx,
391
424
  oval, key_spread_cj, 'map-peer-spread')
392
425
 
393
426
  if (!spread_cj.isTop && !oval.isNil) {
394
- ; (oval as any)._spr = (spread_cj as any).id
427
+ ; (oval as any)._spr = spreadId(spread_cj)
395
428
  }
396
429
  }
397
430
  }
@@ -401,6 +434,12 @@ class MapVal extends BagVal {
401
434
  done = (done && DONE === oval.dc)
402
435
  }
403
436
  }
437
+ else if (true === (peer as any).isContainerKind) {
438
+ // The container KIND delegates to its own arm, exactly as a
439
+ // scalar delegates to a ScalarKindVal peer: the kind knows to
440
+ // admit this map, and this map knows nothing about kinds.
441
+ out = peer.unify(this, ctx) as any
442
+ }
404
443
  else if (!peer.isTop) {
405
444
  out = makeNilErr(ctx, 'map', this, peer)
406
445
  }
@@ -479,6 +518,7 @@ class MapVal extends BagVal {
479
518
 
480
519
  out.closed = this.closed
481
520
  out.optionalKeys = [...this.optionalKeys]
521
+ out.aliasKeys = [...this.aliasKeys]
482
522
 
483
523
  return out
484
524
  }
@@ -509,6 +549,7 @@ class MapVal extends BagVal {
509
549
 
510
550
  out.closed = this.closed
511
551
  out.optionalKeys = [...this.optionalKeys]
552
+ out.aliasKeys = [...this.aliasKeys]
512
553
 
513
554
  // out.from = this.from
514
555
 
@@ -522,7 +563,14 @@ class MapVal extends BagVal {
522
563
  // independent of insertion/unification order and matches the Go
523
564
  // port. A bare .sort() is UTF-16 code-unit order, which puts an
524
565
  // astral key ahead of everything in U+E000-U+FFFF -- see cmpCodePoint.
525
- let keys = Object.keys(this.peg).sort(cmpCodePoint)
566
+ // An alias declaration is not part of the document, so canon does
567
+ // not render it: a document with aliases and the document with
568
+ // every alias written out longhand must produce the same text and
569
+ // therefore the same `aon1-` hash. That is the sharpest statement
570
+ // of what an alias IS -- a name for a value, and nothing more.
571
+ let keys = Object.keys(this.peg)
572
+ .filter(k => !this.aliasKeys.includes(k))
573
+ .sort(cmpCodePoint)
526
574
  return '' +
527
575
  // this.errcanon() +
528
576
  // (this.mark.type ? '<type>' : '') +
@@ -1,5 +1,31 @@
1
1
  /* Copyright (c) 2021-2025 Richard Rodger, MIT License */
2
2
 
3
+ // THE PATH CONSTRUCTOR (docs/design/PATHS.0.md). `path(p)` CAPTURES
4
+ // `p` -- the one non-strict argument position in the language: every
5
+ // other call reads its argument's value, this one reads its spelling.
6
+ // `path($.a.b)` is the address `$.a.b` as a first-class value, not
7
+ // the value found there; a plain reference (`$.a.b` alone) stays the
8
+ // embedding it has always been.
9
+ //
10
+ // The capture happens in prepare(), which receives the argument
11
+ // BEFORE the driving loop resolves it -- the same interception this
12
+ // function always used, now kept instead of released: the RefVal's
13
+ // spelling is read off its segments and the RefVal itself is never
14
+ // driven, so no pass ever holds a resolvable reference here.
15
+ //
16
+ // `path()` with no argument is the path KIND (PathKindVal): the
17
+ // vacuous constructor call is the kind, as `{}`-the-unit is to maps
18
+ // -- see ContainerKindVal for the other half of that convention.
19
+ //
20
+ // A string argument is read by the ADDRESS grammar, not resolved as a
21
+ // segment: `path("$.a")` is the same capture `path($.a)` is, and text
22
+ // with no anchor is RELATIVE (`path("a.b")` is `path(.a.b)`, the
23
+ // address the raw spelling captures). A COMPUTED argument -- an
24
+ // expression, a reference to a string -- is the one shape that does
25
+ // evaluate: the driven result converts by the same grammar at
26
+ // resolve, which is what makes an address buildable (`refer() &
27
+ // path("$.customers." + key())`) while a bare string still never IS
28
+ // one (ADR-016): the conversion happens only inside this call.
3
29
 
4
30
  import type {
5
31
  Val,
@@ -12,8 +38,41 @@ import {
12
38
 
13
39
  import { makeNilErr } from '../err'
14
40
 
15
- import { RefVal } from '../val/RefVal'
16
41
  import { FuncBaseVal } from './FuncBaseVal'
42
+ import { PathVal, PathKindVal, parseAddress, textAddress } from './PathVal'
43
+
44
+
45
+ // The address a reference SPELLS, or undefined when its segments
46
+ // cannot spell one (a variable segment, a parent step after the first
47
+ // named segment). Leading `.` entries in a relative ref's peg are
48
+ // parent steps; the spelling is the same grammar refer reads, so one
49
+ // address parser stays the single gate.
50
+ function captureSpelling(rv: any): string | undefined {
51
+ const parts: string[] = []
52
+ let up = 0
53
+ let lead = true
54
+ for (const p of rv.peg) {
55
+ if ('string' !== typeof p) {
56
+ return undefined
57
+ }
58
+ if ('.' === p) {
59
+ if (!lead) {
60
+ return undefined
61
+ }
62
+ up++
63
+ }
64
+ else {
65
+ lead = false
66
+ parts.push(p)
67
+ }
68
+ }
69
+ if (0 === parts.length || (rv.absolute && 0 < up)) {
70
+ return undefined
71
+ }
72
+ return rv.absolute ?
73
+ '$.' + parts.join('.') :
74
+ '.'.repeat(up + 1) + parts.join('.')
75
+ }
17
76
 
18
77
 
19
78
  class PathFuncVal extends FuncBaseVal {
@@ -41,37 +100,66 @@ class PathFuncVal extends FuncBaseVal {
41
100
 
42
101
 
43
102
  prepare(ctx: AontuContext, args: Val[]) {
44
- let arg = args[0]
45
-
46
103
  if (0 === this.prepared) {
47
- // A missing argument (`path()`) must produce an invalid-arg error
48
- // value, as the Go port does -- reading .isScalar off nothing threw
49
- // a TypeError that the unifier could only report as an opaque
50
- // internal error. Same discipline breach, and same fix, as the one
51
- // UpperFuncVal records; path() was missed in that sweep.
104
+ this.prepared++
105
+
106
+ const arg: any = args[0]
107
+
108
+ // The kind form: no argument to capture.
52
109
  if (null == arg) {
53
- arg = makeNilErr(ctx, 'invalid-arg', this)
110
+ return []
54
111
  }
55
- else if (arg.isScalar) {
56
- arg = this.place(new RefVal({ peg: [arg], absolute: false }))
112
+
113
+ // The captured spelling, from a reference's segments or from a
114
+ // string literal read as address text. Both go through
115
+ // parseAddress, so what capture admits and what refer reads
116
+ // cannot drift. Anything else -- an expression, a reference to
117
+ // a string -- is left for the driving loop, and resolve
118
+ // converts the driven result below.
119
+ let spelling: string | undefined
120
+ if (true === arg.isRef) {
121
+ spelling = captureSpelling(arg)
57
122
  }
58
- else if (!arg.isRef) {
59
- arg = makeNilErr(ctx, 'invalid-arg', this)
123
+ else if (true === arg.isScalar && 'string' === typeof arg.peg) {
124
+ spelling = textAddress(arg.peg)
125
+ }
126
+ else {
127
+ return args
60
128
  }
61
- }
62
129
 
63
- args[0] = arg
130
+ if (undefined === spelling || undefined === parseAddress(spelling)) {
131
+ return [makeNilErr(ctx, 'path_address', this, arg)]
132
+ }
64
133
 
65
- this.prepared++
134
+ return [new PathVal({ peg: spelling }, ctx)]
135
+ }
66
136
 
67
137
  return args
68
138
  }
69
139
 
70
140
 
71
141
  resolve(ctx: AontuContext, args: Val[]) {
72
- let out = args[0] ?? makeNilErr(ctx, 'arg', this)
73
-
74
- return out
142
+ if (0 === args.length) {
143
+ const out = new PathKindVal({}, ctx)
144
+ out.site = this.site
145
+ out.path = this.path
146
+ return out
147
+ }
148
+ const arg: any = args[0]
149
+ if (true === arg.isPath || true === arg.isNil) {
150
+ return arg
151
+ }
152
+ // The COMPUTED argument, driven by the loop above: a string
153
+ // converts by the address grammar, exactly as a literal does at
154
+ // capture; anything else was never a path expression.
155
+ if (true === arg.isScalar && 'string' === typeof arg.peg) {
156
+ const spelling = textAddress(arg.peg)
157
+ if (undefined === parseAddress(spelling)) {
158
+ return makeNilErr(ctx, 'path_address', this, arg)
159
+ }
160
+ return new PathVal({ peg: spelling }, ctx)
161
+ }
162
+ return makeNilErr(ctx, 'invalid-arg', this)
75
163
  }
76
164
 
77
165
  } /* node:coverage ignore next 6 */