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
package/src/val/JunctionVal.ts
CHANGED
|
@@ -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
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
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
|
}
|
package/src/val/ListVal.ts
CHANGED
|
@@ -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 (`&:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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>' : '') +
|
package/src/val/PathFuncVal.ts
CHANGED
|
@@ -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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
//
|
|
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
|
-
|
|
110
|
+
return []
|
|
54
111
|
}
|
|
55
|
-
|
|
56
|
-
|
|
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 (
|
|
59
|
-
|
|
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
|
-
|
|
130
|
+
if (undefined === spelling || undefined === parseAddress(spelling)) {
|
|
131
|
+
return [makeNilErr(ctx, 'path_address', this, arg)]
|
|
132
|
+
}
|
|
64
133
|
|
|
65
|
-
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 */
|