aontu 0.41.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ctx.d.ts +5 -2
- package/dist/ctx.js +2 -1
- package/dist/ctx.js.map +1 -1
- package/dist/lang.d.ts +5 -1
- package/dist/lang.js +100 -24
- package/dist/lang.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +5 -0
- package/dist/type.js.map +1 -1
- package/dist/unify.js +292 -13
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +6 -2
- package/dist/utility.js.map +1 -1
- package/dist/val/BagVal.d.ts +0 -3
- package/dist/val/BagVal.js +6 -6
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/ConjunctVal.d.ts +1 -1
- package/dist/val/ConjunctVal.js +137 -14
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/CopyFuncVal.js +3 -2
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +4 -0
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +16 -3
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/JunctionVal.d.ts +1 -0
- package/dist/val/JunctionVal.js +6 -1
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.js +0 -2
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.d.ts +1 -0
- package/dist/val/ListVal.js +33 -66
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +3 -2
- package/dist/val/MapVal.js +67 -95
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MoveFuncVal.d.ts +2 -1
- package/dist/val/MoveFuncVal.js +78 -13
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +25 -4
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/{RefVal.d.ts → PathVal.d.ts} +4 -3
- package/dist/val/{RefVal.js → PathVal.js} +75 -77
- package/dist/val/PathVal.js.map +1 -0
- package/dist/val/PlusOpVal.d.ts +1 -1
- package/dist/val/PrefVal.js +18 -5
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/SpreadVal.d.ts +20 -0
- package/dist/val/SpreadVal.js +194 -0
- package/dist/val/SpreadVal.js.map +1 -0
- package/dist/val/Val.d.ts +2 -1
- package/dist/val/Val.js +7 -8
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +2 -2
- package/dist/val/VarVal.js.map +1 -1
- package/package.json +5 -6
- package/src/ctx.ts +16 -3
- package/src/lang.ts +113 -23
- package/src/type.ts +5 -0
- package/src/unify.ts +310 -13
- package/src/utility.ts +5 -2
- package/src/val/BagVal.ts +6 -7
- package/src/val/ConjunctVal.ts +131 -13
- package/src/val/CopyFuncVal.ts +3 -2
- package/src/val/DisjunctVal.ts +6 -0
- package/src/val/ExpectVal.ts +18 -4
- package/src/val/JunctionVal.ts +5 -1
- package/src/val/KeyFuncVal.ts +0 -3
- package/src/val/ListVal.ts +38 -88
- package/src/val/MapVal.ts +75 -124
- package/src/val/MoveFuncVal.ts +79 -14
- package/src/val/PathFuncVal.ts +29 -4
- package/src/val/PathVal.ts +435 -0
- package/src/val/PrefVal.ts +19 -6
- package/src/val/{RefVal.ts → RefVal.ts.old} +30 -19
- package/src/val/SpreadVal.ts +275 -0
- package/src/val/Val.ts +9 -9
- package/src/val/VarVal.ts +2 -2
- package/dist/val/RefVal.js.map +0 -1
package/src/val/MoveFuncVal.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { makeNilErr } from '../err'
|
|
14
14
|
|
|
15
15
|
import { NilVal } from '../val/NilVal'
|
|
16
|
+
import { PathVal } from '../val/PathVal'
|
|
16
17
|
|
|
17
18
|
import {
|
|
18
19
|
walk
|
|
@@ -26,6 +27,60 @@ import { PrefFuncVal } from './PrefFuncVal'
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
// Navigate the tree using a PathVal's resolved path and hide the source.
|
|
31
|
+
function hideAtPath(root: Val, pv: PathVal) {
|
|
32
|
+
// Compute the refpath the same way PathVal.find does
|
|
33
|
+
const parts: string[] = []
|
|
34
|
+
for (const part of pv.peg) {
|
|
35
|
+
if ('string' === typeof part) parts.push(part)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let refpath: string[]
|
|
39
|
+
if (pv.absolute) {
|
|
40
|
+
refpath = parts
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
refpath = pv.path.slice(0, -1).concat(parts)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Walk to the source, handling conjuncts/disjuncts
|
|
47
|
+
let node: any = root
|
|
48
|
+
for (let i = 0; i < refpath.length; i++) {
|
|
49
|
+
const part = refpath[i]
|
|
50
|
+
if (node?.isMap || node?.isList) {
|
|
51
|
+
node = node.peg[part]
|
|
52
|
+
}
|
|
53
|
+
else if (node?.isConjunct || node?.isDisjunct) {
|
|
54
|
+
// Search junction terms for the key
|
|
55
|
+
let found = null
|
|
56
|
+
const stack = [...node.peg]
|
|
57
|
+
while (stack.length > 0) {
|
|
58
|
+
const term = stack.pop()
|
|
59
|
+
if (term?.isConjunct || term?.isDisjunct) {
|
|
60
|
+
stack.push(...term.peg)
|
|
61
|
+
}
|
|
62
|
+
else if ((term?.isMap || term?.isList) && term.peg[part] != null) {
|
|
63
|
+
found = term.peg[part]
|
|
64
|
+
break
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
node = found
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
if (node == null) return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Mark the source value hidden
|
|
76
|
+
node.mark.hide = true
|
|
77
|
+
walk(node, (_key: string | number | undefined, val: Val) => {
|
|
78
|
+
val.mark.hide = true
|
|
79
|
+
return val
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
29
84
|
class MoveFuncVal extends FuncBaseVal {
|
|
30
85
|
isMoveFunc = true
|
|
31
86
|
|
|
@@ -50,30 +105,40 @@ class MoveFuncVal extends FuncBaseVal {
|
|
|
50
105
|
}
|
|
51
106
|
|
|
52
107
|
resolve(ctx: AontuContext, args: Val[]) {
|
|
53
|
-
let
|
|
108
|
+
let arg = args[0] ?? makeNilErr(ctx, 'arg', this)
|
|
109
|
+
if (arg.isNil) return arg
|
|
54
110
|
|
|
55
|
-
|
|
111
|
+
let src: Val
|
|
56
112
|
|
|
57
|
-
if (
|
|
58
|
-
|
|
113
|
+
if (arg instanceof PathVal) {
|
|
114
|
+
// Get clone for the destination
|
|
115
|
+
src = arg.find(ctx) as Val
|
|
116
|
+
if (src == null || src.isNil) return makeNilErr(ctx, 'arg', this)
|
|
59
117
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
118
|
+
// Hide the source in the tree by navigating to it
|
|
119
|
+
hideAtPath(ctx.root as Val, arg)
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
src = arg.clone(ctx)
|
|
63
123
|
|
|
64
|
-
|
|
124
|
+
// Hide the original
|
|
125
|
+
arg.mark.hide = true
|
|
126
|
+
walk(arg, (_key: string | number | undefined, val: Val) => {
|
|
65
127
|
val.mark.hide = true
|
|
66
128
|
return val
|
|
67
129
|
})
|
|
68
|
-
|
|
69
|
-
// out = new CopyFuncVal({ peg: [src] }, ctx)
|
|
70
|
-
out = new PrefFuncVal({ peg: [src] }, ctx)
|
|
71
|
-
// out = src
|
|
72
130
|
}
|
|
73
131
|
|
|
74
|
-
//
|
|
132
|
+
// Clear marks on the clone (like copy)
|
|
133
|
+
src.mark.type = false
|
|
134
|
+
src.mark.hide = false
|
|
135
|
+
walk(src, (_key: string | number | undefined, val: Val) => {
|
|
136
|
+
val.mark.type = false
|
|
137
|
+
val.mark.hide = false
|
|
138
|
+
return val
|
|
139
|
+
})
|
|
75
140
|
|
|
76
|
-
return
|
|
141
|
+
return new PrefFuncVal({ peg: [src] }, ctx)
|
|
77
142
|
}
|
|
78
143
|
}
|
|
79
144
|
|
package/src/val/PathFuncVal.ts
CHANGED
|
@@ -6,6 +6,10 @@ import type {
|
|
|
6
6
|
ValSpec,
|
|
7
7
|
} from '../type'
|
|
8
8
|
|
|
9
|
+
import {
|
|
10
|
+
DONE,
|
|
11
|
+
} from '../type'
|
|
12
|
+
|
|
9
13
|
import {
|
|
10
14
|
AontuContext,
|
|
11
15
|
} from '../ctx'
|
|
@@ -13,7 +17,7 @@ import {
|
|
|
13
17
|
import { makeNilErr } from '../err'
|
|
14
18
|
|
|
15
19
|
import { NilVal } from '../val/NilVal'
|
|
16
|
-
import {
|
|
20
|
+
import { PathVal } from '../val/PathVal'
|
|
17
21
|
import { FuncBaseVal } from './FuncBaseVal'
|
|
18
22
|
|
|
19
23
|
|
|
@@ -45,12 +49,26 @@ class PathFuncVal extends FuncBaseVal {
|
|
|
45
49
|
let arg = args[0]
|
|
46
50
|
|
|
47
51
|
if (0 === this.prepared) {
|
|
48
|
-
if (arg
|
|
49
|
-
|
|
52
|
+
if (arg instanceof PathVal) {
|
|
53
|
+
// PathVal from dotted arg — resolve via find().
|
|
54
|
+
const found = arg.find(ctx)
|
|
55
|
+
if (found != null && !found.isNil) {
|
|
56
|
+
arg = found
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
arg.dc = DONE
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (arg.isScalar && arg.peg != null && arg.peg !== ''
|
|
63
|
+
&& ('string' === typeof arg.peg || arg.isNumber)) {
|
|
64
|
+
// String or number arg like path("foo") or path(0.2) — create a path value
|
|
65
|
+
arg = this.place(new PathVal({ peg: [arg], absolute: false })) as PathVal
|
|
66
|
+
arg.dc = DONE
|
|
50
67
|
}
|
|
51
|
-
else if (
|
|
68
|
+
else if (arg.isNil || (arg.isScalar && (arg.peg === '' || arg.peg == null))) {
|
|
52
69
|
arg = makeNilErr(ctx, 'invalid-arg', this)
|
|
53
70
|
}
|
|
71
|
+
// else: already resolved by preprocessing — pass through
|
|
54
72
|
}
|
|
55
73
|
|
|
56
74
|
args[0] = arg
|
|
@@ -64,6 +82,13 @@ class PathFuncVal extends FuncBaseVal {
|
|
|
64
82
|
resolve(ctx: AontuContext, args: Val[]) {
|
|
65
83
|
let out = args[0] ?? makeNilErr(ctx, 'arg', this)
|
|
66
84
|
|
|
85
|
+
if (out instanceof PathVal) {
|
|
86
|
+
const found = out.find(ctx)
|
|
87
|
+
if (found != null && !found.isNil) {
|
|
88
|
+
out = found
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
67
92
|
return out
|
|
68
93
|
}
|
|
69
94
|
|
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
Val,
|
|
6
|
+
ValSpec,
|
|
7
|
+
} from '../type'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
DONE,
|
|
11
|
+
} from '../type'
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
walk,
|
|
15
|
+
} from '../utility'
|
|
16
|
+
|
|
17
|
+
import { AontuContext } from '../ctx'
|
|
18
|
+
import { unite } from '../unify'
|
|
19
|
+
|
|
20
|
+
import { makeNilErr } from '../err'
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
top
|
|
24
|
+
} from './top'
|
|
25
|
+
|
|
26
|
+
import { StringVal } from './StringVal'
|
|
27
|
+
import { IntegerVal } from './IntegerVal'
|
|
28
|
+
import { NumberVal } from './NumberVal'
|
|
29
|
+
import { VarVal } from './VarVal'
|
|
30
|
+
import { ConjunctVal } from './ConjunctVal'
|
|
31
|
+
import { DisjunctVal } from './DisjunctVal'
|
|
32
|
+
import { FeatureVal } from './FeatureVal'
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PathVal extends FeatureVal {
|
|
36
|
+
isPath = true
|
|
37
|
+
isGenable = true
|
|
38
|
+
cjo = 32500
|
|
39
|
+
|
|
40
|
+
absolute: boolean = false
|
|
41
|
+
prefix: boolean = false
|
|
42
|
+
_resolved: Val | undefined = undefined
|
|
43
|
+
|
|
44
|
+
constructor(
|
|
45
|
+
spec: {
|
|
46
|
+
peg: any[],
|
|
47
|
+
absolute?: boolean,
|
|
48
|
+
prefix?: boolean
|
|
49
|
+
},
|
|
50
|
+
ctx?: AontuContext
|
|
51
|
+
) {
|
|
52
|
+
super(spec, ctx)
|
|
53
|
+
this.peg = []
|
|
54
|
+
|
|
55
|
+
this.absolute = true === this.absolute ? true : // absolute sticks
|
|
56
|
+
true === spec.absolute ? true : false
|
|
57
|
+
|
|
58
|
+
this.prefix = true === spec.prefix
|
|
59
|
+
|
|
60
|
+
for (let pI = 0; pI < spec.peg.length; pI++) {
|
|
61
|
+
this.append(spec.peg[pI])
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
append(part: any) {
|
|
67
|
+
let partval
|
|
68
|
+
|
|
69
|
+
if ('string' === typeof part) {
|
|
70
|
+
partval = part
|
|
71
|
+
this.peg.push(partval)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
else if (part instanceof StringVal) {
|
|
75
|
+
partval = part.peg
|
|
76
|
+
this.peg.push(partval)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
else if (part instanceof IntegerVal) {
|
|
80
|
+
partval = part.src
|
|
81
|
+
this.peg.push(partval)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
else if (part instanceof NumberVal) {
|
|
85
|
+
let partvals: string[] = part.src.split('.')
|
|
86
|
+
this.peg.push(...partvals)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
else if (part instanceof VarVal) {
|
|
90
|
+
partval = part
|
|
91
|
+
this.peg.push(partval)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
else if (part instanceof PathVal) {
|
|
95
|
+
if (part.absolute) {
|
|
96
|
+
this.absolute = true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (this.prefix) {
|
|
100
|
+
if (part.prefix) {
|
|
101
|
+
this.peg.push('.')
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
if (part.prefix) {
|
|
106
|
+
if (0 === this.peg.length) {
|
|
107
|
+
this.prefix = true
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
else if (0 < this.peg.length) {
|
|
111
|
+
this.peg.push('.')
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
this.peg.push(...part.peg)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
unify(peer: Val, ctx: AontuContext): Val {
|
|
122
|
+
peer = peer ?? top()
|
|
123
|
+
|
|
124
|
+
// Already resolved (e.g. path value from path() function) — skip find
|
|
125
|
+
if (this.done) return this
|
|
126
|
+
|
|
127
|
+
let out: Val = this
|
|
128
|
+
const found = this.find(ctx)
|
|
129
|
+
|
|
130
|
+
if (found != null && !found.isNil) {
|
|
131
|
+
out = unite(ctx, found, peer, 'path')
|
|
132
|
+
}
|
|
133
|
+
else if (found?.isNil) {
|
|
134
|
+
out = found
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
// Not yet resolvable — increment dc to signal not done
|
|
138
|
+
this.dc = DONE === this.dc ? DONE : this.dc + 1
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return out
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
find(ctx: AontuContext) {
|
|
146
|
+
let out: Val | undefined = undefined
|
|
147
|
+
|
|
148
|
+
// Check if self.path starts with peg (cycle detection).
|
|
149
|
+
// Element-by-element comparison avoids string join+startsWith allocations.
|
|
150
|
+
let isprefixpath = this.peg.length <= this.path.length
|
|
151
|
+
if (isprefixpath) {
|
|
152
|
+
for (let i = 0; i < this.peg.length; i++) {
|
|
153
|
+
if (this.peg[i] !== this.path[i]) {
|
|
154
|
+
isprefixpath = false
|
|
155
|
+
break
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Degenerate case: peg is all empty strings (e.g. path("")) and path is empty.
|
|
160
|
+
if (!isprefixpath && this.peg.length > 0 && this.path.length === 0) {
|
|
161
|
+
let allEmpty = true
|
|
162
|
+
for (let i = 0; i < this.peg.length; i++) {
|
|
163
|
+
if ('' !== this.peg[i]) { allEmpty = false; break }
|
|
164
|
+
}
|
|
165
|
+
isprefixpath = allEmpty
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let refpath: string[] = []
|
|
169
|
+
let pI = 0
|
|
170
|
+
// let descent = ''
|
|
171
|
+
|
|
172
|
+
if (isprefixpath) {
|
|
173
|
+
// console.log('SELFPATH', selfpath, 'PEGPATH', pegpath)
|
|
174
|
+
out = makeNilErr(ctx, 'path_cycle', this)
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
|
|
178
|
+
let parts: string[] = []
|
|
179
|
+
|
|
180
|
+
let modes: string[] = []
|
|
181
|
+
|
|
182
|
+
for (let pI = 0; pI < this.peg.length; pI++) {
|
|
183
|
+
let part = this.peg[pI]
|
|
184
|
+
if (part instanceof VarVal) {
|
|
185
|
+
let strval = (part as VarVal).peg
|
|
186
|
+
let name = strval ? '' + strval.peg : ''
|
|
187
|
+
|
|
188
|
+
if ('KEY' === name) {
|
|
189
|
+
if (pI === this.peg.length - 1) {
|
|
190
|
+
modes.push(name)
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// TODO: return a Nil explaining error
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if ('SELF' === name) {
|
|
199
|
+
if (pI === 0) {
|
|
200
|
+
modes.push(name)
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
// TODO: return a Nil explaining error
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else if ('PARENT' === name) {
|
|
208
|
+
if (pI === 0) {
|
|
209
|
+
modes.push(name)
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
// TODO: return a Nil explaining error
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
else if (0 === modes.length) {
|
|
217
|
+
part = (part as VarVal).unify(top(), ctx)
|
|
218
|
+
if (part.isNil) {
|
|
219
|
+
// TODO: var not found, so can't find path
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
part = '' + part.peg
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
parts.push(part)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (this.absolute) {
|
|
233
|
+
refpath = parts
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// TODO: deprecate $KEY, etc
|
|
237
|
+
refpath = this.path.slice(
|
|
238
|
+
0,
|
|
239
|
+
(
|
|
240
|
+
modes.includes('SELF') ? 0 :
|
|
241
|
+
modes.includes('PARENT') ? -1 :
|
|
242
|
+
-1 // siblings
|
|
243
|
+
)
|
|
244
|
+
).concat(parts)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let sep = '.'
|
|
248
|
+
refpath = refpath
|
|
249
|
+
.reduce(((a: string[], p: string) =>
|
|
250
|
+
(p === sep ? a.length = a.length - 1 : a.push(p), a)), [])
|
|
251
|
+
|
|
252
|
+
if (modes.includes('KEY')) {
|
|
253
|
+
let key = this.path[this.path.length - 2]
|
|
254
|
+
let sv = new StringVal({ peg: null == key ? '' : key }, ctx)
|
|
255
|
+
|
|
256
|
+
// TODO: other props?
|
|
257
|
+
sv.dc = DONE
|
|
258
|
+
sv.path = this.path
|
|
259
|
+
|
|
260
|
+
return sv
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let node: Val | null = ctx.root as Val
|
|
264
|
+
|
|
265
|
+
let nopath = false
|
|
266
|
+
|
|
267
|
+
if (null != node) {
|
|
268
|
+
for (; pI < refpath.length; pI++) {
|
|
269
|
+
let part = refpath[pI]
|
|
270
|
+
// console.log('PART', pI, part, node)
|
|
271
|
+
|
|
272
|
+
// descent += (' | ' + pI + '=' + node.canon) // Util.inspect(node))
|
|
273
|
+
|
|
274
|
+
if (node.isMap) {
|
|
275
|
+
node = node.peg[part]
|
|
276
|
+
}
|
|
277
|
+
else if (node.isList) {
|
|
278
|
+
node = node.peg[part]
|
|
279
|
+
}
|
|
280
|
+
else if (node.isConjunct || node.isDisjunct) {
|
|
281
|
+
// Collect matching children from all junction terms,
|
|
282
|
+
// flattening nested conjuncts and disjuncts.
|
|
283
|
+
// Spreads match any key — their peg is always included.
|
|
284
|
+
const matches: Val[] = []
|
|
285
|
+
const stack = [...node.peg]
|
|
286
|
+
while (stack.length > 0) {
|
|
287
|
+
const term = stack.pop()!
|
|
288
|
+
if (term.isConjunct || term.isDisjunct) {
|
|
289
|
+
stack.push(...term.peg)
|
|
290
|
+
}
|
|
291
|
+
else if (term.isSpread) {
|
|
292
|
+
matches.push(term.peg)
|
|
293
|
+
}
|
|
294
|
+
else if ((term.isMap || term.isList) && term.peg[part] != null) {
|
|
295
|
+
matches.push(term.peg[part])
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (matches.length === 1) {
|
|
299
|
+
node = matches[0]
|
|
300
|
+
}
|
|
301
|
+
else if (matches.length > 1) {
|
|
302
|
+
node = node.isConjunct
|
|
303
|
+
? new ConjunctVal({ peg: matches })
|
|
304
|
+
: new DisjunctVal({ peg: matches })
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
node = null
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else if (node.done) {
|
|
311
|
+
nopath = true
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (null == node) {
|
|
319
|
+
nopath = true
|
|
320
|
+
break
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// console.log('REFPATH', ctx.cc, pI, refpath, nopath, ctx.root, node)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
if (nopath) {
|
|
330
|
+
out = makeNilErr(ctx, 'no_path', this)
|
|
331
|
+
}
|
|
332
|
+
else if (pI === refpath.length && node != null) {
|
|
333
|
+
out = node
|
|
334
|
+
|
|
335
|
+
// Types and hidden values are cloned and made concrete
|
|
336
|
+
if (null != out) { // && (out.mark.type || out.mark.hide)) {
|
|
337
|
+
|
|
338
|
+
// console.log('FOUND-A', out)
|
|
339
|
+
|
|
340
|
+
if (this.mark.type || this.mark.hide) {
|
|
341
|
+
out.mark.type = this.mark.type
|
|
342
|
+
out.mark.hide = this.mark.hide
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (this.mark._hide_found) {
|
|
346
|
+
out.mark.hide = true
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Cache clone+walk results per (ref, target) per iteration.
|
|
350
|
+
const cacheKey = this.id + '|' + out.id
|
|
351
|
+
const cache = ctx._refCloneCache
|
|
352
|
+
const cached = cache?.get(cacheKey)
|
|
353
|
+
if (cached !== undefined) {
|
|
354
|
+
out = cached
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
out = out.clone(ctx)
|
|
358
|
+
out.mark.type = false
|
|
359
|
+
out.mark.hide = false
|
|
360
|
+
|
|
361
|
+
walk(out, (_key: string | number | undefined, val: Val) => {
|
|
362
|
+
val.mark.type = false
|
|
363
|
+
val.mark.hide = false
|
|
364
|
+
return val
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
cache?.set(cacheKey, out)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// console.log('REF-FIND', ctx.cc, this.id, selfpath, 'PEG=', pegpath, 'RP', pI, refpath.join('.'), descent, 'O=', out?.id, out?.canon, out?.done)
|
|
374
|
+
|
|
375
|
+
return out
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
same(peer: Val): boolean {
|
|
381
|
+
return null == peer ? false : this.peg === peer.peg
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
clone(ctx: AontuContext, spec?: ValSpec): Val {
|
|
386
|
+
let out = (super.clone(ctx, {
|
|
387
|
+
peg: this.peg,
|
|
388
|
+
absolute: this.absolute,
|
|
389
|
+
...(spec || {})
|
|
390
|
+
}) as PathVal)
|
|
391
|
+
return out
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
get canon() {
|
|
396
|
+
let str =
|
|
397
|
+
(this.absolute ? '$' : '') +
|
|
398
|
+
(0 < this.peg.length ? '.' : '') +
|
|
399
|
+
this.peg.map((p: any) => '.' === p ? '' :
|
|
400
|
+
(p.isVal ? p.canon : '' + p))
|
|
401
|
+
.join('.')
|
|
402
|
+
return str
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
gen(ctx: AontuContext) {
|
|
407
|
+
let nil = makeNilErr(
|
|
408
|
+
ctx,
|
|
409
|
+
'ref',
|
|
410
|
+
this,
|
|
411
|
+
undefined,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
nil.path = this.path
|
|
415
|
+
nil.site.url = this.site.url
|
|
416
|
+
nil.site.row = this.site.row
|
|
417
|
+
nil.site.col = this.site.col
|
|
418
|
+
|
|
419
|
+
return undefined
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
inspection() {
|
|
424
|
+
return [
|
|
425
|
+
this.absolute ? 'absolute' : '',
|
|
426
|
+
this.prefix ? 'prefix' : '',
|
|
427
|
+
].filter(p => '' != p).join(',')
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
export {
|
|
434
|
+
PathVal,
|
|
435
|
+
}
|
package/src/val/PrefVal.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
|
|
23
23
|
import { top } from './top'
|
|
24
24
|
|
|
25
|
+
import { ConjunctVal } from './ConjunctVal'
|
|
25
26
|
import { FeatureVal } from './FeatureVal'
|
|
26
27
|
|
|
27
28
|
|
|
@@ -64,10 +65,13 @@ class PrefVal extends FeatureVal {
|
|
|
64
65
|
if (!this.peg.done) {
|
|
65
66
|
const resolved = unite(te ? ctx.clone({ explain: ec(te, 'RES') }) : ctx,
|
|
66
67
|
this.peg, top(), 'pref/resolve')
|
|
67
|
-
// console.log('PREF-RESOLVED', this.peg.canon, '->', resolved)
|
|
68
68
|
this.peg = resolved
|
|
69
69
|
this.superpeg = this.peg.superior()
|
|
70
70
|
}
|
|
71
|
+
else if (this.superpeg.isTop && !this.peg.isTop) {
|
|
72
|
+
// Stale superpeg from pre-resolved PathVal — recalculate
|
|
73
|
+
this.superpeg = this.peg.superior()
|
|
74
|
+
}
|
|
71
75
|
|
|
72
76
|
if (peer instanceof PrefVal) {
|
|
73
77
|
why += 'pref-'
|
|
@@ -106,11 +110,20 @@ class PrefVal extends FeatureVal {
|
|
|
106
110
|
else if (!peer.isTop) {
|
|
107
111
|
why += 'super-'
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
out = this
|
|
113
|
-
|
|
113
|
+
// If peg is unresolved (e.g. deferred ref), don't decide yet —
|
|
114
|
+
// preserve both sides so a later pass can resolve.
|
|
115
|
+
if (!this.peg.done) {
|
|
116
|
+
out = new ConjunctVal({ peg: [this, peer] }, ctx)
|
|
117
|
+
done = false
|
|
118
|
+
why += 'defer'
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
out = unite(te ? ctx.clone({ explain: ec(te, 'SUPER') }) : ctx,
|
|
122
|
+
this.superpeg, peer, 'pref-super/' + this.id)
|
|
123
|
+
if (out.same(this.superpeg)) {
|
|
124
|
+
out = this.peg
|
|
125
|
+
why += 'same'
|
|
126
|
+
}
|
|
114
127
|
}
|
|
115
128
|
|
|
116
129
|
// }
|