aontu 0.52.0 → 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.
Files changed (273) hide show
  1. package/README.md +88 -0
  2. package/bin/aontu-mcp.js +4 -0
  3. package/dist/agentsmd.d.ts +16 -0
  4. package/dist/agentsmd.js +107 -0
  5. package/dist/agentsmd.js.map +1 -0
  6. package/dist/aontu.d.ts +14 -3
  7. package/dist/aontu.js +145 -4
  8. package/dist/aontu.js.map +1 -1
  9. package/dist/cli.d.ts +44 -1
  10. package/dist/cli.js +2401 -44
  11. package/dist/cli.js.map +1 -1
  12. package/dist/ctx.d.ts +16 -0
  13. package/dist/ctx.js +44 -0
  14. package/dist/ctx.js.map +1 -1
  15. package/dist/diff.d.ts +22 -0
  16. package/dist/diff.js +141 -0
  17. package/dist/diff.js.map +1 -0
  18. package/dist/err.d.ts +3 -1
  19. package/dist/err.js +48 -8
  20. package/dist/err.js.map +1 -1
  21. package/dist/graph.d.ts +16 -0
  22. package/dist/graph.js +73 -0
  23. package/dist/graph.js.map +1 -0
  24. package/dist/hcanon.d.ts +3 -0
  25. package/dist/hcanon.js +146 -0
  26. package/dist/hcanon.js.map +1 -0
  27. package/dist/hints.js +223 -5
  28. package/dist/hints.js.map +1 -1
  29. package/dist/jsonschema.d.ts +20 -0
  30. package/dist/jsonschema.js +391 -0
  31. package/dist/jsonschema.js.map +1 -0
  32. package/dist/lang.js +698 -35
  33. package/dist/lang.js.map +1 -1
  34. package/dist/lsp.d.ts +9 -2
  35. package/dist/lsp.js +262 -46
  36. package/dist/lsp.js.map +1 -1
  37. package/dist/mcp-server.d.ts +20 -0
  38. package/dist/mcp-server.js +147 -0
  39. package/dist/mcp-server.js.map +1 -0
  40. package/dist/mcp.d.ts +42 -0
  41. package/dist/mcp.js +814 -0
  42. package/dist/mcp.js.map +1 -0
  43. package/dist/mod-tool.d.ts +58 -0
  44. package/dist/mod-tool.js +498 -0
  45. package/dist/mod-tool.js.map +1 -0
  46. package/dist/mod.d.ts +31 -0
  47. package/dist/mod.js +250 -0
  48. package/dist/mod.js.map +1 -0
  49. package/dist/patch.d.ts +44 -0
  50. package/dist/patch.js +506 -0
  51. package/dist/patch.js.map +1 -0
  52. package/dist/provenance.d.ts +40 -0
  53. package/dist/provenance.js +335 -0
  54. package/dist/provenance.js.map +1 -0
  55. package/dist/query.d.ts +27 -0
  56. package/dist/query.js +294 -0
  57. package/dist/query.js.map +1 -0
  58. package/dist/reach.d.ts +14 -0
  59. package/dist/reach.js +140 -0
  60. package/dist/reach.js.map +1 -0
  61. package/dist/relation.d.ts +19 -0
  62. package/dist/relation.js +305 -0
  63. package/dist/relation.js.map +1 -0
  64. package/dist/report-sarif.d.ts +14 -0
  65. package/dist/report-sarif.js +102 -0
  66. package/dist/report-sarif.js.map +1 -0
  67. package/dist/site.d.ts +4 -0
  68. package/dist/site.js +31 -0
  69. package/dist/site.js.map +1 -1
  70. package/dist/std.d.ts +1 -0
  71. package/dist/std.js +73 -0
  72. package/dist/std.js.map +1 -0
  73. package/dist/subsume.d.ts +39 -0
  74. package/dist/subsume.js +526 -0
  75. package/dist/subsume.js.map +1 -0
  76. package/dist/trim.d.ts +19 -0
  77. package/dist/trim.js +155 -0
  78. package/dist/trim.js.map +1 -0
  79. package/dist/tsconfig.tsbuildinfo +1 -1
  80. package/dist/type.d.ts +17 -1
  81. package/dist/type.js.map +1 -1
  82. package/dist/unify.d.ts +3 -1
  83. package/dist/unify.js +287 -18
  84. package/dist/unify.js.map +1 -1
  85. package/dist/utility.d.ts +9 -1
  86. package/dist/utility.js +122 -1
  87. package/dist/utility.js.map +1 -1
  88. package/dist/val/AggFuncVal.d.ts +33 -0
  89. package/dist/val/AggFuncVal.js +202 -0
  90. package/dist/val/AggFuncVal.js.map +1 -0
  91. package/dist/val/ArithFuncVal.d.ts +31 -0
  92. package/dist/val/ArithFuncVal.js +62 -0
  93. package/dist/val/ArithFuncVal.js.map +1 -0
  94. package/dist/val/BagVal.d.ts +5 -0
  95. package/dist/val/BagVal.js +96 -5
  96. package/dist/val/BagVal.js.map +1 -1
  97. package/dist/val/CloseFuncVal.js +9 -1
  98. package/dist/val/CloseFuncVal.js.map +1 -1
  99. package/dist/val/ConjunctVal.d.ts +1 -1
  100. package/dist/val/ConjunctVal.js +19 -0
  101. package/dist/val/ConjunctVal.js.map +1 -1
  102. package/dist/val/ConstraintVal.d.ts +48 -1
  103. package/dist/val/ConstraintVal.js +1501 -110
  104. package/dist/val/ConstraintVal.js.map +1 -1
  105. package/dist/val/CopyFuncVal.d.ts +1 -2
  106. package/dist/val/CopyFuncVal.js +7 -0
  107. package/dist/val/CopyFuncVal.js.map +1 -1
  108. package/dist/val/Decimal.d.ts +1 -0
  109. package/dist/val/Decimal.js +13 -0
  110. package/dist/val/Decimal.js.map +1 -1
  111. package/dist/val/DeprecateFuncVal.d.ts +11 -0
  112. package/dist/val/DeprecateFuncVal.js +47 -0
  113. package/dist/val/DeprecateFuncVal.js.map +1 -0
  114. package/dist/val/DisjunctVal.js +130 -21
  115. package/dist/val/DisjunctVal.js.map +1 -1
  116. package/dist/val/EachFuncVal.d.ts +15 -0
  117. package/dist/val/EachFuncVal.js +75 -0
  118. package/dist/val/EachFuncVal.js.map +1 -0
  119. package/dist/val/ExpectVal.d.ts +1 -0
  120. package/dist/val/ExpectVal.js +41 -4
  121. package/dist/val/ExpectVal.js.map +1 -1
  122. package/dist/val/FeatureVal.js +1 -1
  123. package/dist/val/FeatureVal.js.map +1 -1
  124. package/dist/val/FilterFuncVal.d.ts +15 -0
  125. package/dist/val/FilterFuncVal.js +91 -0
  126. package/dist/val/FilterFuncVal.js.map +1 -0
  127. package/dist/val/FuncBaseVal.d.ts +6 -1
  128. package/dist/val/FuncBaseVal.js +178 -2
  129. package/dist/val/FuncBaseVal.js.map +1 -1
  130. package/dist/val/HideFuncVal.js.map +1 -1
  131. package/dist/val/IdFuncVal.d.ts +13 -0
  132. package/dist/val/IdFuncVal.js +54 -0
  133. package/dist/val/IdFuncVal.js.map +1 -0
  134. package/dist/val/JunctionVal.js +7 -1
  135. package/dist/val/JunctionVal.js.map +1 -1
  136. package/dist/val/KeyFuncVal.d.ts +1 -1
  137. package/dist/val/KeyFuncVal.js +38 -30
  138. package/dist/val/KeyFuncVal.js.map +1 -1
  139. package/dist/val/ListVal.js +117 -17
  140. package/dist/val/ListVal.js.map +1 -1
  141. package/dist/val/LowerFuncVal.js.map +1 -1
  142. package/dist/val/MapVal.js +102 -8
  143. package/dist/val/MapVal.js.map +1 -1
  144. package/dist/val/MatchFuncVal.d.ts +15 -0
  145. package/dist/val/MatchFuncVal.js +107 -0
  146. package/dist/val/MatchFuncVal.js.map +1 -0
  147. package/dist/val/MoveFuncVal.js.map +1 -1
  148. package/dist/val/NilVal.js +24 -0
  149. package/dist/val/NilVal.js.map +1 -1
  150. package/dist/val/OpBaseVal.d.ts +1 -1
  151. package/dist/val/OpBaseVal.js +24 -2
  152. package/dist/val/OpBaseVal.js.map +1 -1
  153. package/dist/val/OpenFuncVal.js +4 -1
  154. package/dist/val/OpenFuncVal.js.map +1 -1
  155. package/dist/val/PackFuncVal.d.ts +15 -0
  156. package/dist/val/PackFuncVal.js +108 -0
  157. package/dist/val/PackFuncVal.js.map +1 -0
  158. package/dist/val/PathFuncVal.js.map +1 -1
  159. package/dist/val/PlaceVal.d.ts +13 -0
  160. package/dist/val/PlaceVal.js +131 -0
  161. package/dist/val/PlaceVal.js.map +1 -0
  162. package/dist/val/PlusOpVal.js +11 -2
  163. package/dist/val/PlusOpVal.js.map +1 -1
  164. package/dist/val/PrefFuncVal.js.map +1 -1
  165. package/dist/val/PrefVal.d.ts +2 -2
  166. package/dist/val/PrefVal.js +78 -23
  167. package/dist/val/PrefVal.js.map +1 -1
  168. package/dist/val/RefVal.d.ts +1 -1
  169. package/dist/val/RefVal.js +158 -30
  170. package/dist/val/RefVal.js.map +1 -1
  171. package/dist/val/ReferFuncVal.d.ts +36 -0
  172. package/dist/val/ReferFuncVal.js +303 -0
  173. package/dist/val/ReferFuncVal.js.map +1 -0
  174. package/dist/val/ScalarKindVal.d.ts +1 -2
  175. package/dist/val/ScalarKindVal.js +0 -11
  176. package/dist/val/ScalarKindVal.js.map +1 -1
  177. package/dist/val/TopVal.js.map +1 -1
  178. package/dist/val/TypeFuncVal.js.map +1 -1
  179. package/dist/val/UpperFuncVal.js.map +1 -1
  180. package/dist/val/Val.d.ts +9 -2
  181. package/dist/val/Val.js +150 -4
  182. package/dist/val/Val.js.map +1 -1
  183. package/dist/val/VarVal.js.map +1 -1
  184. package/dist/val/arith.d.ts +6 -0
  185. package/dist/val/arith.js +170 -0
  186. package/dist/val/arith.js.map +1 -0
  187. package/dist/vet.d.ts +45 -0
  188. package/dist/vet.js +776 -0
  189. package/dist/vet.js.map +1 -0
  190. package/dist/walk.d.ts +2 -0
  191. package/dist/walk.js +91 -0
  192. package/dist/walk.js.map +1 -0
  193. package/grammar/aontu.gbnf +130 -0
  194. package/grammar/aontu.lark +113 -0
  195. package/package.json +30 -15
  196. package/skill/SKILL.md +37 -0
  197. package/skill/error-codes.md +62 -0
  198. package/skill/examples.md +99 -0
  199. package/skill/grammar-card.md +57 -0
  200. package/src/agentsmd.ts +135 -0
  201. package/src/aontu.ts +192 -4
  202. package/src/cli.ts +2858 -71
  203. package/src/ctx.ts +81 -0
  204. package/src/diff.ts +196 -0
  205. package/src/err.ts +52 -8
  206. package/src/graph.ts +135 -0
  207. package/src/hcanon.ts +169 -0
  208. package/src/hints.ts +271 -5
  209. package/src/jsonschema.ts +511 -0
  210. package/src/lang.ts +779 -37
  211. package/src/lsp.ts +281 -47
  212. package/src/mcp-server.ts +187 -0
  213. package/src/mcp.ts +993 -0
  214. package/src/mod-tool.ts +679 -0
  215. package/src/mod.ts +344 -0
  216. package/src/patch.ts +624 -0
  217. package/src/provenance.ts +430 -0
  218. package/src/query.ts +379 -0
  219. package/src/reach.ts +184 -0
  220. package/src/relation.ts +395 -0
  221. package/src/report-sarif.ts +137 -0
  222. package/src/site.ts +36 -1
  223. package/src/std.ts +73 -0
  224. package/src/subsume.ts +690 -0
  225. package/src/trim.ts +195 -0
  226. package/src/tsconfig.json +10 -4
  227. package/src/type.ts +51 -2
  228. package/src/unify.ts +311 -16
  229. package/src/utility.ts +139 -1
  230. package/src/val/AggFuncVal.ts +319 -0
  231. package/src/val/ArithFuncVal.ts +108 -0
  232. package/src/val/BagVal.ts +101 -4
  233. package/src/val/CloseFuncVal.ts +9 -1
  234. package/src/val/ConjunctVal.ts +20 -0
  235. package/src/val/ConstraintVal.ts +1699 -116
  236. package/src/val/CopyFuncVal.ts +7 -1
  237. package/src/val/Decimal.ts +15 -0
  238. package/src/val/DeprecateFuncVal.ts +84 -0
  239. package/src/val/DisjunctVal.ts +139 -28
  240. package/src/val/EachFuncVal.ts +133 -0
  241. package/src/val/ExpectVal.ts +43 -6
  242. package/src/val/FeatureVal.ts +1 -1
  243. package/src/val/FilterFuncVal.ts +154 -0
  244. package/src/val/FuncBaseVal.ts +200 -3
  245. package/src/val/HideFuncVal.ts +0 -2
  246. package/src/val/IdFuncVal.ts +91 -0
  247. package/src/val/JunctionVal.ts +7 -1
  248. package/src/val/KeyFuncVal.ts +39 -35
  249. package/src/val/ListVal.ts +125 -18
  250. package/src/val/LowerFuncVal.ts +0 -1
  251. package/src/val/MapVal.ts +110 -8
  252. package/src/val/MatchFuncVal.ts +176 -0
  253. package/src/val/MoveFuncVal.ts +0 -2
  254. package/src/val/NilVal.ts +25 -0
  255. package/src/val/OpBaseVal.ts +26 -3
  256. package/src/val/OpenFuncVal.ts +4 -2
  257. package/src/val/PackFuncVal.ts +175 -0
  258. package/src/val/PathFuncVal.ts +0 -1
  259. package/src/val/PlaceVal.ts +193 -0
  260. package/src/val/PlusOpVal.ts +11 -2
  261. package/src/val/PrefFuncVal.ts +0 -1
  262. package/src/val/PrefVal.ts +79 -36
  263. package/src/val/RefVal.ts +163 -30
  264. package/src/val/ReferFuncVal.ts +387 -0
  265. package/src/val/ScalarKindVal.ts +0 -13
  266. package/src/val/TopVal.ts +0 -1
  267. package/src/val/TypeFuncVal.ts +0 -2
  268. package/src/val/UpperFuncVal.ts +0 -1
  269. package/src/val/Val.ts +213 -3
  270. package/src/val/VarVal.ts +0 -1
  271. package/src/val/arith.ts +316 -0
  272. package/src/vet.ts +992 -0
  273. package/src/walk.ts +99 -0
