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/subsume.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
constraintAdmitsScalar,
|
|
33
33
|
} from './val/ConstraintVal'
|
|
34
34
|
import { kindSubsumes } from './val/ScalarKindVal'
|
|
35
|
+
import { prefixMeet } from './val/PathVal'
|
|
35
36
|
import { prefInnerPeg } from './val/PrefVal'
|
|
36
37
|
|
|
37
38
|
|
|
@@ -360,6 +361,22 @@ export function subsumeNode(
|
|
|
360
361
|
return 'no'
|
|
361
362
|
}
|
|
362
363
|
|
|
364
|
+
// Container kinds (docs/design/PATHS.0.md): `map()` subsumes every
|
|
365
|
+
// map and itself, `list()` every list. The unit literals (`{}`,
|
|
366
|
+
// `[]`) already subsume through the container rules; only the kind
|
|
367
|
+
// former needs an arm.
|
|
368
|
+
if (true === (g as any)?.isContainerKind) {
|
|
369
|
+
const ga: any = g
|
|
370
|
+
const sa: any = s
|
|
371
|
+
if ((true === ga.isMapKind && (true === sa?.isMap || true === sa?.isMapKind))
|
|
372
|
+
|| (true === ga.isListKind && (true === sa?.isList || true === sa?.isListKind))) {
|
|
373
|
+
return 'yes'
|
|
374
|
+
}
|
|
375
|
+
record(state, 'compat_narrowed', path, g, s,
|
|
376
|
+
'the general container kind admits no such value')
|
|
377
|
+
return 'no'
|
|
378
|
+
}
|
|
379
|
+
|
|
363
380
|
// Constraint residuals.
|
|
364
381
|
if (true === g?.isConstraint) {
|
|
365
382
|
if (true === s?.isConstraint) {
|
|
@@ -396,8 +413,14 @@ export function subsumeNode(
|
|
|
396
413
|
}
|
|
397
414
|
|
|
398
415
|
// Concrete scalars subsume only themselves (identity compares kind
|
|
399
|
-
// as well as value)
|
|
416
|
+
// as well as value) -- except paths, whose meet is the prefix rule
|
|
417
|
+
// (ADR-016): a prefix admits every extension of itself, so it
|
|
418
|
+
// subsumes one, exactly as the meet answers the longer.
|
|
400
419
|
if (true === g?.isScalar) {
|
|
420
|
+
if (true === g.isPath && true === (s as any)?.isPath &&
|
|
421
|
+
prefixMeet(g.peg, (s as any).peg) === (s as any).peg) {
|
|
422
|
+
return 'yes'
|
|
423
|
+
}
|
|
401
424
|
if (true === s?.isScalar && true === g.same?.(s)) {
|
|
402
425
|
return 'yes'
|
|
403
426
|
}
|
|
@@ -431,13 +454,29 @@ export function subsumeNode(
|
|
|
431
454
|
(v: any, k: string) => v.peg[Number(k)])
|
|
432
455
|
}
|
|
433
456
|
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
//
|
|
457
|
+
// THE LADDER IS NOT TOTAL, and the formers that fall past it are the
|
|
458
|
+
// ones the evaluator MEANS to leave standing: a recursion, a
|
|
459
|
+
// relation and its graph atom, a `refer()` target constraint. None
|
|
460
|
+
// of them describes a set of values a structural walk can compare,
|
|
461
|
+
// so `undecided` is the honest answer for two DIFFERENT ones.
|
|
462
|
+
//
|
|
463
|
+
// For two IDENTICAL ones it is not. REFLEXIVITY IS A LAW -- every
|
|
464
|
+
// value admits itself -- and identity is the HASH FORM, the same
|
|
465
|
+
// rule the unresolved branch above applies and for the same reason:
|
|
466
|
+
// it costs nothing, because it runs only where the answer would
|
|
467
|
+
// otherwise be `undecided`. Without it a document that declares a
|
|
468
|
+
// relation, shares a template by reference or alias, or recurses did
|
|
469
|
+
// not subsume ITSELF, so `breaking` on the idiom the language exists
|
|
470
|
+
// for hard-failed and had to run --allow-undecided, which masks the
|
|
471
|
+
// genuine undecideds it exists to surface (use-cases/BUGS.md 64,
|
|
472
|
+
// and 28 before it).
|
|
473
|
+
//
|
|
474
|
+
// A NIL is the exception, and the reason the law is spelled here
|
|
475
|
+
// rather than in `unresolved`: a nil is not a value, so it admits
|
|
476
|
+
// nothing, itself included.
|
|
477
|
+
if (true !== g?.isNil && true !== s?.isNil && hcanon(g) === hcanon(s)) {
|
|
478
|
+
return 'yes'
|
|
479
|
+
}
|
|
441
480
|
record(state, 'sub_unresolved', path, g, s,
|
|
442
481
|
'no subsumption rule covers this pair of value formers')
|
|
443
482
|
return 'undecided'
|
|
@@ -510,11 +549,21 @@ function subsumeBag(
|
|
|
510
549
|
}
|
|
511
550
|
|
|
512
551
|
// Spread templates: a path-dependent template's meaning depends on
|
|
513
|
-
// where it lands, which no structural comparison can decide
|
|
552
|
+
// where it lands, which no structural comparison can decide -- UNLESS
|
|
553
|
+
// the two templates are the same template. REFLEXIVITY IS A LAW and
|
|
554
|
+
// identity is the HASH FORM (the same rule the unresolved branch of
|
|
555
|
+
// subsumeNode applies): two byte-identical templates admit the same
|
|
556
|
+
// set wherever they land, so a document with a reference- or
|
|
557
|
+
// alias-valued template subsumes itself, and the comparison either
|
|
558
|
+
// side of the template is decided on its own merits rather than
|
|
559
|
+
// dragged to `undecided` (use-cases/BUGS.md 64). Where they are NOT
|
|
560
|
+
// identical, nothing structural can decide them and the fold stands.
|
|
514
561
|
const gcj = g.spread?.cj
|
|
515
562
|
const scj = s.spread?.cj
|
|
516
563
|
if (null != gcj || null != scj) {
|
|
517
|
-
|
|
564
|
+
const sameTemplate = null != gcj && null != scj && hcanon(gcj) === hcanon(scj)
|
|
565
|
+
if (!sameTemplate &&
|
|
566
|
+
(true === gcj?.isPathDependent || true === scj?.isPathDependent)) {
|
|
518
567
|
record(state, 'sub_path_dependent_spread', path, gcj ?? g, scj ?? s,
|
|
519
568
|
'a path-dependent spread template cannot be compared structurally')
|
|
520
569
|
worse('undecided')
|
package/src/unify.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { AontuContext } from './ctx'
|
|
|
8
8
|
import { DONE } from './type'
|
|
9
9
|
|
|
10
10
|
import { makeNilErr } from './err'
|
|
11
|
+
import { findAt } from './val/ReferFuncVal'
|
|
11
12
|
|
|
12
13
|
import { NilVal } from './val/NilVal'
|
|
13
14
|
import { hasPlace } from './val/PlaceVal'
|
|
@@ -111,13 +112,20 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
111
112
|
&& !a.isMap && !a.isList
|
|
112
113
|
&& !a.isConjunct && !a.isDisjunct
|
|
113
114
|
&& !a.isRef && !a.isPref && !a.isFunc && !a.isExpect
|
|
114
|
-
// NOT two TOPs
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
// NOT two TOPs: every top has the same (absent) peg, so
|
|
116
|
+
// this path would treat any two as the same value and
|
|
117
|
+
// return one of them whole — dropping a rider the other
|
|
118
|
+
// carries. The slow path answers the same thing for two
|
|
119
|
+
// plain tops, so nothing is lost by declining the shortcut.
|
|
120
|
+
&& !a.isTop && !b.isTop
|
|
121
|
+
// NOT two rel residuals (RELATIONS P1) for the same
|
|
122
|
+
// reason: a settled rel is DONE with an absent peg, so
|
|
123
|
+
// any two matched here — dropping one side's type and
|
|
124
|
+
// held constraints. RelVal.unify merges them instead.
|
|
125
|
+
// NOT two graph atoms (P2) either: acyclic() and
|
|
126
|
+
// inverse(x) share a constructor and an absent peg
|
|
127
|
+
// without being the same declaration.
|
|
128
|
+
&& !a.isRel && !a.isGraphAtom && !a.isRecurse) {
|
|
121
129
|
// The deprecation record survives the fast path too (G3):
|
|
122
130
|
// `deprecate(5) & 5` short-circuits here.
|
|
123
131
|
if (null == a.deprecation && null != b.deprecation) {
|
|
@@ -222,14 +230,24 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
222
230
|
// purpose -- every other operator meets its peer the way it
|
|
223
231
|
// always has, through the conjunct fold that drives it.
|
|
224
232
|
|| (b.isOp && hasPlace(b))
|
|
233
|
+
// A graph atom DRIVES (RELATIONS P2): its peer is the value
|
|
234
|
+
// it rides beside -- a container, a rel, a scalar -- and none
|
|
235
|
+
// of them know the atom; the atom knows to residuate.
|
|
236
|
+
|| b.isGraphAtom
|
|
237
|
+
// The recursive residual DRIVES for the same reason: its peer
|
|
238
|
+
// is the concrete structure it expands against.
|
|
239
|
+
|| b.isRecurse
|
|
225
240
|
) {
|
|
226
241
|
out = b.unify(a, te ? ctx.clone({ explain: ec(te, 'BW') }) : ctx)
|
|
227
242
|
unified = true
|
|
228
243
|
why = 'bv'
|
|
229
244
|
}
|
|
230
245
|
// Exactly equal scalars (not caught by early fast-path — e.g.
|
|
231
|
-
// because a or b isn't .done yet).
|
|
232
|
-
|
|
246
|
+
// because a or b isn't .done yet). Rel residuals are excluded
|
|
247
|
+
// exactly as in the fast path: their pegs are equally absent
|
|
248
|
+
// without the values being the same relation.
|
|
249
|
+
else if (a.constructor === b.constructor && a.peg === b.peg
|
|
250
|
+
&& !a.isRel && !a.isGraphAtom && !a.isRecurse) {
|
|
233
251
|
out = update(a, b)
|
|
234
252
|
why = 'up'
|
|
235
253
|
}
|
|
@@ -297,24 +315,6 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
297
315
|
ctx.prov.record(ctx.path, a, b, out)
|
|
298
316
|
}
|
|
299
317
|
|
|
300
|
-
// The IDENTITY survives every meet (G4 phase 1), by the same
|
|
301
|
-
// channel and for the same reason as the deprecation record below.
|
|
302
|
-
// TWO DIFFERENT NAMES on one node is a contradiction, not a merge:
|
|
303
|
-
// one node cannot be two entities, and the error names both sites.
|
|
304
|
-
if (null != out && true === (out as any).isVal && !out.isNil) {
|
|
305
|
-
const ae = null != a ? a.entity : undefined
|
|
306
|
-
const be = null != b ? b.entity : undefined
|
|
307
|
-
if (null != ae && null != be && ae !== be) {
|
|
308
|
-
out = makeNilErr(ctx, 'id_conflict', a, b)
|
|
309
|
-
}
|
|
310
|
-
else if (!out.isTop) {
|
|
311
|
-
const e = ae ?? be
|
|
312
|
-
if (null != e) {
|
|
313
|
-
out.entity = e
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
318
|
// The deprecation record survives EVERY meet (G3 phase 4): the
|
|
319
319
|
// boolean marks have their own sweeps (ConjunctVal, the bag walks),
|
|
320
320
|
// but a record lost in one meet shape is a use the tooling never
|
|
@@ -364,99 +364,69 @@ function residuePaths(v: Val, max: number): string[] {
|
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
|
|
367
|
-
//
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
// it is a meet, and a contradiction between two declarations is an
|
|
371
|
-
// ordinary conflict naming both sites.
|
|
367
|
+
// THE TYPE FLOW, APPLIED (G4 phase 2). `refer(t)` unifies `t` INTO the
|
|
368
|
+
// node it addresses, which is a write at a position the meet is not
|
|
369
|
+
// currently at -- the one non-local effect in the evaluator.
|
|
372
370
|
//
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
371
|
+
// It cannot be only a write made during the pass. A pass BUILDS a new
|
|
372
|
+
// tree from the old one, and `ctx.root` during pass N is pass N-1's
|
|
373
|
+
// result; a subtree rebuilt by pass N (which is exactly what happens
|
|
374
|
+
// when the link sits inside its own target, or when two nodes link at
|
|
375
|
+
// each other) drops a write made into the previous one. So each flow is
|
|
376
|
+
// also RECORDED, keyed by the target's path, and re-applied to the
|
|
377
|
+
// pass's own result here.
|
|
377
378
|
//
|
|
378
|
-
//
|
|
379
|
-
//
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
//
|
|
384
|
-
// is
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
//
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
// COLLECT, then APPLY — the same walk twice, not two walks. A single
|
|
393
|
-
// pass merges each position into the representative as it meets it,
|
|
394
|
-
// which leaves the positions it already passed holding the pre-merge
|
|
395
|
-
// value: `a: id(x) & {k:1}` kept `{k:1}` while `b: id(x) & {j:2}`
|
|
396
|
-
// became `{j:2,k:1}`, and the two sites disagreed about what the one
|
|
397
|
-
// entity is. The representative is therefore settled over the WHOLE
|
|
398
|
-
// tree before any position is written.
|
|
399
|
-
//
|
|
400
|
-
// `write` is which half is running. One function rather than two
|
|
401
|
-
// because the two halves differ in three lines and agree in the walk
|
|
402
|
-
// — and a walk written twice is a walk that drifts.
|
|
403
|
-
const walk = (node: any, seen: Set<any>, nctx: AontuContext,
|
|
404
|
-
write: boolean): any => {
|
|
405
|
-
if (null == node || true !== node.isVal) {
|
|
406
|
-
return node
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const name = (node as any).entity
|
|
410
|
-
if (null != name) {
|
|
411
|
-
if (write) {
|
|
412
|
-
// The SUBSTITUTION happens before the seen-guard, not after.
|
|
413
|
-
// Two positions of one entity hold the SAME object once a pass
|
|
414
|
-
// has merged them, so a guard that ran first would visit the
|
|
415
|
-
// first position, replace it with a newer representative, and
|
|
416
|
-
// then skip the second as already-seen — leaving it on the
|
|
417
|
-
// older value. That is exactly what a `refer(t)` flow
|
|
418
|
-
// produces: it writes a new representative mid-pass, and every
|
|
419
|
-
// position must take it.
|
|
420
|
-
const rep: any = reg.get(name)
|
|
421
|
-
if (null != rep && rep !== node) {
|
|
422
|
-
node = rep
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
else {
|
|
426
|
-
const rep = reg.get(name)
|
|
427
|
-
reg.set(name, null == rep || rep === node ? node :
|
|
428
|
-
unite(nctx, node, rep, 'entity'))
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// The guard bounds the DESCENT, which is all it was ever for: a
|
|
433
|
-
// unified tree is a graph, and a subtree is worth walking once.
|
|
434
|
-
if (seen.has(node)) {
|
|
435
|
-
return node
|
|
436
|
-
}
|
|
437
|
-
seen.add(node)
|
|
438
|
-
|
|
439
|
-
if ((true === node.isMap || true === node.isList) && null != node.peg) {
|
|
440
|
-
for (const k of Object.keys(node.peg)) {
|
|
441
|
-
const out = walk(node.peg[k], seen, nctx.descend(k), write)
|
|
442
|
-
if (write) {
|
|
443
|
-
node.peg[k] = out
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
return node
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
walk(root, new Set(), ctx, false)
|
|
451
|
-
|
|
452
|
-
// NOTHING TO APPLY. The collect half is also the "does this document
|
|
453
|
-
// use identity at all?" answer, so a document that never says `id()`
|
|
454
|
-
// pays for one walk per pass rather than two — and the writing half
|
|
455
|
-
// never runs over a tree it cannot change.
|
|
456
|
-
if (0 === reg.size) {
|
|
379
|
+
// Keyed by PATH, so there is no registry of names to collide in
|
|
380
|
+
// (ADR-014) -- the key is the position the address resolved to, and
|
|
381
|
+
// re-uniting the same type at the same position is idempotent, which is
|
|
382
|
+
// what makes replaying every recorded flow every pass correct rather
|
|
383
|
+
// than merely cheap.
|
|
384
|
+
//
|
|
385
|
+
// The pass loop is its only caller; it is EXPORTED for the test that
|
|
386
|
+
// pins the unresolved-path guard below (see there).
|
|
387
|
+
function applyFlows(ctx: AontuContext, root: Val): Val {
|
|
388
|
+
const flows: Map<string, Val> | undefined = (ctx as any).referflows
|
|
389
|
+
// NOTHING TO APPLY is the common case -- a document with no links
|
|
390
|
+
// pays one property load per pass, and the walk never runs.
|
|
391
|
+
if (null == flows || 0 === flows.size) {
|
|
457
392
|
return root
|
|
458
393
|
}
|
|
459
|
-
|
|
394
|
+
// Sorted, so two flows landing at overlapping positions arrive in the
|
|
395
|
+
// same order in both ports.
|
|
396
|
+
for (const key of [...flows.keys()].sort()) {
|
|
397
|
+
const path = key.split('\x00')
|
|
398
|
+
// The SHARED resolver, the one `refer` itself uses: a second
|
|
399
|
+
// descent written here would be a second answer to "what does this
|
|
400
|
+
// path name", and the two would drift (the Go twin calls the same
|
|
401
|
+
// findAt, arm for arm -- ADR-001).
|
|
402
|
+
//
|
|
403
|
+
// A RECORDED PATH THAT NO LONGER RESOLVES is skipped rather than
|
|
404
|
+
// refused: the record outliving its position is a question about
|
|
405
|
+
// the tree, and the link that named it answers it
|
|
406
|
+
// (test/spec/refer.tsv, `flow-target-moved-away`).
|
|
407
|
+
//
|
|
408
|
+
// NO DOCUMENT REACHES IT: a record is written only for a path that
|
|
409
|
+
// HAD resolved, and unification never takes a node back out of the
|
|
410
|
+
// tree — `move` copies and hides its source rather than removing
|
|
411
|
+
// it, which is the one rearrangement that looked like it would
|
|
412
|
+
// (probed in both ports: the flow still resolves on every pass,
|
|
413
|
+
// `flow-lands-then-its-parent-moves`). The guard is the contract
|
|
414
|
+
// for a rearrangement that does, and it is pinned by a direct call
|
|
415
|
+
// (`apply-flows-skips-a-record-that-stops-resolving` in
|
|
416
|
+
// ts/test/coverage3.test.ts) rather than by an ignore marker: node's
|
|
417
|
+
// `coverage ignore` drops LINES, and the gate reads BRANCH records,
|
|
418
|
+
// which survive it. The Go twin in go/unify.go can use the marker
|
|
419
|
+
// because that gate counts statements.
|
|
420
|
+
const found = findAt(root, path)
|
|
421
|
+
if (undefined === found) {
|
|
422
|
+
continue
|
|
423
|
+
}
|
|
424
|
+
const { parent, key: pkey, val: node } = found
|
|
425
|
+
const merged = unite(ctx.descend(pkey as string), node,
|
|
426
|
+
flows.get(key) as Val, 'refer-flow')
|
|
427
|
+
parent.peg[pkey as string] = merged
|
|
428
|
+
}
|
|
429
|
+
return root
|
|
460
430
|
}
|
|
461
431
|
|
|
462
432
|
|
|
@@ -514,11 +484,17 @@ class Unify {
|
|
|
514
484
|
// keyed by ref canon + source site, shared across all passes.
|
|
515
485
|
; (uctx as any).snapmap = new Map()
|
|
516
486
|
|
|
517
|
-
// The
|
|
518
|
-
//
|
|
519
|
-
//
|
|
520
|
-
//
|
|
521
|
-
|
|
487
|
+
// The RECORDED TYPE FLOWS (G4 phase 2): target path -> the type
|
|
488
|
+
// `refer(t)` unified into it, replayed onto each pass's result by
|
|
489
|
+
// applyFlows. Seeded HERE, on the unify root, for the reason the
|
|
490
|
+
// snapshot map above is: a descended context is Object.create'd
|
|
491
|
+
// from its parent, so a `??=` in the descendant would make its
|
|
492
|
+
// OWN map and the root would never see what was recorded.
|
|
493
|
+
; (uctx as any).referflows = new Map()
|
|
494
|
+
|
|
495
|
+
// The re-entrancy guard for those flows: the set of target paths
|
|
496
|
+
// a flow is currently inside. Same placement, same reason.
|
|
497
|
+
; (uctx as any)._referflow = new Set()
|
|
522
498
|
|
|
523
499
|
const explain = null == ctx?.explain ? undefined : ctx?.explain
|
|
524
500
|
const te = explain && explainOpen(uctx, explain, 'root', res)
|
|
@@ -582,9 +558,10 @@ class Unify {
|
|
|
582
558
|
// conjuncts) are pinned as vet.tsv's multi-* rows in both
|
|
583
559
|
// ports.
|
|
584
560
|
|
|
585
|
-
// The
|
|
586
|
-
//
|
|
587
|
-
|
|
561
|
+
// The recorded type flows, re-applied to the tree THIS pass
|
|
562
|
+
// built: a pass rebuilds subtrees, and a flow written into the
|
|
563
|
+
// previous pass's tree does not survive that.
|
|
564
|
+
res = applyFlows(uctx, res)
|
|
588
565
|
|
|
589
566
|
// The staging signal for the NEXT pass, rendered here rather
|
|
590
567
|
// than at the top of the loop so a model that is FINISHED is
|
|
@@ -635,5 +612,5 @@ export {
|
|
|
635
612
|
Unify,
|
|
636
613
|
unite,
|
|
637
614
|
withDepth,
|
|
638
|
-
|
|
615
|
+
applyFlows,
|
|
639
616
|
}
|
package/src/utility.ts
CHANGED
|
@@ -75,78 +75,19 @@ function deprecationMessage(d: Record<string, string>): string {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
// children that disagreed about a field would fail at a site that
|
|
84
|
-
// explains nothing.
|
|
78
|
+
// The canonical form of a value, wrapped in the RIDER it carries —
|
|
79
|
+
// the deprecation record (G3 phase 4) — reparseably, so
|
|
80
|
+
// `deprecate(x, m)` survives canon. Bags render their children through
|
|
81
|
+
// this (MapVal/ListVal canon), which is where a marked FIELD — the
|
|
82
|
+
// realistic case — lives.
|
|
85
83
|
//
|
|
86
|
-
//
|
|
87
|
-
// what they meant: `&: id(key()) & {…}` names each child distinctly,
|
|
88
|
-
// resolved per destination by the existing spreadClone machinery.
|
|
89
|
-
// Duck-typed on the `isIdFunc` flag rather than imported: this file
|
|
90
|
-
// is below the Val classes, and the identity function sits above
|
|
91
|
-
// them.
|
|
92
|
-
function constantIdFunc(v: any, seen?: Set<any>): any {
|
|
93
|
-
if (null == v || true !== v.isVal) {
|
|
94
|
-
return undefined
|
|
95
|
-
}
|
|
96
|
-
const s = seen ?? new Set()
|
|
97
|
-
if (s.has(v)) {
|
|
98
|
-
return undefined
|
|
99
|
-
}
|
|
100
|
-
s.add(v)
|
|
101
|
-
|
|
102
|
-
if (true === v.isIdFunc && true !== v.isPathDependent) {
|
|
103
|
-
return v
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const peg = v.peg
|
|
107
|
-
if (null != peg && 'object' === typeof peg) {
|
|
108
|
-
for (const k of Object.keys(peg)) {
|
|
109
|
-
const found = constantIdFunc(peg[k], s)
|
|
110
|
-
if (undefined !== found) {
|
|
111
|
-
return found
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return constantIdFunc(v.spread?.cj, s)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// The IDENTITY wrapper (G4 phase 1): `id("svc/auth")&{…}`, written
|
|
120
|
-
// as the conjunct an author writes, so canon reparses to the same
|
|
121
|
-
// entity. This deliberately differs from the type/hide MARKS, which
|
|
122
|
-
// canon drops (test/spec/marks.tsv, row `type-canon`): identity is
|
|
123
|
-
// semantic content, and G6's canon-hash must see it — two documents
|
|
124
|
-
// that disagree about which entity a node IS do not mean the same
|
|
125
|
-
// thing and must not hash alike.
|
|
126
|
-
//
|
|
127
|
-
// The name is JSON-quoted whatever it spells: `-` is not a bare-text
|
|
128
|
-
// character (test/spec/op-chars.tsv pins `a:6-2` as a parse error), so
|
|
129
|
-
// an unquoted `id(team-pay)` would not reparse.
|
|
130
|
-
function canonEntity(v: Val): string {
|
|
131
|
-
const c = v.canon
|
|
132
|
-
const e = v.entity
|
|
133
|
-
return null == e ? c : 'id(' + JSON.stringify(e) + ')&' + c
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// The canonical form of a value, wrapped in the RIDERS it carries —
|
|
138
|
-
// the identity (G4 phase 1) and the deprecation record (G3 phase 4) —
|
|
139
|
-
// reparseably, so `id(name) & x` and `deprecate(x, m)` survive canon.
|
|
140
|
-
// Bags render their children through this (MapVal/ListVal canon),
|
|
141
|
-
// which is where a marked FIELD — the realistic case — lives.
|
|
142
|
-
//
|
|
143
|
-
// The riders render HERE and not in the value's own `canon` for the
|
|
84
|
+
// The rider renders HERE and not in the value's own `canon` for the
|
|
144
85
|
// same reason the guard at the MapVal call site tests the isVal flag:
|
|
145
86
|
// a bag's canon recursion visits each child once, and a child that
|
|
146
87
|
// wrapped itself as well would render its subtree twice per level —
|
|
147
88
|
// 2^depth on a nested document.
|
|
148
89
|
function canonRiders(v: Val): string {
|
|
149
|
-
const c =
|
|
90
|
+
const c = v.canon
|
|
150
91
|
const d = v.deprecation
|
|
151
92
|
if (null == d) {
|
|
152
93
|
return c
|
|
@@ -327,7 +268,6 @@ function items(o: any) {
|
|
|
327
268
|
export {
|
|
328
269
|
items,
|
|
329
270
|
propagateMarks,
|
|
330
|
-
constantIdFunc,
|
|
331
271
|
canonRiders,
|
|
332
272
|
collectDeprecations,
|
|
333
273
|
walkBagVals,
|