aontu 0.52.1 → 0.54.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 +88 -0
- package/bin/aontu-mcp.js +4 -0
- package/dist/agentsmd.d.ts +16 -0
- package/dist/agentsmd.js +107 -0
- package/dist/agentsmd.js.map +1 -0
- package/dist/aontu.d.ts +15 -3
- package/dist/aontu.js +126 -5
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +45 -1
- package/dist/cli.js +2845 -69
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +18 -0
- package/dist/ctx.js +44 -0
- package/dist/ctx.js.map +1 -1
- package/dist/diff.d.ts +22 -0
- package/dist/diff.js +141 -0
- package/dist/diff.js.map +1 -0
- package/dist/err.d.ts +3 -1
- package/dist/err.js +45 -8
- package/dist/err.js.map +1 -1
- package/dist/graph.d.ts +13 -0
- package/dist/graph.js +110 -0
- package/dist/graph.js.map +1 -0
- package/dist/hcanon.d.ts +3 -0
- package/dist/hcanon.js +145 -0
- package/dist/hcanon.js.map +1 -0
- package/dist/hints.js +235 -8
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.d.ts +20 -0
- package/dist/jsonschema.js +425 -0
- package/dist/jsonschema.js.map +1 -0
- package/dist/lang.js +955 -110
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +9 -2
- package/dist/lsp.js +343 -48
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.d.ts +20 -0
- package/dist/mcp-server.js +147 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp.d.ts +42 -0
- package/dist/mcp.js +941 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mod-tool.d.ts +58 -0
- package/dist/mod-tool.js +532 -0
- package/dist/mod-tool.js.map +1 -0
- package/dist/mod.d.ts +35 -0
- package/dist/mod.js +345 -0
- package/dist/mod.js.map +1 -0
- package/dist/patch.d.ts +49 -0
- package/dist/patch.js +506 -0
- package/dist/patch.js.map +1 -0
- package/dist/provenance.d.ts +41 -0
- package/dist/provenance.js +336 -0
- package/dist/provenance.js.map +1 -0
- package/dist/query.d.ts +27 -0
- package/dist/query.js +294 -0
- package/dist/query.js.map +1 -0
- package/dist/reach.d.ts +15 -0
- package/dist/reach.js +168 -0
- package/dist/reach.js.map +1 -0
- package/dist/relation.d.ts +23 -0
- package/dist/relation.js +230 -0
- package/dist/relation.js.map +1 -0
- package/dist/report-sarif.d.ts +14 -0
- package/dist/report-sarif.js +102 -0
- package/dist/report-sarif.js.map +1 -0
- package/dist/sig.d.ts +25 -0
- package/dist/sig.js +277 -0
- package/dist/sig.js.map +1 -0
- package/dist/sigdecl.d.ts +2 -0
- package/dist/sigdecl.js +11 -0
- package/dist/sigdecl.js.map +1 -0
- package/dist/siggate.d.ts +4 -0
- package/dist/siggate.js +90 -0
- package/dist/siggate.js.map +1 -0
- package/dist/site.d.ts +4 -0
- package/dist/site.js +31 -0
- package/dist/site.js.map +1 -1
- package/dist/std.d.ts +1 -0
- package/dist/std.js +129 -0
- package/dist/std.js.map +1 -0
- package/dist/subsume.d.ts +39 -0
- package/dist/subsume.js +573 -0
- package/dist/subsume.js.map +1 -0
- package/dist/trim.d.ts +19 -0
- package/dist/trim.js +155 -0
- package/dist/trim.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +17 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.d.ts +2 -1
- package/dist/unify.js +234 -38
- package/dist/unify.js.map +1 -1
- package/dist/utility.d.ts +8 -1
- package/dist/utility.js +68 -1
- package/dist/utility.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +44 -0
- package/dist/val/AggFuncVal.js +364 -0
- package/dist/val/AggFuncVal.js.map +1 -0
- package/dist/val/ArithFuncVal.d.ts +31 -0
- package/dist/val/ArithFuncVal.js +62 -0
- package/dist/val/ArithFuncVal.js.map +1 -0
- package/dist/val/BagVal.d.ts +7 -0
- package/dist/val/BagVal.js +150 -11
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +9 -1
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.d.ts +1 -1
- package/dist/val/ConjunctVal.js +19 -0
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.d.ts +7 -1
- package/dist/val/ConstraintVal.js +523 -45
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.d.ts +35 -0
- package/dist/val/ContainerKindVal.js +99 -0
- package/dist/val/ContainerKindVal.js.map +1 -0
- package/dist/val/CopyFuncVal.d.ts +1 -2
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.d.ts +1 -0
- package/dist/val/Decimal.js +13 -0
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.d.ts +11 -0
- package/dist/val/DeprecateFuncVal.js +47 -0
- package/dist/val/DeprecateFuncVal.js.map +1 -0
- package/dist/val/DisjunctVal.d.ts +1 -2
- package/dist/val/DisjunctVal.js +269 -53
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.d.ts +15 -0
- package/dist/val/EachFuncVal.js +75 -0
- package/dist/val/EachFuncVal.js.map +1 -0
- package/dist/val/ExpectVal.js +62 -4
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FeatureVal.js +1 -1
- package/dist/val/FeatureVal.js.map +1 -1
- package/dist/val/FilterFuncVal.d.ts +15 -0
- package/dist/val/FilterFuncVal.js +91 -0
- package/dist/val/FilterFuncVal.js.map +1 -0
- package/dist/val/FuncBaseVal.d.ts +7 -1
- package/dist/val/FuncBaseVal.js +183 -2
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.d.ts +39 -0
- package/dist/val/GraphAtomVal.js +184 -0
- package/dist/val/GraphAtomVal.js.map +1 -0
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/JunctionVal.js +24 -1
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.d.ts +1 -1
- package/dist/val/KeyFuncVal.js +38 -30
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +94 -14
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +1 -0
- package/dist/val/MapVal.js +146 -14
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.d.ts +15 -0
- package/dist/val/MatchFuncVal.js +107 -0
- package/dist/val/MatchFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +24 -0
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -1
- package/dist/val/OpBaseVal.js +19 -1
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/OpenFuncVal.js +4 -1
- package/dist/val/OpenFuncVal.js.map +1 -1
- package/dist/val/PackFuncVal.d.ts +15 -0
- package/dist/val/PackFuncVal.js +108 -0
- package/dist/val/PackFuncVal.js.map +1 -0
- package/dist/val/PathFuncVal.d.ts +2 -2
- package/dist/val/PathFuncVal.js +75 -16
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.d.ts +25 -0
- package/dist/val/PathVal.js +150 -0
- package/dist/val/PathVal.js.map +1 -0
- package/dist/val/PlaceVal.d.ts +13 -0
- package/dist/val/PlaceVal.js +131 -0
- package/dist/val/PlaceVal.js.map +1 -0
- package/dist/val/PlusOpVal.d.ts +2 -1
- package/dist/val/PlusOpVal.js +61 -37
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +4 -2
- package/dist/val/PrefVal.js +223 -41
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.d.ts +19 -0
- package/dist/val/RecurseVal.js +217 -0
- package/dist/val/RecurseVal.js.map +1 -0
- package/dist/val/RefVal.d.ts +3 -2
- package/dist/val/RefVal.js +181 -66
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.d.ts +59 -0
- package/dist/val/ReferFuncVal.js +604 -0
- package/dist/val/ReferFuncVal.js.map +1 -0
- package/dist/val/ScalarKindVal.d.ts +4 -3
- package/dist/val/ScalarKindVal.js +12 -12
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/SuperFuncVal.d.ts +4 -2
- package/dist/val/SuperFuncVal.js +118 -14
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.d.ts +1 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TypeFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +7 -1
- package/dist/val/Val.js +170 -2
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.d.ts +6 -0
- package/dist/val/arith.js +173 -0
- package/dist/val/arith.js.map +1 -0
- package/dist/vet.d.ts +45 -0
- package/dist/vet.js +802 -0
- package/dist/vet.js.map +1 -0
- package/dist/view.d.ts +85 -0
- package/dist/view.js +1948 -0
- package/dist/view.js.map +1 -0
- package/dist/walk.d.ts +2 -0
- package/dist/walk.js +91 -0
- package/dist/walk.js.map +1 -0
- package/grammar/aontu.gbnf +138 -0
- package/grammar/aontu.lark +118 -0
- package/grammar/aontu.tmLanguage.json +184 -0
- package/package.json +30 -8
- package/skill/SKILL.md +37 -0
- package/skill/error-codes.md +62 -0
- package/skill/examples.md +99 -0
- package/skill/grammar-card.md +56 -0
- package/src/agentsmd.ts +135 -0
- package/src/aontu.ts +166 -5
- package/src/cli.ts +3324 -72
- package/src/ctx.ts +93 -1
- package/src/diff.ts +198 -0
- package/src/err.ts +50 -8
- package/src/graph.ts +185 -0
- package/src/hcanon.ts +167 -0
- package/src/hints.ts +291 -8
- package/src/jsonschema.ts +552 -0
- package/src/lang.ts +1049 -110
- package/src/lsp.ts +351 -49
- package/src/mcp-server.ts +187 -0
- package/src/mcp.ts +1130 -0
- package/src/mod-tool.ts +718 -0
- package/src/mod.ts +453 -0
- package/src/patch.ts +628 -0
- package/src/provenance.ts +437 -0
- package/src/query.ts +381 -0
- package/src/reach.ts +213 -0
- package/src/relation.ts +298 -0
- package/src/report-sarif.ts +137 -0
- package/src/sig.ts +345 -0
- package/src/sigdecl.ts +11 -0
- package/src/siggate.ts +144 -0
- package/src/site.ts +36 -1
- package/src/std.ts +131 -0
- package/src/subsume.ts +739 -0
- package/src/trim.ts +195 -0
- package/src/tsconfig.json +10 -4
- package/src/type.ts +51 -2
- package/src/unify.ts +253 -38
- package/src/utility.ts +79 -1
- package/src/val/AggFuncVal.ts +546 -0
- package/src/val/ArithFuncVal.ts +108 -0
- package/src/val/BagVal.ts +158 -12
- package/src/val/CloseFuncVal.ts +9 -1
- package/src/val/ConjunctVal.ts +20 -0
- package/src/val/ConstraintVal.ts +576 -48
- package/src/val/ContainerKindVal.ts +158 -0
- package/src/val/CopyFuncVal.ts +0 -1
- package/src/val/Decimal.ts +15 -0
- package/src/val/DeprecateFuncVal.ts +84 -0
- package/src/val/DisjunctVal.ts +282 -59
- package/src/val/EachFuncVal.ts +133 -0
- package/src/val/ExpectVal.ts +65 -8
- package/src/val/FeatureVal.ts +1 -1
- package/src/val/FilterFuncVal.ts +154 -0
- package/src/val/FuncBaseVal.ts +206 -2
- package/src/val/GraphAtomVal.ts +264 -0
- package/src/val/HideFuncVal.ts +0 -2
- package/src/val/JunctionVal.ts +24 -1
- package/src/val/KeyFuncVal.ts +39 -35
- package/src/val/ListVal.ts +101 -15
- package/src/val/LowerFuncVal.ts +0 -1
- package/src/val/MapVal.ts +153 -14
- package/src/val/MatchFuncVal.ts +176 -0
- package/src/val/MoveFuncVal.ts +0 -2
- package/src/val/NilVal.ts +25 -0
- package/src/val/OpBaseVal.ts +20 -1
- package/src/val/OpenFuncVal.ts +4 -2
- package/src/val/PackFuncVal.ts +175 -0
- package/src/val/PathFuncVal.ts +107 -20
- package/src/val/PathVal.ts +221 -0
- package/src/val/PlaceVal.ts +193 -0
- package/src/val/PlusOpVal.ts +67 -39
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +251 -60
- package/src/val/RecurseVal.ts +285 -0
- package/src/val/RefVal.ts +183 -78
- package/src/val/ReferFuncVal.ts +732 -0
- package/src/val/ScalarKindVal.ts +12 -13
- package/src/val/SuperFuncVal.ts +137 -13
- package/src/val/TopVal.ts +1 -2
- package/src/val/TypeFuncVal.ts +0 -2
- package/src/val/UpperFuncVal.ts +0 -1
- package/src/val/Val.ts +216 -4
- package/src/val/VarVal.ts +0 -1
- package/src/val/arith.ts +319 -0
- package/src/vet.ts +1025 -0
- package/src/view.ts +2563 -0
- package/src/walk.ts +99 -0
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
// GENERATION: `pack(data, tmpl)` (G8 phase 1,
|
|
4
|
+
// docs/capability-review/g8-generation.md). One child of the result map
|
|
5
|
+
// per child of `data`, each of them `tmpl` cloned at the destination.
|
|
6
|
+
//
|
|
7
|
+
// names: [web, auth, billing]
|
|
8
|
+
// deploy: pack($.names, {
|
|
9
|
+
// image: "acme/" + key() + ":1.4.2"
|
|
10
|
+
// replicas: *2 | integer
|
|
11
|
+
// })
|
|
12
|
+
//
|
|
13
|
+
// WHY A FUNCTION AND NOT SYNTAX. A comprehension would need keywords,
|
|
14
|
+
// a scoping rule, and string interpolation for the computed name; the
|
|
15
|
+
// language already has functions, spread templates, and `key()`, and
|
|
16
|
+
// `pack` is those three composed. A call is also DATA -- a named node
|
|
17
|
+
// in the tree -- so a document that generates stays as analysable as
|
|
18
|
+
// one that does not.
|
|
19
|
+
//
|
|
20
|
+
// KEYS ARE DATA, NOT POSITION. For a list, the strings themselves are
|
|
21
|
+
// the keys (a non-string element is refused: `pack_key`); for a map,
|
|
22
|
+
// its keys. Terraform's `count` keyed by index is the counter-example
|
|
23
|
+
// the review names: reordering the data churns every downstream child.
|
|
24
|
+
// Duplicate keys are not an error -- the colliding children unify,
|
|
25
|
+
// exactly as duplicate source keys already merge.
|
|
26
|
+
//
|
|
27
|
+
// TOTALITY. `pack` iterates a finite, settled bag and cannot call
|
|
28
|
+
// itself: the number of children it can produce is fixed by data that
|
|
29
|
+
// already exists. Nothing here can recurse, which is the guarantee the
|
|
30
|
+
// combinators exist to keep.
|
|
31
|
+
|
|
32
|
+
import type {
|
|
33
|
+
Val,
|
|
34
|
+
ValSpec,
|
|
35
|
+
} from '../type'
|
|
36
|
+
|
|
37
|
+
import {
|
|
38
|
+
AontuContext,
|
|
39
|
+
} from '../ctx'
|
|
40
|
+
|
|
41
|
+
import { unite } from '../unify'
|
|
42
|
+
import { makeNilErr } from '../err'
|
|
43
|
+
import { MapVal } from './MapVal'
|
|
44
|
+
import { FuncBaseVal } from './FuncBaseVal'
|
|
45
|
+
import { repathInstance } from './Val'
|
|
46
|
+
import { fillPlace } from './PlaceVal'
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
// The keys a data bag names, in the order the result must carry them,
|
|
50
|
+
// or a code naming what is wrong with it. Shared with `each`, which
|
|
51
|
+
// asks the same question of the same argument and answers it with the
|
|
52
|
+
// values rather than the keys.
|
|
53
|
+
function dataKeys(data: Val | undefined): string[] | string {
|
|
54
|
+
const d: any = data
|
|
55
|
+
|
|
56
|
+
if (true === d?.isMap) {
|
|
57
|
+
return Object.keys(d.peg)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (true === d?.isList) {
|
|
61
|
+
const out: string[] = []
|
|
62
|
+
for (const el of d.peg as Val[]) {
|
|
63
|
+
const e: any = el
|
|
64
|
+
// A key is a NAME, and only a string is one. A number would
|
|
65
|
+
// key by position under another spelling, which is the failure
|
|
66
|
+
// mode the data-keyed rule exists to refuse.
|
|
67
|
+
if (true !== e?.isScalar || 'string' !== typeof e.peg) {
|
|
68
|
+
return 'pack_key'
|
|
69
|
+
}
|
|
70
|
+
out.push(e.peg)
|
|
71
|
+
}
|
|
72
|
+
return out
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return 'pack_data'
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class PackFuncVal extends FuncBaseVal {
|
|
80
|
+
isPackFunc = true
|
|
81
|
+
|
|
82
|
+
// THE STAGING RULE (G8 phase 0, see AontuContext.settle). A
|
|
83
|
+
// generator's data argument is not settled merely by being `done`
|
|
84
|
+
// once: a sibling conjunct, an include or a spread can still merge
|
|
85
|
+
// keys into it, and children generated from the half-merged bag
|
|
86
|
+
// would be missing. It fires on the settle pass and only then.
|
|
87
|
+
staged = true
|
|
88
|
+
|
|
89
|
+
constructor(
|
|
90
|
+
spec: ValSpec,
|
|
91
|
+
ctx?: AontuContext
|
|
92
|
+
) {
|
|
93
|
+
super(spec, ctx)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
funcname() {
|
|
99
|
+
return 'pack'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
// The TEMPLATE IS NOT AN ARGUMENT TO DRIVE. Driving it would resolve
|
|
104
|
+
// its `key()` at the CALL SITE -- the one position the template is
|
|
105
|
+
// never used at -- and freeze whatever else in it is path-dependent
|
|
106
|
+
// there too. The data argument is driven by `unify` below instead,
|
|
107
|
+
// one argument by hand rather than all of them by the base.
|
|
108
|
+
prepare(_ctx: AontuContext, _args: Val[]) {
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
unify(peer: Val, ctx: AontuContext): Val {
|
|
114
|
+
// ONE argument is driven: the data. The template is not (see
|
|
115
|
+
// prepare above), and driveStagedArgs answers whether the data has
|
|
116
|
+
// settled -- the other half of "ready to fire".
|
|
117
|
+
const ready = this.driveStagedArgs(ctx, 1)
|
|
118
|
+
|
|
119
|
+
if (!ready || !ctx.settle) {
|
|
120
|
+
return this.residuate(peer, ctx)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return super.unify(peer, ctx)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
resolve(ctx: AontuContext, args: Val[]) {
|
|
128
|
+
const keys = dataKeys(args?.[0])
|
|
129
|
+
if ('string' === typeof keys) {
|
|
130
|
+
return makeNilErr(ctx, keys, this)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Arity is checked at parse (funcArity), so both arguments are here.
|
|
134
|
+
const tmpl: Val = args[1]
|
|
135
|
+
const peg: Record<string, Val> = {}
|
|
136
|
+
|
|
137
|
+
const src: any = args[0]
|
|
138
|
+
|
|
139
|
+
for (const key of keys) {
|
|
140
|
+
const keyctx = ctx.descend(key)
|
|
141
|
+
// THE PLACEHOLDER BINDS THE SOURCE CHILD (G8 phase 3): inside a
|
|
142
|
+
// generator's template `_` is the datum this child is being made
|
|
143
|
+
// FROM. For a map that is the child's value; for a list of names
|
|
144
|
+
// it is the name, which is also the key -- so `_` and `key()`
|
|
145
|
+
// agree there, and differ the moment the data is a map.
|
|
146
|
+
const source: Val = true === src?.isMap ? src.peg[key] : src.peg[keys.indexOf(key)]
|
|
147
|
+
// CLONED, never shared. A spread may share a template that holds
|
|
148
|
+
// nothing path-dependent (MapVal.spreadClone), because a spread
|
|
149
|
+
// CONSTRAINS a child that exists; a generator's template IS the
|
|
150
|
+
// child, and a child is a position. Sharing left every generated
|
|
151
|
+
// child pointing at the template's own parse-time location, which
|
|
152
|
+
// is the position the template is never used at -- visible as the
|
|
153
|
+
// site an error inside a generated child reports.
|
|
154
|
+
//
|
|
155
|
+
// A FULL INSTANCE, to the leaves (`dup`, ADR-005): a bare clone
|
|
156
|
+
// shares the inner structure of any call, preference or
|
|
157
|
+
// operation in the template, so the first child's resolution of
|
|
158
|
+
// a shared key()/ref answered for every child (BUGS.md §8, §9).
|
|
159
|
+
const inst = tmpl.clone(keyctx, { dup: true })
|
|
160
|
+
repathInstance(inst, inst.path)
|
|
161
|
+
const child = fillPlace(inst, source, keyctx)
|
|
162
|
+
peg[key] = undefined === peg[key] ? child :
|
|
163
|
+
unite(keyctx, peg[key], child, 'pack')
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return new MapVal({ peg }, ctx)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
} /* node:coverage ignore next 7 */
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
export {
|
|
173
|
+
dataKeys,
|
|
174
|
+
PackFuncVal,
|
|
175
|
+
}
|
package/src/val/PathFuncVal.ts
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
/* Copyright (c) 2021-2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
+
// THE PATH CONSTRUCTOR (docs/design/PATHS.0.md). `path(p)` CAPTURES
|
|
4
|
+
// `p` -- the one non-strict argument position in the language: every
|
|
5
|
+
// other call reads its argument's value, this one reads its spelling.
|
|
6
|
+
// `path($.a.b)` is the address `$.a.b` as a first-class value, not
|
|
7
|
+
// the value found there; a plain reference (`$.a.b` alone) stays the
|
|
8
|
+
// embedding it has always been.
|
|
9
|
+
//
|
|
10
|
+
// The capture happens in prepare(), which receives the argument
|
|
11
|
+
// BEFORE the driving loop resolves it -- the same interception this
|
|
12
|
+
// function always used, now kept instead of released: the RefVal's
|
|
13
|
+
// spelling is read off its segments and the RefVal itself is never
|
|
14
|
+
// driven, so no pass ever holds a resolvable reference here.
|
|
15
|
+
//
|
|
16
|
+
// `path()` with no argument is the path KIND (PathKindVal): the
|
|
17
|
+
// vacuous constructor call is the kind, as `{}`-the-unit is to maps
|
|
18
|
+
// -- see ContainerKindVal for the other half of that convention.
|
|
19
|
+
//
|
|
20
|
+
// A string argument is read by the ADDRESS grammar, not resolved as a
|
|
21
|
+
// segment: `path("$.a")` is the same capture `path($.a)` is, and text
|
|
22
|
+
// with no anchor is RELATIVE (`path("a.b")` is `path(.a.b)`, the
|
|
23
|
+
// address the raw spelling captures). A COMPUTED argument -- an
|
|
24
|
+
// expression, a reference to a string -- is the one shape that does
|
|
25
|
+
// evaluate: the driven result converts by the same grammar at
|
|
26
|
+
// resolve, which is what makes an address buildable (`refer() &
|
|
27
|
+
// path("$.customers." + key())`) while a bare string still never IS
|
|
28
|
+
// one (ADR-016): the conversion happens only inside this call.
|
|
3
29
|
|
|
4
30
|
import type {
|
|
5
31
|
Val,
|
|
@@ -12,9 +38,41 @@ import {
|
|
|
12
38
|
|
|
13
39
|
import { makeNilErr } from '../err'
|
|
14
40
|
|
|
15
|
-
import { NilVal } from '../val/NilVal'
|
|
16
|
-
import { RefVal } from '../val/RefVal'
|
|
17
41
|
import { FuncBaseVal } from './FuncBaseVal'
|
|
42
|
+
import { PathVal, PathKindVal, parseAddress, textAddress } from './PathVal'
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
// The address a reference SPELLS, or undefined when its segments
|
|
46
|
+
// cannot spell one (a variable segment, a parent step after the first
|
|
47
|
+
// named segment). Leading `.` entries in a relative ref's peg are
|
|
48
|
+
// parent steps; the spelling is the same grammar refer reads, so one
|
|
49
|
+
// address parser stays the single gate.
|
|
50
|
+
function captureSpelling(rv: any): string | undefined {
|
|
51
|
+
const parts: string[] = []
|
|
52
|
+
let up = 0
|
|
53
|
+
let lead = true
|
|
54
|
+
for (const p of rv.peg) {
|
|
55
|
+
if ('string' !== typeof p) {
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
if ('.' === p) {
|
|
59
|
+
if (!lead) {
|
|
60
|
+
return undefined
|
|
61
|
+
}
|
|
62
|
+
up++
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
lead = false
|
|
66
|
+
parts.push(p)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (0 === parts.length || (rv.absolute && 0 < up)) {
|
|
70
|
+
return undefined
|
|
71
|
+
}
|
|
72
|
+
return rv.absolute ?
|
|
73
|
+
'$.' + parts.join('.') :
|
|
74
|
+
'.'.repeat(up + 1) + parts.join('.')
|
|
75
|
+
}
|
|
18
76
|
|
|
19
77
|
|
|
20
78
|
class PathFuncVal extends FuncBaseVal {
|
|
@@ -42,37 +100,66 @@ class PathFuncVal extends FuncBaseVal {
|
|
|
42
100
|
|
|
43
101
|
|
|
44
102
|
prepare(ctx: AontuContext, args: Val[]) {
|
|
45
|
-
let arg = args[0]
|
|
46
|
-
|
|
47
103
|
if (0 === this.prepared) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//
|
|
104
|
+
this.prepared++
|
|
105
|
+
|
|
106
|
+
const arg: any = args[0]
|
|
107
|
+
|
|
108
|
+
// The kind form: no argument to capture.
|
|
53
109
|
if (null == arg) {
|
|
54
|
-
|
|
110
|
+
return []
|
|
55
111
|
}
|
|
56
|
-
|
|
57
|
-
|
|
112
|
+
|
|
113
|
+
// The captured spelling, from a reference's segments or from a
|
|
114
|
+
// string literal read as address text. Both go through
|
|
115
|
+
// parseAddress, so what capture admits and what refer reads
|
|
116
|
+
// cannot drift. Anything else -- an expression, a reference to
|
|
117
|
+
// a string -- is left for the driving loop, and resolve
|
|
118
|
+
// converts the driven result below.
|
|
119
|
+
let spelling: string | undefined
|
|
120
|
+
if (true === arg.isRef) {
|
|
121
|
+
spelling = captureSpelling(arg)
|
|
58
122
|
}
|
|
59
|
-
else if (
|
|
60
|
-
|
|
123
|
+
else if (true === arg.isScalar && 'string' === typeof arg.peg) {
|
|
124
|
+
spelling = textAddress(arg.peg)
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
return args
|
|
61
128
|
}
|
|
62
|
-
}
|
|
63
129
|
|
|
64
|
-
|
|
130
|
+
if (undefined === spelling || undefined === parseAddress(spelling)) {
|
|
131
|
+
return [makeNilErr(ctx, 'path_address', this, arg)]
|
|
132
|
+
}
|
|
65
133
|
|
|
66
|
-
|
|
134
|
+
return [new PathVal({ peg: spelling }, ctx)]
|
|
135
|
+
}
|
|
67
136
|
|
|
68
137
|
return args
|
|
69
138
|
}
|
|
70
139
|
|
|
71
140
|
|
|
72
141
|
resolve(ctx: AontuContext, args: Val[]) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
142
|
+
if (0 === args.length) {
|
|
143
|
+
const out = new PathKindVal({}, ctx)
|
|
144
|
+
out.site = this.site
|
|
145
|
+
out.path = this.path
|
|
146
|
+
return out
|
|
147
|
+
}
|
|
148
|
+
const arg: any = args[0]
|
|
149
|
+
if (true === arg.isPath || true === arg.isNil) {
|
|
150
|
+
return arg
|
|
151
|
+
}
|
|
152
|
+
// The COMPUTED argument, driven by the loop above: a string
|
|
153
|
+
// converts by the address grammar, exactly as a literal does at
|
|
154
|
+
// capture; anything else was never a path expression.
|
|
155
|
+
if (true === arg.isScalar && 'string' === typeof arg.peg) {
|
|
156
|
+
const spelling = textAddress(arg.peg)
|
|
157
|
+
if (undefined === parseAddress(spelling)) {
|
|
158
|
+
return makeNilErr(ctx, 'path_address', this, arg)
|
|
159
|
+
}
|
|
160
|
+
return new PathVal({ peg: spelling }, ctx)
|
|
161
|
+
}
|
|
162
|
+
return makeNilErr(ctx, 'invalid-arg', this)
|
|
76
163
|
}
|
|
77
164
|
|
|
78
165
|
} /* node:coverage ignore next 6 */
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
// FIRST-CLASS PATHS (docs/design/PATHS.0.md). A PathVal is the value
|
|
4
|
+
// `path(p)` captures: a tree address as DATA -- the spelling, never
|
|
5
|
+
// the resolution. It is a scalar whose peg is the address string in
|
|
6
|
+
// exactly the grammar `refer` reads (`$.a.b` from the root, `.b` from
|
|
7
|
+
// the sibling scope, one more leading dot per parent step), which is
|
|
8
|
+
// what lets a captured path meet the checking machinery unchanged.
|
|
9
|
+
//
|
|
10
|
+
// Meets are SYNTACTIC, by the PREFIX rule (amended, ADR-016): two
|
|
11
|
+
// path values meet when one spells a prefix of the other -- same
|
|
12
|
+
// anchor, the shorter's segments opening the longer's -- and the
|
|
13
|
+
// result is the LONGER: a path can always be told more precisely.
|
|
14
|
+
// Incomparable spellings refuse as any two unequal scalars do.
|
|
15
|
+
// Resolving during a meet would make the meet depend on the value's
|
|
16
|
+
// position, which is the property the staging machinery exists to
|
|
17
|
+
// quarantine -- resolution stays the business of `refer`, `rel` and
|
|
18
|
+
// the graph.
|
|
19
|
+
//
|
|
20
|
+
// The kind sits UNDER string (ScalarKindVal.KIND_PARENT), so `string`
|
|
21
|
+
// admits a path value and the string constraints keep working; a
|
|
22
|
+
// plain string LITERAL and a path value refuse each other, exactly as
|
|
23
|
+
// the number tower's leaves do. A bare string is NEVER a path
|
|
24
|
+
// (ADR-016): `path("...")` -- the call's own string argument -- is
|
|
25
|
+
// the one conversion the language has.
|
|
26
|
+
|
|
27
|
+
import type {
|
|
28
|
+
Val,
|
|
29
|
+
ValSpec,
|
|
30
|
+
} from '../type'
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
AontuContext,
|
|
34
|
+
} from '../ctx'
|
|
35
|
+
|
|
36
|
+
import { makeNilErr } from '../err'
|
|
37
|
+
|
|
38
|
+
import { propagateMarks } from '../utility'
|
|
39
|
+
|
|
40
|
+
import { ScalarVal } from './ScalarVal'
|
|
41
|
+
import { ScalarKindVal, Path } from './ScalarKindVal'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
// A segment of a tree path: a map key or a list index. The same
|
|
46
|
+
// grammar the rest of the engine spells keys with, and a leading digit
|
|
47
|
+
// is legitimate because a list index is one.
|
|
48
|
+
const ADDR_SEGMENT = /^[A-Za-z0-9_-]+$/
|
|
49
|
+
|
|
50
|
+
export type Address = {
|
|
51
|
+
// Anchored at the document root (`$.a.b`) rather than at the link's
|
|
52
|
+
// own position (`.a.b`).
|
|
53
|
+
absolute: boolean
|
|
54
|
+
// Parent steps, for a relative address that climbs (`..a` is one).
|
|
55
|
+
up: number
|
|
56
|
+
// The written segments, below the anchor.
|
|
57
|
+
parts: string[]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
// The address a string spells, or undefined when it does not spell
|
|
62
|
+
// one. An address is a TREE PATH, in exactly the two spellings a
|
|
63
|
+
// reference uses: `$.services.auth` from the root, `.auth` from the
|
|
64
|
+
// link's own sibling scope. The tree is the only namespace -- which is
|
|
65
|
+
// what makes a model instantiable more than once, each instance
|
|
66
|
+
// resolving its relative links inside itself (ADR-014).
|
|
67
|
+
export function parseAddress(s: string): Address | undefined {
|
|
68
|
+
if ('$' === s) {
|
|
69
|
+
// The whole document is not a relation's target: an address must
|
|
70
|
+
// name something with a position to be written back into.
|
|
71
|
+
return undefined
|
|
72
|
+
}
|
|
73
|
+
if (s.startsWith('$.')) {
|
|
74
|
+
const parts = s.slice(2).split('.')
|
|
75
|
+
for (const seg of parts) {
|
|
76
|
+
if (!ADDR_SEGMENT.test(seg)) {
|
|
77
|
+
return undefined
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { absolute: true, up: 0, parts }
|
|
81
|
+
}
|
|
82
|
+
if (!s.startsWith('.')) {
|
|
83
|
+
return undefined
|
|
84
|
+
}
|
|
85
|
+
// A relative address: the leading dot anchors it at the sibling
|
|
86
|
+
// scope, and every FURTHER leading dot is one step up from there --
|
|
87
|
+
// the same reduction a relative reference's `.` segments perform.
|
|
88
|
+
let up = 0
|
|
89
|
+
let rest = s.slice(1)
|
|
90
|
+
while (rest.startsWith('.')) {
|
|
91
|
+
up++
|
|
92
|
+
rest = rest.slice(1)
|
|
93
|
+
}
|
|
94
|
+
if ('' === rest) {
|
|
95
|
+
return undefined
|
|
96
|
+
}
|
|
97
|
+
const parts = rest.split('.')
|
|
98
|
+
for (const seg of parts) {
|
|
99
|
+
if (!ADDR_SEGMENT.test(seg)) {
|
|
100
|
+
return undefined
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { absolute: false, up, parts }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
// The spelling string TEXT converts by, inside a `path(...)` call:
|
|
108
|
+
// text that carries no anchor is RELATIVE (`"a.b"` is the address
|
|
109
|
+
// `.a.b`), matching the raw form (`path(a.b)` captures `.a.b`). Only
|
|
110
|
+
// the anchor is supplied -- the result still has to parse, so
|
|
111
|
+
// malformed text (`""`, `"a..b"`, a bad `$` spelling) refuses as
|
|
112
|
+
// before. The prefix is not applied to text that claims an anchor:
|
|
113
|
+
// `"$x"` is a broken absolute address, not a relative one.
|
|
114
|
+
export function textAddress(s: string): string {
|
|
115
|
+
return ('$' === s[0] || '.' === s[0]) ? s : '.' + s
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
// The LONGER of two addresses when one spells a prefix of the other
|
|
120
|
+
// (docs/design/PATHS.0.md, amended): same anchor -- absolute or the
|
|
121
|
+
// same number of parent steps -- and the shorter's segments open the
|
|
122
|
+
// longer's. The meet of two path values, and of a refer's address
|
|
123
|
+
// with a later path peer: a path can always be told more precisely,
|
|
124
|
+
// and the more precise spelling is the result. Undefined when the two
|
|
125
|
+
// are not comparable, which refuses as any two unequal scalars do.
|
|
126
|
+
// Both arguments must already be valid addresses: every caller hands
|
|
127
|
+
// over a PathVal peg or a refer addrsrc, and both are validated at
|
|
128
|
+
// capture or conversion -- the same trust `unify`'s own address arm
|
|
129
|
+
// extends (`parseAddress(p.peg) as Address`).
|
|
130
|
+
export function prefixMeet(a: string, b: string): string | undefined {
|
|
131
|
+
const pa = parseAddress(a) as Address
|
|
132
|
+
const pb = parseAddress(b) as Address
|
|
133
|
+
if (pa.absolute !== pb.absolute || pa.up !== pb.up) {
|
|
134
|
+
return undefined
|
|
135
|
+
}
|
|
136
|
+
const short = pa.parts.length <= pb.parts.length ? pa : pb
|
|
137
|
+
const long = short === pa ? pb : pa
|
|
138
|
+
for (let i = 0; i < short.parts.length; i++) {
|
|
139
|
+
if (short.parts[i] !== long.parts[i]) {
|
|
140
|
+
return undefined
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return short === pa ? b : a
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class PathVal extends ScalarVal {
|
|
149
|
+
isPath = true
|
|
150
|
+
|
|
151
|
+
constructor(
|
|
152
|
+
spec: ValSpec,
|
|
153
|
+
ctx?: AontuContext
|
|
154
|
+
) {
|
|
155
|
+
super({ peg: spec.peg, kind: Path }, ctx)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Two path values meet by the PREFIX rule (ADR-016): the longer
|
|
159
|
+
// when one opens the other, refusal otherwise. Exactly equal pegs
|
|
160
|
+
// are absorbed by unite's fast path before this runs, so the arm
|
|
161
|
+
// sees the unequal pairs; the winner carries both sides' marks, as
|
|
162
|
+
// the equal-scalar arm has always ratcheted them.
|
|
163
|
+
unify(peer: Val, ctx: AontuContext): Val {
|
|
164
|
+
const p: any = peer
|
|
165
|
+
if (true === p.isPath) {
|
|
166
|
+
const merged = prefixMeet(this.peg, p.peg)
|
|
167
|
+
if (undefined === merged) {
|
|
168
|
+
return makeNilErr(ctx, 'scalar_value', this, peer)
|
|
169
|
+
}
|
|
170
|
+
const out = merged === this.peg ? this : p
|
|
171
|
+
const other = out === this ? p : this
|
|
172
|
+
propagateMarks(other, out)
|
|
173
|
+
return out
|
|
174
|
+
}
|
|
175
|
+
return super.unify(peer, ctx)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Reparses to the same VALUE: the call form is the literal syntax
|
|
179
|
+
// for this kind, so canon renders it back. The peg is already the
|
|
180
|
+
// address grammar, which the argument grammar also accepts.
|
|
181
|
+
get canon() {
|
|
182
|
+
return 'path(' + this.peg + ')'
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// The super() ladder lifts a path value to its own kind, and the
|
|
186
|
+
// kind must render as `path()` -- the bare word `path` is an
|
|
187
|
+
// ordinary string. ScalarVal.superior would mint the plain
|
|
188
|
+
// ScalarKindVal, whose canon is the bare word.
|
|
189
|
+
superior() {
|
|
190
|
+
return this.place(new PathKindVal({}))
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
} /* node:coverage ignore next 4 */
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
// The path KIND, `path()`: admits every path value and defaults to
|
|
197
|
+
// nothing, as `string` does. It does NOT promote (ADR-016): a bare
|
|
198
|
+
// string meeting the kind refuses through the generic kind ladder,
|
|
199
|
+
// exactly as `integer & "x"` does -- `path("...")` is the one string
|
|
200
|
+
// conversion, and it happens at the call.
|
|
201
|
+
class PathKindVal extends ScalarKindVal {
|
|
202
|
+
isPathKind = true
|
|
203
|
+
|
|
204
|
+
constructor(
|
|
205
|
+
spec: ValSpec,
|
|
206
|
+
ctx?: AontuContext
|
|
207
|
+
) {
|
|
208
|
+
super({ ...spec, peg: Path }, ctx)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
get canon() {
|
|
212
|
+
return 'path()'
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
} /* node:coverage ignore next 6 */
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
export {
|
|
219
|
+
PathVal,
|
|
220
|
+
PathKindVal,
|
|
221
|
+
}
|