@@ -1,13 +1,396 @@
1
1
  "use strict";
2
2
  /* Copyright (c) 2025 Richard Rodger, MIT License */
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.NeqConstraintVal = exports.BelowConstraintVal = exports.AboveConstraintVal = exports.MaxConstraintVal = exports.MinConstraintVal = exports.ConstraintVal = void 0;
4
+ exports.MustConstraintVal = exports.UniqueConstraintVal = exports.LengthConstraintVal = exports.ReConstraintVal = exports.NeqConstraintVal = exports.BelowConstraintVal = exports.AboveConstraintVal = exports.MaxConstraintVal = exports.MinConstraintVal = exports.ConstraintVal = void 0;
5
+ exports.normaliseRe = normaliseRe;
6
+ exports.constraintSubsumesConstraint = constraintSubsumesConstraint;
7
+ exports.constraintAdmitsScalar = constraintAdmitsScalar;
5
8
  const type_1 = require("../type");
6
9
  const utility_1 = require("../utility");
10
+ const Val_1 = require("./Val");
11
+ const keyorder_1 = require("../keyorder");
12
+ const ConjunctVal_1 = require("./ConjunctVal");
13
+ const BagVal_1 = require("./BagVal");
14
+ const top_1 = require("./top");
15
+ const unify_1 = require("../unify");
16
+ const IntegerVal_1 = require("./IntegerVal");
7
17
  const err_1 = require("../err");
8
18
  const FeatureVal_1 = require("./FeatureVal");
9
19
  const ScalarKindVal_1 = require("./ScalarKindVal");
10
20
  const numcmp_1 = require("./numcmp");
