aontu 0.57.0 → 0.59.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 (132) hide show
  1. package/README.md +2 -2
  2. package/dist/agentsmd.js +1 -1
  3. package/dist/alias.d.ts +3 -0
  4. package/dist/alias.js +59 -0
  5. package/dist/alias.js.map +1 -0
  6. package/dist/aontu.d.ts +4 -2
  7. package/dist/aontu.js +12 -3
  8. package/dist/aontu.js.map +1 -1
  9. package/dist/cli.d.ts +3 -1
  10. package/dist/cli.js +486 -4
  11. package/dist/cli.js.map +1 -1
  12. package/dist/ctx.d.ts +4 -0
  13. package/dist/ctx.js +3 -0
  14. package/dist/ctx.js.map +1 -1
  15. package/dist/err.js +6 -3
  16. package/dist/err.js.map +1 -1
  17. package/dist/format.js +8 -2
  18. package/dist/format.js.map +1 -1
  19. package/dist/hints.js +40 -9
  20. package/dist/hints.js.map +1 -1
  21. package/dist/lang.js +409 -58
  22. package/dist/lang.js.map +1 -1
  23. package/dist/lower.d.ts +20 -0
  24. package/dist/lower.js +575 -0
  25. package/dist/lower.js.map +1 -0
  26. package/dist/lsp.d.ts +1 -1
  27. package/dist/lsp.js +1 -1
  28. package/dist/lsp.js.map +1 -1
  29. package/dist/mcp-server.js +1 -1
  30. package/dist/mcp.d.ts +1 -0
  31. package/dist/mcp.js +40 -3
  32. package/dist/mcp.js.map +1 -1
  33. package/dist/render.d.ts +53 -0
  34. package/dist/render.js +542 -0
  35. package/dist/render.js.map +1 -0
  36. package/dist/sigdecl.js +1 -1
  37. package/dist/sigdecl.js.map +1 -1
  38. package/dist/std.d.ts +2 -0
  39. package/dist/std.js +498 -2
  40. package/dist/std.js.map +1 -1
  41. package/dist/template.d.ts +5 -0
  42. package/dist/template.js +257 -0
  43. package/dist/template.js.map +1 -0
  44. package/dist/tsconfig.tsbuildinfo +1 -1
  45. package/dist/type.d.ts +2 -0
  46. package/dist/type.js.map +1 -1
  47. package/dist/unify.js +43 -0
  48. package/dist/unify.js.map +1 -1
  49. package/dist/val/AggFuncVal.d.ts +1 -1
  50. package/dist/val/AggFuncVal.js +10 -21
  51. package/dist/val/AggFuncVal.js.map +1 -1
  52. package/dist/val/BagVal.js +1 -1
  53. package/dist/val/BagVal.js.map +1 -1
  54. package/dist/val/ConstraintVal.js +1 -1
  55. package/dist/val/DisjunctVal.js +33 -2
  56. package/dist/val/DisjunctVal.js.map +1 -1
  57. package/dist/val/EachFuncVal.d.ts +1 -1
  58. package/dist/val/EachFuncVal.js +8 -13
  59. package/dist/val/EachFuncVal.js.map +1 -1
  60. package/dist/val/EmitFuncVal.d.ts +23 -4
  61. package/dist/val/EmitFuncVal.js +298 -28
  62. package/dist/val/EmitFuncVal.js.map +1 -1
  63. package/dist/val/FilterFuncVal.js +8 -4
  64. package/dist/val/FilterFuncVal.js.map +1 -1
  65. package/dist/val/FormFuncVal.d.ts +14 -0
  66. package/dist/val/FormFuncVal.js +55 -0
  67. package/dist/val/FormFuncVal.js.map +1 -0
  68. package/dist/val/FuncBaseVal.js +36 -13
  69. package/dist/val/FuncBaseVal.js.map +1 -1
  70. package/dist/val/ListVal.js +9 -1
  71. package/dist/val/ListVal.js.map +1 -1
  72. package/dist/val/MapVal.d.ts +2 -1
  73. package/dist/val/MapVal.js +2 -1
  74. package/dist/val/MapVal.js.map +1 -1
  75. package/dist/val/PackFuncVal.d.ts +1 -1
  76. package/dist/val/PackFuncVal.js +22 -18
  77. package/dist/val/PackFuncVal.js.map +1 -1
  78. package/dist/val/PlaceVal.js +7 -2
  79. package/dist/val/PlaceVal.js.map +1 -1
  80. package/dist/val/RefVal.d.ts +3 -0
  81. package/dist/val/RefVal.js +181 -18
  82. package/dist/val/RefVal.js.map +1 -1
  83. package/dist/val/Val.d.ts +8 -1
  84. package/dist/val/Val.js +39 -1
  85. package/dist/val/Val.js.map +1 -1
  86. package/dist/val/members.d.ts +9 -0
  87. package/dist/val/members.js +52 -0
  88. package/dist/val/members.js.map +1 -0
  89. package/grammar/aontu.abnf +1 -1
  90. package/grammar/aontu.gbnf +1 -1
  91. package/grammar/aontu.lark +1 -1
  92. package/grammar/aontu.tmLanguage.json +1 -1
  93. package/package.json +1 -1
  94. package/skill/SKILL.md +4 -4
  95. package/skill/error-codes.md +1 -1
  96. package/skill/examples.md +1 -1
  97. package/skill/grammar-card.md +1 -1
  98. package/src/agentsmd.ts +1 -1
  99. package/src/alias.ts +112 -0
  100. package/src/aontu.ts +20 -2
  101. package/src/cli.ts +528 -4
  102. package/src/ctx.ts +18 -0
  103. package/src/err.ts +6 -3
  104. package/src/format.ts +12 -2
  105. package/src/hints.ts +47 -9
  106. package/src/lang.ts +453 -63
  107. package/src/lower.ts +636 -0
  108. package/src/lsp.ts +1 -1
  109. package/src/mcp-server.ts +1 -1
  110. package/src/mcp.ts +43 -4
  111. package/src/render.ts +727 -0
  112. package/src/sigdecl.ts +1 -1
  113. package/src/std.ts +506 -1
  114. package/src/template.ts +291 -0
  115. package/src/type.ts +10 -1
  116. package/src/unify.ts +47 -0
  117. package/src/val/AggFuncVal.ts +10 -21
  118. package/src/val/BagVal.ts +1 -1
  119. package/src/val/ConstraintVal.ts +1 -1
  120. package/src/val/DisjunctVal.ts +33 -2
  121. package/src/val/EachFuncVal.ts +8 -16
  122. package/src/val/EmitFuncVal.ts +376 -39
  123. package/src/val/FilterFuncVal.ts +11 -4
  124. package/src/val/FormFuncVal.ts +119 -0
  125. package/src/val/FuncBaseVal.ts +36 -13
  126. package/src/val/ListVal.ts +9 -1
  127. package/src/val/MapVal.ts +3 -2
  128. package/src/val/PackFuncVal.ts +23 -19
  129. package/src/val/PlaceVal.ts +7 -2
  130. package/src/val/RefVal.ts +193 -20
  131. package/src/val/Val.ts +75 -4
  132. package/src/val/members.ts +86 -0
