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.
- package/dist/aontu.d.ts +3 -2
- package/dist/aontu.js +36 -7
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +500 -8
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +5 -0
- package/dist/ctx.js +1 -0
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js.map +1 -1
- package/dist/err.js +7 -1
- package/dist/err.js.map +1 -1
- package/dist/graph.d.ts +2 -5
- package/dist/graph.js +83 -46
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +9 -10
- package/dist/hcanon.js.map +1 -1
- package/dist/hints.js +94 -26
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +34 -0
- package/dist/jsonschema.js.map +1 -1
- package/dist/lang.js +593 -191
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +86 -6
- package/dist/lsp.js.map +1 -1
- package/dist/mcp.js +153 -6
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +42 -8
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.d.ts +4 -0
- package/dist/mod.js +97 -2
- package/dist/mod.js.map +1 -1
- package/dist/patch.d.ts +5 -0
- package/dist/patch.js +25 -25
- package/dist/patch.js.map +1 -1
- package/dist/provenance.d.ts +1 -0
- package/dist/provenance.js +2 -1
- package/dist/provenance.js.map +1 -1
- package/dist/query.js.map +1 -1
- package/dist/reach.d.ts +1 -0
- package/dist/reach.js +49 -21
- package/dist/reach.js.map +1 -1
- package/dist/relation.d.ts +4 -0
- package/dist/relation.js +125 -200
- package/dist/relation.js.map +1 -1
- package/dist/sig.d.ts +25 -0
- package/dist/sig.js +277 -0
- package/dist/sig.js.map +1 -0
- package/dist/sigdecl.d.ts +2 -0
- package/dist/sigdecl.js +11 -0
- package/dist/sigdecl.js.map +1 -0
- package/dist/siggate.d.ts +4 -0
- package/dist/siggate.js +90 -0
- package/dist/siggate.js.map +1 -0
- package/dist/std.js +75 -16
- package/dist/std.js.map +1 -1
- package/dist/subsume.js +57 -10
- package/dist/subsume.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.d.ts +2 -2
- package/dist/unify.js +93 -114
- package/dist/unify.js.map +1 -1
- package/dist/utility.d.ts +1 -2
- package/dist/utility.js +7 -61
- package/dist/utility.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +12 -1
- package/dist/val/AggFuncVal.js +165 -3
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/BagVal.d.ts +2 -0
- package/dist/val/BagVal.js +56 -8
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +33 -5
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.d.ts +35 -0
- package/dist/val/ContainerKindVal.js +99 -0
- package/dist/val/ContainerKindVal.js.map +1 -0
- package/dist/val/CopyFuncVal.js +0 -7
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.d.ts +1 -2
- package/dist/val/DisjunctVal.js +146 -39
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +37 -2
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +19 -8
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.d.ts +39 -0
- package/dist/val/GraphAtomVal.js +184 -0
- package/dist/val/GraphAtomVal.js.map +1 -0
- package/dist/val/JunctionVal.js +22 -5
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/ListVal.js +27 -34
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +1 -0
- package/dist/val/MapVal.js +82 -35
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/PathFuncVal.d.ts +2 -2
- package/dist/val/PathFuncVal.js +75 -16
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.d.ts +25 -0
- package/dist/val/PathVal.js +150 -0
- package/dist/val/PathVal.js.map +1 -0
- package/dist/val/PlusOpVal.d.ts +2 -1
- package/dist/val/PlusOpVal.js +50 -35
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +2 -0
- package/dist/val/PrefVal.js +159 -32
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.d.ts +19 -0
- package/dist/val/RecurseVal.js +217 -0
- package/dist/val/RecurseVal.js.map +1 -0
- package/dist/val/RefVal.d.ts +2 -1
- package/dist/val/RefVal.js +105 -72
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.d.ts +30 -7
- package/dist/val/ReferFuncVal.js +395 -94
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.d.ts +4 -2
- package/dist/val/ScalarKindVal.js +12 -1
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/SuperFuncVal.d.ts +4 -2
- package/dist/val/SuperFuncVal.js +118 -14
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.d.ts +1 -1
- package/dist/val/Val.d.ts +2 -3
- package/dist/val/Val.js +39 -20
- package/dist/val/Val.js.map +1 -1
- package/dist/val/arith.js +4 -1
- package/dist/val/arith.js.map +1 -1
- package/dist/vet.d.ts +1 -0
- package/dist/vet.js +32 -1
- package/dist/vet.js.map +1 -1
- package/dist/view.d.ts +90 -0
- package/dist/view.js +2168 -0
- package/dist/view.js.map +1 -0
- package/grammar/aontu.gbnf +18 -10
- package/grammar/aontu.lark +15 -10
- package/grammar/aontu.tmLanguage.json +184 -0
- package/package.json +10 -3
- package/skill/grammar-card.md +1 -2
- package/src/aontu.ts +37 -7
- package/src/cli.ts +553 -8
- package/src/ctx.ts +20 -0
- package/src/diff.ts +4 -2
- package/src/err.ts +8 -1
- package/src/graph.ts +125 -75
- package/src/hcanon.ts +9 -11
- package/src/hints.ts +112 -29
- package/src/jsonschema.ts +41 -0
- package/src/lang.ts +642 -203
- package/src/lsp.ts +75 -6
- package/src/mcp.ts +164 -6
- package/src/mod-tool.ts +48 -9
- package/src/mod.ts +110 -1
- package/src/patch.ts +31 -27
- package/src/provenance.ts +8 -1
- package/src/query.ts +4 -2
- package/src/reach.ts +52 -23
- package/src/relation.ts +139 -236
- package/src/sig.ts +345 -0
- package/src/sigdecl.ts +11 -0
- package/src/siggate.ts +144 -0
- package/src/std.ts +77 -16
- package/src/subsume.ts +59 -10
- package/src/unify.ts +102 -125
- package/src/utility.ts +7 -67
- package/src/val/AggFuncVal.ts +231 -4
- package/src/val/BagVal.ts +58 -9
- package/src/val/ConstraintVal.ts +34 -5
- package/src/val/ContainerKindVal.ts +158 -0
- package/src/val/CopyFuncVal.ts +0 -7
- package/src/val/DisjunctVal.ts +152 -40
- package/src/val/ExpectVal.ts +39 -4
- package/src/val/FuncBaseVal.ts +21 -8
- package/src/val/GraphAtomVal.ts +264 -0
- package/src/val/JunctionVal.ts +23 -6
- package/src/val/ListVal.ts +30 -37
- package/src/val/MapVal.ts +87 -39
- package/src/val/PathFuncVal.ts +107 -19
- package/src/val/PathVal.ts +221 -0
- package/src/val/PlusOpVal.ts +56 -37
- package/src/val/PrefVal.ts +186 -38
- package/src/val/RecurseVal.ts +285 -0
- package/src/val/RefVal.ts +105 -83
- package/src/val/ReferFuncVal.ts +445 -100
- package/src/val/ScalarKindVal.ts +12 -0
- package/src/val/SuperFuncVal.ts +137 -13
- package/src/val/TopVal.ts +1 -1
- package/src/val/Val.ts +44 -35
- package/src/val/arith.ts +4 -1
- package/src/vet.ts +41 -4
- package/src/view.ts +2882 -0
- package/dist/val/IdFuncVal.d.ts +0 -13
- package/dist/val/IdFuncVal.js +0 -54
- package/dist/val/IdFuncVal.js.map +0 -1
- package/src/val/IdFuncVal.ts +0 -91
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
// THE RECURSIVE RESIDUAL (docs/design/RECURSION.0.md): where RefVal's
|
|
4
|
+
// prefix test used to answer `path_cycle` for a self-reference --
|
|
5
|
+
// `$.Node` written anywhere inside `Node` -- it now answers this
|
|
6
|
+
// value: a deferred reference carrying the target path, exactly as a
|
|
7
|
+
// constraint atom carries its bound. No new syntax: the reference the
|
|
8
|
+
// author wrote simply MEANS the fixpoint.
|
|
9
|
+
//
|
|
10
|
+
// The three moments of every residual:
|
|
11
|
+
//
|
|
12
|
+
// - At unification, EXPAND ONE LEVEL PER MEET WITH STRUCTURE: met by
|
|
13
|
+
// a map, list, or scalar, the residual clones the schema body one
|
|
14
|
+
// level -- per destination, under ADR-005's clone discipline -- and
|
|
15
|
+
// unifies the clone with the peer. The clone's own self-reference
|
|
16
|
+
// is again this residual, so each expansion consumes one level of
|
|
17
|
+
// concrete data; data is finite, so expansion terminates
|
|
18
|
+
// (structural descent), with the depth budget as the backstop
|
|
19
|
+
// (recursion_budget).
|
|
20
|
+
// - In canon and the aon1- hash, SYMBOLIC: the residual prints as the
|
|
21
|
+
// reference the author wrote, so a recursive schema's canon is
|
|
22
|
+
// finite and round-trips, and the hash pins the mu-form rather than
|
|
23
|
+
// any unrolling.
|
|
24
|
+
// - At generation, an unexpanded residual REFUSES with
|
|
25
|
+
// recursion_unexpanded. Guardedness is therefore EMERGENT: under
|
|
26
|
+
// `next?:` the refusal is isolated and the optional key drops;
|
|
27
|
+
// under `*null | $.Node` the preference generates; a REQUIRED
|
|
28
|
+
// recursive field with no data refuses at the instance.
|
|
29
|
+
|
|
30
|
+
import type {
|
|
31
|
+
Val,
|
|
32
|
+
ValSpec,
|
|
33
|
+
} from '../type'
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
DONE,
|
|
37
|
+
} from '../type'
|
|
38
|
+
|
|
39
|
+
import {
|
|
40
|
+
AontuContext,
|
|
41
|
+
} from '../ctx'
|
|
42
|
+
|
|
43
|
+
import { makeNilErr } from '../err'
|
|
44
|
+
|
|
45
|
+
import { FeatureVal } from './FeatureVal'
|
|
46
|
+
import { ConjunctVal } from './ConjunctVal'
|
|
47
|
+
import { unite } from '../unify'
|
|
48
|
+
import { propagateMarks, walk } from '../utility'
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class RecurseVal extends FeatureVal {
|
|
52
|
+
isRecurse = true
|
|
53
|
+
isGenable = true
|
|
54
|
+
// LAST in a conjunct fold, after even the graph atoms: the residual
|
|
55
|
+
// wants to see the assembled concrete structure it expands against.
|
|
56
|
+
cjo = 47000
|
|
57
|
+
|
|
58
|
+
// The target path, absolute from the root, as the reference spelled
|
|
59
|
+
// it.
|
|
60
|
+
target: string[]
|
|
61
|
+
// Expansion depth so far along this chain, charged against the
|
|
62
|
+
// depth budget (the T-1 backstop).
|
|
63
|
+
xc: number
|
|
64
|
+
|
|
65
|
+
constructor(spec: ValSpec, ctx?: AontuContext) {
|
|
66
|
+
super(spec, ctx)
|
|
67
|
+
this.target = (spec as any).target ?? []
|
|
68
|
+
this.xc = (spec as any).xc ?? 0
|
|
69
|
+
// A settled residual: a type() body carrying one must settle, and
|
|
70
|
+
// an unmet recursion is its own value until data arrives.
|
|
71
|
+
this.dc = DONE
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
clone(ctx: AontuContext, spec?: ValSpec): Val {
|
|
75
|
+
const out: any = super.clone(ctx, spec)
|
|
76
|
+
out.target = this.target
|
|
77
|
+
out.xc = this.xc
|
|
78
|
+
return out
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// The schema body the target names, from the ROOT: the fixpoint is
|
|
82
|
+
// over the finished definition, and the definition's own residual
|
|
83
|
+
// keeps it finite. When the root does not contain the target -- the
|
|
84
|
+
// residual was LIFTED out of its defining tree, as vet's anchored
|
|
85
|
+
// meet does -- fall back to ctx._fixroot, the settled tree the
|
|
86
|
+
// lifter kept for exactly this walk.
|
|
87
|
+
private body(ctx: AontuContext): Val | undefined {
|
|
88
|
+
return walkTarget(ctx.root, this.target)
|
|
89
|
+
?? walkTarget((ctx as any)._fixroot, this.target)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
unify(peer: Val, ctx: AontuContext): Val {
|
|
93
|
+
const p: any = peer
|
|
94
|
+
|
|
95
|
+
// The self-drive: nothing to advance -- the residual waits for
|
|
96
|
+
// structure. (A null/nil peer never arrives; unite's ladder
|
|
97
|
+
// absorbs both.)
|
|
98
|
+
if (null == peer || true === p.isTop) {
|
|
99
|
+
return this
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// The same fixpoint twice is one fixpoint.
|
|
103
|
+
if (true === p.isRecurse) {
|
|
104
|
+
if (this.target.length === p.target.length
|
|
105
|
+
&& this.target.every((s, i) => s === p.target[i])) {
|
|
106
|
+
return this
|
|
107
|
+
}
|
|
108
|
+
// Different targets -- mutual recursion meeting -- are BOTH
|
|
109
|
+
// held: each expands as data arrives, through the fold that
|
|
110
|
+
// keeps a conjunct's members separate.
|
|
111
|
+
const out = new ConjunctVal({ peg: [this, peer] }, ctx)
|
|
112
|
+
propagateMarks(this, out)
|
|
113
|
+
out.path = this.path
|
|
114
|
+
return out
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// CONCRETE STRUCTURE: expand one level against it.
|
|
118
|
+
if (true === p.isMap || true === p.isList || true === p.isScalar) {
|
|
119
|
+
if (ctx.budget.depth <= this.xc) {
|
|
120
|
+
return makeNilErr(ctx, 'recursion_budget', this, peer, 'recurse',
|
|
121
|
+
{ target: '$.' + this.target.join('.') })
|
|
122
|
+
}
|
|
123
|
+
const body = this.body(ctx)
|
|
124
|
+
if (undefined === body) {
|
|
125
|
+
// The definition has not assembled yet (an early pass): hold
|
|
126
|
+
// the peer beside the residual and try again when it has.
|
|
127
|
+
const out = new ConjunctVal({ peg: [this, peer] }, ctx)
|
|
128
|
+
propagateMarks(this, out)
|
|
129
|
+
out.path = this.path
|
|
130
|
+
return out
|
|
131
|
+
}
|
|
132
|
+
// ADR-005's clone discipline: the expansion is a per-destination
|
|
133
|
+
// instantiation, so the definition itself is never written
|
|
134
|
+
// into. The clone's type/hide marks are CLEARED at every depth,
|
|
135
|
+
// exactly as a plain reference copy clears them (concreteFlow's
|
|
136
|
+
// rule): the schema is hidden, the instances it expands into
|
|
137
|
+
// are the output.
|
|
138
|
+
// The clone is REBASED to the DRIVE path explicitly (spec.path;
|
|
139
|
+
// the Go twin passes cp(r.path), whose stored paths its clone
|
|
140
|
+
// arms keep clean): the default rebase slices the definition's
|
|
141
|
+
// path by the destination's length, which leaks definition
|
|
142
|
+
// segments into instance paths whenever the destination is
|
|
143
|
+
// shallower -- a residual carried inside a copied definition
|
|
144
|
+
// body (`chain: $.spec.Step` inside Policy, copied to a slot
|
|
145
|
+
// shallower than the definition) reported
|
|
146
|
+
// `$.payments_policy.Policy.chain.then.approver` for the
|
|
147
|
+
// finding Go placed at `$.payments_policy.chain.then.approver`.
|
|
148
|
+
// The drive path is also right under vet's anchored meet, which
|
|
149
|
+
// drives AT the anchor's own path (see vet.ts), so anchored
|
|
150
|
+
// findings land in the schema's namespace in both ports.
|
|
151
|
+
const level: any = body.clone(ctx, {
|
|
152
|
+
dup: true, path: [...ctx.path],
|
|
153
|
+
} as any)
|
|
154
|
+
walk(level, (_key: string | number | undefined, v: Val) => {
|
|
155
|
+
v.mark.type = false
|
|
156
|
+
v.mark.hide = false
|
|
157
|
+
return v
|
|
158
|
+
})
|
|
159
|
+
bumpRecurse(level, this.xc + 1)
|
|
160
|
+
return unite(ctx, level, peer, 'recurse-expand')
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Anything else -- a func still resolving, a reference, a
|
|
164
|
+
// constraint -- waits beside the residual.
|
|
165
|
+
const out = new ConjunctVal({ peg: [this, peer] }, ctx)
|
|
166
|
+
propagateMarks(this, out)
|
|
167
|
+
out.path = this.path
|
|
168
|
+
return out
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get canon(): string {
|
|
172
|
+
return '$.' + this.target.join('.')
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
gen(ctx: AontuContext) {
|
|
176
|
+
// An unexpanded residual in a demanded position refuses;
|
|
177
|
+
// guardedness is emergent -- under an optional key the bag's
|
|
178
|
+
// isolated context swallows this and drops the key.
|
|
179
|
+
makeNilErr(ctx, 'recursion_unexpanded', this, undefined, 'recurse',
|
|
180
|
+
{ target: '$.' + this.target.join('.') })
|
|
181
|
+
return undefined
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
// walkTarget descends a tree by the residual's absolute target path,
|
|
187
|
+
// answering the definition node or undefined.
|
|
188
|
+
function walkTarget(root: any, target: string[]): Val | undefined {
|
|
189
|
+
let node: any = root
|
|
190
|
+
for (const seg of target) {
|
|
191
|
+
node = node?.peg?.[seg]
|
|
192
|
+
}
|
|
193
|
+
return null != node && true === node.isVal ? node : undefined
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
// bumpRecurse stamps the expansion depth onto every residual inside a
|
|
198
|
+
// freshly cloned level, so descent is charged along the chain.
|
|
199
|
+
function bumpRecurse(v: any, xc: number): void {
|
|
200
|
+
if (null == v || true !== v.isVal) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
if (true === v.isRecurse) {
|
|
204
|
+
v.xc = Math.max(v.xc, xc)
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
const peg: any = v.peg
|
|
208
|
+
if (true === v.isMap && null != peg) {
|
|
209
|
+
for (const k of Object.keys(peg)) {
|
|
210
|
+
bumpRecurse(peg[k], xc)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (true === v.isList && Array.isArray(peg)) {
|
|
214
|
+
for (const e of peg) {
|
|
215
|
+
bumpRecurse(e, xc)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else if (true === v.isConjunct && Array.isArray(peg)) {
|
|
219
|
+
for (const e of peg) {
|
|
220
|
+
bumpRecurse(e, xc)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (null != v.spread?.cj) {
|
|
224
|
+
bumpRecurse(v.spread.cj, xc)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
// containsRecurseOf answers whether a definition holds a residual of
|
|
230
|
+
// the given target -- i.e. the definition is (transitively) the
|
|
231
|
+
// fixpoint that target names. A reference RESOLVING to such a
|
|
232
|
+
// definition must itself answer the residual: cloned instead, every
|
|
233
|
+
// reparse of a canon unrolled the schema one more level and canon
|
|
234
|
+
// never converged.
|
|
235
|
+
function containsRecurseOf(v: any, target: string[], depth?: number): boolean {
|
|
236
|
+
const d = depth ?? 0
|
|
237
|
+
if (null == v || true !== v.isVal || 8 < d) {
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
if (true === v.isRecurse) {
|
|
241
|
+
return v.target.length === target.length
|
|
242
|
+
&& v.target.every((s: string, i: number) => s === target[i])
|
|
243
|
+
}
|
|
244
|
+
// A RAW REFERENCE to the target IS the recursion, minted or not:
|
|
245
|
+
// the answer must not depend on whether the definition's own
|
|
246
|
+
// prefix position has been visited yet. Without this arm the
|
|
247
|
+
// answer was ORDER-DEPENDENT -- reparsing a generated canon puts
|
|
248
|
+
// the instance before the definition, its trailing `$.spec.Step`
|
|
249
|
+
// leaves resolved before `Step.then` had minted, and each resolve
|
|
250
|
+
// cloned one more unrolled level until the unify depth guard
|
|
251
|
+
// (unify_cycle) killed the document.
|
|
252
|
+
if (true === v.isRef && Array.isArray(v.peg)) {
|
|
253
|
+
if (v.peg.length === target.length
|
|
254
|
+
&& v.peg.every((s: any, i: number) => s === target[i])) {
|
|
255
|
+
return true
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const peg: any = v.peg
|
|
259
|
+
if (true === v.isMap && null != peg) {
|
|
260
|
+
for (const k of Object.keys(peg)) {
|
|
261
|
+
if (containsRecurseOf(peg[k], target, d + 1)) {
|
|
262
|
+
return true
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else if ((true === v.isList || true === v.isConjunct || true === v.isDisjunct)
|
|
267
|
+
&& Array.isArray(peg)) {
|
|
268
|
+
for (const e of peg) {
|
|
269
|
+
if (containsRecurseOf(e, target, d + 1)) {
|
|
270
|
+
return true
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (null != v.spread?.cj && containsRecurseOf(v.spread.cj, target, d + 1)) {
|
|
275
|
+
return true
|
|
276
|
+
}
|
|
277
|
+
return false
|
|
278
|
+
} /* node:coverage ignore next 7 */
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
export {
|
|
282
|
+
RecurseVal,
|
|
283
|
+
bumpRecurse,
|
|
284
|
+
containsRecurseOf,
|
|
285
|
+
}
|
package/src/val/RefVal.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { AontuContext } from '../ctx'
|
|
23
23
|
|
|
24
24
|
import { makeNilErr } from '../err'
|
|
25
|
+
import { RecurseVal, containsRecurseOf } from './RecurseVal'
|
|
25
26
|
import { unite } from '../unify'
|
|
26
27
|
|
|
27
28
|
|
|
@@ -279,15 +280,36 @@ class RefVal extends FeatureVal {
|
|
|
279
280
|
// let descent = ''
|
|
280
281
|
|
|
281
282
|
if (isprefixpath) {
|
|
282
|
-
//
|
|
283
|
-
|
|
283
|
+
// THE DETECTOR'S ANSWER IS A RESIDUAL (RECURSION.0.md): a
|
|
284
|
+
// self-reference under a guarded shape is the fixpoint the
|
|
285
|
+
// author wrote, so the prefix hit mints the recursive residual
|
|
286
|
+
// instead of refusing -- except the degenerate all-empty
|
|
287
|
+
// spelling (path("")), which names nothing and keeps its
|
|
288
|
+
// path_cycle. Only all-string paths recurse; anything else
|
|
289
|
+
// keeps the conservative refusal.
|
|
290
|
+
let degenerate = 0 === this.path.length
|
|
291
|
+
let target: string[] = []
|
|
292
|
+
for (let i = 0; i < this.peg.length && !degenerate; i++) {
|
|
293
|
+
if ('string' !== typeof this.peg[i] || '' === this.peg[i]) {
|
|
294
|
+
degenerate = true
|
|
295
|
+
break
|
|
296
|
+
}
|
|
297
|
+
target.push(this.peg[i] as string)
|
|
298
|
+
}
|
|
299
|
+
if (degenerate) {
|
|
300
|
+
out = makeNilErr(ctx, 'path_cycle', this)
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
const rec: any = new RecurseVal({ target } as any, ctx)
|
|
304
|
+
rec.site = this.site
|
|
305
|
+
rec.path = [...this.path]
|
|
306
|
+
out = rec
|
|
307
|
+
}
|
|
284
308
|
}
|
|
285
309
|
else {
|
|
286
310
|
|
|
287
311
|
let parts: string[] = []
|
|
288
312
|
|
|
289
|
-
let modes: string[] = []
|
|
290
|
-
|
|
291
313
|
for (let pI = 0; pI < this.peg.length; pI++) {
|
|
292
314
|
let part = this.peg[pI]
|
|
293
315
|
// An unspellable segment MISSES BEFORE ANY LOOKUP. The marker
|
|
@@ -301,38 +323,13 @@ class RefVal extends FeatureVal {
|
|
|
301
323
|
return makeNilErr(ctx, 'no_path', this)
|
|
302
324
|
}
|
|
303
325
|
if (part instanceof VarVal) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
else {
|
|
312
|
-
// TODO: return a Nil explaining error
|
|
313
|
-
return
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if ('SELF' === name) {
|
|
318
|
-
if (pI === 0) {
|
|
319
|
-
modes.push(name)
|
|
320
|
-
}
|
|
321
|
-
else {
|
|
322
|
-
// TODO: return a Nil explaining error
|
|
323
|
-
return
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
else if ('PARENT' === name) {
|
|
327
|
-
if (pI === 0) {
|
|
328
|
-
modes.push(name)
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
// TODO: return a Nil explaining error
|
|
332
|
-
return
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
else if (0 === modes.length) {
|
|
326
|
+
// EVERY `$name` IN A PATH IS AN ORDINARY VARIABLE. `$KEY`,
|
|
327
|
+
// `$SELF` and `$PARENT` used to be intercepted here by name;
|
|
328
|
+
// they are gone (ADR-009). `key()` is the replacement for
|
|
329
|
+
// `$KEY` and answers where a value LANDS rather than where it
|
|
330
|
+
// was written, `$SELF.x` was only ever `$.x`, and `$PARENT.x`
|
|
331
|
+
// was only ever `.x`.
|
|
332
|
+
{
|
|
336
333
|
part = (part as VarVal).unify(top(), ctx)
|
|
337
334
|
if (part.isNil) {
|
|
338
335
|
// TODO: var not found, so can't find path
|
|
@@ -367,15 +364,9 @@ class RefVal extends FeatureVal {
|
|
|
367
364
|
refpath = parts
|
|
368
365
|
}
|
|
369
366
|
else {
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
(
|
|
374
|
-
modes.includes('SELF') ? 0 :
|
|
375
|
-
modes.includes('PARENT') ? -1 :
|
|
376
|
-
-1 // siblings
|
|
377
|
-
)
|
|
378
|
-
).concat(parts)
|
|
367
|
+
// A relative reference reads from the SIBLING scope: drop this
|
|
368
|
+
// node's own key and append the written segments.
|
|
369
|
+
refpath = this.path.slice(0, -1).concat(parts)
|
|
379
370
|
}
|
|
380
371
|
|
|
381
372
|
let sep = '.'
|
|
@@ -383,29 +374,6 @@ class RefVal extends FeatureVal {
|
|
|
383
374
|
.reduce(((a: string[], p: string) =>
|
|
384
375
|
(p === sep ? a.length = a.length - 1 : a.push(p), a)), [])
|
|
385
376
|
|
|
386
|
-
if (modes.includes('KEY')) {
|
|
387
|
-
// STRINGIFY. A LIST index arrives here as a JS NUMBER (jsonic puts
|
|
388
|
-
// it in the path as one, and lang.ts copies the path wholesale),
|
|
389
|
-
// so `.$KEY` inside a list built a StringVal whose peg was the
|
|
390
|
-
// number 0 -- an ill-formed value, not a design choice: it canoned
|
|
391
|
-
// as a bare 0, generated a JSON number, satisfied `number` and
|
|
392
|
-
// failed `string`. Go stringifies, and key() already agreed with
|
|
393
|
-
// Go, so this port disagreed with itself.
|
|
394
|
-
//
|
|
395
|
-
// Coerced HERE, at the consumption site, rather than by
|
|
396
|
-
// normalising Val.path: the numeric segment originates in jsonic's
|
|
397
|
-
// own r.k.path and every other path consumer (find's descent,
|
|
398
|
-
// key(), the clone/spread machinery) already handles it.
|
|
399
|
-
let key = this.path[this.path.length - 2]
|
|
400
|
-
let sv = new StringVal({ peg: null == key ? '' : '' + key }, ctx)
|
|
401
|
-
|
|
402
|
-
// TODO: other props?
|
|
403
|
-
sv.dc = DONE
|
|
404
|
-
sv.path = this.path
|
|
405
|
-
|
|
406
|
-
return sv
|
|
407
|
-
}
|
|
408
|
-
|
|
409
377
|
let node = ctx.root as Val
|
|
410
378
|
|
|
411
379
|
let nopath = false
|
|
@@ -423,6 +391,21 @@ class RefVal extends FeatureVal {
|
|
|
423
391
|
else if (node.isList) {
|
|
424
392
|
node = node.peg[part]
|
|
425
393
|
}
|
|
394
|
+
// A PENDING MARK WRAPPER IS TRANSPARENT TO THE WALK: hide()
|
|
395
|
+
// and type() only mark, and their argument is the structure
|
|
396
|
+
// the path names. Without this, two sibling schemas in one
|
|
397
|
+
// hide() bag deadlock -- the wrapper waits for its argument,
|
|
398
|
+
// the argument's members wait for references that walk into
|
|
399
|
+
// the unresolved wrapper (BUGS.md §53's family; the
|
|
400
|
+
// recursive Policy/Step pair found it again). The found node
|
|
401
|
+
// is cloned and mark-cleared exactly as any reference target
|
|
402
|
+
// is, and the wrapper still marks its own field.
|
|
403
|
+
else if (true === (node as any).isFunc
|
|
404
|
+
&& ((node as any).isHideFunc || (node as any).isTypeFunc)
|
|
405
|
+
&& (true === (node as any).peg?.[0]?.isMap
|
|
406
|
+
|| true === (node as any).peg?.[0]?.isList)) {
|
|
407
|
+
node = (node as any).peg[0].peg[part]
|
|
408
|
+
}
|
|
426
409
|
else if (node.done) {
|
|
427
410
|
nopath = true
|
|
428
411
|
break;
|
|
@@ -439,8 +422,41 @@ class RefVal extends FeatureVal {
|
|
|
439
422
|
}
|
|
440
423
|
}
|
|
441
424
|
|
|
442
|
-
//
|
|
443
|
-
|
|
425
|
+
// THE ANCHORED-MEET FALLBACK (vet --at): an absolute reference
|
|
426
|
+
// inside a lifted subtree names the SCHEMA's namespace, and the
|
|
427
|
+
// meet's root does not contain it. The settled schema root the
|
|
428
|
+
// lifter kept (AontuContext._fixroot; see RecurseVal.body) is
|
|
429
|
+
// the tree such a reference means. Without this, a recursion
|
|
430
|
+
// re-entering through a list spread died as no_path at the
|
|
431
|
+
// first element under vet --at, while the map-tail form (whose
|
|
432
|
+
// residual was minted during schema settling) worked; the Go
|
|
433
|
+
// port answers the anchored meet from settled structures and
|
|
434
|
+
// never sees the gap.
|
|
435
|
+
const fixroot: any = (ctx as any)._fixroot
|
|
436
|
+
if (this.absolute && null != fixroot
|
|
437
|
+
&& (nopath || pI !== refpath.length)) {
|
|
438
|
+
nopath = false
|
|
439
|
+
pI = 0
|
|
440
|
+
let fnode: any = fixroot
|
|
441
|
+
for (; pI < refpath.length; pI++) {
|
|
442
|
+
const part = refpath[pI]
|
|
443
|
+
if (true === fnode.isMap || true === fnode.isList) {
|
|
444
|
+
fnode = fnode.peg[part]
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
break
|
|
448
|
+
}
|
|
449
|
+
if (null == fnode) {
|
|
450
|
+
break
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (null != fnode && pI === refpath.length) {
|
|
454
|
+
node = fnode
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
nopath = true
|
|
458
|
+
}
|
|
459
|
+
}
|
|
444
460
|
|
|
445
461
|
if (nopath) {
|
|
446
462
|
out = makeNilErr(ctx, 'no_path', this)
|
|
@@ -494,6 +510,18 @@ class RefVal extends FeatureVal {
|
|
|
494
510
|
!out.done) {
|
|
495
511
|
out = undefined
|
|
496
512
|
}
|
|
513
|
+
// A REFERENCE TO A RECURSIVE DEFINITION IS THE FIXPOINT
|
|
514
|
+
// REFERENCE (RECURSION.0.md): resolving it to a clone
|
|
515
|
+
// unrolled the schema one level, and every reparse of a
|
|
516
|
+
// canon then unrolled one more -- canon never converged. The
|
|
517
|
+
// residual is the resolved form, exactly as at the prefix
|
|
518
|
+
// positions inside the definition.
|
|
519
|
+
else if (null != out && !snap && containsRecurseOf(out, this.peg as any)) {
|
|
520
|
+
const rec: any = new RecurseVal({ target: [...this.peg] } as any, ctx)
|
|
521
|
+
rec.site = this.site
|
|
522
|
+
rec.path = [...this.path]
|
|
523
|
+
out = rec
|
|
524
|
+
}
|
|
497
525
|
// Types and hidden values are cloned and made concrete
|
|
498
526
|
else if (null != out) { // && (out.mark.type || out.mark.hide)) {
|
|
499
527
|
|
|
@@ -522,19 +550,12 @@ class RefVal extends FeatureVal {
|
|
|
522
550
|
walk(out, (_key: string | number | undefined, val: Val) => {
|
|
523
551
|
val.mark.type = false
|
|
524
552
|
val.mark.hide = false
|
|
525
|
-
//
|
|
526
|
-
//
|
|
527
|
-
//
|
|
528
|
-
//
|
|
529
|
-
//
|
|
530
|
-
//
|
|
531
|
-
// wrote at the reference site.
|
|
532
|
-
//
|
|
533
|
-
// The LINK is NOT cleared (G4 phase 3): an identity says
|
|
534
|
-
// what a value IS, so a copy must not be that entity; a
|
|
535
|
-
// link says what a value POINTS AT, and a copy of a link
|
|
536
|
-
// points at the same thing.
|
|
537
|
-
val.entity = undefined
|
|
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).
|
|
538
559
|
return val
|
|
539
560
|
})
|
|
540
561
|
//}
|
|
@@ -719,5 +740,6 @@ class RefVal extends FeatureVal {
|
|
|
719
740
|
|
|
720
741
|
|
|
721
742
|
export {
|
|
743
|
+
pendingMarkWrapper,
|
|
722
744
|
RefVal,
|
|
723
745
|
}
|