aontu 0.43.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/val/PathVal.ts
DELETED
|
@@ -1,435 +0,0 @@
|
|
|
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/SpreadVal.ts
DELETED
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
-
|
|
3
|
-
// SpreadVal represents a spread constraint (&:{...}).
|
|
4
|
-
// It owns all spread application logic — MapVal and ListVal know
|
|
5
|
-
// nothing about spreads. A parsed `{a:1, &:{x:2}}` becomes
|
|
6
|
-
// `ConjunctVal([MapVal({a:1}), SpreadVal({x:2})])`.
|
|
7
|
-
//
|
|
8
|
-
// SpreadVal.unify applies the constraint to a peer MapVal/ListVal
|
|
9
|
-
// by unifying each of the peer's children with a per-key clone of
|
|
10
|
-
// the spread constraint.
|
|
11
|
-
|
|
12
|
-
import type {
|
|
13
|
-
Val,
|
|
14
|
-
ValSpec,
|
|
15
|
-
} from '../type'
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
DONE,
|
|
19
|
-
} from '../type'
|
|
20
|
-
|
|
21
|
-
import { AontuContext } from '../ctx'
|
|
22
|
-
import { unite } from '../unify'
|
|
23
|
-
|
|
24
|
-
import {
|
|
25
|
-
propagateMarks,
|
|
26
|
-
walk,
|
|
27
|
-
explainOpen,
|
|
28
|
-
ec,
|
|
29
|
-
explainClose,
|
|
30
|
-
} from '../utility'
|
|
31
|
-
|
|
32
|
-
import { makeNilErr } from '../err'
|
|
33
|
-
|
|
34
|
-
import {
|
|
35
|
-
top
|
|
36
|
-
} from './top'
|
|
37
|
-
|
|
38
|
-
import { FeatureVal } from './FeatureVal'
|
|
39
|
-
import { ConjunctVal } from './ConjunctVal'
|
|
40
|
-
import { NilVal } from './NilVal'
|
|
41
|
-
import { MapVal } from './MapVal'
|
|
42
|
-
import { ListVal } from './ListVal'
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class SpreadVal extends FeatureVal {
|
|
46
|
-
isSpread = true
|
|
47
|
-
isGenable = true
|
|
48
|
-
cjo = 110000 // Sorts after MapVal/ListVal in ConjunctVal norm
|
|
49
|
-
|
|
50
|
-
constructor(spec: ValSpec, ctx?: AontuContext) {
|
|
51
|
-
super(spec, ctx)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
unify(peer: Val, ctx: AontuContext): Val {
|
|
56
|
-
peer = peer ?? top()
|
|
57
|
-
|
|
58
|
-
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Spread', this, peer)
|
|
59
|
-
|
|
60
|
-
let out: Val
|
|
61
|
-
|
|
62
|
-
if (peer.isTop) {
|
|
63
|
-
// Self-unify: spread is a constraint, not a value that converges.
|
|
64
|
-
// Mark done to prevent fixpoint cycling.
|
|
65
|
-
out = this
|
|
66
|
-
out.dc = DONE
|
|
67
|
-
}
|
|
68
|
-
else if ((peer as any).isSpread) {
|
|
69
|
-
// SpreadVal + SpreadVal: unify the two constraints
|
|
70
|
-
const merged = unite(
|
|
71
|
-
te ? ctx.clone({ explain: ec(te, 'SPR') }) : ctx,
|
|
72
|
-
this.peg, peer.peg, 'spread-merge')
|
|
73
|
-
out = new SpreadVal({ peg: merged }, ctx)
|
|
74
|
-
out.dc = merged.done ? DONE : this.dc + 1
|
|
75
|
-
}
|
|
76
|
-
else if (peer.isMap) {
|
|
77
|
-
// SpreadVal + MapVal: apply spread to each map key
|
|
78
|
-
out = this.applyToMap(peer as any, ctx, te)
|
|
79
|
-
}
|
|
80
|
-
else if (peer.isList) {
|
|
81
|
-
// SpreadVal + ListVal: apply spread to each list element
|
|
82
|
-
out = this.applyToList(peer as any, ctx, te)
|
|
83
|
-
}
|
|
84
|
-
else if (peer.isConjunct) {
|
|
85
|
-
// SpreadVal + ConjunctVal: unify with the conjunct.
|
|
86
|
-
// The conjunct fold will place us last (high cjo).
|
|
87
|
-
out = peer.unify(this, te ? ctx.clone({ explain: ec(te, 'SCJ') }) : ctx)
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
// SpreadVal + other: defer by wrapping in ConjunctVal
|
|
91
|
-
out = new ConjunctVal({ peg: [peer, this] }, ctx)
|
|
92
|
-
out.dc = this.dc + 1
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Track unresolved spread count.
|
|
96
|
-
// cc=0: increment for spreads that don't vanish.
|
|
97
|
-
// cc>0: decrement when a spread vanishes.
|
|
98
|
-
if (0 === ctx.cc) {
|
|
99
|
-
if (out.isSpread && !out.done) {
|
|
100
|
-
ctx.sc++
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
else if (ctx.sc > 0) {
|
|
104
|
-
if (!(out.isSpread && !out.done)) {
|
|
105
|
-
ctx.sc--
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
ctx.explain && explainClose(te, out)
|
|
110
|
-
return out
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// Apply this spread constraint to each key in a MapVal.
|
|
115
|
-
// Uses unite() per key with the correct descended context.
|
|
116
|
-
applyToMap(map: MapVal, ctx: AontuContext, te: any): Val {
|
|
117
|
-
const spread = this.peg
|
|
118
|
-
|
|
119
|
-
const mapKeys = Object.keys(map.peg)
|
|
120
|
-
|
|
121
|
-
// If map has no keys, preserve the spread for future merges.
|
|
122
|
-
// Mark as DONE to prevent fixpoint cycling.
|
|
123
|
-
if (mapKeys.length === 0) {
|
|
124
|
-
const out = new ConjunctVal({ peg: [map, this] }, ctx)
|
|
125
|
-
out.dc = DONE
|
|
126
|
-
return out
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const out = new MapVal({ peg: {} }, ctx)
|
|
130
|
-
out.closed = map.closed
|
|
131
|
-
out.optionalKeys = 0 < map.optionalKeys.length
|
|
132
|
-
? [...map.optionalKeys] : map.optionalKeys
|
|
133
|
-
out.site = map.site
|
|
134
|
-
|
|
135
|
-
let done = true
|
|
136
|
-
|
|
137
|
-
for (const key of mapKeys) {
|
|
138
|
-
const child = map.peg[key]
|
|
139
|
-
const keyctx = ctx.descend(key)
|
|
140
|
-
// Clone the spread for this key's context, then resolve any
|
|
141
|
-
// path-dependent functions (key(), path(), etc.) by unifying
|
|
142
|
-
// with TOP. This ensures the spread values are concrete before
|
|
143
|
-
// being merged as peer keys into the child map.
|
|
144
|
-
let key_spread = spread.spreadClone(keyctx)
|
|
145
|
-
if (!key_spread.done) {
|
|
146
|
-
key_spread = unite(keyctx, key_spread, top(), 'spread-resolve')
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Clear type marks on the spread constraint root and its
|
|
150
|
-
// direct children — type() constrains but should not mark
|
|
151
|
-
// children as type-invisible.
|
|
152
|
-
key_spread.mark.type = false
|
|
153
|
-
key_spread.mark.hide = false
|
|
154
|
-
if (key_spread.isMap) {
|
|
155
|
-
for (const k in key_spread.peg) {
|
|
156
|
-
if (key_spread.peg[k]?.isVal) {
|
|
157
|
-
key_spread.peg[k].mark.type = false
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
propagateMarks(map, child)
|
|
163
|
-
|
|
164
|
-
out.peg[key] =
|
|
165
|
-
undefined === child ? key_spread :
|
|
166
|
-
child.isNil ? child :
|
|
167
|
-
key_spread.isNil ? key_spread :
|
|
168
|
-
key_spread.isTop && child.done ? child :
|
|
169
|
-
child.isTop && key_spread.done ? key_spread :
|
|
170
|
-
unite(te ? keyctx.clone({ explain: ec(te, 'SK:' + key) }) : keyctx,
|
|
171
|
-
child, key_spread, 'spread-apply')
|
|
172
|
-
|
|
173
|
-
done = done && (DONE === out.peg[key].dc)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
out.dc = done ? DONE : map.dc + 1
|
|
177
|
-
|
|
178
|
-
propagateMarks(map, out)
|
|
179
|
-
// NOTE: do not propagate type/hide marks from the spread constraint
|
|
180
|
-
// to the output — a type spread constrains children but doesn't
|
|
181
|
-
// make them type-invisible.
|
|
182
|
-
|
|
183
|
-
return out
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// Apply this spread constraint to each element in a ListVal.
|
|
188
|
-
applyToList(list: ListVal, ctx: AontuContext, te: any): Val {
|
|
189
|
-
const spread = this.peg
|
|
190
|
-
|
|
191
|
-
const listKeys = Object.keys(list.peg)
|
|
192
|
-
|
|
193
|
-
if (listKeys.length === 0) {
|
|
194
|
-
const out = new ConjunctVal({ peg: [list, this] }, ctx)
|
|
195
|
-
out.dc = DONE
|
|
196
|
-
return out
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const out = new ListVal({ peg: [] }, ctx)
|
|
200
|
-
out.closed = list.closed
|
|
201
|
-
out.optionalKeys = 0 < list.optionalKeys.length
|
|
202
|
-
? [...list.optionalKeys] : list.optionalKeys
|
|
203
|
-
out.site = list.site
|
|
204
|
-
|
|
205
|
-
let done = true
|
|
206
|
-
|
|
207
|
-
for (const key of listKeys) {
|
|
208
|
-
const child = list.peg[key]
|
|
209
|
-
const keyctx = ctx.descend(key)
|
|
210
|
-
const key_spread = spread.spreadClone(keyctx)
|
|
211
|
-
|
|
212
|
-
propagateMarks(list, child)
|
|
213
|
-
|
|
214
|
-
out.peg[key as any] =
|
|
215
|
-
undefined === child ? key_spread :
|
|
216
|
-
child.isNil ? child :
|
|
217
|
-
key_spread.isNil ? key_spread :
|
|
218
|
-
key_spread.isTop && child.done ? child :
|
|
219
|
-
child.isTop && key_spread.done ? key_spread :
|
|
220
|
-
unite(te ? keyctx.clone({ explain: ec(te, 'SL:' + key) }) : keyctx,
|
|
221
|
-
child, key_spread, 'spread-apply-list')
|
|
222
|
-
|
|
223
|
-
done = done && (DONE === out.peg[key as any]?.dc)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
out.dc = done ? DONE : list.dc + 1
|
|
227
|
-
|
|
228
|
-
propagateMarks(list, out)
|
|
229
|
-
|
|
230
|
-
return out
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
clone(ctx: AontuContext, spec?: ValSpec): Val {
|
|
235
|
-
const out = new SpreadVal({
|
|
236
|
-
peg: this.peg.clone(ctx, spec),
|
|
237
|
-
...(spec || {}),
|
|
238
|
-
}, ctx)
|
|
239
|
-
out.dc = this.done ? DONE : out.dc
|
|
240
|
-
out.site = this.site
|
|
241
|
-
return out
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
spreadClone(ctx: AontuContext): Val {
|
|
246
|
-
if (!this.isPathDependent) return this
|
|
247
|
-
return this.clone(ctx)
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
get canon(): string {
|
|
252
|
-
// Use {&:...} for map spreads, [&:...] for list spreads, {&:X} for scalars
|
|
253
|
-
const pc = this.peg.canon
|
|
254
|
-
if (this.peg.isMap) return '{&:' + pc.slice(1, -1) + '}' // {&:k:v,...}
|
|
255
|
-
if (this.peg.isList) return '[&:' + pc.slice(1, -1) + ']' // [&:v,...]
|
|
256
|
-
return '{&:' + pc + '}'
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
gen(_ctx: AontuContext) {
|
|
261
|
-
// Unresolved spread (never applied to a map/list) generates
|
|
262
|
-
// as undefined — the spread is a constraint, not a value.
|
|
263
|
-
return undefined
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
inspection() {
|
|
268
|
-
return 'spread'
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
export {
|
|
274
|
-
SpreadVal,
|
|
275
|
-
}
|