aontu 0.40.0 → 0.41.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.js +7 -0
- package/dist/aontu.js.map +1 -1
- package/dist/ctx.d.ts +9 -0
- package/dist/ctx.js +54 -8
- package/dist/ctx.js.map +1 -1
- package/dist/err.js +36 -15
- package/dist/err.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.js +85 -47
- package/dist/unify.js.map +1 -1
- package/dist/val/ConjunctVal.js +2 -2
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +36 -10
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +2 -2
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FuncBaseVal.js +2 -2
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/IntegerVal.js +1 -1
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/ListVal.js +7 -3
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.js +20 -12
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/NilVal.d.ts +2 -1
- package/dist/val/NilVal.js +15 -1
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/OpBaseVal.js +1 -1
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/PrefVal.js +3 -3
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RefVal.js +1 -1
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/Val.d.ts +2 -0
- package/dist/val/Val.js +88 -43
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +1 -1
- package/dist/val/VarVal.js.map +1 -1
- package/package.json +2 -2
- package/src/aontu.ts +9 -1
- package/src/ctx.ts +79 -8
- package/src/err.ts +37 -17
- package/src/tsconfig.json +2 -1
- package/src/unify.ts +86 -57
- package/src/val/ConjunctVal.ts +2 -2
- package/src/val/DisjunctVal.ts +37 -11
- package/src/val/ExpectVal.ts +2 -2
- package/src/val/FuncBaseVal.ts +2 -2
- package/src/val/IntegerVal.ts +1 -1
- package/src/val/ListVal.ts +7 -3
- package/src/val/MapVal.ts +20 -12
- package/src/val/NilVal.ts +17 -1
- package/src/val/OpBaseVal.ts +1 -1
- package/src/val/PrefVal.ts +3 -3
- package/src/val/RefVal.ts +1 -1
- package/src/val/Val.ts +139 -48
- package/src/val/VarVal.ts +1 -1
package/src/unify.ts
CHANGED
|
@@ -32,6 +32,34 @@ const MAXCYCLE = 999
|
|
|
32
32
|
// Vals should only have to unify downwards (in .unify) over Vals they understand.
|
|
33
33
|
// and for complex Vals, TOP, which means self unify if not yet done
|
|
34
34
|
const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
35
|
+
// Fast paths that don't recurse and so don't need cycle-detection:
|
|
36
|
+
// short-circuit before the saw-key build and seen-map lookup (which
|
|
37
|
+
// together cost ~2.5µs per call). Only return early when the result
|
|
38
|
+
// is already `done` — a non-done result would need the trailing
|
|
39
|
+
// top() unify below.
|
|
40
|
+
//
|
|
41
|
+
// A6a: same ref, already done
|
|
42
|
+
// A6b: different ref but same id + both done
|
|
43
|
+
// P1: exact-equal scalars that are already done (14% of calls
|
|
44
|
+
// in foo-sdk, ~100% with a.done=true)
|
|
45
|
+
if (a !== undefined && a !== null) {
|
|
46
|
+
if (a === b) {
|
|
47
|
+
if (a.done) return a
|
|
48
|
+
}
|
|
49
|
+
else if (b !== undefined && b !== null) {
|
|
50
|
+
if (a.done && b.done) {
|
|
51
|
+
if (a.id === b.id) return a
|
|
52
|
+
if (a.constructor === b.constructor && a.peg === b.peg
|
|
53
|
+
&& !a.isNil && !b.isNil
|
|
54
|
+
&& !a.isMap && !a.isList
|
|
55
|
+
&& !a.isConjunct && !a.isDisjunct
|
|
56
|
+
&& !a.isRef && !a.isPref && !a.isFunc && !a.isExpect) {
|
|
57
|
+
return a
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
35
63
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'unite', a, b)
|
|
36
64
|
|
|
37
65
|
let out = a
|
|
@@ -56,63 +84,67 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
56
84
|
ctx.seen[saw] = sawCount + 1
|
|
57
85
|
|
|
58
86
|
try {
|
|
59
|
-
|
|
60
87
|
let unified = false
|
|
61
88
|
|
|
62
|
-
|
|
89
|
+
// Dispatch ladder. Structure note:
|
|
90
|
+
// - `a == null` is degenerate (shouldn't happen in practice:
|
|
91
|
+
// the top-level call seeds with a real Val). Kept for safety.
|
|
92
|
+
// - TOP is the unit element: unifying with it returns the
|
|
93
|
+
// other side. Handle both sides.
|
|
94
|
+
// - Otherwise route by Val type. Complex Vals (Conjunct,
|
|
95
|
+
// Disjunct, Ref, Pref, Func, Expect) have their own unify
|
|
96
|
+
// that knows how to absorb the peer; prefer `a.unify` when
|
|
97
|
+
// `a` is complex, else `b.unify` when `b` is complex. If
|
|
98
|
+
// neither is complex and it's not a plain-scalar match, fall
|
|
99
|
+
// through to the generic `a.unify` (concrete Val classes
|
|
100
|
+
// each handle their own peer case).
|
|
101
|
+
if (a == null) {
|
|
63
102
|
out = b
|
|
64
103
|
why = 'b'
|
|
65
104
|
}
|
|
66
|
-
|
|
67
|
-
else if (a && (!b || b.isTop)) {
|
|
105
|
+
else if (b == null || b.isTop) {
|
|
68
106
|
out = a
|
|
69
107
|
why = 'a'
|
|
70
108
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
else {
|
|
112
|
-
out = a.unify(b, te ? ctx.clone({ explain: ec(te, 'GN') }) : ctx)
|
|
113
|
-
unified = true
|
|
114
|
-
why = 'ab'
|
|
115
|
-
}
|
|
109
|
+
else if (a.isTop) {
|
|
110
|
+
out = b
|
|
111
|
+
why = 'b'
|
|
112
|
+
}
|
|
113
|
+
else if (a.isNil) {
|
|
114
|
+
out = update(a, b)
|
|
115
|
+
why = 'an'
|
|
116
|
+
}
|
|
117
|
+
else if (b.isNil) {
|
|
118
|
+
out = update(b, a)
|
|
119
|
+
why = 'bn'
|
|
120
|
+
}
|
|
121
|
+
else if (a.isConjunct || a.isExpect) {
|
|
122
|
+
out = a.unify(b, te ? ctx.clone({ explain: ec(te, 'AC') }) : ctx)
|
|
123
|
+
unified = true
|
|
124
|
+
why = 'a*'
|
|
125
|
+
}
|
|
126
|
+
else if (
|
|
127
|
+
b.isConjunct
|
|
128
|
+
|| b.isDisjunct
|
|
129
|
+
|| b.isRef
|
|
130
|
+
|| b.isPref
|
|
131
|
+
|| b.isFunc
|
|
132
|
+
|| b.isExpect
|
|
133
|
+
) {
|
|
134
|
+
out = b.unify(a, te ? ctx.clone({ explain: ec(te, 'BW') }) : ctx)
|
|
135
|
+
unified = true
|
|
136
|
+
why = 'bv'
|
|
137
|
+
}
|
|
138
|
+
// Exactly equal scalars (not caught by early fast-path — e.g.
|
|
139
|
+
// because a or b isn't .done yet).
|
|
140
|
+
else if (a.constructor === b.constructor && a.peg === b.peg) {
|
|
141
|
+
out = update(a, b)
|
|
142
|
+
why = 'up'
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
out = a.unify(b, te ? ctx.clone({ explain: ec(te, 'GN') }) : ctx)
|
|
146
|
+
unified = true
|
|
147
|
+
why = 'ab'
|
|
116
148
|
}
|
|
117
149
|
|
|
118
150
|
if (!out || !out.unify) {
|
|
@@ -120,19 +152,16 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
120
152
|
why += 'N'
|
|
121
153
|
}
|
|
122
154
|
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
//
|
|
155
|
+
// Any non-done top-level result self-unifies with TOP to ensure
|
|
156
|
+
// its children finish converging. Skipped when `unified` is true
|
|
157
|
+
// because the branch that set `out = X.unify(Y, ctx)` already
|
|
158
|
+
// ran that Val's own unify logic.
|
|
126
159
|
if (!out.done && !unified) {
|
|
127
|
-
|
|
128
|
-
out = nout
|
|
160
|
+
out = out.unify(top(), te ? ctx.clone({ explain: ec(te, 'ND') }) : ctx)
|
|
129
161
|
why += 'T'
|
|
130
162
|
}
|
|
131
|
-
|
|
132
|
-
// console.log('UNITE', why, a?.id, a?.canon, a?.done, b?.id, b?.canon, b?.done, '->', out?.id, out?.canon, out?.done)
|
|
133
163
|
}
|
|
134
164
|
catch (err: any) {
|
|
135
|
-
// console.log(err)
|
|
136
165
|
// TODO: handle unexpected
|
|
137
166
|
out = makeNilErr(ctx, 'internal', a, b)
|
|
138
167
|
}
|
package/src/val/ConjunctVal.ts
CHANGED
|
@@ -97,7 +97,7 @@ class ConjunctVal extends JunctionVal {
|
|
|
97
97
|
// console.log('CONJUNCT-TERM', this.id, vI, this.peg[vI].canon)
|
|
98
98
|
|
|
99
99
|
upeer[vI] = (this.peg[vI].done && peer.isTop) ? this.peg[vI] :
|
|
100
|
-
unite(ctx.clone({ explain: ec(te, 'OWN') }), this.peg[vI], peer, 'cj-own')
|
|
100
|
+
unite(te ? ctx.clone({ explain: ec(te, 'OWN') }) : ctx, this.peg[vI], peer, 'cj-own')
|
|
101
101
|
|
|
102
102
|
upeer[vI].mark.type = newtype = newtype || upeer[vI].mark.type
|
|
103
103
|
upeer[vI].mark.hide = newhide = newhide || upeer[vI].mark.hide
|
|
@@ -153,7 +153,7 @@ class ConjunctVal extends JunctionVal {
|
|
|
153
153
|
|
|
154
154
|
|
|
155
155
|
else {
|
|
156
|
-
val = unite(ctx.clone({ explain: ec(te, 'DEF') }), t0, t1, 'cj-peer-t0t1')
|
|
156
|
+
val = unite(te ? ctx.clone({ explain: ec(te, 'DEF') }) : ctx, t0, t1, 'cj-peer-t0t1')
|
|
157
157
|
// console.log('CONJUNCT-T', t0.canon, t1?.canon, '->', val.canon)
|
|
158
158
|
done = done && DONE === val.dc
|
|
159
159
|
newtype = this.mark.type || val.mark.type
|
package/src/val/DisjunctVal.ts
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
top
|
|
31
31
|
} from './top'
|
|
32
32
|
|
|
33
|
-
import { NilVal } from '../val/NilVal'
|
|
33
|
+
import { NilVal, TRIAL_NIL } from '../val/NilVal'
|
|
34
34
|
import { PrefVal } from '../val/PrefVal'
|
|
35
35
|
import { JunctionVal } from '../val/JunctionVal'
|
|
36
36
|
|
|
@@ -79,21 +79,46 @@ class DisjunctVal extends JunctionVal {
|
|
|
79
79
|
|
|
80
80
|
let oval: Val[] = []
|
|
81
81
|
|
|
82
|
-
// Conjunction (&) distributes over disjunction (|)
|
|
82
|
+
// Conjunction (&) distributes over disjunction (|).
|
|
83
|
+
//
|
|
84
|
+
// Each member is tried against peer in isolation: if that trial
|
|
85
|
+
// produces any errors, the member fails and is marked with a NilVal.
|
|
86
|
+
// Previously this used `ctx?.clone({err: []})` per member - a
|
|
87
|
+
// per-iteration context clone (two Object.creates) just to hold a
|
|
88
|
+
// throwaway error array. For schemas with many disjunctions
|
|
89
|
+
// (e.g. `*true | boolean`, `method: GET | PUT | ...`) this was the
|
|
90
|
+
// single largest source of clones in the unify hot path.
|
|
91
|
+
//
|
|
92
|
+
// Swap-and-restore avoids the clone: the existing ctx's err array
|
|
93
|
+
// is saved, replaced with a fresh array for each trial, then
|
|
94
|
+
// restored. ctx mutation is scoped to this loop and fully undone
|
|
95
|
+
// before return.
|
|
96
|
+
const savedErr = ctx.err
|
|
97
|
+
const savedTrialMode = ctx._trialMode
|
|
98
|
+
// C1-inner: tell `makeNilErr` to return TRIAL_NIL in this scope
|
|
99
|
+
// instead of allocating per-failure NilVals. Save/restore so
|
|
100
|
+
// nested DisjunctVal trials (and the outer non-trial code) are
|
|
101
|
+
// not affected.
|
|
102
|
+
ctx._trialMode = true
|
|
83
103
|
for (let vI = 0; vI < this.peg.length; vI++) {
|
|
84
104
|
const v = this.peg[vI]
|
|
85
|
-
const
|
|
105
|
+
const trialErr: any[] = []
|
|
106
|
+
ctx.err = trialErr
|
|
86
107
|
|
|
87
|
-
|
|
88
|
-
oval[vI] = unite(cloneCtx.clone({ explain: ec(te, 'DIST:' + vI) }), v, peer, 'dj-peer')
|
|
89
|
-
// // // console.log('DJ-DIST-B', oval[vI].canon, cloneCtx?.err)
|
|
108
|
+
oval[vI] = unite(te ? ctx.clone({ explain: ec(te, 'DIST:' + vI) }) : ctx, v, peer, 'dj-peer')
|
|
90
109
|
|
|
91
|
-
if (0 <
|
|
92
|
-
|
|
110
|
+
if (0 < trialErr.length) {
|
|
111
|
+
// C1: failed-trial marker is never user-visible — it just
|
|
112
|
+
// signals "this disjunct member doesn't match" and is
|
|
113
|
+
// filtered out before the result is built. Use the shared
|
|
114
|
+
// sentinel instead of allocating a fresh NilVal per trial.
|
|
115
|
+
oval[vI] = TRIAL_NIL
|
|
93
116
|
}
|
|
94
117
|
|
|
95
118
|
done = done && DONE === oval[vI].dc
|
|
96
119
|
}
|
|
120
|
+
ctx._trialMode = savedTrialMode
|
|
121
|
+
ctx.err = savedErr
|
|
97
122
|
|
|
98
123
|
// // // console.log('DISJUNCT-unify-B', this.id, oval.map(v => v.canon))
|
|
99
124
|
|
|
@@ -107,12 +132,13 @@ class DisjunctVal extends JunctionVal {
|
|
|
107
132
|
|
|
108
133
|
// // // console.log('DISJUNCT-unify-C', this.id, oval.map(v => v.id + '=' + v.canon))
|
|
109
134
|
|
|
110
|
-
//
|
|
111
|
-
|
|
135
|
+
// Dedup: duplicate Vals in the disjunct are replaced with the
|
|
136
|
+
// trial sentinel, which is filtered out a few lines below.
|
|
137
|
+
// (No need for a fresh NilVal — any isNil value gets filtered.)
|
|
112
138
|
for (let vI = 0; vI < oval.length; vI++) {
|
|
113
139
|
for (let kI = vI + 1; kI < oval.length; kI++) {
|
|
114
140
|
if (oval[kI].same(oval[vI])) {
|
|
115
|
-
oval[kI] =
|
|
141
|
+
oval[kI] = TRIAL_NIL
|
|
116
142
|
}
|
|
117
143
|
}
|
|
118
144
|
}
|
package/src/val/ExpectVal.ts
CHANGED
|
@@ -49,10 +49,10 @@ class ExpectVal extends FeatureVal {
|
|
|
49
49
|
|
|
50
50
|
if (!peer.isTop) {
|
|
51
51
|
this.peer = undefined === this.peer ? peer :
|
|
52
|
-
unite(ctx.clone({ explain: ec(te, 'PEER') }), this.peer, peer, 'expect-peer')
|
|
52
|
+
unite(te ? ctx.clone({ explain: ec(te, 'PEER') }) : ctx, this.peer, peer, 'expect-peer')
|
|
53
53
|
|
|
54
54
|
const peeru =
|
|
55
|
-
unite(ctx.clone({ explain: ec(te, 'EXPECT') }), this.peer, this.peg, 'expect-self')
|
|
55
|
+
unite(te ? ctx.clone({ explain: ec(te, 'EXPECT') }) : ctx, this.peer, this.peg, 'expect-self')
|
|
56
56
|
|
|
57
57
|
if (peeru.isGenable) {
|
|
58
58
|
out = peeru
|
package/src/val/FuncBaseVal.ts
CHANGED
|
@@ -100,7 +100,7 @@ class FuncBaseVal extends FeatureVal {
|
|
|
100
100
|
|
|
101
101
|
let newarg = arg
|
|
102
102
|
if (!arg.done) {
|
|
103
|
-
newarg = arg.unify(TOP, ctx.clone({ explain: ec(te, 'ARG') }))
|
|
103
|
+
newarg = arg.unify(TOP, te ? ctx.clone({ explain: ec(te, 'ARG') }) : ctx)
|
|
104
104
|
newtype = newtype || newarg.mark.type
|
|
105
105
|
newhide = newhide || newarg.mark.hide
|
|
106
106
|
// console.log('FUNCBASE-UNIFY-PEG-B', arg.canon, arg.done, '->', newarg.canon, newarg.done)
|
|
@@ -118,7 +118,7 @@ class FuncBaseVal extends FeatureVal {
|
|
|
118
118
|
// console.log('FUNC-RESOLVED', ctx.cc, resolved?.canon)
|
|
119
119
|
|
|
120
120
|
out = resolved.done && peer.isTop ? resolved :
|
|
121
|
-
unite(ctx.clone({ explain: ec(te, 'PEG') }),
|
|
121
|
+
unite(te ? ctx.clone({ explain: ec(te, 'PEG') }) : ctx,
|
|
122
122
|
resolved, peer, 'func-' + this.funcname() + '/' + this.id)
|
|
123
123
|
propagateMarks(this, out)
|
|
124
124
|
|
package/src/val/IntegerVal.ts
CHANGED
|
@@ -43,7 +43,7 @@ class IntegerVal extends ScalarVal {
|
|
|
43
43
|
|
|
44
44
|
if (null != peer) {
|
|
45
45
|
if (peer.isScalarKind) {
|
|
46
|
-
out = peer.unify(this, ctx.clone({ explain: ec(te, 'KND') }))
|
|
46
|
+
out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'KND') }) : ctx)
|
|
47
47
|
}
|
|
48
48
|
else if (
|
|
49
49
|
peer.isScalar &&
|
package/src/val/ListVal.ts
CHANGED
|
@@ -92,7 +92,7 @@ class ListVal extends BagVal {
|
|
|
92
92
|
|
|
93
93
|
if (peer instanceof ListVal) {
|
|
94
94
|
if (!this.closed && peer.closed) {
|
|
95
|
-
out = peer.unify(this, ctx.clone({ explain: ec(te, 'PMC') })) as ListVal
|
|
95
|
+
out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'PMC') }) : ctx) as ListVal
|
|
96
96
|
exit = true
|
|
97
97
|
}
|
|
98
98
|
else {
|
|
@@ -100,7 +100,7 @@ class ListVal extends BagVal {
|
|
|
100
100
|
out.spread.cj = null == out.spread.cj ? peer.spread.cj : (
|
|
101
101
|
null == peer.spread.cj ? out.spread.cj : (
|
|
102
102
|
out.spread.cj =
|
|
103
|
-
unite(ctx.clone({ explain: ec(te, 'SPR') }),
|
|
103
|
+
unite(te ? ctx.clone({ explain: ec(te, 'SPR') }) : ctx,
|
|
104
104
|
out.spread.cj, peer.spread.cj, 'list-peer')
|
|
105
105
|
)
|
|
106
106
|
)
|
|
@@ -127,7 +127,7 @@ class ListVal extends BagVal {
|
|
|
127
127
|
key_spread_cj.isNil ? key_spread_cj :
|
|
128
128
|
key_spread_cj.isTop && child.done ? child :
|
|
129
129
|
child.isTop && key_spread_cj.done ? key_spread_cj :
|
|
130
|
-
unite(keyctx.clone({ explain: ec(te, 'PEG:' + key) }),
|
|
130
|
+
unite(te ? keyctx.clone({ explain: ec(te, 'PEG:' + key) }) : keyctx,
|
|
131
131
|
child, key_spread_cj, 'list-own')
|
|
132
132
|
|
|
133
133
|
done = (done && DONE === out.peg[key].dc)
|
|
@@ -205,6 +205,10 @@ class ListVal extends BagVal {
|
|
|
205
205
|
// always done, never path-dependent, and never has marks mutated.
|
|
206
206
|
// For anything more complex, fall back to full deep clone.
|
|
207
207
|
spreadClone(ctx: AontuContext): Val {
|
|
208
|
+
// B1: share directly when the spread tree has no path-dependent
|
|
209
|
+
// leaves. See MapVal.spreadClone for rationale.
|
|
210
|
+
if (!this.isPathDependent) return this
|
|
211
|
+
|
|
208
212
|
let allScalarKind = true
|
|
209
213
|
for (let key in this.peg) {
|
|
210
214
|
if (!(this.peg[key] as any)?.isScalarKind) {
|
package/src/val/MapVal.ts
CHANGED
|
@@ -90,7 +90,7 @@ class MapVal extends BagVal {
|
|
|
90
90
|
|
|
91
91
|
if (peer instanceof MapVal) {
|
|
92
92
|
if (!this.closed && peer.closed) {
|
|
93
|
-
out = peer.unify(this, ctx.clone({ explain: ec(te, 'PMC') })) as MapVal
|
|
93
|
+
out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'PMC') }) : ctx) as MapVal
|
|
94
94
|
exit = true
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -105,7 +105,7 @@ class MapVal extends BagVal {
|
|
|
105
105
|
&& peerkeys.join('~') < selfkeys.join('~')
|
|
106
106
|
)
|
|
107
107
|
) {
|
|
108
|
-
out = peer.unify(this, ctx.clone({ explain: ec(te, 'SPC') })) as MapVal
|
|
108
|
+
out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'SPC') }) : ctx) as MapVal
|
|
109
109
|
exit = true
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -115,7 +115,7 @@ class MapVal extends BagVal {
|
|
|
115
115
|
out.spread.cj = null == out.spread.cj ? peer.spread.cj : (
|
|
116
116
|
null == peer.spread.cj ? out.spread.cj : (
|
|
117
117
|
out.spread.cj =
|
|
118
|
-
unite(ctx.clone({ explain: ec(te, 'SPR') }),
|
|
118
|
+
unite(te ? ctx.clone({ explain: ec(te, 'SPR') }) : ctx,
|
|
119
119
|
out.spread.cj, peer.spread.cj, 'map-self')
|
|
120
120
|
)
|
|
121
121
|
)
|
|
@@ -148,7 +148,7 @@ class MapVal extends BagVal {
|
|
|
148
148
|
key_spread_cj.isNil ? key_spread_cj :
|
|
149
149
|
key_spread_cj.isTop && child.done ? child :
|
|
150
150
|
child.isTop && key_spread_cj.done ? key_spread_cj :
|
|
151
|
-
unite(keyctx.clone({ explain: ec(te, 'KEY:' + key) }),
|
|
151
|
+
unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
|
|
152
152
|
child, key_spread_cj, 'map-own')
|
|
153
153
|
|
|
154
154
|
done = (done && DONE === out.peg[key].dc)
|
|
@@ -230,15 +230,23 @@ class MapVal extends BagVal {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
|
|
233
|
-
// Spread clone:
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
//
|
|
233
|
+
// Spread clone: return a Val usable as the per-key spread constraint.
|
|
234
|
+
//
|
|
235
|
+
// Three tiers:
|
|
236
|
+
// 1. tree is path-independent (no RefVal/KeyFuncVal/PathFuncVal/
|
|
237
|
+
// MoveFuncVal/SuperFuncVal anywhere below): return `this` directly.
|
|
238
|
+
// Nothing in the unify path mutates the spread root, and no
|
|
239
|
+
// child depends on its own stored .path, so sharing is safe.
|
|
240
|
+
// 2. top-level children are all ScalarKindVal: shallow clone
|
|
241
|
+
// (share children, fresh MapVal wrapper).
|
|
242
|
+
// 3. otherwise: full deep clone via `this.clone(ctx)`.
|
|
243
|
+
//
|
|
244
|
+
// Tier 1 handles the foo-sdk common case of simple type-constraint
|
|
245
|
+
// spreads like `&:{active: *true | boolean, version: *'0.0.1' | string}`,
|
|
246
|
+
// which are cloned thousands of times per run.
|
|
241
247
|
spreadClone(ctx: AontuContext): Val {
|
|
248
|
+
if (!this.isPathDependent) return this
|
|
249
|
+
|
|
242
250
|
let allScalarKind = true
|
|
243
251
|
for (let key in this.peg) {
|
|
244
252
|
if (!(this.peg[key] as any)?.isScalarKind) {
|
package/src/val/NilVal.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
|
|
17
17
|
import { Val, EMPTY_ERR } from './Val'
|
|
18
18
|
|
|
19
|
-
import { AontuError } from '../err'
|
|
19
|
+
import { AontuError, descErr } from '../err'
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class NilVal extends Val {
|
|
@@ -142,6 +142,9 @@ class NilVal extends Val {
|
|
|
142
142
|
ctx.adderr(this)
|
|
143
143
|
|
|
144
144
|
if (!ctx.collect) {
|
|
145
|
+
if (null == this.msg || '' === this.msg) {
|
|
146
|
+
descErr(this, ctx)
|
|
147
|
+
}
|
|
145
148
|
const err = new AontuError(this.msg, [this])
|
|
146
149
|
throw err
|
|
147
150
|
}
|
|
@@ -162,6 +165,19 @@ class NilVal extends Val {
|
|
|
162
165
|
}
|
|
163
166
|
|
|
164
167
|
|
|
168
|
+
// Shared sentinel for transient "this unification branch failed"
|
|
169
|
+
// markers. Used by DisjunctVal.unify to flag failed member trials
|
|
170
|
+
// (and to dedup results) without allocating a fresh NilVal per
|
|
171
|
+
// failure. The sentinel is filtered out before the disjunct result
|
|
172
|
+
// is constructed, so its .why / .site / .primary fields are never
|
|
173
|
+
// inspected by user-visible code.
|
|
174
|
+
//
|
|
175
|
+
// Do NOT use this sentinel for errors that may surface: those need
|
|
176
|
+
// real NilVals with proper site/path info for descErr formatting.
|
|
177
|
+
const TRIAL_NIL = new NilVal({ why: '|:trial-nil' })
|
|
178
|
+
|
|
179
|
+
|
|
165
180
|
export {
|
|
166
181
|
NilVal,
|
|
182
|
+
TRIAL_NIL,
|
|
167
183
|
}
|
package/src/val/OpBaseVal.ts
CHANGED
|
@@ -117,7 +117,7 @@ class OpBaseVal extends FeatureVal {
|
|
|
117
117
|
}
|
|
118
118
|
else {
|
|
119
119
|
out = result.done && peer.isTop ? result :
|
|
120
|
-
unite(ctx.clone({ explain: ec(te, 'RES') }), result, peer, 'op')
|
|
120
|
+
unite(te ? ctx.clone({ explain: ec(te, 'RES') }) : ctx, result, peer, 'op')
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
out.dc = DONE === out.dc ? DONE : this.dc + 1
|
package/src/val/PrefVal.ts
CHANGED
|
@@ -62,7 +62,7 @@ class PrefVal extends FeatureVal {
|
|
|
62
62
|
let why = ''
|
|
63
63
|
|
|
64
64
|
if (!this.peg.done) {
|
|
65
|
-
const resolved = unite(ctx.clone({ explain:
|
|
65
|
+
const resolved = unite(te ? ctx.clone({ explain: ec(te, 'RES') }) : ctx,
|
|
66
66
|
this.peg, top(), 'pref/resolve')
|
|
67
67
|
// console.log('PREF-RESOLVED', this.peg.canon, '->', resolved)
|
|
68
68
|
this.peg = resolved
|
|
@@ -96,7 +96,7 @@ class PrefVal extends FeatureVal {
|
|
|
96
96
|
// peer.peg.id, peer.peg, peer.peg.done,
|
|
97
97
|
// )
|
|
98
98
|
|
|
99
|
-
let peg = unite(ctx.clone({ explain:
|
|
99
|
+
let peg = unite(te ? ctx.clone({ explain: ec(te, 'PREF-PEER') }) : ctx,
|
|
100
100
|
this.peg, peer.peg, 'pref-peer/' + this.id)
|
|
101
101
|
out = new PrefVal({ peg }, ctx)
|
|
102
102
|
// console.log('PREF-RANK-SAME-OUT', peg, peg.done, out, out.done)
|
|
@@ -106,7 +106,7 @@ class PrefVal extends FeatureVal {
|
|
|
106
106
|
else if (!peer.isTop) {
|
|
107
107
|
why += 'super-'
|
|
108
108
|
|
|
109
|
-
out = unite(ctx.clone({ explain:
|
|
109
|
+
out = unite(te ? ctx.clone({ explain: ec(te, 'SUPER') }) : ctx,
|
|
110
110
|
this.superpeg, peer, 'pref-super/' + this.id)
|
|
111
111
|
if (out.same(this.superpeg)) {
|
|
112
112
|
out = this.peg
|
package/src/val/RefVal.ts
CHANGED