aontu 0.52.0 → 0.53.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 +88 -0
- package/bin/aontu-mcp.js +4 -0
- package/dist/agentsmd.d.ts +16 -0
- package/dist/agentsmd.js +107 -0
- package/dist/agentsmd.js.map +1 -0
- package/dist/aontu.d.ts +14 -3
- package/dist/aontu.js +145 -4
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +44 -1
- package/dist/cli.js +2401 -44
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +16 -0
- package/dist/ctx.js +44 -0
- package/dist/ctx.js.map +1 -1
- package/dist/diff.d.ts +22 -0
- package/dist/diff.js +141 -0
- package/dist/diff.js.map +1 -0
- package/dist/err.d.ts +3 -1
- package/dist/err.js +48 -8
- package/dist/err.js.map +1 -1
- package/dist/graph.d.ts +16 -0
- package/dist/graph.js +73 -0
- package/dist/graph.js.map +1 -0
- package/dist/hcanon.d.ts +3 -0
- package/dist/hcanon.js +146 -0
- package/dist/hcanon.js.map +1 -0
- package/dist/hints.js +223 -5
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.d.ts +20 -0
- package/dist/jsonschema.js +391 -0
- package/dist/jsonschema.js.map +1 -0
- package/dist/lang.js +698 -35
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +9 -2
- package/dist/lsp.js +262 -46
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.d.ts +20 -0
- package/dist/mcp-server.js +147 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp.d.ts +42 -0
- package/dist/mcp.js +814 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mod-tool.d.ts +58 -0
- package/dist/mod-tool.js +498 -0
- package/dist/mod-tool.js.map +1 -0
- package/dist/mod.d.ts +31 -0
- package/dist/mod.js +250 -0
- package/dist/mod.js.map +1 -0
- package/dist/patch.d.ts +44 -0
- package/dist/patch.js +506 -0
- package/dist/patch.js.map +1 -0
- package/dist/provenance.d.ts +40 -0
- package/dist/provenance.js +335 -0
- package/dist/provenance.js.map +1 -0
- package/dist/query.d.ts +27 -0
- package/dist/query.js +294 -0
- package/dist/query.js.map +1 -0
- package/dist/reach.d.ts +14 -0
- package/dist/reach.js +140 -0
- package/dist/reach.js.map +1 -0
- package/dist/relation.d.ts +19 -0
- package/dist/relation.js +305 -0
- package/dist/relation.js.map +1 -0
- package/dist/report-sarif.d.ts +14 -0
- package/dist/report-sarif.js +102 -0
- package/dist/report-sarif.js.map +1 -0
- package/dist/site.d.ts +4 -0
- package/dist/site.js +31 -0
- package/dist/site.js.map +1 -1
- package/dist/std.d.ts +1 -0
- package/dist/std.js +73 -0
- package/dist/std.js.map +1 -0
- package/dist/subsume.d.ts +39 -0
- package/dist/subsume.js +526 -0
- package/dist/subsume.js.map +1 -0
- package/dist/trim.d.ts +19 -0
- package/dist/trim.js +155 -0
- package/dist/trim.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +17 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.d.ts +3 -1
- package/dist/unify.js +287 -18
- package/dist/unify.js.map +1 -1
- package/dist/utility.d.ts +9 -1
- package/dist/utility.js +122 -1
- package/dist/utility.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +33 -0
- package/dist/val/AggFuncVal.js +202 -0
- package/dist/val/AggFuncVal.js.map +1 -0
- package/dist/val/ArithFuncVal.d.ts +31 -0
- package/dist/val/ArithFuncVal.js +62 -0
- package/dist/val/ArithFuncVal.js.map +1 -0
- package/dist/val/BagVal.d.ts +5 -0
- package/dist/val/BagVal.js +96 -5
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +9 -1
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.d.ts +1 -1
- package/dist/val/ConjunctVal.js +19 -0
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.d.ts +48 -1
- package/dist/val/ConstraintVal.js +1501 -110
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/CopyFuncVal.d.ts +1 -2
- package/dist/val/CopyFuncVal.js +7 -0
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.d.ts +1 -0
- package/dist/val/Decimal.js +13 -0
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.d.ts +11 -0
- package/dist/val/DeprecateFuncVal.js +47 -0
- package/dist/val/DeprecateFuncVal.js.map +1 -0
- package/dist/val/DisjunctVal.js +130 -21
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.d.ts +15 -0
- package/dist/val/EachFuncVal.js +75 -0
- package/dist/val/EachFuncVal.js.map +1 -0
- package/dist/val/ExpectVal.d.ts +1 -0
- package/dist/val/ExpectVal.js +41 -4
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FeatureVal.js +1 -1
- package/dist/val/FeatureVal.js.map +1 -1
- package/dist/val/FilterFuncVal.d.ts +15 -0
- package/dist/val/FilterFuncVal.js +91 -0
- package/dist/val/FilterFuncVal.js.map +1 -0
- package/dist/val/FuncBaseVal.d.ts +6 -1
- package/dist/val/FuncBaseVal.js +178 -2
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IdFuncVal.d.ts +13 -0
- package/dist/val/IdFuncVal.js +54 -0
- package/dist/val/IdFuncVal.js.map +1 -0
- package/dist/val/JunctionVal.js +7 -1
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.d.ts +1 -1
- package/dist/val/KeyFuncVal.js +38 -30
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +117 -17
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +102 -8
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.d.ts +15 -0
- package/dist/val/MatchFuncVal.js +107 -0
- package/dist/val/MatchFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +24 -0
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -1
- package/dist/val/OpBaseVal.js +24 -2
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/OpenFuncVal.js +4 -1
- package/dist/val/OpenFuncVal.js.map +1 -1
- package/dist/val/PackFuncVal.d.ts +15 -0
- package/dist/val/PackFuncVal.js +108 -0
- package/dist/val/PackFuncVal.js.map +1 -0
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PlaceVal.d.ts +13 -0
- package/dist/val/PlaceVal.js +131 -0
- package/dist/val/PlaceVal.js.map +1 -0
- package/dist/val/PlusOpVal.js +11 -2
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +2 -2
- package/dist/val/PrefVal.js +78 -23
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RefVal.d.ts +1 -1
- package/dist/val/RefVal.js +158 -30
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.d.ts +36 -0
- package/dist/val/ReferFuncVal.js +303 -0
- package/dist/val/ReferFuncVal.js.map +1 -0
- package/dist/val/ScalarKindVal.d.ts +1 -2
- package/dist/val/ScalarKindVal.js +0 -11
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TypeFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +9 -2
- package/dist/val/Val.js +150 -4
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.d.ts +6 -0
- package/dist/val/arith.js +170 -0
- package/dist/val/arith.js.map +1 -0
- package/dist/vet.d.ts +45 -0
- package/dist/vet.js +776 -0
- package/dist/vet.js.map +1 -0
- package/dist/walk.d.ts +2 -0
- package/dist/walk.js +91 -0
- package/dist/walk.js.map +1 -0
- package/grammar/aontu.gbnf +130 -0
- package/grammar/aontu.lark +113 -0
- package/package.json +30 -15
- package/skill/SKILL.md +37 -0
- package/skill/error-codes.md +62 -0
- package/skill/examples.md +99 -0
- package/skill/grammar-card.md +57 -0
- package/src/agentsmd.ts +135 -0
- package/src/aontu.ts +192 -4
- package/src/cli.ts +2858 -71
- package/src/ctx.ts +81 -0
- package/src/diff.ts +196 -0
- package/src/err.ts +52 -8
- package/src/graph.ts +135 -0
- package/src/hcanon.ts +169 -0
- package/src/hints.ts +271 -5
- package/src/jsonschema.ts +511 -0
- package/src/lang.ts +779 -37
- package/src/lsp.ts +281 -47
- package/src/mcp-server.ts +187 -0
- package/src/mcp.ts +993 -0
- package/src/mod-tool.ts +679 -0
- package/src/mod.ts +344 -0
- package/src/patch.ts +624 -0
- package/src/provenance.ts +430 -0
- package/src/query.ts +379 -0
- package/src/reach.ts +184 -0
- package/src/relation.ts +395 -0
- package/src/report-sarif.ts +137 -0
- package/src/site.ts +36 -1
- package/src/std.ts +73 -0
- package/src/subsume.ts +690 -0
- package/src/trim.ts +195 -0
- package/src/tsconfig.json +10 -4
- package/src/type.ts +51 -2
- package/src/unify.ts +311 -16
- package/src/utility.ts +139 -1
- package/src/val/AggFuncVal.ts +319 -0
- package/src/val/ArithFuncVal.ts +108 -0
- package/src/val/BagVal.ts +101 -4
- package/src/val/CloseFuncVal.ts +9 -1
- package/src/val/ConjunctVal.ts +20 -0
- package/src/val/ConstraintVal.ts +1699 -116
- package/src/val/CopyFuncVal.ts +7 -1
- package/src/val/Decimal.ts +15 -0
- package/src/val/DeprecateFuncVal.ts +84 -0
- package/src/val/DisjunctVal.ts +139 -28
- package/src/val/EachFuncVal.ts +133 -0
- package/src/val/ExpectVal.ts +43 -6
- package/src/val/FeatureVal.ts +1 -1
- package/src/val/FilterFuncVal.ts +154 -0
- package/src/val/FuncBaseVal.ts +200 -3
- package/src/val/HideFuncVal.ts +0 -2
- package/src/val/IdFuncVal.ts +91 -0
- package/src/val/JunctionVal.ts +7 -1
- package/src/val/KeyFuncVal.ts +39 -35
- package/src/val/ListVal.ts +125 -18
- package/src/val/LowerFuncVal.ts +0 -1
- package/src/val/MapVal.ts +110 -8
- package/src/val/MatchFuncVal.ts +176 -0
- package/src/val/MoveFuncVal.ts +0 -2
- package/src/val/NilVal.ts +25 -0
- package/src/val/OpBaseVal.ts +26 -3
- package/src/val/OpenFuncVal.ts +4 -2
- package/src/val/PackFuncVal.ts +175 -0
- package/src/val/PathFuncVal.ts +0 -1
- package/src/val/PlaceVal.ts +193 -0
- package/src/val/PlusOpVal.ts +11 -2
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +79 -36
- package/src/val/RefVal.ts +163 -30
- package/src/val/ReferFuncVal.ts +387 -0
- package/src/val/ScalarKindVal.ts +0 -13
- package/src/val/TopVal.ts +0 -1
- package/src/val/TypeFuncVal.ts +0 -2
- package/src/val/UpperFuncVal.ts +0 -1
- package/src/val/Val.ts +213 -3
- package/src/val/VarVal.ts +0 -1
- package/src/val/arith.ts +316 -0
- package/src/vet.ts +992 -0
- package/src/walk.ts +99 -0
package/src/val/RefVal.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* Copyright (c) 2021-2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
import Util from 'node:util'
|
|
5
4
|
|
|
6
5
|
import {
|
|
7
6
|
walk,
|
|
@@ -49,6 +48,30 @@ import { BigDecimalVal } from './BigDecimalVal'
|
|
|
49
48
|
const UNSPELLABLE_SEGMENT = '\u0000unspellable'
|
|
50
49
|
|
|
51
50
|
|
|
51
|
+
// Is this value an unresolved type()/hide() call — or a conjunct still
|
|
52
|
+
// carrying one? A reference that lands on one must defer rather than
|
|
53
|
+
// clone it (see the call site in `find`): the marks such a call will
|
|
54
|
+
// stamp belong to the field it was WRITTEN at, and a clone resolving
|
|
55
|
+
// at the reference's site re-applies them after the reference's
|
|
56
|
+
// mark-clearing walk has already run. Only the two mark wrappers
|
|
57
|
+
// qualify — every other pending call resolves to an unmarked value,
|
|
58
|
+
// and the existing early-clone behaviour for those is pinned
|
|
59
|
+
// (move()/copy() ghost rows, hole-filling conjuncts).
|
|
60
|
+
function pendingMarkWrapper(v: any): boolean {
|
|
61
|
+
if (true === v.isTypeFunc || true === v.isHideFunc) {
|
|
62
|
+
return !v.done
|
|
63
|
+
}
|
|
64
|
+
if (true === v.isConjunct && Array.isArray(v.peg)) {
|
|
65
|
+
for (const t of v.peg) {
|
|
66
|
+
if (pendingMarkWrapper(t)) {
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return false
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
52
75
|
class RefVal extends FeatureVal {
|
|
53
76
|
isRef = true
|
|
54
77
|
isGenable = true
|
|
@@ -175,7 +198,6 @@ class RefVal extends FeatureVal {
|
|
|
175
198
|
|
|
176
199
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Ref', this, peer)
|
|
177
200
|
let out: Val = this
|
|
178
|
-
let why = 'id'
|
|
179
201
|
|
|
180
202
|
if (this.id !== peer.id) {
|
|
181
203
|
|
|
@@ -191,29 +213,24 @@ class RefVal extends FeatureVal {
|
|
|
191
213
|
if (resolved instanceof RefVal) {
|
|
192
214
|
if (peer.isTop) {
|
|
193
215
|
out = this
|
|
194
|
-
why = 'pt'
|
|
195
216
|
}
|
|
196
217
|
else if (peer.isNil) {
|
|
197
218
|
out = makeNilErr(ctx, 'ref[' + this.peg + ']', this, peer)
|
|
198
|
-
why = 'pn'
|
|
199
219
|
}
|
|
200
220
|
|
|
201
221
|
// same path
|
|
202
222
|
else if (this.canon === peer.canon) {
|
|
203
223
|
out = this
|
|
204
|
-
why = 'pp'
|
|
205
224
|
}
|
|
206
225
|
|
|
207
226
|
else {
|
|
208
227
|
// Ensure RefVal done is incremented
|
|
209
228
|
this.dc = DONE === this.dc ? DONE : this.dc + 1
|
|
210
229
|
out = new ConjunctVal({ peg: [this, peer] }, ctx)
|
|
211
|
-
why = 'cj'
|
|
212
230
|
}
|
|
213
231
|
}
|
|
214
232
|
else {
|
|
215
233
|
out = unite(te ? ctx.clone({ explain: ec(te, 'RES') }) : ctx, resolved, peer, 'ref')
|
|
216
|
-
why = 'u'
|
|
217
234
|
}
|
|
218
235
|
|
|
219
236
|
out.dc = DONE === out.dc ? DONE : this.dc + 1
|
|
@@ -226,7 +243,15 @@ class RefVal extends FeatureVal {
|
|
|
226
243
|
}
|
|
227
244
|
|
|
228
245
|
|
|
229
|
-
|
|
246
|
+
// `snap` is set by snapshotRefSpread (MapVal): a SPREAD snapshot
|
|
247
|
+
// wants the target's pre-resolution STRUCTURE — key()/path() still
|
|
248
|
+
// unresolved, to be re-resolved per destination — so the
|
|
249
|
+
// pending-mark-wrapper defer below must not apply to it. Deferring
|
|
250
|
+
// there made the snapshot wait until the target's own key() had
|
|
251
|
+
// resolved at the target, and the literal leaked into every
|
|
252
|
+
// destination (the exact failure the snapshot exists to prevent —
|
|
253
|
+
// test/spec/spread-type.tsv, spread-type-key-ref).
|
|
254
|
+
find(ctx: AontuContext, snap?: boolean) {
|
|
230
255
|
let out: Val | undefined = undefined
|
|
231
256
|
|
|
232
257
|
// Check if self.path starts with peg (cycle detection).
|
|
@@ -265,6 +290,16 @@ class RefVal extends FeatureVal {
|
|
|
265
290
|
|
|
266
291
|
for (let pI = 0; pI < this.peg.length; pI++) {
|
|
267
292
|
let part = this.peg[pI]
|
|
293
|
+
// An unspellable segment MISSES BEFORE ANY LOOKUP. The marker
|
|
294
|
+
// is NUL-prefixed because no spelling produces one, but a
|
|
295
|
+
// document can still hold a key spelled with an escaped NUL
|
|
296
|
+
// (`a:{"unspellable":7}`), and matching it would turn the
|
|
297
|
+
// silent path-shortening this marker exists to prevent into a
|
|
298
|
+
// different silent wrong value. The marker is a marker, never a
|
|
299
|
+
// lookup key.
|
|
300
|
+
if (UNSPELLABLE_SEGMENT === part) {
|
|
301
|
+
return makeNilErr(ctx, 'no_path', this)
|
|
302
|
+
}
|
|
268
303
|
if (part instanceof VarVal) {
|
|
269
304
|
let strval = (part as VarVal).peg
|
|
270
305
|
let name = strval ? '' + strval.peg : ''
|
|
@@ -413,14 +448,52 @@ class RefVal extends FeatureVal {
|
|
|
413
448
|
else if (pI === refpath.length) {
|
|
414
449
|
out = node
|
|
415
450
|
|
|
416
|
-
// A reference landing on another reference
|
|
417
|
-
//
|
|
418
|
-
//
|
|
419
|
-
//
|
|
420
|
-
//
|
|
421
|
-
|
|
451
|
+
// A reference landing on another reference -- or on a FUNCTION,
|
|
452
|
+
// whose arguments the chase now follows (issue #35) -- may be a
|
|
453
|
+
// PROVEN mutual cycle (a: $.b, b: $.a; a: $.b, b: upper($.a)).
|
|
454
|
+
// Follow the chain and, if it returns to a node still open above
|
|
455
|
+
// it, report path_cycle now instead of deferring every pass and
|
|
456
|
+
// dying later as a spent budget. No proof (the chain leaves plain
|
|
457
|
+
// refs and calls, or ends) defers as before.
|
|
458
|
+
if (null != out && ((out as any).isRef || (out as any).isFunc) &&
|
|
459
|
+
this.detectRefCycle(ctx)) {
|
|
422
460
|
out = makeNilErr(ctx, 'path_cycle', this)
|
|
423
461
|
}
|
|
462
|
+
// A PENDING MARK WRAPPER IS NOT YET A VALUE TO COPY (ADR-005).
|
|
463
|
+
// A type()/hide() call still waiting for its argument — an
|
|
464
|
+
// alias reference inside a type() body, a generator inside a
|
|
465
|
+
// hide() — would be cloned here as the CALL, and the clone
|
|
466
|
+
// then resolves at the REFERENCE's site, stamping marks that
|
|
467
|
+
// the mark-clearing walk below has already run too early to
|
|
468
|
+
// clear. That is how a type-marked alias silently suppressed
|
|
469
|
+
// the referring field's emission (use-cases/BUGS.md §12), how
|
|
470
|
+
// `hide(pack(...))` leaked its mark onto downstream packs
|
|
471
|
+
// (§11), and how hide() around a computed field swallowed the
|
|
472
|
+
// value into a silent [] (§35b). Defer instead: the reference
|
|
473
|
+
// residuates until the wrapper has resolved at its OWN field,
|
|
474
|
+
// and the ordinary marked-value path below then clears the
|
|
475
|
+
// marks on the clone as documented. The move() reference
|
|
476
|
+
// (`_hide_found`) is exempt: a move TRANSPLANTS the pending
|
|
477
|
+
// call, and the ghost rows (test/spec/func.tsv) pin that its
|
|
478
|
+
// innards resolve at the destination.
|
|
479
|
+
else if (null != out && !snap && !this.mark._hide_found &&
|
|
480
|
+
pendingMarkWrapper(out)) {
|
|
481
|
+
out = undefined
|
|
482
|
+
}
|
|
483
|
+
// A STAGED ARGUMENT SNAPSHOTS A SETTLED SOURCE (the argsnap
|
|
484
|
+
// flag, set by driveStagedArgs). A generator's data argument is
|
|
485
|
+
// a copy OUTSIDE the tree, so anything in the target that still
|
|
486
|
+
// resolves against its own tree location — a spread-injected
|
|
487
|
+
// relative reference, a pending template — must finish there
|
|
488
|
+
// BEFORE the copy is taken: cloned earlier, the copy's rebased
|
|
489
|
+
// relative refs dangle under the generator and the model dies
|
|
490
|
+
// as *_no_gen with the generator never firing. Deferring here
|
|
491
|
+
// is exactly the documented staging rule: the generator waits
|
|
492
|
+
// for the source, then snapshots it whole.
|
|
493
|
+
else if (null != out && !snap && true === (ctx as any).argsnap &&
|
|
494
|
+
!out.done) {
|
|
495
|
+
out = undefined
|
|
496
|
+
}
|
|
424
497
|
// Types and hidden values are cloned and made concrete
|
|
425
498
|
else if (null != out) { // && (out.mark.type || out.mark.hide)) {
|
|
426
499
|
|
|
@@ -449,6 +522,19 @@ class RefVal extends FeatureVal {
|
|
|
449
522
|
walk(out, (_key: string | number | undefined, val: Val) => {
|
|
450
523
|
val.mark.type = false
|
|
451
524
|
val.mark.hide = false
|
|
525
|
+
// REFERENCES DO NOT CARRY IDENTITY (G4 phase 1, clearing
|
|
526
|
+
// rule 1). The clone is a copy of an entity, not the
|
|
527
|
+
// entity: without this, `w:b:$.q.a & {y:2,z:3}` (row
|
|
528
|
+
// `ref-and-merge`, test/spec/ref.tsv) would push `y:2`
|
|
529
|
+
// back into `q.a` through the identity merge — pinned
|
|
530
|
+
// behaviour, silently changed by a mark the author never
|
|
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
|
|
452
538
|
return val
|
|
453
539
|
})
|
|
454
540
|
//}
|
|
@@ -468,33 +554,80 @@ class RefVal extends FeatureVal {
|
|
|
468
554
|
// chain revisits a node -- a PROVEN reference cycle, distinct from a
|
|
469
555
|
// merely unresolved reference. Detection is only on the resolution
|
|
470
556
|
// chain revisiting a node, never on syntactic shape: a chain that
|
|
471
|
-
// passes through a variable segment, a conjunct
|
|
472
|
-
//
|
|
557
|
+
// passes through a variable segment, a conjunct or any other non-ref
|
|
558
|
+
// value yields no proof and the ref defers as before.
|
|
559
|
+
//
|
|
560
|
+
// A FUNCTION is followed, through its arguments (issue #35). A
|
|
561
|
+
// function resolves only once every argument does, so a chain that
|
|
562
|
+
// reaches `b:upper($.a)` and out through `$.a` has proved the same
|
|
563
|
+
// dependency a bare `b:$.a` proves -- `a:$.b b:upper($.a)` is a cycle
|
|
564
|
+
// whichever link wears the call. Without this the shape exhausted the
|
|
565
|
+
// depth budget instead: a `unify_cycle`, which under the G5 taxonomy
|
|
566
|
+
// means "retry with more may help", where a proven structural cycle is
|
|
567
|
+
// FIX THE MODEL. A conjunct and a disjunct stay unfollowed for reasons
|
|
568
|
+
// that are not the same: a disjunct member may simply not be taken, so
|
|
569
|
+
// reaching one proves nothing; a conjunct would be sound to follow, and
|
|
570
|
+
// is left out only because nothing needs it yet.
|
|
571
|
+
//
|
|
572
|
+
// The Go port reaches the same verdict on this shape by a DIFFERENT
|
|
573
|
+
// arm, and that difference outlives this method: its clonePath re-paths
|
|
574
|
+
// a resolved clone to the referring site, so the inner `$.a` lands at
|
|
575
|
+
// path [a] and the plain isprefixpath test proves the cycle before any
|
|
576
|
+
// chase is needed. TypeScript's clone keeps the source paths. ADR-001
|
|
577
|
+
// asks for arm-for-arm correspondence, so the clone-path difference is
|
|
578
|
+
// worth closing on its own; it is wider than this issue and both ports
|
|
579
|
+
// now agree on the verdict and the code either way.
|
|
473
580
|
detectRefCycle(ctx: AontuContext): boolean {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
581
|
+
// Depth-first with an explicit ANCESTOR set, because a function may
|
|
582
|
+
// carry several reference arguments and the cycle can run through
|
|
583
|
+
// any one of them. The set holds the chain currently being walked,
|
|
584
|
+
// not every node ever walked: revisiting a node reached down a
|
|
585
|
+
// DIFFERENT branch is an ordinary shared reference (two keys reading
|
|
586
|
+
// one third key), and only revisiting one that is still open above
|
|
587
|
+
// us is a cycle.
|
|
588
|
+
//
|
|
589
|
+
// Identity is the RESOLVED PATH, not the RefVal instance: the same
|
|
590
|
+
// target can be reached through distinct ref instances, and it is
|
|
591
|
+
// returning to the same place that closes a loop.
|
|
592
|
+
const chase = (ref: RefVal, ancestors: Set<string>): boolean => {
|
|
593
|
+
const rp = ref.plainRefPath()
|
|
486
594
|
if (null == rp) {
|
|
487
595
|
return false
|
|
488
596
|
}
|
|
597
|
+
const key = rp.join('')
|
|
598
|
+
if (ancestors.has(key)) {
|
|
599
|
+
return true
|
|
600
|
+
}
|
|
601
|
+
|
|
489
602
|
let node: any = ctx.root
|
|
490
603
|
for (let i = 0; i < rp.length && null != node; i++) {
|
|
491
604
|
node = (node.isMap || node.isList) ? node.peg[rp[i]] : undefined
|
|
492
605
|
}
|
|
493
|
-
if (null == node
|
|
606
|
+
if (null == node) {
|
|
494
607
|
return false
|
|
495
608
|
}
|
|
496
|
-
|
|
609
|
+
|
|
610
|
+
// Terminates: each level adds a path to `ancestors` and refuses a
|
|
611
|
+
// repeat, and the tree holds finitely many distinct paths.
|
|
612
|
+
ancestors.add(key)
|
|
613
|
+
let found = false
|
|
614
|
+
if (node.isRef) {
|
|
615
|
+
found = chase(node, ancestors)
|
|
616
|
+
}
|
|
617
|
+
else if (node.isFunc && Array.isArray(node.peg)) {
|
|
618
|
+
for (const arg of node.peg) {
|
|
619
|
+
if (null != arg && arg.isRef && chase(arg, ancestors)) {
|
|
620
|
+
found = true
|
|
621
|
+
break
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
ancestors.delete(key)
|
|
626
|
+
|
|
627
|
+
return found
|
|
497
628
|
}
|
|
629
|
+
|
|
630
|
+
return chase(this, new Set<string>())
|
|
498
631
|
}
|
|
499
632
|
|
|
500
633
|
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
// CHECKED, TYPED, LINK-SHAPED REFERENCES (G4 phase 2,
|
|
4
|
+
// docs/capability-review/g4-identity-relations.md): `refer(t)` is a
|
|
5
|
+
// constraint on a string-valued field. The string must be an ENTITY
|
|
6
|
+
// ADDRESS, the addressed node must exist in the evaluation, and — when
|
|
7
|
+
// `t` is given — `t` is unified INTO the target. The field's own value
|
|
8
|
+
// stays the address string: a LINK, not an embedding.
|
|
9
|
+
//
|
|
10
|
+
// This is the piece a plain reference cannot be. `$.a.b` resolves by
|
|
11
|
+
// CLONING its target into place, so `dependsOn: [$.services.auth]`
|
|
12
|
+
// generates a full copy of the auth node where the author meant a
|
|
13
|
+
// name. `refer` leaves the name and checks it.
|
|
14
|
+
//
|
|
15
|
+
// Constraint FLOW rather than a check: `refer(t)` does not merely test
|
|
16
|
+
// the target against `t`, it unifies `t` into it. Referring to
|
|
17
|
+
// something as a Service MAKES it one, and if it cannot be, the
|
|
18
|
+
// conflict is an ordinary located error. Check-only semantics would be
|
|
19
|
+
// non-monotone — true, then false as the target grows — and the
|
|
20
|
+
// lattice guarantee is that more information never falsifies what has
|
|
21
|
+
// been observed.
|
|
22
|
+
|
|
23
|
+
import type {
|
|
24
|
+
Val,
|
|
25
|
+
ValSpec,
|
|
26
|
+
} from '../type'
|
|
27
|
+
|
|
28
|
+
import {
|
|
29
|
+
DONE,
|
|
30
|
+
} from '../type'
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
AontuContext,
|
|
34
|
+
} from '../ctx'
|
|
35
|
+
|
|
36
|
+
import { makeNilErr } from '../err'
|
|
37
|
+
|
|
38
|
+
import { FuncBaseVal } from './FuncBaseVal'
|
|
39
|
+
import { FeatureVal } from './FeatureVal'
|
|
40
|
+
import { StringVal } from './StringVal'
|
|
41
|
+
import { unite } from '../unify'
|
|
42
|
+
import { top } from './top'
|
|
43
|
+
import { propagateMarks, walk } from '../utility'
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
// A segment of the path INSIDE an entity. The entity name's own
|
|
47
|
+
// grammar (no dots) is what makes the split unambiguous: everything
|
|
48
|
+
// before the first dot names the entity, everything after walks its
|
|
49
|
+
// value.
|
|
50
|
+
const ADDR_SEGMENT = /^[A-Za-z0-9_-]+$/
|
|
51
|
+
const ADDR_NAME = /^[A-Za-z0-9_/-]+$/
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
export type Address = {
|
|
55
|
+
name: string
|
|
56
|
+
path: string[]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
// The address a string spells, or undefined when it does not spell
|
|
61
|
+
// one. `svc/auth` is the entity; `svc/auth.ports.http` is a node
|
|
62
|
+
// inside it — the two addressing schemes reconciled: `$.a.b` answers
|
|
63
|
+
// WHERE, an address answers WHAT, and beneath entity granularity the
|
|
64
|
+
// tree is authoritative again.
|
|
65
|
+
export function parseAddress(s: string): Address | undefined {
|
|
66
|
+
const parts = s.split('.')
|
|
67
|
+
if (!ADDR_NAME.test(parts[0])) {
|
|
68
|
+
return undefined
|
|
69
|
+
}
|
|
70
|
+
for (const seg of parts.slice(1)) {
|
|
71
|
+
if (!ADDR_SEGMENT.test(seg)) {
|
|
72
|
+
return undefined
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { name: parts[0], path: parts.slice(1) }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
// The value an address names, or undefined when the evaluation does
|
|
80
|
+
// not (yet) have one. Pending is not failure: an entity may be
|
|
81
|
+
// declared by a later conjunct, include or spread, so `refer`
|
|
82
|
+
// residuates exactly as a forward reference does.
|
|
83
|
+
export function findEntity(
|
|
84
|
+
reg: Map<string, Val> | undefined, addr: Address
|
|
85
|
+
): { parent?: any, key?: string, val: Val } | undefined {
|
|
86
|
+
const rep: any = reg?.get(addr.name)
|
|
87
|
+
if (null == rep) {
|
|
88
|
+
return undefined
|
|
89
|
+
}
|
|
90
|
+
let parent: any = undefined
|
|
91
|
+
let key: string | undefined = undefined
|
|
92
|
+
let val: any = rep
|
|
93
|
+
for (const seg of addr.path) {
|
|
94
|
+
if (true !== val?.isMap && true !== val?.isList) {
|
|
95
|
+
return undefined
|
|
96
|
+
}
|
|
97
|
+
const next = val.peg[seg]
|
|
98
|
+
if (null == next) {
|
|
99
|
+
return undefined
|
|
100
|
+
}
|
|
101
|
+
parent = val
|
|
102
|
+
key = seg
|
|
103
|
+
val = next
|
|
104
|
+
}
|
|
105
|
+
return { parent, key, val }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
// concreteFlow is `t` as it enters the target: a copy with the
|
|
110
|
+
// type/hide marks cleared at every depth. The clone matters as much as
|
|
111
|
+
// the clearing — `t` is shared by every position that refers to the
|
|
112
|
+
// same thing, and clearing in place would unmark the schema itself.
|
|
113
|
+
function concreteFlow(ctx: AontuContext, t: Val): Val {
|
|
114
|
+
let marked = false
|
|
115
|
+
walk(t, (_key: string | number | undefined, v: Val) => {
|
|
116
|
+
marked = marked || v.mark.type || v.mark.hide
|
|
117
|
+
return v
|
|
118
|
+
})
|
|
119
|
+
// An unmarked flow type is passed THROUGH: cloning one anyway would
|
|
120
|
+
// move the site an error names, and a conflict has to point at what
|
|
121
|
+
// the author wrote.
|
|
122
|
+
if (!marked) {
|
|
123
|
+
return t
|
|
124
|
+
}
|
|
125
|
+
const out = t.clone(ctx)
|
|
126
|
+
walk(out, (_key: string | number | undefined, v: Val) => {
|
|
127
|
+
v.mark.type = false
|
|
128
|
+
v.mark.hide = false
|
|
129
|
+
return v
|
|
130
|
+
})
|
|
131
|
+
return out
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
// ReferVal is what `refer(t)` RESOLVES to: the residual constraint,
|
|
136
|
+
// carrying the type to flow and — once it has met a string — the
|
|
137
|
+
// address to flow it into. A separate value from the function for the
|
|
138
|
+
// reason every residual is: the function is written once and the
|
|
139
|
+
// constraint is met many times, and only the constraint has state
|
|
140
|
+
// worth carrying.
|
|
141
|
+
class ReferVal extends FeatureVal {
|
|
142
|
+
isRefer = true
|
|
143
|
+
isGenable = true
|
|
144
|
+
|
|
145
|
+
// The type to flow into the target; TOP when `refer()` was written
|
|
146
|
+
// with no argument.
|
|
147
|
+
tval: Val
|
|
148
|
+
// The address, once a string has been met.
|
|
149
|
+
addr?: Address
|
|
150
|
+
// The address AS WRITTEN, for canon and for the error message.
|
|
151
|
+
addrsrc?: string
|
|
152
|
+
// Constraints met while the address was still pending — a kind, a
|
|
153
|
+
// regex, a preference. They meet the LINK once there is one.
|
|
154
|
+
held?: Val
|
|
155
|
+
|
|
156
|
+
constructor(spec: ValSpec, ctx?: AontuContext) {
|
|
157
|
+
super(spec, ctx)
|
|
158
|
+
this.tval = (spec as any).tval ?? top()
|
|
159
|
+
this.addr = (spec as any).addr
|
|
160
|
+
this.addrsrc = (spec as any).addrsrc
|
|
161
|
+
this.held = (spec as any).held
|
|
162
|
+
this.dc = 0
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// The residual's own state — the type to flow, the address it has
|
|
166
|
+
// met, the constraints it holds — TRAVELS with the clone. A spread
|
|
167
|
+
// template holds the FUNCTION, so a template never needs this; a
|
|
168
|
+
// REFERENCE to a value that already contains a resolved link does
|
|
169
|
+
// (`z: id(a) & {u: refer() & "a"}` then `s: $.z`). Without it the
|
|
170
|
+
// clone came back as a bare `refer()` — the address silently
|
|
171
|
+
// dropped, and the copied link resolving to nothing.
|
|
172
|
+
//
|
|
173
|
+
// No path-dependence hook, though: a residual is minted at its
|
|
174
|
+
// destination, so `key()` inside a template resolves there already.
|
|
175
|
+
clone(ctx: AontuContext, spec?: ValSpec): Val {
|
|
176
|
+
const out: any = super.clone(ctx, spec)
|
|
177
|
+
out.tval = this.tval
|
|
178
|
+
out.addr = this.addr
|
|
179
|
+
out.addrsrc = this.addrsrc
|
|
180
|
+
out.held = this.held
|
|
181
|
+
return out
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
unify(peer: Val, ctx: AontuContext): Val {
|
|
185
|
+
const p: any = peer
|
|
186
|
+
|
|
187
|
+
// Another `refer` at the same position: one constraint, both
|
|
188
|
+
// types. `refer(A) & refer(B)` is a target that must be both.
|
|
189
|
+
if (true === p?.isRefer) {
|
|
190
|
+
return this.with(ctx, {
|
|
191
|
+
tval: unite(ctx, this.tval, p.tval, 'refer-t'),
|
|
192
|
+
addr: this.addr ?? p.addr,
|
|
193
|
+
addrsrc: this.addrsrc ?? p.addrsrc,
|
|
194
|
+
held: null == this.held ? p.held
|
|
195
|
+
: null == p.held ? this.held
|
|
196
|
+
: unite(ctx, this.held, p.held, 'refer-held'),
|
|
197
|
+
}, this)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (null == peer || true === p.isTop) {
|
|
201
|
+
return this.settle(ctx, this)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (true === p.isNil) {
|
|
205
|
+
return peer
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// A STRING is the ADDRESS, when there is not one yet. It is the
|
|
209
|
+
// only thing that can be: a link's value is its address.
|
|
210
|
+
if (undefined === this.addr
|
|
211
|
+
&& true === p.isScalar && 'string' === typeof p.peg) {
|
|
212
|
+
const addr = parseAddress(p.peg)
|
|
213
|
+
if (undefined === addr) {
|
|
214
|
+
return makeNilErr(ctx, 'refer_address', this, peer, 'refer',
|
|
215
|
+
{ addr: p.peg })
|
|
216
|
+
}
|
|
217
|
+
return this.with(ctx, { addr, addrsrc: p.peg }, peer)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// A value that can never BE a string cannot constrain one either,
|
|
221
|
+
// and no later pass can repair it — so this arm refuses rather
|
|
222
|
+
// than defers. A KIND or a constraint is not in it: `string`,
|
|
223
|
+
// `re("^svc/")` and the like are perfectly good constraints on an
|
|
224
|
+
// address, and are held below until there is one to apply them to.
|
|
225
|
+
if ((true === p.isScalar && 'string' !== typeof p.peg)
|
|
226
|
+
|| true === p.isMap || true === p.isList) {
|
|
227
|
+
return makeNilErr(ctx, 'refer_address', this, peer, 'refer')
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// HELD: everything else waits for the address. Carried on the
|
|
231
|
+
// residual rather than parked in a conjunct, because a conjunct
|
|
232
|
+
// rebuilt every pass grows a level every pass; the held constraint
|
|
233
|
+
// meets the link the moment the address resolves, so
|
|
234
|
+
// `refer() & "x" & "y"` still conflicts and `refer() & string & "x"`
|
|
235
|
+
// still passes.
|
|
236
|
+
return this.with(ctx, {
|
|
237
|
+
held: null == this.held ? peer : unite(ctx, this.held, peer, 'refer-held'),
|
|
238
|
+
}, this)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// with is the residual reshaped: every arm above answers a NEW
|
|
242
|
+
// ReferVal rather than mutating this one, because a spread template's
|
|
243
|
+
// residual is shared by every child it is applied to.
|
|
244
|
+
with(ctx: AontuContext, spec: any, site: Val): Val {
|
|
245
|
+
const out = new ReferVal({}, ctx)
|
|
246
|
+
out.tval = spec.tval ?? this.tval
|
|
247
|
+
out.addr = spec.addr ?? this.addr
|
|
248
|
+
out.addrsrc = spec.addrsrc ?? this.addrsrc
|
|
249
|
+
out.held = spec.held ?? this.held
|
|
250
|
+
propagateMarks(this, out)
|
|
251
|
+
out.site = site.site
|
|
252
|
+
out.path = this.path
|
|
253
|
+
return out.settle(ctx, site)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// settle answers the address if the evaluation can, and stays
|
|
257
|
+
// pending if it cannot YET. `site` is the value whose position the
|
|
258
|
+
// resolved string should take.
|
|
259
|
+
settle(ctx: AontuContext, site: Val): Val {
|
|
260
|
+
if (undefined === this.addr) {
|
|
261
|
+
// NOT DONE, unlike `string` or `min(1)`. A refer without an
|
|
262
|
+
// address has not done its work — it exists to check one — and
|
|
263
|
+
// the pass loop must keep offering it the chance. The cost is
|
|
264
|
+
// that a SCHEMA mentioning a link never resolves either, so
|
|
265
|
+
// `type({from: refer($.std.Port)})` is not expressible today;
|
|
266
|
+
// G4 phase 4 records why, and what it would take.
|
|
267
|
+
this.dc = 0
|
|
268
|
+
return this
|
|
269
|
+
}
|
|
270
|
+
const reg: Map<string, Val> | undefined = (ctx as any)?.entities
|
|
271
|
+
const found = findEntity(reg, this.addr)
|
|
272
|
+
if (undefined === found) {
|
|
273
|
+
// PENDING, not failed — until the last pass. An entity may be
|
|
274
|
+
// declared by a later conjunct, include or spread, so `refer`
|
|
275
|
+
// residuates as a forward reference does; but within ONE
|
|
276
|
+
// evaluation the document-set is fixed, so existence IS
|
|
277
|
+
// decidable, and the final pass is where it is decided. A
|
|
278
|
+
// pending refer keeps the tree not-done, so the pass loop always
|
|
279
|
+
// reaches that pass when there is one to decide.
|
|
280
|
+
if (ctx.cc + 1 >= ctx.budget.passes) {
|
|
281
|
+
return makeNilErr(ctx, 'refer_unresolved', this, undefined, 'refer',
|
|
282
|
+
{ addr: this.addrsrc as string })
|
|
283
|
+
}
|
|
284
|
+
this.dc = 0
|
|
285
|
+
return this
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// THE FLOW. `t` is unified into the target and written back, so
|
|
289
|
+
// every position of the entity carries it after the pass's
|
|
290
|
+
// identity merge — the same channel the merge itself uses.
|
|
291
|
+
//
|
|
292
|
+
// RE-ENTRANT ONLY ONCE PER ENTITY (use-cases/BUGS.md §19). Uniting
|
|
293
|
+
// the target drives the target's OWN subtree, and if the target
|
|
294
|
+
// links back — `a` typed-refers `b`, `b` typed-refers `a`, the
|
|
295
|
+
// shape every inverse pair has — that drives this entity again,
|
|
296
|
+
// and the two flow into each other until the depth budget or the
|
|
297
|
+
// host stack ends it. `unify_cycle` on a model whose meet plainly
|
|
298
|
+
// converges: `{k:1}` meeting `{k:1}` is a fixpoint, and the
|
|
299
|
+
// evaluator never got far enough to notice.
|
|
300
|
+
//
|
|
301
|
+
// The guard is the set of entities a flow is currently inside, on
|
|
302
|
+
// the context. A flow that would re-enter one is SKIPPED, not
|
|
303
|
+
// failed: the outer flow it is nested in is already uniting that
|
|
304
|
+
// entity, so the same information arrives by the same channel one
|
|
305
|
+
// frame up. What each flow contributes is unchanged; only the
|
|
306
|
+
// order it arrives in is, and unification does not care.
|
|
307
|
+
const flowing: Set<string> = ((ctx as any)._referflow ??=
|
|
308
|
+
new Set<string>())
|
|
309
|
+
if (!this.tval.isTop && !flowing.has(this.addr.name)) {
|
|
310
|
+
flowing.add(this.addr.name)
|
|
311
|
+
try {
|
|
312
|
+
// The flowed type is CONCRETE at the target: a schema flowing
|
|
313
|
+
// into a value must not make the value a schema. Same reasoning
|
|
314
|
+
// as a reference's clone clearing marks — `refer($.std.Service)`
|
|
315
|
+
// says the target IS a Service, not that it is the definition
|
|
316
|
+
// of one — and without it the target silently stopped
|
|
317
|
+
// generating.
|
|
318
|
+
const merged = unite(ctx, found.val, concreteFlow(ctx, this.tval),
|
|
319
|
+
'refer-flow')
|
|
320
|
+
if (true === (merged as any).isNil) {
|
|
321
|
+
return merged
|
|
322
|
+
}
|
|
323
|
+
if (undefined === found.parent) {
|
|
324
|
+
reg!.set(this.addr.name, merged)
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
found.parent.peg[found.key as string] = merged
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
finally {
|
|
331
|
+
flowing.delete(this.addr.name)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// The value IS the address string: a link, not an embedding.
|
|
336
|
+
const out: any = new StringVal({ peg: this.addrsrc as string }, ctx)
|
|
337
|
+
out.dc = DONE
|
|
338
|
+
// STAMPED as a link (G4 phase 3): the value is the address string,
|
|
339
|
+
// so without this nothing downstream could tell a checked link from
|
|
340
|
+
// a literal that happens to look like one. The edge set is exactly
|
|
341
|
+
// the set of these stamps.
|
|
342
|
+
out.link = this.addrsrc
|
|
343
|
+
propagateMarks(this, out)
|
|
344
|
+
out.site = site.site
|
|
345
|
+
out.path = this.path
|
|
346
|
+
return null == this.held ? out : unite(ctx, out, this.held, 'refer-held')
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
get canon() {
|
|
350
|
+
const t = this.tval.isTop ? '' : this.tval.canon
|
|
351
|
+
const call = 'refer(' + t + ')' +
|
|
352
|
+
(null == this.held ? '' : '&' + this.held.canon)
|
|
353
|
+
return undefined === this.addrsrc
|
|
354
|
+
? call : call + '&' + JSON.stringify(this.addrsrc)
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class ReferFuncVal extends FuncBaseVal {
|
|
360
|
+
isReferFunc = true
|
|
361
|
+
|
|
362
|
+
constructor(spec: ValSpec, ctx?: AontuContext) {
|
|
363
|
+
super(spec, ctx)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
make(_ctx: AontuContext, spec: ValSpec): Val {
|
|
367
|
+
return new ReferFuncVal(spec)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
funcname() {
|
|
371
|
+
return 'refer'
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
resolve(ctx: AontuContext, args: Val[]) {
|
|
375
|
+
const out = new ReferVal({}, ctx)
|
|
376
|
+
out.tval = 0 < args.length ? args[0] : top()
|
|
377
|
+
out.site = this.site
|
|
378
|
+
out.path = this.path
|
|
379
|
+
return out
|
|
380
|
+
}
|
|
381
|
+
} /* node:coverage ignore next 6 */
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
export {
|
|
385
|
+
ReferFuncVal,
|
|
386
|
+
ReferVal,
|
|
387
|
+
}
|