aontu 0.41.0 → 0.43.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/ctx.d.ts +5 -2
- package/dist/ctx.js +2 -1
- package/dist/ctx.js.map +1 -1
- package/dist/lang.d.ts +5 -1
- package/dist/lang.js +100 -24
- package/dist/lang.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +5 -0
- package/dist/type.js.map +1 -1
- package/dist/unify.js +292 -13
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +6 -2
- package/dist/utility.js.map +1 -1
- package/dist/val/BagVal.d.ts +0 -3
- package/dist/val/BagVal.js +6 -6
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/ConjunctVal.d.ts +1 -1
- package/dist/val/ConjunctVal.js +137 -14
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/CopyFuncVal.js +3 -2
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +4 -0
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +16 -3
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/JunctionVal.d.ts +1 -0
- package/dist/val/JunctionVal.js +6 -1
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.js +0 -2
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.d.ts +1 -0
- package/dist/val/ListVal.js +33 -66
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +3 -2
- package/dist/val/MapVal.js +67 -95
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MoveFuncVal.d.ts +2 -1
- package/dist/val/MoveFuncVal.js +78 -13
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +25 -4
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/{RefVal.d.ts → PathVal.d.ts} +4 -3
- package/dist/val/{RefVal.js → PathVal.js} +75 -77
- package/dist/val/PathVal.js.map +1 -0
- package/dist/val/PlusOpVal.d.ts +1 -1
- package/dist/val/PrefVal.js +18 -5
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/SpreadVal.d.ts +20 -0
- package/dist/val/SpreadVal.js +194 -0
- package/dist/val/SpreadVal.js.map +1 -0
- package/dist/val/Val.d.ts +2 -1
- package/dist/val/Val.js +7 -8
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +2 -2
- package/dist/val/VarVal.js.map +1 -1
- package/package.json +5 -6
- package/src/ctx.ts +16 -3
- package/src/lang.ts +113 -23
- package/src/type.ts +5 -0
- package/src/unify.ts +310 -13
- package/src/utility.ts +5 -2
- package/src/val/BagVal.ts +6 -7
- package/src/val/ConjunctVal.ts +131 -13
- package/src/val/CopyFuncVal.ts +3 -2
- package/src/val/DisjunctVal.ts +6 -0
- package/src/val/ExpectVal.ts +18 -4
- package/src/val/JunctionVal.ts +5 -1
- package/src/val/KeyFuncVal.ts +0 -3
- package/src/val/ListVal.ts +38 -88
- package/src/val/MapVal.ts +75 -124
- package/src/val/MoveFuncVal.ts +79 -14
- package/src/val/PathFuncVal.ts +29 -4
- package/src/val/PathVal.ts +435 -0
- package/src/val/PrefVal.ts +19 -6
- package/src/val/{RefVal.ts → RefVal.ts.old} +30 -19
- package/src/val/SpreadVal.ts +275 -0
- package/src/val/Val.ts +9 -9
- package/src/val/VarVal.ts +2 -2
- package/dist/val/RefVal.js.map +0 -1
package/src/val/DisjunctVal.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
top
|
|
31
31
|
} from './top'
|
|
32
32
|
|
|
33
|
+
|
|
33
34
|
import { NilVal, TRIAL_NIL } from '../val/NilVal'
|
|
34
35
|
import { PrefVal } from '../val/PrefVal'
|
|
35
36
|
import { JunctionVal } from '../val/JunctionVal'
|
|
@@ -67,6 +68,11 @@ class DisjunctVal extends JunctionVal {
|
|
|
67
68
|
unify(peer: Val, ctx: AontuContext): Val {
|
|
68
69
|
peer = peer ?? top()
|
|
69
70
|
|
|
71
|
+
// Fast path: already-done disjunct unifying with TOP.
|
|
72
|
+
if (this.done && peer.isTop) {
|
|
73
|
+
return this
|
|
74
|
+
}
|
|
75
|
+
|
|
70
76
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Disjunct', this, peer)
|
|
71
77
|
|
|
72
78
|
if (!this.prefsRanked) {
|
package/src/val/ExpectVal.ts
CHANGED
|
@@ -51,11 +51,25 @@ class ExpectVal extends FeatureVal {
|
|
|
51
51
|
this.peer = undefined === this.peer ? peer :
|
|
52
52
|
unite(te ? ctx.clone({ explain: ec(te, 'PEER') }) : ctx, this.peer, peer, 'expect-peer')
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Unwrap nested ExpectVals to prevent infinite recursion
|
|
55
|
+
let peg = this.peg
|
|
56
|
+
while (peg?.isExpect) {
|
|
57
|
+
peg = peg.peg
|
|
58
|
+
}
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
// Guard against re-entrant recursion
|
|
61
|
+
if ((this as any)._expectDepth > 0) {
|
|
62
|
+
out.dc = this.dc + 1
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
(this as any)._expectDepth = ((this as any)._expectDepth || 0) + 1
|
|
66
|
+
const peeru =
|
|
67
|
+
unite(te ? ctx.clone({ explain: ec(te, 'EXPECT') }) : ctx, this.peer, peg, 'expect-self')
|
|
68
|
+
;(this as any)._expectDepth--
|
|
69
|
+
|
|
70
|
+
if (peeru.isGenable) {
|
|
71
|
+
out = peeru
|
|
72
|
+
}
|
|
59
73
|
}
|
|
60
74
|
}
|
|
61
75
|
|
package/src/val/JunctionVal.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { FeatureVal } from './FeatureVal'
|
|
|
17
17
|
// (ConjunctVal and DisjunctVal)
|
|
18
18
|
abstract class JunctionVal extends FeatureVal {
|
|
19
19
|
isJunction = true
|
|
20
|
+
_canonCache?: string
|
|
20
21
|
|
|
21
22
|
constructor(
|
|
22
23
|
spec: ValSpec,
|
|
@@ -38,10 +39,13 @@ abstract class JunctionVal extends FeatureVal {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
get canon() {
|
|
41
|
-
|
|
42
|
+
if (this._canonCache !== undefined) return this._canonCache
|
|
43
|
+
const c = this.peg.map((v: Val) => {
|
|
42
44
|
return (v as any).isJunction && Array.isArray(v.peg) && 1 < v.peg.length ?
|
|
43
45
|
'(' + v.canon + ')' : v.canon // v.id + '=' + v.canon
|
|
44
46
|
}).join(this.getJunctionSymbol()) // + '<' + (this.mark.hide ? 'H' : '') + '>'
|
|
47
|
+
if (this.done) this._canonCache = c
|
|
48
|
+
return c
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
// Abstract method to be implemented by subclasses to define their junction symbol
|
package/src/val/KeyFuncVal.ts
CHANGED
|
@@ -18,7 +18,6 @@ import { ConjunctVal } from '../val/ConjunctVal'
|
|
|
18
18
|
|
|
19
19
|
import { FuncBaseVal } from './FuncBaseVal'
|
|
20
20
|
|
|
21
|
-
|
|
22
21
|
class KeyFuncVal extends FuncBaseVal {
|
|
23
22
|
isKeyFunc = true
|
|
24
23
|
|
|
@@ -41,14 +40,12 @@ class KeyFuncVal extends FuncBaseVal {
|
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
unify(peer: Val, ctx: AontuContext): Val {
|
|
44
|
-
// TODO: this delay makes keys in spreads and refs work, but it is a hack - find a better way.
|
|
45
43
|
let out: Val = this
|
|
46
44
|
|
|
47
45
|
if (ctx.cc < 3) {
|
|
48
46
|
this.notdone()
|
|
49
47
|
|
|
50
48
|
if (peer.isTop || (peer.id === this.id)) {
|
|
51
|
-
// TODO: clone needed to avoid triggering unify_cycle - find a better way
|
|
52
49
|
out = this.clone(ctx)
|
|
53
50
|
}
|
|
54
51
|
else if (peer.isNil) {
|
package/src/val/ListVal.ts
CHANGED
|
@@ -9,7 +9,6 @@ import type {
|
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
DONE,
|
|
12
|
-
SPREAD,
|
|
13
12
|
} from '../type'
|
|
14
13
|
|
|
15
14
|
import { AontuContext } from '../ctx'
|
|
@@ -30,7 +29,6 @@ import {
|
|
|
30
29
|
top
|
|
31
30
|
} from './top'
|
|
32
31
|
|
|
33
|
-
import { ConjunctVal } from './ConjunctVal'
|
|
34
32
|
import { NilVal } from './NilVal'
|
|
35
33
|
import { BagVal } from './BagVal'
|
|
36
34
|
import { empty } from './Val'
|
|
@@ -38,6 +36,7 @@ import { empty } from './Val'
|
|
|
38
36
|
|
|
39
37
|
class ListVal extends BagVal {
|
|
40
38
|
isList = true
|
|
39
|
+
_canonCache?: string
|
|
41
40
|
|
|
42
41
|
constructor(
|
|
43
42
|
spec: {
|
|
@@ -51,24 +50,6 @@ class ListVal extends BagVal {
|
|
|
51
50
|
throw new AontuError('ListVal spec.peg undefined')
|
|
52
51
|
}
|
|
53
52
|
|
|
54
|
-
let spread = (this.peg as any)[SPREAD]
|
|
55
|
-
delete (this.peg as any)[SPREAD]
|
|
56
|
-
|
|
57
|
-
if (spread) {
|
|
58
|
-
if ('&' === spread.o) {
|
|
59
|
-
|
|
60
|
-
// TODO: handle existing spread!
|
|
61
|
-
this.spread.cj =
|
|
62
|
-
Array.isArray(spread.v) ?
|
|
63
|
-
1 < spread.v.length ?
|
|
64
|
-
new ConjunctVal({ peg: spread.v }, ctx) :
|
|
65
|
-
spread.v[0] :
|
|
66
|
-
spread.v
|
|
67
|
-
|
|
68
|
-
// let tmv = Array.isArray(spread.v) ? spread.v : [spread.v]
|
|
69
|
-
// this.spread.cj = new ConjunctVal({ peg: tmv }, ctx)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
53
|
}
|
|
73
54
|
|
|
74
55
|
|
|
@@ -86,8 +67,7 @@ class ListVal extends BagVal {
|
|
|
86
67
|
let out: ListVal | NilVal = (peer.isTop ? this : new ListVal({ peg: [] }, ctx))
|
|
87
68
|
|
|
88
69
|
out.closed = this.closed
|
|
89
|
-
out.optionalKeys = [...this.optionalKeys]
|
|
90
|
-
out.spread.cj = this.spread.cj
|
|
70
|
+
out.optionalKeys = 0 < this.optionalKeys.length ? [...this.optionalKeys] : this.optionalKeys
|
|
91
71
|
out.site = this.site
|
|
92
72
|
|
|
93
73
|
if (peer instanceof ListVal) {
|
|
@@ -97,13 +77,6 @@ class ListVal extends BagVal {
|
|
|
97
77
|
}
|
|
98
78
|
else {
|
|
99
79
|
out.closed = out.closed || peer.closed
|
|
100
|
-
out.spread.cj = null == out.spread.cj ? peer.spread.cj : (
|
|
101
|
-
null == peer.spread.cj ? out.spread.cj : (
|
|
102
|
-
out.spread.cj =
|
|
103
|
-
unite(te ? ctx.clone({ explain: ec(te, 'SPR') }) : ctx,
|
|
104
|
-
out.spread.cj, peer.spread.cj, 'list-peer')
|
|
105
|
-
)
|
|
106
|
-
)
|
|
107
80
|
}
|
|
108
81
|
}
|
|
109
82
|
|
|
@@ -111,29 +84,39 @@ class ListVal extends BagVal {
|
|
|
111
84
|
if (!exit) {
|
|
112
85
|
out.dc = this.dc + 1
|
|
113
86
|
|
|
114
|
-
|
|
87
|
+
// Fast path: self-unify with TOP.
|
|
88
|
+
if (peer.isTop) {
|
|
89
|
+
let allChildrenDone = true
|
|
90
|
+
for (let key in this.peg) {
|
|
91
|
+
if (DONE !== this.peg[key]?.dc) {
|
|
92
|
+
allChildrenDone = false
|
|
93
|
+
break
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (allChildrenDone) {
|
|
97
|
+
out.dc = DONE
|
|
98
|
+
ctx.explain && explainClose(te, out)
|
|
99
|
+
return out
|
|
100
|
+
}
|
|
101
|
+
}
|
|
115
102
|
|
|
116
|
-
//
|
|
103
|
+
// Unify own children
|
|
117
104
|
for (let key in this.peg) {
|
|
118
105
|
const keyctx = ctx.descend(key)
|
|
119
|
-
const key_spread_cj = spread_cj.spreadClone(keyctx)
|
|
120
106
|
const child = this.peg[key]
|
|
121
107
|
|
|
122
108
|
propagateMarks(this, child)
|
|
123
109
|
|
|
124
110
|
out.peg[key] =
|
|
125
|
-
undefined === child ?
|
|
111
|
+
undefined === child ? top() :
|
|
126
112
|
child.isNil ? child :
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
child
|
|
130
|
-
unite(te ? keyctx.clone({ explain: ec(te, 'PEG:' + key) }) : keyctx,
|
|
131
|
-
child, key_spread_cj, 'list-own')
|
|
113
|
+
child.done ? child :
|
|
114
|
+
unite(te ? keyctx.clone({ explain: ec(te, 'PEG:' + key) }) : keyctx,
|
|
115
|
+
child, top(), 'list-own')
|
|
132
116
|
|
|
133
117
|
done = (done && DONE === out.peg[key].dc)
|
|
134
118
|
}
|
|
135
119
|
|
|
136
|
-
const allowedKeys: string[] = this.closed ? Object.keys(this.peg) : []
|
|
137
120
|
let bad: NilVal | undefined = undefined
|
|
138
121
|
|
|
139
122
|
if (peer instanceof ListVal) {
|
|
@@ -141,11 +124,10 @@ class ListVal extends BagVal {
|
|
|
141
124
|
te ? ctx.clone({ explain: ec(te, 'PER') }) : ctx,
|
|
142
125
|
peer, TOP, 'list-peer-list') as ListVal)
|
|
143
126
|
|
|
144
|
-
// NOTE: peerkey is the index
|
|
145
127
|
for (let peerkey in upeer.peg) {
|
|
146
128
|
let peerchild = upeer.peg[peerkey]
|
|
147
129
|
|
|
148
|
-
if (this.closed && !
|
|
130
|
+
if (this.closed && !(peerkey in this.peg)) {
|
|
149
131
|
bad = makeNilErr(ctx, 'closed', peerchild, undefined)
|
|
150
132
|
}
|
|
151
133
|
|
|
@@ -155,19 +137,11 @@ class ListVal extends BagVal {
|
|
|
155
137
|
|
|
156
138
|
let oval = out.peg[peerkey] =
|
|
157
139
|
undefined === child ? peerchild :
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (this.spread.cj) {
|
|
165
|
-
let key_spread_cj = spread_cj.spreadClone(peerctx)
|
|
166
|
-
|
|
167
|
-
oval = out.peg[peerkey] =
|
|
168
|
-
unite(te ? peerctx.clone({ explain: ec(te, 'PSP:' + peerkey) }) : peerctx,
|
|
169
|
-
out.peg[peerkey], key_spread_cj, 'list-spread')
|
|
170
|
-
}
|
|
140
|
+
child.isTop && peerchild.done ? peerchild :
|
|
141
|
+
child.isNil ? child :
|
|
142
|
+
peerchild.isNil ? peerchild :
|
|
143
|
+
unite(te ? peerctx.clone({ explain: ec(te, 'CHD') }) : peerctx,
|
|
144
|
+
child, peerchild, 'list-peer')
|
|
171
145
|
|
|
172
146
|
propagateMarks(this, oval)
|
|
173
147
|
|
|
@@ -197,39 +171,18 @@ class ListVal extends BagVal {
|
|
|
197
171
|
}
|
|
198
172
|
|
|
199
173
|
|
|
200
|
-
// Spread clone:
|
|
201
|
-
//
|
|
202
|
-
// Spread clone: when all children are ScalarKindVal (simple type
|
|
203
|
-
// constraints like `string`, `number`), share them directly to avoid
|
|
204
|
-
// N x M allocations. ScalarKindVal is safe to share: it is immutable,
|
|
205
|
-
// always done, never path-dependent, and never has marks mutated.
|
|
206
|
-
// For anything more complex, fall back to full deep clone.
|
|
174
|
+
// Spread clone: share path-independent children directly, clone
|
|
175
|
+
// only path-dependent ones. See MapVal.spreadClone for rationale.
|
|
207
176
|
spreadClone(ctx: AontuContext): Val {
|
|
208
|
-
// B1: share directly when the spread tree has no path-dependent
|
|
209
|
-
// leaves. See MapVal.spreadClone for rationale.
|
|
210
177
|
if (!this.isPathDependent) return this
|
|
211
178
|
|
|
212
|
-
let allScalarKind = true
|
|
213
|
-
for (let key in this.peg) {
|
|
214
|
-
if (!(this.peg[key] as any)?.isScalarKind) {
|
|
215
|
-
allScalarKind = false
|
|
216
|
-
break
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (!allScalarKind) {
|
|
221
|
-
return this.clone(ctx)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
179
|
let out = (super.clone(ctx) as ListVal)
|
|
225
180
|
|
|
226
181
|
for (let entry of Object.entries(this.peg)) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
out.spread = {
|
|
232
|
-
cj: this.spread.cj ? this.spread.cj.spreadClone(ctx) : undefined,
|
|
182
|
+
const child = entry[1] as Val
|
|
183
|
+
out.peg[entry[0]] = child?.isPathDependent
|
|
184
|
+
? child.clone(ctx, { mark: {} })
|
|
185
|
+
: child
|
|
233
186
|
}
|
|
234
187
|
|
|
235
188
|
out.closed = this.closed
|
|
@@ -245,10 +198,6 @@ class ListVal extends BagVal {
|
|
|
245
198
|
out.peg[entry[0]] =
|
|
246
199
|
(entry[1] as any)?.isVal ? (entry[1] as Val).clone(ctx, spec?.mark ? { mark: spec.mark } : {}) : entry[1]
|
|
247
200
|
}
|
|
248
|
-
if (this.spread.cj) {
|
|
249
|
-
out.spread.cj = this.spread.cj.clone(ctx, spec?.mark ? { mark: spec.mark } : {})
|
|
250
|
-
}
|
|
251
|
-
|
|
252
201
|
out.closed = this.closed
|
|
253
202
|
out.optionalKeys = [...this.optionalKeys]
|
|
254
203
|
|
|
@@ -258,18 +207,19 @@ class ListVal extends BagVal {
|
|
|
258
207
|
|
|
259
208
|
|
|
260
209
|
get canon() {
|
|
210
|
+
if (this._canonCache !== undefined) return this._canonCache
|
|
261
211
|
// console.log('LISTVAL-CANON', this.optionalKeys)
|
|
262
212
|
let keys = Object.keys(this.peg)
|
|
263
|
-
|
|
213
|
+
const c = '' +
|
|
264
214
|
// this.errcanon() +
|
|
265
215
|
'[' +
|
|
266
|
-
(this.spread.cj ? '&:' + this.spread.cj.canon +
|
|
267
|
-
(0 < keys.length ? ',' : '') : '') +
|
|
268
216
|
keys
|
|
269
217
|
.map(k => this.optionalKeys.includes(k) ?
|
|
270
218
|
k + '?:' + this.peg[k].canon :
|
|
271
219
|
this.peg[k].canon).join(',') +
|
|
272
220
|
']'
|
|
221
|
+
if (this.done) this._canonCache = c
|
|
222
|
+
return c
|
|
273
223
|
}
|
|
274
224
|
}
|
|
275
225
|
|
package/src/val/MapVal.ts
CHANGED
|
@@ -8,7 +8,6 @@ import type {
|
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
10
|
DONE,
|
|
11
|
-
SPREAD,
|
|
12
11
|
} from '../type'
|
|
13
12
|
|
|
14
13
|
import { AontuContext } from '../ctx'
|
|
@@ -28,13 +27,13 @@ import {
|
|
|
28
27
|
} from './top'
|
|
29
28
|
|
|
30
29
|
|
|
31
|
-
import { ConjunctVal } from './ConjunctVal'
|
|
32
30
|
import { NilVal } from './NilVal'
|
|
33
31
|
import { BagVal } from './BagVal'
|
|
34
32
|
|
|
35
33
|
|
|
36
34
|
class MapVal extends BagVal {
|
|
37
35
|
isMap = true
|
|
36
|
+
_canonCache?: string
|
|
38
37
|
|
|
39
38
|
constructor(
|
|
40
39
|
spec: ValSpec,
|
|
@@ -48,23 +47,6 @@ class MapVal extends BagVal {
|
|
|
48
47
|
|
|
49
48
|
this.mark.type = !!spec.mark?.type
|
|
50
49
|
this.mark.hide = !!spec.mark?.hide
|
|
51
|
-
|
|
52
|
-
let spread = (this.peg as any)[SPREAD]
|
|
53
|
-
delete (this.peg as any)[SPREAD]
|
|
54
|
-
|
|
55
|
-
if (spread) {
|
|
56
|
-
if ('&' === spread.o) {
|
|
57
|
-
// TODO: handle existing spread!
|
|
58
|
-
this.spread.cj =
|
|
59
|
-
Array.isArray(spread.v) ?
|
|
60
|
-
1 < spread.v.length ?
|
|
61
|
-
new ConjunctVal({ peg: spread.v }, ctx) :
|
|
62
|
-
spread.v[0] :
|
|
63
|
-
spread.v
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// console.log('MAPVAL-ctor', this.type, spec)
|
|
68
50
|
}
|
|
69
51
|
|
|
70
52
|
|
|
@@ -75,6 +57,23 @@ class MapVal extends BagVal {
|
|
|
75
57
|
|
|
76
58
|
const TOP = top()
|
|
77
59
|
peer = peer ?? TOP
|
|
60
|
+
|
|
61
|
+
// Fast path: both maps done, no spreads, peer's keys are a
|
|
62
|
+
// subset of this's keys with the same child references, and
|
|
63
|
+
// neither side has type/hide marks. No new information.
|
|
64
|
+
if (this.done && peer instanceof MapVal && peer.done
|
|
65
|
+
&& !this.mark.type && !this.mark.hide
|
|
66
|
+
&& !peer.mark.type && !peer.mark.hide) {
|
|
67
|
+
let canSkip = true
|
|
68
|
+
for (const k in peer.peg) {
|
|
69
|
+
if (this.peg[k] !== peer.peg[k]) {
|
|
70
|
+
canSkip = false
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (canSkip) return this
|
|
75
|
+
}
|
|
76
|
+
|
|
78
77
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Map', this, peer)
|
|
79
78
|
|
|
80
79
|
let done: boolean = true
|
|
@@ -84,8 +83,7 @@ class MapVal extends BagVal {
|
|
|
84
83
|
let out: MapVal | NilVal = (peer.isTop ? this : new MapVal({ peg: {} }, ctx))
|
|
85
84
|
|
|
86
85
|
out.closed = this.closed
|
|
87
|
-
out.optionalKeys = [...this.optionalKeys]
|
|
88
|
-
out.spread.cj = this.spread.cj
|
|
86
|
+
out.optionalKeys = 0 < this.optionalKeys.length ? [...this.optionalKeys] : this.optionalKeys
|
|
89
87
|
out.site = this.site
|
|
90
88
|
|
|
91
89
|
if (peer instanceof MapVal) {
|
|
@@ -108,53 +106,47 @@ class MapVal extends BagVal {
|
|
|
108
106
|
out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'SPC') }) : ctx) as MapVal
|
|
109
107
|
exit = true
|
|
110
108
|
}
|
|
111
|
-
|
|
112
109
|
}
|
|
113
|
-
|
|
114
|
-
if (!exit) {
|
|
115
|
-
out.spread.cj = null == out.spread.cj ? peer.spread.cj : (
|
|
116
|
-
null == peer.spread.cj ? out.spread.cj : (
|
|
117
|
-
out.spread.cj =
|
|
118
|
-
unite(te ? ctx.clone({ explain: ec(te, 'SPR') }) : ctx,
|
|
119
|
-
out.spread.cj, peer.spread.cj, 'map-self')
|
|
120
|
-
)
|
|
121
|
-
)
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
// console.log('MAPVAL-PEER-OTHER', this.id, this.canon, this.done, peer.id, peer.canon, peer.done)
|
|
126
110
|
}
|
|
127
111
|
|
|
128
112
|
|
|
129
113
|
if (!exit) {
|
|
130
114
|
out.dc = this.dc + 1
|
|
131
115
|
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
116
|
+
// Fast path: self-unify with TOP.
|
|
117
|
+
// If all children are already done, the map is fully converged.
|
|
118
|
+
if (peer.isTop) {
|
|
119
|
+
let allChildrenDone = true
|
|
120
|
+
for (let key in this.peg) {
|
|
121
|
+
if (DONE !== this.peg[key]?.dc) {
|
|
122
|
+
allChildrenDone = false
|
|
123
|
+
break
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (allChildrenDone) {
|
|
127
|
+
out.dc = DONE
|
|
128
|
+
ctx.explain && explainClose(te, out)
|
|
129
|
+
return out
|
|
130
|
+
}
|
|
131
|
+
}
|
|
135
132
|
|
|
136
|
-
//
|
|
133
|
+
// Unify own children
|
|
137
134
|
for (let key in this.peg) {
|
|
138
|
-
const keyctx = ctx.descend(key)
|
|
139
|
-
|
|
140
|
-
const key_spread_cj = spread_cj.spreadClone(keyctx)
|
|
141
135
|
const child = this.peg[key]
|
|
136
|
+
const keyctx = ctx.descend(key)
|
|
142
137
|
|
|
143
138
|
propagateMarks(this, child)
|
|
144
139
|
|
|
145
140
|
out.peg[key] =
|
|
146
|
-
undefined === child ?
|
|
141
|
+
undefined === child ? top() :
|
|
147
142
|
child.isNil ? child :
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
child
|
|
151
|
-
unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
|
|
152
|
-
child, key_spread_cj, 'map-own')
|
|
143
|
+
child.done ? child :
|
|
144
|
+
unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
|
|
145
|
+
child, top(), 'map-own')
|
|
153
146
|
|
|
154
147
|
done = (done && DONE === out.peg[key].dc)
|
|
155
148
|
}
|
|
156
149
|
|
|
157
|
-
const allowedKeys: string[] = this.closed ? Object.keys(this.peg) : []
|
|
158
150
|
let bad: NilVal | undefined = undefined
|
|
159
151
|
|
|
160
152
|
if (peer instanceof MapVal) {
|
|
@@ -165,12 +157,14 @@ class MapVal extends BagVal {
|
|
|
165
157
|
for (let peerkey in upeer.peg) {
|
|
166
158
|
let peerchild = upeer.peg[peerkey]
|
|
167
159
|
|
|
168
|
-
if (this.closed && !
|
|
160
|
+
if (this.closed && !(peerkey in this.peg)) {
|
|
169
161
|
bad = makeNilErr(ctx, 'closed', peerchild, undefined)
|
|
170
162
|
}
|
|
171
163
|
|
|
172
164
|
// key optionality is additive
|
|
173
|
-
if (
|
|
165
|
+
if (0 < upeer.optionalKeys.length
|
|
166
|
+
&& upeer.optionalKeys.includes(peerkey)
|
|
167
|
+
&& !out.optionalKeys.includes(peerkey)) {
|
|
174
168
|
out.optionalKeys.push(peerkey)
|
|
175
169
|
}
|
|
176
170
|
|
|
@@ -180,19 +174,11 @@ class MapVal extends BagVal {
|
|
|
180
174
|
|
|
181
175
|
let oval = out.peg[peerkey] =
|
|
182
176
|
undefined === child ? this.handleExpectedVal(peerkey, peerchild, this, ctx) :
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (this.spread.cj) {
|
|
190
|
-
let key_spread_cj = spread_cj.spreadClone(peerctx)
|
|
191
|
-
|
|
192
|
-
oval = out.peg[peerkey] =
|
|
193
|
-
unite(te ? peerctx.clone({ explain: ec(te, 'PSP:' + peerkey) }) : peerctx,
|
|
194
|
-
oval, key_spread_cj, 'map-peer-spread')
|
|
195
|
-
}
|
|
177
|
+
child.isTop && peerchild.done ? peerchild :
|
|
178
|
+
child.isNil ? child :
|
|
179
|
+
peerchild.isNil ? peerchild :
|
|
180
|
+
unite(te ? peerctx.clone({ explain: ec(te, 'CHD') }) : peerctx,
|
|
181
|
+
child, peerchild, 'map-peer')
|
|
196
182
|
|
|
197
183
|
propagateMarks(this, oval)
|
|
198
184
|
|
|
@@ -213,66 +199,41 @@ class MapVal extends BagVal {
|
|
|
213
199
|
out.dc = done ? DONE : out.dc
|
|
214
200
|
propagateMarks(peer, out)
|
|
215
201
|
propagateMarks(this, out)
|
|
202
|
+
|
|
216
203
|
}
|
|
217
204
|
}
|
|
218
205
|
|
|
219
|
-
// console.log(
|
|
220
|
-
// 'MAPVAL-OUT', out.canon,
|
|
221
|
-
// '\n SELF', this,
|
|
222
|
-
// '\n PEER', peer,
|
|
223
|
-
// '\n OUT', out,
|
|
224
|
-
// '\n FROM', (out as any).spread.cj
|
|
225
|
-
// )
|
|
226
|
-
|
|
227
206
|
ctx.explain && explainClose(te, out)
|
|
228
207
|
|
|
229
208
|
return out
|
|
230
209
|
}
|
|
231
210
|
|
|
232
211
|
|
|
233
|
-
//
|
|
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.
|
|
212
|
+
// Optimized clone for use as a spread constraint: share
|
|
213
|
+
// path-independent children, clone only path-dependent ones.
|
|
247
214
|
spreadClone(ctx: AontuContext): Val {
|
|
248
215
|
if (!this.isPathDependent) return this
|
|
249
216
|
|
|
250
|
-
let allScalarKind = true
|
|
251
|
-
for (let key in this.peg) {
|
|
252
|
-
if (!(this.peg[key] as any)?.isScalarKind) {
|
|
253
|
-
allScalarKind = false
|
|
254
|
-
break
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (!allScalarKind) {
|
|
259
|
-
return this.clone(ctx)
|
|
260
|
-
}
|
|
261
|
-
|
|
262
217
|
let out = (super.clone(ctx) as MapVal)
|
|
263
218
|
out.peg = {}
|
|
264
219
|
|
|
265
220
|
for (let entry of Object.entries(this.peg)) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
221
|
+
const child = entry[1] as Val
|
|
222
|
+
if (child?.isVal && child.isPathDependent) {
|
|
223
|
+
out.peg[entry[0]] = child.clone(ctx, { path: [...out.path, entry[0]] })
|
|
224
|
+
}
|
|
225
|
+
else if (child?.isVal) {
|
|
226
|
+
const wrapper = Object.create(child)
|
|
227
|
+
wrapper.mark = { ...child.mark }
|
|
228
|
+
out.peg[entry[0]] = wrapper
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
out.peg[entry[0]] = child
|
|
232
|
+
}
|
|
272
233
|
}
|
|
273
234
|
|
|
274
235
|
out.closed = this.closed
|
|
275
|
-
out.optionalKeys = [...this.optionalKeys]
|
|
236
|
+
out.optionalKeys = 0 < this.optionalKeys.length ? [...this.optionalKeys] : this.optionalKeys
|
|
276
237
|
|
|
277
238
|
return out
|
|
278
239
|
}
|
|
@@ -285,36 +246,25 @@ class MapVal extends BagVal {
|
|
|
285
246
|
for (let entry of Object.entries(this.peg)) {
|
|
286
247
|
out.peg[entry[0]] =
|
|
287
248
|
(entry[1] as any)?.isVal ?
|
|
288
|
-
// (entry[1] as Val).clone(ctx, spec?.mark ? { mark: spec.mark } : {}) :
|
|
289
249
|
(entry[1] as Val).clone(ctx, {
|
|
290
250
|
mark: spec?.mark ?? {},
|
|
291
251
|
path: [...out.path, entry[0]]
|
|
292
252
|
}) :
|
|
293
253
|
entry[1]
|
|
294
254
|
}
|
|
295
|
-
if (this.spread.cj) {
|
|
296
|
-
out.spread.cj = this.spread.cj.clone(ctx, spec?.mark ? { mark: spec.mark } : {})
|
|
297
|
-
}
|
|
298
255
|
|
|
299
256
|
out.closed = this.closed
|
|
300
257
|
out.optionalKeys = [...this.optionalKeys]
|
|
301
258
|
|
|
302
|
-
// out.from = this.from
|
|
303
|
-
|
|
304
|
-
// console.log('MAPVAL-CLONE', this.canon, '->', out.canon)
|
|
305
259
|
return out
|
|
306
260
|
}
|
|
307
261
|
|
|
308
262
|
|
|
309
263
|
get canon() {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
// (this.mark.type ? '<type>' : '') +
|
|
314
|
-
// (this.id + '=') +
|
|
264
|
+
if (this._canonCache !== undefined) return this._canonCache
|
|
265
|
+
let keys = Object.keys(this.peg).sort()
|
|
266
|
+
const c = '' +
|
|
315
267
|
'{' +
|
|
316
|
-
(this.spread.cj ? '&:' + this.spread.cj.canon +
|
|
317
|
-
(0 < keys.length ? ',' : '') : '') +
|
|
318
268
|
keys
|
|
319
269
|
.map(k => [
|
|
320
270
|
JSON.stringify(k) +
|
|
@@ -323,18 +273,19 @@ class MapVal extends BagVal {
|
|
|
323
273
|
(this.peg[k]?.canon ?? this.peg[k])
|
|
324
274
|
])
|
|
325
275
|
.join(',') +
|
|
326
|
-
'}'
|
|
327
|
-
|
|
276
|
+
'}'
|
|
277
|
+
if (this.done) this._canonCache = c
|
|
278
|
+
return c
|
|
328
279
|
}
|
|
329
280
|
|
|
330
281
|
|
|
331
|
-
inspection(
|
|
332
|
-
return
|
|
282
|
+
inspection(_d?: number) {
|
|
283
|
+
return ''
|
|
333
284
|
}
|
|
334
285
|
|
|
335
286
|
}
|
|
336
287
|
|
|
337
288
|
|
|
338
289
|
export {
|
|
339
|
-
MapVal
|
|
290
|
+
MapVal,
|
|
340
291
|
}
|