aontu 0.53.0 → 0.55.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/dist/aontu.d.ts +3 -2
- package/dist/aontu.js +36 -7
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +500 -8
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +5 -0
- package/dist/ctx.js +1 -0
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js.map +1 -1
- package/dist/err.js +7 -1
- package/dist/err.js.map +1 -1
- package/dist/graph.d.ts +2 -5
- package/dist/graph.js +83 -46
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +9 -10
- package/dist/hcanon.js.map +1 -1
- package/dist/hints.js +94 -26
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +34 -0
- package/dist/jsonschema.js.map +1 -1
- package/dist/lang.js +593 -191
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +86 -6
- package/dist/lsp.js.map +1 -1
- package/dist/mcp.js +153 -6
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +42 -8
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.d.ts +4 -0
- package/dist/mod.js +97 -2
- package/dist/mod.js.map +1 -1
- package/dist/patch.d.ts +5 -0
- package/dist/patch.js +25 -25
- package/dist/patch.js.map +1 -1
- package/dist/provenance.d.ts +1 -0
- package/dist/provenance.js +2 -1
- package/dist/provenance.js.map +1 -1
- package/dist/query.js.map +1 -1
- package/dist/reach.d.ts +1 -0
- package/dist/reach.js +49 -21
- package/dist/reach.js.map +1 -1
- package/dist/relation.d.ts +4 -0
- package/dist/relation.js +125 -200
- package/dist/relation.js.map +1 -1
- 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/std.js +75 -16
- package/dist/std.js.map +1 -1
- package/dist/subsume.js +57 -10
- package/dist/subsume.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.d.ts +2 -2
- package/dist/unify.js +93 -114
- package/dist/unify.js.map +1 -1
- package/dist/utility.d.ts +1 -2
- package/dist/utility.js +7 -61
- package/dist/utility.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +12 -1
- package/dist/val/AggFuncVal.js +165 -3
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/BagVal.d.ts +2 -0
- package/dist/val/BagVal.js +56 -8
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +33 -5
- 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.js +0 -7
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.d.ts +1 -2
- package/dist/val/DisjunctVal.js +146 -39
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +37 -2
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +19 -8
- 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/JunctionVal.js +22 -5
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/ListVal.js +27 -34
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +1 -0
- package/dist/val/MapVal.js +82 -35
- package/dist/val/MapVal.js.map +1 -1
- 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/PlusOpVal.d.ts +2 -1
- package/dist/val/PlusOpVal.js +50 -35
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +2 -0
- package/dist/val/PrefVal.js +159 -32
- 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 +2 -1
- package/dist/val/RefVal.js +105 -72
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.d.ts +30 -7
- package/dist/val/ReferFuncVal.js +395 -94
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.d.ts +4 -2
- package/dist/val/ScalarKindVal.js +12 -1
- 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/Val.d.ts +2 -3
- package/dist/val/Val.js +39 -20
- package/dist/val/Val.js.map +1 -1
- package/dist/val/arith.js +4 -1
- package/dist/val/arith.js.map +1 -1
- package/dist/vet.d.ts +1 -0
- package/dist/vet.js +32 -1
- package/dist/vet.js.map +1 -1
- package/dist/view.d.ts +90 -0
- package/dist/view.js +2168 -0
- package/dist/view.js.map +1 -0
- package/grammar/aontu.gbnf +18 -10
- package/grammar/aontu.lark +15 -10
- package/grammar/aontu.tmLanguage.json +184 -0
- package/package.json +10 -3
- package/skill/grammar-card.md +1 -2
- package/src/aontu.ts +37 -7
- package/src/cli.ts +553 -8
- package/src/ctx.ts +20 -0
- package/src/diff.ts +4 -2
- package/src/err.ts +8 -1
- package/src/graph.ts +125 -75
- package/src/hcanon.ts +9 -11
- package/src/hints.ts +112 -29
- package/src/jsonschema.ts +41 -0
- package/src/lang.ts +642 -203
- package/src/lsp.ts +75 -6
- package/src/mcp.ts +164 -6
- package/src/mod-tool.ts +48 -9
- package/src/mod.ts +110 -1
- package/src/patch.ts +31 -27
- package/src/provenance.ts +8 -1
- package/src/query.ts +4 -2
- package/src/reach.ts +52 -23
- package/src/relation.ts +139 -236
- package/src/sig.ts +345 -0
- package/src/sigdecl.ts +11 -0
- package/src/siggate.ts +144 -0
- package/src/std.ts +77 -16
- package/src/subsume.ts +59 -10
- package/src/unify.ts +102 -125
- package/src/utility.ts +7 -67
- package/src/val/AggFuncVal.ts +231 -4
- package/src/val/BagVal.ts +58 -9
- package/src/val/ConstraintVal.ts +34 -5
- package/src/val/ContainerKindVal.ts +158 -0
- package/src/val/CopyFuncVal.ts +0 -7
- package/src/val/DisjunctVal.ts +152 -40
- package/src/val/ExpectVal.ts +39 -4
- package/src/val/FuncBaseVal.ts +21 -8
- package/src/val/GraphAtomVal.ts +264 -0
- package/src/val/JunctionVal.ts +23 -6
- package/src/val/ListVal.ts +30 -37
- package/src/val/MapVal.ts +87 -39
- package/src/val/PathFuncVal.ts +107 -19
- package/src/val/PathVal.ts +221 -0
- package/src/val/PlusOpVal.ts +56 -37
- package/src/val/PrefVal.ts +186 -38
- package/src/val/RecurseVal.ts +285 -0
- package/src/val/RefVal.ts +105 -83
- package/src/val/ReferFuncVal.ts +445 -100
- package/src/val/ScalarKindVal.ts +12 -0
- package/src/val/SuperFuncVal.ts +137 -13
- package/src/val/TopVal.ts +1 -1
- package/src/val/Val.ts +44 -35
- package/src/val/arith.ts +4 -1
- package/src/vet.ts +41 -4
- package/src/view.ts +2882 -0
- package/dist/val/IdFuncVal.d.ts +0 -13
- package/dist/val/IdFuncVal.js +0 -54
- package/dist/val/IdFuncVal.js.map +0 -1
- package/src/val/IdFuncVal.ts +0 -91
|
@@ -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
|
+
}
|
package/src/val/PlusOpVal.ts
CHANGED
|
@@ -59,6 +59,61 @@ function isBig(k: OpKind): boolean {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
// Only concrete scalar operands are valid: anything else (kinds, maps,
|
|
63
|
+
// lists, null, top, funcs) must not coerce — the JS `+` would leak
|
|
64
|
+
// internals like "[object Object]" into output. A non-scalar operand
|
|
65
|
+
// leaves the op unresolved, which generate() reports.
|
|
66
|
+
//
|
|
67
|
+
// A pref operand contributes its preferred value (`pref(1)+2`), and
|
|
68
|
+
// therefore that value's kind too.
|
|
69
|
+
function operand(v: any) {
|
|
70
|
+
while (v?.isPref) {
|
|
71
|
+
v = v.peg
|
|
72
|
+
}
|
|
73
|
+
return v
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
// The operand's LEAF, not the JavaScript type of its peg: integer and
|
|
78
|
+
// float share `number`, while the exact leaves are told apart from each
|
|
79
|
+
// other and from everything else by their own flags. The whole ladder in
|
|
80
|
+
// `operate` dispatches on this and never on `typeof`.
|
|
81
|
+
function opkind(v: any): OpKind | undefined {
|
|
82
|
+
if (!(v?.isVal && v.isScalar)) {
|
|
83
|
+
return undefined
|
|
84
|
+
}
|
|
85
|
+
if (v.isBigInteger) {
|
|
86
|
+
return 'biginteger'
|
|
87
|
+
}
|
|
88
|
+
if (v.isBigDecimal) {
|
|
89
|
+
return 'bigdecimal'
|
|
90
|
+
}
|
|
91
|
+
if (v.isInteger) {
|
|
92
|
+
return 'integer'
|
|
93
|
+
}
|
|
94
|
+
const t = typeof v.peg
|
|
95
|
+
return 'number' === t ? 'float' :
|
|
96
|
+
'string' === t ? 'string' :
|
|
97
|
+
'boolean' === t ? 'boolean' :
|
|
98
|
+
undefined
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
// THE TEXT `+` WOULD MAKE OF THIS OPERAND, or undefined if `+` would not
|
|
103
|
+
// take it at all.
|
|
104
|
+
//
|
|
105
|
+
// `join` folds with `+` seeded with `""`, so every member goes through
|
|
106
|
+
// concatenation's string branch — and this is the function that branch
|
|
107
|
+
// calls. Exported so that the fold and the operator cannot drift into
|
|
108
|
+
// two answers to "how does a number become text": there is one
|
|
109
|
+
// rendering, `digits` below, and both reach it here.
|
|
110
|
+
function plusText(v: Val): string | undefined {
|
|
111
|
+
const o: any = operand(v)
|
|
112
|
+
const k = opkind(o)
|
|
113
|
+
return undefined === k ? undefined : digits(o, k)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
62
117
|
class PlusOpVal extends OpBaseVal {
|
|
63
118
|
isPlusOp = true
|
|
64
119
|
|
|
@@ -80,43 +135,6 @@ class PlusOpVal extends OpBaseVal {
|
|
|
80
135
|
|
|
81
136
|
|
|
82
137
|
operate(ctx: AontuContext, args: Val[]) {
|
|
83
|
-
// Only concrete scalar operands are valid: anything else (kinds,
|
|
84
|
-
// maps, lists, null, top, funcs) must not coerce — the JS `+` would
|
|
85
|
-
// leak internals like "[object Object]" into output. A non-scalar
|
|
86
|
-
// operand leaves the op unresolved, which generate() reports.
|
|
87
|
-
const operand = (v: any) => {
|
|
88
|
-
// A pref operand contributes its preferred value (`pref(1)+2`),
|
|
89
|
-
// and therefore that value's kind too.
|
|
90
|
-
while (v?.isPref) {
|
|
91
|
-
v = v.peg
|
|
92
|
-
}
|
|
93
|
-
return v
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// The operand's LEAF, not the JavaScript type of its peg: integer
|
|
97
|
-
// and float share `number`, while the exact leaves are told apart
|
|
98
|
-
// from each other and from everything else by their own flags. The
|
|
99
|
-
// whole ladder below dispatches on this and never on `typeof`.
|
|
100
|
-
const opkind = (v: any): OpKind | undefined => {
|
|
101
|
-
if (!(v?.isVal && v.isScalar)) {
|
|
102
|
-
return undefined
|
|
103
|
-
}
|
|
104
|
-
if (v.isBigInteger) {
|
|
105
|
-
return 'biginteger'
|
|
106
|
-
}
|
|
107
|
-
if (v.isBigDecimal) {
|
|
108
|
-
return 'bigdecimal'
|
|
109
|
-
}
|
|
110
|
-
if (v.isInteger) {
|
|
111
|
-
return 'integer'
|
|
112
|
-
}
|
|
113
|
-
const t = typeof v.peg
|
|
114
|
-
return 'number' === t ? 'float' :
|
|
115
|
-
'string' === t ? 'string' :
|
|
116
|
-
'boolean' === t ? 'boolean' :
|
|
117
|
-
undefined
|
|
118
|
-
}
|
|
119
|
-
|
|
120
138
|
const av: any = operand(args[0])
|
|
121
139
|
const bv: any = operand(args[1])
|
|
122
140
|
const ak = opkind(av)
|
|
@@ -251,4 +269,5 @@ function decimal(v: any, k: OpKind): Decimal {
|
|
|
251
269
|
|
|
252
270
|
export {
|
|
253
271
|
PlusOpVal,
|
|
272
|
+
plusText,
|
|
254
273
|
}
|
package/src/val/PrefVal.ts
CHANGED
|
@@ -11,18 +11,20 @@ import {
|
|
|
11
11
|
|
|
12
12
|
import { AontuContext } from '../ctx'
|
|
13
13
|
import { unite } from '../unify'
|
|
14
|
-
import { AontuError } from '../err'
|
|
14
|
+
import { AontuError, makeNilErr } from '../err'
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
17
|
explainOpen,
|
|
18
18
|
ec,
|
|
19
|
-
explainClose
|
|
19
|
+
explainClose
|
|
20
20
|
} from '../utility'
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
import { top } from './top'
|
|
24
24
|
|
|
25
25
|
import { FeatureVal } from './FeatureVal'
|
|
26
|
+
import { trialUnify } from './FuncBaseVal'
|
|
27
|
+
import { superOf } from './SuperFuncVal'
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
// The innermost preferred value under every pref layer: the value a
|
|
@@ -39,6 +41,22 @@ function prefInnerPeg(v: Val): Val {
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
|
|
44
|
+
// A CONTAINER DEFAULT IS LEAFWISE (ADR-011 R3,
|
|
45
|
+
// docs/design/DEFAULTS.0.md): `*{p:1}` MEANS `{p: *1}` and `*[1]`
|
|
46
|
+
// means `[*1]`, so there is no such thing as a preference whose
|
|
47
|
+
// preferred value is a bag. Shape is not a value and takes no star --
|
|
48
|
+
// key optionality, closedness and a `&:` spread template ride through
|
|
49
|
+
// untouched; only the values a reader could override are defaulted.
|
|
50
|
+
//
|
|
51
|
+
// This is the rule `pref()` already had (its resolve walked and
|
|
52
|
+
// wrapped every scalar child) and the star prefix did not, so the two
|
|
53
|
+
// spellings of one operator disagreed: `pref({p:1}) & {q:2}` kept the
|
|
54
|
+
// `p` default and `*{p:1} & {q:2}` dropped it. Both spellings and the
|
|
55
|
+
// resolve-time case (`*$.shape`) come through here now, so they
|
|
56
|
+
// cannot drift apart again. Wrapping the non-bag arm rather than
|
|
57
|
+
// walking for scalars also closes the other half of that gap:
|
|
58
|
+
// `pref(integer)` and `pref(min(3))` used to answer the bare value,
|
|
59
|
+
// losing the preference outright.
|
|
42
60
|
class PrefVal extends FeatureVal {
|
|
43
61
|
isPref = true
|
|
44
62
|
isGenable = true
|
|
@@ -75,6 +93,15 @@ class PrefVal extends FeatureVal {
|
|
|
75
93
|
|
|
76
94
|
rank: number = 0
|
|
77
95
|
|
|
96
|
+
// THE OVERRIDE SPACE, NARROWED (ADR-011 R1). The second arm of the
|
|
97
|
+
// distribution -- `super(x) & every peer met so far` -- kept here as
|
|
98
|
+
// well as in `superpeg`, because resuper() recomputes the gate from
|
|
99
|
+
// the peg whenever the peg resolves and would otherwise widen it
|
|
100
|
+
// back to `super(x)`: a rank>=2 default, whose peg is itself a
|
|
101
|
+
// preference and so is re-driven, lost its narrowing that way and
|
|
102
|
+
// let a pinned `***false & false` be overridden by `true`.
|
|
103
|
+
narrowed?: Val
|
|
104
|
+
|
|
78
105
|
constructor(
|
|
79
106
|
spec: ValSpec,
|
|
80
107
|
ctx?: AontuContext
|
|
@@ -88,14 +115,23 @@ class PrefVal extends FeatureVal {
|
|
|
88
115
|
this.rank = 1 + spec.peg.rank
|
|
89
116
|
}
|
|
90
117
|
|
|
91
|
-
this.resuper()
|
|
118
|
+
this.resuper(ctx)
|
|
92
119
|
// console.log('PVC', this.peg.canon, this.superpeg.canon)
|
|
93
120
|
}
|
|
94
121
|
|
|
95
122
|
|
|
96
123
|
// Recompute the type yardstick and the override gate from the current
|
|
97
124
|
// peg. Called again whenever the peg resolves (e.g. a ref).
|
|
98
|
-
|
|
125
|
+
//
|
|
126
|
+
// THE GATE IS super() (ADR-011 R4, docs/design/DEFAULTS.0.md). `*x`
|
|
127
|
+
// is sugar for `*x | super(x)`, so the type an overriding peer must
|
|
128
|
+
// pass is the one the long form spells out loud -- one function, not
|
|
129
|
+
// a second implementation that agrees with it on the common case.
|
|
130
|
+
// Two special cases retired with the switch: a KIND peg gated
|
|
131
|
+
// nothing (`*integer` was overridden by `"s"`) and a CONSTRAINT peg
|
|
132
|
+
// had no gate at all, both written when a kind's superior was top.
|
|
133
|
+
// `super(integer)` is `number`, so `7` still wins and `"s"` refuses.
|
|
134
|
+
private resuper(ctx?: AontuContext) {
|
|
99
135
|
// THE RANK-UNIFORM MEET (ADR-004). The yardstick is the INNERMOST
|
|
100
136
|
// preferred value's kind, whatever the preference's rank: `**1.5`
|
|
101
137
|
// defends `float` exactly as `*1.5` does. The old rule read the
|
|
@@ -107,25 +143,38 @@ class PrefVal extends FeatureVal {
|
|
|
107
143
|
// rank-1 spelling of the same document kept it. One rule, every
|
|
108
144
|
// rank. Pinned by test/spec/pref.tsv (pref-rank2-* rows, and the
|
|
109
145
|
// flipped pref-nested-concrete-wins).
|
|
146
|
+
// THE RANK-UNIFORM MEET (ADR-004) is unchanged: the yardstick is
|
|
147
|
+
// the INNERMOST preferred value, whatever the rank, so `**1.5`
|
|
148
|
+
// defends `float` exactly as `*1.5` does.
|
|
110
149
|
let peg: any = this.peg
|
|
111
150
|
while (true === peg?.isPref) {
|
|
112
151
|
peg = peg.peg
|
|
113
152
|
}
|
|
114
153
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
154
|
+
const base = superOf(ctx as AontuContext, peg)
|
|
155
|
+
|
|
156
|
+
// A gate that a meet has already narrowed stays narrowed: the
|
|
157
|
+
// override space only ever shrinks.
|
|
158
|
+
this.superpeg = null == this.narrowed ? base
|
|
159
|
+
: unite(ctx as AontuContext, base, this.narrowed,
|
|
160
|
+
'pref-narrow/' + this.id)
|
|
161
|
+
}
|
|
162
|
+
|
|
124
163
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
164
|
+
// The default, standing but NARROWED: `*integer & 7` is `*7`, which
|
|
165
|
+
// is what the long form answers (`(integer&7) | (number&7)` keeps
|
|
166
|
+
// the star on the arm that survived). The rank rides across -- a
|
|
167
|
+
// ladder rung that narrows is still that rung.
|
|
168
|
+
// The rank is REBUILT as nesting rather than stamped: canon renders
|
|
169
|
+
// one star per layer (`'*' + peg.canon`), so a rank set directly on
|
|
170
|
+
// a single layer would print `*x` for a rank-2 default and the
|
|
171
|
+
// document would no longer round-trip.
|
|
172
|
+
private restand(met: Val, ctx: AontuContext): Val {
|
|
173
|
+
let out: Val = met
|
|
174
|
+
for (let rI = 0; rI <= this.rank; rI++) {
|
|
175
|
+
out = new PrefVal({ peg: out }, ctx)
|
|
176
|
+
}
|
|
177
|
+
return this.place(out)
|
|
129
178
|
}
|
|
130
179
|
|
|
131
180
|
|
|
@@ -143,9 +192,27 @@ class PrefVal extends FeatureVal {
|
|
|
143
192
|
this.peg, top(), 'pref/resolve')
|
|
144
193
|
// console.log('PREF-RESOLVED', this.peg.canon, '->', resolved)
|
|
145
194
|
this.peg = resolved
|
|
146
|
-
this.resuper()
|
|
195
|
+
this.resuper(ctx)
|
|
147
196
|
}
|
|
148
197
|
|
|
198
|
+
// A CONTAINER DEFAULT IS LEAFWISE IN EFFECT (ADR-011 R3), and it
|
|
199
|
+
// gets there through the meet above rather than through a rewrite.
|
|
200
|
+
// `*{p:1}` keeps its written shape -- canon prints `*{"p":1}` and
|
|
201
|
+
// reparses to itself, which a rewrite to `{p:*1}` would break, and
|
|
202
|
+
// an alternative still has a star for a disjunction to choose by.
|
|
203
|
+
// What makes it leafwise is that the two arms decide:
|
|
204
|
+
//
|
|
205
|
+
// `& {q:2}` the preferred value ITSELF admits the peer (maps
|
|
206
|
+
// merge), so the default stands as `*{p:1,q:2}` and
|
|
207
|
+
// `p` survives -- it used to be REPLACED outright.
|
|
208
|
+
// `& {p:2}` the preferred value refuses, so the gate answers:
|
|
209
|
+
// `super({p:1})` is `{p:integer}`, which admits it.
|
|
210
|
+
// `& "s"` both arms are empty, so the whole default is --
|
|
211
|
+
// a string used to override a map default silently.
|
|
212
|
+
//
|
|
213
|
+
// Which is every rule R3 asks for, with no bag ever standing where
|
|
214
|
+
// the author wrote a scalar and no canon that fails to round-trip.
|
|
215
|
+
|
|
149
216
|
if (peer instanceof PrefVal) {
|
|
150
217
|
why += 'pref-'
|
|
151
218
|
if (this.id === peer.id) {
|
|
@@ -173,9 +240,20 @@ class PrefVal extends FeatureVal {
|
|
|
173
240
|
// peer.peg.id, peer.peg, peer.peg.done,
|
|
174
241
|
// )
|
|
175
242
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
243
|
+
const peg = trialUnify(ctx, prefInnerPeg(this).clone(ctx),
|
|
244
|
+
prefInnerPeg(peer))
|
|
245
|
+
|
|
246
|
+
// TWO DEFAULTS OF EQUAL RANK THAT CANNOT AGREE (ADR-011 R2):
|
|
247
|
+
// the refusal is about the DEFAULTS, not about the values they
|
|
248
|
+
// happen to hold, and its hint names the fix -- rank one of
|
|
249
|
+
// them. Compatible pegs still fold (`*1 & *integer` is `*1`),
|
|
250
|
+
// and identical ones collapse, so only a real disagreement
|
|
251
|
+
// reaches this arm. The three spellings of it -- `*1 & *7`,
|
|
252
|
+
// `(*1|integer) & *7` and `*1|*7` -- used to answer a value
|
|
253
|
+
// conflict, the newcomer, and the newcomer again.
|
|
254
|
+
out = undefined === peg
|
|
255
|
+
? makeNilErr(ctx, 'pref_rank_clash', this, peer, 'unify')
|
|
256
|
+
: this.restand(peg, ctx)
|
|
179
257
|
// console.log('PREF-RANK-SAME-OUT', peg, peg.done, out, out.done)
|
|
180
258
|
why += 'rank-same'
|
|
181
259
|
}
|
|
@@ -183,25 +261,84 @@ class PrefVal extends FeatureVal {
|
|
|
183
261
|
else if (!peer.isTop) {
|
|
184
262
|
why += 'super-'
|
|
185
263
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
// that
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
264
|
+
// THE MEET IS THE DESUGARING, DISTRIBUTED (ADR-011 R1,
|
|
265
|
+
// docs/design/DEFAULTS.0.md). `*x` stands for `*x | super(x)`,
|
|
266
|
+
// and a peer meets a disjunction arm by arm:
|
|
267
|
+
//
|
|
268
|
+
// (x & peer) | (super(x) & peer)
|
|
269
|
+
//
|
|
270
|
+
// THE FIRST ARM DECIDES. When the preferred value itself still
|
|
271
|
+
// satisfies the peer the default STANDS -- `*1 & integer`,
|
|
272
|
+
// `*8080 & min(1024)`, `**2 & neq(1)`: the peer narrowed the
|
|
273
|
+
// type without ruling the default out, which is the whole point
|
|
274
|
+
// of writing one. Only when that arm is empty does the second
|
|
275
|
+
// answer, and that is the override. When BOTH are empty nothing
|
|
276
|
+
// remains of the disjunction the star stands for -- `empty`,
|
|
277
|
+
// the same refusal the written-out long form gives.
|
|
278
|
+
//
|
|
279
|
+
// The old rule asked instead whether the peer resolved to
|
|
280
|
+
// exactly `super(x)`, so any narrowing at all counted as an
|
|
281
|
+
// override: `*1 & integer` stood (the peer WAS the gate) but
|
|
282
|
+
// `*8080 & min(1024)` silently dropped the default and answered
|
|
283
|
+
// the bare constraint, and a rank ladder lost its weaker arm to
|
|
284
|
+
// the same rule the moment anything narrowed it.
|
|
285
|
+
//
|
|
286
|
+
// Trialled against a CLONE, on the innermost value (the
|
|
287
|
+
// rank-uniform meet): the preferred value must stay pristine for
|
|
288
|
+
// the arm that stands, and a failed trial must not leave its
|
|
289
|
+
// errors on the context -- the DisjunctVal admission gate's own
|
|
290
|
+
// precedent, and its mechanism.
|
|
291
|
+
const met = trialUnify(ctx, prefInnerPeg(this).clone(ctx), peer)
|
|
292
|
+
|
|
293
|
+
if (undefined !== met) {
|
|
294
|
+
// THE SECOND ARM IS CARRIED FORWARD, not discarded. It is the
|
|
295
|
+
// override space -- everything the peer would still admit
|
|
296
|
+
// INSTEAD of the default -- and meeting a peer narrows it just
|
|
297
|
+
// as it narrows the default: two successive meets compose to
|
|
298
|
+
// `(x & p1 & p2) | (super(x) & p1 & p2)`, which is the
|
|
299
|
+
// distribution over both. Dropping it let a constraint that
|
|
300
|
+
// arrived beside a default vanish: `r:*2` with `r:max(20)`
|
|
301
|
+
// stood as a bare `*2`, and `r:40` then overrode it through a
|
|
302
|
+
// gate that no longer remembered the bound.
|
|
303
|
+
//
|
|
304
|
+
// It cannot fail: the first arm succeeded, so `x & peer` has a
|
|
305
|
+
// value, and that value satisfies `super(x)` and `peer` both.
|
|
306
|
+
const gate = unite(te ? ctx.clone({ explain: ec(te, 'GATE') }) : ctx,
|
|
307
|
+
this.superpeg.clone(ctx), peer, 'pref-gate/' + this.id)
|
|
308
|
+
|
|
309
|
+
// Unchanged on both counts is the SAME preference, returned as
|
|
310
|
+
// itself: minting a new one every pass would keep the fixpoint
|
|
311
|
+
// moving for ever.
|
|
312
|
+
if (met.same(prefInnerPeg(this)) && gate.same(this.superpeg)) {
|
|
313
|
+
out = this
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
const stood = this.restand(met, ctx) as PrefVal
|
|
317
|
+
stood.narrowed = gate
|
|
318
|
+
stood.superpeg = gate
|
|
319
|
+
out = stood
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
why += 'stands'
|
|
323
|
+
explainClose(te, out)
|
|
324
|
+
out.dc = DONE
|
|
325
|
+
return out
|
|
203
326
|
}
|
|
204
327
|
|
|
328
|
+
// The override arm is trialled too: its failure is not the
|
|
329
|
+
// answer, it is half of the reason the answer is `empty`, and a
|
|
330
|
+
// recorded `no_scalar_unify` would be the code the reader sees
|
|
331
|
+
// however the refusal is relabelled afterwards.
|
|
332
|
+
const over = trialUnify(ctx, this.superpeg.clone(ctx), peer)
|
|
333
|
+
|
|
334
|
+
out = undefined !== over ? over
|
|
335
|
+
// A peer that arrived already failed keeps its own refusal:
|
|
336
|
+
// that is its failure, not the default's.
|
|
337
|
+
: peer.isNil ? peer
|
|
338
|
+
: makeNilErr(ctx, 'empty', this, peer, 'unify')
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
205
342
|
// }
|
|
206
343
|
}
|
|
207
344
|
else {
|
|
@@ -234,6 +371,17 @@ class PrefVal extends FeatureVal {
|
|
|
234
371
|
|
|
235
372
|
clone(ctx: AontuContext, spec?: ValSpec): Val {
|
|
236
373
|
let out = (super.clone(ctx, spec) as PrefVal)
|
|
374
|
+
|
|
375
|
+
// THE NARROWED OVERRIDE SPACE TRAVELS WITH THE COPY (ADR-011 R1).
|
|
376
|
+
// A default a meet has pinned stays pinned through a reference:
|
|
377
|
+
// without this, `T:{e:***false & false}` copied by `f:$.T` handed
|
|
378
|
+
// back a default whose gate had widened again to `boolean`, and
|
|
379
|
+
// `f:{e:true}` overrode a value the author had pinned. The Go
|
|
380
|
+
// twin carries the same two fields explicitly (clone.go).
|
|
381
|
+
if (null != this.narrowed) {
|
|
382
|
+
out.narrowed = this.narrowed
|
|
383
|
+
out.superpeg = this.superpeg
|
|
384
|
+
}
|
|
237
385
|
// THE PER-DESTINATION INSTANTIATION RULE (ADR-005). The default
|
|
238
386
|
// clone shares the preferred value (`peg: this.peg` in Val.clone)
|
|
239
387
|
// — a cloned pref spread template resolves its inner value at the
|