aontu 0.56.0 → 0.58.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.
- package/README.md +2 -2
- package/dist/agentsmd.js +1 -1
- package/dist/alias.d.ts +3 -0
- package/dist/alias.js +59 -0
- package/dist/alias.js.map +1 -0
- package/dist/aontu.d.ts +5 -2
- package/dist/aontu.js +11 -3
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +8 -2
- package/dist/cli.js +564 -21
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +2 -0
- package/dist/ctx.js +1 -0
- package/dist/ctx.js.map +1 -1
- package/dist/escape.d.ts +5 -0
- package/dist/escape.js +455 -0
- package/dist/escape.js.map +1 -0
- package/dist/format.d.ts +9 -0
- package/dist/format.js +550 -55
- package/dist/format.js.map +1 -1
- package/dist/hints.js +74 -9
- package/dist/hints.js.map +1 -1
- package/dist/lang.js +374 -58
- package/dist/lang.js.map +1 -1
- package/dist/lower.d.ts +20 -0
- package/dist/lower.js +575 -0
- package/dist/lower.js.map +1 -0
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +4 -4
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.js +2 -2
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +40 -3
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +8 -7
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.js +6 -6
- package/dist/mod.js.map +1 -1
- package/dist/render.d.ts +53 -0
- package/dist/render.js +542 -0
- package/dist/render.js.map +1 -0
- package/dist/sigdecl.js +1 -1
- package/dist/sigdecl.js.map +1 -1
- package/dist/std.d.ts +2 -0
- package/dist/std.js +498 -2
- package/dist/std.js.map +1 -1
- package/dist/template.d.ts +5 -0
- package/dist/template.js +257 -0
- package/dist/template.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.js +43 -0
- package/dist/unify.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +1 -1
- package/dist/val/AggFuncVal.js +10 -21
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/BagVal.js +1 -1
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +1 -1
- package/dist/val/EachFuncVal.d.ts +1 -1
- package/dist/val/EachFuncVal.js +9 -15
- package/dist/val/EachFuncVal.js.map +1 -1
- package/dist/val/EmitFuncVal.d.ts +42 -0
- package/dist/val/EmitFuncVal.js +531 -0
- package/dist/val/EmitFuncVal.js.map +1 -0
- package/dist/val/FilterFuncVal.js +9 -6
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FormFuncVal.d.ts +14 -0
- package/dist/val/FormFuncVal.js +55 -0
- package/dist/val/FormFuncVal.js.map +1 -0
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +16 -0
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +2 -1
- package/dist/val/MapVal.js +2 -1
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/PackFuncVal.d.ts +1 -1
- package/dist/val/PackFuncVal.js +23 -20
- package/dist/val/PackFuncVal.js.map +1 -1
- package/dist/val/PlaceVal.d.ts +3 -1
- package/dist/val/PlaceVal.js +9 -6
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/RefVal.d.ts +3 -0
- package/dist/val/RefVal.js +156 -17
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/StrFuncVal.d.ts +32 -0
- package/dist/val/StrFuncVal.js +292 -0
- package/dist/val/StrFuncVal.js.map +1 -0
- package/dist/val/Val.d.ts +7 -1
- package/dist/val/Val.js +12 -1
- package/dist/val/Val.js.map +1 -1
- package/dist/val/members.d.ts +9 -0
- package/dist/val/members.js +52 -0
- package/dist/val/members.js.map +1 -0
- package/grammar/aontu.abnf +4 -3
- package/grammar/aontu.gbnf +4 -3
- package/grammar/aontu.lark +4 -3
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +4 -4
- package/skill/error-codes.md +1 -1
- package/skill/examples.md +1 -1
- package/skill/grammar-card.md +1 -1
- package/src/agentsmd.ts +1 -1
- package/src/alias.ts +112 -0
- package/src/aontu.ts +20 -2
- package/src/cli.ts +630 -23
- package/src/ctx.ts +13 -0
- package/src/escape.ts +371 -0
- package/src/format.ts +648 -56
- package/src/hints.ts +94 -9
- package/src/lang.ts +430 -63
- package/src/lower.ts +636 -0
- package/src/lsp.ts +4 -4
- package/src/mcp-server.ts +3 -2
- package/src/mcp.ts +43 -4
- package/src/mod-tool.ts +9 -8
- package/src/mod.ts +6 -6
- package/src/render.ts +727 -0
- package/src/sigdecl.ts +1 -1
- package/src/std.ts +506 -1
- package/src/template.ts +291 -0
- package/src/unify.ts +47 -0
- package/src/val/AggFuncVal.ts +10 -21
- package/src/val/BagVal.ts +1 -1
- package/src/val/ConstraintVal.ts +1 -1
- package/src/val/EachFuncVal.ts +9 -19
- package/src/val/EmitFuncVal.ts +738 -0
- package/src/val/FilterFuncVal.ts +12 -7
- package/src/val/FormFuncVal.ts +119 -0
- package/src/val/FuncBaseVal.ts +18 -0
- package/src/val/MapVal.ts +3 -2
- package/src/val/PackFuncVal.ts +24 -22
- package/src/val/PlaceVal.ts +9 -6
- package/src/val/RefVal.ts +167 -18
- package/src/val/StrFuncVal.ts +334 -0
- package/src/val/Val.ts +42 -1
- package/src/val/members.ts +86 -0
package/src/val/FilterFuncVal.ts
CHANGED
|
@@ -46,6 +46,10 @@ import { ListVal } from './ListVal'
|
|
|
46
46
|
import { FuncBaseVal, trialUnify } from './FuncBaseVal'
|
|
47
47
|
import { repathInstance } from './Val'
|
|
48
48
|
import { fillPlace } from './PlaceVal'
|
|
49
|
+
import { bagMembers } from './members'
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
type Member = { key: string, val: Val }
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
class FilterFuncVal extends FuncBaseVal {
|
|
@@ -83,9 +87,7 @@ class FilterFuncVal extends FuncBaseVal {
|
|
|
83
87
|
|
|
84
88
|
|
|
85
89
|
unify(peer: Val, ctx: AontuContext): Val {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (!ready || !ctx.settle) {
|
|
90
|
+
if (!this.stagedReady(peer, ctx, 1)) {
|
|
89
91
|
return this.residuate(peer, ctx)
|
|
90
92
|
}
|
|
91
93
|
|
|
@@ -117,12 +119,15 @@ class FilterFuncVal extends FuncBaseVal {
|
|
|
117
119
|
return undefined !== met && met.canon === child.canon
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
// The candidates are the bag's MEMBERS -- what generation would
|
|
123
|
+
// emit (./members.ts, BUGS.md §79) -- so a hidden child is never
|
|
124
|
+
// selected into the result.
|
|
120
125
|
if (true === data?.isMap) {
|
|
121
126
|
const peg: Record<string, Val> = {}
|
|
122
|
-
for (const key of
|
|
127
|
+
for (const { key, val } of bagMembers(data, ctx) as Member[]) {
|
|
123
128
|
const kctx = ctx.descend(key)
|
|
124
|
-
if (keeps(
|
|
125
|
-
peg[key] =
|
|
129
|
+
if (keeps(val, kctx)) {
|
|
130
|
+
peg[key] = val.clone(kctx)
|
|
126
131
|
}
|
|
127
132
|
}
|
|
128
133
|
return new MapVal({ peg }, ctx)
|
|
@@ -130,7 +135,7 @@ class FilterFuncVal extends FuncBaseVal {
|
|
|
130
135
|
|
|
131
136
|
if (true === data?.isList) {
|
|
132
137
|
const peg: Val[] = []
|
|
133
|
-
for (const el of data
|
|
138
|
+
for (const { val: el } of bagMembers(data, ctx) as Member[]) {
|
|
134
139
|
// The element context is the position it will END UP at, which
|
|
135
140
|
// is its index in the RESULT: dropping the third of five moves
|
|
136
141
|
// the fourth up, and a kept element must be pathed where it
|
|
@@ -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
|
+
}
|
package/src/val/FuncBaseVal.ts
CHANGED
|
@@ -139,6 +139,24 @@ class FuncBaseVal extends FeatureVal {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
|
|
142
|
+
// THE STAGED READINESS GATE, stated once for the generators that
|
|
143
|
+
// drive their own data argument. Such a func fires when the model
|
|
144
|
+
// has settled AND that argument is done -- EXCEPT when the argument
|
|
145
|
+
// is a HOLE and a PEER has arrived to fill it. `_` is never done: it
|
|
146
|
+
// is FILLED, and the peer is what fills it, so gating on doneness
|
|
147
|
+
// alone held every placeheld generator residual for ever and the
|
|
148
|
+
// fill in `unify` below was never reached (`["a"] & pack(_, {x:1})`
|
|
149
|
+
// was `*_no_gen`, while the unstaged `"hello" & upper(_)` filled as
|
|
150
|
+
// documented -- an undriven `_` makes an ordinary call's `pegdone`
|
|
151
|
+
// false, and a generator's prepare() forces it true). Against TOP
|
|
152
|
+
// there is nothing to fill with, so a placeheld generator waits
|
|
153
|
+
// exactly as the hole itself does.
|
|
154
|
+
stagedReady(peer: Val, ctx: AontuContext, count: number): boolean {
|
|
155
|
+
const ready = this.driveStagedArgs(ctx, count)
|
|
156
|
+
return (ready || (!peer.isTop && hasPlace(this))) && true === ctx.settle
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
142
160
|
// THE PER-DESTINATION INSTANTIATION RULE (ADR-005). The default
|
|
143
161
|
// clone shares the argument array AND the argument Vals — pinned
|
|
144
162
|
// sharing for the move()/copy() ghost artifacts (test/spec/func.tsv,
|
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.
|
|
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
|
}
|
package/src/val/PackFuncVal.ts
CHANGED
|
@@ -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
|
-
|
|
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 ===
|
|
57
|
-
return
|
|
64
|
+
if (true === (data as any).isMap) {
|
|
65
|
+
return members.map((m) => m.key)
|
|
58
66
|
}
|
|
59
67
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
77
|
+
out.push(e.peg)
|
|
73
78
|
}
|
|
74
|
-
|
|
75
|
-
return 'pack_data'
|
|
79
|
+
return out
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
|
|
@@ -114,9 +118,7 @@ class PackFuncVal extends FuncBaseVal {
|
|
|
114
118
|
// ONE argument is driven: the data. The template is not (see
|
|
115
119
|
// prepare above), and driveStagedArgs answers whether the data has
|
|
116
120
|
// settled -- the other half of "ready to fire".
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (!ready || !ctx.settle) {
|
|
121
|
+
if (!this.stagedReady(peer, ctx, 1)) {
|
|
120
122
|
return this.residuate(peer, ctx)
|
|
121
123
|
}
|
|
122
124
|
|
|
@@ -125,7 +127,7 @@ class PackFuncVal extends FuncBaseVal {
|
|
|
125
127
|
|
|
126
128
|
|
|
127
129
|
resolve(ctx: AontuContext, args: Val[]) {
|
|
128
|
-
const keys = dataKeys(args?.[0])
|
|
130
|
+
const keys = dataKeys(args?.[0], ctx)
|
|
129
131
|
if ('string' === typeof keys) {
|
|
130
132
|
return makeNilErr(ctx, keys, this)
|
|
131
133
|
}
|
package/src/val/PlaceVal.ts
CHANGED
|
@@ -73,10 +73,10 @@ class PlaceVal extends ValBase {
|
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
// A HOLE BELONGS TO ITS NEAREST ENCLOSING GENERATOR. A `_` inside a
|
|
76
|
-
// generator's template (pack/each, arg 1)
|
|
77
|
-
// is that generator's to bind — "_ is the
|
|
78
|
-
// the hole test nor the fill walk may cross
|
|
79
|
-
// outside. Before this boundary, `close(pack(d, _ & t))` reported a
|
|
76
|
+
// generator's template (pack/each, arg 1), condition (filter, arg 1)
|
|
77
|
+
// or rule table (emit, arg 1) is that generator's to bind — "_ is the
|
|
78
|
+
// source child" — so neither the hole test nor the fill walk may cross
|
|
79
|
+
// into those arguments from outside. Before this boundary, `close(pack(d, _ & t))` reported a
|
|
80
80
|
// hole to the OUTER call, so an ordinary overlay statement was
|
|
81
81
|
// absorbed into the template instead of merging with the generated
|
|
82
82
|
// child (use-cases/BUGS.md §10), and an outer pack's fill pass
|
|
@@ -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
|
|
88
|
+
true === v.isFilterFunc || true === v.isEmitFunc ||
|
|
89
|
+
true === v.isFormFunc ? 1 : Infinity
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
|
|
@@ -183,10 +184,12 @@ function rebuild(v: Val, peg: any, ctx: AontuContext): Val {
|
|
|
183
184
|
out.peg = peg
|
|
184
185
|
out.dc = 0
|
|
185
186
|
return out
|
|
186
|
-
} /* node:coverage ignore next
|
|
187
|
+
} /* node:coverage ignore next 10 */
|
|
187
188
|
|
|
188
189
|
|
|
189
190
|
export {
|
|
191
|
+
boundArgStart,
|
|
192
|
+
rebuild,
|
|
190
193
|
hasPlace,
|
|
191
194
|
fillPlace,
|
|
192
195
|
PlaceVal,
|
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.
|
|
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,38 @@ class RefVal extends FeatureVal {
|
|
|
544
637
|
|
|
545
638
|
// console.log('FOUND-B', out)
|
|
546
639
|
|
|
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
|
+
|
|
547
656
|
out = out.clone(ctx)
|
|
548
657
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
658
|
+
if (lifted) {
|
|
659
|
+
walk(out, (_key: string | number | undefined, val: Val) => {
|
|
660
|
+
val.mark.type = false
|
|
661
|
+
val.mark.hide = false
|
|
662
|
+
// THE LINK IS NOT CLEARED (G4 phase 3): a link says what
|
|
663
|
+
// a value POINTS AT, and a copy of a link points at the
|
|
664
|
+
// same thing. An ABSOLUTE address still names the same
|
|
665
|
+
// node from the copy; a RELATIVE one is read from the
|
|
666
|
+
// copy's own position, which is what makes a referenced
|
|
667
|
+
// model resolve its internal links inside the copy
|
|
668
|
+
// (ADR-014).
|
|
669
|
+
return val
|
|
670
|
+
})
|
|
671
|
+
}
|
|
562
672
|
|
|
563
673
|
// onsole.log('FOUND-C', out)
|
|
564
674
|
}
|
|
@@ -694,19 +804,58 @@ class RefVal extends FeatureVal {
|
|
|
694
804
|
absolute: this.absolute,
|
|
695
805
|
...(spec || {})
|
|
696
806
|
}) as RefVal)
|
|
807
|
+
out.expansion = this.expansion
|
|
697
808
|
return out
|
|
698
809
|
}
|
|
699
810
|
|
|
700
811
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
812
|
+
// THE NAME OF THE ALIAS THIS REFERENCE NAMES, or undefined for a
|
|
813
|
+
// path reference. `%u` is spelled internally as the root reference
|
|
814
|
+
// `$.%u` (docs/design/ALIASES.0.md: the name is a path into the
|
|
815
|
+
// declaration), so an alias reference is an absolute reference of
|
|
816
|
+
// one segment that is an alias name.
|
|
817
|
+
get aliasName(): string | undefined {
|
|
818
|
+
return this.absolute && 1 === this.peg.length &&
|
|
819
|
+
'string' === typeof this.peg[0] && ALIAS_NAME_RE.test(this.peg[0]) ?
|
|
820
|
+
this.peg[0] : undefined
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
// THE REFERENCE'S OWN SPELLING: the alias name, or the path. This is
|
|
825
|
+
// the reference's identity (the snapshot key of a ref spread, the
|
|
826
|
+
// same-path test in unify), which `canon` below is not once an
|
|
827
|
+
// expansion is attached.
|
|
828
|
+
get spelling(): string {
|
|
829
|
+
const name = this.aliasName
|
|
830
|
+
if (undefined !== name) {
|
|
831
|
+
return name
|
|
832
|
+
}
|
|
833
|
+
return (this.absolute ? '$' : '') +
|
|
704
834
|
(0 < this.peg.length ? '.' : '') +
|
|
705
835
|
// this.peg.join(this.sep)
|
|
706
836
|
this.peg.map((p: any) => '.' === p ? '' :
|
|
707
837
|
(p.isVal ? p.canon : '' + p))
|
|
708
838
|
.join('.')
|
|
709
|
-
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
get canon() {
|
|
843
|
+
// AN ALIAS REFERENCE CANONS AS THE VALUE IT NAMES. A reference left
|
|
844
|
+
// standing after unification is one inside a spread template
|
|
845
|
+
// (`[&: %u]`, `{&: {a: %u}}`): the template applies to children
|
|
846
|
+
// that have not arrived, so it is not resolved in place. Canon
|
|
847
|
+
// erases the declaration (an alias is a name for a value, and
|
|
848
|
+
// nothing more -- ALIASES.0.md §4), so the name alone would not
|
|
849
|
+
// reparse, and the hash of `t: {&: %u}` would differ from the hash
|
|
850
|
+
// of `t: {&: integer}`, which is the same document. The expansion
|
|
851
|
+
// is attached by expandAliases (ts/src/alias.ts) once the tree has
|
|
852
|
+
// settled; without one -- a parse-only tree, an unresolved name, or
|
|
853
|
+
// the KNOT of a recursive alias inside its own template -- the
|
|
854
|
+
// reference spells its name. Twin of RefVal.Canon in go/ref.go.
|
|
855
|
+
if (undefined !== this.expansion) {
|
|
856
|
+
return this.expansion.canon
|
|
857
|
+
}
|
|
858
|
+
return this.spelling
|
|
710
859
|
}
|
|
711
860
|
|
|
712
861
|
|