21
+ // THE PATTERN SUBSET, AND HOW IT IS ENFORCED (G1 phase 2; ADR-003).
22
+ //
23
+ // `re(p)` must mean the same thing in both engines and cost about the
24
+ // same, and the two host engines guarantee neither: TypeScript compiles
25
+ // with JavaScript's backtracking RegExp, Go with RE2 — a different
26
+ // language in a different complexity class, over a different alphabet.
27
+ //
28
+ // The enforcement is NORMALISATION, not refusal (ADR-003). Every
29
+ // construct whose expansion is engine-defined is rewritten here, by
30
+ // this function, into an explicit form that cannot be read two ways;
31
+ // only the rewritten pattern reaches a host engine. The alternative —
32
+ // refusing everything that might differ — was tried first and leaked
33
+ // three times, because its correctness depended on this comment knowing
34
+ // every difference between two large engines.
35
+ //
36
+ // Aontu therefore DEFINES the abbreviations rather than inheriting
37
+ // either host's. The definitions are deliberately the small ASCII ones,
38
+ // because a config value containing U+00A0 is a mistake to catch, not a
39
+ // space to accept silently:
40
+ //
41
+ // \d [0-9] \D [^0-9]
42
+ // \w [0-9A-Za-z_] \W [^0-9A-Za-z_]
43
+ // \s [ \t\n\r\f\v] \S [^ \t\n\r\f\v]
44
+ // . [^\n]
45
+ // \A ^ \z $
46
+ //
47
+ // Neither host agreed with all of these before rewriting: JavaScript's
48
+ // \s also matches U+00A0 and other Unicode spaces, RE2's omits \v, and
49
+ // the two `.` sets differ by \r and the Unicode line separators. After
50
+ // rewriting, both engines see one explicit class and cannot disagree.
51
+ //
52
+ // What is still REFUSED, and why refusal is right for these:
53
+ //
54
+ // 1. Constructs one engine simply lacks — backreferences and
55
+ // lookaround (not in RE2, and not expressible as regular
56
+ // expressions at all), POSIX classes, `\p{...}`, `\x{...}`, `\u`.
57
+ // There is nothing to normalise them TO.
58
+ // 2. `(?` other than `(?:` — named groups are spelled differently
59
+ // (`(?P<n>` in RE2, `(?<n>` in JavaScript) and inline flags change
60
+ // the meaning of everything after them.
61
+ // 3. A quantifier applied to a group containing a quantifier or an
62
+ // alternation. This one is about TIME, not meaning: `(a+)+$`
63
+ // against twenty-nine characters takes 45 SECONDS in JavaScript
64
+ // and 0.065s under RE2, and a regex match is counted by no
65
+ // evaluator budget, so an untrusted schema could otherwise stall
66
+ // the TypeScript evaluator indefinitely (docs/trust.md clause 2).
67
+ // Normalisation cannot fix a complexity difference; only owning the
68
+ // matcher could, which ADR-003 records as the future option.
69
+ //
70
+ // The alphabet is fixed separately, by compiling with the `u` flag:
71
+ // JavaScript otherwise matches UTF-16 code units where RE2 matches code
72
+ // points, so `^.$` accepted U+1D11E in Go and refused it in TypeScript.
73
+ //
74
+ // This function is mirrored statement for statement in go/constraint.go,
75
+ // and `test/spec/files/regex-corpus.txt` pins that the two produce
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;
88
+ // The normative expansions. These are Aontu's definitions, not either
89
+ // host's; both hosts are rewritten to them.
90
+ const RE_CLASS_DIGIT = '0-9';
91
+ const RE_CLASS_WORD = '0-9A-Za-z_';
92
+ const RE_CLASS_SPACE = ' \\t\\n\\r\\f\\v';
93
+ // Metacharacters that may be escaped to mean themselves, in both
94
+ // engines. `-` is handled separately: it is legal escaped only INSIDE a
95
+ // character class, because RE2 accepts `a\-b` and JavaScript's unicode
96
+ // mode makes it a syntax error.
97
+ const RE_ESCAPE_PUNCT = '\\.+*?()[]{}|^$/';
98
+ // Escapes passed through unchanged: the control characters, the ASCII
99
+ // word boundary, and `\xHH`. Each was probed in both engines.
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
+ }
160
+ function isHexDigit(c) {
161
+ return null != c && (('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'));
162
+ }
163
+ // normaliseEscape rewrites one `\<n>` into its engine-neutral form.
164
+ // Returns [emitted, why, extra]: `why` non-empty means refused, and
165
+ // `extra` counts source characters consumed beyond the backslash and n.
166
+ function normaliseEscape(n, src, i, inClass) {
167
+ if (null == n) {
168
+ return ['', 'a trailing backslash', 0];
169
+ }
170
+ if ('1' <= n && n <= '9') {
171
+ return ['', 'a backreference (\\' + n + '): RE2 has no equivalent, and a' +
172
+ ' pattern with one is not a regular expression', 0];
173
+ }
174
+ if ('k' === n) {
175
+ return ['', 'a named backreference (\\k): RE2 has no equivalent', 0];
176
+ }
177
+ if ('u' === n) {
178
+ return ['', 'a \\u escape, which RE2 spells \\x{...}: write the character' +
179
+ ' itself, or \\xHH for a byte', 0];
180
+ }
181
+ if ('p' === n || 'P' === n) {
182
+ return ['', 'a Unicode class (\\' + n + '), which JavaScript reads as a' +
183
+ ' literal "' + n + '" without a flag Aontu does not set', 0];
184
+ }
185
+ if ('Z' === n) {
186
+ return ['', '\\Z, which RE2 does not accept and JavaScript reads as a' +
187
+ ' literal "Z": write $ for end of text', 0];
188
+ }
189
+ if ('x' === n) {
190
+ if ('{' === src[i + 2]) {
191
+ return ['', 'a \\x{...} escape, which JavaScript spells \\u: write the' +
192
+ ' character itself', 0];
193
+ }
194
+ if (!isHexDigit(src[i + 2]) || !isHexDigit(src[i + 3])) {
195
+ return ['', 'an \\x escape without two hex digits', 0];
196
+ }
197
+ return ['\\x' + src[i + 2] + src[i + 3], '', 2];
198
+ }
199
+ // The abbreviations, rewritten to Aontu's definitions. Inside a class
200
+ // the expansion splices without its brackets (`[\dx]` -> `[0-9x]`).
201
+ if ('d' === n || 'w' === n || 's' === n) {
202
+ const set = 'd' === n ? RE_CLASS_DIGIT :
203
+ 'w' === n ? RE_CLASS_WORD : RE_CLASS_SPACE;
204
+ return [inClass ? set : '[' + set + ']', '', 0];
205
+ }
206
+ if ('D' === n || 'W' === n || 'S' === n) {
207
+ if (inClass) {
208
+ // `[^...]` cannot be spliced into an enclosing class: the negation
209
+ // would apply to the whole class rather than this member.
210
+ return ['', 'a negated abbreviation (\\' + n + ') inside a character' +
211
+ ' class, which cannot be expanded in place: write the characters out', 0];
212
+ }
213
+ const set = 'D' === n ? RE_CLASS_DIGIT :
214
+ 'W' === n ? RE_CLASS_WORD : RE_CLASS_SPACE;
215
+ return ['[^' + set + ']', '', 0];
216
+ }
217
+ // Anchors. `\A`/`\z` are RE2 spellings that JavaScript reads as
218
+ // literals, so they are rewritten rather than refused. Inside a class
219
+ // an anchor is meaningless, and `[\b]` is a BACKSPACE in JavaScript.
220
+ if ('A' === n || 'z' === n || 'b' === n || 'B' === n) {
221
+ if (inClass) {
222
+ return ['', '\\' + n + ' inside a character class, where the two' +
223
+ ' engines do not agree what it means', 0];
224
+ }
225
+ return ['A' === n ? '^' : 'z' === n ? '$' : '\\' + n, '', 0];
226
+ }
227
+ if ('-' === n) {
228
+ return inClass ? ['\\-', '', 0] :
229
+ ['', '\\- outside a character class: it is a range separator inside' +
230
+ ' one and a syntax error outside one (write a bare -)', 0];
231
+ }
232
+ if (RE_ESCAPE_PASS.includes(n) || RE_ESCAPE_PUNCT.includes(n)) {
233
+ return ['\\' + n, '', 0];
234
+ }
235
+ return ['', '\\' + n + ', an escape whose meaning the two engines do not' +
236
+ ' share', 0];
237
+ }
238
+ // normaliseRe rewrites a pattern into the engine-neutral subset.
239
+ // Returns [normalised, why]: a non-empty `why` means the pattern is
240
+ // outside the subset and names the construct.
241
+ function normaliseRe(src) {
242
+ let inClass = false;
243
+ const out = [];
244
+ // One frame per open group, recording whether it contains a quantifier
245
+ // or an alternation. A group carrying either may not itself be
246
+ // quantified; containment is transitive, so a frame hands its flags up
247
+ // to its parent when it closes.
248
+ const groups = [];
249
+ const mark = (k) => {
250
+ if (0 < groups.length) {
251
+ groups[groups.length - 1][k] = true;
252
+ }
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;
259
+ for (let i = 0; i < src.length; i++) {
260
+ const c = src[i];
261
+ const afterAnchor = anchorPrev;
262
+ anchorPrev = false;
263
+ if ('\\' === c) {
264
+ const [emit, why, extra] = normaliseEscape(src[i + 1], src, i, inClass);
265
+ if ('' !== why) {
266
+ return ['', why];
267
+ }
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
+ }
280
+ i += 1 + extra;
281
+ continue;
282
+ }
283
+ // A POSIX class opener, anywhere: the form lives inside an ordinary
284
+ // class (`[[:alpha:]]`), and refusing it everywhere is one rule
285
+ // rather than two.
286
+ if ('[' === c && ':' === src[i + 1]) {
287
+ return ['', 'a POSIX class ([:...:]), which JavaScript does not have'];
288
+ }
289
+ if (inClass) {
290
+ if (']' === c) {
291
+ inClass = false;
292
+ }
293
+ out.push(c);
294
+ continue;
295
+ }
296
+ if ('[' === c) {
297
+ // `[]` is a never-matching class in JavaScript and a parse error in
298
+ // RE2; `[^]` is the same disagreement one character along.
299
+ const first = '^' === src[i + 1] ? src[i + 2] : src[i + 1];
300
+ if (']' === first) {
301
+ return ['', 'an empty character class, which RE2 refuses'];
302
+ }
303
+ inClass = true;
304
+ out.push(c);
305
+ continue;
306
+ }
307
+ if ('.' === c) {
308
+ out.push('[^\\n]');
309
+ continue;
310
+ }
311
+ if ('(' === c) {
312
+ if ('?' === src[i + 1]) {
313
+ if (':' !== src[i + 2]) {
314
+ return ['', 'a (?...) group other than the non-capturing (?:'];
315
+ }
316
+ out.push('(?:');
317
+ i += 2;
318
+ }
319
+ else {
320
+ out.push(c);
321
+ }
322
+ groups.push({ q: false, alt: false });
323
+ continue;
324
+ }
325
+ if (')' === c) {
326
+ const g = groups.pop();
327
+ if (null == g) {
328
+ return ['', 'an unbalanced group'];
329
+ }
330
+ const nx = src[i + 1];
331
+ const quantified = '*' === nx || '+' === nx || '?' === nx || '{' === nx;
332
+ if (quantified && (g.q || g.alt)) {
333
+ return ['', 'a quantifier applied to a group containing ' +
334
+ (g.q ? 'another quantifier' : 'an alternation') +
335
+ ', which backtracks exponentially in JavaScript'];
336
+ }
337
+ if (g.q)
338
+ mark('q');
339
+ if (g.alt)
340
+ mark('alt');
341
+ out.push(c);
342
+ continue;
343
+ }
344
+ if ('|' === c) {
345
+ mark('alt');
346
+ out.push(c);
347
+ continue;
348
+ }
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
+ }
367
+ mark('q');
368
+ out.push(c);
369
+ continue;
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
+ }
383
+ out.push(c);
384
+ anchorPrev = ('^' === c || '$' === c);
385
+ }
386
+ if (inClass) {
387
+ return ['', 'an unterminated character class'];
388
+ }
389
+ if (0 < groups.length) {
390
+ return ['', 'an unclosed group'];
391
+ }
392
+ return [out.join(''), ''];
393
+ }
11
394
  // True for a scalar Val the algebra can order: a numeric leaf or a
12
395
  // string. (Booleans and null have no order and no bounds.)
