aontu 0.57.0 → 0.58.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 +11 -3
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +3 -1
- package/dist/cli.js +475 -3
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +2 -0
- package/dist/ctx.js +1 -0
- package/dist/ctx.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 +354 -57
- 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/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/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/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 +2 -1
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/RefVal.d.ts +3 -0
- package/dist/val/RefVal.js +156 -17
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/Val.d.ts +7 -1
- package/dist/val/Val.js +12 -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 +19 -2
- package/src/cli.ts +517 -3
- package/src/ctx.ts +13 -0
- package/src/format.ts +12 -2
- package/src/hints.ts +47 -9
- package/src/lang.ts +406 -62
- 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/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/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/MapVal.ts +3 -2
- package/src/val/PackFuncVal.ts +23 -19
- package/src/val/PlaceVal.ts +2 -1
- package/src/val/RefVal.ts +167 -18
- package/src/val/Val.ts +42 -1
- 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/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
|
|
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,38 @@ class RefVal extends FeatureVal {
|
|
|
544
637
|
|
|
545
638
|
// console.log('FOUND-B', out)
|
|
546
639
|
|
|
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
|
+
|
|
547
656
|
out = out.clone(ctx)
|
|
548
657
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
658
|
+
if (lifted) {
|
|
659
|
+
walk(out, (_key: string | number | undefined, val: Val) => {
|
|
660
|
+
val.mark.type = false
|
|
661
|
+
val.mark.hide = false
|
|
662
|
+
// THE LINK IS NOT CLEARED (G4 phase 3): a link says what
|
|
663
|
+
// a value POINTS AT, and a copy of a link points at the
|
|
664
|
+
// same thing. An ABSOLUTE address still names the same
|
|
665
|
+
// node from the copy; a RELATIVE one is read from the
|
|
666
|
+
// copy's own position, which is what makes a referenced
|
|
667
|
+
// model resolve its internal links inside the copy
|
|
668
|
+
// (ADR-014).
|
|
669
|
+
return val
|
|
670
|
+
})
|
|
671
|
+
}
|
|
562
672
|
|
|
563
673
|
// onsole.log('FOUND-C', out)
|
|
564
674
|
}
|
|
@@ -694,19 +804,58 @@ class RefVal extends FeatureVal {
|
|
|
694
804
|
absolute: this.absolute,
|
|
695
805
|
...(spec || {})
|
|
696
806
|
}) as RefVal)
|
|
807
|
+
out.expansion = this.expansion
|
|
697
808
|
return out
|
|
698
809
|
}
|
|
699
810
|
|
|
700
811
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
812
|
+
// THE NAME OF THE ALIAS THIS REFERENCE NAMES, or undefined for a
|
|
813
|
+
// path reference. `%u` is spelled internally as the root reference
|
|
814
|
+
// `$.%u` (docs/design/ALIASES.0.md: the name is a path into the
|
|
815
|
+
// declaration), so an alias reference is an absolute reference of
|
|
816
|
+
// one segment that is an alias name.
|
|
817
|
+
get aliasName(): string | undefined {
|
|
818
|
+
return this.absolute && 1 === this.peg.length &&
|
|
819
|
+
'string' === typeof this.peg[0] && ALIAS_NAME_RE.test(this.peg[0]) ?
|
|
820
|
+
this.peg[0] : undefined
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
// THE REFERENCE'S OWN SPELLING: the alias name, or the path. This is
|
|
825
|
+
// the reference's identity (the snapshot key of a ref spread, the
|
|
826
|
+
// same-path test in unify), which `canon` below is not once an
|
|
827
|
+
// expansion is attached.
|
|
828
|
+
get spelling(): string {
|
|
829
|
+
const name = this.aliasName
|
|
830
|
+
if (undefined !== name) {
|
|
831
|
+
return name
|
|
832
|
+
}
|
|
833
|
+
return (this.absolute ? '$' : '') +
|
|
704
834
|
(0 < this.peg.length ? '.' : '') +
|
|
705
835
|
// this.peg.join(this.sep)
|
|
706
836
|
this.peg.map((p: any) => '.' === p ? '' :
|
|
707
837
|
(p.isVal ? p.canon : '' + p))
|
|
708
838
|
.join('.')
|
|
709
|
-
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
get canon() {
|
|
843
|
+
// AN ALIAS REFERENCE CANONS AS THE VALUE IT NAMES. A reference left
|
|
844
|
+
// standing after unification is one inside a spread template
|
|
845
|
+
// (`[&: %u]`, `{&: {a: %u}}`): the template applies to children
|
|
846
|
+
// that have not arrived, so it is not resolved in place. Canon
|
|
847
|
+
// erases the declaration (an alias is a name for a value, and
|
|
848
|
+
// nothing more -- ALIASES.0.md §4), so the name alone would not
|
|
849
|
+
// reparse, and the hash of `t: {&: %u}` would differ from the hash
|
|
850
|
+
// of `t: {&: integer}`, which is the same document. The expansion
|
|
851
|
+
// is attached by expandAliases (ts/src/alias.ts) once the tree has
|
|
852
|
+
// settled; without one -- a parse-only tree, an unresolved name, or
|
|
853
|
+
// the KNOT of a recursive alias inside its own template -- the
|
|
854
|
+
// reference spells its name. Twin of RefVal.Canon in go/ref.go.
|
|
855
|
+
if (undefined !== this.expansion) {
|
|
856
|
+
return this.expansion.canon
|
|
857
|
+
}
|
|
858
|
+
return this.spelling
|
|
710
859
|
}
|
|
711
860
|
|
|
712
861
|
|
package/src/val/Val.ts
CHANGED
|
@@ -11,6 +11,16 @@ import {
|
|
|
11
11
|
import { INNER_OF, WRITTEN } from '../provenance'
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
// THE DISPATCH RECORD (RENDER.0.md D11, P7): the address of the node
|
|
15
|
+
// a rule matched, and the address of the rule that matched it -- the
|
|
16
|
+
// table's own address and the rule's index in it, joined by `#`,
|
|
17
|
+
// which no path holds.
|
|
18
|
+
type EmitOrigin = {
|
|
19
|
+
node: string,
|
|
20
|
+
rule: string,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
14
24
|
type ValMark = {
|
|
15
25
|
type: boolean,
|
|
16
26
|
hide: boolean,
|
|
@@ -168,6 +178,24 @@ abstract class Val {
|
|
|
168
178
|
// and the edge set (ts/src/graph.ts) is exactly the set of these.
|
|
169
179
|
link?: string
|
|
170
180
|
|
|
181
|
+
// THE READ ADDRESS (RENDER.0.md P7): the tree path a reference
|
|
182
|
+
// resolved this value AT, stamped where it was found. A resolved
|
|
183
|
+
// reference CLONES its target into the referring position, so a
|
|
184
|
+
// value that arrived by reference otherwise knows only where it came
|
|
185
|
+
// to rest -- and a trace saying a line came from `$.code.units.0`
|
|
186
|
+
// names the output, not the model. Written only by an instrumented
|
|
187
|
+
// run (AontuContext.reads), carried by clone and by the meet rider,
|
|
188
|
+
// exactly as the deprecation record is.
|
|
189
|
+
origin?: string
|
|
190
|
+
|
|
191
|
+
// THE DISPATCH THAT PRODUCED THIS PIECE (RENDER.0.md D11, P7): the
|
|
192
|
+
// node an `emit` matched and the rule it took, stamped on every
|
|
193
|
+
// piece the dispatch instantiated. The INNERMOST dispatch wins: a
|
|
194
|
+
// nested rule set splices its pieces into the outer result, and the
|
|
195
|
+
// rule that wrote a line is the one that wrote it. Written only by
|
|
196
|
+
// an instrumented run, and carried like `origin`.
|
|
197
|
+
emitted?: EmitOrigin
|
|
198
|
+
|
|
171
199
|
// The GRAPH of an evaluated document (G4 phase 3): the edge set,
|
|
172
200
|
// stamped on the result by Aontu.unify the way the include manifest
|
|
173
201
|
// is. Absent on every Val that is not a unify result.
|
|
@@ -301,6 +329,18 @@ abstract class Val {
|
|
|
301
329
|
out.deprecation = this.deprecation
|
|
302
330
|
}
|
|
303
331
|
|
|
332
|
+
// THE RENDER RIDERS TRAVEL WITH THE CLONE (P7), for the reason the
|
|
333
|
+
// deprecation record does: a clone of a value read at `$.schema`
|
|
334
|
+
// was read at `$.schema`, and a clone of an emitted piece is still
|
|
335
|
+
// that dispatch's. Both are absent unless the run is instrumented,
|
|
336
|
+
// so this is two undefined reads otherwise.
|
|
337
|
+
if (null != this.origin) {
|
|
338
|
+
out.origin = this.origin
|
|
339
|
+
}
|
|
340
|
+
if (null != this.emitted) {
|
|
341
|
+
out.emitted = this.emitted
|
|
342
|
+
}
|
|
343
|
+
|
|
304
344
|
// THE APPLY-ONCE MARK TRAVELS WITH THE CLONE. `_spr` records which
|
|
305
345
|
// spread template has already been merged into this value, and the
|
|
306
346
|
// bag loops read it to keep a template from being applied twice
|
|
@@ -670,10 +710,11 @@ function empty(o: any) {
|
|
|
670
710
|
|| (null != o && 'object' === typeof o && 0 === Object.keys(o).length)
|
|
671
711
|
|| false
|
|
672
712
|
)
|
|
673
|
-
} /* node:coverage ignore next
|
|
713
|
+
} /* node:coverage ignore next 18 */
|
|
674
714
|
|
|
675
715
|
|
|
676
716
|
export type {
|
|
717
|
+
EmitOrigin,
|
|
677
718
|
ValMark,
|
|
678
719
|
ValSpec,
|
|
679
720
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// THE MEMBERS OF A BAG, as every fold and every generator over a bag
|
|
5
|
+
// sees them (use-cases/BUGS.md §79; G9 phase 0 item 4). A member is a
|
|
6
|
+
// child that generation would EMIT: a hide()- or type()-marked child
|
|
7
|
+
// is not one, an alias declaration is not one, and an optional key
|
|
8
|
+
// whose value generates nothing -- an unfilled `y?: string`, an absent
|
|
9
|
+
// optional subtree -- is not one. `each`, `emit`, `filter`, `pick`,
|
|
10
|
+
// `join` and the aggregates read a bag through this one function, so a
|
|
11
|
+
// value the document withholds from its output is withheld from every
|
|
12
|
+
// text and every total the document computes from it: a hidden field
|
|
13
|
+
// that reached generated source through `join` is the defect this
|
|
14
|
+
// closes, and it was one behaviour in both ports because each verb
|
|
15
|
+
// had its own enumeration and none of them asked the mark.
|
|
16
|
+
//
|
|
17
|
+
// Maps list their members in code-point key order (../keyorder.ts),
|
|
18
|
+
// lists in index order -- the one order canon, generation and the Go
|
|
19
|
+
// port agree on (BUGS.md §62). Twin of bagMembers in go/members.go.
|
|
20
|
+
|
|
21
|
+
import type { Val } from '../type'
|
|
22
|
+
import { AontuContext } from '../ctx'
|
|
23
|
+
import { cmpCodePoint } from '../keyorder'
|
|
24
|
+
import { empty } from './Val'
|
|
25
|
+
import { bagGenable } from './BagVal'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
type Member = { key: string, val: Val }
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
function bagMembers(data: any, ctx: AontuContext): Member[] | undefined {
|
|
32
|
+
if (true !== data?.isMap && true !== data?.isList) {
|
|
33
|
+
return undefined
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const keys: string[] = true === data.isMap ?
|
|
37
|
+
Object.keys(data.peg).sort(cmpCodePoint) :
|
|
38
|
+
(data.peg as Val[]).map((_v: Val, i: number) => '' + i)
|
|
39
|
+
|
|
40
|
+
// A member marked while its bag is not was marked in its own right,
|
|
41
|
+
// and is left out as generation leaves it out. Under a MARKED bag
|
|
42
|
+
// every child carries the mark (hide() and type() mark to the
|
|
43
|
+
// leaves), so there the mark says nothing about the member and every
|
|
44
|
+
// child is one -- the members of a hidden bag are what the bag
|
|
45
|
+
// holds, exactly as a reference to it lifts them.
|
|
46
|
+
const lifted = true === data.mark.hide || true === data.mark.type
|
|
47
|
+
|
|
48
|
+
const out: Member[] = []
|
|
49
|
+
for (const key of keys) {
|
|
50
|
+
const val: any = data.peg[key]
|
|
51
|
+
if ((!lifted && (true === val.mark.hide || true === val.mark.type))
|
|
52
|
+
|| data.aliasKeys.includes(key)) {
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
if (data.optionalKeys.includes(key) && !filled(val, ctx)) {
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
out.push({ key, val })
|
|
59
|
+
}
|
|
60
|
+
return out
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// An optional child is a member when it generates something, decided
|
|
65
|
+
// as BagVal.gen decides it: in an isolated collect context, so residue
|
|
66
|
+
// inside an absent optional subtree is dropped rather than raised.
|
|
67
|
+
function filled(val: any, ctx: AontuContext): boolean {
|
|
68
|
+
if (!bagGenable(val)) {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
const cval = val.gen(ctx.clone({ err: [], collect: true }))
|
|
72
|
+
return undefined !== cval && !empty(cval)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
// The member values alone, for the verbs that do not need the keys.
|
|
77
|
+
function memberVals(data: any, ctx: AontuContext): Val[] | undefined {
|
|
78
|
+
const members = bagMembers(data, ctx)
|
|
79
|
+
return undefined === members ? undefined : members.map((m) => m.val)
|
|
80
|
+
} /* node:coverage ignore next 6 */
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
bagMembers,
|
|
85
|
+
memberVals,
|
|
86
|
+
}
|