aontu 0.51.0 → 0.52.1

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 (182) hide show
  1. package/bin/aontu-lsp.js +6 -0
  2. package/bin/aontu.js +7 -0
  3. package/dist/aontu.d.ts +1 -1
  4. package/dist/aontu.js +53 -7
  5. package/dist/aontu.js.map +1 -1
  6. package/dist/cli.d.ts +0 -1
  7. package/dist/cli.js +10 -46
  8. package/dist/cli.js.map +1 -1
  9. package/dist/ctx.d.ts +3 -0
  10. package/dist/ctx.js +2 -1
  11. package/dist/ctx.js.map +1 -1
  12. package/dist/err.js +13 -3
  13. package/dist/err.js.map +1 -1
  14. package/dist/exactjson.js +1 -1
  15. package/dist/exactjson.js.map +1 -1
  16. package/dist/hints.d.ts +3 -1
  17. package/dist/hints.js +181 -1
  18. package/dist/hints.js.map +1 -1
  19. package/dist/keyorder.js +1 -1
  20. package/dist/keyorder.js.map +1 -1
  21. package/dist/lang.js +299 -42
  22. package/dist/lang.js.map +1 -1
  23. package/dist/lsp-server.d.ts +2 -2
  24. package/dist/lsp-server.js +10 -10
  25. package/dist/lsp-server.js.map +1 -1
  26. package/dist/lsp.js +28 -19
  27. package/dist/lsp.js.map +1 -1
  28. package/dist/site.js +1 -1
  29. package/dist/site.js.map +1 -1
  30. package/dist/tsconfig.tsbuildinfo +1 -1
  31. package/dist/unify.d.ts +2 -1
  32. package/dist/unify.js +103 -4
  33. package/dist/unify.js.map +1 -1
  34. package/dist/utility.js +3 -8
  35. package/dist/utility.js.map +1 -1
  36. package/dist/val/BagVal.js +1 -1
  37. package/dist/val/BagVal.js.map +1 -1
  38. package/dist/val/BigDecimalVal.js +2 -2
  39. package/dist/val/BigDecimalVal.js.map +1 -1
  40. package/dist/val/BigIntegerVal.js +2 -2
  41. package/dist/val/BigIntegerVal.js.map +1 -1
  42. package/dist/val/BooleanVal.js +1 -1
  43. package/dist/val/BooleanVal.js.map +1 -1
  44. package/dist/val/CloseFuncVal.js +1 -1
  45. package/dist/val/CloseFuncVal.js.map +1 -1
  46. package/dist/val/ConjunctVal.js +1 -1
  47. package/dist/val/ConjunctVal.js.map +1 -1
  48. package/dist/val/ConstraintVal.d.ts +97 -0
  49. package/dist/val/ConstraintVal.js +1348 -0
  50. package/dist/val/ConstraintVal.js.map +1 -0
  51. package/dist/val/CopyFuncVal.js +1 -1
  52. package/dist/val/CopyFuncVal.js.map +1 -1
  53. package/dist/val/Decimal.js +1 -1
  54. package/dist/val/Decimal.js.map +1 -1
  55. package/dist/val/DisjunctVal.js +1 -29
  56. package/dist/val/DisjunctVal.js.map +1 -1
  57. package/dist/val/ExpectVal.d.ts +1 -0
  58. package/dist/val/ExpectVal.js +15 -1
  59. package/dist/val/ExpectVal.js.map +1 -1
  60. package/dist/val/FeatureVal.js +1 -1
  61. package/dist/val/FeatureVal.js.map +1 -1
  62. package/dist/val/FuncBaseVal.js +8 -2
  63. package/dist/val/FuncBaseVal.js.map +1 -1
  64. package/dist/val/HideFuncVal.js +1 -1
  65. package/dist/val/HideFuncVal.js.map +1 -1
  66. package/dist/val/IntegerVal.js +2 -2
  67. package/dist/val/IntegerVal.js.map +1 -1
  68. package/dist/val/JunctionVal.js +1 -1
  69. package/dist/val/JunctionVal.js.map +1 -1
  70. package/dist/val/KeyFuncVal.js +1 -1
  71. package/dist/val/KeyFuncVal.js.map +1 -1
  72. package/dist/val/ListVal.js +24 -11
  73. package/dist/val/ListVal.js.map +1 -1
  74. package/dist/val/LowerFuncVal.js +1 -1
  75. package/dist/val/LowerFuncVal.js.map +1 -1
  76. package/dist/val/MapVal.js +20 -9
  77. package/dist/val/MapVal.js.map +1 -1
  78. package/dist/val/MoveFuncVal.js +1 -1
  79. package/dist/val/MoveFuncVal.js.map +1 -1
  80. package/dist/val/NilVal.d.ts +1 -0
  81. package/dist/val/NilVal.js +10 -3
  82. package/dist/val/NilVal.js.map +1 -1
  83. package/dist/val/NullVal.js +1 -1
  84. package/dist/val/NullVal.js.map +1 -1
  85. package/dist/val/NumberVal.js +2 -2
  86. package/dist/val/NumberVal.js.map +1 -1
  87. package/dist/val/OpBaseVal.js +15 -9
  88. package/dist/val/OpBaseVal.js.map +1 -1
  89. package/dist/val/OpenFuncVal.js +1 -1
  90. package/dist/val/OpenFuncVal.js.map +1 -1
  91. package/dist/val/PathFuncVal.js +1 -1
  92. package/dist/val/PathFuncVal.js.map +1 -1
  93. package/dist/val/PlusOpVal.js +1 -1
  94. package/dist/val/PlusOpVal.js.map +1 -1
  95. package/dist/val/PrefFuncVal.js +1 -1
  96. package/dist/val/PrefFuncVal.js.map +1 -1
  97. package/dist/val/PrefVal.js +4 -3
  98. package/dist/val/PrefVal.js.map +1 -1
  99. package/dist/val/RefVal.d.ts +2 -0
  100. package/dist/val/RefVal.js +124 -9
  101. package/dist/val/RefVal.js.map +1 -1
  102. package/dist/val/ScalarKindVal.js +8 -2
  103. package/dist/val/ScalarKindVal.js.map +1 -1
  104. package/dist/val/ScalarVal.js +2 -2
  105. package/dist/val/ScalarVal.js.map +1 -1
  106. package/dist/val/StringVal.js +1 -1
  107. package/dist/val/StringVal.js.map +1 -1
  108. package/dist/val/SuperFuncVal.js +1 -1
  109. package/dist/val/SuperFuncVal.js.map +1 -1
  110. package/dist/val/TopVal.js +1 -1
  111. package/dist/val/TopVal.js.map +1 -1
  112. package/dist/val/TypeFuncVal.js +1 -1
  113. package/dist/val/TypeFuncVal.js.map +1 -1
  114. package/dist/val/UpperFuncVal.js +1 -1
  115. package/dist/val/UpperFuncVal.js.map +1 -1
  116. package/dist/val/Val.d.ts +2 -2
  117. package/dist/val/Val.js +11 -24
  118. package/dist/val/Val.js.map +1 -1
  119. package/dist/val/VarVal.js +1 -1
  120. package/dist/val/VarVal.js.map +1 -1
  121. package/dist/val/numcmp.d.ts +14 -0
  122. package/dist/val/numcmp.js +134 -0
  123. package/dist/val/numcmp.js.map +1 -0
  124. package/dist/val/numkind.js +1 -1
  125. package/dist/val/numkind.js.map +1 -1
  126. package/package.json +15 -13
  127. package/src/aontu.ts +61 -8
  128. package/src/cli.ts +12 -13
  129. package/src/ctx.ts +10 -1
  130. package/src/err.ts +14 -4
  131. package/src/exactjson.ts +1 -1
  132. package/src/hints.ts +202 -0
  133. package/src/keyorder.ts +1 -1
  134. package/src/lang.ts +333 -43
  135. package/src/lsp-server.ts +17 -15
  136. package/src/lsp.ts +28 -12
  137. package/src/site.ts +1 -1
  138. package/src/type.ts +1 -1
  139. package/src/unify.ts +111 -3
  140. package/src/utility.ts +3 -9
  141. package/src/val/BagVal.ts +1 -1
  142. package/src/val/BigDecimalVal.ts +2 -2
  143. package/src/val/BigIntegerVal.ts +2 -2
  144. package/src/val/BooleanVal.ts +1 -1
  145. package/src/val/CloseFuncVal.ts +1 -1
  146. package/src/val/ConjunctVal.ts +1 -1
  147. package/src/val/ConstraintVal.ts +1645 -0
  148. package/src/val/CopyFuncVal.ts +1 -1
  149. package/src/val/Decimal.ts +1 -1
  150. package/src/val/DisjunctVal.ts +1 -37
  151. package/src/val/ExpectVal.ts +16 -1
  152. package/src/val/FeatureVal.ts +1 -1
  153. package/src/val/FuncBaseVal.ts +9 -3
  154. package/src/val/HideFuncVal.ts +1 -1
  155. package/src/val/IntegerVal.ts +2 -2
  156. package/src/val/JunctionVal.ts +1 -1
  157. package/src/val/KeyFuncVal.ts +1 -1
  158. package/src/val/ListVal.ts +21 -9
  159. package/src/val/LowerFuncVal.ts +1 -1
  160. package/src/val/MapVal.ts +18 -5
  161. package/src/val/MoveFuncVal.ts +1 -1
  162. package/src/val/NilVal.ts +13 -3
  163. package/src/val/NullVal.ts +1 -1
  164. package/src/val/NumberVal.ts +2 -2
  165. package/src/val/OpBaseVal.ts +16 -12
  166. package/src/val/OpenFuncVal.ts +1 -1
  167. package/src/val/PathFuncVal.ts +1 -1
  168. package/src/val/PlusOpVal.ts +1 -1
  169. package/src/val/PrefFuncVal.ts +1 -1
  170. package/src/val/PrefVal.ts +4 -3
  171. package/src/val/RefVal.ts +132 -9
  172. package/src/val/ScalarKindVal.ts +8 -2
  173. package/src/val/ScalarVal.ts +2 -2
  174. package/src/val/StringVal.ts +1 -1
  175. package/src/val/SuperFuncVal.ts +1 -1
  176. package/src/val/TopVal.ts +1 -1
  177. package/src/val/TypeFuncVal.ts +1 -1
  178. package/src/val/UpperFuncVal.ts +1 -1
  179. package/src/val/Val.ts +21 -9
  180. package/src/val/VarVal.ts +1 -1
  181. package/src/val/numcmp.ts +189 -0
  182. package/src/val/numkind.ts +1 -1