13
396
  function numericLeaf(v) {
@@ -40,47 +423,145 @@ function leafMarker(v) {
40
423
  return v.isBigDecimal ? ScalarKindVal_1.BigDecimal : v.isBigInteger ? ScalarKindVal_1.BigInteger :
41
424
  v.isInteger ? ScalarKindVal_1.Integer : ScalarKindVal_1.Float;
42
425
  }
426
+ // Conjunct sort order for an atom that must see the WHOLE value.
427
+ // Every other value sorts below the container default (99999), so such
428
+ // an atom is the LAST term to fold: `a:length(2) a:{x:1} a:{y:2}` must
429
+ // count the MERGED map, and a constraint that folded at 50000 would
430
+ // count `{x:1}` alone and refuse the layering that is the whole point
431
+ // of the language.
432
+ //
433
+ // The order atoms keep the low slot: `min(2) & 1 & 2` may decide as
434
+ // soon as it sees a scalar, because meeting more scalars can only
435
+ // narrow. Meeting more containers GROWS the member set, which is why
436
+ // the two orders differ.
437
+ //
438
+ // Three atoms are late: `length` and `unique` (they count members), and
439
+ // `must` (an evaluate-only check against the finished value).
440
+ const LATE_CJO = 150000;
441
+ function lateAtom(atom) {
442
+ return 'length' === atom || 'unique' === atom || 'must' === atom;
443
+ }
43
444
  class ConstraintVal extends FeatureVal_1.FeatureVal {
44
445
  constructor(spec, ctx) {
45
446
  super({ ...spec, peg: spec.peg ?? [] }, ctx);
46
447
  this.isConstraint = true;
47
448
  this.cjo = 50000;
48
449
  this.neqs = [];
450
+ this.res = [];
451
+ this.uniq = false;
452
+ this.uniqBy = [];
453
+ this.musts = [];
49
454
  if (spec.state) {
50
455
  this.domain = spec.state.domain;
51
456
  this.kind = spec.state.kind;
52
457
  this.lo = spec.state.lo;
53
458
  this.hi = spec.state.hi;
54
459
  this.neqs = spec.state.neqs;
460
+ // A state built by an embedder (or by a per-port test) may predate
461
+ // the pattern field; an absent one means "no patterns", not undefined.
462
+ this.res = spec.state.res ?? [];
463
+ this.count = spec.state.count;
464
+ this.uniq = spec.state.uniq ?? false;
465
+ this.uniqBy = spec.state.uniqBy ?? [];
466
+ this.musts = spec.state.musts ?? [];
55
467
  this.invalid = spec.state.invalid;
56
468
  }
57
469
  else if (spec.atom) {
58
- this.fromAtom(spec.atom, spec.peg ?? []);
470
+ const args = atomArgs(spec.atom, spec.peg ?? []);
471
+ // An argument that is not yet concrete — a reference, an
472
+ // arithmetic expression, a conjunct of atoms — makes the whole
473
+ // atom PENDING rather than invalid (G1 phase 4). It is resolved
474
+ // in unify, where there is a ctx to resolve through, and the
475
+ // residual is built from the settled arguments. Only a settled
476
+ // argument of the wrong shape is an `invalid-arg`.
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)) {
485
+ this.pending = { atom: spec.atom, args };
486
+ }
487
+ else {
488
+ this.fromAtom(spec.atom, args);
489
+ }
490
+ }
491
+ if (null != this.count || this.uniq || 0 < this.uniqBy.length ||
492
+ 0 < this.musts.length ||
493
+ (null != this.pending && lateAtom(this.pending.atom))) {
494
+ this.cjo = LATE_CJO;
495
+ }
496
+ // A residual constraint is stable, like a ScalarKindVal — but a
497
+ // pending atom is not a residual yet, and must be re-entered on
498
+ // later passes until its arguments settle.
499
+ if (null == this.pending) {
500
+ this.dc = type_1.DONE;
501
+ }
502
+ else {
503
+ this.notdone();
59
504
  }
60
- // A residual constraint is stable, like a ScalarKindVal.
61
- this.dc = type_1.DONE;
62
505
  }
