aontu 0.62.0 → 0.64.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 +7 -7
- package/dist/agentsmd.js +0 -27
- package/dist/agentsmd.js.map +1 -1
- package/dist/alias.js.map +1 -1
- package/dist/allow.js +0 -92
- package/dist/allow.js.map +1 -1
- package/dist/aontu.d.ts +3 -3
- package/dist/aontu.js +4 -84
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.d.ts +4 -0
- package/dist/aontumodel.js +27 -0
- package/dist/aontumodel.js.map +1 -0
- package/dist/casing.d.ts +5 -0
- package/dist/casing.js +90 -0
- package/dist/casing.js.map +1 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +228 -935
- package/dist/cli.js.map +1 -1
- package/dist/ctx.js +0 -48
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js +0 -32
- package/dist/diff.js.map +1 -1
- package/dist/err.js +0 -40
- package/dist/err.js.map +1 -1
- package/dist/escape.js +0 -45
- package/dist/escape.js.map +1 -1
- package/dist/exactjson.d.ts +0 -35
- package/dist/exactjson.js +0 -131
- package/dist/exactjson.js.map +1 -1
- package/dist/format.js +13 -203
- package/dist/format.js.map +1 -1
- package/dist/grammar.d.ts +9 -0
- package/dist/grammar.js +54 -0
- package/dist/grammar.js.map +1 -0
- package/dist/graph.js +0 -26
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +0 -82
- package/dist/hcanon.js.map +1 -1
- package/dist/helpdoc.js +2 -2
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.d.ts +0 -6
- package/dist/hints.js +57 -55
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +0 -114
- package/dist/jsonschema.js.map +1 -1
- package/dist/keyorder.d.ts +0 -7
- package/dist/keyorder.js +0 -41
- package/dist/keyorder.js.map +1 -1
- package/dist/lang.js +17 -915
- package/dist/lang.js.map +1 -1
- package/dist/lsp-server.js +0 -16
- package/dist/lsp-server.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +12 -159
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.js +0 -26
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp.js +0 -149
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +16 -131
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.js +0 -162
- package/dist/mod.js.map +1 -1
- package/dist/patch.js +0 -217
- package/dist/patch.js.map +1 -1
- package/dist/profile.d.ts +9 -0
- package/dist/profile.js +28 -0
- package/dist/profile.js.map +1 -0
- package/dist/provenance.js +0 -140
- package/dist/provenance.js.map +1 -1
- package/dist/query.js +0 -75
- package/dist/query.js.map +1 -1
- package/dist/reach.js +0 -43
- package/dist/reach.js.map +1 -1
- package/dist/relation.js +0 -61
- package/dist/relation.js.map +1 -1
- package/dist/report-sarif.d.ts +0 -11
- package/dist/report-sarif.js +0 -28
- package/dist/report-sarif.js.map +1 -1
- package/dist/sig.js +0 -35
- package/dist/sig.js.map +1 -1
- package/dist/sigdecl.js +1 -1
- package/dist/sigdecl.js.map +1 -1
- package/dist/siggate.js +0 -4
- package/dist/siggate.js.map +1 -1
- package/dist/site.js +3 -29
- package/dist/site.js.map +1 -1
- package/dist/subsume.d.ts +0 -10
- package/dist/subsume.js +0 -137
- package/dist/subsume.js.map +1 -1
- package/dist/template.d.ts +2 -1
- package/dist/template.js +58 -138
- package/dist/template.js.map +1 -1
- package/dist/trace.d.ts +21 -0
- package/dist/trace.js +107 -0
- package/dist/trace.js.map +1 -0
- package/dist/trim.js +0 -41
- package/dist/trim.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.js +13 -248
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +0 -22
- package/dist/utility.js.map +1 -1
- package/dist/val/AbnfFuncVal.d.ts +18 -0
- package/dist/val/AbnfFuncVal.js +132 -0
- package/dist/val/AbnfFuncVal.js.map +1 -0
- package/dist/val/AbsentVal.d.ts +11 -0
- package/dist/val/AbsentVal.js +30 -0
- package/dist/val/AbsentVal.js.map +1 -0
- package/dist/val/AggFuncVal.d.ts +10 -1
- package/dist/val/AggFuncVal.js +104 -116
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/ArithFuncVal.js +0 -12
- package/dist/val/ArithFuncVal.js.map +1 -1
- package/dist/val/BagVal.js +1 -78
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/BigDecimalVal.js +0 -16
- package/dist/val/BigDecimalVal.js.map +1 -1
- package/dist/val/BigIntegerVal.js +0 -16
- package/dist/val/BigIntegerVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +0 -9
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/CmpFuncVal.d.ts +2 -0
- package/dist/val/CmpFuncVal.js +49 -72
- package/dist/val/CmpFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.js +0 -29
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +0 -500
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.js +0 -2
- package/dist/val/ContainerKindVal.js.map +1 -1
- package/dist/val/CopyFuncVal.js +0 -3
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.js +0 -179
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +0 -152
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.js +0 -3
- package/dist/val/EachFuncVal.js.map +1 -1
- package/dist/val/EmitFuncVal.d.ts +1 -1
- package/dist/val/EmitFuncVal.js +6 -119
- package/dist/val/EmitFuncVal.js.map +1 -1
- package/dist/val/ExpectVal.js +0 -62
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FilterFuncVal.js +0 -25
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +7 -127
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.js +0 -15
- package/dist/val/GraphAtomVal.js.map +1 -1
- package/dist/val/HideFuncVal.js +0 -13
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IntegerVal.js +0 -61
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/JunctionVal.js +0 -20
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.js +0 -46
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +0 -57
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js +0 -11
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +0 -151
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.js +0 -27
- package/dist/val/MatchFuncVal.js.map +1 -1
- package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
- package/dist/val/MaybeFuncVal.js +50 -0
- package/dist/val/MaybeFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js +0 -18
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +0 -60
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/NomFuncVal.js +18 -54
- package/dist/val/NomFuncVal.js.map +1 -1
- package/dist/val/NumberVal.js +0 -15
- package/dist/val/NumberVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -0
- package/dist/val/OpBaseVal.js +3 -15
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/PackFuncVal.js +0 -34
- package/dist/val/PackFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +0 -6
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.js +0 -41
- package/dist/val/PathVal.js.map +1 -1
- package/dist/val/PlaceVal.js +0 -25
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/PlusOpVal.d.ts +1 -7
- package/dist/val/PlusOpVal.js +13 -74
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js +0 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.js +0 -167
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.js +0 -55
- package/dist/val/RecurseVal.js.map +1 -1
- package/dist/val/RefVal.js +0 -282
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.js +3 -232
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.js +0 -49
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/ScalarVal.js +0 -11
- package/dist/val/ScalarVal.js.map +1 -1
- package/dist/val/StrFuncVal.js +0 -18
- package/dist/val/StrFuncVal.js.map +1 -1
- package/dist/val/SuperFuncVal.js +0 -32
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.js +0 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TranslateFuncVal.js +1 -3
- package/dist/val/TranslateFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js +0 -11
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +1 -0
- package/dist/val/Val.js +2 -133
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +0 -12
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.js +0 -37
- package/dist/val/arith.js.map +1 -1
- package/dist/val/caserange.js +0 -61
- package/dist/val/caserange.js.map +1 -1
- package/dist/val/members.js +0 -6
- package/dist/val/members.js.map +1 -1
- package/dist/val/numcmp.js +0 -11
- package/dist/val/numcmp.js.map +1 -1
- package/dist/val/numkind.js +0 -145
- package/dist/val/numkind.js.map +1 -1
- package/dist/val/valutil.js +0 -16
- package/dist/val/valutil.js.map +1 -1
- package/dist/vet.js +0 -461
- package/dist/vet.js.map +1 -1
- package/dist/view.js +0 -414
- package/dist/view.js.map +1 -1
- package/dist/walk.js +0 -41
- package/dist/walk.js.map +1 -1
- package/grammar/aontu.abnf +8 -6
- package/grammar/aontu.gbnf +4 -4
- package/grammar/aontu.lark +4 -4
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +4 -2
- package/skill/tasks.md +9 -7
- package/src/agentsmd.ts +0 -35
- package/src/alias.ts +0 -39
- package/src/allow.ts +1 -96
- package/src/aontu.ts +4 -116
- package/src/aontumodel.ts +26 -0
- package/src/casing.ts +95 -0
- package/src/cli.ts +258 -1024
- package/src/ctx.ts +0 -103
- package/src/diff.ts +0 -40
- package/src/err.ts +0 -40
- package/src/escape.ts +0 -46
- package/src/exactjson.ts +0 -131
- package/src/format.ts +14 -257
- package/src/grammar.ts +72 -0
- package/src/graph.ts +0 -61
- package/src/hcanon.ts +0 -82
- package/src/helpdoc.ts +2 -2
- package/src/hints.ts +69 -57
- package/src/jsonschema.ts +0 -123
- package/src/keyorder.ts +0 -42
- package/src/lang.ts +19 -931
- package/src/lsp-server.ts +0 -16
- package/src/lsp.ts +12 -180
- package/src/mcp-server.ts +0 -31
- package/src/mcp.ts +0 -169
- package/src/mod-tool.ts +18 -159
- package/src/mod.ts +0 -178
- package/src/patch.ts +0 -232
- package/src/profile.ts +42 -0
- package/src/provenance.ts +0 -183
- package/src/query.ts +0 -84
- package/src/reach.ts +0 -53
- package/src/relation.ts +0 -84
- package/src/report-sarif.ts +0 -48
- package/src/sig.ts +0 -35
- package/src/sigdecl.ts +1 -1
- package/src/siggate.ts +0 -30
- package/src/site.ts +3 -29
- package/src/subsume.ts +1 -161
- package/src/template.ts +69 -140
- package/src/trace.ts +157 -0
- package/src/trim.ts +0 -53
- package/src/type.ts +2 -45
- package/src/unify.ts +14 -257
- package/src/utility.ts +0 -31
- package/src/val/AbnfFuncVal.ts +181 -0
- package/src/val/AbsentVal.ts +54 -0
- package/src/val/AggFuncVal.ts +152 -188
- package/src/val/ArithFuncVal.ts +0 -20
- package/src/val/BagVal.ts +1 -78
- package/src/val/BigDecimalVal.ts +0 -16
- package/src/val/BigIntegerVal.ts +0 -16
- package/src/val/CloseFuncVal.ts +0 -9
- package/src/val/CmpFuncVal.ts +68 -184
- package/src/val/ConjunctVal.ts +0 -33
- package/src/val/ConstraintVal.ts +2 -537
- package/src/val/ContainerKindVal.ts +0 -18
- package/src/val/CopyFuncVal.ts +0 -5
- package/src/val/Decimal.ts +1 -185
- package/src/val/DeprecateFuncVal.ts +0 -10
- package/src/val/DisjunctVal.ts +0 -157
- package/src/val/EachFuncVal.ts +0 -40
- package/src/val/EmitFuncVal.ts +8 -208
- package/src/val/ExpectVal.ts +0 -62
- package/src/val/FilterFuncVal.ts +0 -55
- package/src/val/FuncBaseVal.ts +9 -130
- package/src/val/GraphAtomVal.ts +0 -42
- package/src/val/HideFuncVal.ts +0 -15
- package/src/val/IntegerVal.ts +0 -61
- package/src/val/JunctionVal.ts +0 -20
- package/src/val/KeyFuncVal.ts +0 -48
- package/src/val/ListVal.ts +0 -59
- package/src/val/LowerFuncVal.ts +0 -12
- package/src/val/MapVal.ts +0 -151
- package/src/val/MatchFuncVal.ts +0 -59
- package/src/val/MaybeFuncVal.ts +86 -0
- package/src/val/MoveFuncVal.ts +0 -20
- package/src/val/NilVal.ts +0 -60
- package/src/val/NomFuncVal.ts +10 -99
- package/src/val/NumberVal.ts +0 -16
- package/src/val/OpBaseVal.ts +4 -17
- package/src/val/PackFuncVal.ts +0 -63
- package/src/val/PathFuncVal.ts +0 -32
- package/src/val/PathVal.ts +0 -66
- package/src/val/PlaceVal.ts +0 -45
- package/src/val/PlusOpVal.ts +18 -75
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +0 -179
- package/src/val/RecurseVal.ts +0 -81
- package/src/val/RefVal.ts +1 -285
- package/src/val/ReferFuncVal.ts +4 -255
- package/src/val/ScalarKindVal.ts +0 -50
- package/src/val/ScalarVal.ts +0 -12
- package/src/val/StrFuncVal.ts +0 -44
- package/src/val/SuperFuncVal.ts +0 -42
- package/src/val/TopVal.ts +0 -1
- package/src/val/TranslateFuncVal.ts +1 -51
- package/src/val/UpperFuncVal.ts +0 -12
- package/src/val/Val.ts +3 -192
- package/src/val/VarVal.ts +0 -15
- package/src/val/arith.ts +0 -92
- package/src/val/caserange.ts +0 -62
- package/src/val/members.ts +0 -23
- package/src/val/numcmp.ts +1 -27
- package/src/val/numkind.ts +0 -149
- package/src/val/valutil.ts +0 -16
- package/src/vet.ts +1 -582
- package/src/view.ts +0 -507
- package/src/walk.ts +0 -41
- package/dist/lower.d.ts +0 -23
- package/dist/lower.js +0 -578
- package/dist/lower.js.map +0 -1
- package/dist/render.d.ts +0 -53
- package/dist/render.js +0 -547
- package/dist/render.js.map +0 -1
- package/dist/std.d.ts +0 -3
- package/dist/std.js +0 -672
- package/dist/std.js.map +0 -1
- package/dist/val/FormFuncVal.js +0 -55
- package/dist/val/FormFuncVal.js.map +0 -1
- package/dist/val/NamerFuncVal.d.ts +0 -12
- package/dist/val/NamerFuncVal.js +0 -176
- package/dist/val/NamerFuncVal.js.map +0 -1
- package/src/lower.ts +0 -636
- package/src/render.ts +0 -732
- package/src/std.ts +0 -683
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/* Copyright (c) 2026 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
// The grammar pair, ADR-033. Twin of go/abnf.go.
|
|
4
|
+
|
|
5
|
+
import type { Val, ValSpec } from '../type'
|
|
6
|
+
import { DONE } from '../type'
|
|
7
|
+
import { AontuContext } from '../ctx'
|
|
8
|
+
import { FuncBaseVal } from './FuncBaseVal'
|
|
9
|
+
import { StringVal } from './StringVal'
|
|
10
|
+
import { MapVal } from './MapVal'
|
|
11
|
+
import { ListVal } from './ListVal'
|
|
12
|
+
import { ConjunctVal } from './ConjunctVal'
|
|
13
|
+
import { makeNilErr } from '../err'
|
|
14
|
+
import { compileGrammar, parseWith } from '../grammar'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AbnfFuncVal extends FuncBaseVal {
|
|
18
|
+
isAbnfFunc = true
|
|
19
|
+
|
|
20
|
+
constructor(spec: ValSpec, ctx?: AontuContext) {
|
|
21
|
+
super(spec, ctx)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
funcname() {
|
|
25
|
+
return 'abnf'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
resolve(ctx: AontuContext, args: Val[]): Val {
|
|
29
|
+
const a: any = args[0]
|
|
30
|
+
|
|
31
|
+
// Not an arity check: the arity and signature gates run first, so
|
|
32
|
+
// what reaches here is a nil argument, which both pass over.
|
|
33
|
+
if ('string' !== typeof a?.peg) {
|
|
34
|
+
return this.place(makeNilErr(ctx, 'abnf_grammar', this, undefined,
|
|
35
|
+
'abnf'))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const [, why] = compileGrammar(a.peg)
|
|
39
|
+
if (undefined !== why) {
|
|
40
|
+
return this.place(makeNilErr(ctx, 'abnf_grammar', this, undefined,
|
|
41
|
+
'abnf', { reason: why }))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return this.place(new StringVal({ peg: a.peg }, ctx))
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
// A tree node is just the map {rule, src, kids}, so it needs no case of
|
|
50
|
+
// its own. Keys are sorted so both ports build the same member order.
|
|
51
|
+
function astVal(node: any, ctx: AontuContext): Val {
|
|
52
|
+
if ('string' === typeof node) {
|
|
53
|
+
return new StringVal({ peg: node }, ctx)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (Array.isArray(node)) {
|
|
57
|
+
return new ListVal({
|
|
58
|
+
peg: node.map(
|
|
59
|
+
(e: any, i: number) => astVal(e, ctx.descend(String(i)))),
|
|
60
|
+
}, ctx)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const peg: Record<string, Val> = {}
|
|
64
|
+
for (const k of Object.keys(node).sort()) {
|
|
65
|
+
peg[k] = astVal(node[k], ctx.descend(k))
|
|
66
|
+
}
|
|
67
|
+
return new MapVal({ peg }, ctx)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ParseFuncVal extends FuncBaseVal {
|
|
72
|
+
isParseFunc = true
|
|
73
|
+
|
|
74
|
+
constructor(spec: ValSpec, ctx?: AontuContext) {
|
|
75
|
+
super(spec, ctx)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
funcname() {
|
|
79
|
+
return 'parse'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
// Meets its peer instead of resolving, so it must see the peer
|
|
84
|
+
// before the base drives to resolve().
|
|
85
|
+
unify(peer: Val, ctx: AontuContext): Val {
|
|
86
|
+
if (1 !== this.peg.length) {
|
|
87
|
+
return super.unify(peer, ctx)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
this.driveStagedArgs(ctx, 1)
|
|
91
|
+
|
|
92
|
+
const g: any = this.peg[0]
|
|
93
|
+
if ('string' !== typeof g?.peg) {
|
|
94
|
+
return this.residuate(peer, ctx)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// A settled constraint is stable, as a residual constraint atom is.
|
|
98
|
+
// Without this a `type()` holding one never resolves.
|
|
99
|
+
this.dc = DONE
|
|
100
|
+
|
|
101
|
+
const p: any = peer
|
|
102
|
+
if (true !== p?.isString || 'string' !== typeof p?.peg) {
|
|
103
|
+
return this.hold(peer, ctx)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const [grammar, why] = compileGrammar(g.peg)
|
|
107
|
+
if (undefined !== why) {
|
|
108
|
+
return makeNilErr(ctx, 'abnf_grammar', this, peer, 'parse',
|
|
109
|
+
{ reason: why })
|
|
110
|
+
}
|
|
111
|
+
if ('' === p.peg) {
|
|
112
|
+
return makeNilErr(ctx, 'parse_failed', this, peer, 'parse',
|
|
113
|
+
{ reason: 'the empty string parses under no grammar' })
|
|
114
|
+
}
|
|
115
|
+
const [, err] = parseWith(grammar, p.peg, ctx)
|
|
116
|
+
if (undefined !== err) {
|
|
117
|
+
return makeNilErr(ctx, 'parse_failed', this, peer, 'parse',
|
|
118
|
+
{ reason: err })
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return peer
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
// The stable twin of residuate: no `notdone()`, because a settled
|
|
126
|
+
// constraint stays done while it waits for a value.
|
|
127
|
+
hold(peer: Val, ctx: AontuContext): Val {
|
|
128
|
+
if (peer.isTop || peer.id === this.id) {
|
|
129
|
+
// Cloned, or a driver meeting it twice reports `unify_cycle`.
|
|
130
|
+
const out: Val = this.clone(ctx)
|
|
131
|
+
out.dc = DONE
|
|
132
|
+
return out
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (peer.isFunc
|
|
136
|
+
&& (peer as any).funcname() === this.funcname()
|
|
137
|
+
&& peer.path.join('.') === this.path.join('.')
|
|
138
|
+
&& peer.canon === this.canon) {
|
|
139
|
+
return this
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return new ConjunctVal({ peg: [this, peer] }, ctx)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
resolve(ctx: AontuContext, args: Val[]): Val {
|
|
147
|
+
const g: any = args[0]
|
|
148
|
+
const v: any = args[1]
|
|
149
|
+
|
|
150
|
+
// Not an arity check: see the abnf twin above.
|
|
151
|
+
if ('string' !== typeof g?.peg || 'string' !== typeof v?.peg) {
|
|
152
|
+
return this.place(makeNilErr(ctx, 'parse_arg', this, undefined, 'parse'))
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const [grammar, why] = compileGrammar(g.peg)
|
|
156
|
+
if (undefined !== why) {
|
|
157
|
+
return this.place(makeNilErr(ctx, 'abnf_grammar', this, undefined,
|
|
158
|
+
'parse', { reason: why }))
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// The host answers an empty tree for empty input.
|
|
162
|
+
if ('' === v.peg) {
|
|
163
|
+
return this.place(makeNilErr(ctx, 'parse_failed', this, undefined,
|
|
164
|
+
'parse', { reason: 'the empty string parses under no grammar' }))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const [node, err] = parseWith(grammar, v.peg, ctx)
|
|
168
|
+
if (undefined !== err) {
|
|
169
|
+
return this.place(makeNilErr(ctx, 'parse_failed', this, undefined,
|
|
170
|
+
'parse', { reason: err }))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return this.place(astVal(node, ctx))
|
|
174
|
+
}
|
|
175
|
+
} /* node:coverage ignore next 6 */
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
export {
|
|
179
|
+
AbnfFuncVal,
|
|
180
|
+
ParseFuncVal,
|
|
181
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* Copyright (c) 2026 Richard Rodger, MIT License */
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
Val,
|
|
6
|
+
ValSpec,
|
|
7
|
+
} from '../type'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
DONE,
|
|
11
|
+
} from '../type'
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
AontuContext,
|
|
15
|
+
} from '../ctx'
|
|
16
|
+
|
|
17
|
+
import { FeatureVal } from './FeatureVal'
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// Top with one difference (ADR-034): absence is GENERABLE, and
|
|
21
|
+
// generates nothing, so a bag drops it at a required key too.
|
|
22
|
+
class AbsentVal extends FeatureVal {
|
|
23
|
+
isAbsent = true
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
spec: ValSpec,
|
|
27
|
+
ctx?: AontuContext
|
|
28
|
+
) {
|
|
29
|
+
super(spec, ctx)
|
|
30
|
+
this.dc = DONE
|
|
31
|
+
this.mark.type = false
|
|
32
|
+
this.mark.hide = false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// The unit of the meet, called for EITHER operand so `&` commutes.
|
|
36
|
+
// Nothing here meets an absence with TOP, so there is no top arm;
|
|
37
|
+
// Go has one because it MEETS an op's result where this port places
|
|
38
|
+
// it. edge-plus-list-absent guards the day that changes.
|
|
39
|
+
unify(peer: Val, _ctx: AontuContext): Val {
|
|
40
|
+
return peer
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get canon() { return 'maybe()' }
|
|
44
|
+
|
|
45
|
+
gen(_ctx?: AontuContext) {
|
|
46
|
+
return undefined
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
} /* node:coverage ignore next 6 */
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
AbsentVal,
|
|
54
|
+
}
|
package/src/val/AggFuncVal.ts
CHANGED
|
@@ -1,47 +1,5 @@
|
|
|
1
1
|
/* Copyright (c) 2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
|
-
// AGGREGATION, PROJECTION AND THE STRING FOLD: `sum(d)`, `least(d)`,
|
|
4
|
-
// `greatest(d)`, `pick(d, k)` and `join(d, sep?)`.
|
|
5
|
-
//
|
|
6
|
-
// The review's finding I: "No aggregation. `length()` counts but
|
|
7
|
-
// nothing sums: an invoice total, a fleet-wide resource budget, a quota
|
|
8
|
-
// roll-up -- all inexpressible." Use case 10 had to write totals by
|
|
9
|
-
// hand and spot-check them with `must()`, which is a model asserting
|
|
10
|
-
// what it should be able to COMPUTE.
|
|
11
|
-
//
|
|
12
|
-
// total: sum($.lines) # an invoice total
|
|
13
|
-
// cheapest: least($.quotes) # ... and the extremes
|
|
14
|
-
// peak: greatest($.hourly)
|
|
15
|
-
//
|
|
16
|
-
// A FOLD OVER A FINITE, SETTLED BAG IS AS TOTAL AS `each`. There is no
|
|
17
|
-
// recursion here and no user-supplied step: the bag is the one the
|
|
18
|
-
// model already holds, the operation is fixed, and the walk visits each
|
|
19
|
-
// child exactly once. That is the whole reason these are built-ins
|
|
20
|
-
// rather than a `fold` combinator -- a fold takes a function, and a
|
|
21
|
-
// language with no user functions has none to take.
|
|
22
|
-
//
|
|
23
|
-
// THE NAMES ARE `least` AND `greatest`, NOT `min` AND `max`, because
|
|
24
|
-
// those two are already the constraint atoms for a LOWER and UPPER
|
|
25
|
-
// BOUND (`min(3)` means "at least 3"). An aggregate over a bag and a
|
|
26
|
-
// bound on a value are different things and must not share a spelling;
|
|
27
|
-
// least and greatest are the lattice's own words for the extremes of a
|
|
28
|
-
// set, which is exactly what these compute.
|
|
29
|
-
//
|
|
30
|
-
// `sum` FOLDS WITH `add`, so the number tower's whole law comes with it
|
|
31
|
-
// (arith.ts): the exact ladder, R5 contagion, the refusal to mix a
|
|
32
|
-
// binary float with an exact leaf, and the refusal to store a result
|
|
33
|
-
// that will not fit. A bag of integers sums to an integer; one integer
|
|
34
|
-
// float among them makes the total a float; `0d` operands keep it
|
|
35
|
-
// exact. And `sum([])` is `0` -- addition has an identity, which is
|
|
36
|
-
// what makes the empty case answerable.
|
|
37
|
-
//
|
|
38
|
-
// `least`/`greatest` have NO identity to answer an empty bag with: the
|
|
39
|
-
// least element of nothing is not a value, it is a question with no
|
|
40
|
-
// answer, so an empty bag is a located `aggregate_empty` rather than an
|
|
41
|
-
// invented zero or infinity. They compare with the tower's EXACT
|
|
42
|
-
// comparator (numcmp), so an integer and a bigdecimal in one bag order
|
|
43
|
-
// correctly rather than through binary64, and the winner is returned
|
|
44
|
-
// with its own kind intact.
|
|
45
3
|
|
|
46
4
|
import type {
|
|
47
5
|
Val,
|
|
@@ -59,6 +17,7 @@ import { StringVal } from './StringVal'
|
|
|
59
17
|
import { FuncBaseVal } from './FuncBaseVal'
|
|
60
18
|
import { arith } from './arith'
|
|
61
19
|
import { cmpNumeric } from './numcmp'
|
|
20
|
+
import { cmpCodePoint } from '../keyorder'
|
|
62
21
|
import { memberVals } from './members'
|
|
63
22
|
import { plusText } from './PlusOpVal'
|
|
64
23
|
|
|
@@ -66,12 +25,6 @@ import { plusText } from './PlusOpVal'
|
|
|
66
25
|
type AggOp = 'sum' | 'least' | 'greatest'
|
|
67
26
|
|
|
68
27
|
|
|
69
|
-
// The children of a bag, in the order the aggregate sees them: source
|
|
70
|
-
// order for a list, sorted-key order for a map -- `each`'s order, and
|
|
71
|
-
// for the same reason (a map has no order of its own, so the language
|
|
72
|
-
// picks one and states it).
|
|
73
|
-
// The members of the bag a fold reads: what generation would emit
|
|
74
|
-
// (./members.ts, BUGS.md §79), in the one map-key order (BUGS.md §62).
|
|
75
28
|
function bagChildren(data: any, ctx: AontuContext): Val[] | undefined {
|
|
76
29
|
return memberVals(data, ctx)
|
|
77
30
|
}
|
|
@@ -80,9 +33,6 @@ function bagChildren(data: any, ctx: AontuContext): Val[] | undefined {
|
|
|
80
33
|
class AggFuncVal extends FuncBaseVal {
|
|
81
34
|
isAggFunc = true
|
|
82
35
|
|
|
83
|
-
// THE STAGING RULE (G8 phase 0). A total over a bag that is still
|
|
84
|
-
// being merged into is a total of the wrong bag -- the same reason
|
|
85
|
-
// `filter` and `each` wait.
|
|
86
36
|
staged = true
|
|
87
37
|
|
|
88
38
|
op: AggOp
|
|
@@ -97,21 +47,11 @@ class AggFuncVal extends FuncBaseVal {
|
|
|
97
47
|
}
|
|
98
48
|
|
|
99
49
|
|
|
100
|
-
// NO `make` OVERRIDE, matching the other STAGED funcs (pack, each,
|
|
101
|
-
// filter, match): a staged call returns `residuate` before `unify`
|
|
102
|
-
// ever reaches the rebuild branch, so an override there would be
|
|
103
|
-
// unreachable code pretending to be a contract. The base's `make`
|
|
104
|
-
// raises `func:<name>` if that ever stops being true, which is the
|
|
105
|
-
// loud answer rather than a value that silently lost its operation.
|
|
106
|
-
|
|
107
50
|
funcname() {
|
|
108
51
|
return this.op
|
|
109
52
|
}
|
|
110
53
|
|
|
111
54
|
|
|
112
|
-
// The base does not drive the argument: `unify` drives it by hand,
|
|
113
|
-
// because a staged func must advance what it is waiting on every
|
|
114
|
-
// pass rather than only on the pass it fires.
|
|
115
55
|
prepare(_ctx: AontuContext, _args: Val[]) {
|
|
116
56
|
return null
|
|
117
57
|
}
|
|
@@ -137,9 +77,6 @@ class AggFuncVal extends FuncBaseVal {
|
|
|
137
77
|
}
|
|
138
78
|
|
|
139
79
|
if ('sum' === this.op) {
|
|
140
|
-
// Zero is addition's identity, so an empty bag has an answer and
|
|
141
|
-
// it is an integer -- the narrowest kind, which the first real
|
|
142
|
-
// operand then widens under R5 exactly as `add(0, x)` would.
|
|
143
80
|
let total: Val = new IntegerVal({ peg: 0 })
|
|
144
81
|
for (const child of children) {
|
|
145
82
|
total = arith(ctx, 'add', this, total, child, this.op)
|
|
@@ -192,28 +129,6 @@ function unpref(v: any): any {
|
|
|
192
129
|
}
|
|
193
130
|
|
|
194
131
|
|
|
195
|
-
// PROJECTION: `pick(d, k)` -- one element per child of `d`, being that
|
|
196
|
-
// child's `k`.
|
|
197
|
-
//
|
|
198
|
-
// The other half of the review's finding I: "`_.field` is unspellable,
|
|
199
|
-
// `filter` cannot see into lists, `unique()`-by-field is reserved but
|
|
200
|
-
// absent -- so 'no two services share a port' and 'unique event ids'
|
|
201
|
-
// cannot be said." Without it the aggregates above cannot reach the
|
|
202
|
-
// case that motivated them, because `sum` needs a bag of NUMBERS and a
|
|
203
|
-
// model holds a bag of RECORDS:
|
|
204
|
-
//
|
|
205
|
-
// total: sum(pick($.lines, amountCents))
|
|
206
|
-
//
|
|
207
|
-
// IT IS NOT `each` WITH A CLEVER TEMPLATE. `each(d, _ & t)` MEETS each
|
|
208
|
-
// child with `t`, and a meet cannot select: `each($.lines, _ & _.amount)`
|
|
209
|
-
// asks for a child that is simultaneously the whole record and one of
|
|
210
|
-
// its fields, which is why every spelling of it answers `no_path`.
|
|
211
|
-
// Selection is a different operation and gets its own verb.
|
|
212
|
-
//
|
|
213
|
-
// A CHILD MISSING THE KEY IS AN ERROR, not a silently shorter list.
|
|
214
|
-
// Skipping would make `sum(pick(...))` quietly total the wrong set of
|
|
215
|
-
// records -- the failure mode an aggregate exists to prevent -- so the
|
|
216
|
-
// refusal names the child (`pick_key`).
|
|
217
132
|
class PickFuncVal extends FuncBaseVal {
|
|
218
133
|
isPickFunc = true
|
|
219
134
|
|
|
@@ -293,56 +208,159 @@ class PickFuncVal extends FuncBaseVal {
|
|
|
293
208
|
}
|
|
294
209
|
|
|
295
210
|
|
|
211
|
+
// `pick`'s rule, plus: the empty string is the member itself, which is
|
|
212
|
+
// how a keyless descending sort is spelled.
|
|
213
|
+
function projectorName(key: any): [string, boolean] {
|
|
214
|
+
if (null == key || null == key.peg) {
|
|
215
|
+
return ['', true]
|
|
216
|
+
}
|
|
217
|
+
if ('string' === typeof key.peg) {
|
|
218
|
+
return [key.peg, true]
|
|
219
|
+
}
|
|
220
|
+
if ('number' === typeof key.peg && key.isInteger) {
|
|
221
|
+
return [String(key.peg), true]
|
|
222
|
+
}
|
|
223
|
+
return ['', false]
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
function fieldOf(child: any, name: string): Val | undefined {
|
|
228
|
+
return true === child?.isMap ? child.peg[name] :
|
|
229
|
+
true === child?.isList ? child.peg[Number(name)] :
|
|
230
|
+
undefined
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
const SORT_DIRECTIONS: Record<string, number> = { asc: 1, desc: -1 }
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
function directionOf(v: any): number | undefined {
|
|
238
|
+
if (null == v || null == v.peg) {
|
|
239
|
+
return 1
|
|
240
|
+
}
|
|
241
|
+
return 'string' === typeof v.peg ? SORT_DIRECTIONS[v.peg] : undefined
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
// Text by code point, numbers by the exact comparator, nothing else.
|
|
246
|
+
function sortDomain(u: any): 'text' | 'number' | undefined {
|
|
247
|
+
if (true !== u?.isVal || true !== u.isScalar || true === u.isNull) {
|
|
248
|
+
return undefined
|
|
249
|
+
}
|
|
250
|
+
if ('string' === typeof u.peg) {
|
|
251
|
+
return 'text'
|
|
252
|
+
}
|
|
253
|
+
return 'boolean' === typeof u.peg ? undefined : 'number'
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
type SortRow = { idx: number, val: Val, key: any }
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class SortFuncVal extends FuncBaseVal {
|
|
261
|
+
isSortFunc = true
|
|
262
|
+
|
|
263
|
+
// The bag must settle before it is ordered, exactly as it must
|
|
264
|
+
// before it is projected.
|
|
265
|
+
staged = true
|
|
266
|
+
|
|
267
|
+
constructor(
|
|
268
|
+
spec: ValSpec,
|
|
269
|
+
ctx?: AontuContext
|
|
270
|
+
) {
|
|
271
|
+
super(spec, ctx)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
funcname() {
|
|
276
|
+
return 'sort'
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
// The base drives no argument: the DATA is driven by hand below, and
|
|
281
|
+
// the projector is a bare word the parser has already made a string.
|
|
282
|
+
prepare(_ctx: AontuContext, _args: Val[]) {
|
|
283
|
+
return null
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
unify(peer: Val, ctx: AontuContext): Val {
|
|
288
|
+
const ready = this.driveStagedArgs(ctx, 1)
|
|
289
|
+
|
|
290
|
+
if (!ready || !ctx.settle) {
|
|
291
|
+
return this.residuate(peer, ctx)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return super.unify(peer, ctx)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
resolve(ctx: AontuContext, args: Val[]) {
|
|
299
|
+
const children = bagChildren(args?.[0], ctx)
|
|
300
|
+
|
|
301
|
+
if (undefined === children) {
|
|
302
|
+
return this.place(makeNilErr(ctx, 'aggregate_data', this, undefined,
|
|
303
|
+
'sort'))
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const [name, nameok] = projectorName(args?.[1])
|
|
307
|
+
|
|
308
|
+
if (!nameok) {
|
|
309
|
+
return this.place(makeNilErr(ctx, 'invalid-arg', this, undefined,
|
|
310
|
+
'sort'))
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const dir = directionOf(args?.[2])
|
|
314
|
+
|
|
315
|
+
if (undefined === dir) {
|
|
316
|
+
return this.place(makeNilErr(ctx, 'sort_dir', this, undefined, 'sort',
|
|
317
|
+
{ dir: (args[2] as any).canon }))
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const rows: SortRow[] = []
|
|
321
|
+
let domain: 'text' | 'number' | undefined = undefined
|
|
322
|
+
|
|
323
|
+
for (const child of children) {
|
|
324
|
+
const at: any = '' === name ? child : fieldOf(child, name)
|
|
325
|
+
|
|
326
|
+
if (null == at) {
|
|
327
|
+
return this.place(makeNilErr(ctx, 'sort_key', this, undefined, 'sort',
|
|
328
|
+
{ key: name }))
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const u = unpref(at)
|
|
332
|
+
const found = sortDomain(u)
|
|
333
|
+
|
|
334
|
+
if (undefined === found ||
|
|
335
|
+
(undefined !== domain && found !== domain)) {
|
|
336
|
+
return this.place(makeNilErr(ctx, 'sort_domain', this, undefined,
|
|
337
|
+
'sort', { member: u.canon }))
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
domain = found
|
|
341
|
+
rows.push({ idx: rows.length, val: child, key: u })
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// The source index breaks every tie, so the order is TOTAL and
|
|
345
|
+
// neither port's sort algorithm can reorder equals.
|
|
346
|
+
rows.sort((a, b) => {
|
|
347
|
+
const c = 'text' === domain ?
|
|
348
|
+
cmpCodePoint(a.key.peg as string, b.key.peg as string) :
|
|
349
|
+
cmpNumeric(a.key, b.key)
|
|
350
|
+
return 0 === c ? a.idx - b.idx : dir * c
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
const peg: Val[] = rows.map(
|
|
354
|
+
(r, i) => r.val.clone(ctx.descend(String(i))))
|
|
355
|
+
|
|
356
|
+
return this.place(new ListVal({ peg }, ctx))
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
296
360
|
|
|
297
|
-
// THE FOLD TO A STRING: `join(coll, sep?)` -- G9 phase 2.
|
|
298
|
-
//
|
|
299
|
-
// The one primitive between a model and a generated file. A spread can
|
|
300
|
-
// put a separator AFTER each element; putting one BETWEEN N elements is
|
|
301
|
-
// a reduction over strings, and the language had none: `sum` is numeric,
|
|
302
|
-
// `+` does not reduce a list, and indexed concatenation needs the arity
|
|
303
|
-
// known in advance. So a generated SQL column list carried a trailing
|
|
304
|
-
// comma and did not parse (use-cases/15-code-generation).
|
|
305
|
-
//
|
|
306
|
-
// IT FOLDS WITH `+` SEEDED WITH `""`, exactly as `sum` folds with `add`
|
|
307
|
-
// seeded with `0`. That is not a figure of speech: the members are
|
|
308
|
-
// rendered by `plusText`, which IS the function `+`'s string branch
|
|
309
|
-
// calls, so the language keeps exactly one answer to "how does a number
|
|
310
|
-
// become text" and the two cannot drift. `join(coll)` is therefore
|
|
311
|
-
// concatenation and no `concat` is needed; `join(coll, "\n")` is lines
|
|
312
|
-
// and no `lines` is needed. ONE BUILTIN, NOT A FAMILY.
|
|
313
|
-
//
|
|
314
|
-
// `split`/`words` is the inverse -- an input-PARSING operation with no
|
|
315
|
-
// generation use -- and is deliberately absent.
|
|
316
|
-
//
|
|
317
|
-
// EMPTY IS `""`, concatenation's identity, the exact parallel of
|
|
318
|
-
// `sum([]) == 0` and the opposite of `least([])`: a fold with an
|
|
319
|
-
// identity can answer the empty bag, and one without has to refuse it.
|
|
320
|
-
//
|
|
321
|
-
// MEMBERS ARE VALIDATED BEFORE THE FOLD, which is the whole reason this
|
|
322
|
-
// is not three lines. `+` with a string on the left RESIDUATES on a
|
|
323
|
-
// container or a null rather than refusing (`"" + {b:1}` and `"x" +
|
|
324
|
-
// null` both reach generation as `mapval_no_gen`), so folding blindly
|
|
325
|
-
// would turn a bad member into a useless late error pointing at the
|
|
326
|
-
// wrong thing. The three verdicts below are that check.
|
|
327
361
|
type MemberVerdict = 'text' | 'never' | 'notyet'
|
|
328
362
|
|
|
329
363
|
|
|
330
|
-
// Which of the three a value is.
|
|
331
|
-
//
|
|
332
|
-
// `text` -- `+` would take it, so the fold can.
|
|
333
|
-
//
|
|
334
|
-
// `never` -- a SETTLED value that will never become text: a map, a
|
|
335
|
-
// list, a null. This is `join_member`, class `conflict`, and it names
|
|
336
|
-
// the member rather than the call, because "one of these is not a
|
|
337
|
-
// string" is only actionable if you are told which.
|
|
338
|
-
//
|
|
339
|
-
// `notyet` -- an unresolved kind, a top, a stable residue. NOT a join
|
|
340
|
-
// failure at all: the call stays residual and generation reports
|
|
341
|
-
// `mapval_no_gen`, class `incomplete`, exactly as docs/trust.md
|
|
342
|
-
// requires ("a stable residue ... is ordinary incompleteness"). Getting
|
|
343
|
-
// this split wrong in either direction is the defect that matters here:
|
|
344
|
-
// refusing a residue makes `join` unusable inside a schema, and
|
|
345
|
-
// deferring on a map makes a real error arrive as a shrug.
|
|
346
364
|
function memberVerdict(v: any): MemberVerdict {
|
|
347
365
|
const u = unpref(v)
|
|
348
366
|
if (undefined !== plusText(u)) {
|
|
@@ -355,15 +373,6 @@ function memberVerdict(v: any): MemberVerdict {
|
|
|
355
373
|
}
|
|
356
374
|
|
|
357
375
|
|
|
358
|
-
// The separator is a STRING or it is nothing.
|
|
359
|
-
//
|
|
360
|
-
// A number would render perfectly well through `+`, and is still
|
|
361
|
-
// refused: the separator is not a member of the fold, it is the
|
|
362
|
-
// parameter naming the text between members, and `join(x, 5)` is far
|
|
363
|
-
// likelier a mistake than an intent to separate with "5". `pick`'s key
|
|
364
|
-
// argument draws the same line for the same reason. This is the
|
|
365
|
-
// direction that can be loosened later without breaking a document;
|
|
366
|
-
// the other direction cannot.
|
|
367
376
|
function sepVerdict(v: any): MemberVerdict {
|
|
368
377
|
const u = unpref(v)
|
|
369
378
|
if (u?.isVal && u.isScalar) {
|
|
@@ -391,22 +400,6 @@ class JoinFuncVal extends FuncBaseVal {
|
|
|
391
400
|
}
|
|
392
401
|
|
|
393
402
|
|
|
394
|
-
// A `make` OVERRIDE, WHERE ITS AggFuncVal SIBLINGS HAVE NONE, and the
|
|
395
|
-
// difference is worth stating because it is not arbitrary. `sum`,
|
|
396
|
-
// `pick` and the rest are staged and nothing more: they residuate
|
|
397
|
-
// before `unify` reaches the rebuild branch, so a `make` there would
|
|
398
|
-
// be unreachable code pretending to be a contract, and the base's
|
|
399
|
-
// `func:<name>` refusal is the loud answer if that ever changes.
|
|
400
|
-
//
|
|
401
|
-
// `join` is the first builtin that is BOTH staged AND defers its
|
|
402
|
-
// resolution (see `deferResolve`). A deferred call has settled
|
|
403
|
-
// arguments, so it does reach the rebuild branch, and without this it
|
|
404
|
-
// raised `func:join` on the first document with an unresolved member
|
|
405
|
-
// — where the Go port residuated and reported `mapval_no_gen`.
|
|
406
|
-
// Opposite answers on `join($.m, ",")` with `m: [string]`, caught by
|
|
407
|
-
// running both engines rather than by either test suite. `super` and
|
|
408
|
-
// bare `id`, the two other deferring calls, each carry the same
|
|
409
|
-
// override for the same reason.
|
|
410
403
|
make(_ctx: AontuContext, spec: ValSpec): Val {
|
|
411
404
|
return new JoinFuncVal(spec)
|
|
412
405
|
}
|
|
@@ -426,10 +419,6 @@ class JoinFuncVal extends FuncBaseVal {
|
|
|
426
419
|
|
|
427
420
|
|
|
428
421
|
unify(peer: Val, ctx: AontuContext): Val {
|
|
429
|
-
// BOTH arguments, unlike `pick`, which drives only its bag: `pick`'s
|
|
430
|
-
// key is a bare word the parser has already made a string, while a
|
|
431
|
-
// separator is an ordinary expression and `join($.rows, $.sep)` has
|
|
432
|
-
// to wait for it.
|
|
433
422
|
const ready = this.driveStagedArgs(ctx, 2)
|
|
434
423
|
|
|
435
424
|
if (!ready || !ctx.settle) {
|
|
@@ -440,10 +429,6 @@ class JoinFuncVal extends FuncBaseVal {
|
|
|
440
429
|
}
|
|
441
430
|
|
|
442
431
|
|
|
443
|
-
// The `notyet` half of the verdict, taken before `resolve` runs: with
|
|
444
|
-
// arguments settled but a member still a kind, the call rides the
|
|
445
|
-
// ordinary args-not-done path and residuates, which is what makes an
|
|
446
|
-
// unresolved member ordinary incompleteness rather than a refusal.
|
|
447
432
|
deferResolve(ctx: AontuContext, args?: Val[]): boolean {
|
|
448
433
|
const children = bagChildren(args?.[0], ctx)
|
|
449
434
|
if (undefined === children) {
|
|
@@ -467,28 +452,11 @@ class JoinFuncVal extends FuncBaseVal {
|
|
|
467
452
|
'join'))
|
|
468
453
|
}
|
|
469
454
|
|
|
470
|
-
// Arity is [1,2], so a second argument is present or the separator
|
|
471
|
-
// is the empty string -- which makes `join(coll)` concatenation.
|
|
472
|
-
// The separator's SHAPE is settled before resolve runs: the
|
|
473
|
-
// signature gate refuses every concrete non-string
|
|
474
|
-
// (docs/design/SIGNATURES.0.md; join(d: map|list, sep?: string)),
|
|
475
|
-
// and deferResolve holds the call while sepVerdict answers notyet
|
|
476
|
-
// -- so a present separator here is text.
|
|
477
455
|
let sep = ''
|
|
478
456
|
if (1 < args.length) {
|
|
479
457
|
sep = unpref(args[1]).peg
|
|
480
458
|
}
|
|
481
459
|
|
|
482
|
-
// NO NIL-MEMBER GUARD, WHERE `sum` HAS ONE, and the difference is
|
|
483
|
-
// the fold's shape rather than an oversight. `sum` folds with
|
|
484
|
-
// `arith`, which MINTS a nil part-way through — a non-numeric child
|
|
485
|
-
// or an overflow — so it has to stop and return it. `join` folds
|
|
486
|
-
// already-unified values, and a nil among a list's elements
|
|
487
|
-
// collapses the list before this call resolves: `join([least([])],
|
|
488
|
-
// ",")` reports `aggregate_empty` at the member's own path,
|
|
489
|
-
// `$.o.0`, and never reaches here. A guard was written, the
|
|
490
|
-
// ADR-002 gate found it unexecuted, and probing confirmed no
|
|
491
|
-
// spelling reaches it, so it is removed rather than excused.
|
|
492
460
|
const parts: string[] = []
|
|
493
461
|
for (const child of children) {
|
|
494
462
|
const u: any = unpref(child)
|
|
@@ -500,16 +468,11 @@ class JoinFuncVal extends FuncBaseVal {
|
|
|
500
468
|
parts.push(text)
|
|
501
469
|
}
|
|
502
470
|
|
|
503
|
-
// Every part is already a string, so this IS the `+` fold seeded
|
|
504
|
-
// with `""` -- written as one concatenation because a loop of `+`
|
|
505
|
-
// over settled strings cannot differ from it, and because the Go
|
|
506
|
-
// twin's strings.Join must produce the same bytes.
|
|
507
471
|
return this.place(new StringVal({ peg: parts.join(sep) }))
|
|
508
472
|
}
|
|
509
473
|
}
|
|
510
474
|
|
|
511
475
|
|
|
512
|
-
// The three the registry names. Each is its operation and nothing else.
|
|
513
476
|
class SumFuncVal extends AggFuncVal {
|
|
514
477
|
constructor(spec: ValSpec, ctx?: AontuContext) { super(spec, ctx, 'sum') }
|
|
515
478
|
}
|
|
@@ -522,13 +485,14 @@ class GreatestFuncVal extends AggFuncVal {
|
|
|
522
485
|
constructor(spec: ValSpec, ctx?: AontuContext) {
|
|
523
486
|
super(spec, ctx, 'greatest')
|
|
524
487
|
}
|
|
525
|
-
} /* node:coverage ignore next
|
|
488
|
+
} /* node:coverage ignore next 11 */
|
|
526
489
|
|
|
527
490
|
|
|
528
491
|
export {
|
|
529
492
|
AggFuncVal,
|
|
530
493
|
JoinFuncVal,
|
|
531
494
|
PickFuncVal,
|
|
495
|
+
SortFuncVal,
|
|
532
496
|
SumFuncVal,
|
|
533
497
|
LeastFuncVal,
|
|
534
498
|
GreatestFuncVal,
|