aontu 0.52.1 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -0
- package/bin/aontu-mcp.js +4 -0
- package/dist/agentsmd.d.ts +16 -0
- package/dist/agentsmd.js +107 -0
- package/dist/agentsmd.js.map +1 -0
- package/dist/aontu.d.ts +14 -3
- package/dist/aontu.js +96 -4
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +44 -1
- package/dist/cli.js +2401 -44
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +13 -0
- package/dist/ctx.js +43 -0
- package/dist/ctx.js.map +1 -1
- package/dist/diff.d.ts +22 -0
- package/dist/diff.js +141 -0
- package/dist/diff.js.map +1 -0
- package/dist/err.d.ts +3 -1
- package/dist/err.js +38 -7
- package/dist/err.js.map +1 -1
- package/dist/graph.d.ts +16 -0
- package/dist/graph.js +73 -0
- package/dist/graph.js.map +1 -0
- package/dist/hcanon.d.ts +3 -0
- package/dist/hcanon.js +146 -0
- package/dist/hcanon.js.map +1 -0
- package/dist/hints.js +167 -4
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.d.ts +20 -0
- package/dist/jsonschema.js +391 -0
- package/dist/jsonschema.js.map +1 -0
- package/dist/lang.js +512 -69
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +9 -2
- package/dist/lsp.js +262 -47
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.d.ts +20 -0
- package/dist/mcp-server.js +147 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp.d.ts +42 -0
- package/dist/mcp.js +814 -0
- package/dist/mcp.js.map +1 -0
- package/dist/mod-tool.d.ts +58 -0
- package/dist/mod-tool.js +498 -0
- package/dist/mod-tool.js.map +1 -0
- package/dist/mod.d.ts +31 -0
- package/dist/mod.js +250 -0
- package/dist/mod.js.map +1 -0
- package/dist/patch.d.ts +44 -0
- package/dist/patch.js +506 -0
- package/dist/patch.js.map +1 -0
- package/dist/provenance.d.ts +40 -0
- package/dist/provenance.js +335 -0
- package/dist/provenance.js.map +1 -0
- package/dist/query.d.ts +27 -0
- package/dist/query.js +294 -0
- package/dist/query.js.map +1 -0
- package/dist/reach.d.ts +14 -0
- package/dist/reach.js +140 -0
- package/dist/reach.js.map +1 -0
- package/dist/relation.d.ts +19 -0
- package/dist/relation.js +305 -0
- package/dist/relation.js.map +1 -0
- package/dist/report-sarif.d.ts +14 -0
- package/dist/report-sarif.js +102 -0
- package/dist/report-sarif.js.map +1 -0
- package/dist/site.d.ts +4 -0
- package/dist/site.js +31 -0
- package/dist/site.js.map +1 -1
- package/dist/std.d.ts +1 -0
- package/dist/std.js +73 -0
- package/dist/std.js.map +1 -0
- package/dist/subsume.d.ts +39 -0
- package/dist/subsume.js +526 -0
- package/dist/subsume.js.map +1 -0
- package/dist/trim.d.ts +19 -0
- package/dist/trim.js +155 -0
- package/dist/trim.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +17 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.d.ts +2 -1
- package/dist/unify.js +253 -36
- package/dist/unify.js.map +1 -1
- package/dist/utility.d.ts +9 -1
- package/dist/utility.js +122 -1
- package/dist/utility.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +33 -0
- package/dist/val/AggFuncVal.js +202 -0
- package/dist/val/AggFuncVal.js.map +1 -0
- package/dist/val/ArithFuncVal.d.ts +31 -0
- package/dist/val/ArithFuncVal.js +62 -0
- package/dist/val/ArithFuncVal.js.map +1 -0
- package/dist/val/BagVal.d.ts +5 -0
- package/dist/val/BagVal.js +96 -5
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +9 -1
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.d.ts +1 -1
- package/dist/val/ConjunctVal.js +19 -0
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.d.ts +7 -1
- package/dist/val/ConstraintVal.js +490 -40
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/CopyFuncVal.d.ts +1 -2
- package/dist/val/CopyFuncVal.js +7 -0
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.d.ts +1 -0
- package/dist/val/Decimal.js +13 -0
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.d.ts +11 -0
- package/dist/val/DeprecateFuncVal.js +47 -0
- package/dist/val/DeprecateFuncVal.js.map +1 -0
- package/dist/val/DisjunctVal.js +130 -21
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.d.ts +15 -0
- package/dist/val/EachFuncVal.js +75 -0
- package/dist/val/EachFuncVal.js.map +1 -0
- package/dist/val/ExpectVal.js +27 -4
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FeatureVal.js +1 -1
- package/dist/val/FeatureVal.js.map +1 -1
- package/dist/val/FilterFuncVal.d.ts +15 -0
- package/dist/val/FilterFuncVal.js +91 -0
- package/dist/val/FilterFuncVal.js.map +1 -0
- package/dist/val/FuncBaseVal.d.ts +6 -1
- package/dist/val/FuncBaseVal.js +171 -1
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IdFuncVal.d.ts +13 -0
- package/dist/val/IdFuncVal.js +54 -0
- package/dist/val/IdFuncVal.js.map +1 -0
- package/dist/val/JunctionVal.js +7 -1
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.d.ts +1 -1
- package/dist/val/KeyFuncVal.js +38 -30
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +101 -14
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +93 -8
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.d.ts +15 -0
- package/dist/val/MatchFuncVal.js +107 -0
- package/dist/val/MatchFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +24 -0
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -1
- package/dist/val/OpBaseVal.js +19 -1
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/OpenFuncVal.js +4 -1
- package/dist/val/OpenFuncVal.js.map +1 -1
- package/dist/val/PackFuncVal.d.ts +15 -0
- package/dist/val/PackFuncVal.js +108 -0
- package/dist/val/PackFuncVal.js.map +1 -0
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PlaceVal.d.ts +13 -0
- package/dist/val/PlaceVal.js +131 -0
- package/dist/val/PlaceVal.js.map +1 -0
- package/dist/val/PlusOpVal.js +11 -2
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +2 -2
- package/dist/val/PrefVal.js +78 -23
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RefVal.d.ts +1 -1
- package/dist/val/RefVal.js +89 -7
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.d.ts +36 -0
- package/dist/val/ReferFuncVal.js +303 -0
- package/dist/val/ReferFuncVal.js.map +1 -0
- package/dist/val/ScalarKindVal.d.ts +1 -2
- package/dist/val/ScalarKindVal.js +0 -11
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TypeFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +8 -1
- package/dist/val/Val.js +150 -1
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.d.ts +6 -0
- package/dist/val/arith.js +170 -0
- package/dist/val/arith.js.map +1 -0
- package/dist/vet.d.ts +45 -0
- package/dist/vet.js +776 -0
- package/dist/vet.js.map +1 -0
- package/dist/walk.d.ts +2 -0
- package/dist/walk.js +91 -0
- package/dist/walk.js.map +1 -0
- package/grammar/aontu.gbnf +130 -0
- package/grammar/aontu.lark +113 -0
- package/package.json +21 -6
- package/skill/SKILL.md +37 -0
- package/skill/error-codes.md +62 -0
- package/skill/examples.md +99 -0
- package/skill/grammar-card.md +57 -0
- package/src/agentsmd.ts +135 -0
- package/src/aontu.ts +135 -4
- package/src/cli.ts +2858 -71
- package/src/ctx.ts +73 -1
- package/src/diff.ts +196 -0
- package/src/err.ts +42 -7
- package/src/graph.ts +135 -0
- package/src/hcanon.ts +169 -0
- package/src/hints.ts +208 -4
- package/src/jsonschema.ts +511 -0
- package/src/lang.ts +570 -70
- package/src/lsp.ts +281 -48
- package/src/mcp-server.ts +187 -0
- package/src/mcp.ts +993 -0
- package/src/mod-tool.ts +679 -0
- package/src/mod.ts +344 -0
- package/src/patch.ts +624 -0
- package/src/provenance.ts +430 -0
- package/src/query.ts +379 -0
- package/src/reach.ts +184 -0
- package/src/relation.ts +395 -0
- package/src/report-sarif.ts +137 -0
- package/src/site.ts +36 -1
- package/src/std.ts +73 -0
- package/src/subsume.ts +690 -0
- package/src/trim.ts +195 -0
- package/src/tsconfig.json +10 -4
- package/src/type.ts +51 -2
- package/src/unify.ts +274 -36
- package/src/utility.ts +139 -1
- package/src/val/AggFuncVal.ts +319 -0
- package/src/val/ArithFuncVal.ts +108 -0
- package/src/val/BagVal.ts +101 -4
- package/src/val/CloseFuncVal.ts +9 -1
- package/src/val/ConjunctVal.ts +20 -0
- package/src/val/ConstraintVal.ts +542 -43
- package/src/val/CopyFuncVal.ts +7 -1
- package/src/val/Decimal.ts +15 -0
- package/src/val/DeprecateFuncVal.ts +84 -0
- package/src/val/DisjunctVal.ts +139 -28
- package/src/val/EachFuncVal.ts +133 -0
- package/src/val/ExpectVal.ts +28 -6
- package/src/val/FeatureVal.ts +1 -1
- package/src/val/FilterFuncVal.ts +154 -0
- package/src/val/FuncBaseVal.ts +192 -1
- package/src/val/HideFuncVal.ts +0 -2
- package/src/val/IdFuncVal.ts +91 -0
- package/src/val/JunctionVal.ts +7 -1
- package/src/val/KeyFuncVal.ts +39 -35
- package/src/val/ListVal.ts +108 -15
- package/src/val/LowerFuncVal.ts +0 -1
- package/src/val/MapVal.ts +99 -8
- package/src/val/MatchFuncVal.ts +176 -0
- package/src/val/MoveFuncVal.ts +0 -2
- package/src/val/NilVal.ts +25 -0
- package/src/val/OpBaseVal.ts +20 -1
- package/src/val/OpenFuncVal.ts +4 -2
- package/src/val/PackFuncVal.ts +175 -0
- package/src/val/PathFuncVal.ts +0 -1
- package/src/val/PlaceVal.ts +193 -0
- package/src/val/PlusOpVal.ts +11 -2
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +79 -36
- package/src/val/RefVal.ts +91 -8
- package/src/val/ReferFuncVal.ts +387 -0
- package/src/val/ScalarKindVal.ts +0 -13
- package/src/val/TopVal.ts +0 -1
- package/src/val/TypeFuncVal.ts +0 -2
- package/src/val/UpperFuncVal.ts +0 -1
- package/src/val/Val.ts +205 -2
- package/src/val/VarVal.ts +0 -1
- package/src/val/arith.ts +316 -0
- package/src/vet.ts +992 -0
- package/src/walk.ts +99 -0
|
@@ -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'];
|
|
@@ -328,6 +449,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
328
449
|
this.neqs = [];
|
|
329
450
|
this.res = [];
|
|
330
451
|
this.uniq = false;
|
|
452
|
+
this.uniqBy = [];
|
|
331
453
|
this.musts = [];
|
|
332
454
|
if (spec.state) {
|
|
333
455
|
this.domain = spec.state.domain;
|
|
@@ -340,6 +462,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
340
462
|
this.res = spec.state.res ?? [];
|
|
341
463
|
this.count = spec.state.count;
|
|
342
464
|
this.uniq = spec.state.uniq ?? false;
|
|
465
|
+
this.uniqBy = spec.state.uniqBy ?? [];
|
|
343
466
|
this.musts = spec.state.musts ?? [];
|
|
344
467
|
this.invalid = spec.state.invalid;
|
|
345
468
|
}
|
|
@@ -351,14 +474,22 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
351
474
|
// in unify, where there is a ctx to resolve through, and the
|
|
352
475
|
// residual is built from the settled arguments. Only a settled
|
|
353
476
|
// argument of the wrong shape is an `invalid-arg`.
|
|
354
|
-
|
|
477
|
+
// The effectful-argument refusal happens HERE, on the written
|
|
478
|
+
// form, and not in fromAtom: settling is what runs the effect, so
|
|
479
|
+
// by the time fromAtom sees a settled `move($.b)` the move has
|
|
480
|
+
// already happened and the argument is just its result.
|
|
481
|
+
if ('must' === spec.atom && args.some((a) => holdsMove(a))) {
|
|
482
|
+
this.invalid = 'invalid-arg';
|
|
483
|
+
}
|
|
484
|
+
else if (args.some((a) => true !== a?.done)) {
|
|
355
485
|
this.pending = { atom: spec.atom, args };
|
|
356
486
|
}
|
|
357
487
|
else {
|
|
358
488
|
this.fromAtom(spec.atom, args);
|
|
359
489
|
}
|
|
360
490
|
}
|
|
361
|
-
if (null != this.count || this.uniq || 0 < this.
|
|
491
|
+
if (null != this.count || this.uniq || 0 < this.uniqBy.length ||
|
|
492
|
+
0 < this.musts.length ||
|
|
362
493
|
(null != this.pending && lateAtom(this.pending.atom))) {
|
|
363
494
|
this.cjo = LATE_CJO;
|
|
364
495
|
}
|
|
@@ -377,17 +508,30 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
377
508
|
// so a shape this cannot use is a genuine `invalid-arg`, not a
|
|
378
509
|
// not-yet.
|
|
379
510
|
fromAtom(atom, args) {
|
|
380
|
-
// Mark the residual invalid
|
|
381
|
-
//
|
|
511
|
+
// Mark the residual invalid. It answers NOTHING: every caller
|
|
512
|
+
// spells its exit `return bad(...)` and none reads a value, so a
|
|
513
|
+
// return type would be a value nobody consumes -- and a routine
|
|
514
|
+
// that answers on some paths and not others is the shape a reader
|
|
515
|
+
// (and a static analyser) has to stop and check.
|
|
382
516
|
const bad = (why) => {
|
|
383
517
|
this.invalid = why;
|
|
384
|
-
return true;
|
|
385
518
|
};
|
|
386
|
-
// `unique()`
|
|
387
|
-
//
|
|
388
|
-
//
|
|
519
|
+
// `unique()` is a property of the container -- not a comparison
|
|
520
|
+
// against a value -- so with no argument it says the members are
|
|
521
|
+
// pairwise DISTINCT. THE ONE ARGUMENT IS A PROJECTOR (the review's
|
|
522
|
+
// finding I: "unique()-by-field is reserved but absent", and the
|
|
523
|
+
// arity was reserved for exactly this): `unique(port)` says no two
|
|
524
|
+
// members share a `port`, which is how "no two services share a
|
|
525
|
+
// port" and "event ids are unique" are said.
|
|
389
526
|
if ('unique' === atom) {
|
|
390
|
-
|
|
527
|
+
if (0 === args.length) {
|
|
528
|
+
this.uniq = true;
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
if (1 !== args.length || !stringLeaf(args[0])) {
|
|
532
|
+
return bad('invalid-arg');
|
|
533
|
+
}
|
|
534
|
+
this.uniqBy = [args[0].peg];
|
|
391
535
|
return;
|
|
392
536
|
}
|
|
393
537
|
// `must(c, msg)` is Band B: an evaluate-only check against the
|
|
@@ -411,6 +555,9 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
411
555
|
if (holdsNil(args[0])) {
|
|
412
556
|
return bad('invalid-arg');
|
|
413
557
|
}
|
|
558
|
+
// (An effectful argument is refused at construction, in the
|
|
559
|
+
// constructor: by the time this arm sees a settled `move($.b)`
|
|
560
|
+
// the move has already run.)
|
|
414
561
|
this.musts = [{ v: args[0], msg: args[1] }];
|
|
415
562
|
return;
|
|
416
563
|
}
|
|
@@ -609,16 +756,17 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
609
756
|
if (null == peer || peer.isTop) {
|
|
610
757
|
return again;
|
|
611
758
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
759
|
+
// No nil-peer arm: `unite` returns a nil operand before dispatching
|
|
760
|
+
// to any Val's unify (ts/src/unify.ts), so a nil never reaches here
|
|
761
|
+
// — and were one to, the conjunct below folds to it unchanged.
|
|
615
762
|
return new ConjunctVal_1.ConjunctVal({ peg: [again, peer] }, ctx);
|
|
616
763
|
}
|
|
617
764
|
// Membership: the peer scalar passes every part of the residual, or
|
|
618
765
|
// the whole meet is a located conflict.
|
|
619
766
|
admit(peer, ctx) {
|
|
620
|
-
// No scalar has members, so a `unique()` residual admits none
|
|
621
|
-
|
|
767
|
+
// No scalar has members, so a `unique()` residual admits none --
|
|
768
|
+
// and neither does a `unique(k)` one, for the same reason.
|
|
769
|
+
if (this.uniq || 0 < this.uniqBy.length) {
|
|
622
770
|
return this.fail(ctx, peer);
|
|
623
771
|
}
|
|
624
772
|
if (!stateAdmits(this, peer)) {
|
|
@@ -635,7 +783,10 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
635
783
|
return this.fail(ctx, peer);
|
|
636
784
|
}
|
|
637
785
|
}
|
|
638
|
-
|
|
786
|
+
// A SCALAR HAS NO MEMBERS to accumulate, so its musts are decided
|
|
787
|
+
// here and never residuate: the final reading is the only reading
|
|
788
|
+
// a scalar has.
|
|
789
|
+
const bad = this.checkMusts(peer, ctx, true);
|
|
639
790
|
if (null != bad) {
|
|
640
791
|
return bad;
|
|
641
792
|
}
|
|
@@ -649,10 +800,29 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
649
800
|
// The trial runs in an isolated collect context so a failing check
|
|
650
801
|
// leaves nothing on the caller's error list — only the located nil
|
|
651
802
|
// this returns, carrying the author's own message.
|
|
652
|
-
|
|
803
|
+
// `final` is the generation-time reading (see settleContainer): a
|
|
804
|
+
// must whose own check is a SIZING atom inherits that atom's
|
|
805
|
+
// provisionality, because `must(length(2), …)` against a map that may
|
|
806
|
+
// still gain members has not failed yet -- it has not been answered.
|
|
807
|
+
// Without this the must would decide against whatever the container
|
|
808
|
+
// held when it first settled, which is the very defect the sizing
|
|
809
|
+
// atoms were fixed for (use-cases/BUGS.md §16, §17).
|
|
810
|
+
checkMusts(peer, ctx, final) {
|
|
653
811
|
for (const m of this.musts) {
|
|
654
812
|
const trial = ctx.clone({ err: [], collect: true });
|
|
655
|
-
|
|
813
|
+
let got = (0, unify_1.unite)(trial, m.v.clone(trial), peer.clone(trial), 'must');
|
|
814
|
+
// A must whose check is a SIZING atom answers a residue rather
|
|
815
|
+
// than a verdict, because the atom is waiting for members that
|
|
816
|
+
// may still arrive. Before generation that residue IS the answer
|
|
817
|
+
// -- the must has not failed, it has not been asked yet -- and at
|
|
818
|
+
// generation it is settled, because nothing more can arrive.
|
|
819
|
+
const residue = (0, BagVal_1.sizingResidue)(got);
|
|
820
|
+
if (undefined !== residue) {
|
|
821
|
+
if (true !== final) {
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
got = residue.con.settleContainer(residue.bag, trial);
|
|
825
|
+
}
|
|
656
826
|
if (true === got?.isNil || 0 < trial.err.length) {
|
|
657
827
|
return (0, err_1.makeNilErr)(ctx, 'must', this, peer, undefined, {
|
|
658
828
|
message: m.msg.peg,
|
|
@@ -663,6 +833,14 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
663
833
|
}
|
|
664
834
|
return undefined;
|
|
665
835
|
}
|
|
836
|
+
// THE FINAL READING of a sizing atom over a container: the same
|
|
837
|
+
// checks admitContainer makes, with the provisional ones taken as
|
|
838
|
+
// decided. Called from ConjunctVal.gen, which is where no more
|
|
839
|
+
// members can arrive. Returns the container (to generate) or the
|
|
840
|
+
// constraint's own nil (to report).
|
|
841
|
+
settleContainer(peer, ctx) {
|
|
842
|
+
return this.admitContainer(peer, ctx, true);
|
|
843
|
+
}
|
|
666
844
|
// Membership for a container peer. Only the SIZING atoms have anything
|
|
667
845
|
// to say about a map or a list; every other atom is scalar-domain and
|
|
668
846
|
// refuses one.
|
|
@@ -674,7 +852,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
674
852
|
// isolated collect context so nothing leaks into the caller's errors.
|
|
675
853
|
// A mirror would be a second copy of a filter that has already grown
|
|
676
854
|
// subtle, free to drift from it; asking is correct by construction.
|
|
677
|
-
admitContainer(peer, ctx) {
|
|
855
|
+
admitContainer(peer, ctx, final) {
|
|
678
856
|
// A scalar-domain residual has no reading over a container.
|
|
679
857
|
if (null != this.domain) {
|
|
680
858
|
return this.fail(ctx, peer);
|
|
@@ -686,21 +864,76 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
686
864
|
this.dc = 0;
|
|
687
865
|
return new ConjunctVal_1.ConjunctVal({ peg: [this, peer] }, ctx);
|
|
688
866
|
}
|
|
689
|
-
const bad = this.checkMusts(peer, ctx);
|
|
867
|
+
const bad = this.checkMusts(peer, ctx, final);
|
|
690
868
|
if (null != bad) {
|
|
691
869
|
return bad;
|
|
692
870
|
}
|
|
693
|
-
|
|
694
|
-
|
|
871
|
+
// A MUST OVER A CONTAINER RESIDUATES with the atoms, for the same
|
|
872
|
+
// reason (use-cases/BUGS.md §17): `must({t: max(60)}, …)` beside a
|
|
873
|
+
// `{t: integer}` schema was answered against the schema layer
|
|
874
|
+
// ALONE, and discharged before the data it was written to judge
|
|
875
|
+
// ever arrived. It is decided at generation, where the value is
|
|
876
|
+
// whole -- unless there is nothing else on the constraint AND the
|
|
877
|
+
// reading is already final.
|
|
878
|
+
if (!this.uniq && 0 === this.uniqBy.length && null == this.count) {
|
|
879
|
+
if (true === final || 0 === this.musts.length) {
|
|
880
|
+
return peer;
|
|
881
|
+
}
|
|
882
|
+
return this.hold(peer, ctx);
|
|
695
883
|
}
|
|
696
884
|
const members = emittedMembers(peer, ctx);
|
|
697
885
|
if (null == members) {
|
|
698
|
-
//
|
|
699
|
-
//
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
886
|
+
// NOT COUNTABLE YET IS NOT A PASS. A container that cannot
|
|
887
|
+
// generate cannot be counted, and a SCHEMA is exactly that: the
|
|
888
|
+
// members of `{a: integer}` are types, so nothing is emitted and
|
|
889
|
+
// nothing can be counted. Discharging the atom here was the §16
|
|
890
|
+
// defect wearing its other face -- `length(min(2)) & {a: integer}`
|
|
891
|
+
// dropped its bound while still alone, so the data half of a
|
|
892
|
+
// `vet` meet was never measured at all and short data vetted
|
|
893
|
+
// clean against a bound the evaluator enforces.
|
|
894
|
+
//
|
|
895
|
+
// At generation the reading IS final: a container that still
|
|
896
|
+
// cannot generate has its own error, and that error is the one
|
|
897
|
+
// worth reporting, so it passes through untouched.
|
|
898
|
+
if (true === final) {
|
|
899
|
+
return peer;
|
|
900
|
+
}
|
|
901
|
+
return this.hold(peer, ctx);
|
|
902
|
+
}
|
|
903
|
+
// MONOTONE READINGS ONLY (the review's finding C, use-cases/BUGS.md
|
|
904
|
+
// §16). Members ACCUMULATE under unification -- a meet adds keys and
|
|
905
|
+
// never removes them -- so a settled container is settled ON ITS
|
|
906
|
+
// OWN and not against every value that may still contribute. The
|
|
907
|
+
// schema half of a `vet` meet, a document that is about to receive
|
|
908
|
+
// an `@` include, a bag a later `pack` will fill: each settles, and
|
|
909
|
+
// an atom that decided there decided too early.
|
|
910
|
+
//
|
|
911
|
+
// So a verdict is taken only when MORE MEMBERS CANNOT CHANGE IT:
|
|
912
|
+
// - an upper bound VIOLATED is permanent -> refuse now;
|
|
913
|
+
// satisfied is provisional -> keep the atom, re-check later.
|
|
914
|
+
// - a lower bound SATISFIED is permanent -> that part may go;
|
|
915
|
+
// violated is provisional -> keep the atom (which is why
|
|
916
|
+
// `length(min(1))` beside a template no longer kills the schema
|
|
917
|
+
// it was written for).
|
|
918
|
+
// - a DUPLICATE is permanent -> refuse now; distinctness is
|
|
919
|
+
// provisional -> keep the atom.
|
|
920
|
+
// Anything provisional residuates, exactly as an unsettled
|
|
921
|
+
// container does above, and the two spellings then agree.
|
|
922
|
+
const count = null == this.count ? undefined : this.count;
|
|
923
|
+
const n = members.length;
|
|
924
|
+
if (null != count) {
|
|
925
|
+
if (null != count.hi && !stateAdmits({ ...count, lo: undefined }, countVal(n))) {
|
|
926
|
+
return this.fail(ctx, peer);
|
|
927
|
+
}
|
|
928
|
+
if (0 < count.neqs.length && !stateAdmits({ ...count, lo: undefined, hi: undefined }, countVal(n))) {
|
|
929
|
+
return this.fail(ctx, peer);
|
|
930
|
+
}
|
|
931
|
+
// The provisional half, decided only when nothing more can
|
|
932
|
+
// arrive: a lower bound still short is a refusal at generation
|
|
933
|
+
// and a residue before it.
|
|
934
|
+
if (true === final && !stateAdmits(count, countVal(n))) {
|
|
935
|
+
return this.fail(ctx, peer);
|
|
936
|
+
}
|
|
704
937
|
}
|
|
705
938
|
if (this.uniq) {
|
|
706
939
|
// Members compare by CANONICAL FORM, which reduces to scalar
|
|
@@ -717,7 +950,65 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
717
950
|
seen.add(key);
|
|
718
951
|
}
|
|
719
952
|
}
|
|
720
|
-
|
|
953
|
+
// ... and the same test per PROJECTED key. A member that has no such
|
|
954
|
+
// key FAILS the constraint rather than being skipped: distinctness
|
|
955
|
+
// it cannot be shown to have is distinctness it does not have, and
|
|
956
|
+
// skipping would let one keyless record hide a duplicate.
|
|
957
|
+
for (const field of this.uniqBy) {
|
|
958
|
+
const seen = new Set();
|
|
959
|
+
for (const m of members) {
|
|
960
|
+
const at = true === m.isMap ?
|
|
961
|
+
m.peg[field] : undefined;
|
|
962
|
+
if (null == at) {
|
|
963
|
+
return this.fail(ctx, peer);
|
|
964
|
+
}
|
|
965
|
+
const key = at.canon;
|
|
966
|
+
if (seen.has(key)) {
|
|
967
|
+
return this.fail(ctx, peer);
|
|
968
|
+
}
|
|
969
|
+
seen.add(key);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
// WHAT IS LEFT IS PROVISIONAL, so the atom stays on the value. A
|
|
973
|
+
// lower bound already met is the one reading that cannot be undone,
|
|
974
|
+
// and an atom holding nothing else is spent: that is when it goes.
|
|
975
|
+
const spent = true === final || (0 === this.musts.length &&
|
|
976
|
+
!this.uniq && 0 === this.uniqBy.length &&
|
|
977
|
+
(null == count ||
|
|
978
|
+
(null == count.hi && 0 === count.neqs.length &&
|
|
979
|
+
stateAdmits(count, countVal(n)))));
|
|
980
|
+
if (spent) {
|
|
981
|
+
return peer;
|
|
982
|
+
}
|
|
983
|
+
// DONE, unlike the unsettled case above. The container HAS settled;
|
|
984
|
+
// the atom is kept only because a LATER value could still add
|
|
985
|
+
// members, and a residue that reported itself unresolved would
|
|
986
|
+
// leave every enclosing value unresolved with it -- a `type()`
|
|
987
|
+
// waiting on its argument for ever, and use case 09's whole
|
|
988
|
+
// registry with it.
|
|
989
|
+
// The ATOM is done too, not just the conjunct. An earlier pass may
|
|
990
|
+
// have set `dc = 0` on the unsettled branch above, and a conjunct
|
|
991
|
+
// recomputes its own doneness from its terms on every later meet --
|
|
992
|
+
// so a stale 0 here would drag the residue, and every value holding
|
|
993
|
+
// it, back to unresolved for ever.
|
|
994
|
+
return this.hold(peer, ctx);
|
|
995
|
+
}
|
|
996
|
+
// The atom kept on a value whose reading is still provisional.
|
|
997
|
+
// DONE, unlike the unsettled container above: the value HAS settled;
|
|
998
|
+
// the atom is kept only because a LATER value could still add
|
|
999
|
+
// members, and a residue that reported itself unresolved would leave
|
|
1000
|
+
// every enclosing value unresolved with it -- a `type()` waiting on
|
|
1001
|
+
// its argument for ever, and use case 09's whole registry with it.
|
|
1002
|
+
// The ATOM is done too, not just the conjunct. An earlier pass may
|
|
1003
|
+
// have set `dc = 0` on the unsettled branch, and a conjunct
|
|
1004
|
+
// recomputes its own doneness from its terms on every later meet --
|
|
1005
|
+
// so a stale 0 here would drag the residue, and every value holding
|
|
1006
|
+
// it, back to unresolved for ever.
|
|
1007
|
+
hold(peer, ctx) {
|
|
1008
|
+
this.dc = type_1.DONE;
|
|
1009
|
+
const held = new ConjunctVal_1.ConjunctVal({ peg: [this, peer] }, ctx);
|
|
1010
|
+
held.dc = type_1.DONE;
|
|
1011
|
+
return held;
|
|
721
1012
|
}
|
|
722
1013
|
// Meet with a kind: `number` (or `string` on the string domain) is
|
|
723
1014
|
// already implied by an ORDER atom's argument; a numeric LEAF narrows
|
|
@@ -782,6 +1073,11 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
782
1073
|
null == peer.count ? this.count : meetCount(this.count, peer.count);
|
|
783
1074
|
// `unique()` is idempotent: two of them are one.
|
|
784
1075
|
merged.uniq = this.uniq || peer.uniq;
|
|
1076
|
+
// `unique(a) & unique(b)` is BOTH, not the later one: each names a
|
|
1077
|
+
// key on which the members must differ, and dropping either would
|
|
1078
|
+
// silently weaken the constraint. Sorted and deduplicated, so the
|
|
1079
|
+
// meet is commutative and `unique(a) & unique(a)` is one atom.
|
|
1080
|
+
merged.uniqBy = [...new Set([...this.uniqBy, ...peer.uniqBy])].sort();
|
|
785
1081
|
// Band B checks accumulate in written order and are never merged,
|
|
786
1082
|
// deduplicated or reordered: each carries its own author message,
|
|
787
1083
|
// and two checks with the same shape may still say different things.
|
|
@@ -822,6 +1118,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
822
1118
|
res: [...this.res],
|
|
823
1119
|
count: this.count,
|
|
824
1120
|
uniq: this.uniq,
|
|
1121
|
+
uniqBy: [...this.uniqBy],
|
|
825
1122
|
musts: [...this.musts],
|
|
826
1123
|
invalid: this.invalid,
|
|
827
1124
|
};
|
|
@@ -839,6 +1136,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
|
|
|
839
1136
|
out.res = [...this.res];
|
|
840
1137
|
out.count = this.count;
|
|
841
1138
|
out.uniq = this.uniq;
|
|
1139
|
+
out.uniqBy = [...this.uniqBy];
|
|
842
1140
|
out.musts = [...this.musts];
|
|
843
1141
|
out.pending = this.pending;
|
|
844
1142
|
out.cjo = this.cjo;
|
|
@@ -903,10 +1201,11 @@ function dedupSorted(domain, neqs) {
|
|
|
903
1201
|
// Is this value, or anything inside it, a nil? A written argument that
|
|
904
1202
|
// holds one can never be satisfied, so the atom refuses it as an
|
|
905
1203
|
// argument rather than reporting a mystery failure against every peer.
|
|
1204
|
+
// Every caller passes a Val: `args[0]` comes from atomArgs, and the
|
|
1205
|
+
// walk below descends only into container pegs, which hold Vals. The
|
|
1206
|
+
// null/non-Val guard this function used to open with was therefore
|
|
1207
|
+
// unreachable, and the coverage gate said so.
|
|
906
1208
|
function holdsNil(v) {
|
|
907
|
-
if (null == v || true !== v.isVal) {
|
|
908
|
-
return false;
|
|
909
|
-
}
|
|
910
1209
|
if (true === v.isNil) {
|
|
911
1210
|
return true;
|
|
912
1211
|
}
|
|
@@ -923,6 +1222,29 @@ function holdsNil(v) {
|
|
|
923
1222
|
}
|
|
924
1223
|
return false;
|
|
925
1224
|
}
|
|
1225
|
+
// Whether a value, or anything inside it, is an effectful call — one
|
|
1226
|
+
// whose evaluation changes a node OTHER than the one being computed.
|
|
1227
|
+
// `move()` is the only builtin that does: it hides its resolution
|
|
1228
|
+
// target in place. Band B refuses one as an argument, because settling
|
|
1229
|
+
// it runs the effect against the live root before `must`'s trial clone
|
|
1230
|
+
// is taken, and a check that mutates cannot be report-only.
|
|
1231
|
+
function holdsMove(v) {
|
|
1232
|
+
if (true === v.isFunc && 'move' === v.funcname?.()) {
|
|
1233
|
+
return true;
|
|
1234
|
+
}
|
|
1235
|
+
const peg = v.peg;
|
|
1236
|
+
if (Array.isArray(peg)) {
|
|
1237
|
+
return peg.some((c) => holdsMove(c));
|
|
1238
|
+
}
|
|
1239
|
+
if (null != peg && 'object' === typeof peg) {
|
|
1240
|
+
for (const k in peg) {
|
|
1241
|
+
if (holdsMove(peg[k])) {
|
|
1242
|
+
return true;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
return false;
|
|
1247
|
+
}
|
|
926
1248
|
// The written arguments of an atom, flattened.
|
|
927
1249
|
//
|
|
928
1250
|
// A multi-argument call arrives from the func-paren grammar as ONE
|
|
@@ -979,6 +1301,12 @@ function canonState(s) {
|
|
|
979
1301
|
if (s.uniq) {
|
|
980
1302
|
parts.push('unique()');
|
|
981
1303
|
}
|
|
1304
|
+
// After the bare atom, and sorted: canon is a normal form, so two
|
|
1305
|
+
// documents writing the same keys in different orders must render
|
|
1306
|
+
// the same string.
|
|
1307
|
+
for (const key of s.uniqBy) {
|
|
1308
|
+
parts.push('unique(' + JSON.stringify(key) + ')');
|
|
1309
|
+
}
|
|
982
1310
|
for (const m of s.musts) {
|
|
983
1311
|
parts.push('must(' + m.v.canon + ',' + m.msg.canon + ')');
|
|
984
1312
|
}
|
|
@@ -988,6 +1316,110 @@ function canonState(s) {
|
|
|
988
1316
|
}
|
|
989
1317
|
return parts.join('&');
|
|
990
1318
|
}
|
|
1319
|
+
// SUBSUMPTION over two residuals (G3, the query built on the table in
|
|
1320
|
+
// docs/reference-language.md "Subsumption"): does every value `s`
|
|
1321
|
+
// admits pass `g` too? Implemented here because the compare machinery
|
|
1322
|
+
// (cmpVal, sameScalar, the recursive count state) is this module's.
|
|
1323
|
+
//
|
|
1324
|
+
// Three-valued: true / false / 'undecided'. `must` on the GENERAL side
|
|
1325
|
+
// is undecided by construction (a Band B predicate is opaque — G3 maps
|
|
1326
|
+
// the table's "never" to the query's honest sub_evaluate_only); every
|
|
1327
|
+
// other approximation folds toward false, the safe direction.
|
|
1328
|
+
function constraintStateSubsumes(g, s) {
|
|
1329
|
+
// A Band B predicate on the general side makes its admitted set
|
|
1330
|
+
// unknowable; an extra `must` on the SPECIFIC side only narrows it
|
|
1331
|
+
// and is ignored.
|
|
1332
|
+
if (0 < g.musts.length) {
|
|
1333
|
+
return 'undecided';
|
|
1334
|
+
}
|
|
1335
|
+
// Domains must agree where both constrain one; a sizing-only residual
|
|
1336
|
+
// has no domain and passes this gate.
|
|
1337
|
+
if (null != g.domain && g.domain !== s.domain) {
|
|
1338
|
+
return false;
|
|
1339
|
+
}
|
|
1340
|
+
// A general leaf restriction requires the same leaf on the specific
|
|
1341
|
+
// side (leaves are disjoint; an unrestricted specific admits other
|
|
1342
|
+
// leaves the general refuses).
|
|
1343
|
+
if (null != g.kind && g.kind !== s.kind) {
|
|
1344
|
+
return false;
|
|
1345
|
+
}
|
|
1346
|
+
// Interval containment: the general's endpoints at or beyond the
|
|
1347
|
+
// specific's, and where they coincide the general's may not be the
|
|
1348
|
+
// open one.
|
|
1349
|
+
const d = g.domain ?? s.domain;
|
|
1350
|
+
if (null != g.lo) {
|
|
1351
|
+
if (null == s.lo || null == d) {
|
|
1352
|
+
return false;
|
|
1353
|
+
}
|
|
1354
|
+
const c = cmpVal(d, g.lo.v, s.lo.v);
|
|
1355
|
+
if (0 < c || (0 === c && g.lo.open && !s.lo.open)) {
|
|
1356
|
+
return false;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
if (null != g.hi) {
|
|
1360
|
+
if (null == s.hi || null == d) {
|
|
1361
|
+
return false;
|
|
1362
|
+
}
|
|
1363
|
+
const c = cmpVal(d, g.hi.v, s.hi.v);
|
|
1364
|
+
if (c < 0 || (0 === c && g.hi.open && !s.hi.open)) {
|
|
1365
|
+
return false;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
// Excluding FEWER values is more general: every general exclusion
|
|
1369
|
+
// must be excluded by the specific too.
|
|
1370
|
+
for (const n of g.neqs) {
|
|
1371
|
+
if (!s.neqs.some((m) => sameScalar(n, m))) {
|
|
1372
|
+
return false;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
// Patterns compare as TEXT sets (the sanctioned approximation:
|
|
1376
|
+
// deciding regex containment is what this algebra refuses to do).
|
|
1377
|
+
for (const r of g.res) {
|
|
1378
|
+
if (!s.res.some((q) => q.src === r.src)) {
|
|
1379
|
+
return false;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
// `unique()` subsumes only itself on that axis: a general uniqueness
|
|
1383
|
+
// demand admits no container the unconstrained specific also admits.
|
|
1384
|
+
// ... and a general `unique(k)` needs the same key on the specific
|
|
1385
|
+
// side: distinctness on `port` says nothing about distinctness on
|
|
1386
|
+
// `name`.
|
|
1387
|
+
if (g.uniqBy.some((k) => !s.uniqBy.includes(k))) {
|
|
1388
|
+
return false;
|
|
1389
|
+
}
|
|
1390
|
+
if (g.uniq && !s.uniq) {
|
|
1391
|
+
return false;
|
|
1392
|
+
}
|
|
1393
|
+
// The count atom reuses this same table over the integer domain.
|
|
1394
|
+
if (null != g.count) {
|
|
1395
|
+
if (null == s.count) {
|
|
1396
|
+
return false;
|
|
1397
|
+
}
|
|
1398
|
+
return constraintStateSubsumes(g.count, s.count);
|
|
1399
|
+
}
|
|
1400
|
+
return true;
|
|
1401
|
+
}
|
|
1402
|
+
// The query surface for ts/src/subsume.ts: residual-vs-residual, and
|
|
1403
|
+
// residual-vs-scalar membership (with `must` and `unique` making the
|
|
1404
|
+
// scalar case undecided/false exactly as the meet would).
|
|
1405
|
+
function constraintSubsumesConstraint(g, s) {
|
|
1406
|
+
return constraintStateSubsumes(g, s);
|
|
1407
|
+
}
|
|
1408
|
+
function constraintAdmitsScalar(g, scalar) {
|
|
1409
|
+
if (0 < g.musts.length) {
|
|
1410
|
+
return 'undecided';
|
|
1411
|
+
}
|
|
1412
|
+
if (g.uniq || 0 < g.uniqBy.length) {
|
|
1413
|
+
return false;
|
|
1414
|
+
}
|
|
1415
|
+
// A count residual admits by LENGTH, which is the meet's business;
|
|
1416
|
+
// the query answers false (the safe direction) rather than
|
|
1417
|
+
// reimplementing the sizing walk here.
|
|
1418
|
+
if (null != g.count) {
|
|
1419
|
+
return false;
|
|
1420
|
+
}
|
|
1421
|
+
return stateAdmits(g, scalar);
|
|
1422
|
+
}
|
|
991
1423
|
// Does this residual's ORDER and MEMBERSHIP part admit the scalar? The
|
|
992
1424
|
// sizing atoms are deliberately not consulted: `admit` applies them to
|
|
993
1425
|
// the peer's length, and the count check applies this same function to
|
|
@@ -1096,7 +1528,8 @@ function stateEmpty(s) {
|
|
|
1096
1528
|
// members, so `integer & length(3)` and `min(2) & unique()` admit
|
|
1097
1529
|
// nothing. Uniqueness over the string domain is empty for the same
|
|
1098
1530
|
// reason -- a string's members are not values the algebra compares.
|
|
1099
|
-
if ('number' === d && (null != s.count || s.uniq
|
|
1531
|
+
if ('number' === d && (null != s.count || s.uniq ||
|
|
1532
|
+
0 < s.uniqBy.length)) {
|
|
1100
1533
|
return true;
|
|
1101
1534
|
}
|
|
1102
1535
|
if ('string' === d && s.uniq) {
|
|
@@ -1121,7 +1554,9 @@ function countBase() {
|
|
|
1121
1554
|
neqs: [],
|
|
1122
1555
|
res: [],
|
|
1123
1556
|
musts: [],
|
|
1557
|
+
// A COUNT is a number, and a number has no members to be distinct.
|
|
1124
1558
|
uniq: false,
|
|
1559
|
+
uniqBy: [],
|
|
1125
1560
|
};
|
|
1126
1561
|
}
|
|
1127
1562
|
// A count as a Val, so the count residual can be applied by exactly the
|
|
@@ -1138,13 +1573,16 @@ function countVal(n) {
|
|
|
1138
1573
|
function meetCount(a, b) {
|
|
1139
1574
|
return {
|
|
1140
1575
|
domain: 'number',
|
|
1141
|
-
|
|
1576
|
+
// `a` is always a countBase()-seeded residual, so its kind is
|
|
1577
|
+
// always Integer — there is no kindless side to fall back from.
|
|
1578
|
+
kind: a.kind,
|
|
1142
1579
|
lo: tighter('number', a.lo, b.lo, true),
|
|
1143
1580
|
hi: tighter('number', a.hi, b.hi, false),
|
|
1144
1581
|
neqs: dedupSorted('number', [...a.neqs, ...b.neqs]),
|
|
1145
1582
|
res: [],
|
|
1146
1583
|
musts: [],
|
|
1147
1584
|
uniq: false,
|
|
1585
|
+
uniqBy: [],
|
|
1148
1586
|
clash: true === a.clash || true === b.clash ||
|
|
1149
1587
|
(null != a.kind && null != b.kind && a.kind !== b.kind),
|
|
1150
1588
|
};
|
|
@@ -1167,14 +1605,15 @@ function countArgState(arg) {
|
|
|
1167
1605
|
domain: 'number',
|
|
1168
1606
|
lo: { v: arg, open: false },
|
|
1169
1607
|
hi: { v: arg, open: false },
|
|
1170
|
-
neqs: [], res: [], musts: [], uniq: false,
|
|
1608
|
+
neqs: [], res: [], musts: [], uniq: false, uniqBy: [],
|
|
1171
1609
|
};
|
|
1172
1610
|
}
|
|
1173
1611
|
if (true === arg?.isConstraint) {
|
|
1174
1612
|
const c = arg;
|
|
1175
1613
|
// A pattern, a sizing atom or a string bound inside a count is not
|
|
1176
1614
|
// a count constraint at all, and neither is a broken one.
|
|
1177
|
-
if (null != c.invalid || 0 < c.res.length || c.uniq ||
|
|
1615
|
+
if (null != c.invalid || 0 < c.res.length || c.uniq ||
|
|
1616
|
+
0 < c.uniqBy.length || null != c.count ||
|
|
1178
1617
|
'number' !== c.domain) {
|
|
1179
1618
|
return undefined;
|
|
1180
1619
|
}
|
|
@@ -1184,17 +1623,23 @@ function countArgState(arg) {
|
|
|
1184
1623
|
lo: c.lo,
|
|
1185
1624
|
hi: c.hi,
|
|
1186
1625
|
neqs: [...c.neqs],
|
|
1187
|
-
res: [], musts: [], uniq: false,
|
|
1626
|
+
res: [], musts: [], uniq: false, uniqBy: [],
|
|
1188
1627
|
};
|
|
1189
1628
|
}
|
|
1190
1629
|
if (true === arg?.isScalarKind) {
|
|
1191
1630
|
const marker = arg.peg;
|
|
1192
1631
|
if (Number === marker) {
|
|
1193
|
-
return {
|
|
1632
|
+
return {
|
|
1633
|
+
domain: 'number', neqs: [], res: [], musts: [],
|
|
1634
|
+
uniq: false, uniqBy: [],
|
|
1635
|
+
};
|
|
1194
1636
|
}
|
|
1195
1637
|
if (ScalarKindVal_1.Integer === marker || ScalarKindVal_1.Float === marker ||
|
|
1196
1638
|
ScalarKindVal_1.BigInteger === marker || ScalarKindVal_1.BigDecimal === marker) {
|
|
1197
|
-
return {
|
|
1639
|
+
return {
|
|
1640
|
+
domain: 'number', kind: marker, neqs: [], res: [], musts: [],
|
|
1641
|
+
uniq: false, uniqBy: [],
|
|
1642
|
+
};
|
|
1198
1643
|
}
|
|
1199
1644
|
return undefined;
|
|
1200
1645
|
}
|
|
@@ -1221,7 +1666,12 @@ function genable(child) {
|
|
|
1221
1666
|
return true === child.isScalar || true === child.isMap ||
|
|
1222
1667
|
true === child.isList || true === child.isPref ||
|
|
1223
1668
|
true === child.isRef || true === child.isDisjunct ||
|
|
1224
|
-
true === child.isNil
|
|
1669
|
+
true === child.isNil ||
|
|
1670
|
+
// A settled sizing residue generates through ConjunctVal.gen, so it
|
|
1671
|
+
// is a member like any other -- and a `length` that did not count
|
|
1672
|
+
// its sibling's `unique` would be the early-fold defect again, one
|
|
1673
|
+
// level up.
|
|
1674
|
+
undefined !== (0, BagVal_1.sizingResidue)(child);
|
|
1225
1675
|
}
|
|
1226
1676
|
// The children a bag would EMIT, mirroring the selection in
|
|
1227
1677
|
// `BagVal.gen` — type/hide marks skipped, non-generable residue and
|
|
@@ -1343,6 +1793,6 @@ class UniqueConstraintVal extends ConstraintVal {
|
|
|
1343
1793
|
constructor(spec, ctx) {
|
|
1344
1794
|
super({ ...spec, atom: 'unique' }, ctx);
|
|
1345
1795
|
}
|
|
1346
|
-
} /* node:coverage ignore next
|
|
1796
|
+
} /* node:coverage ignore next 23 */
|
|
1347
1797
|
exports.UniqueConstraintVal = UniqueConstraintVal;
|
|
1348
1798
|
//# sourceMappingURL=ConstraintVal.js.map
|