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
package/src/val/ConstraintVal.ts
CHANGED
|
@@ -57,6 +57,7 @@ import { empty } from './Val'
|
|
|
57
57
|
import { cmpCodePoint } from '../keyorder'
|
|
58
58
|
|
|
59
59
|
import { ConjunctVal } from './ConjunctVal'
|
|
60
|
+
import { sizingResidue } from './BagVal'
|
|
60
61
|
|
|
61
62
|
import { top } from './top'
|
|
62
63
|
|
|
@@ -115,6 +116,8 @@ type ConstraintState = {
|
|
|
115
116
|
// over the integer domain -- the count atom reuses
|
|
116
117
|
// this same algebra recursively
|
|
117
118
|
uniq: boolean // members must be pairwise distinct (unique())
|
|
119
|
+
uniqBy: string[] // ... and distinct ON EACH OF THESE KEYS
|
|
120
|
+
// (unique(k)), sorted and deduplicated
|
|
118
121
|
musts: MustAtom[] // Band B checks, kept in written order, never simplified
|
|
119
122
|
clash?: boolean // a kind disagreement inside a length() argument, recorded
|
|
120
123
|
// rather than raised: the argument's own meet has no
|
|
@@ -180,6 +183,18 @@ type ConstraintState = {
|
|
|
180
183
|
// and `test/spec/files/regex-corpus.txt` pins that the two produce
|
|
181
184
|
// byte-identical output for every pattern in a generated corpus.
|
|
182
185
|
|
|
186
|
+
// The largest repeat count Aontu's pattern language admits. RE2 caps a
|
|
187
|
+
// repeat at 1000 and refuses to compile past it, where JavaScript's
|
|
188
|
+
// backtracking engine accepts any count -- so `re("^a{1001}$")` was
|
|
189
|
+
// VALID in TypeScript and an `error` verdict in Go, a cross-port
|
|
190
|
+
// verdict flip on a schema an agent could be handed
|
|
191
|
+
// (status-2026-08-21.md section 4). Under ADR-003 the host's own
|
|
192
|
+
// compile failure must never be what a user sees, so the bound is
|
|
193
|
+
// Aontu's: at most RE_REPEAT_MAX, refused identically in both ports
|
|
194
|
+
// before either engine compiles. Nested products beyond the cap were
|
|
195
|
+
// already refused, by the quantified-group rule below.
|
|
196
|
+
const RE_REPEAT_MAX = 1000
|
|
197
|
+
|
|
183
198
|
// The normative expansions. These are Aontu's definitions, not either
|
|
184
199
|
// host's; both hosts are rewritten to them.
|
|
185
200
|
const RE_CLASS_DIGIT = '0-9'
|
|
@@ -196,6 +211,67 @@ const RE_ESCAPE_PUNCT = '\\.+*?()[]{}|^$/'
|
|
|
196
211
|
// word boundary, and `\xHH`. Each was probed in both engines.
|
|
197
212
|
const RE_ESCAPE_PASS = 'tnrfv'
|
|
198
213
|
|
|
214
|
+
// A counted quantifier `{n}`, `{n,}` or `{n,m}` starting at `src[at]`.
|
|
215
|
+
// Two rules, both about what the hosts do NOT share:
|
|
216
|
+
//
|
|
217
|
+
// - A bound above RE_REPEAT_MAX: RE2 refuses to compile it, JavaScript
|
|
218
|
+
// accepts it.
|
|
219
|
+
// - A brace that does not open a well-formed counted quantifier at
|
|
220
|
+
// all (`x{y}`, `a{`, `{,5}`): JavaScript compiled with the `u` flag
|
|
221
|
+
// makes it a SYNTAX ERROR, while RE2 reads it as a literal brace, so
|
|
222
|
+
// `re("^x{y}$")` was `constraint_pattern` in TypeScript and a plain
|
|
223
|
+
// unresolved value in Go -- the same class of verdict flip as the
|
|
224
|
+
// repeat cap, found by sweeping around it. A literal brace is
|
|
225
|
+
// written escaped (`a\\{1001\\}`), which both engines share.
|
|
226
|
+
function repeatWhy(src: string, at: number): [string, number] {
|
|
227
|
+
const bad = (what: string): [string, number] => [
|
|
228
|
+
'a ' + what + ', which the two engines do not read the same way', -1]
|
|
229
|
+
|
|
230
|
+
let i = at + 1
|
|
231
|
+
let digits = ''
|
|
232
|
+
const bounds: number[] = []
|
|
233
|
+
let commas = 0
|
|
234
|
+
for (; i < src.length; i++) {
|
|
235
|
+
const c = src[i]
|
|
236
|
+
if ('0' <= c && c <= '9') {
|
|
237
|
+
digits += c
|
|
238
|
+
continue
|
|
239
|
+
}
|
|
240
|
+
if (',' === c) {
|
|
241
|
+
if ('' === digits || 0 < commas) {
|
|
242
|
+
return bad('{ that does not open a counted quantifier')
|
|
243
|
+
}
|
|
244
|
+
bounds.push(parseInt(digits, 10))
|
|
245
|
+
digits = ''
|
|
246
|
+
commas++
|
|
247
|
+
continue
|
|
248
|
+
}
|
|
249
|
+
if ('}' === c) {
|
|
250
|
+
if ('' !== digits) {
|
|
251
|
+
bounds.push(parseInt(digits, 10))
|
|
252
|
+
}
|
|
253
|
+
else if (0 === commas) {
|
|
254
|
+
return bad('{ that does not open a counted quantifier')
|
|
255
|
+
}
|
|
256
|
+
break
|
|
257
|
+
}
|
|
258
|
+
return bad('{ that does not open a counted quantifier')
|
|
259
|
+
}
|
|
260
|
+
if (i >= src.length) {
|
|
261
|
+
return bad('{ that does not open a counted quantifier')
|
|
262
|
+
}
|
|
263
|
+
for (const b of bounds) {
|
|
264
|
+
if (RE_REPEAT_MAX < b) {
|
|
265
|
+
return ['a repeat count above ' + RE_REPEAT_MAX +
|
|
266
|
+
', which RE2 refuses to compile', -1]
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// A descending range (`{5,2}`) is refused by both engines' own
|
|
270
|
+
// compilers, so it needs no rule here.
|
|
271
|
+
return ['', i]
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
199
275
|
function isHexDigit(c: string | undefined): boolean {
|
|
200
276
|
return null != c && (
|
|
201
277
|
('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'))
|
|
@@ -302,8 +378,16 @@ function normaliseRe(src: string): [string, string] {
|
|
|
302
378
|
}
|
|
303
379
|
}
|
|
304
380
|
|
|
381
|
+
// Where the counted quantifier validated below closes, so its own
|
|
382
|
+
// `}` is told apart from a stray one; and whether the atom just
|
|
383
|
+
// emitted was `^` or `$`, which cannot be quantified.
|
|
384
|
+
let repeatEnd = -1
|
|
385
|
+
let anchorPrev = false
|
|
386
|
+
|
|
305
387
|
for (let i = 0; i < src.length; i++) {
|
|
306
388
|
const c = src[i]
|
|
389
|
+
const afterAnchor = anchorPrev
|
|
390
|
+
anchorPrev = false
|
|
307
391
|
|
|
308
392
|
if ('\\' === c) {
|
|
309
393
|
const [emit, why, extra] = normaliseEscape(src[i + 1], src, i, inClass)
|
|
@@ -311,6 +395,17 @@ function normaliseRe(src: string): [string, string] {
|
|
|
311
395
|
return ['', why]
|
|
312
396
|
}
|
|
313
397
|
out.push(emit)
|
|
398
|
+
// `\b` AND `\B` ARE ASSERTIONS TOO, and quantifying one is the
|
|
399
|
+
// same disagreement `^{1}` is: JavaScript under `u` calls it a
|
|
400
|
+
// syntax error, RE2 quantifies the assertion happily. The rule
|
|
401
|
+
// said "`\b` and `\B` quantify identically in both and are left
|
|
402
|
+
// alone" and that was measured wrong -- `re("\\b{1}x")` is
|
|
403
|
+
// `constraint_pattern` in TypeScript and an accepted schema in
|
|
404
|
+
// Go. Only OUTSIDE a class: inside one, `\b` is a backspace in
|
|
405
|
+
// both engines and repeats like any other character.
|
|
406
|
+
if (!inClass && ('b' === src[i + 1] || 'B' === src[i + 1])) {
|
|
407
|
+
anchorPrev = true
|
|
408
|
+
}
|
|
314
409
|
i += 1 + extra
|
|
315
410
|
continue
|
|
316
411
|
}
|
|
@@ -387,12 +482,43 @@ function normaliseRe(src: string): [string, string] {
|
|
|
387
482
|
}
|
|
388
483
|
|
|
389
484
|
if ('*' === c || '+' === c || '?' === c || '{' === c) {
|
|
485
|
+
// Nothing to repeat. JavaScript under the `u` flag makes this a
|
|
486
|
+
// SYNTAX ERROR, where RE2 quantifies the assertion happily and
|
|
487
|
+
// matches, so `re("^{1}")` was `constraint_pattern` in
|
|
488
|
+
// TypeScript and an accepted schema in Go. The four assertions
|
|
489
|
+
// behave alike here -- `^`, `$`, `\b` and `\B` -- so the rule
|
|
490
|
+
// names all four.
|
|
491
|
+
if (afterAnchor) {
|
|
492
|
+
return ['', 'a quantifier applied to `^`, `$`, `\\b` or ' +
|
|
493
|
+
'`\\B`, which has nothing to repeat']
|
|
494
|
+
}
|
|
495
|
+
if ('{' === c) {
|
|
496
|
+
const [why, end] = repeatWhy(src, i)
|
|
497
|
+
if ('' !== why) {
|
|
498
|
+
return ['', why]
|
|
499
|
+
}
|
|
500
|
+
repeatEnd = end
|
|
501
|
+
}
|
|
390
502
|
mark('q')
|
|
391
503
|
out.push(c)
|
|
392
504
|
continue
|
|
393
505
|
}
|
|
394
506
|
|
|
507
|
+
// A `}` that closes no counted quantifier: JavaScript under `u`
|
|
508
|
+
// refuses it as a lone quantifier bracket, RE2 reads it as a
|
|
509
|
+
// literal. Written escaped inside a class (`[}]`) it is a literal
|
|
510
|
+
// in both, which is the spelling that survives.
|
|
511
|
+
if ('}' === c) {
|
|
512
|
+
if (i !== repeatEnd) {
|
|
513
|
+
return ['', 'a `}` that closes no counted quantifier, which ' +
|
|
514
|
+
'the two engines do not read the same way']
|
|
515
|
+
}
|
|
516
|
+
out.push(c)
|
|
517
|
+
continue
|
|
518
|
+
}
|
|
519
|
+
|
|
395
520
|
out.push(c)
|
|
521
|
+
anchorPrev = ('^' === c || '$' === c)
|
|
396
522
|
}
|
|
397
523
|
|
|
398
524
|
if (inClass) {
|
|
@@ -418,6 +544,16 @@ function stringLeaf(v: any): boolean {
|
|
|
418
544
|
return true === v?.isScalar && 'string' === typeof v.peg && v.isString
|
|
419
545
|
}
|
|
420
546
|
|
|
547
|
+
// The VALUE-admission widening of stringLeaf: a path value is a string
|
|
548
|
+
// with more structure (path <: string, ADR-016), so the string-domain
|
|
549
|
+
// constraints admit one -- `refer() & re("auth$")` must still check a
|
|
550
|
+
// link whose value is now a path. The ARGUMENT sites keep the strict
|
|
551
|
+
// test: a pattern, a bound or an exclusion is spelled with a plain
|
|
552
|
+
// string, never a path.
|
|
553
|
+
function stringishLeaf(v: any): boolean {
|
|
554
|
+
return stringLeaf(v) || (true === v?.isScalar && true === v.isPath)
|
|
555
|
+
}
|
|
556
|
+
|
|
421
557
|
|
|
422
558
|
// Scalar identity: leaf AND value, the lattice's own rule (1 and 1.0
|
|
423
559
|
// are different scalars). Exact numeric comparison decides the value
|
|
@@ -426,6 +562,12 @@ function sameScalar(a: any, b: any): boolean {
|
|
|
426
562
|
if (numericLeaf(a) && numericLeaf(b)) {
|
|
427
563
|
return towerRank(a) === towerRank(b) && 0 === cmpNumeric(a, b)
|
|
428
564
|
}
|
|
565
|
+
if (true === a?.isPath || true === b?.isPath) {
|
|
566
|
+
// Path identity is kind AND spelling (ADR-016): `neq(path($.x))`
|
|
567
|
+
// excludes exactly that address, and never a plain string that
|
|
568
|
+
// happens to spell it.
|
|
569
|
+
return true === a?.isPath && true === b?.isPath && a.peg === b.peg
|
|
570
|
+
}
|
|
429
571
|
if (stringLeaf(a) && stringLeaf(b)) {
|
|
430
572
|
return a.peg === b.peg
|
|
431
573
|
}
|
|
@@ -480,6 +622,7 @@ class ConstraintVal extends FeatureVal {
|
|
|
480
622
|
res: ReAtom[] = []
|
|
481
623
|
count?: ConstraintState
|
|
482
624
|
uniq = false
|
|
625
|
+
uniqBy: string[] = []
|
|
483
626
|
musts: MustAtom[] = []
|
|
484
627
|
// An atom whose arguments have not settled yet (G1 phase 4). Held
|
|
485
628
|
// until unify has a ctx to resolve them through; never present on a
|
|
@@ -506,6 +649,7 @@ class ConstraintVal extends FeatureVal {
|
|
|
506
649
|
this.res = spec.state.res ?? []
|
|
507
650
|
this.count = spec.state.count
|
|
508
651
|
this.uniq = spec.state.uniq ?? false
|
|
652
|
+
this.uniqBy = spec.state.uniqBy ?? []
|
|
509
653
|
this.musts = spec.state.musts ?? []
|
|
510
654
|
this.invalid = spec.state.invalid
|
|
511
655
|
}
|
|
@@ -517,7 +661,14 @@ class ConstraintVal extends FeatureVal {
|
|
|
517
661
|
// in unify, where there is a ctx to resolve through, and the
|
|
518
662
|
// residual is built from the settled arguments. Only a settled
|
|
519
663
|
// argument of the wrong shape is an `invalid-arg`.
|
|
520
|
-
|
|
664
|
+
// The effectful-argument refusal happens HERE, on the written
|
|
665
|
+
// form, and not in fromAtom: settling is what runs the effect, so
|
|
666
|
+
// by the time fromAtom sees a settled `move($.b)` the move has
|
|
667
|
+
// already happened and the argument is just its result.
|
|
668
|
+
if ('must' === spec.atom && args.some((a: any) => holdsMove(a))) {
|
|
669
|
+
this.invalid = 'invalid-arg'
|
|
670
|
+
}
|
|
671
|
+
else if (args.some((a: any) => true !== a?.done)) {
|
|
521
672
|
this.pending = { atom: spec.atom, args }
|
|
522
673
|
}
|
|
523
674
|
else {
|
|
@@ -525,7 +676,8 @@ class ConstraintVal extends FeatureVal {
|
|
|
525
676
|
}
|
|
526
677
|
}
|
|
527
678
|
|
|
528
|
-
if (null != this.count || this.uniq || 0 < this.
|
|
679
|
+
if (null != this.count || this.uniq || 0 < this.uniqBy.length ||
|
|
680
|
+
0 < this.musts.length ||
|
|
529
681
|
(null != this.pending && lateAtom(this.pending.atom))) {
|
|
530
682
|
this.cjo = LATE_CJO
|
|
531
683
|
}
|
|
@@ -546,19 +698,32 @@ class ConstraintVal extends FeatureVal {
|
|
|
546
698
|
// SETTLED — the constructor routes an unsettled one to `pending` —
|
|
547
699
|
// so a shape this cannot use is a genuine `invalid-arg`, not a
|
|
548
700
|
// not-yet.
|
|
549
|
-
private fromAtom(atom: string, args: any[]) {
|
|
550
|
-
// Mark the residual invalid
|
|
551
|
-
//
|
|
552
|
-
|
|
701
|
+
private fromAtom(atom: string, args: any[]): void {
|
|
702
|
+
// Mark the residual invalid. It answers NOTHING: every caller
|
|
703
|
+
// spells its exit `return bad(...)` and none reads a value, so a
|
|
704
|
+
// return type would be a value nobody consumes -- and a routine
|
|
705
|
+
// that answers on some paths and not others is the shape a reader
|
|
706
|
+
// (and a static analyser) has to stop and check.
|
|
707
|
+
const bad = (why: string): void => {
|
|
553
708
|
this.invalid = why
|
|
554
|
-
return true
|
|
555
709
|
}
|
|
556
710
|
|
|
557
|
-
// `unique()`
|
|
558
|
-
//
|
|
559
|
-
//
|
|
711
|
+
// `unique()` is a property of the container -- not a comparison
|
|
712
|
+
// against a value -- so with no argument it says the members are
|
|
713
|
+
// pairwise DISTINCT. THE ONE ARGUMENT IS A PROJECTOR (the review's
|
|
714
|
+
// finding I: "unique()-by-field is reserved but absent", and the
|
|
715
|
+
// arity was reserved for exactly this): `unique(port)` says no two
|
|
716
|
+
// members share a `port`, which is how "no two services share a
|
|
717
|
+
// port" and "event ids are unique" are said.
|
|
560
718
|
if ('unique' === atom) {
|
|
561
|
-
|
|
719
|
+
if (0 === args.length) {
|
|
720
|
+
this.uniq = true
|
|
721
|
+
return
|
|
722
|
+
}
|
|
723
|
+
if (1 !== args.length || !stringLeaf(args[0])) {
|
|
724
|
+
return bad('invalid-arg')
|
|
725
|
+
}
|
|
726
|
+
this.uniqBy = [args[0].peg]
|
|
562
727
|
return
|
|
563
728
|
}
|
|
564
729
|
|
|
@@ -583,6 +748,9 @@ class ConstraintVal extends FeatureVal {
|
|
|
583
748
|
if (holdsNil(args[0])) {
|
|
584
749
|
return bad('invalid-arg')
|
|
585
750
|
}
|
|
751
|
+
// (An effectful argument is refused at construction, in the
|
|
752
|
+
// constructor: by the time this arm sees a settled `move($.b)`
|
|
753
|
+
// the move has already run.)
|
|
586
754
|
this.musts = [{ v: args[0], msg: args[1] }]
|
|
587
755
|
return
|
|
588
756
|
}
|
|
@@ -593,7 +761,7 @@ class ConstraintVal extends FeatureVal {
|
|
|
593
761
|
}
|
|
594
762
|
let domain: 'number' | 'string' | undefined = undefined
|
|
595
763
|
for (const a of args) {
|
|
596
|
-
const d = numericLeaf(a) ? 'number' :
|
|
764
|
+
const d = numericLeaf(a) ? 'number' : stringishLeaf(a) ? 'string' : undefined
|
|
597
765
|
if (null == d || (null != domain && d !== domain)) {
|
|
598
766
|
return bad('invalid-arg')
|
|
599
767
|
}
|
|
@@ -676,7 +844,7 @@ class ConstraintVal extends FeatureVal {
|
|
|
676
844
|
return
|
|
677
845
|
}
|
|
678
846
|
|
|
679
|
-
const domain = numericLeaf(a) ? 'number' :
|
|
847
|
+
const domain = numericLeaf(a) ? 'number' : stringishLeaf(a) ? 'string' : undefined
|
|
680
848
|
if (null == domain) {
|
|
681
849
|
return bad('invalid-arg')
|
|
682
850
|
}
|
|
@@ -700,7 +868,20 @@ class ConstraintVal extends FeatureVal {
|
|
|
700
868
|
// residual is stable and the ladder is total.
|
|
701
869
|
let out: Val
|
|
702
870
|
|
|
703
|
-
|
|
871
|
+
// A rel residual or a graph atom HOLDS constraints for the value
|
|
872
|
+
// they stand for (RELATIONS P1/P2): hand the drive over before
|
|
873
|
+
// any settling, exactly as a container does, so `rel(t) & re(x)`
|
|
874
|
+
// reads the same in either order. Reached through the INCLUDE
|
|
875
|
+
// flow (a loaded schema's conjunct re-drives with the constraint
|
|
876
|
+
// on the left) -- inline documents route rel/atom peers through
|
|
877
|
+
// unite's b-drives first, which is why use-cases/12-relations
|
|
878
|
+
// refused without this arm while every inline row stayed green;
|
|
879
|
+
// pinned by constraint-hands-drive-to-rel-and-atom.
|
|
880
|
+
if (true === (peer as any)?.isRel
|
|
881
|
+
|| true === (peer as any)?.isGraphAtom) {
|
|
882
|
+
out = (peer as any).unify(this, ctx)
|
|
883
|
+
}
|
|
884
|
+
else if (null != this.pending) {
|
|
704
885
|
out = this.settle(peer, ctx)
|
|
705
886
|
}
|
|
706
887
|
else if (null != this.invalid) {
|
|
@@ -801,9 +982,9 @@ class ConstraintVal extends FeatureVal {
|
|
|
801
982
|
if (null == peer || (peer as any).isTop) {
|
|
802
983
|
return again
|
|
803
984
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
985
|
+
// No nil-peer arm: `unite` returns a nil operand before dispatching
|
|
986
|
+
// to any Val's unify (ts/src/unify.ts), so a nil never reaches here
|
|
987
|
+
// — and were one to, the conjunct below folds to it unchanged.
|
|
807
988
|
return new ConjunctVal({ peg: [again, peer] }, ctx)
|
|
808
989
|
}
|
|
809
990
|
|
|
@@ -811,8 +992,9 @@ class ConstraintVal extends FeatureVal {
|
|
|
811
992
|
// Membership: the peer scalar passes every part of the residual, or
|
|
812
993
|
// the whole meet is a located conflict.
|
|
813
994
|
private admit(peer: any, ctx: AontuContext): Val {
|
|
814
|
-
// No scalar has members, so a `unique()` residual admits none
|
|
815
|
-
|
|
995
|
+
// No scalar has members, so a `unique()` residual admits none --
|
|
996
|
+
// and neither does a `unique(k)` one, for the same reason.
|
|
997
|
+
if (this.uniq || 0 < this.uniqBy.length) {
|
|
816
998
|
return this.fail(ctx, peer)
|
|
817
999
|
}
|
|
818
1000
|
if (!stateAdmits(this, peer)) {
|
|
@@ -822,14 +1004,17 @@ class ConstraintVal extends FeatureVal {
|
|
|
822
1004
|
// Only a string among the scalars has a length, and it is counted
|
|
823
1005
|
// in CODE POINTS -- not UTF-16 units (this host's native count)
|
|
824
1006
|
// and not bytes (Go's). Iterating a string yields code points.
|
|
825
|
-
if (!
|
|
1007
|
+
if (!stringishLeaf(peer)) {
|
|
826
1008
|
return this.fail(ctx, peer)
|
|
827
1009
|
}
|
|
828
1010
|
if (!stateAdmits(this.count, countVal([...peer.peg].length))) {
|
|
829
1011
|
return this.fail(ctx, peer)
|
|
830
1012
|
}
|
|
831
1013
|
}
|
|
832
|
-
|
|
1014
|
+
// A SCALAR HAS NO MEMBERS to accumulate, so its musts are decided
|
|
1015
|
+
// here and never residuate: the final reading is the only reading
|
|
1016
|
+
// a scalar has.
|
|
1017
|
+
const bad = this.checkMusts(peer, ctx, true)
|
|
833
1018
|
if (null != bad) {
|
|
834
1019
|
return bad
|
|
835
1020
|
}
|
|
@@ -845,10 +1030,30 @@ class ConstraintVal extends FeatureVal {
|
|
|
845
1030
|
// The trial runs in an isolated collect context so a failing check
|
|
846
1031
|
// leaves nothing on the caller's error list — only the located nil
|
|
847
1032
|
// this returns, carrying the author's own message.
|
|
848
|
-
|
|
1033
|
+
// `final` is the generation-time reading (see settleContainer): a
|
|
1034
|
+
// must whose own check is a SIZING atom inherits that atom's
|
|
1035
|
+
// provisionality, because `must(length(2), …)` against a map that may
|
|
1036
|
+
// still gain members has not failed yet -- it has not been answered.
|
|
1037
|
+
// Without this the must would decide against whatever the container
|
|
1038
|
+
// held when it first settled, which is the very defect the sizing
|
|
1039
|
+
// atoms were fixed for (use-cases/BUGS.md §16, §17).
|
|
1040
|
+
private checkMusts(
|
|
1041
|
+
peer: any, ctx: AontuContext, final?: boolean): Val | undefined {
|
|
849
1042
|
for (const m of this.musts) {
|
|
850
1043
|
const trial = ctx.clone({ err: [], collect: true })
|
|
851
|
-
|
|
1044
|
+
let got: any = unite(trial, m.v.clone(trial), peer.clone(trial), 'must')
|
|
1045
|
+
// A must whose check is a SIZING atom answers a residue rather
|
|
1046
|
+
// than a verdict, because the atom is waiting for members that
|
|
1047
|
+
// may still arrive. Before generation that residue IS the answer
|
|
1048
|
+
// -- the must has not failed, it has not been asked yet -- and at
|
|
1049
|
+
// generation it is settled, because nothing more can arrive.
|
|
1050
|
+
const residue = sizingResidue(got)
|
|
1051
|
+
if (undefined !== residue) {
|
|
1052
|
+
if (true !== final) {
|
|
1053
|
+
continue
|
|
1054
|
+
}
|
|
1055
|
+
got = residue.con.settleContainer(residue.bag, trial)
|
|
1056
|
+
}
|
|
852
1057
|
if (true === (got as any)?.isNil || 0 < trial.err.length) {
|
|
853
1058
|
return makeNilErr(ctx, 'must', this, peer, undefined, {
|
|
854
1059
|
message: m.msg.peg,
|
|
@@ -861,6 +1066,16 @@ class ConstraintVal extends FeatureVal {
|
|
|
861
1066
|
}
|
|
862
1067
|
|
|
863
1068
|
|
|
1069
|
+
// THE FINAL READING of a sizing atom over a container: the same
|
|
1070
|
+
// checks admitContainer makes, with the provisional ones taken as
|
|
1071
|
+
// decided. Called from ConjunctVal.gen, which is where no more
|
|
1072
|
+
// members can arrive. Returns the container (to generate) or the
|
|
1073
|
+
// constraint's own nil (to report).
|
|
1074
|
+
settleContainer(peer: any, ctx: AontuContext): Val {
|
|
1075
|
+
return this.admitContainer(peer, ctx, true)
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
|
|
864
1079
|
// Membership for a container peer. Only the SIZING atoms have anything
|
|
865
1080
|
// to say about a map or a list; every other atom is scalar-domain and
|
|
866
1081
|
// refuses one.
|
|
@@ -872,7 +1087,8 @@ class ConstraintVal extends FeatureVal {
|
|
|
872
1087
|
// isolated collect context so nothing leaks into the caller's errors.
|
|
873
1088
|
// A mirror would be a second copy of a filter that has already grown
|
|
874
1089
|
// subtle, free to drift from it; asking is correct by construction.
|
|
875
|
-
private admitContainer(
|
|
1090
|
+
private admitContainer(
|
|
1091
|
+
peer: any, ctx: AontuContext, final?: boolean): Val {
|
|
876
1092
|
// A scalar-domain residual has no reading over a container.
|
|
877
1093
|
if (null != this.domain) {
|
|
878
1094
|
return this.fail(ctx, peer)
|
|
@@ -885,24 +1101,81 @@ class ConstraintVal extends FeatureVal {
|
|
|
885
1101
|
return new ConjunctVal({ peg: [this, peer] }, ctx)
|
|
886
1102
|
}
|
|
887
1103
|
|
|
888
|
-
const bad = this.checkMusts(peer, ctx)
|
|
1104
|
+
const bad = this.checkMusts(peer, ctx, final)
|
|
889
1105
|
if (null != bad) {
|
|
890
1106
|
return bad
|
|
891
1107
|
}
|
|
892
1108
|
|
|
893
|
-
|
|
894
|
-
|
|
1109
|
+
// A MUST OVER A CONTAINER RESIDUATES with the atoms, for the same
|
|
1110
|
+
// reason (use-cases/BUGS.md §17): `must({t: max(60)}, …)` beside a
|
|
1111
|
+
// `{t: integer}` schema was answered against the schema layer
|
|
1112
|
+
// ALONE, and discharged before the data it was written to judge
|
|
1113
|
+
// ever arrived. It is decided at generation, where the value is
|
|
1114
|
+
// whole -- unless there is nothing else on the constraint AND the
|
|
1115
|
+
// reading is already final.
|
|
1116
|
+
if (!this.uniq && 0 === this.uniqBy.length && null == this.count) {
|
|
1117
|
+
if (true === final || 0 === this.musts.length) {
|
|
1118
|
+
return peer
|
|
1119
|
+
}
|
|
1120
|
+
return this.hold(peer, ctx)
|
|
895
1121
|
}
|
|
896
1122
|
|
|
897
1123
|
const members = emittedMembers(peer, ctx)
|
|
898
1124
|
if (null == members) {
|
|
899
|
-
//
|
|
900
|
-
//
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1125
|
+
// NOT COUNTABLE YET IS NOT A PASS. A container that cannot
|
|
1126
|
+
// generate cannot be counted, and a SCHEMA is exactly that: the
|
|
1127
|
+
// members of `{a: integer}` are types, so nothing is emitted and
|
|
1128
|
+
// nothing can be counted. Discharging the atom here was the §16
|
|
1129
|
+
// defect wearing its other face -- `length(min(2)) & {a: integer}`
|
|
1130
|
+
// dropped its bound while still alone, so the data half of a
|
|
1131
|
+
// `vet` meet was never measured at all and short data vetted
|
|
1132
|
+
// clean against a bound the evaluator enforces.
|
|
1133
|
+
//
|
|
1134
|
+
// At generation the reading IS final: a container that still
|
|
1135
|
+
// cannot generate has its own error, and that error is the one
|
|
1136
|
+
// worth reporting, so it passes through untouched.
|
|
1137
|
+
if (true === final) {
|
|
1138
|
+
return peer
|
|
1139
|
+
}
|
|
1140
|
+
return this.hold(peer, ctx)
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// MONOTONE READINGS ONLY (the review's finding C, use-cases/BUGS.md
|
|
1144
|
+
// §16). Members ACCUMULATE under unification -- a meet adds keys and
|
|
1145
|
+
// never removes them -- so a settled container is settled ON ITS
|
|
1146
|
+
// OWN and not against every value that may still contribute. The
|
|
1147
|
+
// schema half of a `vet` meet, a document that is about to receive
|
|
1148
|
+
// an `@` include, a bag a later `pack` will fill: each settles, and
|
|
1149
|
+
// an atom that decided there decided too early.
|
|
1150
|
+
//
|
|
1151
|
+
// So a verdict is taken only when MORE MEMBERS CANNOT CHANGE IT:
|
|
1152
|
+
// - an upper bound VIOLATED is permanent -> refuse now;
|
|
1153
|
+
// satisfied is provisional -> keep the atom, re-check later.
|
|
1154
|
+
// - a lower bound SATISFIED is permanent -> that part may go;
|
|
1155
|
+
// violated is provisional -> keep the atom (which is why
|
|
1156
|
+
// `length(min(1))` beside a template no longer kills the schema
|
|
1157
|
+
// it was written for).
|
|
1158
|
+
// - a DUPLICATE is permanent -> refuse now; distinctness is
|
|
1159
|
+
// provisional -> keep the atom.
|
|
1160
|
+
// Anything provisional residuates, exactly as an unsettled
|
|
1161
|
+
// container does above, and the two spellings then agree.
|
|
1162
|
+
const count = null == this.count ? undefined : this.count
|
|
1163
|
+
const n = members.length
|
|
1164
|
+
if (null != count) {
|
|
1165
|
+
if (null != count.hi && !stateAdmits({ ...count, lo: undefined },
|
|
1166
|
+
countVal(n))) {
|
|
1167
|
+
return this.fail(ctx, peer)
|
|
1168
|
+
}
|
|
1169
|
+
if (0 < count.neqs.length && !stateAdmits(
|
|
1170
|
+
{ ...count, lo: undefined, hi: undefined }, countVal(n))) {
|
|
1171
|
+
return this.fail(ctx, peer)
|
|
1172
|
+
}
|
|
1173
|
+
// The provisional half, decided only when nothing more can
|
|
1174
|
+
// arrive: a lower bound still short is a refusal at generation
|
|
1175
|
+
// and a residue before it.
|
|
1176
|
+
if (true === final && !stateAdmits(count, countVal(n))) {
|
|
1177
|
+
return this.fail(ctx, peer)
|
|
1178
|
+
}
|
|
906
1179
|
}
|
|
907
1180
|
|
|
908
1181
|
if (this.uniq) {
|
|
@@ -921,7 +1194,68 @@ class ConstraintVal extends FeatureVal {
|
|
|
921
1194
|
}
|
|
922
1195
|
}
|
|
923
1196
|
|
|
924
|
-
|
|
1197
|
+
// ... and the same test per PROJECTED key. A member that has no such
|
|
1198
|
+
// key FAILS the constraint rather than being skipped: distinctness
|
|
1199
|
+
// it cannot be shown to have is distinctness it does not have, and
|
|
1200
|
+
// skipping would let one keyless record hide a duplicate.
|
|
1201
|
+
for (const field of this.uniqBy) {
|
|
1202
|
+
const seen = new Set<string>()
|
|
1203
|
+
for (const m of members) {
|
|
1204
|
+
const at: any = true === (m as any).isMap ?
|
|
1205
|
+
(m as any).peg[field] : undefined
|
|
1206
|
+
if (null == at) {
|
|
1207
|
+
return this.fail(ctx, peer)
|
|
1208
|
+
}
|
|
1209
|
+
const key = at.canon
|
|
1210
|
+
if (seen.has(key)) {
|
|
1211
|
+
return this.fail(ctx, peer)
|
|
1212
|
+
}
|
|
1213
|
+
seen.add(key)
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// WHAT IS LEFT IS PROVISIONAL, so the atom stays on the value. A
|
|
1218
|
+
// lower bound already met is the one reading that cannot be undone,
|
|
1219
|
+
// and an atom holding nothing else is spent: that is when it goes.
|
|
1220
|
+
const spent = true === final || (0 === this.musts.length &&
|
|
1221
|
+
!this.uniq && 0 === this.uniqBy.length &&
|
|
1222
|
+
(null == count ||
|
|
1223
|
+
(null == count.hi && 0 === count.neqs.length &&
|
|
1224
|
+
stateAdmits(count, countVal(n)))))
|
|
1225
|
+
if (spent) {
|
|
1226
|
+
return peer
|
|
1227
|
+
}
|
|
1228
|
+
// DONE, unlike the unsettled case above. The container HAS settled;
|
|
1229
|
+
// the atom is kept only because a LATER value could still add
|
|
1230
|
+
// members, and a residue that reported itself unresolved would
|
|
1231
|
+
// leave every enclosing value unresolved with it -- a `type()`
|
|
1232
|
+
// waiting on its argument for ever, and use case 09's whole
|
|
1233
|
+
// registry with it.
|
|
1234
|
+
// The ATOM is done too, not just the conjunct. An earlier pass may
|
|
1235
|
+
// have set `dc = 0` on the unsettled branch above, and a conjunct
|
|
1236
|
+
// recomputes its own doneness from its terms on every later meet --
|
|
1237
|
+
// so a stale 0 here would drag the residue, and every value holding
|
|
1238
|
+
// it, back to unresolved for ever.
|
|
1239
|
+
return this.hold(peer, ctx)
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
// The atom kept on a value whose reading is still provisional.
|
|
1244
|
+
// DONE, unlike the unsettled container above: the value HAS settled;
|
|
1245
|
+
// the atom is kept only because a LATER value could still add
|
|
1246
|
+
// members, and a residue that reported itself unresolved would leave
|
|
1247
|
+
// every enclosing value unresolved with it -- a `type()` waiting on
|
|
1248
|
+
// its argument for ever, and use case 09's whole registry with it.
|
|
1249
|
+
// The ATOM is done too, not just the conjunct. An earlier pass may
|
|
1250
|
+
// have set `dc = 0` on the unsettled branch, and a conjunct
|
|
1251
|
+
// recomputes its own doneness from its terms on every later meet --
|
|
1252
|
+
// so a stale 0 here would drag the residue, and every value holding
|
|
1253
|
+
// it, back to unresolved for ever.
|
|
1254
|
+
private hold(peer: any, ctx: AontuContext): Val {
|
|
1255
|
+
this.dc = DONE
|
|
1256
|
+
const held: any = new ConjunctVal({ peg: [this, peer] }, ctx)
|
|
1257
|
+
held.dc = DONE
|
|
1258
|
+
return held
|
|
925
1259
|
}
|
|
926
1260
|
|
|
927
1261
|
|
|
@@ -994,6 +1328,11 @@ class ConstraintVal extends FeatureVal {
|
|
|
994
1328
|
null == peer.count ? this.count : meetCount(this.count, peer.count)
|
|
995
1329
|
// `unique()` is idempotent: two of them are one.
|
|
996
1330
|
merged.uniq = this.uniq || peer.uniq
|
|
1331
|
+
// `unique(a) & unique(b)` is BOTH, not the later one: each names a
|
|
1332
|
+
// key on which the members must differ, and dropping either would
|
|
1333
|
+
// silently weaken the constraint. Sorted and deduplicated, so the
|
|
1334
|
+
// meet is commutative and `unique(a) & unique(a)` is one atom.
|
|
1335
|
+
merged.uniqBy = [...new Set([...this.uniqBy, ...peer.uniqBy])].sort()
|
|
997
1336
|
// Band B checks accumulate in written order and are never merged,
|
|
998
1337
|
// deduplicated or reordered: each carries its own author message,
|
|
999
1338
|
// and two checks with the same shape may still say different things.
|
|
@@ -1042,6 +1381,7 @@ class ConstraintVal extends FeatureVal {
|
|
|
1042
1381
|
res: [...this.res],
|
|
1043
1382
|
count: this.count,
|
|
1044
1383
|
uniq: this.uniq,
|
|
1384
|
+
uniqBy: [...this.uniqBy],
|
|
1045
1385
|
musts: [...this.musts],
|
|
1046
1386
|
invalid: this.invalid,
|
|
1047
1387
|
}
|
|
@@ -1061,6 +1401,7 @@ class ConstraintVal extends FeatureVal {
|
|
|
1061
1401
|
out.res = [...this.res]
|
|
1062
1402
|
out.count = this.count
|
|
1063
1403
|
out.uniq = this.uniq
|
|
1404
|
+
out.uniqBy = [...this.uniqBy]
|
|
1064
1405
|
out.musts = [...this.musts]
|
|
1065
1406
|
out.pending = this.pending
|
|
1066
1407
|
out.cjo = this.cjo
|
|
@@ -1137,10 +1478,11 @@ function dedupSorted(domain: 'number' | 'string', neqs: any[]): any[] {
|
|
|
1137
1478
|
// Is this value, or anything inside it, a nil? A written argument that
|
|
1138
1479
|
// holds one can never be satisfied, so the atom refuses it as an
|
|
1139
1480
|
// argument rather than reporting a mystery failure against every peer.
|
|
1481
|
+
// Every caller passes a Val: `args[0]` comes from atomArgs, and the
|
|
1482
|
+
// walk below descends only into container pegs, which hold Vals. The
|
|
1483
|
+
// null/non-Val guard this function used to open with was therefore
|
|
1484
|
+
// unreachable, and the coverage gate said so.
|
|
1140
1485
|
function holdsNil(v: any): boolean {
|
|
1141
|
-
if (null == v || true !== v.isVal) {
|
|
1142
|
-
return false
|
|
1143
|
-
}
|
|
1144
1486
|
if (true === v.isNil) {
|
|
1145
1487
|
return true
|
|
1146
1488
|
}
|
|
@@ -1159,6 +1501,31 @@ function holdsNil(v: any): boolean {
|
|
|
1159
1501
|
}
|
|
1160
1502
|
|
|
1161
1503
|
|
|
1504
|
+
// Whether a value, or anything inside it, is an effectful call — one
|
|
1505
|
+
// whose evaluation changes a node OTHER than the one being computed.
|
|
1506
|
+
// `move()` is the only builtin that does: it hides its resolution
|
|
1507
|
+
// target in place. Band B refuses one as an argument, because settling
|
|
1508
|
+
// it runs the effect against the live root before `must`'s trial clone
|
|
1509
|
+
// is taken, and a check that mutates cannot be report-only.
|
|
1510
|
+
function holdsMove(v: any): boolean {
|
|
1511
|
+
if (true === v.isFunc && 'move' === v.funcname?.()) {
|
|
1512
|
+
return true
|
|
1513
|
+
}
|
|
1514
|
+
const peg = v.peg
|
|
1515
|
+
if (Array.isArray(peg)) {
|
|
1516
|
+
return peg.some((c: any) => holdsMove(c))
|
|
1517
|
+
}
|
|
1518
|
+
if (null != peg && 'object' === typeof peg) {
|
|
1519
|
+
for (const k in peg) {
|
|
1520
|
+
if (holdsMove(peg[k])) {
|
|
1521
|
+
return true
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
return false
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
|
|
1162
1529
|
// The written arguments of an atom, flattened.
|
|
1163
1530
|
//
|
|
1164
1531
|
// A multi-argument call arrives from the func-paren grammar as ONE
|
|
@@ -1217,6 +1584,12 @@ function canonState(s: ConstraintState): string {
|
|
|
1217
1584
|
if (s.uniq) {
|
|
1218
1585
|
parts.push('unique()')
|
|
1219
1586
|
}
|
|
1587
|
+
// After the bare atom, and sorted: canon is a normal form, so two
|
|
1588
|
+
// documents writing the same keys in different orders must render
|
|
1589
|
+
// the same string.
|
|
1590
|
+
for (const key of s.uniqBy) {
|
|
1591
|
+
parts.push('unique(' + JSON.stringify(key) + ')')
|
|
1592
|
+
}
|
|
1220
1593
|
for (const m of s.musts) {
|
|
1221
1594
|
parts.push('must(' + m.v.canon + ',' + m.msg.canon + ')')
|
|
1222
1595
|
}
|
|
@@ -1228,13 +1601,145 @@ function canonState(s: ConstraintState): string {
|
|
|
1228
1601
|
}
|
|
1229
1602
|
|
|
1230
1603
|
|
|
1604
|
+
// SUBSUMPTION over two residuals (G3, the query built on the table in
|
|
1605
|
+
// docs/reference-language.md "Subsumption"): does every value `s`
|
|
1606
|
+
// admits pass `g` too? Implemented here because the compare machinery
|
|
1607
|
+
// (cmpVal, sameScalar, the recursive count state) is this module's.
|
|
1608
|
+
//
|
|
1609
|
+
// Three-valued: true / false / 'undecided'. `must` on the GENERAL side
|
|
1610
|
+
// is undecided by construction (a Band B predicate is opaque — G3 maps
|
|
1611
|
+
// the table's "never" to the query's honest sub_evaluate_only); every
|
|
1612
|
+
// other approximation folds toward false, the safe direction.
|
|
1613
|
+
function constraintStateSubsumes(
|
|
1614
|
+
g: {
|
|
1615
|
+
domain?: 'number' | 'string', kind?: any, lo?: Bound, hi?: Bound,
|
|
1616
|
+
neqs: any[], res: ReAtom[], count?: ConstraintState, uniq: boolean,
|
|
1617
|
+
uniqBy: string[],
|
|
1618
|
+
musts: MustAtom[],
|
|
1619
|
+
},
|
|
1620
|
+
s: {
|
|
1621
|
+
domain?: 'number' | 'string', kind?: any, lo?: Bound, hi?: Bound,
|
|
1622
|
+
neqs: any[], res: ReAtom[], count?: ConstraintState, uniq: boolean,
|
|
1623
|
+
uniqBy: string[],
|
|
1624
|
+
musts: MustAtom[],
|
|
1625
|
+
},
|
|
1626
|
+
): boolean | 'undecided' {
|
|
1627
|
+
// A Band B predicate on the general side makes its admitted set
|
|
1628
|
+
// unknowable; an extra `must` on the SPECIFIC side only narrows it
|
|
1629
|
+
// and is ignored.
|
|
1630
|
+
if (0 < g.musts.length) {
|
|
1631
|
+
return 'undecided'
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// Domains must agree where both constrain one; a sizing-only residual
|
|
1635
|
+
// has no domain and passes this gate.
|
|
1636
|
+
if (null != g.domain && g.domain !== s.domain) {
|
|
1637
|
+
return false
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// A general leaf restriction requires the same leaf on the specific
|
|
1641
|
+
// side (leaves are disjoint; an unrestricted specific admits other
|
|
1642
|
+
// leaves the general refuses).
|
|
1643
|
+
if (null != g.kind && g.kind !== s.kind) {
|
|
1644
|
+
return false
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// Interval containment: the general's endpoints at or beyond the
|
|
1648
|
+
// specific's, and where they coincide the general's may not be the
|
|
1649
|
+
// open one.
|
|
1650
|
+
const d = g.domain ?? s.domain
|
|
1651
|
+
if (null != g.lo) {
|
|
1652
|
+
if (null == s.lo || null == d) {
|
|
1653
|
+
return false
|
|
1654
|
+
}
|
|
1655
|
+
const c = cmpVal(d, g.lo.v, s.lo.v)
|
|
1656
|
+
if (0 < c || (0 === c && g.lo.open && !s.lo.open)) {
|
|
1657
|
+
return false
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
if (null != g.hi) {
|
|
1661
|
+
if (null == s.hi || null == d) {
|
|
1662
|
+
return false
|
|
1663
|
+
}
|
|
1664
|
+
const c = cmpVal(d, g.hi.v, s.hi.v)
|
|
1665
|
+
if (c < 0 || (0 === c && g.hi.open && !s.hi.open)) {
|
|
1666
|
+
return false
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
// Excluding FEWER values is more general: every general exclusion
|
|
1671
|
+
// must be excluded by the specific too.
|
|
1672
|
+
for (const n of g.neqs) {
|
|
1673
|
+
if (!s.neqs.some((m: any) => sameScalar(n, m))) {
|
|
1674
|
+
return false
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
// Patterns compare as TEXT sets (the sanctioned approximation:
|
|
1679
|
+
// deciding regex containment is what this algebra refuses to do).
|
|
1680
|
+
for (const r of g.res) {
|
|
1681
|
+
if (!s.res.some((q: ReAtom) => q.src === r.src)) {
|
|
1682
|
+
return false
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// `unique()` subsumes only itself on that axis: a general uniqueness
|
|
1687
|
+
// demand admits no container the unconstrained specific also admits.
|
|
1688
|
+
// ... and a general `unique(k)` needs the same key on the specific
|
|
1689
|
+
// side: distinctness on `port` says nothing about distinctness on
|
|
1690
|
+
// `name`.
|
|
1691
|
+
if (g.uniqBy.some((k: string) => !s.uniqBy.includes(k))) {
|
|
1692
|
+
return false
|
|
1693
|
+
}
|
|
1694
|
+
if (g.uniq && !s.uniq) {
|
|
1695
|
+
return false
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
// The count atom reuses this same table over the integer domain.
|
|
1699
|
+
if (null != g.count) {
|
|
1700
|
+
if (null == s.count) {
|
|
1701
|
+
return false
|
|
1702
|
+
}
|
|
1703
|
+
return constraintStateSubsumes(g.count, s.count)
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
return true
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
|
|
1710
|
+
// The query surface for ts/src/subsume.ts: residual-vs-residual, and
|
|
1711
|
+
// residual-vs-scalar membership (with `must` and `unique` making the
|
|
1712
|
+
// scalar case undecided/false exactly as the meet would).
|
|
1713
|
+
function constraintSubsumesConstraint(
|
|
1714
|
+
g: ConstraintVal, s: ConstraintVal): boolean | 'undecided' {
|
|
1715
|
+
return constraintStateSubsumes(g as any, s as any)
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
function constraintAdmitsScalar(
|
|
1719
|
+
g: ConstraintVal, scalar: any): boolean | 'undecided' {
|
|
1720
|
+
if (0 < g.musts.length) {
|
|
1721
|
+
return 'undecided'
|
|
1722
|
+
}
|
|
1723
|
+
if (g.uniq || 0 < g.uniqBy.length) {
|
|
1724
|
+
return false
|
|
1725
|
+
}
|
|
1726
|
+
// A count residual admits by LENGTH, which is the meet's business;
|
|
1727
|
+
// the query answers false (the safe direction) rather than
|
|
1728
|
+
// reimplementing the sizing walk here.
|
|
1729
|
+
if (null != (g as any).count) {
|
|
1730
|
+
return false
|
|
1731
|
+
}
|
|
1732
|
+
return stateAdmits(g as any, scalar)
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
|
|
1231
1736
|
// Does this residual's ORDER and MEMBERSHIP part admit the scalar? The
|
|
1232
1737
|
// sizing atoms are deliberately not consulted: `admit` applies them to
|
|
1233
1738
|
// the peer's length, and the count check applies this same function to
|
|
1234
1739
|
// the count.
|
|
1235
1740
|
function stateAdmits(s: ConstraintState, peer: any): boolean {
|
|
1236
1741
|
const domainOf = numericLeaf(peer) ? 'number' :
|
|
1237
|
-
|
|
1742
|
+
stringishLeaf(peer) ? 'string' : undefined
|
|
1238
1743
|
|
|
1239
1744
|
if (null == s.domain) {
|
|
1240
1745
|
// A sizing residual has no domain, and admits any scalar the sizing
|
|
@@ -1345,7 +1850,8 @@ function stateEmpty(s: ConstraintState): boolean {
|
|
|
1345
1850
|
// members, so `integer & length(3)` and `min(2) & unique()` admit
|
|
1346
1851
|
// nothing. Uniqueness over the string domain is empty for the same
|
|
1347
1852
|
// reason -- a string's members are not values the algebra compares.
|
|
1348
|
-
if ('number' === d && (null != s.count || s.uniq
|
|
1853
|
+
if ('number' === d && (null != s.count || s.uniq ||
|
|
1854
|
+
0 < s.uniqBy.length)) {
|
|
1349
1855
|
return true
|
|
1350
1856
|
}
|
|
1351
1857
|
if ('string' === d && s.uniq) {
|
|
@@ -1374,7 +1880,9 @@ function countBase(): ConstraintState {
|
|
|
1374
1880
|
neqs: [],
|
|
1375
1881
|
res: [],
|
|
1376
1882
|
musts: [],
|
|
1883
|
+
// A COUNT is a number, and a number has no members to be distinct.
|
|
1377
1884
|
uniq: false,
|
|
1885
|
+
uniqBy: [],
|
|
1378
1886
|
}
|
|
1379
1887
|
}
|
|
1380
1888
|
|
|
@@ -1395,13 +1903,16 @@ function countVal(n: number): any {
|
|
|
1395
1903
|
function meetCount(a: ConstraintState, b: ConstraintState): ConstraintState {
|
|
1396
1904
|
return {
|
|
1397
1905
|
domain: 'number',
|
|
1398
|
-
|
|
1906
|
+
// `a` is always a countBase()-seeded residual, so its kind is
|
|
1907
|
+
// always Integer — there is no kindless side to fall back from.
|
|
1908
|
+
kind: a.kind,
|
|
1399
1909
|
lo: tighter('number', a.lo, b.lo, true),
|
|
1400
1910
|
hi: tighter('number', a.hi, b.hi, false),
|
|
1401
1911
|
neqs: dedupSorted('number', [...a.neqs, ...b.neqs]),
|
|
1402
1912
|
res: [],
|
|
1403
1913
|
musts: [],
|
|
1404
1914
|
uniq: false,
|
|
1915
|
+
uniqBy: [],
|
|
1405
1916
|
clash: true === a.clash || true === b.clash ||
|
|
1406
1917
|
(null != a.kind && null != b.kind && a.kind !== b.kind),
|
|
1407
1918
|
}
|
|
@@ -1426,7 +1937,7 @@ function countArgState(arg: any): ConstraintState | undefined {
|
|
|
1426
1937
|
domain: 'number',
|
|
1427
1938
|
lo: { v: arg, open: false },
|
|
1428
1939
|
hi: { v: arg, open: false },
|
|
1429
|
-
neqs: [], res: [], musts: [], uniq: false,
|
|
1940
|
+
neqs: [], res: [], musts: [], uniq: false, uniqBy: [],
|
|
1430
1941
|
}
|
|
1431
1942
|
}
|
|
1432
1943
|
|
|
@@ -1434,7 +1945,8 @@ function countArgState(arg: any): ConstraintState | undefined {
|
|
|
1434
1945
|
const c = arg as ConstraintVal
|
|
1435
1946
|
// A pattern, a sizing atom or a string bound inside a count is not
|
|
1436
1947
|
// a count constraint at all, and neither is a broken one.
|
|
1437
|
-
if (null != c.invalid || 0 < c.res.length || c.uniq ||
|
|
1948
|
+
if (null != c.invalid || 0 < c.res.length || c.uniq ||
|
|
1949
|
+
0 < c.uniqBy.length || null != c.count ||
|
|
1438
1950
|
'number' !== c.domain) {
|
|
1439
1951
|
return undefined
|
|
1440
1952
|
}
|
|
@@ -1444,18 +1956,24 @@ function countArgState(arg: any): ConstraintState | undefined {
|
|
|
1444
1956
|
lo: c.lo,
|
|
1445
1957
|
hi: c.hi,
|
|
1446
1958
|
neqs: [...c.neqs],
|
|
1447
|
-
res: [], musts: [], uniq: false,
|
|
1959
|
+
res: [], musts: [], uniq: false, uniqBy: [],
|
|
1448
1960
|
}
|
|
1449
1961
|
}
|
|
1450
1962
|
|
|
1451
1963
|
if (true === arg?.isScalarKind) {
|
|
1452
1964
|
const marker = arg.peg
|
|
1453
1965
|
if (Number === marker) {
|
|
1454
|
-
return {
|
|
1966
|
+
return {
|
|
1967
|
+
domain: 'number', neqs: [], res: [], musts: [],
|
|
1968
|
+
uniq: false, uniqBy: [],
|
|
1969
|
+
}
|
|
1455
1970
|
}
|
|
1456
1971
|
if (Integer === marker || Float === marker ||
|
|
1457
1972
|
BigInteger === marker || BigDecimal === marker) {
|
|
1458
|
-
return {
|
|
1973
|
+
return {
|
|
1974
|
+
domain: 'number', kind: marker, neqs: [], res: [], musts: [],
|
|
1975
|
+
uniq: false, uniqBy: [],
|
|
1976
|
+
}
|
|
1459
1977
|
}
|
|
1460
1978
|
return undefined
|
|
1461
1979
|
}
|
|
@@ -1487,7 +2005,12 @@ function genable(child: any): boolean {
|
|
|
1487
2005
|
return true === child.isScalar || true === child.isMap ||
|
|
1488
2006
|
true === child.isList || true === child.isPref ||
|
|
1489
2007
|
true === child.isRef || true === child.isDisjunct ||
|
|
1490
|
-
true === child.isNil
|
|
2008
|
+
true === child.isNil ||
|
|
2009
|
+
// A settled sizing residue generates through ConjunctVal.gen, so it
|
|
2010
|
+
// is a member like any other -- and a `length` that did not count
|
|
2011
|
+
// its sibling's `unique` would be the early-fold defect again, one
|
|
2012
|
+
// level up.
|
|
2013
|
+
undefined !== sizingResidue(child)
|
|
1491
2014
|
}
|
|
1492
2015
|
|
|
1493
2016
|
|
|
@@ -1624,7 +2147,7 @@ class UniqueConstraintVal extends ConstraintVal {
|
|
|
1624
2147
|
constructor(spec: ValSpec, ctx?: AontuContext) {
|
|
1625
2148
|
super({ ...spec, atom: 'unique' }, ctx)
|
|
1626
2149
|
}
|
|
1627
|
-
} /* node:coverage ignore next
|
|
2150
|
+
} /* node:coverage ignore next 23 */
|
|
1628
2151
|
|
|
1629
2152
|
|
|
1630
2153
|
export {
|
|
@@ -1632,6 +2155,11 @@ export {
|
|
|
1632
2155
|
// normalisers must produce byte-identical output, and that can only be
|
|
1633
2156
|
// checked by calling them.
|
|
1634
2157
|
normaliseRe,
|
|
2158
|
+
// Exported for the subsumption query (G3, ts/src/subsume.ts): the
|
|
2159
|
+
// residual-vs-residual and residual-vs-scalar rules live here beside
|
|
2160
|
+
// the compare machinery they reuse.
|
|
2161
|
+
constraintSubsumesConstraint,
|
|
2162
|
+
constraintAdmitsScalar,
|
|
1635
2163
|
ConstraintVal,
|
|
1636
2164
|
MinConstraintVal,
|
|
1637
2165
|
MaxConstraintVal,
|