@@ -0,0 +1,119 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+ // TRANSFORMATION: `form(data, tmpl)` (G9 §4; docs/design/RENDER.0.md
4
+ // D11 and P6). One element of the result list per child of `data`,
5
+ // being `tmpl` instantiated at that position with `_` bound to the
6
+ // source child. It REPLACES; it does not meet.
7
+ //
8
+ // names: [web, auth]
9
+ // units: form($.names, {path: _ + ".ts"}) -> [{path: "web.ts"}, {path: "auth.ts"}]
10
+ //
11
+ // WHY BESIDE `each`. `each($.ports, integer)` is a schema statement
12
+ // and is monotone -- more information about the template narrows the
13
+ // children -- so it is a lattice citizen, and a constructor is not.
14
+ // The language draws this line twice already: `min`/`max` (bounds)
15
+ // against `least`/`greatest` (aggregates), and `filter` (select by
16
+ // unifiability) against `match` (choose a result). `each` is the
17
+ // bound; `form` is the construction. `each` also carries the source
18
+ // child's identity (its clone keeps the entity), where `form`'s
19
+ // element IS the template.
20
+ //
21
+ // WHY IT EXISTS AT ALL: ORDER. `pick(pack(d, {f: t}), f)` maps too,
22
+ // but it goes through a map and re-sorts to code-point order, and it
23
+ // refuses a list of records outright (pack_key). A struct's fields, a
24
+ // DDL's columns and a file's imports are lists whose order is the
25
+ // model's, and silently alphabetising them is wrong output. `form`
26
+ // reads its members through the one ordering helper `each` uses
27
+ // (members.ts), so the two can never disagree about order -- source
28
+ // order for a list, sorted-key order for a map -- and skips what
29
+ // generation would not emit: a hidden child, an unfilled optional.
30
+ //
31
+ // It joins `boundArgStart` (PlaceVal.ts): a `_` inside its template
32
+ // is ITS hole to bind, never an outer generator's. Forgetting that is
33
+ // BUGS.md §34's silent failure -- every existing test passes and the
34
+ // new combinator captures an outer generator's hole -- which is why
35
+ // the shared spec's nesting rows exist.
36
+
37
+ import type {
38
+ Val,
39
+ ValSpec,
40
+ } from '../type'
41
+
42
+ import {
43
+ AontuContext,
44
+ } from '../ctx'
45
+
46
+ import { makeNilErr } from '../err'
47
+ import { ListVal } from './ListVal'
48
+ import { FuncBaseVal } from './FuncBaseVal'
49
+ import { repathInstance } from './Val'
50
+ import { fillPlace } from './PlaceVal'
51
+ import { memberVals } from './members'
52
+
53
+
54
+ class FormFuncVal extends FuncBaseVal {
55
+ isFormFunc = true
56
+
57
+ // THE STAGING RULE, for the reason given in PackFuncVal: the data
58
+ // is not settled merely by being `done` once.
59
+ staged = true
60
+
61
+ constructor(
62
+ spec: ValSpec,
63
+ ctx?: AontuContext
64
+ ) {
65
+ super(spec, ctx)
66
+ }
67
+
68
+
69
+ funcname() {
70
+ return 'form'
71
+ }
72
+
73
+
74
+ // The template is not an argument to drive (see PackFuncVal.prepare).
75
+ prepare(_ctx: AontuContext, _args: Val[]) {
76
+ return null
77
+ }
78
+
79
+
80
+ unify(peer: Val, ctx: AontuContext): Val {
81
+ // ONE argument is driven: the data. The template is not (see
82
+ // prepare above).
83
+ if (!this.stagedReady(peer, ctx, 1)) {
84
+ return this.residuate(peer, ctx)
85
+ }
86
+
87
+ return super.unify(peer, ctx)
88
+ }
89
+
90
+
91
+ resolve(ctx: AontuContext, args: Val[]) {
92
+ const vals = memberVals(args?.[0], ctx)
93
+ if (undefined === vals) {
94
+ return makeNilErr(ctx, 'form_data', this)
95
+ }
96
+
97
+ // Arity is checked at parse (funcArity), so the template is here.
98
+ const tmpl: Val = args[1]
99
+ const peg: Val[] = []
100
+
101
+ for (let i = 0; i < vals.length; i++) {
102
+ const elctx = ctx.descend(String(i))
103
+ // A FULL INSTANCE per element, to the leaves (`dup`, ADR-005),
104
+ // at the element's own position -- see PackFuncVal.resolve --
105
+ // with `_` bound to the source child and NOTHING met into it.
106
+ const inst = tmpl.clone(elctx, { dup: true })
107
+ repathInstance(inst, inst.path)
108
+ peg.push(fillPlace(inst, vals[i], elctx))
109
+ }
110
+
111
+ return new ListVal({ peg }, ctx)
112
+ }
113
+
114
+ } /* node:coverage ignore next 6 */
115
+
116
+
117
+ export {
118
+ FormFuncVal,
119
+ }
@@ -46,6 +46,13 @@ import { hasPlace, fillPlace } from '../val/PlaceVal'
46
46
  function trialUnify(ctx: AontuContext, a: Val, b: Val): Val | undefined {
47
47
  const savedErr = ctx.err
48
48
  const savedTrial = ctx._trialMode
49
+ // Restored by DELETION where they were inherited, for the reason
50
+ // DisjunctVal.unify's own sandbox gives at length: contexts are
51
+ // Object.create(parent) and cached per (parent, key), so writing
52
+ // these back leaves own properties that shadow the ancestor and make
53
+ // a later trial invisible to the value running inside it.
54
+ const ownErr = Object.prototype.hasOwnProperty.call(ctx, 'err')
55
+ const ownTrial = Object.prototype.hasOwnProperty.call(ctx, '_trialMode')
49
56
  const trialErr: any[] = []
50
57
 
51
58
  ctx.err = trialErr
@@ -56,8 +63,18 @@ function trialUnify(ctx: AontuContext, a: Val, b: Val): Val | undefined {
56
63
  out = unite(ctx, a, b, 'trial')
57
64
  }
58
65
  finally {
59
- ctx.err = savedErr
60
- ctx._trialMode = savedTrial
66
+ if (ownErr) {
67
+ ctx.err = savedErr
68
+ }
69
+ else {
70
+ delete (ctx as any).err
71
+ }
72
+ if (ownTrial) {
73
+ ctx._trialMode = savedTrial
74
+ }
75
+ else {
76
+ delete (ctx as any)._trialMode
77
+ }
61
78
  }
62
79
 
63
80
  return 0 < trialErr.length || out.isNil ? undefined : out
@@ -158,22 +175,28 @@ class FuncBaseVal extends FeatureVal {
158
175
 
159
176
 
160
177
  // THE PER-DESTINATION INSTANTIATION RULE (ADR-005). The default
161
- // clone shares the argument array AND the argument Vals — pinned
162
- // sharing for the move()/copy() ghost artifacts (test/spec/func.tsv,
163
- // ghost-*-innard-canon) — but a clone that is a template INSTANCE
164
- // must own the full inner structure: with the args shared,
165
- // `pack($.names, close({name: key()}))` resolved key() once inside
166
- // the one shared inner map and stamped the FIRST child's key on
167
- // every child (use-cases/BUGS.md §8). The `dup` spec flag asks for
168
- // that depth; everything else keeps the sharing it has always had.
178
+ // clone shares the argument array AND the argument Vals — the
179
+ // residuation clone, which stays at one position and wants the
180
+ // sharing, and the reference copy of a target that still holds a
181
+ // staged call, which pins the move()/copy() ghost artifacts
182
+ // (test/spec/func.tsv, ghost-*-innard-canon; ADR-025) — but a clone
183
+ // that is an INSTANCE must own the full inner structure: with the
184
+ // args shared, `pack($.names, close({name: key()}))` resolved key()
185
+ // once inside the one shared inner map and stamped the FIRST
186
+ // child's key on every child (use-cases/BUGS.md §8). The `dup` spec
187
+ // flag asks for that depth; everything else keeps the sharing it
188
+ // has always had.
169
189
  clone(ctx: AontuContext, spec?: ValSpec): Val {
170
190
  const out = super.clone(ctx, spec) as FuncBaseVal
171
191
  if (true === spec?.dup && Array.isArray(this.peg)) {
172
192
  // Every argument is a Val by construction (the parser builds
173
193
  // them; make() rebuilds from driven Vals), as the Go twin's
174
- // []Val typing states outright. The instantiation sites then
175
- // normalise every path in the clone (repathInstance), so the
176
- // argument-shaped parse paths never leak into an instance.
194
+ // []Val typing states outright. The generator and spread
195
+ // instantiation sites then normalise every path in the clone
196
+ // (repathInstance), so the argument-shaped parse paths never
197
+ // leak into an instance; the reference copy takes the paths
198
+ // this rebasing gives, which is what an absolute address in a
199
+ // copied model already expects (ADR-014).
177
200
  out.peg = this.peg.map((a: Val) => a.clone(ctx, { dup: true }))
178
201
  }
179
202
  return out
@@ -335,7 +335,15 @@ class ListVal extends BagVal {
335
335
  { mark: spec?.mark, dup: spec?.dup } : {}
336
336
  for (let entry of Object.entries(this.peg)) {
337
337
  out.peg[entry[0]] =
338
- (entry[1] as any)?.isVal ? (entry[1] as Val).clone(ctx, childspec) : entry[1]
338
+ (entry[1] as any)?.isVal ? (entry[1] as Val).clone(ctx, {
339
+ ...childspec,
340
+ // AN ELEMENT IS A POSITION, exactly as a map's child is
341
+ // (MapVal.clone). Without this an explicitly pathed clone
342
+ // rebased the list and left every element at its source
343
+ // path -- the Go twin descends by index (go/clone.go, the
344
+ // *ListVal arm of clonePathKind).
345
+ path: [...out.path, entry[0]],
346
+ }) : entry[1]
339
347
  }
340
348
  if (this.spread.cj) {
341
349
  out.spread.cj = this.spread.cj.clone(ctx, childspec)
package/src/val/MapVal.ts CHANGED
@@ -46,7 +46,7 @@ import { markSpread } from '../provenance'
46
46
  // pass. The map lives on the unify root ctx (see Unify), so it persists
47
47
  // across fixpoint passes and is GC'd with the run.
48
48
  function spreadSnapKey(cj: any): string {
49
- return cj.canon + '~' + cj.site.row + ':' + cj.site.col
49
+ return cj.spelling + '~' + cj.site.row + ':' + cj.site.col
50
50
  }
51
51
 
52
52
  // Snapshot a path-dependent ref spread to its structural target once,
@@ -605,5 +605,6 @@ class MapVal extends BagVal {
605
605
 
606
606
 
607
607
  export {
608
- MapVal
608
+ MapVal,
609
+ spreadSnapKey,
609
610
  }
@@ -44,35 +44,39 @@ import { MapVal } from './MapVal'
44
44
  import { FuncBaseVal } from './FuncBaseVal'
45
45
  import { repathInstance } from './Val'
46
46
  import { fillPlace } from './PlaceVal'
47
+ import { bagMembers } from './members'
47
48
 
48
49
 
49
50
  // The keys a data bag names, in the order the result must carry them,
50
51
  // or a code naming what is wrong with it. Shared with `each`, which
51
52
  // asks the same question of the same argument and answers it with the
52
53
  // values rather than the keys.
53
- function dataKeys(data: Val | undefined): string[] | string {
54
- const d: any = data
54
+ function dataKeys(data: Val | undefined, ctx: AontuContext): string[] | string {
55
+ // The candidates are the bag's MEMBERS -- what generation would
56
+ // emit (./members.ts, BUGS.md §79) -- so a hidden key, or a hidden
57
+ // name in a list of names, packs nothing.
58
+ const members = bagMembers(data, ctx)
59
+
60
+ if (undefined === members) {
61
+ return 'pack_data'
62
+ }
55
63
 
56
- if (true === d?.isMap) {
57
- return Object.keys(d.peg)
64
+ if (true === (data as any).isMap) {
65
+ return members.map((m) => m.key)
58
66
  }
59
67
 
60
- if (true === d?.isList) {
61
- const out: string[] = []
62
- for (const el of d.peg as Val[]) {
63
- const e: any = el
64
- // A key is a NAME, and only a string is one. A number would
65
- // key by position under another spelling, which is the failure
66
- // mode the data-keyed rule exists to refuse.
67
- if (true !== e?.isScalar || 'string' !== typeof e.peg) {
68
- return 'pack_key'
69
- }
70
- out.push(e.peg)
68
+ const out: string[] = []
69
+ for (const { val } of members) {
70
+ const e: any = val
71
+ // A key is a NAME, and only a string is one. A number would
72
+ // key by position under another spelling, which is the failure
73
+ // mode the data-keyed rule exists to refuse.
74
+ if (true !== e?.isScalar || 'string' !== typeof e.peg) {
75
+ return 'pack_key'
71
76
  }
72
- return out
77
+ out.push(e.peg)
73
78
  }
74
-
75
- return 'pack_data'
79
+ return out
76
80
  }
77
81
 
78
82
 
@@ -123,7 +127,7 @@ class PackFuncVal extends FuncBaseVal {
123
127
 
124
128
 
125
129
  resolve(ctx: AontuContext, args: Val[]) {
126
- const keys = dataKeys(args?.[0])
130
+ const keys = dataKeys(args?.[0], ctx)
127
131
  if ('string' === typeof keys) {
128
132
  return makeNilErr(ctx, keys, this)
129
133
  }
@@ -85,7 +85,8 @@ class PlaceVal extends ValBase {
85
85
  // so it stays visible: a hole there is an outer hole as before.
86
86
  function boundArgStart(v: any): number {
87
87
  return true === v.isPackFunc || true === v.isEachFunc ||
88
- true === v.isFilterFunc || true === v.isEmitFunc ? 1 : Infinity
88
+ true === v.isFilterFunc || true === v.isEmitFunc ||
89
+ true === v.isFormFunc ? 1 : Infinity
89
90
  }
90
91
 
91
92
 
@@ -132,7 +133,11 @@ function hasPlace(v: Val): boolean {
132
133
  // generator's to fill with its OWN source children when it fires.
133
134
  function fillPlace(v: Val, fill: Val, ctx: AontuContext): Val {
134
135
  if (true === (v as any).isPlace) {
135
- return fill
136
+ // A FILL IS A POSITION. The hole knows where it sits in the
137
+ // instance; the datum arriving in it does not, and inserted as it
138
+ // stands it keeps the paths it had at its SOURCE, so every finding
139
+ // under it names a path that does not exist.
140
+ return fill.clone(ctx, { path: [...(v as any).path] })
136
141
  }
137
142
 
138
143
  const peg: any = (v as any).peg
package/src/val/RefVal.ts CHANGED
@@ -73,12 +73,40 @@ function pendingMarkWrapper(v: any): boolean {
73
73
  }
74
74
 
75
75
 
76
+ // The child a term of the walk can supply for one path segment, or
77
+ // undefined when it has none. A map or a list answers from its own
78
+ // members; a PENDING type()/hide() answers from its argument's, because
79
+ // the wrapper only marks and its argument is the structure the path
80
+ // names (see the call sites in `find`).
81
+ function markedChild(v: any, part: any): Val | undefined {
82
+ if (true === v?.isMap || true === v?.isList) {
83
+ return v.peg[part]
84
+ }
85
+ if (true === v?.isFunc
86
+ && (true === v.isHideFunc || true === v.isTypeFunc)
87
+ && (true === v.peg?.[0]?.isMap || true === v.peg?.[0]?.isList)) {
88
+ return v.peg[0].peg[part]
89
+ }
90
+ return undefined
91
+ }
92
+
93
+
94
+ // An alias name, whole: the sigil and an identifier (the lexer's
95
+ // ALIAS_RE, anchored at both ends, for the canon spelling above).
96
+ const ALIAS_NAME_RE = /^%[A-Za-z_][A-Za-z0-9_]*$/
97
+
98
+
76
99
  class RefVal extends FeatureVal {
77
100
  isRef = true
78
101
  isGenable = true
79
102
  cjo = 32500
80
103
 
81
104
  absolute: boolean = false
105
+
106
+ // The value an alias reference canons as, attached by expandAliases
107
+ // after unification (see `canon` below). Not a ValSpec field: it is
108
+ // a rendering of the settled tree, never a parse-time property.
109
+ expansion: Val | undefined = undefined
82
110
  prefix: boolean = false
83
111
 
84
112
  constructor(
@@ -220,7 +248,8 @@ class RefVal extends FeatureVal {
220
248
  }
221
249
 
222
250
  // same path
223
- else if (this.canon === peer.canon) {
251
+ else if (this.spelling ===
252
+ (true === (peer as any).isRef ? (peer as any).spelling : peer.canon)) {
224
253
  out = this
225
254
  }
226
255
 
@@ -406,6 +435,45 @@ class RefVal extends FeatureVal {
406
435
  || true === (node as any).peg?.[0]?.isList)) {
407
436
  node = (node as any).peg[0].peg[part]
408
437
  }
438
+
439
+ // AND SO IS A CONJUNCT THAT STILL CARRIES ONE (issue #164).
440
+ // Two statements for one key meet, so a key written as
441
+ // `T: type({...})` twice is a CONJUNCT of two wrappers -- and
442
+ // one written once beside a plain `T: {...}` is a conjunct
443
+ // too. The arm above sees a wrapper only when it is the whole
444
+ // node, so a reference into such a key walked into the
445
+ // conjunct and stopped: the wrapper waited for its argument,
446
+ // the argument waited for the reference, and neither moved.
447
+ // The first referring child of every consumer stayed
448
+ // unresolved and generation reported mapval_no_gen at a path
449
+ // that names none of this, which is how a five-file schema
450
+ // spent its evening being bisected.
451
+ //
452
+ // The answer at a segment is the MEET of what each term
453
+ // supplies, so terms that have no such member are skipped and
454
+ // the rest are conjoined -- one term answers as itself, and
455
+ // the ordinary map arm answers once the fold has happened.
456
+ // Restricted to a conjunct that still holds a pending
457
+ // wrapper: every other conjunct folds on its own, and this
458
+ // walk exists only to break the wrapper's deadlock.
459
+ else if (true === (node as any).isConjunct
460
+ && Array.isArray((node as any).peg)
461
+ && pendingMarkWrapper(node)) {
462
+ const kids: Val[] = []
463
+ for (const term of (node as any).peg) {
464
+ const kid = markedChild(term, part)
465
+ if (undefined !== kid && null !== kid) {
466
+ kids.push(kid)
467
+ }
468
+ }
469
+ // No term has it YET. Not a miss: the conjunct is still
470
+ // folding, and the member may arrive with the fold.
471
+ if (0 === kids.length) {
472
+ break
473
+ }
474
+ node = 1 === kids.length ?
475
+ kids[0] : new ConjunctVal({ peg: kids }, ctx)
476
+ }
409
477
  else if (node.done) {
410
478
  nopath = true
411
479
  break;
@@ -464,6 +532,31 @@ class RefVal extends FeatureVal {
464
532
  else if (pI === refpath.length) {
465
533
  out = node
466
534
 
535
+ // THE READ IS RECORDED, AND THE VALUE STAMPED WITH WHERE IT
536
+ // WAS FOUND (RENDER.0.md P7). A resolved reference clones its
537
+ // target into the referring position, so without the stamp a
538
+ // value that arrived by reference knows only where it came to
539
+ // rest -- and `render --coverage` has nothing to measure the
540
+ // model against. Off unless the run is instrumented; the first
541
+ // address wins, and every reference to one node names the same
542
+ // address anyway.
543
+ if (undefined !== ctx.reads && null != node) {
544
+ // The root's own address is `$`, as the coverage walk spells
545
+ // it: a dot with nothing after it would match no path there.
546
+ const addr = '$' + refpath.map((seg: string) => '.' + seg).join('')
547
+ // AN ALIAS IS NOT A PATH. `%wire` names a value the document
548
+ // holds unevaluated and the tree never carries, so it is an
549
+ // address a rule can be reported AT and never a path coverage
550
+ // could call dead: it is stamped, and it is not in the set
551
+ // the model is measured against.
552
+ if (!refpath[0]?.startsWith('%')) {
553
+ ctx.reads.add(addr)
554
+ }
555
+ if (null == (node as any).origin) {
556
+ ; (node as any).origin = addr
557
+ }
558
+ }
559
+
467
560
  // A reference landing on another reference -- or on a FUNCTION,
468
561
  // whose arguments the chase now follows (issue #35) -- may be a
469
562
  // PROVEN mutual cycle (a: $.b, b: $.a; a: $.b, b: upper($.a)).
@@ -544,21 +637,62 @@ class RefVal extends FeatureVal {
544
637
 
545
638
  // console.log('FOUND-B', out)
546
639
 
547
- out = out.clone(ctx)
548
-
549
- // if (this.mark.type || this.mark.hide) {
550
- walk(out, (_key: string | number | undefined, val: Val) => {
551
- val.mark.type = false
552
- val.mark.hide = false
553
- // THE LINK IS NOT CLEARED (G4 phase 3): a link says what a
554
- // value POINTS AT, and a copy of a link points at the same
555
- // thing. An ABSOLUTE address still names the same node from
556
- // the copy; a RELATIVE one is read from the copy's own
557
- // position, which is what makes a referenced model resolve
558
- // its internal links inside the copy (ADR-014).
559
- return val
560
- })
561
- //}
640
+ // A REFERENCE LIFTS: the copy is concrete, its type and hide
641
+ // marks cleared to the leaves -- a marked target is what the
642
+ // author hid or typed, and a reference to it is the author
643
+ // asking for it in the open. EXCEPT the snapshot a staged
644
+ // verb takes of its data (`argsnap`: each, emit, filter,
645
+ // pack, pick, join, the aggregates), when the target itself
646
+ // is not marked: a member marked inside an unmarked bag was
647
+ // hidden IN ITS OWN RIGHT, and the verb's enumeration
648
+ // (ts/src/val/members.ts, BUGS.md §79) needs to see that
649
+ // mark to leave the member out, as generation does. A marked
650
+ // target lifts even there, or `each($.schema.entities, _)`
651
+ // under `schema: hide({...})` would see every entity as
652
+ // hidden, since hide() marks to the leaves.
653
+ const lifted = true !== (ctx as any).argsnap
654
+ || true === out.mark.type || true === out.mark.hide
655
+
656
+ // A REFERENCE'S COPY OWNS ITS ARGUMENTS (ADR-025). The copy
657
+ // is a per-destination instance exactly as a spread's or a
658
+ // generator's is, so ADR-005's rule holds here too: nothing
659
+ // path-dependent may be shared between two destinations, or
660
+ // the first destination's resolution answers for them all.
661
+ // The shallow clone shared a call's ARGUMENTS -- so
662
+ // `items: [&: $.entities.User]` over a `close({...})` target
663
+ // gave every element the one inner map, whose path each
664
+ // element rebased in turn, and the constraint that failed at
665
+ // element 2 reported element 0's path (use-cases GAP 8).
666
+ //
667
+ // A STAGED CALL IS THE EXCEPTION, because it has not decided
668
+ // yet. Its arguments are still being driven AT ITS OWN SITE
669
+ // (the staging rule, G8 phase 0), and a copy that owned them
670
+ // would drive its own set at the referring position instead:
671
+ // the relative `.side_effect` in a `match()` would read the
672
+ // referring field's siblings (use-cases/09-agent-tools), and
673
+ // an alias naming an `emit` rule table -- a template, which
674
+ // is exactly a value copied before it resolves -- would read
675
+ // its recursive `%w` as a self-reference. A staged call
676
+ // ANYWHERE in the target counts: what is referenced is
677
+ // usually the conjunct the call sits in, not the call.
678
+ // The copy shares what the source is still settling, and
679
+ // owns the rest.
680
+ out = out.clone(ctx, { dup: !out.holdsStaged })
681
+
682
+ if (lifted) {
683
+ walk(out, (_key: string | number | undefined, val: Val) => {
684
+ val.mark.type = false
685
+ val.mark.hide = false
686
+ // THE LINK IS NOT CLEARED (G4 phase 3): a link says what
687
+ // a value POINTS AT, and a copy of a link points at the
688
+ // same thing. An ABSOLUTE address still names the same
689
+ // node from the copy; a RELATIVE one is read from the
690
+ // copy's own position, which is what makes a referenced
691
+ // model resolve its internal links inside the copy
692
+ // (ADR-014).
693
+ return val
694
+ })
695
+ }
562
696
 
563
697
  // onsole.log('FOUND-C', out)
564
698
  }
@@ -694,19 +828,58 @@ class RefVal extends FeatureVal {
694
828
  absolute: this.absolute,
695
829
  ...(spec || {})
696
830
  }) as RefVal)
831
+ out.expansion = this.expansion
697
832
  return out
698
833
  }
699
834
 
700
835
 
701
- get canon() {
702
- let str =
703
- (this.absolute ? '$' : '') +
836
+ // THE NAME OF THE ALIAS THIS REFERENCE NAMES, or undefined for a
837
+ // path reference. `%u` is spelled internally as the root reference
838
+ // `$.%u` (docs/design/ALIASES.0.md: the name is a path into the
839
+ // declaration), so an alias reference is an absolute reference of
840
+ // one segment that is an alias name.
841
+ get aliasName(): string | undefined {
842
+ return this.absolute && 1 === this.peg.length &&
843
+ 'string' === typeof this.peg[0] && ALIAS_NAME_RE.test(this.peg[0]) ?
844
+ this.peg[0] : undefined
845
+ }
846
+
847
+
848
+ // THE REFERENCE'S OWN SPELLING: the alias name, or the path. This is
849
+ // the reference's identity (the snapshot key of a ref spread, the
850
+ // same-path test in unify), which `canon` below is not once an
851
+ // expansion is attached.
852
+ get spelling(): string {
853
+ const name = this.aliasName
854
+ if (undefined !== name) {
855
+ return name
856
+ }
857
+ return (this.absolute ? '$' : '') +
704
858
  (0 < this.peg.length ? '.' : '') +
705
859
  // this.peg.join(this.sep)
706
860
  this.peg.map((p: any) => '.' === p ? '' :
707
861
  (p.isVal ? p.canon : '' + p))
708
862
  .join('.')
709
- return str
863
+ }
864
+
865
+
866
+ get canon() {
867
+ // AN ALIAS REFERENCE CANONS AS THE VALUE IT NAMES. A reference left
868
+ // standing after unification is one inside a spread template
869
+ // (`[&: %u]`, `{&: {a: %u}}`): the template applies to children
870
+ // that have not arrived, so it is not resolved in place. Canon
871
+ // erases the declaration (an alias is a name for a value, and
872
+ // nothing more -- ALIASES.0.md §4), so the name alone would not
873
+ // reparse, and the hash of `t: {&: %u}` would differ from the hash
874
+ // of `t: {&: integer}`, which is the same document. The expansion
875
+ // is attached by expandAliases (ts/src/alias.ts) once the tree has
876
+ // settled; without one -- a parse-only tree, an unresolved name, or
877
+ // the KNOT of a recursive alias inside its own template -- the
878
+ // reference spells its name. Twin of RefVal.Canon in go/ref.go.
879
+ if (undefined !== this.expansion) {
880
+ return this.expansion.canon
881
+ }
882
+ return this.spelling
710
883
  }
711
884
 
712
885