aontu 0.44.0 → 0.45.1
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 +2 -5
- package/dist/ctx.js +1 -2
- package/dist/ctx.js.map +1 -1
- package/dist/lang.d.ts +1 -5
- package/dist/lang.js +24 -100
- package/dist/lang.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +0 -5
- package/dist/type.js.map +1 -1
- package/dist/unify.js +13 -292
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +2 -6
- package/dist/utility.js.map +1 -1
- package/dist/val/BagVal.d.ts +3 -0
- 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 +14 -137
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/CopyFuncVal.js +2 -3
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +0 -4
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +3 -16
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/JunctionVal.d.ts +0 -1
- package/dist/val/JunctionVal.js +1 -6
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.js +2 -0
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.d.ts +0 -1
- package/dist/val/ListVal.js +66 -33
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +2 -3
- package/dist/val/MapVal.js +95 -67
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MoveFuncVal.d.ts +1 -2
- package/dist/val/MoveFuncVal.js +13 -78
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +4 -25
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PlusOpVal.d.ts +1 -1
- package/dist/val/PrefVal.js +5 -18
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/{PathVal.d.ts → RefVal.d.ts} +3 -4
- package/dist/val/{PathVal.js → RefVal.js} +77 -75
- package/dist/val/RefVal.js.map +1 -0
- package/dist/val/Val.d.ts +1 -2
- package/dist/val/Val.js +8 -7
- 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 +6 -5
- package/src/ctx.ts +3 -16
- package/src/lang.ts +23 -113
- package/src/type.ts +0 -5
- package/src/unify.ts +13 -310
- package/src/utility.ts +2 -5
- package/src/val/BagVal.ts +7 -6
- package/src/val/ConjunctVal.ts +13 -131
- package/src/val/CopyFuncVal.ts +2 -3
- package/src/val/DisjunctVal.ts +0 -6
- package/src/val/ExpectVal.ts +4 -18
- package/src/val/JunctionVal.ts +1 -5
- package/src/val/KeyFuncVal.ts +3 -0
- package/src/val/ListVal.ts +88 -38
- package/src/val/MapVal.ts +124 -75
- package/src/val/MoveFuncVal.ts +14 -79
- package/src/val/PathFuncVal.ts +4 -29
- package/src/val/PrefVal.ts +6 -19
- package/src/val/{RefVal.ts.old → RefVal.ts} +19 -30
- package/src/val/Val.ts +9 -9
- package/src/val/VarVal.ts +2 -2
- package/README.md +0 -18
- package/dist/val/PathVal.js.map +0 -1
- package/dist/val/SpreadVal.d.ts +0 -20
- package/dist/val/SpreadVal.js +0 -194
- package/dist/val/SpreadVal.js.map +0 -1
- package/src/val/PathVal.ts +0 -435
- package/src/val/SpreadVal.ts +0 -275
package/src/utility.ts
CHANGED
|
@@ -11,11 +11,8 @@ function propagateMarks(source: Val, target: Val): void {
|
|
|
11
11
|
if (source.isTop || target.isTop) {
|
|
12
12
|
return
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const tm = target.mark as any
|
|
17
|
-
for (let name in sm) {
|
|
18
|
-
tm[name] = tm[name] || sm[name]
|
|
14
|
+
for (let name in source.mark) {
|
|
15
|
+
(target.mark as any)[name] = (target.mark as any)[name] || (source.mark as any)[name]
|
|
19
16
|
}
|
|
20
17
|
}
|
|
21
18
|
|
package/src/val/BagVal.ts
CHANGED
|
@@ -29,6 +29,10 @@ abstract class BagVal extends FeatureVal {
|
|
|
29
29
|
closed: boolean = false
|
|
30
30
|
optionalKeys: string[] = []
|
|
31
31
|
|
|
32
|
+
spread = {
|
|
33
|
+
cj: (undefined as Val | undefined),
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
constructor(
|
|
33
37
|
spec: ValSpec,
|
|
34
38
|
ctx?: AontuContext
|
|
@@ -38,6 +42,7 @@ abstract class BagVal extends FeatureVal {
|
|
|
38
42
|
|
|
39
43
|
clone(ctx: AontuContext, spec?: ValSpec): Val {
|
|
40
44
|
const bag = super.clone(ctx, spec) as BagVal
|
|
45
|
+
bag.spread = this.spread
|
|
41
46
|
return bag
|
|
42
47
|
}
|
|
43
48
|
|
|
@@ -87,15 +92,11 @@ abstract class BagVal extends FeatureVal {
|
|
|
87
92
|
|| child.isMap
|
|
88
93
|
|| child.isList
|
|
89
94
|
|| child.isPref
|
|
90
|
-
|| child.
|
|
95
|
+
|| child.isRef
|
|
91
96
|
|| child.isDisjunct
|
|
92
|
-
|| child.isConjunct
|
|
93
97
|
|| child.isNil
|
|
94
98
|
) {
|
|
95
|
-
|
|
96
|
-
// don't propagate when the key is dropped.
|
|
97
|
-
const genctx = optional ? ctx.clone({ err: [], collect: true }) : ctx
|
|
98
|
-
let cval = child.gen(genctx)
|
|
99
|
+
let cval = child.gen(ctx)
|
|
99
100
|
|
|
100
101
|
if (optional && (undefined === cval || empty(cval))) {
|
|
101
102
|
continue
|
package/src/val/ConjunctVal.ts
CHANGED
|
@@ -36,57 +36,6 @@ import {
|
|
|
36
36
|
top
|
|
37
37
|
} from './top'
|
|
38
38
|
|
|
39
|
-
import { MapVal } from './MapVal'
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Shallow merge two MapVals: combine keys without deep unification.
|
|
43
|
-
// For shared keys, merge child terms from both maps into one ConjunctVal
|
|
44
|
-
// so spreads at that level see all children from both terms.
|
|
45
|
-
function shallowMerge(a: MapVal, b: MapVal, ctx: AontuContext): MapVal {
|
|
46
|
-
const out = new MapVal({ peg: {} }, ctx)
|
|
47
|
-
out.site = a.site
|
|
48
|
-
out.closed = a.closed || b.closed
|
|
49
|
-
out.optionalKeys = [...a.optionalKeys]
|
|
50
|
-
for (const k of b.optionalKeys) {
|
|
51
|
-
if (!out.optionalKeys.includes(k)) out.optionalKeys.push(k)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
let done = true
|
|
55
|
-
|
|
56
|
-
// Copy all keys from a
|
|
57
|
-
for (const k in a.peg) {
|
|
58
|
-
out.peg[k] = a.peg[k]
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Merge keys from b
|
|
62
|
-
for (const k in b.peg) {
|
|
63
|
-
if (k in out.peg) {
|
|
64
|
-
const av = out.peg[k]
|
|
65
|
-
const bv = b.peg[k]
|
|
66
|
-
|
|
67
|
-
// Flatten both sides' terms into a single ConjunctVal
|
|
68
|
-
const terms: Val[] = []
|
|
69
|
-
if (av.isConjunct) { terms.push(...av.peg) } else { terms.push(av) }
|
|
70
|
-
if (bv.isConjunct) { terms.push(...bv.peg) } else { terms.push(bv) }
|
|
71
|
-
|
|
72
|
-
out.peg[k] = new ConjunctVal({ peg: terms }, ctx)
|
|
73
|
-
done = false
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
out.peg[k] = b.peg[k]
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
for (const k in out.peg) {
|
|
81
|
-
done = done && (DONE === out.peg[k]?.dc)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
out.dc = done ? DONE : 1
|
|
85
|
-
propagateMarks(a, out)
|
|
86
|
-
propagateMarks(b, out)
|
|
87
|
-
return out
|
|
88
|
-
}
|
|
89
|
-
|
|
90
39
|
|
|
91
40
|
|
|
92
41
|
// TODO: move main logic to op/conjunct
|
|
@@ -122,11 +71,6 @@ class ConjunctVal extends JunctionVal {
|
|
|
122
71
|
unify(peer: Val, ctx: AontuContext): Val {
|
|
123
72
|
peer = peer ?? top()
|
|
124
73
|
|
|
125
|
-
// Fast path: done conjunct self-unifying with TOP.
|
|
126
|
-
if (this.done && peer.isTop) {
|
|
127
|
-
return this
|
|
128
|
-
}
|
|
129
|
-
|
|
130
74
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Conjunct', this, peer)
|
|
131
75
|
|
|
132
76
|
let done = true
|
|
@@ -152,12 +96,7 @@ class ConjunctVal extends JunctionVal {
|
|
|
152
96
|
this.peg[vI].mark.hide = newhide
|
|
153
97
|
// console.log('CONJUNCT-TERM', this.id, vI, this.peg[vI].canon)
|
|
154
98
|
|
|
155
|
-
// Defer unify-with-TOP for map terms with spreads when other
|
|
156
|
-
// map terms exist — the shallow merge fold handles them instead.
|
|
157
|
-
const hasMapPeers = this.peg.length > 1 && this.peg[vI].isMap &&
|
|
158
|
-
this.peg.some((p: Val, i: number) => i !== vI && p.isMap)
|
|
159
99
|
upeer[vI] = (this.peg[vI].done && peer.isTop) ? this.peg[vI] :
|
|
160
|
-
(hasMapPeers && hasSpreads(this.peg[vI])) ? this.peg[vI] :
|
|
161
100
|
unite(te ? ctx.clone({ explain: ec(te, 'OWN') }) : ctx, this.peg[vI], peer, 'cj-own')
|
|
162
101
|
|
|
163
102
|
upeer[vI].mark.type = newtype = newtype || upeer[vI].mark.type
|
|
@@ -178,22 +117,7 @@ class ConjunctVal extends JunctionVal {
|
|
|
178
117
|
}
|
|
179
118
|
|
|
180
119
|
upeer = norm(upeer)
|
|
181
|
-
|
|
182
|
-
// Stable partition: fold pure terms first, spread-bearing terms
|
|
183
|
-
// last. Pure terms (no spreads, no path-dependent functions)
|
|
184
|
-
// unify cheaply. Dynamic terms are then folded in, applying
|
|
185
|
-
// spread constraints once to the merged result rather than
|
|
186
|
-
// incrementally at every intermediate fold step.
|
|
187
|
-
const pure: Val[] = []
|
|
188
|
-
const dyn: Val[] = []
|
|
189
|
-
for (const term of upeer) {
|
|
190
|
-
if (term.isPathDependent || hasSpreads(term)) {
|
|
191
|
-
dyn.push(term)
|
|
192
|
-
} else {
|
|
193
|
-
pure.push(term)
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
upeer = pure.concat(dyn)
|
|
120
|
+
// console.log('CONJUNCT-UPEER', this.id, upeer.map((v: Val) => v.canon))
|
|
197
121
|
|
|
198
122
|
// Unify terms against each other
|
|
199
123
|
|
|
@@ -217,28 +141,17 @@ class ConjunctVal extends JunctionVal {
|
|
|
217
141
|
|
|
218
142
|
// Can't unite with a RefVal, unless also a RefVal with same path.
|
|
219
143
|
// else if (t0 instanceof RefVal && !(t1 instanceof RefVal)) {
|
|
220
|
-
else if (t0.
|
|
144
|
+
else if (t0.isRef && !(t1.isRef)) {
|
|
221
145
|
outvals.push(t0)
|
|
222
146
|
t0 = t1
|
|
223
147
|
}
|
|
224
148
|
|
|
225
|
-
else if (t1.
|
|
149
|
+
else if (t1.isRef && !(t0.isRef)) {
|
|
226
150
|
outvals.push(t0)
|
|
227
151
|
t0 = t1
|
|
228
152
|
}
|
|
229
153
|
|
|
230
154
|
|
|
231
|
-
// Shallow merge: when both are maps AND the conjunct contains spreads,
|
|
232
|
-
// combine keys into inner ConjunctVals instead of deep unification.
|
|
233
|
-
// This ensures spreads see ALL children across terms before being applied.
|
|
234
|
-
else if (t0.isMap && t1.isMap && (hasSpreads(t0) || hasSpreads(t1))) {
|
|
235
|
-
const merged = shallowMerge(t0 as any, t1 as any, ctx)
|
|
236
|
-
done = done && DONE === merged.dc
|
|
237
|
-
newtype = this.mark.type || merged.mark.type
|
|
238
|
-
newhide = this.mark.hide || merged.mark.hide
|
|
239
|
-
t0 = merged
|
|
240
|
-
}
|
|
241
|
-
|
|
242
155
|
else {
|
|
243
156
|
val = unite(te ? ctx.clone({ explain: ec(te, 'DEF') }) : ctx, t0, t1, 'cj-peer-t0t1')
|
|
244
157
|
// console.log('CONJUNCT-T', t0.canon, t1?.canon, '->', val.canon)
|
|
@@ -246,7 +159,7 @@ class ConjunctVal extends JunctionVal {
|
|
|
246
159
|
newtype = this.mark.type || val.mark.type
|
|
247
160
|
newhide = this.mark.hide || val.mark.hide
|
|
248
161
|
|
|
249
|
-
// Unite was just a
|
|
162
|
+
// Unite was just a conjunt anyway, so discard.
|
|
250
163
|
if (val.isConjunct) {
|
|
251
164
|
outvals.push(t0)
|
|
252
165
|
t0 = t1
|
|
@@ -302,15 +215,6 @@ class ConjunctVal extends JunctionVal {
|
|
|
302
215
|
|
|
303
216
|
|
|
304
217
|
gen(ctx?: AontuContext) {
|
|
305
|
-
// If this conjunct contains a SpreadVal + a generable Val,
|
|
306
|
-
// gen the generable Val (spread is an unresolved constraint
|
|
307
|
-
// that's a no-op at gen time — e.g., empty map with spread).
|
|
308
|
-
if (this.peg.length === 2) {
|
|
309
|
-
const [a, b] = this.peg
|
|
310
|
-
if (a.isSpread && b.isGenable) return b.gen(ctx as any)
|
|
311
|
-
if (b.isSpread && a.isGenable) return a.gen(ctx as any)
|
|
312
|
-
}
|
|
313
|
-
|
|
314
218
|
// Unresolved conjunct cannot be generated, so always an error.
|
|
315
219
|
let nil = makeNilErr(
|
|
316
220
|
ctx,
|
|
@@ -343,17 +247,18 @@ class ConjunctVal extends JunctionVal {
|
|
|
343
247
|
function norm(terms: Val[]): Val[] {
|
|
344
248
|
|
|
345
249
|
let expand: Val[] = []
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
else {
|
|
352
|
-
expand.push(terms[tI])
|
|
250
|
+
for (let tI = 0, pI = 0; tI < terms.length; tI++, pI++) {
|
|
251
|
+
if (terms[tI].isConjunct) {
|
|
252
|
+
const cterms = terms[tI].peg
|
|
253
|
+
for (let cI = 0; cI < cterms.length; cI++) {
|
|
254
|
+
expand[pI + cI] = cterms[cI]
|
|
353
255
|
}
|
|
256
|
+
pI += cterms.length - 1
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
expand[pI] = terms[tI]
|
|
354
260
|
}
|
|
355
261
|
}
|
|
356
|
-
flattenTerms(terms)
|
|
357
262
|
|
|
358
263
|
|
|
359
264
|
// Consistent ordering ensures order independent unification.
|
|
@@ -377,29 +282,6 @@ function norm(terms: Val[]): Val[] {
|
|
|
377
282
|
}
|
|
378
283
|
|
|
379
284
|
|
|
380
|
-
// Check if a Val tree contains any SpreadVal constraints.
|
|
381
|
-
// Cached on _hasSpreads for performance.
|
|
382
|
-
function hasSpreads(v: any): boolean {
|
|
383
|
-
if (v._hasSpreads !== undefined) return v._hasSpreads
|
|
384
|
-
let result = false
|
|
385
|
-
if (v.isSpread) {
|
|
386
|
-
result = true
|
|
387
|
-
}
|
|
388
|
-
else if (v.isMap || v.isList) {
|
|
389
|
-
for (const k in v.peg) {
|
|
390
|
-
if (v.peg[k]?.isVal && hasSpreads(v.peg[k])) { result = true; break }
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
else if (v.isJunction) {
|
|
394
|
-
for (const p of v.peg) {
|
|
395
|
-
if (p?.isVal && hasSpreads(p)) { result = true; break }
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
v._hasSpreads = result
|
|
399
|
-
return result
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
|
|
403
285
|
export {
|
|
404
286
|
norm,
|
|
405
287
|
ConjunctVal,
|
package/src/val/CopyFuncVal.ts
CHANGED
|
@@ -61,10 +61,9 @@ class CopyFuncVal extends FuncBaseVal {
|
|
|
61
61
|
|
|
62
62
|
// console.log('CR', out)
|
|
63
63
|
|
|
64
|
-
if (!out.
|
|
65
|
-
out.mark.type = false
|
|
66
|
-
out.mark.hide = false
|
|
64
|
+
if (!out.isRef) {
|
|
67
65
|
walk(out, (_key: string | number | undefined, val: Val) => {
|
|
66
|
+
// console.log('WALK', val)
|
|
68
67
|
val.mark.type = false
|
|
69
68
|
val.mark.hide = false
|
|
70
69
|
return val
|
package/src/val/DisjunctVal.ts
CHANGED
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
top
|
|
31
31
|
} from './top'
|
|
32
32
|
|
|
33
|
-
|
|
34
33
|
import { NilVal, TRIAL_NIL } from '../val/NilVal'
|
|
35
34
|
import { PrefVal } from '../val/PrefVal'
|
|
36
35
|
import { JunctionVal } from '../val/JunctionVal'
|
|
@@ -68,11 +67,6 @@ class DisjunctVal extends JunctionVal {
|
|
|
68
67
|
unify(peer: Val, ctx: AontuContext): Val {
|
|
69
68
|
peer = peer ?? top()
|
|
70
69
|
|
|
71
|
-
// Fast path: already-done disjunct unifying with TOP.
|
|
72
|
-
if (this.done && peer.isTop) {
|
|
73
|
-
return this
|
|
74
|
-
}
|
|
75
|
-
|
|
76
70
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Disjunct', this, peer)
|
|
77
71
|
|
|
78
72
|
if (!this.prefsRanked) {
|
package/src/val/ExpectVal.ts
CHANGED
|
@@ -51,25 +51,11 @@ 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
|
-
|
|
56
|
-
while (peg?.isExpect) {
|
|
57
|
-
peg = peg.peg
|
|
58
|
-
}
|
|
54
|
+
const peeru =
|
|
55
|
+
unite(te ? ctx.clone({ explain: ec(te, 'EXPECT') }) : ctx, this.peer, this.peg, 'expect-self')
|
|
59
56
|
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
}
|
|
57
|
+
if (peeru.isGenable) {
|
|
58
|
+
out = peeru
|
|
73
59
|
}
|
|
74
60
|
}
|
|
75
61
|
|
package/src/val/JunctionVal.ts
CHANGED
|
@@ -17,7 +17,6 @@ import { FeatureVal } from './FeatureVal'
|
|
|
17
17
|
// (ConjunctVal and DisjunctVal)
|
|
18
18
|
abstract class JunctionVal extends FeatureVal {
|
|
19
19
|
isJunction = true
|
|
20
|
-
_canonCache?: string
|
|
21
20
|
|
|
22
21
|
constructor(
|
|
23
22
|
spec: ValSpec,
|
|
@@ -39,13 +38,10 @@ abstract class JunctionVal extends FeatureVal {
|
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
get canon() {
|
|
42
|
-
|
|
43
|
-
const c = this.peg.map((v: Val) => {
|
|
41
|
+
return this.peg.map((v: Val) => {
|
|
44
42
|
return (v as any).isJunction && Array.isArray(v.peg) && 1 < v.peg.length ?
|
|
45
43
|
'(' + v.canon + ')' : v.canon // v.id + '=' + v.canon
|
|
46
44
|
}).join(this.getJunctionSymbol()) // + '<' + (this.mark.hide ? 'H' : '') + '>'
|
|
47
|
-
if (this.done) this._canonCache = c
|
|
48
|
-
return c
|
|
49
45
|
}
|
|
50
46
|
|
|
51
47
|
// Abstract method to be implemented by subclasses to define their junction symbol
|
package/src/val/KeyFuncVal.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { ConjunctVal } from '../val/ConjunctVal'
|
|
|
18
18
|
|
|
19
19
|
import { FuncBaseVal } from './FuncBaseVal'
|
|
20
20
|
|
|
21
|
+
|
|
21
22
|
class KeyFuncVal extends FuncBaseVal {
|
|
22
23
|
isKeyFunc = true
|
|
23
24
|
|
|
@@ -40,12 +41,14 @@ class KeyFuncVal extends FuncBaseVal {
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
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.
|
|
43
45
|
let out: Val = this
|
|
44
46
|
|
|
45
47
|
if (ctx.cc < 3) {
|
|
46
48
|
this.notdone()
|
|
47
49
|
|
|
48
50
|
if (peer.isTop || (peer.id === this.id)) {
|
|
51
|
+
// TODO: clone needed to avoid triggering unify_cycle - find a better way
|
|
49
52
|
out = this.clone(ctx)
|
|
50
53
|
}
|
|
51
54
|
else if (peer.isNil) {
|
package/src/val/ListVal.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
11
|
DONE,
|
|
12
|
+
SPREAD,
|
|
12
13
|
} from '../type'
|
|
13
14
|
|
|
14
15
|
import { AontuContext } from '../ctx'
|
|
@@ -29,6 +30,7 @@ import {
|
|
|
29
30
|
top
|
|
30
31
|
} from './top'
|
|
31
32
|
|
|
33
|
+
import { ConjunctVal } from './ConjunctVal'
|
|
32
34
|
import { NilVal } from './NilVal'
|
|
33
35
|
import { BagVal } from './BagVal'
|
|
34
36
|
import { empty } from './Val'
|
|
@@ -36,7 +38,6 @@ import { empty } from './Val'
|
|
|
36
38
|
|
|
37
39
|
class ListVal extends BagVal {
|
|
38
40
|
isList = true
|
|
39
|
-
_canonCache?: string
|
|
40
41
|
|
|
41
42
|
constructor(
|
|
42
43
|
spec: {
|
|
@@ -50,6 +51,24 @@ class ListVal extends BagVal {
|
|
|
50
51
|
throw new AontuError('ListVal spec.peg undefined')
|
|
51
52
|
}
|
|
52
53
|
|
|
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
|
+
}
|
|
53
72
|
}
|
|
54
73
|
|
|
55
74
|
|
|
@@ -67,7 +86,8 @@ class ListVal extends BagVal {
|
|
|
67
86
|
let out: ListVal | NilVal = (peer.isTop ? this : new ListVal({ peg: [] }, ctx))
|
|
68
87
|
|
|
69
88
|
out.closed = this.closed
|
|
70
|
-
out.optionalKeys =
|
|
89
|
+
out.optionalKeys = [...this.optionalKeys]
|
|
90
|
+
out.spread.cj = this.spread.cj
|
|
71
91
|
out.site = this.site
|
|
72
92
|
|
|
73
93
|
if (peer instanceof ListVal) {
|
|
@@ -77,6 +97,13 @@ class ListVal extends BagVal {
|
|
|
77
97
|
}
|
|
78
98
|
else {
|
|
79
99
|
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
|
+
)
|
|
80
107
|
}
|
|
81
108
|
}
|
|
82
109
|
|
|
@@ -84,39 +111,29 @@ class ListVal extends BagVal {
|
|
|
84
111
|
if (!exit) {
|
|
85
112
|
out.dc = this.dc + 1
|
|
86
113
|
|
|
87
|
-
|
|
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
|
-
}
|
|
114
|
+
let spread_cj = out.spread.cj || TOP
|
|
102
115
|
|
|
103
|
-
//
|
|
116
|
+
// Always unify children first
|
|
104
117
|
for (let key in this.peg) {
|
|
105
118
|
const keyctx = ctx.descend(key)
|
|
119
|
+
const key_spread_cj = spread_cj.spreadClone(keyctx)
|
|
106
120
|
const child = this.peg[key]
|
|
107
121
|
|
|
108
122
|
propagateMarks(this, child)
|
|
109
123
|
|
|
110
124
|
out.peg[key] =
|
|
111
|
-
undefined === child ?
|
|
125
|
+
undefined === child ? key_spread_cj :
|
|
112
126
|
child.isNil ? child :
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
child
|
|
127
|
+
key_spread_cj.isNil ? key_spread_cj :
|
|
128
|
+
key_spread_cj.isTop && child.done ? child :
|
|
129
|
+
child.isTop && key_spread_cj.done ? key_spread_cj :
|
|
130
|
+
unite(te ? keyctx.clone({ explain: ec(te, 'PEG:' + key) }) : keyctx,
|
|
131
|
+
child, key_spread_cj, 'list-own')
|
|
116
132
|
|
|
117
133
|
done = (done && DONE === out.peg[key].dc)
|
|
118
134
|
}
|
|
119
135
|
|
|
136
|
+
const allowedKeys: string[] = this.closed ? Object.keys(this.peg) : []
|
|
120
137
|
let bad: NilVal | undefined = undefined
|
|
121
138
|
|
|
122
139
|
if (peer instanceof ListVal) {
|
|
@@ -124,10 +141,11 @@ class ListVal extends BagVal {
|
|
|
124
141
|
te ? ctx.clone({ explain: ec(te, 'PER') }) : ctx,
|
|
125
142
|
peer, TOP, 'list-peer-list') as ListVal)
|
|
126
143
|
|
|
144
|
+
// NOTE: peerkey is the index
|
|
127
145
|
for (let peerkey in upeer.peg) {
|
|
128
146
|
let peerchild = upeer.peg[peerkey]
|
|
129
147
|
|
|
130
|
-
if (this.closed && !(peerkey
|
|
148
|
+
if (this.closed && !allowedKeys.includes(peerkey)) {
|
|
131
149
|
bad = makeNilErr(ctx, 'closed', peerchild, undefined)
|
|
132
150
|
}
|
|
133
151
|
|
|
@@ -137,11 +155,19 @@ class ListVal extends BagVal {
|
|
|
137
155
|
|
|
138
156
|
let oval = out.peg[peerkey] =
|
|
139
157
|
undefined === child ? peerchild :
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
158
|
+
child.isTop && peerchild.done ? peerchild :
|
|
159
|
+
child.isNil ? child :
|
|
160
|
+
peerchild.isNil ? peerchild :
|
|
161
|
+
unite(te ? peerctx.clone({ explain: ec(te, 'CHD') }) : peerctx,
|
|
162
|
+
child, peerchild, 'list-peer')
|
|
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
|
+
}
|
|
145
171
|
|
|
146
172
|
propagateMarks(this, oval)
|
|
147
173
|
|
|
@@ -171,18 +197,39 @@ class ListVal extends BagVal {
|
|
|
171
197
|
}
|
|
172
198
|
|
|
173
199
|
|
|
174
|
-
// Spread clone:
|
|
175
|
-
//
|
|
200
|
+
// Spread clone: only deep-clone children that are path-dependent
|
|
201
|
+
// (isFunc, isRef). Share all other children directly.
|
|
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.
|
|
176
207
|
spreadClone(ctx: AontuContext): Val {
|
|
208
|
+
// B1: share directly when the spread tree has no path-dependent
|
|
209
|
+
// leaves. See MapVal.spreadClone for rationale.
|
|
177
210
|
if (!this.isPathDependent) return this
|
|
178
211
|
|
|
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
|
+
|
|
179
224
|
let out = (super.clone(ctx) as ListVal)
|
|
180
225
|
|
|
181
226
|
for (let entry of Object.entries(this.peg)) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
227
|
+
out.peg[entry[0]] = entry[1]
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Must create a new spread object to avoid mutating the original.
|
|
231
|
+
out.spread = {
|
|
232
|
+
cj: this.spread.cj ? this.spread.cj.spreadClone(ctx) : undefined,
|
|
186
233
|
}
|
|
187
234
|
|
|
188
235
|
out.closed = this.closed
|
|
@@ -198,6 +245,10 @@ class ListVal extends BagVal {
|
|
|
198
245
|
out.peg[entry[0]] =
|
|
199
246
|
(entry[1] as any)?.isVal ? (entry[1] as Val).clone(ctx, spec?.mark ? { mark: spec.mark } : {}) : entry[1]
|
|
200
247
|
}
|
|
248
|
+
if (this.spread.cj) {
|
|
249
|
+
out.spread.cj = this.spread.cj.clone(ctx, spec?.mark ? { mark: spec.mark } : {})
|
|
250
|
+
}
|
|
251
|
+
|
|
201
252
|
out.closed = this.closed
|
|
202
253
|
out.optionalKeys = [...this.optionalKeys]
|
|
203
254
|
|
|
@@ -207,19 +258,18 @@ class ListVal extends BagVal {
|
|
|
207
258
|
|
|
208
259
|
|
|
209
260
|
get canon() {
|
|
210
|
-
if (this._canonCache !== undefined) return this._canonCache
|
|
211
261
|
// console.log('LISTVAL-CANON', this.optionalKeys)
|
|
212
262
|
let keys = Object.keys(this.peg)
|
|
213
|
-
|
|
263
|
+
return '' +
|
|
214
264
|
// this.errcanon() +
|
|
215
265
|
'[' +
|
|
266
|
+
(this.spread.cj ? '&:' + this.spread.cj.canon +
|
|
267
|
+
(0 < keys.length ? ',' : '') : '') +
|
|
216
268
|
keys
|
|
217
269
|
.map(k => this.optionalKeys.includes(k) ?
|
|
218
270
|
k + '?:' + this.peg[k].canon :
|
|
219
271
|
this.peg[k].canon).join(',') +
|
|
220
272
|
']'
|
|
221
|
-
if (this.done) this._canonCache = c
|
|
222
|
-
return c
|
|
223
273
|
}
|
|
224
274
|
}
|
|
225
275
|
|