aontu 0.57.0 → 0.59.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/README.md +2 -2
- package/dist/agentsmd.js +1 -1
- package/dist/alias.d.ts +3 -0
- package/dist/alias.js +59 -0
- package/dist/alias.js.map +1 -0
- package/dist/aontu.d.ts +4 -2
- package/dist/aontu.js +12 -3
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +486 -4
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +4 -0
- package/dist/ctx.js +3 -0
- package/dist/ctx.js.map +1 -1
- package/dist/err.js +6 -3
- package/dist/err.js.map +1 -1
- package/dist/format.js +8 -2
- package/dist/format.js.map +1 -1
- package/dist/hints.js +40 -9
- package/dist/hints.js.map +1 -1
- package/dist/lang.js +409 -58
- package/dist/lang.js.map +1 -1
- package/dist/lower.d.ts +20 -0
- package/dist/lower.js +575 -0
- package/dist/lower.js.map +1 -0
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +1 -1
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +40 -3
- package/dist/mcp.js.map +1 -1
- package/dist/render.d.ts +53 -0
- package/dist/render.js +542 -0
- package/dist/render.js.map +1 -0
- package/dist/sigdecl.js +1 -1
- package/dist/sigdecl.js.map +1 -1
- package/dist/std.d.ts +2 -0
- package/dist/std.js +498 -2
- package/dist/std.js.map +1 -1
- package/dist/template.d.ts +5 -0
- package/dist/template.js +257 -0
- package/dist/template.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +2 -0
- package/dist/type.js.map +1 -1
- package/dist/unify.js +43 -0
- package/dist/unify.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +1 -1
- package/dist/val/AggFuncVal.js +10 -21
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/BagVal.js +1 -1
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +1 -1
- package/dist/val/DisjunctVal.js +33 -2
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.d.ts +1 -1
- package/dist/val/EachFuncVal.js +8 -13
- package/dist/val/EachFuncVal.js.map +1 -1
- package/dist/val/EmitFuncVal.d.ts +23 -4
- package/dist/val/EmitFuncVal.js +298 -28
- package/dist/val/EmitFuncVal.js.map +1 -1
- package/dist/val/FilterFuncVal.js +8 -4
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FormFuncVal.d.ts +14 -0
- package/dist/val/FormFuncVal.js +55 -0
- package/dist/val/FormFuncVal.js.map +1 -0
- package/dist/val/FuncBaseVal.js +36 -13
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/ListVal.js +9 -1
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +2 -1
- package/dist/val/MapVal.js +2 -1
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/PackFuncVal.d.ts +1 -1
- package/dist/val/PackFuncVal.js +22 -18
- package/dist/val/PackFuncVal.js.map +1 -1
- package/dist/val/PlaceVal.js +7 -2
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/RefVal.d.ts +3 -0
- package/dist/val/RefVal.js +181 -18
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/Val.d.ts +8 -1
- package/dist/val/Val.js +39 -1
- package/dist/val/Val.js.map +1 -1
- package/dist/val/members.d.ts +9 -0
- package/dist/val/members.js +52 -0
- package/dist/val/members.js.map +1 -0
- package/grammar/aontu.abnf +1 -1
- package/grammar/aontu.gbnf +1 -1
- package/grammar/aontu.lark +1 -1
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +4 -4
- package/skill/error-codes.md +1 -1
- package/skill/examples.md +1 -1
- package/skill/grammar-card.md +1 -1
- package/src/agentsmd.ts +1 -1
- package/src/alias.ts +112 -0
- package/src/aontu.ts +20 -2
- package/src/cli.ts +528 -4
- package/src/ctx.ts +18 -0
- package/src/err.ts +6 -3
- package/src/format.ts +12 -2
- package/src/hints.ts +47 -9
- package/src/lang.ts +453 -63
- package/src/lower.ts +636 -0
- package/src/lsp.ts +1 -1
- package/src/mcp-server.ts +1 -1
- package/src/mcp.ts +43 -4
- package/src/render.ts +727 -0
- package/src/sigdecl.ts +1 -1
- package/src/std.ts +506 -1
- package/src/template.ts +291 -0
- package/src/type.ts +10 -1
- package/src/unify.ts +47 -0
- package/src/val/AggFuncVal.ts +10 -21
- package/src/val/BagVal.ts +1 -1
- package/src/val/ConstraintVal.ts +1 -1
- package/src/val/DisjunctVal.ts +33 -2
- package/src/val/EachFuncVal.ts +8 -16
- package/src/val/EmitFuncVal.ts +376 -39
- package/src/val/FilterFuncVal.ts +11 -4
- package/src/val/FormFuncVal.ts +119 -0
- package/src/val/FuncBaseVal.ts +36 -13
- package/src/val/ListVal.ts +9 -1
- package/src/val/MapVal.ts +3 -2
- package/src/val/PackFuncVal.ts +23 -19
- package/src/val/PlaceVal.ts +7 -2
- package/src/val/RefVal.ts +193 -20
- package/src/val/Val.ts +75 -4
- package/src/val/members.ts +86 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
// TRANSFORMATION: `form(data, tmpl)` (G9 §4; docs/design/RENDER.0.md
|
|
4
|
+
// D11 and P6). One element of the result list per child of `data`,
|
|
5
|
+
// being `tmpl` instantiated at that position with `_` bound to the
|
|
6
|
+
// source child. It REPLACES; it does not meet.
|
|
7
|
+
//
|
|
8
|
+
// names: [web, auth]
|
|
9
|
+
// units: form($.names, {path: _ + ".ts"}) -> [{path: "web.ts"}, {path: "auth.ts"}]
|
|
10
|
+
//
|
|
11
|
+
// WHY BESIDE `each`. `each($.ports, integer)` is a schema statement
|
|
12
|
+
// and is monotone -- more information about the template narrows the
|
|
13
|
+
// children -- so it is a lattice citizen, and a constructor is not.
|
|
14
|
+
// The language draws this line twice already: `min`/`max` (bounds)
|
|
15
|
+
// against `least`/`greatest` (aggregates), and `filter` (select by
|
|
16
|
+
// unifiability) against `match` (choose a result). `each` is the
|
|
17
|
+
// bound; `form` is the construction. `each` also carries the source
|
|
18
|
+
// child's identity (its clone keeps the entity), where `form`'s
|
|
19
|
+
// element IS the template.
|
|
20
|
+
//
|
|
21
|
+
// WHY IT EXISTS AT ALL: ORDER. `pick(pack(d, {f: t}), f)` maps too,
|
|
22
|
+
// but it goes through a map and re-sorts to code-point order, and it
|
|
23
|
+
// refuses a list of records outright (pack_key). A struct's fields, a
|
|
24
|
+
// DDL's columns and a file's imports are lists whose order is the
|
|
25
|
+
// model's, and silently alphabetising them is wrong output. `form`
|
|
26
|
+
// reads its members through the one ordering helper `each` uses
|
|
27
|
+
// (members.ts), so the two can never disagree about order -- source
|
|
28
|
+
// order for a list, sorted-key order for a map -- and skips what
|
|
29
|
+
// generation would not emit: a hidden child, an unfilled optional.
|
|
30
|
+
//
|
|
31
|
+
// It joins `boundArgStart` (PlaceVal.ts): a `_` inside its template
|
|
32
|
+
// is ITS hole to bind, never an outer generator's. Forgetting that is
|
|
33
|
+
// BUGS.md §34's silent failure -- every existing test passes and the
|
|
34
|
+
// new combinator captures an outer generator's hole -- which is why
|
|
35
|
+
// the shared spec's nesting rows exist.
|
|
36
|
+
|
|
37
|
+
import type {
|
|
38
|
+
Val,
|
|
39
|
+
ValSpec,
|
|
40
|
+
} from '../type'
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
AontuContext,
|
|
44
|
+
} from '../ctx'
|
|
45
|
+
|
|
46
|
+
import { makeNilErr } from '../err'
|
|
47
|
+
import { ListVal } from './ListVal'
|
|
48
|
+
import { FuncBaseVal } from './FuncBaseVal'
|
|
49
|
+
import { repathInstance } from './Val'
|
|
50
|
+
import { fillPlace } from './PlaceVal'
|
|
51
|
+
import { memberVals } from './members'
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class FormFuncVal extends FuncBaseVal {
|
|
55
|
+
isFormFunc = true
|
|
56
|
+
|
|
57
|
+
// THE STAGING RULE, for the reason given in PackFuncVal: the data
|
|
58
|
+
// is not settled merely by being `done` once.
|
|
59
|
+
staged = true
|
|
60
|
+
|
|
61
|
+
constructor(
|
|
62
|
+
spec: ValSpec,
|
|
63
|
+
ctx?: AontuContext
|
|
64
|
+
) {
|
|
65
|
+
super(spec, ctx)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
funcname() {
|
|
70
|
+
return 'form'
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
// The template is not an argument to drive (see PackFuncVal.prepare).
|
|
75
|
+
prepare(_ctx: AontuContext, _args: Val[]) {
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
unify(peer: Val, ctx: AontuContext): Val {
|
|
81
|
+
// ONE argument is driven: the data. The template is not (see
|
|
82
|
+
// prepare above).
|
|
83
|
+
if (!this.stagedReady(peer, ctx, 1)) {
|
|
84
|
+
return this.residuate(peer, ctx)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return super.unify(peer, ctx)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
resolve(ctx: AontuContext, args: Val[]) {
|
|
92
|
+
const vals = memberVals(args?.[0], ctx)
|
|
93
|
+
if (undefined === vals) {
|
|
94
|
+
return makeNilErr(ctx, 'form_data', this)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Arity is checked at parse (funcArity), so the template is here.
|
|
98
|
+
const tmpl: Val = args[1]
|
|
99
|
+
const peg: Val[] = []
|
|
100
|
+
|
|
101
|
+
for (let i = 0; i < vals.length; i++) {
|
|
102
|
+
const elctx = ctx.descend(String(i))
|
|
103
|
+
// A FULL INSTANCE per element, to the leaves (`dup`, ADR-005),
|
|
104
|
+
// at the element's own position -- see PackFuncVal.resolve --
|
|
105
|
+
// with `_` bound to the source child and NOTHING met into it.
|
|
106
|
+
const inst = tmpl.clone(elctx, { dup: true })
|
|
107
|
+
repathInstance(inst, inst.path)
|
|
108
|
+
peg.push(fillPlace(inst, vals[i], elctx))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return new ListVal({ peg }, ctx)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
} /* node:coverage ignore next 6 */
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
export {
|
|
118
|
+
FormFuncVal,
|
|
119
|
+
}
|
package/src/val/FuncBaseVal.ts
CHANGED
|
@@ -46,6 +46,13 @@ import { hasPlace, fillPlace } from '../val/PlaceVal'
|
|
|
46
46
|
function trialUnify(ctx: AontuContext, a: Val, b: Val): Val | undefined {
|
|
47
47
|
const savedErr = ctx.err
|
|
48
48
|
const savedTrial = ctx._trialMode
|
|
49
|
+
// Restored by DELETION where they were inherited, for the reason
|
|
50
|
+
// DisjunctVal.unify's own sandbox gives at length: contexts are
|
|
51
|
+
// Object.create(parent) and cached per (parent, key), so writing
|
|
52
|
+
// these back leaves own properties that shadow the ancestor and make
|
|
53
|
+
// a later trial invisible to the value running inside it.
|
|
54
|
+
const ownErr = Object.prototype.hasOwnProperty.call(ctx, 'err')
|
|
55
|
+
const ownTrial = Object.prototype.hasOwnProperty.call(ctx, '_trialMode')
|
|
49
56
|
const trialErr: any[] = []
|
|
50
57
|
|
|
51
58
|
ctx.err = trialErr
|
|
@@ -56,8 +63,18 @@ function trialUnify(ctx: AontuContext, a: Val, b: Val): Val | undefined {
|
|
|
56
63
|
out = unite(ctx, a, b, 'trial')
|
|
57
64
|
}
|
|
58
65
|
finally {
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
if (ownErr) {
|
|
67
|
+
ctx.err = savedErr
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
delete (ctx as any).err
|
|
71
|
+
}
|
|
72
|
+
if (ownTrial) {
|
|
73
|
+
ctx._trialMode = savedTrial
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
delete (ctx as any)._trialMode
|
|
77
|
+
}
|
|
61
78
|
}
|
|
62
79
|
|
|
63
80
|
return 0 < trialErr.length || out.isNil ? undefined : out
|
|
@@ -158,22 +175,28 @@ class FuncBaseVal extends FeatureVal {
|
|
|
158
175
|
|
|
159
176
|
|
|
160
177
|
// THE PER-DESTINATION INSTANTIATION RULE (ADR-005). The default
|
|
161
|
-
// clone shares the argument array AND the argument Vals —
|
|
162
|
-
//
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
178
|
+
// clone shares the argument array AND the argument Vals — the
|
|
179
|
+
// residuation clone, which stays at one position and wants the
|
|
180
|
+
// sharing, and the reference copy of a target that still holds a
|
|
181
|
+
// staged call, which pins the move()/copy() ghost artifacts
|
|
182
|
+
// (test/spec/func.tsv, ghost-*-innard-canon; ADR-025) — but a clone
|
|
183
|
+
// that is an INSTANCE must own the full inner structure: with the
|
|
184
|
+
// args shared, `pack($.names, close({name: key()}))` resolved key()
|
|
185
|
+
// once inside the one shared inner map and stamped the FIRST
|
|
186
|
+
// child's key on every child (use-cases/BUGS.md §8). The `dup` spec
|
|
187
|
+
// flag asks for that depth; everything else keeps the sharing it
|
|
188
|
+
// has always had.
|
|
169
189
|
clone(ctx: AontuContext, spec?: ValSpec): Val {
|
|
170
190
|
const out = super.clone(ctx, spec) as FuncBaseVal
|
|
171
191
|
if (true === spec?.dup && Array.isArray(this.peg)) {
|
|
172
192
|
// Every argument is a Val by construction (the parser builds
|
|
173
193
|
// them; make() rebuilds from driven Vals), as the Go twin's
|
|
174
|
-
// []Val typing states outright. The
|
|
175
|
-
// normalise every path in the clone
|
|
176
|
-
// argument-shaped parse paths never
|
|
194
|
+
// []Val typing states outright. The generator and spread
|
|
195
|
+
// instantiation sites then normalise every path in the clone
|
|
196
|
+
// (repathInstance), so the argument-shaped parse paths never
|
|
197
|
+
// leak into an instance; the reference copy takes the paths
|
|
198
|
+
// this rebasing gives, which is what an absolute address in a
|
|
199
|
+
// copied model already expects (ADR-014).
|
|
177
200
|
out.peg = this.peg.map((a: Val) => a.clone(ctx, { dup: true }))
|
|
178
201
|
}
|
|
179
202
|
return out
|
package/src/val/ListVal.ts
CHANGED
|
@@ -335,7 +335,15 @@ class ListVal extends BagVal {
|
|
|
335
335
|
{ mark: spec?.mark, dup: spec?.dup } : {}
|
|
336
336
|
for (let entry of Object.entries(this.peg)) {
|
|
337
337
|
out.peg[entry[0]] =
|
|
338
|
-
(entry[1] as any)?.isVal ? (entry[1] as Val).clone(ctx,
|
|
338
|
+
(entry[1] as any)?.isVal ? (entry[1] as Val).clone(ctx, {
|
|
339
|
+
...childspec,
|
|
340
|
+
// AN ELEMENT IS A POSITION, exactly as a map's child is
|
|
341
|
+
// (MapVal.clone). Without this an explicitly pathed clone
|
|
342
|
+
// rebased the list and left every element at its source
|
|
343
|
+
// path -- the Go twin descends by index (go/clone.go, the
|
|
344
|
+
// *ListVal arm of clonePathKind).
|
|
345
|
+
path: [...out.path, entry[0]],
|
|
346
|
+
}) : entry[1]
|
|
339
347
|
}
|
|
340
348
|
if (this.spread.cj) {
|
|
341
349
|
out.spread.cj = this.spread.cj.clone(ctx, childspec)
|
package/src/val/MapVal.ts
CHANGED
|
@@ -46,7 +46,7 @@ import { markSpread } from '../provenance'
|
|
|
46
46
|
// pass. The map lives on the unify root ctx (see Unify), so it persists
|
|
47
47
|
// across fixpoint passes and is GC'd with the run.
|
|
48
48
|
function spreadSnapKey(cj: any): string {
|
|
49
|
-
return cj.
|
|
49
|
+
return cj.spelling + '~' + cj.site.row + ':' + cj.site.col
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// Snapshot a path-dependent ref spread to its structural target once,
|
|
@@ -605,5 +605,6 @@ class MapVal extends BagVal {
|
|
|
605
605
|
|
|
606
606
|
|
|
607
607
|
export {
|
|
608
|
-
MapVal
|
|
608
|
+
MapVal,
|
|
609
|
+
spreadSnapKey,
|
|
609
610
|
}
|
package/src/val/PackFuncVal.ts
CHANGED
|
@@ -44,35 +44,39 @@ import { MapVal } from './MapVal'
|
|
|
44
44
|
import { FuncBaseVal } from './FuncBaseVal'
|
|
45
45
|
import { repathInstance } from './Val'
|
|
46
46
|
import { fillPlace } from './PlaceVal'
|
|
47
|
+
import { bagMembers } from './members'
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
// The keys a data bag names, in the order the result must carry them,
|
|
50
51
|
// or a code naming what is wrong with it. Shared with `each`, which
|
|
51
52
|
// asks the same question of the same argument and answers it with the
|
|
52
53
|
// values rather than the keys.
|
|
53
|
-
function dataKeys(data: Val | undefined): string[] | string {
|
|
54
|
-
|
|
54
|
+
function dataKeys(data: Val | undefined, ctx: AontuContext): string[] | string {
|
|
55
|
+
// The candidates are the bag's MEMBERS -- what generation would
|
|
56
|
+
// emit (./members.ts, BUGS.md §79) -- so a hidden key, or a hidden
|
|
57
|
+
// name in a list of names, packs nothing.
|
|
58
|
+
const members = bagMembers(data, ctx)
|
|
59
|
+
|
|
60
|
+
if (undefined === members) {
|
|
61
|
+
return 'pack_data'
|
|
62
|
+
}
|
|
55
63
|
|
|
56
|
-
if (true ===
|
|
57
|
-
return
|
|
64
|
+
if (true === (data as any).isMap) {
|
|
65
|
+
return members.map((m) => m.key)
|
|
58
66
|
}
|
|
59
67
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return 'pack_key'
|
|
69
|
-
}
|
|
70
|
-
out.push(e.peg)
|
|
68
|
+
const out: string[] = []
|
|
69
|
+
for (const { val } of members) {
|
|
70
|
+
const e: any = val
|
|
71
|
+
// A key is a NAME, and only a string is one. A number would
|
|
72
|
+
// key by position under another spelling, which is the failure
|
|
73
|
+
// mode the data-keyed rule exists to refuse.
|
|
74
|
+
if (true !== e?.isScalar || 'string' !== typeof e.peg) {
|
|
75
|
+
return 'pack_key'
|
|
71
76
|
}
|
|
72
|
-
|
|
77
|
+
out.push(e.peg)
|
|
73
78
|
}
|
|
74
|
-
|
|
75
|
-
return 'pack_data'
|
|
79
|
+
return out
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
|
|
@@ -123,7 +127,7 @@ class PackFuncVal extends FuncBaseVal {
|
|
|
123
127
|
|
|
124
128
|
|
|
125
129
|
resolve(ctx: AontuContext, args: Val[]) {
|
|
126
|
-
const keys = dataKeys(args?.[0])
|
|
130
|
+
const keys = dataKeys(args?.[0], ctx)
|
|
127
131
|
if ('string' === typeof keys) {
|
|
128
132
|
return makeNilErr(ctx, keys, this)
|
|
129
133
|
}
|
package/src/val/PlaceVal.ts
CHANGED
|
@@ -85,7 +85,8 @@ class PlaceVal extends ValBase {
|
|
|
85
85
|
// so it stays visible: a hole there is an outer hole as before.
|
|
86
86
|
function boundArgStart(v: any): number {
|
|
87
87
|
return true === v.isPackFunc || true === v.isEachFunc ||
|
|
88
|
-
true === v.isFilterFunc || true === v.isEmitFunc
|
|
88
|
+
true === v.isFilterFunc || true === v.isEmitFunc ||
|
|
89
|
+
true === v.isFormFunc ? 1 : Infinity
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
|
|
@@ -132,7 +133,11 @@ function hasPlace(v: Val): boolean {
|
|
|
132
133
|
// generator's to fill with its OWN source children when it fires.
|
|
133
134
|
function fillPlace(v: Val, fill: Val, ctx: AontuContext): Val {
|
|
134
135
|
if (true === (v as any).isPlace) {
|
|
135
|
-
|
|
136
|
+
// A FILL IS A POSITION. The hole knows where it sits in the
|
|
137
|
+
// instance; the datum arriving in it does not, and inserted as it
|
|
138
|
+
// stands it keeps the paths it had at its SOURCE, so every finding
|
|
139
|
+
// under it names a path that does not exist.
|
|
140
|
+
return fill.clone(ctx, { path: [...(v as any).path] })
|
|
136
141
|
}
|
|
137
142
|
|
|
138
143
|
const peg: any = (v as any).peg
|
package/src/val/RefVal.ts
CHANGED
|
@@ -73,12 +73,40 @@ function pendingMarkWrapper(v: any): boolean {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
// The child a term of the walk can supply for one path segment, or
|
|
77
|
+
// undefined when it has none. A map or a list answers from its own
|
|
78
|
+
// members; a PENDING type()/hide() answers from its argument's, because
|
|
79
|
+
// the wrapper only marks and its argument is the structure the path
|
|
80
|
+
// names (see the call sites in `find`).
|
|
81
|
+
function markedChild(v: any, part: any): Val | undefined {
|
|
82
|
+
if (true === v?.isMap || true === v?.isList) {
|
|
83
|
+
return v.peg[part]
|
|
84
|
+
}
|
|
85
|
+
if (true === v?.isFunc
|
|
86
|
+
&& (true === v.isHideFunc || true === v.isTypeFunc)
|
|
87
|
+
&& (true === v.peg?.[0]?.isMap || true === v.peg?.[0]?.isList)) {
|
|
88
|
+
return v.peg[0].peg[part]
|
|
89
|
+
}
|
|
90
|
+
return undefined
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
// An alias name, whole: the sigil and an identifier (the lexer's
|
|
95
|
+
// ALIAS_RE, anchored at both ends, for the canon spelling above).
|
|
96
|
+
const ALIAS_NAME_RE = /^%[A-Za-z_][A-Za-z0-9_]*$/
|
|
97
|
+
|
|
98
|
+
|
|
76
99
|
class RefVal extends FeatureVal {
|
|
77
100
|
isRef = true
|
|
78
101
|
isGenable = true
|
|
79
102
|
cjo = 32500
|
|
80
103
|
|
|
81
104
|
absolute: boolean = false
|
|
105
|
+
|
|
106
|
+
// The value an alias reference canons as, attached by expandAliases
|
|
107
|
+
// after unification (see `canon` below). Not a ValSpec field: it is
|
|
108
|
+
// a rendering of the settled tree, never a parse-time property.
|
|
109
|
+
expansion: Val | undefined = undefined
|
|
82
110
|
prefix: boolean = false
|
|
83
111
|
|
|
84
112
|
constructor(
|
|
@@ -220,7 +248,8 @@ class RefVal extends FeatureVal {
|
|
|
220
248
|
}
|
|
221
249
|
|
|
222
250
|
// same path
|
|
223
|
-
else if (this.
|
|
251
|
+
else if (this.spelling ===
|
|
252
|
+
(true === (peer as any).isRef ? (peer as any).spelling : peer.canon)) {
|
|
224
253
|
out = this
|
|
225
254
|
}
|
|
226
255
|
|
|
@@ -406,6 +435,45 @@ class RefVal extends FeatureVal {
|
|
|
406
435
|
|| true === (node as any).peg?.[0]?.isList)) {
|
|
407
436
|
node = (node as any).peg[0].peg[part]
|
|
408
437
|
}
|
|
438
|
+
|
|
439
|
+
// AND SO IS A CONJUNCT THAT STILL CARRIES ONE (issue #164).
|
|
440
|
+
// Two statements for one key meet, so a key written as
|
|
441
|
+
// `T: type({...})` twice is a CONJUNCT of two wrappers -- and
|
|
442
|
+
// one written once beside a plain `T: {...}` is a conjunct
|
|
443
|
+
// too. The arm above sees a wrapper only when it is the whole
|
|
444
|
+
// node, so a reference into such a key walked into the
|
|
445
|
+
// conjunct and stopped: the wrapper waited for its argument,
|
|
446
|
+
// the argument waited for the reference, and neither moved.
|
|
447
|
+
// The first referring child of every consumer stayed
|
|
448
|
+
// unresolved and generation reported mapval_no_gen at a path
|
|
449
|
+
// that names none of this, which is how a five-file schema
|
|
450
|
+
// spent its evening being bisected.
|
|
451
|
+
//
|
|
452
|
+
// The answer at a segment is the MEET of what each term
|
|
453
|
+
// supplies, so terms that have no such member are skipped and
|
|
454
|
+
// the rest are conjoined -- one term answers as itself, and
|
|
455
|
+
// the ordinary map arm answers once the fold has happened.
|
|
456
|
+
// Restricted to a conjunct that still holds a pending
|
|
457
|
+
// wrapper: every other conjunct folds on its own, and this
|
|
458
|
+
// walk exists only to break the wrapper's deadlock.
|
|
459
|
+
else if (true === (node as any).isConjunct
|
|
460
|
+
&& Array.isArray((node as any).peg)
|
|
461
|
+
&& pendingMarkWrapper(node)) {
|
|
462
|
+
const kids: Val[] = []
|
|
463
|
+
for (const term of (node as any).peg) {
|
|
464
|
+
const kid = markedChild(term, part)
|
|
465
|
+
if (undefined !== kid && null !== kid) {
|
|
466
|
+
kids.push(kid)
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
// No term has it YET. Not a miss: the conjunct is still
|
|
470
|
+
// folding, and the member may arrive with the fold.
|
|
471
|
+
if (0 === kids.length) {
|
|
472
|
+
break
|
|
473
|
+
}
|
|
474
|
+
node = 1 === kids.length ?
|
|
475
|
+
kids[0] : new ConjunctVal({ peg: kids }, ctx)
|
|
476
|
+
}
|
|
409
477
|
else if (node.done) {
|
|
410
478
|
nopath = true
|
|
411
479
|
break;
|
|
@@ -464,6 +532,31 @@ class RefVal extends FeatureVal {
|
|
|
464
532
|
else if (pI === refpath.length) {
|
|
465
533
|
out = node
|
|
466
534
|
|
|
535
|
+
// THE READ IS RECORDED, AND THE VALUE STAMPED WITH WHERE IT
|
|
536
|
+
// WAS FOUND (RENDER.0.md P7). A resolved reference clones its
|
|
537
|
+
// target into the referring position, so without the stamp a
|
|
538
|
+
// value that arrived by reference knows only where it came to
|
|
539
|
+
// rest -- and `render --coverage` has nothing to measure the
|
|
540
|
+
// model against. Off unless the run is instrumented; the first
|
|
541
|
+
// address wins, and every reference to one node names the same
|
|
542
|
+
// address anyway.
|
|
543
|
+
if (undefined !== ctx.reads && null != node) {
|
|
544
|
+
// The root's own address is `$`, as the coverage walk spells
|
|
545
|
+
// it: a dot with nothing after it would match no path there.
|
|
546
|
+
const addr = '$' + refpath.map((seg: string) => '.' + seg).join('')
|
|
547
|
+
// AN ALIAS IS NOT A PATH. `%wire` names a value the document
|
|
548
|
+
// holds unevaluated and the tree never carries, so it is an
|
|
549
|
+
// address a rule can be reported AT and never a path coverage
|
|
550
|
+
// could call dead: it is stamped, and it is not in the set
|
|
551
|
+
// the model is measured against.
|
|
552
|
+
if (!refpath[0]?.startsWith('%')) {
|
|
553
|
+
ctx.reads.add(addr)
|
|
554
|
+
}
|
|
555
|
+
if (null == (node as any).origin) {
|
|
556
|
+
; (node as any).origin = addr
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
467
560
|
// A reference landing on another reference -- or on a FUNCTION,
|
|
468
561
|
// whose arguments the chase now follows (issue #35) -- may be a
|
|
469
562
|
// PROVEN mutual cycle (a: $.b, b: $.a; a: $.b, b: upper($.a)).
|
|
@@ -544,21 +637,62 @@ class RefVal extends FeatureVal {
|
|
|
544
637
|
|
|
545
638
|
// console.log('FOUND-B', out)
|
|
546
639
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
640
|
+
// A REFERENCE LIFTS: the copy is concrete, its type and hide
|
|
641
|
+
// marks cleared to the leaves -- a marked target is what the
|
|
642
|
+
// author hid or typed, and a reference to it is the author
|
|
643
|
+
// asking for it in the open. EXCEPT the snapshot a staged
|
|
644
|
+
// verb takes of its data (`argsnap`: each, emit, filter,
|
|
645
|
+
// pack, pick, join, the aggregates), when the target itself
|
|
646
|
+
// is not marked: a member marked inside an unmarked bag was
|
|
647
|
+
// hidden IN ITS OWN RIGHT, and the verb's enumeration
|
|
648
|
+
// (ts/src/val/members.ts, BUGS.md §79) needs to see that
|
|
649
|
+
// mark to leave the member out, as generation does. A marked
|
|
650
|
+
// target lifts even there, or `each($.schema.entities, _)`
|
|
651
|
+
// under `schema: hide({...})` would see every entity as
|
|
652
|
+
// hidden, since hide() marks to the leaves.
|
|
653
|
+
const lifted = true !== (ctx as any).argsnap
|
|
654
|
+
|| true === out.mark.type || true === out.mark.hide
|
|
655
|
+
|
|
656
|
+
// A REFERENCE'S COPY OWNS ITS ARGUMENTS (ADR-025). The copy
|
|
657
|
+
// is a per-destination instance exactly as a spread's or a
|
|
658
|
+
// generator's is, so ADR-005's rule holds here too: nothing
|
|
659
|
+
// path-dependent may be shared between two destinations, or
|
|
660
|
+
// the first destination's resolution answers for them all.
|
|
661
|
+
// The shallow clone shared a call's ARGUMENTS -- so
|
|
662
|
+
// `items: [&: $.entities.User]` over a `close({...})` target
|
|
663
|
+
// gave every element the one inner map, whose path each
|
|
664
|
+
// element rebased in turn, and the constraint that failed at
|
|
665
|
+
// element 2 reported element 0's path (use-cases GAP 8).
|
|
666
|
+
//
|
|
667
|
+
// A STAGED CALL IS THE EXCEPTION, because it has not decided
|
|
668
|
+
// yet. Its arguments are still being driven AT ITS OWN SITE
|
|
669
|
+
// (the staging rule, G8 phase 0), and a copy that owned them
|
|
670
|
+
// would drive its own set at the referring position instead:
|
|
671
|
+
// the relative `.side_effect` in a `match()` would read the
|
|
672
|
+
// referring field's siblings (use-cases/09-agent-tools), and
|
|
673
|
+
// an alias naming an `emit` rule table -- a template, which
|
|
674
|
+
// is exactly a value copied before it resolves -- would read
|
|
675
|
+
// its recursive `%w` as a self-reference. A staged call
|
|
676
|
+
// ANYWHERE in the target counts: what is referenced is
|
|
677
|
+
// usually the conjunct the call sits in, not the call.
|
|
678
|
+
// The copy shares what the source is still settling, and
|
|
679
|
+
// owns the rest.
|
|
680
|
+
out = out.clone(ctx, { dup: !out.holdsStaged })
|
|
681
|
+
|
|
682
|
+
if (lifted) {
|
|
683
|
+
walk(out, (_key: string | number | undefined, val: Val) => {
|
|
684
|
+
val.mark.type = false
|
|
685
|
+
val.mark.hide = false
|
|
686
|
+
// THE LINK IS NOT CLEARED (G4 phase 3): a link says what
|
|
687
|
+
// a value POINTS AT, and a copy of a link points at the
|
|
688
|
+
// same thing. An ABSOLUTE address still names the same
|
|
689
|
+
// node from the copy; a RELATIVE one is read from the
|
|
690
|
+
// copy's own position, which is what makes a referenced
|
|
691
|
+
// model resolve its internal links inside the copy
|
|
692
|
+
// (ADR-014).
|
|
693
|
+
return val
|
|
694
|
+
})
|
|
695
|
+
}
|
|
562
696
|
|
|
563
697
|
// onsole.log('FOUND-C', out)
|
|
564
698
|
}
|
|
@@ -694,19 +828,58 @@ class RefVal extends FeatureVal {
|
|
|
694
828
|
absolute: this.absolute,
|
|
695
829
|
...(spec || {})
|
|
696
830
|
}) as RefVal)
|
|
831
|
+
out.expansion = this.expansion
|
|
697
832
|
return out
|
|
698
833
|
}
|
|
699
834
|
|
|
700
835
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
836
|
+
// THE NAME OF THE ALIAS THIS REFERENCE NAMES, or undefined for a
|
|
837
|
+
// path reference. `%u` is spelled internally as the root reference
|
|
838
|
+
// `$.%u` (docs/design/ALIASES.0.md: the name is a path into the
|
|
839
|
+
// declaration), so an alias reference is an absolute reference of
|
|
840
|
+
// one segment that is an alias name.
|
|
841
|
+
get aliasName(): string | undefined {
|
|
842
|
+
return this.absolute && 1 === this.peg.length &&
|
|
843
|
+
'string' === typeof this.peg[0] && ALIAS_NAME_RE.test(this.peg[0]) ?
|
|
844
|
+
this.peg[0] : undefined
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
// THE REFERENCE'S OWN SPELLING: the alias name, or the path. This is
|
|
849
|
+
// the reference's identity (the snapshot key of a ref spread, the
|
|
850
|
+
// same-path test in unify), which `canon` below is not once an
|
|
851
|
+
// expansion is attached.
|
|
852
|
+
get spelling(): string {
|
|
853
|
+
const name = this.aliasName
|
|
854
|
+
if (undefined !== name) {
|
|
855
|
+
return name
|
|
856
|
+
}
|
|
857
|
+
return (this.absolute ? '$' : '') +
|
|
704
858
|
(0 < this.peg.length ? '.' : '') +
|
|
705
859
|
// this.peg.join(this.sep)
|
|
706
860
|
this.peg.map((p: any) => '.' === p ? '' :
|
|
707
861
|
(p.isVal ? p.canon : '' + p))
|
|
708
862
|
.join('.')
|
|
709
|
-
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
get canon() {
|
|
867
|
+
// AN ALIAS REFERENCE CANONS AS THE VALUE IT NAMES. A reference left
|
|
868
|
+
// standing after unification is one inside a spread template
|
|
869
|
+
// (`[&: %u]`, `{&: {a: %u}}`): the template applies to children
|
|
870
|
+
// that have not arrived, so it is not resolved in place. Canon
|
|
871
|
+
// erases the declaration (an alias is a name for a value, and
|
|
872
|
+
// nothing more -- ALIASES.0.md §4), so the name alone would not
|
|
873
|
+
// reparse, and the hash of `t: {&: %u}` would differ from the hash
|
|
874
|
+
// of `t: {&: integer}`, which is the same document. The expansion
|
|
875
|
+
// is attached by expandAliases (ts/src/alias.ts) once the tree has
|
|
876
|
+
// settled; without one -- a parse-only tree, an unresolved name, or
|
|
877
|
+
// the KNOT of a recursive alias inside its own template -- the
|
|
878
|
+
// reference spells its name. Twin of RefVal.Canon in go/ref.go.
|
|
879
|
+
if (undefined !== this.expansion) {
|
|
880
|
+
return this.expansion.canon
|
|
881
|
+
}
|
|
882
|
+
return this.spelling
|
|
710
883
|
}
|
|
711
884
|
|
|
712
885
|
|