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/lang.ts
CHANGED
|
@@ -25,10 +25,6 @@ import {
|
|
|
25
25
|
makeFileResolver
|
|
26
26
|
} from '@jsonic/multisource/resolver/file'
|
|
27
27
|
|
|
28
|
-
import {
|
|
29
|
-
preloadFiles
|
|
30
|
-
} from '@jsonic/multisource'
|
|
31
|
-
|
|
32
28
|
import {
|
|
33
29
|
makePkgResolver
|
|
34
30
|
} from '@jsonic/multisource/resolver/pkg'
|
|
@@ -79,8 +75,7 @@ import { NilVal } from './val/NilVal'
|
|
|
79
75
|
import { NullVal } from './val/NullVal'
|
|
80
76
|
import { NumberVal } from './val/NumberVal'
|
|
81
77
|
import { PrefVal } from './val/PrefVal'
|
|
82
|
-
import {
|
|
83
|
-
// RefVal replaced by PathVal — see RefVal.ts.old
|
|
78
|
+
import { RefVal } from './val/RefVal'
|
|
84
79
|
import { StringVal } from './val/StringVal'
|
|
85
80
|
import { VarVal } from './val/VarVal'
|
|
86
81
|
import { PlusOpVal } from './val/PlusOpVal'
|
|
@@ -96,7 +91,6 @@ import { PrefFuncVal } from './val/PrefFuncVal'
|
|
|
96
91
|
import { CloseFuncVal } from './val/CloseFuncVal'
|
|
97
92
|
import { OpenFuncVal } from './val/OpenFuncVal'
|
|
98
93
|
import { SuperFuncVal } from './val/SuperFuncVal'
|
|
99
|
-
import { SpreadVal } from './val/SpreadVal'
|
|
100
94
|
|
|
101
95
|
|
|
102
96
|
let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
@@ -116,17 +110,6 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
116
110
|
}
|
|
117
111
|
|
|
118
112
|
|
|
119
|
-
// Helper: create a SpreadVal from the raw SPREAD symbol data.
|
|
120
|
-
function makeSpreadVal(rawSpread: any): SpreadVal {
|
|
121
|
-
const constraint = Array.isArray(rawSpread.v)
|
|
122
|
-
? (rawSpread.v.length > 1
|
|
123
|
-
? new ConjunctVal({ peg: rawSpread.v })
|
|
124
|
-
: rawSpread.v[0])
|
|
125
|
-
: rawSpread.v
|
|
126
|
-
return new SpreadVal({ peg: constraint })
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
113
|
jsonic.options({
|
|
131
114
|
hint: {
|
|
132
115
|
unknown: `
|
|
@@ -240,25 +223,12 @@ help isolate the syntax error.`,
|
|
|
240
223
|
addsite(new DisjunctVal({ peg: terms }), r, ctx),
|
|
241
224
|
|
|
242
225
|
'dot-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
|
|
243
|
-
|
|
244
|
-
if (ctx.meta.paths) { ctx.meta.paths.push(pv) }
|
|
245
|
-
return pv
|
|
226
|
+
return addsite(new RefVal({ peg: terms, prefix: true }), r, ctx)
|
|
246
227
|
},
|
|
247
228
|
|
|
248
229
|
'dot-infix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
// Remove consumed intermediates
|
|
252
|
-
for (const t of terms) {
|
|
253
|
-
if (t instanceof PathVal) {
|
|
254
|
-
const idx = paths.indexOf(t)
|
|
255
|
-
if (idx >= 0) paths.splice(idx, 1)
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
const pv = addsite(new PathVal({ peg: terms }), r, ctx)
|
|
260
|
-
if (paths) { paths.push(pv) }
|
|
261
|
-
return pv
|
|
230
|
+
// // console.log('DOT-INFIX-OP', terms)
|
|
231
|
+
return addsite(new RefVal({ peg: terms }), r, ctx)
|
|
262
232
|
},
|
|
263
233
|
|
|
264
234
|
'star-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) =>
|
|
@@ -266,7 +236,7 @@ help isolate the syntax error.`,
|
|
|
266
236
|
|
|
267
237
|
'dollar-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
|
|
268
238
|
// $.a.b absolute path
|
|
269
|
-
if (terms[0] instanceof
|
|
239
|
+
if (terms[0] instanceof RefVal) {
|
|
270
240
|
terms[0].absolute = true
|
|
271
241
|
return terms[0]
|
|
272
242
|
}
|
|
@@ -301,9 +271,6 @@ help isolate the syntax error.`,
|
|
|
301
271
|
})
|
|
302
272
|
}
|
|
303
273
|
const out = addsite(val, r, ctx)
|
|
304
|
-
if (out.isKeyFunc && ctx.meta.keys) {
|
|
305
|
-
ctx.meta.keys.push(out)
|
|
306
|
-
}
|
|
307
274
|
return out
|
|
308
275
|
},
|
|
309
276
|
}
|
|
@@ -314,11 +281,11 @@ help isolate the syntax error.`,
|
|
|
314
281
|
op: {
|
|
315
282
|
// disjunct < conjunct: c & b | a -> (c & b) | a
|
|
316
283
|
'conjunct': {
|
|
317
|
-
infix: true, src: '&', left: 16_000_000, right: 17_000_000
|
|
284
|
+
infix: true, src: '&', left: 16_000_000, right: 17_000_000
|
|
318
285
|
},
|
|
319
286
|
|
|
320
287
|
'disjunct': {
|
|
321
|
-
infix: true, src: '|', left: 14_000_000, right: 15_000_000
|
|
288
|
+
infix: true, src: '|', left: 14_000_000, right: 15_000_000
|
|
322
289
|
},
|
|
323
290
|
|
|
324
291
|
'plus-infix': {
|
|
@@ -357,6 +324,7 @@ help isolate the syntax error.`,
|
|
|
357
324
|
paren: true,
|
|
358
325
|
preval: {
|
|
359
326
|
active: true,
|
|
327
|
+
// allow: ['floor'], //Object.keys(funcMap)
|
|
360
328
|
},
|
|
361
329
|
osrc: '(',
|
|
362
330
|
csrc: ')',
|
|
@@ -477,46 +445,26 @@ help isolate the syntax error.`,
|
|
|
477
445
|
{ s: [OPTKEY, QM], p: 'pair', b: 2, g: 'pair,list,val,imp,jsonic,aontu-optional' },
|
|
478
446
|
])
|
|
479
447
|
|
|
480
|
-
|
|
481
448
|
.bc((r: Rule, ctx: JsonicContext) => {
|
|
482
449
|
const optionalKeys = r.u.aontu_optional_keys ?? []
|
|
483
450
|
|
|
484
451
|
let mo = r.node
|
|
485
452
|
|
|
486
|
-
// Extract spread constraint before creating MapVal.
|
|
487
|
-
// If a spread exists, wrap: ConjunctVal([MapVal, SpreadVal])
|
|
488
|
-
const rawSpread = (mo as any)[SPREAD]
|
|
489
|
-
if (rawSpread) {
|
|
490
|
-
delete (mo as any)[SPREAD]
|
|
491
|
-
}
|
|
492
|
-
|
|
493
453
|
// Handle defered conjuncts, e.g. `{x:1 @"foo"}`
|
|
494
454
|
if (mo.___merge) {
|
|
495
455
|
let mop = { ...mo }
|
|
496
456
|
delete mop.___merge
|
|
497
457
|
|
|
458
|
+
// TODO: needs addpath?
|
|
498
459
|
let mopv = new MapVal({ peg: mop })
|
|
499
460
|
mopv.optionalKeys = optionalKeys
|
|
500
461
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
terms.push(makeSpreadVal(rawSpread))
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
r.node = addsite(new ConjunctVal({ peg: terms }), r, ctx)
|
|
462
|
+
r.node =
|
|
463
|
+
addsite(new ConjunctVal({ peg: [mopv, ...mo.___merge] }), r, ctx)
|
|
507
464
|
}
|
|
508
465
|
else {
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
if (rawSpread && '&' === rawSpread.o) {
|
|
513
|
-
const spreadVal = makeSpreadVal(rawSpread)
|
|
514
|
-
r.node = addsite(
|
|
515
|
-
new ConjunctVal({ peg: [mapVal, spreadVal] }), r, ctx)
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
r.node = addsite(mapVal, r, ctx)
|
|
519
|
-
}
|
|
466
|
+
r.node = addsite(new MapVal({ peg: mo }), r, ctx)
|
|
467
|
+
r.node.optionalKeys = optionalKeys
|
|
520
468
|
}
|
|
521
469
|
|
|
522
470
|
return undefined
|
|
@@ -538,39 +486,20 @@ help isolate the syntax error.`,
|
|
|
538
486
|
|
|
539
487
|
let ao = r.node
|
|
540
488
|
|
|
541
|
-
// Extract spread from list before creating ListVal.
|
|
542
|
-
const rawSpread = (ao as any)[SPREAD]
|
|
543
|
-
if (rawSpread) {
|
|
544
|
-
delete (ao as any)[SPREAD]
|
|
545
|
-
}
|
|
546
|
-
|
|
547
489
|
if (ao.___merge) {
|
|
548
490
|
let aop = [...ao]
|
|
549
491
|
delete (aop as any).___merge
|
|
550
492
|
|
|
493
|
+
// TODO: needs addpath?
|
|
551
494
|
let aopv = new ListVal({ peg: aop })
|
|
552
495
|
aopv.optionalKeys = optionalKeys
|
|
553
496
|
|
|
554
|
-
let terms = [aopv, ...ao.___merge]
|
|
555
|
-
if (rawSpread && '&' === rawSpread.o) {
|
|
556
|
-
terms.push(makeSpreadVal(rawSpread))
|
|
557
|
-
}
|
|
558
|
-
|
|
559
497
|
r.node =
|
|
560
|
-
addsite(new ConjunctVal({ peg:
|
|
498
|
+
addsite(new ConjunctVal({ peg: [aopv, ...ao.___merge] }), r, ctx)
|
|
561
499
|
}
|
|
562
500
|
else {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
if (rawSpread && '&' === rawSpread.o) {
|
|
567
|
-
const spreadVal = makeSpreadVal(rawSpread)
|
|
568
|
-
r.node = addsite(
|
|
569
|
-
new ConjunctVal({ peg: [listVal, spreadVal] }), r, ctx)
|
|
570
|
-
}
|
|
571
|
-
else {
|
|
572
|
-
r.node = addsite(listVal, r, ctx)
|
|
573
|
-
}
|
|
501
|
+
r.node = addsite(new ListVal({ peg: ao }), r, ctx)
|
|
502
|
+
r.node.optionalKeys = optionalKeys
|
|
574
503
|
}
|
|
575
504
|
|
|
576
505
|
return undefined
|
|
@@ -626,13 +555,11 @@ help isolate the syntax error.`,
|
|
|
626
555
|
}
|
|
627
556
|
])
|
|
628
557
|
|
|
629
|
-
//
|
|
630
|
-
// The path gets adjusted correctly when SpreadVal applies
|
|
631
|
-
// the constraint via spreadClone(keyctx).
|
|
558
|
+
// NOTE: manually adjust path - @jsonic/path ignores as not pair:true
|
|
632
559
|
.ao((r) => {
|
|
633
560
|
if (0 < r.d && r.u.spread) {
|
|
634
|
-
r.child.k.path = r.k.path
|
|
635
|
-
r.child.k.key =
|
|
561
|
+
r.child.k.path = [...r.k.path, '&']
|
|
562
|
+
r.child.k.key = '&'
|
|
636
563
|
}
|
|
637
564
|
})
|
|
638
565
|
|
|
@@ -720,18 +647,10 @@ function makeModelResolver(options: any) {
|
|
|
720
647
|
...(options.resolver?.mem || {})
|
|
721
648
|
})
|
|
722
649
|
|
|
723
|
-
|
|
650
|
+
// TODO: make this consistent with other resolvers
|
|
651
|
+
let fileResolver = makeFileResolver((spec: any) => {
|
|
724
652
|
return 'string' === typeof spec ? spec : spec?.peg
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
let preload: { [fullpath: string]: string } | undefined
|
|
728
|
-
if (options.preload) {
|
|
729
|
-
preload = preloadFiles(options.preload)
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
let fileResolver = makeFileResolver(
|
|
733
|
-
preload ? { pathfinder, preload } : pathfinder
|
|
734
|
-
)
|
|
653
|
+
})
|
|
735
654
|
|
|
736
655
|
let pkgResolver = makePkgResolver({
|
|
737
656
|
require: useRequire,
|
|
@@ -780,8 +699,6 @@ class Lang {
|
|
|
780
699
|
jsonic: Jsonic
|
|
781
700
|
opts: AontuOptions
|
|
782
701
|
idcount: number | undefined
|
|
783
|
-
paths: PathVal[] = []
|
|
784
|
-
keys: KeyFuncVal[] = []
|
|
785
702
|
|
|
786
703
|
|
|
787
704
|
constructor(options?: Partial<AontuOptions>) {
|
|
@@ -815,14 +732,9 @@ class Lang {
|
|
|
815
732
|
// const start = performance.now()
|
|
816
733
|
|
|
817
734
|
// JSONIC-UPDATE - check meta
|
|
818
|
-
this.paths = []
|
|
819
|
-
this.keys = []
|
|
820
|
-
|
|
821
735
|
let jm: any = {
|
|
822
736
|
fs: opts?.fs,
|
|
823
737
|
fileName: opts?.path ?? this.opts.path,
|
|
824
|
-
paths: this.paths,
|
|
825
|
-
keys: this.keys,
|
|
826
738
|
multisource: {
|
|
827
739
|
path: opts?.path ?? this.opts.path,
|
|
828
740
|
deps: (opts && opts.deps) || undefined
|
|
@@ -867,7 +779,5 @@ class Lang {
|
|
|
867
779
|
|
|
868
780
|
export {
|
|
869
781
|
Lang,
|
|
870
|
-
PathVal,
|
|
871
|
-
KeyFuncVal,
|
|
872
782
|
Site,
|
|
873
783
|
}
|
package/src/type.ts
CHANGED
|
@@ -30,11 +30,6 @@ type AontuOptions = {
|
|
|
30
30
|
collect?: boolean // Collect errors into an errs property, rather than throw them.
|
|
31
31
|
err?: any[]
|
|
32
32
|
explain?: any[]
|
|
33
|
-
preload?: { // Preload files into memory before parsing.
|
|
34
|
-
folders: string[] // Folders to scan.
|
|
35
|
-
ext?: string[] // Extensions (default: ['.jsonic', '.json']).
|
|
36
|
-
recursive?: boolean // Recurse into subfolders (default: false).
|
|
37
|
-
}
|
|
38
33
|
}
|
|
39
34
|
|
|
40
35
|
|
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) {
|