aontu 0.44.0 → 0.46.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/cli.d.ts +9 -0
- package/dist/cli.js +203 -0
- package/dist/cli.js.map +1 -0
- 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 +9 -4
- package/src/cli.ts +193 -0
- 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/unify.ts
CHANGED
|
@@ -10,9 +10,6 @@ import { DONE } from './type'
|
|
|
10
10
|
import { makeNilErr } from './err'
|
|
11
11
|
|
|
12
12
|
import { NilVal } from './val/NilVal'
|
|
13
|
-
import { StringVal } from './val/StringVal'
|
|
14
|
-
import { PathVal } from './val/PathVal'
|
|
15
|
-
import { KeyFuncVal } from './val/KeyFuncVal'
|
|
16
13
|
|
|
17
14
|
import {
|
|
18
15
|
Lang
|
|
@@ -29,9 +26,8 @@ import {
|
|
|
29
26
|
} from './val/top'
|
|
30
27
|
|
|
31
28
|
|
|
32
|
-
|
|
33
29
|
// TODO: FIX: false positive when too many top unifications
|
|
34
|
-
const MAXCYCLE =
|
|
30
|
+
const MAXCYCLE = 999
|
|
35
31
|
|
|
36
32
|
// Vals should only have to unify downwards (in .unify) over Vals they understand.
|
|
37
33
|
// and for complex Vals, TOP, which means self unify if not yet done
|
|
@@ -54,19 +50,12 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
54
50
|
if (a.done && b.done) {
|
|
55
51
|
if (a.id === b.id) return a
|
|
56
52
|
if (a.constructor === b.constructor && a.peg === b.peg
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
&& !a.isNil && !b.isNil
|
|
54
|
+
&& !a.isMap && !a.isList
|
|
55
|
+
&& !a.isConjunct && !a.isDisjunct
|
|
56
|
+
&& !a.isRef && !a.isPref && !a.isFunc && !a.isExpect) {
|
|
60
57
|
return a
|
|
61
58
|
}
|
|
62
|
-
|
|
63
|
-
// Id-keyed cache: reuse results for the exact same Val pair.
|
|
64
|
-
const uc = ctx._uniteCache
|
|
65
|
-
if (uc !== undefined) {
|
|
66
|
-
const ucKey = a.id + '|' + b.id
|
|
67
|
-
const ucHit = uc.get(ucKey)
|
|
68
|
-
if (ucHit !== undefined) return ucHit
|
|
69
|
-
}
|
|
70
59
|
}
|
|
71
60
|
}
|
|
72
61
|
}
|
|
@@ -78,25 +67,21 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
78
67
|
|
|
79
68
|
// Cycle-detection key. Use numeric path index for speed; fall back to
|
|
80
69
|
// full string key when debug is enabled so the saw value is human-readable.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
saw = (a ? a.id + (a.done ? '' : '*') : '') + '~' +
|
|
70
|
+
const saw = ctx.opts.debug
|
|
71
|
+
? (a ? a.id + (a.done ? '' : '*') : '') + '~' +
|
|
84
72
|
(b ? b.id + (b.done ? '' : '*') : '') + '@' + ctx.pathstr
|
|
85
|
-
|
|
86
|
-
else {
|
|
87
|
-
saw = (a ? a.id + (a.done ? 'd' : '') : 0) + '~' +
|
|
73
|
+
: (a ? a.id + (a.done ? 'd' : '') : 0) + '~' +
|
|
88
74
|
(b ? b.id + (b.done ? 'd' : '') : 0) + '~' + ctx.pathidx
|
|
89
|
-
}
|
|
90
75
|
|
|
91
76
|
// NOTE: if this error occurs "unreasonably", attemp to avoid unnecesary unification
|
|
92
77
|
// See for example PrefVal peg.id equality inspection.
|
|
93
|
-
const sawCount = ctx.seen
|
|
78
|
+
const sawCount = ctx.seen[saw] ?? 0
|
|
94
79
|
if (MAXCYCLE < sawCount) {
|
|
95
80
|
// console.log('SAW', sawCount, saw, a?.id, a?.canon, b?.id, b?.canon, ctx.cc)
|
|
96
81
|
out = makeNilErr(ctx, 'unify_cycle', a, b)
|
|
97
82
|
}
|
|
98
83
|
else {
|
|
99
|
-
ctx.seen
|
|
84
|
+
ctx.seen[saw] = sawCount + 1
|
|
100
85
|
|
|
101
86
|
try {
|
|
102
87
|
let unified = false
|
|
@@ -133,7 +118,7 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
133
118
|
out = update(b, a)
|
|
134
119
|
why = 'bn'
|
|
135
120
|
}
|
|
136
|
-
else if (a.isConjunct || a.isExpect
|
|
121
|
+
else if (a.isConjunct || a.isExpect) {
|
|
137
122
|
out = a.unify(b, te ? ctx.clone({ explain: ec(te, 'AC') }) : ctx)
|
|
138
123
|
unified = true
|
|
139
124
|
why = 'a*'
|
|
@@ -141,11 +126,10 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
141
126
|
else if (
|
|
142
127
|
b.isConjunct
|
|
143
128
|
|| b.isDisjunct
|
|
144
|
-
|| b.
|
|
129
|
+
|| b.isRef
|
|
145
130
|
|| b.isPref
|
|
146
131
|
|| b.isFunc
|
|
147
132
|
|| b.isExpect
|
|
148
|
-
|| b.isSpread
|
|
149
133
|
) {
|
|
150
134
|
out = b.unify(a, te ? ctx.clone({ explain: ec(te, 'BW') }) : ctx)
|
|
151
135
|
unified = true
|
|
@@ -185,11 +169,6 @@ const unite = (ctx: AontuContext, a: any, b: any, whence: string) => {
|
|
|
185
169
|
|
|
186
170
|
ctx.explain && explainClose(te, out)
|
|
187
171
|
|
|
188
|
-
// Store in id-keyed cache when both operands were done.
|
|
189
|
-
if (a?.done && b?.done && out?.done && ctx._uniteCache !== undefined) {
|
|
190
|
-
ctx._uniteCache.set(a.id + '|' + b.id, out)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
172
|
return out
|
|
194
173
|
}
|
|
195
174
|
|
|
@@ -200,269 +179,6 @@ function update(x: Val, _y: Val) {
|
|
|
200
179
|
}
|
|
201
180
|
|
|
202
181
|
|
|
203
|
-
// Resolve all PathVals using the pre-collected paths list.
|
|
204
|
-
// Mutates the tree in place, replacing each PathVal with its cloned target.
|
|
205
|
-
function resolvePaths(root: Val, ctx: AontuContext, paths: PathVal[]) {
|
|
206
|
-
for (const pv of paths) {
|
|
207
|
-
if (pv.done) continue
|
|
208
|
-
|
|
209
|
-
// Resolve: find target, following chains.
|
|
210
|
-
// Suppress errors during pre-resolution — intermediate PathVals
|
|
211
|
-
// (from multi-dot expressions like ..a.q) may fail here but the
|
|
212
|
-
// final PathVal resolves correctly during unification.
|
|
213
|
-
const savedErr = ctx.err
|
|
214
|
-
ctx.err = []
|
|
215
|
-
let found: any = pv.find(ctx)
|
|
216
|
-
ctx.err = savedErr
|
|
217
|
-
if (found == null || found.isNil) continue
|
|
218
|
-
|
|
219
|
-
// Skip if target or container contains a mark-setting function
|
|
220
|
-
// (type/hide/move) — let unification resolve it first.
|
|
221
|
-
if (hasMarkFunc(found)) continue
|
|
222
|
-
if (hasMarkFuncAtPath(root, pv.path)) continue
|
|
223
|
-
|
|
224
|
-
while (found instanceof PathVal) {
|
|
225
|
-
if (found.done && found._resolved) {
|
|
226
|
-
found = found._resolved
|
|
227
|
-
break
|
|
228
|
-
}
|
|
229
|
-
const next = found.find(ctx)
|
|
230
|
-
if (next == null || next.isNil) break
|
|
231
|
-
found.dc = DONE
|
|
232
|
-
found._resolved = next
|
|
233
|
-
found = next
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// If found value is a key() function, set its path to the
|
|
237
|
-
// destination so it evaluates at the right position.
|
|
238
|
-
if (found.isKeyFunc) {
|
|
239
|
-
found.path = pv.path
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
pv.dc = DONE
|
|
243
|
-
pv._resolved = found
|
|
244
|
-
|
|
245
|
-
// Replace PathVal in tree using its path
|
|
246
|
-
replaceAtPath(root, pv.path, pv, found)
|
|
247
|
-
|
|
248
|
-
// Walk the placed value to resolve any PathVals cloned into it
|
|
249
|
-
resolveNestedPaths(found, ctx)
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
// Check if a value contains a type() or hide() function.
|
|
255
|
-
// Check if the value AT the path position is inside a mark-setting function.
|
|
256
|
-
function hasMarkFuncAtPath(root: Val, path: string[]): boolean {
|
|
257
|
-
let node: any = root
|
|
258
|
-
for (let i = 0; i < path.length; i++) {
|
|
259
|
-
const part = path[i]
|
|
260
|
-
if (node.isMap || node.isList) {
|
|
261
|
-
node = node.peg[part]
|
|
262
|
-
}
|
|
263
|
-
else if (node.isConjunct || node.isDisjunct) {
|
|
264
|
-
let found = null
|
|
265
|
-
const stack = [...node.peg]
|
|
266
|
-
while (stack.length > 0) {
|
|
267
|
-
const term = stack.pop()
|
|
268
|
-
if (term?.isConjunct || term?.isDisjunct) {
|
|
269
|
-
stack.push(...term.peg)
|
|
270
|
-
}
|
|
271
|
-
else if ((term?.isMap || term?.isList) && term.peg[part] != null) {
|
|
272
|
-
found = term.peg[part]
|
|
273
|
-
break
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
node = found
|
|
277
|
-
}
|
|
278
|
-
else {
|
|
279
|
-
return false
|
|
280
|
-
}
|
|
281
|
-
if (node == null) return false
|
|
282
|
-
}
|
|
283
|
-
return hasMarkFunc(node)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
function hasMarkFunc(val: any): boolean {
|
|
288
|
-
if (val == null || !val.isVal) return false
|
|
289
|
-
if (val.isTypeFunc || val.isHideFunc || val.isMoveFunc) return true
|
|
290
|
-
if (val.isConjunct || val.isDisjunct) {
|
|
291
|
-
for (const t of val.peg) {
|
|
292
|
-
if (hasMarkFunc(t)) return true
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return false
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// Resolve any PathVals found inside a value (e.g. cloned subtrees).
|
|
300
|
-
// Iterative stack-based walk — no recursion.
|
|
301
|
-
function resolveNestedPaths(val: any, ctx: AontuContext) {
|
|
302
|
-
const stack: any[] = [val]
|
|
303
|
-
while (stack.length > 0) {
|
|
304
|
-
const v = stack.pop()
|
|
305
|
-
if (v == null || !v.isVal) continue
|
|
306
|
-
|
|
307
|
-
if (v.isMap || v.isList) {
|
|
308
|
-
for (const k in v.peg) {
|
|
309
|
-
const child = v.peg[k]
|
|
310
|
-
if (child instanceof PathVal && !child.done) {
|
|
311
|
-
const found = child.find(ctx)
|
|
312
|
-
if (found != null && !found.isNil) {
|
|
313
|
-
v.peg[k] = found
|
|
314
|
-
child.dc = DONE
|
|
315
|
-
child._resolved = found
|
|
316
|
-
stack.push(found)
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
else if (child?.isVal) {
|
|
320
|
-
stack.push(child)
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
else if (v.isConjunct || v.isDisjunct) {
|
|
325
|
-
for (let i = 0; i < v.peg.length; i++) {
|
|
326
|
-
const child = v.peg[i]
|
|
327
|
-
if (child instanceof PathVal && !child.done) {
|
|
328
|
-
const found = child.find(ctx)
|
|
329
|
-
if (found != null && !found.isNil) {
|
|
330
|
-
v.peg[i] = found
|
|
331
|
-
child.dc = DONE
|
|
332
|
-
child._resolved = found
|
|
333
|
-
stack.push(found)
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
else if (child?.isVal) {
|
|
337
|
-
stack.push(child)
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
else if (v.isFeature) {
|
|
342
|
-
if (v.peg instanceof PathVal && !v.peg.done) {
|
|
343
|
-
const found = v.peg.find(ctx)
|
|
344
|
-
if (found != null && !found.isNil) {
|
|
345
|
-
v.peg.dc = DONE
|
|
346
|
-
v.peg._resolved = found
|
|
347
|
-
v.peg = found
|
|
348
|
-
stack.push(found)
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
else if (Array.isArray(v.peg)) {
|
|
352
|
-
for (let i = 0; i < v.peg.length; i++) {
|
|
353
|
-
if (v.peg[i]?.isVal) stack.push(v.peg[i])
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
else if (v.peg?.isVal) {
|
|
357
|
-
stack.push(v.peg)
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
// Replace target Val in the tree, using path to navigate and a stack
|
|
365
|
-
// to search through junctions, features, and nested structures.
|
|
366
|
-
// No recursion — uses a single loop with an explicit stack.
|
|
367
|
-
function replaceAtPath(root: Val, path: string[], target: Val, replacement: Val,
|
|
368
|
-
intoSpreads: boolean = true): boolean {
|
|
369
|
-
let node: any = root
|
|
370
|
-
|
|
371
|
-
// Descend through map/list using path segments.
|
|
372
|
-
// When a non-map/list is encountered (junction, feature),
|
|
373
|
-
// push its children onto the search stack and stop descending.
|
|
374
|
-
let pi = 0
|
|
375
|
-
for (; pi < path.length; pi++) {
|
|
376
|
-
if (node.isMap || node.isList) {
|
|
377
|
-
const child = node.peg[path[pi]]
|
|
378
|
-
if (child == null) break
|
|
379
|
-
|
|
380
|
-
// Last segment: check for direct replacement
|
|
381
|
-
if (pi === path.length - 1) {
|
|
382
|
-
if (child === target) {
|
|
383
|
-
node.peg[path[pi]] = replacement
|
|
384
|
-
return true
|
|
385
|
-
}
|
|
386
|
-
// Target not at this position — search within child
|
|
387
|
-
node = child
|
|
388
|
-
pi++
|
|
389
|
-
break
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
node = child
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
395
|
-
// Hit a non-navigable node — search it
|
|
396
|
-
break
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// If path fully consumed with no match, or hit a junction/feature,
|
|
401
|
-
// search the current node using a stack.
|
|
402
|
-
const stack: any[] = [node]
|
|
403
|
-
|
|
404
|
-
while (stack.length > 0) {
|
|
405
|
-
const val = stack.pop()
|
|
406
|
-
if (val == null || !val.isVal) continue
|
|
407
|
-
|
|
408
|
-
if (val.isMap || val.isList) {
|
|
409
|
-
for (const k in val.peg) {
|
|
410
|
-
if (val.peg[k] === target) {
|
|
411
|
-
val.peg[k] = replacement
|
|
412
|
-
return true
|
|
413
|
-
}
|
|
414
|
-
if (val.peg[k]?.isVal) stack.push(val.peg[k])
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
else if (val.isConjunct || val.isDisjunct) {
|
|
418
|
-
for (let i = 0; i < val.peg.length; i++) {
|
|
419
|
-
if (val.peg[i] === target) {
|
|
420
|
-
val.peg[i] = replacement
|
|
421
|
-
return true
|
|
422
|
-
}
|
|
423
|
-
stack.push(val.peg[i])
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
else if (val.isFeature) {
|
|
427
|
-
if (val.peg === target) {
|
|
428
|
-
val.peg = replacement
|
|
429
|
-
return true
|
|
430
|
-
}
|
|
431
|
-
if (intoSpreads || !val.isSpread) {
|
|
432
|
-
if (Array.isArray(val.peg)) {
|
|
433
|
-
for (let i = 0; i < val.peg.length; i++) {
|
|
434
|
-
if (val.peg[i] === target) {
|
|
435
|
-
val.peg[i] = replacement
|
|
436
|
-
return true
|
|
437
|
-
}
|
|
438
|
-
if (val.peg[i]?.isVal) stack.push(val.peg[i])
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
else if (val.peg?.isVal) {
|
|
442
|
-
stack.push(val.peg)
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
return false
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
// Resolve all KeyFuncVals (not inside spreads) to StringVals.
|
|
453
|
-
// Uses the KeyFuncVal's path to determine the key name.
|
|
454
|
-
function resolveKeys(root: Val, keys: KeyFuncVal[]) {
|
|
455
|
-
for (const kv of keys) {
|
|
456
|
-
const resolved = kv.resolve(null as any, kv.peg)
|
|
457
|
-
if (resolved instanceof StringVal) {
|
|
458
|
-
resolved.dc = DONE
|
|
459
|
-
resolved.path = kv.path
|
|
460
|
-
replaceAtPath(root, kv.path, kv, resolved, false)
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
|
|
466
182
|
class Unify {
|
|
467
183
|
root: Val
|
|
468
184
|
res: Val
|
|
@@ -513,28 +229,15 @@ class Unify {
|
|
|
513
229
|
uctx.err = this.err
|
|
514
230
|
uctx.explain = this.explain
|
|
515
231
|
|
|
516
|
-
// Path resolution phase: replace all PathVals with cloned targets.
|
|
517
|
-
// Pure structural replacement — no unification.
|
|
518
|
-
resolvePaths(res, uctx, this.lang.paths)
|
|
519
|
-
uctx = uctx.clone({ root: res })
|
|
520
|
-
|
|
521
|
-
// Key resolution phase: replace key() functions (not in spreads)
|
|
522
|
-
// with their resolved StringVal.
|
|
523
|
-
resolveKeys(res, this.lang.keys)
|
|
524
|
-
|
|
525
232
|
const explain = null == ctx?.explain ? undefined : ctx?.explain
|
|
526
233
|
const te = explain && explainOpen(uctx, explain, 'root', res)
|
|
527
234
|
|
|
528
235
|
// NOTE: if true === res.done already, then this loop never needs to run.
|
|
529
|
-
// RefVals defer on cc=0 and while ctx.sc > 0.
|
|
530
|
-
// SpreadVal.unify maintains ctx.sc via increment/decrement.
|
|
531
236
|
let maxcc = 9 // 99
|
|
532
237
|
for (; this.cc < maxcc && DONE !== res.dc; this.cc++) {
|
|
533
238
|
// console.log('CC', this.cc, res.canon)
|
|
534
239
|
uctx.cc = this.cc
|
|
535
|
-
uctx.seen =
|
|
536
|
-
uctx._refCloneCache = new Map()
|
|
537
|
-
uctx._uniteCache = new Map()
|
|
240
|
+
uctx.seen = {}
|
|
538
241
|
res = unite(te ? uctx.clone({ explain: ec(te, 'run') }) : uctx, res, top(), 'unify')
|
|
539
242
|
|
|
540
243
|
if (0 < uctx.err.length) {
|
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
|