aontu 0.40.0 → 0.42.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 +14 -2
- package/dist/ctx.js +56 -9
- package/dist/ctx.js.map +1 -1
- package/dist/err.js +36 -15
- package/dist/err.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 +372 -55
- 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 +138 -15
- 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 +40 -10
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +17 -4
- 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/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 +36 -65
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +3 -2
- package/dist/val/MapVal.js +71 -91
- 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/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/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 +20 -7
- 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 +4 -1
- package/dist/val/Val.js +88 -44
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +3 -3
- package/dist/val/VarVal.js.map +1 -1
- package/package.json +6 -7
- package/src/aontu.ts +9 -1
- package/src/ctx.ts +95 -11
- package/src/err.ts +37 -17
- package/src/lang.ts +113 -23
- package/src/tsconfig.json +2 -1
- package/src/type.ts +5 -0
- package/src/unify.ts +390 -64
- package/src/utility.ts +5 -2
- package/src/val/BagVal.ts +6 -7
- package/src/val/ConjunctVal.ts +133 -15
- package/src/val/CopyFuncVal.ts +3 -2
- package/src/val/DisjunctVal.ts +43 -11
- package/src/val/ExpectVal.ts +19 -5
- package/src/val/FuncBaseVal.ts +2 -2
- package/src/val/IntegerVal.ts +1 -1
- package/src/val/JunctionVal.ts +5 -1
- package/src/val/KeyFuncVal.ts +0 -3
- package/src/val/ListVal.ts +40 -86
- package/src/val/MapVal.ts +78 -119
- package/src/val/MoveFuncVal.ts +79 -14
- package/src/val/NilVal.ts +17 -1
- package/src/val/OpBaseVal.ts +1 -1
- package/src/val/PathFuncVal.ts +29 -4
- package/src/val/PathVal.ts +435 -0
- package/src/val/PrefVal.ts +21 -8
- package/src/val/{RefVal.ts → RefVal.ts.old} +31 -20
- package/src/val/SpreadVal.ts +275 -0
- package/src/val/Val.ts +141 -50
- package/src/val/VarVal.ts +3 -3
- package/dist/val/RefVal.js.map +0 -1
package/src/val/ConjunctVal.ts
CHANGED
|
@@ -36,6 +36,57 @@ 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
|
+
|
|
39
90
|
|
|
40
91
|
|
|
41
92
|
// TODO: move main logic to op/conjunct
|
|
@@ -71,6 +122,11 @@ class ConjunctVal extends JunctionVal {
|
|
|
71
122
|
unify(peer: Val, ctx: AontuContext): Val {
|
|
72
123
|
peer = peer ?? top()
|
|
73
124
|
|
|
125
|
+
// Fast path: done conjunct self-unifying with TOP.
|
|
126
|
+
if (this.done && peer.isTop) {
|
|
127
|
+
return this
|
|
128
|
+
}
|
|
129
|
+
|
|
74
130
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Conjunct', this, peer)
|
|
75
131
|
|
|
76
132
|
let done = true
|
|
@@ -96,8 +152,13 @@ class ConjunctVal extends JunctionVal {
|
|
|
96
152
|
this.peg[vI].mark.hide = newhide
|
|
97
153
|
// console.log('CONJUNCT-TERM', this.id, vI, this.peg[vI].canon)
|
|
98
154
|
|
|
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)
|
|
99
159
|
upeer[vI] = (this.peg[vI].done && peer.isTop) ? this.peg[vI] :
|
|
100
|
-
|
|
160
|
+
(hasMapPeers && hasSpreads(this.peg[vI])) ? this.peg[vI] :
|
|
161
|
+
unite(te ? ctx.clone({ explain: ec(te, 'OWN') }) : ctx, this.peg[vI], peer, 'cj-own')
|
|
101
162
|
|
|
102
163
|
upeer[vI].mark.type = newtype = newtype || upeer[vI].mark.type
|
|
103
164
|
upeer[vI].mark.hide = newhide = newhide || upeer[vI].mark.hide
|
|
@@ -117,7 +178,22 @@ class ConjunctVal extends JunctionVal {
|
|
|
117
178
|
}
|
|
118
179
|
|
|
119
180
|
upeer = norm(upeer)
|
|
120
|
-
|
|
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)
|
|
121
197
|
|
|
122
198
|
// Unify terms against each other
|
|
123
199
|
|
|
@@ -141,25 +217,36 @@ class ConjunctVal extends JunctionVal {
|
|
|
141
217
|
|
|
142
218
|
// Can't unite with a RefVal, unless also a RefVal with same path.
|
|
143
219
|
// else if (t0 instanceof RefVal && !(t1 instanceof RefVal)) {
|
|
144
|
-
else if (t0.
|
|
220
|
+
else if (t0.isPath && !(t1.isPath)) {
|
|
145
221
|
outvals.push(t0)
|
|
146
222
|
t0 = t1
|
|
147
223
|
}
|
|
148
224
|
|
|
149
|
-
else if (t1.
|
|
225
|
+
else if (t1.isPath && !(t0.isPath)) {
|
|
150
226
|
outvals.push(t0)
|
|
151
227
|
t0 = t1
|
|
152
228
|
}
|
|
153
229
|
|
|
154
230
|
|
|
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
|
+
|
|
155
242
|
else {
|
|
156
|
-
val = unite(ctx.clone({ explain: ec(te, 'DEF') }), t0, t1, 'cj-peer-t0t1')
|
|
243
|
+
val = unite(te ? ctx.clone({ explain: ec(te, 'DEF') }) : ctx, t0, t1, 'cj-peer-t0t1')
|
|
157
244
|
// console.log('CONJUNCT-T', t0.canon, t1?.canon, '->', val.canon)
|
|
158
245
|
done = done && DONE === val.dc
|
|
159
246
|
newtype = this.mark.type || val.mark.type
|
|
160
247
|
newhide = this.mark.hide || val.mark.hide
|
|
161
248
|
|
|
162
|
-
// Unite was just a
|
|
249
|
+
// Unite was just a conjunct anyway, so discard.
|
|
163
250
|
if (val.isConjunct) {
|
|
164
251
|
outvals.push(t0)
|
|
165
252
|
t0 = t1
|
|
@@ -215,6 +302,15 @@ class ConjunctVal extends JunctionVal {
|
|
|
215
302
|
|
|
216
303
|
|
|
217
304
|
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
|
+
|
|
218
314
|
// Unresolved conjunct cannot be generated, so always an error.
|
|
219
315
|
let nil = makeNilErr(
|
|
220
316
|
ctx,
|
|
@@ -247,18 +343,17 @@ class ConjunctVal extends JunctionVal {
|
|
|
247
343
|
function norm(terms: Val[]): Val[] {
|
|
248
344
|
|
|
249
345
|
let expand: Val[] = []
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
346
|
+
function flattenTerms(terms: Val[]) {
|
|
347
|
+
for (let tI = 0; tI < terms.length; tI++) {
|
|
348
|
+
if (terms[tI].isConjunct) {
|
|
349
|
+
flattenTerms(terms[tI].peg)
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
expand.push(terms[tI])
|
|
255
353
|
}
|
|
256
|
-
pI += cterms.length - 1
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
expand[pI] = terms[tI]
|
|
260
354
|
}
|
|
261
355
|
}
|
|
356
|
+
flattenTerms(terms)
|
|
262
357
|
|
|
263
358
|
|
|
264
359
|
// Consistent ordering ensures order independent unification.
|
|
@@ -282,6 +377,29 @@ function norm(terms: Val[]): Val[] {
|
|
|
282
377
|
}
|
|
283
378
|
|
|
284
379
|
|
|
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
|
+
|
|
285
403
|
export {
|
|
286
404
|
norm,
|
|
287
405
|
ConjunctVal,
|
package/src/val/CopyFuncVal.ts
CHANGED
|
@@ -61,9 +61,10 @@ class CopyFuncVal extends FuncBaseVal {
|
|
|
61
61
|
|
|
62
62
|
// console.log('CR', out)
|
|
63
63
|
|
|
64
|
-
if (!out.
|
|
64
|
+
if (!out.isPath) {
|
|
65
|
+
out.mark.type = false
|
|
66
|
+
out.mark.hide = false
|
|
65
67
|
walk(out, (_key: string | number | undefined, val: Val) => {
|
|
66
|
-
// console.log('WALK', val)
|
|
67
68
|
val.mark.type = false
|
|
68
69
|
val.mark.hide = false
|
|
69
70
|
return val
|
package/src/val/DisjunctVal.ts
CHANGED
|
@@ -30,7 +30,8 @@ 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'
|
|
36
37
|
|
|
@@ -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) {
|
|
@@ -79,21 +85,46 @@ class DisjunctVal extends JunctionVal {
|
|
|
79
85
|
|
|
80
86
|
let oval: Val[] = []
|
|
81
87
|
|
|
82
|
-
// Conjunction (&) distributes over disjunction (|)
|
|
88
|
+
// Conjunction (&) distributes over disjunction (|).
|
|
89
|
+
//
|
|
90
|
+
// Each member is tried against peer in isolation: if that trial
|
|
91
|
+
// produces any errors, the member fails and is marked with a NilVal.
|
|
92
|
+
// Previously this used `ctx?.clone({err: []})` per member - a
|
|
93
|
+
// per-iteration context clone (two Object.creates) just to hold a
|
|
94
|
+
// throwaway error array. For schemas with many disjunctions
|
|
95
|
+
// (e.g. `*true | boolean`, `method: GET | PUT | ...`) this was the
|
|
96
|
+
// single largest source of clones in the unify hot path.
|
|
97
|
+
//
|
|
98
|
+
// Swap-and-restore avoids the clone: the existing ctx's err array
|
|
99
|
+
// is saved, replaced with a fresh array for each trial, then
|
|
100
|
+
// restored. ctx mutation is scoped to this loop and fully undone
|
|
101
|
+
// before return.
|
|
102
|
+
const savedErr = ctx.err
|
|
103
|
+
const savedTrialMode = ctx._trialMode
|
|
104
|
+
// C1-inner: tell `makeNilErr` to return TRIAL_NIL in this scope
|
|
105
|
+
// instead of allocating per-failure NilVals. Save/restore so
|
|
106
|
+
// nested DisjunctVal trials (and the outer non-trial code) are
|
|
107
|
+
// not affected.
|
|
108
|
+
ctx._trialMode = true
|
|
83
109
|
for (let vI = 0; vI < this.peg.length; vI++) {
|
|
84
110
|
const v = this.peg[vI]
|
|
85
|
-
const
|
|
111
|
+
const trialErr: any[] = []
|
|
112
|
+
ctx.err = trialErr
|
|
86
113
|
|
|
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)
|
|
114
|
+
oval[vI] = unite(te ? ctx.clone({ explain: ec(te, 'DIST:' + vI) }) : ctx, v, peer, 'dj-peer')
|
|
90
115
|
|
|
91
|
-
if (0 <
|
|
92
|
-
|
|
116
|
+
if (0 < trialErr.length) {
|
|
117
|
+
// C1: failed-trial marker is never user-visible — it just
|
|
118
|
+
// signals "this disjunct member doesn't match" and is
|
|
119
|
+
// filtered out before the result is built. Use the shared
|
|
120
|
+
// sentinel instead of allocating a fresh NilVal per trial.
|
|
121
|
+
oval[vI] = TRIAL_NIL
|
|
93
122
|
}
|
|
94
123
|
|
|
95
124
|
done = done && DONE === oval[vI].dc
|
|
96
125
|
}
|
|
126
|
+
ctx._trialMode = savedTrialMode
|
|
127
|
+
ctx.err = savedErr
|
|
97
128
|
|
|
98
129
|
// // // console.log('DISJUNCT-unify-B', this.id, oval.map(v => v.canon))
|
|
99
130
|
|
|
@@ -107,12 +138,13 @@ class DisjunctVal extends JunctionVal {
|
|
|
107
138
|
|
|
108
139
|
// // // console.log('DISJUNCT-unify-C', this.id, oval.map(v => v.id + '=' + v.canon))
|
|
109
140
|
|
|
110
|
-
//
|
|
111
|
-
|
|
141
|
+
// Dedup: duplicate Vals in the disjunct are replaced with the
|
|
142
|
+
// trial sentinel, which is filtered out a few lines below.
|
|
143
|
+
// (No need for a fresh NilVal — any isNil value gets filtered.)
|
|
112
144
|
for (let vI = 0; vI < oval.length; vI++) {
|
|
113
145
|
for (let kI = vI + 1; kI < oval.length; kI++) {
|
|
114
146
|
if (oval[kI].same(oval[vI])) {
|
|
115
|
-
oval[kI] =
|
|
147
|
+
oval[kI] = TRIAL_NIL
|
|
116
148
|
}
|
|
117
149
|
}
|
|
118
150
|
}
|
package/src/val/ExpectVal.ts
CHANGED
|
@@ -49,13 +49,27 @@ 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
|
-
|
|
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/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/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,24 +67,16 @@ 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) {
|
|
94
74
|
if (!this.closed && peer.closed) {
|
|
95
|
-
out = peer.unify(this, ctx.clone({ explain: ec(te, 'PMC') })) as ListVal
|
|
75
|
+
out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'PMC') }) : ctx) as ListVal
|
|
96
76
|
exit = true
|
|
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(ctx.clone({ explain: ec(te, 'SPR') }),
|
|
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(keyctx.clone({ explain: ec(te, 'PEG:' + key) }),
|
|
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,35 +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
|
-
|
|
209
|
-
for (let key in this.peg) {
|
|
210
|
-
if (!(this.peg[key] as any)?.isScalarKind) {
|
|
211
|
-
allScalarKind = false
|
|
212
|
-
break
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (!allScalarKind) {
|
|
217
|
-
return this.clone(ctx)
|
|
218
|
-
}
|
|
177
|
+
if (!this.isPathDependent) return this
|
|
219
178
|
|
|
220
179
|
let out = (super.clone(ctx) as ListVal)
|
|
221
180
|
|
|
222
181
|
for (let entry of Object.entries(this.peg)) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
out.spread = {
|
|
228
|
-
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
|
|
229
186
|
}
|
|
230
187
|
|
|
231
188
|
out.closed = this.closed
|
|
@@ -241,10 +198,6 @@ class ListVal extends BagVal {
|
|
|
241
198
|
out.peg[entry[0]] =
|
|
242
199
|
(entry[1] as any)?.isVal ? (entry[1] as Val).clone(ctx, spec?.mark ? { mark: spec.mark } : {}) : entry[1]
|
|
243
200
|
}
|
|
244
|
-
if (this.spread.cj) {
|
|
245
|
-
out.spread.cj = this.spread.cj.clone(ctx, spec?.mark ? { mark: spec.mark } : {})
|
|
246
|
-
}
|
|
247
|
-
|
|
248
201
|
out.closed = this.closed
|
|
249
202
|
out.optionalKeys = [...this.optionalKeys]
|
|
250
203
|
|
|
@@ -254,18 +207,19 @@ class ListVal extends BagVal {
|
|
|
254
207
|
|
|
255
208
|
|
|
256
209
|
get canon() {
|
|
210
|
+
if (this._canonCache !== undefined) return this._canonCache
|
|
257
211
|
// console.log('LISTVAL-CANON', this.optionalKeys)
|
|
258
212
|
let keys = Object.keys(this.peg)
|
|
259
|
-
|
|
213
|
+
const c = '' +
|
|
260
214
|
// this.errcanon() +
|
|
261
215
|
'[' +
|
|
262
|
-
(this.spread.cj ? '&:' + this.spread.cj.canon +
|
|
263
|
-
(0 < keys.length ? ',' : '') : '') +
|
|
264
216
|
keys
|
|
265
217
|
.map(k => this.optionalKeys.includes(k) ?
|
|
266
218
|
k + '?:' + this.peg[k].canon :
|
|
267
219
|
this.peg[k].canon).join(',') +
|
|
268
220
|
']'
|
|
221
|
+
if (this.done) this._canonCache = c
|
|
222
|
+
return c
|
|
269
223
|
}
|
|
270
224
|
}
|
|
271
225
|
|