aontu 0.52.1 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/bin/aontu-mcp.js +4 -0
- package/dist/agentsmd.d.ts +16 -0
- package/dist/agentsmd.js +107 -0
- package/dist/agentsmd.js.map +1 -0
- package/dist/aontu.d.ts +15 -3
- package/dist/aontu.js +126 -5
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +45 -1
- package/dist/cli.js +2845 -69
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +18 -0
- package/dist/ctx.js +44 -0
- package/dist/ctx.js.map +1 -1
- package/dist/diff.d.ts +22 -0
- package/dist/diff.js +141 -0
- package/dist/diff.js.map +1 -0
- package/dist/err.d.ts +3 -1
- package/dist/err.js +45 -8
- package/dist/err.js.map +1 -1
- package/dist/graph.d.ts +13 -0
- package/dist/graph.js +110 -0
- package/dist/graph.js.map +1 -0
- package/dist/hcanon.d.ts +3 -0
- package/dist/hcanon.js +145 -0
- package/dist/hcanon.js.map +1 -0
- package/dist/hints.js +235 -8
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.d.ts +20 -0
- package/dist/jsonschema.js +425 -0
- package/dist/jsonschema.js.map +1 -0
- package/dist/lang.js +955 -110
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +9 -2
- package/dist/lsp.js +343 -48
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.d.ts +20 -0
- package/dist/mcp-server.js +147 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp.d.ts +42 -0
- package/dist/mcp.js +941 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mod-tool.d.ts +58 -0
- package/dist/mod-tool.js +532 -0
- package/dist/mod-tool.js.map +1 -0
- package/dist/mod.d.ts +35 -0
- package/dist/mod.js +345 -0
- package/dist/mod.js.map +1 -0
- package/dist/patch.d.ts +49 -0
- package/dist/patch.js +506 -0
- package/dist/patch.js.map +1 -0
- package/dist/provenance.d.ts +41 -0
- package/dist/provenance.js +336 -0
- package/dist/provenance.js.map +1 -0
- package/dist/query.d.ts +27 -0
- package/dist/query.js +294 -0
- package/dist/query.js.map +1 -0
- package/dist/reach.d.ts +15 -0
- package/dist/reach.js +168 -0
- package/dist/reach.js.map +1 -0
- package/dist/relation.d.ts +23 -0
- package/dist/relation.js +230 -0
- package/dist/relation.js.map +1 -0
- package/dist/report-sarif.d.ts +14 -0
- package/dist/report-sarif.js +102 -0
- package/dist/report-sarif.js.map +1 -0
- package/dist/sig.d.ts +25 -0
- package/dist/sig.js +277 -0
- package/dist/sig.js.map +1 -0
- package/dist/sigdecl.d.ts +2 -0
- package/dist/sigdecl.js +11 -0
- package/dist/sigdecl.js.map +1 -0
- package/dist/siggate.d.ts +4 -0
- package/dist/siggate.js +90 -0
- package/dist/siggate.js.map +1 -0
- package/dist/site.d.ts +4 -0
- package/dist/site.js +31 -0
- package/dist/site.js.map +1 -1
- package/dist/std.d.ts +1 -0
- package/dist/std.js +129 -0
- package/dist/std.js.map +1 -0
- package/dist/subsume.d.ts +39 -0
- package/dist/subsume.js +573 -0
- package/dist/subsume.js.map +1 -0
- package/dist/trim.d.ts +19 -0
- package/dist/trim.js +155 -0
- package/dist/trim.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +17 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.d.ts +2 -1
- package/dist/unify.js +234 -38
- package/dist/unify.js.map +1 -1
- package/dist/utility.d.ts +8 -1
- package/dist/utility.js +68 -1
- package/dist/utility.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +44 -0
- package/dist/val/AggFuncVal.js +364 -0
- package/dist/val/AggFuncVal.js.map +1 -0
- package/dist/val/ArithFuncVal.d.ts +31 -0
- package/dist/val/ArithFuncVal.js +62 -0
- package/dist/val/ArithFuncVal.js.map +1 -0
- package/dist/val/BagVal.d.ts +7 -0
- package/dist/val/BagVal.js +150 -11
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +9 -1
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.d.ts +1 -1
- package/dist/val/ConjunctVal.js +19 -0
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.d.ts +7 -1
- package/dist/val/ConstraintVal.js +523 -45
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.d.ts +35 -0
- package/dist/val/ContainerKindVal.js +99 -0
- package/dist/val/ContainerKindVal.js.map +1 -0
- package/dist/val/CopyFuncVal.d.ts +1 -2
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.d.ts +1 -0
- package/dist/val/Decimal.js +13 -0
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.d.ts +11 -0
- package/dist/val/DeprecateFuncVal.js +47 -0
- package/dist/val/DeprecateFuncVal.js.map +1 -0
- package/dist/val/DisjunctVal.d.ts +1 -2
- package/dist/val/DisjunctVal.js +269 -53
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.d.ts +15 -0
- package/dist/val/EachFuncVal.js +75 -0
- package/dist/val/EachFuncVal.js.map +1 -0
- package/dist/val/ExpectVal.js +62 -4
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FeatureVal.js +1 -1
- package/dist/val/FeatureVal.js.map +1 -1
- package/dist/val/FilterFuncVal.d.ts +15 -0
- package/dist/val/FilterFuncVal.js +91 -0
- package/dist/val/FilterFuncVal.js.map +1 -0
- package/dist/val/FuncBaseVal.d.ts +7 -1
- package/dist/val/FuncBaseVal.js +183 -2
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.d.ts +39 -0
- package/dist/val/GraphAtomVal.js +184 -0
- package/dist/val/GraphAtomVal.js.map +1 -0
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/JunctionVal.js +24 -1
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.d.ts +1 -1
- package/dist/val/KeyFuncVal.js +38 -30
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +94 -14
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +1 -0
- package/dist/val/MapVal.js +146 -14
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.d.ts +15 -0
- package/dist/val/MatchFuncVal.js +107 -0
- package/dist/val/MatchFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +24 -0
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -1
- package/dist/val/OpBaseVal.js +19 -1
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/OpenFuncVal.js +4 -1
- package/dist/val/OpenFuncVal.js.map +1 -1
- package/dist/val/PackFuncVal.d.ts +15 -0
- package/dist/val/PackFuncVal.js +108 -0
- package/dist/val/PackFuncVal.js.map +1 -0
- package/dist/val/PathFuncVal.d.ts +2 -2
- package/dist/val/PathFuncVal.js +75 -16
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.d.ts +25 -0
- package/dist/val/PathVal.js +150 -0
- package/dist/val/PathVal.js.map +1 -0
- package/dist/val/PlaceVal.d.ts +13 -0
- package/dist/val/PlaceVal.js +131 -0
- package/dist/val/PlaceVal.js.map +1 -0
- package/dist/val/PlusOpVal.d.ts +2 -1
- package/dist/val/PlusOpVal.js +61 -37
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +4 -2
- package/dist/val/PrefVal.js +223 -41
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.d.ts +19 -0
- package/dist/val/RecurseVal.js +217 -0
- package/dist/val/RecurseVal.js.map +1 -0
- package/dist/val/RefVal.d.ts +3 -2
- package/dist/val/RefVal.js +181 -66
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.d.ts +59 -0
- package/dist/val/ReferFuncVal.js +604 -0
- package/dist/val/ReferFuncVal.js.map +1 -0
- package/dist/val/ScalarKindVal.d.ts +4 -3
- package/dist/val/ScalarKindVal.js +12 -12
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/SuperFuncVal.d.ts +4 -2
- package/dist/val/SuperFuncVal.js +118 -14
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.d.ts +1 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TypeFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +7 -1
- package/dist/val/Val.js +170 -2
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.d.ts +6 -0
- package/dist/val/arith.js +173 -0
- package/dist/val/arith.js.map +1 -0
- package/dist/vet.d.ts +45 -0
- package/dist/vet.js +802 -0
- package/dist/vet.js.map +1 -0
- package/dist/view.d.ts +85 -0
- package/dist/view.js +1948 -0
- package/dist/view.js.map +1 -0
- package/dist/walk.d.ts +2 -0
- package/dist/walk.js +91 -0
- package/dist/walk.js.map +1 -0
- package/grammar/aontu.gbnf +138 -0
- package/grammar/aontu.lark +118 -0
- package/grammar/aontu.tmLanguage.json +184 -0
- package/package.json +30 -8
- package/skill/SKILL.md +37 -0
- package/skill/error-codes.md +62 -0
- package/skill/examples.md +99 -0
- package/skill/grammar-card.md +56 -0
- package/src/agentsmd.ts +135 -0
- package/src/aontu.ts +166 -5
- package/src/cli.ts +3324 -72
- package/src/ctx.ts +93 -1
- package/src/diff.ts +198 -0
- package/src/err.ts +50 -8
- package/src/graph.ts +185 -0
- package/src/hcanon.ts +167 -0
- package/src/hints.ts +291 -8
- package/src/jsonschema.ts +552 -0
- package/src/lang.ts +1049 -110
- package/src/lsp.ts +351 -49
- package/src/mcp-server.ts +187 -0
- package/src/mcp.ts +1130 -0
- package/src/mod-tool.ts +718 -0
- package/src/mod.ts +453 -0
- package/src/patch.ts +628 -0
- package/src/provenance.ts +437 -0
- package/src/query.ts +381 -0
- package/src/reach.ts +213 -0
- package/src/relation.ts +298 -0
- package/src/report-sarif.ts +137 -0
- package/src/sig.ts +345 -0
- package/src/sigdecl.ts +11 -0
- package/src/siggate.ts +144 -0
- package/src/site.ts +36 -1
- package/src/std.ts +131 -0
- package/src/subsume.ts +739 -0
- package/src/trim.ts +195 -0
- package/src/tsconfig.json +10 -4
- package/src/type.ts +51 -2
- package/src/unify.ts +253 -38
- package/src/utility.ts +79 -1
- package/src/val/AggFuncVal.ts +546 -0
- package/src/val/ArithFuncVal.ts +108 -0
- package/src/val/BagVal.ts +158 -12
- package/src/val/CloseFuncVal.ts +9 -1
- package/src/val/ConjunctVal.ts +20 -0
- package/src/val/ConstraintVal.ts +576 -48
- package/src/val/ContainerKindVal.ts +158 -0
- package/src/val/CopyFuncVal.ts +0 -1
- package/src/val/Decimal.ts +15 -0
- package/src/val/DeprecateFuncVal.ts +84 -0
- package/src/val/DisjunctVal.ts +282 -59
- package/src/val/EachFuncVal.ts +133 -0
- package/src/val/ExpectVal.ts +65 -8
- package/src/val/FeatureVal.ts +1 -1
- package/src/val/FilterFuncVal.ts +154 -0
- package/src/val/FuncBaseVal.ts +206 -2
- package/src/val/GraphAtomVal.ts +264 -0
- package/src/val/HideFuncVal.ts +0 -2
- package/src/val/JunctionVal.ts +24 -1
- package/src/val/KeyFuncVal.ts +39 -35
- package/src/val/ListVal.ts +101 -15
- package/src/val/LowerFuncVal.ts +0 -1
- package/src/val/MapVal.ts +153 -14
- package/src/val/MatchFuncVal.ts +176 -0
- package/src/val/MoveFuncVal.ts +0 -2
- package/src/val/NilVal.ts +25 -0
- package/src/val/OpBaseVal.ts +20 -1
- package/src/val/OpenFuncVal.ts +4 -2
- package/src/val/PackFuncVal.ts +175 -0
- package/src/val/PathFuncVal.ts +107 -20
- package/src/val/PathVal.ts +221 -0
- package/src/val/PlaceVal.ts +193 -0
- package/src/val/PlusOpVal.ts +67 -39
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +251 -60
- package/src/val/RecurseVal.ts +285 -0
- package/src/val/RefVal.ts +183 -78
- package/src/val/ReferFuncVal.ts +732 -0
- package/src/val/ScalarKindVal.ts +12 -13
- package/src/val/SuperFuncVal.ts +137 -13
- package/src/val/TopVal.ts +1 -2
- package/src/val/TypeFuncVal.ts +0 -2
- package/src/val/UpperFuncVal.ts +0 -1
- package/src/val/Val.ts +216 -4
- package/src/val/VarVal.ts +0 -1
- package/src/val/arith.ts +319 -0
- package/src/vet.ts +1025 -0
- package/src/view.ts +2563 -0
- package/src/walk.ts +99 -0
|
@@ -3,11 +3,14 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.MustConstraintVal = exports.UniqueConstraintVal = exports.LengthConstraintVal = exports.ReConstraintVal = exports.NeqConstraintVal = exports.BelowConstraintVal = exports.AboveConstraintVal = exports.MaxConstraintVal = exports.MinConstraintVal = exports.ConstraintVal = void 0;
|
|
5
5
|
exports.normaliseRe = normaliseRe;
|
|
6
|
+
exports.constraintSubsumesConstraint = constraintSubsumesConstraint;
|
|
7
|
+
exports.constraintAdmitsScalar = constraintAdmitsScalar;
|
|
6
8
|
const type_1 = require("../type");
|
|
7
9
|
const utility_1 = require("../utility");
|
|
8
10
|
const Val_1 = require("./Val");
|
|
9
11
|
const keyorder_1 = require("../keyorder");
|
|
10
12
|
const ConjunctVal_1 = require("./ConjunctVal");
|
|
13
|
+
const BagVal_1 = require("./BagVal");
|
|
11
14
|
const top_1 = require("./top");
|
|
12
15
|
const unify_1 = require("../unify");
|
|
13
16
|
const IntegerVal_1 = require("./IntegerVal");
|
|
@@ -71,6 +74,17 @@ const numcmp_1 = require("./numcmp");
|
|
|
71
74
|
// This function is mirrored statement for statement in go/constraint.go,
|
|
72
75
|
// and `test/spec/files/regex-corpus.txt` pins that the two produce
|
|
73
76
|
// byte-identical output for every pattern in a generated corpus.
|
|
77
|
+
// The largest repeat count Aontu's pattern language admits. RE2 caps a
|
|
78
|
+
// repeat at 1000 and refuses to compile past it, where JavaScript's
|
|
79
|
+
// backtracking engine accepts any count -- so `re("^a{1001}$")` was
|
|
80
|
+
// VALID in TypeScript and an `error` verdict in Go, a cross-port
|
|
81
|
+
// verdict flip on a schema an agent could be handed
|
|
82
|
+
// (status-2026-08-21.md section 4). Under ADR-003 the host's own
|
|
83
|
+
// compile failure must never be what a user sees, so the bound is
|
|
84
|
+
// Aontu's: at most RE_REPEAT_MAX, refused identically in both ports
|
|
85
|
+
// before either engine compiles. Nested products beyond the cap were
|
|
86
|
+
// already refused, by the quantified-group rule below.
|
|
87
|
+
const RE_REPEAT_MAX = 1000;
|
|
74
88
|
// The normative expansions. These are Aontu's definitions, not either
|
|
75
89
|
// host's; both hosts are rewritten to them.
|
|
76
90
|
const RE_CLASS_DIGIT = '0-9';
|
|
@@ -84,6 +98,65 @@ const RE_ESCAPE_PUNCT = '\\.+*?()[]{}|^$/';
|
|
|
84
98
|
// Escapes passed through unchanged: the control characters, the ASCII
|
|
85
99
|
// word boundary, and `\xHH`. Each was probed in both engines.
|
|
86
100
|
const RE_ESCAPE_PASS = 'tnrfv';
|
|
101
|
+
// A counted quantifier `{n}`, `{n,}` or `{n,m}` starting at `src[at]`.
|
|
102
|
+
// Two rules, both about what the hosts do NOT share:
|
|
103
|
+
//
|
|
104
|
+
// - A bound above RE_REPEAT_MAX: RE2 refuses to compile it, JavaScript
|
|
105
|
+
// accepts it.
|
|
106
|
+
// - A brace that does not open a well-formed counted quantifier at
|
|
107
|
+
// all (`x{y}`, `a{`, `{,5}`): JavaScript compiled with the `u` flag
|
|
108
|
+
// makes it a SYNTAX ERROR, while RE2 reads it as a literal brace, so
|
|
109
|
+
// `re("^x{y}$")` was `constraint_pattern` in TypeScript and a plain
|
|
110
|
+
// unresolved value in Go -- the same class of verdict flip as the
|
|
111
|
+
// repeat cap, found by sweeping around it. A literal brace is
|
|
112
|
+
// written escaped (`a\\{1001\\}`), which both engines share.
|
|
113
|
+
function repeatWhy(src, at) {
|
|
114
|
+
const bad = (what) => [
|
|
115
|
+
'a ' + what + ', which the two engines do not read the same way', -1
|
|
116
|
+
];
|
|
117
|
+
let i = at + 1;
|
|
118
|
+
let digits = '';
|
|
119
|
+
const bounds = [];
|
|
120
|
+
let commas = 0;
|
|
121
|
+
for (; i < src.length; i++) {
|
|
122
|
+
const c = src[i];
|
|
123
|
+
if ('0' <= c && c <= '9') {
|
|
124
|
+
digits += c;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (',' === c) {
|
|
128
|
+
if ('' === digits || 0 < commas) {
|
|
129
|
+
return bad('{ that does not open a counted quantifier');
|
|
130
|
+
}
|
|
131
|
+
bounds.push(parseInt(digits, 10));
|
|
132
|
+
digits = '';
|
|
133
|
+
commas++;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if ('}' === c) {
|
|
137
|
+
if ('' !== digits) {
|
|
138
|
+
bounds.push(parseInt(digits, 10));
|
|
139
|
+
}
|
|
140
|
+
else if (0 === commas) {
|
|
141
|
+
return bad('{ that does not open a counted quantifier');
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
return bad('{ that does not open a counted quantifier');
|
|
146
|
+
}
|
|
147
|
+
if (i >= src.length) {
|
|
148
|
+
return bad('{ that does not open a counted quantifier');
|
|
149
|
+
}
|
|
150
|
+
for (const b of bounds) {
|
|
151
|
+
if (RE_REPEAT_MAX < b) {
|
|
152
|
+
return ['a repeat count above ' + RE_REPEAT_MAX +
|
|
153
|
+
', which RE2 refuses to compile', -1];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// A descending range (`{5,2}`) is refused by both engines' own
|
|
157
|
+
// compilers, so it needs no rule here.
|
|
158
|
+
return ['', i];
|
|
159
|
+
}
|
|
87
160
|
function isHexDigit(c) {
|
|
88
161
|
return null != c && (('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'));
|
|
89
162
|
}
|
|
@@ -178,14 +251,32 @@ function normaliseRe(src) {
|
|
|
178
251
|
groups[groups.length - 1][k] = true;
|
|
179
252
|
}
|
|
180
253
|
};
|
|
254
|
+
// Where the counted quantifier validated below closes, so its own
|
|
255
|
+
// `}` is told apart from a stray one; and whether the atom just
|
|
256
|
+
// emitted was `^` or `$`, which cannot be quantified.
|
|
257
|
+
let repeatEnd = -1;
|
|
258
|
+
let anchorPrev = false;
|
|
181
259
|
for (let i = 0; i < src.length; i++) {
|
|
182
260
|
const c = src[i];
|
|
261
|
+
const afterAnchor = anchorPrev;
|
|
262
|
+
anchorPrev = false;
|
|
183
263
|
if ('\\' === c) {
|
|
184
264
|
const [emit, why, extra] = normaliseEscape(src[i + 1], src, i, inClass);
|
|
185
265
|
if ('' !== why) {
|
|
186
266
|
return ['', why];
|
|
187
267
|
}
|
|
188
268
|
out.push(emit);
|
|
269
|
+
// `\b` AND `\B` ARE ASSERTIONS TOO, and quantifying one is the
|
|
270
|
+
// same disagreement `^{1}` is: JavaScript under `u` calls it a
|
|
271
|
+
// syntax error, RE2 quantifies the assertion happily. The rule
|
|
272
|
+
// said "`\b` and `\B` quantify identically in both and are left
|
|
273
|
+
// alone" and that was measured wrong -- `re("\\b{1}x")` is
|
|
274
|
+
// `constraint_pattern` in TypeScript and an accepted schema in
|
|
275
|
+
// Go. Only OUTSIDE a class: inside one, `\b` is a backspace in
|
|
276
|
+
// both engines and repeats like any other character.
|
|
277
|
+
if (!inClass && ('b' === src[i + 1] || 'B' === src[i + 1])) {
|
|
278
|
+
anchorPrev = true;
|
|
279
|
+
}
|
|
189
280
|
i += 1 + extra;
|
|
190
281
|
continue;
|
|
191
282
|
}
|
|
@@ -256,11 +347,41 @@ function normaliseRe(src) {
|
|
|
256
347
|
continue;
|
|
257
348
|
}
|
|
258
349
|
if ('*' === c || '+' === c || '?' === c || '{' === c) {
|
|
350
|
+
// Nothing to repeat. JavaScript under the `u` flag makes this a
|
|
351
|
+
// SYNTAX ERROR, where RE2 quantifies the assertion happily and
|
|
352
|
+
// matches, so `re("^{1}")` was `constraint_pattern` in
|
|
353
|
+
// TypeScript and an accepted schema in Go. The four assertions
|
|
354
|
+
// behave alike here -- `^`, `$`, `\b` and `\B` -- so the rule
|
|
355
|
+
// names all four.
|
|
356
|
+
if (afterAnchor) {
|
|
357
|
+
return ['', 'a quantifier applied to `^`, `$`, `\\b` or ' +
|
|
358
|
+
'`\\B`, which has nothing to repeat'];
|
|
359
|
+
}
|
|
360
|
+
if ('{' === c) {
|
|
361
|
+
const [why, end] = repeatWhy(src, i);
|
|
362
|
+
if ('' !== why) {
|
|
363
|
+
return ['', why];
|
|
364
|
+
}
|
|
365
|
+
repeatEnd = end;
|
|
366
|
+
}
|
|
259
367
|
mark('q');
|
|
260
368
|
out.push(c);
|
|
261
369
|
continue;
|
|
262
370
|
}
|
|
371
|
+
// A `}` that closes no counted quantifier: JavaScript under `u`
|
|
372
|
+
// refuses it as a lone quantifier bracket, RE2 reads it as a
|
|
373
|
+
// literal. Written escaped inside a class (`[}]`) it is a literal
|
|
374
|
+
// in both, which is the spelling that survives.
|
|
375
|
+
if ('}' === c) {
|
|
376
|
+
if (i !== repeatEnd) {
|
|
377
|
+
return ['', 'a `}` that closes no counted quantifier, which ' +
|
|
378
|
+
'the two engines do not read the same way'];
|
|
379
|
+
}
|
|
380
|
+
out.push(c);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
263
383
|
out.push(c);
|
|
384
|
+
anchorPrev = ('^' === c || '$' === c);
|
|
264
385
|
}
|
|
265
386
|
if (inClass) {
|
|
266
387
|
return ['', 'an unterminated character class'];
|
|
@@ -280,6 +401,15 @@ function numericLeaf(v) {
|
|
|
280
401
|
function stringLeaf(v) {
|
|
281
402
|
return true === v?.isScalar && 'string' === typeof v.peg && v.isString;
|
|
282
403
|
}
|
|
404
|
+
// The VALUE-admission widening of stringLeaf: a path value is a string
|
|
405
|
+
// with more structure (path <: string, ADR-016), so the string-domain
|
|
406
|
+
// constraints admit one -- `refer() & re("auth$")` must still check a
|
|
407
|
+
// link whose value is now a path. The ARGUMENT sites keep the strict
|
|
408
|
+
// test: a pattern, a bound or an exclusion is spelled with a plain
|
|
409
|
+
// string, never a path.
|
|
410
|
+
function stringishLeaf(v) {
|
|
411
|
+
return stringLeaf(v) || (true === v?.isScalar && true === v.isPath);
|
|
412
|
+
}
|
|
283
413
|
// Scalar identity: leaf AND value, the lattice's own rule (1 and 1.0
|
|
284
414
|
// are different scalars). Exact numeric comparison decides the value
|
|
285
415
|
// half for numeric leaves.
|
|
@@ -287,6 +417,12 @@ function sameScalar(a, b) {
|
|
|
287
417
|
if (numericLeaf(a) && numericLeaf(b)) {
|
|
288
418
|
return (0, numcmp_1.towerRank)(a) === (0, numcmp_1.towerRank)(b) && 0 === (0, numcmp_1.cmpNumeric)(a, b);
|
|
289
419
|
}
|
|
420
|
+
if (true === a?.isPath || true === b?.isPath) {
|
|
421
|
+
// Path identity is kind AND spelling (ADR-016): `neq(path($.x))`
|
|
422
|
+
// excludes exactly that address, and never a plain string that
|
|
423
|
+
// happens to spell it.
|
|
424
|
+
return true === a?.isPath && true === b?.isPath && a.peg === b.peg;
|
|
425
|
+
}
|
|
290
426
|
if (stringLeaf(a) && stringLeaf(b)) {
|
|
291
427
|
return a.peg === b.peg;
|
|
292
428
|
}
|
|
@@ -328,6 +464,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
328
464
|
this.neqs = [];
|
|
329
465
|
this.res = [];
|
|
330
466
|
this.uniq = false;
|
|
467
|
+
this.uniqBy = [];
|
|
331
468
|
this.musts = [];
|
|
332
469
|
if (spec.state) {
|
|
333
470
|
this.domain = spec.state.domain;
|
|
@@ -340,6 +477,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
340
477
|
this.res = spec.state.res ?? [];
|
|
341
478
|
this.count = spec.state.count;
|
|
342
479
|
this.uniq = spec.state.uniq ?? false;
|
|
480
|
+
this.uniqBy = spec.state.uniqBy ?? [];
|
|
343
481
|
this.musts = spec.state.musts ?? [];
|
|
344
482
|
this.invalid = spec.state.invalid;
|
|
345
483
|
}
|
|
@@ -351,14 +489,22 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
351
489
|
// in unify, where there is a ctx to resolve through, and the
|
|
352
490
|
// residual is built from the settled arguments. Only a settled
|
|
353
491
|
// argument of the wrong shape is an `invalid-arg`.
|
|
354
|
-
|
|
492
|
+
// The effectful-argument refusal happens HERE, on the written
|
|
493
|
+
// form, and not in fromAtom: settling is what runs the effect, so
|
|
494
|
+
// by the time fromAtom sees a settled `move($.b)` the move has
|
|
495
|
+
// already happened and the argument is just its result.
|
|
496
|
+
if ('must' === spec.atom && args.some((a) => holdsMove(a))) {
|
|
497
|
+
this.invalid = 'invalid-arg';
|
|
498
|
+
}
|
|
499
|
+
else if (args.some((a) => true !== a?.done)) {
|
|
355
500
|
this.pending = { atom: spec.atom, args };
|
|
356
501
|
}
|
|
357
502
|
else {
|
|
358
503
|
this.fromAtom(spec.atom, args);
|
|
359
504
|
}
|
|
360
505
|
}
|
|
361
|
-
if (null != this.count || this.uniq || 0 < this.
|
|
506
|
+
if (null != this.count || this.uniq || 0 < this.uniqBy.length ||
|
|
507
|
+
0 < this.musts.length ||
|
|
362
508
|
(null != this.pending && lateAtom(this.pending.atom))) {
|
|
363
509
|
this.cjo = LATE_CJO;
|
|
364
510
|
}
|
|
@@ -377,17 +523,30 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
377
523
|
// so a shape this cannot use is a genuine `invalid-arg`, not a
|
|
378
524
|
// not-yet.
|
|
379
525
|
fromAtom(atom, args) {
|
|
380
|
-
// Mark the residual invalid
|
|
381
|
-
//
|
|
526
|
+
// Mark the residual invalid. It answers NOTHING: every caller
|
|
527
|
+
// spells its exit `return bad(...)` and none reads a value, so a
|
|
528
|
+
// return type would be a value nobody consumes -- and a routine
|
|
529
|
+
// that answers on some paths and not others is the shape a reader
|
|
530
|
+
// (and a static analyser) has to stop and check.
|
|
382
531
|
const bad = (why) => {
|
|
383
532
|
this.invalid = why;
|
|
384
|
-
return true;
|
|
385
533
|
};
|
|
386
|
-
// `unique()`
|
|
387
|
-
//
|
|
388
|
-
//
|
|
534
|
+
// `unique()` is a property of the container -- not a comparison
|
|
535
|
+
// against a value -- so with no argument it says the members are
|
|
536
|
+
// pairwise DISTINCT. THE ONE ARGUMENT IS A PROJECTOR (the review's
|
|
537
|
+
// finding I: "unique()-by-field is reserved but absent", and the
|
|
538
|
+
// arity was reserved for exactly this): `unique(port)` says no two
|
|
539
|
+
// members share a `port`, which is how "no two services share a
|
|
540
|
+
// port" and "event ids are unique" are said.
|
|
389
541
|
if ('unique' === atom) {
|
|
390
|
-
|
|
542
|
+
if (0 === args.length) {
|
|
543
|
+
this.uniq = true;
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (1 !== args.length || !stringLeaf(args[0])) {
|
|
547
|
+
return bad('invalid-arg');
|
|
548
|
+
}
|
|
549
|
+
this.uniqBy = [args[0].peg];
|
|
391
550
|
return;
|
|
392
551
|
}
|
|
393
552
|
// `must(c, msg)` is Band B: an evaluate-only check against the
|
|
@@ -411,6 +570,9 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
411
570
|
if (holdsNil(args[0])) {
|
|
412
571
|
return bad('invalid-arg');
|
|
413
572
|
}
|
|
573
|
+
// (An effectful argument is refused at construction, in the
|
|
574
|
+
// constructor: by the time this arm sees a settled `move($.b)`
|
|
575
|
+
// the move has already run.)
|
|
414
576
|
this.musts = [{ v: args[0], msg: args[1] }];
|
|
415
577
|
return;
|
|
416
578
|
}
|
|
@@ -420,7 +582,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
420
582
|
}
|
|
421
583
|
let domain = undefined;
|
|
422
584
|
for (const a of args) {
|
|
423
|
-
const d = numericLeaf(a) ? 'number' :
|
|
585
|
+
const d = numericLeaf(a) ? 'number' : stringishLeaf(a) ? 'string' : undefined;
|
|
424
586
|
if (null == d || (null != domain && d !== domain)) {
|
|
425
587
|
return bad('invalid-arg');
|
|
426
588
|
}
|
|
@@ -499,7 +661,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
499
661
|
}
|
|
500
662
|
return;
|
|
501
663
|
}
|
|
502
|
-
const domain = numericLeaf(a) ? 'number' :
|
|
664
|
+
const domain = numericLeaf(a) ? 'number' : stringishLeaf(a) ? 'string' : undefined;
|
|
503
665
|
if (null == domain) {
|
|
504
666
|
return bad('invalid-arg');
|
|
505
667
|
}
|
|
@@ -518,7 +680,20 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
518
680
|
// Every branch of the ladder assigns, so no initialiser: a
|
|
519
681
|
// residual is stable and the ladder is total.
|
|
520
682
|
let out;
|
|
521
|
-
|
|
683
|
+
// A rel residual or a graph atom HOLDS constraints for the value
|
|
684
|
+
// they stand for (RELATIONS P1/P2): hand the drive over before
|
|
685
|
+
// any settling, exactly as a container does, so `rel(t) & re(x)`
|
|
686
|
+
// reads the same in either order. Reached through the INCLUDE
|
|
687
|
+
// flow (a loaded schema's conjunct re-drives with the constraint
|
|
688
|
+
// on the left) -- inline documents route rel/atom peers through
|
|
689
|
+
// unite's b-drives first, which is why use-cases/12-relations
|
|
690
|
+
// refused without this arm while every inline row stayed green;
|
|
691
|
+
// pinned by constraint-hands-drive-to-rel-and-atom.
|
|
692
|
+
if (true === peer?.isRel
|
|
693
|
+
|| true === peer?.isGraphAtom) {
|
|
694
|
+
out = peer.unify(this, ctx);
|
|
695
|
+
}
|
|
696
|
+
else if (null != this.pending) {
|
|
522
697
|
out = this.settle(peer, ctx);
|
|
523
698
|
}
|
|
524
699
|
else if (null != this.invalid) {
|
|
@@ -609,16 +784,17 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
609
784
|
if (null == peer || peer.isTop) {
|
|
610
785
|
return again;
|
|
611
786
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
787
|
+
// No nil-peer arm: `unite` returns a nil operand before dispatching
|
|
788
|
+
// to any Val's unify (ts/src/unify.ts), so a nil never reaches here
|
|
789
|
+
// — and were one to, the conjunct below folds to it unchanged.
|
|
615
790
|
return new ConjunctVal_1.ConjunctVal({ peg: [again, peer] }, ctx);
|
|
616
791
|
}
|
|
617
792
|
// Membership: the peer scalar passes every part of the residual, or
|
|
618
793
|
// the whole meet is a located conflict.
|
|
619
794
|
admit(peer, ctx) {
|
|
620
|
-
// No scalar has members, so a `unique()` residual admits none
|
|
621
|
-
|
|
795
|
+
// No scalar has members, so a `unique()` residual admits none --
|
|
796
|
+
// and neither does a `unique(k)` one, for the same reason.
|
|
797
|
+
if (this.uniq || 0 < this.uniqBy.length) {
|
|
622
798
|
return this.fail(ctx, peer);
|
|
623
799
|
}
|
|
624
800
|
if (!stateAdmits(this, peer)) {
|
|
@@ -628,14 +804,17 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
628
804
|
// Only a string among the scalars has a length, and it is counted
|
|
629
805
|
// in CODE POINTS -- not UTF-16 units (this host's native count)
|
|
630
806
|
// and not bytes (Go's). Iterating a string yields code points.
|
|
631
|
-
if (!
|
|
807
|
+
if (!stringishLeaf(peer)) {
|
|
632
808
|
return this.fail(ctx, peer);
|
|
633
809
|
}
|
|
634
810
|
if (!stateAdmits(this.count, countVal([...peer.peg].length))) {
|
|
635
811
|
return this.fail(ctx, peer);
|
|
636
812
|
}
|
|
637
813
|
}
|
|
638
|
-
|
|
814
|
+
// A SCALAR HAS NO MEMBERS to accumulate, so its musts are decided
|
|
815
|
+
// here and never residuate: the final reading is the only reading
|
|
816
|
+
// a scalar has.
|
|
817
|
+
const bad = this.checkMusts(peer, ctx, true);
|
|
639
818
|
if (null != bad) {
|
|
640
819
|
return bad;
|
|
641
820
|
}
|
|
@@ -649,10 +828,29 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
649
828
|
// The trial runs in an isolated collect context so a failing check
|
|
650
829
|
// leaves nothing on the caller's error list — only the located nil
|
|
651
830
|
// this returns, carrying the author's own message.
|
|
652
|
-
|
|
831
|
+
// `final` is the generation-time reading (see settleContainer): a
|
|
832
|
+
// must whose own check is a SIZING atom inherits that atom's
|
|
833
|
+
// provisionality, because `must(length(2), …)` against a map that may
|
|
834
|
+
// still gain members has not failed yet -- it has not been answered.
|
|
835
|
+
// Without this the must would decide against whatever the container
|
|
836
|
+
// held when it first settled, which is the very defect the sizing
|
|
837
|
+
// atoms were fixed for (use-cases/BUGS.md §16, §17).
|
|
838
|
+
checkMusts(peer, ctx, final) {
|
|
653
839
|
for (const m of this.musts) {
|
|
654
840
|
const trial = ctx.clone({ err: [], collect: true });
|
|
655
|
-
|
|
841
|
+
let got = (0, unify_1.unite)(trial, m.v.clone(trial), peer.clone(trial), 'must');
|
|
842
|
+
// A must whose check is a SIZING atom answers a residue rather
|
|
843
|
+
// than a verdict, because the atom is waiting for members that
|
|
844
|
+
// may still arrive. Before generation that residue IS the answer
|
|
845
|
+
// -- the must has not failed, it has not been asked yet -- and at
|
|
846
|
+
// generation it is settled, because nothing more can arrive.
|
|
847
|
+
const residue = (0, BagVal_1.sizingResidue)(got);
|
|
848
|
+
if (undefined !== residue) {
|
|
849
|
+
if (true !== final) {
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
got = residue.con.settleContainer(residue.bag, trial);
|
|
853
|
+
}
|
|
656
854
|
if (true === got?.isNil || 0 < trial.err.length) {
|
|
657
855
|
return (0, err_1.makeNilErr)(ctx, 'must', this, peer, undefined, {
|
|
658
856
|
message: m.msg.peg,
|
|
@@ -663,6 +861,14 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
663
861
|
}
|
|
664
862
|
return undefined;
|
|
665
863
|
}
|
|
864
|
+
// THE FINAL READING of a sizing atom over a container: the same
|
|
865
|
+
// checks admitContainer makes, with the provisional ones taken as
|
|
866
|
+
// decided. Called from ConjunctVal.gen, which is where no more
|
|
867
|
+
// members can arrive. Returns the container (to generate) or the
|
|
868
|
+
// constraint's own nil (to report).
|
|
869
|
+
settleContainer(peer, ctx) {
|
|
870
|
+
return this.admitContainer(peer, ctx, true);
|
|
871
|
+
}
|
|
666
872
|
// Membership for a container peer. Only the SIZING atoms have anything
|
|
667
873
|
// to say about a map or a list; every other atom is scalar-domain and
|
|
668
874
|
// refuses one.
|
|
@@ -674,7 +880,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
674
880
|
// isolated collect context so nothing leaks into the caller's errors.
|
|
675
881
|
// A mirror would be a second copy of a filter that has already grown
|
|
676
882
|
// subtle, free to drift from it; asking is correct by construction.
|
|
677
|
-
admitContainer(peer, ctx) {
|
|
883
|
+
admitContainer(peer, ctx, final) {
|
|
678
884
|
// A scalar-domain residual has no reading over a container.
|
|
679
885
|
if (null != this.domain) {
|
|
680
886
|
return this.fail(ctx, peer);
|
|
@@ -686,21 +892,76 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
686
892
|
this.dc = 0;
|
|
687
893
|
return new ConjunctVal_1.ConjunctVal({ peg: [this, peer] }, ctx);
|
|
688
894
|
}
|
|
689
|
-
const bad = this.checkMusts(peer, ctx);
|
|
895
|
+
const bad = this.checkMusts(peer, ctx, final);
|
|
690
896
|
if (null != bad) {
|
|
691
897
|
return bad;
|
|
692
898
|
}
|
|
693
|
-
|
|
694
|
-
|
|
899
|
+
// A MUST OVER A CONTAINER RESIDUATES with the atoms, for the same
|
|
900
|
+
// reason (use-cases/BUGS.md §17): `must({t: max(60)}, …)` beside a
|
|
901
|
+
// `{t: integer}` schema was answered against the schema layer
|
|
902
|
+
// ALONE, and discharged before the data it was written to judge
|
|
903
|
+
// ever arrived. It is decided at generation, where the value is
|
|
904
|
+
// whole -- unless there is nothing else on the constraint AND the
|
|
905
|
+
// reading is already final.
|
|
906
|
+
if (!this.uniq && 0 === this.uniqBy.length && null == this.count) {
|
|
907
|
+
if (true === final || 0 === this.musts.length) {
|
|
908
|
+
return peer;
|
|
909
|
+
}
|
|
910
|
+
return this.hold(peer, ctx);
|
|
695
911
|
}
|
|
696
912
|
const members = emittedMembers(peer, ctx);
|
|
697
913
|
if (null == members) {
|
|
698
|
-
//
|
|
699
|
-
//
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
914
|
+
// NOT COUNTABLE YET IS NOT A PASS. A container that cannot
|
|
915
|
+
// generate cannot be counted, and a SCHEMA is exactly that: the
|
|
916
|
+
// members of `{a: integer}` are types, so nothing is emitted and
|
|
917
|
+
// nothing can be counted. Discharging the atom here was the §16
|
|
918
|
+
// defect wearing its other face -- `length(min(2)) & {a: integer}`
|
|
919
|
+
// dropped its bound while still alone, so the data half of a
|
|
920
|
+
// `vet` meet was never measured at all and short data vetted
|
|
921
|
+
// clean against a bound the evaluator enforces.
|
|
922
|
+
//
|
|
923
|
+
// At generation the reading IS final: a container that still
|
|
924
|
+
// cannot generate has its own error, and that error is the one
|
|
925
|
+
// worth reporting, so it passes through untouched.
|
|
926
|
+
if (true === final) {
|
|
927
|
+
return peer;
|
|
928
|
+
}
|
|
929
|
+
return this.hold(peer, ctx);
|
|
930
|
+
}
|
|
931
|
+
// MONOTONE READINGS ONLY (the review's finding C, use-cases/BUGS.md
|
|
932
|
+
// §16). Members ACCUMULATE under unification -- a meet adds keys and
|
|
933
|
+
// never removes them -- so a settled container is settled ON ITS
|
|
934
|
+
// OWN and not against every value that may still contribute. The
|
|
935
|
+
// schema half of a `vet` meet, a document that is about to receive
|
|
936
|
+
// an `@` include, a bag a later `pack` will fill: each settles, and
|
|
937
|
+
// an atom that decided there decided too early.
|
|
938
|
+
//
|
|
939
|
+
// So a verdict is taken only when MORE MEMBERS CANNOT CHANGE IT:
|
|
940
|
+
// - an upper bound VIOLATED is permanent -> refuse now;
|
|
941
|
+
// satisfied is provisional -> keep the atom, re-check later.
|
|
942
|
+
// - a lower bound SATISFIED is permanent -> that part may go;
|
|
943
|
+
// violated is provisional -> keep the atom (which is why
|
|
944
|
+
// `length(min(1))` beside a template no longer kills the schema
|
|
945
|
+
// it was written for).
|
|
946
|
+
// - a DUPLICATE is permanent -> refuse now; distinctness is
|
|
947
|
+
// provisional -> keep the atom.
|
|
948
|
+
// Anything provisional residuates, exactly as an unsettled
|
|
949
|
+
// container does above, and the two spellings then agree.
|
|
950
|
+
const count = null == this.count ? undefined : this.count;
|
|
951
|
+
const n = members.length;
|
|
952
|
+
if (null != count) {
|
|
953
|
+
if (null != count.hi && !stateAdmits({ ...count, lo: undefined }, countVal(n))) {
|
|
954
|
+
return this.fail(ctx, peer);
|
|
955
|
+
}
|
|
956
|
+
if (0 < count.neqs.length && !stateAdmits({ ...count, lo: undefined, hi: undefined }, countVal(n))) {
|
|
957
|
+
return this.fail(ctx, peer);
|
|
958
|
+
}
|
|
959
|
+
// The provisional half, decided only when nothing more can
|
|
960
|
+
// arrive: a lower bound still short is a refusal at generation
|
|
961
|
+
// and a residue before it.
|
|
962
|
+
if (true === final && !stateAdmits(count, countVal(n))) {
|
|
963
|
+
return this.fail(ctx, peer);
|
|
964
|
+
}
|
|
704
965
|
}
|
|
705
966
|
if (this.uniq) {
|
|
706
967
|
// Members compare by CANONICAL FORM, which reduces to scalar
|
|
@@ -717,7 +978,65 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
717
978
|
seen.add(key);
|
|
718
979
|
}
|
|
719
980
|
}
|
|
720
|
-
|
|
981
|
+
// ... and the same test per PROJECTED key. A member that has no such
|
|
982
|
+
// key FAILS the constraint rather than being skipped: distinctness
|
|
983
|
+
// it cannot be shown to have is distinctness it does not have, and
|
|
984
|
+
// skipping would let one keyless record hide a duplicate.
|
|
985
|
+
for (const field of this.uniqBy) {
|
|
986
|
+
const seen = new Set();
|
|
987
|
+
for (const m of members) {
|
|
988
|
+
const at = true === m.isMap ?
|
|
989
|
+
m.peg[field] : undefined;
|
|
990
|
+
if (null == at) {
|
|
991
|
+
return this.fail(ctx, peer);
|
|
992
|
+
}
|
|
993
|
+
const key = at.canon;
|
|
994
|
+
if (seen.has(key)) {
|
|
995
|
+
return this.fail(ctx, peer);
|
|
996
|
+
}
|
|
997
|
+
seen.add(key);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
// WHAT IS LEFT IS PROVISIONAL, so the atom stays on the value. A
|
|
1001
|
+
// lower bound already met is the one reading that cannot be undone,
|
|
1002
|
+
// and an atom holding nothing else is spent: that is when it goes.
|
|
1003
|
+
const spent = true === final || (0 === this.musts.length &&
|
|
1004
|
+
!this.uniq && 0 === this.uniqBy.length &&
|
|
1005
|
+
(null == count ||
|
|
1006
|
+
(null == count.hi && 0 === count.neqs.length &&
|
|
1007
|
+
stateAdmits(count, countVal(n)))));
|
|
1008
|
+
if (spent) {
|
|
1009
|
+
return peer;
|
|
1010
|
+
}
|
|
1011
|
+
// DONE, unlike the unsettled case above. The container HAS settled;
|
|
1012
|
+
// the atom is kept only because a LATER value could still add
|
|
1013
|
+
// members, and a residue that reported itself unresolved would
|
|
1014
|
+
// leave every enclosing value unresolved with it -- a `type()`
|
|
1015
|
+
// waiting on its argument for ever, and use case 09's whole
|
|
1016
|
+
// registry with it.
|
|
1017
|
+
// The ATOM is done too, not just the conjunct. An earlier pass may
|
|
1018
|
+
// have set `dc = 0` on the unsettled branch above, and a conjunct
|
|
1019
|
+
// recomputes its own doneness from its terms on every later meet --
|
|
1020
|
+
// so a stale 0 here would drag the residue, and every value holding
|
|
1021
|
+
// it, back to unresolved for ever.
|
|
1022
|
+
return this.hold(peer, ctx);
|
|
1023
|
+
}
|
|
1024
|
+
// The atom kept on a value whose reading is still provisional.
|
|
1025
|
+
// DONE, unlike the unsettled container above: the value HAS settled;
|
|
1026
|
+
// the atom is kept only because a LATER value could still add
|
|
1027
|
+
// members, and a residue that reported itself unresolved would leave
|
|
1028
|
+
// every enclosing value unresolved with it -- a `type()` waiting on
|
|
1029
|
+
// its argument for ever, and use case 09's whole registry with it.
|
|
1030
|
+
// The ATOM is done too, not just the conjunct. An earlier pass may
|
|
1031
|
+
// have set `dc = 0` on the unsettled branch, and a conjunct
|
|
1032
|
+
// recomputes its own doneness from its terms on every later meet --
|
|
1033
|
+
// so a stale 0 here would drag the residue, and every value holding
|
|
1034
|
+
// it, back to unresolved for ever.
|
|
1035
|
+
hold(peer, ctx) {
|
|
1036
|
+
this.dc = type_1.DONE;
|
|
1037
|
+
const held = new ConjunctVal_1.ConjunctVal({ peg: [this, peer] }, ctx);
|
|
1038
|
+
held.dc = type_1.DONE;
|
|
1039
|
+
return held;
|
|
721
1040
|
}
|
|
722
1041
|
// Meet with a kind: `number` (or `string` on the string domain) is
|
|
723
1042
|
// already implied by an ORDER atom's argument; a numeric LEAF narrows
|
|
@@ -782,6 +1101,11 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
782
1101
|
null == peer.count ? this.count : meetCount(this.count, peer.count);
|
|
783
1102
|
// `unique()` is idempotent: two of them are one.
|
|
784
1103
|
merged.uniq = this.uniq || peer.uniq;
|
|
1104
|
+
// `unique(a) & unique(b)` is BOTH, not the later one: each names a
|
|
1105
|
+
// key on which the members must differ, and dropping either would
|
|
1106
|
+
// silently weaken the constraint. Sorted and deduplicated, so the
|
|
1107
|
+
// meet is commutative and `unique(a) & unique(a)` is one atom.
|
|
1108
|
+
merged.uniqBy = [...new Set([...this.uniqBy, ...peer.uniqBy])].sort();
|
|
785
1109
|
// Band B checks accumulate in written order and are never merged,
|
|
786
1110
|
// deduplicated or reordered: each carries its own author message,
|
|
787
1111
|
// and two checks with the same shape may still say different things.
|
|
@@ -822,6 +1146,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
822
1146
|
res: [...this.res],
|
|
823
1147
|
count: this.count,
|
|
824
1148
|
uniq: this.uniq,
|
|
1149
|
+
uniqBy: [...this.uniqBy],
|
|
825
1150
|
musts: [...this.musts],
|
|
826
1151
|
invalid: this.invalid,
|
|
827
1152
|
};
|
|
@@ -839,6 +1164,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
839
1164
|
out.res = [...this.res];
|
|
840
1165
|
out.count = this.count;
|
|
841
1166
|
out.uniq = this.uniq;
|
|
1167
|
+
out.uniqBy = [...this.uniqBy];
|
|
842
1168
|
out.musts = [...this.musts];
|
|
843
1169
|
out.pending = this.pending;
|
|
844
1170
|
out.cjo = this.cjo;
|
|
@@ -903,10 +1229,11 @@ function dedupSorted(domain, neqs) {
|
|
|
903
1229
|
// Is this value, or anything inside it, a nil? A written argument that
|
|
904
1230
|
// holds one can never be satisfied, so the atom refuses it as an
|
|
905
1231
|
// argument rather than reporting a mystery failure against every peer.
|
|
1232
|
+
// Every caller passes a Val: `args[0]` comes from atomArgs, and the
|
|
1233
|
+
// walk below descends only into container pegs, which hold Vals. The
|
|
1234
|
+
// null/non-Val guard this function used to open with was therefore
|
|
1235
|
+
// unreachable, and the coverage gate said so.
|
|
906
1236
|
function holdsNil(v) {
|
|
907
|
-
if (null == v || true !== v.isVal) {
|
|
908
|
-
return false;
|
|
909
|
-
}
|
|
910
1237
|
if (true === v.isNil) {
|
|
911
1238
|
return true;
|
|
912
1239
|
}
|
|
@@ -923,6 +1250,29 @@ function holdsNil(v) {
|
|
|
923
1250
|
}
|
|
924
1251
|
return false;
|
|
925
1252
|
}
|
|
1253
|
+
// Whether a value, or anything inside it, is an effectful call — one
|
|
1254
|
+
// whose evaluation changes a node OTHER than the one being computed.
|
|
1255
|
+
// `move()` is the only builtin that does: it hides its resolution
|
|
1256
|
+
// target in place. Band B refuses one as an argument, because settling
|
|
1257
|
+
// it runs the effect against the live root before `must`'s trial clone
|
|
1258
|
+
// is taken, and a check that mutates cannot be report-only.
|
|
1259
|
+
function holdsMove(v) {
|
|
1260
|
+
if (true === v.isFunc && 'move' === v.funcname?.()) {
|
|
1261
|
+
return true;
|
|
1262
|
+
}
|
|
1263
|
+
const peg = v.peg;
|
|
1264
|
+
if (Array.isArray(peg)) {
|
|
1265
|
+
return peg.some((c) => holdsMove(c));
|
|
1266
|
+
}
|
|
1267
|
+
if (null != peg && 'object' === typeof peg) {
|
|
1268
|
+
for (const k in peg) {
|
|
1269
|
+
if (holdsMove(peg[k])) {
|
|
1270
|
+
return true;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return false;
|
|
1275
|
+
}
|
|
926
1276
|
// The written arguments of an atom, flattened.
|
|
927
1277
|
//
|
|
928
1278
|
// A multi-argument call arrives from the func-paren grammar as ONE
|
|
@@ -979,6 +1329,12 @@ function canonState(s) {
|
|
|
979
1329
|
if (s.uniq) {
|
|
980
1330
|
parts.push('unique()');
|
|
981
1331
|
}
|
|
1332
|
+
// After the bare atom, and sorted: canon is a normal form, so two
|
|
1333
|
+
// documents writing the same keys in different orders must render
|
|
1334
|
+
// the same string.
|
|
1335
|
+
for (const key of s.uniqBy) {
|
|
1336
|
+
parts.push('unique(' + JSON.stringify(key) + ')');
|
|
1337
|
+
}
|
|
982
1338
|
for (const m of s.musts) {
|
|
983
1339
|
parts.push('must(' + m.v.canon + ',' + m.msg.canon + ')');
|
|
984
1340
|
}
|
|
@@ -988,13 +1344,117 @@ function canonState(s) {
|
|
|
988
1344
|
}
|
|
989
1345
|
return parts.join('&');
|
|
990
1346
|
}
|
|
1347
|
+
// SUBSUMPTION over two residuals (G3, the query built on the table in
|
|
1348
|
+
// docs/reference-language.md "Subsumption"): does every value `s`
|
|
1349
|
+
// admits pass `g` too? Implemented here because the compare machinery
|
|
1350
|
+
// (cmpVal, sameScalar, the recursive count state) is this module's.
|
|
1351
|
+
//
|
|
1352
|
+
// Three-valued: true / false / 'undecided'. `must` on the GENERAL side
|
|
1353
|
+
// is undecided by construction (a Band B predicate is opaque — G3 maps
|
|
1354
|
+
// the table's "never" to the query's honest sub_evaluate_only); every
|
|
1355
|
+
// other approximation folds toward false, the safe direction.
|
|
1356
|
+
function constraintStateSubsumes(g, s) {
|
|
1357
|
+
// A Band B predicate on the general side makes its admitted set
|
|
1358
|
+
// unknowable; an extra `must` on the SPECIFIC side only narrows it
|
|
1359
|
+
// and is ignored.
|
|
1360
|
+
if (0 < g.musts.length) {
|
|
1361
|
+
return 'undecided';
|
|
1362
|
+
}
|
|
1363
|
+
// Domains must agree where both constrain one; a sizing-only residual
|
|
1364
|
+
// has no domain and passes this gate.
|
|
1365
|
+
if (null != g.domain && g.domain !== s.domain) {
|
|
1366
|
+
return false;
|
|
1367
|
+
}
|
|
1368
|
+
// A general leaf restriction requires the same leaf on the specific
|
|
1369
|
+
// side (leaves are disjoint; an unrestricted specific admits other
|
|
1370
|
+
// leaves the general refuses).
|
|
1371
|
+
if (null != g.kind && g.kind !== s.kind) {
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
// Interval containment: the general's endpoints at or beyond the
|
|
1375
|
+
// specific's, and where they coincide the general's may not be the
|
|
1376
|
+
// open one.
|
|
1377
|
+
const d = g.domain ?? s.domain;
|
|
1378
|
+
if (null != g.lo) {
|
|
1379
|
+
if (null == s.lo || null == d) {
|
|
1380
|
+
return false;
|
|
1381
|
+
}
|
|
1382
|
+
const c = cmpVal(d, g.lo.v, s.lo.v);
|
|
1383
|
+
if (0 < c || (0 === c && g.lo.open && !s.lo.open)) {
|
|
1384
|
+
return false;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
if (null != g.hi) {
|
|
1388
|
+
if (null == s.hi || null == d) {
|
|
1389
|
+
return false;
|
|
1390
|
+
}
|
|
1391
|
+
const c = cmpVal(d, g.hi.v, s.hi.v);
|
|
1392
|
+
if (c < 0 || (0 === c && g.hi.open && !s.hi.open)) {
|
|
1393
|
+
return false;
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
// Excluding FEWER values is more general: every general exclusion
|
|
1397
|
+
// must be excluded by the specific too.
|
|
1398
|
+
for (const n of g.neqs) {
|
|
1399
|
+
if (!s.neqs.some((m) => sameScalar(n, m))) {
|
|
1400
|
+
return false;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
// Patterns compare as TEXT sets (the sanctioned approximation:
|
|
1404
|
+
// deciding regex containment is what this algebra refuses to do).
|
|
1405
|
+
for (const r of g.res) {
|
|
1406
|
+
if (!s.res.some((q) => q.src === r.src)) {
|
|
1407
|
+
return false;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
// `unique()` subsumes only itself on that axis: a general uniqueness
|
|
1411
|
+
// demand admits no container the unconstrained specific also admits.
|
|
1412
|
+
// ... and a general `unique(k)` needs the same key on the specific
|
|
1413
|
+
// side: distinctness on `port` says nothing about distinctness on
|
|
1414
|
+
// `name`.
|
|
1415
|
+
if (g.uniqBy.some((k) => !s.uniqBy.includes(k))) {
|
|
1416
|
+
return false;
|
|
1417
|
+
}
|
|
1418
|
+
if (g.uniq && !s.uniq) {
|
|
1419
|
+
return false;
|
|
1420
|
+
}
|
|
1421
|
+
// The count atom reuses this same table over the integer domain.
|
|
1422
|
+
if (null != g.count) {
|
|
1423
|
+
if (null == s.count) {
|
|
1424
|
+
return false;
|
|
1425
|
+
}
|
|
1426
|
+
return constraintStateSubsumes(g.count, s.count);
|
|
1427
|
+
}
|
|
1428
|
+
return true;
|
|
1429
|
+
}
|
|
1430
|
+
// The query surface for ts/src/subsume.ts: residual-vs-residual, and
|
|
1431
|
+
// residual-vs-scalar membership (with `must` and `unique` making the
|
|
1432
|
+
// scalar case undecided/false exactly as the meet would).
|
|
1433
|
+
function constraintSubsumesConstraint(g, s) {
|
|
1434
|
+
return constraintStateSubsumes(g, s);
|
|
1435
|
+
}
|
|
1436
|
+
function constraintAdmitsScalar(g, scalar) {
|
|
1437
|
+
if (0 < g.musts.length) {
|
|
1438
|
+
return 'undecided';
|
|
1439
|
+
}
|
|
1440
|
+
if (g.uniq || 0 < g.uniqBy.length) {
|
|
1441
|
+
return false;
|
|
1442
|
+
}
|
|
1443
|
+
// A count residual admits by LENGTH, which is the meet's business;
|
|
1444
|
+
// the query answers false (the safe direction) rather than
|
|
1445
|
+
// reimplementing the sizing walk here.
|
|
1446
|
+
if (null != g.count) {
|
|
1447
|
+
return false;
|
|
1448
|
+
}
|
|
1449
|
+
return stateAdmits(g, scalar);
|
|
1450
|
+
}
|
|
991
1451
|
// Does this residual's ORDER and MEMBERSHIP part admit the scalar? The
|
|
992
1452
|
// sizing atoms are deliberately not consulted: `admit` applies them to
|
|
993
1453
|
// the peer's length, and the count check applies this same function to
|
|
994
1454
|
// the count.
|
|
995
1455
|
function stateAdmits(s, peer) {
|
|
996
1456
|
const domainOf = numericLeaf(peer) ? 'number' :
|
|
997
|
-
|
|
1457
|
+
stringishLeaf(peer) ? 'string' : undefined;
|
|
998
1458
|
if (null == s.domain) {
|
|
999
1459
|
// A sizing residual has no domain, and admits any scalar the sizing
|
|
1000
1460
|
// atoms can then rule on. Booleans and null are not among them:
|
|
@@ -1096,7 +1556,8 @@ function stateEmpty(s) {
|
|
|
1096
1556
|
// members, so `integer & length(3)` and `min(2) & unique()` admit
|
|
1097
1557
|
// nothing. Uniqueness over the string domain is empty for the same
|
|
1098
1558
|
// reason -- a string's members are not values the algebra compares.
|
|
1099
|
-
if ('number' === d && (null != s.count || s.uniq
|
|
1559
|
+
if ('number' === d && (null != s.count || s.uniq ||
|
|
1560
|
+
0 < s.uniqBy.length)) {
|
|
1100
1561
|
return true;
|
|
1101
1562
|
}
|
|
1102
1563
|
if ('string' === d && s.uniq) {
|
|
@@ -1121,7 +1582,9 @@ function countBase() {
|
|
|
1121
1582
|
neqs: [],
|
|
1122
1583
|
res: [],
|
|
1123
1584
|
musts: [],
|
|
1585
|
+
// A COUNT is a number, and a number has no members to be distinct.
|
|
1124
1586
|
uniq: false,
|
|
1587
|
+
uniqBy: [],
|
|
1125
1588
|
};
|
|
1126
1589
|
}
|
|
1127
1590
|
// A count as a Val, so the count residual can be applied by exactly the
|
|
@@ -1138,13 +1601,16 @@ function countVal(n) {
|
|
|
1138
1601
|
function meetCount(a, b) {
|
|
1139
1602
|
return {
|
|
1140
1603
|
domain: 'number',
|
|
1141
|
-
|
|
1604
|
+
// `a` is always a countBase()-seeded residual, so its kind is
|
|
1605
|
+
// always Integer — there is no kindless side to fall back from.
|
|
1606
|
+
kind: a.kind,
|
|
1142
1607
|
lo: tighter('number', a.lo, b.lo, true),
|
|
1143
1608
|
hi: tighter('number', a.hi, b.hi, false),
|
|
1144
1609
|
neqs: dedupSorted('number', [...a.neqs, ...b.neqs]),
|
|
1145
1610
|
res: [],
|
|
1146
1611
|
musts: [],
|
|
1147
1612
|
uniq: false,
|
|
1613
|
+
uniqBy: [],
|
|
1148
1614
|
clash: true === a.clash || true === b.clash ||
|
|
1149
1615
|
(null != a.kind && null != b.kind && a.kind !== b.kind),
|
|
1150
1616
|
};
|
|
@@ -1167,14 +1633,15 @@ function countArgState(arg) {
|
|
|
1167
1633
|
domain: 'number',
|
|
1168
1634
|
lo: { v: arg, open: false },
|
|
1169
1635
|
hi: { v: arg, open: false },
|
|
1170
|
-
neqs: [], res: [], musts: [], uniq: false,
|
|
1636
|
+
neqs: [], res: [], musts: [], uniq: false, uniqBy: [],
|
|
1171
1637
|
};
|
|
1172
1638
|
}
|
|
1173
1639
|
if (true === arg?.isConstraint) {
|
|
1174
1640
|
const c = arg;
|
|
1175
1641
|
// A pattern, a sizing atom or a string bound inside a count is not
|
|
1176
1642
|
// a count constraint at all, and neither is a broken one.
|
|
1177
|
-
if (null != c.invalid || 0 < c.res.length || c.uniq ||
|
|
1643
|
+
if (null != c.invalid || 0 < c.res.length || c.uniq ||
|
|
1644
|
+
0 < c.uniqBy.length || null != c.count ||
|
|
1178
1645
|
'number' !== c.domain) {
|
|
1179
1646
|
return undefined;
|
|
1180
1647
|
}
|
|
@@ -1184,17 +1651,23 @@ function countArgState(arg) {
|
|
|
1184
1651
|
lo: c.lo,
|
|
1185
1652
|
hi: c.hi,
|
|
1186
1653
|
neqs: [...c.neqs],
|
|
1187
|
-
res: [], musts: [], uniq: false,
|
|
1654
|
+
res: [], musts: [], uniq: false, uniqBy: [],
|
|
1188
1655
|
};
|
|
1189
1656
|
}
|
|
1190
1657
|
if (true === arg?.isScalarKind) {
|
|
1191
1658
|
const marker = arg.peg;
|
|
1192
1659
|
if (Number === marker) {
|
|
1193
|
-
return {
|
|
1660
|
+
return {
|
|
1661
|
+
domain: 'number', neqs: [], res: [], musts: [],
|
|
1662
|
+
uniq: false, uniqBy: [],
|
|
1663
|
+
};
|
|
1194
1664
|
}
|
|
1195
1665
|
if (ScalarKindVal_1.Integer === marker || ScalarKindVal_1.Float === marker ||
|
|
1196
1666
|
ScalarKindVal_1.BigInteger === marker || ScalarKindVal_1.BigDecimal === marker) {
|
|
1197
|
-
return {
|
|
1667
|
+
return {
|
|
1668
|
+
domain: 'number', kind: marker, neqs: [], res: [], musts: [],
|
|
1669
|
+
uniq: false, uniqBy: [],
|
|
1670
|
+
};
|
|
1198
1671
|
}
|
|
1199
1672
|
return undefined;
|
|
1200
1673
|
}
|
|
@@ -1221,7 +1694,12 @@ function genable(child) {
|
|
|
1221
1694
|
return true === child.isScalar || true === child.isMap ||
|
|
1222
1695
|
true === child.isList || true === child.isPref ||
|
|
1223
1696
|
true === child.isRef || true === child.isDisjunct ||
|
|
1224
|
-
true === child.isNil
|
|
1697
|
+
true === child.isNil ||
|
|
1698
|
+
// A settled sizing residue generates through ConjunctVal.gen, so it
|
|
1699
|
+
// is a member like any other -- and a `length` that did not count
|
|
1700
|
+
// its sibling's `unique` would be the early-fold defect again, one
|
|
1701
|
+
// level up.
|
|
1702
|
+
undefined !== (0, BagVal_1.sizingResidue)(child);
|
|
1225
1703
|
}
|
|
1226
1704
|
// The children a bag would EMIT, mirroring the selection in
|
|
1227
1705
|
// `BagVal.gen` — type/hide marks skipped, non-generable residue and
|
|
@@ -1343,6 +1821,6 @@ class UniqueConstraintVal extends ConstraintVal {
|
|
|
1343
1821
|
constructor(spec, ctx) {
|
|
1344
1822
|
super({ ...spec, atom: 'unique' }, ctx);
|
|
1345
1823
|
}
|
|
1346
|
-
} /* node:coverage ignore next
|
|
1824
|
+
} /* node:coverage ignore next 23 */
|
|
1347
1825
|
exports.UniqueConstraintVal = UniqueConstraintVal;
|
|
1348
1826
|
//# sourceMappingURL=ConstraintVal.js.map
|