aontu 0.52.0 → 0.52.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/aontu.d.ts +1 -1
- package/dist/aontu.js +50 -1
- package/dist/aontu.js.map +1 -1
- package/dist/ctx.d.ts +3 -0
- package/dist/ctx.js +1 -0
- package/dist/ctx.js.map +1 -1
- package/dist/err.js +10 -1
- package/dist/err.js.map +1 -1
- package/dist/hints.js +56 -1
- package/dist/hints.js.map +1 -1
- package/dist/lang.js +235 -15
- package/dist/lang.js.map +1 -1
- package/dist/lsp.js +4 -3
- package/dist/lsp.js.map +1 -1
- package/dist/unify.d.ts +2 -1
- package/dist/unify.js +55 -3
- package/dist/unify.js.map +1 -1
- package/dist/val/ConstraintVal.d.ts +42 -1
- package/dist/val/ConstraintVal.js +1048 -107
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ExpectVal.d.ts +1 -0
- package/dist/val/ExpectVal.js +14 -0
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FuncBaseVal.js +7 -1
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/ListVal.js +17 -4
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.js +9 -0
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/OpBaseVal.js +5 -1
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/RefVal.js +69 -23
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/Val.d.ts +1 -1
- package/dist/val/Val.js +0 -3
- package/dist/val/Val.js.map +1 -1
- package/package.json +10 -10
- package/src/aontu.ts +58 -1
- package/src/ctx.ts +9 -0
- package/src/err.ts +10 -1
- package/src/hints.ts +63 -1
- package/src/lang.ts +258 -16
- package/src/lsp.ts +4 -3
- package/src/unify.ts +59 -2
- package/src/val/ConstraintVal.ts +1195 -111
- package/src/val/ExpectVal.ts +15 -0
- package/src/val/FuncBaseVal.ts +8 -2
- package/src/val/ListVal.ts +18 -4
- package/src/val/MapVal.ts +11 -0
- package/src/val/OpBaseVal.ts +6 -2
- package/src/val/RefVal.ts +72 -22
- package/src/val/Val.ts +8 -1
package/src/val/ExpectVal.ts
CHANGED
|
@@ -35,6 +35,21 @@ class ExpectVal extends FeatureVal {
|
|
|
35
35
|
parent?: Val
|
|
36
36
|
key?: string
|
|
37
37
|
|
|
38
|
+
// An expectation canons as THE EXPECTATION ITSELF -- the peg the peer
|
|
39
|
+
// must satisfy. Val.canon's default was the empty string, which
|
|
40
|
+
// rendered a key with no value (`{"r":}`): text that is not a document
|
|
41
|
+
// and could not be reparsed, breaking canon's round-trip contract in
|
|
42
|
+
// both engines (issue #43).
|
|
43
|
+
//
|
|
44
|
+
// Not `top`, which was the first fix here and was wrong. An ExpectVal
|
|
45
|
+
// is created for EVERY peer-introduced non-generable key, not just for
|
|
46
|
+
// `&:` spread children -- `m:{x:1} m:{y:string}` makes one at `y` with
|
|
47
|
+
// no spread in sight -- so rendering `top` erased the `string` and the
|
|
48
|
+
// canon reparsed into a document that accepts values the original
|
|
49
|
+
// rejects. A canon that silently drops a constraint is worse than one
|
|
50
|
+
// that fails to parse. Go's ExpectVal.Canon renders the same peg.
|
|
51
|
+
get canon() { return this.peg.canon }
|
|
52
|
+
|
|
38
53
|
constructor(
|
|
39
54
|
spec: ValSpec,
|
|
40
55
|
ctx?: AontuContext
|
package/src/val/FuncBaseVal.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from '../type'
|
|
12
12
|
|
|
13
13
|
import { AontuContext } from '../ctx'
|
|
14
|
-
import { unite } from '../unify'
|
|
14
|
+
import { unite, withDepth } from '../unify'
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
import {
|
|
@@ -100,7 +100,13 @@ class FuncBaseVal extends FeatureVal {
|
|
|
100
100
|
|
|
101
101
|
let newarg = arg
|
|
102
102
|
if (!arg.done) {
|
|
103
|
-
|
|
103
|
+
// Charged to the depth budget: this recurses without going
|
|
104
|
+
// through `unite`, so the counter would otherwise stay flat
|
|
105
|
+
// while the stack grows (see withDepth in unify.ts). The
|
|
106
|
+
// arg context is built OUTSIDE the closure so its explain
|
|
107
|
+
// ternary stays one branch rather than one per call.
|
|
108
|
+
const argctx = te ? ctx.clone({ explain: ec(te, 'ARG') }) : ctx
|
|
109
|
+
newarg = withDepth(ctx, arg, TOP, () => arg.unify(TOP, argctx))
|
|
104
110
|
newtype = newtype || newarg.mark.type
|
|
105
111
|
newhide = newhide || newarg.mark.hide
|
|
106
112
|
// console.log('FUNCBASE-UNIFY-PEG-B', arg.canon, arg.done, '->', newarg.canon, newarg.done)
|
package/src/val/ListVal.ts
CHANGED
|
@@ -76,6 +76,16 @@ class ListVal extends BagVal {
|
|
|
76
76
|
const TOP = top()
|
|
77
77
|
peer = peer ?? TOP
|
|
78
78
|
|
|
79
|
+
// A sizing residual (`length`, `unique`) sorts AFTER containers in a
|
|
80
|
+
// conjunct so that it counts the MERGED list rather than the first
|
|
81
|
+
// fragment (SIZING_CJO in ConstraintVal.ts). That makes the list the
|
|
82
|
+
// accumulator and the constraint its peer, the reverse of the usual
|
|
83
|
+
// order — and the reading belongs to the constraint either way, so
|
|
84
|
+
// hand it straight back.
|
|
85
|
+
if (true === (peer as any).isConstraint) {
|
|
86
|
+
return peer.unify(this, ctx)
|
|
87
|
+
}
|
|
88
|
+
|
|
79
89
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'List', this, peer)
|
|
80
90
|
let done: boolean = true
|
|
81
91
|
let exit = false
|
|
@@ -263,10 +273,14 @@ class ListVal extends BagVal {
|
|
|
263
273
|
'[' +
|
|
264
274
|
(this.spread.cj ? '&:' + this.spread.cj.canon +
|
|
265
275
|
(0 < keys.length ? ',' : '') : '') +
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
276
|
+
// No optional-element rendering. A list HAS no optional elements to
|
|
277
|
+
// render: a key:value pair in list position contributes no element
|
|
278
|
+
// at all, in either spelling and whatever its key (issue #40), so
|
|
279
|
+
// nothing a source can write reaches this method with an optional
|
|
280
|
+
// key. The Go port's ListVal.Canon has no such arm either, and the
|
|
281
|
+
// two canons must agree -- a canon is round-trippable, and a marker
|
|
282
|
+
// on an element the grammar cannot produce would not reparse.
|
|
283
|
+
keys.map(k => this.peg[k].canon).join(',') +
|
|
270
284
|
']'
|
|
271
285
|
}
|
|
272
286
|
} /* node:coverage ignore next 8 */
|
package/src/val/MapVal.ts
CHANGED
|
@@ -125,6 +125,17 @@ class MapVal extends BagVal {
|
|
|
125
125
|
|
|
126
126
|
const TOP = top()
|
|
127
127
|
peer = peer ?? TOP
|
|
128
|
+
|
|
129
|
+
// A sizing residual (`length`, `unique`) sorts AFTER containers in a
|
|
130
|
+
// conjunct so that it counts the MERGED map rather than the first
|
|
131
|
+
// fragment (SIZING_CJO in ConstraintVal.ts). That makes the map the
|
|
132
|
+
// accumulator and the constraint its peer, the reverse of the usual
|
|
133
|
+
// order — and the reading belongs to the constraint either way, so
|
|
134
|
+
// hand it straight back.
|
|
135
|
+
if (true === (peer as any).isConstraint) {
|
|
136
|
+
return peer.unify(this, ctx)
|
|
137
|
+
}
|
|
138
|
+
|
|
128
139
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Map', this, peer)
|
|
129
140
|
|
|
130
141
|
let done: boolean = true
|
package/src/val/OpBaseVal.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
} from '../err'
|
|
18
18
|
|
|
19
19
|
import { AontuContext } from '../ctx'
|
|
20
|
-
import { unite } from '../unify'
|
|
20
|
+
import { unite, withDepth } from '../unify'
|
|
21
21
|
|
|
22
22
|
import {
|
|
23
23
|
explainOpen,
|
|
@@ -83,7 +83,11 @@ class OpBaseVal extends FeatureVal {
|
|
|
83
83
|
|
|
84
84
|
for (let arg of this.peg) {
|
|
85
85
|
if (!arg.done) {
|
|
86
|
-
|
|
86
|
+
// Charged to the depth budget: this recurses without going
|
|
87
|
+
// through `unite`, so the counter would otherwise stay flat
|
|
88
|
+
// while the stack grows (see withDepth in unify.ts).
|
|
89
|
+
const a = arg
|
|
90
|
+
arg = withDepth(ctx, a, top(), () => a.unify(top(), ctx, ec(te, 'ARG')))
|
|
87
91
|
}
|
|
88
92
|
pegdone &&= arg.done
|
|
89
93
|
newpeg.push(arg)
|
package/src/val/RefVal.ts
CHANGED
|
@@ -413,12 +413,15 @@ class RefVal extends FeatureVal {
|
|
|
413
413
|
else if (pI === refpath.length) {
|
|
414
414
|
out = node
|
|
415
415
|
|
|
416
|
-
// A reference landing on another reference
|
|
417
|
-
//
|
|
418
|
-
//
|
|
419
|
-
//
|
|
420
|
-
//
|
|
421
|
-
|
|
416
|
+
// A reference landing on another reference -- or on a FUNCTION,
|
|
417
|
+
// whose arguments the chase now follows (issue #35) -- may be a
|
|
418
|
+
// PROVEN mutual cycle (a: $.b, b: $.a; a: $.b, b: upper($.a)).
|
|
419
|
+
// Follow the chain and, if it returns to a node still open above
|
|
420
|
+
// it, report path_cycle now instead of deferring every pass and
|
|
421
|
+
// dying later as a spent budget. No proof (the chain leaves plain
|
|
422
|
+
// refs and calls, or ends) defers as before.
|
|
423
|
+
if (null != out && ((out as any).isRef || (out as any).isFunc) &&
|
|
424
|
+
this.detectRefCycle(ctx)) {
|
|
422
425
|
out = makeNilErr(ctx, 'path_cycle', this)
|
|
423
426
|
}
|
|
424
427
|
// Types and hidden values are cloned and made concrete
|
|
@@ -468,33 +471,80 @@ class RefVal extends FeatureVal {
|
|
|
468
471
|
// chain revisits a node -- a PROVEN reference cycle, distinct from a
|
|
469
472
|
// merely unresolved reference. Detection is only on the resolution
|
|
470
473
|
// chain revisiting a node, never on syntactic shape: a chain that
|
|
471
|
-
// passes through a variable segment, a conjunct
|
|
472
|
-
//
|
|
474
|
+
// passes through a variable segment, a conjunct or any other non-ref
|
|
475
|
+
// value yields no proof and the ref defers as before.
|
|
476
|
+
//
|
|
477
|
+
// A FUNCTION is followed, through its arguments (issue #35). A
|
|
478
|
+
// function resolves only once every argument does, so a chain that
|
|
479
|
+
// reaches `b:upper($.a)` and out through `$.a` has proved the same
|
|
480
|
+
// dependency a bare `b:$.a` proves -- `a:$.b b:upper($.a)` is a cycle
|
|
481
|
+
// whichever link wears the call. Without this the shape exhausted the
|
|
482
|
+
// depth budget instead: a `unify_cycle`, which under the G5 taxonomy
|
|
483
|
+
// means "retry with more may help", where a proven structural cycle is
|
|
484
|
+
// FIX THE MODEL. A conjunct and a disjunct stay unfollowed for reasons
|
|
485
|
+
// that are not the same: a disjunct member may simply not be taken, so
|
|
486
|
+
// reaching one proves nothing; a conjunct would be sound to follow, and
|
|
487
|
+
// is left out only because nothing needs it yet.
|
|
488
|
+
//
|
|
489
|
+
// The Go port reaches the same verdict on this shape by a DIFFERENT
|
|
490
|
+
// arm, and that difference outlives this method: its clonePath re-paths
|
|
491
|
+
// a resolved clone to the referring site, so the inner `$.a` lands at
|
|
492
|
+
// path [a] and the plain isprefixpath test proves the cycle before any
|
|
493
|
+
// chase is needed. TypeScript's clone keeps the source paths. ADR-001
|
|
494
|
+
// asks for arm-for-arm correspondence, so the clone-path difference is
|
|
495
|
+
// worth closing on its own; it is wider than this issue and both ports
|
|
496
|
+
// now agree on the verdict and the code either way.
|
|
473
497
|
detectRefCycle(ctx: AontuContext): boolean {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
//
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
498
|
+
// Depth-first with an explicit ANCESTOR set, because a function may
|
|
499
|
+
// carry several reference arguments and the cycle can run through
|
|
500
|
+
// any one of them. The set holds the chain currently being walked,
|
|
501
|
+
// not every node ever walked: revisiting a node reached down a
|
|
502
|
+
// DIFFERENT branch is an ordinary shared reference (two keys reading
|
|
503
|
+
// one third key), and only revisiting one that is still open above
|
|
504
|
+
// us is a cycle.
|
|
505
|
+
//
|
|
506
|
+
// Identity is the RESOLVED PATH, not the RefVal instance: the same
|
|
507
|
+
// target can be reached through distinct ref instances, and it is
|
|
508
|
+
// returning to the same place that closes a loop.
|
|
509
|
+
const chase = (ref: RefVal, ancestors: Set<string>): boolean => {
|
|
510
|
+
const rp = ref.plainRefPath()
|
|
486
511
|
if (null == rp) {
|
|
487
512
|
return false
|
|
488
513
|
}
|
|
514
|
+
const key = rp.join('')
|
|
515
|
+
if (ancestors.has(key)) {
|
|
516
|
+
return true
|
|
517
|
+
}
|
|
518
|
+
|
|
489
519
|
let node: any = ctx.root
|
|
490
520
|
for (let i = 0; i < rp.length && null != node; i++) {
|
|
491
521
|
node = (node.isMap || node.isList) ? node.peg[rp[i]] : undefined
|
|
492
522
|
}
|
|
493
|
-
if (null == node
|
|
523
|
+
if (null == node) {
|
|
494
524
|
return false
|
|
495
525
|
}
|
|
496
|
-
|
|
526
|
+
|
|
527
|
+
// Terminates: each level adds a path to `ancestors` and refuses a
|
|
528
|
+
// repeat, and the tree holds finitely many distinct paths.
|
|
529
|
+
ancestors.add(key)
|
|
530
|
+
let found = false
|
|
531
|
+
if (node.isRef) {
|
|
532
|
+
found = chase(node, ancestors)
|
|
533
|
+
}
|
|
534
|
+
else if (node.isFunc && Array.isArray(node.peg)) {
|
|
535
|
+
for (const arg of node.peg) {
|
|
536
|
+
if (null != arg && arg.isRef && chase(arg, ancestors)) {
|
|
537
|
+
found = true
|
|
538
|
+
break
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
ancestors.delete(key)
|
|
543
|
+
|
|
544
|
+
return found
|
|
497
545
|
}
|
|
546
|
+
|
|
547
|
+
return chase(this, new Set<string>())
|
|
498
548
|
}
|
|
499
549
|
|
|
500
550
|
|
package/src/val/Val.ts
CHANGED
|
@@ -297,7 +297,14 @@ abstract class Val {
|
|
|
297
297
|
|
|
298
298
|
// TODO: indicate marks in some way that is ignored by reparse.
|
|
299
299
|
// Need an annotation/taggins syntax? a:{}/type ?
|
|
300
|
-
|
|
300
|
+
// ABSTRACT: every concrete Val renders its own canonical form. There
|
|
301
|
+
// used to be an empty-string default here, and the one class relying
|
|
302
|
+
// on it (ExpectVal) was rendering a key with no value -- `{"r":}`,
|
|
303
|
+
// text that is not a document (issue #43). With that fixed, nothing
|
|
304
|
+
// reached the default, so it is declared rather than defaulted: a new
|
|
305
|
+
// Val that forgets `canon` is now a compile error instead of silently
|
|
306
|
+
// canoning as nothing.
|
|
307
|
+
abstract get canon(): string
|
|
301
308
|
|
|
302
309
|
|
|
303
310
|
errcanon(): string {
|