aontu 0.52.1 → 0.53.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 +14 -3
- package/dist/aontu.js +96 -4
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +44 -1
- package/dist/cli.js +2401 -44
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +13 -0
- package/dist/ctx.js +43 -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 +38 -7
- package/dist/err.js.map +1 -1
- package/dist/graph.d.ts +16 -0
- package/dist/graph.js +73 -0
- package/dist/graph.js.map +1 -0
- package/dist/hcanon.d.ts +3 -0
- package/dist/hcanon.js +146 -0
- package/dist/hcanon.js.map +1 -0
- package/dist/hints.js +167 -4
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.d.ts +20 -0
- package/dist/jsonschema.js +391 -0
- package/dist/jsonschema.js.map +1 -0
- package/dist/lang.js +512 -69
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +9 -2
- package/dist/lsp.js +262 -47
- 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 +814 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mod-tool.d.ts +58 -0
- package/dist/mod-tool.js +498 -0
- package/dist/mod-tool.js.map +1 -0
- package/dist/mod.d.ts +31 -0
- package/dist/mod.js +250 -0
- package/dist/mod.js.map +1 -0
- package/dist/patch.d.ts +44 -0
- package/dist/patch.js +506 -0
- package/dist/patch.js.map +1 -0
- package/dist/provenance.d.ts +40 -0
- package/dist/provenance.js +335 -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 +14 -0
- package/dist/reach.js +140 -0
- package/dist/reach.js.map +1 -0
- package/dist/relation.d.ts +19 -0
- package/dist/relation.js +305 -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/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 +73 -0
- package/dist/std.js.map +1 -0
- package/dist/subsume.d.ts +39 -0
- package/dist/subsume.js +526 -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 +253 -36
- package/dist/unify.js.map +1 -1
- package/dist/utility.d.ts +9 -1
- package/dist/utility.js +122 -1
- package/dist/utility.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +33 -0
- package/dist/val/AggFuncVal.js +202 -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 +5 -0
- package/dist/val/BagVal.js +96 -5
- 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 +490 -40
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/CopyFuncVal.d.ts +1 -2
- package/dist/val/CopyFuncVal.js +7 -0
- 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.js +130 -21
- 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 +27 -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 +6 -1
- package/dist/val/FuncBaseVal.js +171 -1
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IdFuncVal.d.ts +13 -0
- package/dist/val/IdFuncVal.js +54 -0
- package/dist/val/IdFuncVal.js.map +1 -0
- package/dist/val/JunctionVal.js +7 -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 +101 -14
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +93 -8
- 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.js.map +1 -1
- 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.js +11 -2
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +2 -2
- package/dist/val/PrefVal.js +78 -23
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RefVal.d.ts +1 -1
- package/dist/val/RefVal.js +89 -7
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.d.ts +36 -0
- package/dist/val/ReferFuncVal.js +303 -0
- package/dist/val/ReferFuncVal.js.map +1 -0
- package/dist/val/ScalarKindVal.d.ts +1 -2
- package/dist/val/ScalarKindVal.js +0 -11
- package/dist/val/ScalarKindVal.js.map +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 +8 -1
- package/dist/val/Val.js +150 -1
- 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 +170 -0
- package/dist/val/arith.js.map +1 -0
- package/dist/vet.d.ts +45 -0
- package/dist/vet.js +776 -0
- package/dist/vet.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 +130 -0
- package/grammar/aontu.lark +113 -0
- package/package.json +21 -6
- package/skill/SKILL.md +37 -0
- package/skill/error-codes.md +62 -0
- package/skill/examples.md +99 -0
- package/skill/grammar-card.md +57 -0
- package/src/agentsmd.ts +135 -0
- package/src/aontu.ts +135 -4
- package/src/cli.ts +2858 -71
- package/src/ctx.ts +73 -1
- package/src/diff.ts +196 -0
- package/src/err.ts +42 -7
- package/src/graph.ts +135 -0
- package/src/hcanon.ts +169 -0
- package/src/hints.ts +208 -4
- package/src/jsonschema.ts +511 -0
- package/src/lang.ts +570 -70
- package/src/lsp.ts +281 -48
- package/src/mcp-server.ts +187 -0
- package/src/mcp.ts +993 -0
- package/src/mod-tool.ts +679 -0
- package/src/mod.ts +344 -0
- package/src/patch.ts +624 -0
- package/src/provenance.ts +430 -0
- package/src/query.ts +379 -0
- package/src/reach.ts +184 -0
- package/src/relation.ts +395 -0
- package/src/report-sarif.ts +137 -0
- package/src/site.ts +36 -1
- package/src/std.ts +73 -0
- package/src/subsume.ts +690 -0
- package/src/trim.ts +195 -0
- package/src/tsconfig.json +10 -4
- package/src/type.ts +51 -2
- package/src/unify.ts +274 -36
- package/src/utility.ts +139 -1
- package/src/val/AggFuncVal.ts +319 -0
- package/src/val/ArithFuncVal.ts +108 -0
- package/src/val/BagVal.ts +101 -4
- package/src/val/CloseFuncVal.ts +9 -1
- package/src/val/ConjunctVal.ts +20 -0
- package/src/val/ConstraintVal.ts +542 -43
- package/src/val/CopyFuncVal.ts +7 -1
- package/src/val/Decimal.ts +15 -0
- package/src/val/DeprecateFuncVal.ts +84 -0
- package/src/val/DisjunctVal.ts +139 -28
- package/src/val/EachFuncVal.ts +133 -0
- package/src/val/ExpectVal.ts +28 -6
- package/src/val/FeatureVal.ts +1 -1
- package/src/val/FilterFuncVal.ts +154 -0
- package/src/val/FuncBaseVal.ts +192 -1
- package/src/val/HideFuncVal.ts +0 -2
- package/src/val/IdFuncVal.ts +91 -0
- package/src/val/JunctionVal.ts +7 -1
- package/src/val/KeyFuncVal.ts +39 -35
- package/src/val/ListVal.ts +108 -15
- package/src/val/LowerFuncVal.ts +0 -1
- package/src/val/MapVal.ts +99 -8
- 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 +0 -1
- package/src/val/PlaceVal.ts +193 -0
- package/src/val/PlusOpVal.ts +11 -2
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +79 -36
- package/src/val/RefVal.ts +91 -8
- package/src/val/ReferFuncVal.ts +387 -0
- package/src/val/ScalarKindVal.ts +0 -13
- package/src/val/TopVal.ts +0 -1
- package/src/val/TypeFuncVal.ts +0 -2
- package/src/val/UpperFuncVal.ts +0 -1
- package/src/val/Val.ts +205 -2
- package/src/val/VarVal.ts +0 -1
- package/src/val/arith.ts +316 -0
- package/src/vet.ts +992 -0
- package/src/walk.ts +99 -0
package/src/val/Val.ts
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
Site
|
|
9
9
|
} from '../site'
|
|
10
10
|
|
|
11
|
+
import { INNER_OF, WRITTEN } from '../provenance'
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
type ValMark = {
|
|
13
15
|
type: boolean,
|
|
@@ -22,6 +24,21 @@ type ValSpec = {
|
|
|
22
24
|
mark?: Partial<ValMark>,
|
|
23
25
|
kind?: any,
|
|
24
26
|
|
|
27
|
+
// THE PER-DESTINATION INSTANTIATION FLAG (ADR-005). A generator's
|
|
28
|
+
// template is cloned once per destination, and every clone must be
|
|
29
|
+
// a full instance: nothing path-dependent may be shared between two
|
|
30
|
+
// destinations, or the first destination's resolution answers for
|
|
31
|
+
// them all. The default clone shares inner structure deliberately
|
|
32
|
+
// (`peg: this.peg` below — the move()/copy() ghost rows in
|
|
33
|
+
// test/spec/func.tsv pin that sharing), so instantiation asks for
|
|
34
|
+
// depth explicitly: `dup: true` makes FuncBaseVal, PrefVal and
|
|
35
|
+
// OpBaseVal clone their inner Vals too, and the bag/junction clones
|
|
36
|
+
// carry the flag down. Set by pack/each template instantiation,
|
|
37
|
+
// filter condition testing, and spread application (MapVal/
|
|
38
|
+
// ListVal.spreadClone) — never by the residuation or ref-resolution
|
|
39
|
+
// clones, whose sharing is pinned behaviour.
|
|
40
|
+
dup?: boolean,
|
|
41
|
+
|
|
25
42
|
|
|
26
43
|
row?: number,
|
|
27
44
|
col?: number,
|
|
@@ -63,6 +80,17 @@ const EMPTY_ERR: any[] = Object.freeze([]) as unknown as any[]
|
|
|
63
80
|
let ID = 1000
|
|
64
81
|
|
|
65
82
|
|
|
83
|
+
// A fresh Val id, for the one carrier that cannot take the one its
|
|
84
|
+
// class fixes: TopVal pins `id = 0` (there is only one top), and the
|
|
85
|
+
// identity mark (G4 phase 1) resolves to a top that must NOT collide
|
|
86
|
+
// with it — the fast path in `unite` returns early on two done Vals
|
|
87
|
+
// with the same id, which would drop an identity before the rider
|
|
88
|
+
// could carry it.
|
|
89
|
+
export function nextValId(): number {
|
|
90
|
+
return ID++
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
66
94
|
abstract class Val {
|
|
67
95
|
// Type-discriminator flags: defaults live on Val.prototype (see
|
|
68
96
|
// bottom of this file). Each subclass overrides only its own
|
|
@@ -134,6 +162,37 @@ abstract class Val {
|
|
|
134
162
|
hide: false,
|
|
135
163
|
}
|
|
136
164
|
|
|
165
|
+
// The deprecation record (G3 phase 4, `deprecate(x, m)`): boolean
|
|
166
|
+
// marks cannot hold a message, a replacement path and a version, so
|
|
167
|
+
// the Val carries one optional record. Keys are the three the
|
|
168
|
+
// builtin defines (msg, use, since), all optional, values strings;
|
|
169
|
+
// `use` is a path spelled as a STRING — a live reference would
|
|
170
|
+
// resolve and unify, which is not wanted. Propagated through meets
|
|
171
|
+
// by propagateMarks and carried by clone, exactly as the boolean
|
|
172
|
+
// marks are.
|
|
173
|
+
deprecation?: Record<string, string>
|
|
174
|
+
|
|
175
|
+
// The IDENTITY (G4 phase 1, `id(name)`): the entity this value IS.
|
|
176
|
+
// A separate slot for the same reason the deprecation record has
|
|
177
|
+
// one — a boolean ValMark cannot hold a name — and carried through
|
|
178
|
+
// meets by the same rider in `unite`. Unlike the marks, canon
|
|
179
|
+
// RENDERS it: identity is semantic content, and G6's hash must see
|
|
180
|
+
// it.
|
|
181
|
+
entity?: string
|
|
182
|
+
|
|
183
|
+
// The LINK (G4 phase 2/3): the entity address a `refer` resolved to,
|
|
184
|
+
// stamped on the string it answers. The string IS the value — a
|
|
185
|
+
// link, not an embedding — so nothing downstream could otherwise
|
|
186
|
+
// tell a checked link from a literal that happens to look like one,
|
|
187
|
+
// and the edge set (ts/src/graph.ts) is exactly the set of these.
|
|
188
|
+
link?: string
|
|
189
|
+
|
|
190
|
+
// The GRAPH of an evaluated document (G4 phase 3): the entity index
|
|
191
|
+
// and the edge set, stamped on the result by Aontu.unify the way the
|
|
192
|
+
// include manifest is. Absent on every Val that is not a unify
|
|
193
|
+
// result.
|
|
194
|
+
graph?: any
|
|
195
|
+
|
|
137
196
|
// Actual native value.
|
|
138
197
|
peg: any = undefined
|
|
139
198
|
|
|
@@ -225,11 +284,71 @@ abstract class Val {
|
|
|
225
284
|
out.site.row = spec?.row ?? this.site.row
|
|
226
285
|
out.site.col = spec?.col ?? this.site.col
|
|
227
286
|
out.site.url = spec?.url ?? this.site.url
|
|
287
|
+
// THE SPAN TRAVELS WITH THE POSITION. Copying row and col but not
|
|
288
|
+
// the extent would leave a site that names a place and denies it has
|
|
289
|
+
// any width — internally inconsistent, and it made the two ports
|
|
290
|
+
// disagree on every derived value (the shared subsume rows caught
|
|
291
|
+
// it). Safe because the span is VERIFIABLE: a consumer reads the
|
|
292
|
+
// document at (row, col, len) and refuses when it does not match
|
|
293
|
+
// `src`, so a span that has stopped describing its value is
|
|
294
|
+
// detectable rather than believed. See ts/src/site.ts.
|
|
295
|
+
//
|
|
296
|
+
// Read from `this.site`, never from the spec: ValSpec.src is a
|
|
297
|
+
// DIFFERENT field — ScalarVal's literal spelling, kept so `$.a.0x0`
|
|
298
|
+
// addresses the key `0x0` — and reading it here would put a path
|
|
299
|
+
// segment where a source span belongs.
|
|
300
|
+
//
|
|
301
|
+
// UNCONDITIONAL, as the Go twin is (clonePath, go/clone.go). A
|
|
302
|
+
// guard dropping the span when the spec relocates the value was
|
|
303
|
+
// written first and the coverage gate refused it as dead: nothing
|
|
304
|
+
// clones to a new row or column. Should a relocating caller ever
|
|
305
|
+
// appear it must drop both fields — an extent belongs to a place,
|
|
306
|
+
// and the text at a new one is not this value's to claim.
|
|
307
|
+
out.site.len = this.site.len
|
|
308
|
+
out.site.src = this.site.src
|
|
228
309
|
|
|
229
310
|
out.mark = Object.assign({}, this.mark, fullspec.mark ?? {})
|
|
230
311
|
out.mark.type = this.mark.type && (fullspec.mark?.type ?? true)
|
|
231
312
|
out.mark.hide = this.mark.hide && (fullspec.mark?.hide ?? true)
|
|
232
313
|
|
|
314
|
+
// The two IDENTITY riders travel together, under one test: the
|
|
315
|
+
// entity a value IS, and the address a resolved link POINTS AT.
|
|
316
|
+
// One guard rather than two because the pair is what a clone
|
|
317
|
+
// either carries or does not — and because a second test for the
|
|
318
|
+
// link alone would be a branch no document takes, the reference
|
|
319
|
+
// clone catching the pending residual before it ever resolves.
|
|
320
|
+
if (null != this.entity || null != this.link) {
|
|
321
|
+
out.entity = this.entity
|
|
322
|
+
out.link = this.link
|
|
323
|
+
}
|
|
324
|
+
if (null != this.deprecation) {
|
|
325
|
+
out.deprecation = this.deprecation
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// PROVENANCE TRAVELS WITH THE CLONE, exactly as the site does, and
|
|
329
|
+
// for the same reason: a clone of a value the author wrote IS that
|
|
330
|
+
// written value somewhere else, and it carries the author's site,
|
|
331
|
+
// so it can be pointed at. Without this a default reaching a
|
|
332
|
+
// `pack()`-generated child, or a shape carried by a `$ref`, was
|
|
333
|
+
// invisible to `why` -- which answered "nothing met at this path"
|
|
334
|
+
// over a value it had just printed (the review's finding E). See
|
|
335
|
+
// WRITTEN in ts/src/provenance.ts; the mark is only ever set by an
|
|
336
|
+
// instrumented run, so this is one undefined read otherwise.
|
|
337
|
+
if (true === (this as any)[WRITTEN]) {
|
|
338
|
+
(out as any)[WRITTEN] = true
|
|
339
|
+
}
|
|
340
|
+
// AND SO DOES BEING PART OF SOMETHING. A clone of a disjunction's
|
|
341
|
+
// member is still that member of that written disjunction, and the
|
|
342
|
+
// whole statement is what the author needs shown -- otherwise a
|
|
343
|
+
// default reaching a generated child reports `*"info"` and
|
|
344
|
+
// `string` as two contributions at two columns, where the author
|
|
345
|
+
// wrote `*info | string` once. A number, deliberately: a Val
|
|
346
|
+
// holding another Val as an own property is a cycle through the
|
|
347
|
+
// tree. See INNER_OF in ts/src/provenance.ts.
|
|
348
|
+
if (null != (this as any)[INNER_OF]) {
|
|
349
|
+
(out as any)[INNER_OF] = (this as any)[INNER_OF]
|
|
350
|
+
}
|
|
351
|
+
|
|
233
352
|
return out
|
|
234
353
|
}
|
|
235
354
|
|
|
@@ -237,8 +356,17 @@ abstract class Val {
|
|
|
237
356
|
// Shallow clone for spread constraints: creates a new Val with the
|
|
238
357
|
// correct path context but shares non-path-dependent children.
|
|
239
358
|
// Override in MapVal/ListVal to avoid deep-cloning simple children.
|
|
359
|
+
//
|
|
360
|
+
// A FULL INSTANCE (`dup`, ADR-005): a spread constraint is applied
|
|
361
|
+
// once per destination child, and each application must own its
|
|
362
|
+
// path-dependent innards — a bare clone shared a call's arguments
|
|
363
|
+
// and a preference's inner value across destinations, so a spread
|
|
364
|
+
// like `&: id(key(0)) & $.schema.C` resolved its one shared key()
|
|
365
|
+
// at the first child it met (use-cases/BUGS.md §12's id_name form).
|
|
240
366
|
spreadClone(ctx: AontuContext): Val {
|
|
241
|
-
|
|
367
|
+
const out = this.clone(ctx, { dup: true })
|
|
368
|
+
repathInstance(out, out.path)
|
|
369
|
+
return out
|
|
242
370
|
}
|
|
243
371
|
|
|
244
372
|
|
|
@@ -278,10 +406,26 @@ abstract class Val {
|
|
|
278
406
|
}
|
|
279
407
|
|
|
280
408
|
|
|
409
|
+
// PUT A MINTED VALUE WHERE THIS ONE STANDS: the site travels, and so
|
|
410
|
+
// does provenance, because the two answer one question. A narrowed
|
|
411
|
+
// disjunction, a lifted kind, a resolved reference -- each is a
|
|
412
|
+
// value the engine built from a value the author wrote, standing
|
|
413
|
+
// where that one stood. Carrying the site and withholding the mark
|
|
414
|
+
// would let `why` print a value, know the line it came from, and
|
|
415
|
+
// still answer "nothing met at this path" (the review's finding E).
|
|
416
|
+
// See WRITTEN and INNER_OF in ts/src/provenance.ts.
|
|
281
417
|
place(v: Val) {
|
|
282
418
|
v.site.row = this.site.row
|
|
283
419
|
v.site.col = this.site.col
|
|
284
420
|
v.site.url = this.site.url
|
|
421
|
+
v.site.len = this.site.len
|
|
422
|
+
v.site.src = this.site.src
|
|
423
|
+
if (true === (this as any)[WRITTEN]) {
|
|
424
|
+
(v as any)[WRITTEN] = true
|
|
425
|
+
}
|
|
426
|
+
if (null != (this as any)[INNER_OF]) {
|
|
427
|
+
(v as any)[INNER_OF] = (this as any)[INNER_OF]
|
|
428
|
+
}
|
|
285
429
|
return v
|
|
286
430
|
}
|
|
287
431
|
|
|
@@ -431,6 +575,64 @@ Object.assign(Val.prototype, {
|
|
|
431
575
|
})
|
|
432
576
|
|
|
433
577
|
|
|
578
|
+
// THE INSTANCE PATH NORMALISATION (ADR-005), the TS mirror of the Go
|
|
579
|
+
// port's setPaths (go/clone.go): assign every value in a freshly
|
|
580
|
+
// instantiated template the path the PARSER would have given it at the
|
|
581
|
+
// instance's destination. A deep instance clone (`dup`) copies values
|
|
582
|
+
// whose stored parse paths are argument-shaped — a func argument has
|
|
583
|
+
// no key of its own, a spread template lives under a '&' segment — and
|
|
584
|
+
// Val.clone's ctx-cut cannot rebase those: it derives the child path
|
|
585
|
+
// from the driving ctx alone and drops the segments in between, which
|
|
586
|
+
// is how a nested list spread inside a close()d template lost its
|
|
587
|
+
// parent key and every finding under it named the wrong path (the
|
|
588
|
+
// 06-k8s use case's env findings). One canonical walk instead:
|
|
589
|
+
// bag children descend by key (numeric for a list element, as the
|
|
590
|
+
// parser records them), a spread constraint sits under '&' with its
|
|
591
|
+
// content at the bag's own path, and junction members, operator
|
|
592
|
+
// operands, function arguments and a preference's value all sit AT
|
|
593
|
+
// their holder's path — exactly the parse-time shape.
|
|
594
|
+
function repathInstance(v: any, path: string[]): void {
|
|
595
|
+
if (true !== v?.isVal) {
|
|
596
|
+
return
|
|
597
|
+
}
|
|
598
|
+
v.path = path
|
|
599
|
+
|
|
600
|
+
const peg = v.peg
|
|
601
|
+
|
|
602
|
+
if (true === v.isBag) {
|
|
603
|
+
const spread = v.spread?.cj
|
|
604
|
+
if (null != spread && true === spread.isVal) {
|
|
605
|
+
// The spread's CONTENT is pathed at the bag (its fields land on
|
|
606
|
+
// the bag's children); only its ROOT carries the '&' segment —
|
|
607
|
+
// the same two steps as the Go twin's setPaths.
|
|
608
|
+
repathInstance(spread, path)
|
|
609
|
+
spread.path = [...path, '&']
|
|
610
|
+
}
|
|
611
|
+
if (true === v.isList) {
|
|
612
|
+
for (let i = 0; i < peg.length; i++) {
|
|
613
|
+
// Numeric, as the parser records list positions: a numeric
|
|
614
|
+
// segment is what tells key() an element is not a keyed
|
|
615
|
+
// position (KeyFuncVal.resolve, `positioned`).
|
|
616
|
+
repathInstance(peg[i], [...path, i as unknown as string])
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
for (const k of Object.keys(peg)) {
|
|
621
|
+
repathInstance(peg[k], [...path, k])
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
else if (Array.isArray(peg)) {
|
|
626
|
+
for (const t of peg) {
|
|
627
|
+
repathInstance(t, path)
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
else if (true === peg?.isVal) {
|
|
631
|
+
repathInstance(peg, path)
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
|
|
434
636
|
function inspectpeg(peg: any, d: number) {
|
|
435
637
|
const indent = ' '.repeat(d)
|
|
436
638
|
return pretty(Array.isArray(peg) ?
|
|
@@ -474,5 +676,6 @@ export {
|
|
|
474
676
|
DONE,
|
|
475
677
|
SPREAD,
|
|
476
678
|
EMPTY_ERR,
|
|
477
|
-
empty
|
|
679
|
+
empty,
|
|
680
|
+
repathInstance,
|
|
478
681
|
}
|
package/src/val/VarVal.ts
CHANGED
package/src/val/arith.ts
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
// THE ARITHMETIC FAMILY -- add, sub, mul, div, mod, rem.
|
|
5
|
+
//
|
|
6
|
+
// The review's finding I: "Arithmetic stops at `+`", so "prod gets
|
|
7
|
+
// double the replicas" is inexpressible and Kubernetes quantity strings
|
|
8
|
+
// silently CONCATENATE ("500m" + "500m" is "500m500m"). The design
|
|
9
|
+
// pre-registered the semantics these functions must have
|
|
10
|
+
// (docs/capability-review/g8-generation.md, "Arithmetic semantics,
|
|
11
|
+
// pre-registered") and the boundary that keeps `-` `*` `/` `%`
|
|
12
|
+
// reserved: maths arrives as functions or not at all.
|
|
13
|
+
//
|
|
14
|
+
// THE FAMILY IS NUMERIC, WHICH IS WHAT MAKES `add` MORE THAN A SYNONYM
|
|
15
|
+
// FOR `+`. The operator is polymorphic -- concatenation for strings,
|
|
16
|
+
// disjunction for booleans, addition for numbers -- and that is why the
|
|
17
|
+
// quantity strings above concatenate instead of failing. `add("500m",
|
|
18
|
+
// "500m")` is a located error, because a function named for a numeric
|
|
19
|
+
// operation has no business inventing a string. So the two spellings
|
|
20
|
+
// mean different things, and both are kept.
|
|
21
|
+
//
|
|
22
|
+
// Everything else here is the number tower's existing law, applied to
|
|
23
|
+
// five more operations (docs/design/number-model.md):
|
|
24
|
+
//
|
|
25
|
+
// R5 CONTAGION no operation introduces a kind narrower than its
|
|
26
|
+
// operands, so an integer result needs integer
|
|
27
|
+
// operands.
|
|
28
|
+
// D6 EXACT LADDER integer < biginteger < bigdecimal. A mixed exact
|
|
29
|
+
// operation promotes to the WIDEST operand, is
|
|
30
|
+
// computed exactly, and never demotes.
|
|
31
|
+
// FLOAT IS OFF IT binary64 mixed with either big leaf is a hard
|
|
32
|
+
// error in both operand orders, because promotion
|
|
33
|
+
// either way throws away exactness the document
|
|
34
|
+
// asked for by writing `0d`.
|
|
35
|
+
// NO ROUNDING an exact result that will not store is refused,
|
|
36
|
+
// never rounded to fit.
|
|
37
|
+
//
|
|
38
|
+
// and three refusals this file adds, which the pre-registration named:
|
|
39
|
+
//
|
|
40
|
+
// div/mod/rem by ZERO is a hard error in every kind. A ground-truth
|
|
41
|
+
// language has no business manufacturing infinity, and Aontu cannot
|
|
42
|
+
// even write one down -- an overflowing literal is a `not_number`
|
|
43
|
+
// error nil -- so propagating one would invent a value no generated
|
|
44
|
+
// JSON could carry.
|
|
45
|
+
//
|
|
46
|
+
// A NON-FINITE FLOAT RESULT is a located error. This one was already
|
|
47
|
+
// reachable through `+` and reported as neither: `1.0e308+1.0e308`
|
|
48
|
+
// crashed TypeScript with `[aontu/internal]` and leaked Go's raw
|
|
49
|
+
// `json: unsupported value: +Inf` with no code at all (use-cases/
|
|
50
|
+
// BUGS.md 39). PlusOpVal and its Go twin now go through the same
|
|
51
|
+
// check.
|
|
52
|
+
//
|
|
53
|
+
// DIVISION IS NOT CLOSED OVER THE DECIMAL LEAF, so div/mod/rem refuse
|
|
54
|
+
// a bigdecimal operand rather than rounding one third to fit. See
|
|
55
|
+
// `inexact_divide`'s hint: scale to integers, which is the money wire
|
|
56
|
+
// convention anyway, or accept a float.
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
import type { Val } from '../type'
|
|
60
|
+
|
|
61
|
+
import { AontuContext } from '../ctx'
|
|
62
|
+
import { makeNilErr } from '../err'
|
|
63
|
+
|
|
64
|
+
import { IntegerVal } from './IntegerVal'
|
|
65
|
+
import { NumberVal } from './NumberVal'
|
|
66
|
+
import { BigIntegerVal } from './BigIntegerVal'
|
|
67
|
+
import { BigDecimalVal } from './BigDecimalVal'
|
|
68
|
+
import { Decimal, decimalOverBudget } from './Decimal'
|
|
69
|
+
import { isIntegerStorable } from './numkind'
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
type ArithOp = 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'rem'
|
|
73
|
+
|
|
74
|
+
// The three that divide, and therefore the three that can be handed a
|
|
75
|
+
// zero divisor and cannot answer over the decimal leaf.
|
|
76
|
+
function divides(op: ArithOp): boolean {
|
|
77
|
+
return 'div' === op || 'mod' === op || 'rem' === op
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
// The numeric leaves, told apart by their own flags rather than by the
|
|
82
|
+
// JavaScript type of the peg: `integer` and `float` share `number`.
|
|
83
|
+
type ArithKind = 'integer' | 'float' | 'biginteger' | 'bigdecimal'
|
|
84
|
+
|
|
85
|
+
const EXACT_RANK: Record<string, number> = {
|
|
86
|
+
integer: 1,
|
|
87
|
+
biginteger: 2,
|
|
88
|
+
bigdecimal: 3,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
function isBig(k: ArithKind): boolean {
|
|
93
|
+
return 'biginteger' === k || 'bigdecimal' === k
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
// A pref operand contributes its preferred value, and therefore that
|
|
98
|
+
// value's kind too -- the same rule `+` applies.
|
|
99
|
+
function unpref(v: any): any {
|
|
100
|
+
while (v?.isPref) {
|
|
101
|
+
v = v.peg
|
|
102
|
+
}
|
|
103
|
+
return v
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
function arithKind(v: any): ArithKind | undefined {
|
|
108
|
+
if (!(v?.isVal && v.isScalar)) {
|
|
109
|
+
return undefined
|
|
110
|
+
}
|
|
111
|
+
if (v.isBigInteger) {
|
|
112
|
+
return 'biginteger'
|
|
113
|
+
}
|
|
114
|
+
if (v.isBigDecimal) {
|
|
115
|
+
return 'bigdecimal'
|
|
116
|
+
}
|
|
117
|
+
if (v.isInteger) {
|
|
118
|
+
return 'integer'
|
|
119
|
+
}
|
|
120
|
+
return 'number' === typeof v.peg ? 'float' : undefined
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
// An exact-ladder operand as an exact integer. Only reached for the two
|
|
125
|
+
// integral leaves; an `integer` peg is integral by construction.
|
|
126
|
+
function asInteger(v: any, k: ArithKind): bigint {
|
|
127
|
+
return 'biginteger' === k ? v.peg : BigInt(v.peg)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
function asDecimal(v: any, k: ArithKind): Decimal {
|
|
132
|
+
return 'bigdecimal' === k ? v.peg : new Decimal(asInteger(v, k), 0)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
// The whole family, in one function, because every rule above is a rule
|
|
137
|
+
// about ARITHMETIC and not about any one operation. `node` is the value
|
|
138
|
+
// the error is located at -- the call, or the `+` op.
|
|
139
|
+
// `attempt` is the name the ERROR reports, which is the operation
|
|
140
|
+
// except when a fold borrows one: `sum` adds, but a bad member is the
|
|
141
|
+
// author's `sum` call and must say so.
|
|
142
|
+
function arith(
|
|
143
|
+
ctx: AontuContext | undefined,
|
|
144
|
+
op: ArithOp,
|
|
145
|
+
node: Val,
|
|
146
|
+
a: Val,
|
|
147
|
+
b: Val,
|
|
148
|
+
attempt?: string
|
|
149
|
+
): Val {
|
|
150
|
+
const name = attempt ?? op
|
|
151
|
+
const av: any = unpref(a)
|
|
152
|
+
const bv: any = unpref(b)
|
|
153
|
+
const ak = arithKind(av)
|
|
154
|
+
const bk = arithKind(bv)
|
|
155
|
+
|
|
156
|
+
// A non-numeric operand is not something to wait for: `resolve` is
|
|
157
|
+
// only reached once every argument has settled, so a kind, a map, a
|
|
158
|
+
// string or a boolean here is the author's mistake and is named as
|
|
159
|
+
// one. (`+` differs, and must: it has answers for strings and
|
|
160
|
+
// booleans.)
|
|
161
|
+
if (undefined === ak || undefined === bk) {
|
|
162
|
+
return makeNilErr(ctx, 'invalid-arg', node, undefined, name)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// A big leaf never silently becomes a binary float, in EITHER operand
|
|
166
|
+
// order. The error names both leaves in operand order.
|
|
167
|
+
if (('float' === ak && isBig(bk)) || (isBig(ak) && 'float' === bk)) {
|
|
168
|
+
return makeNilErr(ctx, 'exact_float_mix', node, undefined, name,
|
|
169
|
+
{ left: ak, right: bk })
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if ('float' === ak || 'float' === bk) {
|
|
173
|
+
return floatArith(ctx, op, name, node, av.peg, bv.peg)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const rank = EXACT_RANK[bk] < EXACT_RANK[ak] ? EXACT_RANK[ak] : EXACT_RANK[bk]
|
|
177
|
+
|
|
178
|
+
if (EXACT_RANK.bigdecimal === rank) {
|
|
179
|
+
return decimalArith(ctx, op, name, node, asDecimal(av, ak), asDecimal(bv, bk))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return integerArith(ctx, op, name, node, asInteger(av, ak),
|
|
183
|
+
asInteger(bv, bk), EXACT_RANK.biginteger === rank)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
// IEEE-754 binary64, with the JSON-superset constraint still biting: an
|
|
188
|
+
// infinite or NaN result is a located error rather than a value, because
|
|
189
|
+
// there is no way to write one down and no JSON that could carry it.
|
|
190
|
+
function floatArith(
|
|
191
|
+
ctx: AontuContext | undefined,
|
|
192
|
+
op: ArithOp,
|
|
193
|
+
name: string,
|
|
194
|
+
node: Val,
|
|
195
|
+
x: number,
|
|
196
|
+
y: number
|
|
197
|
+
): Val {
|
|
198
|
+
if (divides(op) && 0 === y) {
|
|
199
|
+
return makeNilErr(ctx, 'divide_by_zero', node, undefined, name)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const out =
|
|
203
|
+
'add' === op ? x + y :
|
|
204
|
+
'sub' === op ? x - y :
|
|
205
|
+
'mul' === op ? x * y :
|
|
206
|
+
'div' === op ? x / y :
|
|
207
|
+
// Truncated remainder, sign following the DIVIDEND, which is
|
|
208
|
+
// what JavaScript's `%` and Go's math.Mod both give...
|
|
209
|
+
'rem' === op ? x % y :
|
|
210
|
+
// ...and the floored modulus, sign following the DIVISOR,
|
|
211
|
+
// built from it. Adding the divisor back moves a remainder
|
|
212
|
+
// whose sign disagrees into agreement, and leaves an exact
|
|
213
|
+
// zero alone.
|
|
214
|
+
flooredMod(x % y, y)
|
|
215
|
+
|
|
216
|
+
return Number.isFinite(out) ?
|
|
217
|
+
new NumberVal({ peg: out }) :
|
|
218
|
+
makeNilErr(ctx, 'float_overflow', node, undefined, name)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
function flooredMod(rem: number, y: number): number {
|
|
223
|
+
return 0 !== rem && (rem < 0) !== (y < 0) ? rem + y : rem
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
// The exact integral leaves. Both compute in bigint, so nothing passes
|
|
228
|
+
// through binary64 and nothing rounds; only the storage test at the end
|
|
229
|
+
// differs, because `biginteger` is unbounded and `integer` is not.
|
|
230
|
+
function integerArith(
|
|
231
|
+
ctx: AontuContext | undefined,
|
|
232
|
+
op: ArithOp,
|
|
233
|
+
name: string,
|
|
234
|
+
node: Val,
|
|
235
|
+
x: bigint,
|
|
236
|
+
y: bigint,
|
|
237
|
+
big: boolean
|
|
238
|
+
): Val {
|
|
239
|
+
if (divides(op) && 0n === y) {
|
|
240
|
+
return makeNilErr(ctx, 'divide_by_zero', node, undefined, name)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const out =
|
|
244
|
+
'add' === op ? x + y :
|
|
245
|
+
'sub' === op ? x - y :
|
|
246
|
+
'mul' === op ? x * y :
|
|
247
|
+
// TRUNCATION TOWARD ZERO, stated once here rather than left to
|
|
248
|
+
// whichever host `/` each port happens to call: div(-7, 2) is
|
|
249
|
+
// -3, not -4. BigInt division truncates, and so does Go's
|
|
250
|
+
// big.Int.Quo (its Div floors, which is why the Go twin must
|
|
251
|
+
// not use it).
|
|
252
|
+
'div' === op ? x / y :
|
|
253
|
+
'rem' === op ? x % y :
|
|
254
|
+
flooredModBig(x % y, y)
|
|
255
|
+
|
|
256
|
+
if (big) {
|
|
257
|
+
// Unbounded and exact: nothing to check, and no demotion to
|
|
258
|
+
// `integer` however small the result.
|
|
259
|
+
return new BigIntegerVal({ peg: out })
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// The result faces the SAME storage contract R1 puts on a literal --
|
|
263
|
+
// integral, inside the int64 window, and exactly representable in
|
|
264
|
+
// binary64 -- because Go's int64 holds results TypeScript's double
|
|
265
|
+
// cannot, and without a shared test a document would resolve in one
|
|
266
|
+
// port and round in the other.
|
|
267
|
+
return isIntegerStorable(out) ?
|
|
268
|
+
new IntegerVal({ peg: Number(out) }) :
|
|
269
|
+
makeNilErr(ctx, 'inexact_integer_sum', node, undefined, name,
|
|
270
|
+
{ sum: out.toString() })
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
function flooredModBig(rem: bigint, y: bigint): bigint {
|
|
275
|
+
return 0n !== rem && (rem < 0n) !== (y < 0n) ? rem + y : rem
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
// The decimal leaf. Addition, subtraction and multiplication are exact
|
|
280
|
+
// coefficient arithmetic and land here; division does not, and says so.
|
|
281
|
+
function decimalArith(
|
|
282
|
+
ctx: AontuContext | undefined,
|
|
283
|
+
op: ArithOp,
|
|
284
|
+
name: string,
|
|
285
|
+
node: Val,
|
|
286
|
+
x: Decimal,
|
|
287
|
+
y: Decimal
|
|
288
|
+
): Val {
|
|
289
|
+
if (divides(op)) {
|
|
290
|
+
// EXACT DECIMAL DIVISION IS NOT CLOSED: one third has no finite
|
|
291
|
+
// decimal form, so a `div` over this leaf either rounds -- the one
|
|
292
|
+
// thing the leaf exists to refuse -- or refuses. It refuses, and the
|
|
293
|
+
// hint names both ways out.
|
|
294
|
+
return makeNilErr(ctx, 'inexact_divide', node, undefined, name)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const out =
|
|
298
|
+
'add' === op ? x.add(y) :
|
|
299
|
+
'sub' === op ? x.add(y.negate()) :
|
|
300
|
+
x.multiply(y)
|
|
301
|
+
|
|
302
|
+
// The budget applies to RESULTS as well as literals: an exact answer
|
|
303
|
+
// too wide to hold is refused, never rounded to fit.
|
|
304
|
+
return decimalOverBudget(out) ?
|
|
305
|
+
makeNilErr(ctx, 'decimal_budget', node, undefined, name) :
|
|
306
|
+
new BigDecimalVal({ peg: out })
|
|
307
|
+
} /* node:coverage ignore next 9 */
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
export type {
|
|
311
|
+
ArithOp,
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export {
|
|
315
|
+
arith,
|
|
316
|
+
}
|