63
- // Normalise one atom call (min/max/above/below/neq) into state.
64
- // Arguments must be concrete orderable scalars in phase 1;
65
- // reference-valued arguments are phase 4 (residuation).
506
+ // Normalise one atom call into state. Every argument here is already
507
+ // SETTLED — the constructor routes an unsettled one to `pending` —
508
+ // so a shape this cannot use is a genuine `invalid-arg`, not a
509
+ // not-yet.
66
510
  fromAtom(atom, args) {
67
- // Mark the residual invalid and report so (a plain boolean, so no
68
- // void value is consumed by the callers' `return` statements).
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.
69
516
  const bad = (why) => {
70
517
  this.invalid = why;
71
- return true;
72
518
  };
73
- if ('neq' === atom) {
74
- // Multiple arguments arrive from the func-paren grammar as one
75
- // entry holding the comma group: a raw array of Vals (or an
76
- // implicit ListVal via some spellings). `neq(3,1,2)` therefore
77
- // has peg [[3,1,2]], and `neq([3,1,2])` means the same thing.
78
- if (1 === args.length && Array.isArray(args[0])) {
79
- args = args[0];
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.
526
+ if ('unique' === atom) {
527
+ if (0 === args.length) {
528
+ this.uniq = true;
529
+ return;
80
530
  }
81
- else if (1 === args.length && true === args[0]?.isList) {
82
- args = args[0].peg;
531
+ if (1 !== args.length || !stringLeaf(args[0])) {
532
+ return bad('invalid-arg');
83
533
  }
534
+ this.uniqBy = [args[0].peg];
535
+ return;
536
+ }
537
+ // `must(c, msg)` is Band B: an evaluate-only check against the
538
+ // finished value, carrying the author's own message. It is KEPT,
539
+ // never simplified and never consulted for emptiness or
540
+ // subsumption — Band B is opaque by construction, which is exactly
541
+ // what makes it the honest channel for a rule the algebra cannot
542
+ // reason about (docs/reference-language.md, "Band B: `must`").
543
+ if ('must' === atom) {
544
+ if (2 !== args.length) {
545
+ return bad('arg');
546
+ }
547
+ if (!stringLeaf(args[1])) {
548
+ return bad('invalid-arg');
549
+ }
550
+ // A check carrying a nil can never be satisfied, so it is refused
551
+ // as an ARGUMENT rather than left to fail against every value with
552
+ // the author's message attached -- which would blame the data for
553
+ // a mistake in the check. `must([1-x],m)` is the reachable case: a
554
+ // degenerate expression leaves a nil inside the written list.
555
+ if (holdsNil(args[0])) {
556
+ return bad('invalid-arg');
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.)
561
+ this.musts = [{ v: args[0], msg: args[1] }];
562
+ return;
563
+ }
564
+ if ('neq' === atom) {
84
565
  if (0 === args.length) {
85
566
  return bad('arg');
86
567
  }
@@ -100,6 +581,71 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
100
581
  return bad('arg');
101
582
  }
102
583
  const a = args[0];
584
+ // `re` is the one atom whose argument is not an ORDER point: a
585
+ // pattern is a membership test, so it takes the string domain
586
+ // outright rather than inferring a domain from the argument's leaf.
587
+ if ('re' === atom) {
588
+ if (!stringLeaf(a)) {
589
+ return bad('invalid-arg');
590
+ }
591
+ const src = a.peg;
592
+ // Normalise BEFORE compiling: the host engines only ever see a
593
+ // pattern that cannot be read two ways (ADR-003).
594
+ const [norm, why] = normaliseRe(src);
595
+ if ('' !== why) {
596
+ this.invalidWhy = why;
597
+ return bad('constraint_pattern');
598
+ }
599
+ let re;
600
+ try {
601
+ // The `u` flag is REQUIRED for parity, not an optimisation.
602
+ // Without it JavaScript matches UTF-16 code units while RE2
603
+ // matches code points, so `re("^.$")` accepted U+1D11E in Go
604
+ // and refused it in TypeScript (and `^..$` did the reverse).
605
+ // With it, `.` and every quantifier count code points in both.
606
+ // It also makes JavaScript refuse the identity escapes this
607
+ // scanner rejects by hand, which is defence in depth rather
608
+ // than a substitute: RE2 accepts some of them, so the scanner
609
+ // is what keeps the two ports agreeing.
610
+ re = new RegExp(norm, 'u');
611
+ }
612
+ catch (e) {
613
+ // The host engine refuses what the subset scanner passed — a
614
+ // malformed quantifier, an unbalanced group. Same refusal under
615
+ // the same code: the author gets one rule, not two. The message
616
+ // is the host's, so it is NOT pinned by a shared row; the code
617
+ // and the located frame are.
618
+ this.invalidWhy = 'not a valid pattern';
619
+ return bad('constraint_pattern');
620
+ }
621
+ this.domain = 'string';
622
+ this.res = [{ v: a, src, norm, re }];
623
+ return;
624
+ }
625
+ // `length` is the other non-ORDER atom: its argument constrains the
626
+ // COUNT, not the value, so it is itself a residual over the integer
627
+ // domain (docs/reference-language.md, "`length` semantics"). It is
628
+ // resolved HERE, at construction, by walking the written argument
629
+ // rather than by unifying it: the func-paren handler builds atoms
630
+ // without an AontuContext, so there is nothing to fold a nested
631
+ // conjunct through. Walking is enough because a length argument is by
632
+ // definition a meet of concrete Band A atoms; anything else (a
633
+ // reference, an expression) is refused rather than deferred, the
634
+ // same discipline min/max apply to their own arguments.
635
+ if ('length' === atom) {
636
+ const arg = countArgState(a);
637
+ if (null == arg) {
638
+ return bad('invalid-arg');
639
+ }
640
+ const inner = meetCount(countBase(), arg);
641
+ this.count = inner;
642
+ // `length(min(5)&max(3))` is unsatisfiable with no peer in sight, so
643
+ // it is refused at composition time like any other empty meet.
644
+ if (stateEmpty(inner)) {
645
+ return bad('constraint');
646
+ }
647
+ return;
648
+ }
103
649
  const domain = numericLeaf(a) ? 'number' : stringLeaf(a) ? 'string' : undefined;
104
650
  if (null == domain) {
105
651
  return bad('invalid-arg');
@@ -119,8 +665,11 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
119
665
  // Every branch of the ladder assigns, so no initialiser: a
120
666
  // residual is stable and the ladder is total.
121
667
  let out;
122
- if (null != this.invalid) {
123
- out = (0, err_1.makeNilErr)(ctx, this.invalid, this, undefined, 'constrain');
668
+ if (null != this.pending) {
669
+ out = this.settle(peer, ctx);
670
+ }
671
+ else if (null != this.invalid) {
672
+ out = (0, err_1.makeNilErr)(ctx, this.invalid, this, undefined, 'constrain', null == this.invalidWhy ? undefined : { reason: this.invalidWhy });
124
673
  }
125
674
  else if (null == peer || peer.isTop) {
126
675
  out = this;
@@ -137,65 +686,363 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
137
686
  else if (peer.isScalar) {
138
687
  out = this.admit(peer, ctx);
139
688
  }
689
+ else if (peer.isMap || peer.isList) {
690
+ out = this.admitContainer(peer, ctx);
691
+ }
692
+ /* node:coverage ignore next 12 */
140
693
  else {
141
- // Maps, lists, and every other non-scalar shape: no order, no
142
- // membership — a conflict of the constraint family.
694
+ // Every other shape: no order, no membership, nothing to count —
695
+ // a conflict of the constraint family. The ladder above is total
696
+ // in practice: every remaining Val kind either sorts BELOW a
697
+ // constraint in a conjunct (conjunct, disjunct, pref, ref) and so
698
+ // drives the meet from its own side, or resolves to a
699
+ // scalar/container before a constraint sees it (func, op, var,
700
+ // expect). The arm is kept because "in practice" depends on the
701
+ // cjo table, and a future value class would land here rather than
702
+ // falling out of unify with no result.
143
703
  out = this.fail(ctx, peer);
144
704
  }
145
705
  ctx.explain && (0, utility_1.explainClose)(te, out);
146
706
  return out;
147
707
  }
708
+ // Resolve a pending atom's arguments and, once they have all settled,
709
+ // become the residual they describe (G1 phase 4).
710
+ //
711
+ // This is the residuation discipline FuncBaseVal already follows for
712
+ // its own operands: push each unsettled argument one step by unifying
713
+ // it with `top`, and if any is still moving, mark not-done and defer
714
+ // — either as this same pending atom (against a `top` peer) or
715
+ // wrapped with the peer in a conjunct the next pass will re-enter.
716
+ // Nothing is decided from a half-resolved argument, which is what
717
+ // keeps `min($.lo)` from reporting a conflict against a bound that
718
+ // has not arrived yet.
719
+ settle(peer, ctx) {
720
+ const TOP = (0, top_1.top)();
721
+ const pend = this.pending;
722
+ let settled = true;
723
+ const args = [];
724
+ for (const arg of pend.args) {
725
+ let next = arg;
726
+ if (true !== arg?.done) {
727
+ // Charged to the depth budget: this recurses without going
728
+ // through `unite`, so the counter would otherwise stay flat
729
+ // while the stack grows (the rule FuncBaseVal follows).
730
+ next = (0, unify_1.withDepth)(ctx, arg, TOP, () => arg.unify(TOP, ctx));
731
+ }
732
+ settled = settled && true === next?.done;
733
+ args.push(next);
734
+ }
735
+ if (settled) {
736
+ // Build the residual the atom always meant, at this atom's site,
737
+ // then let the ordinary ladder meet it with the peer.
738
+ const built = new ConstraintVal({ peg: args, atom: pend.atom }, ctx);
739
+ built.path = this.path;
740
+ built.site.row = this.site.row;
741
+ built.site.col = this.site.col;
742
+ built.site.url = this.site.url;
743
+ (0, utility_1.propagateMarks)(this, built);
744
+ return built.unify(peer, ctx);
745
+ }
746
+ this.notdone();
747
+ // A fresh pending atom carrying the partially-resolved arguments, so
748
+ // the next pass starts from the progress this one made rather than
749
+ // re-resolving from source.
750
+ const again = new ConstraintVal({ peg: args, atom: pend.atom }, ctx);
751
+ again.path = this.path;
752
+ again.site.row = this.site.row;
753
+ again.site.col = this.site.col;
754
+ again.site.url = this.site.url;
755
+ (0, utility_1.propagateMarks)(this, again);
756
+ if (null == peer || peer.isTop) {
757
+ return again;
758
+ }
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.
762
+ return new ConjunctVal_1.ConjunctVal({ peg: [again, peer] }, ctx);
763
+ }
148
764
  // Membership: the peer scalar passes every part of the residual, or
149
765
  // the whole meet is a located conflict.
150
766
  admit(peer, ctx) {
151
- const domainOf = numericLeaf(peer) ? 'number' :
152
- stringLeaf(peer) ? 'string' : undefined;
153
- if (domainOf !== this.domain) {
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) {
154
770
  return this.fail(ctx, peer);
155
771
  }
156
- if (null != this.kind && leafMarker(peer) !== this.kind) {
772
+ if (!stateAdmits(this, peer)) {
157
773
  return this.fail(ctx, peer);
158
774
  }
159
- const d = this.domain;
160
- if (null != this.lo) {
161
- const c = cmpVal(d, peer, this.lo.v);
162
- if (c < 0 || (0 === c && this.lo.open)) {
775
+ if (null != this.count) {
776
+ // Only a string among the scalars has a length, and it is counted
777
+ // in CODE POINTS -- not UTF-16 units (this host's native count)
778
+ // and not bytes (Go's). Iterating a string yields code points.
779
+ if (!stringLeaf(peer)) {
163
780
  return this.fail(ctx, peer);
164
781
  }
165
- }
166
- if (null != this.hi) {
167
- const c = cmpVal(d, peer, this.hi.v);
168
- if (c > 0 || (0 === c && this.hi.open)) {
782
+ if (!stateAdmits(this.count, countVal([...peer.peg].length))) {
169
783
  return this.fail(ctx, peer);
170
784
  }
171
785
  }
172
- for (const n of this.neqs) {
173
- if (sameScalar(peer, n)) {
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);
790
+ if (null != bad) {
791
+ return bad;
792
+ }
793
+ return peer;
794
+ }
795
+ // Band B, applied to a finished value. Each check is a plain
796
+ // unification against a CLONE of the peer: `must` reports, it never
797
+ // contributes: whatever the check would have added to the value is
798
+ // discarded, and `peer` is returned untouched by the callers.
799
+ //
800
+ // The trial runs in an isolated collect context so a failing check
801
+ // leaves nothing on the caller's error list — only the located nil
802
+ // this returns, carrying the author's own message.
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) {
811
+ for (const m of this.musts) {
812
+ const trial = ctx.clone({ err: [], collect: true });
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
+ }
826
+ if (true === got?.isNil || 0 < trial.err.length) {
827
+ return (0, err_1.makeNilErr)(ctx, 'must', this, peer, undefined, {
828
+ message: m.msg.peg,
829
+ expected: m.v.canon,
830
+ actual: peer.canon,
831
+ });
832
+ }
833
+ }
834
+ return undefined;
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
+ }
844
+ // Membership for a container peer. Only the SIZING atoms have anything
845
+ // to say about a map or a list; every other atom is scalar-domain and
846
+ // refuses one.
847
+ //
848
+ // The members that count are the members that GENERATE
849
+ // (docs/reference-language.md, "`length` semantics"), and rather than
850
+ // mirror generation's filter — type/hide marks, optional keys that
851
+ // drop, empty optional values — this asks generation itself, in an
852
+ // isolated collect context so nothing leaks into the caller's errors.
853
+ // A mirror would be a second copy of a filter that has already grown
854
+ // subtle, free to drift from it; asking is correct by construction.
855
+ admitContainer(peer, ctx, final) {
856
+ // A scalar-domain residual has no reading over a container.
857
+ if (null != this.domain) {
858
+ return this.fail(ctx, peer);
859
+ }
860
+ // Not yet settled: the container, or an optional child, may still
861
+ // resolve, so the member set is not final. Defer rather than decide
862
+ // — the same discipline OpBaseVal follows for a non-concrete operand.
863
+ if (!containerSettled(peer)) {
864
+ this.dc = 0;
865
+ return new ConjunctVal_1.ConjunctVal({ peg: [this, peer] }, ctx);
866
+ }
867
+ const bad = this.checkMusts(peer, ctx, final);
868
+ if (null != bad) {
869
+ return bad;
870
+ }
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);
883
+ }
884
+ const members = emittedMembers(peer, ctx);
885
+ if (null == members) {
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))) {
174
935
  return this.fail(ctx, peer);
175
936
  }
176
937
  }
177
- return peer;
938
+ if (this.uniq) {
939
+ // Members compare by CANONICAL FORM, which reduces to scalar
940
+ // identity for scalars (so [1, 1.0] is distinct) and gives
941
+ // structural equality for container members without a second
942
+ // rule. A generated value could not express the first: `1` and
943
+ // `1.0` generate the same JSON number.
944
+ const seen = new Set();
945
+ for (const m of members) {
946
+ const key = m.canon;
947
+ if (seen.has(key)) {
948
+ return this.fail(ctx, peer);
949
+ }
950
+ seen.add(key);
951
+ }
952
+ }
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;
178
1012
  }
179
1013
  // Meet with a kind: `number` (or `string` on the string domain) is
180
- // already implied; a numeric LEAF narrows the residual; anything
181
- // else has an empty intersection with the constraint's domain.
1014
+ // already implied by an ORDER atom's argument; a numeric LEAF narrows
1015
+ // the residual; anything else has an empty intersection with the
1016
+ // constraint's domain.
1017
+ //
1018
+ // A sizing residual (`length`, `unique`) has no domain of its own -- a
1019
+ // count says nothing about what is counted -- so a kind here SETS one
1020
+ // rather than merely agreeing with it: `string & length(3)` is a
1021
+ // three-character string, and `number & length(3)` is empty because a
1022
+ // number has no length (stateEmpty decides that, not this).
182
1023
  meetKind(peer, ctx) {
183
1024
  const marker = peer.peg;
184
- if (Number === marker) {
185
- return 'number' === this.domain ? this : this.fail(ctx, peer);
186
- }
187
- if (String === marker) {
188
- return 'string' === this.domain ? this : this.fail(ctx, peer);
1025
+ const merged = this.cloneState();
1026
+ if (Number === marker || String === marker) {
1027
+ const d = Number === marker ? 'number' : 'string';
1028
+ if (d === this.domain) {
1029
+ return this;
1030
+ }
1031
+ if (null != this.domain) {
1032
+ return this.fail(ctx, peer);
1033
+ }
1034
+ merged.domain = d;
1035
+ return this.finish(merged, ctx, peer);
189
1036
  }
190
1037
  const isLeaf = ScalarKindVal_1.Integer === marker || ScalarKindVal_1.Float === marker ||
191
1038
  ScalarKindVal_1.BigInteger === marker || ScalarKindVal_1.BigDecimal === marker;
192
- if (!isLeaf || 'number' !== this.domain) {
1039
+ if (!isLeaf || 'string' === this.domain) {
193
1040
  return this.fail(ctx, peer);
194
1041
  }
195
1042
  if (null != this.kind && this.kind !== marker) {
196
1043
  return this.fail(ctx, peer);
197
1044
  }
198
- const merged = this.cloneState();
1045
+ merged.domain = 'number';
199
1046
  merged.kind = marker;
200
1047
  return this.finish(merged, ctx, peer);
201
1048
  }
@@ -203,7 +1050,7 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
203
1050
  // union, kind union — then the eager emptiness rules.
204
1051
  meetConstraint(peer, ctx) {
205
1052
  if (null != peer.invalid) {
206
- return (0, err_1.makeNilErr)(ctx, peer.invalid, peer, undefined, 'constrain');
1053
+ return (0, err_1.makeNilErr)(ctx, peer.invalid, peer, undefined, 'constrain', null == peer.invalidWhy ? undefined : { reason: peer.invalidWhy });
207
1054
  }
208
1055
  if (null != this.domain && null != peer.domain && this.domain !== peer.domain) {
209
1056
  return this.fail(ctx, peer);
@@ -218,51 +1065,29 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
218
1065
  merged.lo = tighter(d, this.lo, peer.lo, true);
219
1066
  merged.hi = tighter(d, this.hi, peer.hi, false);
220
1067
  merged.neqs = dedupSorted(d, [...this.neqs, ...peer.neqs]);
1068
+ merged.res = dedupSortedRes([...this.res, ...peer.res]);
1069
+ // `length(c1) & length(c2)` is `length(c1 & c2)`: the count atom reuses
1070
+ // numeric algebra recursively, over the counts rather than the
1071
+ // values.
1072
+ merged.count = null == this.count ? peer.count :
1073
+ null == peer.count ? this.count : meetCount(this.count, peer.count);
1074
+ // `unique()` is idempotent: two of them are one.
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();
1081
+ // Band B checks accumulate in written order and are never merged,
1082
+ // deduplicated or reordered: each carries its own author message,
1083
+ // and two checks with the same shape may still say different things.
1084
+ merged.musts = [...this.musts, ...peer.musts];
221
1085
  return this.finish(merged, ctx, peer);
222
1086
  }
223
1087
  // Build the merged residual, applying the eager emptiness rules.
224
1088
  finish(state, ctx, peer) {
225
- const d = state.domain;
226
- if (null != state.lo && null != state.hi) {
227
- const c = cmpVal(d, state.hi.v, state.lo.v);
228
- if (c < 0 || (0 === c && (state.lo.open || state.hi.open))) {
229
- return this.fail(ctx, peer);
230
- }
231
- }
232
- const integral = ScalarKindVal_1.Integer === state.kind || ScalarKindVal_1.BigInteger === state.kind;
233
- // Integral gap: an integer-narrowed interval containing no whole
234
- // number is empty (integer & above(1) & below(2)).
235
- if (integral && null != state.lo && null != state.hi) {
236
- const lo = (0, numcmp_1.scaledOfNumeric)(state.lo.v);
237
- const hi = (0, numcmp_1.scaledOfNumeric)(state.hi.v);
238
- if (!lo.inf && !hi.inf) {
239
- // Smallest admissible integer above/at the lower bound.
240
- let n = (0, numcmp_1.scaledFloor)(lo);
241
- if (!(0, numcmp_1.scaledIsIntegral)(lo) || state.lo.open) {
242
- n += 1n;
243
- }
244
- // Largest admissible integer below/at the upper bound.
245
- let m = (0, numcmp_1.scaledFloor)(hi);
246
- if (state.hi.open && (0, numcmp_1.scaledIsIntegral)(hi)) {
247
- m -= 1n;
248
- }
249
- if (m < n) {
250
- return this.fail(ctx, peer);
251
- }
252
- }
253
- }
254
- // Point deletion under a narrowed leaf: a closed point interval
255
- // whose single value of the narrowed leaf is excluded is empty
256
- // (integer & min(3) & max(3) & neq(3)). Without a narrowing the
257
- // point survives in the other leaves.
258
- if (null != state.kind && null != state.lo && null != state.hi &&
259
- !state.lo.open && !state.hi.open &&
260
- 0 === cmpVal(d, state.lo.v, state.hi.v)) {
261
- for (const n of state.neqs) {
262
- if (leafMarker(n) === state.kind && 0 === (0, numcmp_1.cmpNumeric)(n, state.lo.v)) {
263
- return this.fail(ctx, peer);
264
- }
265
- }
1089
+ if (stateEmpty(state)) {
1090
+ return this.fail(ctx, peer);
266
1091
  }
267
1092
  const out = new ConstraintVal({ peg: [], state }, ctx);
268
1093
  out.path = this.path;
@@ -290,6 +1115,11 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
290
1115
  lo: this.lo,
291
1116
  hi: this.hi,
292
1117
  neqs: [...this.neqs],
1118
+ res: [...this.res],
1119
+ count: this.count,
1120
+ uniq: this.uniq,
1121
+ uniqBy: [...this.uniqBy],
1122
+ musts: [...this.musts],
293
1123
  invalid: this.invalid,
294
1124
  };
295
1125
  }
@@ -303,31 +1133,28 @@ class ConstraintVal extends FeatureVal_1.FeatureVal {
303
1133
  out.lo = this.lo;
304
1134
  out.hi = this.hi;
305
1135
  out.neqs = [...this.neqs];
1136
+ out.res = [...this.res];
1137
+ out.count = this.count;
1138
+ out.uniq = this.uniq;
1139
+ out.uniqBy = [...this.uniqBy];
1140
+ out.musts = [...this.musts];
1141
+ out.pending = this.pending;
1142
+ out.cjo = this.cjo;
306
1143
  out.invalid = this.invalid;
1144
+ out.invalidWhy = this.invalidWhy;
307
1145
  return out;
308
1146
  }
309
1147
  // The fixed canonical atom order: kind, lower, upper, neq (arguments
310
- // sorted). No spaces; reparses to a conjunct that normalises back to
311
- // this exact residual.
1148
+ // sorted), re, length, unique. No spaces; reparses to a conjunct that
1149
+ // normalises back to this exact residual.
312
1150
  get canon() {
313
- const parts = [];
314
- if (null != this.kind) {
315
- parts.push(this.kind.name.toLowerCase());
316
- }
317
- if (null != this.lo) {
318
- parts.push((this.lo.open ? 'above(' : 'min(') + this.lo.v.canon + ')');
319
- }
320
- if (null != this.hi) {
321
- parts.push((this.hi.open ? 'below(' : 'max(') + this.hi.v.canon + ')');
1151
+ if (null != this.pending) {
1152
+ // A pending atom has no residual yet, so canon renders the call as
1153
+ // written — the same shape FuncBaseVal renders while deferring.
1154
+ return this.pending.atom +
1155
+ '(' + this.pending.args.map((a) => a.canon).join(',') + ')';
322
1156
  }
323
- if (0 < this.neqs.length) {
324
- parts.push('neq(' + this.neqs.map((n) => n.canon).join(',') + ')');
325
- }
326
- if (0 === parts.length) {
327
- // Raw invalid atom: render the call so the error frame shows it.
328
- return 'constraint()';
329
- }
330
- return parts.join('&');
1157
+ return canonState(this);
331
1158
  }
332
1159
  same(peer) {
333
1160
  return true === peer?.isConstraint && this.canon === peer.canon;
@@ -371,7 +1198,547 @@ function dedupSorted(domain, neqs) {
371
1198
  }
372
1199
  return out;
373
1200
  }
374
- // The five atom classes registered in the parser's funcMap: each is a
1201
+ // Is this value, or anything inside it, a nil? A written argument that
1202
+ // holds one can never be satisfied, so the atom refuses it as an
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.
1208
+ function holdsNil(v) {
1209
+ if (true === v.isNil) {
1210
+ return true;
1211
+ }
1212
+ const peg = v.peg;
1213
+ if (Array.isArray(peg)) {
1214
+ return peg.some((c) => holdsNil(c));
1215
+ }
1216
+ if (null != peg && 'object' === typeof peg) {
1217
+ for (const k in peg) {
1218
+ if (holdsNil(peg[k])) {
1219
+ return true;
1220
+ }
1221
+ }
1222
+ }
1223
+ return false;
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
+ }
1248
+ // The written arguments of an atom, flattened.
1249
+ //
1250
+ // A multi-argument call arrives from the func-paren grammar as ONE
1251
+ // entry holding the comma group, and `neq([3,1,2])` means the same as
1252
+ // `neq(3,1,2)`. The group is always a ListVal by the time it reaches
1253
+ // here -- the func-paren handler rawToVals every argument (issue #49) --
1254
+ // so there is no raw-array case to unwrap. Flattening happens before
1255
+ // the settled check, because an unsettled member hiding inside the
1256
+ // group would otherwise make the atom look ready.
1257
+ function atomArgs(atom, args) {
1258
+ if (('neq' === atom || 'must' === atom) && 1 === args.length &&
1259
+ true === args[0]?.isList) {
1260
+ return args[0].peg;
1261
+ }
1262
+ return args;
1263
+ }
1264
+ // The canonical rendering of a residual, in the fixed atom order:
1265
+ // kind, lower bound, upper bound, neq, re, length, unique. Taken over the
1266
+ // STATE rather than the Val because `length`'s argument is a residual too,
1267
+ // and renders by exactly the same rules.
1268
+ function canonState(s) {
1269
+ const parts = [];
1270
+ if (null != s.kind) {
1271
+ parts.push(s.kind.name.toLowerCase());
1272
+ }
1273
+ // An ORDER atom's argument implies the domain, so it is not spelled
1274
+ // out. A SIZING residual carries no order, and there `string` is the
1275
+ // only thing saying what is being sized -- drop it and the reparse
1276
+ // would admit lists and maps too.
1277
+ else if ('string' === s.domain &&
1278
+ null == s.lo && null == s.hi && 0 === s.neqs.length && 0 === s.res.length) {
1279
+ parts.push('string');
1280
+ }
1281
+ if (null != s.lo) {
1282
+ parts.push((s.lo.open ? 'above(' : 'min(') + s.lo.v.canon + ')');
1283
+ }
1284
+ if (null != s.hi) {
1285
+ parts.push((s.hi.open ? 'below(' : 'max(') + s.hi.v.canon + ')');
1286
+ }
1287
+ if (0 < s.neqs.length) {
1288
+ parts.push('neq(' + s.neqs.map((n) => n.canon).join(',') + ')');
1289
+ }
1290
+ for (const r of s.res) {
1291
+ parts.push('re(' + r.v.canon + ')');
1292
+ }
1293
+ if (null != s.count) {
1294
+ // Rendered UNABRIDGED, implied parts and all: `length(3)` canonicalises
1295
+ // to `length(integer&min(3)&max(3))` because that IS the residual the
1296
+ // count must satisfy, and canon is a normal form (G6 hashes it),
1297
+ // not a pretty-printer. Abbreviating would mean a second set of
1298
+ // rules for when the implied `integer & min(0)` may be dropped.
1299
+ parts.push('length(' + canonState(s.count) + ')');
1300
+ }
1301
+ if (s.uniq) {
1302
+ parts.push('unique()');
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
+ }
1310
+ for (const m of s.musts) {
1311
+ parts.push('must(' + m.v.canon + ',' + m.msg.canon + ')');
1312
+ }
1313
+ if (0 === parts.length) {
1314
+ // Raw invalid atom: render the call so the error frame shows it.
1315
+ return 'constraint()';
1316
+ }
1317
+ return parts.join('&');
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
+ }
1423
+ // Does this residual's ORDER and MEMBERSHIP part admit the scalar? The
1424
+ // sizing atoms are deliberately not consulted: `admit` applies them to
1425
+ // the peer's length, and the count check applies this same function to
1426
+ // the count.
1427
+ function stateAdmits(s, peer) {
1428
+ const domainOf = numericLeaf(peer) ? 'number' :
1429
+ stringLeaf(peer) ? 'string' : undefined;
1430
+ if (null == s.domain) {
1431
+ // A sizing residual has no domain, and admits any scalar the sizing
1432
+ // atoms can then rule on. Booleans and null are not among them:
1433
+ // they have no order, no length and no members.
1434
+ if (null == domainOf) {
1435
+ return false;
1436
+ }
1437
+ return true;
1438
+ }
1439
+ if (domainOf !== s.domain) {
1440
+ return false;
1441
+ }
1442
+ if (null != s.kind && leafMarker(peer) !== s.kind) {
1443
+ return false;
1444
+ }
1445
+ const d = s.domain;
1446
+ if (null != s.lo) {
1447
+ const c = cmpVal(d, peer, s.lo.v);
1448
+ if (c < 0 || (0 === c && s.lo.open)) {
1449
+ return false;
1450
+ }
1451
+ }
1452
+ if (null != s.hi) {
1453
+ const c = cmpVal(d, peer, s.hi.v);
1454
+ if (c > 0 || (0 === c && s.hi.open)) {
1455
+ return false;
1456
+ }
1457
+ }
1458
+ for (const n of s.neqs) {
1459
+ if (sameScalar(peer, n)) {
1460
+ return false;
1461
+ }
1462
+ }
1463
+ // Every accumulated pattern must match: the meet of two `re` atoms
1464
+ // is conjunction, and matching is UNANCHORED in both engines
1465
+ // (JS RegExp.test, Go regexp.MatchString), so `re("el")` admits
1466
+ // "hello". Anchor with ^ and $ to mean the whole string.
1467
+ for (const r of s.res) {
1468
+ if (!r.re.test(peer.peg)) {
1469
+ return false;
1470
+ }
1471
+ }
1472
+ return true;
1473
+ }
1474
+ // The eager emptiness rules. Each is EXACT -- it reports empty only
1475
+ // where no value could satisfy the residual -- so the algebra stays
1476
+ // sound; the incompleteness it accepts is documented in
1477
+ // docs/reference-language.md, "Emptiness".
1478
+ function stateEmpty(s) {
1479
+ // Two disagreeing kind narrowings inside a length argument, recorded by
1480
+ // meetCount because that meet has no ctx to fail through.
1481
+ if (s.clash) {
1482
+ return true;
1483
+ }
1484
+ const d = s.domain;
1485
+ // Empty interval.
1486
+ if (null != s.lo && null != s.hi) {
1487
+ const c = cmpVal(d, s.hi.v, s.lo.v);
1488
+ if (c < 0 || (0 === c && (s.lo.open || s.hi.open))) {
1489
+ return true;
1490
+ }
1491
+ }
1492
+ const integral = ScalarKindVal_1.Integer === s.kind || ScalarKindVal_1.BigInteger === s.kind;
1493
+ // Integral gap: an integer-narrowed interval containing no whole
1494
+ // number is empty (integer & above(1) & below(2)).
1495
+ if (integral && null != s.lo && null != s.hi) {
1496
+ const lo = (0, numcmp_1.scaledOfNumeric)(s.lo.v);
1497
+ const hi = (0, numcmp_1.scaledOfNumeric)(s.hi.v);
1498
+ if (!lo.inf && !hi.inf) {
1499
+ // Smallest admissible integer above/at the lower bound.
1500
+ let n = (0, numcmp_1.scaledFloor)(lo);
1501
+ if (!(0, numcmp_1.scaledIsIntegral)(lo) || s.lo.open) {
1502
+ n += 1n;
1503
+ }
1504
+ // Largest admissible integer below/at the upper bound.
1505
+ let m = (0, numcmp_1.scaledFloor)(hi);
1506
+ if (s.hi.open && (0, numcmp_1.scaledIsIntegral)(hi)) {
1507
+ m -= 1n;
1508
+ }
1509
+ if (m < n) {
1510
+ return true;
1511
+ }
1512
+ }
1513
+ }
1514
+ // Point deletion under a narrowed leaf: a closed point interval
1515
+ // whose single value of the narrowed leaf is excluded is empty
1516
+ // (integer & min(3) & max(3) & neq(3)). Without a narrowing the
1517
+ // point survives in the other leaves.
1518
+ if (null != s.kind && null != s.lo && null != s.hi &&
1519
+ !s.lo.open && !s.hi.open &&
1520
+ 0 === cmpVal(d, s.lo.v, s.hi.v)) {
1521
+ for (const n of s.neqs) {
1522
+ if (leafMarker(n) === s.kind && 0 === (0, numcmp_1.cmpNumeric)(n, s.lo.v)) {
1523
+ return true;
1524
+ }
1525
+ }
1526
+ }
1527
+ // Sizing over the number domain: a number has neither a length nor
1528
+ // members, so `integer & length(3)` and `min(2) & unique()` admit
1529
+ // nothing. Uniqueness over the string domain is empty for the same
1530
+ // reason -- a string's members are not values the algebra compares.
1531
+ if ('number' === d && (null != s.count || s.uniq ||
1532
+ 0 < s.uniqBy.length)) {
1533
+ return true;
1534
+ }
1535
+ if ('string' === d && s.uniq) {
1536
+ return true;
1537
+ }
1538
+ // An empty count residual makes the whole thing empty: no container
1539
+ // and no string has a length no integer can take.
1540
+ if (null != s.count && stateEmpty(s.count)) {
1541
+ return true;
1542
+ }
1543
+ return false;
1544
+ }
1545
+ // The base every `length` argument meets: a count is a non-negative
1546
+ // integer, whatever else the argument says (docs/reference-language.md,
1547
+ // "`length` semantics" -- `length(c)` is empty iff `c & integer & min(0)`
1548
+ // is).
1549
+ function countBase() {
1550
+ return {
1551
+ domain: 'number',
1552
+ kind: ScalarKindVal_1.Integer,
1553
+ lo: { v: countVal(0), open: false },
1554
+ neqs: [],
1555
+ res: [],
1556
+ musts: [],
1557
+ // A COUNT is a number, and a number has no members to be distinct.
1558
+ uniq: false,
1559
+ uniqBy: [],
1560
+ };
1561
+ }
1562
+ // A count as a Val, so the count residual can be applied by exactly the
1563
+ // same membership function as any other numeric residual.
1564
+ function countVal(n) {
1565
+ return new IntegerVal_1.IntegerVal({ peg: n });
1566
+ }
1567
+ // The pure meet of two count residuals. Both are number-domain and
1568
+ // carry no pattern or sizing atom of their own, so the merge is the
1569
+ // interval/exclusion part alone. A kind disagreement becomes a `clash`
1570
+ // rather than an error: this meet runs at construction, where there is
1571
+ // no AontuContext to raise through, and an empty residual carries the
1572
+ // same news to `unify`.
1573
+ function meetCount(a, b) {
1574
+ return {
1575
+ domain: 'number',
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,
1579
+ lo: tighter('number', a.lo, b.lo, true),
1580
+ hi: tighter('number', a.hi, b.hi, false),
1581
+ neqs: dedupSorted('number', [...a.neqs, ...b.neqs]),
1582
+ res: [],
1583
+ musts: [],
1584
+ uniq: false,
1585
+ uniqBy: [],
1586
+ clash: true === a.clash || true === b.clash ||
1587
+ (null != a.kind && null != b.kind && a.kind !== b.kind),
1588
+ };
1589
+ }
1590
+ // Read a WRITTEN `length` argument as a count residual, or undefined when
1591
+ // it is not one. Accepted: an integer literal (an exact count), a
1592
+ // numeric kind, a Band A residual over the number domain, and any
1593
+ // conjunct of those. A conjunct never reaches here any more: an
1594
+ // unsettled argument is held as `pending` and folded before the count
1595
+ // reads it (G1 phase 4), so `length(min(2)&max(5))` arrives as the
1596
+ // single residual it folds to.
1597
+ //
1598
+ // Anything else is refused rather than deferred. A reference or an
1599
+ // expression would have to residuate, and the sizing atoms do not
1600
+ // residuate on their ARGUMENT -- only on the peer whose members are
1601
+ // still settling.
1602
+ function countArgState(arg) {
1603
+ if (numericLeaf(arg)) {
1604
+ return {
1605
+ domain: 'number',
1606
+ lo: { v: arg, open: false },
1607
+ hi: { v: arg, open: false },
1608
+ neqs: [], res: [], musts: [], uniq: false, uniqBy: [],
1609
+ };
1610
+ }
1611
+ if (true === arg?.isConstraint) {
1612
+ const c = arg;
1613
+ // A pattern, a sizing atom or a string bound inside a count is not
1614
+ // a count constraint at all, and neither is a broken one.
1615
+ if (null != c.invalid || 0 < c.res.length || c.uniq ||
1616
+ 0 < c.uniqBy.length || null != c.count ||
1617
+ 'number' !== c.domain) {
1618
+ return undefined;
1619
+ }
1620
+ return {
1621
+ domain: 'number',
1622
+ kind: c.kind,
1623
+ lo: c.lo,
1624
+ hi: c.hi,
1625
+ neqs: [...c.neqs],
1626
+ res: [], musts: [], uniq: false, uniqBy: [],
1627
+ };
1628
+ }
1629
+ if (true === arg?.isScalarKind) {
1630
+ const marker = arg.peg;
1631
+ if (Number === marker) {
1632
+ return {
1633
+ domain: 'number', neqs: [], res: [], musts: [],
1634
+ uniq: false, uniqBy: [],
1635
+ };
1636
+ }
1637
+ if (ScalarKindVal_1.Integer === marker || ScalarKindVal_1.Float === marker ||
1638
+ ScalarKindVal_1.BigInteger === marker || ScalarKindVal_1.BigDecimal === marker) {
1639
+ return {
1640
+ domain: 'number', kind: marker, neqs: [], res: [], musts: [],
1641
+ uniq: false, uniqBy: [],
1642
+ };
1643
+ }
1644
+ return undefined;
1645
+ }
1646
+ return undefined;
1647
+ }
1648
+ // A container is SETTLED when it and every child have converged. Until
1649
+ // then the member set can still change — an optional key whose value is
1650
+ // a still-resolving reference may yet generate — so a sizing atom must
1651
+ // defer rather than decide (docs/reference-language.md, "`length`
1652
+ // semantics"). Note that an optional holding a settled-but-ungenerable
1653
+ // value, `{x:1,y?:number}`, IS settled: the map converges on the first
1654
+ // pass and `y` is simply never emitted.
1655
+ function containerSettled(bag) {
1656
+ // The bag's OWN done-counter is enough: BagVal.unify sets it from the
1657
+ // AND over its children, so an unsettled child already leaves the bag
1658
+ // unsettled. Walking the children again would be a second, drifting
1659
+ // copy of that rule.
1660
+ return true === bag.done;
1661
+ }
1662
+ // The child kinds `BagVal.gen` will attempt to generate. Anything else
1663
+ // is residue: dropped when the key is optional, and a bag-level error
1664
+ // otherwise.
1665
+ function genable(child) {
1666
+ return true === child.isScalar || true === child.isMap ||
1667
+ true === child.isList || true === child.isPref ||
1668
+ true === child.isRef || true === child.isDisjunct ||
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);
1675
+ }
1676
+ // The children a bag would EMIT, mirroring the selection in
1677
+ // `BagVal.gen` — type/hide marks skipped, non-generable residue and
1678
+ // values that generate nothing dropped, optional keys dropped when
1679
+ // they generate empty.
1680
+ //
1681
+ // It returns the member VALS rather than their generated values,
1682
+ // because uniqueness compares canon and a generated value cannot
1683
+ // express that distinction: `1` and `1.0` generate the same JSON number
1684
+ // and canon differently. Counting uses the same list, so both sizing
1685
+ // atoms see exactly one definition of "member".
1686
+ //
1687
+ // Returns undefined when a REQUIRED child is residue: the container's
1688
+ // own `gen` raises there, and that error is the one worth reporting.
1689
+ function emittedMembers(bag, ctx) {
1690
+ const out = [];
1691
+ let entries = (0, utility_1.items)(bag.peg);
1692
+ if (bag.isMap) {
1693
+ // Code-point order, because the two ports disagree on raw key order
1694
+ // — JavaScript hoists integer-like keys, Go keeps insertion order —
1695
+ // and a duplicate report must name the same pair in both.
1696
+ entries = entries
1697
+ .slice()
1698
+ .sort((a, b) => (0, keyorder_1.cmpCodePoint)(String(a[0]), String(b[0])));
1699
+ }
1700
+ for (const item of entries) {
1701
+ const key = item[0];
1702
+ const child = item[1];
1703
+ if (child.mark.type || child.mark.hide) {
1704
+ continue;
1705
+ }
1706
+ const optional = bag.optionalKeys.includes('' + key);
1707
+ if (!genable(child)) {
1708
+ if (optional) {
1709
+ continue;
1710
+ }
1711
+ return undefined;
1712
+ }
1713
+ // Generation decides in an isolated collect context, so an
1714
+ // unresolved inner value neither raises here nor pollutes the
1715
+ // caller's errors — the same isolation BagVal.gen uses for an
1716
+ // optional child.
1717
+ const cval = child.gen(ctx.clone({ err: [], collect: true }));
1718
+ if (undefined === cval || (optional && (0, Val_1.empty)(cval))) {
1719
+ continue;
1720
+ }
1721
+ out.push(child);
1722
+ }
1723
+ return out;
1724
+ }
1725
+ // Sort accumulated patterns by source in code-point order and drop
1726
+ // exact duplicates. Patterns are NEVER simplified or compared for
1727
+ // containment: deciding `re("a")` subsumes `re("a|b")` is regex
1728
+ // containment, which the algebra deliberately does not do (emptiness
1729
+ // stays approximate — sound, incomplete). Two spellings of one language
1730
+ // therefore both survive, and both are tested.
1731
+ function dedupSortedRes(res) {
1732
+ const sorted = [...res].sort((a, b) => (0, numcmp_1.cmpCodePoints)(a.src, b.src));
1733
+ const out = [];
1734
+ for (const r of sorted) {
1735
+ if (0 === out.length || out[out.length - 1].src !== r.src) {
1736
+ out.push(r);
1737
+ }
1738
+ }
1739
+ return out;
1740
+ }
1741
+ // The atom classes registered in the parser's funcMap: each is a
375
1742
  // ConstraintVal that knows its atom name. Constructed by the
376
1743
  // func-paren handler as `new funcval({peg: args})`.
377
1744
  class MinConstraintVal extends ConstraintVal {
@@ -402,6 +1769,30 @@ class NeqConstraintVal extends ConstraintVal {
402
1769
  constructor(spec, ctx) {
403
1770
  super({ ...spec, atom: 'neq' }, ctx);
404
1771
  }
405
- } /* node:coverage ignore next 11 */
1772
+ }
406
1773
  exports.NeqConstraintVal = NeqConstraintVal;
1774
+ class ReConstraintVal extends ConstraintVal {
1775
+ constructor(spec, ctx) {
1776
+ super({ ...spec, atom: 're' }, ctx);
1777
+ }
1778
+ }
1779
+ exports.ReConstraintVal = ReConstraintVal;
1780
+ class MustConstraintVal extends ConstraintVal {
1781
+ constructor(spec, ctx) {
1782
+ super({ ...spec, atom: 'must' }, ctx);
1783
+ }
1784
+ }
1785
+ exports.MustConstraintVal = MustConstraintVal;
1786
+ class LengthConstraintVal extends ConstraintVal {
1787
+ constructor(spec, ctx) {
1788
+ super({ ...spec, atom: 'length' }, ctx);
1789
+ }
1790
+ }
1791
+ exports.LengthConstraintVal = LengthConstraintVal;
1792
+ class UniqueConstraintVal extends ConstraintVal {
1793
+ constructor(spec, ctx) {
1794
+ super({ ...spec, atom: 'unique' }, ctx);
1795
+ }
1796
+ } /* node:coverage ignore next 23 */
1797
+ exports.UniqueConstraintVal = UniqueConstraintVal;
407
1798
  //# sourceMappingURL=ConstraintVal.js.map