@@ -0,0 +1,1348 @@
1
+ "use strict";
2
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
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
+ const type_1 = require("../type");
7
+ const utility_1 = require("../utility");
8
+ const Val_1 = require("./Val");
9
+ const keyorder_1 = require("../keyorder");
10
+ const ConjunctVal_1 = require("./ConjunctVal");
11
+ const top_1 = require("./top");
12
+ const unify_1 = require("../unify");
13
+ const IntegerVal_1 = require("./IntegerVal");
14
+ const err_1 = require("../err");
15
+ const FeatureVal_1 = require("./FeatureVal");
16
+ const ScalarKindVal_1 = require("./ScalarKindVal");
17
+ const numcmp_1 = require("./numcmp");
18
+ // THE PATTERN SUBSET, AND HOW IT IS ENFORCED (G1 phase 2; ADR-003).
19
+ //
20
+ // `re(p)` must mean the same thing in both engines and cost about the
21
+ // same, and the two host engines guarantee neither: TypeScript compiles
22
+ // with JavaScript's backtracking RegExp, Go with RE2 — a different
23
+ // language in a different complexity class, over a different alphabet.
24
+ //
25
+ // The enforcement is NORMALISATION, not refusal (ADR-003). Every
26
+ // construct whose expansion is engine-defined is rewritten here, by
27
+ // this function, into an explicit form that cannot be read two ways;
28
+ // only the rewritten pattern reaches a host engine. The alternative —
29
+ // refusing everything that might differ — was tried first and leaked
30
+ // three times, because its correctness depended on this comment knowing
31
+ // every difference between two large engines.
32
+ //
33
+ // Aontu therefore DEFINES the abbreviations rather than inheriting
34
+ // either host's. The definitions are deliberately the small ASCII ones,
35
+ // because a config value containing U+00A0 is a mistake to catch, not a
36
+ // space to accept silently:
37
+ //
38
+ // \d [0-9] \D [^0-9]
39
+ // \w [0-9A-Za-z_] \W [^0-9A-Za-z_]
40
+ // \s [ \t\n\r\f\v] \S [^ \t\n\r\f\v]
41
+ // . [^\n]
42
+ // \A ^ \z $
43
+ //
44
+ // Neither host agreed with all of these before rewriting: JavaScript's
45
+ // \s also matches U+00A0 and other Unicode spaces, RE2's omits \v, and
46
+ // the two `.` sets differ by \r and the Unicode line separators. After
47
+ // rewriting, both engines see one explicit class and cannot disagree.
48
+ //
49
+ // What is still REFUSED, and why refusal is right for these:
50
+ //
51
+ // 1. Constructs one engine simply lacks — backreferences and
52
+ // lookaround (not in RE2, and not expressible as regular
53
+ // expressions at all), POSIX classes, `\p{...}`, `\x{...}`, `\u`.
54
+ // There is nothing to normalise them TO.
55
+ // 2. `(?` other than `(?:` — named groups are spelled differently
56
+ // (`(?P<n>` in RE2, `(?<n>` in JavaScript) and inline flags change
57
+ // the meaning of everything after them.
58
+ // 3. A quantifier applied to a group containing a quantifier or an
59
+ // alternation. This one is about TIME, not meaning: `(a+)+$`
60
+ // against twenty-nine characters takes 45 SECONDS in JavaScript
61
+ // and 0.065s under RE2, and a regex match is counted by no
62
+ // evaluator budget, so an untrusted schema could otherwise stall
63
+ // the TypeScript evaluator indefinitely (docs/trust.md clause 2).
64
+ // Normalisation cannot fix a complexity difference; only owning the
65
+ // matcher could, which ADR-003 records as the future option.
66
+ //
67
+ // The alphabet is fixed separately, by compiling with the `u` flag:
68
+ // JavaScript otherwise matches UTF-16 code units where RE2 matches code
69
+ // points, so `^.$` accepted U+1D11E in Go and refused it in TypeScript.
70
+ //
71
+ // This function is mirrored statement for statement in go/constraint.go,
72
+ // and `test/spec/files/regex-corpus.txt` pins that the two produce
73
+ // byte-identical output for every pattern in a generated corpus.
74
+ // The normative expansions. These are Aontu's definitions, not either
75
+ // host's; both hosts are rewritten to them.
76
+ const RE_CLASS_DIGIT = '0-9';
77
+ const RE_CLASS_WORD = '0-9A-Za-z_';
78
+ const RE_CLASS_SPACE = ' \\t\\n\\r\\f\\v';
79
+ // Metacharacters that may be escaped to mean themselves, in both
80
+ // engines. `-` is handled separately: it is legal escaped only INSIDE a
81
+ // character class, because RE2 accepts `a\-b` and JavaScript's unicode
82
+ // mode makes it a syntax error.
83
+ const RE_ESCAPE_PUNCT = '\\.+*?()[]{}|^$/';
84
+ // Escapes passed through unchanged: the control characters, the ASCII
85
+ // word boundary, and `\xHH`. Each was probed in both engines.
86
+ const RE_ESCAPE_PASS = 'tnrfv';
87
+ function isHexDigit(c) {
88
+ return null != c && (('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F'));
89
+ }
90
+ // normaliseEscape rewrites one `\<n>` into its engine-neutral form.
91
+ // Returns [emitted, why, extra]: `why` non-empty means refused, and
92
+ // `extra` counts source characters consumed beyond the backslash and n.
93
+ function normaliseEscape(n, src, i, inClass) {
94
+ if (null == n) {
95
+ return ['', 'a trailing backslash', 0];
96
+ }
97
+ if ('1' <= n && n <= '9') {
98
+ return ['', 'a backreference (\\' + n + '): RE2 has no equivalent, and a' +
99
+ ' pattern with one is not a regular expression', 0];
100
+ }
101
+ if ('k' === n) {
102
+ return ['', 'a named backreference (\\k): RE2 has no equivalent', 0];
103
+ }
104
+ if ('u' === n) {
105
+ return ['', 'a \\u escape, which RE2 spells \\x{...}: write the character' +
106
+ ' itself, or \\xHH for a byte', 0];
107
+ }
108
+ if ('p' === n || 'P' === n) {
109
+ return ['', 'a Unicode class (\\' + n + '), which JavaScript reads as a' +
110
+ ' literal "' + n + '" without a flag Aontu does not set', 0];
111
+ }
112
+ if ('Z' === n) {
113
+ return ['', '\\Z, which RE2 does not accept and JavaScript reads as a' +
114
+ ' literal "Z": write $ for end of text', 0];
115
+ }
116
+ if ('x' === n) {
117
+ if ('{' === src[i + 2]) {
118
+ return ['', 'a \\x{...} escape, which JavaScript spells \\u: write the' +
119
+ ' character itself', 0];
120
+ }
121
+ if (!isHexDigit(src[i + 2]) || !isHexDigit(src[i + 3])) {
122
+ return ['', 'an \\x escape without two hex digits', 0];
123
+ }
124
+ return ['\\x' + src[i + 2] + src[i + 3], '', 2];
125
+ }
126
+ // The abbreviations, rewritten to Aontu's definitions. Inside a class
127
+ // the expansion splices without its brackets (`[\dx]` -> `[0-9x]`).
128
+ if ('d' === n || 'w' === n || 's' === n) {
129
+ const set = 'd' === n ? RE_CLASS_DIGIT :
130
+ 'w' === n ? RE_CLASS_WORD : RE_CLASS_SPACE;
131
+ return [inClass ? set : '[' + set + ']', '', 0];
132
+ }
133
+ if ('D' === n || 'W' === n || 'S' === n) {
134
+ if (inClass) {
135
+ // `[^...]` cannot be spliced into an enclosing class: the negation
136
+ // would apply to the whole class rather than this member.
137
+ return ['', 'a negated abbreviation (\\' + n + ') inside a character' +
138
+ ' class, which cannot be expanded in place: write the characters out', 0];
139
+ }
140
+ const set = 'D' === n ? RE_CLASS_DIGIT :
141
+ 'W' === n ? RE_CLASS_WORD : RE_CLASS_SPACE;
142
+ return ['[^' + set + ']', '', 0];
143
+ }
144
+ // Anchors. `\A`/`\z` are RE2 spellings that JavaScript reads as
145
+ // literals, so they are rewritten rather than refused. Inside a class
146
+ // an anchor is meaningless, and `[\b]` is a BACKSPACE in JavaScript.
147
+ if ('A' === n || 'z' === n || 'b' === n || 'B' === n) {
148
+ if (inClass) {
149
+ return ['', '\\' + n + ' inside a character class, where the two' +
150
+ ' engines do not agree what it means', 0];
151
+ }
152
+ return ['A' === n ? '^' : 'z' === n ? '$' : '\\' + n, '', 0];
153
+ }
154
+ if ('-' === n) {
155
+ return inClass ? ['\\-', '', 0] :
156
+ ['', '\\- outside a character class: it is a range separator inside' +
157
+ ' one and a syntax error outside one (write a bare -)', 0];
158
+ }
159
+ if (RE_ESCAPE_PASS.includes(n) || RE_ESCAPE_PUNCT.includes(n)) {
160
+ return ['\\' + n, '', 0];
161
+ }
162
+ return ['', '\\' + n + ', an escape whose meaning the two engines do not' +
163
+ ' share', 0];
164
+ }
165
+ // normaliseRe rewrites a pattern into the engine-neutral subset.
166
+ // Returns [normalised, why]: a non-empty `why` means the pattern is
167
+ // outside the subset and names the construct.
168
+ function normaliseRe(src) {
169
+ let inClass = false;
170
+ const out = [];
171
+ // One frame per open group, recording whether it contains a quantifier
172
+ // or an alternation. A group carrying either may not itself be
173
+ // quantified; containment is transitive, so a frame hands its flags up
174
+ // to its parent when it closes.
175
+ const groups = [];
176
+ const mark = (k) => {
177
+ if (0 < groups.length) {
178
+ groups[groups.length - 1][k] = true;
179
+ }
180
+ };
181
+ for (let i = 0; i < src.length; i++) {
182
+ const c = src[i];
183
+ if ('\\' === c) {
184
+ const [emit, why, extra] = normaliseEscape(src[i + 1], src, i, inClass);
185
+ if ('' !== why) {
186
+ return ['', why];
187
+ }
188
+ out.push(emit);
189
+ i += 1 + extra;
190
+ continue;
191
+ }
192
+ // A POSIX class opener, anywhere: the form lives inside an ordinary
193
+ // class (`[[:alpha:]]`), and refusing it everywhere is one rule
194
+ // rather than two.
195
+ if ('[' === c && ':' === src[i + 1]) {
196
+ return ['', 'a POSIX class ([:...:]), which JavaScript does not have'];
197
+ }
198
+ if (inClass) {
199
+ if (']' === c) {
200
+ inClass = false;
201
+ }
202
+ out.push(c);
203
+ continue;
204
+ }
205
+ if ('[' === c) {
206
+ // `[]` is a never-matching class in JavaScript and a parse error in
207
+ // RE2; `[^]` is the same disagreement one character along.
208
+ const first = '^' === src[i + 1] ? src[i + 2] : src[i + 1];
209
+ if (']' === first) {
210
+ return ['', 'an empty character class, which RE2 refuses'];
211
+ }
212
+ inClass = true;
213
+ out.push(c);
214
+ continue;
215
+ }
216
+ if ('.' === c) {
217
+ out.push('[^\\n]');
218
+ continue;
219
+ }
220
+ if ('(' === c) {
221
+ if ('?' === src[i + 1]) {
222
+ if (':' !== src[i + 2]) {
223
+ return ['', 'a (?...) group other than the non-capturing (?:'];
224
+ }
225
+ out.push('(?:');
226
+ i += 2;
227
+ }
228
+ else {
229
+ out.push(c);
230
+ }
231
+ groups.push({ q: false, alt: false });
232
+ continue;
233
+ }
234
+ if (')' === c) {
235
+ const g = groups.pop();
236
+ if (null == g) {
237
+ return ['', 'an unbalanced group'];
238
+ }
239
+ const nx = src[i + 1];
240
+ const quantified = '*' === nx || '+' === nx || '?' === nx || '{' === nx;
241
+ if (quantified && (g.q || g.alt)) {
242
+ return ['', 'a quantifier applied to a group containing ' +
243
+ (g.q ? 'another quantifier' : 'an alternation') +
244
+ ', which backtracks exponentially in JavaScript'];
245
+ }
246
+ if (g.q)
247
+ mark('q');
248
+ if (g.alt)
249
+ mark('alt');
250
+ out.push(c);
251
+ continue;
252
+ }
253
+ if ('|' === c) {
254
+ mark('alt');
255
+ out.push(c);
256
+ continue;
257
+ }
258
+ if ('*' === c || '+' === c || '?' === c || '{' === c) {
259
+ mark('q');
260
+ out.push(c);
261
+ continue;
262
+ }
263
+ out.push(c);
264
+ }
265
+ if (inClass) {
266
+ return ['', 'an unterminated character class'];
267
+ }
268
+ if (0 < groups.length) {
269
+ return ['', 'an unclosed group'];
270
+ }
271
+ return [out.join(''), ''];
272
+ }
273
+ // True for a scalar Val the algebra can order: a numeric leaf or a
274
+ // string. (Booleans and null have no order and no bounds.)
275
+ function numericLeaf(v) {
276
+ return true === v?.isScalar &&
277
+ (v.isInteger || v.isNumber || v.isBigInteger || v.isBigDecimal) &&
278
+ !(v.isNumber && Number.isNaN(v.peg));
279
+ }
280
+ function stringLeaf(v) {
281
+ return true === v?.isScalar && 'string' === typeof v.peg && v.isString;
282
+ }
283
+ // Scalar identity: leaf AND value, the lattice's own rule (1 and 1.0
284
+ // are different scalars). Exact numeric comparison decides the value
285
+ // half for numeric leaves.
286
+ function sameScalar(a, b) {
287
+ if (numericLeaf(a) && numericLeaf(b)) {
288
+ return (0, numcmp_1.towerRank)(a) === (0, numcmp_1.towerRank)(b) && 0 === (0, numcmp_1.cmpNumeric)(a, b);
289
+ }
290
+ if (stringLeaf(a) && stringLeaf(b)) {
291
+ return a.peg === b.peg;
292
+ }
293
+ return false;
294
+ }
295
+ // Domain-aware value comparison for bounds and neq ordering.
296
+ function cmpVal(domain, a, b) {
297
+ return 'number' === domain ? (0, numcmp_1.cmpNumeric)(a, b) : (0, numcmp_1.cmpCodePoints)(a.peg, b.peg);
298
+ }
299
+ // The numeric leaf marker a concrete scalar carries, for the kind
300
+ // narrowing check.
301
+ function leafMarker(v) {
302
+ return v.isBigDecimal ? ScalarKindVal_1.BigDecimal : v.isBigInteger ? ScalarKindVal_1.BigInteger :
303
+ v.isInteger ? ScalarKindVal_1.Integer : ScalarKindVal_1.Float;
304
+ }
305
+ // Conjunct sort order for an atom that must see the WHOLE value.
306
+ // Every other value sorts below the container default (99999), so such
307
+ // an atom is the LAST term to fold: `a:length(2) a:{x:1} a:{y:2}` must
308
+ // count the MERGED map, and a constraint that folded at 50000 would
309
+ // count `{x:1}` alone and refuse the layering that is the whole point
310
+ // of the language.
311
+ //
312
+ // The order atoms keep the low slot: `min(2) & 1 & 2` may decide as
313
+ // soon as it sees a scalar, because meeting more scalars can only
314
+ // narrow. Meeting more containers GROWS the member set, which is why
315
+ // the two orders differ.
316
+ //
317
+ // Three atoms are late: `length` and `unique` (they count members), and
318
+ // `must` (an evaluate-only check against the finished value).
319
+ const LATE_CJO = 150000;
320
+ function lateAtom(atom) {
321
+ return 'length' === atom || 'unique' === atom || 'must' === atom;
322
+ }
323
+ class ConstraintVal extends FeatureVal_1.FeatureVal {
324
+ constructor(spec, ctx) {
325
+ super({ ...spec, peg: spec.peg ?? [] }, ctx);
326
+ this.isConstraint = true;
327
+ this.cjo = 50000;
328
+ this.neqs = [];
329
+ this.res = [];
330
+ this.uniq = false;
331
+ this.musts = [];
332
+ if (spec.state) {
333
+ this.domain = spec.state.domain;
334
+ this.kind = spec.state.kind;
335
+ this.lo = spec.state.lo;
336
+ this.hi = spec.state.hi;
337
+ this.neqs = spec.state.neqs;
338
+ // A state built by an embedder (or by a per-port test) may predate
339
+ // the pattern field; an absent one means "no patterns", not undefined.
340
+ this.res = spec.state.res ?? [];
341
+ this.count = spec.state.count;
342
+ this.uniq = spec.state.uniq ?? false;
343
+ this.musts = spec.state.musts ?? [];
344
+ this.invalid = spec.state.invalid;
345
+ }
346
+ else if (spec.atom) {
347
+ const args = atomArgs(spec.atom, spec.peg ?? []);
348
+ // An argument that is not yet concrete — a reference, an
349
+ // arithmetic expression, a conjunct of atoms — makes the whole
350
+ // atom PENDING rather than invalid (G1 phase 4). It is resolved
351
+ // in unify, where there is a ctx to resolve through, and the
352
+ // residual is built from the settled arguments. Only a settled
353
+ // argument of the wrong shape is an `invalid-arg`.
354
+ if (args.some((a) => true !== a?.done)) {
355
+ this.pending = { atom: spec.atom, args };
356
+ }
357
+ else {
358
+ this.fromAtom(spec.atom, args);
359
+ }
360
+ }
361
+ if (null != this.count || this.uniq || 0 < this.musts.length ||
362
+ (null != this.pending && lateAtom(this.pending.atom))) {
363
+ this.cjo = LATE_CJO;
364
+ }
365
+ // A residual constraint is stable, like a ScalarKindVal — but a
366
+ // pending atom is not a residual yet, and must be re-entered on
367
+ // later passes until its arguments settle.
368
+ if (null == this.pending) {
369
+ this.dc = type_1.DONE;
370
+ }
371
+ else {
372
+ this.notdone();
373
+ }
374
+ }
375
+ // Normalise one atom call into state. Every argument here is already
376
+ // SETTLED — the constructor routes an unsettled one to `pending` —
377
+ // so a shape this cannot use is a genuine `invalid-arg`, not a
378
+ // not-yet.
379
+ fromAtom(atom, args) {
380
+ // Mark the residual invalid and report so (a plain boolean, so no
381
+ // void value is consumed by the callers' `return` statements).
382
+ const bad = (why) => {
383
+ this.invalid = why;
384
+ return true;
385
+ };
386
+ // `unique()` takes no argument: it is a property of the container,
387
+ // not a comparison against a value. Arity is checked at parse, so a
388
+ // written argument never reaches here.
389
+ if ('unique' === atom) {
390
+ this.uniq = true;
391
+ return;
392
+ }
393
+ // `must(c, msg)` is Band B: an evaluate-only check against the
394
+ // finished value, carrying the author's own message. It is KEPT,
395
+ // never simplified and never consulted for emptiness or
396
+ // subsumption — Band B is opaque by construction, which is exactly
397
+ // what makes it the honest channel for a rule the algebra cannot
398
+ // reason about (docs/reference-language.md, "Band B: `must`").
399
+ if ('must' === atom) {
400
+ if (2 !== args.length) {
401
+ return bad('arg');
402
+ }
403
+ if (!stringLeaf(args[1])) {
404
+ return bad('invalid-arg');
405
+ }
406
+ // A check carrying a nil can never be satisfied, so it is refused
407
+ // as an ARGUMENT rather than left to fail against every value with
408
+ // the author's message attached -- which would blame the data for
409
+ // a mistake in the check. `must([1-x],m)` is the reachable case: a
410
+ // degenerate expression leaves a nil inside the written list.
411
+ if (holdsNil(args[0])) {
412
+ return bad('invalid-arg');
413
+ }
414
+ this.musts = [{ v: args[0], msg: args[1] }];
415
+ return;
416
+ }
417
+ if ('neq' === atom) {
418
+ if (0 === args.length) {
419
+ return bad('arg');
420
+ }
421
+ let domain = undefined;
422
+ for (const a of args) {
423
+ const d = numericLeaf(a) ? 'number' : stringLeaf(a) ? 'string' : undefined;
424
+ if (null == d || (null != domain && d !== domain)) {
425
+ return bad('invalid-arg');
426
+ }
427
+ domain = d;
428
+ }
429
+ this.domain = domain;
430
+ this.neqs = dedupSorted(domain, args);
431
+ return;
432
+ }
433
+ if (1 !== args.length) {
434
+ return bad('arg');
435
+ }
436
+ const a = args[0];
437
+ // `re` is the one atom whose argument is not an ORDER point: a
438
+ // pattern is a membership test, so it takes the string domain
439
+ // outright rather than inferring a domain from the argument's leaf.
440
+ if ('re' === atom) {
441
+ if (!stringLeaf(a)) {
442
+ return bad('invalid-arg');
443
+ }
444
+ const src = a.peg;
445
+ // Normalise BEFORE compiling: the host engines only ever see a
446
+ // pattern that cannot be read two ways (ADR-003).
447
+ const [norm, why] = normaliseRe(src);
448
+ if ('' !== why) {
449
+ this.invalidWhy = why;
450
+ return bad('constraint_pattern');
451
+ }
452
+ let re;
453
+ try {
454
+ // The `u` flag is REQUIRED for parity, not an optimisation.
455
+ // Without it JavaScript matches UTF-16 code units while RE2
456
+ // matches code points, so `re("^.$")` accepted U+1D11E in Go
457
+ // and refused it in TypeScript (and `^..$` did the reverse).
458
+ // With it, `.` and every quantifier count code points in both.
459
+ // It also makes JavaScript refuse the identity escapes this
460
+ // scanner rejects by hand, which is defence in depth rather
461
+ // than a substitute: RE2 accepts some of them, so the scanner
462
+ // is what keeps the two ports agreeing.
463
+ re = new RegExp(norm, 'u');
464
+ }
465
+ catch (e) {
466
+ // The host engine refuses what the subset scanner passed — a
467
+ // malformed quantifier, an unbalanced group. Same refusal under
468
+ // the same code: the author gets one rule, not two. The message
469
+ // is the host's, so it is NOT pinned by a shared row; the code
470
+ // and the located frame are.
471
+ this.invalidWhy = 'not a valid pattern';
472
+ return bad('constraint_pattern');
473
+ }
474
+ this.domain = 'string';
475
+ this.res = [{ v: a, src, norm, re }];
476
+ return;
477
+ }
478
+ // `length` is the other non-ORDER atom: its argument constrains the
479
+ // COUNT, not the value, so it is itself a residual over the integer
480
+ // domain (docs/reference-language.md, "`length` semantics"). It is
481
+ // resolved HERE, at construction, by walking the written argument
482
+ // rather than by unifying it: the func-paren handler builds atoms
483
+ // without an AontuContext, so there is nothing to fold a nested
484
+ // conjunct through. Walking is enough because a length argument is by
485
+ // definition a meet of concrete Band A atoms; anything else (a
486
+ // reference, an expression) is refused rather than deferred, the
487
+ // same discipline min/max apply to their own arguments.
488
+ if ('length' === atom) {
489
+ const arg = countArgState(a);
490
+ if (null == arg) {
491
+ return bad('invalid-arg');
492
+ }
493
+ const inner = meetCount(countBase(), arg);
494
+ this.count = inner;
495
+ // `length(min(5)&max(3))` is unsatisfiable with no peer in sight, so
496
+ // it is refused at composition time like any other empty meet.
497
+ if (stateEmpty(inner)) {
498
+ return bad('constraint');
499
+ }
500
+ return;
501
+ }
502
+ const domain = numericLeaf(a) ? 'number' : stringLeaf(a) ? 'string' : undefined;
503
+ if (null == domain) {
504
+ return bad('invalid-arg');
505
+ }
506
+ this.domain = domain;
507
+ const open = 'above' === atom || 'below' === atom;
508
+ const bound = { v: a, open };
509
+ if ('min' === atom || 'above' === atom) {
510
+ this.lo = bound;
511
+ }
512
+ else {
513
+ this.hi = bound;
514
+ }
515
+ }
516
+ unify(peer, ctx) {
517
+ const te = ctx.explain && (0, utility_1.explainOpen)(ctx, ctx.explain, 'Constraint', this, peer);
518
+ // Every branch of the ladder assigns, so no initialiser: a
519
+ // residual is stable and the ladder is total.
520
+ let out;
521
+ if (null != this.pending) {
522
+ out = this.settle(peer, ctx);
523
+ }
524
+ else if (null != this.invalid) {
525
+ out = (0, err_1.makeNilErr)(ctx, this.invalid, this, undefined, 'constrain', null == this.invalidWhy ? undefined : { reason: this.invalidWhy });
526
+ }
527
+ else if (null == peer || peer.isTop) {
528
+ out = this;
529
+ }
530
+ else if (peer.isNil) {
531
+ out = peer;
532
+ }
533
+ else if (peer.isConstraint) {
534
+ out = this.meetConstraint(peer, ctx);
535
+ }
536
+ else if (peer.isScalarKind) {
537
+ out = this.meetKind(peer, ctx);
538
+ }
539
+ else if (peer.isScalar) {
540
+ out = this.admit(peer, ctx);
541
+ }
542
+ else if (peer.isMap || peer.isList) {
543
+ out = this.admitContainer(peer, ctx);
544
+ }
545
+ /* node:coverage ignore next 12 */
546
+ else {
547
+ // Every other shape: no order, no membership, nothing to count —
548
+ // a conflict of the constraint family. The ladder above is total
549
+ // in practice: every remaining Val kind either sorts BELOW a
550
+ // constraint in a conjunct (conjunct, disjunct, pref, ref) and so
551
+ // drives the meet from its own side, or resolves to a
552
+ // scalar/container before a constraint sees it (func, op, var,
553
+ // expect). The arm is kept because "in practice" depends on the
554
+ // cjo table, and a future value class would land here rather than
555
+ // falling out of unify with no result.
556
+ out = this.fail(ctx, peer);
557
+ }
558
+ ctx.explain && (0, utility_1.explainClose)(te, out);
559
+ return out;
560
+ }
561
+ // Resolve a pending atom's arguments and, once they have all settled,
562
+ // become the residual they describe (G1 phase 4).
563
+ //
564
+ // This is the residuation discipline FuncBaseVal already follows for
565
+ // its own operands: push each unsettled argument one step by unifying
566
+ // it with `top`, and if any is still moving, mark not-done and defer
567
+ // — either as this same pending atom (against a `top` peer) or
568
+ // wrapped with the peer in a conjunct the next pass will re-enter.
569
+ // Nothing is decided from a half-resolved argument, which is what
570
+ // keeps `min($.lo)` from reporting a conflict against a bound that
571
+ // has not arrived yet.
572
+ settle(peer, ctx) {
573
+ const TOP = (0, top_1.top)();
574
+ const pend = this.pending;
575
+ let settled = true;
576
+ const args = [];
577
+ for (const arg of pend.args) {
578
+ let next = arg;
579
+ if (true !== arg?.done) {
580
+ // Charged to the depth budget: this recurses without going
581
+ // through `unite`, so the counter would otherwise stay flat
582
+ // while the stack grows (the rule FuncBaseVal follows).
583
+ next = (0, unify_1.withDepth)(ctx, arg, TOP, () => arg.unify(TOP, ctx));
584
+ }
585
+ settled = settled && true === next?.done;
586
+ args.push(next);
587
+ }
588
+ if (settled) {
589
+ // Build the residual the atom always meant, at this atom's site,
590
+ // then let the ordinary ladder meet it with the peer.
591
+ const built = new ConstraintVal({ peg: args, atom: pend.atom }, ctx);
592
+ built.path = this.path;
593
+ built.site.row = this.site.row;
594
+ built.site.col = this.site.col;
595
+ built.site.url = this.site.url;
596
+ (0, utility_1.propagateMarks)(this, built);
597
+ return built.unify(peer, ctx);
598
+ }
599
+ this.notdone();
600
+ // A fresh pending atom carrying the partially-resolved arguments, so
601
+ // the next pass starts from the progress this one made rather than
602
+ // re-resolving from source.
603
+ const again = new ConstraintVal({ peg: args, atom: pend.atom }, ctx);
604
+ again.path = this.path;
605
+ again.site.row = this.site.row;
606
+ again.site.col = this.site.col;
607
+ again.site.url = this.site.url;
608
+ (0, utility_1.propagateMarks)(this, again);
609
+ if (null == peer || peer.isTop) {
610
+ return again;
611
+ }
612
+ if (peer.isNil) {
613
+ return peer;
614
+ }
615
+ return new ConjunctVal_1.ConjunctVal({ peg: [again, peer] }, ctx);
616
+ }
617
+ // Membership: the peer scalar passes every part of the residual, or
618
+ // the whole meet is a located conflict.
619
+ admit(peer, ctx) {
620
+ // No scalar has members, so a `unique()` residual admits none.
621
+ if (this.uniq) {
622
+ return this.fail(ctx, peer);
623
+ }
624
+ if (!stateAdmits(this, peer)) {
625
+ return this.fail(ctx, peer);
626
+ }
627
+ if (null != this.count) {
628
+ // Only a string among the scalars has a length, and it is counted
629
+ // in CODE POINTS -- not UTF-16 units (this host's native count)
630
+ // and not bytes (Go's). Iterating a string yields code points.
631
+ if (!stringLeaf(peer)) {
632
+ return this.fail(ctx, peer);
633
+ }
634
+ if (!stateAdmits(this.count, countVal([...peer.peg].length))) {
635
+ return this.fail(ctx, peer);
636
+ }
637
+ }
638
+ const bad = this.checkMusts(peer, ctx);
639
+ if (null != bad) {
640
+ return bad;
641
+ }
642
+ return peer;
643
+ }
644
+ // Band B, applied to a finished value. Each check is a plain
645
+ // unification against a CLONE of the peer: `must` reports, it never
646
+ // contributes: whatever the check would have added to the value is
647
+ // discarded, and `peer` is returned untouched by the callers.
648
+ //
649
+ // The trial runs in an isolated collect context so a failing check
650
+ // leaves nothing on the caller's error list — only the located nil
651
+ // this returns, carrying the author's own message.
652
+ checkMusts(peer, ctx) {
653
+ for (const m of this.musts) {
654
+ const trial = ctx.clone({ err: [], collect: true });
655
+ const got = (0, unify_1.unite)(trial, m.v.clone(trial), peer.clone(trial), 'must');
656
+ if (true === got?.isNil || 0 < trial.err.length) {
657
+ return (0, err_1.makeNilErr)(ctx, 'must', this, peer, undefined, {
658
+ message: m.msg.peg,
659
+ expected: m.v.canon,
660
+ actual: peer.canon,
661
+ });
662
+ }
663
+ }
664
+ return undefined;
665
+ }
666
+ // Membership for a container peer. Only the SIZING atoms have anything
667
+ // to say about a map or a list; every other atom is scalar-domain and
668
+ // refuses one.
669
+ //
670
+ // The members that count are the members that GENERATE
671
+ // (docs/reference-language.md, "`length` semantics"), and rather than
672
+ // mirror generation's filter — type/hide marks, optional keys that
673
+ // drop, empty optional values — this asks generation itself, in an
674
+ // isolated collect context so nothing leaks into the caller's errors.
675
+ // A mirror would be a second copy of a filter that has already grown
676
+ // subtle, free to drift from it; asking is correct by construction.
677
+ admitContainer(peer, ctx) {
678
+ // A scalar-domain residual has no reading over a container.
679
+ if (null != this.domain) {
680
+ return this.fail(ctx, peer);
681
+ }
682
+ // Not yet settled: the container, or an optional child, may still
683
+ // resolve, so the member set is not final. Defer rather than decide
684
+ // — the same discipline OpBaseVal follows for a non-concrete operand.
685
+ if (!containerSettled(peer)) {
686
+ this.dc = 0;
687
+ return new ConjunctVal_1.ConjunctVal({ peg: [this, peer] }, ctx);
688
+ }
689
+ const bad = this.checkMusts(peer, ctx);
690
+ if (null != bad) {
691
+ return bad;
692
+ }
693
+ if (!this.uniq && null == this.count) {
694
+ return peer;
695
+ }
696
+ const members = emittedMembers(peer, ctx);
697
+ if (null == members) {
698
+ // The container cannot generate at all; its own error is the one
699
+ // worth reporting, so pass it through untouched.
700
+ return peer;
701
+ }
702
+ if (null != this.count && !stateAdmits(this.count, countVal(members.length))) {
703
+ return this.fail(ctx, peer);
704
+ }
705
+ if (this.uniq) {
706
+ // Members compare by CANONICAL FORM, which reduces to scalar
707
+ // identity for scalars (so [1, 1.0] is distinct) and gives
708
+ // structural equality for container members without a second
709
+ // rule. A generated value could not express the first: `1` and
710
+ // `1.0` generate the same JSON number.
711
+ const seen = new Set();
712
+ for (const m of members) {
713
+ const key = m.canon;
714
+ if (seen.has(key)) {
715
+ return this.fail(ctx, peer);
716
+ }
717
+ seen.add(key);
718
+ }
719
+ }
720
+ return peer;
721
+ }
722
+ // Meet with a kind: `number` (or `string` on the string domain) is
723
+ // already implied by an ORDER atom's argument; a numeric LEAF narrows
724
+ // the residual; anything else has an empty intersection with the
725
+ // constraint's domain.
726
+ //
727
+ // A sizing residual (`length`, `unique`) has no domain of its own -- a
728
+ // count says nothing about what is counted -- so a kind here SETS one
729
+ // rather than merely agreeing with it: `string & length(3)` is a
730
+ // three-character string, and `number & length(3)` is empty because a
731
+ // number has no length (stateEmpty decides that, not this).
732
+ meetKind(peer, ctx) {
733
+ const marker = peer.peg;
734
+ const merged = this.cloneState();
735
+ if (Number === marker || String === marker) {
736
+ const d = Number === marker ? 'number' : 'string';
737
+ if (d === this.domain) {
738
+ return this;
739
+ }
740
+ if (null != this.domain) {
741
+ return this.fail(ctx, peer);
742
+ }
743
+ merged.domain = d;
744
+ return this.finish(merged, ctx, peer);
745
+ }
746
+ const isLeaf = ScalarKindVal_1.Integer === marker || ScalarKindVal_1.Float === marker ||
747
+ ScalarKindVal_1.BigInteger === marker || ScalarKindVal_1.BigDecimal === marker;
748
+ if (!isLeaf || 'string' === this.domain) {
749
+ return this.fail(ctx, peer);
750
+ }
751
+ if (null != this.kind && this.kind !== marker) {
752
+ return this.fail(ctx, peer);
753
+ }
754
+ merged.domain = 'number';
755
+ merged.kind = marker;
756
+ return this.finish(merged, ctx, peer);
757
+ }
758
+ // Meet with another residual: interval intersection, exclusion
759
+ // union, kind union — then the eager emptiness rules.
760
+ meetConstraint(peer, ctx) {
761
+ if (null != peer.invalid) {
762
+ return (0, err_1.makeNilErr)(ctx, peer.invalid, peer, undefined, 'constrain', null == peer.invalidWhy ? undefined : { reason: peer.invalidWhy });
763
+ }
764
+ if (null != this.domain && null != peer.domain && this.domain !== peer.domain) {
765
+ return this.fail(ctx, peer);
766
+ }
767
+ if (null != this.kind && null != peer.kind && this.kind !== peer.kind) {
768
+ return this.fail(ctx, peer);
769
+ }
770
+ const d = (this.domain ?? peer.domain);
771
+ const merged = this.cloneState();
772
+ merged.domain = d;
773
+ merged.kind = this.kind ?? peer.kind;
774
+ merged.lo = tighter(d, this.lo, peer.lo, true);
775
+ merged.hi = tighter(d, this.hi, peer.hi, false);
776
+ merged.neqs = dedupSorted(d, [...this.neqs, ...peer.neqs]);
777
+ merged.res = dedupSortedRes([...this.res, ...peer.res]);
778
+ // `length(c1) & length(c2)` is `length(c1 & c2)`: the count atom reuses
779
+ // numeric algebra recursively, over the counts rather than the
780
+ // values.
781
+ merged.count = null == this.count ? peer.count :
782
+ null == peer.count ? this.count : meetCount(this.count, peer.count);
783
+ // `unique()` is idempotent: two of them are one.
784
+ merged.uniq = this.uniq || peer.uniq;
785
+ // Band B checks accumulate in written order and are never merged,
786
+ // deduplicated or reordered: each carries its own author message,
787
+ // and two checks with the same shape may still say different things.
788
+ merged.musts = [...this.musts, ...peer.musts];
789
+ return this.finish(merged, ctx, peer);
790
+ }
791
+ // Build the merged residual, applying the eager emptiness rules.
792
+ finish(state, ctx, peer) {
793
+ if (stateEmpty(state)) {
794
+ return this.fail(ctx, peer);
795
+ }
796
+ const out = new ConstraintVal({ peg: [], state }, ctx);
797
+ out.path = this.path;
798
+ out.site.row = this.site.row;
799
+ out.site.col = this.site.col;
800
+ out.site.url = this.site.url;
801
+ // Marks ratchet across a meet (the propagateMarks rule): a fresh
802
+ // residual must carry both operands' type/hide marks, or a
803
+ // `type(min(0)) & max(10)` merge would silently unmark the field
804
+ // (the Go fold re-ratchets from its terms; this is the TS twin).
805
+ (0, utility_1.propagateMarks)(this, out);
806
+ (0, utility_1.propagateMarks)(peer, out);
807
+ return out;
808
+ }
809
+ fail(ctx, peer) {
810
+ return (0, err_1.makeNilErr)(ctx, 'constraint', this, peer, undefined, {
811
+ expected: this.canon,
812
+ actual: peer?.canon,
813
+ });
814
+ }
815
+ cloneState() {
816
+ return {
817
+ domain: this.domain,
818
+ kind: this.kind,
819
+ lo: this.lo,
820
+ hi: this.hi,
821
+ neqs: [...this.neqs],
822
+ res: [...this.res],
823
+ count: this.count,
824
+ uniq: this.uniq,
825
+ musts: [...this.musts],
826
+ invalid: this.invalid,
827
+ };
828
+ }
829
+ clone(ctx, spec) {
830
+ let out = super.clone(ctx, {
831
+ ...(spec || {}),
832
+ peg: this.peg,
833
+ });
834
+ out.domain = this.domain;
835
+ out.kind = this.kind;
836
+ out.lo = this.lo;
837
+ out.hi = this.hi;
838
+ out.neqs = [...this.neqs];
839
+ out.res = [...this.res];
840
+ out.count = this.count;
841
+ out.uniq = this.uniq;
842
+ out.musts = [...this.musts];
843
+ out.pending = this.pending;
844
+ out.cjo = this.cjo;
845
+ out.invalid = this.invalid;
846
+ out.invalidWhy = this.invalidWhy;
847
+ return out;
848
+ }
849
+ // The fixed canonical atom order: kind, lower, upper, neq (arguments
850
+ // sorted), re, length, unique. No spaces; reparses to a conjunct that
851
+ // normalises back to this exact residual.
852
+ get canon() {
853
+ if (null != this.pending) {
854
+ // A pending atom has no residual yet, so canon renders the call as
855
+ // written — the same shape FuncBaseVal renders while deferring.
856
+ return this.pending.atom +
857
+ '(' + this.pending.args.map((a) => a.canon).join(',') + ')';
858
+ }
859
+ return canonState(this);
860
+ }
861
+ same(peer) {
862
+ return true === peer?.isConstraint && this.canon === peer.canon;
863
+ }
864
+ }
865
+ exports.ConstraintVal = ConstraintVal;
866
+ // The tighter of two like-direction bounds: the higher lower bound (or
867
+ // lower upper bound); on the same point the OPEN bound wins, and on a
868
+ // full tie the tower-lowest endpoint spelling survives.
869
+ function tighter(domain, a, b, lower) {
870
+ if (null == a)
871
+ return b;
872
+ if (null == b)
873
+ return a;
874
+ const c = cmpVal(domain, a.v, b.v);
875
+ if (0 !== c) {
876
+ return (lower ? 0 < c : c < 0) ? a : b;
877
+ }
878
+ if (a.open !== b.open) {
879
+ return a.open ? a : b;
880
+ }
881
+ if ('number' === domain && (0, numcmp_1.towerRank)(b.v) < (0, numcmp_1.towerRank)(a.v)) {
882
+ return b;
883
+ }
884
+ return a;
885
+ }
886
+ // Sort excluded scalars for canon (numeric: by point then tower rank;
887
+ // string: code-point order) and drop identity duplicates.
888
+ function dedupSorted(domain, neqs) {
889
+ const sorted = [...neqs].sort((a, b) => {
890
+ const c = cmpVal(domain, a, b);
891
+ if (0 !== c)
892
+ return c;
893
+ return 'number' === domain ? (0, numcmp_1.towerRank)(a) - (0, numcmp_1.towerRank)(b) : 0;
894
+ });
895
+ const out = [];
896
+ for (const n of sorted) {
897
+ if (0 === out.length || !sameScalar(out[out.length - 1], n)) {
898
+ out.push(n);
899
+ }
900
+ }
901
+ return out;
902
+ }
903
+ // Is this value, or anything inside it, a nil? A written argument that
904
+ // holds one can never be satisfied, so the atom refuses it as an
905
+ // argument rather than reporting a mystery failure against every peer.
906
+ function holdsNil(v) {
907
+ if (null == v || true !== v.isVal) {
908
+ return false;
909
+ }
910
+ if (true === v.isNil) {
911
+ return true;
912
+ }
913
+ const peg = v.peg;
914
+ if (Array.isArray(peg)) {
915
+ return peg.some((c) => holdsNil(c));
916
+ }
917
+ if (null != peg && 'object' === typeof peg) {
918
+ for (const k in peg) {
919
+ if (holdsNil(peg[k])) {
920
+ return true;
921
+ }
922
+ }
923
+ }
924
+ return false;
925
+ }
926
+ // The written arguments of an atom, flattened.
927
+ //
928
+ // A multi-argument call arrives from the func-paren grammar as ONE
929
+ // entry holding the comma group, and `neq([3,1,2])` means the same as
930
+ // `neq(3,1,2)`. The group is always a ListVal by the time it reaches
931
+ // here -- the func-paren handler rawToVals every argument (issue #49) --
932
+ // so there is no raw-array case to unwrap. Flattening happens before
933
+ // the settled check, because an unsettled member hiding inside the
934
+ // group would otherwise make the atom look ready.
935
+ function atomArgs(atom, args) {
936
+ if (('neq' === atom || 'must' === atom) && 1 === args.length &&
937
+ true === args[0]?.isList) {
938
+ return args[0].peg;
939
+ }
940
+ return args;
941
+ }
942
+ // The canonical rendering of a residual, in the fixed atom order:
943
+ // kind, lower bound, upper bound, neq, re, length, unique. Taken over the
944
+ // STATE rather than the Val because `length`'s argument is a residual too,
945
+ // and renders by exactly the same rules.
946
+ function canonState(s) {
947
+ const parts = [];
948
+ if (null != s.kind) {
949
+ parts.push(s.kind.name.toLowerCase());
950
+ }
951
+ // An ORDER atom's argument implies the domain, so it is not spelled
952
+ // out. A SIZING residual carries no order, and there `string` is the
953
+ // only thing saying what is being sized -- drop it and the reparse
954
+ // would admit lists and maps too.
955
+ else if ('string' === s.domain &&
956
+ null == s.lo && null == s.hi && 0 === s.neqs.length && 0 === s.res.length) {
957
+ parts.push('string');
958
+ }
959
+ if (null != s.lo) {
960
+ parts.push((s.lo.open ? 'above(' : 'min(') + s.lo.v.canon + ')');
961
+ }
962
+ if (null != s.hi) {
963
+ parts.push((s.hi.open ? 'below(' : 'max(') + s.hi.v.canon + ')');
964
+ }
965
+ if (0 < s.neqs.length) {
966
+ parts.push('neq(' + s.neqs.map((n) => n.canon).join(',') + ')');
967
+ }
968
+ for (const r of s.res) {
969
+ parts.push('re(' + r.v.canon + ')');
970
+ }
971
+ if (null != s.count) {
972
+ // Rendered UNABRIDGED, implied parts and all: `length(3)` canonicalises
973
+ // to `length(integer&min(3)&max(3))` because that IS the residual the
974
+ // count must satisfy, and canon is a normal form (G6 hashes it),
975
+ // not a pretty-printer. Abbreviating would mean a second set of
976
+ // rules for when the implied `integer & min(0)` may be dropped.
977
+ parts.push('length(' + canonState(s.count) + ')');
978
+ }
979
+ if (s.uniq) {
980
+ parts.push('unique()');
981
+ }
982
+ for (const m of s.musts) {
983
+ parts.push('must(' + m.v.canon + ',' + m.msg.canon + ')');
984
+ }
985
+ if (0 === parts.length) {
986
+ // Raw invalid atom: render the call so the error frame shows it.
987
+ return 'constraint()';
988
+ }
989
+ return parts.join('&');
990
+ }
991
+ // Does this residual's ORDER and MEMBERSHIP part admit the scalar? The
992
+ // sizing atoms are deliberately not consulted: `admit` applies them to
993
+ // the peer's length, and the count check applies this same function to
994
+ // the count.
995
+ function stateAdmits(s, peer) {
996
+ const domainOf = numericLeaf(peer) ? 'number' :
997
+ stringLeaf(peer) ? 'string' : undefined;
998
+ if (null == s.domain) {
999
+ // A sizing residual has no domain, and admits any scalar the sizing
1000
+ // atoms can then rule on. Booleans and null are not among them:
1001
+ // they have no order, no length and no members.
1002
+ if (null == domainOf) {
1003
+ return false;
1004
+ }
1005
+ return true;
1006
+ }
1007
+ if (domainOf !== s.domain) {
1008
+ return false;
1009
+ }
1010
+ if (null != s.kind && leafMarker(peer) !== s.kind) {
1011
+ return false;
1012
+ }
1013
+ const d = s.domain;
1014
+ if (null != s.lo) {
1015
+ const c = cmpVal(d, peer, s.lo.v);
1016
+ if (c < 0 || (0 === c && s.lo.open)) {
1017
+ return false;
1018
+ }
1019
+ }
1020
+ if (null != s.hi) {
1021
+ const c = cmpVal(d, peer, s.hi.v);
1022
+ if (c > 0 || (0 === c && s.hi.open)) {
1023
+ return false;
1024
+ }
1025
+ }
1026
+ for (const n of s.neqs) {
1027
+ if (sameScalar(peer, n)) {
1028
+ return false;
1029
+ }
1030
+ }
1031
+ // Every accumulated pattern must match: the meet of two `re` atoms
1032
+ // is conjunction, and matching is UNANCHORED in both engines
1033
+ // (JS RegExp.test, Go regexp.MatchString), so `re("el")` admits
1034
+ // "hello". Anchor with ^ and $ to mean the whole string.
1035
+ for (const r of s.res) {
1036
+ if (!r.re.test(peer.peg)) {
1037
+ return false;
1038
+ }
1039
+ }
1040
+ return true;
1041
+ }
1042
+ // The eager emptiness rules. Each is EXACT -- it reports empty only
1043
+ // where no value could satisfy the residual -- so the algebra stays
1044
+ // sound; the incompleteness it accepts is documented in
1045
+ // docs/reference-language.md, "Emptiness".
1046
+ function stateEmpty(s) {
1047
+ // Two disagreeing kind narrowings inside a length argument, recorded by
1048
+ // meetCount because that meet has no ctx to fail through.
1049
+ if (s.clash) {
1050
+ return true;
1051
+ }
1052
+ const d = s.domain;
1053
+ // Empty interval.
1054
+ if (null != s.lo && null != s.hi) {
1055
+ const c = cmpVal(d, s.hi.v, s.lo.v);
1056
+ if (c < 0 || (0 === c && (s.lo.open || s.hi.open))) {
1057
+ return true;
1058
+ }
1059
+ }
1060
+ const integral = ScalarKindVal_1.Integer === s.kind || ScalarKindVal_1.BigInteger === s.kind;
1061
+ // Integral gap: an integer-narrowed interval containing no whole
1062
+ // number is empty (integer & above(1) & below(2)).
1063
+ if (integral && null != s.lo && null != s.hi) {
1064
+ const lo = (0, numcmp_1.scaledOfNumeric)(s.lo.v);
1065
+ const hi = (0, numcmp_1.scaledOfNumeric)(s.hi.v);
1066
+ if (!lo.inf && !hi.inf) {
1067
+ // Smallest admissible integer above/at the lower bound.
1068
+ let n = (0, numcmp_1.scaledFloor)(lo);
1069
+ if (!(0, numcmp_1.scaledIsIntegral)(lo) || s.lo.open) {
1070
+ n += 1n;
1071
+ }
1072
+ // Largest admissible integer below/at the upper bound.
1073
+ let m = (0, numcmp_1.scaledFloor)(hi);
1074
+ if (s.hi.open && (0, numcmp_1.scaledIsIntegral)(hi)) {
1075
+ m -= 1n;
1076
+ }
1077
+ if (m < n) {
1078
+ return true;
1079
+ }
1080
+ }
1081
+ }
1082
+ // Point deletion under a narrowed leaf: a closed point interval
1083
+ // whose single value of the narrowed leaf is excluded is empty
1084
+ // (integer & min(3) & max(3) & neq(3)). Without a narrowing the
1085
+ // point survives in the other leaves.
1086
+ if (null != s.kind && null != s.lo && null != s.hi &&
1087
+ !s.lo.open && !s.hi.open &&
1088
+ 0 === cmpVal(d, s.lo.v, s.hi.v)) {
1089
+ for (const n of s.neqs) {
1090
+ if (leafMarker(n) === s.kind && 0 === (0, numcmp_1.cmpNumeric)(n, s.lo.v)) {
1091
+ return true;
1092
+ }
1093
+ }
1094
+ }
1095
+ // Sizing over the number domain: a number has neither a length nor
1096
+ // members, so `integer & length(3)` and `min(2) & unique()` admit
1097
+ // nothing. Uniqueness over the string domain is empty for the same
1098
+ // reason -- a string's members are not values the algebra compares.
1099
+ if ('number' === d && (null != s.count || s.uniq)) {
1100
+ return true;
1101
+ }
1102
+ if ('string' === d && s.uniq) {
1103
+ return true;
1104
+ }
1105
+ // An empty count residual makes the whole thing empty: no container
1106
+ // and no string has a length no integer can take.
1107
+ if (null != s.count && stateEmpty(s.count)) {
1108
+ return true;
1109
+ }
1110
+ return false;
1111
+ }
1112
+ // The base every `length` argument meets: a count is a non-negative
1113
+ // integer, whatever else the argument says (docs/reference-language.md,
1114
+ // "`length` semantics" -- `length(c)` is empty iff `c & integer & min(0)`
1115
+ // is).
1116
+ function countBase() {
1117
+ return {
1118
+ domain: 'number',
1119
+ kind: ScalarKindVal_1.Integer,
1120
+ lo: { v: countVal(0), open: false },
1121
+ neqs: [],
1122
+ res: [],
1123
+ musts: [],
1124
+ uniq: false,
1125
+ };
1126
+ }
1127
+ // A count as a Val, so the count residual can be applied by exactly the
1128
+ // same membership function as any other numeric residual.
1129
+ function countVal(n) {
1130
+ return new IntegerVal_1.IntegerVal({ peg: n });
1131
+ }
1132
+ // The pure meet of two count residuals. Both are number-domain and
1133
+ // carry no pattern or sizing atom of their own, so the merge is the
1134
+ // interval/exclusion part alone. A kind disagreement becomes a `clash`
1135
+ // rather than an error: this meet runs at construction, where there is
1136
+ // no AontuContext to raise through, and an empty residual carries the
1137
+ // same news to `unify`.
1138
+ function meetCount(a, b) {
1139
+ return {
1140
+ domain: 'number',
1141
+ kind: a.kind ?? b.kind,
1142
+ lo: tighter('number', a.lo, b.lo, true),
1143
+ hi: tighter('number', a.hi, b.hi, false),
1144
+ neqs: dedupSorted('number', [...a.neqs, ...b.neqs]),
1145
+ res: [],
1146
+ musts: [],
1147
+ uniq: false,
1148
+ clash: true === a.clash || true === b.clash ||
1149
+ (null != a.kind && null != b.kind && a.kind !== b.kind),
1150
+ };
1151
+ }
1152
+ // Read a WRITTEN `length` argument as a count residual, or undefined when
1153
+ // it is not one. Accepted: an integer literal (an exact count), a
1154
+ // numeric kind, a Band A residual over the number domain, and any
1155
+ // conjunct of those. A conjunct never reaches here any more: an
1156
+ // unsettled argument is held as `pending` and folded before the count
1157
+ // reads it (G1 phase 4), so `length(min(2)&max(5))` arrives as the
1158
+ // single residual it folds to.
1159
+ //
1160
+ // Anything else is refused rather than deferred. A reference or an
1161
+ // expression would have to residuate, and the sizing atoms do not
1162
+ // residuate on their ARGUMENT -- only on the peer whose members are
1163
+ // still settling.
1164
+ function countArgState(arg) {
1165
+ if (numericLeaf(arg)) {
1166
+ return {
1167
+ domain: 'number',
1168
+ lo: { v: arg, open: false },
1169
+ hi: { v: arg, open: false },
1170
+ neqs: [], res: [], musts: [], uniq: false,
1171
+ };
1172
+ }
1173
+ if (true === arg?.isConstraint) {
1174
+ const c = arg;
1175
+ // A pattern, a sizing atom or a string bound inside a count is not
1176
+ // a count constraint at all, and neither is a broken one.
1177
+ if (null != c.invalid || 0 < c.res.length || c.uniq || null != c.count ||
1178
+ 'number' !== c.domain) {
1179
+ return undefined;
1180
+ }
1181
+ return {
1182
+ domain: 'number',
1183
+ kind: c.kind,
1184
+ lo: c.lo,
1185
+ hi: c.hi,
1186
+ neqs: [...c.neqs],
1187
+ res: [], musts: [], uniq: false,
1188
+ };
1189
+ }
1190
+ if (true === arg?.isScalarKind) {
1191
+ const marker = arg.peg;
1192
+ if (Number === marker) {
1193
+ return { domain: 'number', neqs: [], res: [], musts: [], uniq: false };
1194
+ }
1195
+ if (ScalarKindVal_1.Integer === marker || ScalarKindVal_1.Float === marker ||
1196
+ ScalarKindVal_1.BigInteger === marker || ScalarKindVal_1.BigDecimal === marker) {
1197
+ return { domain: 'number', kind: marker, neqs: [], res: [], musts: [], uniq: false };
1198
+ }
1199
+ return undefined;
1200
+ }
1201
+ return undefined;
1202
+ }
1203
+ // A container is SETTLED when it and every child have converged. Until
1204
+ // then the member set can still change — an optional key whose value is
1205
+ // a still-resolving reference may yet generate — so a sizing atom must
1206
+ // defer rather than decide (docs/reference-language.md, "`length`
1207
+ // semantics"). Note that an optional holding a settled-but-ungenerable
1208
+ // value, `{x:1,y?:number}`, IS settled: the map converges on the first
1209
+ // pass and `y` is simply never emitted.
1210
+ function containerSettled(bag) {
1211
+ // The bag's OWN done-counter is enough: BagVal.unify sets it from the
1212
+ // AND over its children, so an unsettled child already leaves the bag
1213
+ // unsettled. Walking the children again would be a second, drifting
1214
+ // copy of that rule.
1215
+ return true === bag.done;
1216
+ }
1217
+ // The child kinds `BagVal.gen` will attempt to generate. Anything else
1218
+ // is residue: dropped when the key is optional, and a bag-level error
1219
+ // otherwise.
1220
+ function genable(child) {
1221
+ return true === child.isScalar || true === child.isMap ||
1222
+ true === child.isList || true === child.isPref ||
1223
+ true === child.isRef || true === child.isDisjunct ||
1224
+ true === child.isNil;
1225
+ }
1226
+ // The children a bag would EMIT, mirroring the selection in
1227
+ // `BagVal.gen` — type/hide marks skipped, non-generable residue and
1228
+ // values that generate nothing dropped, optional keys dropped when
1229
+ // they generate empty.
1230
+ //
1231
+ // It returns the member VALS rather than their generated values,
1232
+ // because uniqueness compares canon and a generated value cannot
1233
+ // express that distinction: `1` and `1.0` generate the same JSON number
1234
+ // and canon differently. Counting uses the same list, so both sizing
1235
+ // atoms see exactly one definition of "member".
1236
+ //
1237
+ // Returns undefined when a REQUIRED child is residue: the container's
1238
+ // own `gen` raises there, and that error is the one worth reporting.
1239
+ function emittedMembers(bag, ctx) {
1240
+ const out = [];
1241
+ let entries = (0, utility_1.items)(bag.peg);
1242
+ if (bag.isMap) {
1243
+ // Code-point order, because the two ports disagree on raw key order
1244
+ // — JavaScript hoists integer-like keys, Go keeps insertion order —
1245
+ // and a duplicate report must name the same pair in both.
1246
+ entries = entries
1247
+ .slice()
1248
+ .sort((a, b) => (0, keyorder_1.cmpCodePoint)(String(a[0]), String(b[0])));
1249
+ }
1250
+ for (const item of entries) {
1251
+ const key = item[0];
1252
+ const child = item[1];
1253
+ if (child.mark.type || child.mark.hide) {
1254
+ continue;
1255
+ }
1256
+ const optional = bag.optionalKeys.includes('' + key);
1257
+ if (!genable(child)) {
1258
+ if (optional) {
1259
+ continue;
1260
+ }
1261
+ return undefined;
1262
+ }
1263
+ // Generation decides in an isolated collect context, so an
1264
+ // unresolved inner value neither raises here nor pollutes the
1265
+ // caller's errors — the same isolation BagVal.gen uses for an
1266
+ // optional child.
1267
+ const cval = child.gen(ctx.clone({ err: [], collect: true }));
1268
+ if (undefined === cval || (optional && (0, Val_1.empty)(cval))) {
1269
+ continue;
1270
+ }
1271
+ out.push(child);
1272
+ }
1273
+ return out;
1274
+ }
1275
+ // Sort accumulated patterns by source in code-point order and drop
1276
+ // exact duplicates. Patterns are NEVER simplified or compared for
1277
+ // containment: deciding `re("a")` subsumes `re("a|b")` is regex
1278
+ // containment, which the algebra deliberately does not do (emptiness
1279
+ // stays approximate — sound, incomplete). Two spellings of one language
1280
+ // therefore both survive, and both are tested.
1281
+ function dedupSortedRes(res) {
1282
+ const sorted = [...res].sort((a, b) => (0, numcmp_1.cmpCodePoints)(a.src, b.src));
1283
+ const out = [];
1284
+ for (const r of sorted) {
1285
+ if (0 === out.length || out[out.length - 1].src !== r.src) {
1286
+ out.push(r);
1287
+ }
1288
+ }
1289
+ return out;
1290
+ }
1291
+ // The atom classes registered in the parser's funcMap: each is a
1292
+ // ConstraintVal that knows its atom name. Constructed by the
1293
+ // func-paren handler as `new funcval({peg: args})`.
1294
+ class MinConstraintVal extends ConstraintVal {
1295
+ constructor(spec, ctx) {
1296
+ super({ ...spec, atom: 'min' }, ctx);
1297
+ }
1298
+ }
1299
+ exports.MinConstraintVal = MinConstraintVal;
1300
+ class MaxConstraintVal extends ConstraintVal {
1301
+ constructor(spec, ctx) {
1302
+ super({ ...spec, atom: 'max' }, ctx);
1303
+ }
1304
+ }
1305
+ exports.MaxConstraintVal = MaxConstraintVal;
1306
+ class AboveConstraintVal extends ConstraintVal {
1307
+ constructor(spec, ctx) {
1308
+ super({ ...spec, atom: 'above' }, ctx);
1309
+ }
1310
+ }
1311
+ exports.AboveConstraintVal = AboveConstraintVal;
1312
+ class BelowConstraintVal extends ConstraintVal {
1313
+ constructor(spec, ctx) {
1314
+ super({ ...spec, atom: 'below' }, ctx);
1315
+ }
1316
+ }
1317
+ exports.BelowConstraintVal = BelowConstraintVal;
1318
+ class NeqConstraintVal extends ConstraintVal {
1319
+ constructor(spec, ctx) {
1320
+ super({ ...spec, atom: 'neq' }, ctx);
1321
+ }
1322
+ }
1323
+ exports.NeqConstraintVal = NeqConstraintVal;
1324
+ class ReConstraintVal extends ConstraintVal {
1325
+ constructor(spec, ctx) {
1326
+ super({ ...spec, atom: 're' }, ctx);
1327
+ }
1328
+ }
1329
+ exports.ReConstraintVal = ReConstraintVal;
1330
+ class MustConstraintVal extends ConstraintVal {
1331
+ constructor(spec, ctx) {
1332
+ super({ ...spec, atom: 'must' }, ctx);
1333
+ }
1334
+ }
1335
+ exports.MustConstraintVal = MustConstraintVal;
1336
+ class LengthConstraintVal extends ConstraintVal {
1337
+ constructor(spec, ctx) {
1338
+ super({ ...spec, atom: 'length' }, ctx);
1339
+ }
1340
+ }
1341
+ exports.LengthConstraintVal = LengthConstraintVal;
1342
+ class UniqueConstraintVal extends ConstraintVal {
1343
+ constructor(spec, ctx) {
1344
+ super({ ...spec, atom: 'unique' }, ctx);
1345
+ }
1346
+ } /* node:coverage ignore next 19 */
1347
+ exports.UniqueConstraintVal = UniqueConstraintVal;
1348
+ //# sourceMappingURL=ConstraintVal.js.map