tatara_lisp/ast.rs
1//! S-expression AST.
2
3use crate::error::{SexpShape, SexpWitness, StructuralKind, UnquoteForm};
4use std::fmt;
5// Bring `fmt::Write` into scope so `f.write_char(Self::LIST_OPEN)` at
6// [`fmt::Display for Sexp`] resolves — the outer-structural delimiter
7// arms of the `Self::Nil` / `Self::List` rendering routes through
8// [`Sexp::LIST_OPEN`] / [`Sexp::LIST_CLOSE`] via `fmt::Write::write_char`
9// rather than through the format! machinery (`write!(f, "{}", '(')`)
10// so the (typed delimiter, formatter emission) pair binds directly on
11// the closed-set outer-algebra without a per-char formatting round-trip.
12// The `as _` alias imports the trait's methods without adding a name to
13// the ast.rs namespace, matching the Rust idiom for extension-trait
14// import hygiene.
15use std::fmt::Write as _;
16use std::hash::{Hash, Hasher};
17
18/// Compile-time contract verifier — panics at const evaluation time if
19/// any two entries of `arr` alias byte-for-byte.
20///
21/// Lifts the "each family-wide `[char; N]` on the substrate's reader-
22/// boundary vocabulary is pairwise distinct" invariant from a runtime
23/// pin-per-array (`sexp_non_whitespace_bare_atom_terminators_are_
24/// pairwise_distinct`, `sexp_list_delimiters_pairwise_distinct`,
25/// `sexp_comment_delimiters_pairwise_distinct`, `quote_form_leads_
26/// pairwise_distinct`, `atom_self_escape_table_pairwise_distinct`,
27/// `atom_escape_sources_pairwise_distinct`, `atom_escape_decoded_
28/// pairwise_distinct`) into a COMPILE-TIME theorem the substrate
29/// carries at every array declaration via a co-located `const _: () =
30/// assert_char_array_pairwise_distinct(&Self::FOO_ARRAY);` witness.
31///
32/// The invariant is load-bearing for every consumer that pattern-
33/// matches the array's entries as DISJOINT arms — the reader's outer-
34/// dispatch cascade in `crate::reader::tokenize` matches on the four
35/// [`Sexp::LIST_OPEN`] / [`Sexp::LIST_CLOSE`] / [`Atom::STR_DELIMITER`]
36/// / [`Sexp::COMMENT_LEAD`] arms (a duplicate here would break the
37/// `match c { … }` exhaustiveness); [`Atom::decode_str_escape`]'s
38/// escape table matches on the FIVE [`Atom::ESCAPE_SOURCES`] arms
39/// (a duplicate here would silently shadow the second arm);
40/// [`QuoteForm::from_lead_char`] matches on the THREE [`QuoteForm::LEADS`]
41/// arms (a duplicate here would break the three-way outer-dispatch);
42/// and every future family-wide `[char; N]` on the substrate that
43/// participates in a `match`-arm partition benefits from the SAME
44/// compile-time guarantee via one `const _` line.
45///
46/// Pre-lift each array carried its pairwise-distinctness contract at
47/// ONE runtime test (`cargo test`-triggered): a regression that
48/// silently collided two entries would compile cleanly and fail only
49/// at test run. Post-lift the contract binds at `cargo check` time —
50/// the const-eval panic surfaces the collision at COMPILE time, one
51/// invocation stage earlier, catching regressions on `cargo build` /
52/// `cargo clippy` runs that skip the test suite.
53///
54/// Adding a new family-wide `[char; N]` array to the substrate: pair
55/// the declaration with `const _: () = assert_char_array_pairwise_
56/// distinct(&Self::FOO_ARRAY);` co-located immediately after the
57/// array's declaration and the distinctness contract is enforced at
58/// compile time. The rustc-forced arity `[char; N]` composes with
59/// this const-eval sweep so BOTH cardinality AND injectivity are
60/// compile-time theorems on the SAME array.
61///
62/// Runtime callability: the function is a normal `pub const fn`, so
63/// callers CAN also invoke it at runtime — e.g. a REPL / LSP tokenizer
64/// that constructs a `[char; N]` at runtime and wants to verify
65/// pairwise distinctness before consuming it — and the panic surfaces
66/// normally in that path (pinned by `assert_char_array_pairwise_
67/// distinct_panics_at_runtime_on_collision`).
68///
69/// Theory grounding:
70/// - THEORY.md §V.1 — knowable platform; the family-wide distinctness
71/// contract becomes a TYPE-LEVEL theorem the substrate carries per
72/// array declaration rather than a runtime test the developer must
73/// remember to write per array.
74/// - THEORY.md §VI.1 — generation over composition; the const-eval
75/// sweep IS the generative shape. Every new closed-set char array
76/// adds ONE `const _` line to get the distinctness theorem rather
77/// than re-deriving a `HashSet::insert` loop test per array.
78/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
79/// distinctness proof at declaration site AND the outer-dispatch
80/// match-arm exhaustiveness the consumer relies on regenerate
81/// through the SAME `const _` witness.
82pub const fn assert_char_array_pairwise_distinct<const N: usize>(arr: &[char; N]) {
83 let mut i = 0;
84 while i < N {
85 let mut j = i + 1;
86 while j < N {
87 if arr[i] as u32 == arr[j] as u32 {
88 panic!(
89 "assert_char_array_pairwise_distinct: family-wide \
90 char array carries a duplicate entry across two \
91 positions — the substrate's pairwise-distinctness \
92 contract on the array is broken; every consumer \
93 that pattern-matches the array's entries as \
94 DISJOINT arms (reader outer-dispatch, escape-table \
95 decode, quote-family lead dispatch) relies on this \
96 invariant"
97 );
98 }
99 j += 1;
100 }
101 i += 1;
102 }
103}
104
105// Compile-time pairwise-distinctness witnesses — one `const _: () =
106// assert_char_array_pairwise_distinct(&…)` per family-wide `[char; N]`
107// char array on the substrate's reader-boundary vocabulary. Each
108// invocation is const-evaluated at `cargo check` time; a regression
109// that silently collides two entries fails the build rather than the
110// test suite. Sibling to the runtime `_pairwise_distinct` tests at
111// `ast.rs`'s tests module — the two enforce the same theorem at TWO
112// stages of the toolchain, so a build that skips tests still catches
113// the regression here, and a build that runs tests catches it a
114// second time as a safety net if the const-eval sweep is ever
115// silently dropped.
116const _: () = assert_char_array_pairwise_distinct(&Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS);
117const _: () = assert_char_array_pairwise_distinct(&Sexp::LIST_DELIMITERS);
118const _: () = assert_char_array_pairwise_distinct(&Sexp::COMMENT_DELIMITERS);
119const _: () = assert_char_array_pairwise_distinct(&QuoteForm::LEADS);
120const _: () = assert_char_array_pairwise_distinct(&Atom::SELF_ESCAPE_TABLE);
121const _: () = assert_char_array_pairwise_distinct(&Atom::ESCAPE_SOURCES);
122const _: () = assert_char_array_pairwise_distinct(&Atom::ESCAPE_DECODED);
123
124/// Compile-time contract verifier — panics at const evaluation time if
125/// any entry of `arr` carries a Unicode scalar value outside the
126/// seven-bit ASCII range (`> 0x7F`).
127///
128/// Element-type row-dual peer to [`assert_str_array_all_ascii`] on the
129/// (element-type) axis of the (element-type × contract-shape) matrix at
130/// the (per-entry, ASCII) column: where the (`&'static str`) sibling
131/// closes the ASCII-BYTE-RANGE per-entry corner on the substrate's
132/// closed-set outer algebras' family-wide `[&'static str; N]` label
133/// vocabularies, this (`char`) sibling closes the SAME per-entry
134/// contract-shape corner on the substrate's reader-boundary `[char; N]`
135/// scalar vocabularies. The two helpers close the (element-type ∈
136/// {char, `&'static str`} × contract-shape ∈ {ASCII}) 2-corner row of
137/// the per-entry ASCII column at ONE peer const-fn helper per element-
138/// type. Contract-orthogonal peer to
139/// [`assert_char_array_pairwise_distinct`] on the (INJECTIVITY, ASCII)
140/// axis of the (per-entry × set-level) column on the SAME (`char`) row:
141/// where the pairwise-distinctness sibling binds SET-LEVEL `∀ i ≠ j :
142/// arr[i] ≠ arr[j]`, this ASCII sibling binds PER-ENTRY `∀ i : (arr[i]
143/// as u32) <= 0x7F` — the two together give every `[char; N]` sub-
144/// vocabulary on the substrate BOTH set-level injectivity AND per-
145/// entry byte-range containment at compile time. Note the (per-entry,
146/// NONEMPTY) corner from the (`&'static str`) row is intentionally
147/// absent here: a `char` is a single Unicode scalar value and carries
148/// no length dimension, so the NONEMPTY per-entry gate degenerates to
149/// a vacuous tautology on the (`char`) row and needs no peer.
150///
151/// The invariant is load-bearing for every consumer that ships an
152/// array entry through a downstream surface whose canonical form is
153/// seven-bit-clean — the reader's `matches!(c, LIST_OPEN | LIST_CLOSE
154/// | …)` outer-dispatch on [`Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS`]
155/// entries; the tokenizer's byte-level scan on [`Sexp::LIST_DELIMITERS`]
156/// / [`Sexp::COMMENT_DELIMITERS`] / [`QuoteForm::LEADS`] entries; the
157/// atom-payload escape-decode dispatch on [`Atom::SELF_ESCAPE_TABLE`] /
158/// [`Atom::ESCAPE_SOURCES`] / [`Atom::ESCAPE_DECODED`] entries. Every
159/// consumer treats each entry as a single seven-bit-clean ASCII scalar;
160/// a regression that silently re-inlined `LIST_OPEN = '('` (fullwidth
161/// U+FF08) or `QUOTE_LEAD = '‘'` (U+2018) — lookalike scalars that
162/// would parse as a Rust `char` literal but ship a multi-byte UTF-8
163/// sequence at every reader-boundary byte comparison — fails at `cargo
164/// check` BEFORE any test scheduler runs.
165///
166/// Adding a new family-wide `[char; N]` reader-boundary scalar
167/// vocabulary whose canonical spelling is seven-bit-clean: pair the
168/// declaration with `const _: () = assert_char_array_all_ascii
169/// (&Self::FOO_ARRAY);` co-located after the array's declaration and
170/// the ASCII-SCALAR-RANGE contract binds at compile time. The rustc-
171/// forced arity `[char; N]` composes with this const-eval sweep so
172/// BOTH cardinality AND per-entry ASCII are compile-time theorems on
173/// the SAME array declaration.
174///
175/// Runtime callability: the function is a normal `pub const fn`, so
176/// callers CAN also invoke it at runtime — pinned by
177/// `assert_char_array_all_ascii_panics_at_runtime_on_head_non_ascii` /
178/// `_interior_non_ascii` / `_tail_non_ascii` and
179/// `assert_char_array_all_ascii_panic_message_names_the_helper_and_axis`.
180/// The panic site carries the `"CHAR-NON-ASCII-SCALAR"` axis-
181/// provenance string chosen DISTINCT from every sibling helper's axis
182/// vocabulary (`"duplicate"` on the pairwise-distinct sibling;
183/// `"CHAR-SUBSET-VIOLATION"` on the within-finite-set sibling;
184/// `"CHAR-DISJOINTNESS-VIOLATION"` on the arrays-disjoint sibling;
185/// `"STR-NON-ASCII-ENTRY"` on the (`&'static str`) row-dual ASCII
186/// sibling) so a diagnostic that names the failed axis routes
187/// UNAMBIGUOUSLY to THIS specific (`char`)-row ASCII helper. The
188/// `"CHAR-"` prefix disambiguates from the (`&'static str`) row-dual
189/// ASCII sibling; the shared `"-NON-ASCII-"` infix lets callers grep
190/// any row's ASCII sibling by `"NON-ASCII"` alone.
191///
192/// Theory grounding:
193/// - THEORY.md §V.1 — knowable platform; the family-wide per-entry
194/// ASCII-scalar-range contract on the substrate's reader-boundary
195/// `char` vocabulary becomes a TYPE-LEVEL theorem the substrate
196/// carries per array declaration rather than a runtime test the
197/// developer must remember to write per scalar constant.
198/// - THEORY.md §II.1 invariant 1 — typed entry; a reader-boundary
199/// scalar's `char` projection IS the entry-point discriminator into
200/// the tokenizer's outer dispatch, and a non-ASCII scalar in that
201/// projection silently escapes the byte-level assumption every
202/// downstream parser encodes into its own byte-position arithmetic.
203/// - THEORY.md §III — the typescape; the (element-type × contract-
204/// shape) matrix now carries the ASCII per-entry corner on TWO rows
205/// ({`char`, `&'static str`}) at ONE peer const-fn helper per row.
206/// The (element-type ∈ {char, `&'static str`}) × (contract-shape ∈
207/// {per-entry ASCII}) 2-corner row of the per-entry ASCII column is
208/// now closed at TWO peer const-fn helpers.
209/// - THEORY.md §VI.1 — generation over composition; the const-eval
210/// scalar-range sweep IS the generative shape. Every new closed-set
211/// reader-boundary scalar array adds ONE `const _` line to get the
212/// ASCII theorem rather than re-deriving a per-array runtime iterator
213/// sweep at each call site.
214///
215/// Frontier inspiration: Lean 4's `List.all` unfolded to `∀ i : arr[i]
216/// ∈ ascii` at the concrete `[char; N]` monomorphic realisation, where
217/// `ascii := { c : Char // c.toNat ≤ 0x7F }`. The (`char`, `&'static
218/// str`) row-dual pair mirrors Lean's element-polymorphic `List α`
219/// realised at the two concrete element-type instantiations the
220/// substrate closes at compile time.
221pub const fn assert_char_array_all_ascii<const N: usize>(arr: &[char; N]) {
222 let mut i = 0;
223 while i < N {
224 if arr[i] as u32 > 0x7F {
225 panic!(
226 "assert_char_array_all_ascii: CHAR-NON-ASCII-SCALAR — \
227 the family-wide char array carries an entry with a \
228 Unicode scalar value outside the seven-bit ASCII \
229 range (> 0x7F) at some position — the substrate's \
230 ASCII-SCALAR-RANGE contract on the array is broken; \
231 every consumer that ships an entry through a seven-\
232 bit-clean reader-boundary surface (the reader's \
233 `matches!(c, LIST_OPEN | ...)` outer-dispatch on \
234 NON_WHITESPACE_BARE_ATOM_TERMINATORS entries; the \
235 tokenizer's byte-level scan on LIST_DELIMITERS / \
236 COMMENT_DELIMITERS / QuoteForm::LEADS entries; the \
237 atom-payload escape-decode dispatch on SELF_ESCAPE_\
238 TABLE / ESCAPE_SOURCES / ESCAPE_DECODED entries) \
239 treats each entry as a single seven-bit-clean ASCII \
240 scalar — a non-ASCII scalar silently invites lookalike-\
241 char collisions (fullwidth U+FF08 `(` vs ASCII U+0028 \
242 `(`) and multi-byte UTF-8 drift that byte-position \
243 arithmetic cannot detect. Fix at the ARRAY-DECLARATION \
244 site by re-inlining the offending scalar constant to \
245 its seven-bit-clean canonical spelling"
246 );
247 }
248 i += 1;
249 }
250}
251
252// Compile-time ASCII-SCALAR-RANGE witnesses — one `const _: () =
253// assert_char_array_all_ascii(&…)` per family-wide `[char; N]` char
254// array on the substrate's reader-boundary vocabulary. Each invocation
255// is const-evaluated at `cargo check` time; a regression that silently
256// re-inlined one scalar constant to a lookalike non-ASCII scalar
257// (fullwidth U+FF08 `(`, curly-quote U+2018 `‘`, etc.) fails the
258// build rather than deferring to a per-consumer byte-parse
259// misbehavior at runtime. Sibling to the pairwise-distinctness
260// witnesses above — those pin SET-LEVEL INJECTIVITY on each array,
261// these pin the strictly-orthogonal PER-ENTRY byte-range gate on the
262// SAME arrays. The two contracts compose orthogonally on every reader-
263// boundary `[char; N]` scalar vocabulary. The seven arrays covered
264// here mirror the seven arrays already pinned by the
265// `_pairwise_distinct` witnesses above — the (per-entry × set-level)
266// coverage matrix on the (`char`) row of this file now holds at TWO
267// corners {INJECTIVITY (set-level), ASCII (per-entry)} for the seven
268// reader-boundary arrays declared here.
269const _: () = assert_char_array_all_ascii(&Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS);
270const _: () = assert_char_array_all_ascii(&Sexp::LIST_DELIMITERS);
271const _: () = assert_char_array_all_ascii(&Sexp::COMMENT_DELIMITERS);
272const _: () = assert_char_array_all_ascii(&QuoteForm::LEADS);
273const _: () = assert_char_array_all_ascii(&Atom::SELF_ESCAPE_TABLE);
274const _: () = assert_char_array_all_ascii(&Atom::ESCAPE_SOURCES);
275const _: () = assert_char_array_all_ascii(&Atom::ESCAPE_DECODED);
276
277/// Compile-time contract verifier — panics at const evaluation time if
278/// the distinct-values set of `arr` is not a subset of the distinct-
279/// values set of `set` on the substrate's reader-boundary `char`
280/// vocabulary. Binds ONE conjunct clause: CHAR-SUBSET-VIOLATION —
281/// every entry in `arr` MUST also appear as an entry in `set`.
282///
283/// Element-type column-dual peer to
284/// [`assert_u8_array_within_u8_finite_set`] on the (element-type) axis:
285/// where the `u8` sibling closes the SUBSET-EMBEDDING corner on the
286/// outer-`Sexp` cache-key `u8` vocabulary, this `char` sibling closes
287/// the SAME corner on the reader-boundary `char` vocabulary. The two
288/// helpers close the SUBSET-EMBEDDING × non-contiguous-target-set
289/// corner of the (element-type × contract-shape) matrix at ONE
290/// primitive per element-type row rather than at a per-embedding
291/// runtime iterator sweep per call site. Contract-strength peer to
292/// (the future-lift) `assert_char_array_covers_char_finite_set` on the
293/// (equality-vs-subset) axis: this helper binds ONLY arr ⊆ set (the
294/// SUBSET-VIOLATION arm read in isolation without the SET-CHAR-MISSING
295/// full-coverage clause) — a strictly WEAKER contract for arrays that
296/// intentionally cover only a PROPER SUBSET of the target partition.
297///
298/// SET-side well-formedness delegation: [`assert_char_array_pairwise_distinct`]
299/// is called on `set` at the top of the helper as the SET-side well-
300/// formedness gate. Placed FIRST so drift on the CALLER'S TARGET-SET
301/// SPEC (e.g. `['a', 'a', 'b']` fed as `set`) routes to the SET-side
302/// well-formedness axis (via the sibling's `"assert_char_array_
303/// pairwise_distinct"` panic-name prefix) rather than to a downstream
304/// CHAR-SUBSET-VIOLATION symptom on `arr`. Delegates to the SAME
305/// helper the ARRAY-side pairwise-distinctness pin uses rather than to
306/// a bespoke `assert_char_finite_set_pairwise_distinct` alias because
307/// the char row does NOT yet carry a sibling `_covers_char_finite_set`
308/// / `_permutes_char_finite_set` helper that would benefit from a
309/// distinct SET-side axis-provenance vocabulary across three call
310/// sites — the (u8) row's separate `assert_u8_finite_set_pairwise_distinct`
311/// alias exists exactly because the finite-set-family compound helpers
312/// (`_within_u8_finite_set`, `_covers_u8_finite_set`,
313/// `_permutes_u8_finite_set`) all delegate to it. When the char row
314/// grows its second finite-set-family compound helper, a future run
315/// lifts the SET-side alias then; today the delegation reuses the
316/// ARRAY-side helper directly. A well-formed `set` passes this arm as
317/// a no-op — const-eval-elidable at rustc-time on the substrate call
318/// site.
319///
320/// The invariant is load-bearing for three intentionally-closed
321/// SUBSET-embedding relations on the substrate's reader-boundary
322/// `char` vocabulary that pre-lift lived only as prose in the
323/// [`Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS`] docstring's
324/// four-category composition rule (LIST_DELIMITERS + QuoteForm::LEADS +
325/// {STR_DELIMITER} + {COMMENT_LEAD}):
326///
327/// 1. [`Sexp::LIST_DELIMITERS`] (`[char; 2]` = `[LIST_OPEN,
328/// LIST_CLOSE]` — the outer-structural paired-delimiter arm-set)
329/// MUST be a SUBSET of
330/// [`Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS`] (`[char; 7]`
331/// — the reader outer-dispatch category-leading char ALL array).
332/// A drift that dropped `LIST_OPEN` (or `LIST_CLOSE`) from
333/// `NON_WHITESPACE_BARE_ATOM_TERMINATORS` while it stayed in
334/// `LIST_DELIMITERS` (or vice versa: a drift that re-inlined
335/// `LIST_DELIMITERS` to a fresh char not in the terminator ALL
336/// array) would silently break the four-category composition
337/// rule. Post-lift the SUBSET embedding binds at rustc time.
338///
339/// 2. [`QuoteForm::LEADS`] (`[char; 3]` = `[QUOTE_LEAD,
340/// QUASIQUOTE_LEAD, UNQUOTE_LEAD]` — the quote-family lead-char
341/// sub-vocabulary) MUST be a SUBSET of
342/// [`Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS`]. Same argument
343/// as above on the three quote-lead category of the four-category
344/// composition rule.
345///
346/// 3. [`Atom::SELF_ESCAPE_TABLE`] (`[char; 2]` = `[STR_DELIMITER,
347/// STR_ESCAPE_LEAD]` — the pattern-EQUALS-value sub-vocabulary of
348/// the escape arm-set) MUST be a SUBSET of
349/// [`Atom::ESCAPE_SOURCES`] (`[char; 5]` — the SOURCE-column
350/// SPAN of all five non-passthrough escape arms). A drift that
351/// re-inlined a SELF entry to a fresh char not in the SOURCE-
352/// column SPAN would silently violate the composition law
353/// `ESCAPE_SOURCES == [NAMED[0].0, NAMED[1].0, NAMED[2].0,
354/// SELF[0], SELF[1]]` at the SELF-slot suffix. Post-lift the
355/// SUBSET embedding binds at rustc time.
356///
357/// Every future family-wide `[char; N]` typed-subset carving on the
358/// substrate's reader-boundary vocabulary participates in the SAME
359/// compile-time guarantee via one `const _` line.
360///
361/// Adding a new family-wide `[char; N]` subset-embedded array to the
362/// substrate: pair the declaration with `const _: () =
363/// assert_char_array_within_char_finite_set::<N, M>(&Self::FOO_ARRAY,
364/// &Other::SUPERSET_ARRAY);` co-located after the array's declaration
365/// and the SUBSET contract binds at compile time. The rustc-forced
366/// arities `[char; N]` and `[char; M]` compose with this const-eval
367/// sweep so BOTH cardinality-pair AND every-entry-in-superset are
368/// compile-time theorems on the SAME (subset, superset) char-array
369/// pair.
370///
371/// Runtime callability: the function is a normal `pub const fn`, so
372/// callers CAN also invoke it at runtime — pinned by
373/// `assert_char_array_within_char_finite_set_panics_at_runtime_on_out_of_set_entry`
374/// and
375/// `assert_char_array_within_char_finite_set_panic_message_names_the_helper_and_char_subset_violation_axis`.
376/// The panic site carries the `"CHAR-SUBSET-VIOLATION"` axis-
377/// provenance string chosen DISTINCT from every sibling helper's axis
378/// vocabulary (`"duplicate"` on the ARRAY-side pairwise-distinct
379/// sibling; `"SUBSET-VIOLATION"` on the (u8) finite-set SUBSET-only
380/// sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8) range SUBSET-only
381/// sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the (u8) covers-
382/// finite-set sibling; `"OUT-OF-RANGE"` / `"MISSING"` on the (u8)
383/// covers-inclusive-range sibling; `"ARITY-MISMATCH"` on both (u8)
384/// `_permutes_*` compound helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on
385/// the (u8) SET-side well-formedness sibling) so a diagnostic that
386/// names the failed axis routes UNAMBIGUOUSLY to THIS specific char
387/// SUBSET-embedding helper. The `"CHAR-"` prefix disambiguates from
388/// the (u8) SUBSET-only helper's bare `"SUBSET-VIOLATION"`; the shared
389/// `"-VIOLATION"` suffix lets callers grep either SUBSET-embedding
390/// sibling by `"VIOLATION"` alone or route to the specific element-
391/// type row by the disambiguator prefix.
392///
393/// Theory grounding:
394/// - THEORY.md §V.1 — knowable platform; the family-wide subset-
395/// embedding contract on the reader-boundary `char` vocabulary
396/// becomes a TYPE-LEVEL theorem the substrate carries per (subset,
397/// superset) array pair rather than a runtime test the developer
398/// must remember to write per embedding. The three witness sites
399/// above (`LIST_DELIMITERS ⊆ NON_WHITESPACE_BARE_ATOM_TERMINATORS`,
400/// `QuoteForm::LEADS ⊆ NON_WHITESPACE_BARE_ATOM_TERMINATORS`,
401/// `SELF_ESCAPE_TABLE ⊆ ESCAPE_SOURCES`) previously lived only as
402/// prose in their parent arrays' composition-rule docstrings.
403/// - THEORY.md §III — the typescape; the (element-type × contract-
404/// shape) matrix now carries the SUBSET-EMBEDDING corner at BOTH
405/// the `u8` row and the `char` row, closing the element-type column
406/// on the SUBSET-corner face at two peer const-fn helpers rather
407/// than at one primitive with a runtime cast per element-type.
408/// - THEORY.md §VI.1 — generation over composition; the const-eval
409/// set-membership sweep IS the generative shape. Every new closed-
410/// set `char` sub-vocabulary array whose distinct-value set is an
411/// intentional SUBSET of another substrate `char` array adds ONE
412/// `const _` line to get the subset-embedding theorem rather than
413/// re-deriving a per-embedding runtime iterator sweep at each call
414/// site.
415/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
416/// SUBSET-embedding proof at declaration site AND the reader outer-
417/// dispatch cascade's four-category exhaustiveness contract (which
418/// assumes LIST_DELIMITERS + QuoteForm::LEADS + STR_DELIMITER +
419/// COMMENT_LEAD partition the NON_WHITESPACE_BARE_ATOM_TERMINATORS
420/// ALL array) regenerate through the SAME `const _` witnesses at
421/// the ARRAY level.
422///
423/// Frontier inspiration: Lean 4's `Finset.instHasSubsetFinset :
424/// (⊆) : Finset α → Finset α → Prop` as a decidable relation on
425/// `Finset α` combined with `Finset.subset_iff` unfolding the relation
426/// to per-element membership — the substrate primitive here embeds
427/// the same subset relation as a rustc const-eval-time proof
428/// obligation at every `assert_char_array_within_char_finite_set`
429/// call site rather than as a Lean tactic invocation deferred to
430/// `elab_command`. The element-type generalization from `u8` to `char`
431/// mirrors Lean's polymorphism over the `α` type parameter on
432/// `Finset`; the substrate binds the two concrete instantiations
433/// (`u8` at [`assert_u8_array_within_u8_finite_set`], `char` here)
434/// as separate `pub const fn`s because Rust's const-generic system
435/// does not yet admit polymorphism over element type through a
436/// custom trait bound at const-eval time. Each element-type row is a
437/// monomorphic realisation of the SAME subset-embedding contract
438/// shape.
439pub const fn assert_char_array_within_char_finite_set<const N: usize, const M: usize>(
440 arr: &[char; N],
441 set: &[char; M],
442) {
443 // Delegate target-set well-formedness to the sibling ARRAY-side
444 // pairwise-distinctness helper FIRST. Placed BEFORE the CHAR-
445 // SUBSET-VIOLATION sweep below because a malformed `set` (e.g.
446 // `['a', 'a', 'b']`) is not a well-formed finite set of
447 // cardinality `M` and silently mis-verifies the intended subset
448 // contract on any `arr` embedded in the DISTINCT-value subset.
449 // Routes drift on the CALLER'S TARGET-SET SPEC to the SET-side
450 // well-formedness axis (via the sibling's own panic-name prefix)
451 // rather than to a downstream CHAR-SUBSET-VIOLATION symptom on
452 // `arr`. A well-formed `set` passes this arm as a no-op — the
453 // sweep is const-eval-elidable and costs zero at rustc-time on
454 // the substrate call sites.
455 assert_char_array_pairwise_distinct(set);
456 let mut i = 0;
457 while i < N {
458 let mut j = 0;
459 let mut found = false;
460 while j < M {
461 if arr[i] as u32 == set[j] as u32 {
462 found = true;
463 break;
464 }
465 j += 1;
466 }
467 if !found {
468 panic!(
469 "assert_char_array_within_char_finite_set: CHAR-\
470 SUBSET-VIOLATION — the family-wide char array `arr` \
471 carries an entry at some position whose char is NOT \
472 a member of the target finite superset partition \
473 `set`. The substrate's SUBSET-EMBEDDING contract on \
474 the array is broken; every consumer that expects the \
475 array's distinct-value set to be a subset of the \
476 target finite partition (`Sexp::LIST_DELIMITERS ⊂ \
477 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS` on the \
478 outer-structural paired-delimiter axis of the reader \
479 outer-dispatch terminator ALL array; \
480 `QuoteForm::LEADS ⊂ \
481 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS` on the \
482 quote-family lead-char axis of the SAME terminator \
483 ALL array; `Atom::SELF_ESCAPE_TABLE ⊂ \
484 Atom::ESCAPE_SOURCES` on the pattern-EQUALS-value \
485 sub-vocabulary axis of the escape-arm SOURCE-column \
486 SPAN; any future typed-subset embedding on the \
487 substrate's reader-boundary char algebras) relies on \
488 every array entry staying within the target \
489 superset. Fix at the ARRAY-DECLARATION site (the \
490 `arr` under verification, NOT the `set` argument \
491 specifying the target superset) by dropping the \
492 offending entry OR by extending `set` to cover it — \
493 the choice depends on whether the drift is an \
494 unintended overshoot outside the parent superset or \
495 an intentional extension of the superset vocabulary"
496 );
497 }
498 i += 1;
499 }
500}
501
502// Compile-time SUBSET-embedding witnesses — the THREE family-wide
503// `[char; N]` intentionally-closed PROPER-SUBSET carvings on the
504// substrate's reader-boundary vocabulary whose distinct-value set is
505// a subset of another family-wide `[char; M]` array's distinct-value
506// set. Pre-lift the three subset relations lived only as prose in the
507// parent arrays' composition-rule docstrings (the four-category
508// composition rule on `NON_WHITESPACE_BARE_ATOM_TERMINATORS`, the
509// pattern-EQUALS-value sub-vocabulary partition on `ESCAPE_SOURCES`).
510// Post-lift the three ARRAY-LEVEL subset embeddings bind at rustc
511// time — a regression that re-inlined either the SUBSET or the
512// SUPERSET side of any relation to a fresh char breaking the subset
513// containment (e.g. dropping `Sexp::LIST_OPEN` from
514// `NON_WHITESPACE_BARE_ATOM_TERMINATORS` while it stayed in
515// `LIST_DELIMITERS`; drifting `QuoteForm::QUOTE_LEAD` to a fresh
516// char not in the terminator ALL array; re-shuffling `ESCAPE_SOURCES`
517// to swap its SELF-slot suffix bytes with unrelated NAMED-slot bytes)
518// fails at `cargo check` BEFORE any test scheduler runs. Sibling to
519// the pairwise-distinctness witnesses above — those pin INJECTIVITY
520// on each individual array, these pin SUBSET containment across
521// PAIRS of arrays.
522const _: () = assert_char_array_within_char_finite_set::<2, 7>(
523 &Sexp::LIST_DELIMITERS,
524 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
525);
526const _: () = assert_char_array_within_char_finite_set::<3, 7>(
527 &QuoteForm::LEADS,
528 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
529);
530const _: () = assert_char_array_within_char_finite_set::<2, 5>(
531 &Atom::SELF_ESCAPE_TABLE,
532 &Atom::ESCAPE_SOURCES,
533);
534
535/// Compile-time contract verifier — panics at const evaluation time if
536/// the distinct-values sets of `a` and `b` share any char on the
537/// substrate's reader-boundary `char` vocabulary. Binds ONE conjunct
538/// clause: CHAR-DISJOINTNESS-VIOLATION — no entry in `a` may appear as
539/// an entry in `b` (symmetric across the two array arguments).
540///
541/// Contract-orthogonal peer to [`assert_char_array_within_char_finite_set`]
542/// on the (subset-vs-disjointness) axis of the (contract-shape) axis:
543/// where the SUBSET-EMBEDDING helper binds `arr ⊆ set` at compile time
544/// on the (char) row, this DISJOINTNESS helper binds `a ∩ b = ∅` at
545/// compile time on the SAME element-type row. Together the two helpers
546/// close the (subset, disjointness) 2-corner face on the (char)
547/// contract-shape column at ONE primitive per corner rather than at a
548/// per-pair runtime iterator sweep per call site. The disjointness
549/// relation is SYMMETRIC (unlike the SUBSET-EMBEDDING relation, which
550/// distinguishes `arr` from `set`) so the two arguments carry NO axis-
551/// provenance role split — either drift site (a char in `a` that
552/// aliases a char in `b`, OR a char in `b` that aliases a char in `a`)
553/// surfaces at the CHAR-DISJOINTNESS-VIOLATION panic with the OFFENDING
554/// arm named by BOTH position indices (`a[i]` AND `b[j]`) rather than
555/// by only one side of the pair.
556///
557/// SYMMETRY IN THE NESTED SWEEP: the inner `while j < M` sweep visits
558/// every position of `b` per outer `i` and panics at the FIRST cross-
559/// array collision (`a[i] == b[j]`). Because the relation is symmetric
560/// and the two-loop sweep visits every `(i, j) ∈ [0, N) × [0, M)` pair,
561/// swapping `a` and `b` at the call site produces the SAME verdict —
562/// the helper does NOT gratuitously depend on argument order. A future
563/// call site that intends to name a SPECIFIC drift side can still route
564/// its own preferred first-mention by picking the argument order that
565/// serves its diagnostic story; the helper itself carries no such
566/// preference.
567///
568/// The invariant is load-bearing for the reader's outer-dispatch
569/// cascade at [`crate::reader::tokenize`]: the SIX outer-dispatch
570/// category-leading char families the tokenizer specialises on
571/// ([`Sexp::LIST_OPEN`] / [`Sexp::LIST_CLOSE`] arm; every
572/// [`QuoteForm::lead_char`] arm; [`Atom::STR_DELIMITER`] arm;
573/// [`Sexp::COMMENT_LEAD`] arm; whitespace arm) MUST route to DISJOINT
574/// arms — otherwise a shared byte would silently reclassify at the
575/// same-position outer dispatch (e.g. a `(` that aliased `,` would
576/// silently promote a list-open byte through the quote-family arm, or
577/// vice versa). Post-lift the disjointness of the substrate's pinned
578/// arm-set pairs binds at rustc time via one `const _` line per pair.
579///
580/// The invariant is ALSO load-bearing for the Str-payload escape-arm
581/// dispatch at [`Atom::decode_str_escape`]: the FIVE non-passthrough
582/// escape arms partition into the [`Atom::SELF_ESCAPE_TABLE`] (2 rows,
583/// pattern-EQUALS-value) and [`Atom::NAMED_ESCAPE_TABLE`] (3 rows,
584/// pattern-DISTINCT-from-value) sub-vocabularies whose entries MUST
585/// remain disjoint at the CHAR level from every other reader-boundary
586/// sub-vocabulary — otherwise a Str-escape byte would silently
587/// promote through the outer-dispatch cascade instead of staying inside
588/// the Str-payload boundary.
589///
590/// Pre-lift the substrate carried these disjointness relations at ONE
591/// runtime test per pair (`sexp_comment_delimiters_disjoint_from_list_delimiters`,
592/// `sexp_list_delimiters_disjoint_from_comment_lead`, etc.); post-lift
593/// the ARRAY-LEVEL disjointness binds at rustc time via one `const _`
594/// line per pair. A regression that silently drifted `Sexp::LIST_OPEN`
595/// to `';'` (colliding with `Sexp::COMMENT_LEAD`), or drifted
596/// `QuoteForm::QUOTE_LEAD` to `'('` (colliding with `Sexp::LIST_OPEN`),
597/// fails at `cargo check` BEFORE any test scheduler runs.
598///
599/// Adding a new family-wide `[char; N]` sub-vocabulary whose distinct-
600/// values set must remain disjoint from another substrate `[char; M]`
601/// array's distinct-values set: pair the declaration with `const _: ()
602/// = assert_char_arrays_disjoint::<N, M>(&Self::FOO_ARRAY,
603/// &Other::BAR_ARRAY);` co-located after the array's declaration and
604/// the DISJOINTNESS contract binds at compile time. The rustc-forced
605/// arities `[char; N]` and `[char; M]` compose with this const-eval
606/// sweep so BOTH cardinality-pair AND cross-array disjointness are
607/// compile-time theorems on the SAME (a, b) char-array pair.
608///
609/// Runtime callability: the function is a normal `pub const fn`, so
610/// callers CAN also invoke it at runtime — pinned by
611/// `assert_char_arrays_disjoint_panics_at_runtime_on_collision` and
612/// `assert_char_arrays_disjoint_panic_message_names_the_helper_and_char_disjointness_violation_axis`.
613/// The panic site carries the `"CHAR-DISJOINTNESS-VIOLATION"` axis-
614/// provenance string chosen DISTINCT from every sibling helper's axis
615/// vocabulary (`"duplicate"` on the ARRAY-side pairwise-distinct
616/// sibling; `"CHAR-SUBSET-VIOLATION"` on the (char) SUBSET-embedding
617/// sibling; `"SUBSET-VIOLATION"` on the (u8) finite-set SUBSET-only
618/// sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8) range SUBSET-only
619/// sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the (u8) covers-
620/// finite-set sibling; `"OUT-OF-RANGE"` / `"MISSING"` on the (u8)
621/// covers-inclusive-range sibling; `"ARITY-MISMATCH"` on both (u8)
622/// `_permutes_*` compound helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on
623/// the (u8) SET-side well-formedness sibling) so a diagnostic that
624/// names the failed axis routes UNAMBIGUOUSLY to THIS specific
625/// disjointness helper. The `"CHAR-"` prefix disambiguates from a
626/// future-lift (u8) disjointness sibling; the `"-VIOLATION"` suffix
627/// lets callers grep either DISJOINTNESS or SUBSET-embedding sibling
628/// by `"VIOLATION"` alone or route to the specific contract-shape by
629/// the axis stem (`"DISJOINTNESS"` vs `"SUBSET"`).
630///
631/// Theory grounding:
632/// - THEORY.md §V.1 — knowable platform; the family-wide cross-array
633/// disjointness contract on the reader-boundary `char` vocabulary
634/// becomes a TYPE-LEVEL theorem the substrate carries per (a, b)
635/// char-array pair rather than a runtime test the developer must
636/// remember to write per pair.
637/// - THEORY.md §III — the typescape; the (element-type × contract-
638/// shape) matrix now carries the DISJOINTNESS corner on the (char)
639/// row at ONE peer const-fn helper rather than at a per-pair runtime
640/// iterator sweep. The (subset, disjointness) 2-corner face on the
641/// (char) row is now closed at TWO peer const-fn helpers —
642/// [`assert_char_array_within_char_finite_set`] on the SUBSET corner
643/// and this helper on the DISJOINTNESS corner.
644/// - THEORY.md §VI.1 — generation over composition; the const-eval
645/// cross-array membership sweep IS the generative shape. Every new
646/// closed-set `char` sub-vocabulary array whose distinct-values set
647/// is an intentionally-disjoint peer of another substrate `char`
648/// array adds ONE `const _` line to get the disjointness theorem
649/// rather than re-deriving a per-pair runtime iterator sweep at
650/// each call site.
651/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
652/// DISJOINTNESS proof at declaration site AND the reader outer-
653/// dispatch cascade's arm-set partition contract regenerate through
654/// the SAME `const _` witnesses at the ARRAY level.
655///
656/// Frontier inspiration: Lean 4's `Finset.Disjoint : Finset α →
657/// Finset α → Prop` as a decidable relation on `Finset α` combined
658/// with `Finset.disjoint_iff` unfolding the relation to negated
659/// per-element membership — the substrate primitive here embeds the
660/// same disjointness relation as a rustc const-eval-time proof
661/// obligation at every `assert_char_arrays_disjoint` call site rather
662/// than as a Lean tactic invocation deferred to `elab_command`. The
663/// symmetric nested sweep mirrors Lean's `Finset.disjoint_iff_ne`
664/// characterisation `∀ a ∈ s, ∀ b ∈ t, a ≠ b` at the concrete
665/// two-array `[char; N] × [char; M]` monomorphic realisation.
666pub const fn assert_char_arrays_disjoint<const N: usize, const M: usize>(
667 a: &[char; N],
668 b: &[char; M],
669) {
670 let mut i = 0;
671 while i < N {
672 let mut j = 0;
673 while j < M {
674 if a[i] as u32 == b[j] as u32 {
675 panic!(
676 "assert_char_arrays_disjoint: CHAR-DISJOINTNESS-\
677 VIOLATION — the two family-wide char arrays `a` \
678 and `b` share an entry at some (i, j) position \
679 pair. The substrate's CROSS-ARRAY DISJOINTNESS \
680 contract on the pair is broken; every consumer \
681 that partitions the two arrays' distinct-values \
682 sets into disjoint sub-vocabularies of the reader-\
683 boundary char algebra (the reader outer-dispatch \
684 cascade's SIX category-leading char arm-set \
685 partition through `Sexp::LIST_DELIMITERS`, \
686 `QuoteForm::LEADS`, `Sexp::COMMENT_DELIMITERS`, \
687 `Atom::STR_DELIMITER`; the Str-payload escape-arm \
688 dispatch cascade through `Atom::SELF_ESCAPE_TABLE`, \
689 `Atom::NAMED_ESCAPE_TABLE`; any future typed-\
690 disjointness pair on the substrate's reader-\
691 boundary char algebras) relies on the two arrays' \
692 distinct-values sets NOT sharing a byte. Fix at \
693 WHICHEVER ARRAY-DECLARATION site drifted (the \
694 symmetric disjointness relation carries no built-\
695 in axis-provenance role split between `a` and `b`) \
696 by dropping the offending entry from one array OR \
697 re-shaping the partition to route the shared \
698 entry to a single sub-vocabulary"
699 );
700 }
701 j += 1;
702 }
703 i += 1;
704 }
705}
706
707// Compile-time DISJOINTNESS witnesses — the FIVE substrate-pinned
708// (a, b) `[char; N] × [char; M]` pairs whose distinct-values sets are
709// intentionally-closed disjoint sub-vocabularies of the reader-boundary
710// char algebra. Pre-lift the five disjointness relations lived only as
711// runtime tests (`sexp_comment_delimiters_disjoint_from_list_delimiters`
712// on pair 1; `sexp_list_delimiters_disjoint_from_comment_lead` +
713// `sexp_list_delimiters_disjoint_from_str_delimiter` +
714// `sexp_comment_delimiters_disjoint_from_str_delimiter` as scalar-vs-
715// array runtime pins peer to pairs 1–5; the implicit disjointness
716// arms of `NON_WHITESPACE_BARE_ATOM_TERMINATORS`'s four-category
717// composition rule between LIST_DELIMITERS + LEADS + {STR_DELIMITER,
718// COMMENT_LEAD}); post-lift the ARRAY-LEVEL disjointness of the six
719// pinned pairs binds at rustc time via one `const _` line per pair.
720// A regression that silently drifted one of the eight arm-set-leading
721// chars into another arm (e.g. `Sexp::LIST_OPEN` to `';'`, colliding
722// with `Sexp::COMMENT_LEAD`; `QuoteForm::QUOTE_LEAD` to `'('`,
723// colliding with `Sexp::LIST_OPEN`; `Atom::STR_DELIMITER` to `';'`,
724// colliding with `Sexp::COMMENT_LEAD`; `Atom::STR_ESCAPE_LEAD` to
725// `'\n'`, colliding with `Sexp::COMMENT_TERM`) fails at `cargo check`
726// BEFORE any test scheduler runs. Sibling to the pairwise-distinctness
727// witnesses above — those pin INJECTIVITY on each individual array,
728// these pin DISJOINTNESS across PAIRS of arrays.
729//
730// The six pinned pairs jointly close the C(4, 2) = 6 pairwise-
731// disjoint face on the four-array reader-boundary set
732// `{Sexp::LIST_DELIMITERS, Sexp::COMMENT_DELIMITERS, QuoteForm::LEADS,
733// Atom::SELF_ESCAPE_TABLE}` EXHAUSTIVELY — the same closure shape as
734// the three-way `SexpShape::LABELS` partition proof in `error.rs`
735// (which pins C(3, 2) = 3 pairs on the `AtomKind::LABELS`,
736// `QuoteForm::LABELS`, `StructuralKind::LABELS` sub-vocabulary triple)
737// scaled up to the four-array reader-boundary partition. Composed
738// with the six ARRAY-side pairwise-distinctness `const _:` witnesses
739// above, the substrate carries a full DISJOINT-UNION theorem on the
740// reader-boundary partition
741// `LIST_DELIMITERS ⊕ COMMENT_DELIMITERS ⊕ QuoteForm::LEADS ⊕
742// Atom::SELF_ESCAPE_TABLE`
743// (with cardinality 2 + 2 + 3 + 2 = 9) as a rustc-time proof
744// obligation, closing the eight-arm reader-boundary vocabulary
745// partition at compile time.
746const _: () =
747 assert_char_arrays_disjoint::<2, 2>(&Sexp::LIST_DELIMITERS, &Sexp::COMMENT_DELIMITERS);
748const _: () = assert_char_arrays_disjoint::<2, 3>(&Sexp::LIST_DELIMITERS, &QuoteForm::LEADS);
749const _: () = assert_char_arrays_disjoint::<2, 3>(&Sexp::COMMENT_DELIMITERS, &QuoteForm::LEADS);
750const _: () = assert_char_arrays_disjoint::<2, 2>(&Sexp::LIST_DELIMITERS, &Atom::SELF_ESCAPE_TABLE);
751const _: () = assert_char_arrays_disjoint::<3, 2>(&QuoteForm::LEADS, &Atom::SELF_ESCAPE_TABLE);
752// The sixth (Sexp::COMMENT_DELIMITERS × Atom::SELF_ESCAPE_TABLE) pair
753// pins that the comment-boundary vocabulary `{COMMENT_LEAD (`;`),
754// COMMENT_TERM (`\n`)}` and the string self-escape vocabulary
755// `{STR_DELIMITER (`"`), STR_ESCAPE_LEAD (`\\`)}` remain byte-
756// disjoint. Load-bearing because a regression that renamed
757// `Sexp::COMMENT_LEAD` from `';'` to `'"'` would make `'"'` both a
758// string opener AND a comment starter (silently swallowing every
759// string literal into a comment run at the reader's outer dispatch
760// cascade), and a regression that renamed `Atom::STR_ESCAPE_LEAD`
761// from `'\\'` to `'\n'` would silently mis-terminate every string
762// literal at the first newline (drifting the reader-boundary
763// vocabulary into overlap with the comment-boundary vocabulary).
764// Closes the sixth (C, S) corner of the C(4, 2) = 6-pair
765// pairwise-disjoint face on the four-array reader-boundary set
766// EXHAUSTIVELY.
767const _: () =
768 assert_char_arrays_disjoint::<2, 2>(&Sexp::COMMENT_DELIMITERS, &Atom::SELF_ESCAPE_TABLE);
769
770/// Compile-time contract verifier — panics at const evaluation time if
771/// the sub-slice `full[START..START + M)` does NOT byte-equal the peer
772/// sub-array `sub[..]` positionwise (char-by-char).
773///
774/// Row-dual peer of [`assert_u8_array_slice_equals_u8_array`] on the
775/// (element-type) axis: where the `u8` sibling closes the outer-`Sexp`
776/// cache-key discriminator sub-carving vocabulary at compile time, this
777/// closes the substrate's reader-boundary `[char; N]` scalar-composed
778/// vocabulary at compile time. Opens the SUB-SLICE ARRAY-image column
779/// on the (char) row of the (element-type × contract-shape) matrix peer
780/// to the u8-row sibling's SUB-SLICE ARRAY-image column — the two
781/// helpers together lift EVERY positionwise-composition contract
782/// `arr[START..START + M) == sub[..]` on scalar-family-wide substrate
783/// arrays into a COMPILE-TIME theorem.
784///
785/// The FULL-ARRAY corner (`M == N`, `START == 0`) collapses to the
786/// pointwise identity `arr == [c_0, c_1, …, c_{N-1}]` binding BOTH
787/// (a) the per-position ORDER of the outer array's declaration (the
788/// CANONICAL variant-declaration order every reader-outer-dispatch
789/// consumer depends on) AND (b) each per-role `pub const *_LEAD` /
790/// `*_DELIMITER` / `*_ESCAPE_LEAD` / `*_ESCAPE_SOURCE` /
791/// `*_ESCAPE_DECODED` alias's canonical `char` value the outer
792/// array's slots re-export. Strictly STRONGER on the (contract-
793/// strength) axis than the sibling `_pairwise_distinct` witnesses at
794/// [`assert_char_array_pairwise_distinct`]: those bind each array's
795/// IMAGE SET via INJECTIVITY but are SILENT on which SLOT each char
796/// lands at — a regression that swapped [`Sexp::LIST_OPEN`] (`'('`) and
797/// [`Sexp::LIST_CLOSE`] (`')'`) (drifting [`Sexp::LIST_DELIMITERS`]
798/// from `['(', ')']` to `[')', '(']`) preserves the pairwise-
799/// distinctness witness (both chars still distinct) but silently
800/// misaligns every consumer indexing `LIST_DELIMITERS[0]` for the
801/// reader-open dispatch. THIS helper binds each slot's LITERAL char
802/// value at rustc time.
803///
804/// Consumer sites this helper closes at the FULL-ARRAY corner:
805/// * [`Sexp::LIST_DELIMITERS`] `== ['(', ')']` — the reader-outer-
806/// dispatch list-delimiter pair whose ordering matches
807/// [`Sexp::LIST_OPEN`] / [`Sexp::LIST_CLOSE`] declaration order.
808/// * [`Sexp::COMMENT_DELIMITERS`] `== [';', '\n']` — the outer-`Sexp`
809/// comment-boundary pair.
810/// * [`Atom::SELF_ESCAPE_TABLE`] `== ['"', '\\']` — the pattern-
811/// equals-value sub-vocabulary of the Str-escape closed set.
812/// * [`Atom::ESCAPE_SOURCES`] `== ['n', 't', 'r', '"', '\\']` — the
813/// FIVE non-passthrough SOURCE-column chars of
814/// [`Atom::decode_str_escape`] in canonical declaration order.
815/// * [`Atom::ESCAPE_DECODED`] `== ['\n', '\t', '\r', '"', '\\']` —
816/// the FIVE column-dual DECODED-column chars.
817/// * [`Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS`] `== ['(', ')',
818/// '\'', '`', ',', '"', ';']` — the SEVEN category-leading chars
819/// the reader's outer-dispatch cascade specialises on, spanning
820/// THREE type namespaces ([`Sexp`], [`Atom`], [`QuoteForm`]) in
821/// canonical outer-dispatch order.
822/// * [`QuoteForm::LEADS`] `== ['\'', '`', ',']` — the three quote-
823/// family reader-lead-chars in canonical variant-declaration order.
824/// * [`crate::error::UnquoteForm::LEADS`] `== [',']` — the shared-
825/// lead-char collapse of the two-of-four substitution subset (both
826/// `Unquote` and `UnquoteSplice` share the `,` lead byte and
827/// disambiguate on the peek-then-consume `@` second char).
828///
829/// Pre-lift each identity lived ONLY at the array's DECLARATION site
830/// (the per-slot initializer's identifier references resolve at
831/// substitution to the LITERAL char values); a regression that
832/// renamed [`Sexp::LIST_OPEN`] from `'('` to `'['` (a hypothetical
833/// Racket-compat port) would compile silently — the array's slot
834/// inherits the drifted char, every consumer indexing into
835/// `LIST_DELIMITERS[0]` continues to work with the drifted value, and
836/// only test-surface references to the LITERAL char `'('` would
837/// catch the drift at test runtime rather than at `cargo check` time.
838/// Post-lift the per-slot LITERAL char value binds at rustc time via
839/// ONE `const _` witness per array; the drift fails at `cargo check`
840/// BEFORE any test scheduler runs. Sibling to the pairwise-
841/// distinctness witnesses above [`assert_char_array_pairwise_distinct`]
842/// — those pin INJECTIVITY on each array; these pin per-slot ORDER
843/// against the CANONICAL literal-char listing.
844///
845/// The three axis-partitioned panic messages (`START-OUT-OF-BOUNDS`,
846/// `SLICE-LENGTH-OUT-OF-BOUNDS`, `CHAR-SLICE-EQUALS-ARRAY-VIOLATION`)
847/// mirror the u8 sibling's message vocabulary with the `CHAR-` prefix
848/// on the CONTENT-drift axis so callers grep either the (u8) row's
849/// plain `SLICE-EQUALS-ARRAY-VIOLATION` or the (char) row's
850/// `CHAR-SLICE-EQUALS-ARRAY-VIOLATION` axis-prefix by element-type.
851///
852/// Adding a new family-wide `[char; N]` array to the substrate whose
853/// declaration is a positionwise composition against named per-role
854/// `pub const` `char` constants: pair the declaration with `const _:
855/// () = assert_char_array_slice_equals_char_array::<N, N, 0>(
856/// &Self::FOO_ARRAY, &[literal chars; N]);` co-located after the
857/// array's declaration and the per-slot ORDER contract binds at
858/// compile time. The rustc-forced arity `[char; N]` composes with
859/// this const-eval sweep so BOTH cardinality AND per-slot canonical-
860/// char-value are compile-time theorems on the SAME array.
861///
862/// Runtime callability: the function is a normal `pub const fn`, so
863/// callers CAN also invoke it at runtime — e.g. a REPL / LSP
864/// tokenizer that constructs a `[char; N]` at runtime and wants to
865/// verify positionwise composition against a peer sub-array before
866/// consuming it — and the panic surfaces normally in that path
867/// (pinned by
868/// `assert_char_array_slice_equals_char_array_panics_at_runtime_on_positionwise_drift`,
869/// `assert_char_array_slice_equals_char_array_panics_at_runtime_on_start_out_of_bounds`,
870/// `assert_char_array_slice_equals_char_array_panics_at_runtime_on_slice_length_out_of_bounds`,
871/// AND
872/// `assert_char_array_slice_equals_char_array_panic_message_names_the_helper_and_char_slice_equals_array_violation_axis`).
873///
874/// Theory grounding:
875/// - THEORY.md §V.1 — knowable platform; the family-wide per-position
876/// ORDER contract on the `char`-typed vocabulary becomes a TYPE-
877/// LEVEL theorem the substrate carries per array declaration
878/// rather than a runtime iterator sweep the developer must
879/// remember to write per array.
880/// - THEORY.md §VI.1 — generation over composition; the const-eval
881/// positionwise sweep IS the generative shape. Every new closed-
882/// set char array declared as a positionwise composition against
883/// named-scalar constants adds ONE `const _` line to get the
884/// per-slot ORDER theorem rather than re-deriving a runtime index-
885/// by-index `assert_eq!` block per array.
886/// - THEORY.md §II.1 invariant 5 — composition preserves proofs;
887/// the per-slot-ORDER proof at declaration site AND the per-role
888/// `pub const` alias-chain composition every consumer relies on
889/// regenerate through the SAME `const _` witness.
890pub const fn assert_char_array_slice_equals_char_array<
891 const N: usize,
892 const M: usize,
893 const START: usize,
894>(
895 full: &[char; N],
896 sub: &[char; M],
897) {
898 if START > N {
899 panic!(
900 "assert_char_array_slice_equals_char_array: START-OUT-OF-\
901 BOUNDS — the const parameter `START` sits OUTSIDE the \
902 outer array's valid position range `[0..N]` (inclusive \
903 upper bound: `START == N` combined with `M == 0` is the \
904 LEGAL empty-slice-at-right-endpoint corner). Fix at the \
905 `const _` witness's turbofish by reconciling `START` \
906 against the outer array's declared arity `N`. The \
907 START-OUT-OF-BOUNDS gate fires FIRST — a mistyped \
908 `START` on the caller side fails HERE before the peer \
909 `SLICE-LENGTH-OUT-OF-BOUNDS` gate reads `N - START` \
910 (which would underflow `usize` had this gate not caught \
911 the slip), so a subtle bounds slip doesn't silently \
912 degenerate into a subtraction wrap-around OR a panic \
913 deeper in `full[START + i]` bounds-checking."
914 );
915 }
916 if M > N - START {
917 panic!(
918 "assert_char_array_slice_equals_char_array: SLICE-LENGTH-\
919 OUT-OF-BOUNDS — the peer sub-array's arity `M` exceeds \
920 the outer array's tail cardinality `N - START`, so the \
921 positionwise sweep `full[START + i]` for `i ∈ [0..M)` \
922 would overrun the outer array's valid position range \
923 `[0..N)` at some `i ∈ [N - START..M)`. Fix at the \
924 `const _` witness's turbofish by reconciling `M` against \
925 the outer array's tail cardinality `N - START` OR by \
926 narrowing `START` to leave a longer tail. The peer \
927 `START-OUT-OF-BOUNDS` gate above guarantees `START ≤ N` \
928 so `N - START` never underflows `usize` at this gate. \
929 The LEGAL exact-fit corner `M == N - START` (the sub-\
930 array reaches EXACTLY to the outer array's right \
931 endpoint) is accepted; the STRICT `M > N - START` slip \
932 is what this gate rejects."
933 );
934 }
935 let mut i = 0;
936 while i < M {
937 if full[START + i] as u32 != sub[i] as u32 {
938 panic!(
939 "assert_char_array_slice_equals_char_array: CHAR-\
940 SLICE-EQUALS-ARRAY-VIOLATION — the outer `[char; N]` \
941 array `full` carries a `char` at some position \
942 `START + i` (for `i ∈ [0..M)`) that does NOT byte-\
943 equal the peer `[char; M]` sub-array `sub` at the \
944 offset-matched position `i`. The substrate's SLICE-\
945 EQUALS-ARRAY positionwise-composition contract on the \
946 sub-slice `full[START..START + M) == sub[..]` is \
947 broken; every consumer that reads `full[START..START \
948 + M)` as a positionwise-aligned copy of a peer literal-\
949 char listing (the reader-outer-dispatch pair \
950 `Sexp::LIST_DELIMITERS == [Sexp::LIST_OPEN, \
951 Sexp::LIST_CLOSE]` at `[0..2)`; the comment-boundary \
952 pair `Sexp::COMMENT_DELIMITERS == [Sexp::COMMENT_LEAD, \
953 Sexp::COMMENT_TERM]` at `[0..2)`; the escape-self \
954 pair `Atom::SELF_ESCAPE_TABLE == [Atom::STR_DELIMITER, \
955 Atom::STR_ESCAPE_LEAD]` at `[0..2)`; the escape-\
956 source column `Atom::ESCAPE_SOURCES == [n, t, r, \", \
957 \\]` at `[0..5)`; the escape-decoded column \
958 `Atom::ESCAPE_DECODED == [\\n, \\t, \\r, \", \\]` at \
959 `[0..5)`; the reader-boundary category-leading \
960 seven-char SPAN `Sexp::NON_WHITESPACE_BARE_ATOM_\
961 TERMINATORS` at `[0..7)`; the quote-family lead-char \
962 triple `QuoteForm::LEADS == [QuoteForm::QUOTE_LEAD, \
963 QuoteForm::QUASIQUOTE_LEAD, QuoteForm::UNQUOTE_LEAD]` \
964 at `[0..3)`; the substitution-subset singleton \
965 `UnquoteForm::LEADS == [UnquoteForm::UNQUOTE_LEAD]` \
966 at `[0..1)`; any future family-wide `[char; N]` sub-\
967 slice byte-for-byte equal to a peer literal-char \
968 listing) relies on this invariant. Fix at the ARRAY-\
969 DECLARATION site (the drifted `full[START + i]` slot \
970 inside the slice segment) OR at the peer per-role \
971 `pub const *_LEAD` / `*_DELIMITER` / `*_ESCAPE_LEAD` \
972 / `*_ESCAPE_SOURCE` / `*_ESCAPE_DECODED` alias's \
973 declaration — the choice depends on whether the \
974 drift is an unintended slot reorder in the outer \
975 array's initializer OR in the per-role alias's \
976 canonical `char` value."
977 );
978 }
979 i += 1;
980 }
981}
982
983// Compile-time FULL-ARRAY per-position ORDER pins — one `const _: () =
984// assert_char_array_slice_equals_char_array::<N, N, 0>(&…, &[literal
985// chars; N])` per family-wide `[char; N]` scalar-composed substrate
986// array on the reader-boundary closed-set outer algebras. Each
987// invocation exercises the [`assert_char_array_slice_equals_char_array`]
988// helper at its FULL-ARRAY corner (`M == N`, `START == 0`) — the
989// SLICE-EQUALS-ARRAY sweep collapses to the ALL-positions-equal-peer-
990// array pointwise identity `arr == [c_0, c_1, …, c_{N-1}]`. The peer
991// literal-char sub-array on the RHS pins BOTH (a) the per-position
992// ORDER of the outer array's declaration (the CANONICAL variant-
993// declaration order every reader-outer-dispatch consumer depends on)
994// AND (b) each per-role `pub const *_LEAD` / `*_DELIMITER` /
995// `*_ESCAPE_LEAD` / `*_ESCAPE_SOURCE` / `*_ESCAPE_DECODED` alias's
996// canonical `char` value the outer array's slots re-export. Strictly
997// STRONGER on the (contract-strength) axis than the sibling
998// `_pairwise_distinct` witnesses at lines 116..=122 above: those bind
999// each array's IMAGE SET via INJECTIVITY but are SILENT on which SLOT
1000// each char lands at — a regression that swapped `Sexp::LIST_OPEN`
1001// (`'('`) and `Sexp::LIST_CLOSE` (`')'`) (drifting
1002// `Sexp::LIST_DELIMITERS` from `['(', ')']` to `[')', '(']`) preserves
1003// the pairwise-distinctness witness (both chars still distinct) but
1004// silently misaligns every consumer indexing `LIST_DELIMITERS[0]` for
1005// the reader-open dispatch. Post-lift the ARRAY-LEVEL per-position
1006// order binds at rustc time via ONE `const _` witness per array; a
1007// drift at either the per-role `pub const` char OR the array
1008// declaration's ordering fails at `cargo check` BEFORE any test
1009// scheduler runs.
1010//
1011// Sibling posture to the FOUR-witness EXHAUSTIVE per-position sweep on
1012// the FOUR sub-carving `[u8; N]` `HASH_DISCRIMINATORS` arrays in the
1013// (u8) row's FULL-ARRAY per-position ORDER cluster below in this file.
1014// Those four witnesses close the FOUR sub-carving arrays against their
1015// LITERAL byte listing at the (u8) row's FULL-ARRAY corner; these
1016// EIGHT witnesses close the EIGHT reader-boundary scalar-composed
1017// arrays against their LITERAL char listing at the (char) row's FULL-
1018// ARRAY corner. Together the twelve witnesses close the (element-
1019// type × contract-shape) matrix's FULL-ARRAY per-position ORDER
1020// column across BOTH the (u8) row (outer-`Sexp` cache-key
1021// discriminator vocabulary) AND the (char) row (reader-boundary char
1022// vocabulary) EXHAUSTIVELY at rustc time.
1023//
1024// The eight pinned arrays appear here in canonical (owning-algebra,
1025// per-role-alias-count-ascending) order:
1026// * `Sexp::LIST_DELIMITERS == ['(', ')']` — outer-`Sexp` list pair.
1027// * `Sexp::COMMENT_DELIMITERS == [';', '\n']` — outer-`Sexp` comment
1028// boundary pair.
1029// * `Atom::SELF_ESCAPE_TABLE == ['"', '\\']` — `Atom` escape-self
1030// pair (pattern-equals-value sub-vocabulary of the Str-escape
1031// closed set).
1032// * `Atom::ESCAPE_SOURCES == ['n', 't', 'r', '"', '\\']` — `Atom`
1033// escape-source column (non-passthrough SOURCE-column chars).
1034// * `Atom::ESCAPE_DECODED == ['\n', '\t', '\r', '"', '\\']` —
1035// `Atom` escape-decoded column (column-dual DECODED-column chars).
1036// * `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS == ['(', ')', '\'',
1037// '`', ',', '"', ';']` — outer-`Sexp` reader-boundary category-
1038// leading seven-char SPAN across THREE type namespaces ([`Sexp`],
1039// [`QuoteForm`], [`Atom`]).
1040// * `QuoteForm::LEADS == ['\'', '`', ',']` — quote-family reader-
1041// lead-char triple.
1042// * `UnquoteForm::LEADS == [',']` — substitution-subset shared-lead
1043// singleton (both `Unquote` and `UnquoteSplice` share the `,`
1044// lead byte and disambiguate on the peek-then-consume `@` second
1045// char).
1046const _: () =
1047 assert_char_array_slice_equals_char_array::<2, 2, 0>(&Sexp::LIST_DELIMITERS, &['(', ')']);
1048const _: () =
1049 assert_char_array_slice_equals_char_array::<2, 2, 0>(&Sexp::COMMENT_DELIMITERS, &[';', '\n']);
1050const _: () =
1051 assert_char_array_slice_equals_char_array::<2, 2, 0>(&Atom::SELF_ESCAPE_TABLE, &['"', '\\']);
1052const _: () = assert_char_array_slice_equals_char_array::<5, 5, 0>(
1053 &Atom::ESCAPE_SOURCES,
1054 &['n', 't', 'r', '"', '\\'],
1055);
1056const _: () = assert_char_array_slice_equals_char_array::<5, 5, 0>(
1057 &Atom::ESCAPE_DECODED,
1058 &['\n', '\t', '\r', '"', '\\'],
1059);
1060const _: () = assert_char_array_slice_equals_char_array::<7, 7, 0>(
1061 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
1062 &['(', ')', '\'', '`', ',', '"', ';'],
1063);
1064const _: () =
1065 assert_char_array_slice_equals_char_array::<3, 3, 0>(&QuoteForm::LEADS, &['\'', '`', ',']);
1066const _: () =
1067 assert_char_array_slice_equals_char_array::<1, 1, 0>(&crate::error::UnquoteForm::LEADS, &[',']);
1068
1069// Compile-time SUB-CARVING per-position ARRAY-LEVEL POSITIONAL-
1070// COMPOSITION witnesses on `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS`
1071// (`[char; 7]`) — the outer-`Sexp` reader-boundary category-leading
1072// seven-char SPAN whose declaration order composes across THREE type
1073// namespaces (`Sexp`, `QuoteForm`, `Atom`) as the segmented
1074// concatenation
1075//
1076// NON_WHITESPACE_BARE_ATOM_TERMINATORS
1077// == Sexp::LIST_DELIMITERS // slots [0..2)
1078// ++ QuoteForm::LEADS // slots [2..5)
1079// ++ [Atom::STR_DELIMITER] // slot [5..6)
1080// ++ [Sexp::COMMENT_LEAD] // slot [6..7)
1081//
1082// Each of the FOUR right-hand-side segments names an INDEPENDENT
1083// substrate primitive on ONE of the three sub-algebras — the OUTER
1084// terminator SPAN is the composition surface where the FOUR sub-
1085// vocabularies land at their canonical reader-outer-dispatch slots. The
1086// pre-existing FULL-ARRAY LITERAL-listing witness IMMEDIATELY ABOVE
1087// (`assert_char_array_slice_equals_char_array::<7, 7, 0>(&…, &['(', ')',
1088// '\'', '`', ',', '"', ';'])`) binds the SPAN against a HARDCODED
1089// `[char; 7]` peer literal — it catches drifts of the PER-ROLE `pub
1090// const` chars each sub-carving array's slot re-exports through the
1091// underlying `Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE` / `QuoteForm::
1092// QUOTE_LEAD` / … / `Sexp::COMMENT_LEAD` primitives, but is SILENT on
1093// the ARRAY-LEVEL structural composition axis — a regression that
1094// reorders `Sexp::LIST_DELIMITERS` from `[LIST_OPEN, LIST_CLOSE]` to
1095// `[LIST_CLOSE, LIST_OPEN]` (or `QuoteForm::LEADS` from `[QUOTE_LEAD,
1096// QUASIQUOTE_LEAD, UNQUOTE_LEAD]` to any other permutation of the same
1097// three chars) while leaving `NON_WHITESPACE_BARE_ATOM_TERMINATORS` in
1098// its canonical initializer form silently misaligns every consumer that
1099// treats the composite's HEAD-2-slice as positionally-interchangeable
1100// with `Sexp::LIST_DELIMITERS` (or the MIDDLE-3-slice with
1101// `QuoteForm::LEADS`) — the OUTER LITERAL witness accepts both
1102// sub-carving reorders because the LITERAL peer array's slot-0 (`'('`)
1103// and slot-1 (`')'`) still match `NON_WHITESPACE_BARE_ATOM_TERMINATORS`
1104// even though the sub-carving's declaration slot-0 and slot-1 drifted.
1105// The pre-existing SET-level SUBSET witnesses at lines 369..=380 above
1106// (`assert_char_array_within_char_finite_set::<{2,3}, 7>(&{LIST_
1107// DELIMITERS, QuoteForm::LEADS}, &NON_WHITESPACE_BARE_ATOM_TERMINATORS)`)
1108// bind the sub-carvings' distinct-value SET is CONTAINED in the
1109// composite's distinct-value SET but are silent on positional alignment
1110// — the SAME sub-carving reorders survive those witnesses because
1111// distinct-value SET membership is order-invariant.
1112//
1113// These FOUR new `const _` witnesses close the missing corner: each
1114// binds a SUB-CARVING array positionwise-equal to its canonical SLOT
1115// SEGMENT of the composite terminator SPAN via
1116// [`assert_char_array_slice_equals_char_array`] at the SUB-SLICE ARRAY-
1117// image corner (`START ∈ {0, 2, 5, 6}`, `M ∈ {2, 3, 1, 1}`,
1118// `M < N == 7`). Post-lift a drift at ANY of the sub-carvings'
1119// declaration ordering (or at the composite's slot ordering) fails
1120// AT rustc time BEFORE the pre-existing FULL-ARRAY LITERAL witness re-
1121// verifies the composite's literal identity — the two witness families
1122// bind the composite through DISTINCT drift-detection axes (LITERAL
1123// against inline chars, POSITIONAL against sub-carving arrays) that
1124// TOGETHER catch every ARRAY-LEVEL misalignment the pre-lift
1125// (INJECTIVITY + SUBSET-EMBEDDING) 2-corner face silently accepted.
1126//
1127// Sibling posture to the FOUR-witness EXHAUSTIVE per-position sub-
1128// carving composition sweep on the OUTER `SexpShape::HASH_DISCRIMINATORS`
1129// container (the trio of `assert_u8_array_slice_equals_u8_array::<12,
1130// {1, 4}, {0, 7, 8}>` witnesses + the `assert_u8_array_slice_is_scalar_
1131// replica::<12, 1, 7>` witness below in this file). Those FOUR
1132// witnesses pin the (u8) row's twelve-slot outer container against its
1133// FOUR sub-carvings at rustc time; THESE FOUR witnesses pin the (char)
1134// row's seven-slot outer terminator SPAN against its FOUR sub-carvings
1135// at rustc time. Together the eight witnesses close the (element-type
1136// × contract-shape) matrix's SUB-CARVING per-position POSITIONAL-
1137// COMPOSITION corner across BOTH the (u8) row (outer-`Sexp` cache-key
1138// discriminator hierarchy) AND the (char) row (reader-outer-dispatch
1139// terminator SPAN) EXHAUSTIVELY at the FOUR-sub-carving-per-container
1140// depth.
1141//
1142// A hypothetical seventh reader-outer-dispatch category (e.g. a
1143// `#|…|#` block-comment lead byte pinning a fresh `Sexp::BLOCK_COMMENT_
1144// LEAD` primitive) would extend `NON_WHITESPACE_BARE_ATOM_TERMINATORS`
1145// to `[char; 8]` AND require a FIFTH witness below binding
1146// `NON_WHITESPACE_BARE_ATOM_TERMINATORS[7..8) == [Sexp::BLOCK_COMMENT_
1147// LEAD]` (or, if the new lead byte joins an EXISTING sub-carving like
1148// `Sexp::COMMENT_LEAD`'s comment axis, a widened `Sexp::COMMENT_LEADS`
1149// array replaces the singleton at slot `[6..7)` and its widened arity
1150// propagates through the widened witness's const-generic turbofish).
1151// Rustc's forced-arity check on `[char; N]` fails compilation if the
1152// composite's arity grows without the corresponding sub-carving
1153// widening (or vice versa).
1154const _: () = assert_char_array_slice_equals_char_array::<7, 2, 0>(
1155 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
1156 &Sexp::LIST_DELIMITERS,
1157);
1158const _: () = assert_char_array_slice_equals_char_array::<7, 3, 2>(
1159 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
1160 &QuoteForm::LEADS,
1161);
1162const _: () = assert_char_array_slice_equals_char_array::<7, 1, 5>(
1163 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
1164 &[Atom::STR_DELIMITER],
1165);
1166const _: () = assert_char_array_slice_equals_char_array::<7, 1, 6>(
1167 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
1168 &[Sexp::COMMENT_LEAD],
1169);
1170
1171/// Compile-time contract verifier — panics at const evaluation time if
1172/// any two entries of `arr` alias byte-for-byte through
1173/// [`str::as_bytes`].
1174///
1175/// Column-dual peer to [`assert_char_array_pairwise_distinct`] on the
1176/// (element-type) axis: where the `char` sibling closes the reader-
1177///
1178/// Column-dual peer to [`assert_char_array_pairwise_distinct`] on the
1179/// (element-type) axis: where the `char` sibling closes the reader-
1180/// boundary `[char; N]` vocabulary at compile time, this closes the
1181/// substrate's family-wide `[&'static str; N]` vocabulary at compile
1182/// time. The two helpers together lift EVERY `pub const` scalar-
1183/// family-wide array declared on the substrate's closed-set outer
1184/// algebras (`Sexp` / `Atom` / `AtomKind` / `QuoteForm`) into a
1185/// COMPILE-TIME pairwise-distinctness theorem — a regression that
1186/// silently collides two entries fails the build at `cargo check`
1187/// time, one invocation stage earlier than the test-run pin.
1188///
1189/// The invariant is load-bearing for every consumer that pattern-
1190/// matches the array's entries as DISJOINT arms —
1191/// [`Atom::bool_literal`]'s two-arm projection through
1192/// [`Atom::BOOL_LITERALS`] (a duplicate here would silently shadow
1193/// the second arm's `#f` spelling at the same-index projection);
1194/// [`AtomKind::label`]'s six-arm projection through
1195/// [`AtomKind::LABELS`] (a duplicate here would collapse two
1196/// distinct atomic-payload variants onto the same diagnostic
1197/// label, breaking every downstream label-driven partition on
1198/// the closed-set AtomKind algebra); [`QuoteForm::prefix`]'s
1199/// four-arm projection over [`QuoteForm::PREFIXES`] (a duplicate
1200/// here would silently collapse two distinct quote-family
1201/// variants onto the same reader-prefix `&'static str`);
1202/// [`QuoteForm::iac_forge_tag`]'s four-arm projection through
1203/// [`QuoteForm::IAC_FORGE_TAGS`] (a duplicate here would silently
1204/// collapse two distinct quote-family variants onto the same
1205/// canonical-form tag, breaking the iac-forge interop round-trip
1206/// through [`QuoteForm::from_iac_forge_tag`]); AND
1207/// [`QuoteForm::label`]'s four-arm projection through
1208/// [`QuoteForm::LABELS`] (a duplicate here would collapse two
1209/// distinct quote-family variants onto the same diagnostic
1210/// label). Every future family-wide `[&'static str; N]` on the
1211/// substrate that participates in a `match`-arm partition or a
1212/// same-index alias-chain benefits from the SAME compile-time
1213/// guarantee via one `const _` line.
1214///
1215/// Pre-lift each array carried its pairwise-distinctness contract
1216/// EITHER at a runtime test (`atom_bool_literals_pairwise_distinct`,
1217/// `atom_kind_labels_pairwise_distinct`, `quote_form_prefixes_
1218/// pairwise_distinct`, `quote_form_iac_forge_tags_pairwise_distinct`,
1219/// `quote_form_labels_pairwise_distinct`) OR implicitly through the
1220/// same-index alias-chain composition law's runtime pin; post-lift
1221/// the pairwise-distinctness contract binds at `cargo check` time,
1222/// one invocation stage earlier, catching regressions on `cargo
1223/// build` / `cargo clippy` runs that skip the test suite.
1224///
1225/// Adding a new family-wide `[&'static str; N]` array to the
1226/// substrate: pair the declaration with `const _: () = assert_str_
1227/// array_pairwise_distinct(&Self::FOO_ARRAY);` co-located immediately
1228/// after the array's declaration and the distinctness contract is
1229/// enforced at compile time. The rustc-forced arity `[&'static str;
1230/// N]` composes with this const-eval sweep so BOTH cardinality AND
1231/// injectivity are compile-time theorems on the SAME array.
1232///
1233/// Runtime callability: the function is a normal `pub const fn`, so
1234/// callers CAN also invoke it at runtime — e.g. a REPL / LSP
1235/// autocomplete surface that constructs a `[&'static str; N]` at
1236/// runtime from a user-supplied vocabulary AND wants to verify
1237/// pairwise distinctness before consuming it — and the panic
1238/// surfaces normally in that path (pinned by
1239/// `assert_str_array_pairwise_distinct_panics_at_runtime_on_binary_
1240/// collision`).
1241///
1242/// Theory grounding:
1243/// - THEORY.md §V.1 — knowable platform; the family-wide
1244/// distinctness contract on the `&'static str`-typed vocabulary
1245/// becomes a TYPE-LEVEL theorem the substrate carries per array
1246/// declaration rather than a runtime test the developer must
1247/// remember to write per array.
1248/// - THEORY.md §VI.1 — generation over composition; the const-eval
1249/// sweep IS the generative shape. Every new closed-set string
1250/// array adds ONE `const _` line to get the distinctness theorem
1251/// rather than re-deriving a per-array runtime iterator sweep.
1252/// - THEORY.md §II.1 invariant 5 — composition preserves proofs;
1253/// the distinctness proof at declaration site AND the same-index
1254/// alias-chain composition the consumer relies on regenerate
1255/// through the SAME `const _` witness.
1256pub const fn assert_str_array_pairwise_distinct<const N: usize>(arr: &[&'static str; N]) {
1257 let mut i = 0;
1258 while i < N {
1259 let mut j = i + 1;
1260 while j < N {
1261 if str_bytes_equal(arr[i], arr[j]) {
1262 panic!(
1263 "assert_str_array_pairwise_distinct: family-wide \
1264 &'static str array carries a duplicate entry \
1265 across two positions — the substrate's pairwise-\
1266 distinctness contract on the array is broken; \
1267 every consumer that pattern-matches the array's \
1268 entries as DISJOINT arms (bool-literal dispatch, \
1269 atomic-kind label decode, quote-family prefix \
1270 decode, iac-forge tag decode, quote-family label \
1271 projection) relies on this invariant"
1272 );
1273 }
1274 j += 1;
1275 }
1276 i += 1;
1277 }
1278}
1279
1280/// Const-fn byte-equality helper for `assert_str_array_pairwise_
1281/// distinct` — compares two `&'static str`s byte-for-byte through
1282/// their [`str::as_bytes`] projections. Lifted as a co-located
1283/// module-private helper rather than an inline sweep so the outer
1284/// helper's triangular `(i, j)` pair-walk mirrors the shape of
1285/// [`assert_char_array_pairwise_distinct`] at the outer method
1286/// axis without an inline byte-loop obscuring the `(i, j)` sweep.
1287///
1288/// The equality relation this helper computes is EXACTLY
1289/// [`str::eq`] (i.e. byte-for-byte length + content equality on
1290/// the underlying UTF-8 bytes), just re-derived in a const-eval
1291/// friendly shape since `str::eq` / `<[u8]>::eq` are not (yet)
1292/// callable from `const fn` context on the substrate's toolchain.
1293/// A future toolchain that stabilises `const fn str::eq` collapses
1294/// this helper to a one-line `str::eq(a, b)` delegation.
1295const fn str_bytes_equal(a: &str, b: &str) -> bool {
1296 let a = a.as_bytes();
1297 let b = b.as_bytes();
1298 if a.len() != b.len() {
1299 return false;
1300 }
1301 let mut k = 0;
1302 while k < a.len() {
1303 if a[k] != b[k] {
1304 return false;
1305 }
1306 k += 1;
1307 }
1308 true
1309}
1310
1311// Compile-time pairwise-distinctness witnesses — one `const _: () =
1312// assert_str_array_pairwise_distinct(&…)` per family-wide `[&'static
1313// str; N]` array on the substrate's closed-set outer algebras. Each
1314// invocation is const-evaluated at `cargo check` time; a regression
1315// that silently collides two entries fails the build rather than the
1316// test suite. Sibling to the runtime `_pairwise_distinct` tests at
1317// `ast.rs`'s tests module — the two enforce the same theorem at TWO
1318// stages of the toolchain, so a build that skips tests still catches
1319// the regression here, and a build that runs tests catches it a
1320// second time as a safety net if the const-eval sweep is ever
1321// silently dropped. Peer to the seven `assert_char_array_pairwise_
1322// distinct` witnesses above on the (element-type) axis: `char` covers
1323// the reader-boundary vocabulary; `&'static str` covers the closed-
1324// set outer-algebras' family-wide label / prefix / tag / literal
1325// vocabularies.
1326const _: () = assert_str_array_pairwise_distinct(&Atom::BOOL_LITERALS);
1327const _: () = assert_str_array_pairwise_distinct(&AtomKind::LABELS);
1328const _: () = assert_str_array_pairwise_distinct(&QuoteForm::PREFIXES);
1329const _: () = assert_str_array_pairwise_distinct(&QuoteForm::IAC_FORGE_TAGS);
1330const _: () = assert_str_array_pairwise_distinct(&QuoteForm::LABELS);
1331
1332// Compile-time FULL-ARRAY per-position ORDER pins — one `const _: () =
1333// assert_str_array_slice_equals_str_array::<N, N, 0>(&…, &[literal strs;
1334// N])` per family-wide `[&'static str; N]` scalar-composed substrate
1335// array on the closed-set outer-algebras' label / prefix / tag / literal
1336// vocabularies. Each invocation exercises the
1337// [`assert_str_array_slice_equals_str_array`] helper at its FULL-ARRAY
1338// corner (`M == N`, `START == 0`) — the SLICE-EQUALS-ARRAY sweep
1339// collapses to the ALL-positions-equal-peer-array pointwise identity
1340// `arr == [s_0, s_1, …, s_{N-1}]`. The peer literal-str sub-array on
1341// the RHS pins BOTH (a) the per-position ORDER of the outer array's
1342// declaration (the CANONICAL variant-declaration order every
1343// closed-set outer-algebra consumer depends on) AND (b) each per-role
1344// `pub const *_LABEL` / `*_PREFIX` / `*_TAG` / `TRUE_LITERAL` /
1345// `FALSE_LITERAL` alias's canonical `&'static str` value the outer
1346// array's slots re-export. Strictly STRONGER on the (contract-strength)
1347// axis than the sibling `_pairwise_distinct` witnesses at lines
1348// 1173..=1177 above: those bind each array's IMAGE SET via INJECTIVITY
1349// but are SILENT on which SLOT each str lands at — a regression that
1350// swapped `Atom::TRUE_LITERAL` (`"#t"`) and `Atom::FALSE_LITERAL`
1351// (`"#f"`) (drifting `Atom::BOOL_LITERALS` from `["#t", "#f"]` to
1352// `["#f", "#t"]`) preserves the pairwise-distinctness witness (both
1353// strs still distinct) but silently misaligns every consumer indexing
1354// `BOOL_LITERALS[0]` for the canonical `true`-lexeme dispatch. A
1355// silent value-drift of a per-role scalar (e.g. an ELisp-compat
1356// rename of `AtomKind::SYMBOL_LABEL` from `"symbol"` to `"sym"`, a
1357// Racket-compat rename of `QuoteForm::QUASIQUOTE_LABEL`) that flows
1358// through the composed array without updating the outer array's
1359// literal image ALSO fails HERE where the pairwise-distinctness
1360// witness stays silent. Post-lift the ARRAY-LEVEL per-position order
1361// binds at rustc time via ONE `const _` witness per array; a drift
1362// at either the per-role `pub const *_LABEL` / `*_PREFIX` / `*_TAG`
1363// str OR the array declaration's ordering fails at `cargo check`
1364// BEFORE any test scheduler runs.
1365//
1366// Sibling posture to the EIGHT-witness (char)-row FULL-ARRAY per-
1367// position ORDER cluster at lines 893..=914 above (the eight
1368// `assert_char_array_slice_equals_char_array::<N, N, 0>(&…, &[literal
1369// chars; N])` witnesses on the substrate's `[char; N]` reader-boundary
1370// vocabulary) and the FOUR-witness (u8)-row FULL-ARRAY per-position
1371// ORDER cluster below in this file (four
1372// `assert_u8_array_slice_equals_u8_array::<N, N, 0>(&…, &[literal
1373// bytes; N])` witnesses on the sub-carving `[u8; N]`
1374// `HASH_DISCRIMINATORS` arrays). Together the eight (char)-row + four
1375// (u8)-row + these five (str)-row witnesses close the (element-type
1376// × contract-shape) matrix's FULL-ARRAY per-position ORDER column
1377// across ALL THREE scalar element-type rows (char, u8, &'static str)
1378// EXHAUSTIVELY at rustc time.
1379//
1380// The five pinned arrays appear here in canonical (owning-algebra,
1381// per-role-alias-count-ascending) order:
1382// * `Atom::BOOL_LITERALS == ["#t", "#f"]` — Scheme-canonical bool-
1383// lexeme spellings on the `Atom` algebra (two per-role literals).
1384// * `AtomKind::LABELS == ["symbol", "keyword", "string", "int",
1385// "float", "bool"]` — atomic-payload diagnostic labels on the
1386// `AtomKind` subset algebra (six per-role labels).
1387// * `QuoteForm::PREFIXES == ["'", "`", ",", ",@"]` — quote-family
1388// reader-punctuation prefix bytes on the `QuoteForm` algebra (four
1389// per-role prefixes; the fourth `",@"` is the ONLY two-char prefix
1390// on the closed set — pinned separately by
1391// `quote_form_unquote_splice_prefix_constant_composes_from_unquote_lead_and_splice_discriminator`).
1392// * `QuoteForm::LABELS == ["quote", "quasiquote", "unquote",
1393// "unquote-splice"]` — diagnostic labels on the `QuoteForm` algebra
1394// (four per-role labels; the fourth `"unquote-splice"` is the
1395// substrate's SHORTER diagnostic idiom for `LispError::TypeMismatch.got`
1396// surfaces — INTENTIONALLY DISTINCT from the iac-forge tag's
1397// Common-Lisp-canonical `"unquote-splicing"` spelling).
1398// * `QuoteForm::IAC_FORGE_TAGS == ["quote", "quasiquote", "unquote",
1399// "unquote-splicing"]` — cross-crate canonical-form tags on the
1400// `QuoteForm` algebra (four per-role tags; the fourth
1401// `"unquote-splicing"` is Common-Lisp-canonical and distinct from
1402// `QuoteForm::LABELS[3]`'s shorter `"unquote-splice"` spelling —
1403// pinned by
1404// `quote_form_iac_forge_tag_and_label_disagree_only_on_unquote_splice_arm`).
1405const _: () =
1406 assert_str_array_slice_equals_str_array::<2, 2, 0>(&Atom::BOOL_LITERALS, &["#t", "#f"]);
1407const _: () = assert_str_array_slice_equals_str_array::<6, 6, 0>(
1408 &AtomKind::LABELS,
1409 &["symbol", "keyword", "string", "int", "float", "bool"],
1410);
1411const _: () = assert_str_array_slice_equals_str_array::<4, 4, 0>(
1412 &QuoteForm::PREFIXES,
1413 &["'", "`", ",", ",@"],
1414);
1415const _: () = assert_str_array_slice_equals_str_array::<4, 4, 0>(
1416 &QuoteForm::LABELS,
1417 &["quote", "quasiquote", "unquote", "unquote-splice"],
1418);
1419const _: () = assert_str_array_slice_equals_str_array::<4, 4, 0>(
1420 &QuoteForm::IAC_FORGE_TAGS,
1421 &["quote", "quasiquote", "unquote", "unquote-splicing"],
1422);
1423
1424/// Compile-time contract verifier — panics at const evaluation time if
1425/// any entry of `arr` has zero length under [`str::len`] (equivalently
1426/// `str::is_empty`).
1427///
1428/// Contract-orthogonal peer to [`assert_str_array_pairwise_distinct`]
1429/// on the (contract-shape) column of the (`&'static str`) row of the
1430/// (element-type × contract-shape) matrix: where the pairwise-
1431/// distinctness sibling binds INTRA-array `∀ i ≠ j : arr[i] ≠ arr[j]`
1432/// (INJECTIVITY on the multiset of entries), this NONEMPTY-CARDINALITY-
1433/// LOWER-BOUND sibling binds the strictly-weaker per-entry cardinality
1434/// gate `∀ i : arr[i].len() > 0` (NO ENTRY is the zero-length byte
1435/// sequence). The two contracts compose orthogonally: an array that
1436/// carries `["", ""]` fails the pairwise-distinctness contract at the
1437/// zero-length-pair corner (pinned by
1438/// `assert_str_array_pairwise_distinct_rejects_length_zero_collision`),
1439/// but an array that carries `["", "a"]` passes pairwise-distinctness
1440/// while failing NONEMPTY — so the NONEMPTY sibling closes the
1441/// remaining `""`-carrying corner that INJECTIVITY alone cannot pin.
1442/// The inner test is a direct [`str::is_empty`] delegation — const-
1443/// stable since Rust 1.39 and const-callable on the substrate's
1444/// toolchain — so no `str_bytes_equal`-shaped auxiliary helper is
1445/// needed for this contract (the peer `_pairwise_distinct` sibling
1446/// uses `str_bytes_equal` because it must compare TWO strings byte-
1447/// for-byte while `const fn str::eq` remains unstable; this sibling
1448/// only tests ONE string's length so it delegates directly).
1449///
1450/// The invariant is load-bearing for every consumer that spells a
1451/// closed-set variant through its `&'static str` label — every
1452/// [`AtomKind::label`] / [`QuoteForm::label`] / `parse_label` /
1453/// `find_by_label` composition on the ClosedSet trait's family-wide
1454/// label vocabularies (`Atom::BOOL_LITERALS` on the bool-literal
1455/// dispatch; `AtomKind::LABELS` on the atomic-payload kind decode;
1456/// `QuoteForm::LABELS` on the quote-family diagnostic vocabulary;
1457/// `QuoteForm::PREFIXES` on the reader-boundary prefix vocabulary;
1458/// `QuoteForm::IAC_FORGE_TAGS` on the canonical iac-forge tag
1459/// vocabulary) treats each entry as a NONEMPTY identifier and would
1460/// silently mis-behave on a `""` entry: `parse_label("")` would decode
1461/// the empty string to that variant (silently making empty user input a
1462/// valid variant spelling); `find_by_label("")` would return `Some(v)`
1463/// rather than `None`; every string-search consumer that scans for a
1464/// prefix or contains a label as a substring would spuriously match on
1465/// every input (since `""` is a prefix and a substring of every
1466/// string). Post-lift a regression that silently re-inlined one label
1467/// constant to `""` (e.g. `AtomKind::SYMBOL_LABEL = "";`) fails at
1468/// `cargo check` BEFORE any test scheduler runs.
1469///
1470/// Adding a new family-wide `[&'static str; N]` label / prefix / tag /
1471/// literal vocabulary to the substrate: pair the declaration with
1472/// `const _: () = assert_str_array_all_nonempty(&Self::FOO_ARRAY);`
1473/// co-located after the array's declaration and the NONEMPTY-CARDINALITY-
1474/// LOWER-BOUND contract binds at compile time. The rustc-forced arity
1475/// `[&'static str; N]` composes with this const-eval sweep so BOTH
1476/// cardinality AND per-entry nonempty are compile-time theorems on the
1477/// SAME array.
1478///
1479/// Runtime callability: the function is a normal `pub const fn`, so
1480/// callers CAN also invoke it at runtime — pinned by
1481/// `assert_str_array_all_nonempty_panics_at_runtime_on_head_empty` /
1482/// `_interior_empty` / `_tail_empty` and
1483/// `assert_str_array_all_nonempty_panic_message_names_the_helper`. The
1484/// panic site carries the `"STR-EMPTY-ENTRY"` axis-provenance string
1485/// chosen DISTINCT from every sibling helper's axis vocabulary
1486/// (`"duplicate"` on the pairwise-distinct sibling; `"STR-DISJOINTNESS-
1487/// VIOLATION"` on the arrays-disjoint sibling; `"STR-SUBSET-VIOLATION"`
1488/// on the within-finite-set sibling) so a diagnostic that names the
1489/// failed axis routes UNAMBIGUOUSLY to THIS specific NONEMPTY helper.
1490///
1491/// Theory grounding:
1492/// - THEORY.md §V.1 — knowable platform; the family-wide nonempty-
1493/// cardinality-lower-bound contract on the `&'static str` label
1494/// vocabulary becomes a TYPE-LEVEL theorem the substrate carries per
1495/// array declaration rather than a runtime test the developer must
1496/// remember to write per label constant.
1497/// - THEORY.md §II.1 invariant 1 — typed entry; a closed-set variant's
1498/// label projection is the entry-point discriminator into the typed
1499/// algebra, and a `""` entry would silently break the discriminator
1500/// at the boundary between untyped `&str` input and typed enum
1501/// variant.
1502/// - THEORY.md §VI.1 — generation over composition; the const-eval
1503/// sweep IS the generative shape. Every new closed-set label array
1504/// adds ONE `const _` line to get the NONEMPTY theorem rather than
1505/// re-deriving a per-array runtime iterator sweep at each call site.
1506pub const fn assert_str_array_all_nonempty<const N: usize>(arr: &[&'static str; N]) {
1507 let mut i = 0;
1508 while i < N {
1509 if arr[i].is_empty() {
1510 panic!(
1511 "assert_str_array_all_nonempty: STR-EMPTY-ENTRY — the \
1512 family-wide &'static str array carries a zero-length \
1513 entry at some position — the substrate's NONEMPTY-\
1514 CARDINALITY-LOWER-BOUND contract on the array is \
1515 broken; every consumer that spells a closed-set variant \
1516 through its `&'static str` label (AtomKind / QuoteForm \
1517 / UnquoteForm / StructuralKind / SexpShape label \
1518 projections; the reader-boundary prefix / tag \
1519 vocabularies; the bool-literal dispatch) treats each \
1520 entry as a NONEMPTY identifier — `parse_label(\"\")` \
1521 would silently decode the empty string to the offending \
1522 variant, `find_by_label(\"\")` would return `Some(v)` \
1523 rather than `None`, every substring / prefix scan would \
1524 spuriously match on every input. Fix at the ARRAY-\
1525 DECLARATION site by removing the `\"\"` entry OR by \
1526 giving it a nonempty canonical spelling"
1527 );
1528 }
1529 i += 1;
1530 }
1531}
1532
1533// Compile-time NONEMPTY-CARDINALITY-LOWER-BOUND witnesses — one
1534// `const _: () = assert_str_array_all_nonempty(&…)` per family-wide
1535// `[&'static str; N]` array on the substrate's closed-set outer
1536// algebras. Each invocation is const-evaluated at `cargo check` time; a
1537// regression that silently re-inlined one label constant to `""` fails
1538// the build rather than deferring to a per-consumer misbehavior at
1539// runtime. Sibling to the pairwise-distinctness witnesses above — those
1540// pin INJECTIVITY on each array, these pin the strictly-weaker per-
1541// entry cardinality gate on the SAME arrays. The two contracts compose
1542// orthogonally on every closed-set outer algebra's label vocabulary.
1543// The five arrays covered here mirror the five arrays already pinned
1544// by the `_pairwise_distinct` witnesses above (`Atom::BOOL_LITERALS`,
1545// `AtomKind::LABELS`, `QuoteForm::PREFIXES`, `QuoteForm::IAC_FORGE_TAGS`,
1546// `QuoteForm::LABELS`) — the (array × contract-shape) coverage matrix
1547// on the (`&'static str`) row of this file now holds at every
1548// (INJECTIVITY, NONEMPTY) corner for the five outer-algebra arrays
1549// declared here. Analogous witnesses on the (`&'static str`) arrays
1550// declared under `crate::error` (`CompilerSpecIoStage::LABELS`,
1551// `MacroDefHead::KEYWORDS`, `MacroParams::LAMBDA_LIST_KEYWORDS`,
1552// `UnquoteForm::MARKERS` / `IAC_FORGE_TAGS` / `LABELS`,
1553// `KwargPathKind::LABELS`, `ExpectedKwargShape::LABELS`,
1554// `SexpShape::LABELS`, `StructuralKind::LABELS`) land co-located with
1555// the pre-existing `_pairwise_distinct` witnesses at that file's
1556// module-level prelude.
1557const _: () = assert_str_array_all_nonempty(&Atom::BOOL_LITERALS);
1558const _: () = assert_str_array_all_nonempty(&AtomKind::LABELS);
1559const _: () = assert_str_array_all_nonempty(&QuoteForm::PREFIXES);
1560const _: () = assert_str_array_all_nonempty(&QuoteForm::IAC_FORGE_TAGS);
1561const _: () = assert_str_array_all_nonempty(&QuoteForm::LABELS);
1562
1563/// Compile-time contract verifier — panics at const evaluation time if
1564/// any entry of `arr` carries a byte outside the seven-bit ASCII range
1565/// (`>= 0x80`, the first byte of any non-ASCII UTF-8 sequence).
1566///
1567/// Per-entry peer to [`assert_str_array_all_nonempty`] on the (per-
1568/// entry × contract-shape) axis of the (`&'static str`) row: where the
1569/// NONEMPTY sibling pins the length-lower-bound gate (`∀ i :
1570/// arr[i].len() > 0`), this ASCII sibling pins the byte-range
1571/// containment gate (`∀ i, ∀ b ∈ arr[i].as_bytes() : b <= 0x7F`). The
1572/// two contracts compose orthogonally — an array carrying `["café"]`
1573/// passes NONEMPTY while failing ASCII; an array carrying `["", "a"]`
1574/// passes ASCII while failing NONEMPTY. Together with the INJECTIVITY
1575/// sibling ([`assert_str_array_pairwise_distinct`]) the substrate's
1576/// (per-entry × contract-shape) coverage matrix on the (`&'static
1577/// str`) row closes at THREE corners: {NONEMPTY, ASCII, INJECTIVITY}
1578/// on the SAME five outer-algebra arrays declared here.
1579///
1580/// The invariant is load-bearing for every consumer that ships an
1581/// array entry through a downstream surface whose canonical form is
1582/// seven-bit-clean — Kubernetes annotation keys + label values (RFC
1583/// 1123 subset of ASCII); YAML flow-scalar map keys the reader-boundary
1584/// prefix vocabulary threads through the four-Lisp projection; BLAKE3
1585/// hash inputs on the three-pillar attestation chain (identical byte
1586/// sequences hash identically regardless of encoding, but authoring a
1587/// non-ASCII label silently invites U+FEFF BOMs and Unicode-normalization
1588/// drift on the wire); Rust `matches!(s, "quote" | …)` byte-pattern
1589/// arms the compiler lowers to `[u8]` comparison. Post-lift a
1590/// regression that silently re-inlined one label constant to a byte-
1591/// equivalent non-ASCII spelling (e.g. `AtomKind::SYMBOL_LABEL =
1592/// "sýmbol";`, a lookalike that would parse as a Rust `&'static str`
1593/// but ship non-ASCII bytes at every consumer) fails at `cargo check`
1594/// BEFORE any test scheduler runs.
1595///
1596/// Adding a new family-wide `[&'static str; N]` label / prefix / tag /
1597/// literal vocabulary whose canonical spelling is seven-bit-clean:
1598/// pair the declaration with `const _: () =
1599/// assert_str_array_all_ascii(&Self::FOO_ARRAY);` co-located after
1600/// the array's declaration and the ASCII-BYTE-RANGE contract binds at
1601/// compile time. The rustc-forced arity `[&'static str; N]` composes
1602/// with this const-eval sweep so BOTH cardinality AND per-entry ASCII
1603/// are compile-time theorems on the SAME array declaration.
1604///
1605/// Runtime callability: the function is a normal `pub const fn`, so
1606/// callers CAN also invoke it at runtime — pinned by
1607/// `assert_str_array_all_ascii_panics_at_runtime_on_head_non_ascii` /
1608/// `_interior_non_ascii` / `_tail_non_ascii` and
1609/// `assert_str_array_all_ascii_panic_message_names_the_helper_and_axis`.
1610/// The panic site carries the `"STR-NON-ASCII-ENTRY"` axis-provenance
1611/// string chosen DISTINCT from every sibling helper's axis vocabulary
1612/// (`"duplicate"` on the pairwise-distinct sibling; `"STR-EMPTY-
1613/// ENTRY"` on the per-entry NONEMPTY sibling; `"STR-DISJOINTNESS-
1614/// VIOLATION"` on the arrays-disjoint sibling; `"STR-SUBSET-
1615/// VIOLATION"` on the within-finite-set sibling) so a diagnostic that
1616/// names the failed axis routes UNAMBIGUOUSLY to THIS specific ASCII
1617/// helper.
1618///
1619/// Theory grounding:
1620/// - THEORY.md §V.1 — knowable platform; the family-wide per-entry
1621/// ASCII-byte-range contract on the substrate's `&'static str`
1622/// label vocabulary becomes a TYPE-LEVEL theorem the substrate
1623/// carries per array declaration rather than a runtime test the
1624/// developer must remember to write per label constant.
1625/// - THEORY.md §II.1 invariant 1 — typed entry; a closed-set variant's
1626/// label projection is the entry-point discriminator into the typed
1627/// algebra, and a non-ASCII byte in that projection silently
1628/// escapes the seven-bit-clean assumption every downstream wire
1629/// surface encodes into its own byte-level parser.
1630/// - THEORY.md §VI.1 — generation over composition; the const-eval
1631/// byte-range sweep IS the generative shape. Every new closed-set
1632/// label array adds ONE `const _` line to get the ASCII theorem
1633/// rather than re-deriving a per-array runtime iterator sweep at
1634/// each call site.
1635pub const fn assert_str_array_all_ascii<const N: usize>(arr: &[&'static str; N]) {
1636 let mut i = 0;
1637 while i < N {
1638 let bytes = arr[i].as_bytes();
1639 let mut j = 0;
1640 while j < bytes.len() {
1641 if bytes[j] > 0x7F {
1642 panic!(
1643 "assert_str_array_all_ascii: STR-NON-ASCII-ENTRY — \
1644 the family-wide &'static str array carries an \
1645 entry with a byte outside the seven-bit ASCII \
1646 range (>= 0x80) at some position — the \
1647 substrate's ASCII-BYTE-RANGE contract on the \
1648 array is broken; every consumer that ships an \
1649 entry through a seven-bit-clean downstream \
1650 surface (K8s annotation keys + label values; \
1651 YAML flow-scalar map keys; BLAKE3 hash inputs on \
1652 the three-pillar attestation chain; Rust \
1653 `matches!(s, ...)` byte-pattern arms) treats \
1654 each entry as ASCII — a non-ASCII byte silently \
1655 invites Unicode-normalization drift on the wire, \
1656 BOM injection, and lookalike-label collisions \
1657 that byte-equality parsing cannot detect. Fix at \
1658 the ARRAY-DECLARATION site by re-inlining the \
1659 offending label constant to its seven-bit-clean \
1660 canonical spelling"
1661 );
1662 }
1663 j += 1;
1664 }
1665 i += 1;
1666 }
1667}
1668
1669// Compile-time ASCII-BYTE-RANGE witnesses — one `const _: () =
1670// assert_str_array_all_ascii(&…)` per family-wide `[&'static str; N]`
1671// array on the substrate's closed-set outer algebras. Each invocation
1672// is const-evaluated at `cargo check` time; a regression that
1673// silently re-inlined one label constant to a lookalike non-ASCII
1674// spelling fails the build rather than deferring to a per-consumer
1675// byte-parse misbehavior at runtime. Sibling to the NONEMPTY witnesses
1676// above — those pin the per-entry length-lower-bound gate on each
1677// array, these pin the strictly-orthogonal per-entry byte-range gate
1678// on the SAME arrays. The two contracts compose orthogonally on every
1679// closed-set outer algebra's label vocabulary. The five arrays covered
1680// here mirror the five arrays already pinned by the `_pairwise_distinct`
1681// AND `_all_nonempty` witnesses above — the (per-entry × contract-shape)
1682// coverage matrix on the (`&'static str`) row of this file now holds
1683// at THREE corners {NONEMPTY, ASCII, INJECTIVITY} for the five outer-
1684// algebra arrays declared here. Analogous witnesses on the (`&'static
1685// str`) arrays declared under `crate::error` land co-located with the
1686// pre-existing `_pairwise_distinct` + `_all_nonempty` witnesses at
1687// that file's module-level prelude.
1688const _: () = assert_str_array_all_ascii(&Atom::BOOL_LITERALS);
1689const _: () = assert_str_array_all_ascii(&AtomKind::LABELS);
1690const _: () = assert_str_array_all_ascii(&QuoteForm::PREFIXES);
1691const _: () = assert_str_array_all_ascii(&QuoteForm::IAC_FORGE_TAGS);
1692const _: () = assert_str_array_all_ascii(&QuoteForm::LABELS);
1693
1694/// Compile-time contract verifier — panics at const evaluation time if
1695/// any entry of `a` aliases any entry of `b` byte-for-byte through
1696/// [`str::as_bytes`].
1697///
1698/// Row-dual peer to [`assert_char_arrays_disjoint`] and
1699/// [`assert_u8_arrays_disjoint`] on the (element-type) axis of the
1700/// (element-type × contract-shape) matrix at the (disjointness)
1701/// column: where the (char) sibling closes the reader-boundary char
1702/// DISJOINTNESS corner and the (u8) sibling closes the outer-`Sexp`
1703/// cache-key `u8` DISJOINTNESS corner at compile time, this (`&'static
1704/// str`) sibling closes the outer-algebras' family-wide `[&'static
1705/// str; N]` label / prefix / tag / literal DISJOINTNESS corner on the
1706/// SAME contract-shape column. Together with the pre-existing
1707/// [`assert_char_arrays_disjoint`] + [`assert_u8_arrays_disjoint`]
1708/// row-siblings the three helpers close the (element-type ∈
1709/// {char, u8, &'static str} × contract-shape ∈ {disjointness})
1710/// 3-corner row of the DISJOINTNESS column at ONE peer const-fn helper
1711/// per element-type. Contract-orthogonal peer to
1712/// [`assert_str_array_pairwise_distinct`] on the (INJECTIVITY,
1713/// DISJOINTNESS) axis of the (contract-shape) column on the SAME
1714/// (`&'static str`) row: where the pairwise-distinctness sibling binds
1715/// INTRA-array `∀ i ≠ j : arr[i] ≠ arr[j]`, this DISJOINTNESS sibling
1716/// binds INTER-array `∀ i, j : a[i] ≠ b[j]` — the two together give
1717/// every `&'static str` sub-vocabulary on the substrate BOTH intra-
1718/// array injectivity AND inter-array disjointness at compile time.
1719///
1720/// The invariant is load-bearing for every consumer that partitions
1721/// the two arrays' distinct-values sets into disjoint sub-vocabularies
1722/// of a shared outer surface —
1723/// [`QuoteForm::PREFIXES`] (`["'", "\`", ",", ",@"]` — reader-boundary
1724/// prefix tokens the tokenizer scans in [`crate::reader::tokenize`])
1725/// is intentionally-closed disjoint from
1726/// [`QuoteForm::LABELS`] (`["quote", "quasiquote", "unquote",
1727/// "unquote-splice"]` — human-diagnostic labels the error module
1728/// projects through [`QuoteForm::label`]) so a reader-boundary token
1729/// never aliases a diagnostic label spelling; the SAME `QuoteForm::
1730/// PREFIXES` array is disjoint from
1731/// [`QuoteForm::IAC_FORGE_TAGS`] (`["quote", "quasiquote", "unquote",
1732/// "unquote-splicing"]` — canonical iac-forge interop symbol heads the
1733/// `crate::interop` (removed) round-trip pins through
1734/// [`QuoteForm::from_iac_forge_tag`]) so the reader-boundary vocabulary
1735/// stays clean of the canonical-form serialization vocabulary; the
1736/// SAME `QuoteForm::PREFIXES` array is disjoint from
1737/// [`AtomKind::LABELS`] (`["symbol", "keyword", "string", "int",
1738/// "float", "bool"]` — atomic-payload kind labels) so a reader-boundary
1739/// prefix never aliases an atom-kind diagnostic label; AND
1740/// [`AtomKind::LABELS`] is intentionally-closed disjoint from
1741/// [`QuoteForm::LABELS`] so a diagnostic that identifies "the token is
1742/// an atom of kind X" never aliases "the token is a quote form of
1743/// kind Y". Every future `[&'static str; N]` pair on the substrate
1744/// whose distinct-values sets must remain disjoint sub-vocabularies of
1745/// a shared outer surface participates in the SAME compile-time
1746/// guarantee via one `const _` line per pair.
1747///
1748/// Pre-lift the four disjointness relations lived only as runtime
1749/// tests (`quote_form_prefixes_disjoint_from_quote_form_labels`,
1750/// `quote_form_prefixes_disjoint_from_iac_forge_tags`,
1751/// `quote_form_prefixes_disjoint_from_atom_kind_labels`,
1752/// `atom_kind_labels_disjoint_from_quote_form_labels`) or implicitly
1753/// through the outer-algebra's non-aliasing composition rule; post-
1754/// lift the ARRAY-LEVEL disjointness of the four pinned pairs binds at
1755/// rustc time via one `const _` line per pair. A regression that
1756/// silently renamed one of `QuoteForm::PREFIXES`'s entries to a string
1757/// that aliased a `QuoteForm::LABELS` / `QuoteForm::IAC_FORGE_TAGS` /
1758/// `AtomKind::LABELS` entry (or vice versa) fails at `cargo check`
1759/// BEFORE any test scheduler runs.
1760///
1761/// Adding a new `[&'static str; N]` sub-vocabulary whose distinct-
1762/// values set must remain disjoint from another substrate `[&'static
1763/// str; M]` array's distinct-values set: pair the declaration with
1764/// `const _: () = assert_str_arrays_disjoint::<N, M>(&Self::FOO_ARRAY,
1765/// &Other::BAR_ARRAY);` co-located after the array's declaration and
1766/// the DISJOINTNESS contract binds at compile time. The rustc-forced
1767/// arities `[&'static str; N]` and `[&'static str; M]` compose with
1768/// this const-eval sweep so BOTH cardinality-pair AND cross-array
1769/// disjointness are compile-time theorems on the SAME (a, b) str-array
1770/// pair.
1771///
1772/// Runtime callability: the function is a normal `pub const fn`, so
1773/// callers CAN also invoke it at runtime — pinned by
1774/// `assert_str_arrays_disjoint_panics_at_runtime_on_collision` and
1775/// `assert_str_arrays_disjoint_panic_message_names_the_helper_and_str_disjointness_violation_axis`.
1776/// The panic site carries the `"STR-DISJOINTNESS-VIOLATION"` axis-
1777/// provenance string chosen DISTINCT from every sibling helper's axis
1778/// vocabulary (`"duplicate"` on the ARRAY-side pairwise-distinct
1779/// sibling; `"CHAR-DISJOINTNESS-VIOLATION"` on the (char) row-dual
1780/// DISJOINTNESS sibling; `"U8-DISJOINTNESS-VIOLATION"` on the (u8)
1781/// row-dual DISJOINTNESS sibling; `"CHAR-SUBSET-VIOLATION"` on the
1782/// (char) SUBSET-embedding sibling; `"SUBSET-VIOLATION"` on the (u8)
1783/// finite-set SUBSET-only sibling; `"RANGE-SUBSET-VIOLATION"` on the
1784/// (u8) range SUBSET-only sibling; `"OUT-OF-SET"` / `"SET-BYTE-
1785/// MISSING"` on the (u8) covers-finite-set sibling; `"OUT-OF-RANGE"` /
1786/// `"MISSING"` on the (u8) covers-inclusive-range sibling; `"ARITY-
1787/// MISMATCH"` on both (u8) `_permutes_*` compound helpers; `"SET-NOT-
1788/// PAIRWISE-DISTINCT"` on the (u8) SET-side well-formedness sibling)
1789/// so a diagnostic that names the failed axis routes UNAMBIGUOUSLY to
1790/// THIS specific `&'static str` DISJOINTNESS helper. The `"STR-"`
1791/// prefix disambiguates from the (char) + (u8) row-dual DISJOINTNESS
1792/// siblings; the shared `"-DISJOINTNESS-VIOLATION"` suffix lets
1793/// callers grep any row's DISJOINTNESS sibling by
1794/// `"DISJOINTNESS-VIOLATION"` alone.
1795///
1796/// Byte-equality reuse: the helper delegates to the same module-
1797/// private [`str_bytes_equal`] const-fn helper the sibling
1798/// [`assert_str_array_pairwise_distinct`] uses — a single canonical
1799/// site for `&'static str` byte-equality in const context, so a future
1800/// toolchain stabilising `const fn str::eq` collapses BOTH callers at
1801/// ONE edit rather than two.
1802///
1803/// Theory grounding:
1804/// - THEORY.md §V.1 — knowable platform; the family-wide cross-array
1805/// disjointness contract on the substrate's `&'static str`
1806/// vocabulary becomes a TYPE-LEVEL theorem the substrate carries per
1807/// (a, b) str-array pair rather than a runtime test the developer
1808/// must remember to write per pair.
1809/// - THEORY.md §III — the typescape; the (element-type × contract-
1810/// shape) matrix now carries the DISJOINTNESS corner on THREE rows
1811/// ({char, u8, `&'static str`}) at ONE peer const-fn helper per row.
1812/// The (element-type ∈ {char, u8, `&'static str`}) × (contract-shape
1813/// ∈ {pairwise-distinctness (INJECTIVITY), disjointness}) 3×2 =
1814/// 6-corner face on the array-pair-contract prism is now closed at
1815/// SIX peer const-fn helpers.
1816/// - THEORY.md §VI.1 — generation over composition; the const-eval
1817/// cross-array byte-membership sweep IS the generative shape. Every
1818/// new `[&'static str; N]` sub-vocabulary array whose distinct-
1819/// values set is an intentionally-disjoint peer of another substrate
1820/// `[&'static str; M]` array adds ONE `const _` line to get the
1821/// disjointness theorem rather than re-deriving a per-pair runtime
1822/// iterator sweep at each call site.
1823/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
1824/// DISJOINTNESS proof at declaration site AND the outer-algebra's
1825/// arm-set partition contract (`QuoteForm::PREFIXES` on the reader-
1826/// boundary token surface vs. `QuoteForm::LABELS` on the human-
1827/// diagnostic label surface, etc.) regenerate through the SAME
1828/// `const _` witnesses at the ARRAY level.
1829///
1830/// Frontier inspiration: Lean 4's `Finset.disjoint_iff` unfolded to
1831/// `∀ a ∈ s, ∀ b ∈ t, a ≠ b` at the concrete two-array
1832/// `[&'static str; N] × [&'static str; M]` monomorphic realisation.
1833/// The (char, u8, `&'static str`) row triple mirrors Lean's
1834/// polymorphic `[DecidableEq α] → Finset α → Finset α → Prop`
1835/// realised at the three concrete element-type instantiations the
1836/// substrate closes at compile time.
1837pub const fn assert_str_arrays_disjoint<const N: usize, const M: usize>(
1838 a: &[&'static str; N],
1839 b: &[&'static str; M],
1840) {
1841 let mut i = 0;
1842 while i < N {
1843 let mut j = 0;
1844 while j < M {
1845 if str_bytes_equal(a[i], b[j]) {
1846 panic!(
1847 "assert_str_arrays_disjoint: STR-DISJOINTNESS-\
1848 VIOLATION — the two family-wide &'static str \
1849 arrays `a` and `b` share an entry at some (i, j) \
1850 position pair. The substrate's CROSS-ARRAY \
1851 DISJOINTNESS contract on the pair is broken; \
1852 every consumer that partitions the two arrays' \
1853 distinct-values sets into disjoint sub-\
1854 vocabularies of a shared outer surface (the \
1855 reader-boundary prefix vocabulary at \
1856 `QuoteForm::PREFIXES` vs the human-diagnostic \
1857 label vocabulary at `QuoteForm::LABELS`; the \
1858 reader-boundary prefix vocabulary vs the \
1859 canonical iac-forge tag vocabulary at \
1860 `QuoteForm::IAC_FORGE_TAGS`; the reader-boundary \
1861 prefix vocabulary vs the atomic-kind label \
1862 vocabulary at `AtomKind::LABELS`; the atomic-kind \
1863 label vocabulary vs the quote-family label \
1864 vocabulary; any future typed-disjointness pair on \
1865 the substrate's `&'static str` sub-vocabularies) \
1866 relies on the two arrays' distinct-values sets \
1867 NOT sharing an entry. Fix at WHICHEVER ARRAY-\
1868 DECLARATION site drifted (the symmetric \
1869 disjointness relation carries no built-in axis-\
1870 provenance role split between `a` and `b`) by \
1871 renaming the offending entry on one array OR re-\
1872 shaping the partition to route the shared entry \
1873 to a single sub-vocabulary"
1874 );
1875 }
1876 j += 1;
1877 }
1878 i += 1;
1879 }
1880}
1881
1882// Compile-time DISJOINTNESS witnesses — the FOUR substrate-pinned
1883// (a, b) `[&'static str; N] × [&'static str; M]` pairs whose distinct-
1884// values sets are intentionally-closed disjoint sub-vocabularies of a
1885// shared outer surface. Pre-lift the four disjointness relations lived
1886// only as runtime tests (or implicitly through the outer-algebra's
1887// non-aliasing composition rule); post-lift the ARRAY-LEVEL
1888// disjointness of the four pinned pairs binds at rustc time via one
1889// `const _` line per pair. A regression that silently renamed
1890// `QuoteForm::QUOTE_PREFIX` (`"'"`) to `"quote"` (aliasing
1891// `QuoteForm::QUOTE_LABEL` and `QuoteForm::QUOTE_IAC_FORGE_TAG`),
1892// renamed `AtomKind::SYMBOL_LABEL` (`"symbol"`) to `"quote"` (aliasing
1893// `QuoteForm::QUOTE_LABEL`), or drifted any entry of one array to
1894// bytes shared with an entry of a disjoint peer array fails at
1895// `cargo check` BEFORE any test scheduler runs. Sibling to the FIVE
1896// `assert_char_arrays_disjoint` witnesses and the TWO
1897// `assert_u8_arrays_disjoint` witnesses above on the (element-type)
1898// axis: `char` covers the reader-boundary char sub-vocabularies;
1899// `u8` covers the outer-`Sexp` cache-key discriminator sub-
1900// vocabularies; `&'static str` covers the outer-algebras' family-wide
1901// label / prefix / tag vocabularies.
1902//
1903// The four pinned pairs are (all under the shared `&'static str`
1904// element-type):
1905// 1. `QuoteForm::PREFIXES` ∩ `QuoteForm::LABELS` = ∅
1906// 2. `QuoteForm::PREFIXES` ∩ `QuoteForm::IAC_FORGE_TAGS` = ∅
1907// 3. `QuoteForm::PREFIXES` ∩ `AtomKind::LABELS` = ∅
1908// 4. `AtomKind::LABELS` ∩ `QuoteForm::LABELS` = ∅
1909//
1910// The TWO remaining disjointness pairs on the twelve-arm
1911// `SexpShape::LABELS` partition triple
1912// (`AtomKind::LABELS`, `QuoteForm::LABELS`, `StructuralKind::LABELS`)
1913// — namely (`AtomKind::LABELS`, `StructuralKind::LABELS`) and
1914// (`QuoteForm::LABELS`, `StructuralKind::LABELS`) — are pinned as
1915// peer `const _` lines in `error.rs` (the file where the
1916// `StructuralKind` host type lives). Split by host-file, unified in
1917// theorem: together with pair 4 above they cover every pair on the
1918// three-element sub-vocabulary triple (C(3, 2) = 3), closing the
1919// DISJOINT-UNION proof
1920// `SexpShape::LABELS ≡ AtomKind::LABELS ⊕ QuoteForm::LABELS ⊕
1921// StructuralKind::LABELS`
1922// at compile time.
1923//
1924// Note on the intentionally-NOT-pinned pair
1925// (`QuoteForm::LABELS`, `QuoteForm::IAC_FORGE_TAGS`): the two
1926// deliberately OVERLAP on three of four arms (`"quote"`, `"quasiquote"`,
1927// `"unquote"`) because both surfaces spell those three quote-family
1928// heads with the Common-Lisp-canonical name — `QuoteForm::LABELS` for
1929// diagnostics, `QuoteForm::IAC_FORGE_TAGS` for canonical serialization.
1930// Only the fourth arm differs (`QuoteForm::UNQUOTE_SPLICE_LABEL` at
1931// `"unquote-splice"` vs `QuoteForm::UNQUOTE_SPLICE_IAC_FORGE_TAG` at
1932// `"unquote-splicing"`) so the pair is NOT disjoint and would fail
1933// this witness. Pinning it here would be a category error — the
1934// overlap is load-bearing, not accidental.
1935const _: () = assert_str_arrays_disjoint::<4, 4>(&QuoteForm::PREFIXES, &QuoteForm::LABELS);
1936const _: () = assert_str_arrays_disjoint::<4, 4>(&QuoteForm::PREFIXES, &QuoteForm::IAC_FORGE_TAGS);
1937const _: () = assert_str_arrays_disjoint::<4, 6>(&QuoteForm::PREFIXES, &AtomKind::LABELS);
1938const _: () = assert_str_arrays_disjoint::<6, 4>(&AtomKind::LABELS, &QuoteForm::LABELS);
1939
1940/// Compile-time contract verifier — panics at const evaluation time if
1941/// any entry of `arr` is NOT a member of `set` (byte-for-byte through
1942/// [`str::as_bytes`]).
1943///
1944/// Row-dual peer to [`assert_char_array_within_char_finite_set`] and
1945/// [`assert_u8_array_within_u8_finite_set`] on the (element-type) axis
1946/// of the (element-type × contract-shape) matrix at the (subset-
1947/// embedding) column: where the (char) sibling closes the reader-
1948/// boundary `[char; N] ⊆ [char; M]` corner and the (u8) sibling closes
1949/// the outer-`Sexp` cache-key `[u8; N] ⊆ [u8; M]` finite-set embedding
1950/// corner at compile time, this (`&'static str`) sibling closes the
1951/// outer-algebras' family-wide `[&'static str; N] ⊆ [&'static str; M]`
1952/// label / prefix / tag SUB-VOCABULARY carving corner. Together with
1953/// the pre-existing [`assert_char_array_within_char_finite_set`] +
1954/// [`assert_u8_array_within_u8_finite_set`] row-siblings the three
1955/// helpers close the (element-type ∈ {char, u8, &'static str} ×
1956/// contract-shape ∈ {subset-embedding}) 3-corner row of the SUBSET-
1957/// EMBEDDING column at ONE peer const-fn helper per element-type.
1958/// Contract-orthogonal peer to [`assert_str_array_pairwise_distinct`]
1959/// and [`assert_str_arrays_disjoint`] on the (INJECTIVITY,
1960/// DISJOINTNESS, SUBSET-EMBEDDING) axis of the (contract-shape) column
1961/// on the SAME (`&'static str`) row: where the pairwise-distinctness
1962/// sibling binds INTRA-array `∀ i ≠ j : arr[i] ≠ arr[j]` and the
1963/// disjointness sibling binds INTER-array `∀ i, j : a[i] ≠ b[j]`, this
1964/// SUBSET-EMBEDDING sibling binds ORIENTED-INTER-array `∀ i : ∃ j :
1965/// arr[i] = set[j]` — the three together give every `&'static str`
1966/// sub-vocabulary on the substrate INTRA-array injectivity AND INTER-
1967/// array disjointness (symmetric) AND INTER-array subset embedding
1968/// (oriented) at compile time.
1969///
1970/// The invariant is load-bearing for every consumer that carves a
1971/// SUB-vocabulary of a shared OUTER `&'static str` vocabulary:
1972/// [`AtomKind::LABELS`] (`[&; 6]`, `["symbol", "keyword", "string",
1973/// "int", "float", "bool"]` — the atomic-payload kind labels) is an
1974/// intentionally-closed proper subset of
1975/// [`crate::error::SexpShape::LABELS`] (`[&; 12]`, the twelve outer-
1976/// `Sexp` shape labels; six atomic + two structural + four quote-
1977/// family) so every atom-kind diagnostic label stays inside the outer-
1978/// shape label vocabulary; [`QuoteForm::LABELS`] (`[&; 4]`, `["quote",
1979/// "quasiquote", "unquote", "unquote-splice"]` — the quote-family
1980/// labels) is likewise a proper subset of the SAME
1981/// [`crate::error::SexpShape::LABELS`] so every quote-family diagnostic
1982/// stays inside the outer-shape vocabulary; and
1983/// [`crate::error::StructuralKind::LABELS`] (`[&; 2]`, `["nil",
1984/// "list"]` — the structural-shape labels) is the third proper subset
1985/// of the SAME twelve-arm superset. Union together `AtomKind::LABELS`
1986/// (6) + `QuoteForm::LABELS` (4) + `StructuralKind::LABELS` (2) = 12
1987/// = `SexpShape::LABELS.len()` closes a NON-CONTIGUOUS PARTITION of
1988/// the outer twelve-arm vocabulary at compile time; the three
1989/// SUBSET-EMBEDDING witnesses PLUS the pre-existing pairwise-
1990/// distinctness witnesses PLUS the pre-existing (AtomKind, QuoteForm)
1991/// disjointness witness compose to a full partition proof. Every
1992/// future `[&'static str; N]` sub-vocabulary on the substrate whose
1993/// distinct-values set must remain embedded in a shared outer surface
1994/// (a new tokenizer keyword vocabulary embedded in an outer prefix
1995/// vocabulary; a new diagnostic label vocabulary embedded in an outer
1996/// error-family label vocabulary; a new algebra whose display / label
1997/// / prefix arrays must remain sub-vocabularies of the closed-set
1998/// outer algebras' `&'static str` surface) gets the subset-embedding
1999/// theorem at ONE `const _` line rather than a per-pair runtime
2000/// iterator sweep.
2001///
2002/// SET-side well-formedness is DELEGATED to the sibling ARRAY-side
2003/// pairwise-distinctness helper ([`assert_str_array_pairwise_distinct`])
2004/// via a co-located call at the TOP of the sweep — a malformed `set`
2005/// (e.g. `["a", "a", "b"]`) is NOT a well-formed finite set of
2006/// cardinality `M` and silently mis-verifies the intended subset
2007/// contract on any `arr` embedded in the DISTINCT-value subset. The
2008/// delegated arm routes drift on the CALLER'S TARGET-SET SPEC to the
2009/// SET-side well-formedness axis rather than to a downstream STR-
2010/// SUBSET-VIOLATION symptom on `arr`. A well-formed `set` passes this
2011/// arm as a no-op — the sweep is const-eval-elidable and costs zero
2012/// at rustc-time on the substrate call sites. The (str) row does NOT
2013/// carry a separate `assert_str_finite_set_pairwise_distinct` alias
2014/// (the (u8) row's `assert_u8_finite_set_pairwise_distinct` is the
2015/// only SET-side well-formedness peer on the substrate); the
2016/// delegation reuses the ARRAY-side helper directly, matching the
2017/// (char) row's (`assert_char_array_within_char_finite_set` →
2018/// `assert_char_array_pairwise_distinct`) delegation shape.
2019///
2020/// Pre-lift the three subset embeddings lived as prose in the parent
2021/// arrays' partition-rule docstrings (the `SexpShape::LABELS` twelve-
2022/// arm decomposition into atomic / structural / quote-family sub-
2023/// vocabularies) and as runtime `_pairwise_distinct` cross-checks on
2024/// the tests submodule — the ARRAY-LEVEL embedding was not itself
2025/// pinned. Post-lift the three witnesses bind at rustc time via one
2026/// `const _` line per pair; a regression that silently re-inlined
2027/// either the SUBSET side (dropping `AtomKind::SYMBOL_LABEL`'s
2028/// `SexpShape::SYMBOL_LABEL` alias to a fresh distinct byte spelling)
2029/// or the SUPERSET side (dropping `SexpShape::SYMBOL_LABEL` and
2030/// leaving `AtomKind::SYMBOL_LABEL` as a stale copy of `"symbol"`)
2031/// fails at `cargo check` BEFORE any test scheduler runs.
2032///
2033/// Adding a new family-wide `[&'static str; N]` sub-vocabulary whose
2034/// distinct-values set must remain embedded in another substrate
2035/// `[&'static str; M]` array's distinct-values set: pair the
2036/// declaration with `const _: () = assert_str_array_within_str_finite_
2037/// set::<N, M>(&Self::FOO_ARRAY, &Other::BAR_ARRAY);` co-located after
2038/// the array's declaration and the SUBSET-EMBEDDING contract binds at
2039/// compile time. The rustc-forced arities `[&'static str; N]` and
2040/// `[&'static str; M]` compose with this const-eval sweep so BOTH
2041/// cardinality-pair AND cross-array subset embedding are compile-time
2042/// theorems on the SAME (arr, set) str-array pair.
2043///
2044/// Delegates to the existing module-private [`str_bytes_equal`] const-
2045/// fn helper so a future toolchain stabilising `const fn str::eq`
2046/// collapses ALL THREE (str)-row helpers ((str, pairwise-distinct),
2047/// (str, disjointness), (str, subset-embedding)) through ONE edit.
2048///
2049/// Runtime callability: the function is a normal `pub const fn`, so
2050/// callers CAN also invoke it at runtime — pinned by
2051/// `assert_str_array_within_str_finite_set_panics_at_runtime_on_out_of_set_entry`
2052/// and
2053/// `assert_str_array_within_str_finite_set_panic_message_names_the_helper_and_str_subset_violation_axis`.
2054/// The panic site carries the `"STR-SUBSET-VIOLATION"` axis-provenance
2055/// string chosen DISTINCT from every sibling helper's axis vocabulary
2056/// (`"duplicate"` on the ARRAY-side pairwise-distinct sibling; `"STR-
2057/// DISJOINTNESS-VIOLATION"` on the (str) row DISJOINTNESS sibling;
2058/// `"CHAR-SUBSET-VIOLATION"` on the (char) row-dual SUBSET sibling;
2059/// `"SUBSET-VIOLATION"` on the (u8) row-dual finite-set SUBSET-only
2060/// sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8) range SUBSET-only
2061/// sibling; `"CHAR-DISJOINTNESS-VIOLATION"` / `"U8-DISJOINTNESS-
2062/// VIOLATION"` on the (char) / (u8) row-dual DISJOINTNESS siblings;
2063/// `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the (u8) covers-finite-
2064/// set sibling; `"OUT-OF-RANGE"` / `"MISSING"` on the (u8) covers-
2065/// inclusive-range sibling; `"ARITY-MISMATCH"` on both (u8)
2066/// `_permutes_*` compound helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on
2067/// the (u8) SET-side well-formedness sibling) so a diagnostic that
2068/// names the failed axis routes UNAMBIGUOUSLY to (a) this specific
2069/// `&'static str` SUBSET-embedding helper, (b) the `arr` argument as
2070/// the drift site rather than the `set` argument specifying the
2071/// target superset. The `"STR-"` prefix disambiguates from the
2072/// (char) + (u8) row-dual peers; the shared `"-SUBSET-VIOLATION"`
2073/// suffix lets callers grep any row's SUBSET-embedding sibling by
2074/// the shared `"SUBSET-VIOLATION"` suffix alone.
2075///
2076/// Theory grounding:
2077/// - THEORY.md §V.1 — knowable platform; the family-wide cross-array
2078/// subset-embedding contract on `&'static str` sub-vocabularies
2079/// becomes a TYPE-LEVEL theorem the substrate carries per (arr,
2080/// set) str-array pair rather than a runtime test the developer
2081/// must remember to write per pair.
2082/// - THEORY.md §III — the typescape; the (element-type × contract-
2083/// shape) matrix now carries the SUBSET-EMBEDDING corner on THREE
2084/// rows at ONE peer const-fn helper per row. The (subset,
2085/// disjointness) 2-corner face on the (`&'static str`) row is now
2086/// closed at TWO peer const-fn helpers —
2087/// [`assert_str_arrays_disjoint`] on the DISJOINTNESS corner and
2088/// this helper on the SUBSET corner.
2089/// - THEORY.md §VI.1 — generation over composition; the const-eval
2090/// cross-array membership sweep IS the generative shape. Every new
2091/// closed-set `&'static str` sub-vocabulary array whose distinct-
2092/// values set is an intentionally-embedded proper subset of another
2093/// substrate `&'static str` array adds ONE `const _` line to get
2094/// the subset-embedding theorem rather than re-deriving a per-pair
2095/// runtime iterator sweep at each call site.
2096/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
2097/// SUBSET-EMBEDDING proof at declaration site AND the outer-
2098/// algebra's twelve-arm shape-label partition contract regenerate
2099/// through the SAME `const _` witnesses at the ARRAY level.
2100///
2101/// Frontier inspiration: Lean 4's `Finset.subset_iff : s ⊆ t ↔ ∀ a ∈
2102/// s, a ∈ t` unfolded at the concrete `[&'static str; N] ⊆ [&'static
2103/// str; M]` monomorphic realisation — the substrate primitive here
2104/// embeds the same subset relation as a rustc const-eval-time proof
2105/// obligation at every `assert_str_array_within_str_finite_set` call
2106/// site rather than as a Lean tactic invocation deferred to
2107/// `elab_command`. The (char, u8, `&'static str`) row triple mirrors
2108/// Lean's polymorphic `[DecidableEq α] → Finset α → Finset α → Prop`
2109/// realised at three concrete element-type instantiations the
2110/// substrate closes at compile time.
2111pub const fn assert_str_array_within_str_finite_set<const N: usize, const M: usize>(
2112 arr: &[&'static str; N],
2113 set: &[&'static str; M],
2114) {
2115 // Delegate target-set well-formedness to the sibling ARRAY-side
2116 // pairwise-distinctness helper FIRST. Placed BEFORE the STR-
2117 // SUBSET-VIOLATION sweep below because a malformed `set` (e.g.
2118 // `["a", "a", "b"]`) is not a well-formed finite set of
2119 // cardinality `M` and silently mis-verifies the intended subset
2120 // contract on any `arr` embedded in the DISTINCT-value subset.
2121 // Routes drift on the CALLER'S TARGET-SET SPEC to the SET-side
2122 // well-formedness axis (via the sibling's own panic-name prefix)
2123 // rather than to a downstream STR-SUBSET-VIOLATION symptom on
2124 // `arr`. A well-formed `set` passes this arm as a no-op — the
2125 // sweep is const-eval-elidable and costs zero at rustc-time on
2126 // the substrate call sites.
2127 assert_str_array_pairwise_distinct(set);
2128 let mut i = 0;
2129 while i < N {
2130 let mut j = 0;
2131 let mut found = false;
2132 while j < M {
2133 if str_bytes_equal(arr[i], set[j]) {
2134 found = true;
2135 break;
2136 }
2137 j += 1;
2138 }
2139 if !found {
2140 panic!(
2141 "assert_str_array_within_str_finite_set: STR-SUBSET-\
2142 VIOLATION — the family-wide &'static str array `arr` \
2143 carries an entry at some position whose bytes are \
2144 NOT a member of the target finite superset partition \
2145 `set`. The substrate's SUBSET-EMBEDDING contract on \
2146 the array is broken; every consumer that expects the \
2147 array's distinct-value set to be a subset of the \
2148 target finite partition (`AtomKind::LABELS ⊂ \
2149 SexpShape::LABELS` on the atomic-payload sub-\
2150 vocabulary carve of the twelve-arm outer-shape label \
2151 vocabulary; `QuoteForm::LABELS ⊂ SexpShape::LABELS` \
2152 on the quote-family sub-vocabulary carve of the SAME \
2153 twelve-arm outer-shape label vocabulary; \
2154 `StructuralKind::LABELS ⊂ SexpShape::LABELS` on the \
2155 structural sub-vocabulary carve of the SAME twelve-\
2156 arm outer-shape label vocabulary; any future typed-\
2157 subset embedding on the substrate's `&'static str` \
2158 sub-vocabularies) relies on every array entry \
2159 staying within the target superset. Fix at the \
2160 ARRAY-DECLARATION site (the `arr` under \
2161 verification, NOT the `set` argument specifying the \
2162 target superset) by dropping the offending entry OR \
2163 by extending `set` to cover it — the choice depends \
2164 on whether the drift is an unintended overshoot \
2165 outside the parent superset or an intentional \
2166 extension of the superset vocabulary"
2167 );
2168 }
2169 i += 1;
2170 }
2171}
2172
2173/// Compile-time contract verifier — panics at const evaluation time if
2174/// any entry of the target finite `set` is NOT reached by at least one
2175/// entry across the three sub-vocabulary arrays `a`, `b`, `c`.
2176///
2177/// SURJECTIVITY dual of [`assert_str_array_within_str_finite_set`] at
2178/// the (`&'static str`) row of the (element-type × contract-shape)
2179/// matrix, extended to the 3-array-union carrier shape: where the
2180/// within-helper closes the SUBSET direction for a single array
2181/// (`arr ⊆ set`), this helper closes the COVERAGE direction for a
2182/// three-array partition triple (`set ⊆ a ∪ b ∪ c`) at compile time.
2183/// Composed with the three sibling SUBSET-EMBEDDING witnesses (each
2184/// sub-array's `_within_str_finite_set` pin) AND the three sibling
2185/// pairwise-DISJOINTNESS witnesses (each pair's
2186/// `assert_str_arrays_disjoint` pin) AND the parent's INJECTIVITY
2187/// witness (`_pairwise_distinct` on the parent set), the four
2188/// contract-shape corners jointly close the full DISJOINT-UNION
2189/// theorem `set ≡ a ⊕ b ⊕ c` at rustc const-eval time on the
2190/// substrate's twelve-arm `SexpShape::LABELS` outer-vocabulary
2191/// partition — the pre-existing runtime cross-check
2192/// `sexp_shape_labels_is_disjoint_union_of_three_sub_vocabularies`
2193/// becomes a defense-in-depth safety net for the SAME theorem the
2194/// compile-time witness triple now enforces at `cargo check` time,
2195/// one invocation stage earlier.
2196///
2197/// Delegates target-set well-formedness to the ARRAY-side
2198/// [`assert_str_array_pairwise_distinct`] helper via a co-located
2199/// call at the TOP of the sweep — a malformed `set` (e.g.
2200/// `["a", "a", "b"]`) is not a well-formed finite set of cardinality
2201/// `W` and silently mis-verifies the intended COVERAGE contract on
2202/// any `(a, b, c)` triple whose distinct-value union misses the
2203/// duplicated set byte (the duplicated byte still counts as covered
2204/// on the FIRST hit even if the second copy is absent from the
2205/// union). Routes drift on the CALLER'S TARGET-SET SPEC to the SET-
2206/// side well-formedness axis rather than to a downstream SET-STR-
2207/// MISSING symptom on `(a, b, c)`. The (str) row does NOT carry a
2208/// separate `assert_str_finite_set_pairwise_distinct` alias; the
2209/// delegation reuses the ARRAY-side helper directly, matching the
2210/// (str) row's sibling delegation shape at
2211/// [`assert_str_array_within_str_finite_set`].
2212///
2213/// The sub-vocabulary arities `N`, `M`, `K` and the parent
2214/// cardinality `W` are INDEPENDENT const generics: this helper
2215/// intentionally does NOT enforce `N + M + K == W` at const-eval
2216/// time. That arity sum is a CONSEQUENCE of the disjoint-union
2217/// theorem (COVERAGE here + pairwise DISJOINTNESS at the sibling
2218/// witnesses + parent INJECTIVITY) rather than a separate pre-
2219/// condition; a caller that binds all three peer witnesses AND this
2220/// COVERAGE witness AND finds a cardinality mismatch has necessarily
2221/// broken one of the four contract corners, and the diagnostic fires
2222/// on the specific corner that broke rather than on a synthetic
2223/// arity-sum pre-check. Under-covering triples (`N + M + K < W`) fire
2224/// the SET-STR-MISSING panic here; over-covering triples
2225/// (`N + M + K > W`) fire the STR-DISJOINTNESS-VIOLATION panic at the
2226/// sibling pairwise-disjointness witness or the STR-SUBSET-VIOLATION
2227/// panic at the sibling `_within_str_finite_set` witness. The four-
2228/// corner diagnostic partition stays sharp on the FAILURE mode rather
2229/// than collapsing every drift onto a single arity-sum axis.
2230///
2231/// Pre-lift the twelve-arm `SexpShape::LABELS` disjoint-union
2232/// theorem lived as a runtime cross-check
2233/// (`sexp_shape_labels_is_disjoint_union_of_three_sub_vocabularies`
2234/// at `error.rs` tests module) that iterated every parent label and
2235/// counted its multiplicity across the three sub-vocabularies. The
2236/// runtime sweep enforced BOTH directions (⊆) and (⊇) of the
2237/// disjoint-union at test time; the (⊇) direction was already lifted
2238/// to compile time via three `_within_str_finite_set` witnesses in
2239/// `error.rs` (each sub-vocab ⊂ parent) and three
2240/// `assert_str_arrays_disjoint` witnesses (pairwise disjoint), but
2241/// the (⊆) direction — every parent label appears in at least one
2242/// sub-vocabulary — remained runtime-only. Post-lift this helper
2243/// binds the (⊆) direction at `cargo check` time via ONE `const _`
2244/// line on the (`AtomKind::LABELS`, `QuoteForm::LABELS`,
2245/// `StructuralKind::LABELS`, `SexpShape::LABELS`) partition-triple-
2246/// with-parent quadruple; a regression that silently drops a variant
2247/// from one of the three sub-vocabularies (e.g. removing
2248/// `AtomKind::Bool` and its `AtomKind::BOOL_LABEL` alias while
2249/// leaving `SexpShape::Bool` and its `SexpShape::BOOL_LABEL` alias in
2250/// the parent vocabulary) fires the SET-STR-MISSING panic at
2251/// `cargo check` BEFORE any test scheduler runs.
2252///
2253/// Adding a new n-way partition proof on the substrate's `&'static
2254/// str` vocabularies (e.g. a hypothetical fourth sub-vocabulary
2255/// carving of a widened `SexpShape` parent, or an independent
2256/// partition on `Atom::ESCAPE_SOURCES` into printable / whitespace /
2257/// control sub-vocabularies): pair the parent+partition declaration
2258/// with `const _: () = assert_str_finite_set_covered_by_three_str_
2259/// arrays::<N, M, K, W>(&Foo::LABELS, &Bar::LABELS, &Baz::LABELS,
2260/// &Parent::LABELS);` co-located after the partition arrays'
2261/// declarations and the COVERAGE contract binds at compile time. The
2262/// rustc-forced arities `[&'static str; N]`, `[&'static str; M]`,
2263/// `[&'static str; K]`, `[&'static str; W]` compose with this const-
2264/// eval sweep so BOTH the four cardinalities AND the (⊆) disjoint-
2265/// union direction are compile-time theorems on the SAME partition
2266/// quadruple.
2267///
2268/// Delegates to the existing module-private [`str_bytes_equal`]
2269/// const-fn helper so a future toolchain stabilising `const fn
2270/// str::eq` collapses ALL FOUR (str)-row helpers ((str, pairwise-
2271/// distinct), (str, disjointness), (str, subset-embedding), (str,
2272/// three-array-coverage)) through ONE edit.
2273///
2274/// Runtime callability: the function is a normal `pub const fn`, so
2275/// callers CAN also invoke it at runtime — pinned by
2276/// `assert_str_finite_set_covered_by_three_str_arrays_panics_at_runtime_on_uncovered_parent_entry`
2277/// and
2278/// `assert_str_finite_set_covered_by_three_str_arrays_panic_message_names_the_helper_and_set_str_missing_axis`.
2279/// The panic site carries the `"SET-STR-MISSING"` axis-provenance
2280/// string chosen DISTINCT from every sibling helper's axis vocabulary
2281/// (`"duplicate"` on the ARRAY-side pairwise-distinct sibling; `"STR-
2282/// SUBSET-VIOLATION"` on the (str) row SUBSET sibling; `"STR-
2283/// DISJOINTNESS-VIOLATION"` on the (str) row DISJOINTNESS sibling;
2284/// `"CHAR-SUBSET-VIOLATION"` on the (char) row-dual SUBSET sibling;
2285/// `"SUBSET-VIOLATION"` on the (u8) row-dual finite-set SUBSET-only
2286/// sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the (u8) covers-
2287/// finite-set sibling; `"OUT-OF-RANGE"` / `"MISSING"` on the (u8)
2288/// covers-inclusive-range sibling; `"ARITY-MISMATCH"` on both (u8)
2289/// `_permutes_*` compound helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on
2290/// the (u8) SET-side well-formedness sibling) so a diagnostic that
2291/// names the failed axis routes UNAMBIGUOUSLY to (a) this specific
2292/// three-array COVERAGE helper on the `&'static str` element-type
2293/// row, (b) the `set` argument as the drift-target (the parent
2294/// element that no sub-array reaches) rather than any single sub-
2295/// vocabulary carrier. The `"SET-STR-MISSING"` axis distinguishes
2296/// from the (u8) row's `"SET-BYTE-MISSING"` peer via the element-
2297/// type infix — one substring search per element-type routes any
2298/// row's SET-side COVERAGE-VIOLATION back to its element-type peer.
2299///
2300/// Theory grounding:
2301/// - THEORY.md §V.1 — knowable platform; the DISJOINT-UNION theorem
2302/// on `&'static str` sub-vocabulary partitions becomes a TYPE-LEVEL
2303/// theorem the substrate carries per (partition-triple, parent)
2304/// quadruple rather than a runtime test the developer must
2305/// remember to write per partition.
2306/// - THEORY.md §III — the typescape; the (element-type × contract-
2307/// shape) matrix now carries the THREE-ARRAY-COVERAGE corner on
2308/// the (`&'static str`) row at ONE peer const-fn helper. Combined
2309/// with the pre-existing (str, pairwise-distinct), (str, subset-
2310/// embedding), and (str, disjointness) siblings, the four contract-
2311/// shape corners on the (`&'static str`) row jointly close the
2312/// full DISJOINT-UNION theorem on any n-way partition of a
2313/// `&'static str` vocabulary at compile time.
2314/// - THEORY.md §VI.1 — generation over composition; the const-eval
2315/// coverage sweep IS the generative shape. Every new n-way `&'static
2316/// str` vocabulary partition adds ONE `const _` line (plus the
2317/// sibling SUBSET-EMBEDDING and pairwise-DISJOINTNESS witnesses per
2318/// sub-array pair) to get the DISJOINT-UNION theorem rather than
2319/// re-deriving a per-partition runtime iterator sweep at each site.
2320/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
2321/// COVERAGE proof at declaration site AND the outer-algebra's
2322/// twelve-arm shape-label partition regenerate through the SAME
2323/// `const _` witness at the ARRAY level.
2324///
2325/// Frontier inspiration: Lean 4's `Finset.disjUnion` /
2326/// `Finset.biUnion_eq_iff_forall_mem_exists` unfolded at the
2327/// concrete 3-arm `[&'static str; W] ⊆ [&'static str; N] ∪ [&'static
2328/// str; M] ∪ [&'static str; K]` monomorphic realisation — the
2329/// substrate primitive here embeds the same coverage relation as a
2330/// rustc const-eval-time proof obligation at every partition site
2331/// rather than as a Lean tactic invocation deferred to `elab_command`.
2332pub const fn assert_str_finite_set_covered_by_three_str_arrays<
2333 const N: usize,
2334 const M: usize,
2335 const K: usize,
2336 const W: usize,
2337>(
2338 a: &[&'static str; N],
2339 b: &[&'static str; M],
2340 c: &[&'static str; K],
2341 set: &[&'static str; W],
2342) {
2343 // Delegate target-set well-formedness to the sibling ARRAY-side
2344 // pairwise-distinctness helper FIRST. Placed BEFORE the SET-STR-
2345 // MISSING sweep below because a malformed `set` (e.g. `["a", "a",
2346 // "b"]`) is not a well-formed finite set of cardinality `W` and
2347 // silently mis-verifies the intended COVERAGE contract — the
2348 // duplicated byte still counts as covered on the FIRST hit even
2349 // if the second copy is absent from the union. Routes drift on
2350 // the CALLER'S TARGET-SET SPEC to the SET-side well-formedness
2351 // axis rather than to a downstream SET-STR-MISSING symptom on
2352 // `(a, b, c)`. A well-formed `set` passes this arm as a no-op —
2353 // the sweep is const-eval-elidable and costs zero at rustc-time
2354 // on the substrate call sites.
2355 assert_str_array_pairwise_distinct(set);
2356 let mut w = 0;
2357 while w < W {
2358 let target = set[w];
2359 let mut found = false;
2360 let mut i = 0;
2361 while i < N {
2362 if str_bytes_equal(target, a[i]) {
2363 found = true;
2364 break;
2365 }
2366 i += 1;
2367 }
2368 if !found {
2369 let mut j = 0;
2370 while j < M {
2371 if str_bytes_equal(target, b[j]) {
2372 found = true;
2373 break;
2374 }
2375 j += 1;
2376 }
2377 }
2378 if !found {
2379 let mut k = 0;
2380 while k < K {
2381 if str_bytes_equal(target, c[k]) {
2382 found = true;
2383 break;
2384 }
2385 k += 1;
2386 }
2387 }
2388 if !found {
2389 panic!(
2390 "assert_str_finite_set_covered_by_three_str_arrays: \
2391 SET-STR-MISSING — the target finite `set` carries a \
2392 parent entry at some position whose bytes are NOT \
2393 reached by any of the three sub-vocabulary arrays \
2394 `a`, `b`, `c`. The substrate's THREE-ARRAY COVERAGE \
2395 contract on the partition-triple is broken; every \
2396 consumer that expects the union `a ∪ b ∪ c` to span \
2397 the parent finite vocabulary (`SexpShape::LABELS ≡ \
2398 AtomKind::LABELS ⊕ QuoteForm::LABELS ⊕ \
2399 StructuralKind::LABELS` on the twelve-arm outer-\
2400 shape label partition; any future n-way disjoint-\
2401 union theorem on the substrate's `&'static str` \
2402 vocabularies) relies on every parent entry being \
2403 reached by at least one sub-array. Fix at the SUB-\
2404 VOCABULARY DECLARATION site (the missing entry is \
2405 an intentional variant of the parent that ONE sub-\
2406 vocabulary must carry) OR at the PARENT-VOCABULARY \
2407 DECLARATION site (the missing entry was inadvertently \
2408 added to the parent without extending any sub-\
2409 vocabulary) — the choice depends on whether the \
2410 drift is an unintended parent overshoot or an \
2411 unintended sub-vocabulary shrinkage"
2412 );
2413 }
2414 w += 1;
2415 }
2416}
2417
2418/// Compile-time contract verifier — panics at const evaluation time if
2419/// `arr` is NOT the concatenation of `K` byte-verbatim replicas of
2420/// `head` followed by `N - K` byte-verbatim replicas of `tail` on the
2421/// substrate's family-wide `[&'static str; N]` MANY-TO-ONE variant →
2422/// canonical-projection vocabulary. Binds ONE conjunct clause: BLOCK-
2423/// CONSTANCY-VIOLATION — every entry in the HEAD segment `arr[0..K)`
2424/// MUST byte-equal `head` and every entry in the TAIL segment
2425/// `arr[K..N)` MUST byte-equal `tail`.
2426///
2427/// Contract-orthogonal peer to [`assert_str_array_pairwise_distinct`]
2428/// on the (INJECTIVITY, MANY-TO-ONE-BLOCK-CONSTANCY) axis of the
2429/// (contract-shape) column on the SAME (`&'static str`) row: where the
2430/// pairwise-distinctness sibling binds `∀ i ≠ j : arr[i] ≠ arr[j]` at
2431/// compile time (INTRA-ARRAY INJECTIVITY on arrays whose per-index
2432/// projection is bijective with a per-index typed source), this BLOCK-
2433/// CONSTANCY sibling binds `arr = [head; K] ++ [tail; N - K]` at
2434/// compile time (INTRA-ARRAY BLOCK-CONSTANT MANY-TO-ONE PROJECTION on
2435/// arrays whose per-index projection is a MANY-TO-ONE pattern
2436/// collapsing multiple contiguous typed sources onto ONE canonical
2437/// scalar byte). The two helpers close the (INJECTIVITY, MANY-TO-ONE-
2438/// BLOCK-CONSTANCY) 2-corner face on the (`&'static str`) row at ONE
2439/// peer const-fn helper per structural shape; a single family-wide
2440/// `[&'static str; N]` array picks whichever helper matches its
2441/// per-index projection cardinality (a BIJECTIVE per-index projection
2442/// binds the pairwise-distinct sibling; a MANY-TO-ONE per-index
2443/// projection binds this block-constancy sibling).
2444///
2445/// The invariant is load-bearing for the substrate's typed variant →
2446/// canonical-projection MANY-TO-ONE closed-set surface at
2447/// [`crate::error::CompilerSpecIoStage::OPERATIONS`]: the four typed
2448/// variants of [`crate::error::CompilerSpecIoStage`] project through
2449/// [`crate::error::CompilerSpecIoStage::operation`] onto EXACTLY TWO
2450/// canonical `&'static str` operation labels
2451/// ([`crate::error::CompilerSpecIoStage::REALIZE_TO_DISK_OPERATION`]
2452/// (`"realize_to_disk"`) shared by
2453/// [`crate::error::CompilerSpecIoStage::RealizeToDiskSerialize`] and
2454/// [`crate::error::CompilerSpecIoStage::RealizeToDiskWrite`];
2455/// [`crate::error::CompilerSpecIoStage::LOAD_FROM_DISK_OPERATION`]
2456/// (`"load_from_disk"`) shared by
2457/// [`crate::error::CompilerSpecIoStage::LoadFromDiskRead`] and
2458/// [`crate::error::CompilerSpecIoStage::LoadFromDiskDeserialize`]).
2459/// The `[REALIZE, REALIZE, LOAD, LOAD]` block-constant shape encodes
2460/// the compound-key `"{operation}: {stage}"` surface's 2-of-2-to-2
2461/// partition — a regression that silently flip-flopped the projection
2462/// (e.g. reorder to `[REALIZE, LOAD, REALIZE, LOAD]`, drift a variant
2463/// slot to a distinct third operation label) would compile cleanly
2464/// past the sibling `_pairwise_distinct` exclusion (this array is
2465/// INTENTIONALLY non-injective, so the pairwise-distinct helper does
2466/// NOT bind it) and only fail at test time via the runtime pin
2467/// `compiler_spec_io_stage_operations_align_with_all_by_index`; post-
2468/// lift the ARRAY-LEVEL block-constancy binds at rustc time via ONE
2469/// `const _` line, one invocation stage earlier than the runtime pin.
2470///
2471/// The `K = 0` and `K = N` degenerate corners collapse the array into
2472/// a ONE-BLOCK replica: `K = 0` yields `arr = [tail; N]`; `K = N`
2473/// yields `arr = [head; N]`. Both corners pass through the same
2474/// sweep without a distinct code path, and both are covered by the
2475/// helper's test surface — a future substrate array whose per-index
2476/// projection collapses ALL positions onto ONE canonical byte binds
2477/// through either degenerate corner at ONE const-generic setting.
2478///
2479/// SET-side well-formedness: no SET-side arm here — the helper takes
2480/// TWO scalars, NOT a scalar plus a target-set spec, so there's no
2481/// SET well-formedness axis to gate on the CALLER'S input. The two
2482/// scalars `head` and `tail` MAY be byte-equal (in which case the
2483/// helper degenerates to a SINGLE-BLOCK replica-check that binds the
2484/// SAME constancy across all `N` positions with `head == tail`); the
2485/// two scalars MAY be byte-distinct (the intended TWO-BLOCK partition
2486/// shape). Either case is a well-formed BLOCK-CONSTANCY invariant.
2487///
2488/// CARDINALITY-MISMATCH gate: `K > N` fails FIRST at const-eval time
2489/// (before any per-position sweep begins) with a CARDINALITY-MISMATCH
2490/// arm so a caller-side turbofish arity slip on the `K` const-generic
2491/// routes to the CARDINALITY axis rather than silently degenerating
2492/// into a truncated head-only sweep. The `K == N` and `K == 0`
2493/// corners are LEGAL (the ONE-BLOCK degenerate shapes) so the gate
2494/// is `K > N`, not `K >= N`.
2495///
2496/// Adding a new family-wide `[&'static str; N]` MANY-TO-ONE variant →
2497/// canonical-projection array to the substrate: pair the declaration
2498/// with `const _: () = assert_str_array_is_concatenation_of_two_
2499/// scalar_replicas::<N, K>(&Self::FOO, HEAD_SCALAR, TAIL_SCALAR);`
2500/// co-located after the array's declaration and the block-constancy
2501/// contract binds at compile time. The rustc-forced arity
2502/// `[&'static str; N]` composes with this const-eval sweep so BOTH
2503/// cardinality AND per-index MANY-TO-ONE block-constancy are compile-
2504/// time theorems on the SAME array.
2505///
2506/// Runtime callability: the function is a normal `pub const fn`, so
2507/// callers CAN also invoke it at runtime — pinned by
2508/// `assert_str_array_is_concatenation_of_two_scalar_replicas_panics_
2509/// at_runtime_on_head_segment_drift`, `..._on_tail_segment_drift`,
2510/// `..._on_arity_slip`, and `..._panic_message_names_the_helper_and_
2511/// block_constancy_violation_axis`.
2512///
2513/// Theory grounding:
2514/// - THEORY.md §V.1 — knowable platform; the MANY-TO-ONE variant →
2515/// canonical-projection block-constant contract on the `&'static
2516/// str` per-index projection axis becomes a TYPE-LEVEL theorem the
2517/// substrate carries per (arr, head, tail, K) quadruple rather than
2518/// a runtime iterator sweep the developer must remember to write
2519/// per quadruple.
2520/// - THEORY.md §III — the typescape; the (element-type × contract-
2521/// shape) matrix now carries the MANY-TO-ONE-BLOCK-CONSTANCY corner
2522/// on the (`&'static str`) row at ONE peer const-fn helper. Combined
2523/// with the pre-existing (`_pairwise_distinct`) INJECTIVITY sibling
2524/// the two helpers close the (INJECTIVITY, MANY-TO-ONE-BLOCK-
2525/// CONSTANCY) 2-corner face on the (`&'static str`) row — every
2526/// family-wide `[&'static str; N]` per-index projection array picks
2527/// the corner that matches its per-index projection cardinality.
2528/// - THEORY.md §VI.1 — generation over composition; the const-eval
2529/// block-constant sweep IS the generative shape. Every new MANY-TO-
2530/// ONE variant → canonical-projection substrate closed set adds ONE
2531/// `const _` line to get the block-constant theorem rather than
2532/// re-deriving a per-projection runtime multiplicity check.
2533/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
2534/// BLOCK-CONSTANCY proof at declaration site AND the compound-key
2535/// `"{operation}: {stage}"` surface's partition contract regenerate
2536/// through the SAME `const _` witness at the ARRAY level.
2537///
2538/// Frontier inspiration: Lean 4's `List.replicate` unfolded at the
2539/// concrete 2-block `List.replicate K head ++ List.replicate (N - K)
2540/// tail` monomorphic realisation — the substrate primitive embeds
2541/// the same run-length shape as a rustc const-eval-time proof
2542/// obligation at every block-constant projection site rather than as
2543/// a Lean tactic invocation deferred to `elab_command`. The MANY-TO-
2544/// ONE variant → canonical-projection shape mirrors GHC Core's
2545/// constant-folding of a `case` scrutinee whose match arms collapse a
2546/// wider sum type onto a narrower sum type via a per-arm literal
2547/// projection; where GHC folds this at Core compilation, the
2548/// substrate binds the projection identity at rustc const-eval time.
2549pub const fn assert_str_array_is_concatenation_of_two_scalar_replicas<
2550 const N: usize,
2551 const K: usize,
2552>(
2553 arr: &[&'static str; N],
2554 head: &'static str,
2555 tail: &'static str,
2556) {
2557 if K > N {
2558 panic!(
2559 "assert_str_array_is_concatenation_of_two_scalar_replicas: \
2560 CARDINALITY-MISMATCH — the two const parameters `N` and \
2561 `K` must satisfy `K <= N` so the HEAD segment of `arr` \
2562 (positions `[0..K)`) followed by the TAIL segment \
2563 (positions `[K..N)`) exactly cover `arr`'s `N` \
2564 positions. Fix at the `const _` witness's turbofish by \
2565 reconciling the two arities against the composite's \
2566 declared arity. The CARDINALITY-MISMATCH gate \
2567 distinguishes THIS failure from every content-drift arm \
2568 — a mistyped ARITY on the caller side fails HERE before \
2569 any per-position sweep begins, so a subtle arity slip \
2570 doesn't silently degenerate into a truncated head-only \
2571 sweep."
2572 );
2573 }
2574 let mut i = 0;
2575 while i < K {
2576 if !str_bytes_equal(arr[i], head) {
2577 panic!(
2578 "assert_str_array_is_concatenation_of_two_scalar_replicas: \
2579 HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION — the family-\
2580 wide `&'static str` array `arr` carries an entry at \
2581 some position in `[0, K)` (the HEAD segment) that \
2582 does NOT byte-for-byte equal the peer `head` scalar. \
2583 The substrate's HEAD-SEGMENT BLOCK-CONSTANCY \
2584 contract on the array is broken; every consumer that \
2585 reads `arr[0..K)` and the peer `head` scalar as \
2586 INTERCHANGEABLE (any MANY-TO-ONE variant → \
2587 canonical-projection consumer expecting the first \
2588 `K` variant slots to share ONE canonical projection \
2589 byte — e.g. the `zip(Self::ALL, Self::OPERATIONS)` \
2590 compound-key `\"{{operation}}: {{stage}}\"` surface \
2591 consumers on \
2592 `crate::error::CompilerSpecIoStage::OPERATIONS` \
2593 whose first two slots project through \
2594 `crate::error::CompilerSpecIoStage::REALIZE_TO_DISK_\
2595 OPERATION`) relies on this invariant. Fix at the \
2596 ARRAY-DECLARATION site (the drifted `arr[i]` entry) \
2597 OR at the per-role scalar constant that `head` \
2598 re-exports — the choice depends on whether the drift \
2599 is an unintended slot reorder inside the array or a \
2600 rename of the canonical projection byte upstream."
2601 );
2602 }
2603 i += 1;
2604 }
2605 let mut j = K;
2606 while j < N {
2607 if !str_bytes_equal(arr[j], tail) {
2608 panic!(
2609 "assert_str_array_is_concatenation_of_two_scalar_replicas: \
2610 TAIL-SEGMENT-BLOCK-CONSTANCY-VIOLATION — the family-\
2611 wide `&'static str` array `arr` carries an entry at \
2612 some position in `[K, N)` (the TAIL segment) that \
2613 does NOT byte-for-byte equal the peer `tail` scalar. \
2614 The substrate's TAIL-SEGMENT BLOCK-CONSTANCY \
2615 contract on the array is broken; every consumer that \
2616 reads `arr[K..N)` and the peer `tail` scalar as \
2617 INTERCHANGEABLE (any MANY-TO-ONE variant → \
2618 canonical-projection consumer expecting the last \
2619 `N - K` variant slots to share ONE canonical \
2620 projection byte — e.g. the `zip(Self::ALL, \
2621 Self::OPERATIONS)` compound-key `\"{{operation}}: \
2622 {{stage}}\"` surface consumers on \
2623 `crate::error::CompilerSpecIoStage::OPERATIONS` whose \
2624 last two slots project through \
2625 `crate::error::CompilerSpecIoStage::LOAD_FROM_DISK_\
2626 OPERATION`) relies on this invariant. Fix at the \
2627 ARRAY-DECLARATION site (the drifted `arr[j]` entry) \
2628 OR at the per-role scalar constant that `tail` \
2629 re-exports — the choice depends on whether the drift \
2630 is an unintended slot reorder inside the array or a \
2631 rename of the canonical projection byte upstream."
2632 );
2633 }
2634 j += 1;
2635 }
2636}
2637
2638/// Compile-time contract verifier — panics at const evaluation time if
2639/// the sub-slice `full[START..START + M)` does NOT byte-equal the peer
2640/// sub-array `sub[..]` positionwise (`&'static str`-by-`&'static str`).
2641///
2642/// Row-dual peer of [`assert_u8_array_slice_equals_u8_array`] and
2643/// [`assert_char_array_slice_equals_char_array`] on the (element-type)
2644/// axis: where the `u8` sibling closes the outer-`Sexp` cache-key
2645/// discriminator sub-carving vocabulary at compile time AND the `char`
2646/// sibling closes the substrate's reader-boundary `[char; N]` scalar-
2647/// composed vocabulary at compile time, this closes the substrate's
2648/// family-wide `[&'static str; N]` label / prefix / tag / literal
2649/// vocabulary at compile time. Opens the SUB-SLICE ARRAY-image column
2650/// on the (str) row of the (element-type × contract-shape) matrix peer
2651/// to the u8-row + char-row siblings' SUB-SLICE ARRAY-image column —
2652/// the three helpers together lift EVERY positionwise-composition
2653/// contract `arr[START..START + M) == sub[..]` on scalar-family-wide
2654/// substrate arrays into a COMPILE-TIME theorem, one per element-type
2655/// row of the matrix.
2656///
2657/// The MIDDLE-SLICE corner (`0 < START`, `START + M < N`) — the shape
2658/// the (str) row uniquely exercises against the substrate's twelve-arm
2659/// [`crate::error::SexpShape::LABELS`] vocabulary — pins that each
2660/// sub-carving's LABELS array occupies its CANONICAL SLOTS on the
2661/// parent superset's declaration order, one invocation stage stronger
2662/// than the pre-existing SET-level DISJOINT-UNION witnesses (three
2663/// `assert_str_array_within_str_finite_set::<sub, 12>` embeddings,
2664/// three `assert_str_arrays_disjoint::<a, b>` pairwise-disjointness
2665/// witnesses, one `assert_str_finite_set_covered_by_three_str_arrays::
2666/// <6, 4, 2, 12>` coverage witness, one `assert_str_array_pairwise_
2667/// distinct(&SexpShape::LABELS)` INJECTIVITY witness). The SET-level
2668/// theorem `SexpShape::LABELS ≡ AtomKind::LABELS ⊕ QuoteForm::LABELS ⊕
2669/// StructuralKind::LABELS` those seven witnesses close is SILENT on
2670/// which SLOTS each sub-vocabulary's arms occupy — a regression that
2671/// permuted `SexpShape::LABELS` from
2672/// `[NIL, SYMBOL, KEYWORD, STRING, INT, FLOAT, BOOL, LIST, QUOTE,
2673/// QUASIQUOTE, UNQUOTE, UNQUOTE_SPLICE]` (`StructuralKind` at slots
2674/// `{0, 7}`, `AtomKind` at slots `[1..7)`, `QuoteForm` at slots
2675/// `[8..12)` — the CANONICAL positional decomposition) to
2676/// `[SYMBOL, NIL, KEYWORD, STRING, INT, FLOAT, BOOL, LIST, QUOTE,
2677/// QUASIQUOTE, UNQUOTE, UNQUOTE_SPLICE]` (swapping slots `0` and `1`,
2678/// interleaving `AtomKind` into a slot the structural-residual carving
2679/// previously owned) preserves the SET-level disjoint-union theorem
2680/// (both sub-vocabularies still embed into the parent, still cover, still
2681/// disjoint, parent still injective) but silently misaligns every
2682/// consumer indexing `SexpShape::LABELS[0]` for the NIL diagnostic
2683/// literal. THIS helper binds each sub-slice's positionwise composition
2684/// against its sub-vocabulary's canonical array at rustc time —
2685/// strictly STRONGER on the (contract-strength) axis than the sibling
2686/// SET-level DISJOINT-UNION witnesses.
2687///
2688/// Consumer sites this helper closes at the MIDDLE-SLICE corner:
2689/// * [`crate::error::SexpShape::LABELS`] `[0..1) ==
2690/// [crate::error::StructuralKind::NIL_LABEL]` — the singleton left-
2691/// endpoint slot of the outer twelve-shape LABELS array binds the
2692/// structural-residual carving's NIL role at the CANONICAL slot `0`.
2693/// * [`crate::error::SexpShape::LABELS`] `[1..7) == AtomKind::LABELS` —
2694/// the six-slot atomic-payload middle slice binds the six atomic
2695/// variants' LABELS at the CANONICAL slots `[1..7)`, one invocation
2696/// stage stronger than the (u8)-row peer at the SAME slice range
2697/// `[1..7)` where the six slots collapse to a single scalar
2698/// [`AtomKind::OUTER_HASH_DISCRIMINATOR`] byte (`1u8`) — the (str)
2699/// row's per-slot LABELS listing distinguishes ALL SIX slots
2700/// individually, so a permutation of the six atomic arms inside the
2701/// parent's `[1..7)` slice fails HERE where the (u8)-row's SCALAR-
2702/// REPLICA sibling stays silent.
2703/// * [`crate::error::SexpShape::LABELS`] `[7..8) ==
2704/// [crate::error::StructuralKind::LIST_LABEL]` — the singleton mirror-
2705/// endpoint slot at the atomic-collapse right endpoint binds the
2706/// structural-residual carving's LIST role at the CANONICAL slot `7`.
2707/// * [`crate::error::SexpShape::LABELS`] `[8..12) == QuoteForm::LABELS`
2708/// — the four-slot quote-family tail slice binds the four quote-
2709/// family variants' LABELS at the CANONICAL slots `[8..12)`, peer to
2710/// the (u8)-row's `assert_u8_array_slice_equals_u8_array::<12, 4, 8>
2711/// (&SexpShape::HASH_DISCRIMINATORS, &QuoteForm::HASH_DISCRIMINATORS)`
2712/// witness.
2713///
2714/// Together the FOUR positional witnesses cover the ENTIRE twelve-slot
2715/// outer container's per-position LABEL sequence at rustc time — the
2716/// UNION of the four disjoint slice ranges `[0..1) ∪ [1..7) ∪ [7..8) ∪
2717/// [8..12)` exhausts the twelve-slot outer container's position space.
2718/// Sibling posture to the (u8)-row's FOUR positional witnesses on
2719/// `SexpShape::HASH_DISCRIMINATORS` (the two singleton
2720/// slice-equals-array witnesses on `[0..1)` and `[7..8)`, the six-slot
2721/// slice-is-scalar-replica witness on `[1..7)`, the four-slot
2722/// slice-equals-array witness on `[8..12)`) — both rows now carry the
2723/// FULL positional decomposition of the twelve-slot outer container at
2724/// rustc time on BOTH the (u8) discriminator axis AND the (str) label
2725/// axis. A regression that reordered `SexpShape::LABELS` fails at BOTH
2726/// the (u8)-row `HASH_DISCRIMINATORS` positional witnesses (through
2727/// the parallel outer twelve-slot ordering) AND the (str)-row LABELS
2728/// positional witnesses lifted here.
2729///
2730/// Pre-lift the twelve-arm `SexpShape::LABELS` positional decomposition
2731/// lived ONLY through the runtime cross-check
2732/// `sexp_shape_labels_align_with_sub_vocabularies_by_position` (in
2733/// `error.rs`, sweeping the twelve positions and routing each
2734/// `SexpShape::LABELS[i]` through its sub-vocabulary via
2735/// `SexpShape::ALL[i].as_atom_kind() / .as_quote_form()` composition);
2736/// post-lift the ARRAY-LEVEL positional decomposition binds at rustc
2737/// time via FOUR `const _` lines, one invocation stage earlier than
2738/// the runtime pin. A regression that reorders the outer
2739/// `SexpShape::LABELS` array's initializer (e.g. swapping slot `0`'s
2740/// `Self::NIL_LABEL` with slot `1`'s `Self::SYMBOL_LABEL`) while
2741/// leaving each sub-vocabulary in its canonical order fails at
2742/// `cargo check` BEFORE any test scheduler runs.
2743///
2744/// The three axis-partitioned panic messages (`START-OUT-OF-BOUNDS`,
2745/// `SLICE-LENGTH-OUT-OF-BOUNDS`, `STR-SLICE-EQUALS-ARRAY-VIOLATION`)
2746/// mirror the u8 sibling's message vocabulary with the `STR-` prefix
2747/// on the CONTENT-drift axis so callers grep either the (u8) row's
2748/// plain `SLICE-EQUALS-ARRAY-VIOLATION`, the (char) row's
2749/// `CHAR-SLICE-EQUALS-ARRAY-VIOLATION`, or the (str) row's
2750/// `STR-SLICE-EQUALS-ARRAY-VIOLATION` axis-prefix by element-type. The
2751/// shared `-SLICE-EQUALS-ARRAY-VIOLATION` infix lets callers grep any
2752/// element-type variant by the shared axis substring.
2753///
2754/// Adding a new family-wide `[&'static str; N]` array to the substrate
2755/// whose declaration is a positionwise composition against named per-
2756/// role `pub const *_LABEL` / `*_PREFIX` / `*_TAG` `&'static str`
2757/// constants: pair the declaration with `const _: () =
2758/// assert_str_array_slice_equals_str_array::<N, M, START>(&Self::FOO_
2759/// ARRAY, &Self::SUB_ARRAY);` co-located after the array's declaration
2760/// and the per-slot ORDER contract binds at compile time. The rustc-
2761/// forced arities `[&'static str; N]` + `[&'static str; M]` compose
2762/// with this const-eval sweep so BOTH cardinality AND per-slot
2763/// canonical-str-value are compile-time theorems on the SAME array.
2764///
2765/// Runtime callability: the function is a normal `pub const fn`, so
2766/// callers CAN also invoke it at runtime — e.g. a REPL / LSP surface
2767/// that constructs a `[&'static str; N]` at runtime from a user-
2768/// supplied vocabulary and wants to verify positionwise composition
2769/// against a peer sub-array before consuming it — and the panic
2770/// surfaces normally in that path (pinned by
2771/// `assert_str_array_slice_equals_str_array_panics_at_runtime_on_positionwise_drift`,
2772/// `assert_str_array_slice_equals_str_array_panics_at_runtime_on_start_out_of_bounds`,
2773/// `assert_str_array_slice_equals_str_array_panics_at_runtime_on_slice_length_out_of_bounds`,
2774/// AND
2775/// `assert_str_array_slice_equals_str_array_panic_message_names_the_helper_and_str_slice_equals_array_violation_axis`).
2776///
2777/// Theory grounding:
2778/// - THEORY.md §V.1 — knowable platform; the family-wide per-position
2779/// ORDER contract on the `&'static str`-typed vocabulary becomes a
2780/// TYPE-LEVEL theorem the substrate carries per array declaration
2781/// rather than a runtime iterator sweep the developer must remember
2782/// to write per array.
2783/// - THEORY.md §VI.1 — generation over composition; the const-eval
2784/// positionwise sweep IS the generative shape. Every new closed-set
2785/// string array declared as a positionwise composition against
2786/// named-per-role `pub const` labels adds ONE `const _` line to get
2787/// the per-slot ORDER theorem rather than re-deriving a runtime
2788/// index-by-index `assert_eq!` block per array.
2789/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
2790/// per-slot-ORDER proof at declaration site AND the per-role
2791/// `pub const *_LABEL` alias-chain composition every consumer relies
2792/// on regenerate through the SAME `const _` witness.
2793pub const fn assert_str_array_slice_equals_str_array<
2794 const N: usize,
2795 const M: usize,
2796 const START: usize,
2797>(
2798 full: &[&'static str; N],
2799 sub: &[&'static str; M],
2800) {
2801 if START > N {
2802 panic!(
2803 "assert_str_array_slice_equals_str_array: START-OUT-OF-\
2804 BOUNDS — the const parameter `START` sits OUTSIDE the \
2805 outer array's valid position range `[0..N]` (inclusive \
2806 upper bound: `START == N` combined with `M == 0` is the \
2807 LEGAL empty-slice-at-right-endpoint corner). Fix at the \
2808 `const _` witness's turbofish by reconciling `START` \
2809 against the outer array's declared arity `N`. The \
2810 START-OUT-OF-BOUNDS gate fires FIRST — a mistyped \
2811 `START` on the caller side fails HERE before the peer \
2812 `SLICE-LENGTH-OUT-OF-BOUNDS` gate reads `N - START` \
2813 (which would underflow `usize` had this gate not caught \
2814 the slip), so a subtle bounds slip doesn't silently \
2815 degenerate into a subtraction wrap-around OR a panic \
2816 deeper in `full[START + i]` bounds-checking."
2817 );
2818 }
2819 if M > N - START {
2820 panic!(
2821 "assert_str_array_slice_equals_str_array: SLICE-LENGTH-\
2822 OUT-OF-BOUNDS — the peer sub-array's arity `M` exceeds \
2823 the outer array's tail cardinality `N - START`, so the \
2824 positionwise sweep `full[START + i]` for `i ∈ [0..M)` \
2825 would overrun the outer array's valid position range \
2826 `[0..N)` at some `i ∈ [N - START..M)`. Fix at the \
2827 `const _` witness's turbofish by reconciling `M` against \
2828 the outer array's tail cardinality `N - START` OR by \
2829 narrowing `START` to leave a longer tail. The peer \
2830 `START-OUT-OF-BOUNDS` gate above guarantees `START ≤ N` \
2831 so `N - START` never underflows `usize` at this gate. \
2832 The LEGAL exact-fit corner `M == N - START` (the sub-\
2833 array reaches EXACTLY to the outer array's right \
2834 endpoint) is accepted; the STRICT `M > N - START` slip \
2835 is what this gate rejects."
2836 );
2837 }
2838 let mut i = 0;
2839 while i < M {
2840 if !str_bytes_equal(full[START + i], sub[i]) {
2841 panic!(
2842 "assert_str_array_slice_equals_str_array: STR-SLICE-\
2843 EQUALS-ARRAY-VIOLATION — the outer `[&'static str; \
2844 N]` array `full` carries a str at some position \
2845 `START + i` (for `i ∈ [0..M)`) that does NOT byte-\
2846 equal the peer `[&'static str; M]` sub-array `sub` \
2847 at the offset-matched position `i`. The substrate's \
2848 SLICE-EQUALS-ARRAY positionwise-composition contract \
2849 on the sub-slice `full[START..START + M) == sub[..]` \
2850 is broken; every consumer that reads `full[START..\
2851 START + M)` as a positionwise-aligned copy of a peer \
2852 sub-vocabulary's canonical `[&'static str; M]` \
2853 listing (the twelve-slot outer container \
2854 `crate::error::SexpShape::LABELS` whose four \
2855 canonical sub-slices `[0..1) == \
2856 [crate::error::StructuralKind::NIL_LABEL]`, `[1..7) \
2857 == AtomKind::LABELS`, `[7..8) == [crate::error::\
2858 StructuralKind::LIST_LABEL]`, `[8..12) == \
2859 QuoteForm::LABELS` compose the parent LABELS \
2860 vocabulary from the three sub-carvings' LABELS \
2861 arrays; any future container-array sub-slice byte-\
2862 for-byte equal to a peer sub-carving's canonical \
2863 `[&'static str; M]` listing) relies on this \
2864 invariant. Fix at the ARRAY-DECLARATION site (the \
2865 drifted `full[START + i]` entry inside the slice \
2866 segment) OR at the peer sub-array's arm listing — \
2867 the choice depends on whether the drift is an \
2868 unintended slot reorder in the outer array's tail \
2869 OR in the sub-carving's own listing."
2870 );
2871 }
2872 i += 1;
2873 }
2874}
2875
2876/// Compile-time contract verifier — panics at const evaluation time if
2877/// any two entries of `arr` alias byte-for-byte.
2878///
2879/// Column-dual peer to [`assert_char_array_pairwise_distinct`] and
2880/// [`assert_str_array_pairwise_distinct`] on the (element-type) axis:
2881/// where the `char` sibling closes the reader-boundary `[char; N]`
2882/// vocabulary at compile time and the `&'static str` sibling closes
2883/// the outer-algebras' family-wide label / prefix / tag / literal
2884/// vocabularies, this closes the substrate's family-wide `[u8; N]`
2885/// cache-key discriminator vocabulary. The three helpers together
2886/// lift EVERY `pub const` scalar-family-wide array declared on the
2887/// substrate's closed-set outer algebras (`Sexp` / `Atom` /
2888/// `AtomKind` / `QuoteForm` / `StructuralKind` / `UnquoteForm`) into
2889/// a COMPILE-TIME pairwise-distinctness theorem — a regression that
2890/// silently collides two entries fails the build at `cargo check`
2891/// time, one invocation stage earlier than the test-run pin.
2892///
2893/// The invariant is load-bearing for the outer-`Sexp` cache-key
2894/// algebra: every consumer that pattern-matches the array's entries
2895/// as DISJOINT arms of a hash-discriminator projection —
2896/// [`AtomKind::hash_discriminator`]'s six-arm `{0..=5}` byte partition
2897/// under [`Hash for Atom`](crate::ast::Atom); [`QuoteForm::hash_discriminator`]'s
2898/// four-arm `{3, 4, 5, 6}` byte partition under
2899/// [`Hash for Sexp`](crate::ast::Sexp); [`crate::error::StructuralKind::hash_discriminator`]'s
2900/// two-arm `{0, 2}` byte partition under the outer-`Sexp` cache-key
2901/// algebra's structural-residual carve; AND
2902/// [`crate::error::UnquoteForm::hash_discriminator`]'s two-arm byte
2903/// partition on the substitution-subset carving — relies on this
2904/// invariant. A duplicate here would silently collide two DISTINCT
2905/// variants at the SAME cache-key byte, breaking the `Expander::cache`
2906/// keying on `(macro_name, args)` hash and mis-hashing every cached
2907/// expansion across the collided variants.
2908///
2909/// Pre-lift each of these four arrays carried its pairwise-
2910/// distinctness contract at a runtime test
2911/// (`atom_kind_hash_discriminators_pairwise_distinct`,
2912/// `quote_form_hash_discriminators_pairwise_distinct`,
2913/// `structural_kind_hash_discriminators_pairwise_distinct`,
2914/// `unquote_form_hash_discriminators_pairwise_distinct`); post-lift
2915/// the pairwise-distinctness contract binds at `cargo check` time,
2916/// one invocation stage earlier, catching regressions on `cargo
2917/// build` / `cargo clippy` runs that skip the test suite.
2918///
2919/// NB: [`crate::error::SexpShape::HASH_DISCRIMINATORS`] (`[u8; 12]`)
2920/// is INTENTIONALLY excluded from this compile-time sweep — the
2921/// twelve outer shapes DELIBERATELY collapse onto only SEVEN outer
2922/// cache-key bytes `{0..=6}` (the six atomic shapes all map to `1`
2923/// per the outer-Sexp `Atom` marker byte, per the collapse rule
2924/// documented on [`AtomKind::OUTER_HASH_DISCRIMINATOR`]). A
2925/// pairwise-distinctness pin there would fire correctly, matching
2926/// the load-bearing non-injectivity that closes the twelve-arm
2927/// shape space onto the seven-arm outer-Sexp cache-key space.
2928///
2929/// Adding a new family-wide `[u8; N]` array to the substrate: pair
2930/// the declaration with `const _: () = assert_u8_array_pairwise_
2931/// distinct(&Self::FOO_ARRAY);` co-located after the array's
2932/// declaration and the distinctness contract is enforced at
2933/// compile time. The rustc-forced arity `[u8; N]` composes with
2934/// this const-eval sweep so BOTH cardinality AND injectivity are
2935/// compile-time theorems on the SAME array.
2936///
2937/// Runtime callability: the function is a normal `pub const fn`, so
2938/// callers CAN also invoke it at runtime — pinned by
2939/// `assert_u8_array_pairwise_distinct_panics_at_runtime_on_binary_
2940/// collision`. Unlike [`assert_str_array_pairwise_distinct`] this
2941/// helper needs no auxiliary byte-equality helper: `u8` supports
2942/// `==` directly in const-fn context, collapsing the triangular
2943/// pair sweep to a two-loop shape without an inner byte walk.
2944///
2945/// Theory grounding:
2946/// - THEORY.md §V.1 — knowable platform; the family-wide
2947/// distinctness contract on the `u8` cache-key vocabulary becomes
2948/// a TYPE-LEVEL theorem the substrate carries per array
2949/// declaration rather than a runtime test the developer must
2950/// remember to write per array.
2951/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
2952/// cache-key partition is the `intent_hash` composition axis —
2953/// binding the discriminator arrays on the typed algebra makes
2954/// attestation-key drift a compile error rather than a silent
2955/// BLAKE3 mis-hash on any consumer keyed on `Hash for Sexp`.
2956/// - THEORY.md §VI.1 — generation over composition; the const-eval
2957/// sweep IS the generative shape. Every new closed-set
2958/// discriminator array adds ONE `const _` line to get the
2959/// distinctness theorem rather than re-deriving a per-array
2960/// runtime iterator sweep.
2961pub const fn assert_u8_array_pairwise_distinct<const N: usize>(arr: &[u8; N]) {
2962 let mut i = 0;
2963 while i < N {
2964 let mut j = i + 1;
2965 while j < N {
2966 if arr[i] == arr[j] {
2967 panic!(
2968 "assert_u8_array_pairwise_distinct: family-wide \
2969 u8 array carries a duplicate entry across two \
2970 positions — the substrate's pairwise-\
2971 distinctness contract on the array is broken; \
2972 every consumer that pattern-matches the array's \
2973 entries as DISJOINT arms (Atom / Sexp cache-key \
2974 hash-discriminator projection, structural-\
2975 residual / quote-family / substitution-subset \
2976 byte partition) relies on this invariant",
2977 );
2978 }
2979 j += 1;
2980 }
2981 i += 1;
2982 }
2983}
2984
2985// Compile-time pairwise-distinctness on family-wide `[u8; N]` hash-
2986// discriminator arrays no longer surfaces at this level as DIRECT
2987// witnesses — every family-wide `[u8; N]` HASH_DISCRIMINATORS array
2988// whose INJECTIVITY contract binds now does so through ONE of the two
2989// stronger COMPOUND (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) permutation
2990// helpers defined below (`assert_u8_array_permutes_inclusive_range` on
2991// the CONTIGUOUS-INCLUSIVE-RANGE corner of the (contiguity) axis at
2992// `AtomKind::HASH_DISCRIMINATORS` / `QuoteForm::HASH_DISCRIMINATORS` /
2993// `UnquoteForm::HASH_DISCRIMINATORS`; `assert_u8_array_permutes_
2994// finite_set` on the NON-CONTIGUOUS-FINITE-SET corner at
2995// `StructuralKind::HASH_DISCRIMINATORS`), each of which delegates
2996// through this pairwise-distinct helper for the INJECTIVITY arm — the
2997// helper is now purely a delegation target. `SexpShape::HASH_
2998// DISCRIMINATORS` is intentionally OMITTED from BOTH compound helpers
2999// per the intentionally-non-injective twelve-shape → seven-byte
3000// collapse rule documented on the helper above — INJECTIVITY does not
3001// hold, so no permutation contract can bind on any contiguity corner
3002// (its SURJECTIVITY-only contract binds through the single-axis
3003// `assert_u8_array_covers_inclusive_range` sibling further below).
3004//
3005// Adding a new family-wide `[u8; N]` permutation-shaped HASH_
3006// DISCRIMINATORS array: prefer the compound helpers below
3007// (`_permutes_inclusive_range` for the contiguous-range corner or
3008// `_permutes_finite_set` for the non-contiguous-finite-set corner)
3009// which bind INJECTIVITY ∧ SURJECTIVITY ∧ ARITY at ONE `const _` line;
3010// fall back to a DIRECT `const _: () = assert_u8_array_pairwise_
3011// distinct(&Self::FOO_ARRAY);` witness at this level ONLY for an array
3012// that is intentionally injective but whose distinct-value set is
3013// NEITHER a contiguous inclusive range NOR a known finite set (a
3014// hypothetical looser-contract case not currently exercised by any
3015// substrate array). Sibling to the runtime `_hash_discriminators_
3016// pairwise_distinct` tests at `ast.rs` + `error.rs`'s tests modules —
3017// those enforce the same theorem at `cargo test` time through direct
3018// runtime calls to this helper, so the theorem is still bound at
3019// TWO stages of the toolchain (compile time through the delegated
3020// path inside the compound helpers, test time through the direct
3021// runtime calls). Peer to the seven `assert_char_array_pairwise_
3022// distinct` witnesses AND the thirteen `assert_str_array_pairwise_
3023// distinct` witnesses above on the (element-type) axis: `char`
3024// covers the reader-boundary vocabulary; `&'static str` covers the
3025// closed-set outer-algebras' label / prefix / tag / literal
3026// vocabularies; `u8` (via the compound helpers below) covers the
3027// outer-`Sexp` cache-key discriminator vocabulary.
3028
3029/// Compile-time contract verifier — panics at const evaluation time if
3030/// any two entries of `arr` share their LEFT `char` column OR their
3031/// RIGHT `char` column, i.e. binds the `[(char, char); N]` array as a
3032/// BIJECTION on the substrate's escape-table product-vocabulary.
3033///
3034/// Product-element sibling to the scalar-element trio
3035/// ([`assert_char_array_pairwise_distinct`],
3036/// [`assert_str_array_pairwise_distinct`], and
3037/// [`assert_u8_array_pairwise_distinct`]) on the (element-type) axis:
3038/// where the three scalar-element helpers close every family-wide
3039/// SINGLE-column array declared on the substrate's closed-set outer
3040/// algebras (`Sexp` / `Atom` / `AtomKind` / `QuoteForm` /
3041/// `StructuralKind` / `UnquoteForm` on the reader-boundary + label +
3042/// prefix + tag + cache-key vocabularies), this closes the substrate's
3043/// family-wide TWO-column `[(char, char); N]` bijection tables at
3044/// compile time. A `[(char, char); N]` array is a bijection iff BOTH
3045/// column projections are pairwise-distinct (LEFT-column injectivity
3046/// witnesses that `source → decoded` is a well-defined function on
3047/// distinct sources; RIGHT-column injectivity witnesses that
3048/// `decoded → source` inverts unambiguously) — since |LEFT| = |RIGHT| =
3049/// N is finite, the conjunction of the two injectivities IS bijectivity
3050/// on the paired substrate vocabulary.
3051///
3052/// The invariant is load-bearing for the Str-payload escape-table
3053/// tokenization boundary. [`Atom::NAMED_ESCAPE_TABLE`] (`[(char, char);
3054/// 3]`, the three pattern-DISTINCT-from-value named-escape rows —
3055/// `'n' → '\n'`, `'t' → '\t'`, `'r' → '\r'`) is the paired algebra
3056/// [`Atom::decode_str_escape`]'s three named arms dispatch through; a
3057/// LEFT-column collision would silently route two escape sequences
3058/// through the first-matching decoded byte (e.g. a drift of
3059/// `TAB_ESCAPE_SOURCE` to `'n'` would collapse `\t` and `\n` at the
3060/// same source arm), and a RIGHT-column collision would collapse two
3061/// distinct decoded bytes onto ONE (e.g. a drift of `TAB_ESCAPE_DECODED`
3062/// to `'\n'` would emit `\n` on BOTH `\t` and `\n` in the tokenized
3063/// payload). [`Atom::ESCAPE_TABLE`] (`[(char, char); 5]`, the
3064/// composite paired SPAN over the three named + two self-escape rows)
3065/// binds the SAME bijection at the composite level; a LEFT-column
3066/// collision AT THE COMPOSITE would witness cross-sub-vocabulary
3067/// aliasing (a named-escape source colliding with a self-escape
3068/// source, e.g. `'n'` = `Self::STR_DELIMITER`), and a RIGHT-column
3069/// collision would witness the same at the DECODED axis. Every future
3070/// family-wide `[(char, char); N]` paired substrate array participates
3071/// in the SAME compile-time guarantee via one `const _` line.
3072///
3073/// Pre-lift each of these two bijection tables carried its
3074/// column-wise pairwise-distinctness contract at TWO runtime tests
3075/// (`atom_named_escape_table_sources_pairwise_distinct` +
3076/// `atom_named_escape_table_decoded_pairwise_distinct` on the
3077/// NAMED_ESCAPE_TABLE arm; the composite `atom_escape_table_
3078/// sources_and_decoded_columns_pairwise_distinct` on the ESCAPE_TABLE
3079/// arm); post-lift the CONJOINED bijectivity contract binds at `cargo
3080/// check` time — the const-eval panic surfaces the collision at
3081/// COMPILE time, one invocation stage earlier, catching regressions
3082/// on `cargo build` / `cargo clippy` runs that skip the test suite.
3083///
3084/// Adding a new family-wide `[(char, char); N]` paired array to the
3085/// substrate: pair the declaration with `const _: () =
3086/// assert_char_pair_array_bijective(&Self::FOO_TABLE);` co-located
3087/// after the declaration and the BIJECTION contract binds at compile
3088/// time. The rustc-forced arity `[(char, char); N]` composes with this
3089/// const-eval sweep so cardinality AND left-column injectivity AND
3090/// right-column injectivity are ALL compile-time theorems on the SAME
3091/// array.
3092///
3093/// Runtime callability: the function is a normal `pub const fn`, so
3094/// callers CAN also invoke it at runtime — e.g. a REPL / LSP tokenizer
3095/// that constructs a `[(char, char); N]` at runtime and wants to
3096/// verify bijectivity before consuming it — and the two panic sites
3097/// (LEFT-column collision, RIGHT-column collision) surface normally in
3098/// that path with column-provenance-preserving panic messages
3099/// (pinned by
3100/// `assert_char_pair_array_bijective_panics_at_runtime_on_left_
3101/// column_collision` +
3102/// `assert_char_pair_array_bijective_panics_at_runtime_on_right_
3103/// column_collision`).
3104///
3105/// Column-provenance in the panic message is load-bearing: downstream
3106/// diagnostics (`cargo check` const-eval error output, test-suite
3107/// failure reports) route the drift back to the failed COLUMN (LEFT
3108/// source vs. RIGHT decoded) by string search — a bijection failure
3109/// on a `[(char, char); N]` table names WHICH column collapsed,
3110/// halving the search space for the operator debugging the drift.
3111///
3112/// Theory grounding:
3113/// - THEORY.md §V.1 — knowable platform; the family-wide bijectivity
3114/// contract on the `(char, char)` paired escape-table vocabulary
3115/// becomes a TYPE-LEVEL theorem the substrate carries per paired-
3116/// array declaration rather than a two-runtime-test pair the
3117/// developer must remember to write per array (one per column).
3118/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
3119/// bijection proof at declaration site AND the outer-dispatch
3120/// match-arm exhaustiveness at [`Atom::decode_str_escape`]'s five
3121/// arms regenerate through the SAME `const _` witness.
3122/// - THEORY.md §VI.1 — generation over composition; the two-column
3123/// const-eval sweep IS the generative shape. Every new closed-set
3124/// paired-array vocabulary adds ONE `const _` line to get the
3125/// bijection theorem rather than re-deriving TWO per-column
3126/// iterator sweeps at runtime.
3127pub const fn assert_char_pair_array_bijective<const N: usize>(arr: &[(char, char); N]) {
3128 let mut i = 0;
3129 while i < N {
3130 let mut j = i + 1;
3131 while j < N {
3132 if arr[i].0 as u32 == arr[j].0 as u32 {
3133 panic!(
3134 "assert_char_pair_array_bijective: LEFT column of \
3135 family-wide `[(char, char); N]` paired substrate \
3136 array carries a duplicate SOURCE char across two \
3137 positions — the substrate's LEFT-column pairwise-\
3138 distinctness contract (source-column injectivity, \
3139 i.e. `source → decoded` is a well-defined \
3140 function on distinct sources) is broken; every \
3141 consumer that pattern-matches the array's SOURCE \
3142 column as DISJOINT arms (Atom::decode_str_escape \
3143 escape-source outer dispatch, ESCAPE_SOURCES \
3144 column-dual SPAN projection) relies on this \
3145 invariant"
3146 );
3147 }
3148 if arr[i].1 as u32 == arr[j].1 as u32 {
3149 panic!(
3150 "assert_char_pair_array_bijective: RIGHT column of \
3151 family-wide `[(char, char); N]` paired substrate \
3152 array carries a duplicate DECODED char across two \
3153 positions — the substrate's RIGHT-column \
3154 pairwise-distinctness contract (decoded-column \
3155 injectivity, i.e. `source → decoded` inverts \
3156 unambiguously through `decoded → source` on the \
3157 finite paired vocabulary) is broken; every \
3158 consumer that enumerates the array's DECODED \
3159 column as a disjoint alphabet (ESCAPE_DECODED \
3160 column-dual SPAN projection, escape-family \
3161 decoded-byte enumeration) relies on this \
3162 invariant"
3163 );
3164 }
3165 j += 1;
3166 }
3167 i += 1;
3168 }
3169}
3170
3171// Compile-time bijectivity witnesses — one `const _: () =
3172// assert_char_pair_array_bijective(&…)` per family-wide `[(char, char);
3173// N]` paired escape-table array on the substrate's Str-payload
3174// tokenization boundary. Each invocation is const-evaluated at `cargo
3175// check` time; a regression that silently collided two entries at
3176// EITHER the LEFT source column OR the RIGHT decoded column fails the
3177// build rather than the test suite. Sibling to the runtime
3178// `_pairwise_distinct` tests at `ast::tests` (`atom_named_escape_
3179// table_sources_pairwise_distinct`, `atom_named_escape_table_decoded_
3180// pairwise_distinct`, `atom_escape_table_sources_and_decoded_columns_
3181// pairwise_distinct`) — the two enforce the same theorem at TWO
3182// stages of the toolchain, so a build that skips tests still catches
3183// the regression here, and a build that runs tests catches it a
3184// second time as a safety net if the const-eval sweep is ever
3185// silently dropped. Peer to the seven `assert_char_array_pairwise_
3186// distinct` + thirteen `assert_str_array_pairwise_distinct` + four
3187// `assert_u8_array_pairwise_distinct` witnesses above on the
3188// (element-type) axis: the three scalar-element helpers close every
3189// family-wide SINGLE-column array; this product-element helper closes
3190// the family-wide TWO-column paired escape-table arrays.
3191const _: () = assert_char_pair_array_bijective(&Atom::NAMED_ESCAPE_TABLE);
3192const _: () = assert_char_pair_array_bijective(&Atom::ESCAPE_TABLE);
3193
3194/// Compile-time contract verifier — panics at const evaluation time if
3195/// the family-wide `[(char, char); N]` paired substrate array `arr`
3196/// carries a pair at some position whose `(SOURCE, DECODED)` tuple is
3197/// NOT a member of the target finite superset paired-array partition
3198/// `set`. Binds ONE conjunct clause on the `(char, char)` product-
3199/// element row of the (element-type × contract-shape) matrix at the
3200/// (subset-embedding) column — CHAR-PAIR-SUBSET-VIOLATION — every
3201/// entry of `arr` MUST be a byte-for-byte-equal `(char, char)` pair
3202/// found somewhere in `set` (ORIENTED across the two arguments — the
3203/// SUBSET side `arr` vs the SUPERSET side `set`). Pair equality is
3204/// CONJOINED across BOTH columns: `arr[i] == set[j]` iff `arr[i].0 ==
3205/// set[j].0 AND arr[i].1 == set[j].1` — a per-column membership check
3206/// on either column alone would silently accept a pair whose LEFT
3207/// column matches ONE set entry and RIGHT column matches a DIFFERENT
3208/// set entry (a cross-row aliasing collision the CONJOINED-pair
3209/// contract catches).
3210///
3211/// Delegates target-set well-formedness to the sibling
3212/// [`assert_char_pair_array_bijective`] helper FIRST (peer to the
3213/// (char, u8, `&'static str`) scalar rows' delegation to the SCALAR
3214/// pairwise-distinct sibling — the well-formedness delegation lifts
3215/// TO THE `(char, char)` row through the strongest well-formedness
3216/// contract on the row, which is BIJECTIVITY on the paired array). A
3217/// malformed `set` (e.g. `[('a', 'x'), ('a', 'y')]` — LEFT-column
3218/// collision, or `[('a', 'x'), ('b', 'x')]` — RIGHT-column collision)
3219/// routes to the SET-side BIJECTIVITY panic (via the sibling's own
3220/// panic-name prefix) BEFORE the CHAR-PAIR-SUBSET-VIOLATION sweep
3221/// silently mis-verifies against the distinct-value bijective subset.
3222/// A well-formed `set` passes this arm as a no-op — the sweep is
3223/// const-eval-elidable and costs zero at rustc-time on the substrate
3224/// call sites.
3225///
3226/// The substrate binds the paired-array SUBSET theorem at ONE
3227/// intentionally-closed `(char, char)` sub-vocabulary pair: [`Atom::
3228/// NAMED_ESCAPE_TABLE`] (`[(char, char); 3]`, the three pattern-
3229/// DISTINCT-from-value named-escape rows — `'n' → '\n'`, `'t' → '\t'`,
3230/// `'r' → '\r'`) ⊂ [`Atom::ESCAPE_TABLE`] (`[(char, char); 5]`, the
3231/// composite paired SPAN over the three named + two self-escape rows).
3232/// Pre-lift the SUBSET relation lived only implicitly at the composite
3233/// array's declaration site (`Self::NAMED_ESCAPE_TABLE[0]`,
3234/// `Self::NAMED_ESCAPE_TABLE[1]`, `Self::NAMED_ESCAPE_TABLE[2]`
3235/// indexing into the composite's first three positions); post-lift
3236/// the ARRAY-LEVEL subset embedding binds at rustc time via ONE
3237/// `const _` line. A regression that silently re-inlined either the
3238/// SUBSET or the SUPERSET side of the relation to a fresh pair
3239/// breaking the subset containment (e.g. dropping `NEWLINE` from
3240/// `NAMED_ESCAPE_TABLE` while keeping it as a `Self::NAMED_ESCAPE_
3241/// TABLE[0]` index in `ESCAPE_TABLE`; drifting `Self::TAB_ESCAPE_
3242/// SOURCE` on the NAMED side to a fresh char not present at the
3243/// composite's second position; re-shuffling the composite's first
3244/// three positions to swap with the SELF-escape suffix bytes) fails
3245/// at `cargo check` BEFORE any test scheduler runs.
3246///
3247/// Row-dual peer to
3248/// [`assert_char_array_within_char_finite_set`] +
3249/// [`assert_u8_array_within_u8_finite_set`] +
3250/// [`assert_str_array_within_str_finite_set`] on the (element-type)
3251/// axis of the SAME (subset-embedding) contract-shape column: where
3252/// the three sibling helpers close the SCALAR (`char`, `u8`,
3253/// `&'static str`) rows at ONE peer helper per row, this helper
3254/// opens the PRODUCT-ELEMENT `(char, char)` row on the same column
3255/// — extending the (element-type × contract-shape) matrix's
3256/// (subset-embedding) column past the three scalar rows onto the
3257/// paired-array row.
3258///
3259/// Contract-orthogonal peer to
3260/// [`assert_char_pair_array_bijective`] on the (BIJECTIVITY,
3261/// SUBSET-EMBEDDING) axis of the (contract-shape) column on the SAME
3262/// `(char, char)` row: where the BIJECTIVITY sibling binds
3263/// (LEFT-column INJECTIVITY ∧ RIGHT-column INJECTIVITY) on the SAME
3264/// array, this SUBSET-EMBEDDING sibling binds (CONJOINED-pair
3265/// membership) across TWO arrays. Together the two helpers close the
3266/// (paired-array well-formedness, paired-array subset) 2-corner face
3267/// on the `(char, char)` row.
3268///
3269/// Panic message carries the axis-provenance-named
3270/// `"CHAR-PAIR-SUBSET-VIOLATION"` string chosen DISTINCT from every
3271/// sibling helper's axis vocabulary (`"CHAR-SUBSET-VIOLATION"` on the
3272/// (char) scalar row-dual SUBSET peer; `"SUBSET-VIOLATION"` on the
3273/// (u8) row-dual finite-set SUBSET peer; `"STR-SUBSET-VIOLATION"` on
3274/// the (`&'static str`) row-dual SUBSET peer; `"RANGE-SUBSET-
3275/// VIOLATION"` on the (u8) range SUBSET peer; `"CHAR-DISJOINTNESS-
3276/// VIOLATION"` / `"U8-DISJOINTNESS-VIOLATION"` / `"STR-DISJOINTNESS-
3277/// VIOLATION"` on the DISJOINTNESS-column row peers; `"LEFT column"`
3278/// / `"RIGHT column"` on the paired-array BIJECTIVITY sibling). The
3279/// `"CHAR-PAIR-"` prefix disambiguates from the (char) SCALAR row-
3280/// dual SUBSET peer; the shared `"-SUBSET-VIOLATION"` suffix lets
3281/// callers grep any row's SUBSET-embedding sibling by the shared
3282/// suffix alone.
3283///
3284/// Adding a new family-wide `[(char, char); N]` paired-array
3285/// intentionally-closed SUBSET carving of another substrate
3286/// `[(char, char); M]` paired array to the substrate: pair the
3287/// declaration with `const _: () =
3288/// assert_char_pair_array_within_char_pair_finite_set::<N, M>(
3289/// &Self::FOO_SUB_TABLE, &Self::FOO_SUPER_TABLE);` co-located after
3290/// the SUBSET-side declaration and the paired-array SUBSET-embedding
3291/// contract binds at compile time. The rustc-forced arity
3292/// `[(char, char); N] × [(char, char); M]` composes with this const-
3293/// eval sweep so cardinality AND SUBSET-embedding are BOTH compile-
3294/// time theorems on the SAME pair.
3295///
3296/// Runtime callability: the function is a normal `pub const fn`, so
3297/// callers CAN also invoke it at runtime — pinned by
3298/// `assert_char_pair_array_within_char_pair_finite_set_panics_at_
3299/// runtime_on_out_of_set_entry` +
3300/// `assert_char_pair_array_within_char_pair_finite_set_panic_message_
3301/// names_the_helper_and_char_pair_subset_violation_axis`.
3302///
3303/// Theory grounding:
3304/// - THEORY.md §V.1 — knowable platform; the family-wide paired-array
3305/// SUBSET-embedding contract on `(char, char)` sub-vocabularies
3306/// becomes a TYPE-LEVEL theorem the substrate carries per (arr,
3307/// set) `[(char, char); N] × [(char, char); M]` pair rather than a
3308/// runtime test per pair.
3309/// - THEORY.md §III — the typescape; the (element-type × contract-
3310/// shape) matrix's (SUBSET-EMBEDDING) column now carries the
3311/// PRODUCT-ELEMENT `(char, char)` row alongside the three scalar
3312/// rows at ONE peer const-fn helper per row.
3313/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
3314/// SUBSET-EMBEDDING proof at declaration site AND the outer-
3315/// algebra's paired-array partition contract regenerate through
3316/// the SAME `const _` witness at the ARRAY level.
3317/// - THEORY.md §VI.1 — generation over composition; the const-eval
3318/// CONJOINED-pair membership sweep IS the generative shape — every
3319/// new `(char, char)` sub-vocabulary paired array whose distinct-
3320/// value set is an intentionally-closed subset of another substrate
3321/// paired array adds ONE `const _` line.
3322pub const fn assert_char_pair_array_within_char_pair_finite_set<const N: usize, const M: usize>(
3323 arr: &[(char, char); N],
3324 set: &[(char, char); M],
3325) {
3326 // Delegate target-set well-formedness to the sibling paired-
3327 // array BIJECTIVITY helper FIRST. Placed BEFORE the CHAR-PAIR-
3328 // SUBSET-VIOLATION sweep below because a malformed `set` (e.g.
3329 // `[('a', 'x'), ('a', 'y')]` — LEFT-column collision; or
3330 // `[('a', 'x'), ('b', 'x')]` — RIGHT-column collision) is not a
3331 // well-formed finite bijective set of paired-cardinality `M` and
3332 // silently mis-verifies the intended SUBSET contract on any
3333 // `arr` embedded in the distinct-value bijective subset. Routes
3334 // drift on the CALLER'S TARGET-SET SPEC to the SET-side
3335 // well-formedness axis (via the sibling's own panic-name
3336 // prefix) rather than to a downstream CHAR-PAIR-SUBSET-VIOLATION
3337 // symptom on `arr`. A well-formed `set` passes this arm as a no-
3338 // op — the sweep is const-eval-elidable and costs zero at
3339 // rustc-time on the substrate call sites.
3340 assert_char_pair_array_bijective(set);
3341 let mut i = 0;
3342 while i < N {
3343 let mut j = 0;
3344 let mut found = false;
3345 while j < M {
3346 // CONJOINED-pair equality — BOTH columns must match the
3347 // SAME `set[j]` entry. A per-column membership check on
3348 // either column alone (e.g. `arr[i].0 in set.map(|p|
3349 // p.0)` AND separately `arr[i].1 in set.map(|p| p.1)`)
3350 // would silently accept a cross-row aliasing collision
3351 // where `arr[i].0 == set[j1].0` for some `j1` AND
3352 // `arr[i].1 == set[j2].1` for some DIFFERENT `j2` while
3353 // `arr[i]` itself is NOT in `set` (a pair the CONJOINED
3354 // gate rejects).
3355 if arr[i].0 as u32 == set[j].0 as u32 && arr[i].1 as u32 == set[j].1 as u32 {
3356 found = true;
3357 break;
3358 }
3359 j += 1;
3360 }
3361 if !found {
3362 panic!(
3363 "assert_char_pair_array_within_char_pair_finite_set: \
3364 CHAR-PAIR-SUBSET-VIOLATION — the family-wide \
3365 `[(char, char); N]` paired substrate array `arr` \
3366 carries a pair at some position whose (SOURCE, \
3367 DECODED) tuple is NOT a byte-for-byte-equal member \
3368 of the target finite superset paired-array \
3369 partition `set`. The substrate's paired-array \
3370 SUBSET-EMBEDDING contract on `arr` is broken; every \
3371 consumer that expects the paired array's distinct-\
3372 value set of `(SOURCE, DECODED)` tuples to be a \
3373 subset of the target finite superset paired \
3374 partition (`Atom::NAMED_ESCAPE_TABLE ⊂ Atom::\
3375 ESCAPE_TABLE` on the pattern-DISTINCT-from-value \
3376 sub-vocabulary axis of the paired escape-arm SPAN; \
3377 any future typed-paired-subset embedding on the \
3378 substrate's reader-boundary `(char, char)` \
3379 algebras) relies on every array pair staying within \
3380 the target paired superset. Fix at the SUBSET-side \
3381 ARRAY-DECLARATION site (the `arr` under \
3382 verification, NOT the `set` argument specifying the \
3383 target superset) by dropping the offending pair OR \
3384 by extending `set` to cover it — the choice depends \
3385 on whether the drift is an unintended overshoot \
3386 outside the parent superset or an intentional \
3387 extension of the superset paired vocabulary. The \
3388 CONJOINED-pair equality gate distinguishes THIS \
3389 helper from a per-column membership check that \
3390 would silently accept a cross-row aliasing pair \
3391 (`arr[i].0` matches ONE set entry's LEFT column \
3392 while `arr[i].1` matches a DIFFERENT set entry's \
3393 RIGHT column) — the CONJOINED gate rejects the \
3394 pair unless BOTH columns match the SAME `set[j]`"
3395 );
3396 }
3397 i += 1;
3398 }
3399}
3400
3401// Compile-time SUBSET-embedding witness — the ONE intentionally-
3402// closed `[(char, char); N] ⊂ [(char, char); M]` paired sub-
3403// vocabulary carving on the substrate's Str-payload tokenization
3404// boundary whose distinct-value set of `(SOURCE, DECODED)` tuples
3405// is a subset of another family-wide paired substrate array's
3406// distinct-value set. Pre-lift the SUBSET relation lived only
3407// implicitly at the composite `Atom::ESCAPE_TABLE`'s declaration
3408// site (`Self::NAMED_ESCAPE_TABLE[0]`, `Self::NAMED_ESCAPE_TABLE[1]`,
3409// `Self::NAMED_ESCAPE_TABLE[2]` indexing into the composite's first
3410// three positions). Post-lift the ARRAY-LEVEL subset embedding
3411// binds at rustc time — a regression that silently re-inlined the
3412// SUBSET or SUPERSET side of the relation to a fresh pair breaking
3413// the subset containment fails at `cargo check` BEFORE any test
3414// scheduler runs. Sibling to the pre-existing BIJECTIVITY witnesses
3415// above — those pin (LEFT-column INJECTIVITY ∧ RIGHT-column
3416// INJECTIVITY) on each individual paired array, this pins
3417// CONJOINED-pair SUBSET containment across a PAIR of paired arrays.
3418// Peer to the (char) + (u8) + (`&'static str`) scalar rows' SUBSET-
3419// embedding witnesses above on the (element-type) axis of the SAME
3420// (subset-embedding) contract-shape column.
3421const _: () = assert_char_pair_array_within_char_pair_finite_set::<3, 5>(
3422 &Atom::NAMED_ESCAPE_TABLE,
3423 &Atom::ESCAPE_TABLE,
3424);
3425
3426/// Compile-time contract verifier — panics at const evaluation time if
3427/// any pair of `a` aliases any pair of `b` byte-for-byte through
3428/// CONJOINED-pair equality on both `(char, char)` columns.
3429///
3430/// Row-dual peer to [`assert_char_arrays_disjoint`] +
3431/// [`assert_u8_arrays_disjoint`] + [`assert_str_arrays_disjoint`] on the
3432/// (element-type) axis of the (element-type × contract-shape) matrix at
3433/// the (disjointness) column: where the three SCALAR (char, u8,
3434/// `&'static str`) siblings close the DISJOINTNESS corner at ONE peer
3435/// helper per scalar element type, this helper opens the PRODUCT-ELEMENT
3436/// `(char, char)` row on the same column — extending the
3437/// (element-type × contract-shape) matrix's (disjointness) column past
3438/// the three scalar rows onto the paired-array row. Combined with the
3439/// pre-existing [`assert_char_pair_array_bijective`] (INJECTIVITY axis)
3440/// and [`assert_char_pair_array_within_char_pair_finite_set`] (SUBSET-
3441/// EMBEDDING axis), the substrate now closes the (paired-array
3442/// well-formedness, paired-array subset, paired-array disjointness)
3443/// 3-corner face on the `(char, char)` row of the (element-type ×
3444/// contract-shape) matrix at THREE peer const-fn helpers.
3445///
3446/// Contract-orthogonal peer to
3447/// [`assert_char_pair_array_within_char_pair_finite_set`] on the
3448/// (SUBSET-EMBEDDING, DISJOINTNESS) axis of the (contract-shape) column
3449/// on the SAME `(char, char)` row: where the SUBSET-EMBEDDING sibling
3450/// binds `arr ⊆ set` (every `arr` pair IS in `set`), this DISJOINTNESS
3451/// sibling binds `a ∩ b = ∅` (no `a` pair is in `b`). Together the two
3452/// helpers close the (paired-array subset, paired-array disjointness)
3453/// 2-corner face on the `(char, char)` row peer to the same face on
3454/// each of the (char, u8, `&'static str`) SCALAR rows.
3455///
3456/// CONJOINED-pair equality gate: `a[i] == b[j]` iff `a[i].0 == b[j].0
3457/// AND a[i].1 == b[j].1`. A per-column disjointness check on either
3458/// column alone (e.g. `a[i].0 ∉ b.map(|p| p.0)` AND separately
3459/// `a[i].1 ∉ b.map(|p| p.1)`) would REJECT paired arrays whose columns
3460/// share entries INDIVIDUALLY across rows even when NO CONJOINED pair
3461/// aliases — a stricter contract than paired-array disjointness. The
3462/// CONJOINED gate here PERMITS the per-column aliasing corner as
3463/// disjoint (a pair `('a', 'x')` in `a` and a pair `('a', 'y')` in `b`
3464/// alias at the LEFT column only — the CONJOINED pairs differ so the
3465/// paired arrays are correctly disjoint). This is the SAME gate the
3466/// sibling [`assert_char_pair_array_within_char_pair_finite_set`] uses
3467/// on its SUBSET arm — the two helpers share ONE product-element
3468/// equality relation across the (SUBSET, DISJOINTNESS) axis so a
3469/// caller who understands the gate on ONE arm carries the intuition to
3470/// the other.
3471///
3472/// The invariant is load-bearing for the substrate's Str-payload
3473/// escape-arm dispatch at [`Atom::decode_str_escape`]: the FIVE
3474/// non-passthrough escape arms partition into the pattern-DISTINCT-
3475/// from-value [`Atom::NAMED_ESCAPE_TABLE`] (`[(char, char); 3]`,
3476/// `'n' → '\n'`, `'t' → '\t'`, `'r' → '\r'`) and the pattern-EQUALS-
3477/// value self-escape rows (`STR_DELIMITER → STR_DELIMITER`,
3478/// `STR_ESCAPE_LEAD → STR_ESCAPE_LEAD` — expressed as pairs
3479/// `[(SELF_ESCAPE_TABLE[0], SELF_ESCAPE_TABLE[0]),
3480/// (SELF_ESCAPE_TABLE[1], SELF_ESCAPE_TABLE[1])]` at the paired-
3481/// vocabulary level). The two sub-vocabularies MUST remain disjoint —
3482/// otherwise a NAMED escape source (`'n'` / `'t'` / `'r'`) would
3483/// simultaneously ALSO be a SELF-escape source, breaking the
3484/// two-sub-vocabulary partition of [`Atom::ESCAPE_TABLE`] and
3485/// silently routing an escape byte through TWO cascading arms of
3486/// `decode_str_escape` at the same input position (with the earlier-
3487/// matched arm winning arbitrarily). Post-lift the paired-array
3488/// DISJOINTNESS of the two sub-vocabularies binds at rustc time via
3489/// ONE `const _` line — a regression that silently drifted a NAMED
3490/// escape source to `Atom::STR_DELIMITER` (colliding with the self-
3491/// escape row's first entry) OR drifted [`Atom::STR_DELIMITER`] to
3492/// `'n'` (colliding with the NEWLINE named-escape source) fails at
3493/// `cargo check` BEFORE any test scheduler runs.
3494///
3495/// SYMMETRY IN THE NESTED SWEEP: the inner `while j < M` sweep visits
3496/// every position of `b` per outer `i` and panics at the FIRST cross-
3497/// array CONJOINED-pair collision (`a[i] == b[j]` on both columns).
3498/// Because the relation is symmetric and the two-loop sweep visits
3499/// every `(i, j) ∈ [0, N) × [0, M)` pair, swapping `a` and `b` at the
3500/// call site produces the SAME verdict — the helper does NOT
3501/// gratuitously depend on argument order. Row-parallel to the SCALAR
3502/// siblings' symmetric-nested-sweep posture — the shape of the
3503/// disjointness relation carries no argument-order preference across
3504/// the (element-type) axis.
3505///
3506/// Adding a new family-wide `[(char, char); N]` paired sub-vocabulary
3507/// whose distinct-values set must remain disjoint from another substrate
3508/// `[(char, char); M]` paired array's distinct-values set: pair the
3509/// declaration with `const _: () =
3510/// assert_char_pair_arrays_disjoint::<N, M>(&Self::FOO_ARRAY,
3511/// &Other::BAR_ARRAY);` co-located after the array's declaration and
3512/// the paired-array DISJOINTNESS contract binds at compile time. The
3513/// rustc-forced arities `[(char, char); N]` and `[(char, char); M]`
3514/// compose with this const-eval sweep so BOTH cardinality-pair AND
3515/// cross-array CONJOINED-pair disjointness are compile-time theorems
3516/// on the SAME (a, b) paired-array pair.
3517///
3518/// Runtime callability: the function is a normal `pub const fn`, so
3519/// callers CAN also invoke it at runtime — pinned by
3520/// `assert_char_pair_arrays_disjoint_panics_at_runtime_on_collision`
3521/// and `assert_char_pair_arrays_disjoint_panic_message_names_the_
3522/// helper_and_char_pair_disjointness_violation_axis`. The panic site
3523/// carries the `"CHAR-PAIR-DISJOINTNESS-VIOLATION"` axis-provenance
3524/// string chosen DISTINCT from every sibling helper's axis vocabulary
3525/// (`"CHAR-DISJOINTNESS-VIOLATION"` / `"U8-DISJOINTNESS-VIOLATION"` /
3526/// `"STR-DISJOINTNESS-VIOLATION"` on the three SCALAR row-dual
3527/// DISJOINTNESS peers; `"CHAR-PAIR-SUBSET-VIOLATION"` on the paired-
3528/// array SUBSET-embedding sibling; `"LEFT column"` / `"RIGHT column"`
3529/// on the paired-array BIJECTIVITY sibling). The `"CHAR-PAIR-"` prefix
3530/// disambiguates from the (char) SCALAR row-dual DISJOINTNESS peer;
3531/// the shared `"-DISJOINTNESS-VIOLATION"` suffix lets callers grep any
3532/// row's DISJOINTNESS sibling by `"DISJOINTNESS-VIOLATION"` alone or
3533/// route to the specific element-type by the axis prefix.
3534///
3535/// Theory grounding:
3536/// - THEORY.md §V.1 — knowable platform; the family-wide paired-array
3537/// cross-array disjointness contract on `(char, char)` sub-
3538/// vocabularies becomes a TYPE-LEVEL theorem the substrate carries
3539/// per (a, b) paired-array pair rather than a runtime test the
3540/// developer must remember to write per pair.
3541/// - THEORY.md §III — the typescape; the (element-type × contract-
3542/// shape) matrix's (DISJOINTNESS) column now carries the PRODUCT-
3543/// ELEMENT `(char, char)` row alongside the three SCALAR rows at ONE
3544/// peer const-fn helper per row. The (element-type ∈ {char, u8,
3545/// `&'static str`, `(char, char)`} × contract-shape ∈ {disjointness})
3546/// 4-corner column of the paired-array-contract prism is now closed
3547/// at FOUR peer const-fn helpers.
3548/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
3549/// DISJOINTNESS proof at declaration site AND the outer-algebra's
3550/// two-sub-vocabulary partition contract on `Atom::ESCAPE_TABLE`
3551/// (pattern-DISTINCT-from-value NAMED rows disjoint from pattern-
3552/// EQUALS-value SELF rows) regenerate through the SAME `const _`
3553/// witness at the paired-ARRAY level.
3554/// - THEORY.md §VI.1 — generation over composition; the const-eval
3555/// CONJOINED-pair membership sweep IS the generative shape. Every
3556/// new `(char, char)` sub-vocabulary paired array whose distinct-
3557/// value set is an intentionally-disjoint peer of another substrate
3558/// paired array adds ONE `const _` line.
3559///
3560/// Frontier inspiration: Lean 4's `Finset.disjoint_iff_ne` unfolded to
3561/// `∀ a ∈ s, ∀ b ∈ t, a ≠ b` at the concrete two-array
3562/// `[(char, char); N] × [(char, char); M]` monomorphic realisation
3563/// with the CONJOINED-pair `≠` decidable on product `(α × β)` reducing
3564/// to `a ≠ b ∨ c ≠ d` on `(a, c) ≠ (b, d)` — the substrate primitive
3565/// here embeds the same product-decidable disjointness relation as a
3566/// rustc const-eval-time proof obligation at every
3567/// `assert_char_pair_arrays_disjoint` call site.
3568pub const fn assert_char_pair_arrays_disjoint<const N: usize, const M: usize>(
3569 a: &[(char, char); N],
3570 b: &[(char, char); M],
3571) {
3572 let mut i = 0;
3573 while i < N {
3574 let mut j = 0;
3575 while j < M {
3576 // CONJOINED-pair equality — BOTH columns must match the
3577 // SAME `(a[i], b[j])` entry pair. A per-column disjointness
3578 // check on either column alone would REJECT paired arrays
3579 // whose columns share entries INDIVIDUALLY across rows even
3580 // when no CONJOINED pair aliases (a stricter contract than
3581 // paired-array disjointness). The CONJOINED gate permits
3582 // per-column aliasing as disjoint when the pairs themselves
3583 // differ — mirrors the sibling paired-array SUBSET
3584 // helper's CONJOINED gate on the same `(char, char)` row.
3585 if a[i].0 as u32 == b[j].0 as u32 && a[i].1 as u32 == b[j].1 as u32 {
3586 panic!(
3587 "assert_char_pair_arrays_disjoint: CHAR-PAIR-\
3588 DISJOINTNESS-VIOLATION — the two family-wide \
3589 `[(char, char); N]` paired substrate arrays `a` \
3590 and `b` share a CONJOINED-pair entry at some \
3591 (i, j) position pair (BOTH columns of `a[i]` \
3592 byte-for-byte equal BOTH columns of `b[j]`). The \
3593 substrate's CROSS-ARRAY PAIRED-DISJOINTNESS \
3594 contract on the pair is broken; every consumer \
3595 that partitions the two paired arrays' distinct-\
3596 value CONJOINED-pair sets into disjoint sub-\
3597 vocabularies of a shared outer surface (the Str-\
3598 payload escape-arm dispatch through `Atom::decode_\
3599 str_escape` partitioning `Atom::ESCAPE_TABLE` \
3600 into pattern-DISTINCT-from-value `Atom::NAMED_\
3601 ESCAPE_TABLE` rows disjoint from pattern-EQUALS-\
3602 value self-escape rows expressed as `(c, c)` \
3603 pairs; any future typed-disjointness pair on the \
3604 substrate's reader-boundary `(char, char)` \
3605 algebras) relies on the two paired arrays' \
3606 distinct-value CONJOINED-pair sets NOT sharing a \
3607 pair. Fix at WHICHEVER ARRAY-DECLARATION site \
3608 drifted (the symmetric disjointness relation \
3609 carries no built-in axis-provenance role split \
3610 between `a` and `b`) by dropping the offending \
3611 pair from one array OR re-shaping the partition \
3612 to route the shared pair to a single sub-\
3613 vocabulary. The CONJOINED-pair equality gate \
3614 distinguishes THIS helper from a per-column \
3615 disjointness check that would REJECT paired \
3616 arrays whose columns share entries INDIVIDUALLY \
3617 across rows even when no CONJOINED pair aliases \
3618 — the CONJOINED gate permits the per-column \
3619 aliasing corner as disjoint"
3620 );
3621 }
3622 j += 1;
3623 }
3624 i += 1;
3625 }
3626}
3627
3628// Compile-time DISJOINTNESS witness — the ONE substrate-pinned
3629// `[(char, char); N] × [(char, char); M]` paired-array pair whose
3630// distinct-value CONJOINED-pair sets are intentionally-closed disjoint
3631// sub-vocabularies of the Str-payload escape-arm dispatch at
3632// `Atom::decode_str_escape`. The pattern-DISTINCT-from-value NAMED rows
3633// (`Atom::NAMED_ESCAPE_TABLE`, `[(char, char); 3]`) partition
3634// `Atom::ESCAPE_TABLE`'s first three positions; the pattern-EQUALS-
3635// value SELF rows lifted to `(c, c)` pair form (constructed inline
3636// from `Atom::SELF_ESCAPE_TABLE`) partition `Atom::ESCAPE_TABLE`'s
3637// remaining two positions. Pre-lift the paired-DISJOINTNESS of the
3638// two sub-vocabularies lived only implicitly at `Atom::ESCAPE_TABLE`'s
3639// composite declaration site (positions [0..3] as `NAMED_ESCAPE_TABLE`
3640// indexings + positions [3..5] as `(SELF[k], SELF[k])` pair
3641// constructions — the disjointness of the two SLICES holding by
3642// construction rather than by a checked contract). Post-lift the
3643// paired-ARRAY-LEVEL disjointness binds at rustc time via ONE `const
3644// _` line. A regression that silently drifted a NAMED escape source
3645// (e.g. `NEWLINE_ESCAPE_SOURCE` from `'n'` to `Atom::STR_DELIMITER`,
3646// colliding with the SELF row's first entry) OR drifted `Atom::STR_
3647// DELIMITER` to `'n'` (colliding with the NAMED NEWLINE source) fails
3648// at `cargo check` BEFORE any test scheduler runs. Sibling to the
3649// pre-existing paired-array BIJECTIVITY witnesses above (which pin
3650// LEFT-column ∧ RIGHT-column injectivity on each individual paired
3651// array) and the paired-array SUBSET-EMBEDDING witness (which pins
3652// `NAMED_ESCAPE_TABLE ⊂ ESCAPE_TABLE` at CONJOINED-pair containment)
3653// on the `(char, char)` row of the (element-type × contract-shape)
3654// matrix — the three witnesses together close the (paired-array well-
3655// formedness, paired-array subset, paired-array disjointness) 3-corner
3656// face on the paired-array row at ONE peer const-fn helper per corner.
3657// Row-parallel to the (char) + (u8) + (`&'static str`) SCALAR rows'
3658// DISJOINTNESS witnesses above on the (element-type) axis of the SAME
3659// (disjointness) contract-shape column.
3660const _: () = assert_char_pair_arrays_disjoint::<3, 2>(
3661 &Atom::NAMED_ESCAPE_TABLE,
3662 &[
3663 (Atom::SELF_ESCAPE_TABLE[0], Atom::SELF_ESCAPE_TABLE[0]),
3664 (Atom::SELF_ESCAPE_TABLE[1], Atom::SELF_ESCAPE_TABLE[1]),
3665 ],
3666);
3667
3668/// Compile-time contract verifier — panics at const evaluation time if
3669/// the family-wide `[(char, char); N]` paired substrate array `arr`
3670/// carries a pair at two distinct positions whose `(SOURCE, DECODED)`
3671/// tuples are byte-for-byte equal. Binds ONE conjunct clause on the
3672/// `(char, char)` product-element row of the (element-type × contract-
3673/// shape) matrix at the (pairwise-distinctness) column — CHAR-PAIR-
3674/// TUPLE-COLLISION — every position of `arr` MUST carry a
3675/// CONJOINED-pair `(a, b)` byte-for-byte distinct from every other
3676/// position's pair. Pair equality is CONJOINED across BOTH columns:
3677/// `arr[i] == arr[j]` iff `arr[i].0 == arr[j].0 AND arr[i].1 ==
3678/// arr[j].1` — a per-column pairwise-distinct check on either column
3679/// alone would REJECT paired arrays whose columns share entries
3680/// INDIVIDUALLY across rows even when no CONJOINED tuple aliases
3681/// (a stricter contract than tuple-level pairwise-distinctness, which
3682/// the sibling [`assert_char_pair_array_bijective`] closes as the
3683/// (column-INDEPENDENT injectivity) axis on the SAME row).
3684///
3685/// Contract-strength peer to [`assert_char_pair_array_bijective`] on
3686/// the (independent-column-injectivity vs tuple-injectivity) axis
3687/// splitting the (char, char) row's INJECTIVITY column into TWO
3688/// distinct sub-contracts. Bijectivity is strictly stronger — a
3689/// bijective paired array (`assert_char_pair_array_bijective`) has
3690/// LEFT-column pairwise-distinct AND RIGHT-column pairwise-distinct
3691/// INDEPENDENTLY, which implies tuple-level pairwise-distinctness
3692/// (if `a[i].0 == a[j].0` fails and `a[i].1 == a[j].1` fails
3693/// independently, then the tuples cannot collide either). This
3694/// helper enforces ONLY the CONJOINED-tuple distinctness — a strictly
3695/// WEAKER contract that permits per-column aliasing across rows as
3696/// long as no CONJOINED tuple aliases. Mirrors the sibling
3697/// (char, char) row helpers' CONJOINED-pair gate posture:
3698/// [`assert_char_pair_array_within_char_pair_finite_set`] and
3699/// [`assert_char_pair_arrays_disjoint`] BOTH gate on
3700/// CONJOINED-tuple equality; this helper closes the third
3701/// CONJOINED-tuple sibling on the SAME (char, char) row.
3702///
3703/// Element-type row-parallel to the three SCALAR-element pairwise-
3704/// distinctness sibling helpers on the (element-type) axis:
3705/// [`assert_char_array_pairwise_distinct`] (the (char) scalar-element
3706/// peer), [`assert_str_array_pairwise_distinct`] (the (`&'static str`)
3707/// scalar-element peer), and [`assert_u8_array_pairwise_distinct`]
3708/// (the (u8) scalar-element peer). Where the three scalar-element
3709/// siblings close the INJECTIVITY column on the three SCALAR rows of
3710/// the (element-type × contract-shape) matrix, this helper closes the
3711/// TUPLE-level INJECTIVITY corner on the (char, char) PRODUCT-element
3712/// row — the fourth row of the matrix's element-type axis. Together
3713/// with [`assert_char_pair_array_bijective`] (the column-INDEPENDENT
3714/// INJECTIVITY peer on the SAME row) the (char, char) row now closes
3715/// BOTH INJECTIVITY sub-shapes at ONE const-fn helper each: the
3716/// stronger column-independent axis at the bijective sibling; the
3717/// weaker CONJOINED-tuple axis here.
3718///
3719/// Runtime callability: the function is a normal `pub const fn`, so
3720/// callers CAN also invoke it at runtime — e.g. a REPL / LSP
3721/// tokenizer that constructs a `[(char, char); N]` paired array at
3722/// runtime and wants to verify tuple-level pairwise-distinctness
3723/// before consuming it — and the panic surfaces normally in that
3724/// path (pinned by
3725/// `assert_char_pair_array_pairwise_distinct_panics_at_runtime_on_tuple_collision`
3726/// and
3727/// `assert_char_pair_array_pairwise_distinct_panic_message_names_the_helper_and_char_pair_tuple_collision_axis`).
3728/// The panic message carries a CHAR-PAIR-TUPLE-COLLISION axis-
3729/// provenance suffix so downstream diagnostics (`cargo check` const-
3730/// eval error output, test-suite failure reports) route the drift
3731/// back to THIS helper's tuple-level axis rather than to the
3732/// sibling bijective helper's column-LABELED axis (LEFT-column vs
3733/// RIGHT-column) — halving the search space for the operator
3734/// debugging a drift that surfaces at the tuple axis but is invisible
3735/// at either column axis alone.
3736///
3737/// Adding a new family-wide `[(char, char); N]` paired-array whose
3738/// distinct-value tuple-set carries a pairwise-distinct-but-NOT-
3739/// bijective contract: pair the declaration with `const _: () =
3740/// assert_char_pair_array_pairwise_distinct(&Self::FOO_TABLE);` co-
3741/// located after the array's declaration and the tuple-level
3742/// distinctness contract binds at compile time. The rustc-forced
3743/// arity `[(char, char); N]` composes with this const-eval sweep so
3744/// cardinality AND tuple-level injectivity are compile-time theorems
3745/// on the SAME array. A future consumer keyed on tuple-level
3746/// (rather than column-level) distinctness — a hypothetical (short-
3747/// form, canonical-form) alias table that permits many-to-one
3748/// aliasing on either column but requires the paired short↔canonical
3749/// tuples to be pairwise-distinct — reaches for THIS helper without
3750/// composing through the strictly stronger bijective sibling.
3751///
3752/// Theory grounding:
3753/// - THEORY.md §V.1 — knowable platform; the family-wide tuple-
3754/// level pairwise-distinctness contract on the `(char, char)`
3755/// paired substrate vocabulary becomes a TYPE-LEVEL theorem the
3756/// substrate carries per paired-array declaration rather than a
3757/// runtime test the developer must remember to write per array.
3758/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; a
3759/// tuple-level distinctness proof at declaration site AND the
3760/// tuple-level match-arm exhaustiveness at every consumer that
3761/// enumerates the array's tuples as DISJOINT arms regenerate
3762/// through the SAME `const _` witness.
3763/// - THEORY.md §VI.1 — generation over composition; the const-eval
3764/// tuple-CONJOINED sweep IS the generative shape. Every new
3765/// closed-set paired-array vocabulary whose distinct-value tuple-
3766/// set is intentionally pairwise-distinct-at-the-tuple-level adds
3767/// ONE `const _` line to get the tuple-level distinctness theorem
3768/// rather than re-deriving a `HashSet::insert` loop test per array.
3769///
3770/// Frontier inspiration: Lean 4's `List.Nodup` on `List (α × β)` at
3771/// the concrete monomorphic realisation `[(char, char); N]` with the
3772/// CONJOINED-tuple `≠` decidable on product `(α × β)` reducing to
3773/// `a ≠ b ∨ c ≠ d` on `(a, c) ≠ (b, d)` — the substrate primitive
3774/// here embeds the same product-decidable pairwise-distinctness
3775/// relation as a rustc const-eval-time proof obligation at every
3776/// `assert_char_pair_array_pairwise_distinct` call site.
3777pub const fn assert_char_pair_array_pairwise_distinct<const N: usize>(arr: &[(char, char); N]) {
3778 let mut i = 0;
3779 while i < N {
3780 let mut j = i + 1;
3781 while j < N {
3782 // CONJOINED-tuple equality — BOTH columns must match the
3783 // SAME `(arr[i], arr[j])` position pair. A per-column
3784 // pairwise-distinct check on either column alone would
3785 // REJECT paired arrays whose columns share entries
3786 // INDIVIDUALLY across rows even when no CONJOINED tuple
3787 // aliases (a stricter contract than tuple-level pairwise-
3788 // distinctness, which the sibling
3789 // `assert_char_pair_array_bijective` closes as the
3790 // column-INDEPENDENT INJECTIVITY axis on the SAME row).
3791 // The CONJOINED gate is the (char, char) row's WEAKER
3792 // pairwise-distinctness axis — mirrors the sibling
3793 // `assert_char_pair_arrays_disjoint` +
3794 // `assert_char_pair_array_within_char_pair_finite_set`
3795 // CONJOINED-tuple gates on the same row.
3796 if arr[i].0 as u32 == arr[j].0 as u32 && arr[i].1 as u32 == arr[j].1 as u32 {
3797 panic!(
3798 "assert_char_pair_array_pairwise_distinct: CHAR-\
3799 PAIR-TUPLE-COLLISION — family-wide `[(char, \
3800 char); N]` paired substrate array carries a \
3801 duplicate CONJOINED-tuple entry across two \
3802 positions (BOTH columns of `arr[i]` byte-for-\
3803 byte equal BOTH columns of `arr[j]`). The \
3804 substrate's TUPLE-level pairwise-distinctness \
3805 contract on the array is broken; every consumer \
3806 that pattern-matches the array's CONJOINED-\
3807 tuples as DISJOINT arms (any future \
3808 `match (a, b) {{ … }}` outer dispatch on a paired \
3809 vocabulary, any future tuple-keyed cache index \
3810 on a `[(char, char); N]` table) relies on this \
3811 invariant. Fix at the ARRAY-DECLARATION site by \
3812 dropping the duplicate tuple OR re-shaping the \
3813 partition to route the shared tuple to a single \
3814 position. The CONJOINED-tuple equality gate \
3815 distinguishes THIS helper from a per-column \
3816 pairwise-distinct check that would REJECT paired \
3817 arrays whose columns share entries INDIVIDUALLY \
3818 across rows even when no CONJOINED tuple aliases \
3819 — the CONJOINED gate is the (char, char) row's \
3820 WEAKER pairwise-distinctness axis, peer to the \
3821 stricter column-INDEPENDENT INJECTIVITY axis at \
3822 `assert_char_pair_array_bijective`"
3823 );
3824 }
3825 j += 1;
3826 }
3827 i += 1;
3828 }
3829}
3830
3831// Compile-time TUPLE-level pairwise-distinctness witnesses — one
3832// `const _: () = assert_char_pair_array_pairwise_distinct(&…)` per
3833// family-wide `[(char, char); N]` paired substrate array on the Str-
3834// payload escape-table product-vocabulary. Each invocation is const-
3835// evaluated at `cargo check` time; a regression that silently
3836// collided TWO CONJOINED-tuples across positions fails the build
3837// rather than the test suite. Sibling to the pre-existing paired-
3838// array BIJECTIVITY witnesses above (which pin the strictly stronger
3839// column-INDEPENDENT INJECTIVITY axis — LEFT-column ∧ RIGHT-column
3840// pairwise-distinct per array); this helper pins the WEAKER
3841// CONJOINED-tuple INJECTIVITY axis at the SAME two arrays. Both
3842// axes are compile-time theorems on both arrays post-lift: bijective
3843// binds the strictly stronger axis (implied on both existing
3844// arrays); this helper binds the WEAKER tuple-level axis. Row-
3845// parallel to the three SCALAR-element rows' pairwise-distinct
3846// witnesses above on the (element-type) axis: (char) closes the
3847// reader-boundary vocabulary; (`&'static str`) closes the closed-set
3848// outer-algebras' label / prefix / tag / literal vocabularies; (u8)
3849// closes the outer-`Sexp` cache-key discriminator vocabulary; and
3850// this (char, char) product-element sibling closes the paired
3851// escape-table vocabulary at the TUPLE-level. The dual-axis coverage
3852// (bijective + pairwise-distinct-tuples) gives downstream consumers
3853// TWO axis-provenance vocabularies to route regressions through: a
3854// future consumer keyed on tuple-level (rather than column-level)
3855// distinctness — a hypothetical (short-form, canonical-form) alias
3856// table that permits many-to-one aliasing on either column but
3857// requires paired short↔canonical tuples to be pairwise-distinct —
3858// reaches for THIS helper's `CHAR-PAIR-TUPLE-COLLISION` panic
3859// message rather than for the bijective sibling's column-LABELED
3860// panic (LEFT-column collision vs RIGHT-column collision).
3861const _: () = assert_char_pair_array_pairwise_distinct(&Atom::NAMED_ESCAPE_TABLE);
3862const _: () = assert_char_pair_array_pairwise_distinct(&Atom::ESCAPE_TABLE);
3863
3864/// Compile-time contract verifier — panics at const evaluation time
3865/// if either column-projection of the family-wide `[(char, char); N]`
3866/// paired substrate array `pairs` diverges byte-for-byte from its
3867/// declared peer scalar `[char; N]` array. Binds a JOINT
3868/// (LEFT_col == left) ∧ (RIGHT_col == right) POSITIONWISE-EQUALITY
3869/// contract at ONE `const _` line on the (char, char) product-element
3870/// row of the (element-type × contract-shape) matrix at the
3871/// (column-projection-equality) column — a NEW column opened past the
3872/// (INJECTIVITY, SUBSET-EMBEDDING, DISJOINTNESS) triple the four
3873/// sibling helpers close ([`assert_char_pair_array_bijective`] +
3874/// [`assert_char_pair_array_pairwise_distinct`] on INJECTIVITY,
3875/// [`assert_char_pair_array_within_char_pair_finite_set`] on SUBSET-
3876/// EMBEDDING, [`assert_char_pair_arrays_disjoint`] on DISJOINTNESS).
3877///
3878/// Rust's forced `[T; N]` cardinality composes with the const-eval
3879/// sweep to pin THREE clauses at ONE `const _` line:
3880/// 1. ARITY: `pairs.len() == left.len() == right.len() == N` — a
3881/// regression that drifts EITHER peer scalar array's arity away
3882/// from the paired array's `N` fails-loudly at type-check time
3883/// (before const eval), so the two clauses BELOW walk a
3884/// length-parallel sweep unconditionally.
3885/// 2. LEFT-COLUMN-EQUALITY: `pairs[i].0 == left[i]` for every
3886/// `i ∈ 0..N` — a regression that drifts ONE paired-table LEFT
3887/// entry away from the peer scalar SOURCE array (e.g. renames
3888/// `NEWLINE_ESCAPE_SOURCE` to `LINEFEED_ESCAPE_SOURCE` and
3889/// updates ONLY the pair entry without updating the scalar
3890/// declaration, OR vice versa) fails-loudly at const eval.
3891/// 3. RIGHT-COLUMN-EQUALITY: `pairs[i].1 == right[i]` for every
3892/// `i ∈ 0..N` — same posture on the RIGHT / DECODED column.
3893///
3894/// Currently applied to the (`ESCAPE_TABLE`, `ESCAPE_SOURCES`,
3895/// `ESCAPE_DECODED`) triple where the paired array is CONSTRUCTED
3896/// from `NAMED_ESCAPE_TABLE` + `SELF_ESCAPE_TABLE` (see
3897/// [`Atom::ESCAPE_TABLE`]'s definition) and the two peer scalar
3898/// arrays are DECLARED INDEPENDENTLY as
3899/// [`Atom::ESCAPE_SOURCES`](Atom::ESCAPE_SOURCES) +
3900/// [`Atom::ESCAPE_DECODED`](Atom::ESCAPE_DECODED). Pre-lift the
3901/// three-way column bond ("`ESCAPE_TABLE`'s LEFT column IS
3902/// `ESCAPE_SOURCES` AND RIGHT column IS `ESCAPE_DECODED`") lived
3903/// as PROSE in the peer arrays' docstrings ("the SPAN of the two
3904/// peer sub-vocabulary source columns") plus as a runtime test that
3905/// zipped the three arrays; post-lift the bond binds at `cargo
3906/// check` time — a regression that renames a source or decoded byte
3907/// on ONE of the three arrays without updating the other two fails
3908/// the build rather than the test suite. The const-eval panic
3909/// surfaces the column-divergence at COMPILE time, one invocation
3910/// stage earlier, catching regressions on `cargo build` /
3911/// `cargo clippy` runs that skip the test suite.
3912///
3913/// Contract-shape peer to the four sibling paired-array verifiers
3914/// on the (char, char) row: where the sibling
3915/// [`assert_char_pair_array_bijective`] closes the INJECTIVITY axis
3916/// (LEFT-column ∧ RIGHT-column pairwise-distinct, INDEPENDENTLY),
3917/// [`assert_char_pair_array_pairwise_distinct`] closes the WEAKER
3918/// CONJOINED-tuple INJECTIVITY axis, [`assert_char_pair_array_
3919/// within_char_pair_finite_set`] closes the SUBSET-EMBEDDING axis,
3920/// and [`assert_char_pair_arrays_disjoint`] closes the DISJOINTNESS
3921/// axis, THIS helper opens the fifth (column-projection-EQUALITY)
3922/// axis — the (LEFT column == left) ∧ (RIGHT column == right) JOINT
3923/// clause that bonds a paired vocabulary to TWO peer scalar
3924/// vocabularies. Compound-JOINT posture parallels the pre-existing
3925/// [`assert_scalar_plus_two_u8_arrays_permute_inclusive_range`] on
3926/// the (u8) row (three-array joint contract at one witness line);
3927/// unlike the permutation posture there, this helper's joint
3928/// contract is POSITIONWISE-EQUALITY (order-sensitive) rather than
3929/// set-EQUALITY.
3930///
3931/// Runtime callability: the function is a normal `pub const fn`, so
3932/// callers CAN also invoke it at runtime — e.g. a REPL / LSP
3933/// tokenizer that constructs a paired escape table + two peer
3934/// scalar columns at runtime and wants to verify column-projection
3935/// equality before consuming them — and the two panic sites (LEFT-
3936/// column divergence, RIGHT-column divergence) surface normally in
3937/// that path with column-provenance-preserving panic messages
3938/// (pinned by the two `_panics_at_runtime_on_left_head_divergence`
3939/// and `_panics_at_runtime_on_right_head_divergence` tests plus
3940/// their middle-position and tail-position peers). The two axis-
3941/// provenance strings `"LEFT-COLUMN-DIVERGENCE"` and
3942/// `"RIGHT-COLUMN-DIVERGENCE"` are chosen DISTINCT from every
3943/// sibling helper's axis vocabulary (`"LEFT column"` and
3944/// `"RIGHT column"` on the paired-array BIJECTIVITY sibling name the
3945/// COLUMN but the failure MODE is `duplicate SOURCE / DECODED char`;
3946/// this helper's failure MODE is `divergence from peer scalar
3947/// array`) so a diagnostic that names the failed axis routes
3948/// UNAMBIGUOUSLY to (a) this specific paired-array COLUMN-
3949/// PROJECTION-EQUALITY helper, (b) the failed COLUMN by the
3950/// `"LEFT-"` and `"RIGHT-"` prefix.
3951///
3952/// Adding a new family-wide `[(char, char); N]` paired array
3953/// declared alongside TWO peer scalar `[char; N]` arrays whose
3954/// LEFT + RIGHT columns are meant to project to the peer arrays'
3955/// contents: pair the declarations with `const _: () =
3956/// assert_char_pair_array_columns_equal_char_arrays(&Self::FOO_
3957/// TABLE, &Self::FOO_LEFT, &Self::FOO_RIGHT);` and the three-way
3958/// column bond binds at compile time WITHOUT a runtime zip loop.
3959///
3960/// Theory grounding:
3961/// - THEORY.md §V.1 — knowable platform; the three-way column bond
3962/// on the paired-array + peer-scalar-columns vocabulary becomes
3963/// a TYPE-LEVEL theorem the substrate carries per declaration
3964/// triple rather than a runtime zip loop the developer must
3965/// remember to write per triple.
3966/// - THEORY.md §II.1 invariant 5 — composition preserves proofs;
3967/// the column-projection-equality proof at declaration site AND
3968/// the peer-array outer-dispatch consumers regenerate through
3969/// the SAME `const _` witness.
3970/// - THEORY.md §VI.1 — generation over composition; the two-column
3971/// positionwise-equality sweep IS the generative shape. Every
3972/// new closed-set paired-array declared alongside peer scalar
3973/// columns adds ONE `const _` line to get the three-way bond
3974/// theorem rather than re-deriving a runtime zip loop per triple.
3975pub const fn assert_char_pair_array_columns_equal_char_arrays<const N: usize>(
3976 pairs: &[(char, char); N],
3977 left: &[char; N],
3978 right: &[char; N],
3979) {
3980 let mut i = 0;
3981 while i < N {
3982 if pairs[i].0 as u32 != left[i] as u32 {
3983 panic!(
3984 "assert_char_pair_array_columns_equal_char_arrays: \
3985 LEFT-COLUMN-DIVERGENCE — family-wide `[(char, \
3986 char); N]` paired substrate array's LEFT column \
3987 projection carries an entry at some position that \
3988 does NOT byte-for-byte equal the peer scalar \
3989 `[char; N]` array's entry at the SAME position. \
3990 The substrate's LEFT-column POSITIONWISE-EQUALITY \
3991 contract on the (paired, peer-scalar) column-\
3992 projection bond is broken; every consumer that \
3993 reads the paired array's LEFT column and the peer \
3994 scalar SOURCE array as INTERCHANGEABLE (any \
3995 outer-tokenizer `Sexp` dispatch that pattern-\
3996 matches on `Atom::ESCAPE_SOURCES` while a peer \
3997 attestation code path reads `Atom::ESCAPE_TABLE`'s \
3998 LEFT column, any future `[char; N]` peer-column \
3999 dual projection that assumes the two are equal at \
4000 every position) relies on this invariant. Fix at \
4001 one of the THREE declaration sites (`pairs`, \
4002 `left`, or the peer scalar RIGHT column's \
4003 declaration if the drift is a cross-column rename) \
4004 by reconciling the entry across the three arrays. \
4005 The LEFT-column-divergence gate distinguishes THIS \
4006 helper from the sibling `assert_char_pair_array_\
4007 bijective` (which surfaces LEFT-column DUPLICATE-\
4008 SOURCE failures, not LEFT-column-vs-peer-scalar \
4009 DIVERGENCE failures) and from the sibling `assert_\
4010 char_pair_array_within_char_pair_finite_set` \
4011 (which surfaces CONJOINED-tuple SUBSET-VIOLATION \
4012 failures, not per-column POSITIONWISE-EQUALITY \
4013 failures)"
4014 );
4015 }
4016 if pairs[i].1 as u32 != right[i] as u32 {
4017 panic!(
4018 "assert_char_pair_array_columns_equal_char_arrays: \
4019 RIGHT-COLUMN-DIVERGENCE — family-wide `[(char, \
4020 char); N]` paired substrate array's RIGHT column \
4021 projection carries an entry at some position that \
4022 does NOT byte-for-byte equal the peer scalar \
4023 `[char; N]` array's entry at the SAME position. \
4024 The substrate's RIGHT-column POSITIONWISE-EQUALITY \
4025 contract on the (paired, peer-scalar) column-\
4026 projection bond is broken; every consumer that \
4027 reads the paired array's RIGHT column and the peer \
4028 scalar DECODED array as INTERCHANGEABLE (any \
4029 outer-tokenizer `Sexp` dispatch that pattern-\
4030 matches on `Atom::ESCAPE_DECODED` while a peer \
4031 attestation code path reads `Atom::ESCAPE_TABLE`'s \
4032 RIGHT column, any future `[char; N]` peer-column \
4033 dual projection that assumes the two are equal at \
4034 every position) relies on this invariant. Fix at \
4035 one of the THREE declaration sites (`pairs`, \
4036 `right`, or the peer scalar LEFT column's \
4037 declaration if the drift is a cross-column rename) \
4038 by reconciling the entry across the three arrays"
4039 );
4040 }
4041 i += 1;
4042 }
4043}
4044
4045// Compile-time column-projection-EQUALITY witness — one `const _: ()
4046// = assert_char_pair_array_columns_equal_char_arrays(&…, &…, &…)`
4047// binding the (ESCAPE_TABLE, ESCAPE_SOURCES, ESCAPE_DECODED) three-
4048// way column bond on the substrate's Str-payload escape-table
4049// vocabulary. `Atom::ESCAPE_TABLE` (`[(char, char); 5]`) is
4050// CONSTRUCTED from `NAMED_ESCAPE_TABLE[0..=2]` + two SELF-diagonal
4051// pairs `(SELF_ESCAPE_TABLE[0], SELF_ESCAPE_TABLE[0])` +
4052// `(SELF_ESCAPE_TABLE[1], SELF_ESCAPE_TABLE[1])`, whose LEFT +
4053// RIGHT column projections match `Atom::ESCAPE_SOURCES` +
4054// `Atom::ESCAPE_DECODED` position-for-position by declaration
4055// intent — this const-eval witness binds that intent as a compile-
4056// time theorem so a regression that renames a source or decoded
4057// byte on ONE of the three arrays without updating the other two
4058// fails the build. Sibling to the `_pairwise_distinct` + `_bijective`
4059// witnesses above on the SAME paired array — the FOUR const-eval
4060// sweeps enforce complementary axes of the same substrate table at
4061// FOUR stages of the toolchain, so a build that skips tests still
4062// catches column-projection drift here, and a build that runs tests
4063// catches it a second time as a safety net if the const-eval sweep
4064// is ever silently dropped.
4065const _: () = assert_char_pair_array_columns_equal_char_arrays(
4066 &Atom::ESCAPE_TABLE,
4067 &Atom::ESCAPE_SOURCES,
4068 &Atom::ESCAPE_DECODED,
4069);
4070
4071/// Compile-time contract verifier — panics at const evaluation time if
4072/// the family-wide `[(char, char); N]` paired substrate array `arr`
4073/// carries a character that appears in BOTH its LEFT column projection
4074/// AND its RIGHT column projection (across ANY position pair). Binds
4075/// ONE conjunct clause on the `(char, char)` product-element row of the
4076/// (element-type × contract-shape) matrix at the
4077/// (cross-column-disjointness) column — CROSS-COLUMN-COLLISION — the
4078/// set of characters appearing as LEFT entries MUST be DISJOINT from
4079/// the set of characters appearing as RIGHT entries. Formally:
4080/// `∀ i, j ∈ 0..N : arr[i].0 as u32 != arr[j].1 as u32` — including
4081/// the diagonal `i == j` case (a `('a', 'a')` self-mapping pair
4082/// FAILS this contract even though it satisfies BOTH the SELF-arm
4083/// identity-relation shape AND the sibling
4084/// [`assert_char_pair_array_bijective`] per-column injectivity check).
4085///
4086/// Contract-shape orthogonality with the FOUR sibling paired-array
4087/// verifiers on the SAME row:
4088/// * [`assert_char_pair_array_bijective`] closes the per-column
4089/// INJECTIVITY axis (LEFT-column ∧ RIGHT-column pairwise-distinct,
4090/// INDEPENDENTLY) — a table like `[('a', 'x'), ('a', 'y')]` fails
4091/// BIJECTIVITY (LEFT duplicated) but passes CROSS-COLUMN-DISJOINT
4092/// (LEFT = {'a'}, RIGHT = {'x', 'y'}, disjoint). The two axes are
4093/// INDEPENDENT: neither implies the other.
4094/// * [`assert_char_pair_array_pairwise_distinct`] closes the WEAKER
4095/// CONJOINED-tuple INJECTIVITY axis — a table like
4096/// `[('a', 'a')]` passes CONJOINED-tuple pairwise-distinctness
4097/// (only one tuple) but FAILS CROSS-COLUMN-DISJOINT (LEFT[0] ==
4098/// RIGHT[0] on the diagonal).
4099/// * [`assert_char_pair_array_within_char_pair_finite_set`] closes
4100/// the SUBSET-EMBEDDING axis — orthogonal to cross-column
4101/// disjointness (a table can be a subset of a well-formed set and
4102/// still violate cross-column disjointness if the set itself does).
4103/// * [`assert_char_pair_arrays_disjoint`] closes the BETWEEN-ARRAY
4104/// DISJOINTNESS axis (two arrays share no CONJOINED tuple) — this
4105/// helper closes the WITHIN-ARRAY CROSS-COLUMN DISJOINTNESS axis
4106/// (one array's LEFT column shares no CHARACTER with its RIGHT
4107/// column). The two axes are DUAL: BETWEEN-ARRAY vs WITHIN-ARRAY,
4108/// CONJOINED-tuple vs CROSS-COLUMN-CHARACTER.
4109///
4110/// The invariant is load-bearing for the substrate's
4111/// pattern-DISTINCT-from-value sub-vocabulary at
4112/// [`Atom::NAMED_ESCAPE_TABLE`] (`[(char, char); 3]`): the three
4113/// named-escape rows `('n', '\n')`, `('t', '\t')`, `('r', '\r')` are
4114/// pattern-DISTINCT-from-value by algebra design (the printable ASCII
4115/// SOURCE column and the control-character DECODED column MUST NOT
4116/// overlap — a regression that added a pair like `('n', 'r')` or
4117/// `('a', 'a')` would silently collapse the pattern-DISTINCT-from-value
4118/// axis by routing a SOURCE character back to itself OR to another
4119/// SOURCE character, making the two sub-vocabularies
4120/// (pattern-distinct at [`Atom::NAMED_ESCAPE_TABLE`] +
4121/// pattern-equals at [`Atom::SELF_ESCAPE_TABLE`]) share membership).
4122/// The SHAPE ASYMMETRY between the two peer arrays (`[(char, char); N]`
4123/// vs `[char; N]`) already encodes the identity-relation asymmetry in
4124/// the TYPE — but a `[(char, char); N]` on the DISTINCT sub-vocabulary
4125/// side can silently drift a pair onto the diagonal while retaining
4126/// its paired shape. Pre-lift, the cross-column disjointness of
4127/// `NAMED_ESCAPE_TABLE` lived ONLY as prose in the substrate's escape-
4128/// table docstrings ("the THREE pattern-DISTINCT-from-value named-
4129/// escape rows") plus as an implicit consequence of the ASCII-letter
4130/// vs control-character byte-range partition. Post-lift the cross-
4131/// column disjointness binds at `cargo check` time — a regression
4132/// that adds a `('a', 'a')` diagonal pair, a `('n', 'r')` cross-row
4133/// SOURCE-to-SOURCE aliasing pair, or any pair drifting a character
4134/// across the LEFT / RIGHT partition fails the build rather than the
4135/// test suite.
4136///
4137/// Applies ONLY to the pattern-DISTINCT-from-value sub-vocabulary of
4138/// the escape-table family. [`Atom::ESCAPE_TABLE`] (`[(char, char); 5]`)
4139/// INTENTIONALLY violates cross-column disjointness on its two SELF
4140/// arms (the tail two positions `(STR_DELIMITER, STR_DELIMITER)` +
4141/// `(STR_ESCAPE_LEAD, STR_ESCAPE_LEAD)` are diagonal self-mappings by
4142/// design). No witness is co-located on `Atom::ESCAPE_TABLE` — the
4143/// SHAPE ASYMMETRY between the two escape-table sub-vocabularies IS
4144/// the axis distinguishing them, and this helper's contract holds
4145/// ONLY on the DISTINCT side.
4146///
4147/// Runtime callability: the function is a normal `pub const fn`, so
4148/// callers CAN also invoke it at runtime — e.g. a REPL / LSP
4149/// tokenizer that constructs a paired vocabulary at runtime and wants
4150/// to verify cross-column disjointness before consuming it — and the
4151/// panic surfaces normally in that path (pinned by
4152/// `assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_
4153/// on_diagonal_collision` and its off-diagonal peers). The axis-
4154/// provenance string `"CROSS-COLUMN-COLLISION"` is chosen DISTINCT
4155/// from every sibling helper's axis vocabulary (`"CHAR-PAIR-TUPLE-
4156/// COLLISION"` on `_pairwise_distinct`, `"LEFT column"` / `"RIGHT
4157/// column"` on `_bijective`, `"CHAR-PAIR-SUBSET-VIOLATION"` on
4158/// `_within_char_pair_finite_set`, `"CHAR-PAIR-DISJOINTNESS-
4159/// VIOLATION"` on `_arrays_disjoint`, `"LEFT-COLUMN-DIVERGENCE"` /
4160/// `"RIGHT-COLUMN-DIVERGENCE"` on `_columns_equal_char_arrays`) so a
4161/// diagnostic that names the failed axis routes UNAMBIGUOUSLY to
4162/// this specific cross-column-disjointness helper rather than to a
4163/// sibling paired-array verifier.
4164///
4165/// Adding a new family-wide `[(char, char); N]` paired array on a
4166/// pattern-DISTINCT-from-value sub-vocabulary (e.g. a hypothetical
4167/// (short-form, long-form) alias table where short and long forms
4168/// live in disjoint character partitions, or a `(bracket-open,
4169/// bracket-close)` table where open and close characters MUST NOT
4170/// alias): pair the declaration with `const _: () = assert_char_
4171/// pair_array_columns_cross_disjoint(&Self::FOO_TABLE);` co-located
4172/// immediately after the array's declaration and the cross-column
4173/// disjointness contract binds at compile time WITHOUT a runtime
4174/// LEFT.iter().any(RIGHT.contains) sweep.
4175///
4176/// Theory grounding:
4177/// - THEORY.md §V.1 — knowable platform; the WITHIN-ARRAY CROSS-
4178/// COLUMN disjointness contract becomes a TYPE-LEVEL theorem the
4179/// substrate carries per pattern-DISTINCT-from-value paired-array
4180/// declaration rather than a runtime membership sweep the developer
4181/// must remember to write per array.
4182/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
4183/// cross-column disjointness proof at declaration site AND the
4184/// escape-table consumers that route SOURCE-column characters
4185/// THROUGH `decode_str_escape` to DECODED-column characters (and
4186/// rely on that route being one-way) regenerate through the SAME
4187/// `const _` witness.
4188/// - THEORY.md §VI.1 — generation over composition; the doubly-nested
4189/// `while` cross-column sweep IS the generative shape. Every new
4190/// pattern-DISTINCT-from-value closed-set paired-array adds ONE
4191/// `const _` line to get the cross-column disjointness theorem
4192/// rather than re-deriving a `HashSet::intersection` runtime test
4193/// per array.
4194pub const fn assert_char_pair_array_columns_cross_disjoint<const N: usize>(
4195 arr: &[(char, char); N],
4196) {
4197 let mut i = 0;
4198 while i < N {
4199 let mut j = 0;
4200 while j < N {
4201 if arr[i].0 as u32 == arr[j].1 as u32 {
4202 panic!(
4203 "assert_char_pair_array_columns_cross_disjoint: \
4204 CROSS-COLUMN-COLLISION — family-wide `[(char, \
4205 char); N]` paired substrate array carries a \
4206 character appearing in BOTH the LEFT column at \
4207 some position `i` AND the RIGHT column at some \
4208 position `j` (possibly `i == j`, the diagonal \
4209 self-mapping case). The substrate's WITHIN-ARRAY \
4210 CROSS-COLUMN DISJOINTNESS contract on the array \
4211 is broken; every consumer that treats the paired \
4212 array as a pattern-DISTINCT-from-value sub-\
4213 vocabulary (any escape-table decode path that \
4214 assumes SOURCE bytes and DECODED bytes live in \
4215 disjoint character partitions so `decode_str_\
4216 escape` is a strictly ONE-WAY mapping, any \
4217 future `(bracket-open, bracket-close)` paired \
4218 vocabulary that assumes open and close characters \
4219 alias with neither each other nor a peer role) \
4220 relies on this invariant. Fix at the ARRAY-\
4221 DECLARATION site by moving the diagonal or \
4222 cross-row aliased pair to the peer SELF-arm \
4223 vocabulary (`Atom::SELF_ESCAPE_TABLE` on the \
4224 escape-table family) OR by re-shaping the pair \
4225 to route the shared character to a single column. \
4226 The CROSS-COLUMN-COLLISION axis is DISTINCT from \
4227 the sibling `_bijective` per-column INJECTIVITY \
4228 axis (a table like `[('a', 'x'), ('a', 'y')]` \
4229 fails `_bijective` on the LEFT-column-duplicate \
4230 arm but PASSES this helper because LEFT = {{'a'}} \
4231 and RIGHT = {{'x', 'y'}} are disjoint) and from \
4232 the sibling `_pairwise_distinct` CONJOINED-tuple \
4233 INJECTIVITY axis (a table like `[('a', 'a')]` \
4234 passes `_pairwise_distinct` on the singleton but \
4235 FAILS this helper on the diagonal `arr[0].0 == \
4236 arr[0].1` — the two axes cross-check different \
4237 failure modes on the SAME paired vocabulary)"
4238 );
4239 }
4240 j += 1;
4241 }
4242 i += 1;
4243 }
4244}
4245
4246// Compile-time WITHIN-ARRAY CROSS-COLUMN disjointness witness — one
4247// `const _: () = assert_char_pair_array_columns_cross_disjoint(&…)`
4248// binding the pattern-DISTINCT-from-value sub-vocabulary's cross-
4249// column character-set disjointness at `cargo check` time on the
4250// substrate's escape-table family. Applied to
4251// `Atom::NAMED_ESCAPE_TABLE` (`[(char, char); 3]` — the three named-
4252// escape rows `('n', '\n')`, `('t', '\t')`, `('r', '\r')`); the LEFT
4253// column projection = {'n', 't', 'r'} (printable ASCII) and the RIGHT
4254// column projection = {'\n', '\t', '\r'} (control characters) are
4255// disjoint by algebra design. A regression that added a `('n', 'r')`
4256// SOURCE-to-SOURCE aliasing pair, a `('a', 'a')` diagonal self-
4257// mapping pair, or any pair drifting a character across the LEFT /
4258// RIGHT partition fails the build rather than the test suite.
4259// Deliberately NOT applied to `Atom::ESCAPE_TABLE` — that array
4260// composes `NAMED_ESCAPE_TABLE` with two SELF arms at the tail
4261// (`(STR_DELIMITER, STR_DELIMITER)` + `(STR_ESCAPE_LEAD, STR_ESCAPE_
4262// LEAD)`), which INTENTIONALLY violate cross-column disjointness on
4263// the diagonal by algebra design. The SHAPE ASYMMETRY between the
4264// pattern-DISTINCT-from-value (`NAMED_ESCAPE_TABLE`) and the pattern-
4265// EQUALS-value (`SELF_ESCAPE_TABLE`) sub-vocabularies is the axis
4266// distinguishing them, and this helper's contract holds ONLY on the
4267// DISTINCT side of that partition. Sibling to the `_bijective` +
4268// `_pairwise_distinct` witnesses above on the SAME paired array — the
4269// three witnesses close complementary INJECTIVITY axes (per-column,
4270// per-tuple, per-CROSS-column) at `cargo check` time.
4271const _: () = assert_char_pair_array_columns_cross_disjoint(&Atom::NAMED_ESCAPE_TABLE);
4272
4273/// Compile-time contract verifier — panics at const evaluation time if
4274/// the family-wide `[(char, char); N]` paired substrate array `arr` is
4275/// NOT byte-for-byte equal to the CONCATENATION of the peer paired
4276/// array `head` (contributing the FIRST `K` positions of `arr`
4277/// verbatim) followed by the DIAGONAL-EMBEDDING of the peer scalar
4278/// `[char; M]` array `tail_diag` (contributing the REMAINING `M = N -
4279/// K` positions as `(tail_diag[j], tail_diag[j])` at position `K + j`).
4280/// Binds ONE compound (SEGMENTED-CONCATENATION × DIAGONAL-EMBEDDING)
4281/// contract clause on the `(char, char)` product-element row of the
4282/// (element-type × contract-shape) matrix at a fresh
4283/// (concat-with-diagonal-tail) column — an ORDERING-SENSITIVE
4284/// SEGMENTED-POSITIONWISE contract joining the pre-existing
4285/// (pairwise-distinct), (bijective), (subset-embedding),
4286/// (disjointness), and (column-projection-equality) columns on the
4287/// SAME paired-array row.
4288///
4289/// Cardinality precondition: `K + M == N` — the caller passes the
4290/// three const parameters explicitly (`N`, `K`, `M`), and the helper's
4291/// FIRST arm fires at const-eval if the three fail to sum. A caller
4292/// who supplies mismatched arities (e.g. `<5, 3, 3>` — `K + M == 6 ≠
4293/// N == 5`) fails-loudly at the CARDINALITY-MISMATCH panic BEFORE the
4294/// sweep begins, so a mistyped ARITY doesn't degenerate into a silent
4295/// truncation of the paired array. Pinned by
4296/// `assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_cardinality_mismatch`.
4297///
4298/// Failure axes — the helper partitions its rejection surface into
4299/// FIVE disjoint arms, each with a DISTINCT axis-provenance prefix on
4300/// the panic message so downstream diagnostics route the drift back
4301/// to the specific SEGMENT and COLUMN that diverged:
4302/// 1. `CARDINALITY-MISMATCH` — `K + M != N`.
4303/// 2. `HEAD-SEGMENT-LEFT-DIVERGENCE` — some `i ∈ [0, K)` where
4304/// `arr[i].0 != head[i].0`.
4305/// 3. `HEAD-SEGMENT-RIGHT-DIVERGENCE` — some `i ∈ [0, K)` where
4306/// `arr[i].1 != head[i].1`.
4307/// 4. `DIAGONAL-TAIL-SEGMENT-LEFT-DIVERGENCE` — some `j ∈ [0, M)`
4308/// where `arr[K + j].0 != tail_diag[j]`.
4309/// 5. `DIAGONAL-TAIL-SEGMENT-RIGHT-DIVERGENCE` — some `j ∈ [0, M)`
4310/// where `arr[K + j].1 != tail_diag[j]`.
4311///
4312/// Applied to the substrate's ([`Atom::ESCAPE_TABLE`],
4313/// [`Atom::NAMED_ESCAPE_TABLE`], [`Atom::SELF_ESCAPE_TABLE`]) triple
4314/// at the module-level `const _: () = assert_char_pair_array_is_
4315/// concatenation_of_char_pair_array_and_char_array_diagonal::<5, 3,
4316/// 2>(&Atom::ESCAPE_TABLE, &Atom::NAMED_ESCAPE_TABLE,
4317/// &Atom::SELF_ESCAPE_TABLE);` witness below the helper. Pre-lift,
4318/// the identity `Atom::ESCAPE_TABLE == Atom::NAMED_ESCAPE_TABLE ++
4319/// diagonal(Atom::SELF_ESCAPE_TABLE)` lived ONLY implicitly at
4320/// `Atom::ESCAPE_TABLE`'s declaration site (positions `[0..3]`
4321/// indexing `Self::NAMED_ESCAPE_TABLE[0]`, `[1]`, `[2]`; positions
4322/// `[3..5]` materializing `(Self::SELF_ESCAPE_TABLE[0], Self::SELF_
4323/// ESCAPE_TABLE[0])` + `(Self::SELF_ESCAPE_TABLE[1], Self::SELF_
4324/// ESCAPE_TABLE[1])`). Post-lift, that identity binds at `cargo
4325/// check` time — a regression that reorders `Atom::ESCAPE_TABLE`'s
4326/// segments, drifts one entry across the three arrays, or changes
4327/// the diagonal-embedding shape at the tail fails the build rather
4328/// than the test suite. The five prior paired-array witnesses on
4329/// `Atom::ESCAPE_TABLE` bind INDIVIDUAL axes (pairwise-distinct,
4330/// bijective) OR CROSS-ROW column-projection bonds
4331/// (columns-equal-peer-scalars), but NONE bind the SEGMENTED
4332/// composite-construction identity that this helper closes.
4333///
4334/// Contract-strength peer to [`assert_char_pair_array_columns_equal_
4335/// char_arrays`] on the (segmented-cross-row-bond vs full-cross-row-
4336/// bond) axis: where the sibling binds the FULL two-column bond
4337/// against TWO peer scalar arrays at EVERY position, this helper
4338/// binds the SEGMENTED composite-construction bond against a peer
4339/// PAIRED array (head) and a peer SCALAR array (diagonal tail) split
4340/// at position `K`. The two helpers together close the
4341/// (paired-vs-peer, cross-row-projection) 2-dimensional surface on
4342/// the `(char, char)` row at ONE `const _` line per compound-bond
4343/// theorem — the full-column peer binds every position at the
4344/// (char, char) row of a paired array against TWO scalar peer
4345/// vocabularies; THIS peer binds the head segment against a paired
4346/// vocabulary and the diagonal tail against a scalar vocabulary
4347/// (embedded diagonally). Row-parallel to the SCALAR-row helpers on
4348/// the (element-type) axis — where those close INJECTIVITY /
4349/// COVERING / PERMUTATION on the scalar rows, this closes the
4350/// SEGMENTED-CONCATENATION corner on the PRODUCT-element row.
4351///
4352/// Runtime callability: the function is a normal `pub const fn`, so
4353/// callers CAN also invoke it at runtime — e.g. a REPL / LSP
4354/// tokenizer that constructs a `[(char, char); N]` paired array at
4355/// runtime and wants to verify SEGMENTED-CONCATENATION structure
4356/// before consuming it — and the panic surfaces normally in that
4357/// path. Every panic site names the helper AND identifies the failed
4358/// AXIS distinctly so downstream diagnostics route regressions back
4359/// to (a) the helper by string search on
4360/// `"assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"`
4361/// and (b) the specific segment/column by axis-prefix.
4362///
4363/// Adding a new paired substrate array declared as CONCATENATION of a
4364/// peer paired vocabulary with a DIAGONAL-embedded peer scalar
4365/// vocabulary: pair the declaration with `const _: () = assert_char_
4366/// pair_array_is_concatenation_of_char_pair_array_and_char_array_
4367/// diagonal::<N, K, M>(&Self::FOO_TABLE, &Self::FOO_HEAD,
4368/// &Self::FOO_DIAG_TAIL);` co-located after the composite's
4369/// declaration and the compound identity binds at compile time
4370/// WITHOUT a runtime concat-and-zip loop.
4371///
4372/// Theory grounding:
4373/// - THEORY.md §V.1 — knowable platform; the SEGMENTED composite-
4374/// construction identity becomes a TYPE-LEVEL theorem carried per
4375/// declaration triple rather than a runtime concat-and-zip loop
4376/// the developer must remember to write per triple.
4377/// - THEORY.md §II.1 invariant 5 — composition preserves proofs;
4378/// the (head-segment, diagonal-tail-segment) composition proof at
4379/// declaration site AND the peer-vocabulary consumers regenerate
4380/// through the SAME `const _` witness.
4381/// - THEORY.md §VI.1 — generation over composition; the SEGMENTED
4382/// concat-and-diagonal-embed sweep IS the generative shape. Every
4383/// new paired-array declared as a segmented concat with a
4384/// diagonal-embedded scalar tail adds ONE `const _` line to get
4385/// the compound theorem.
4386pub const fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal<
4387 const N: usize,
4388 const K: usize,
4389 const M: usize,
4390>(
4391 arr: &[(char, char); N],
4392 head: &[(char, char); K],
4393 tail_diag: &[char; M],
4394) {
4395 if K + M != N {
4396 panic!(
4397 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal: \
4398 CARDINALITY-MISMATCH — the three const parameters `N`, \
4399 `K`, `M` must satisfy `K + M == N` so the peer paired \
4400 `head` (contributing positions `[0..K)` of the composite \
4401 `arr`) followed by the peer scalar `tail_diag` (embedded \
4402 diagonally as `(tail_diag[j], tail_diag[j])` at positions \
4403 `[K..N)`) exactly cover `arr`'s `N` positions. Fix at the \
4404 `const _` witness's turbofish by reconciling the three \
4405 arities against the composite's declared arity. The \
4406 CARDINALITY-MISMATCH gate distinguishes THIS failure from \
4407 every content-drift arm — a mistyped ARITY on the caller \
4408 side fails HERE before any per-position sweep begins, so \
4409 a subtle arity slip doesn't silently degenerate into a \
4410 truncated segment sweep."
4411 );
4412 }
4413 let mut i = 0;
4414 while i < K {
4415 if arr[i].0 as u32 != head[i].0 as u32 {
4416 panic!(
4417 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal: \
4418 HEAD-SEGMENT-LEFT-DIVERGENCE — the composite paired \
4419 array `arr` carries a LEFT-column entry at some \
4420 position in `[0, K)` (the HEAD segment) that does NOT \
4421 byte-for-byte equal the peer paired `head` array's \
4422 LEFT-column entry at the SAME position. The \
4423 substrate's SEGMENTED-CONCATENATION contract on the \
4424 (composite, head-segment) HEAD-arm LEFT column is \
4425 broken; every consumer that reads `arr[0..K)` and the \
4426 peer `head` array as INTERCHANGEABLE (any outer-\
4427 dispatch pattern-matcher on the head sub-vocabulary \
4428 that expects the composite's HEAD segment to project \
4429 verbatim to the peer paired vocabulary's LEFT column) \
4430 relies on this invariant. Fix at one of the two \
4431 declaration sites (`arr[0..K)` or `head`) by \
4432 reconciling the entry across the two arrays."
4433 );
4434 }
4435 if arr[i].1 as u32 != head[i].1 as u32 {
4436 panic!(
4437 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal: \
4438 HEAD-SEGMENT-RIGHT-DIVERGENCE — the composite paired \
4439 array `arr` carries a RIGHT-column entry at some \
4440 position in `[0, K)` (the HEAD segment) that does NOT \
4441 byte-for-byte equal the peer paired `head` array's \
4442 RIGHT-column entry at the SAME position. The \
4443 substrate's SEGMENTED-CONCATENATION contract on the \
4444 (composite, head-segment) HEAD-arm RIGHT column is \
4445 broken; every consumer that reads the composite's \
4446 head-segment RIGHT column as INTERCHANGEABLE with the \
4447 peer paired vocabulary's RIGHT column relies on this \
4448 invariant. Fix at one of the two declaration sites \
4449 (`arr[0..K)` or `head`) by reconciling the entry."
4450 );
4451 }
4452 i += 1;
4453 }
4454 let mut j = 0;
4455 while j < M {
4456 if arr[K + j].0 as u32 != tail_diag[j] as u32 {
4457 panic!(
4458 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal: \
4459 DIAGONAL-TAIL-SEGMENT-LEFT-DIVERGENCE — the composite \
4460 paired array `arr` carries a LEFT-column entry at \
4461 some position in `[K, N)` (the DIAGONAL TAIL segment) \
4462 that does NOT byte-for-byte equal the peer scalar \
4463 `tail_diag` array's entry at the SAME diagonal-\
4464 embedded position. The substrate's DIAGONAL-EMBEDDING \
4465 contract on the (composite, tail-segment) TAIL-arm \
4466 LEFT column is broken; every consumer that reads \
4467 `arr[K..N)` and the peer scalar `tail_diag` array as \
4468 diagonally interchangeable (any outer-dispatch \
4469 pattern-matcher on the tail sub-vocabulary that \
4470 expects `arr[K + j]` to be `(tail_diag[j], \
4471 tail_diag[j])`) relies on this invariant. Fix at one \
4472 of the two declaration sites (`arr[K..N)` or \
4473 `tail_diag`) by reconciling the entry."
4474 );
4475 }
4476 if arr[K + j].1 as u32 != tail_diag[j] as u32 {
4477 panic!(
4478 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal: \
4479 DIAGONAL-TAIL-SEGMENT-RIGHT-DIVERGENCE — the \
4480 composite paired array `arr` carries a RIGHT-column \
4481 entry at some position in `[K, N)` (the DIAGONAL TAIL \
4482 segment) that does NOT byte-for-byte equal the peer \
4483 scalar `tail_diag` array's entry at the SAME \
4484 diagonal-embedded position. The substrate's DIAGONAL-\
4485 EMBEDDING contract on the (composite, tail-segment) \
4486 TAIL-arm RIGHT column is broken; every consumer that \
4487 reads the composite's tail-segment RIGHT column as \
4488 diagonally interchangeable with `tail_diag[j]` relies \
4489 on this invariant. Fix at one of the two declaration \
4490 sites (`arr[K..N)` or `tail_diag`) by reconciling the \
4491 entry."
4492 );
4493 }
4494 j += 1;
4495 }
4496}
4497
4498// Compile-time SEGMENTED-CONCATENATION-with-DIAGONAL-TAIL witness —
4499// one `const _: () = assert_char_pair_array_is_concatenation_of_char_
4500// pair_array_and_char_array_diagonal::<5, 3, 2>(&…, &…, &…)` binding
4501// the (`Atom::ESCAPE_TABLE`, `Atom::NAMED_ESCAPE_TABLE`,
4502// `Atom::SELF_ESCAPE_TABLE`) three-way composite-construction bond on
4503// the substrate's Str-payload escape-table vocabulary.
4504// `Atom::ESCAPE_TABLE` (`[(char, char); 5]`) is CONSTRUCTED as
4505// `NAMED_ESCAPE_TABLE[0..=2]` (the HEAD segment) followed by two
4506// SELF-diagonal pairs `(SELF_ESCAPE_TABLE[0], SELF_ESCAPE_TABLE[0])` +
4507// `(SELF_ESCAPE_TABLE[1], SELF_ESCAPE_TABLE[1])` (the DIAGONAL TAIL
4508// segment). Pre-lift, the SEGMENTED composite identity lived ONLY as
4509// an implicit declaration-site expression that a refactor could
4510// silently drift by reordering the HEAD entries, drifting one entry
4511// across the three arrays, or changing the diagonal-embedding shape
4512// at the tail — the pre-existing `_within_char_pair_finite_set`,
4513// `_arrays_disjoint`, `_bijective`, `_pairwise_distinct`, and
4514// `_columns_equal_char_arrays` witnesses each bind ADJACENT axes but
4515// NONE bind the composite-construction structural identity. Post-
4516// lift, this const-eval witness binds that identity as a compile-
4517// time theorem so a regression that renames a source or decoded byte
4518// on ONE of the three arrays without updating the other two, OR
4519// reorders the HEAD segment's positions, OR breaks the diagonal-
4520// embedding at the TAIL, fails the build rather than the test suite.
4521// SIXTH witness on the SAME paired array in complementary posture to
4522// the FIVE prior const-eval sweeps — the SIX sweeps enforce
4523// complementary axes of the same substrate table at SIX stages of
4524// the toolchain, so a build that skips tests still catches
4525// composite-construction drift here.
4526const _: () =
4527 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<5, 3, 2>(
4528 &Atom::ESCAPE_TABLE,
4529 &Atom::NAMED_ESCAPE_TABLE,
4530 &Atom::SELF_ESCAPE_TABLE,
4531 );
4532
4533/// Compile-time contract verifier — panics at const evaluation time if
4534/// the sub-slice `full[START..START + M)` of the family-wide `[(char,
4535/// char); N]` paired substrate array `full` is NOT byte-for-byte equal
4536/// to the peer `[(char, char); M]` paired sub-array `sub` at the
4537/// offset-matched positions. Binds ONE compound (SLICE × PAIRED-
4538/// POSITIONWISE-EQUALITY) contract on the `(char, char)` product-
4539/// element row of the (element-type × contract-shape) matrix at the
4540/// (SUB-SLICE ARRAY-image) column — opens the FOURTH element-type row
4541/// of that column peer to the pre-existing (u8) sibling
4542/// [`assert_u8_array_slice_equals_u8_array`], (char) sibling
4543/// [`assert_char_array_slice_equals_char_array`], and (str) sibling
4544/// [`assert_str_array_slice_equals_str_array`]. Together the four
4545/// helpers close the (SUB-SLICE ARRAY-image) column across the FULL
4546/// (u8, char, str, (char, char)) element-type row set of the
4547/// (element-type × contract-shape) matrix.
4548///
4549/// Bounds preconditions: `START ≤ N` (inclusive upper bound —
4550/// `START == N` combined with `M == 0` is the LEGAL empty-slice-at-
4551/// right-endpoint corner) AND `M ≤ N - START` (inclusive upper bound —
4552/// `M == N - START` is the LEGAL exact-fit-to-right-endpoint corner).
4553/// The two bounds gates fire IN ORDER — `START` is validated FIRST so
4554/// the peer `M` gate can safely evaluate `N - START` without `usize`
4555/// underflow. A caller-side turbofish arity slip fails-loudly on the
4556/// specific bounds axis it violated BEFORE the positionwise sweep
4557/// reads `full[START + i]`, so a bounds slip doesn't silently
4558/// degenerate into a subtraction wrap-around OR a panic deeper in
4559/// `full[START + i]` bounds-checking.
4560///
4561/// Failure axes — the helper partitions its rejection surface into
4562/// FOUR disjoint arms, each with a DISTINCT axis-provenance prefix on
4563/// the panic message so downstream diagnostics route the drift back
4564/// to the specific gate (and, on the CONTENT arms, the specific
4565/// COLUMN of the paired sub-slice) that diverged:
4566/// 1. `START-OUT-OF-BOUNDS` — `START > N`.
4567/// 2. `SLICE-LENGTH-OUT-OF-BOUNDS` — `M > N - START` (the peer
4568/// `START` gate above guarantees `N - START` never underflows).
4569/// 3. `CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-VIOLATION` — some
4570/// `i ∈ [0..M)` where `full[START + i].0 != sub[i].0` (LEFT
4571/// column of the paired positionwise sweep).
4572/// 4. `CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-VIOLATION` — some
4573/// `i ∈ [0..M)` where `full[START + i].1 != sub[i].1` (RIGHT
4574/// column of the paired positionwise sweep).
4575///
4576/// The two CONTENT arms split by COLUMN in the SAME style as the
4577/// sibling (char, char)-row [`assert_char_pair_array_columns_equal_
4578/// char_arrays`] helper's `LEFT-COLUMN-DIVERGENCE` /
4579/// `RIGHT-COLUMN-DIVERGENCE` split, so a diagnostic that names the
4580/// failed axis routes UNAMBIGUOUSLY to (a) this specific (char, char)-
4581/// row SLICE-EQUALS-ARRAY helper by string search on the axis substring
4582/// `-CHAR-PAIR-SLICE-EQUALS-ARRAY-` (distinguishing it from the sibling
4583/// `-CHAR-SLICE-EQUALS-ARRAY-` (char) row axis and the plain `-SLICE-
4584/// EQUALS-ARRAY-` (u8) row axis) and (b) the failed COLUMN by the
4585/// `-LEFT-COLUMN-` / `-RIGHT-COLUMN-` infix. The shared `-SLICE-EQUALS-
4586/// ARRAY-VIOLATION` suffix lets callers grep any of the FOUR element-
4587/// type variants by ONE substring.
4588///
4589/// Applied to the substrate's (`Atom::ESCAPE_TABLE`,
4590/// `Atom::NAMED_ESCAPE_TABLE`) pair at ONE module-level `const _: () =
4591/// assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 0>(&
4592/// Atom::ESCAPE_TABLE, &Atom::NAMED_ESCAPE_TABLE);` witness below the
4593/// helper. `Atom::ESCAPE_TABLE[0..3]` (`[(char, char); 3]` HEAD
4594/// segment) is byte-for-byte equal to `Atom::NAMED_ESCAPE_TABLE`
4595/// verbatim by declaration intent — the outer `ESCAPE_TABLE` array's
4596/// first three initializer slots each spell an entry drawn directly
4597/// from `NAMED_ESCAPE_TABLE`'s corresponding slot. Pre-lift, this
4598/// HEAD-segment positionwise-composition identity was bound TRANSITIVELY
4599/// through the sibling [`assert_char_pair_array_is_concatenation_of_
4600/// char_pair_array_and_char_array_diagonal`]`::<5, 3, 2>` witness on the
4601/// same three-array triple — that sibling binds the FULL composite
4602/// `ESCAPE_TABLE == NAMED_ESCAPE_TABLE ++ diagonal(SELF_ESCAPE_TABLE)`
4603/// so the HEAD is transitively bound alongside the DIAGONAL TAIL.
4604/// Post-lift, the HEAD-segment identity binds INDEPENDENTLY on its
4605/// OWN axis at ONE additional `const _` line — a regression that
4606/// silently broke the diagonal-tail binding (e.g. by swapping the
4607/// diagonal embedding to an anti-diagonal `(SELF[1], SELF[0])`)
4608/// would trip the sibling witness's `DIAGONAL-TAIL-*` arm alone;
4609/// this new witness continues to guarantee the HEAD-segment identity
4610/// on the CHAR-PAIR-SLICE-EQUALS-ARRAY-* axis regardless of the
4611/// tail's shape, so a HEAD-only drift (e.g. reordering the three
4612/// NAMED entries in ESCAPE_TABLE's initializer while leaving
4613/// NAMED_ESCAPE_TABLE's order intact) trips THIS witness's LEFT-
4614/// COLUMN / RIGHT-COLUMN axis on its OWN axis-provenance vocabulary.
4615/// The two witnesses partition the drift-detection surface by SEGMENT
4616/// (HEAD vs DIAGONAL TAIL) and by AXIS-PROVENANCE (SLICE-EQUALS-ARRAY
4617/// vs SEGMENTED-CONCATENATION-with-DIAGONAL-TAIL), catching each
4618/// SEGMENT's drift on the axis best suited to route the fix back to
4619/// the SPECIFIC declaration site at fault.
4620///
4621/// Runtime callability: the function is a normal `pub const fn`, so
4622/// callers CAN also invoke it at runtime — e.g. a REPL / LSP
4623/// tokenizer that constructs a paired sub-array at runtime and wants
4624/// to verify SLICE-EQUALS-ARRAY structure before consuming it — and
4625/// the panic surfaces normally in that path (pinned by
4626/// `assert_char_pair_array_slice_equals_char_pair_array_panics_at_
4627/// runtime_on_left_column_drift`,
4628/// `assert_char_pair_array_slice_equals_char_pair_array_panics_at_
4629/// runtime_on_right_column_drift`,
4630/// `assert_char_pair_array_slice_equals_char_pair_array_panics_at_
4631/// runtime_on_start_out_of_bounds`,
4632/// `assert_char_pair_array_slice_equals_char_pair_array_panics_at_
4633/// runtime_on_slice_length_out_of_bounds`, and the two axis-
4634/// provenance pins on the LEFT-COLUMN + RIGHT-COLUMN axes).
4635///
4636/// Adding a new family-wide `[(char, char); N]` substrate array whose
4637/// sub-slice is byte-for-byte equal to a peer paired sub-vocabulary's
4638/// canonical `[(char, char); M]` listing: pair the declaration with
4639/// `const _: () = assert_char_pair_array_slice_equals_char_pair_array::
4640/// <N, M, START>(&Self::FOO_TABLE, &Self::FOO_SUB_TABLE);` co-located
4641/// after the composite's declaration and the compound identity binds
4642/// at compile time.
4643///
4644/// Theory grounding:
4645/// - THEORY.md §V.1 — knowable platform; the SLICE-EQUALS-ARRAY
4646/// positionwise-composition contract on the `(char, char)` paired
4647/// vocabulary becomes a TYPE-LEVEL theorem the substrate carries
4648/// per (container, sub-carving) pair rather than a runtime iterator
4649/// sweep the developer must remember to write per pair.
4650/// - THEORY.md §III — the typescape; the (element-type × contract-
4651/// shape) matrix now carries the (SUB-SLICE ARRAY-image) column
4652/// at ALL FOUR element-type rows — the (u8), (char), (str), and
4653/// `(char, char)` rows each ship a peer const-fn helper. The FOUR
4654/// helpers close the SUB-SLICE ARRAY-image column of the matrix.
4655/// - THEORY.md §VI.1 — generation over composition; the paired
4656/// positionwise sweep IS the generative shape. Every new paired
4657/// composite declared as a positionwise-composition against a peer
4658/// paired sub-vocabulary adds ONE `const _` line to get the
4659/// compound theorem rather than re-deriving a runtime
4660/// `full[START + i] == sub[i]` per-position sweep.
4661/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
4662/// SUB-SLICE positionwise-composition proof at declaration site AND
4663/// the peer-sub-vocabulary consumers that read the composite's sub-
4664/// slice regenerate through the SAME `const _` witness.
4665pub const fn assert_char_pair_array_slice_equals_char_pair_array<
4666 const N: usize,
4667 const M: usize,
4668 const START: usize,
4669>(
4670 full: &[(char, char); N],
4671 sub: &[(char, char); M],
4672) {
4673 if START > N {
4674 panic!(
4675 "assert_char_pair_array_slice_equals_char_pair_array: \
4676 START-OUT-OF-BOUNDS — the const parameter `START` sits \
4677 OUTSIDE the outer array's valid position range `[0..N]` \
4678 (inclusive upper bound: `START == N` combined with `M == \
4679 0` is the LEGAL empty-slice-at-right-endpoint corner). \
4680 Fix at the `const _` witness's turbofish by reconciling \
4681 `START` against the outer array's declared arity `N`. \
4682 The START-OUT-OF-BOUNDS gate fires FIRST — a mistyped \
4683 `START` on the caller side fails HERE before the peer \
4684 `SLICE-LENGTH-OUT-OF-BOUNDS` gate reads `N - START` \
4685 (which would underflow `usize` had this gate not caught \
4686 the slip), so a subtle bounds slip doesn't silently \
4687 degenerate into a subtraction wrap-around OR a panic \
4688 deeper in `full[START + i]` bounds-checking."
4689 );
4690 }
4691 if M > N - START {
4692 panic!(
4693 "assert_char_pair_array_slice_equals_char_pair_array: \
4694 SLICE-LENGTH-OUT-OF-BOUNDS — the peer sub-array's arity \
4695 `M` exceeds the outer array's tail cardinality `N - \
4696 START`, so the positionwise sweep `full[START + i]` for \
4697 `i ∈ [0..M)` would overrun the outer array's valid \
4698 position range `[0..N)` at some `i ∈ [N - START..M)`. \
4699 Fix at the `const _` witness's turbofish by reconciling \
4700 `M` against the outer array's tail cardinality `N - \
4701 START` OR by narrowing `START` to leave a longer tail. \
4702 The peer `START-OUT-OF-BOUNDS` gate above guarantees \
4703 `START ≤ N` so `N - START` never underflows `usize` at \
4704 this gate. The LEGAL exact-fit corner `M == N - START` \
4705 (the sub-array reaches EXACTLY to the outer array's \
4706 right endpoint) is accepted; the STRICT `M > N - START` \
4707 slip is what this gate rejects."
4708 );
4709 }
4710 let mut i = 0;
4711 while i < M {
4712 if full[START + i].0 as u32 != sub[i].0 as u32 {
4713 panic!(
4714 "assert_char_pair_array_slice_equals_char_pair_array: \
4715 CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-VIOLATION — \
4716 the outer `[(char, char); N]` paired array `full` \
4717 carries a LEFT-column entry at some position `START + \
4718 i` (for `i ∈ [0..M)`) that does NOT byte-equal the \
4719 peer `[(char, char); M]` paired sub-array `sub` at \
4720 the offset-matched position `i`. The substrate's \
4721 (SLICE × PAIRED-POSITIONWISE-EQUALITY) contract on \
4722 the LEFT column of the sub-slice `full[START..START + \
4723 M) == sub[..]` is broken; every consumer that reads \
4724 `full[START..START + M)`'s LEFT column as a \
4725 positionwise-aligned copy of the peer paired sub-\
4726 vocabulary's LEFT column (the substrate's Str-payload \
4727 escape-table HEAD segment `Atom::ESCAPE_TABLE[0..3] \
4728 == Atom::NAMED_ESCAPE_TABLE` — a regression that \
4729 reordered ESCAPE_TABLE's first three initializer \
4730 entries away from NAMED_ESCAPE_TABLE's canonical \
4731 order without updating NAMED_ESCAPE_TABLE trips \
4732 HERE on the LEFT column; any future container-array \
4733 paired sub-slice byte-for-byte equal to a peer \
4734 paired sub-vocabulary's canonical `[(char, char); \
4735 M]` listing) relies on this invariant. Fix at the \
4736 ARRAY-DECLARATION site (the drifted `full[START + \
4737 i].0` slot inside the slice segment) OR at the peer \
4738 paired sub-array's arm listing — the choice depends \
4739 on whether the drift is an unintended slot reorder \
4740 in the outer array's slice OR in the sub-carving's \
4741 own listing. The LEFT-COLUMN-* axis prefix routes \
4742 the fix specifically to the LEFT column of the \
4743 paired sweep — a RIGHT-column drift would trip the \
4744 peer RIGHT-COLUMN-* axis arm below on its OWN \
4745 distinct axis-provenance vocabulary."
4746 );
4747 }
4748 if full[START + i].1 as u32 != sub[i].1 as u32 {
4749 panic!(
4750 "assert_char_pair_array_slice_equals_char_pair_array: \
4751 CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-VIOLATION \
4752 — the outer `[(char, char); N]` paired array `full` \
4753 carries a RIGHT-column entry at some position `START \
4754 + i` (for `i ∈ [0..M)`) that does NOT byte-equal the \
4755 peer `[(char, char); M]` paired sub-array `sub` at \
4756 the offset-matched position `i`. The substrate's \
4757 (SLICE × PAIRED-POSITIONWISE-EQUALITY) contract on \
4758 the RIGHT column of the sub-slice `full[START..START \
4759 + M) == sub[..]` is broken; every consumer that \
4760 reads `full[START..START + M)`'s RIGHT column as a \
4761 positionwise-aligned copy of the peer paired sub-\
4762 vocabulary's RIGHT column (the substrate's Str-\
4763 payload escape-table HEAD segment `Atom::ESCAPE_\
4764 TABLE[0..3] == Atom::NAMED_ESCAPE_TABLE` — a \
4765 regression that drifted a DECODED byte across \
4766 ESCAPE_TABLE and NAMED_ESCAPE_TABLE without updating \
4767 the other trips HERE on the RIGHT column; any \
4768 future container-array paired sub-slice byte-for-\
4769 byte equal to a peer paired sub-vocabulary's \
4770 canonical `[(char, char); M]` listing) relies on \
4771 this invariant. Fix at the ARRAY-DECLARATION site \
4772 (the drifted `full[START + i].1` slot inside the \
4773 slice segment) OR at the peer paired sub-array's \
4774 arm listing — the choice depends on whether the \
4775 drift is an unintended slot rewrite in the outer \
4776 array's slice OR in the sub-carving's own listing. \
4777 The RIGHT-COLUMN-* axis prefix routes the fix \
4778 specifically to the RIGHT column of the paired \
4779 sweep — a LEFT-column drift would trip the peer \
4780 LEFT-COLUMN-* axis arm above on its OWN distinct \
4781 axis-provenance vocabulary."
4782 );
4783 }
4784 i += 1;
4785 }
4786}
4787
4788// Compile-time SLICE-EQUALS-ARRAY witness — the ONE `(container, sub-
4789// carving)` pair on the substrate whose ARRAY-LEVEL structure composes
4790// a paired container-array sub-slice byte-for-byte identical to a
4791// peer paired sub-carving's canonical `[(char, char); M]` listing.
4792// `Atom::ESCAPE_TABLE[0..3]` (the three-slot NAMED-escape HEAD segment
4793// of the outer five-slot Str-payload escape-table array) is byte-for-
4794// byte equal to `Atom::NAMED_ESCAPE_TABLE` verbatim — the outer
4795// ESCAPE_TABLE array's first three initializer slots each spell an
4796// entry drawn directly from NAMED_ESCAPE_TABLE's corresponding slot.
4797// Pre-lift, this HEAD-segment positionwise-composition identity was
4798// bound TRANSITIVELY through the sibling `assert_char_pair_array_is_
4799// concatenation_of_char_pair_array_and_char_array_diagonal::<5, 3, 2>`
4800// witness at line ~3909 above (that sibling binds the FULL composite
4801// `ESCAPE_TABLE == NAMED_ESCAPE_TABLE ++ diagonal(SELF_ESCAPE_TABLE)`
4802// so the HEAD segment is bound alongside the DIAGONAL TAIL through a
4803// single composite-construction proof); post-lift, the HEAD segment
4804// binds INDEPENDENTLY on the (SLICE × PAIRED-POSITIONWISE-EQUALITY)
4805// axis at ONE additional `const _` line, routing any HEAD-only drift
4806// (e.g. reordering ESCAPE_TABLE's first three initializer entries
4807// while leaving NAMED_ESCAPE_TABLE's order intact) through the
4808// CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN / -RIGHT-COLUMN axis-
4809// provenance vocabulary rather than the sibling's `HEAD-SEGMENT-
4810// LEFT-DIVERGENCE` / `HEAD-SEGMENT-RIGHT-DIVERGENCE` axis-
4811// provenance vocabulary. The two witnesses partition the drift-
4812// detection surface by SEGMENT and AXIS-PROVENANCE.
4813//
4814// Sibling posture to the FOUR (str)-row per-position witnesses on
4815// `crate::error::SexpShape::LABELS` in `error.rs` (four
4816// `assert_str_array_slice_equals_str_array::<12, {1,3,4,6,8,12},
4817// {0,1,7,8}>` witnesses that positionally decompose the twelve-slot
4818// LABELS parent array against its four sub-carvings' LABELS arrays)
4819// and to the TWO singleton + ONE four-slot (u8)-row witnesses on
4820// `SexpShape::HASH_DISCRIMINATORS` in this file. Those siblings each
4821// carry the sub-carving-projection SLICE-EQUALS-ARRAY sweep at the
4822// (u8) row and the (str) row of the (SUB-SLICE ARRAY-image) column;
4823// this witness opens the SAME sweep at the `(char, char)` product-
4824// element row on the SAME column. Together the (u8, char, str, (char,
4825// char)) FOUR-row column closure carries the (SUB-SLICE ARRAY-image)
4826// column of the (element-type × contract-shape) matrix across the
4827// FULL element-type row set the substrate declares family-wide
4828// arrays for at rustc time.
4829const _: () = assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 0>(
4830 &Atom::ESCAPE_TABLE,
4831 &Atom::NAMED_ESCAPE_TABLE,
4832);
4833
4834/// Compile-time contract verifier — panics at const evaluation time if
4835/// the family-wide `[char; N]` scalar substrate array `arr` is NOT byte-
4836/// for-byte equal to the CONCATENATION of the SELECTED COLUMN of the
4837/// peer paired `[(char, char); K]` array `head_table` (contributing the
4838/// FIRST `K` positions of `arr` verbatim as either
4839/// `head_table[i].0` — LEFT column when `take_right_column == false` —
4840/// or `head_table[i].1` — RIGHT column when `take_right_column == true`)
4841/// followed by the peer scalar `[char; M]` array `tail` (contributing
4842/// the REMAINING `M = N - K` positions verbatim at position `K + j`).
4843/// Binds ONE compound (SEGMENTED-CONCATENATION × PAIRED-COLUMN-
4844/// PROJECTION) contract clause on the (char) scalar-element row of the
4845/// (element-type × contract-shape) matrix at a fresh (concat-through-
4846/// paired-column-projection) column — an ORDERING-SENSITIVE SEGMENTED-
4847/// POSITIONWISE contract joining the pre-existing (pairwise-distinct),
4848/// (arrays-disjoint), and (within-char-finite-set) columns on the SAME
4849/// (char) scalar-element row.
4850///
4851/// Cardinality precondition: `K + M == N` — the caller passes the
4852/// three const parameters explicitly (`N`, `K`, `M`), and the helper's
4853/// FIRST arm fires at const-eval if the three fail to sum. A caller
4854/// who supplies mismatched arities (e.g. `<5, 3, 3>` — `K + M == 6 ≠
4855/// N == 5`) fails-loudly at the CARDINALITY-MISMATCH panic BEFORE the
4856/// sweep begins, so a mistyped ARITY doesn't degenerate into a silent
4857/// truncation of the scalar composite array. Sibling-shape to the
4858/// paired-row helper [`assert_char_pair_array_is_concatenation_of_
4859/// char_pair_array_and_char_array_diagonal`]'s `CARDINALITY-MISMATCH`
4860/// pre-arm on the SAME cardinality axis.
4861///
4862/// Failure axes — the helper partitions its rejection surface into
4863/// FOUR disjoint arms, each with a DISTINCT axis-provenance prefix on
4864/// the panic message so downstream diagnostics route the drift back
4865/// to the specific SEGMENT (and, on the HEAD arm, the specific
4866/// COLUMN) that diverged:
4867/// 1. `CARDINALITY-MISMATCH` — `K + M != N`.
4868/// 2. `HEAD-SEGMENT-LEFT-COLUMN-DIVERGENCE` — `take_right_column ==
4869/// false` AND some `i ∈ [0, K)` where `arr[i] != head_table[i].0`.
4870/// 3. `HEAD-SEGMENT-RIGHT-COLUMN-DIVERGENCE` — `take_right_column ==
4871/// true` AND some `i ∈ [0, K)` where `arr[i] != head_table[i].1`.
4872/// 4. `TAIL-SEGMENT-DIVERGENCE` — some `j ∈ [0, M)` where
4873/// `arr[K + j] != tail[j]` (INDEPENDENT of `take_right_column`
4874/// since the tail is a scalar `[char; M]` array projected verbatim,
4875/// not through a paired column).
4876///
4877/// Applied to the substrate's ([`Atom::ESCAPE_SOURCES`],
4878/// [`Atom::NAMED_ESCAPE_TABLE`], [`Atom::SELF_ESCAPE_TABLE`]) triple
4879/// (LEFT-column projection) AND the ([`Atom::ESCAPE_DECODED`],
4880/// [`Atom::NAMED_ESCAPE_TABLE`], [`Atom::SELF_ESCAPE_TABLE`]) triple
4881/// (RIGHT-column projection) at TWO module-level `const _: () =
4882/// assert_char_array_is_concatenation_of_char_pair_array_column_and_
4883/// char_array::<5, 3, 2>(&…, &Atom::NAMED_ESCAPE_TABLE,
4884/// &Atom::SELF_ESCAPE_TABLE, take_right_column);` witnesses below
4885/// the helper. Pre-lift, the composition law
4886/// ```ignore
4887/// ESCAPE_SOURCES == [NAMED_ESCAPE_TABLE[0].0, NAMED_ESCAPE_TABLE[1].0,
4888/// NAMED_ESCAPE_TABLE[2].0, SELF_ESCAPE_TABLE[0],
4889/// SELF_ESCAPE_TABLE[1]]
4890/// ESCAPE_DECODED == [NAMED_ESCAPE_TABLE[0].1, NAMED_ESCAPE_TABLE[1].1,
4891/// NAMED_ESCAPE_TABLE[2].1, SELF_ESCAPE_TABLE[0],
4892/// SELF_ESCAPE_TABLE[1]]
4893/// ```
4894/// lived ONLY as PROSE at [`Atom::ESCAPE_SOURCES`]'s and
4895/// [`Atom::ESCAPE_DECODED`]'s docstrings (each explicitly names the
4896/// "NAMED-column prefix + SELF-column suffix" partition) plus as an
4897/// implicit transitive consequence of the pre-existing
4898/// (`_columns_equal_char_arrays` on `ESCAPE_TABLE` against the two
4899/// scalar peer columns) + (`_is_concatenation_of_char_pair_array_and_
4900/// char_array_diagonal` on `ESCAPE_TABLE` against `NAMED_ESCAPE_TABLE`
4901/// and diagonal-embedded `SELF_ESCAPE_TABLE`) witness pair. Neither
4902/// pre-existing witness DIRECTLY binds the scalar-projection identity
4903/// on `ESCAPE_SOURCES` and `ESCAPE_DECODED` at the char-array level
4904/// — the two scalar arrays could be independently mutated (e.g.
4905/// reorder `ESCAPE_SOURCES`'s five entries so its LEFT-column is no
4906/// longer NAMED-source-prefix + SELF-suffix in the canonical order)
4907/// while both prior witnesses continue to hold on the paired
4908/// `ESCAPE_TABLE`. Post-lift, that scalar-projection identity binds
4909/// at `cargo check` time — a regression that reorders EITHER of the
4910/// two scalar arrays' entries away from the "NAMED-column-prefix +
4911/// SELF-suffix" partition, drifts one entry across NAMED_ESCAPE_TABLE
4912/// and its scalar-column projection, or breaks the SELF-suffix
4913/// verbatim-embedding at the tail, fails the build rather than the
4914/// test suite.
4915///
4916/// Contract-strength peer to [`assert_char_pair_array_is_
4917/// concatenation_of_char_pair_array_and_char_array_diagonal`] on the
4918/// (scalar-vs-paired composite element type) axis: where the sibling
4919/// binds the paired composite's SEGMENTED-CONCATENATION identity
4920/// against a paired HEAD and a DIAGONAL-EMBEDDED scalar TAIL at the
4921/// (char, char) row, this helper binds the SCALAR composite's
4922/// SEGMENTED-CONCATENATION identity against a SELECTED COLUMN of a
4923/// paired HEAD and a VERBATIM scalar TAIL at the (char) row. The two
4924/// helpers together close the CROSS-ROW SEGMENTED-CONCATENATION
4925/// surface across the (char, char) × (char) element-type product:
4926/// * `[(char, char); N] == [(char, char); K] ++ diagonal([char; M])`
4927/// at the paired-row diagonal-tail helper (existing);
4928/// * `[char; N] == col(paired [(char, char); K]) ++ [char; M]` at
4929/// the scalar-row column-projection helper (this one).
4930///
4931/// Both helpers compose against the SAME
4932/// ([`Atom::NAMED_ESCAPE_TABLE`], [`Atom::SELF_ESCAPE_TABLE`]) peer
4933/// pair — the paired-row helper reads `NAMED_ESCAPE_TABLE`'s BOTH
4934/// columns verbatim into `ESCAPE_TABLE[0..3]` + a diagonal embedding
4935/// of `SELF_ESCAPE_TABLE` at `ESCAPE_TABLE[3..5]`; this scalar-row
4936/// helper reads ONE column of `NAMED_ESCAPE_TABLE` into `ESCAPE_
4937/// SOURCES[0..3]` / `ESCAPE_DECODED[0..3]` + `SELF_ESCAPE_TABLE`
4938/// verbatim into positions `[3..5]`. The two-witness closure over
4939/// `take_right_column ∈ {false, true}` bonds the (LEFT, RIGHT)
4940/// scalar-column projection pair at ONE const-eval sweep per column,
4941/// binding TWO substrate composition laws through the SAME primitive.
4942///
4943/// The `take_right_column: bool` parameter routes the head-segment
4944/// sweep to the corresponding column of the paired `head_table`:
4945/// * `false` selects LEFT (`head_table[i].0`) — the pattern-SOURCE
4946/// column (the byte the reader sees BEFORE decoding an escape).
4947/// * `true` selects RIGHT (`head_table[i].1`) — the pattern-DECODED
4948/// column (the byte the reader emits AFTER decoding an escape).
4949///
4950/// The runtime bool composes with const-eval so BOTH substrate
4951/// witnesses (LEFT for `ESCAPE_SOURCES`, RIGHT for `ESCAPE_DECODED`)
4952/// share ONE primitive definition — halving the per-helper source
4953/// surface while keeping the per-column panic message DISTINCT via
4954/// the two `HEAD-SEGMENT-LEFT-COLUMN-DIVERGENCE` /
4955/// `HEAD-SEGMENT-RIGHT-COLUMN-DIVERGENCE` axes.
4956///
4957/// Runtime callability: the function is a normal `pub const fn`, so
4958/// callers CAN also invoke it at runtime — e.g. a REPL / LSP
4959/// tokenizer that constructs a scalar `[char; N]` composite from a
4960/// paired-column projection AND a scalar peer tail at runtime and
4961/// wants to verify SEGMENTED-CONCATENATION structure before consuming
4962/// it. Every panic site names the helper AND identifies the failed
4963/// AXIS distinctly so downstream diagnostics route regressions back
4964/// to (a) the helper by string search on
4965/// `"assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"`
4966/// and (b) the specific segment/column by axis-prefix. The four axis-
4967/// provenance strings (`CARDINALITY-MISMATCH`, `HEAD-SEGMENT-LEFT-
4968/// COLUMN-DIVERGENCE`, `HEAD-SEGMENT-RIGHT-COLUMN-DIVERGENCE`, `TAIL-
4969/// SEGMENT-DIVERGENCE`) are chosen DISTINCT from every sibling
4970/// helper's axis vocabulary (`HEAD-SEGMENT-LEFT-DIVERGENCE` /
4971/// `HEAD-SEGMENT-RIGHT-DIVERGENCE` / `DIAGONAL-TAIL-SEGMENT-LEFT-
4972/// DIVERGENCE` / `DIAGONAL-TAIL-SEGMENT-RIGHT-DIVERGENCE` on the
4973/// paired-row diagonal-tail sibling; `LEFT-COLUMN-DIVERGENCE` /
4974/// `RIGHT-COLUMN-DIVERGENCE` on the paired-row `_columns_equal_char_
4975/// arrays` sibling) so a diagnostic that names the failed axis routes
4976/// UNAMBIGUOUSLY to (a) this specific (char) scalar-row
4977/// column-projection concatenation helper, (b) the failed SEGMENT
4978/// (HEAD vs TAIL), (c) the failed COLUMN on the HEAD segment (LEFT
4979/// vs RIGHT). The `-COLUMN-` infix distinguishes this helper's HEAD
4980/// arms from the sibling paired-row diagonal-tail helper's HEAD arms
4981/// on any downstream substring search.
4982///
4983/// Adding a new scalar `[char; N]` substrate array declared as
4984/// CONCATENATION of a SELECTED COLUMN of a peer paired `[(char,
4985/// char); K]` vocabulary with a peer scalar `[char; M]` tail (e.g. a
4986/// future extension to `Atom::decode_str_escape` whose SOURCE or
4987/// DECODED SPAN grows through a new NAMED-column-prefix + SELF-suffix
4988/// partition): pair the declaration with `const _: () = assert_char_
4989/// array_is_concatenation_of_char_pair_array_column_and_char_array::
4990/// <N, K, M>(&Self::FOO_SPAN, &Self::FOO_TABLE, &Self::FOO_TAIL,
4991/// take_right_column);` co-located after the composite's
4992/// declaration and the compound identity binds at compile time
4993/// WITHOUT a runtime concat-and-project loop.
4994///
4995/// Theory grounding:
4996/// - THEORY.md §V.1 — knowable platform; the SEGMENTED composite-
4997/// construction identity on the CROSS-ROW (paired → scalar) column-
4998/// projection axis becomes a TYPE-LEVEL theorem carried per
4999/// declaration triple rather than a runtime concat-and-project loop
5000/// the developer must remember to write per triple.
5001/// - THEORY.md §III — the typescape; the (element-type × contract-
5002/// shape) matrix now carries the CROSS-ROW (paired-column ⊕
5003/// scalar-tail → scalar-composite) segmented-concatenation corner
5004/// at ONE peer const-fn helper. Combined with the pre-existing
5005/// (char, char)-row diagonal-tail sibling, the two helpers close
5006/// the CROSS-ROW SEGMENTED-CONCATENATION face of the substrate's
5007/// Str-payload escape-table product-vocabulary.
5008/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
5009/// (paired-column-head-segment, scalar-tail-segment) composition
5010/// proof at declaration site AND the peer-vocabulary consumers
5011/// regenerate through the SAME `const _` witness at the (char)
5012/// scalar row.
5013/// - THEORY.md §VI.1 — generation over composition; the paired-
5014/// column-project + scalar-verbatim-copy sweep IS the generative
5015/// shape. Every new scalar-composite declared as a segmented
5016/// concatenation-through-column-projection adds ONE `const _` line
5017/// to get the compound theorem.
5018///
5019/// Frontier inspiration: MLIR's typed IR-rewriter pattern of
5020/// projecting a wider tuple-typed operation into its per-column
5021/// scalar residual through a rewrite pass. Where MLIR routes the
5022/// projection through a Pass-driven rewrite that fires at IR
5023/// compilation, this helper routes the projection through a rustc
5024/// const-eval-time proof obligation at every scalar-composite
5025/// declaration site — the composition law binds as a type-level
5026/// theorem at `cargo check` rather than as an IR-rewrite-time check
5027/// deferred to a compilation stage the substrate's `pub const`
5028/// declarations never enter.
5029pub const fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array<
5030 const N: usize,
5031 const K: usize,
5032 const M: usize,
5033>(
5034 arr: &[char; N],
5035 head_table: &[(char, char); K],
5036 tail: &[char; M],
5037 take_right_column: bool,
5038) {
5039 if K + M != N {
5040 panic!(
5041 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array: \
5042 CARDINALITY-MISMATCH — the three const parameters `N`, \
5043 `K`, `M` must satisfy `K + M == N` so the SELECTED column \
5044 projection of the peer paired `head_table` (contributing \
5045 positions `[0..K)` of the composite scalar `arr`) followed \
5046 by the peer scalar `tail` (contributing positions `[K..N)` \
5047 of `arr` verbatim) exactly cover `arr`'s `N` positions. \
5048 Fix at the `const _` witness's turbofish by reconciling \
5049 the three arities against the composite's declared arity. \
5050 The CARDINALITY-MISMATCH gate distinguishes THIS failure \
5051 from every content-drift arm — a mistyped ARITY on the \
5052 caller side fails HERE before any per-position sweep \
5053 begins, so a subtle arity slip doesn't silently degenerate \
5054 into a truncated segment sweep."
5055 );
5056 }
5057 let mut i = 0;
5058 while i < K {
5059 if take_right_column {
5060 if arr[i] as u32 != head_table[i].1 as u32 {
5061 panic!(
5062 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array: \
5063 HEAD-SEGMENT-RIGHT-COLUMN-DIVERGENCE — the \
5064 composite scalar array `arr` carries an entry at \
5065 some position in `[0, K)` (the HEAD segment) that \
5066 does NOT byte-for-byte equal the peer paired \
5067 `head_table` array's RIGHT-column entry at the \
5068 SAME position (i.e. `arr[i] != head_table[i].1`). \
5069 The substrate's SEGMENTED-CONCATENATION-through-\
5070 PAIRED-COLUMN-PROJECTION contract on the \
5071 (composite scalar, head-segment) HEAD-arm RIGHT \
5072 column is broken; every consumer that reads \
5073 `arr[0..K)` and the RIGHT-column projection of \
5074 `head_table` as INTERCHANGEABLE (any outer-\
5075 dispatch pattern-matcher on the head sub-\
5076 vocabulary that expects the composite's HEAD \
5077 segment to project verbatim to the peer paired \
5078 vocabulary's RIGHT column) relies on this \
5079 invariant. Fix at one of the two declaration \
5080 sites (`arr[0..K)` or `head_table`) by reconciling \
5081 the entry across the two arrays."
5082 );
5083 }
5084 } else if arr[i] as u32 != head_table[i].0 as u32 {
5085 panic!(
5086 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array: \
5087 HEAD-SEGMENT-LEFT-COLUMN-DIVERGENCE — the composite \
5088 scalar array `arr` carries an entry at some position \
5089 in `[0, K)` (the HEAD segment) that does NOT byte-for-\
5090 byte equal the peer paired `head_table` array's LEFT-\
5091 column entry at the SAME position (i.e. `arr[i] != \
5092 head_table[i].0`). The substrate's SEGMENTED-\
5093 CONCATENATION-through-PAIRED-COLUMN-PROJECTION \
5094 contract on the (composite scalar, head-segment) \
5095 HEAD-arm LEFT column is broken; every consumer that \
5096 reads `arr[0..K)` and the LEFT-column projection of \
5097 `head_table` as INTERCHANGEABLE (any outer-dispatch \
5098 pattern-matcher on the head sub-vocabulary that \
5099 expects the composite's HEAD segment to project \
5100 verbatim to the peer paired vocabulary's LEFT column) \
5101 relies on this invariant. Fix at one of the two \
5102 declaration sites (`arr[0..K)` or `head_table`) by \
5103 reconciling the entry across the two arrays."
5104 );
5105 }
5106 i += 1;
5107 }
5108 let mut j = 0;
5109 while j < M {
5110 if arr[K + j] as u32 != tail[j] as u32 {
5111 panic!(
5112 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array: \
5113 TAIL-SEGMENT-DIVERGENCE — the composite scalar array \
5114 `arr` carries an entry at some position in `[K, N)` \
5115 (the TAIL segment) that does NOT byte-for-byte equal \
5116 the peer scalar `tail` array's entry at the SAME \
5117 position offset (i.e. `arr[K + j] != tail[j]`). The \
5118 substrate's SEGMENTED-CONCATENATION-through-PAIRED-\
5119 COLUMN-PROJECTION contract on the (composite scalar, \
5120 tail-segment) TAIL-arm is broken; every consumer that \
5121 reads `arr[K..N)` and the peer scalar `tail` array as \
5122 verbatim-interchangeable relies on this invariant. \
5123 Fix at one of the two declaration sites (`arr[K..N)` \
5124 or `tail`) by reconciling the entry. The TAIL-SEGMENT \
5125 arm is INDEPENDENT of `take_right_column` because the \
5126 tail is a scalar array projected verbatim into the \
5127 composite rather than routed through a paired-column \
5128 projection at the head."
5129 );
5130 }
5131 j += 1;
5132 }
5133}
5134
5135// Compile-time SEGMENTED-CONCATENATION-through-PAIRED-COLUMN-PROJECTION
5136// witnesses — two `const _: () = assert_char_array_is_concatenation_
5137// of_char_pair_array_column_and_char_array::<5, 3, 2>(&…, &Atom::
5138// NAMED_ESCAPE_TABLE, &Atom::SELF_ESCAPE_TABLE, take_right_column)`
5139// lines binding the (`Atom::ESCAPE_SOURCES`, `Atom::NAMED_ESCAPE_TABLE`,
5140// `Atom::SELF_ESCAPE_TABLE`) LEFT-column-projection triple AND the
5141// (`Atom::ESCAPE_DECODED`, `Atom::NAMED_ESCAPE_TABLE`,
5142// `Atom::SELF_ESCAPE_TABLE`) RIGHT-column-projection triple on the
5143// substrate's Str-payload escape-table product-vocabulary at ONE
5144// const-eval sweep per scalar-column projection. `Atom::ESCAPE_SOURCES`
5145// (`[char; 5]`) is CONSTRUCTED as `NAMED_ESCAPE_TABLE`'s LEFT-column
5146// projection at positions `[0..3]` followed by `SELF_ESCAPE_TABLE`
5147// verbatim at positions `[3..5]`; `Atom::ESCAPE_DECODED` (`[char; 5]`)
5148// is CONSTRUCTED as `NAMED_ESCAPE_TABLE`'s RIGHT-column projection at
5149// positions `[0..3]` followed by `SELF_ESCAPE_TABLE` verbatim at
5150// positions `[3..5]` (SELF's pattern-EQUALS-value property collapses
5151// the SELF-column-projection into SELF-verbatim on BOTH scalar
5152// composite arrays). Pre-lift, the two scalar-projection identities
5153// lived ONLY as PROSE at [`Atom::ESCAPE_SOURCES`]'s and
5154// [`Atom::ESCAPE_DECODED`]'s docstrings AND as implicit transitive
5155// consequences of the pre-existing (`_columns_equal_char_arrays` on
5156// `ESCAPE_TABLE` against the two scalar peer columns) +
5157// (`_is_concatenation_of_char_pair_array_and_char_array_diagonal` on
5158// `ESCAPE_TABLE` against `NAMED_ESCAPE_TABLE` and diagonal-embedded
5159// `SELF_ESCAPE_TABLE`) witness pair — neither pre-existing witness
5160// DIRECTLY binds the scalar-projection identity on the two `[char; 5]`
5161// scalar composite arrays at the char-array level. Post-lift, the two
5162// scalar-projection identities bind at `cargo check` time — a
5163// regression that reorders EITHER scalar composite's five entries so
5164// its NAMED-column-prefix + SELF-suffix partition breaks, drifts one
5165// entry across NAMED_ESCAPE_TABLE and its scalar-column projection, or
5166// silently drops a SELF-suffix entry from EITHER scalar composite,
5167// fails the build rather than the test suite. Sibling to the pre-
5168// existing (`_pairwise_distinct` on `ESCAPE_SOURCES` +
5169// `_pairwise_distinct` on `ESCAPE_DECODED` +
5170// `_within_char_finite_set` on `ESCAPE_SOURCES` +
5171// `_within_char_finite_set` on `ESCAPE_DECODED`) witnesses above on
5172// the SAME two scalar arrays — the four prior witnesses bind SET-level
5173// axes (INJECTIVITY, SUBSET-EMBEDDING) at the scalar-composite level
5174// without pinning the SEGMENTED-CONCATENATION structural identity;
5175// these two new witnesses close that structural identity as compile-
5176// time theorems on the SAME two scalar arrays. The four SET-level
5177// axes on the two scalar arrays combine with the two SEGMENTED-
5178// CONCATENATION axes here to give downstream consumers a SIX-witness
5179// closure on each scalar composite (four SET-level axes plus the
5180// SEGMENTED structural axis routing through EITHER the LEFT-column or
5181// RIGHT-column projection of the peer paired vocabulary).
5182const _: () = assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<5, 3, 2>(
5183 &Atom::ESCAPE_SOURCES,
5184 &Atom::NAMED_ESCAPE_TABLE,
5185 &Atom::SELF_ESCAPE_TABLE,
5186 false,
5187);
5188const _: () = assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<5, 3, 2>(
5189 &Atom::ESCAPE_DECODED,
5190 &Atom::NAMED_ESCAPE_TABLE,
5191 &Atom::SELF_ESCAPE_TABLE,
5192 true,
5193);
5194
5195/// Compile-time contract verifier — panics at const evaluation time if
5196/// the distinct-values set of `arr` does not equal exactly the inclusive
5197/// range `{LO..=HI}` on the substrate's `u8` cache-key vocabulary. Binds
5198/// TWO conjunct clauses at ONE `const _` line:
5199/// 1. RANGE-BOUND: every entry lies within `[LO, HI]` — no byte
5200/// outside the target partition. A regression that drifts ONE
5201/// entry above `HI` or below `LO` (e.g. lifts a fresh entry at
5202/// `7u8` on a `{0..=6}` array) fails-loudly at const-eval.
5203/// 2. FULL-COVERAGE: every byte in `[LO..=HI]` appears at least once
5204/// in `arr` — no byte inside the target partition missing. A
5205/// regression that silently unifies two entries onto ONE byte,
5206/// leaving another byte in the range unreached (e.g. drops the
5207/// `Nil` arm's `0u8` from `SexpShape::HASH_DISCRIMINATORS` in
5208/// favour of a redundant `1u8`), fails-loudly at const-eval too.
5209///
5210/// The RANGE-COVERAGE contract is a NON-INJECTIVE peer of the
5211/// pre-existing pairwise-DISTINCTNESS contract
5212/// ([`assert_u8_array_pairwise_distinct`]) on the SAME `u8` cache-key
5213/// vocabulary: where distinctness is the INJECTIVITY axis (every entry
5214/// unique), range-coverage is the SURJECTIVITY-onto-a-range axis (every
5215/// range byte reached, entries CAN duplicate). Applied to arrays where
5216/// duplicates are load-bearing by construction — [`SexpShape::
5217/// HASH_DISCRIMINATORS`](crate::error::SexpShape::HASH_DISCRIMINATORS)
5218/// (`[u8; 12]` covering `{0..=6}` with SIX atomic-shape arms all
5219/// collapsing to the outer Atom marker byte `1u8`) is the archetype
5220/// case, intentionally omitted from the distinctness sweep per the
5221/// twelve-shape → seven-byte collapse rule documented on the
5222/// pairwise-distinct helper above; this range-coverage helper binds
5223/// the outer-partition contract at compile time despite the six-fold
5224/// collapse.
5225///
5226/// The invariant is load-bearing for the outer-`Sexp` cache-key
5227/// algebra's SPAN across the shape-level projection surface. The
5228/// twelve-arm sweep of `SexpShape::HASH_DISCRIMINATORS` MUST cover
5229/// exactly the outer discriminator space `{0..=6}` — no byte outside
5230/// (a `7u8` drift would introduce an unreachable cache slot for
5231/// [`crate::macro_expand::Expander::cache`]), no byte inside missing
5232/// (a `2u8` drop would silently unhash every `Sexp::List(_)` through
5233/// whatever cache slot the drift routes to). Every future family-wide
5234/// `[u8; N]` array whose distinct-value set is an intentionally-
5235/// closed inclusive range participates in the SAME compile-time
5236/// guarantee via one `const _` line.
5237///
5238/// Adding a new family-wide `[u8; N]` range-covering array to the
5239/// substrate: pair the declaration with `const _: () =
5240/// assert_u8_array_covers_inclusive_range::<N, LO, HI>(&Self::FOO_
5241/// ARRAY);` co-located after the array's declaration and the range-
5242/// coverage contract binds at compile time. The rustc-forced arity
5243/// `[u8; N]` composes with this const-eval sweep so cardinality AND
5244/// range-bound AND full-coverage are ALL compile-time theorems on the
5245/// SAME array.
5246///
5247/// Runtime callability: the function is a normal `pub const fn`, so
5248/// callers CAN also invoke it at runtime — pinned by
5249/// `assert_u8_array_covers_inclusive_range_panics_at_runtime_on_
5250/// out_of_range_entry` + `assert_u8_array_covers_inclusive_range_
5251/// panics_at_runtime_on_missing_range_byte`. Both panic sites carry
5252/// axis-provenance strings so downstream diagnostics (`cargo check`
5253/// const-eval error output, test-suite failure reports) route the
5254/// drift back to the failed axis (RANGE-BOUND vs. FULL-COVERAGE) by
5255/// string search — halving the search space for the operator
5256/// debugging the drift.
5257///
5258/// Cross-argument constraint: `LO <= HI` is required — a caller who
5259/// passes `LO > HI` fails-loudly at the first `LO..=HI` sweep step
5260/// (the `while cur <= HI` guard rejects immediately) with a
5261/// well-defined "empty range" outcome that is nonetheless surfaced as
5262/// a full-coverage failure since `arr` must then be empty (`N == 0`).
5263/// The three-parameter shape `(N, LO, HI)` composes rustc's forced
5264/// arity `[u8; N]` with the two const-parameter bounds so ALL THREE
5265/// invariants (cardinality, min-bound, max-bound) are compile-time
5266/// theorems on the SAME `const _` line.
5267///
5268/// Theory grounding:
5269/// - THEORY.md §V.1 — knowable platform; the family-wide range-
5270/// coverage contract on the `u8` cache-key vocabulary becomes a
5271/// TYPE-LEVEL theorem the substrate carries per array declaration
5272/// rather than a runtime test the developer must remember to write
5273/// per array (one range-bound sweep + one full-coverage sweep).
5274/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
5275/// cache-key partition is the `intent_hash` composition axis —
5276/// binding the range-coverage arrays on the typed algebra makes
5277/// attestation-key drift a compile error rather than a silent
5278/// BLAKE3 mis-hash on any consumer keyed on `Hash for Sexp`. A
5279/// regression that drops a byte from the outer partition (or adds
5280/// an out-of-range byte) fails the build before it can silently
5281/// invalidate a cached expansion or a Sekiban audit-trail metric
5282/// keyed on the outer discriminator space.
5283/// - THEORY.md §VI.1 — generation over composition; the const-eval
5284/// range-and-coverage sweep IS the generative shape. Every new
5285/// closed-set discriminator array whose distinct-value set is an
5286/// intentionally-closed inclusive range adds ONE `const _` line to
5287/// get the range-coverage theorem rather than re-deriving two
5288/// per-array runtime iterator sweeps (one for the range bound, one
5289/// for the coverage completeness).
5290///
5291/// Element-type sibling posture to the four pre-existing const-fn
5292/// contract verifiers ([`assert_char_array_pairwise_distinct`],
5293/// [`assert_str_array_pairwise_distinct`],
5294/// [`assert_u8_array_pairwise_distinct`],
5295/// [`assert_char_pair_array_bijective`]): where the four
5296/// distinctness/bijectivity helpers close the INJECTIVITY axis of the
5297/// substrate's typed-array vocabulary, this range-coverage helper
5298/// opens the SURJECTIVITY-onto-a-range axis on the SAME `u8` cache-key
5299/// element type — extending the family-wide contract-verifier surface
5300/// past the closed-set distinctness axis onto the closed-set covering
5301/// axis.
5302pub const fn assert_u8_array_covers_inclusive_range<const N: usize, const LO: u8, const HI: u8>(
5303 arr: &[u8; N],
5304) {
5305 let mut i = 0;
5306 while i < N {
5307 if arr[i] < LO || arr[i] > HI {
5308 panic!(
5309 "assert_u8_array_covers_inclusive_range: family-wide \
5310 u8 array carries an OUT-OF-RANGE entry at some \
5311 position — the entry's byte lies outside the target \
5312 inclusive range `[LO, HI]`. The substrate's RANGE-\
5313 BOUND contract on the array is broken; every \
5314 consumer that expects the array's entries to \
5315 partition an outer cache-key space (Hash for Sexp's \
5316 outer discriminator space, StructuralKind / \
5317 AtomKind / QuoteForm sub-carving spaces) relies on \
5318 the entries staying within the target range",
5319 );
5320 }
5321 i += 1;
5322 }
5323 let mut cur = LO;
5324 loop {
5325 let mut k = 0;
5326 let mut found = false;
5327 while k < N {
5328 if arr[k] == cur {
5329 found = true;
5330 break;
5331 }
5332 k += 1;
5333 }
5334 if !found {
5335 panic!(
5336 "assert_u8_array_covers_inclusive_range: family-wide \
5337 u8 array is MISSING a byte from the target inclusive \
5338 range `[LO, HI]` — every byte in the range must \
5339 appear at least once in the array. The substrate's \
5340 FULL-COVERAGE contract on the array is broken; every \
5341 consumer that expects the array's distinct-value set \
5342 to span the target range (SexpShape's twelve-shape → \
5343 seven-byte outer collapse across `{{0..=6}}`, the \
5344 sub-carvings' partition-span contracts) relies on \
5345 every range byte being reached",
5346 );
5347 }
5348 if cur == HI {
5349 break;
5350 }
5351 cur += 1;
5352 }
5353}
5354
5355// Compile-time range-coverage witnesses — one `const _: () =
5356// assert_u8_array_covers_inclusive_range::<N, LO, HI>(&…)` per
5357// family-wide `[u8; N]` hash-discriminator array on the substrate's
5358// closed-set outer algebras whose distinct-value set is an
5359// intentionally-closed inclusive range AND WHOSE JOINT (INJECTIVITY,
5360// SURJECTIVITY, ARITY) CONTRACT DOES NOT BIND THROUGH THE STRONGER
5361// COMPOUND `assert_u8_array_permutes_inclusive_range` HELPER BELOW.
5362// Each invocation is const-evaluated at `cargo check` time; a
5363// regression that silently drifts an entry above HI, below LO, OR
5364// silently drops a range byte from the distinct-value set fails the
5365// build rather than the test suite. Sibling to the runtime
5366// `_span_outer_partition_*` / `_covers_*` tests at `error.rs`'s
5367// tests module — the two enforce the same theorem at TWO stages of
5368// the toolchain, so a build that skips tests still catches the
5369// regression here, and a build that runs tests catches it a second
5370// time as a safety net if the const-eval sweep is ever silently
5371// dropped. The three permutation-shaped sub-carvings
5372// (`AtomKind::HASH_DISCRIMINATORS`, `QuoteForm::HASH_DISCRIMINATORS`,
5373// `UnquoteForm::HASH_DISCRIMINATORS`) bind SURJECTIVITY-onto-a-range
5374// through the stronger compound `assert_u8_array_permutes_inclusive_
5375// range` helper below — the compound helper composes injectivity ∧
5376// surjectivity ∧ arity-cardinality-match at ONE `const _` line per
5377// array, halving the per-array witness surface at strictly stronger
5378// contract strength. Only `SexpShape::HASH_DISCRIMINATORS` remains
5379// on this single-axis SURJECTIVITY sweep because its intentionally-
5380// non-injective twelve-shape → seven-byte collapse means DISTINCTNESS
5381// DOES NOT hold (the six atomic-shape arms all collapse to `1u8`)
5382// yet range-coverage of `{0..=6}` DOES hold (every outer byte
5383// reached) — it CANNOT bind a permutation contract on any range
5384// corner. `StructuralKind::HASH_DISCRIMINATORS` covers the non-
5385// inclusive-range partition `{0, 2}` (gap at `1u8` where the
5386// atomic-carve outer marker lives, per the outer-`Sexp` carve
5387// semantics) — intentionally OMITTED from this range-coverage sweep
5388// since its distinct-value set is not a contiguous inclusive range;
5389// it binds SURJECTIVITY through the sibling non-contiguous-corner
5390// helper `assert_u8_array_covers_finite_set` below.
5391const _: () = assert_u8_array_covers_inclusive_range::<12, 0, 6>(
5392 &crate::error::SexpShape::HASH_DISCRIMINATORS,
5393);
5394
5395/// Compile-time contract verifier — panics at const evaluation time if
5396/// any entry of `arr` falls OUTSIDE the inclusive range `[LO, HI]` on
5397/// the substrate's `u8` cache-key vocabulary. Binds ONE conjunct clause
5398/// at ONE `const _` line:
5399///
5400/// * RANGE-SUBSET-VIOLATION: every entry in `arr` lies in `[LO..=HI]` —
5401/// the array's distinct-value set is a SUBSET of the target inclusive
5402/// range. A regression that drifts ONE entry to a byte OUTSIDE the
5403/// range (e.g. lifts a `[u8; 2]` sub-carve of the outer-`Sexp` cache-
5404/// key `[0..=6]` partition to `[0u8, 7u8]`, drifting past the outer-
5405/// discriminator space's upper endpoint) fails-loudly at const-eval.
5406///
5407/// Contract-strength peer to [`assert_u8_array_covers_inclusive_range`]
5408/// on the (equality-vs-subset) axis: where `_covers_inclusive_range`
5409/// binds `arr`'s distinct-value set FULLY COVERS `[LO..=HI]` (every
5410/// entry is in the range AND every range byte is reached — the RANGE-
5411/// BOUND arm ∧ the FULL-COVERAGE arm), this helper binds ONLY the
5412/// RANGE-BOUND arm read in isolation (`arr ⊆ [LO..=HI]` without the
5413/// FULL-COVERAGE clause) — a strictly WEAKER contract for arrays
5414/// intentionally covering only a PROPER SUBSET of the target range.
5415///
5416/// Contiguity-axis peer to [`assert_u8_array_within_u8_finite_set`]:
5417/// where the finite-set SUBSET-only sibling binds `arr ⊆ set` at the
5418/// non-contiguous-finite-set corner (`set` is any `[u8; M]` —
5419/// contiguous, gapped, singleton, or scattered), this helper binds
5420/// `arr ⊆ [LO..=HI]` at the contiguous-range corner (target super-
5421/// set is a contiguous inclusive range parameterised by the `LO/HI`
5422/// const generics rather than a runtime-provided literal array). The
5423/// two helpers together close the (equality-vs-subset) × (contiguity)
5424/// 2×2 = 4-corner face of the substrate's `u8` array vocabulary at
5425/// compile time: (equality, contiguous) at
5426/// [`assert_u8_array_covers_inclusive_range`], (equality, finite-
5427/// set) at [`assert_u8_array_covers_finite_set`], (subset,
5428/// contiguous) at THIS helper, (subset, finite-set) at
5429/// [`assert_u8_array_within_u8_finite_set`]. Prefer the tighter
5430/// [`assert_u8_array_covers_inclusive_range`] when the array's
5431/// distinct-value set intentionally EQUALS the target range; this
5432/// helper is for the strictly-weaker SUBSET corner where `arr`
5433/// covers only a PROPER SUBSET of `[LO..=HI]`. Prefer
5434/// [`assert_u8_array_within_u8_finite_set`] when the target super-
5435/// set is NON-contiguous — this range-based helper cannot express
5436/// gaps within the target.
5437///
5438/// The invariant is load-bearing for the substrate's OUTER-`Sexp`
5439/// cache-key partition-embedding contract on the ONE outer discri-
5440/// minator array whose parent-range embedding is NOT ALREADY compile-
5441/// time-enforced through a tighter contract:
5442/// [`crate::error::StructuralKind::HASH_DISCRIMINATORS`] (`[u8; 2]`
5443/// = `[0, 2]`, the non-contiguous two-of-seven structural-residual
5444/// sub-carving covering `{0, 2}` with a gap at `1u8` where the
5445/// atomic-carve outer marker lives) MUST be a SUBSET of `[0..=6]`
5446/// (the outer-`Sexp` cache-key discriminator space defined by
5447/// [`crate::error::SexpShape::HASH_DISCRIMINATORS`]'s twelve-shape
5448/// → seven-byte collapse). Pre-lift the array bound SURJECTIVITY
5449/// through
5450/// `assert_u8_array_permutes_finite_set::<2, 2>(&StructuralKind::HASH_DISCRIMINATORS, &[0u8, 2u8])`
5451/// — this compound witness binds a permutation of the FINITE-SET
5452/// `{0, 2}` but leaves the OUTER-RANGE embedding UNCONSTRAINED at
5453/// compile time. A coordinated regression that drifted BOTH
5454/// `StructuralKind::LIST_HASH_DISCRIMINATOR` from `2u8` to (say)
5455/// `8u8` AND updated the finite-set literal from `&[0u8, 2u8]` to
5456/// `&[0u8, 8u8]` in lockstep at the `_permutes_finite_set` call site
5457/// would pass the finite-set-permutation witness (the drifted pair
5458/// is a permutation of the drifted set) but VIOLATE the outer-
5459/// `[0..=6]` partition semantic — `8u8` falls OUTSIDE the twelve-
5460/// shape SexpShape hash arm's reach. Post-lift the ARRAY-LEVEL
5461/// RANGE-SUBSET witness catches the coordinated finite-set drift
5462/// at const-eval time; the panic message routes operator attention
5463/// to the ARRAY-DECLARATION site as the drift origin. The runtime
5464/// per-role scalar alias-chain pins in `error.rs`'s test module
5465/// survive as sibling checks (a distinct failure mode: a drift
5466/// that KEPT the byte within `[0..=6]` but ROUTED to the wrong
5467/// per-role outer marker still passes the ARRAY-level range-
5468/// subset check but fails the per-role scalar pin) — together the
5469/// two pins bind the outer-partition embedding at TWO stages of
5470/// the toolchain.
5471///
5472/// The three OTHER family-wide outer-`Sexp` discriminator arrays
5473/// (`SexpShape`, `QuoteForm`, `UnquoteForm`) are intentionally
5474/// OMITTED from this range-SUBSET sweep because their outer-
5475/// `[0..=6]` embedding is ALREADY compile-time-enforced through
5476/// TIGHTER contracts on their respective sub-ranges:
5477/// [`crate::error::SexpShape::HASH_DISCRIMINATORS`] covers `[0..=6]`
5478/// exactly via [`assert_u8_array_covers_inclusive_range::<12, 0, 6>`]
5479/// (equality tighter than subset — the covers witness implies the
5480/// subset witness); [`QuoteForm::HASH_DISCRIMINATORS`] permutes
5481/// `[3..=6]` via [`assert_u8_array_permutes_inclusive_range::<4, 3, 6>`]
5482/// (a permutation of `[3..=6]` — which is a subset of `[0..=6]` by
5483/// numeric inclusion — implies `⊆ [0..=6]`);
5484/// [`crate::error::UnquoteForm::HASH_DISCRIMINATORS`] permutes
5485/// `[5..=6]` via [`assert_u8_array_permutes_inclusive_range::<2, 5, 6>`]
5486/// (same, doubly transitive through QuoteForm's parent-range
5487/// containment). Adding redundant SUBSET-of-`[0..=6]` witnesses on
5488/// those three would double-bind claims strictly weaker than what
5489/// the tighter permutes / covers contracts already prove.
5490///
5491/// Every future family-wide `[u8; N]` typed-range-subset carving on
5492/// the substrate's closed-set outer algebras whose target-superset
5493/// IS a contiguous inclusive range (e.g. any further intentionally-
5494/// closed sub-carving of the `[0..=6]` outer-`Sexp` partition whose
5495/// distinct-value set is intentionally NON-CONTIGUOUS within the
5496/// target range) participates in the SAME compile-time guarantee
5497/// via one `const _` line.
5498///
5499/// Adding a new family-wide `[u8; N]` range-embedded array to the
5500/// substrate: pair the declaration with `const _: () =
5501/// assert_u8_array_within_inclusive_range::<N, LO, HI>(&Self::
5502/// FOO_ARRAY);` co-located after the array's declaration and the
5503/// RANGE-SUBSET contract binds at compile time. The rustc-forced
5504/// arity `[u8; N]` composes with this const-eval sweep so both
5505/// cardinality-N AND every-entry-in-range are compile-time theorems
5506/// on the SAME array.
5507///
5508/// Runtime callability: the function is a normal `pub const fn`, so
5509/// callers CAN also invoke it at runtime — pinned by
5510/// `assert_u8_array_within_inclusive_range_panics_at_runtime_on_entry_above_hi`,
5511/// `assert_u8_array_within_inclusive_range_panics_at_runtime_on_entry_below_lo`,
5512/// `assert_u8_array_within_inclusive_range_panics_at_runtime_on_terminal_out_of_range_entry`,
5513/// and
5514/// `assert_u8_array_within_inclusive_range_panic_message_names_the_helper_and_range_subset_violation_axis`.
5515/// The panic site carries the axis-provenance string
5516/// `"RANGE-SUBSET-VIOLATION"` chosen DISTINCT from every sibling
5517/// helper's axis vocabulary (`"duplicate"` on the ARRAY-side pairwise-
5518/// distinct sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the
5519/// covers-finite-set sibling; `"OUT-OF-RANGE"` / `"MISSING"` on the
5520/// covers-inclusive-range sibling; `"SUBSET-VIOLATION"` on the finite-
5521/// set SUBSET-only sibling; `"ARITY-MISMATCH"` on both `_permutes_*`
5522/// compound helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on the SET-side
5523/// well-formedness sibling) so a diagnostic that names the failed
5524/// axis routes UNAMBIGUOUSLY to THIS specific range SUBSET-embedding
5525/// helper. The `"RANGE-"` prefix disambiguates from the finite-set
5526/// peer's bare `"SUBSET-VIOLATION"`; the `"-VIOLATION"` suffix is
5527/// shared with the finite-set peer so callers can grep either
5528/// SUBSET-embedding sibling by `"VIOLATION"` alone or route to the
5529/// specific contiguity corner by the `"RANGE-"` / bare-`"SUBSET-"`
5530/// disambiguator prefix.
5531///
5532/// Theory grounding:
5533/// - THEORY.md §V.1 — knowable platform; the family-wide range-
5534/// subset-embedding contract on the `u8` cache-key vocabulary
5535/// becomes a TYPE-LEVEL theorem the substrate carries per
5536/// (array, range) pair rather than a transitively-implied claim
5537/// the developer must trust to hold across sibling witnesses
5538/// drifting in lockstep.
5539/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
5540/// cache-key `[0..=6]` partition is the `intent_hash` composition
5541/// axis — binding the range-subset embedding on the typed algebra
5542/// makes a coordinated drift outside the parent range a compile
5543/// error rather than a silent BLAKE3 mis-hash on any
5544/// `Expander::cache` consumer keyed on the sub-carving.
5545/// - THEORY.md §VI.1 — generation over composition; the const-eval
5546/// range-membership-only sweep IS the generative shape. Every new
5547/// closed-set discriminator array whose distinct-value set is an
5548/// intentional SUBSET of a contiguous inclusive range on the
5549/// substrate adds ONE `const _` line to get the range-subset-
5550/// embedding theorem rather than re-deriving a per-embedding
5551/// runtime iterator sweep at each call site.
5552/// - THEORY.md §II.1 invariant 5 — composition preserves proofs;
5553/// the RANGE-SUBSET embedding proof at declaration site AND the
5554/// parent [`assert_u8_array_covers_inclusive_range`] proof on the
5555/// parent-range-covering array regenerate through the SAME
5556/// `const _` witness at the SUBSET-embedded array level.
5557///
5558/// Frontier inspiration: Lean 4's `Set.Icc` (closed interval) combined
5559/// with `Set.subset_Icc_iff` giving the equivalence `s ⊆ Icc a b ↔
5560/// ∀ x ∈ s, a ≤ x ∧ x ≤ b` — the substrate primitive here embeds
5561/// the same interval-subset relation as a rustc const-eval-time proof
5562/// obligation at every `assert_u8_array_within_inclusive_range` call
5563/// site rather than as a Lean tactic invocation deferred to
5564/// `elab_command`. TLA+'s `\A x \in S : LO <= x /\ x <= HI` first-
5565/// class quantified-interval-subset relation composes similarly at
5566/// TLC model-checking time. Coq's `Included A (Ensembles.Included U
5567/// (fun x => LO <= x <= HI))` predicate encodes the same per-element
5568/// containment. Translation through pleme-io primitives: the RANGE-
5569/// SUBSET embedding predicate binds through ONE forward sweep
5570/// (`LO ≤ arr[i] ≤ HI`) at const-eval time on a rustc-forced-arity
5571/// `[u8; N]` × two `u8` const generics — no `Ord` / `Eq` / `Hash`
5572/// supertrait bound, no allocation, no set carrier.
5573///
5574/// # Panics
5575///
5576/// Panics if any `arr[i]` satisfies `arr[i] < LO || arr[i] > HI`.
5577pub const fn assert_u8_array_within_inclusive_range<const N: usize, const LO: u8, const HI: u8>(
5578 arr: &[u8; N],
5579) {
5580 let mut i = 0;
5581 while i < N {
5582 if arr[i] < LO || arr[i] > HI {
5583 panic!(
5584 "assert_u8_array_within_inclusive_range: RANGE-\
5585 SUBSET-VIOLATION — the family-wide u8 array `arr` \
5586 carries an entry at some position whose byte falls \
5587 OUTSIDE the target inclusive range `[LO, HI]`. The \
5588 substrate's RANGE-SUBSET-EMBEDDING contract on the \
5589 array is broken; every consumer that expects the \
5590 array's distinct-value set to embed within the \
5591 target inclusive range (`StructuralKind::HASH_\
5592 DISCRIMINATORS ⊂ [0..=6]` on the outer-`Sexp` \
5593 cache-key partition; any future typed-range-subset \
5594 embedding on the substrate's closed-set outer \
5595 algebras) relies on every array entry staying \
5596 inside the target range. Fix at the ARRAY-\
5597 DECLARATION site (the `arr` under verification, \
5598 NOT the `LO`/`HI` const parameters specifying the \
5599 target range) by dropping the offending entry OR \
5600 by widening `[LO..=HI]` to cover it — the choice \
5601 depends on whether the drift is an unintended \
5602 overshoot outside the parent range or an \
5603 intentional extension of the range vocabulary"
5604 );
5605 }
5606 i += 1;
5607 }
5608}
5609
5610// Compile-time RANGE-SUBSET-embedding witness — the ONE family-wide
5611// `[u8; N]` HASH_DISCRIMINATORS array on the substrate whose distinct-
5612// value set is an intentionally-closed PROPER SUBSET of a contiguous
5613// inclusive range NOT ALREADY BOUND by a TIGHTER `_covers_inclusive_
5614// range` / `_permutes_inclusive_range` witness on a sub-range:
5615// `StructuralKind::HASH_DISCRIMINATORS` (`[u8; 2]` = `[0, 2]`, the
5616// non-contiguous two-of-seven structural-residual sub-carving covering
5617// `{0, 2}` with a gap at `1u8` where the atomic-carve outer marker
5618// lives) MUST be a SUBSET of `[0..=6]` (the outer-`Sexp` cache-key
5619// discriminator space defined by `SexpShape::HASH_DISCRIMINATORS`'s
5620// twelve-shape → seven-byte collapse). Pre-lift the array bound
5621// SURJECTIVITY through `assert_u8_array_permutes_finite_set::<2, 2>(
5622// &StructuralKind::HASH_DISCRIMINATORS, &[0u8, 2u8])` (module-level
5623// `const _` further down in this file) — this compound witness binds
5624// a permutation of the FINITE-SET `{0, 2}` but leaves the OUTER-RANGE
5625// embedding UNCONSTRAINED at compile time. A coordinated regression
5626// that drifted BOTH `StructuralKind::LIST_HASH_DISCRIMINATOR` from
5627// `2u8` to (say) `8u8` AND updated the finite-set literal from
5628// `&[0u8, 2u8]` to `&[0u8, 8u8]` at the `_permutes_finite_set` call
5629// site would pass the finite-set-permutation witness (the drifted
5630// pair is a permutation of the drifted set) but VIOLATE the outer-
5631// `Sexp` `[0..=6]` partition semantic every `Hash for Sexp` consumer
5632// relies on. Post-lift this ARRAY-LEVEL RANGE-SUBSET witness catches
5633// the coordinated finite-set drift at const-eval time — the panic
5634// message routes operator attention to the ARRAY-DECLARATION site
5635// as the drift origin. The three OTHER family-wide outer-`Sexp`
5636// discriminator arrays (`SexpShape`, `QuoteForm`, `UnquoteForm`) are
5637// intentionally OMITTED from this range-SUBSET sweep because their
5638// outer-`[0..=6]` embedding is ALREADY compile-time-enforced through
5639// TIGHTER contracts on their respective sub-ranges (SexpShape covers
5640// `[0..=6]` exactly; QuoteForm permutes `[3..=6]`; UnquoteForm
5641// permutes `[5..=6]`) — adding redundant SUBSET-of-`[0..=6]`
5642// witnesses on those three would double-bind claims strictly weaker
5643// than what the tighter permutes / covers contracts already prove.
5644const _: () = assert_u8_array_within_inclusive_range::<2, 0, 6>(
5645 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
5646);
5647
5648/// Compile-time WELL-FORMEDNESS contract verifier on a caller-provided
5649/// TARGET-SET spec `set` — panics at const evaluation time (or at
5650/// runtime for dynamic callers) with the axis-provenance-named
5651/// `"SET-NOT-PAIRWISE-DISTINCT"` message iff `set` carries a duplicate
5652/// entry across two positions.
5653///
5654/// Closes the pre-existing caller-side WELL-FORMEDNESS gap on the
5655/// finite-set corner of the substrate's `u8` cache-key contract
5656/// lattice: pre-lift, both [`assert_u8_array_covers_finite_set`] and
5657/// [`assert_u8_array_permutes_finite_set`] documented (but did NOT
5658/// enforce) the pairwise-distinctness of the caller-provided target
5659/// `set` spec as a well-formedness *precondition* — a malformed set
5660/// (e.g. `[0u8, 2u8, 2u8]` at `M = 3`) silently passed BOTH covers-
5661/// helper arms on any `arr` that partitioned the DISTINCT-value subset
5662/// `{0, 2}` (post-lift `assert_u8_array_covers_finite_set` calls into
5663/// this helper as its FIRST arm; a malformed set now fails-loudly at
5664/// const-eval with a SET-side panic message BEFORE the OUT-OF-SET /
5665/// SET-BYTE-MISSING arms fire). The pigeonhole invariants both
5666/// downstream covers/permutes helpers rely on (`N == M` in the
5667/// permutation helper forces exact-set-partitioning) assume `set`
5668/// is well-formed AS a mathematical finite set; this helper turns
5669/// that assumption into a compile-time theorem the substrate
5670/// carries per call site rather than a docstring-level responsibility
5671/// the operator must remember to keep in lockstep across every
5672/// finite-set-covering call site.
5673///
5674/// Element-type sibling posture to [`assert_u8_array_pairwise_distinct`]:
5675/// the ARRAY sibling closes pairwise-distinctness on the ARRAY side of
5676/// the finite-set-coverage / permutation-of-finite-set compound
5677/// contracts (`arr` MUST be pairwise-distinct for the SURJECTIVITY
5678/// arm of the permutation helper to imply full-set-coverage by
5679/// pigeonhole); this SET sibling closes pairwise-distinctness on the
5680/// caller-provided TARGET-SET spec side (the SET spec MUST itself be
5681/// pairwise-distinct as a mathematical set — a `[u8; M]` literal that
5682/// silently duplicates a byte is not really a set of cardinality `M`
5683/// but of cardinality `< M`, and every downstream pigeonhole argument
5684/// gets a phantom-`M` inflating the arity check). The two helpers
5685/// together close BOTH sides of the pairwise-distinctness axis on
5686/// the finite-set-coverage compound tier: `arr` (the array under
5687/// verification) AND `set` (the caller-provided target-set spec).
5688///
5689/// The invariant is load-bearing for `StructuralKind::HASH_DISCRIMINATORS`'
5690/// permutation-of-finite-set witness at
5691/// [`assert_u8_array_permutes_finite_set`]'s module-level `const _`
5692/// invocation — the caller passes `&[0u8, 2u8]` as the target-set
5693/// spec; a regression at the CALL SITE that silently duplicated a
5694/// byte (e.g. `&[0u8, 0u8]` or `&[2u8, 2u8]`) would render the
5695/// permutation contract unsound: the pigeonhole `N == M` arity check
5696/// would still hold on the substrate's `[u8; 2]` array but the
5697/// intended set-cardinality is really `1`, so a permutation of
5698/// `{0, 2}` (via `arr = [0u8, 2u8]`) would silently mis-verify as
5699/// a permutation of `{0}` or `{2}`. Post-lift the well-formedness
5700/// check at the top of the compound helper's delegation chain
5701/// catches the SET-side drift at const-eval time with a message
5702/// routing operator attention to the CALLER'S SPEC rather than to
5703/// a downstream symptom.
5704///
5705/// Panic-message provenance: the axis-name string
5706/// `"SET-NOT-PAIRWISE-DISTINCT"` is chosen DISTINCT from every
5707/// sibling helper's axis-provenance vocabulary (`"duplicate"` on
5708/// [`assert_u8_array_pairwise_distinct`]'s ARRAY-side sweep;
5709/// `"OUT-OF-RANGE"` / `"MISSING"` on
5710/// [`assert_u8_array_covers_inclusive_range`];
5711/// `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on
5712/// [`assert_u8_array_covers_finite_set`];
5713/// `"ARITY-MISMATCH"` on the two compound `_permutes_*` helpers)
5714/// so a diagnostic that names the failed axis routes UNAMBIGUOUSLY
5715/// to (a) this specific SET-side well-formedness helper, and (b)
5716/// the CALLER'S TARGET-SET SPEC as the drift site rather than the
5717/// downstream `arr` under verification. The two-element prefix
5718/// `"SET-"` is shared with the sibling covers-finite-set arm's
5719/// `"SET-BYTE-MISSING"` — both are SET-side drifts, disambiguated
5720/// by the trailing `"NOT-PAIRWISE-DISTINCT"` vs. `"BYTE-MISSING"`
5721/// noun to disambiguate the specific SET-side axis.
5722///
5723/// Adding a new family-wide `[u8; N]` finite-set-covering array to
5724/// the substrate: no new call site needed at this level — every
5725/// invocation of [`assert_u8_array_covers_finite_set`] or
5726/// [`assert_u8_array_permutes_finite_set`] delegates through this
5727/// helper as its FIRST arm, so the well-formedness contract on the
5728/// caller-provided target-set spec binds at compile time as a side-
5729/// effect of the primary covers/permutes contract. Callers CAN also
5730/// invoke this helper directly at runtime to verify a dynamically-
5731/// constructed target-set spec's well-formedness before passing it
5732/// into the covers/permutes helpers — pinned by the negative
5733/// runtime pins (`_panics_at_runtime_on_binary_collision`,
5734/// `_panics_at_runtime_on_non_adjacent_collision`,
5735/// `_panics_at_runtime_on_terminal_collision`).
5736///
5737/// Theory grounding:
5738/// - THEORY.md §V.1 — knowable platform; the caller-side target-set
5739/// well-formedness contract becomes a TYPE-LEVEL theorem the
5740/// substrate carries per call site rather than a docstring-level
5741/// responsibility the developer must remember to keep in lockstep
5742/// with every downstream covers/permutes invocation.
5743/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
5744/// set-well-formedness proof at the CALLER's site AND the covers/
5745/// permutes helpers' downstream pigeonhole arguments regenerate
5746/// through the SAME `const _` witness at each call site.
5747/// - THEORY.md §VI.1 — generation over composition; the const-eval
5748/// set-pairwise-distinct sweep IS the generative shape. Every
5749/// downstream call to `assert_u8_array_covers_finite_set` or
5750/// `assert_u8_array_permutes_finite_set` now composes the set-
5751/// well-formedness precondition through the delegation chain
5752/// rather than the developer re-deriving a per-call-site
5753/// pairwise-distinct sweep on the target-set literal.
5754///
5755/// Compound sibling posture to the constituent
5756/// [`assert_u8_array_pairwise_distinct`] on the (verifier-target-
5757/// side) axis: the two helpers close the pairwise-distinctness axis
5758/// on the TWO complementary sides of the finite-set-coverage
5759/// compound tier — ARRAY side (`arr` in the covers/permutes
5760/// helpers) and SET side (`set` in the covers/permutes helpers).
5761/// Every intentionally-closed finite-set-covering `[u8; N]` array
5762/// on the substrate now binds pairwise-distinctness on BOTH the
5763/// ARRAY under verification AND the caller-provided target-set
5764/// spec at compile time.
5765pub const fn assert_u8_finite_set_pairwise_distinct<const M: usize>(set: &[u8; M]) {
5766 let mut i = 0;
5767 while i < M {
5768 let mut j = i + 1;
5769 while j < M {
5770 if set[i] == set[j] {
5771 panic!(
5772 "assert_u8_finite_set_pairwise_distinct: SET-NOT-\
5773 PAIRWISE-DISTINCT — the caller-provided target \
5774 FINITE-SET spec `set` for a downstream `assert_u8_\
5775 array_covers_finite_set` / `assert_u8_array_\
5776 permutes_finite_set` invocation carries a DUPLICATE \
5777 entry across two positions. A mathematical finite \
5778 set carries each element at most once, so a `[u8; \
5779 M]` literal with `M` positions but fewer than `M` \
5780 DISTINCT values is not a well-formed finite set of \
5781 cardinality `M` — every downstream pigeonhole \
5782 argument (`N == M` arity check in the permutation \
5783 helper; SET-BYTE-MISSING coverage arm in the \
5784 covers helper) gets a PHANTOM-`M` inflating the \
5785 effective cardinality and silently mis-verifies \
5786 the intended finite-set-coverage / permutation \
5787 contract. Fix at the SET-DECLARATION site (the \
5788 `&[...]` literal passed as the `set` argument, NOT \
5789 the `arr` argument being verified) by removing the \
5790 duplicate byte from the target-set spec"
5791 );
5792 }
5793 j += 1;
5794 }
5795 i += 1;
5796 }
5797}
5798
5799/// Compile-time contract verifier — panics at const evaluation time if
5800/// any entry of `arr` is NOT a member of `set` on the substrate's `u8`
5801/// cache-key vocabulary. Binds ONE conjunct clause at ONE `const _`
5802/// line:
5803///
5804/// * SUBSET-VIOLATION: every entry in `arr` appears in `set` — the
5805/// array's distinct-value set is a SUBSET of the target finite
5806/// partition `set`. A regression that drifts ONE entry to a byte
5807/// NOT in the set (e.g. lifts a fresh `7u8` entry into
5808/// `UnquoteForm::HASH_DISCRIMINATORS`, drifting the two-of-four
5809/// substitution-subset carving OUTSIDE its parent superset
5810/// `QuoteForm::HASH_DISCRIMINATORS = [3, 4, 5, 6]`) fails-loudly
5811/// at const-eval.
5812///
5813/// SET-side well-formedness delegation: [`assert_u8_finite_set_pairwise_distinct`]
5814/// is called at the top of the helper — a malformed `set` (e.g.
5815/// `[0u8, 2u8, 2u8]`) is not a well-formed finite set of cardinality
5816/// `M` and would silently mis-verify the intended subset contract on
5817/// any `arr` embedded in the DISTINCT-value subset. Placed FIRST so
5818/// drift on the CALLER'S TARGET-SET SPEC routes to the SET-side
5819/// well-formedness axis rather than to a downstream SUBSET-VIOLATION
5820/// symptom on `arr`. A well-formed `set` passes this arm as a no-op —
5821/// const-eval-elidable at rustc-time on the substrate call site.
5822///
5823/// Contract-strength peer to [`assert_u8_array_covers_finite_set`] on
5824/// the (equality-vs-subset) axis: where `_covers_finite_set` binds
5825/// arr's distinct-value set EQUALS `set` (arr's entries ⊆ set AND
5826/// set ⊆ arr's entries — the OUT-OF-SET arm ∧ the SET-BYTE-MISSING
5827/// arm), this helper binds ONLY arr ⊆ set (the OUT-OF-SET arm read
5828/// in isolation without the SET-BYTE-MISSING arm) — a strictly
5829/// WEAKER contract for arrays that intentionally cover only a
5830/// PROPER SUBSET of the target partition rather than the whole
5831/// partition. The RANGE analog of this SUBSET-only helper is the
5832/// RANGE-BOUND arm of [`assert_u8_array_covers_inclusive_range`]
5833/// read in isolation (arr ⊆ `[LO..=HI]` without the FULL-COVERAGE
5834/// clause) — no substrate call site currently exercises the range
5835/// analog on its own because every substrate range-family array
5836/// either fully covers its assigned range (the three permutation-
5837/// shaped `_permutes_inclusive_range` arrays: `AtomKind`,
5838/// `QuoteForm`, `UnquoteForm`) or fully covers it non-injectively
5839/// (`SexpShape::HASH_DISCRIMINATORS`). The finite-set analog of this
5840/// SUBSET-only helper is exactly the primitive this lift adds.
5841///
5842/// The invariant is load-bearing for the outer-`Sexp` cache-key
5843/// algebra's SUBSTITUTION-SUBSET embedding.
5844/// [`crate::error::UnquoteForm::HASH_DISCRIMINATORS`] (`[u8; 2]` —
5845/// the two-of-four substitution-subset carving covering `Unquote`
5846/// / `UnquoteSplice`) MUST be a SUBSET of
5847/// [`QuoteForm::HASH_DISCRIMINATORS`] (`[u8; 4]` — the four-arm
5848/// quote-family superset carving covering `Quote` / `Quasiquote` /
5849/// `Unquote` / `UnquoteSplice`). Pre-lift the subset embedding was
5850/// pinned ONLY at runtime via
5851/// `unquote_form_per_role_hash_discriminators_alias_quote_form_per_role_hash_discriminators_byte_for_byte`
5852/// (in `error.rs`, checking per-role scalar byte-equality between
5853/// the two `pub(crate) const UnquoteForm::*_HASH_DISCRIMINATOR`
5854/// constants and their `QuoteForm::*_HASH_DISCRIMINATOR`
5855/// namesakes) — the theorem held only after `cargo test` scheduled
5856/// the pin. Post-lift the ARRAY-LEVEL subset containment binds at
5857/// rustc time — a regression that re-inlined either UnquoteForm
5858/// per-role alias to a fresh literal byte NOT in the QuoteForm
5859/// superset (e.g. `7u8`) fails at `cargo check` BEFORE any test
5860/// scheduler runs. The runtime per-role scalar alias-chain pin
5861/// survives as a sibling check (a distinct failure mode: a drift
5862/// that KEPT the byte in the superset but ROUTED the alias to the
5863/// WRONG QuoteForm arm still passes the ARRAY-level subset check
5864/// but fails the scalar per-role pin) — together the two pins
5865/// bind the substitution-subset embedding at TWO stages of the
5866/// toolchain, const-time on the ARRAYS and test-time on the per-
5867/// role scalar aliases.
5868///
5869/// Every future family-wide `[u8; N]` typed-subset carving on the
5870/// substrate's closed-set outer algebras (a hypothetical
5871/// name-punctuation-subset of a keyword vocabulary, a strict
5872/// numeric-vs-string atomic-payload subset of `AtomKind`, or any
5873/// further sub-carving of the `{0..=6}` outer-`Sexp` cache-key
5874/// partition) participates in the SAME compile-time guarantee via
5875/// one `const _` line.
5876///
5877/// Adding a new family-wide `[u8; N]` subset-embedded array to the
5878/// substrate: pair the declaration with `const _: () =
5879/// assert_u8_array_within_u8_finite_set::<N, M>(&Self::FOO_ARRAY,
5880/// &Other::SUPERSET_ARRAY);` co-located after the array's
5881/// declaration and the SUBSET contract binds at compile time. The
5882/// rustc-forced arities `[u8; N]` and `[u8; M]` compose with this
5883/// const-eval sweep so BOTH cardinality-pair AND every-entry-in-
5884/// superset are compile-time theorems on the SAME (subset, superset)
5885/// array pair. Prefer [`assert_u8_array_covers_finite_set`] when
5886/// the array's distinct-value set is intentionally EQUAL to the
5887/// target partition; this helper is for the strictly-weaker SUBSET
5888/// corner where `arr` covers only a PROPER SUBSET of `set`.
5889///
5890/// Runtime callability: the function is a normal `pub const fn`, so
5891/// callers CAN also invoke it at runtime — pinned by
5892/// `assert_u8_array_within_u8_finite_set_panics_at_runtime_on_out_of_set_entry`
5893/// and
5894/// `assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis`.
5895/// The panic site carries the `"SUBSET-VIOLATION"` axis-provenance
5896/// string chosen DISTINCT from every sibling helper's axis
5897/// vocabulary (`"duplicate"` on the ARRAY-side pairwise-distinct
5898/// sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the
5899/// covers-finite-set sibling; `"OUT-OF-RANGE"` / `"MISSING"` on
5900/// the covers-inclusive-range sibling; `"ARITY-MISMATCH"` on both
5901/// `_permutes_*` compound helpers; `"SET-NOT-PAIRWISE-DISTINCT"`
5902/// on the SET-side well-formedness sibling) so a diagnostic that
5903/// names the failed axis routes UNAMBIGUOUSLY to THIS specific
5904/// SUBSET-embedding helper.
5905///
5906/// Theory grounding:
5907/// - THEORY.md §V.1 — knowable platform; the family-wide subset-
5908/// embedding contract on the `u8` cache-key vocabulary becomes a
5909/// TYPE-LEVEL theorem the substrate carries per (subset, superset)
5910/// array pair rather than a runtime test the developer must
5911/// remember to write per embedding.
5912/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
5913/// cache-key partition is the `intent_hash` composition axis —
5914/// binding the subset-embedding arrays on the typed algebra
5915/// makes a substitution-subset drift outside the parent superset
5916/// a compile error rather than a silent BLAKE3 mis-hash on any
5917/// `Expander::cache` consumer keyed on the subset carving.
5918/// - THEORY.md §VI.1 — generation over composition; the const-eval
5919/// set-membership-only sweep IS the generative shape. Every new
5920/// closed-set discriminator array whose distinct-value set is an
5921/// intentional SUBSET of another substrate array adds ONE
5922/// `const _` line to get the subset-embedding theorem rather
5923/// than re-deriving a per-embedding runtime iterator sweep at
5924/// each call site.
5925/// - THEORY.md §II.1 invariant 5 — composition preserves proofs;
5926/// the SUBSET-embedding proof at declaration site AND the per-
5927/// role scalar alias-chain composition through
5928/// `UnquoteForm::V_HASH_DISCRIMINATOR = QuoteForm::V_HASH_DISCRIMINATOR`
5929/// regenerate through the SAME `const _` witness at the ARRAY
5930/// level.
5931///
5932/// Frontier inspiration: Lean 4's `Finset.instHasSubsetFinset :
5933/// (⊆) : Finset α → Finset α → Prop` as a decidable relation on
5934/// `Finset α` combined with `Finset.subset_iff` unfolding the
5935/// relation to per-element membership — the substrate primitive
5936/// here embeds the same subset relation as a rustc const-eval-time
5937/// proof obligation at every `assert_u8_array_within_u8_finite_set`
5938/// call site rather than as a Lean tactic invocation deferred to
5939/// `elab_command`. TLA+'s `S \subseteq T` first-class relation on
5940/// specification sets composes similarly at TLC model-checking
5941/// time. Coq's `Ensembles.Included : Ensemble U -> Ensemble U ->
5942/// Prop` capturing the same per-element containment predicate.
5943/// Translation through pleme-io primitives: the SUBSET-embedding
5944/// predicate binds through ONE forward sweep (`arr[i] in set`) at
5945/// const-eval time on a rustc-forced-arity `[u8; N]` × `[u8; M]`
5946/// pair — no `Ord` / `Eq` / `Hash` supertrait bound, no
5947/// `HashSet`-shape carrier, no allocation. The delegation-first
5948/// ordering (SET-well-formedness before SUBSET-VIOLATION) matches
5949/// Lean's `Finset`-forward reasoning: prove the SET is well-formed
5950/// AS a finite set, then reason about arrays embedded in it.
5951pub const fn assert_u8_array_within_u8_finite_set<const N: usize, const M: usize>(
5952 arr: &[u8; N],
5953 set: &[u8; M],
5954) {
5955 // Delegate target-set well-formedness to the sibling SET-side
5956 // pairwise-distinctness helper FIRST. Placed BEFORE the SUBSET-
5957 // VIOLATION sweep below because a malformed `set` (e.g. `[0u8,
5958 // 2u8, 2u8]`) is not a well-formed finite set of cardinality
5959 // `M` and silently mis-verifies the intended subset contract on
5960 // any `arr` embedded in the DISTINCT-value subset. Routes drift
5961 // on the CALLER'S TARGET-SET SPEC to the SET-side well-formedness
5962 // axis rather than to a downstream SUBSET-VIOLATION symptom on
5963 // `arr`. A well-formed `set` passes this arm as a no-op — the
5964 // sweep is const-eval-elidable and costs zero at rustc-time on
5965 // the one substrate call site.
5966 assert_u8_finite_set_pairwise_distinct(set);
5967 let mut i = 0;
5968 while i < N {
5969 let mut j = 0;
5970 let mut found = false;
5971 while j < M {
5972 if arr[i] == set[j] {
5973 found = true;
5974 break;
5975 }
5976 j += 1;
5977 }
5978 if !found {
5979 panic!(
5980 "assert_u8_array_within_u8_finite_set: SUBSET-\
5981 VIOLATION — the family-wide u8 array `arr` carries \
5982 an entry at some position whose byte is NOT a \
5983 member of the target finite superset partition \
5984 `set`. The substrate's SUBSET-EMBEDDING contract \
5985 on the array is broken; every consumer that \
5986 expects the array's distinct-value set to be a \
5987 subset of the target finite partition \
5988 (`UnquoteForm::HASH_DISCRIMINATORS ⊂ \
5989 QuoteForm::HASH_DISCRIMINATORS` on the outer-\
5990 `Sexp` cache-key substitution-subset carving; any \
5991 future typed-subset embedding on the substrate's \
5992 closed-set outer algebras) relies on every array \
5993 entry staying within the target superset. Fix at \
5994 the ARRAY-DECLARATION site (the `arr` under \
5995 verification, NOT the `set` argument specifying \
5996 the target superset) by dropping the offending \
5997 entry OR by extending `set` to cover it — the \
5998 choice depends on whether the drift is an \
5999 unintended overshoot outside the parent superset \
6000 or an intentional extension of the superset \
6001 vocabulary"
6002 );
6003 }
6004 i += 1;
6005 }
6006}
6007
6008// Compile-time SUBSET-embedding witness — the ONE family-wide
6009// `[u8; N]` HASH_DISCRIMINATORS array on the substrate whose
6010// distinct-value set is an intentionally-closed PROPER SUBSET of
6011// another substrate array's distinct-value set:
6012// `UnquoteForm::HASH_DISCRIMINATORS` (`[u8; 2]` = `[5, 6]`, the
6013// two-of-four substitution-subset carving covering
6014// `Unquote` / `UnquoteSplice`) MUST be a SUBSET of
6015// `QuoteForm::HASH_DISCRIMINATORS` (`[u8; 4]` = `[3, 4, 5, 6]`, the
6016// four-arm quote-family superset carving covering
6017// `Quote` / `Quasiquote` / `Unquote` / `UnquoteSplice`). Pre-lift
6018// the subset embedding was pinned ONLY at runtime via the per-role
6019// alias-chain check
6020// `unquote_form_per_role_hash_discriminators_alias_quote_form_per_role_hash_discriminators_byte_for_byte`
6021// (in `error.rs`, checking scalar-per-role byte-equality between
6022// the two `UnquoteForm::*_HASH_DISCRIMINATOR` constants and their
6023// `QuoteForm::*_HASH_DISCRIMINATOR` namesakes). Post-lift the
6024// ARRAY-LEVEL subset embedding binds at rustc time via ONE
6025// `const _` witness on the two arrays directly; a drift that
6026// re-inlined either UnquoteForm alias to a fresh literal byte NOT
6027// in the QuoteForm superset (e.g. `7u8`) would fail at `cargo
6028// check` BEFORE any test scheduler runs. The runtime per-role
6029// alias-chain pin survives as a sibling check for the scalar
6030// aliases (a distinct failure mode: a drift that KEPT the byte in
6031// the superset but ROUTED the alias to the WRONG QuoteForm arm
6032// still passes the ARRAY-level subset check but fails the scalar
6033// per-role pin); together with this const witness the
6034// substitution-subset embedding theorem is enforced at BOTH stages
6035// of the toolchain — const-time on the ARRAYS, test-time on the
6036// scalar per-role aliases.
6037const _: () = assert_u8_array_within_u8_finite_set::<2, 4>(
6038 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
6039 &QuoteForm::HASH_DISCRIMINATORS,
6040);
6041
6042// Compile-time TIGHTENING witness — the (u8)-row analog of the
6043// (str)-row three-witness SUB-AS-SLICE cluster in `error.rs`
6044// (`assert_str_array_slice_equals_str_array::<4, 2, 2>` on the
6045// `UnquoteForm::{LABELS, MARKERS, IAC_FORGE_TAGS} ⊂
6046// QuoteForm::{LABELS, PREFIXES, IAC_FORGE_TAGS}` sub-carve). The
6047// pre-existing SUBSET-embedding witness above binds `UnquoteForm::
6048// HASH_DISCRIMINATORS ⊂ QuoteForm::HASH_DISCRIMINATORS` at the SET
6049// level (`{5, 6} ⊂ {3, 4, 5, 6}`); this witness tightens the
6050// binding to POSITIONWISE SLICE-EQUALS on the SAME sub-carve —
6051// `QuoteForm::HASH_DISCRIMINATORS[2..4] == UnquoteForm::HASH_
6052// DISCRIMINATORS[..]` byte-for-byte in canonical slot order.
6053//
6054// Two failure axes survive the SET-level SUBSET witness above that
6055// this POSITIONWISE witness rejects at rustc time:
6056// 1. Sub-array SLOT REORDER: a regression that swaps
6057// `UnquoteForm::HASH_DISCRIMINATORS` from `[UNQUOTE_HASH_
6058// DISCRIMINATOR, SPLICE_HASH_DISCRIMINATOR]` (`[5, 6]`) to
6059// `[SPLICE_HASH_DISCRIMINATOR, UNQUOTE_HASH_DISCRIMINATOR]`
6060// (`[6, 5]`) preserves the SUBSET witness (both bytes still
6061// appear in the superset) — but silently misaligns every
6062// consumer indexing the sub-array by slot ordinal (the
6063// `UnquoteForm::hash_discriminator` per-variant projection at
6064// `error.rs`'s `UnquoteForm` inherent impl, the runtime
6065// partition test
6066// `unquote_form_hash_discriminators_align_with_quote_form_
6067// hash_discriminators_by_projection`).
6068// 2. Superset SLOT REORDER that leaves sub-array bytes still
6069// present but at different positions: a regression that
6070// reorders `QuoteForm::HASH_DISCRIMINATORS` from `[3, 4, 5,
6071// 6]` to any permutation that leaves `{5, 6}` non-contiguous
6072// or non-tail (e.g. `[5, 3, 6, 4]`) preserves the SUBSET
6073// witness (both sub-array bytes still appear in the superset,
6074// just at scattered non-`[2..4)` positions) — but the sub-
6075// carving no longer sits at the tail of the superset's four-
6076// arm declaration listing, breaking the compositional
6077// invariant that `QuoteForm::HASH_DISCRIMINATORS[2..4] ==
6078// UnquoteForm::HASH_DISCRIMINATORS` presumes.
6079//
6080// Sibling posture: the (str)-row tightening cluster at `error.rs`
6081// (three `assert_str_array_slice_equals_str_array::<4, 2, 2>`
6082// witnesses on the `LABELS` / `MARKERS↔PREFIXES` / `IAC_FORGE_TAGS`
6083// vocabulary triple) closes the SLICE-EQUALS column on the SAME
6084// (UnquoteForm ⊂ QuoteForm) 2-of-4 sub-carve at the STR element-
6085// type row; this witness closes the SAME column at the U8 element-
6086// type row. Together the four-witness cluster (three str + one u8)
6087// exhausts the (element-type × vocabulary-axis) matrix of the
6088// substitution-subset carving at the SLICE-EQUALS positionwise-
6089// composition contract.
6090//
6091// Composition with the sibling FULL-ARRAY LITERAL witnesses at
6092// lines 6961..=6976 below: those two witnesses independently pin
6093// `QuoteForm::HASH_DISCRIMINATORS == [3u8, 4, 5, 6]` and
6094// `UnquoteForm::HASH_DISCRIMINATORS == [5u8, 6]` against their
6095// literal byte listings. This SLICE-EQUALS witness composes with
6096// them — a drift on EITHER array's literal listing fails first at
6097// the peer FULL-ARRAY witness; a drift on the sub-carving's
6098// contiguous-tail placement inside the superset that leaves BOTH
6099// literal listings intact (e.g. a fifth quote-family variant
6100// landing at an interior position of `QuoteForm::HASH_DISCRIMINATORS`
6101// shifting UNQUOTE/UNQUOTE_SPLICE off `[2..4)`) fails HERE at the
6102// SUB-AS-SLICE witness. Sibling to the JOINT permutation witness
6103// `assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4,
6104// 0, 6>` at line 7029 — that witness binds the outer partition
6105// `{0..=6} = {OUTER} ⊕ StructuralKind::HASH_DISCRIMINATORS ⊕
6106// QuoteForm::HASH_DISCRIMINATORS` as a bijection; this witness
6107// binds the sub-carving `UnquoteForm::HASH_DISCRIMINATORS` to a
6108// SPECIFIC contiguous slice of that quote-family carving.
6109const _: () = assert_u8_array_slice_equals_u8_array::<4, 2, 2>(
6110 &QuoteForm::HASH_DISCRIMINATORS,
6111 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
6112);
6113
6114/// Compile-time contract verifier — panics at const evaluation time if
6115/// the distinct-values sets of `a` and `b` share any byte on the
6116/// substrate's `u8` cache-key vocabulary. Binds ONE conjunct clause:
6117/// U8-DISJOINTNESS-VIOLATION — no entry in `a` may appear as an entry
6118/// in `b` (symmetric across the two array arguments).
6119///
6120/// Row-dual peer to [`assert_char_arrays_disjoint`] on the (element-
6121/// type) axis of the (subset, disjointness) 2-corner face of the
6122/// (contract-shape) axis: where the (char) sibling closes the reader-
6123/// boundary `char` disjointness corner at compile time, this (u8)
6124/// sibling closes the outer-`Sexp` cache-key `u8` disjointness corner
6125/// on the SAME element-type row. Together the two helpers close the
6126/// (element-type × contract-shape) 2×2 = 4-corner face at ONE peer
6127/// const-fn helper per corner rather than at a per-pair runtime
6128/// iterator sweep per call site. Contract-orthogonal peer to
6129/// [`assert_u8_array_within_u8_finite_set`] on the (subset-vs-
6130/// disjointness) axis of the (contract-shape) axis on the SAME (u8)
6131/// row: where the SUBSET-EMBEDDING sibling binds `arr ⊆ set`, this
6132/// DISJOINTNESS sibling binds `a ∩ b = ∅` on the SAME element type.
6133///
6134/// The disjointness relation is SYMMETRIC (unlike the SUBSET-EMBEDDING
6135/// relation, which distinguishes `arr` from `set`) so the two
6136/// arguments carry NO axis-provenance role split — either drift site
6137/// (a byte in `a` that aliases a byte in `b`, OR a byte in `b` that
6138/// aliases a byte in `a`) surfaces at the U8-DISJOINTNESS-VIOLATION
6139/// panic with the OFFENDING arm named by BOTH position indices
6140/// (`a[i]` AND `b[j]`) rather than by only one side of the pair.
6141///
6142/// SYMMETRY IN THE NESTED SWEEP: the inner `while j < M` sweep visits
6143/// every position of `b` per outer `i` and panics at the FIRST
6144/// cross-array collision (`a[i] == b[j]`). Because the relation is
6145/// symmetric and the two-loop sweep visits every `(i, j) ∈ [0, N) ×
6146/// [0, M)` pair, swapping `a` and `b` at the call site produces the
6147/// SAME verdict — the helper does NOT gratuitously depend on argument
6148/// order. A future call site that intends to name a SPECIFIC drift
6149/// side can still route its own preferred first-mention by picking
6150/// the argument order that serves its diagnostic story; the helper
6151/// itself carries no such preference.
6152///
6153/// The invariant is load-bearing for the outer-`Sexp` cache-key
6154/// algebra's JOINT PARTITION contract at [`Hash for Sexp`]: the
6155/// outer-`Sexp` discriminator space `{0..=6}` MUST partition across
6156/// the three closed-set sub-carvings ([`crate::error::StructuralKind::HASH_DISCRIMINATORS`]
6157/// at `{0, 2}`, [`AtomKind::OUTER_HASH_DISCRIMINATOR`] scalar at `{1}`,
6158/// [`QuoteForm::HASH_DISCRIMINATORS`] at `{3, 4, 5, 6}`) with NO
6159/// overlap — otherwise two structurally-distinct outer-`Sexp` variants
6160/// would silently mis-hash through the same cache-key byte and the
6161/// substrate's `Expander::cache` would leak macro-expansion hits
6162/// across carvings. Pre-lift the substrate carried these array-vs-
6163/// array disjointness relations at runtime tests
6164/// (`structural_kind_hash_discriminator_disjoint_from_atom_outer_carve_byte_and_quote_form_hash_discriminator_partition`
6165/// on the (StructuralKind, QuoteForm) pair;
6166/// `unquote_form_hash_discriminator_partitions_disjointly_from_non_substitution_carvings`
6167/// on the (UnquoteForm, StructuralKind) pair); post-lift the ARRAY-
6168/// LEVEL disjointness of the pinned pairs binds at rustc time via one
6169/// `const _` line per pair. A regression that silently drifted
6170/// `StructuralKind::LIST_HASH_DISCRIMINATOR` from `2u8` to `3u8`
6171/// (colliding with `QuoteForm::QUOTE_HASH_DISCRIMINATOR`), or drifted
6172/// `QuoteForm::QUOTE_HASH_DISCRIMINATOR` from `3u8` to `0u8`
6173/// (colliding with `StructuralKind::NIL_HASH_DISCRIMINATOR`), fails at
6174/// `cargo check` BEFORE any test scheduler runs. Sibling to the
6175/// pairwise-distinctness witnesses above — those pin INJECTIVITY on
6176/// each individual `[u8; N]` HASH_DISCRIMINATORS array, this pins
6177/// DISJOINTNESS across PAIRS of arrays on the SAME outer-`Sexp`
6178/// cache-key byte space.
6179///
6180/// Adding a new family-wide `[u8; N]` sub-vocabulary whose distinct-
6181/// values set must remain disjoint from another substrate `[u8; M]`
6182/// array's distinct-values set: pair the declaration with `const _:
6183/// () = assert_u8_arrays_disjoint::<N, M>(&Self::FOO_ARRAY,
6184/// &Other::BAR_ARRAY);` co-located after the array's declaration and
6185/// the DISJOINTNESS contract binds at compile time. The rustc-forced
6186/// arities `[u8; N]` and `[u8; M]` compose with this const-eval
6187/// sweep so BOTH cardinality-pair AND cross-array disjointness are
6188/// compile-time theorems on the SAME (a, b) u8-array pair.
6189///
6190/// Runtime callability: the function is a normal `pub const fn`, so
6191/// callers CAN also invoke it at runtime — pinned by
6192/// `assert_u8_arrays_disjoint_panics_at_runtime_on_collision` and
6193/// `assert_u8_arrays_disjoint_panic_message_names_the_helper_and_u8_disjointness_violation_axis`.
6194/// The panic site carries the `"U8-DISJOINTNESS-VIOLATION"` axis-
6195/// provenance string chosen DISTINCT from every sibling helper's axis
6196/// vocabulary (`"duplicate"` on the ARRAY-side pairwise-distinct
6197/// sibling; `"CHAR-DISJOINTNESS-VIOLATION"` on the (char) row-dual
6198/// DISJOINTNESS sibling; `"CHAR-SUBSET-VIOLATION"` on the (char)
6199/// SUBSET-embedding sibling; `"SUBSET-VIOLATION"` on the (u8) finite-
6200/// set SUBSET-only sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8)
6201/// range SUBSET-only sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"`
6202/// on the (u8) covers-finite-set sibling; `"OUT-OF-RANGE"` /
6203/// `"MISSING"` on the (u8) covers-inclusive-range sibling;
6204/// `"ARITY-MISMATCH"` on both (u8) `_permutes_*` compound helpers;
6205/// `"SET-NOT-PAIRWISE-DISTINCT"` on the (u8) SET-side well-formedness
6206/// sibling) so a diagnostic that names the failed axis routes
6207/// UNAMBIGUOUSLY to THIS specific u8 DISJOINTNESS helper. The `"U8-"`
6208/// prefix disambiguates from the (char) DISJOINTNESS sibling; the
6209/// shared `"-VIOLATION"` suffix lets callers grep either row's
6210/// DISJOINTNESS or SUBSET-embedding sibling by `"VIOLATION"` alone or
6211/// route to the specific contract-shape by the axis stem
6212/// (`"DISJOINTNESS"` vs `"SUBSET"`).
6213///
6214/// Theory grounding:
6215/// - THEORY.md §V.1 — knowable platform; the family-wide cross-array
6216/// disjointness contract on the outer-`Sexp` cache-key `u8`
6217/// vocabulary becomes a TYPE-LEVEL theorem the substrate carries
6218/// per (a, b) u8-array pair rather than a runtime test the
6219/// developer must remember to write per pair.
6220/// - THEORY.md §III — the typescape; the (element-type × contract-
6221/// shape) 2×2 = 4-corner matrix is now closed at ONE peer const-fn
6222/// helper per corner — [`assert_char_array_within_char_finite_set`]
6223/// on the (char, subset) corner, [`assert_char_arrays_disjoint`] on
6224/// the (char, disjointness) corner,
6225/// [`assert_u8_array_within_u8_finite_set`] on the (u8, subset)
6226/// corner, and this helper on the (u8, disjointness) corner.
6227/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
6228/// cache-key partition is the substrate's `intent_hash` composition
6229/// axis — binding the ARRAY-vs-ARRAY disjointness pairs at compile
6230/// time makes a joint-partition-overlap drift a compile error
6231/// rather than a silent BLAKE3 mis-hash on any `Expander::cache` or
6232/// `Sekiban` audit-trail metric keyed on the outer discriminator
6233/// space.
6234/// - THEORY.md §VI.1 — generation over composition; the const-eval
6235/// cross-array membership sweep IS the generative shape. Every new
6236/// closed-set `u8` sub-vocabulary array whose distinct-values set is
6237/// an intentionally-disjoint peer of another substrate `u8` array
6238/// adds ONE `const _` line to get the disjointness theorem rather
6239/// than re-deriving a per-pair runtime iterator sweep at each call
6240/// site.
6241/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
6242/// DISJOINTNESS proof at declaration site AND the outer-`Sexp`
6243/// cache-key JOINT PARTITION contract regenerate through the SAME
6244/// `const _` witnesses at the ARRAY level.
6245///
6246/// Frontier inspiration: Lean 4's `Finset.Disjoint : Finset α →
6247/// Finset α → Prop` as a decidable relation on `Finset α` combined
6248/// with `Finset.disjoint_iff_ne`'s characterisation `∀ a ∈ s, ∀ b ∈
6249/// t, a ≠ b` — this substrate primitive embeds the same disjointness
6250/// relation as a rustc const-eval-time proof obligation at every
6251/// `assert_u8_arrays_disjoint` call site rather than as a Lean tactic
6252/// invocation deferred to `elab_command`. Coq's `Ensembles.Disjoint :
6253/// Ensemble U -> Ensemble U -> Prop` captures the same relation as a
6254/// Prop-level predicate. Translation through pleme-io primitives: the
6255/// DISJOINTNESS predicate binds through the same nested `(i, j)`
6256/// sweep as the (char) row-dual peer, monomorphised to the concrete
6257/// `[u8; N] × [u8; M]` element-type realisation — no `Ord` / `Eq` /
6258/// `Hash` supertrait bound, no `HashSet`-shape carrier, no allocation.
6259pub const fn assert_u8_arrays_disjoint<const N: usize, const M: usize>(a: &[u8; N], b: &[u8; M]) {
6260 let mut i = 0;
6261 while i < N {
6262 let mut j = 0;
6263 while j < M {
6264 if a[i] == b[j] {
6265 panic!(
6266 "assert_u8_arrays_disjoint: U8-DISJOINTNESS-\
6267 VIOLATION — the two family-wide u8 arrays `a` \
6268 and `b` share an entry at some (i, j) position \
6269 pair. The substrate's CROSS-ARRAY DISJOINTNESS \
6270 contract on the pair is broken; every consumer \
6271 that partitions the two arrays' distinct-values \
6272 sets into disjoint sub-vocabularies of the outer-\
6273 `Sexp` cache-key `u8` algebra (the outer-`Sexp` \
6274 joint-partition contract at `Hash for Sexp`: \
6275 `StructuralKind::HASH_DISCRIMINATORS` at `{{0, 2}}` \
6276 disjoint from `QuoteForm::HASH_DISCRIMINATORS` at \
6277 `{{3, 4, 5, 6}}` disjoint from `UnquoteForm::HASH_\
6278 DISCRIMINATORS` at `{{5, 6}}` on the non-parent \
6279 carvings; any future typed-disjointness pair on \
6280 the substrate's outer-`Sexp` cache-key `u8` \
6281 algebras) relies on the two arrays' distinct-\
6282 values sets NOT sharing a byte. Fix at WHICHEVER \
6283 ARRAY-DECLARATION site drifted (the symmetric \
6284 disjointness relation carries no built-in axis-\
6285 provenance role split between `a` and `b`) by \
6286 dropping the offending entry from one array OR \
6287 re-shaping the partition to route the shared \
6288 entry to a single sub-vocabulary"
6289 );
6290 }
6291 j += 1;
6292 }
6293 i += 1;
6294 }
6295}
6296
6297// Compile-time DISJOINTNESS witnesses — the TWO substrate-pinned
6298// (a, b) `[u8; N] × [u8; M]` pairs whose distinct-values sets are
6299// intentionally-closed disjoint sub-vocabularies of the outer-`Sexp`
6300// cache-key `u8` algebra. Pre-lift the two disjointness relations
6301// lived only as runtime tests
6302// (`structural_kind_hash_discriminator_disjoint_from_atom_outer_carve_byte_and_quote_form_hash_discriminator_partition`
6303// on pair 1, sweeping `StructuralKind::hash_discriminator`'s `{0, 2}`
6304// against `QuoteForm::hash_discriminator`'s `{3, 4, 5, 6}` through
6305// per-variant `Vec<u8>` collection into a HashSet-shape overlap
6306// check; `unquote_form_hash_discriminator_partitions_disjointly_from_non_substitution_carvings`
6307// on pair 2, sweeping `UnquoteForm::hash_discriminator`'s `{5, 6}`
6308// against `StructuralKind::hash_discriminator`'s `{0, 2}` through the
6309// same HashSet-shape `is_disjoint` check); post-lift the ARRAY-LEVEL
6310// disjointness of the pinned pairs binds at rustc time via one
6311// `const _` line per pair. A regression that silently drifted
6312// `StructuralKind::LIST_HASH_DISCRIMINATOR` from `2u8` to `3u8`
6313// (colliding with `QuoteForm::QUOTE_HASH_DISCRIMINATOR`), or drifted
6314// `QuoteForm::QUOTE_HASH_DISCRIMINATOR` from `3u8` to `0u8`
6315// (colliding with `StructuralKind::NIL_HASH_DISCRIMINATOR`), or
6316// drifted `UnquoteForm::UNQUOTE_HASH_DISCRIMINATOR` from `5u8` to
6317// `2u8` (colliding with `StructuralKind::LIST_HASH_DISCRIMINATOR`)
6318// fails at `cargo check` BEFORE any test scheduler runs.
6319//
6320// Note on pair 2 (`UnquoteForm::HD` vs `StructuralKind::HD`): the
6321// disjointness is FORMALLY IMPLIED by pair 1 (`StructuralKind::HD` vs
6322// `QuoteForm::HD`) composed with the pre-existing SUBSET-embedding
6323// witness `assert_u8_array_within_u8_finite_set::<2, 4>(&UnquoteForm::HD,
6324// &QuoteForm::HD)` (transitivity of subset-through-disjoint: `X ⊂ Y ∧
6325// Y ∩ Z = ∅ ⇒ X ∩ Z = ∅`). Post-lift the direct pair-2 witness pins
6326// the substitution-subset drift mode DIRECTLY at rustc time rather
6327// than through a two-hop const-time inference — a drift that broke
6328// the pair-2 disjointness EITHER by drifting `UnquoteForm::HD` out of
6329// the parent superset OR by drifting `StructuralKind::HD` into the
6330// parent superset surfaces at THIS witness with the exact drifting
6331// arm identified. The redundant witness stays intentional: it mirrors
6332// the runtime pin's structure (a direct-per-pair check rather than a
6333// composed-through-parent check) so the runtime-vs-const witness
6334// pairs stay in one-to-one correspondence and a runtime pin has an
6335// EXACT const-time peer without pair-mapping ambiguity.
6336const _: () = assert_u8_arrays_disjoint::<2, 4>(
6337 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
6338 &QuoteForm::HASH_DISCRIMINATORS,
6339);
6340const _: () = assert_u8_arrays_disjoint::<2, 2>(
6341 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
6342 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
6343);
6344
6345/// Compile-time contract verifier — panics at const evaluation time if
6346/// the distinct-values set of `arr` does not equal exactly the distinct-
6347/// values set of `set` on the substrate's `u8` cache-key vocabulary.
6348/// Binds TWO conjunct clauses at ONE `const _` line:
6349/// 1. OUT-OF-SET: every entry in `arr` appears in `set` — no byte
6350/// outside the target finite partition. A regression that drifts
6351/// ONE entry to a byte NOT in the set (e.g. lifts a fresh entry at
6352/// `3u8` on a `{0, 2}` array) fails-loudly at const-eval.
6353/// 2. SET-BYTE-MISSING: every byte in `set` appears at least once in
6354/// `arr` — no byte inside the target finite partition missing. A
6355/// regression that silently unifies two entries onto ONE byte,
6356/// leaving another set byte unreached (e.g. drops the `Nil` arm's
6357/// `0u8` from `StructuralKind::HASH_DISCRIMINATORS` in favour of
6358/// a redundant `2u8`), fails-loudly at const-eval too.
6359///
6360/// Generalises [`assert_u8_array_covers_inclusive_range`] along the
6361/// (contiguity) axis: where the range helper closes the SURJECTIVITY
6362/// axis at the contiguous corner (the target partition is an inclusive
6363/// `[LO, HI]` range), this finite-set helper closes the SURJECTIVITY
6364/// axis at the arbitrary-finite-set corner (the target partition is
6365/// any `[u8; M]` — contiguous, gapped, singleton, or scattered). The
6366/// contiguous-range helper stays as the tighter idiom for arrays whose
6367/// distinct-value set happens to be a contiguous inclusive range; this
6368/// finite-set helper binds the arrays whose distinct-value set is
6369/// NON-contiguous and therefore cannot be expressed as `[LO..=HI]`.
6370///
6371/// The invariant is load-bearing for the outer-`Sexp` cache-key
6372/// algebra's SPAN across the structural-residual sub-carve.
6373/// [`crate::error::StructuralKind::HASH_DISCRIMINATORS`] (`[u8; 2]`
6374/// covering `{0, 2}` with a gap at `1u8` where the atomic-carve outer
6375/// marker lives) is the archetype non-contiguous case — its
6376/// distinct-value set is a two-element finite set that is NOT a
6377/// contiguous inclusive range, so the range helper cannot bind. This
6378/// finite-set helper binds the structural-residual sub-carve's
6379/// surjectivity contract at compile time despite the intentional
6380/// gap: a regression that drops the `Nil` arm's `0u8` (or the `List`
6381/// arm's `2u8`) — or lifts a fresh `1u8` colliding with the outer-
6382/// carve atomic marker byte — fails the build. Every future family-
6383/// wide `[u8; N]` array whose distinct-value set is an intentionally-
6384/// closed FINITE (possibly non-contiguous) partition participates in
6385/// the SAME compile-time guarantee via one `const _` line.
6386///
6387/// Adding a new family-wide `[u8; N]` finite-set-covering array to
6388/// the substrate: pair the declaration with `const _: () =
6389/// assert_u8_array_covers_finite_set::<N, M>(&Self::FOO_ARRAY,
6390/// &[…the M target-set bytes…]);` co-located after the array's
6391/// declaration and the finite-set-coverage contract binds at compile
6392/// time. The rustc-forced arity `[u8; N]` composes with this
6393/// const-eval sweep so cardinality AND every-entry-in-set AND
6394/// every-set-byte-reached are ALL compile-time theorems on the SAME
6395/// array. Prefer the tighter [`assert_u8_array_covers_inclusive_range`]
6396/// when the target set IS a contiguous inclusive range — this helper
6397/// is the fallback for the non-contiguous corner.
6398///
6399/// Runtime callability: the function is a normal `pub const fn`, so
6400/// callers CAN also invoke it at runtime — pinned by
6401/// `assert_u8_array_covers_finite_set_panics_at_runtime_on_
6402/// out_of_set_entry` + `assert_u8_array_covers_finite_set_panics_at_
6403/// runtime_on_missing_set_byte`. Both panic sites carry axis-provenance
6404/// strings ("OUT-OF-SET" vs. "SET-BYTE-MISSING") so downstream
6405/// diagnostics (`cargo check` const-eval error output, test-suite
6406/// failure reports) route the drift back to the failed axis by string
6407/// search — halving the search space for the operator debugging the
6408/// drift. The two axis-provenance strings are chosen DISTINCT from
6409/// the sibling [`assert_u8_array_covers_inclusive_range`]'s
6410/// ("OUT-OF-RANGE" vs. "MISSING") so a diagnostic that names the
6411/// failed axis routes UNAMBIGUOUSLY to the failed HELPER too.
6412///
6413/// Theory grounding:
6414/// - THEORY.md §V.1 — knowable platform; the family-wide finite-set-
6415/// coverage contract on the `u8` cache-key vocabulary becomes a
6416/// TYPE-LEVEL theorem the substrate carries per array declaration
6417/// rather than a runtime test the developer must remember to write
6418/// per non-contiguous partition.
6419/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
6420/// cache-key partition is the `intent_hash` composition axis —
6421/// binding the finite-set-covering arrays on the typed algebra makes
6422/// attestation-key drift a compile error rather than a silent
6423/// BLAKE3 mis-hash on any consumer keyed on `Hash for Sexp`. A
6424/// regression that drops a byte from the structural-residual
6425/// sub-carve (or drifts an entry into the atomic-carve gap at `1u8`)
6426/// fails the build before it can silently invalidate a cached
6427/// expansion or a Sekiban audit-trail metric keyed on the outer
6428/// discriminator space.
6429/// - THEORY.md §VI.1 — generation over composition; the const-eval
6430/// set-membership + set-coverage sweep IS the generative shape.
6431/// Every new closed-set discriminator array whose distinct-value set
6432/// is an intentionally-closed finite (possibly non-contiguous)
6433/// partition adds ONE `const _` line to get the finite-set-coverage
6434/// theorem rather than re-deriving two per-array runtime iterator
6435/// sweeps (one for the set-membership bound, one for the set-
6436/// coverage completeness).
6437///
6438/// Contiguity-axis sibling posture to
6439/// [`assert_u8_array_covers_inclusive_range`]: the two helpers close
6440/// the SURJECTIVITY axis of the substrate's typed `u8` array
6441/// vocabulary at the TWO complementary corners of the (contiguity)
6442/// axis. Combined with the four pre-existing const-fn contract
6443/// verifiers ([`assert_char_array_pairwise_distinct`],
6444/// [`assert_str_array_pairwise_distinct`],
6445/// [`assert_u8_array_pairwise_distinct`],
6446/// [`assert_char_pair_array_bijective`]) closing the INJECTIVITY axis,
6447/// the substrate now carries a complete `(injectivity, surjectivity)`
6448/// compile-time cache-key contract lattice: every family-wide `[u8; N]`
6449/// discriminator array binds AT LEAST ONE axis, and the four
6450/// intentionally-injective + range-covering arrays
6451/// (`AtomKind`, `QuoteForm`, `UnquoteForm` HASH_DISCRIMINATORS +
6452/// `SexpShape::HASH_DISCRIMINATORS` on the range-only arm) bind BOTH
6453/// axes.
6454pub const fn assert_u8_array_covers_finite_set<const N: usize, const M: usize>(
6455 arr: &[u8; N],
6456 set: &[u8; M],
6457) {
6458 // Delegate target-set well-formedness to the sibling SET-side
6459 // pairwise-distinctness helper FIRST. Placed BEFORE the OUT-OF-SET
6460 // and SET-BYTE-MISSING sweeps below because a malformed `set`
6461 // (e.g. `[0u8, 2u8, 2u8]`) inflates the effective cardinality
6462 // used by the SET-BYTE-MISSING sweep with a PHANTOM-`M` and
6463 // silently mis-verifies the intended finite-set-coverage contract.
6464 // Routes drift on the CALLER'S TARGET-SET SPEC to the SET-side
6465 // well-formedness axis rather than to a downstream OUT-OF-SET /
6466 // SET-BYTE-MISSING symptom on `arr`. A well-formed `set` passes
6467 // this arm as a no-op — the sweep is const-eval-elidable and
6468 // costs zero at rustc-time on a substrate with only one such
6469 // call site.
6470 assert_u8_finite_set_pairwise_distinct(set);
6471 let mut i = 0;
6472 while i < N {
6473 let mut j = 0;
6474 let mut found = false;
6475 while j < M {
6476 if arr[i] == set[j] {
6477 found = true;
6478 break;
6479 }
6480 j += 1;
6481 }
6482 if !found {
6483 panic!(
6484 "assert_u8_array_covers_finite_set: family-wide u8 \
6485 array carries an OUT-OF-SET entry at some position — \
6486 the entry's byte is absent from the target finite \
6487 partition `set`. The substrate's SET-MEMBERSHIP \
6488 contract on the array is broken; every consumer that \
6489 expects the array's entries to partition an outer \
6490 cache-key space (Hash for Sexp's outer discriminator \
6491 space, StructuralKind's non-contiguous sub-carving of \
6492 `{{0, 2}}` around the atomic-carve gap at `1u8`) \
6493 relies on the entries staying within the target set",
6494 );
6495 }
6496 i += 1;
6497 }
6498 let mut j = 0;
6499 while j < M {
6500 let mut k = 0;
6501 let mut found = false;
6502 while k < N {
6503 if arr[k] == set[j] {
6504 found = true;
6505 break;
6506 }
6507 k += 1;
6508 }
6509 if !found {
6510 panic!(
6511 "assert_u8_array_covers_finite_set: family-wide u8 \
6512 array is SET-BYTE-MISSING a byte from the target \
6513 finite partition `set` — every byte in the set must \
6514 appear at least once in the array. The substrate's \
6515 FULL-COVERAGE contract on the array is broken; every \
6516 consumer that expects the array's distinct-value set \
6517 to span the target finite partition (StructuralKind's \
6518 `{{0, 2}}` two-arm sub-carve, any future non-\
6519 contiguous partition-span contract) relies on every \
6520 set byte being reached",
6521 );
6522 }
6523 j += 1;
6524 }
6525}
6526
6527// Compile-time finite-set-coverage on family-wide `[u8; N]` hash-
6528// discriminator arrays no longer surfaces at this level as DIRECT
6529// witnesses — the ONE family-wide `[u8; N]` HASH_DISCRIMINATORS array
6530// whose distinct-value set is an intentionally-closed non-contiguous
6531// finite partition (`StructuralKind::HASH_DISCRIMINATORS` (`[u8; 2]`)
6532// covering `{0, 2}` with the load-bearing gap at `1u8` where the
6533// atomic-carve outer marker byte lives) now binds SURJECTIVITY through
6534// the stronger COMPOUND (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY)
6535// `assert_u8_array_permutes_finite_set` helper defined below, which
6536// delegates through THIS helper for the SET-MEMBERSHIP + FULL-
6537// COVERAGE arms — the helper is now purely a delegation target.
6538// Binding the compound witness makes the (StructuralKind sub-carve,
6539// atomic-carve marker byte) disjointness a compile-time theorem: a
6540// regression that lifted a fresh `1u8` entry into
6541// `StructuralKind::HASH_DISCRIMINATORS` would silently collide with
6542// `AtomKind::OUTER_HASH_DISCRIMINATOR = 1u8` on the outer-`Sexp`
6543// cache-key partition and fail the build at the compound helper's
6544// delegation call to THIS helper's `"OUT-OF-SET"` arm rather than
6545// surfacing as a silent BLAKE3 mis-hash on any consumer keyed on
6546// `Hash for Sexp`.
6547//
6548// Adding a new family-wide `[u8; N]` non-contiguous-finite-set-covering
6549// HASH_DISCRIMINATORS array: prefer the compound
6550// `assert_u8_array_permutes_finite_set` helper below which binds
6551// INJECTIVITY ∧ SURJECTIVITY ∧ ARITY at ONE `const _` line if the
6552// array is a PERMUTATION of the target set; fall back to a DIRECT
6553// `const _: () = assert_u8_array_covers_finite_set::<N, M>(&Self::
6554// FOO_ARRAY, &[…set…]);` witness at this level ONLY for an array with
6555// a LOOSER contract (e.g. an intentionally-non-injective mapping onto
6556// the target set — no substrate array currently exercises this
6557// posture). Sibling to the runtime `_span_*` / `_covers_*` tests at
6558// `error.rs`'s tests module — those enforce the same theorem at
6559// `cargo test` time through direct runtime calls to this helper, so
6560// the theorem is still bound at TWO stages of the toolchain (compile
6561// time through the delegated path inside the compound helper, test
6562// time through the direct runtime calls). Contiguity-axis peer to the
6563// four `assert_u8_array_covers_inclusive_range` witnesses above:
6564// those close the contiguous-range corner of the SURJECTIVITY-only
6565// axis at the four range-covering HASH_DISCRIMINATORS arrays;
6566// `StructuralKind::HASH_DISCRIMINATORS` was the sole array closing the
6567// non-contiguous-finite-set corner of the SURJECTIVITY-only axis, and
6568// now instead binds through the stronger compound helper's
6569// non-contiguous corner.
6570
6571/// Compile-time contract verifier — panics at const evaluation time if
6572/// `arr` is not a PERMUTATION of the inclusive integer range `[LO..=HI]`
6573/// on the substrate's `u8` cache-key vocabulary. Binds THREE conjunct
6574/// clauses at ONE `const _` line:
6575/// 1. ARITY-MISMATCH: `N` MUST equal `HI - LO + 1` — a bijection
6576/// between `[0..N)` and `[LO..=HI]` forces the cardinality equality
6577/// by pigeonhole. A regression that adds a spurious duplicate entry
6578/// to a HASH_DISCRIMINATORS array (bumping `N` past the range's
6579/// cardinality) fails-loudly at this arm with the ARITY-MISMATCH-
6580/// provenance panic message. Delegated to no sibling — this arm is
6581/// the compound helper's unique contribution.
6582/// 2. Range-membership: every entry in `arr` lies in `[LO, HI]` —
6583/// delegated to [`assert_u8_array_covers_inclusive_range`] (which
6584/// ALSO sweeps the FULL-COVERAGE axis; both axes bind through the
6585/// one delegation call).
6586/// 3. Pairwise-distinctness: every pair of entries is distinct —
6587/// delegated to [`assert_u8_array_pairwise_distinct`].
6588///
6589/// (1) ∧ (2) ∧ (3) jointly imply the array's entries EXACTLY partition
6590/// the range: `N` distinct entries chosen from a range of cardinality
6591/// `N` MUST equal the entire range by pigeonhole. The single compound
6592/// invocation therefore binds the same theorem as the pair
6593/// `assert_u8_array_pairwise_distinct(&arr) + assert_u8_array_covers_
6594/// inclusive_range::<N, LO, HI>(&arr)` PLUS the arity-mismatch check
6595/// (1) that neither weak sibling carries alone — a strictly stronger
6596/// contract at HALF the per-array witness-line surface.
6597///
6598/// Compression: pre-lift, each of the three intentionally-permutation-
6599/// shaped HASH_DISCRIMINATORS arrays ([`AtomKind::HASH_DISCRIMINATORS`],
6600/// [`QuoteForm::HASH_DISCRIMINATORS`],
6601/// [`crate::error::UnquoteForm::HASH_DISCRIMINATORS`]) bound its
6602/// permutation contract at TWO `const _` lines (one
6603/// `assert_u8_array_pairwise_distinct` for INJECTIVITY, one
6604/// `assert_u8_array_covers_inclusive_range` for SURJECTIVITY-onto-a-
6605/// range) — six witness lines total across the three arrays. Post-lift
6606/// each array binds the same theorem PLUS the arity-mismatch check at
6607/// ONE `const _` line — three witness lines total, a 2:1 compression.
6608/// [`crate::error::StructuralKind::HASH_DISCRIMINATORS`] stays on the
6609/// weak-pair contract because its distinct-value set `{0, 2}` is
6610/// NON-contiguous (gap at `1u8` where the atomic-carve outer marker
6611/// lives) — the non-contiguous corner binds through
6612/// `assert_u8_array_pairwise_distinct` + `assert_u8_array_covers_
6613/// finite_set` instead, awaiting a future `assert_u8_array_permutes_
6614/// finite_set` compound helper on the non-contiguous corner of the
6615/// (contiguity) axis. [`crate::error::SexpShape::HASH_DISCRIMINATORS`]
6616/// stays on the range-coverage-only contract because its twelve-shape →
6617/// seven-byte collapse means INJECTIVITY DOES NOT hold — so it CANNOT
6618/// bind a permutation contract on any range corner.
6619///
6620/// The invariant is load-bearing for the three permutation-shaped
6621/// sub-carvings' outer-`Sexp` cache-key partition binding. Each of the
6622/// three arrays MUST bijectively permute its assigned range corner of
6623/// `{0..=6}` so `Hash for Sexp`'s outer discriminator hash sequence
6624/// stays injective on each sub-partition: [`AtomKind::HASH_DISCRIMINATORS`]
6625/// permutes `{0..=5}` on the nested atomic-payload carve inside `Hash
6626/// for Atom`; [`QuoteForm::HASH_DISCRIMINATORS`] permutes `{3..=6}` on
6627/// the quote-family arms of `Hash for Sexp`; UnquoteForm permutes
6628/// `{5..=6}` on the substitution-subset of the quote-family. A
6629/// regression that silently unified two arm's cache-key bytes on ANY
6630/// of the three arrays — OR lifted a fresh N+1 entry drifting `N` above
6631/// the range's cardinality — would silently invalidate every cached
6632/// `Sexp` participating in `Expander::cache`. The compound witness
6633/// catches all three drift modes at COMPILE time.
6634///
6635/// Adding a new family-wide `[u8; N]` permutation-of-range array to
6636/// the substrate: pair the declaration with `const _: () =
6637/// assert_u8_array_permutes_inclusive_range::<N, LO, HI>(&Self::
6638/// FOO_ARRAY);` co-located after the array's declaration and the
6639/// permutation contract binds at compile time. The rustc-forced arity
6640/// `[u8; N]` composes with this const-eval sweep so cardinality AND
6641/// arity-cardinality-match AND range-membership AND pairwise-
6642/// distinctness AND (by pigeonhole) full range-coverage are ALL
6643/// compile-time theorems on the SAME array from ONE `const _` line.
6644/// Prefer this compound helper over the weak-pair pattern for any
6645/// array whose distinct-value set is intentionally EXACTLY a
6646/// contiguous inclusive range with the array acting as a permutation
6647/// of it — the two weak helpers stay as the fallback for arrays with
6648/// LOOSER contracts (e.g. `SexpShape::HASH_DISCRIMINATORS`'s
6649/// intentionally-non-injective twelve → seven collapse binds only the
6650/// coverage weak sibling).
6651///
6652/// Runtime callability: the function is a normal `pub const fn`, so
6653/// callers CAN also invoke it at runtime — pinned by the four negative
6654/// runtime pins (`_panics_at_runtime_on_arity_below_cardinality`,
6655/// `_panics_at_runtime_on_arity_above_cardinality`,
6656/// `_panics_at_runtime_on_duplicate`,
6657/// `_panics_at_runtime_on_out_of_range`) that jointly exercise each of
6658/// the three failure arms.
6659///
6660/// The ARITY-MISMATCH panic site carries an axis-provenance string
6661/// ("ARITY-MISMATCH") chosen DISTINCT from every sibling helper's axis
6662/// strings ("OUT-OF-RANGE" / "MISSING" on the range-coverage helper;
6663/// "OUT-OF-SET" / "SET-BYTE-MISSING" on the finite-set-coverage
6664/// helper) so downstream diagnostics that name the failed axis route
6665/// UNAMBIGUOUSLY to (a) this compound helper, and (b) the specific
6666/// arm — a permutation-shaped drift that fails ARITY-MISMATCH is
6667/// distinct from a range-coverage-drift that fails OUT-OF-RANGE or
6668/// MISSING at the delegated coverage helper. The two delegated helpers
6669/// each panic with THEIR OWN provenance strings so drift on the
6670/// range-membership or pairwise-distinctness arm surfaces with the
6671/// respective sibling helper's message body.
6672///
6673/// Theory grounding:
6674/// - THEORY.md §V.1 — knowable platform; the family-wide permutation-
6675/// of-range contract on the `u8` cache-key vocabulary becomes a
6676/// TYPE-LEVEL theorem the substrate carries per array declaration
6677/// at ONE `const _` line rather than at TWO per-axis `const _` lines
6678/// the developer must remember to keep in lockstep across every
6679/// permutation-shaped HASH_DISCRIMINATORS array.
6680/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
6681/// cache-key partition is the `intent_hash` composition axis —
6682/// binding the permutation-shaped sub-carvings' arrays on the typed
6683/// algebra makes cardinality drift a compile error rather than a
6684/// silent `Hash for Sexp` mis-hash on a caller keyed on a
6685/// permutation-shaped sub-carve.
6686/// - THEORY.md §VI.1 — generation over composition; the compound
6687/// arity-check + range-coverage + pairwise-distinctness sweep IS
6688/// the generative shape. Every new closed-set discriminator array
6689/// whose distinct-value set is an intentionally-closed contiguous
6690/// inclusive range with the array acting as a permutation of it
6691/// adds ONE `const _` line to get the permutation theorem rather
6692/// than re-deriving TWO per-axis `const _` lines (one for
6693/// injectivity, one for surjectivity) that would silently drift out
6694/// of lockstep if one was updated without the other.
6695/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
6696/// compound permutation proof at declaration site AND the three
6697/// consumer sites (nested atomic-payload carve inside `Hash for
6698/// Atom`, quote-family arms of `Hash for Sexp`, substitution-subset
6699/// of the quote-family) regenerate through the SAME `const _`
6700/// witness at each of the three permutation-shaped arrays.
6701///
6702/// Compound sibling posture to the constituent weak helpers on the
6703/// (axis-count) axis:
6704/// [`assert_u8_array_pairwise_distinct`] closes the SINGLE-axis
6705/// INJECTIVITY corner; [`assert_u8_array_covers_inclusive_range`]
6706/// closes the SINGLE-axis SURJECTIVITY-onto-range corner; this
6707/// compound helper closes the DOUBLE-axis (INJECTIVITY ∧ SURJECTIVITY
6708/// ∧ ARITY) corner on the range side of the (contiguity) axis. The
6709/// non-contiguous compound corner (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY
6710/// on a finite non-contiguous set) is a future lift's target — the
6711/// only pre-existing arm that would bind is
6712/// [`crate::error::StructuralKind::HASH_DISCRIMINATORS`], currently
6713/// held by the two-weak-witness pattern.
6714pub const fn assert_u8_array_permutes_inclusive_range<
6715 const N: usize,
6716 const LO: u8,
6717 const HI: u8,
6718>(
6719 arr: &[u8; N],
6720) {
6721 // Arity check FIRST — pigeonhole forces `N == HI - LO + 1` on a
6722 // bijection between `[0..N)` and `[LO..=HI]`. Placing this arm
6723 // FIRST gives cleaner provenance: a drift that grows the array
6724 // past the range cardinality would ALSO fail either the
6725 // out-of-range arm (if the extra entry lies outside `[LO, HI]`)
6726 // OR the pairwise-distinct arm (if it duplicates within range),
6727 // but the ARITY-MISMATCH-named panic message routes the operator
6728 // to the CARDINALITY axis directly rather than to a downstream
6729 // symptom on either weak-axis helper.
6730 let expected_cardinality = (HI - LO) as usize + 1;
6731 if N != expected_cardinality {
6732 panic!(
6733 "assert_u8_array_permutes_inclusive_range: family-wide u8 \
6734 array's ARITY-MISMATCH — the compile-time array cardinality \
6735 `N` does not equal the target inclusive range's cardinality \
6736 `HI - LO + 1`. A bijection between `[0..N)` and `[LO..=HI]` \
6737 forces `N == HI - LO + 1` by pigeonhole — an array whose \
6738 arity drifts above the range's cardinality CANNOT stay both \
6739 pairwise-distinct AND within the range (extras must \
6740 duplicate OR fall outside), and one whose arity drifts \
6741 below CANNOT reach every range byte. The substrate's \
6742 PERMUTATION contract on the array is broken at the ARITY \
6743 axis; every consumer that expects the array's entries to \
6744 bijectively permute the target range (AtomKind / QuoteForm \
6745 / UnquoteForm sub-carving spaces on their permutation-\
6746 shaped range corners on Hash for Sexp's outer discriminator \
6747 partition) relies on this cardinality equality"
6748 );
6749 }
6750 // Delegate pairwise-distinctness to the sibling injectivity
6751 // helper SECOND (before the range-coverage delegation). Order
6752 // matters for provenance-preservation on the failure modes: with
6753 // `N == HI - LO + 1` (post-arity-check), a duplicate entry
6754 // pigeonhole-forces a missing range byte AND vice-versa — the
6755 // two axes are logically equivalent given arity-cardinality
6756 // match. Placing pairwise-distinct BEFORE covers-range routes a
6757 // duplicate to the sibling's `"duplicate"`-named panic on the
6758 // INJECTIVITY axis (rather than to the covers-range sibling's
6759 // `"MISSING"`-named panic on the SURJECTIVITY axis which would
6760 // fire downstream on the pigeonhole-forced coverage failure).
6761 // Choosing INJECTIVITY-provenance as the primary duplicate-
6762 // surfacing arm matches operator expectations: a "duplicate
6763 // entry" diagnosis names the CAUSE (two entries alias) rather
6764 // than a downstream SYMPTOM (a range byte is unreached because
6765 // its slot is duplicated).
6766 assert_u8_array_pairwise_distinct(arr);
6767 // Delegate range-membership + full-range-coverage to the sibling
6768 // covers-range helper THIRD. Given the two prior arms
6769 // (arity-cardinality-match + pairwise-distinct), the covers-
6770 // range helper's SECOND (`"MISSING"`) arm becomes unreachable
6771 // by pigeonhole: `N` distinct entries chosen from a range of
6772 // cardinality `N` MUST equal the range. Only the FIRST
6773 // (`"OUT-OF-RANGE"`) arm surfaces in practice — a drift that
6774 // lifts an entry outside `[LO, HI]` while staying pairwise-
6775 // distinct panics with the sibling's `"OUT-OF-RANGE"` provenance.
6776 // The delegated coverage sweep at the second arm is kept for
6777 // DEFENSE-IN-DEPTH: a regression that silently dropped the
6778 // pairwise-distinct delegation above would leave a duplicate
6779 // uncaught by the compound helper's OWN body, but the covers-
6780 // range sibling's MISSING check would then catch it as a
6781 // safety-net symptom.
6782 assert_u8_array_covers_inclusive_range::<N, LO, HI>(arr);
6783}
6784
6785/// Compile-time contract verifier — panics at const evaluation time if
6786/// `arr` is not a PERMUTATION of the caller-supplied FINITE SET `set`
6787/// on the substrate's `u8` cache-key vocabulary. NON-CONTIGUOUS-FINITE-
6788/// SET peer of the pre-existing [`assert_u8_array_permutes_inclusive_range`]
6789/// sibling on the (contiguity) axis of the substrate's compound
6790/// (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) permutation verifiers: where
6791/// the sibling closes the CONTIGUOUS-INCLUSIVE-RANGE corner (at the
6792/// three permutation-shaped range-covering HASH_DISCRIMINATORS arrays
6793/// [`AtomKind::HASH_DISCRIMINATORS`] / [`QuoteForm::HASH_DISCRIMINATORS`]
6794/// / [`crate::error::UnquoteForm::HASH_DISCRIMINATORS`]), this closes
6795/// the NON-CONTIGUOUS-FINITE-SET corner (at the one permutation-shaped
6796/// non-contiguous-covering HASH_DISCRIMINATORS array
6797/// [`crate::error::StructuralKind::HASH_DISCRIMINATORS`], whose
6798/// distinct-value set `{0, 2}` is intentionally NON-contiguous with a
6799/// gap at `1u8` where the atomic-carve outer marker
6800/// [`AtomKind::OUTER_HASH_DISCRIMINATOR`] lives). Binds FOUR conjunct
6801/// clauses at ONE `const _` line:
6802///
6803/// 1. SET-NOT-PAIRWISE-DISTINCT: the caller-provided TARGET-SET
6804/// spec `set` MUST itself be pairwise-distinct — a mathematical
6805/// finite set of cardinality `M` cannot carry a duplicate entry.
6806/// Post-lift the pre-lift caveat "assuming `set` is itself
6807/// pairwise-distinct — a well-formedness responsibility on the
6808/// caller-provided target spec" is now a compile-time theorem
6809/// the substrate carries per call site rather than a docstring-
6810/// level responsibility the operator must remember to keep in
6811/// lockstep with every downstream covers/permutes invocation.
6812/// Delegated to [`assert_u8_finite_set_pairwise_distinct`] at
6813/// the TOP of the delegation chain (before the ARITY arm below
6814/// because a malformed `set` renders the pigeonhole invariants
6815/// the ARITY arm relies on unsound with a PHANTOM-`M`).
6816/// 2. ARITY-MISMATCH: `N` MUST equal `M` — a bijection between
6817/// `[0..N)` and the target set `set` (whose cardinality now
6818/// provably equals `M` post-clause-1's well-formedness check)
6819/// forces the cardinality equality by pigeonhole. A regression
6820/// that adds a spurious duplicate entry to a HASH_DISCRIMINATORS
6821/// array (bumping `N` past the set's cardinality) fails-loudly at
6822/// this arm with the ARITY-MISMATCH-provenance panic message.
6823/// Delegated to no sibling — this arm is the compound helper's
6824/// unique contribution.
6825/// 3. Pairwise-distinctness: every pair of entries is distinct —
6826/// delegated to [`assert_u8_array_pairwise_distinct`].
6827/// 4. Set-membership + full-set-coverage: every entry lies in `set`
6828/// AND every set byte is reached — delegated to
6829/// [`assert_u8_array_covers_finite_set`] (which sweeps BOTH the
6830/// SET-MEMBERSHIP and FULL-COVERAGE arms, AND re-runs clause 1's
6831/// SET-WELL-FORMEDNESS delegation as defense-in-depth; all three
6832/// bind through the one delegation call).
6833///
6834/// (1) ∧ (2) ∧ (3) ∧ (4) jointly imply the array's entries EXACTLY
6835/// partition the set: `N == M` distinct entries chosen from a set of
6836/// cardinality `M` (proven-well-formed by clause 1) MUST equal the set
6837/// by pigeonhole. The single compound invocation therefore binds
6838/// the same theorem as the pair `assert_u8_array_pairwise_distinct(
6839/// &arr) + assert_u8_array_covers_finite_set::<N, M>(&arr, &set)` PLUS
6840/// the arity-mismatch check (1) that neither weak sibling carries
6841/// alone — a strictly stronger contract at HALF the per-array
6842/// witness-line surface.
6843///
6844/// Compression: pre-lift,
6845/// [`crate::error::StructuralKind::HASH_DISCRIMINATORS`] (`[u8; 2]`
6846/// permuting the non-contiguous partition `{0, 2}`) bound its
6847/// permutation-of-finite-set contract at TWO `const _` lines (one
6848/// `assert_u8_array_pairwise_distinct` for INJECTIVITY, one
6849/// `assert_u8_array_covers_finite_set` for SURJECTIVITY-onto-set) —
6850/// two scattered per-axis witness lines that would silently drift out
6851/// of lockstep if one was updated without the other. Post-lift the
6852/// array binds the same theorem PLUS the arity-mismatch check at ONE
6853/// `const _` line — a 2:1 compression at strictly stronger contract
6854/// strength, symmetric to the sibling
6855/// [`assert_u8_array_permutes_inclusive_range`]'s 2:1 compression on
6856/// the three range-covering permutation-shaped arrays. Together the
6857/// two compound helpers close the WHOLE compound tier of the
6858/// substrate's `u8` array vocabulary: `StructuralKind` (on the non-
6859/// contiguous corner) plus `AtomKind` / `QuoteForm` / `UnquoteForm`
6860/// (on the contiguous corner) — four permutation-shaped arrays across
6861/// both contiguity postures now bind their compound
6862/// (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) permutation contract at ONE
6863/// `const _` line each. [`crate::error::SexpShape::HASH_DISCRIMINATORS`]
6864/// stays on the range-coverage-only single-axis sibling helper because
6865/// its intentionally-non-injective twelve-shape → seven-byte collapse
6866/// means INJECTIVITY does not hold — it CANNOT bind a permutation
6867/// contract on any contiguity corner.
6868///
6869/// The invariant is load-bearing for `StructuralKind`'s non-contiguous
6870/// sub-carve `{0, 2}` on the outer-`Sexp` cache-key partition:
6871/// `Sexp::Nil` and `Sexp::List(_)` MUST map bijectively to `{0u8, 2u8}`
6872/// so `Hash for Sexp`'s outer discriminator hash sequence stays
6873/// injective on the structural-residual sub-partition. The intentional
6874/// gap at `1u8` — where the atomic-carve outer marker
6875/// [`AtomKind::OUTER_HASH_DISCRIMINATOR`] lives — is EXACTLY the
6876/// reason StructuralKind CANNOT bind through the sibling
6877/// [`assert_u8_array_permutes_inclusive_range`] helper (its distinct-
6878/// value set is not a contiguous inclusive range). This compound
6879/// helper is the non-contiguous corner where StructuralKind's compound
6880/// contract binds at ONE line. A regression that silently unified two
6881/// StructuralKind arms' cache-key bytes (a duplicate on the
6882/// INJECTIVITY arm), OR lifted a fresh entry into the atomic-carve
6883/// gap at `1u8` (a drift on the SET-MEMBERSHIP arm), OR dropped an
6884/// entry making the set non-covering (a drift on the SET-BYTE-MISSING
6885/// arm), OR bumped `N` past the set cardinality (a drift on the ARITY
6886/// arm), would silently invalidate every cached `Sexp::List(_)` /
6887/// `Sexp::Nil` participating in `Expander::cache` — pre-lift these
6888/// four modes were caught only at runtime by the sibling helpers'
6889/// runtime tests + the pre-lift weak `const _` pair, post-lift the
6890/// compound `const _` witness catches all four drift modes at COMPILE
6891/// time at ONE line.
6892///
6893/// Adding a new family-wide `[u8; N]` permutation-of-finite-set array
6894/// to the substrate: pair the declaration with `const _: () =
6895/// assert_u8_array_permutes_finite_set::<N, M>(&Self::FOO_ARRAY,
6896/// &[…set…]);` co-located after the array's declaration and the
6897/// permutation-of-finite-set contract binds at compile time. The
6898/// rustc-forced arity `[u8; N]` composes with this const-eval sweep so
6899/// cardinality AND arity-cardinality-match AND set-membership AND
6900/// pairwise-distinctness AND (by pigeonhole) full set-coverage are
6901/// ALL compile-time theorems on the SAME array from ONE `const _`
6902/// line. Prefer this compound helper over the weak-pair pattern for
6903/// any array whose distinct-value set is intentionally EXACTLY the
6904/// given finite set with the array acting as a permutation of it —
6905/// the two weak helpers stay as the fallback for arrays with LOOSER
6906/// contracts. Prefer the tighter
6907/// [`assert_u8_array_permutes_inclusive_range`] sibling when the
6908/// target finite set IS a contiguous inclusive range — this helper is
6909/// the fallback for the non-contiguous corner.
6910///
6911/// Runtime callability: the function is a normal `pub const fn`, so
6912/// callers CAN also invoke it at runtime — pinned by the four
6913/// negative runtime pins
6914/// (`_panics_at_runtime_on_arity_below_cardinality`,
6915/// `_panics_at_runtime_on_arity_above_cardinality`,
6916/// `_panics_at_runtime_on_duplicate`,
6917/// `_panics_at_runtime_on_out_of_set`) that jointly exercise each of
6918/// the three failure arms.
6919///
6920/// The ARITY-MISMATCH panic site carries the same axis-provenance
6921/// string `"ARITY-MISMATCH"` as the sibling
6922/// [`assert_u8_array_permutes_inclusive_range`]'s ARITY arm — the two
6923/// compound permutation helpers SHARE the arity axis-name because the
6924/// axis is the SAME (a cardinality-equality contract), but the HELPER
6925/// name in the panic message routes UNAMBIGUOUSLY to the SPECIFIC
6926/// contiguity corner (`"assert_u8_array_permutes_finite_set"` vs.
6927/// `"assert_u8_array_permutes_inclusive_range"`) — string search on
6928/// the axis PLUS the helper name jointly disambiguates the failed
6929/// (helper, axis) pair. The two delegated helpers each panic with
6930/// THEIR OWN provenance strings so drift on the pairwise-distinctness
6931/// or set-coverage arm surfaces with the respective sibling helper's
6932/// message body (`"duplicate"` on the INJECTIVITY axis; `"OUT-OF-SET"`
6933/// / `"SET-BYTE-MISSING"` on the SURJECTIVITY axis).
6934///
6935/// Theory grounding:
6936/// - THEORY.md §V.1 — knowable platform; the family-wide permutation-
6937/// of-finite-set contract on the `u8` cache-key vocabulary becomes
6938/// a TYPE-LEVEL theorem the substrate carries per array declaration
6939/// at ONE `const _` line rather than at TWO per-axis `const _` lines
6940/// the developer must remember to keep in lockstep across every
6941/// permutation-of-finite-set-shaped HASH_DISCRIMINATORS array.
6942/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
6943/// cache-key partition is the `intent_hash` composition axis —
6944/// binding the non-contiguous permutation-shaped sub-carvings'
6945/// arrays on the typed algebra makes cardinality drift a compile
6946/// error rather than a silent `Hash for Sexp` mis-hash on a caller
6947/// keyed on a non-contiguous permutation-shaped sub-carve.
6948/// - THEORY.md §VI.1 — generation over composition; the compound
6949/// arity-check + set-coverage + pairwise-distinctness sweep IS the
6950/// generative shape. Every new closed-set discriminator array whose
6951/// distinct-value set is an intentionally-closed non-contiguous
6952/// finite partition with the array acting as a permutation of it
6953/// adds ONE `const _` line to get the permutation theorem rather
6954/// than re-deriving TWO per-axis `const _` lines (one for
6955/// injectivity, one for surjectivity) that would silently drift out
6956/// of lockstep if one was updated without the other.
6957/// - THEORY.md §II.1 invariant 5 — composition preserves proofs; the
6958/// compound permutation-of-finite-set proof at declaration site AND
6959/// the consumer site (`StructuralKind` sub-carve inside
6960/// `Hash for Sexp` on `Sexp::Nil` / `Sexp::List(_)`) regenerate
6961/// through the SAME `const _` witness.
6962///
6963/// Compound sibling posture on the (contiguity) axis:
6964/// [`assert_u8_array_permutes_inclusive_range`] closes the CONTIGUOUS-
6965/// INCLUSIVE-RANGE corner (three arrays); this helper closes the
6966/// NON-CONTIGUOUS-FINITE-SET corner (one array). The two together
6967/// close the whole (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) compound tier
6968/// of the substrate's `u8` array vocabulary on both contiguity
6969/// postures — every intentionally-closed permutation-shaped `[u8; N]`
6970/// HASH_DISCRIMINATORS array on the substrate now binds its compound
6971/// permutation contract at ONE `const _` line regardless of whether
6972/// the target is a contiguous inclusive range or an arbitrary
6973/// non-contiguous finite set.
6974pub const fn assert_u8_array_permutes_finite_set<const N: usize, const M: usize>(
6975 arr: &[u8; N],
6976 set: &[u8; M],
6977) {
6978 // Delegate target-set well-formedness to the sibling SET-side
6979 // pairwise-distinctness helper FIRST — even BEFORE the ARITY
6980 // check below. A malformed `set` (e.g. `[0u8, 2u8, 2u8]` at
6981 // `M = 3`) with a duplicate entry inflates the effective
6982 // cardinality with a PHANTOM-`M` and renders the pigeonhole
6983 // invariants unsound: an `arr` of arity `N == M` that is
6984 // pairwise-distinct and within-set would silently mis-verify
6985 // as a permutation of a cardinality-`M` set when it is really
6986 // covering a cardinality-`< M` set. Placing this arm FIRST
6987 // routes drift on the CALLER'S TARGET-SET SPEC to the SET-side
6988 // well-formedness axis rather than to a downstream ARITY-
6989 // MISMATCH / duplicate / OUT-OF-SET symptom on `arr` (three
6990 // of which would surface DEPENDING on how the caller's mistake
6991 // interacts with the `arr` under verification, producing
6992 // inconsistent operator diagnostics for the SAME underlying
6993 // root cause). The helper's own body re-runs this check via
6994 // the delegated `assert_u8_array_covers_finite_set` call at
6995 // the third arm below as defense-in-depth against a regression
6996 // that silently dropped this delegation at the top; the two
6997 // calls jointly bind the SET-side well-formedness contract at
6998 // TWO stages of the compound helper's delegation chain.
6999 assert_u8_finite_set_pairwise_distinct(set);
7000 // ARITY-MISMATCH check SECOND — pigeonhole forces `N == M` on a
7001 // bijection between `[0..N)` and `set` (given the SET-side
7002 // well-formedness delegated above; the pre-lift docstring's
7003 // "assuming `set` is itself pairwise-distinct" caveat is now a
7004 // compile-time theorem the substrate carries per call site).
7005 // Placing this arm HERE (after well-formedness) gives cleaner
7006 // provenance: a drift that grows the array past the set
7007 // cardinality would ALSO fail either the pairwise-distinct arm
7008 // (if the extra entry duplicates a within-set byte) OR the
7009 // covers-finite-set arm's `"OUT-OF-SET"` axis (if the extra
7010 // entry lies outside `set`), but the ARITY-MISMATCH-named panic
7011 // message routes the operator to the CARDINALITY axis directly
7012 // rather than to a downstream symptom on either weak-axis helper.
7013 if N != M {
7014 panic!(
7015 "assert_u8_array_permutes_finite_set: family-wide u8 array's \
7016 ARITY-MISMATCH — the compile-time array cardinality `N` \
7017 does not equal the target finite set's cardinality `M`. A \
7018 bijection between `[0..N)` and the target set forces `N \
7019 == M` by pigeonhole (given `set`'s well-formedness \
7020 delegated to `assert_u8_finite_set_pairwise_distinct` \
7021 at the top of this helper before the ARITY arm) — an \
7022 array whose arity \
7023 drifts above the set's cardinality CANNOT stay both \
7024 pairwise-distinct AND within the set (extras must \
7025 duplicate OR fall outside), and one whose arity drifts \
7026 below CANNOT reach every set byte. The substrate's \
7027 PERMUTATION-of-FINITE-SET contract on the array is broken \
7028 at the ARITY axis; every consumer that expects the \
7029 array's entries to bijectively permute the target set \
7030 (StructuralKind's `{{0, 2}}` non-contiguous sub-carving \
7031 on Hash for Sexp's outer discriminator partition around \
7032 the atomic-carve gap at `1u8`) relies on this cardinality \
7033 equality"
7034 );
7035 }
7036 // Delegate pairwise-distinctness to the sibling injectivity
7037 // helper SECOND (before the finite-set-coverage delegation). Order
7038 // matters for provenance-preservation on the failure modes: with
7039 // `N == M` (post-arity-check) and `set` well-formed, a duplicate
7040 // entry in `arr` pigeonhole-forces a missing set byte AND vice-
7041 // versa — the two axes are logically equivalent given arity-
7042 // cardinality-match. Placing pairwise-distinct BEFORE covers-
7043 // finite-set routes a duplicate to the sibling's `"duplicate"`-
7044 // named panic on the INJECTIVITY axis (rather than to the
7045 // covers-finite-set sibling's `"SET-BYTE-MISSING"`-named panic
7046 // on the SURJECTIVITY axis which would fire downstream on the
7047 // pigeonhole-forced coverage failure). Matches the sibling
7048 // `assert_u8_array_permutes_inclusive_range`'s ordering: name
7049 // the CAUSE (duplicate) rather than the pigeonhole-forced
7050 // downstream SYMPTOM (SET-BYTE-MISSING).
7051 assert_u8_array_pairwise_distinct(arr);
7052 // Delegate set-membership + full-set-coverage to the sibling
7053 // covers-finite-set helper THIRD. Given the two prior arms
7054 // (arity-cardinality-match + pairwise-distinct on `arr`), the
7055 // covers-finite-set helper's SECOND (`"SET-BYTE-MISSING"`) arm
7056 // becomes unreachable by pigeonhole assuming `set` is well-
7057 // formed: `N == M` distinct entries chosen from a set of
7058 // cardinality `M` MUST equal the set. Only the FIRST
7059 // (`"OUT-OF-SET"`) arm surfaces in practice — a drift that lifts
7060 // an entry outside `set` while staying pairwise-distinct panics
7061 // with the sibling's `"OUT-OF-SET"` provenance. The delegated
7062 // coverage sweep at the second arm is kept for DEFENSE-IN-DEPTH:
7063 // a regression that silently dropped the pairwise-distinct
7064 // delegation above would leave a duplicate uncaught by the
7065 // compound helper's OWN body, but the covers-finite-set
7066 // sibling's `"SET-BYTE-MISSING"` check would then catch it as a
7067 // safety-net symptom.
7068 assert_u8_array_covers_finite_set::<N, M>(arr, set);
7069}
7070
7071// Compile-time permutation-of-finite-set witness — one `const _: () =
7072// assert_u8_array_permutes_finite_set::<N, M>(&…, &[…])` per family-
7073// wide `[u8; N]` hash-discriminator array on the substrate's closed-
7074// set outer algebras whose distinct-value set is an intentionally-
7075// closed non-contiguous finite partition with the array acting as a
7076// permutation of it. Each invocation is const-evaluated at `cargo
7077// check` time; a regression that silently drifts the array's
7078// cardinality away from the set's cardinality OR silently collides
7079// two entries OR silently drifts an entry outside the set (including
7080// the archetype drift into the intentional gap at `1u8`) fails the
7081// build rather than the test suite. Compression peer to the pre-lift
7082// `assert_u8_array_pairwise_distinct` + `assert_u8_array_covers_
7083// finite_set` weak-witness pair on the (axis-count) axis: those bound
7084// `StructuralKind::HASH_DISCRIMINATORS`' invariants at TWO `const _`
7085// lines (one per axis); this compound helper binds the same theorem
7086// PLUS the arity-cardinality-equality contract at ONE `const _`
7087// line — a 2:1 compression at strictly stronger contract strength.
7088// Contiguity-axis peer to the three `assert_u8_array_permutes_
7089// inclusive_range` witnesses above: those close the contiguous-
7090// inclusive-range corner of the compound tier at the three
7091// permutation-shaped range-covering HASH_DISCRIMINATORS arrays
7092// (`AtomKind`, `QuoteForm`, `UnquoteForm`); this closes the non-
7093// contiguous-finite-set corner at the ONE non-contiguous-covering
7094// permutation-shaped array (`StructuralKind`). Together the two
7095// compound helpers close the WHOLE compound (INJECTIVITY ∧
7096// SURJECTIVITY ∧ ARITY) tier of the substrate's `u8` array vocabulary
7097// on both contiguity postures — every intentionally-closed
7098// permutation-shaped `[u8; N]` HASH_DISCRIMINATORS array on the
7099// substrate now binds its compound permutation contract at ONE
7100// `const _` line regardless of whether the target is a contiguous
7101// inclusive range or an arbitrary non-contiguous finite set.
7102const _: () = assert_u8_array_permutes_finite_set::<2, 2>(
7103 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
7104 &[0u8, 2u8],
7105);
7106
7107/// Compile-time compound (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) JOINT
7108/// permutation-of-inclusive-range verifier for a `(scalar, [u8; M], [u8; N])`
7109/// triple — the (`scalar-plus-two-arrays`) corner of the (carving-shape)
7110/// axis peer to [`assert_u8_array_permutes_inclusive_range`]'s
7111/// (`single-array`) corner. Panics at const-eval time (or at runtime for
7112/// dynamic callers) with an axis-provenance-named message iff the joint
7113/// union `{scalar} ∪ arr_a ∪ arr_b` fails ANY of:
7114///
7115/// 1. ARITY-MISMATCH — `1 + M + N != HI - LO + 1`. A joint bijection
7116/// between `[0..1+M+N)` and `[LO..=HI]` forces the cardinality
7117/// equality by pigeonhole. Placed FIRST so the panic message routes
7118/// the operator directly to the CARDINALITY axis rather than to a
7119/// downstream range-membership or count-exactly-once symptom.
7120/// 2. SCALAR-OUT-OF-RANGE — `scalar < LO || scalar > HI`.
7121/// 3. FIRST-ARRAY-OUT-OF-RANGE — any `arr_a[i] < LO || arr_a[i] > HI`.
7122/// 4. SECOND-ARRAY-OUT-OF-RANGE — any `arr_b[j] < LO || arr_b[j] > HI`.
7123/// 5. JOINT-MISSING — some byte in `[LO..=HI]` occurs ZERO times across
7124/// `{scalar} ∪ arr_a ∪ arr_b`. Given post-arity + post-in-range, this
7125/// is equivalent to JOINT-duplicate on some OTHER range byte by
7126/// pigeonhole, but the message routes to the SURJECTIVITY axis for
7127/// the specific missing byte.
7128/// 6. JOINT-duplicate — some byte in `[LO..=HI]` occurs TWO or more times
7129/// across `{scalar} ∪ arr_a ∪ arr_b` (either cross-carving between
7130/// scalar and one array, cross-carving between the two arrays, OR
7131/// intra-carving inside a single array). Given post-arity +
7132/// post-in-range, this is equivalent to JOINT-MISSING on some other
7133/// range byte by pigeonhole, but the message routes to the
7134/// INJECTIVITY axis for the specific duplicated byte.
7135///
7136/// Arms 5 and 6 fuse into ONE `sweep [LO..=HI] and count exactly once` loop
7137/// — a byte with `count == 0` witnesses the SURJECTIVITY failure, a byte
7138/// with `count >= 2` witnesses the INJECTIVITY failure. The two logical
7139/// axes are equivalent given post-arity + post-in-range but the fused
7140/// sweep routes provenance to whichever byte violates first, matching the
7141/// (`"MISSING"` / `"duplicate"`) provenance vocabulary the sibling helpers
7142/// [`assert_u8_array_covers_inclusive_range`] and
7143/// [`assert_u8_array_pairwise_distinct`] use on the SINGLE-array corner.
7144///
7145/// Compression peer to a hypothetical decomposition through the three
7146/// pre-existing single-array helpers (`assert_u8_array_pairwise_distinct`
7147/// each on `[scalar]`/`arr_a`/`arr_b` + `assert_u8_array_covers_
7148/// inclusive_range` on the concatenation) on the (axis-count) axis: those
7149/// would need explicit concatenation at rustc time (impossible without
7150/// runtime `Vec`) OR three per-array + one joint-distinctness call
7151/// (four+ `const _` lines PLUS threading the joint check through a bespoke
7152/// primitive); this compound helper binds the SAME joint theorem PLUS the
7153/// scalar-plus-two-arrays cardinality-equality contract at ONE `const _`
7154/// line — the compression is 4:1 at strictly stronger contract strength on
7155/// the (scalar + two arrays) shape corner. Sibling posture on the
7156/// (carving-shape) axis of the substrate-primitive matrix:
7157/// * (single-array, permutes-range) — [`assert_u8_array_permutes_inclusive_range`]
7158/// * (scalar-plus-two-arrays, permutes-range) — THIS helper.
7159///
7160/// The load-bearing invariant this helper pins: the outer-`Sexp` cache-key
7161/// discriminator space `{0..=6}` partitions across the SUBSTRATE'S THREE
7162/// carvings — [`AtomKind::OUTER_HASH_DISCRIMINATOR`] (scalar `1u8` for
7163/// `Sexp::Atom(_)`), [`crate::error::StructuralKind::HASH_DISCRIMINATORS`]
7164/// (`[u8; 2]` at `{0, 2}` for `Sexp::Nil`/`Sexp::List(_)`),
7165/// [`QuoteForm::HASH_DISCRIMINATORS`] (`[u8; 4]` at `{3, 4, 5, 6}` for the
7166/// four quote-family variants). Post-lift the JOINT permutation binds at
7167/// rustc time via ONE `const _` witness; a drift at ANY of the three
7168/// carvings (renumbered atomic marker, extra `StructuralKind` variant with
7169/// a byte outside `{0, 2}`, collision between `QuoteForm` and the atomic
7170/// marker) becomes a compile error rather than a `sexp_shape_hash_
7171/// discriminator_partitions_by_three_way_carving_disjointly` /
7172/// `structural_kind_hash_discriminator_disjoint_from_atom_outer_carve_
7173/// byte_and_quote_form_hash_discriminator_partition` runtime-test
7174/// regression.
7175///
7176/// Theory anchor: THEORY.md §II.1 invariant 5 — composition preserves
7177/// proofs; the joint carving's compound permutation contract composes
7178/// through the three sub-algebras' `HASH_DISCRIMINATOR`s and the const-fn
7179/// contract preserves the joint permutation-of-range proof across `rustc`
7180/// invocations byte-for-byte. THEORY.md §III — the typescape; the joint-
7181/// partition property becomes a TYPE-level theorem on the substrate rather
7182/// than a per-carving test-time invariant. THEORY.md §V.3 — three-pillar
7183/// attestation; the outer-`Sexp` cache-key partition is the substrate's
7184/// outer `Sexp` `intent_hash` composition axis — binding the joint
7185/// permutation at rustc time makes attestation-key drift a compile error
7186/// rather than a silent BLAKE3 mis-hash.
7187pub const fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range<
7188 const M: usize,
7189 const N: usize,
7190 const LO: u8,
7191 const HI: u8,
7192>(
7193 scalar: u8,
7194 arr_a: &[u8; M],
7195 arr_b: &[u8; N],
7196) {
7197 // ARITY-MISMATCH check FIRST — pigeonhole forces `1 + M + N ==
7198 // HI - LO + 1` on a joint bijection between `[0..1+M+N)` and
7199 // `[LO..=HI]`. Placing this arm FIRST gives cleaner provenance:
7200 // a drift that grows the joint cardinality past the range
7201 // cardinality would ALSO fail either the out-of-range arm OR
7202 // the joint-duplicate arm, but the ARITY-MISMATCH-named panic
7203 // routes the operator to the CARDINALITY axis directly rather
7204 // than to a downstream symptom on the other axes.
7205 let expected_cardinality = (HI - LO) as usize + 1;
7206 if 1 + M + N != expected_cardinality {
7207 panic!(
7208 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range: \
7209 family-wide (scalar, [u8; M], [u8; N]) triple's ARITY-\
7210 MISMATCH — the compile-time joint cardinality `1 + M + N` \
7211 does not equal the target inclusive range's cardinality \
7212 `HI - LO + 1`. A joint bijection between `[0..1+M+N)` and \
7213 `[LO..=HI]` forces `1 + M + N == HI - LO + 1` by pigeonhole \
7214 — a joint carving whose cardinality drifts above the \
7215 range's CANNOT stay both jointly-pairwise-distinct AND \
7216 within the range (extras must duplicate OR fall outside), \
7217 and one whose cardinality drifts below CANNOT reach every \
7218 range byte. The substrate's JOINT PERMUTATION contract on \
7219 the outer-`Sexp` cache-key partition (AtomKind's outer \
7220 marker + StructuralKind + QuoteForm carvings jointly \
7221 covering `{{0..=6}}`) is broken at the ARITY axis; every \
7222 consumer that expects the joint carving to bijectively \
7223 permute the target range (Hash for Sexp's outer \
7224 discriminator partition, the three-carving-plus-atom-\
7225 marker joint disjointness contract) relies on this \
7226 cardinality equality"
7227 );
7228 }
7229 // SCALAR-OUT-OF-RANGE arm.
7230 if scalar < LO || scalar > HI {
7231 panic!(
7232 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range: \
7233 family-wide (scalar, [u8; M], [u8; N]) triple's scalar \
7234 carries an OUT-OF-RANGE byte — the scalar lies outside \
7235 the target inclusive range `[LO, HI]`. The substrate's \
7236 RANGE-BOUND contract on the joint carving is broken at \
7237 the scalar carving's arm; every consumer that expects the \
7238 scalar-plus-two-arrays joint image to partition an outer \
7239 cache-key space (Hash for Sexp's outer discriminator \
7240 `{{0..=6}}`) relies on the scalar staying within the \
7241 target range"
7242 );
7243 }
7244 // FIRST-ARRAY-OUT-OF-RANGE arm.
7245 let mut i = 0;
7246 while i < M {
7247 if arr_a[i] < LO || arr_a[i] > HI {
7248 panic!(
7249 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range: \
7250 family-wide (scalar, [u8; M], [u8; N]) triple's first \
7251 array carries an OUT-OF-RANGE entry at some position — \
7252 the entry's byte lies outside the target inclusive \
7253 range `[LO, HI]`. The substrate's RANGE-BOUND contract \
7254 on the joint carving is broken at the first-array \
7255 carving's arm",
7256 );
7257 }
7258 i += 1;
7259 }
7260 // SECOND-ARRAY-OUT-OF-RANGE arm.
7261 let mut j = 0;
7262 while j < N {
7263 if arr_b[j] < LO || arr_b[j] > HI {
7264 panic!(
7265 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range: \
7266 family-wide (scalar, [u8; M], [u8; N]) triple's second \
7267 array carries an OUT-OF-RANGE entry at some position — \
7268 the entry's byte lies outside the target inclusive \
7269 range `[LO, HI]`. The substrate's RANGE-BOUND contract \
7270 on the joint carving is broken at the second-array \
7271 carving's arm",
7272 );
7273 }
7274 j += 1;
7275 }
7276 // JOINT-DISTINCTNESS ∧ JOINT-SURJECTIVITY fused via sweep-and-count.
7277 // Given post-arity + post-in-range, the two logical axes are
7278 // equivalent by pigeonhole but the fused sweep routes provenance to
7279 // whichever byte violates first with the sibling helpers'
7280 // (`"MISSING"` / `"duplicate"`) vocabulary preserved.
7281 let mut b = LO;
7282 loop {
7283 let mut count: u32 = 0;
7284 if scalar == b {
7285 count += 1;
7286 }
7287 let mut i = 0;
7288 while i < M {
7289 if arr_a[i] == b {
7290 count += 1;
7291 }
7292 i += 1;
7293 }
7294 let mut j = 0;
7295 while j < N {
7296 if arr_b[j] == b {
7297 count += 1;
7298 }
7299 j += 1;
7300 }
7301 if count == 0 {
7302 panic!(
7303 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range: \
7304 family-wide (scalar, [u8; M], [u8; N]) triple is JOINT-\
7305 MISSING a byte from the target inclusive range \
7306 `[LO, HI]` — every byte in the range must appear at \
7307 least once across the joint union `{{scalar}} ∪ arr_a \
7308 ∪ arr_b`. The substrate's JOINT FULL-COVERAGE contract \
7309 on the outer-`Sexp` cache-key partition is broken; the \
7310 SURJECTIVITY axis of the joint permutation fails on \
7311 the specific missing range byte",
7312 );
7313 }
7314 if count >= 2 {
7315 panic!(
7316 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range: \
7317 family-wide (scalar, [u8; M], [u8; N]) triple carries a \
7318 JOINT duplicate byte — some byte in `[LO, HI]` appears \
7319 TWO or more times across the joint union `{{scalar}} ∪ \
7320 arr_a ∪ arr_b` (either cross-carving between scalar and \
7321 an array, cross-carving between the two arrays, OR \
7322 intra-carving inside a single array). The substrate's \
7323 JOINT PAIRWISE-DISTINCTNESS contract on the outer-\
7324 `Sexp` cache-key partition is broken; the INJECTIVITY \
7325 axis of the joint permutation fails on the specific \
7326 duplicated range byte",
7327 );
7328 }
7329 if b == HI {
7330 break;
7331 }
7332 b += 1;
7333 }
7334}
7335
7336// Compile-time permutation-of-range witnesses — one `const _: () =
7337// assert_u8_array_permutes_inclusive_range::<N, LO, HI>(&…)` per
7338// family-wide `[u8; N]` hash-discriminator array on the substrate's
7339// closed-set outer algebras whose distinct-value set is an
7340// intentionally-closed contiguous inclusive range with the array
7341// acting as a permutation of it. Each invocation is const-evaluated
7342// at `cargo check` time; a regression that silently drifts the
7343// array's cardinality above the range's cardinality OR silently
7344// collides two entries OR silently drifts an entry outside the range
7345// fails the build rather than the test suite. Compression peer to
7346// the pre-existing `assert_u8_array_pairwise_distinct` +
7347// `assert_u8_array_covers_inclusive_range` weak-witness pair on the
7348// (axis-count) axis: those bind the SAME three arrays' invariants at
7349// TWO `const _` lines per array (six lines total across the three
7350// arrays); this compound helper binds the same theorem PLUS the
7351// arity-cardinality-equality contract at ONE `const _` line per
7352// array (three lines total across the three arrays) — a 2:1
7353// compression at strictly stronger contract strength.
7354// `StructuralKind::HASH_DISCRIMINATORS` stays on the weak-pair
7355// (pairwise-distinct + covers-finite-set) pattern because its
7356// distinct-value set `{0, 2}` is NON-contiguous (gap at `1u8` where
7357// the atomic-carve outer marker lives) — the non-contiguous corner
7358// binds through a future `assert_u8_array_permutes_finite_set`
7359// compound helper on the non-contiguous corner of the (contiguity)
7360// axis. `SexpShape::HASH_DISCRIMINATORS` stays on the range-
7361// coverage-only pattern because its intentionally-non-injective
7362// twelve-shape → seven-byte collapse means INJECTIVITY does not hold
7363// — it CANNOT bind a permutation contract on any range corner.
7364const _: () = assert_u8_array_permutes_inclusive_range::<6, 0, 5>(&AtomKind::HASH_DISCRIMINATORS);
7365const _: () = assert_u8_array_permutes_inclusive_range::<4, 3, 6>(&QuoteForm::HASH_DISCRIMINATORS);
7366const _: () = assert_u8_array_permutes_inclusive_range::<2, 5, 6>(
7367 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
7368);
7369
7370// Compile-time FULL-ARRAY per-position ORDER pins — one `const _: () =
7371// assert_u8_array_slice_equals_u8_array::<N, N, 0>(&…, &[literal
7372// bytes; N])` per family-wide `[u8; N]` hash-discriminator
7373// SUB-CARVING array on the substrate's closed-set outer algebras.
7374// Each invocation exercises the [`assert_u8_array_slice_equals_u8_array`]
7375// helper at its FULL-ARRAY corner (`M == N`, `START == 0`) — the
7376// SLICE-EQUALS-ARRAY sweep collapses to an ALL-positions-equal-peer-
7377// array pointwise identity `arr == [b_0, b_1, …, b_{N-1}]`. The peer
7378// literal-byte sub-array on the RHS pins BOTH (a) the per-position
7379// ORDER of the outer array's declaration (the CANONICAL variant-
7380// declaration order that all `zip(ALL, HASH_DISCRIMINATORS)` consumers
7381// depend on) AND (b) each per-role `pub(crate) const *_HASH_DISCRIMINATOR`
7382// alias's canonical `u8` byte value the outer array's slots re-
7383// export. Strictly STRONGER on the (contract-strength) axis than the
7384// sibling permutation witnesses IMMEDIATELY ABOVE: those bind each
7385// array's IMAGE SET (`{0..=5}` for `AtomKind`, `{3..=6}` for
7386// `QuoteForm`, `{5..=6}` for `UnquoteForm`) via the JOINT
7387// (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) permutation-of-range contract
7388// but are SILENT on which SLOT of the array each byte lands at — a
7389// regression that swapped `SYMBOL_HASH_DISCRIMINATOR = 0` and
7390// `KEYWORD_HASH_DISCRIMINATOR = 1` (drifting `AtomKind::HASH_DISCRIMINATORS`
7391// from `[0, 1, 2, 3, 4, 5]` to `[1, 0, 2, 3, 4, 5]`) preserves the
7392// permutation witness's `{0..=5}` set-image AND the joint witness's
7393// scalar-plus-two-arrays `{0..=6}` set-image but silently misaligns
7394// the runtime pin `atom_kind_hash_discriminators_align_with_all_by_index`
7395// (which iterates `zip(AtomKind::ALL, AtomKind::HASH_DISCRIMINATORS)`
7396// and pins `HASH_DISCRIMINATORS[i] == ALL[i].hash_discriminator()` —
7397// after the swap `HASH_DISCRIMINATORS[0] == 1` but
7398// `ALL[0].hash_discriminator() == AtomKind::Symbol.hash_discriminator()
7399// == 0` fails the alignment at position 0). Post-lift the ARRAY-LEVEL
7400// per-position order binds at rustc time via ONE `const _` witness per
7401// array; a drift at either the per-role `pub(crate) const` byte OR
7402// the array declaration's ordering fails at `cargo check` BEFORE any
7403// test scheduler runs.
7404//
7405// The four `..._pin_legacy_cache_key_bytes` runtime pins the new
7406// witnesses supersede:
7407// * `atom_kind_hash_discriminators_pin_legacy_cache_key_bytes` (in
7408// `ast.rs`) — six `assert_eq!` on `AtomKind::{SYMBOL, KEYWORD, STR,
7409// INT, FLOAT, BOOL}_HASH_DISCRIMINATOR == {0, 1, 2, 3, 4, 5}`.
7410// * `quote_form_hash_discriminators_pin_legacy_cache_key_bytes` (in
7411// `ast.rs`) — four `assert_eq!` on `QuoteForm::{QUOTE, QUASIQUOTE,
7412// UNQUOTE, UNQUOTE_SPLICE}_HASH_DISCRIMINATOR == {3, 4, 5, 6}`.
7413// * `unquote_form_hash_discriminators_pin_legacy_cache_key_bytes` (in
7414// `error.rs`) — two `assert_eq!` on `UnquoteForm::{UNQUOTE,
7415// SPLICE}_HASH_DISCRIMINATOR == {5, 6}`.
7416// * `structural_kind_hash_discriminators_pin_legacy_cache_key_bytes`
7417// (in `error.rs`) — two `assert_eq!` on `StructuralKind::{NIL,
7418// LIST}_HASH_DISCRIMINATOR == {0, 2}`.
7419// Each runtime pin's fourteen total `assert_eq!` inline byte
7420// comparisons collapse to ONE `const _` witness per array (FOUR
7421// `const _` lines total) that bind the SAME per-role byte values
7422// AND the array's per-position ordering at rustc time. The runtime
7423// pins survive as sibling checks — they compose through the
7424// per-role `pub(crate) const` alias directly rather than the outer
7425// container array's declaration; a regression that drifted a
7426// per-role alias's declaration but LEFT the array's slot-i
7427// initializer with the correct literal byte inline (structurally
7428// distinct from the alias-source-of-truth path this family relies
7429// on) fails at the runtime pin as a distinct failure mode.
7430//
7431// Sibling posture to the four-witness EXHAUSTIVE per-position sweep
7432// on the OUTER `SexpShape::HASH_DISCRIMINATORS` container the trio
7433// of `assert_u8_array_slice_equals_u8_array::<12, {1, 4}, {0, 7, 8}>`
7434// witnesses + the `assert_u8_array_slice_is_scalar_replica::<12, 1, 7>`
7435// witness (all inside `ast.rs`, below the `Sexp` Hash impl area)
7436// close on the outer twelve-shape container. Those four witnesses
7437// pin the OUTER container against its FOUR sub-carvings; these four
7438// witnesses pin each SUB-CARVING array against its LITERAL bytes.
7439// Together the eight witnesses close the (outer container, sub-
7440// carving) × (per-position ORDER) 2×N face across the substrate's
7441// entire outer-`Sexp` cache-key hash-discriminator hierarchy at
7442// rustc time.
7443//
7444// `SexpShape::HASH_DISCRIMINATORS` is INTENTIONALLY OMITTED from
7445// this family sweep — the outer twelve-shape → seven-byte NON-
7446// INJECTIVE collapse means the outer container is NOT expressible
7447// as an equality with any literal `[u8; 12]` peer WITHOUT re-
7448// stating the six-way atomic-collapse block-constancy segment
7449// (which is ALREADY pinned via the sibling
7450// `assert_u8_array_slice_is_scalar_replica::<12, 1, 7>` witness
7451// below). The four SUB-CARVING arrays in this sweep ARE injective
7452// on their own carving; each one is expressible as `arr == [b_0,
7453// b_1, …, b_{N-1}]` with each `b_i` a DISTINCT byte, which is
7454// EXACTLY the shape `assert_u8_array_slice_equals_u8_array` at the
7455// FULL-ARRAY corner (`M == N`, `START == 0`) binds.
7456const _: () = assert_u8_array_slice_equals_u8_array::<6, 6, 0>(
7457 &AtomKind::HASH_DISCRIMINATORS,
7458 &[0u8, 1, 2, 3, 4, 5],
7459);
7460const _: () = assert_u8_array_slice_equals_u8_array::<4, 4, 0>(
7461 &QuoteForm::HASH_DISCRIMINATORS,
7462 &[3u8, 4, 5, 6],
7463);
7464const _: () = assert_u8_array_slice_equals_u8_array::<2, 2, 0>(
7465 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
7466 &[5u8, 6],
7467);
7468const _: () = assert_u8_array_slice_equals_u8_array::<2, 2, 0>(
7469 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
7470 &[0u8, 2],
7471);
7472
7473// Compile-time JOINT permutation-of-range witness — the ONE
7474// `(scalar, [u8; M], [u8; N])` triple across the substrate whose
7475// joint carving permutes the OUTER-`Sexp` cache-key discriminator
7476// range `{0..=6}` byte-for-byte. `AtomKind::OUTER_HASH_DISCRIMINATOR
7477// = 1u8` (the scalar arm on the atomic-carve marker byte for
7478// `Sexp::Atom(_)`) merges with `StructuralKind::HASH_DISCRIMINATORS
7479// = [0, 2]` (the structural-residual carving covering `Sexp::Nil`
7480// and `Sexp::List(_)` — NON-contiguous inside its own range because
7481// the `1u8` slot between `0` and `2` is reserved for the atomic-
7482// carve marker; this is EXACTLY the reason `StructuralKind::HASH_
7483// DISCRIMINATORS` stays on the weak-witness pair per the sibling
7484// comment above and does NOT bind through the single-array
7485// permutation helper) and `QuoteForm::HASH_DISCRIMINATORS = [3, 4,
7486// 5, 6]` (the quote-family carving covering `Sexp::Quote(_)` /
7487// `Sexp::Quasiquote(_)` / `Sexp::Unquote(_)` / `Sexp::UnquoteSplice
7488// (_)`, a permutation on its own range) to jointly cover the WHOLE
7489// outer-`Sexp` cache-key partition `{0..=6}`. Pre-lift this joint
7490// bijection was pinned ONLY at runtime via
7491// `structural_kind_hash_discriminator_disjoint_from_atom_outer_
7492// carve_byte_and_quote_form_hash_discriminator_partition` (in
7493// `error.rs`, sweeping the disjointness of the three carvings' byte
7494// spaces + full-range coverage via `BTreeSet`s) and
7495// `sexp_shape_hash_discriminator_partitions_by_three_way_carving_
7496// disjointly` (in `error.rs`, sweeping the shape-level `hash_
7497// discriminator` image partition via the three carvings'
7498// `as_atom_kind` / `as_quote_form` / `as_structural_kind`
7499// projections) — the theorem held only after `cargo test`
7500// scheduled the two tests. Post-lift the JOINT permutation binds at
7501// rustc time via ONE `const _` witness on the substrate CONSTANTS
7502// directly; a drift at ANY of the three carvings (renumbered atomic
7503// marker, an extra `StructuralKind` variant with a byte outside
7504// `{0, 2}`, a collision between `QuoteForm` and the atomic marker,
7505// an arity drift in either array) fails at `cargo check` BEFORE any
7506// test scheduler runs. The two runtime pins survive as sibling
7507// checks for the shape-level projection methods (a distinct failure
7508// mode from a drift in the constants themselves); together with
7509// this const witness the joint outer-`Sexp` partition theorem is
7510// enforced at BOTH stages of the toolchain — const-time on the
7511// CONSTANTS, test-time on the METHODS.
7512//
7513// The scalar-plus-two-arrays joint carving is the FIRST substrate
7514// primitive that binds a bijection across a NON-uniform tuple shape
7515// (a `u8` PLUS a `[u8; 2]` PLUS a `[u8; 4]`) — the pre-existing
7516// single-array `assert_u8_array_permutes_inclusive_range` witnesses
7517// above bind ONE-array permutations; a hypothetical decomposition
7518// through three single-array calls (one per carving) CANNOT bind
7519// the joint bijection without either concatenating the three
7520// carvings at rustc time (impossible without a runtime `Vec`) or
7521// threading joint-distinctness through a bespoke primitive
7522// separately (four+ `const _` lines total). The compound helper
7523// binds the joint theorem at ONE line.
7524const _: () = assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
7525 AtomKind::OUTER_HASH_DISCRIMINATOR,
7526 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
7527 &QuoteForm::HASH_DISCRIMINATORS,
7528);
7529
7530/// Compile-time contract verifier — panics at const evaluation time if
7531/// any entry of `arr` at positions `[START..END)` is NOT byte-equal to
7532/// the peer `scalar`.
7533///
7534/// Opens the SLICE-BLOCK-CONSTANCY column on the (`u8`) row of the
7535/// (element-type × contract-shape) matrix. Row-dual sibling posture to
7536/// the pre-existing (`&'static str`)-row helper
7537/// [`assert_str_array_is_concatenation_of_two_scalar_replicas`], but
7538/// specialised to the (u8) element type AND generalised from a
7539/// FIXED-PARTITION two-block-covering-the-full-array shape to an
7540/// ARBITRARY sub-slice `[START..END)` inside a possibly-longer array
7541/// `[0..N)`. Where the (str)-row helper binds a FULL-ARRAY partition
7542/// `arr == [head; K] ++ [tail; N - K]` (both blocks together cover
7543/// `[0..N)` exactly), this helper binds a SINGLE-BLOCK SUB-SLICE
7544/// `arr[START..END) == [scalar; END - START]` (the positions OUTSIDE
7545/// the slice are unconstrained by this witness — they can carry any
7546/// bytes, potentially bound by peer witnesses on other slices).
7547///
7548/// The load-bearing carrier on the substrate is
7549/// [`crate::error::SexpShape::HASH_DISCRIMINATORS`] (`[u8; 12]`), whose
7550/// positions `[1..7)` INTENTIONALLY collapse onto the SINGLE outer
7551/// cache-key byte [`AtomKind::OUTER_HASH_DISCRIMINATOR`] (`= 1u8`) —
7552/// the six atomic outer shapes (`Symbol` / `Keyword` / `String` /
7553/// `Int` / `Float` / `Bool`) all route through `Sexp::Atom(_)`'s
7554/// SINGLE outer marker byte on the outer-`Sexp` Hash algebra, so their
7555/// twelve-shape → seven-byte collapse condenses SIX shape slots into
7556/// ONE cache-key byte. The six per-role
7557/// [`SexpShape::SYMBOL_HASH_DISCRIMINATOR`] /
7558/// [`SexpShape::KEYWORD_HASH_DISCRIMINATOR`] /
7559/// [`SexpShape::STRING_HASH_DISCRIMINATOR`] /
7560/// [`SexpShape::INT_HASH_DISCRIMINATOR`] /
7561/// [`SexpShape::FLOAT_HASH_DISCRIMINATOR`] /
7562/// [`SexpShape::BOOL_HASH_DISCRIMINATOR`] `pub(crate) const` aliases
7563/// are ALL declared as `= crate::ast::AtomKind::OUTER_HASH_DISCRIMINATOR`
7564/// on the six atomic-outer arms, so the array-level slice-block-
7565/// constancy theorem `SexpShape::HASH_DISCRIMINATORS[1..7) == [OUTER; 6]`
7566/// is the ARRAY-LEVEL surface of the six per-role aliases' shared
7567/// upstream. A regression that silently reroutes ONE alias (e.g.
7568/// renames `SexpShape::SYMBOL_HASH_DISCRIMINATOR` to alias
7569/// `StructuralKind::LIST_HASH_DISCRIMINATOR` instead) OR that reorders
7570/// `SexpShape::ALL` so a NON-atomic shape lands in positions `[1..7)`
7571/// (e.g. moves `SexpShape::List` into slot `4`, drifting `LIST_BYTE
7572/// = 2` into the atomic-collapse slice) fails at `cargo check` BEFORE
7573/// any test scheduler runs, at ONE `const _` line rather than at the
7574/// pre-lift sextet of per-alias assertions inside
7575/// `sexp_shape_hash_discriminators_pin_legacy_outer_cache_key_bytes`.
7576///
7577/// Bounds preconditions and gate ordering: three CARDINALITY gates
7578/// fire at the TOP of the sweep BEFORE any per-position content check
7579/// begins, so a caller-side turbofish arity slip fails-loud on the
7580/// bounds axis rather than silently degenerating into a truncated or
7581/// vacuous sweep:
7582/// * `START > N` → `START-OUT-OF-BOUNDS` panic. The caller's slice
7583/// start index sits OUTSIDE the array's valid position range
7584/// `[0..N]` (inclusive upper bound: `START == N` is the empty-slice
7585/// corner and is legal). A silent slip past this gate would either
7586/// panic on `arr[i]` bounds-check at runtime (const-eval catches it
7587/// first) or, on an over-cautious future refactor that gated with a
7588/// raw `START >= N`, silently reject the legal `START == N` empty
7589/// corner.
7590/// * `END > N` → `END-OUT-OF-BOUNDS` panic. The caller's slice end
7591/// index sits OUTSIDE the array's valid position range `[0..N]`.
7592/// Analogous to the `START` gate; the two gates jointly enforce
7593/// `START, END ∈ [0..N]` before any content sweep.
7594/// * `START > END` → `INVERTED-RANGE` panic. The caller supplied a
7595/// right-open slice `[START..END)` whose left bound exceeds its
7596/// right bound — mathematically the empty slice, but semantically a
7597/// caller-side turbofish typo (e.g. `::<12, 7, 1>` instead of
7598/// `::<12, 1, 7>`). Routes to a distinct gate rather than silently
7599/// accepting the empty sweep so a coordinated typo across `START`
7600/// and `END` fails-loud on the ordering axis rather than passing
7601/// vacuously.
7602/// * `START == END` is a LEGAL empty-slice corner: the sweep never
7603/// enters the loop body and the helper accepts. The pre-check
7604/// sequence gates only STRICT violations (`>`) so both endpoints
7605/// (`START == 0` and `END == N`) remain in range and the `START ==
7606/// END` empty case degenerates cleanly.
7607///
7608/// Delegates to `u8`'s native `==` in const-fn context — no auxiliary
7609/// byte-equality helper needed (unlike the (str)-row peer's
7610/// [`str_bytes_equal`] delegation). The single-scalar SLICE-BLOCK-
7611/// CONSTANCY sweep is the simplest shape in the const-fn family: ONE
7612/// outer `while i < END` from `i = START`, ONE inner comparison, ONE
7613/// panic arm.
7614///
7615/// Runtime callability: the function is a normal `pub const fn`, so
7616/// callers CAN also invoke it at runtime — pinned by
7617/// `assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_slice_content_drift`,
7618/// `assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_start_out_of_bounds`,
7619/// `assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_end_out_of_bounds`,
7620/// `assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_inverted_range`,
7621/// and
7622/// `assert_u8_array_slice_is_scalar_replica_panic_message_names_the_helper_and_slice_block_constancy_violation_axis`.
7623/// The panic-message axis-provenance strings
7624/// (`"SLICE-BLOCK-CONSTANCY-VIOLATION"`, `"START-OUT-OF-BOUNDS"`,
7625/// `"END-OUT-OF-BOUNDS"`, `"INVERTED-RANGE"`) are chosen DISTINCT
7626/// from every sibling helper's axis vocabulary so a diagnostic that
7627/// crosses helper boundaries stays unambiguous under `grep` on the
7628/// failed axis.
7629///
7630/// Theory grounding:
7631/// - THEORY.md §III — the typescape; the SLICE-BLOCK-CONSTANCY corner
7632/// on the (`u8`) row of the (element-type × contract-shape) matrix
7633/// becomes a TYPE-LEVEL theorem the substrate carries per (arr,
7634/// scalar, START, END) quadruple rather than a runtime iterator
7635/// sweep per quadruple. Complements the pre-existing (str)-row
7636/// FULL-ARRAY BLOCK-CONSTANCY sibling — the two together cover the
7637/// BLOCK-CONSTANCY column at BOTH the SUB-SLICE arity (this helper)
7638/// and the FULL-ARRAY-with-two-blocks arity (the sibling), on
7639/// COMPLEMENTARY element-type rows.
7640/// - THEORY.md §V.1 — knowable platform; the sub-slice constant-
7641/// block contract on the substrate's ONE MANY-TO-ONE-collapse
7642/// `[u8; N]` array binds at `cargo check` time via ONE `const _`
7643/// line, closing the drift-catch loop one invocation stage earlier
7644/// than the pre-lift sextet of per-alias assertions inside
7645/// `sexp_shape_hash_discriminators_pin_legacy_outer_cache_key_bytes`.
7646/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
7647/// cache-key partition is the `intent_hash` composition axis —
7648/// binding the six-way atomic-shape collapse at the ARRAY level
7649/// makes attestation-key drift on the atomic-collapse slice a
7650/// compile error rather than a silent BLAKE3 mis-hash across the
7651/// six atomic outer shapes.
7652/// - THEORY.md §VI.1 — generation over composition; the const-eval
7653/// slice sweep IS the generative shape — every future MANY-TO-ONE
7654/// variant → canonical-projection substrate SUB-SLICE-CONSTANT
7655/// segment (a hypothetical `Signal::CLASSES` array whose middle
7656/// segment collapses several signal variants onto the SAME class
7657/// byte; a `ProcessPhase::CATEGORIES` array whose per-category
7658/// segments each collapse onto a shared category byte) picks up
7659/// the block-constant theorem at ONE new `const _` line rather
7660/// than at (END - START) inline byte comparisons per callsite.
7661pub const fn assert_u8_array_slice_is_scalar_replica<
7662 const N: usize,
7663 const START: usize,
7664 const END: usize,
7665>(
7666 arr: &[u8; N],
7667 scalar: u8,
7668) {
7669 if START > N {
7670 panic!(
7671 "assert_u8_array_slice_is_scalar_replica: START-OUT-OF-\
7672 BOUNDS — the const parameter `START` sits OUTSIDE the \
7673 array's valid position range `[0..N]` (inclusive upper \
7674 bound: `START == N` is the LEGAL empty-slice corner). \
7675 Fix at the `const _` witness's turbofish by reconciling \
7676 `START` against the array's declared arity `N`. The \
7677 START-OUT-OF-BOUNDS gate fires FIRST — a mistyped \
7678 `START` on the caller side fails HERE before any per-\
7679 position sweep begins, so a subtle bounds slip doesn't \
7680 silently degenerate into a vacuous sweep OR a panic \
7681 deeper in `arr[i]` bounds-checking."
7682 );
7683 }
7684 if END > N {
7685 panic!(
7686 "assert_u8_array_slice_is_scalar_replica: END-OUT-OF-\
7687 BOUNDS — the const parameter `END` sits OUTSIDE the \
7688 array's valid position range `[0..N]` (inclusive upper \
7689 bound: `END == N` is the LEGAL slice-to-end corner). \
7690 Fix at the `const _` witness's turbofish by reconciling \
7691 `END` against the array's declared arity `N`. Peer \
7692 gate to the START-OUT-OF-BOUNDS arm above — the two \
7693 gates jointly enforce `START, END ∈ [0..N]` before any \
7694 content sweep."
7695 );
7696 }
7697 if START > END {
7698 panic!(
7699 "assert_u8_array_slice_is_scalar_replica: INVERTED-RANGE \
7700 — the const parameters `START` and `END` satisfy `START \
7701 > END`, so the right-open slice `[START..END)` is \
7702 mathematically empty but semantically a caller-side \
7703 turbofish typo (e.g. `::<N, END, START>` instead of \
7704 `::<N, START, END>`). Routes to a distinct gate rather \
7705 than silently accepting the empty sweep so a coordinated \
7706 typo across the two const parameters fails-loud on the \
7707 ordering axis. The LEGAL empty-slice corner is `START \
7708 == END` (accepted without entering the sweep); the \
7709 STRICT `START > END` slip is what this gate rejects."
7710 );
7711 }
7712 let mut i = START;
7713 while i < END {
7714 if arr[i] != scalar {
7715 panic!(
7716 "assert_u8_array_slice_is_scalar_replica: SLICE-BLOCK-\
7717 CONSTANCY-VIOLATION — the family-wide `[u8; N]` \
7718 array `arr` carries a byte at some position in \
7719 `[START..END)` (the SLICE segment) that does NOT \
7720 byte-equal the peer `scalar`. The substrate's SLICE-\
7721 BLOCK-CONSTANCY contract on the array-slice is \
7722 broken; every consumer that reads `arr[START..END)` \
7723 as a homogeneous constant block sharing ONE \
7724 canonical byte with the peer `scalar` (the SIX \
7725 atomic-outer-shape collapse onto `crate::ast::\
7726 AtomKind::OUTER_HASH_DISCRIMINATOR` at positions \
7727 `[1..7)` of `crate::error::SexpShape::HASH_\
7728 DISCRIMINATORS` — the twelve-shape → seven-byte \
7729 collapse condensing SIX atomic shape slots into ONE \
7730 outer-`Sexp` cache-key byte; any future MANY-TO-ONE \
7731 variant → canonical-projection substrate SUB-SLICE-\
7732 CONSTANT segment) relies on this invariant. Fix at \
7733 the ARRAY-DECLARATION site (the drifted `arr[i]` \
7734 entry inside the slice segment) OR at the per-role \
7735 scalar constant that `scalar` re-exports — the \
7736 choice depends on whether the drift is an \
7737 unintended slot reorder inside the slice OR a \
7738 rename of the canonical projection byte upstream."
7739 );
7740 }
7741 i += 1;
7742 }
7743}
7744
7745// Compile-time SLICE-BLOCK-CONSTANCY witness — the ONE `[u8; N]`
7746// array on the substrate whose ARRAY-LEVEL structure carries a MANY-
7747// TO-ONE-COLLAPSE SUB-SLICE segment. `SexpShape::HASH_DISCRIMINATORS`
7748// (`[u8; 12]`) has positions `[1..7)` INTENTIONALLY collapsing onto
7749// the SINGLE outer cache-key byte `AtomKind::OUTER_HASH_DISCRIMINATOR
7750// = 1u8` — the six atomic outer shapes (`Symbol` / `Keyword` /
7751// `String` / `Int` / `Float` / `Bool`) all route through `Sexp::Atom(_)
7752// `'s SINGLE outer marker byte on the outer-`Sexp` Hash algebra, so
7753// their twelve-shape → seven-byte collapse condenses SIX shape slots
7754// into ONE cache-key byte. Pre-lift this six-way collapse was pinned
7755// ONLY through the six per-alias runtime assertions inside
7756// `sexp_shape_hash_discriminators_pin_legacy_outer_cache_key_bytes`
7757// (`assert_eq!(SexpShape::SYMBOL_HASH_DISCRIMINATOR, 1)` × six roles);
7758// post-lift the ARRAY-LEVEL slice constancy `SexpShape::HASH_
7759// DISCRIMINATORS[1..7) == [OUTER; 6]` binds at rustc-time via ONE
7760// `const _` line, one invocation stage earlier than the runtime pin.
7761// A regression that reorders `SexpShape::ALL` so a non-atomic variant
7762// lands in the atomic-collapse slice (e.g. moves `SexpShape::List`
7763// into slot `4`, drifting `LIST_BYTE = 2` into positions `[1..7)`) OR
7764// that reroutes ONE atomic-shape alias to a different upstream byte
7765// fails-loudly HERE at const-eval on the ARRAY-LEVEL slice.
7766//
7767// The three OTHER load-bearing SexpShape::HASH_DISCRIMINATORS slices
7768// are intentionally OMITTED from this compile-time sweep because they
7769// are ALREADY compile-time-enforced through TIGHTER contracts: the
7770// singleton slices `[0..1) == [NIL_BYTE]` and `[7..8) == [LIST_BYTE]`
7771// (`StructuralKind::NIL_HASH_DISCRIMINATOR` and `StructuralKind::LIST_
7772// HASH_DISCRIMINATOR`) are pinned via the peer `assert_scalar_plus_
7773// two_u8_arrays_permute_inclusive_range` witness above (which binds
7774// the outer joint bijection `{0..=6} == {OUTER} ⊕ StructuralKind::
7775// HASH_DISCRIMINATORS ⊕ QuoteForm::HASH_DISCRIMINATORS`); the four-
7776// element tail slice `[8..12) == QuoteForm::HASH_DISCRIMINATORS`
7777// (`[3, 4, 5, 6]`) is pinned via the peer `assert_u8_array_permutes_
7778// inclusive_range::<4, 3, 6>` witness above (which binds the tail
7779// slice's PERMUTATION-of-`[3..=6]` shape). Only the middle atomic-
7780// collapse slice `[1..7)` requires a NEW helper — its shape is
7781// MANY-TO-ONE-BLOCK-CONSTANT rather than PERMUTATION (which is
7782// injective and would fail on this six-of-one collapse) or SUBSET
7783// (which is weaker and would accept the drifted `[1, 1, 2, 1, 1, 1]`
7784// slice that this witness rejects on position 2).
7785const _: () = assert_u8_array_slice_is_scalar_replica::<12, 1, 7>(
7786 &crate::error::SexpShape::HASH_DISCRIMINATORS,
7787 AtomKind::OUTER_HASH_DISCRIMINATOR,
7788);
7789
7790/// Compile-time contract verifier — panics at const evaluation time if
7791/// any entry of `full` at positions `[START..START + M)` is NOT byte-
7792/// equal to the peer `sub` entry at the same offset `[i - START]`.
7793///
7794/// Opens the SLICE-EQUALS-ARRAY column on the (`u8`) row of the
7795/// (element-type × contract-shape) matrix. Row-sibling posture to the
7796/// pre-existing SLICE-BLOCK-CONSTANCY corner on the SAME (`u8`) row
7797/// ([`assert_u8_array_slice_is_scalar_replica`]) — where that helper
7798/// binds a sub-slice `full[START..END) == [scalar; END - START]` to a
7799/// SINGLE scalar (a MANY-TO-ONE-COLLAPSE shape whose per-position
7800/// image is a CONSTANT byte), this helper binds a sub-slice
7801/// `full[START..START + M) == sub[..]` to a SIBLING ARRAY of arity `M`
7802/// (a POSITIONWISE-COMPOSITION shape whose per-position image is
7803/// carried by an INDEPENDENT peer array). The two together cover the
7804/// SUB-SLICE column at BOTH the SINGLE-scalar-image arity (the sibling
7805/// above) AND the ARRAY-of-length-`M`-image arity (this helper).
7806///
7807/// The load-bearing carrier on the substrate is the pair
7808/// ([`crate::error::SexpShape::HASH_DISCRIMINATORS`] (`[u8; 12]`),
7809/// [`QuoteForm::HASH_DISCRIMINATORS`] (`[u8; 4]`)) at the SexpShape
7810/// sub-slice `[8..12)`. Both arrays share their four byte values
7811/// (`3`, `4`, `5`, `6`) via the four per-role
7812/// [`QuoteForm::QUOTE_HASH_DISCRIMINATOR`] /
7813/// [`QuoteForm::QUASIQUOTE_HASH_DISCRIMINATOR`] /
7814/// [`QuoteForm::UNQUOTE_HASH_DISCRIMINATOR`] /
7815/// [`QuoteForm::UNQUOTE_SPLICE_HASH_DISCRIMINATOR`] `pub(crate) const`
7816/// aliases, but the two per-array declaration LISTINGS are independent
7817/// — a regression that reordered ONE array's arm listing without
7818/// reordering the other's (e.g. swapped positions 10 and 11 of the
7819/// outer `SexpShape::HASH_DISCRIMINATORS` array so `UNQUOTE_SPLICE`'s
7820/// byte `6` slid ahead of `UNQUOTE`'s byte `5` while leaving
7821/// `QuoteForm::HASH_DISCRIMINATORS` in its canonical order) would
7822/// preserve BOTH arrays' PERMUTATION-of-`[3..=6]` contract — the
7823/// pre-existing weak witness
7824/// [`assert_u8_array_permutes_inclusive_range::<4, 3, 6>`] on
7825/// [`QuoteForm::HASH_DISCRIMINATORS`] above binds the tail SLICE'S
7826/// image SET (`{3, 4, 5, 6}`) but is SILENT on the tail slice's
7827/// per-position ORDER against the peer [`QuoteForm::HASH_DISCRIMINATORS`]
7828/// listing. Pre-lift the POSITIONWISE-tail-equality against the sub-
7829/// carving array was pinned ONLY at runtime through the per-position
7830/// sweep inside
7831/// `sexp_shape_hash_discriminators_align_with_sub_carvings_by_projection`
7832/// (in `error.rs`), which routes each `SexpShape::HASH_DISCRIMINATORS
7833/// [i]` through the shape's sub-carving projection method
7834/// (`as_quote_form().unwrap().hash_discriminator()`) at test-time.
7835/// Post-lift the ARRAY-LEVEL slice-equals-array contract binds at
7836/// rustc-time via ONE `const _` line on the substrate CONSTANTS
7837/// directly — a regression that reordered ONE array's arm listing
7838/// fails at `cargo check` BEFORE any test scheduler runs.
7839///
7840/// Bounds preconditions and gate ordering: two CARDINALITY gates fire
7841/// at the TOP of the sweep BEFORE any per-position content check
7842/// begins, so a caller-side turbofish arity slip fails-loud on the
7843/// bounds axis rather than silently degenerating into a truncated or
7844/// vacuous sweep:
7845/// * `START > N` → `START-OUT-OF-BOUNDS` panic. The caller's slice
7846/// start index sits OUTSIDE the outer array's valid position range
7847/// `[0..N]` (inclusive upper bound: `START == N` combined with
7848/// `M == 0` is the LEGAL empty-slice corner and is accepted). A
7849/// silent slip past this gate would either panic on `full[START +
7850/// i]` bounds-check at runtime (const-eval catches it first) or, on
7851/// an over-cautious future refactor that gated with a raw `START >=
7852/// N`, silently reject the legal `START == N, M == 0` empty corner.
7853/// * `M > N - START` → `SLICE-LENGTH-OUT-OF-BOUNDS` panic. The
7854/// caller's sub-array arity `M` overruns the outer array's tail
7855/// `[START..N)` — semantically equivalent to `START + M > N` but
7856/// phrased through subtraction to avoid `usize` arithmetic overflow
7857/// on the (unrealistic but pedantic) `START, M` pair with
7858/// `START + M > usize::MAX`; gate 1 guarantees `START ≤ N` so
7859/// `N - START` never underflows. The overrun would land the sweep
7860/// at `full[START + i]` for some `i ∈ [N - START..M)` and panic
7861/// deeper in bounds-checking with a helper-name-less message.
7862/// * `M == 0` is a LEGAL empty-sub-array corner (the sweep never
7863/// enters the loop body and the helper accepts, regardless of
7864/// `START`). `START == N` with `M == 0` collapses to the empty-slice
7865/// corner at the RIGHT endpoint. The pre-check sequence gates only
7866/// STRICT violations (`>`) so both degenerate corners
7867/// (`M == 0`, `M == N - START` at the exact-fit right endpoint)
7868/// remain in range.
7869///
7870/// Delegates to `u8`'s native `==` in const-fn context — no auxiliary
7871/// byte-equality helper needed. The two-array positionwise-composition
7872/// sweep is the natural arity extension of the single-scalar sibling
7873/// above: ONE outer `while i < M` from `i = 0`, ONE inner comparison
7874/// `full[START + i] != sub[i]`, ONE panic arm.
7875///
7876/// Runtime callability: the function is a normal `pub const fn`, so
7877/// callers CAN also invoke it at runtime — pinned by
7878/// `assert_u8_array_slice_equals_u8_array_panics_at_runtime_on_positionwise_drift`,
7879/// `assert_u8_array_slice_equals_u8_array_panics_at_runtime_on_start_out_of_bounds`,
7880/// `assert_u8_array_slice_equals_u8_array_panics_at_runtime_on_slice_length_out_of_bounds`,
7881/// and
7882/// `assert_u8_array_slice_equals_u8_array_panic_message_names_the_helper_and_slice_equals_array_violation_axis`.
7883/// The panic-message axis-provenance strings
7884/// (`"SLICE-EQUALS-ARRAY-VIOLATION"`, `"START-OUT-OF-BOUNDS"`,
7885/// `"SLICE-LENGTH-OUT-OF-BOUNDS"`) are chosen DISTINCT from every
7886/// sibling helper's axis vocabulary — the sibling
7887/// [`assert_u8_array_slice_is_scalar_replica`] carries
7888/// `"SLICE-BLOCK-CONSTANCY-VIOLATION"` / `"END-OUT-OF-BOUNDS"` /
7889/// `"INVERTED-RANGE"` on the SINGLE-scalar-image sweep, so a
7890/// diagnostic that crosses the two SUB-SLICE helpers routes back to
7891/// its authoring helper by axis string alone. The
7892/// `SLICE-LENGTH-OUT-OF-BOUNDS` axis renames what would be the
7893/// sibling's `END-OUT-OF-BOUNDS` gate to reflect the shift from
7894/// `(START, END)` const generics to `(START, M)` — the CARDINALITY
7895/// carrier is the peer array's arity `M`, not an END index.
7896///
7897/// Theory grounding:
7898/// - THEORY.md §III — the typescape; the SLICE-EQUALS-ARRAY corner on
7899/// the (`u8`) row of the (element-type × contract-shape) matrix
7900/// becomes a TYPE-LEVEL theorem the substrate carries per
7901/// `(full, sub, START)` triple rather than a runtime iterator sweep
7902/// through the sub-carving projection method per triple.
7903/// Complements the pre-existing SLICE-BLOCK-CONSTANCY sibling on
7904/// the SAME row — the two together open the SUB-SLICE column at
7905/// BOTH the SINGLE-scalar-image arity (constant block) AND the
7906/// ARRAY-of-length-`M`-image arity (positionwise composition).
7907/// - THEORY.md §V.1 — knowable platform; the positionwise
7908/// composition contract binding
7909/// `SexpShape::HASH_DISCRIMINATORS[8..12] ==
7910/// QuoteForm::HASH_DISCRIMINATORS` at rustc-time closes the drift-
7911/// catch loop one invocation stage earlier than the pre-lift
7912/// per-position runtime sweep inside
7913/// `sexp_shape_hash_discriminators_align_with_sub_carvings_by_projection`.
7914/// The runtime pin survives as a sibling check for the SHAPE-level
7915/// projection method chain (a distinct failure mode from a drift
7916/// in the constants themselves); together the two enforce the
7917/// theorem at BOTH stages of the toolchain.
7918/// - THEORY.md §V.3 — three-pillar attestation; the outer-`Sexp`
7919/// cache-key partition is the `intent_hash` composition axis —
7920/// binding the quote-family tail-slice's per-position ORDER at the
7921/// ARRAY level makes attestation-key drift on the four-slot quote-
7922/// family sub-carving a compile error rather than a silent BLAKE3
7923/// mis-hash across the four quote-family outer shapes.
7924/// - THEORY.md §VI.1 — generation over composition; the const-eval
7925/// slice sweep IS the generative shape — every future substrate
7926/// POSITIONWISE-COMPOSITION between a container array's sub-slice
7927/// and a sub-carving's canonical `[u8; M]` listing (a hypothetical
7928/// `Signal::CATEGORIES` outer array whose middle segment is
7929/// pointwise equal to a `CriticalSignal::CATEGORIES` sub-carving
7930/// array; a `ProcessPhase::ORDER` array whose leading segment is
7931/// pointwise equal to a `PreparationPhase::ORDER` sub-carving)
7932/// picks up the positionwise-equality theorem at ONE new `const _`
7933/// line rather than at `M` inline byte comparisons per callsite.
7934pub const fn assert_u8_array_slice_equals_u8_array<
7935 const N: usize,
7936 const M: usize,
7937 const START: usize,
7938>(
7939 full: &[u8; N],
7940 sub: &[u8; M],
7941) {
7942 if START > N {
7943 panic!(
7944 "assert_u8_array_slice_equals_u8_array: START-OUT-OF-\
7945 BOUNDS — the const parameter `START` sits OUTSIDE the \
7946 outer array's valid position range `[0..N]` (inclusive \
7947 upper bound: `START == N` combined with `M == 0` is the \
7948 LEGAL empty-slice-at-right-endpoint corner). Fix at the \
7949 `const _` witness's turbofish by reconciling `START` \
7950 against the outer array's declared arity `N`. The \
7951 START-OUT-OF-BOUNDS gate fires FIRST — a mistyped \
7952 `START` on the caller side fails HERE before the peer \
7953 `SLICE-LENGTH-OUT-OF-BOUNDS` gate reads `N - START` \
7954 (which would underflow `usize` had this gate not caught \
7955 the slip), so a subtle bounds slip doesn't silently \
7956 degenerate into a subtraction wrap-around OR a panic \
7957 deeper in `full[START + i]` bounds-checking."
7958 );
7959 }
7960 if M > N - START {
7961 panic!(
7962 "assert_u8_array_slice_equals_u8_array: SLICE-LENGTH-OUT-\
7963 OF-BOUNDS — the peer sub-array's arity `M` exceeds the \
7964 outer array's tail cardinality `N - START`, so the \
7965 positionwise sweep `full[START + i]` for `i ∈ [0..M)` \
7966 would overrun the outer array's valid position range \
7967 `[0..N)` at some `i ∈ [N - START..M)`. Fix at the \
7968 `const _` witness's turbofish by reconciling `M` against \
7969 the outer array's tail cardinality `N - START` OR by \
7970 narrowing `START` to leave a longer tail. The peer \
7971 `START-OUT-OF-BOUNDS` gate above guarantees `START ≤ N` \
7972 so `N - START` never underflows `usize` at this gate. \
7973 The LEGAL exact-fit corner `M == N - START` (the sub-\
7974 array reaches EXACTLY to the outer array's right \
7975 endpoint) is accepted; the STRICT `M > N - START` slip \
7976 is what this gate rejects."
7977 );
7978 }
7979 let mut i = 0;
7980 while i < M {
7981 if full[START + i] != sub[i] {
7982 panic!(
7983 "assert_u8_array_slice_equals_u8_array: SLICE-EQUALS-\
7984 ARRAY-VIOLATION — the outer `[u8; N]` array `full` \
7985 carries a byte at some position `START + i` (for \
7986 `i ∈ [0..M)`) that does NOT byte-equal the peer \
7987 `[u8; M]` sub-array `sub` at the offset-matched \
7988 position `i`. The substrate's SLICE-EQUALS-ARRAY \
7989 positionwise-composition contract on the sub-slice \
7990 `full[START..START + M) == sub[..]` is broken; \
7991 every consumer that reads `full[START..START + M)` \
7992 as a positionwise-aligned copy of the peer sub-\
7993 carving array (the four-slot QUOTE-family tail \
7994 `crate::error::SexpShape::HASH_DISCRIMINATORS[8..12] \
7995 == crate::ast::QuoteForm::HASH_DISCRIMINATORS`; any \
7996 future container-array sub-slice byte-for-byte equal \
7997 to a peer sub-carving's canonical `[u8; M]` listing) \
7998 relies on this invariant. Fix at the ARRAY-\
7999 DECLARATION site (the drifted `full[START + i]` \
8000 entry inside the slice segment) OR at the peer sub-\
8001 array's arm listing — the choice depends on whether \
8002 the drift is an unintended slot reorder in the outer \
8003 array's tail OR in the sub-carving's own listing."
8004 );
8005 }
8006 i += 1;
8007 }
8008}
8009
8010// Compile-time SLICE-EQUALS-ARRAY witness — the ONE `(container,
8011// sub-carving)` pair on the substrate whose ARRAY-LEVEL structure
8012// composes a container-array sub-slice byte-for-byte identical to a
8013// peer sub-carving's canonical `[u8; M]` listing. `SexpShape::HASH_
8014// DISCRIMINATORS[8..12]` (the four-slot quote-family tail of the
8015// outer twelve-shape array, `[3, 4, 5, 6]`) is byte-for-byte equal
8016// to `QuoteForm::HASH_DISCRIMINATORS` (the four-slot quote-family
8017// carving's canonical listing, `[3, 4, 5, 6]`). Both arrays share
8018// their four byte values via the four per-role `QuoteForm::{QUOTE,
8019// QUASIQUOTE, UNQUOTE, UNQUOTE_SPLICE}_HASH_DISCRIMINATOR`
8020// `pub(crate) const` aliases (the outer SexpShape array's tail-arm
8021// initializers each spell `Self::<ROLE>_HASH_DISCRIMINATOR` where
8022// each `SexpShape::<ROLE>_HASH_DISCRIMINATOR` is declared as an
8023// alias for `QuoteForm::<ROLE>_HASH_DISCRIMINATOR`), so the array-
8024// level positionwise-equality theorem `SexpShape::HASH_DISCRIMINATORS
8025// [8..12] == QuoteForm::HASH_DISCRIMINATORS` is the ARRAY-LEVEL
8026// surface of the four per-role aliases' shared upstream. Pre-lift
8027// this per-position tail equality was pinned ONLY at runtime through
8028// the per-position sweep inside `sexp_shape_hash_discriminators_
8029// align_with_sub_carvings_by_projection` (in `error.rs`, routing
8030// each `SexpShape::HASH_DISCRIMINATORS[i]` through the shape's sub-
8031// carving projection `as_quote_form().unwrap().hash_discriminator()`
8032// on the four quote-family arms); post-lift the ARRAY-LEVEL slice-
8033// equals-array contract binds at rustc-time via ONE `const _` line,
8034// one invocation stage earlier than the runtime pin. A regression
8035// that reorders the outer `SexpShape::HASH_DISCRIMINATORS` array's
8036// quote-family tail (e.g. swaps positions 10 and 11 so `UNQUOTE_
8037// SPLICE_HASH_DISCRIMINATOR = 6` slides ahead of `UNQUOTE_HASH_
8038// DISCRIMINATOR = 5`) while leaving `QuoteForm::HASH_DISCRIMINATORS`
8039// in its canonical order fails at `cargo check` BEFORE any test
8040// scheduler runs. The pre-existing `assert_u8_array_permutes_
8041// inclusive_range::<4, 3, 6>(&QuoteForm::HASH_DISCRIMINATORS)`
8042// witness above is STRICTLY WEAKER — it binds the sub-carving
8043// array's image SET is `{3, 4, 5, 6}` but is SILENT on the outer
8044// container array's tail per-position ORDER against the sub-
8045// carving's listing.
8046//
8047// The atomic-collapse sub-slice `SexpShape::HASH_DISCRIMINATORS
8048// [1..7)` stays on the sibling `assert_u8_array_slice_is_scalar_
8049// replica::<12, 1, 7>` witness above because its shape is MANY-TO-
8050// ONE-COLLAPSE onto a single scalar `AtomKind::OUTER_HASH_
8051// DISCRIMINATOR`, not POSITIONWISE-COMPOSITION with a peer array —
8052// there is no natural `[u8; 6]` sub-carving array to compose it
8053// against (the six atomic outer shapes collapse to a SCALAR image,
8054// not an array image). The two singleton slices `[0..1)` and
8055// `[7..8)` on the outer container are pinned via the two sibling
8056// `assert_u8_array_slice_equals_u8_array::<12, 1, _>` witnesses
8057// IMMEDIATELY BELOW — see the prose comment there for the per-
8058// slot binding on `StructuralKind::NIL_HASH_DISCRIMINATOR` and
8059// `StructuralKind::LIST_HASH_DISCRIMINATOR`. Together the four
8060// SexpShape `HASH_DISCRIMINATORS` sub-slice witnesses (the
8061// singleton `[0..1)`, the atomic-collapse `[1..7)`, the singleton
8062// `[7..8)`, the quote-family tail `[8..12)`) EXHAUSTIVELY pin the
8063// twelve-slot outer container's per-position byte SHAPE at rustc-
8064// time, closing the corner the runtime sweep
8065// `sexp_shape_hash_discriminators_align_with_sub_carvings_by_projection`
8066// (in `error.rs`) previously covered alone.
8067const _: () = assert_u8_array_slice_equals_u8_array::<12, 4, 8>(
8068 &crate::error::SexpShape::HASH_DISCRIMINATORS,
8069 &QuoteForm::HASH_DISCRIMINATORS,
8070);
8071
8072// Compile-time SLICE-EQUALS-ARRAY singleton witnesses — the two
8073// remaining unpinned-at-rustc-time slots on
8074// `crate::error::SexpShape::HASH_DISCRIMINATORS` (`[u8; 12]`). Both
8075// are covered by the LARGER sub-carving arrays' `[u8; 1]` slice
8076// projections against the container's singleton slice.
8077//
8078// * Position `[0..1)` — `SexpShape::HASH_DISCRIMINATORS[0] ==
8079// StructuralKind::HASH_DISCRIMINATORS[0] ==
8080// StructuralKind::NIL_HASH_DISCRIMINATOR == 0u8`. The outer
8081// container's slot-0 initializer spells `Self::NIL_HASH_
8082// DISCRIMINATOR` which is declared as an alias for
8083// `StructuralKind::NIL_HASH_DISCRIMINATOR` (see
8084// `SexpShape::NIL_HASH_DISCRIMINATOR = StructuralKind::NIL_HASH_
8085// DISCRIMINATOR` in `error.rs`). Comparing against the singleton
8086// `[StructuralKind::NIL_HASH_DISCRIMINATOR]` sub-array binds the
8087// container's SLOT-0 POSITION to the structural-residual
8088// carving's NIL role at rustc-time.
8089// * Position `[7..8)` — the mirror slot at the atomic-collapse
8090// right endpoint, spelling `Self::LIST_HASH_DISCRIMINATOR` which
8091// aliases `StructuralKind::LIST_HASH_DISCRIMINATOR = 2u8`.
8092//
8093// Pre-lift these two per-position bindings were pinned ONLY at
8094// runtime through the twelve-position sweep inside
8095// `sexp_shape_hash_discriminators_align_with_sub_carvings_by_projection`
8096// (routing each `SexpShape::HASH_DISCRIMINATORS[i]` through the
8097// shape's sub-carving projection `as_structural_kind().unwrap().
8098// hash_discriminator()` on the two structural-residual arms);
8099// post-lift the ARRAY-LEVEL slice-equals-array contract binds at
8100// rustc-time via TWO `const _` lines, one invocation stage earlier
8101// than the runtime pin. The peer joint witness
8102// `assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4,
8103// 0, 6>(AtomKind::OUTER_HASH_DISCRIMINATOR,
8104// &StructuralKind::HASH_DISCRIMINATORS,
8105// &QuoteForm::HASH_DISCRIMINATORS)` above is STRICTLY WEAKER on
8106// these two slots — it binds the SUB-CARVING array
8107// `StructuralKind::HASH_DISCRIMINATORS = [0, 2]` per-position order
8108// but is SILENT on which SLOTS of the outer CONTAINER `SexpShape::
8109// HASH_DISCRIMINATORS` the sub-carving's two bytes land at (a
8110// regression that swapped `Self::NIL_HASH_DISCRIMINATOR` and
8111// `Self::LIST_HASH_DISCRIMINATOR` in the outer array initializer
8112// so slot 0 becomes `2u8` and slot 7 becomes `0u8` would preserve
8113// the sub-carving array's `[0, 2]` order the joint witness pins
8114// AND the outer array's global `{0..=6}` set-image the joint
8115// witness pins, but would silently corrupt the outer container's
8116// per-position slot-to-carving-role mapping the two new witnesses
8117// below reject).
8118//
8119// Sibling posture to the four-slot QUOTE-family tail
8120// `assert_u8_array_slice_equals_u8_array::<12, 4, 8>(&SexpShape::
8121// HASH_DISCRIMINATORS, &QuoteForm::HASH_DISCRIMINATORS)` witness
8122// IMMEDIATELY ABOVE — that witness carries the sub-carving
8123// projection at the LARGEST arity (4 positions in a single
8124// `const _` line); these two witnesses carry the sub-carving
8125// projection at the SMALLEST arity (1 position each, at the two
8126// disjoint singleton slots the structural-residual carving
8127// occupies on the outer container). Together the three SLICE-
8128// EQUALS-ARRAY witnesses cover the ENTIRE SexpShape ⊃
8129// {StructuralKind, QuoteForm} sub-carving composition at rustc-
8130// time — `SexpShape::HASH_DISCRIMINATORS[0..1] ∪ [7..8) ∪ [8..12)`
8131// exhausts the eight non-atomic slots on the outer container, so
8132// combined with the sibling `SLICE-BLOCK-CONSTANCY` witness on
8133// `[1..7)` the twelve-slot outer array is FULLY pinned per-
8134// position at rustc-time.
8135//
8136// The two witnesses use INLINE `[u8; 1]` singleton arrays rather
8137// than `&StructuralKind::HASH_DISCRIMINATORS[0..1]` slice syntax
8138// because the helper's signature takes `&[u8; M]` (a const-generic
8139// array reference, arity-known at rustc time) rather than `&[u8]`
8140// (a slice type with runtime-length). The inline arrays project
8141// the two per-role `pub(crate) const` bytes directly — a
8142// regression that renamed one of the two aliases fails at the
8143// alias's declaration site FIRST (a missing symbol referent),
8144// which routes to a distinct diagnostic axis rather than to the
8145// witness's SLICE-EQUALS-ARRAY-VIOLATION panic.
8146const _: () = assert_u8_array_slice_equals_u8_array::<12, 1, 0>(
8147 &crate::error::SexpShape::HASH_DISCRIMINATORS,
8148 &[crate::error::StructuralKind::NIL_HASH_DISCRIMINATOR],
8149);
8150const _: () = assert_u8_array_slice_equals_u8_array::<12, 1, 7>(
8151 &crate::error::SexpShape::HASH_DISCRIMINATORS,
8152 &[crate::error::StructuralKind::LIST_HASH_DISCRIMINATOR],
8153);
8154
8155// `Sexp` is `PartialEq` but not `Eq` (Float contains NaN). We implement Hash
8156// manually so cache keys can hash a borrowed `&[Sexp]` directly — avoids the
8157// serde_json serialization that would otherwise dominate cache overhead on
8158// cheap macro calls.
8159//
8160// The outer per-variant discriminator byte (`0` for Nil, `1` for Atom, `2`
8161// for List, `3..=6` for the four quote-family variants) binds at ONE site
8162// on the outer-`Sexp` algebra (`Sexp::hash_discriminator`) rather than at
8163// four per-arm byte projections here. The seven outer-variant arms
8164// partition `{0..=6}` injectively; the outer-`Sexp` method routes through
8165// the intermediate shape-level projection `SexpShape::hash_discriminator`
8166// (the 12 outer shapes → 7 outer bytes; the six atomic-shape arms
8167// collapse to the outer Atom marker byte `1`, symmetric with `Sexp::Atom(_)
8168// → 1u8` on the outer method), which in turn composes through the three
8169// sub-carvings' typed discriminator methods:
8170// * `StructuralKind::hash_discriminator` for the two-arm structural-
8171// residual partition `{0, 2}` (Nil, List);
8172// * `AtomKind::hash_discriminator` for the nested atomic payload
8173// `{0..=5}` inside the Atom outer byte `1` (surfaced INSIDE
8174// `Hash for Atom`, NOT through this outer method OR the shape-level
8175// method);
8176// * `QuoteForm::hash_discriminator` for the quote-family arms `{3..=6}`.
8177// The outer-`Sexp` cache-key algebra now closes at FIVE typed layers
8178// (outer `Sexp` → `SexpShape` → three sub-carvings). A future eighth
8179// `Sexp` variant (e.g. `Vector` / `Map` / `Char`) picks a fresh cache-key
8180// byte outside `{0..=6}` and lands at ONE new arm on
8181// `Sexp::hash_discriminator` (or, one algebra level up, at ONE new arm on
8182// `SexpShape::hash_discriminator` — extending either `StructuralKind` if
8183// it is structural-residual or a fresh sub-algebra), with rustc binding
8184// the consistency through exhaustiveness over each closed enum.
8185//
8186// The per-arm body below only handles the inner-payload hash sequence
8187// after the outer discriminator byte is routed through
8188// `self.hash_discriminator().hash(h)` — the recursive `inner.hash(h)` on
8189// the quote-family arm is asserted-total via `expect_quote_form` (the
8190// outer pattern guarantees the projection lands `Some`).
8191impl Hash for Sexp {
8192 fn hash<H: Hasher>(&self, h: &mut H) {
8193 self.hash_discriminator().hash(h);
8194 match self {
8195 Self::Nil => {}
8196 Self::Atom(a) => a.hash(h),
8197 Self::List(items) => {
8198 items.len().hash(h);
8199 for i in items {
8200 i.hash(h);
8201 }
8202 }
8203 Self::Quote(_) | Self::Quasiquote(_) | Self::Unquote(_) | Self::UnquoteSplice(_) => {
8204 let (_, inner) = self.expect_quote_form();
8205 inner.hash(h);
8206 }
8207 }
8208 }
8209}
8210
8211// The six atomic variants share the (discriminator, inner) hash shape —
8212// the per-variant discriminator byte binds at ONE site on the outer-`Atom`
8213// algebra (`Atom::hash_discriminator`) rather than at six inline
8214// `<N>u8.hash(h)` arms here. The outer-value method composes through the
8215// pre-existing marker-level projection `AtomKind::hash_discriminator` (via
8216// `self.kind().hash_discriminator()`) so the (Atom variant, byte) pairing
8217// lives at ONE canonical site on the closed-set `AtomKind` algebra rather
8218// than at TWO (both a parallel six-arm match here AND `AtomKind::hash_
8219// discriminator`'s canonical site). The inner-payload arm stays a match
8220// because the payload type differs per variant (`String` for symbol /
8221// keyword / str, `i64` for int, `f64::to_bits()` for float, `bool` for
8222// bool); the or-pattern collapses the three string-carrying arms. Float:
8223// hash the bit pattern. NaN != NaN so PartialEq is broken, but cache
8224// lookups use PartialEq-by-hash which this satisfies modulo a NaN
8225// collision risk we accept for template args. The (Atom variant, byte)
8226// pairing is pinned bit-for-bit by `atom_kind_hash_discriminator_pins_
8227// legacy_atom_cache_key_bytes` against the pre-lift 0/1/2/3/4/5 sequence
8228// — same posture as `quote_form_hash_discriminator_pins_legacy_cache_
8229// key_bytes` for the four-of-thirteen `Sexp` wrapper variants. Post-lift
8230// the outer-`Atom` `Hash` body is structurally parallel to `Hash for
8231// Sexp` one algebra layer up — both spell `self.hash_discriminator().hash
8232// (h); <inner-payload-hash>` at the outer-value carrier, with the byte
8233// binding routed through the outer-value-level projection at each
8234// algebra. Routing identity pinned by
8235// `hash_for_atom_routes_atom_discriminator_through_atom_hash_discriminator`
8236// on the outer-`Atom` algebra, sibling of
8237// `hash_for_sexp_routes_outer_discriminator_through_sexp_hash_discriminator`
8238// on the outer-`Sexp` algebra.
8239impl Hash for Atom {
8240 fn hash<H: Hasher>(&self, h: &mut H) {
8241 self.hash_discriminator().hash(h);
8242 match self {
8243 Self::Symbol(s) | Self::Keyword(s) | Self::Str(s) => s.hash(h),
8244 Self::Int(n) => n.hash(h),
8245 Self::Float(f) => f.to_bits().hash(h),
8246 Self::Bool(b) => b.hash(h),
8247 }
8248 }
8249}
8250
8251/// An S-expression — the homoiconic value + program representation.
8252/// Lowercase hex for a codepoint, without pulling in a formatter — the escaper
8253/// is on the Display path and must not recurse into it.
8254fn alloc_hex(mut n: u32) -> String {
8255 if n == 0 {
8256 return "0".to_string();
8257 }
8258 const DIGITS: &[u8; 16] = b"0123456789abcdef";
8259 let mut buf = Vec::with_capacity(6);
8260 while n > 0 {
8261 buf.push(DIGITS[(n % 16) as usize]);
8262 n /= 16;
8263 }
8264 buf.reverse();
8265 String::from_utf8(buf).expect("hex digits are ascii")
8266}
8267
8268#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
8269pub enum Sexp {
8270 Nil,
8271 Atom(Atom),
8272 List(Vec<Sexp>),
8273 /// `'x` — literal; does not participate in macro substitution.
8274 Quote(Box<Sexp>),
8275 /// `` `x `` — quasi-quotation; substitution happens inside.
8276 Quasiquote(Box<Sexp>),
8277 /// `,x` — substitute the binding named `x`. Only valid inside a quasi-quote.
8278 Unquote(Box<Sexp>),
8279 /// `,@x` — splice the list `x` into the containing list.
8280 UnquoteSplice(Box<Sexp>),
8281}
8282
8283#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
8284pub enum Atom {
8285 /// Plain symbol (`foo`, `defpoint`, `seph.1`).
8286 Symbol(String),
8287 /// Keyword (`:parent`, `:attr`) — a symbol bound to itself.
8288 Keyword(String),
8289 /// String literal.
8290 Str(String),
8291 /// Integer literal.
8292 Int(i64),
8293 /// Floating literal.
8294 Float(f64),
8295 /// Boolean literal (`#t`, `#f`).
8296 Bool(bool),
8297}
8298
8299impl Atom {
8300 /// The canonical `:` marker prefix that a [`Self::Keyword`] payload
8301 /// projects THROUGH when rendered as / classified from its
8302 /// canonical-string surface across the substrate's four
8303 /// Keyword-round-trip sites — the reader-entry classifier
8304 /// ([`Self::from_lexeme`]), the Lisp-canonical-form projection
8305 /// ([`fmt::Display for Atom`]), the JSON-canonical-form projection
8306 /// ([`Self::to_json`]), and the iac-forge-canonical-form projection
8307 /// (`Atom::to_iac_forge_sexpr` (removed)).
8308 ///
8309 /// Pre-lift the same `":"` byte lived inline at four sites: `':'`
8310 /// (as a `char` pattern) at the [`Self::from_lexeme`] strip site
8311 /// and `":{s}"` (three byte-identical format-string literals) at the
8312 /// three canonical-form projection sites. Post-lift the marker
8313 /// byte lives at ONE canonical constant on the [`Atom`] algebra
8314 /// that all four sites bind to; a future refactor that swaps the
8315 /// marker (e.g. a Racket-compat port to `#:name`, a Clojure-compat
8316 /// port to `::name`) touches ONE line rather than four inline
8317 /// bytes that would silently drift out of round-trip agreement if
8318 /// one was updated without the others.
8319 ///
8320 /// Load-bearing round-trip contract:
8321 /// `Self::from_lexeme(&Self::keyword(name).to_string()) ==
8322 /// Self::keyword(name)` — the reader-entry classifier's
8323 /// `strip_prefix(Self::KEYWORD_MARKER)` gate and the Lisp-canonical
8324 /// [`Display`]'s `write!(f, "{}{name}", Self::KEYWORD_MARKER)`
8325 /// emission both bind to THIS constant so the pair cannot drift.
8326 /// Cross-surface round-trip: `Self::to_json` and
8327 /// `Atom::to_iac_forge_sexpr` (removed) emit the same prefix so any BLAKE3
8328 /// attestation over an iac-forge canonical form of a Keyword atom
8329 /// matches the JSON canonical form byte-for-byte on the prefix
8330 /// axis.
8331 ///
8332 /// Sibling-shape lift to the workspace's other prefix-marker
8333 /// constants: [`crate::error::UnquoteForm::ALL`] projects each
8334 /// template-marker variant to its punctuation prefix via
8335 /// [`crate::ast::QuoteForm::prefix`] (`"'"`, `"`"`, `","`, `",@"`)
8336 /// — a peer axis on the substrate's marker-byte algebra. This
8337 /// constant sits on the [`Atom`] algebra at the atomic-payload
8338 /// axis where the [`QuoteForm::prefix`] projection sits at the
8339 /// homoiconic-wrapper axis.
8340 ///
8341 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
8342 /// (Keyword payload, canonical `:` prefix) pairing now binds at
8343 /// ONE constant on the closed-set [`Atom`] algebra regardless of
8344 /// which of the four reader-entry / rendering surfaces reaches in.
8345 /// THEORY.md §VI.1 — generation over composition; the four
8346 /// byte-identical `":"` / `":{s}"` inline literals collapse onto
8347 /// ONE named constant, matching the substrate's three-times rule
8348 /// (four occurrences, well past the ≥2 lift threshold).
8349 /// THEORY.md §V.1 — knowable platform; the canonical
8350 /// keyword-marker byte becomes a TYPE-level constant on the
8351 /// substrate algebra rather than four inline bytes at four
8352 /// consumer surfaces.
8353 pub const KEYWORD_MARKER: &'static str = ":";
8354
8355 /// Canonical `:` LEAD `char` of the [`Self::KEYWORD_MARKER`] prefix
8356 /// (`":"`) — the ONE canonical `char` on the [`Atom`] algebra the
8357 /// substrate's Keyword-prefix lead-byte disjointness contract binds
8358 /// to.
8359 ///
8360 /// Sibling posture to the closed set of `pub const` reader-punctuation
8361 /// canonical `char` bytes on the substrate: [`Self::STR_DELIMITER`]
8362 /// (`'"'`), [`Self::STR_ESCAPE_LEAD`] (`'\\'`),
8363 /// [`Self::BOOL_LITERAL_LEAD`] (`'#'`), [`Sexp::LIST_OPEN`] (`'('`),
8364 /// [`Sexp::LIST_CLOSE`] (`')'`), [`Sexp::COMMENT_LEAD`] (`';'`),
8365 /// [`Sexp::COMMENT_TERM`] (`'\n'`), [`QuoteForm::SPLICE_DISCRIMINATOR`]
8366 /// (`'@'`) — every canonical per-role byte the reader's tokenizer
8367 /// specialises on is a `pub const` on its owning closed-set algebra.
8368 /// This constant closes the Keyword-prefix lead byte at the SAME
8369 /// algebra as its one-char [`Self::KEYWORD_MARKER`] `&'static str`
8370 /// projection — the ONE `char` that the `&'static str` projects to
8371 /// when the substrate's consumer needs a `char` (not a `&str`) for
8372 /// cross-axis marker-byte comparisons or for the reader's
8373 /// specific-arm outer-dispatch.
8374 ///
8375 /// Structural round-trip contract:
8376 /// `Self::KEYWORD_MARKER.starts_with(Self::KEYWORD_MARKER_LEAD)` —
8377 /// pinned by
8378 /// `atom_keyword_marker_lead_prefixes_keyword_marker`. A regression
8379 /// that drifts EITHER the constant OR the [`Self::KEYWORD_MARKER`]
8380 /// spelling surfaces at the pin rather than at a silent Keyword-
8381 /// prefix reader drift where `:foo` classifies as [`Self::Symbol`]
8382 /// instead of [`Self::Keyword`]. Sibling-shape peer of
8383 /// [`Self::BOOL_LITERAL_LEAD`]'s
8384 /// `Self::bool_literal(b).starts_with(Self::BOOL_LITERAL_LEAD)`
8385 /// round-trip law: where that pin binds the Bool-family two spellings
8386 /// to their shared lead byte, this pin binds the Keyword-marker
8387 /// one-char prefix to its projected lead byte.
8388 ///
8389 /// Disjointness contract: `KEYWORD_MARKER_LEAD`'s byte MUST differ
8390 /// from [`Self::STR_DELIMITER`] (`'"'`), [`Self::STR_ESCAPE_LEAD`]
8391 /// (`'\\'`), [`Self::BOOL_LITERAL_LEAD`] (`'#'`),
8392 /// [`Sexp::LIST_OPEN`] (`'('`), [`Sexp::LIST_CLOSE`] (`')'`),
8393 /// [`Sexp::COMMENT_LEAD`] (`';'`), [`Sexp::COMMENT_TERM`]
8394 /// (`'\n'`), every [`QuoteForm::lead_char`] projection AND
8395 /// [`QuoteForm::SPLICE_DISCRIMINATOR`] (`'@'`) — every other
8396 /// closed-set outer-marker byte the reader's tokenizer specialises
8397 /// on. A collision would silently break the reader's outer
8398 /// dispatch: a `:`-prefixed bare atom `:foo` would collide with
8399 /// whichever marker it aliased. Pinned by
8400 /// `atom_keyword_marker_lead_distinct_from_every_other_algebra_marker`.
8401 ///
8402 /// Consumer sites this constant closes: SEVEN test-surface sites
8403 /// that pre-lift each extracted the lead byte via
8404 /// `Atom::KEYWORD_MARKER.chars().next().expect(_)` (or `.unwrap()`) —
8405 /// the Keyword arm of the [`Sexp::is_bare_atom_boundary`] negative
8406 /// sweep AND the six cross-axis disjointness pins on the sibling
8407 /// marker-byte algebras
8408 /// ([`QuoteForm::SPLICE_DISCRIMINATOR`], [`Self::BOOL_LITERAL_LEAD`],
8409 /// [`Self::STR_DELIMITER`], [`Self::STR_ESCAPE_LEAD`],
8410 /// [`Sexp::LIST_OPEN`] / [`Sexp::LIST_CLOSE`],
8411 /// [`Sexp::COMMENT_LEAD`], [`Sexp::COMMENT_TERM`]) — collapse onto
8412 /// ONE named byte on the substrate algebra.
8413 ///
8414 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
8415 /// (Keyword-prefix lead byte, canonical `':'`) pairing binds at ONE
8416 /// constant on the closed-set outer [`Atom`] algebra regardless of
8417 /// which reader-surface consumer reaches in. THEORY.md §II.1
8418 /// invariant 5 — composition preserves proofs; the
8419 /// `Self::KEYWORD_MARKER.starts_with(Self::KEYWORD_MARKER_LEAD)`
8420 /// round-trip law is a coherence proof BETWEEN the paired projection
8421 /// ([`Self::KEYWORD_MARKER`]) AND its projected lead byte (this
8422 /// constant) on ONE algebra — a regression that drifts either side
8423 /// surfaces at the pin rather than as a silent Keyword-prefix reader
8424 /// drift. THEORY.md §V.1 — knowable platform; the canonical
8425 /// Keyword-prefix lead byte becomes a TYPE-level constant on the
8426 /// substrate algebra rather than an inline
8427 /// `.chars().next().expect(_)` extraction at every test surface AND
8428 /// an inline `':'` mention across every docstring pinning the
8429 /// disjointness contract.
8430 ///
8431 /// Frontier inspiration: Racket's `read-syntax` colon-prefix reader
8432 /// hook (`#:name` for keyword args, `::name` for module-scoped bindings
8433 /// in some dialects) — where the `:` prefix dispatches keyword-family
8434 /// reader-macros keyed on ONE typed lead byte on the port's reader
8435 /// table. Translated to tatara-lisp: `Atom::KEYWORD_MARKER_LEAD`
8436 /// becomes the ONE typed lead byte on the closed-set outer [`Atom`]
8437 /// algebra that a future keyword-family peer method (e.g. a
8438 /// Clojure-compat port to `::foo` namespaced keywords) can extend
8439 /// through — the paired-prefix discipline that Racket's read-syntax
8440 /// table carries lands here as a `pub const` on the algebra rather
8441 /// than as an inline char literal at every consumer site.
8442 pub const KEYWORD_MARKER_LEAD: char = ':';
8443
8444 /// Project a bare keyword `name` to its canonical qualified rendering
8445 /// — `format!("{}{name}", Self::KEYWORD_MARKER)`, i.e. the ONE
8446 /// canonical [`String`] on the [`Atom`] algebra that composes
8447 /// [`Self::KEYWORD_MARKER`] with a bare keyword name to produce the
8448 /// substrate-canonical `":name"` spelling.
8449 ///
8450 /// Sibling projection to [`Self::bool_literal`] on the atomic-payload
8451 /// canonical-rendering axis: where `bool_literal(b)` projects the
8452 /// closed-set `bool` domain to its canonical Scheme spelling
8453 /// `"#t"` / `"#f"` (`&'static str` because the set is finite), THIS
8454 /// method projects the open-set bare-name domain to its canonical
8455 /// qualified spelling `":name"` ([`String`] because the set is
8456 /// unbounded). The [`Atom`] algebra's atomic-payload canonical-
8457 /// rendering axis now carries a typed projection for BOTH the
8458 /// prefix-marked variable-payload variant (Keyword) and the self-
8459 /// marked closed-set-payload variant (Bool).
8460 ///
8461 /// Pre-lift the same `format!("{}{s}", Self::KEYWORD_MARKER)`
8462 /// composition lived inline at THREE Keyword-arm sites on the
8463 /// atomic-payload rendering axis:
8464 ///
8465 /// 1. [`Self::to_json`]'s [`Self::Keyword`] arm —
8466 /// `serde_json::Value::String(format!(...))` for the JSON-
8467 /// canonical projection.
8468 /// 2. `Atom::to_iac_forge_sexpr` (removed)'s [`Self::Keyword`] arm —
8469 /// `SExpr::Symbol(format!(...))` for the iac-forge canonical-
8470 /// attestation projection.
8471 /// 3. [`fmt::Display for Atom`]'s [`Self::Keyword`] arm —
8472 /// `write!(f, "{}{s}", Self::KEYWORD_MARKER)` for the Lisp-
8473 /// canonical-form Display projection.
8474 ///
8475 /// Post-lift the two allocating sites (1) + (2) bind at ONE typed
8476 /// projection on the algebra; the [`fmt::Display for Atom`] site
8477 /// (3) keeps its allocation-free `write!` path but its byte output
8478 /// is pinned bit-identical to this projection by
8479 /// `atom_display_keyword_arm_agrees_with_keyword_qualified_bytes`
8480 /// so the three canonical-rendering surfaces cannot drift out of
8481 /// agreement. Adding a fourth Keyword-rendering site (e.g. a future
8482 /// YAML canonical projection, an LSP hover renderer) binds through
8483 /// THIS projection rather than composing [`Self::KEYWORD_MARKER`]
8484 /// inline — the (Keyword payload, canonical qualified rendering)
8485 /// pairing lives at ONE algebra layer.
8486 ///
8487 /// Round-trip contract (with [`Self::from_lexeme`]):
8488 /// `Self::from_lexeme(&Self::keyword_qualified(name)) ==
8489 /// Self::Keyword(name.to_owned())` for every `name: &str` that
8490 /// does NOT itself parse as a Bool spelling, integer, or float
8491 /// (the four typed-entry classification arms preceding the
8492 /// [`Self::KEYWORD_MARKER`]-prefix arm). The classifier's
8493 /// `s.strip_prefix(Self::KEYWORD_MARKER)` gate is the LEFT-inverse
8494 /// of THIS projection on the Keyword-payload subset — pinned by
8495 /// `atom_from_lexeme_inverts_keyword_qualified_on_bare_name`.
8496 /// Composition preserves proofs across the (typed-EXIT rendering,
8497 /// typed-ENTRY classification) round-trip at ONE algebra site.
8498 ///
8499 /// Sibling posture to [`QuoteForm::prefix`]'s composition with
8500 /// homoiconic inner forms in [`fmt::Display for Sexp`]'s quote-
8501 /// family arm: where `QuoteForm::prefix` is the [`&'static str`]
8502 /// prefix a quote-family variant composes with an inner rendering
8503 /// via `write!(f, "{}{inner}", qf.prefix())`, THIS method is the
8504 /// composed [`String`] a Keyword atom composes with a bare name.
8505 /// Both projections close a (marker, payload) pair on their owning
8506 /// closed-set algebra.
8507 ///
8508 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
8509 /// (Keyword payload, canonical qualified rendering) pairing binds
8510 /// at ONE projection on the closed-set [`Atom`] algebra regardless
8511 /// of which of the three canonical-rendering surfaces reaches in.
8512 /// THEORY.md §II.1 invariant 5 — composition preserves proofs; the
8513 /// round-trip law
8514 /// `Self::from_lexeme(&Self::keyword_qualified(n)) ==
8515 /// Self::Keyword(n.into())` is a coherence proof BETWEEN the
8516 /// paired typed-EXIT rendering (this method) AND the typed-ENTRY
8517 /// classification ([`Self::from_lexeme`]) on ONE algebra — a
8518 /// regression that drifts either side surfaces at the pin rather
8519 /// than as a silent Keyword-round-trip drift. THEORY.md §V.1 —
8520 /// knowable platform; the (Keyword payload → canonical qualified
8521 /// rendering) composition becomes a TYPE-level projection on the
8522 /// substrate algebra rather than an inline
8523 /// `format!("{}{s}", Self::KEYWORD_MARKER)` at every consumer
8524 /// site. THEORY.md §VI.1 — generation over composition; three
8525 /// byte-identical inline compositions collapse onto ONE named
8526 /// projection (two routed sites + one Display byte-identity pin),
8527 /// matching the substrate's three-times rule.
8528 ///
8529 /// Frontier inspiration: Racket's `syntax/parse` keyword-form
8530 /// canonical-rendering hook — where `#:name` keyword args have
8531 /// ONE canonical printer that composes the port's `KEYWORD_PREFIX`
8532 /// with the bare name rather than N per-consumer `printf`
8533 /// compositions. Translated to tatara-lisp:
8534 /// `Atom::keyword_qualified` becomes the ONE canonical-rendering
8535 /// projection on the closed-set outer [`Atom`] algebra so a
8536 /// future prefix migration (a Racket-compat port to `#:name`, a
8537 /// Clojure-compat port to `::name`) touches ONE
8538 /// [`Self::KEYWORD_MARKER`] constant + zero rendering sites,
8539 /// rather than the pre-lift three inline `format!` compositions
8540 /// that would silently drift.
8541 #[must_use]
8542 pub fn keyword_qualified(name: &str) -> String {
8543 let mut out = String::with_capacity(Self::KEYWORD_MARKER.len() + name.len());
8544 out.push_str(Self::KEYWORD_MARKER);
8545 out.push_str(name);
8546 out
8547 }
8548
8549 /// Project the closed-set `bool` domain to its canonical Scheme-
8550 /// spelling `&'static str` — `"#t"` for `true`, `"#f"` for `false`.
8551 /// ONE projection on the [`Atom`] algebra that the substrate's
8552 /// FOUR `Self::Bool`-round-trip inline byte-literals across TWO
8553 /// consumer sites (the two-arm `Bool(true|false)` fork inside
8554 /// [`fmt::Display for Atom`] and the two-line `if s == "#t"` /
8555 /// `if s == "#f"` cascade inside [`Self::from_lexeme`]) collapse
8556 /// onto — parallel to how [`Self::KEYWORD_MARKER`] is the ONE
8557 /// canonical prefix the four Keyword-round-trip sites bind to.
8558 ///
8559 /// Pre-lift the same `"#t"` / `"#f"` bytes lived inline at four
8560 /// sites: two `f.write_str("#t"|"#f")` arms at the Display impl and
8561 /// two `if s == "#t"|"#f"` gates at [`Self::from_lexeme`]. Post-
8562 /// lift the (typed `bool`, canonical Scheme spelling) pairing binds
8563 /// at ONE projection on the [`Atom`] algebra that every consumer
8564 /// routes through; a refactor that swaps the spelling (e.g. a
8565 /// Common-Lisp-compat port to `T` / `NIL`, a JSON-compat port to
8566 /// `true` / `false`) touches ONE method rather than four inline
8567 /// bytes that would silently drift out of round-trip agreement if
8568 /// one was updated without the others. The Display arm also
8569 /// collapses from TWO variant-branches (`Bool(true) => "#t"`,
8570 /// `Bool(false) => "#f"`) to ONE variant-branch
8571 /// (`Bool(b) => Self::bool_literal(*b)`) — the fork on `bool`
8572 /// happens at the projection, not at every consumer's match body.
8573 ///
8574 /// Load-bearing round-trip contract:
8575 /// `Self::from_lexeme(&Self::boolean(b).to_string()) ==
8576 /// Self::boolean(b)` for every `b: bool` — the reader-entry
8577 /// classifier's `s == Self::bool_literal(true|false)` gates and the
8578 /// Lisp-canonical [`Display`]'s
8579 /// `f.write_str(Self::bool_literal(*b))` emission both bind to THIS
8580 /// projection so the pair cannot drift. Guards against the
8581 /// CLAUDE.md pin ("bare `true`/`false` are symbols → strings, not
8582 /// bools") — the closed-set `bool` domain projects only through
8583 /// this typed method, so a reader extension that later accepts
8584 /// bare `true`/`false` extends the projection (or its reverse) at
8585 /// ONE algebra site rather than at every callsite in lockstep.
8586 ///
8587 /// Sibling-shape peer of [`Self::KEYWORD_MARKER`]: where
8588 /// `KEYWORD_MARKER` is the ONE `&'static str` prefix a Keyword
8589 /// payload composes with at four round-trip sites, this method is
8590 /// the ONE projection a Bool payload composes THROUGH at its two
8591 /// round-trip sites. The [`Atom`] algebra's atomic-payload axis
8592 /// now carries a canonical-marker/spelling for BOTH prefix-marked
8593 /// (Keyword) and self-marked (Bool) variants.
8594 ///
8595 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
8596 /// (Bool payload, canonical Scheme spelling) pairing now binds at
8597 /// ONE projection on the closed-set [`Atom`] algebra regardless of
8598 /// which of the two reader-entry / rendering surfaces reaches in.
8599 /// THEORY.md §VI.1 — generation over composition; the four byte-
8600 /// identical `"#t"` / `"#f"` inline literals collapse onto ONE
8601 /// named projection, matching the substrate's three-times rule.
8602 /// THEORY.md §V.1 — knowable platform; the canonical Scheme-bool
8603 /// spellings become a TYPE-level projection on the substrate
8604 /// algebra rather than four inline bytes at two consumer surfaces.
8605 #[must_use]
8606 pub fn bool_literal(b: bool) -> &'static str {
8607 if b {
8608 Self::TRUE_LITERAL
8609 } else {
8610 Self::FALSE_LITERAL
8611 }
8612 }
8613
8614 /// Canonical `&'static str` Scheme-bool spelling for the `true`
8615 /// element of the closed `bool` domain — the `"#t"` bytes
8616 /// [`Self::bool_literal`] projects `true` to, AND the
8617 /// [`Self::from_lexeme`] reader classifier gates on. Sibling
8618 /// posture to [`Self::FALSE_LITERAL`] (`"#f"`) on the same
8619 /// bool-spelling algebra layer.
8620 ///
8621 /// Pre-lift the same `"#t"` bytes lived inline at [`Self::bool_literal`]'s
8622 /// `true`-arm plus at five test-surface sites that pin the
8623 /// canonical spelling — the ≥2 PRIME-DIRECTIVE trigger. Post-lift
8624 /// the (`true`, canonical Scheme spelling) pairing binds at ONE
8625 /// `pub const` on the closed-set [`Atom`] algebra: the
8626 /// [`Self::bool_literal`] `true`-arm AND every consumer that pins
8627 /// the exact bytes route through this constant, so a spelling
8628 /// migration (e.g. a Common-Lisp-compat port to `"T"`, a JSON-compat
8629 /// port to `"true"`, a Racket-compat port to `"#true"`) is ONE
8630 /// edit HERE with the [`Self::bool_literal`] projection AND every
8631 /// downstream reader/Display consumer mechanically picking it up.
8632 ///
8633 /// Sibling posture to the closed set of per-role canonical
8634 /// `pub const` bytes on the substrate's other closed-set outer
8635 /// algebras: [`Self::STR_DELIMITER`] (`'"'`),
8636 /// [`Self::KEYWORD_MARKER`] (`":"`),
8637 /// [`Sexp::LIST_OPEN`] (`'('`),
8638 /// [`crate::error::MacroDefHead::DEFMACRO_KEYWORD`] (`"defmacro"`),
8639 /// [`crate::macro_expand::MacroParams::REST_MARKER`] (`"&rest"`),
8640 /// [`crate::macro_expand::MacroParams::OPTIONAL_MARKER`] (`"&optional"`).
8641 ///
8642 /// Structural round-trip contract (composed with the algebra's
8643 /// [`Self::BOOL_LITERAL_LEAD`] axis peer):
8644 /// `Self::TRUE_LITERAL.starts_with(Self::BOOL_LITERAL_LEAD)` — the
8645 /// lead-byte-prefixes-spelling law from
8646 /// `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`
8647 /// specialises byte-for-byte to this constant, pinning the
8648 /// (`BOOL_LITERAL_LEAD`, `TRUE_LITERAL`) pairing on ONE typed
8649 /// algebra.
8650 ///
8651 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
8652 /// (`true`, canonical Scheme spelling) pairing binds at ONE
8653 /// `pub const` on the closed-set outer [`Atom`] algebra regardless
8654 /// of which of the reader/Display surface consumers reaches in.
8655 /// THEORY.md §VI.1 — generation over composition; the multi-site
8656 /// inline `"#t"` literal collapses onto ONE named `pub const`,
8657 /// matching the substrate's three-times rule. THEORY.md §V.1 —
8658 /// knowable platform; the canonical `true`-spelling becomes a
8659 /// TYPE-level constant on the substrate algebra rather than an
8660 /// inline byte literal at every consumer surface.
8661 pub const TRUE_LITERAL: &'static str = "#t";
8662
8663 /// Canonical `&'static str` Scheme-bool spelling for the `false`
8664 /// element of the closed `bool` domain — the `"#f"` bytes
8665 /// [`Self::bool_literal`] projects `false` to, AND the
8666 /// [`Self::from_lexeme`] reader classifier gates on. Sibling
8667 /// posture to [`Self::TRUE_LITERAL`] (`"#t"`) on the same
8668 /// bool-spelling algebra layer.
8669 ///
8670 /// Pre-lift the same `"#f"` bytes lived inline at [`Self::bool_literal`]'s
8671 /// `false`-arm plus at five test-surface sites that pin the
8672 /// canonical spelling — the ≥2 PRIME-DIRECTIVE trigger. Post-lift
8673 /// the (`false`, canonical Scheme spelling) pairing binds at ONE
8674 /// `pub const` on the closed-set [`Atom`] algebra: the
8675 /// [`Self::bool_literal`] `false`-arm AND every consumer that pins
8676 /// the exact bytes route through this constant.
8677 ///
8678 /// Structural round-trip contract (composed with the algebra's
8679 /// [`Self::BOOL_LITERAL_LEAD`] axis peer):
8680 /// `Self::FALSE_LITERAL.starts_with(Self::BOOL_LITERAL_LEAD)`.
8681 pub const FALSE_LITERAL: &'static str = "#f";
8682
8683 /// The closed set of two canonical `&'static str` Scheme-bool
8684 /// spellings — the [`Self::TRUE_LITERAL`] (`"#t"`) canonical `true`
8685 /// spelling followed by the [`Self::FALSE_LITERAL`] (`"#f"`)
8686 /// canonical `false` spelling. Canonical declaration order matches
8687 /// the `[true, false]` sweep order every existing sibling test in
8688 /// the crate uses (see e.g. the `for b in [true, false]` sweeps at
8689 /// `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`)
8690 /// so `Self::BOOL_LITERALS[i] == Self::bool_literal([true, false][i])`
8691 /// element-wise — pinned by
8692 /// `atom_bool_literals_align_with_bool_literal_by_index`.
8693 ///
8694 /// Sibling posture to
8695 /// [`crate::error::MacroDefHead::KEYWORDS`] (`[&'static str; 3]`),
8696 /// [`crate::macro_expand::MacroParams::LAMBDA_LIST_KEYWORDS`]
8697 /// (`[&'static str; 2]`) — every closed-set spelling algebra now
8698 /// pins its projection ALL array at the declaration site via a
8699 /// forced-arity `[&'static str; N]` array whose length fails
8700 /// compilation if a new spelling lands without being added to the
8701 /// set. The closed `bool` domain admits exactly two spellings by
8702 /// construction (a hypothetical third would require extending the
8703 /// underlying `bool` type itself), so this array's `N == 2` is
8704 /// pinned by the mathematics — the forced-arity `[_; 2]` here
8705 /// records that invariant on the substrate algebra so a future
8706 /// tri-valued-logic extension surfaces at the array-arity check
8707 /// rather than as a silent drift.
8708 ///
8709 /// Future consumers that compose against [`Self::BOOL_LITERALS`]:
8710 /// an LSP / REPL completion provider surfacing every `#…` partial
8711 /// input against the closed set (`Self::BOOL_LITERALS.iter()` is
8712 /// the ONE typed sweep over every legal bool spelling), a
8713 /// `tatara-check` coverage assertion (every workspace `.lisp` file's
8714 /// bare-`#`-prefixed lexeme must classify to some entry of
8715 /// `Self::BOOL_LITERALS` OR be routed through the broader
8716 /// hash-prefix reader-macro family), any future audit-trail metric
8717 /// jointly labeled by the canonical Scheme spelling (e.g.
8718 /// `tatara_lisp_bool_lexeme_total{literal="#t"}`) — the metric
8719 /// label set IS [`Self::BOOL_LITERALS`].
8720 pub const BOOL_LITERALS: [&'static str; 2] = [Self::TRUE_LITERAL, Self::FALSE_LITERAL];
8721
8722 /// Canonical `#` LEAD byte shared across both [`Self::bool_literal`]
8723 /// spellings (`"#t"` for [`true`], `"#f"` for [`false`]) — the ONE
8724 /// canonical `char` on the [`Atom`] algebra the substrate's Bool-
8725 /// prefix disjointness contract binds to.
8726 ///
8727 /// Sibling posture to the closed set of `pub const` reader-punctuation
8728 /// canonical bytes on the substrate: [`Self::STR_DELIMITER`] (`'"'`),
8729 /// [`Self::STR_ESCAPE_LEAD`] (`'\\'`), [`Sexp::LIST_OPEN`] (`'('`),
8730 /// [`Sexp::LIST_CLOSE`] (`')'`), [`Sexp::COMMENT_LEAD`] (`';'`),
8731 /// [`Sexp::COMMENT_TERM`] (`'\n'`), [`QuoteForm::SPLICE_DISCRIMINATOR`]
8732 /// (`'@'`) — every canonical per-role byte the reader's tokenizer
8733 /// specialises on is now a `pub const` on its owning closed-set
8734 /// algebra. This constant closes the Bool-family lead byte at the
8735 /// SAME algebra as its two-char [`Self::bool_literal`] projections
8736 /// so a delimiter swap (e.g. a Common-Lisp-compat port from
8737 /// Scheme `#t`/`#f` to CL `T`/`NIL`, a JSON-compat port to
8738 /// `true`/`false`, or an extension for the broader Scheme
8739 /// hash-prefix reader-macro family `#\char` / `#(vector)` /
8740 /// `#|block-comment|#` / `#;datum-comment`) lands at ONE constant
8741 /// on the algebra rather than at inline `'#'` char literals
8742 /// scattered across the test surface AND the docstrings pinning
8743 /// the disjointness contract.
8744 ///
8745 /// Structural round-trip contract:
8746 /// `Self::bool_literal(b).starts_with(Self::BOOL_LITERAL_LEAD)`
8747 /// for every `b: bool` — pinned by
8748 /// `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`.
8749 /// A regression that drifts EITHER the constant OR the two
8750 /// [`Self::bool_literal`] arms surfaces at the pin rather than at
8751 /// a silent bool-family reader drift where `#t` / `#f` classify as
8752 /// [`Self::Symbol`] instead of [`Self::Bool`].
8753 ///
8754 /// Disjointness contract: `BOOL_LITERAL_LEAD`'s byte MUST differ
8755 /// from [`Self::STR_DELIMITER`] (`'"'`), [`Self::STR_ESCAPE_LEAD`]
8756 /// (`'\\'`), [`Self::KEYWORD_MARKER`]'s lead byte (`':'`),
8757 /// [`Sexp::LIST_OPEN`] (`'('`), [`Sexp::LIST_CLOSE`] (`')'`),
8758 /// [`Sexp::COMMENT_LEAD`] (`';'`), [`Sexp::COMMENT_TERM`]
8759 /// (`'\n'`), every [`QuoteForm::lead_char`] projection AND
8760 /// [`QuoteForm::SPLICE_DISCRIMINATOR`] (`'@'`) — every other
8761 /// closed-set outer-marker byte the reader's tokenizer specialises
8762 /// on. A collision would silently break the reader's outer
8763 /// dispatch: a `#`-prefixed bare atom `#t` would collide with
8764 /// whichever marker it aliased. Pinned by
8765 /// `atom_bool_literal_lead_distinct_from_every_other_algebra_marker`.
8766 ///
8767 /// Consumer sites this constant closes: the three test-surface
8768 /// sites that pre-lift each extracted the lead byte via
8769 /// `Self::bool_literal(b).chars().next().unwrap()` (or `.expect(_)`)
8770 /// — the `Bool`-arm of [`Sexp::is_bare_atom_boundary`]'s negative
8771 /// sweep AND the two [`QuoteForm::SPLICE_DISCRIMINATOR`]
8772 /// disjointness assertions — collapse onto ONE named byte on the
8773 /// substrate algebra. The two SPLICE_DISCRIMINATOR assertions
8774 /// (one per `bool_literal` spelling) further collapse to ONE
8775 /// assertion since the lead byte is by construction the SAME
8776 /// across both spellings.
8777 ///
8778 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
8779 /// (Bool-family lead byte, canonical `'#'`) pairing binds at ONE
8780 /// constant on the closed-set outer [`Atom`] algebra regardless of
8781 /// which reader-surface consumer reaches in. THEORY.md §II.1
8782 /// invariant 5 — composition preserves proofs; the
8783 /// [`Self::bool_literal(b).starts_with(Self::BOOL_LITERAL_LEAD)`]
8784 /// round-trip law is a coherence proof BETWEEN the paired
8785 /// projection ([`Self::bool_literal`]) AND the shared lead byte
8786 /// (this constant) on ONE algebra — a regression that drifts
8787 /// either side surfaces at the pin rather than as a silent
8788 /// hash-prefix reader-family drift. THEORY.md §V.1 — knowable
8789 /// platform; the canonical Bool-family lead byte becomes a
8790 /// TYPE-level constant on the substrate algebra rather than an
8791 /// inline `.chars().next().unwrap()` extraction at every test
8792 /// surface AND an inline `'#'` mention across every docstring
8793 /// pinning the disjointness contract.
8794 pub const BOOL_LITERAL_LEAD: char = '#';
8795
8796 /// Canonical `"` delimiter that opens AND closes a [`Self::Str`]
8797 /// atom in the reader's tokenizer AND self-escapes inside a
8798 /// backslash-escape sequence — ONE canonical `char` on the
8799 /// [`Atom`] algebra the substrate's FOUR `"`-round-trip inline
8800 /// `char` literals at [`crate::reader::tokenize`] bind to.
8801 ///
8802 /// Sibling constant of [`Self::KEYWORD_MARKER`] on the atomic-
8803 /// payload marker/delimiter axis of the closed-set [`Atom`]
8804 /// algebra: where `KEYWORD_MARKER` is the ONE `&'static str`
8805 /// prefix a [`Self::Keyword`] payload composes WITH at four
8806 /// canonical-form round-trip sites (reader-entry classifier,
8807 /// Lisp-canonical Display, JSON canonical form, iac-forge
8808 /// canonical form) and [`Self::bool_literal`] is the ONE
8809 /// projection a [`Self::Bool`] payload composes THROUGH at its
8810 /// two Bool-round-trip sites, this constant is the ONE canonical
8811 /// delimiter a [`Self::Str`] payload pairs with at the reader's
8812 /// four `"`-round-trip sites inside [`crate::reader::tokenize`]:
8813 /// 1. The outer-match string-opening arm — the `"` byte that
8814 /// begins a [`crate::reader::Token::Str`] tokenization run.
8815 /// 2. The escape-handler mapping — `\"` unescapes to a bare `"`
8816 /// character inside the accumulated string payload (the
8817 /// only self-escape arm on the reader's five-arm escape
8818 /// table: `\n → \n`, `\t → \t`, `\r → \r`, `\" → "`,
8819 /// `\\ → \\`).
8820 /// 3. The string-closing arm — the `"` byte that terminates the
8821 /// current [`crate::reader::Token::Str`] tokenization run
8822 /// and emits the accumulated payload.
8823 /// 4. The bare-atom tokenizer's break-disjunct — `"` is one of
8824 /// the seven characters that terminates a
8825 /// [`crate::reader::Token::Atom`] run so a bare atom
8826 /// followed by a string (e.g. `foo"body"`) tokenizes as two
8827 /// distinct tokens rather than one Symbol payload.
8828 ///
8829 /// Pre-lift the same `"` byte lived inline at four `char`
8830 /// literals scattered across `crate::reader::tokenize`: two outer-
8831 /// match arm patterns (opening + escape-handler), one inner-loop
8832 /// termination pattern (closing), one bare-atom termination
8833 /// disjunct. Post-lift the (Str payload, canonical `"` delimiter)
8834 /// pairing binds at ONE `char` constant on the [`Atom`] algebra
8835 /// that every reader consumer routes through; a refactor that
8836 /// swaps the delimiter (e.g. a Racket-compat port to `#"…"#`
8837 /// heredoc mode, a Python-compat port that also accepts `'`,
8838 /// a triple-quoted heredoc mode) touches ONE constant + one
8839 /// reader table rather than four inline byte literals that would
8840 /// silently drift out of round-trip agreement if one was updated
8841 /// without the others (e.g. an opening `#` without a matching
8842 /// closing `#` would round-trip a broken string with a
8843 /// silently-truncated payload).
8844 ///
8845 /// Load-bearing round-trip contract:
8846 /// `read(&format!("{}{s}{}", Atom::STR_DELIMITER,
8847 /// Atom::STR_DELIMITER))[0] == Sexp::Atom(Atom::string(s))` for
8848 /// every escape-free `s: &str`. The reader's opening + closing
8849 /// arms both bind to THIS constant so the delimiter cannot drift
8850 /// silently between opener and closer; a regression that swaps
8851 /// ONE arm's pattern to a different byte fails the round-trip
8852 /// even when the byte-value at the other arm still agrees at
8853 /// the surface. Guards the CLAUDE.md-implicit convention that
8854 /// operator-visible strings use ONE canonical delimiter across
8855 /// the reader entry surface — the delimiter is a first-class
8856 /// algebra fact rather than a per-callsite reader convention.
8857 ///
8858 /// Sibling-shape peer of [`Self::KEYWORD_MARKER`] on the closed-
8859 /// set [`Atom`] algebra: where `KEYWORD_MARKER` (`":"`) partitions
8860 /// bare-atom lexemes at reader-entry classifier `strip_prefix`
8861 /// gate into `Keyword` vs default `Symbol`, this constant (`'"'`)
8862 /// partitions the reader's outer tokenizer arm into
8863 /// `Token::Str` vs `Token::Atom` — both are the ONE canonical
8864 /// marker byte the reader binds to when discriminating an
8865 /// [`Atom`] variant's typed-entry classification path. A `Str`
8866 /// payload takes the `Token::Str` reader branch (with THIS
8867 /// delimiter) NOT the `Token::Atom` reader branch (routed
8868 /// through [`Self::from_lexeme`]) — the two paths remain
8869 /// structurally disjoint through the reader's delimiter
8870 /// dispatch.
8871 ///
8872 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
8873 /// (Str payload, canonical `"` delimiter) pairing now binds at
8874 /// ONE constant on the closed-set [`Atom`] algebra regardless of
8875 /// which of the four reader tokenizer sites reaches in.
8876 /// THEORY.md §VI.1 — generation over composition; four byte-
8877 /// identical inline `'"'` char literals in `crate::reader::tokenize`
8878 /// collapse onto ONE named constant. Four occurrences, well past
8879 /// the ≥2 lift threshold — the substrate's three-times rule at
8880 /// the Str-delimiter axis. THEORY.md §V.1 — knowable platform;
8881 /// the canonical string-delimiter byte becomes a TYPE-level
8882 /// constant on the substrate algebra rather than four inline
8883 /// bytes at four consumer sites inside one reader file.
8884 pub const STR_DELIMITER: char = '"';
8885
8886 /// Canonical `\` escape-lead byte that OPENS a backslash-escape
8887 /// sequence inside a [`Self::Str`] payload AND self-escapes to the
8888 /// same byte inside that sequence — ONE canonical `char` on the
8889 /// [`Atom`] algebra the substrate's TWO `\`-round-trip inline
8890 /// `char` literals at [`crate::reader::tokenize`] bind to.
8891 ///
8892 /// Sibling constant of [`Self::STR_DELIMITER`] on the same
8893 /// Str-payload delimiter axis of the closed-set [`Atom`] algebra:
8894 /// where `STR_DELIMITER` is the ONE canonical delimiter that
8895 /// BOUNDS a Str payload from the outside (opener, closer, self-
8896 /// escape, bare-atom terminator), this constant is the ONE
8897 /// canonical escape lead that ESCAPES-IN a following byte from
8898 /// the INSIDE of the same payload. The two constants together
8899 /// span the Str-tokenization boundary — every `char` the reader's
8900 /// `Token::Str` accumulation loop specialises on binds to one of
8901 /// them.
8902 ///
8903 /// The reader's TWO `\`-round-trip sites inside
8904 /// [`crate::reader::tokenize`]:
8905 /// 1. The escape-lead outer arm — the `\` byte that triggers the
8906 /// inner escape-handler branch that consumes the following
8907 /// byte as an escape sequence.
8908 /// 2. The escape-handler's self-escape arm — inside the reader's
8909 /// six-arm escape table (`\n → \n`, `\t → \t`, `\r → \r`,
8910 /// `\" → "`, `\\ → \`, passthrough), the self-escape arm on
8911 /// the escape-lead axis: pattern AND mapped value both bind
8912 /// to THIS constant so `\\` unescapes to a single `\` byte
8913 /// in the accumulated payload. Sibling posture to the
8914 /// analogous self-escape arm on [`Self::STR_DELIMITER`] axis
8915 /// (`\"` unescapes to `"`) — the two self-escape arms are
8916 /// the escape table's ONLY pattern-equals-value arms; every
8917 /// other arm is pattern-distinct-from-value.
8918 ///
8919 /// Pre-lift the same `\` byte lived inline at two `char` literals
8920 /// scattered across `crate::reader::tokenize`: one outer-arm
8921 /// pattern (escape-lead detection), one inner-loop escape-handler
8922 /// arm's pattern + value pair (the self-escape mapping). Post-
8923 /// lift the (Str-payload escape lead, canonical `\` byte) pairing
8924 /// binds at ONE `char` constant on the [`Atom`] algebra that every
8925 /// reader consumer routes through; a refactor that swaps the
8926 /// escape lead (e.g. a Rust-compat port to `\\` byte-strings, a
8927 /// hypothetical Racket-compat port that adopts `#\` prefix syntax
8928 /// as the escape lead, or a heredoc mode that suspends escaping
8929 /// altogether) touches ONE constant + one reader table rather
8930 /// than two inline byte literals that would silently drift out of
8931 /// round-trip agreement if one was updated without the other
8932 /// (e.g. the outer arm's pattern updated without the inner self-
8933 /// escape's pattern + value would leak a stale escape lead through
8934 /// the wrong branch).
8935 ///
8936 /// Load-bearing round-trip contract:
8937 /// `read(&format!("{}{}{}{}", Atom::STR_DELIMITER,
8938 /// Atom::STR_ESCAPE_LEAD, Atom::STR_ESCAPE_LEAD,
8939 /// Atom::STR_DELIMITER))[0] ==
8940 /// Sexp::Atom(Atom::string(Atom::STR_ESCAPE_LEAD.to_string()))`.
8941 /// The `\\` inside a STR_DELIMITER-wrapped payload unescapes to
8942 /// ONE `\` byte on the accumulated payload — pinning the (self-
8943 /// escape pattern, self-escape mapped value) pair against
8944 /// re-inlining. A regression that swaps ONE side of the self-
8945 /// escape arm to a different byte fails this round-trip even when
8946 /// the pattern OR value at the other side still agrees at the
8947 /// surface, because both sides bind to THIS constant.
8948 ///
8949 /// Sibling-shape peer of [`Self::STR_DELIMITER`] on the closed-set
8950 /// [`Atom`] algebra: where `STR_DELIMITER` partitions the outer-
8951 /// tokenizer arm into `Token::Str` vs `Token::Atom`, this constant
8952 /// partitions the inner-tokenizer arm (inside `Token::Str`
8953 /// accumulation) into the escape-handler branch vs the passthrough
8954 /// `Some((_, ch))` branch — both are the ONE canonical marker
8955 /// byte the reader binds to when discriminating the Str-payload
8956 /// accumulation loop's branch dispatch.
8957 ///
8958 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
8959 /// (Str-payload escape lead, canonical `\` byte) pairing now binds
8960 /// at ONE constant on the closed-set [`Atom`] algebra regardless
8961 /// of which of the two reader tokenizer sites reaches in.
8962 /// THEORY.md §VI.1 — generation over composition; two byte-
8963 /// identical inline `'\\'` char literals in `crate::reader::tokenize`
8964 /// collapse onto ONE named constant. Two occurrences at the ≥2
8965 /// lift threshold — the substrate's three-times rule at the
8966 /// Str-escape-lead axis. THEORY.md §V.1 — knowable platform; the
8967 /// canonical string-escape-lead byte becomes a TYPE-level constant
8968 /// on the substrate algebra rather than two inline bytes at two
8969 /// consumer sites inside one reader file.
8970 pub const STR_ESCAPE_LEAD: char = '\\';
8971
8972 /// Canonical Str-payload escape-table projection — total closed-set
8973 /// decode from the ONE post-escape-lead source byte the reader's
8974 /// escape-handler branch consumes to the ONE decoded byte pushed
8975 /// onto the accumulated Str payload. ONE typed projection on the
8976 /// closed-set [`Atom`] algebra that the substrate's Str-escape
8977 /// decode boundary binds to; every consumer that reaches inside a
8978 /// [`Self::Str`] payload for a backslash-escape sequence routes
8979 /// through THIS method rather than a per-site inline
8980 /// `match esc { … }` table.
8981 ///
8982 /// Closes the Str-payload tokenization boundary that
8983 /// [`Self::STR_DELIMITER`] + [`Self::STR_ESCAPE_LEAD`] began: where
8984 /// those two constants pin the ONE canonical delimiter byte AND
8985 /// the ONE canonical escape-lead byte the reader's outer + inner
8986 /// branch dispatch specialises on, this method pins the ONE
8987 /// canonical decode table the escape-handler's inner branch
8988 /// consumes AFTER the escape-lead byte fires. The three constants
8989 /// together span the Str-payload tokenization axis — every byte
8990 /// the reader's `Token::Str` accumulation loop reads either
8991 /// terminates the payload (`STR_DELIMITER`), triggers the escape
8992 /// handler (`STR_ESCAPE_LEAD`), pushes through unchanged
8993 /// (passthrough), OR is fed into THIS method as the escape source
8994 /// byte AND its result pushed onto the payload.
8995 ///
8996 /// Total function: EVERY `char` maps to exactly one decoded
8997 /// `char`. The five typed arms bind the substrate's canonical
8998 /// escape shorthand:
8999 ///
9000 /// | esc source | decoded byte |
9001 /// | ------------------------- | ------------------------- |
9002 /// | `'n'` | `'\n'` |
9003 /// | `'t'` | `'\t'` |
9004 /// | `'r'` | `'\r'` |
9005 /// | `Self::STR_DELIMITER` | `Self::STR_DELIMITER` |
9006 /// | `Self::STR_ESCAPE_LEAD` | `Self::STR_ESCAPE_LEAD` |
9007 /// | any other `char` | itself (passthrough) |
9008 ///
9009 /// The two self-escape arms (`STR_DELIMITER → STR_DELIMITER`,
9010 /// `STR_ESCAPE_LEAD → STR_ESCAPE_LEAD`) are the ONLY
9011 /// pattern-equals-value arms in the table; both bind through the
9012 /// closed-set [`Atom`] algebra constants so a delimiter-swap or
9013 /// escape-lead-swap on the algebra propagates through pattern AND
9014 /// value at ONE site rather than as scattered inline byte literals
9015 /// that would silently drift out of round-trip agreement if one
9016 /// was updated without the other.
9017 ///
9018 /// The three named-escape arms (`'n'` / `'t'` / `'r'`) are the
9019 /// substrate's canonical whitespace shorthand — pattern-distinct-
9020 /// from-value on every arm (each maps a printable ASCII letter to
9021 /// its corresponding C0 control byte). Pre-lift the whole table
9022 /// lived inline at ONE site inside [`crate::reader::tokenize`]'s
9023 /// escape-handler branch; post-lift the table lives at ONE typed
9024 /// projection on the [`Atom`] algebra that the reader consumes
9025 /// through a single `Self::decode_str_escape(esc)` call. Adding a
9026 /// sixth named-escape arm (e.g. `'0' → '\0'` for the NUL byte, or
9027 /// an `'x'` hex-byte-prefix arm) extends THIS method's match
9028 /// rather than mutating the reader's inline block.
9029 ///
9030 /// Load-bearing round-trip contract: for every `esc: char`,
9031 /// `read(&format!("{}{}{}{}", Atom::STR_DELIMITER,
9032 /// Atom::STR_ESCAPE_LEAD, esc, Atom::STR_DELIMITER))[0] ==
9033 /// Sexp::Atom(Atom::string(Atom::decode_str_escape(esc)
9034 /// .to_string()))`. Every escape-source byte inside a
9035 /// STR_DELIMITER-wrapped payload decodes through THIS projection
9036 /// end-to-end — pinning the (reader escape-handler branch, this
9037 /// method) pairing against a silent drift at either side. A
9038 /// regression that re-inlines the reader's table would break the
9039 /// pin the moment a new arm lands here but NOT there (or vice
9040 /// versa).
9041 ///
9042 /// Sibling-shape peer of [`QuoteForm::from_lead_char`] on the
9043 /// closed-set [`QuoteForm`] algebra: where `from_lead_char` is
9044 /// the ONE typed dispatch on the outer-tokenizer quote-family
9045 /// axis (four homoiconic prefix chars decode to `Option<Self>`),
9046 /// this method is the ONE typed dispatch on the inner-tokenizer
9047 /// Str-escape axis (every escape-source char decodes to a
9048 /// resolved `char`). Both are the substrate's canonical
9049 /// closed-set projections the reader consumes through ONE call
9050 /// site each; both close the reader's per-char specialization
9051 /// point onto the algebra.
9052 ///
9053 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
9054 /// (Str-escape source byte, decoded byte) pairing now binds at
9055 /// ONE typed projection on the closed-set [`Atom`] algebra
9056 /// regardless of which consumer reaches in. THEORY.md §VI.1 —
9057 /// generation over composition; the reader's five-arm inline
9058 /// table plus one passthrough arm at [`crate::reader::tokenize`]
9059 /// collapses onto ONE named projection. THEORY.md §V.1 — knowable
9060 /// platform; the canonical Str-escape decode table becomes a
9061 /// TYPE-level method on the substrate algebra rather than an
9062 /// inline block inside one reader file — a future decoder
9063 /// (e.g. a Racket-compat port, a heredoc mode, a raw-string
9064 /// mode) plugs a peer projection onto the same algebra rather
9065 /// than forking the reader.
9066 ///
9067 /// Canonical newline-escape SOURCE char — the ONE `'n'` byte
9068 /// [`Self::decode_str_escape`]'s newline arm pattern-matches on.
9069 /// Sibling constant of [`Self::NEWLINE_ESCAPE_DECODED`] on the
9070 /// same (source, decoded) escape-arm axis: the (`'n'`, `'\n'`)
9071 /// pairing is the first of the substrate's three named-escape
9072 /// arms; each pairing binds at ONE `pub const` per role rather
9073 /// than at an inline `char` literal at the match-arm pattern +
9074 /// value. Sibling posture to [`Self::STR_DELIMITER`] /
9075 /// [`Self::STR_ESCAPE_LEAD`] one axis over on the same
9076 /// Str-payload tokenization boundary — those two constants pin
9077 /// the ONE canonical delimiter byte AND the ONE canonical
9078 /// escape-lead byte the reader's outer + inner branch dispatch
9079 /// specialises on; this constant + its five per-role peers
9080 /// close the (named-escape, self-escape) decode-arm surface at
9081 /// the same closed-set [`Atom`] algebra.
9082 pub const NEWLINE_ESCAPE_SOURCE: char = 'n';
9083
9084 /// Canonical newline-escape DECODED byte — the ONE `'\n'` C0
9085 /// control byte [`Self::decode_str_escape`]'s newline arm
9086 /// value-emits when the source char is [`Self::NEWLINE_ESCAPE_SOURCE`].
9087 /// Peer of the SOURCE constant on the SAME (source, decoded)
9088 /// escape-arm axis; the pairing (`NEWLINE_ESCAPE_SOURCE`,
9089 /// `NEWLINE_ESCAPE_DECODED`) IS the first row of the
9090 /// [`Self::NAMED_ESCAPE_TABLE`] two-column algebra.
9091 pub const NEWLINE_ESCAPE_DECODED: char = '\n';
9092
9093 /// Canonical tab-escape SOURCE char — the ONE `'t'` byte
9094 /// [`Self::decode_str_escape`]'s tab arm pattern-matches on.
9095 /// Second of the three named-escape SOURCE `pub const` peers.
9096 pub const TAB_ESCAPE_SOURCE: char = 't';
9097
9098 /// Canonical tab-escape DECODED byte — the ONE `'\t'` C0
9099 /// control byte [`Self::decode_str_escape`]'s tab arm
9100 /// value-emits when the source char is [`Self::TAB_ESCAPE_SOURCE`].
9101 /// Peer of the SOURCE constant on the SAME (source, decoded)
9102 /// escape-arm axis; the pairing (`TAB_ESCAPE_SOURCE`,
9103 /// `TAB_ESCAPE_DECODED`) IS the second row of the
9104 /// [`Self::NAMED_ESCAPE_TABLE`] two-column algebra.
9105 pub const TAB_ESCAPE_DECODED: char = '\t';
9106
9107 /// Canonical carriage-return-escape SOURCE char — the ONE `'r'`
9108 /// byte [`Self::decode_str_escape`]'s carriage-return arm
9109 /// pattern-matches on. Third of the three named-escape SOURCE
9110 /// `pub const` peers.
9111 pub const CARRIAGE_RETURN_ESCAPE_SOURCE: char = 'r';
9112
9113 /// Canonical carriage-return-escape DECODED byte — the ONE
9114 /// `'\r'` C0 control byte [`Self::decode_str_escape`]'s
9115 /// carriage-return arm value-emits when the source char is
9116 /// [`Self::CARRIAGE_RETURN_ESCAPE_SOURCE`]. Peer of the SOURCE
9117 /// constant on the SAME (source, decoded) escape-arm axis; the
9118 /// pairing (`CARRIAGE_RETURN_ESCAPE_SOURCE`,
9119 /// `CARRIAGE_RETURN_ESCAPE_DECODED`) IS the third row of the
9120 /// [`Self::NAMED_ESCAPE_TABLE`] two-column algebra.
9121 pub const CARRIAGE_RETURN_ESCAPE_DECODED: char = '\r';
9122
9123 /// Canonical NAMED-escape table — the closed-set ALL array over
9124 /// the substrate's three (SOURCE, DECODED) pairings the
9125 /// pattern-distinct-from-value named-escape arms of
9126 /// [`Self::decode_str_escape`] emit, in canonical declaration
9127 /// order (newline / tab / carriage-return). Forced-arity
9128 /// `[(char, char); 3]` composition — a hypothetical fourth
9129 /// named-escape arm (e.g. `'0' → '\0'` for the NUL byte, `'e'
9130 /// → '\x1b'` for ESC, or a Racket-compat `'a' → '\x07'` for
9131 /// BEL) extends [`Self::decode_str_escape`]'s match ONCE +
9132 /// this array ONCE + TWO new per-role `pub const`s (one on
9133 /// each axis) in lockstep; rustc's forced-arity check on
9134 /// `[(char, char); N]` binds the extension through the array
9135 /// declaration site. Sibling posture to
9136 /// [`QuoteForm::PREFIXES`] / [`QuoteForm::IAC_FORGE_TAGS`] on
9137 /// the outer-tokenizer quote-family axis — where those ALL
9138 /// arrays close the reader-prefix + canonical-form byte
9139 /// vocabularies on the [`QuoteForm`] closed set, this array
9140 /// closes the named-escape (source, decoded) pairing vocabulary
9141 /// on the inner-tokenizer Str-escape axis of the [`Atom`]
9142 /// closed set.
9143 ///
9144 /// The `[(char, char); 3]` shape (rather than a `[char; 3]`
9145 /// singleton) is load-bearing: the escape-arm is a projection
9146 /// from a source byte to a decoded byte, both bytes are typed
9147 /// data, and pinning them as a PAIR at the array declaration
9148 /// site closes the drift channel between pattern (source) and
9149 /// value (decoded) that a scalar array would leave open.
9150 ///
9151 /// Excludes the two pattern-equals-value self-escape arms
9152 /// (`Self::STR_DELIMITER → Self::STR_DELIMITER`,
9153 /// `Self::STR_ESCAPE_LEAD → Self::STR_ESCAPE_LEAD`) because
9154 /// those bind through [`Self::STR_DELIMITER`] +
9155 /// [`Self::STR_ESCAPE_LEAD`] one axis over on the Str-payload
9156 /// delimiter axis — the (source, decoded) pairing there is
9157 /// definitionally the identity by algebra design (a delimiter-
9158 /// swap propagates through pattern AND value at ONE constant
9159 /// per axis). This array closes the OTHER three arms — the
9160 /// pattern-DISTINCT-from-value named-escape rows.
9161 pub const NAMED_ESCAPE_TABLE: [(char, char); 3] = [
9162 (Self::NEWLINE_ESCAPE_SOURCE, Self::NEWLINE_ESCAPE_DECODED),
9163 (Self::TAB_ESCAPE_SOURCE, Self::TAB_ESCAPE_DECODED),
9164 (
9165 Self::CARRIAGE_RETURN_ESCAPE_SOURCE,
9166 Self::CARRIAGE_RETURN_ESCAPE_DECODED,
9167 ),
9168 ];
9169
9170 /// Canonical SELF-escape table — the closed-set ALL array over the
9171 /// substrate's TWO pattern-EQUALS-value arms of
9172 /// [`Self::decode_str_escape`], in canonical declaration order
9173 /// ([`Self::STR_DELIMITER`], [`Self::STR_ESCAPE_LEAD`]) matching
9174 /// the projection's match-arm order. Forced-arity `[char; 2]`
9175 /// composition — a hypothetical third self-escape byte (e.g. a
9176 /// raw-string mode adopting `'#'` as an additional self-escaping
9177 /// delimiter, or a Racket-compat `'|'` verbatim-symbol boundary)
9178 /// extends [`Self::decode_str_escape`]'s match ONCE + this array
9179 /// ONCE + ONE new `pub const` on the closed-set [`Atom`] algebra
9180 /// in lockstep; rustc's forced-arity check on `[char; N]` binds
9181 /// the extension through the array declaration site.
9182 ///
9183 /// Peer to [`Self::NAMED_ESCAPE_TABLE`] on the SAME Str-payload
9184 /// tokenization boundary: where `NAMED_ESCAPE_TABLE` closes the
9185 /// THREE pattern-DISTINCT-from-value named-escape rows (`'n' →
9186 /// '\n'`, `'t' → '\t'`, `'r' → '\r'`), this array closes the TWO
9187 /// pattern-EQUALS-value self-escape rows (`STR_DELIMITER →
9188 /// STR_DELIMITER`, `STR_ESCAPE_LEAD → STR_ESCAPE_LEAD`). The two
9189 /// arrays together span the FIVE non-passthrough arms of
9190 /// [`Self::decode_str_escape`] — every byte the reader's
9191 /// `Token::Str` escape-handler branch specialises on lives in
9192 /// exactly ONE of the two closed-set arrays OR falls through the
9193 /// `other => other` passthrough at the algebra's projection.
9194 ///
9195 /// The `[char; 2]` shape (rather than a `[(char, char); 2]`
9196 /// pairing peer to `NAMED_ESCAPE_TABLE`) is load-bearing: the
9197 /// self-escape arm is definitionally the identity by algebra
9198 /// design (a delimiter-swap propagates through pattern AND value
9199 /// at ONE constant per axis), so the PAIRING collapses to a
9200 /// SCALAR on this sub-vocabulary. The SHAPE ASYMMETRY between the
9201 /// two peer arrays ([(char, char); N] vs [char; N]) IS the
9202 /// structural axis distinguishing the pattern-DISTINCT-from-value
9203 /// vocabulary from the pattern-EQUALS-value vocabulary — a
9204 /// consumer that reaches for the array shape encodes its
9205 /// vocabulary's identity relation in the SHAPE it iterates.
9206 ///
9207 /// Sibling posture to [`QuoteForm::PREFIXES`] +
9208 /// [`QuoteForm::IAC_FORGE_TAGS`] on the outer-tokenizer
9209 /// quote-family axis — where those ALL arrays close the TWO
9210 /// byte-vocabulary axes (reader prefix + iac-forge tag) of the
9211 /// outer-tokenizer `QuoteForm` closed set with parallel shapes,
9212 /// `NAMED_ESCAPE_TABLE` + `SELF_ESCAPE_TABLE` close the TWO
9213 /// sub-vocabularies (pattern-distinct + pattern-equals) of the
9214 /// inner-tokenizer `Atom` Str-escape closed set with asymmetric
9215 /// shapes reflecting the identity-relation asymmetry.
9216 pub const SELF_ESCAPE_TABLE: [char; 2] = [Self::STR_DELIMITER, Self::STR_ESCAPE_LEAD];
9217
9218 /// Canonical closed-set ALL array over every escape-SOURCE byte
9219 /// [`Self::decode_str_escape`] has a non-passthrough arm for — the
9220 /// SPAN of the two peer sub-vocabulary source columns
9221 /// ([`Self::NAMED_ESCAPE_TABLE`]'s three (SOURCE, DECODED) pairs'
9222 /// SOURCE column + [`Self::SELF_ESCAPE_TABLE`]'s two rows) in
9223 /// canonical declaration order matching `decode_str_escape`'s
9224 /// match-arm order. Forced-arity `[char; 5]` composition — a
9225 /// hypothetical sixth non-passthrough arm (e.g. a `'0' → '\0'`
9226 /// NUL-byte extension, a Racket-compat `'a' → '\x07'` BEL, a raw-
9227 /// string mode adopting `'#'` as an additional self-escaping
9228 /// delimiter) extends [`Self::decode_str_escape`]'s match ONCE +
9229 /// EITHER `NAMED_ESCAPE_TABLE` or `SELF_ESCAPE_TABLE` ONCE + this
9230 /// ALL array ONCE + one new per-role `pub const` (or pair) in
9231 /// lockstep; rustc's forced-arity check on `[char; N]` binds the
9232 /// extension through the array declaration site.
9233 ///
9234 /// Cross-sub-vocabulary SPAN peer to [`Self::NAMED_ESCAPE_TABLE`] +
9235 /// [`Self::SELF_ESCAPE_TABLE`] at the ALL-array level: where those
9236 /// two arrays partition the FIVE non-passthrough arms of
9237 /// [`Self::decode_str_escape`] into the pattern-DISTINCT-from-value
9238 /// sub-vocabulary (3 rows, `[(char, char); 3]`) AND the pattern-
9239 /// EQUALS-value sub-vocabulary (2 rows, `[char; 2]`), this array
9240 /// closes the UNION of the SOURCE columns at ONE typed
9241 /// `[char; 5]` on the SAME closed-set [`Atom`] algebra. Pre-lift
9242 /// the SPAN identity lived at TWO sites: the runtime iterator
9243 /// chain
9244 /// `Atom::NAMED_ESCAPE_TABLE.iter().map(|&(src, _)| src)
9245 /// .chain(Atom::SELF_ESCAPE_TABLE.iter().copied()).collect()` at
9246 /// `atom_decode_str_escape_composes_end_to_end_through_reader_for_every_named_arm`
9247 /// AND the prose docstring for [`Self::SELF_ESCAPE_TABLE`] naming
9248 /// "the FIVE non-passthrough arms" as a cardinality identity. Post-
9249 /// lift the SPAN binds at ONE forced-arity `[char; 5]` on the
9250 /// [`Atom`] algebra so consumers that want "every char for which
9251 /// decode_str_escape is not the identity passthrough" iterate the
9252 /// typed array rather than reassembling the two peer arrays at
9253 /// each callsite.
9254 ///
9255 /// Also sibling-shape to [`Sexp::LIST_DELIMITERS`] (`[char; 2]` on
9256 /// the outer-structural paired-delimiter axis of the closed-set
9257 /// [`Sexp`] algebra), [`Sexp::COMMENT_DELIMITERS`] (`[char; 2]` on
9258 /// the reader-discard paired-delimiter axis of the SAME [`Sexp`]
9259 /// algebra), [`Atom::BOOL_LITERALS`] (`[&'static str; 2]` on the
9260 /// Scheme-bool spelling axis of this same [`Atom`] algebra), and
9261 /// [`QuoteForm::LEADS`] (`[char; 3]` on the DISTINCT-lead-byte
9262 /// sub-vocabulary axis of the closed-set [`QuoteForm`] algebra) —
9263 /// every closed-set outer projection on the substrate that carries
9264 /// a scalar `[char; N]` sub-vocabulary now pins its canonical
9265 /// bytes at ONE `pub const` per role plus a forced-arity ALL array
9266 /// for family-wide consumers.
9267 ///
9268 /// Composition law (SPAN): `ESCAPE_SOURCES ==
9269 /// [NAMED_ESCAPE_TABLE[0].0, NAMED_ESCAPE_TABLE[1].0,
9270 /// NAMED_ESCAPE_TABLE[2].0, SELF_ESCAPE_TABLE[0],
9271 /// SELF_ESCAPE_TABLE[1]]` AND `ESCAPE_SOURCES.len() ==
9272 /// NAMED_ESCAPE_TABLE.len() + SELF_ESCAPE_TABLE.len()` — the
9273 /// forced-arity + canonical declaration order together pin every
9274 /// downstream index-sweep consumer to the (named-source-column
9275 /// prefix, self-source-column suffix) partition at rustc time; a
9276 /// reorder that broke the partition (e.g. interleaving the two
9277 /// sub-vocabularies' rows) fails at the composition pin below.
9278 ///
9279 /// Path-uniformity contract carried at the row level: for every
9280 /// `esc` in `ESCAPE_SOURCES`, `Self::decode_str_escape(esc) != esc`
9281 /// iff `esc` is a NAMED_ESCAPE_TABLE SOURCE row (the three
9282 /// pattern-DISTINCT-from-value arms), and `Self::decode_str_escape
9283 /// (esc) == esc` iff `esc` is a SELF_ESCAPE_TABLE row (the two
9284 /// pattern-EQUALS-value arms). The union is exhaustive over the
9285 /// FIVE non-passthrough arms so no `ESCAPE_SOURCES` row projects
9286 /// through the `other => other` passthrough branch — the arm-set
9287 /// closure is pinned structurally at
9288 /// `atom_escape_sources_every_row_projects_through_a_non_passthrough_arm_of_decode_str_escape`.
9289 ///
9290 /// Pairwise disjointness: every row is distinct from every other
9291 /// row — the closed-set SPAN inherits pairwise disjointness from
9292 /// the two peer sub-vocabularies (each already pairwise distinct
9293 /// via `atom_named_escape_table_sources_pairwise_distinct` +
9294 /// `atom_self_escape_table_pairwise_distinct`) PLUS the cross-
9295 /// sub-vocabulary disjointness pinned by
9296 /// `atom_self_escape_table_disjoint_from_named_escape_table`. This
9297 /// ALL array's own `atom_escape_sources_pairwise_distinct` test
9298 /// closes the disjointness contract at the SPAN-level so a future
9299 /// refactor that added a sixth arm whose SOURCE aliased an
9300 /// existing arm surfaces HERE rather than at a distant reader
9301 /// round-trip.
9302 ///
9303 /// Future consumers that compose against [`Self::ESCAPE_SOURCES`]:
9304 /// - LSP / REPL completion for the escape-source vocabulary — the
9305 /// completion set IS `Self::ESCAPE_SOURCES` rather than a
9306 /// per-consumer chain over the two peer arrays.
9307 /// - `tatara-check` coverage assertions that a `.lisp` corpus
9308 /// exercises every non-passthrough escape arm — the sweep IS
9309 /// `Self::ESCAPE_SOURCES.iter()` rather than a runtime chain
9310 /// over `NAMED_ESCAPE_TABLE.iter().map(|&(src, _)| src)
9311 /// .chain(SELF_ESCAPE_TABLE.iter().copied())`.
9312 /// - Any future syntax-highlighter that colors escape sequences —
9313 /// the classifier binds through `Self::ESCAPE_SOURCES` rather
9314 /// than through two parallel per-sub-vocabulary lookups.
9315 /// - Any future fuzz-input generator that biases toward escape
9316 /// sequences — the source-byte pool IS `Self::ESCAPE_SOURCES`
9317 /// rather than reassembled from the two peer arrays.
9318 ///
9319 /// Theory anchor: THEORY.md §III — the typescape; the FIVE non-
9320 /// passthrough source bytes now bind at ONE typed `[char; 5]` on
9321 /// the closed-set [`Atom`] algebra rather than at a runtime chain
9322 /// over the two peer sub-vocabulary arrays reassembled per
9323 /// consumer. THEORY.md §V.1 — knowable platform; the non-
9324 /// passthrough source-byte SPAN becomes load-bearing typed data at
9325 /// the algebra level. THEORY.md §VI.1 — generation over
9326 /// composition; the "FIVE non-passthrough arms" identity that
9327 /// lived as prose in the [`Self::SELF_ESCAPE_TABLE`] docstring AND
9328 /// as a runtime iterator chain at one test site regenerates
9329 /// identically through this ONE typed forced-arity array.
9330 pub const ESCAPE_SOURCES: [char; 5] = [
9331 Self::NEWLINE_ESCAPE_SOURCE,
9332 Self::TAB_ESCAPE_SOURCE,
9333 Self::CARRIAGE_RETURN_ESCAPE_SOURCE,
9334 Self::STR_DELIMITER,
9335 Self::STR_ESCAPE_LEAD,
9336 ];
9337
9338 /// Canonical closed-set ALL array over every DECODED byte
9339 /// [`Self::decode_str_escape`] can emit from a non-passthrough arm
9340 /// — the SPAN of the two peer sub-vocabulary DECODED columns
9341 /// ([`Self::NAMED_ESCAPE_TABLE`]'s three (SOURCE, DECODED) pairs'
9342 /// DECODED column + [`Self::SELF_ESCAPE_TABLE`]'s two rows, which
9343 /// are pattern-EQUALS-value so their DECODED column is definitionally
9344 /// the row byte itself) in canonical declaration order matching
9345 /// `decode_str_escape`'s match-arm order. Forced-arity `[char; 5]`
9346 /// composition — a hypothetical sixth non-passthrough arm (e.g. a
9347 /// `'0' → '\0'` NUL-byte extension, a Racket-compat `'a' → '\x07'`
9348 /// BEL, a raw-string mode adopting `'#'` as an additional
9349 /// self-escaping delimiter) extends [`Self::decode_str_escape`]'s
9350 /// match ONCE + EITHER `NAMED_ESCAPE_TABLE` or `SELF_ESCAPE_TABLE`
9351 /// ONCE + this ALL array ONCE + [`Self::ESCAPE_SOURCES`] ONCE + one
9352 /// new per-role `pub const` (or pair) in lockstep; rustc's forced-
9353 /// arity check on `[char; N]` binds the extension through the array
9354 /// declaration site.
9355 ///
9356 /// Column-dual peer to [`Self::ESCAPE_SOURCES`] on the SAME closed-set
9357 /// [`Atom`] algebra: where `ESCAPE_SOURCES` closes the SOURCE column
9358 /// of the FIVE non-passthrough arms at ONE typed `[char; 5]`, this
9359 /// array closes the DECODED column at the SAME shape. Together the
9360 /// two arrays close the (SOURCE, DECODED) cross-product of
9361 /// `decode_str_escape`'s non-passthrough arm-set at two byte-
9362 /// identical `[char; 5]` shapes on the SAME closed-set [`Atom`]
9363 /// algebra — the shape symmetry across the two columns of the SAME
9364 /// arm-set is itself a typed load-bearing invariant carrying the
9365 /// column-dual identity relation on the algebra.
9366 ///
9367 /// Cross-sub-vocabulary SPAN peer to [`Self::NAMED_ESCAPE_TABLE`] +
9368 /// [`Self::SELF_ESCAPE_TABLE`] at the ALL-array level: where those
9369 /// two arrays partition the FIVE non-passthrough arms of
9370 /// [`Self::decode_str_escape`] into the pattern-DISTINCT-from-value
9371 /// sub-vocabulary (3 rows, `[(char, char); 3]`) AND the pattern-
9372 /// EQUALS-value sub-vocabulary (2 rows, `[char; 2]`), this array
9373 /// closes the UNION of the DECODED columns at ONE typed
9374 /// `[char; 5]` on the SAME closed-set [`Atom`] algebra. Pre-lift
9375 /// the DECODED SPAN identity lived at ZERO callsites — the substrate
9376 /// had a typed SOURCE-column SPAN ([`Self::ESCAPE_SOURCES`]) but the
9377 /// DECODED-column SPAN was only reachable by iterating the two peer
9378 /// sub-vocabulary arrays' DECODED columns per consumer OR by mapping
9379 /// `ESCAPE_SOURCES` through [`Self::decode_str_escape`] at runtime;
9380 /// post-lift the DECODED SPAN binds at ONE forced-arity `[char; 5]`
9381 /// on the [`Atom`] algebra so consumers that want "every byte
9382 /// decode_str_escape can emit from a typed non-passthrough arm"
9383 /// iterate the typed array rather than reassembling it per callsite.
9384 ///
9385 /// Also sibling-shape to [`Sexp::LIST_DELIMITERS`] (`[char; 2]` on
9386 /// the outer-structural paired-delimiter axis of the closed-set
9387 /// [`Sexp`] algebra), [`Sexp::COMMENT_DELIMITERS`] (`[char; 2]` on
9388 /// the reader-discard paired-delimiter axis of the SAME [`Sexp`]
9389 /// algebra), [`Atom::BOOL_LITERALS`] (`[&'static str; 2]` on the
9390 /// Scheme-bool spelling axis of this same [`Atom`] algebra), and
9391 /// [`QuoteForm::LEADS`] (`[char; 3]` on the DISTINCT-lead-byte
9392 /// sub-vocabulary axis of the closed-set [`QuoteForm`] algebra) —
9393 /// every closed-set outer projection on the substrate that carries
9394 /// a scalar `[char; N]` sub-vocabulary now pins its canonical bytes
9395 /// at ONE `pub const` per role plus a forced-arity ALL array for
9396 /// family-wide consumers.
9397 ///
9398 /// Composition law (SPAN): `ESCAPE_DECODED ==
9399 /// [NAMED_ESCAPE_TABLE[0].1, NAMED_ESCAPE_TABLE[1].1,
9400 /// NAMED_ESCAPE_TABLE[2].1, SELF_ESCAPE_TABLE[0],
9401 /// SELF_ESCAPE_TABLE[1]]` AND `ESCAPE_DECODED.len() ==
9402 /// NAMED_ESCAPE_TABLE.len() + SELF_ESCAPE_TABLE.len()` — the
9403 /// forced-arity + canonical declaration order together pin every
9404 /// downstream index-sweep consumer to the (named-decoded-column
9405 /// prefix, self-decoded-column suffix) partition at rustc time; a
9406 /// reorder that broke the partition (e.g. interleaving the two
9407 /// sub-vocabularies' rows) fails at the composition pin below.
9408 ///
9409 /// Column-dual pointwise projection law: for every index `i` in
9410 /// `0..5`, `ESCAPE_DECODED[i] == Self::decode_str_escape(
9411 /// ESCAPE_SOURCES[i])`. The two forced-arity `[char; 5]` arrays are
9412 /// the SOURCE column and DECODED column of `decode_str_escape`'s
9413 /// non-passthrough arm-set — the pointwise projection identity is
9414 /// pinned structurally at
9415 /// `atom_escape_decoded_projects_pointwise_from_escape_sources_through_decode_str_escape`.
9416 ///
9417 /// Pairwise disjointness: every row is distinct from every other
9418 /// row — `'\n'`, `'\t'`, `'\r'`, `'"'`, `'\\'` are five distinct
9419 /// C0-and-ASCII bytes. Distinctness on the DECODED column follows
9420 /// from (a) the NAMED sub-vocabulary's DECODED-column pairwise
9421 /// distinctness at `atom_named_escape_table_decoded_pairwise_distinct`,
9422 /// (b) the SELF sub-vocabulary's pairwise distinctness at
9423 /// `atom_self_escape_table_pairwise_distinct`, and (c) the fact
9424 /// that no NAMED-DECODED byte (`'\n'`, `'\t'`, `'\r'` — all C0
9425 /// control bytes) can alias a SELF byte (`'"'`, `'\\'` — printable
9426 /// ASCII bytes). The pairwise-distinctness pin below closes the
9427 /// contract at the SPAN level so a future refactor that added a
9428 /// sixth arm whose DECODED aliased an existing row surfaces HERE
9429 /// rather than at a distant reader round-trip.
9430 ///
9431 /// Future consumers that compose against [`Self::ESCAPE_DECODED`]:
9432 /// - A Str-render / encoder consumer that needs to escape a DECODED
9433 /// byte back into its SOURCE form — the classifier IS "is this
9434 /// byte in `Self::ESCAPE_DECODED`?" rather than a per-consumer
9435 /// chain over the two peer sub-vocabularies' DECODED columns.
9436 /// - LSP / REPL diagnostic rendering that needs to name every byte
9437 /// the substrate's escape-handler can emit — the completion set
9438 /// IS `Self::ESCAPE_DECODED` rather than a runtime map through
9439 /// `decode_str_escape` from `ESCAPE_SOURCES`.
9440 /// - A syntax-highlighter that colors decoded-escape byte payloads —
9441 /// the classifier binds through `Self::ESCAPE_DECODED` rather
9442 /// than through two parallel per-sub-vocabulary lookups.
9443 /// - A fuzz-input generator that biases toward decoded-escape byte
9444 /// payloads (probing the reader's error-recovery path when a
9445 /// raw C0 byte appears inside a Str payload) — the target-byte
9446 /// pool IS `Self::ESCAPE_DECODED` rather than reassembled from
9447 /// the two peer arrays.
9448 ///
9449 /// Theory anchor: THEORY.md §III — the typescape; the FIVE
9450 /// DECODED bytes of the non-passthrough arm-set now bind at ONE
9451 /// typed `[char; 5]` on the closed-set [`Atom`] algebra rather than
9452 /// at a runtime map through `decode_str_escape` from
9453 /// `ESCAPE_SOURCES`. THEORY.md §V.1 — knowable platform; the
9454 /// non-passthrough DECODED-byte SPAN becomes load-bearing typed
9455 /// data at the algebra level. THEORY.md §VI.1 — generation over
9456 /// composition; the column-dual identity that lived only as a
9457 /// runtime `decode_str_escape` projection of the peer SOURCE-column
9458 /// SPAN regenerates identically through this ONE typed forced-arity
9459 /// array.
9460 pub const ESCAPE_DECODED: [char; 5] = [
9461 Self::NEWLINE_ESCAPE_DECODED,
9462 Self::TAB_ESCAPE_DECODED,
9463 Self::CARRIAGE_RETURN_ESCAPE_DECODED,
9464 Self::STR_DELIMITER,
9465 Self::STR_ESCAPE_LEAD,
9466 ];
9467
9468 /// Canonical closed-set ALL array over every `(SOURCE, DECODED)`
9469 /// pair [`Self::decode_str_escape`] projects at a non-passthrough
9470 /// arm — the paired-column SPAN closing BOTH columns of the FIVE
9471 /// non-passthrough arms at ONE typed forced-arity
9472 /// `[(char, char); 5]` on the closed-set [`Atom`] algebra.
9473 /// Composes as `NAMED_ESCAPE_TABLE`'s three pattern-DISTINCT-from-
9474 /// value rows (which are already `(char, char)` shape) followed by
9475 /// `SELF_ESCAPE_TABLE`'s two pattern-EQUALS-value rows re-shaped as
9476 /// `(row, row)` pairs (the definitional-identity closure of the
9477 /// self-escape sub-vocabulary at the paired shape), in canonical
9478 /// declaration order matching `decode_str_escape`'s match-arm order.
9479 ///
9480 /// Cross-column peer-collapse of [`Self::ESCAPE_SOURCES`] +
9481 /// [`Self::ESCAPE_DECODED`] — the two byte-identical `[char; 5]`
9482 /// column-dual arrays close the SOURCE column and DECODED column
9483 /// SEPARATELY at ONE forced-arity `[char; 5]` each; this array
9484 /// closes BOTH columns TOGETHER at ONE typed forced-arity
9485 /// `[(char, char); 5]` on the SAME closed-set [`Atom`] algebra.
9486 /// Together the three arrays close the FIVE non-passthrough arms
9487 /// at THREE typed forced-arity shapes: `[char; 5]` on the SOURCE
9488 /// column, `[char; 5]` on the DECODED column, and `[(char, char);
9489 /// 5]` on the paired-column composition. The shape symmetry
9490 /// `(SOURCE, DECODED)` pair at row `i` IS
9491 /// `(ESCAPE_SOURCES[i], ESCAPE_DECODED[i])` is a load-bearing
9492 /// pointwise identity carrying the two-column composition relation
9493 /// on the algebra.
9494 ///
9495 /// Paired-column SPAN peer to [`Self::NAMED_ESCAPE_TABLE`] +
9496 /// [`Self::SELF_ESCAPE_TABLE`] at the paired-shape level: where
9497 /// those two arrays partition the FIVE arms into the pattern-
9498 /// DISTINCT-from-value sub-vocabulary (3 rows already at
9499 /// `[(char, char); 3]` shape by algebra design) AND the pattern-
9500 /// EQUALS-value sub-vocabulary (2 rows at `[char; 2]` shape by
9501 /// definitional-identity collapse), this array closes the UNION of
9502 /// the two sub-vocabularies at ONE typed `[(char, char); 5]` — the
9503 /// self-escape rows re-shaped from `char` to `(char, char)` at the
9504 /// SPAN level via the definitional-identity `(row, row)`
9505 /// composition. Pre-lift the paired-column SPAN identity lived at
9506 /// ZERO callsites at the paired shape — consumers wanting "every
9507 /// `(SOURCE, DECODED)` pair `decode_str_escape` projects at a non-
9508 /// passthrough arm on the closed-set [`Atom`] algebra" had to zip
9509 /// the two column-dual `[char; 5]` peer arrays at their sites OR
9510 /// iterate `NAMED_ESCAPE_TABLE` and re-shape `SELF_ESCAPE_TABLE`'s
9511 /// rows to `(row, row)` per callsite. Post-lift the paired-column
9512 /// SPAN binds at ONE forced-arity `[(char, char); 5]` on the
9513 /// [`Atom`] algebra.
9514 ///
9515 /// Also sibling-shape to [`Self::NAMED_ESCAPE_TABLE`]
9516 /// (`[(char, char); 3]` on the same paired-column axis, one sub-
9517 /// vocabulary over on the pattern-DISTINCT-from-value axis of the
9518 /// SAME closed-set [`Atom`] algebra) — the paired-column shape is
9519 /// the substrate-canonical shape for closing a `(SOURCE, DECODED)`
9520 /// cross-product at ONE typed array on the algebra; extending it
9521 /// from the NAMED sub-vocabulary's 3-row shape to the SPAN's 5-row
9522 /// shape is a mechanical arity extension. A hypothetical sixth
9523 /// non-passthrough arm (e.g. a `'0' → '\0'` NUL-byte extension,
9524 /// a Racket-compat `'a' → '\x07'` BEL, a raw-string mode adopting
9525 /// `'#'` as an additional self-escaping delimiter) extends
9526 /// [`Self::decode_str_escape`]'s match ONCE plus EITHER
9527 /// `NAMED_ESCAPE_TABLE` or `SELF_ESCAPE_TABLE` ONCE plus this ALL
9528 /// array ONCE plus both column-dual `[char; 5]` peer arrays
9529 /// ([`Self::ESCAPE_SOURCES`] and [`Self::ESCAPE_DECODED`]) ONCE
9530 /// each plus one new per-role `pub const` (or pair) in lockstep;
9531 /// rustc's forced-arity check on `[(char, char); N]` binds the
9532 /// extension through the array declaration site.
9533 ///
9534 /// Composition law (paired SPAN): for every index `i` in `0..5`,
9535 /// `ESCAPE_TABLE[i] == (ESCAPE_SOURCES[i], ESCAPE_DECODED[i])`.
9536 /// The forced-arity `[(char, char); 5]` + canonical declaration
9537 /// order pin every downstream index-sweep consumer to the (named
9538 /// prefix, self suffix) partition at rustc time. The paired-SPAN
9539 /// row identity is pinned structurally at
9540 /// `atom_escape_table_composes_pointwise_from_escape_sources_and_escape_decoded_column_duals`.
9541 ///
9542 /// Sub-vocabulary partition law: `ESCAPE_TABLE[0..3] ==
9543 /// NAMED_ESCAPE_TABLE` (the pattern-DISTINCT-from-value sub-
9544 /// vocabulary at its native paired shape passes through
9545 /// identically), AND for i in 3..5, `ESCAPE_TABLE[i] ==
9546 /// (SELF_ESCAPE_TABLE[i-3], SELF_ESCAPE_TABLE[i-3])` (the pattern-
9547 /// EQUALS-value sub-vocabulary re-shapes from `char` to
9548 /// `(row, row)` at the SPAN level via the definitional-identity
9549 /// collapse). Pinned structurally at
9550 /// `atom_escape_table_partitions_into_named_paired_prefix_and_self_reshape_suffix`.
9551 ///
9552 /// Pattern-classification partition law: for every index `i` in
9553 /// `0..3`, `ESCAPE_TABLE[i].0 != ESCAPE_TABLE[i].1` (the NAMED
9554 /// pattern-DISTINCT-from-value prefix); for every index `i` in
9555 /// `3..5`, `ESCAPE_TABLE[i].0 == ESCAPE_TABLE[i].1` (the SELF
9556 /// pattern-EQUALS-value suffix). The classification carried in the
9557 /// row's per-column identity relation IS the sub-vocabulary
9558 /// identity — a consumer classifying an escape arm reads the sub-
9559 /// vocabulary off `row.0 == row.1` rather than off an index range
9560 /// or a separate tag. Pinned structurally at
9561 /// `atom_escape_table_named_prefix_is_pattern_distinct_and_self_suffix_is_pattern_equals`.
9562 ///
9563 /// Pointwise projection law: for every `(src, decoded)` in
9564 /// `ESCAPE_TABLE`, `Self::decode_str_escape(src) == decoded`. The
9565 /// paired-column SPAN closes the (input, output) cross-product of
9566 /// `decode_str_escape`'s non-passthrough arm-set at ONE typed
9567 /// array so a refactor that drifted the arm-set (swapped rows,
9568 /// added a row without extending the array, or removed a row
9569 /// without extending the array) surfaces HERE at the first drifted
9570 /// pair rather than at a distant sweep site.
9571 ///
9572 /// Future consumers that compose against [`Self::ESCAPE_TABLE`]:
9573 /// - A round-trip reader/renderer that pairs SOURCE bytes with
9574 /// their DECODED payloads — the paired vocabulary IS
9575 /// `Self::ESCAPE_TABLE` rather than a zip of the two column-dual
9576 /// peer arrays.
9577 /// - LSP / REPL diagnostic rendering that names both columns of
9578 /// every non-passthrough arm — the completion set IS
9579 /// `Self::ESCAPE_TABLE` rather than reassembled from the two
9580 /// sub-vocabulary arrays via a `char → (char, char)` re-shape on
9581 /// the SELF rows.
9582 /// - A syntax-highlighter that colors both the SOURCE byte and the
9583 /// DECODED payload of every escape arm — the classifier binds
9584 /// through `Self::ESCAPE_TABLE` rather than through two parallel
9585 /// per-column lookups.
9586 /// - A fuzz-input generator that exercises `decode_str_escape`'s
9587 /// projection round-trip — the input-output pool IS
9588 /// `Self::ESCAPE_TABLE` rather than a runtime zip of the peer
9589 /// arrays.
9590 /// - A hypothetical `EscapeArm` typed sum enumerating the FIVE
9591 /// non-passthrough arms at ONE closed-set enum on the algebra
9592 /// (with `ESCAPE_TABLE` becoming its `.pair()` projection).
9593 ///
9594 /// Theory anchor: THEORY.md §III — the typescape; the FIVE
9595 /// `(SOURCE, DECODED)` pairs of the non-passthrough arm-set now
9596 /// bind at ONE typed `[(char, char); 5]` on the closed-set
9597 /// [`Atom`] algebra rather than at a runtime zip of the two peer
9598 /// column-dual arrays. THEORY.md §V.1 — knowable platform; the
9599 /// non-passthrough paired-column SPAN becomes load-bearing typed
9600 /// data at the algebra level. THEORY.md §VI.1 — generation over
9601 /// composition; the paired-column identity that lived only as a
9602 /// runtime zip of the peer column-dual `[char; 5]` SPANs
9603 /// regenerates identically through this ONE typed forced-arity
9604 /// pair-array. THEORY.md §II.1 invariant 5 — composition preserves
9605 /// proofs; the pointwise composition law `ESCAPE_TABLE[i] ==
9606 /// (ESCAPE_SOURCES[i], ESCAPE_DECODED[i])` is a load-bearing
9607 /// structural invariant carrying the two-column composition
9608 /// relation between the paired SPAN and its two column-dual peer
9609 /// SPANs. A refactor that drifted any of the three arrays'
9610 /// declaration orders (paired SPAN, SOURCE-column SPAN, DECODED-
9611 /// column SPAN) OR drifted `decode_str_escape`'s match-arm
9612 /// ordering surfaces at the first drifted index across the three
9613 /// pointwise composition pins.
9614 pub const ESCAPE_TABLE: [(char, char); 5] = [
9615 Self::NAMED_ESCAPE_TABLE[0],
9616 Self::NAMED_ESCAPE_TABLE[1],
9617 Self::NAMED_ESCAPE_TABLE[2],
9618 (Self::SELF_ESCAPE_TABLE[0], Self::SELF_ESCAPE_TABLE[0]),
9619 (Self::SELF_ESCAPE_TABLE[1], Self::SELF_ESCAPE_TABLE[1]),
9620 ];
9621
9622 #[must_use]
9623 /// A string payload as source text, quoted and escaped so that
9624 /// [`Self::decode_str_escape`] (plus the reader's `\u{…}` arm) reads back
9625 /// exactly these bytes.
9626 ///
9627 /// The inverse of the reader, deliberately written as one: anything the
9628 /// reader cannot decode is emitted as `\u{…}` rather than as a shorter
9629 /// escape that would mean something different.
9630 #[must_use]
9631 pub fn escape_str_payload(s: &str) -> String {
9632 let mut out = String::with_capacity(s.len() + 2);
9633 out.push(Self::STR_DELIMITER);
9634 for c in s.chars() {
9635 match c {
9636 Self::STR_DELIMITER | Self::STR_ESCAPE_LEAD => {
9637 out.push(Self::STR_ESCAPE_LEAD);
9638 out.push(c);
9639 }
9640 Self::NEWLINE_ESCAPE_DECODED => {
9641 out.push(Self::STR_ESCAPE_LEAD);
9642 out.push(Self::NEWLINE_ESCAPE_SOURCE);
9643 }
9644 Self::TAB_ESCAPE_DECODED => {
9645 out.push(Self::STR_ESCAPE_LEAD);
9646 out.push(Self::TAB_ESCAPE_SOURCE);
9647 }
9648 Self::CARRIAGE_RETURN_ESCAPE_DECODED => {
9649 out.push(Self::STR_ESCAPE_LEAD);
9650 out.push(Self::CARRIAGE_RETURN_ESCAPE_SOURCE);
9651 }
9652 // Everything else the reader cannot round-trip literally goes
9653 // out as `\u{…}`, which it CAN. Printable characters — including
9654 // every non-ASCII one — are emitted raw, so ordinary text stays
9655 // readable.
9656 c if c.is_control() => {
9657 out.push_str("\\u{");
9658 out.push_str(&alloc_hex(c as u32));
9659 out.push('}');
9660 }
9661 c => out.push(c),
9662 }
9663 }
9664 out.push(Self::STR_DELIMITER);
9665 out
9666 }
9667
9668 pub const fn decode_str_escape(esc: char) -> char {
9669 match esc {
9670 Self::NEWLINE_ESCAPE_SOURCE => Self::NEWLINE_ESCAPE_DECODED,
9671 Self::TAB_ESCAPE_SOURCE => Self::TAB_ESCAPE_DECODED,
9672 Self::CARRIAGE_RETURN_ESCAPE_SOURCE => Self::CARRIAGE_RETURN_ESCAPE_DECODED,
9673 Self::STR_DELIMITER => Self::STR_DELIMITER,
9674 Self::STR_ESCAPE_LEAD => Self::STR_ESCAPE_LEAD,
9675 other => other,
9676 }
9677 }
9678
9679 /// Canonical [`Self::Symbol`] constructor — first of the six per-
9680 /// variant typed-construct methods on the closed-set [`Atom`]
9681 /// algebra. Takes `impl Into<String>` so the consumer composes any
9682 /// `&str` / `String` / `Cow<'_, str>` into the typed payload without
9683 /// pre-coercing at its site — the `.into()` boundary lives at this
9684 /// method on the algebra, parallel to how the [`Sexp`] outer
9685 /// constructors ([`Sexp::symbol`], [`Sexp::keyword`],
9686 /// [`Sexp::string`]) accept the same `impl Into<String>` shape at
9687 /// the outer algebra layer.
9688 ///
9689 /// Sibling typed-construct family on the closed-set [`Atom`]
9690 /// algebra — paired section-for-retraction with the soft-projection
9691 /// family ([`Self::as_symbol`], [`Self::as_keyword`],
9692 /// [`Self::as_string`], [`Self::as_int`], [`Self::as_float`],
9693 /// [`Self::as_bool`]). Pre-lift the typed-construct family was
9694 /// missing from the algebra: consumers reached for the bare
9695 /// `Self::Symbol(s.into())` tuple-variant constructor + `.into()`
9696 /// coercion at every site (with no `impl Into` ergonomy on the
9697 /// algebra), AND the soft-projection family had no constructor
9698 /// peer — section-for-retraction was uneven. Post-lift every
9699 /// consumer that builds an [`Atom`] from a typed payload at one
9700 /// site AND projects an [`Atom`] back to its typed payload at
9701 /// another binds to ONE method per direction on the algebra. The
9702 /// six [`Sexp`] outer constructors ([`Sexp::symbol`] through
9703 /// [`Sexp::boolean`]) route through `Self::Atom(Atom::X(_))` —
9704 /// `.into()` ergonomy on the inner algebra is reused at the outer
9705 /// algebra without re-derivation.
9706 ///
9707 /// Round-trip law binding it to the soft-projection sibling: for
9708 /// every `s: &str`, `Atom::symbol(s).as_symbol() == Some(s)` —
9709 /// every other arm projects to `None`. Same posture across the
9710 /// five sibling pairs (`Atom::keyword(s).as_keyword() == Some(s)`,
9711 /// …). The `kind()` projection ([`Self::kind`]) similarly
9712 /// round-trips through the construct face: `Atom::symbol(_).kind()
9713 /// == AtomKind::Symbol`.
9714 ///
9715 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle;
9716 /// every consumer that constructs an [`Atom`] of a typed kind binds
9717 /// to ONE typed method on the algebra rather than to the bare
9718 /// tuple-variant constructor + per-site `.into()` coercion.
9719 /// THEORY.md §V.1 — knowable platform; the `(AtomKind variant,
9720 /// typed construct method)` pair becomes a TYPE projection on the
9721 /// substrate's [`Atom`] algebra. THEORY.md §VI.1 — generation over
9722 /// composition; the `[Sexp; 6]` outer constructors at
9723 /// [`Sexp::symbol`]–[`Sexp::boolean`] regenerate identically
9724 /// through `Self::Atom(Atom::X(_))` composition rather than
9725 /// re-deriving the `.into()` + tuple-variant pair per outer
9726 /// constructor.
9727 ///
9728 /// Frontier inspiration: Racket's `(symbol 'x)` / `(string s)` —
9729 /// the typed-construct face the consumer reaches for a typed
9730 /// atomic value paired one-for-one with `(symbol? v)` /
9731 /// `(symbol->string v)` predicate/projection siblings; the
9732 /// substrate's [`Self::symbol`] / [`Self::as_symbol`] pair is the
9733 /// Rust-typed peer on the closed-set [`Atom`] algebra, with
9734 /// `impl Into<String>` standing in for Racket's typed-pair coerce
9735 /// face. MLIR's `mlir::SymbolAttr::get(ctx, name)` — typed-IR
9736 /// attribute construction routes through ONE typed factory paired
9737 /// with `mlir::dyn_cast<SymbolAttr>(attr)` on the projection face;
9738 /// `Atom::symbol` is the substrate's unstructured-Rust peer.
9739 #[must_use]
9740 pub fn symbol(s: impl Into<String>) -> Self {
9741 Self::Symbol(s.into())
9742 }
9743
9744 /// Canonical [`Self::Keyword`] constructor — second of the six
9745 /// per-variant typed-construct methods on the closed-set [`Atom`]
9746 /// algebra. See [`Self::symbol`] for the algebra-level docstring.
9747 #[must_use]
9748 pub fn keyword(s: impl Into<String>) -> Self {
9749 Self::Keyword(s.into())
9750 }
9751
9752 /// Canonical [`Self::Str`] constructor — third of the six per-variant
9753 /// typed-construct methods. The method name is `string` for
9754 /// consumer-vocabulary continuity with [`Self::as_string`] /
9755 /// [`Sexp::string`] / [`crate::error::SexpShape::String`] (the typed
9756 /// payload variant is `Str` for `String` shortening; the consumer-
9757 /// facing method keeps `string` for symmetry).
9758 #[must_use]
9759 pub fn string(s: impl Into<String>) -> Self {
9760 Self::Str(s.into())
9761 }
9762
9763 /// Canonical [`Self::Int`] constructor — fourth of the six per-variant
9764 /// typed-construct methods. The `i64` is taken by value (no
9765 /// `impl Into<…>` widening) — strict typed identity at the algebra
9766 /// boundary, the same posture [`Self::as_int`] preserves on the
9767 /// soft-projection face (`Atom::Int(n)` projects to `Some(n)` only;
9768 /// the `Sexp::as_float` consumer is where Int→Float widening lives).
9769 #[must_use]
9770 pub fn int(n: i64) -> Self {
9771 Self::Int(n)
9772 }
9773
9774 /// Canonical [`Self::Float`] constructor — fifth of the six
9775 /// per-variant typed-construct methods. The `f64` is taken by value
9776 /// (no `impl Into<…>` widening), matching [`Self::int`]'s strict
9777 /// typed-identity posture at the algebra boundary.
9778 #[must_use]
9779 pub fn float(n: f64) -> Self {
9780 Self::Float(n)
9781 }
9782
9783 /// Canonical [`Self::Bool`] constructor — sixth and last of the six
9784 /// per-variant typed-construct methods on the closed-set [`Atom`]
9785 /// algebra. Together with the five siblings ([`Self::symbol`],
9786 /// [`Self::keyword`], [`Self::string`], [`Self::int`],
9787 /// [`Self::float`]) the per-`Atom`-variant typed-construct family is
9788 /// complete across all six closed-set arms, and pairs section-for-
9789 /// retraction with the soft-projection family ([`Self::as_symbol`],
9790 /// [`Self::as_keyword`], [`Self::as_string`], [`Self::as_int`],
9791 /// [`Self::as_float`], [`Self::as_bool`]) — every consumer that
9792 /// constructs an [`Atom`] from a typed payload at one site AND
9793 /// projects an [`Atom`] back to its typed payload at another binds
9794 /// to ONE method per direction on the algebra rather than to the
9795 /// bare tuple-variant constructor + the soft-projection method
9796 /// asymmetrically.
9797 ///
9798 /// The closed-set `bool` payload's Scheme-canonical `#t` / `#f`
9799 /// reader lexemes are dispatched at [`Self::from_lexeme`] (the
9800 /// typed-ENTRY classifier) — this method is the construction face
9801 /// the consumer composes the typed `bool` value into when building
9802 /// an [`Atom`] from already-typed Rust, parallel to how
9803 /// [`Self::int`] and [`Self::float`] take their typed payload by
9804 /// value.
9805 #[must_use]
9806 pub fn boolean(b: bool) -> Self {
9807 Self::Bool(b)
9808 }
9809
9810 /// Project the atomic value into its closed-set [`AtomKind`] marker —
9811 /// `Symbol(_) → AtomKind::Symbol`, `Keyword(_) → AtomKind::Keyword`,
9812 /// `Str(_) → AtomKind::Str`, `Int(_) → AtomKind::Int`,
9813 /// `Float(_) → AtomKind::Float`, `Bool(_) → AtomKind::Bool`. The
9814 /// projection discards the payload and surfaces the typed
9815 /// discriminator that every per-atom-kind dispatch site (Hash cache-
9816 /// key bytes via [`AtomKind::hash_discriminator`], outer-shape
9817 /// projection via [`AtomKind::sexp_shape`], diagnostic label via
9818 /// [`AtomKind::label`]) keys on.
9819 ///
9820 /// Soft-projection peer of [`Sexp::as_quote_form`]: where
9821 /// `as_quote_form` decomposes the four homoiconic prefix wrappers
9822 /// into `(QuoteForm, &Sexp)`, `kind` decomposes the six atomic
9823 /// payloads into `AtomKind` alone — there is no inner-sexp body to
9824 /// surface, so the projection's return type is just the marker.
9825 /// Sibling-arm sweep with the quote-family `as_quote_form` /
9826 /// `QuoteForm` algebra lifts the (Atom variant, byte-discriminator,
9827 /// canonical-label, SexpShape variant) quadruple from per-callsite
9828 /// discipline (`Hash for Atom`'s six byte literals AND
9829 /// `domain::sexp_shape`'s six SexpShape literals) onto ONE typed
9830 /// algebra the substrate's diagnostic + cache-key surfaces both
9831 /// route through.
9832 ///
9833 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
9834 /// (Atom variant, downstream-consumer-payload) pairing now binds at
9835 /// ONE typed projection site (this method composed with
9836 /// [`AtomKind`]'s arms) regardless of which consumer surface
9837 /// (cache-key Hash, diagnostic SexpShape, future LSP completion
9838 /// label) needs it. A regression that drifts ONE consumer's pairing
9839 /// from the others cannot reach the substrate's runtime.
9840 #[must_use]
9841 pub fn kind(&self) -> AtomKind {
9842 match self {
9843 Self::Symbol(_) => AtomKind::Symbol,
9844 Self::Keyword(_) => AtomKind::Keyword,
9845 Self::Str(_) => AtomKind::Str,
9846 Self::Int(_) => AtomKind::Int,
9847 Self::Float(_) => AtomKind::Float,
9848 Self::Bool(_) => AtomKind::Bool,
9849 }
9850 }
9851
9852 /// Project the atomic payload to its canonical `&'static str`
9853 /// diagnostic label — `"symbol"` for [`Self::Symbol`], `"keyword"`
9854 /// for [`Self::Keyword`], `"string"` for [`Self::Str`], `"int"` for
9855 /// [`Self::Int`], `"float"` for [`Self::Float`], `"bool"` for
9856 /// [`Self::Bool`]. The outer-`Atom` peer on the [`Atom`] algebra of
9857 /// [`AtomKind::label`] (the marker-level label projection on the
9858 /// closed-set atomic-kind algebra) and [`crate::ast::Sexp::type_name`]
9859 /// (the outer-value label projection on the [`crate::ast::Sexp`]
9860 /// algebra composed through [`crate::ast::Sexp::shape`] +
9861 /// [`crate::error::SexpShape::label`]). Every label is byte-for-byte
9862 /// identical to the corresponding [`crate::error::SexpShape`] variant's
9863 /// label — the AtomKind ⊂ SexpShape label-vocabulary containment
9864 /// established by [`AtomKind::label`]'s composition through
9865 /// [`AtomKind::sexp_shape`] surfaces at the outer-`Atom` layer through
9866 /// this projection.
9867 ///
9868 /// Composition law: `atom.label() == atom.kind().label() ==
9869 /// atom.kind().sexp_shape().label()` for every `atom: &Atom`. The
9870 /// body composes [`Self::kind`] (the typed projection lifting each
9871 /// [`Atom`] variant into its peer [`AtomKind`] marker) with
9872 /// [`AtomKind::label`] (the canonical `&'static str` projection on the
9873 /// closed-set atomic-payload algebra), so the six atomic-arm labels
9874 /// live at ONE canonical site ([`crate::error::SexpShape::label`]'s
9875 /// atomic arms, via [`AtomKind::label`]'s composition through
9876 /// [`AtomKind::sexp_shape`]) rather than at TWO
9877 /// ([`crate::error::SexpShape::label`] AND a parallel six-arm match
9878 /// on the outer [`Atom`] algebra, pre-lift). Cross-algebra agreement
9879 /// law: `Sexp::Atom(atom.clone()).type_name() == atom.label()` for
9880 /// every `atom: Atom` — the outer-[`crate::ast::Sexp`] label
9881 /// projection at the atomic-payload arms routes through
9882 /// [`crate::ast::Sexp::shape`]'s
9883 /// `Self::Atom(a) => a.kind().sexp_shape()` arm which composes with
9884 /// [`crate::error::SexpShape::label`] byte-for-byte with this
9885 /// projection's `self.kind().label()` composition, so the (outer
9886 /// `Sexp` label, outer `Atom` label) agreement is a TYPED CONSEQUENCE
9887 /// of the two typed compositions rather than literal discipline at
9888 /// two sites.
9889 ///
9890 /// Sibling-shape lift to [`Self::kind`] (the closed-set atomic-kind
9891 /// projection): where `kind()` carries the typed [`AtomKind`] marker
9892 /// on the [`Atom`] algebra, `label()` carries the `&'static str`
9893 /// literal the rendered diagnostic surface wants (still derived from
9894 /// the typed marker, but flattened through [`AtomKind::label`] for
9895 /// substring-grep callers, future
9896 /// [`crate::error::LispError::TypeMismatch`] `got` slots keyed on an
9897 /// atomic witness before the outer [`crate::ast::Sexp`] wrap, and
9898 /// future LSP hover / REPL completion / audit-trail metric surfaces
9899 /// that hold an [`Atom`] value directly rather than a wrapped
9900 /// [`crate::ast::Sexp::Atom`]). The `&'static str` lifetime is
9901 /// load-bearing: the composition allocates nothing at runtime
9902 /// ([`Self::kind`] returns a `Copy` value and [`AtomKind::label`]
9903 /// yields `&'static str`).
9904 ///
9905 /// Pre-lift the (Atom variant, `&'static str` diagnostic label)
9906 /// pairing had no typed projection on the outer-[`Atom`] algebra —
9907 /// a consumer with a typed [`Atom`] in hand (a hand-authored
9908 /// [`Atom`] value at a test-harness diagnostic, a future
9909 /// [`crate::domain`] typed-kwarg gate that rejects on an atomic
9910 /// witness before the outer [`crate::ast::Sexp`] wrap, a future LSP
9911 /// hover surface that emits an atomic-payload identity without an
9912 /// enclosing [`crate::ast::Sexp::Atom`] wrap, a future audit-trail
9913 /// metric keyed on the observed atomic kind) wanting the canonical
9914 /// diagnostic label had to spell the two-step composition
9915 /// `atom.kind().label()` at every callsite, OR go through
9916 /// [`crate::ast::Sexp::Atom(atom.clone()).type_name()`] which wraps
9917 /// and unwraps for no runtime purpose. Post-lift the composition
9918 /// binds at ONE typed-algebra method on the outer [`Atom`] value-
9919 /// carrier — the SIXTH consumer of the outer-[`Atom`] projection
9920 /// surface (sibling of [`Self::kind`], [`Self::to_json`],
9921 /// `Atom::to_iac_forge_sexpr` (removed), [`Self::from_lexeme`], and the six
9922 /// per-variant soft-projection methods [`Self::as_symbol`] /
9923 /// [`Self::as_keyword`] / [`Self::as_string`] / [`Self::as_int`] /
9924 /// [`Self::as_float`] / [`Self::as_bool`] + the composite
9925 /// [`Self::as_symbol_or_string`]).
9926 ///
9927 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (Atom
9928 /// variant, `&'static str` diagnostic label) pairing becomes a TYPE
9929 /// projection on the outer-[`Atom`] algebra rather than a per-
9930 /// callsite `.kind().label()` two-step OR a wrap-through-Sexp
9931 /// [`crate::ast::Sexp::Atom(atom.clone()).type_name()`] round-trip.
9932 /// A typo or swap at the outer-`Atom` label site is no longer a
9933 /// runtime label drift but a compile error against the typed
9934 /// composition — the [`Atom`] ↔ [`AtomKind`] ↔ label chain is
9935 /// rustc-enforced end-to-end. THEORY.md §II.1 invariant 2 — free
9936 /// middle; the outer-[`Atom`] diagnostic-label projection now binds
9937 /// at ONE site on the outer-`Atom` algebra, composing through the
9938 /// pre-existing marker-level label projection ([`AtomKind::label`])
9939 /// rather than duplicating the six-arm match. THEORY.md §VI.1 —
9940 /// generation over composition; the outer-`Atom` label projection is
9941 /// the missing algebra layer between the outer [`Atom`] value-carrier
9942 /// and the pre-existing [`AtomKind`] marker-level label projection —
9943 /// the three pre-existing typed layers ([`Atom`] → [`AtomKind`] →
9944 /// [`crate::error::SexpShape`] → `&'static str`) become a full
9945 /// four-layer typed composition through ONE new named projection on
9946 /// the outer value-carrier.
9947 ///
9948 /// Frontier inspiration: MLIR's `mlir::Attribute::getAbstractAttribute()
9949 /// .getName()` typed projection composed with the attribute-kind's
9950 /// typed string identity — narrowing an attribute-carrier value
9951 /// through its typed kind identity yields the canonical diagnostic
9952 /// string identity in ONE typed composition on the outer attribute
9953 /// algebra. Translated through the substrate's outer-[`Atom`]
9954 /// value-carrier algebra, `atom.kind().label()` closes the (outer
9955 /// value, canonical diagnostic label) pairing at ONE typed projection
9956 /// on the value-carrier algebra composed through the marker-level
9957 /// diagnostic-label face. Racket's `(syntax-kind stx)` composed with
9958 /// `(kind-label kind)` on the datum-kind taxonomy — the typed
9959 /// diagnostic label emerges from a two-hop composition on the outer
9960 /// datum-carrier through the typed kind identity. `Atom::label` is
9961 /// the Rust-typed peer on the closed-set outer-[`Atom`] algebra with
9962 /// [`AtomKind`] standing in for Racket's datum-kind taxonomy.
9963 #[must_use]
9964 pub fn label(&self) -> &'static str {
9965 self.kind().label()
9966 }
9967
9968 /// Project the atomic value into its outer-shape [`SexpShape`]
9969 /// variant — `Symbol(_) → SexpShape::Symbol`,
9970 /// `Keyword(_) → SexpShape::Keyword`, `Str(_) → SexpShape::String`,
9971 /// `Int(_) → SexpShape::Int`, `Float(_) → SexpShape::Float`,
9972 /// `Bool(_) → SexpShape::Bool`. The outer-value peer of
9973 /// [`AtomKind::sexp_shape`] one algebra layer down and of
9974 /// [`Sexp::shape`] one algebra layer up. Body composes through
9975 /// `self.kind().sexp_shape()` — routing through [`Self::kind`]
9976 /// (the typed 6-arm outer-value → marker projection) then
9977 /// [`AtomKind::sexp_shape`] (the canonical 6-of-12 atomic-payload
9978 /// carving of [`SexpShape`]) so the (Atom variant, SexpShape
9979 /// variant) pairing lives at ONE canonical site
9980 /// ([`AtomKind::sexp_shape`]'s six match arms in `ast.rs`) rather
9981 /// than at six byte-identical inline arms across consumers.
9982 ///
9983 /// Same composition-through-carving-marker posture as [`Self::label`]
9984 /// (`self.kind().label()`) one vocabulary axis over on the
9985 /// outer-`Atom` algebra: [`Self::label`] closes the diagnostic-label
9986 /// axis, this method closes the outer-shape-projection axis, and
9987 /// both compose through the SAME typed marker layer
9988 /// ([`Self::kind`] into [`AtomKind`]) into the outer-shape's
9989 /// per-axis canonical site. The two methods now paint the
9990 /// outer-`Atom` value with typed projections onto BOTH the
9991 /// diagnostic-label vocabulary AND the outer-shape closed-set —
9992 /// the pair mirrors how [`Sexp::type_name`] and [`Sexp::shape`]
9993 /// paint the outer-`Sexp` value one algebra layer up.
9994 ///
9995 /// Composition law: `atom.sexp_shape() == atom.kind().sexp_shape()`
9996 /// for every `atom: &Atom`. Pinned by
9997 /// `atom_sexp_shape_composes_through_kind_sexp_shape_for_every_variant`
9998 /// across a representative payload sweep (including NaN via
9999 /// `f64::to_bits` round-trip on the Float arm, matching
10000 /// [`Hash for Atom`]'s posture; both empty and non-empty
10001 /// String/Symbol/Keyword arms; `i64::{MIN, MAX}` on the Int arm;
10002 /// both Bool arms). Sibling of
10003 /// `atom_label_composes_through_kind_label_for_every_variant` one
10004 /// vocabulary axis over.
10005 ///
10006 /// Cross-algebra agreement law: for every `atom: &Atom`,
10007 /// `atom.sexp_shape() == Sexp::Atom(atom.clone()).shape()`. The
10008 /// outer-`Atom` shape projection routes into the SAME canonical
10009 /// site the outer-`Sexp` [`Sexp::shape`] projection lands on for
10010 /// every atomic-payload arm — pinned by
10011 /// `atom_sexp_shape_agrees_with_sexp_shape_at_every_atom_arm` via
10012 /// byte-equality on the `SexpShape` variant across all six atomic
10013 /// arms. Sibling of
10014 /// `atom_label_agrees_with_sexp_type_name_at_every_atom_arm` one
10015 /// vocabulary axis over.
10016 ///
10017 /// Round-trip through the outer-shape's soft-projection sibling:
10018 /// `atom.sexp_shape().as_atom_kind() == Some(atom.kind())` for
10019 /// every `atom: &Atom` — the typed embed `Atom → AtomKind →
10020 /// SexpShape` inverts through the soft-projection retraction
10021 /// `SexpShape → AtomKind` exactly on the 6-of-12 atomic-payload
10022 /// image. Pinned by
10023 /// `atom_sexp_shape_round_trips_through_sexp_shape_as_atom_kind`.
10024 ///
10025 /// The `SexpShape` return type (owned; [`SexpShape`] is not `Copy`
10026 /// because its `Unknown(String)` arm carries a `String`) is the
10027 /// outer-shape closed set; consumers that want the diagnostic
10028 /// label render string compose `atom.sexp_shape().label()`, and
10029 /// that composition IS `atom.label()` byte-for-byte by the
10030 /// composition-through-carving-marker posture the two methods
10031 /// share.
10032 ///
10033 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (outer
10034 /// `Atom` variant, `SexpShape` variant) pairing becomes a TYPE
10035 /// projection on the outermost atomic value-carrier algebra
10036 /// composed through the pre-existing marker-level projection,
10037 /// rather than at parallel inline match arms each future consumer
10038 /// of the outer-shape from an outer-`Atom` value has to re-derive.
10039 /// THEORY.md §II.1 invariant 2 — free middle; the outer-`Atom`
10040 /// outer-shape algebra now closes over THREE typed layers (outer
10041 /// `Atom` → [`AtomKind`] → [`SexpShape`]) with rustc-enforced
10042 /// consistency across each — a regression that drifts ONE layer's
10043 /// shape mapping from the others cannot reach the substrate's
10044 /// runtime typed-witness surface, `LispError::TypeMismatch.got`
10045 /// slot, or [`SexpWitness::shape`] projection. THEORY.md §VI.1 —
10046 /// generation over composition; the outer-value projection is the
10047 /// missing algebra layer between the outer `Atom` and the
10048 /// pre-existing marker-level shape projection — the two
10049 /// pre-existing typed layers become a full THREE-layer typed
10050 /// composition through ONE new named projection.
10051 ///
10052 /// Frontier inspiration: MLIR's `mlir::Attribute::getType()`
10053 /// typed projection composed with the attribute-kind's typed
10054 /// outer-type identity — narrowing an attribute-carrier value
10055 /// through its typed kind identity yields the outer-type identity
10056 /// in ONE typed composition on the outer attribute algebra.
10057 /// Translated through the substrate's outer-`Atom` value-carrier
10058 /// algebra, `atom.kind().sexp_shape()` closes the (outer value,
10059 /// outer-shape) pairing at ONE typed projection on the value-
10060 /// carrier algebra composed through the marker-level
10061 /// outer-shape face.
10062 #[must_use]
10063 pub fn sexp_shape(&self) -> SexpShape {
10064 self.kind().sexp_shape()
10065 }
10066
10067 /// Stable, per-variant byte discriminator that paired with the
10068 /// recursive payload hash builds the substrate's [`Hash for Atom`]
10069 /// projection — `0u8` for [`Self::Symbol`], `1u8` for
10070 /// [`Self::Keyword`], `2u8` for [`Self::Str`], `3u8` for
10071 /// [`Self::Int`], `4u8` for [`Self::Float`], `5u8` for
10072 /// [`Self::Bool`]. The outer-value peer on the [`Atom`] algebra of
10073 /// [`AtomKind::hash_discriminator`] (the marker-level cache-key byte
10074 /// projection on the closed-set atomic-kind algebra), sibling of
10075 /// [`Self::label`] and [`Self::sexp_shape`] one vocabulary axis over
10076 /// on the outer-`Atom` algebra. Body composes through
10077 /// `self.kind().hash_discriminator()` — routing through [`Self::kind`]
10078 /// (the typed 6-arm outer-value → marker projection) then
10079 /// [`AtomKind::hash_discriminator`] (the canonical 6-arm cache-key
10080 /// byte projection) so the (Atom variant, byte) pairing lives at
10081 /// ONE canonical site ([`AtomKind::hash_discriminator`]'s six match
10082 /// arms) rather than at six inline `<N>u8.hash(h)` arms at
10083 /// [`Hash for Atom`]'s callsite.
10084 ///
10085 /// Composition law: `atom.hash_discriminator() ==
10086 /// atom.kind().hash_discriminator()` for every `atom: &Atom`. Pinned
10087 /// by `atom_hash_discriminator_composes_through_kind_hash_discriminator_for_every_variant`
10088 /// across a representative payload sweep (including NaN via
10089 /// `f64::to_bits` round-trip on the Float arm, matching
10090 /// [`Hash for Atom`]'s posture; both empty and non-empty
10091 /// String/Symbol/Keyword arms; `i64::{MIN, MAX}` on the Int arm;
10092 /// both Bool arms). Sibling of
10093 /// `atom_label_composes_through_kind_label_for_every_variant` and
10094 /// `atom_sexp_shape_composes_through_kind_sexp_shape_for_every_variant`
10095 /// one vocabulary axis over.
10096 ///
10097 /// Routing-identity law binding it to [`Hash for Atom`]'s post-lift
10098 /// body: for every `atom: &Atom`, hashing via the impl produces
10099 /// byte-identical output to a hand-driven
10100 /// `atom.hash_discriminator().hash(h); <inner-payload-hash>`
10101 /// sequence. Pinned by
10102 /// `hash_for_atom_routes_atom_discriminator_through_atom_hash_discriminator`.
10103 /// Sibling posture to
10104 /// `hash_for_sexp_routes_outer_discriminator_through_sexp_hash_discriminator`
10105 /// one algebra layer up — the two routing pins jointly enforce the
10106 /// outer-value Hash bodies at BOTH algebras stay structurally
10107 /// parallel (`self.hash_discriminator().hash(h); <inner>`).
10108 ///
10109 /// `pub(crate)` because the byte-discriminator surface is an
10110 /// implementation detail of the substrate's [`Hash for Atom`]
10111 /// cache-key contract; exposing it publicly would leak the cache-key
10112 /// shape through the API without enabling any external consumer the
10113 /// public projections ([`Self::kind`], [`Self::label`],
10114 /// [`Self::sexp_shape`]) don't already serve. Same posture as
10115 /// [`AtomKind::hash_discriminator`] one algebra layer down and
10116 /// [`crate::ast::Sexp::hash_discriminator`] one algebra layer up.
10117 ///
10118 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (outer
10119 /// `Atom` variant, cache-key byte) pairing becomes a TYPE projection
10120 /// on the outermost atomic value-carrier algebra composed through the
10121 /// pre-existing marker-level projection, rather than a two-hop
10122 /// composition at the [`Hash for Atom`] callsite. THEORY.md §II.1
10123 /// invariant 2 — free middle; the outer-`Atom` cache-key algebra now
10124 /// closes over THREE typed layers (outer `Atom` → [`AtomKind`] → byte)
10125 /// with rustc-enforced consistency across each — a regression that
10126 /// drifts the [`Hash for Atom`] callsite's byte routing from the
10127 /// canonical [`AtomKind::hash_discriminator`] site cannot reach the
10128 /// substrate's expansion-cache runtime. THEORY.md §VI.1 — generation
10129 /// over composition; the outer-value byte projection is the missing
10130 /// algebra layer between the outer `Atom` and the pre-existing
10131 /// marker-level byte projection — the two pre-existing typed layers
10132 /// become a full THREE-layer typed composition through ONE new named
10133 /// projection on the outer value-carrier, closing the (label,
10134 /// sexp_shape, hash_discriminator) trio on the outer-`Atom` algebra.
10135 ///
10136 /// Frontier inspiration: MLIR's
10137 /// `mlir::Attribute::getAsOpaquePointer()` typed projection composed
10138 /// with the attribute-kind's stable identifier — narrowing an
10139 /// attribute-carrier value through its typed kind identity yields the
10140 /// canonical cache-key identity in ONE typed composition on the outer
10141 /// attribute algebra. Translated through the substrate's
10142 /// outer-[`Atom`] value-carrier algebra,
10143 /// `atom.kind().hash_discriminator()` closes the (outer value, byte
10144 /// cache-key discriminator) pairing at ONE typed projection on the
10145 /// value-carrier algebra composed through the marker-level cache-key
10146 /// face. Racket's `(datum-hash-key datum)` composed with
10147 /// `(kind-hash-tag kind)` on the datum-kind taxonomy — the byte
10148 /// cache-key identity emerges from a two-hop composition on the
10149 /// outer datum-carrier through the typed kind identity;
10150 /// `Atom::hash_discriminator` is the Rust-typed peer on the
10151 /// closed-set outer-[`Atom`] algebra with [`AtomKind`] standing in
10152 /// for Racket's datum-kind taxonomy.
10153 #[must_use]
10154 pub(crate) fn hash_discriminator(&self) -> u8 {
10155 self.kind().hash_discriminator()
10156 }
10157
10158 /// Project the atomic payload to its canonical [`serde_json::Value`]
10159 /// rendering — the typed-algebra peer of [`fmt::Display for Atom`] at
10160 /// the JSON-projection boundary. Lifts six inline atom arms inside
10161 /// [`crate::domain::sexp_to_json`]'s outer match (one
10162 /// `Sexp::Atom(Atom::<variant>(payload)) => JValue::<…>(…)` arm
10163 /// per [`AtomKind`] variant) onto ONE typed-algebra method that
10164 /// every consumer routes through. Sibling-shape lift to the prior
10165 /// `Display for Atom` (the canonical-string rendering surface),
10166 /// `Hash for Atom` (the cache-key bytes surface via
10167 /// [`AtomKind::hash_discriminator`]), and the upcoming
10168 /// `Atom::to_iac_forge_sexpr` (the canonical-SExpr rendering
10169 /// surface, feature-gated `iac-forge`) — every per-`Atom`-variant
10170 /// projection now binds at ONE method on the closed-set algebra
10171 /// rather than at six inline arms inside its consumer.
10172 ///
10173 /// Mapping (preserves the byte-identical pre-lift behavior at the
10174 /// `sexp_to_json` callsite):
10175 /// * [`Self::Symbol`] payload `s` → [`serde_json::Value::String`] of
10176 /// `s` cloned (Symbols are enum discriminants — the JSON
10177 /// deserializer reads them as the string-form variant tag).
10178 /// * [`Self::Keyword`] payload `s` → [`serde_json::Value::String`]
10179 /// of `":{s}"` (Keywords prefix with `:` in their canonical
10180 /// wire-form; `json_to_sexp`'s inverse strips the prefix).
10181 /// * [`Self::Str`] payload `s` → [`serde_json::Value::String`] of
10182 /// `s` cloned.
10183 /// * [`Self::Int`] payload `n` → [`serde_json::Value::Number`] of
10184 /// `n` (lossless via `serde_json::Number::from(i64)`).
10185 /// * [`Self::Float`] payload `n` → [`serde_json::Value::Number`] of
10186 /// `n` IFF `n` is finite (NaN / ±∞ collapse to
10187 /// [`serde_json::Value::Null`]; this is JSON's structural
10188 /// inexpressibility of those f64 values, NOT a substrate
10189 /// choice). The NaN/∞→Null branch is pinned at one test below
10190 /// (`atom_to_json_float_nan_and_infinity_collapse_to_null`).
10191 /// * [`Self::Bool`] payload `b` → [`serde_json::Value::Bool`] of
10192 /// `b`.
10193 ///
10194 /// Bidirectional contract anchored by tests in this module:
10195 /// * `atom_to_json_projects_each_variant_to_canonical_json_value`
10196 /// — sweeps a representative atom of each [`AtomKind`] variant
10197 /// and pins each variant's canonical JValue mapping
10198 /// byte-for-byte against the pre-lift inline rule, so a future
10199 /// regression that drifts ONE arm (e.g. swaps `Symbol`'s
10200 /// mapping to a Number, or drops `Keyword`'s `:` prefix) fails
10201 /// loudly.
10202 /// * `atom_to_json_float_nan_and_infinity_collapse_to_null`
10203 /// — pins the JSON-structural inexpressibility branch at the
10204 /// atom layer directly, so a future Atom-Display-style refactor
10205 /// that bypasses [`serde_json::Number::from_f64`] (e.g. tries
10206 /// to emit `NaN` as the string `"NaN"`) surfaces at the
10207 /// typed-algebra boundary without requiring a Sexp wrap.
10208 /// * `sexp_to_json_atom_arms_route_through_atom_to_json` (in
10209 /// [`crate::domain::tests`]) — pins the lifted boundary:
10210 /// `sexp_to_json(&Sexp::Atom(a.clone())) == Ok(a.to_json())`
10211 /// for every atomic payload variant. Catches a future drift
10212 /// where one surface's per-variant body changes without the
10213 /// other.
10214 ///
10215 /// Theory anchor: THEORY.md §VI.1 — generation over composition;
10216 /// the (Atom variant, canonical JValue rendering) pair lived inline
10217 /// at the `sexp_to_json` site as six byte-identical arms. The lift
10218 /// retires the per-site fan-out onto ONE method on the `Atom`
10219 /// algebra. THEORY.md §II.1 invariant 2 — free middle; the typed-
10220 /// exit JSON projection, the Display-surface rendering, the
10221 /// diagnostic surface, and any future canonical-form surface
10222 /// (e.g. `Atom::to_iac_forge_sexpr`) all route through ONE
10223 /// per-variant projection family rather than per-callsite
10224 /// re-derivation. THEORY.md §V.1 — knowable platform; a future
10225 /// seventh atomic kind (e.g. `Char` for `#\x` reader syntax) lands
10226 /// at one [`AtomKind::ALL`] entry plus one arm here plus one arm
10227 /// per sibling projection — exhaustively checked by the compiler,
10228 /// not by per-consumer convention.
10229 ///
10230 /// Frontier inspiration: MLIR's `mlir::AsmPrinter::printAttribute`
10231 /// — the typed-IR attribute printer dispatches on the closed-set
10232 /// `AttributeKind` so every printer body for a kind lives at ONE
10233 /// implementation site; `Atom::to_json` is the unstructured-Rust
10234 /// peer on the `Atom` algebra for the JSON canonical-form surface
10235 /// (where `Display for Atom` is the Lisp-canonical-form peer and
10236 /// `From<&Sexp> for iac_forge::SExpr` is the canonical-attestation-
10237 /// form peer). Racket's `(syntax->datum stx)` then a serializer
10238 /// over the datum prim — `to_json` is the substrate's serializer
10239 /// at the atomic-payload layer, with the closed-set `AtomKind`
10240 /// standing in for Racket's datum-prim taxonomy.
10241 #[must_use]
10242 pub fn to_json(&self) -> serde_json::Value {
10243 match self {
10244 Self::Symbol(s) => serde_json::Value::String(s.clone()),
10245 // Keyword arm routes through the typed
10246 // [`Self::keyword_qualified`] projection on the atomic-
10247 // payload canonical-rendering axis — the ONE composition
10248 // of [`Self::KEYWORD_MARKER`] with a bare keyword name on
10249 // the [`Atom`] algebra, shared with
10250 // `Atom::to_iac_forge_sexpr` (removed)'s Keyword arm and pinned
10251 // byte-identical to [`fmt::Display for Atom`]'s Keyword
10252 // arm. Pre-lift each of the three sites carried its own
10253 // inline `format!("{}{s}", Self::KEYWORD_MARKER)`
10254 // composition; post-lift the composition lives at ONE
10255 // typed algebra projection.
10256 Self::Keyword(s) => serde_json::Value::String(Self::keyword_qualified(s)),
10257 Self::Str(s) => serde_json::Value::String(s.clone()),
10258 Self::Int(n) => serde_json::Value::Number((*n).into()),
10259 Self::Float(n) => serde_json::Number::from_f64(*n)
10260 .map(serde_json::Value::Number)
10261 .unwrap_or(serde_json::Value::Null),
10262 Self::Bool(b) => serde_json::Value::Bool(*b),
10263 }
10264 }
10265
10266 /// Inverse of [`Self::to_json`] restricted to the JSON `Number`
10267 /// discriminator — the ONE typed inverse projection on the closed-set
10268 /// [`Atom`] algebra that names the (`serde_json::Number` →
10269 /// [`Self::Int`] / [`Self::Float`]) bifurcation. Lifts the pre-lift
10270 /// inline three-arm cascade inside [`crate::ast::Sexp::from_json`]'s
10271 /// `serde_json::Value::Number(n)` outer-match arm — first
10272 /// `n.as_i64()?` sink to [`Self::Int`] then `n.as_f64()?` sink to
10273 /// [`Self::Float`] then a `Self::Int(0)` typed floor for the
10274 /// structural-impossibility residual — onto ONE typed projection on
10275 /// the [`Atom`] algebra so the paired FORWARD ([`Self::to_json`]'s
10276 /// [`Self::Int`] / [`Self::Float`] arms) and INVERSE (this method)
10277 /// numeric-axis projections live at ONE algebra layer.
10278 ///
10279 /// Mapping (byte-identical to the pre-lift cascade in
10280 /// [`crate::ast::Sexp::from_json`]):
10281 ///
10282 /// | `serde_json::Number` shape | result |
10283 /// | ---------------------------------- | ------------------ |
10284 /// | `n.as_i64() == Some(i)` | [`Self::Int`]`(i)` |
10285 /// | `n.as_i64() == None`, `.as_f64() == Some(f)` | [`Self::Float`]`(f)` |
10286 /// | `n.as_i64() == None`, `.as_f64() == None` | [`Self::Int`]`(0)` — typed floor |
10287 ///
10288 /// Every `serde_json::Number` today is either i64-fitting,
10289 /// u64-fitting (projected through f64), or f64-fitting — the
10290 /// `Int(0)` residual arm is a static-invariant statement that
10291 /// `serde_json::Number`'s closed-set discriminator excludes the
10292 /// "neither i64 nor f64" case in practice; the typed floor stays
10293 /// explicit so a future `serde_json` extension does NOT silently
10294 /// misroute through an unreachable-panic. The `Self::int(0)`
10295 /// composition in the pre-lift code equalled `Self::Atom(Atom::Int(0))`
10296 /// via the `Sexp::int` sugar; post-lift the [`Atom`] algebra owns
10297 /// the typed floor at the atomic layer directly.
10298 ///
10299 /// Round-trip laws (paired with [`Self::to_json`]'s numeric arms):
10300 ///
10301 /// * For every `i: i64`, `Atom::from_json_number(&i.into()) ==
10302 /// Atom::Int(i)` — the [`Self::Int`] → `JValue::Number` →
10303 /// [`Self::Int`] round-trip is byte-identical.
10304 /// * For every finite non-integer-valued `f: f64`,
10305 /// `Atom::from_json_number(&serde_json::Number::from_f64(f)
10306 /// .unwrap()) == Atom::Float(f)` — the [`Self::Float`] →
10307 /// `JValue::Number` → [`Self::Float`] round-trip is byte-identical
10308 /// for `f64` values that don't overlap the i64-fitting subset of
10309 /// [`serde_json::Number`]'s discriminator (i.e. non-integer-valued
10310 /// finite floats; integer-valued floats round-trip through the
10311 /// [`Self::Int`] arm by `as_i64`'s eager check).
10312 /// * Non-finite floats ([`f64::NAN`], [`f64::INFINITY`],
10313 /// [`f64::NEG_INFINITY`]) collapse to [`serde_json::Value::Null`]
10314 /// in [`Self::to_json`] — they NEVER produce a [`serde_json::Number`]
10315 /// value, so the round-trip law does not apply to them. This
10316 /// asymmetry is JSON's structural inexpressibility of non-finite
10317 /// floats (pinned at
10318 /// `atom_to_json_float_nan_and_infinity_collapse_to_null`), not a
10319 /// substrate choice.
10320 ///
10321 /// ONE consumer entrypoint the substrate binds against: the outer
10322 /// [`crate::ast::Sexp::from_json`]'s `serde_json::Value::Number(n)`
10323 /// arm was pre-lift a hand-rolled three-branch cascade
10324 /// (`if let Some(i) = n.as_i64() { Self::int(i) } else if let
10325 /// Some(f) = n.as_f64() { Self::float(f) } else { Self::int(0) }`);
10326 /// post-lift the outer arm collapses to
10327 /// `Self::Atom(Atom::from_json_number(n))` — the ONE typed inverse
10328 /// on the [`Atom`] algebra owns the numeric-axis bifurcation, the
10329 /// outer arm delegates. A regression that drifts the outer arm
10330 /// (e.g. re-inlines the bifurcation and swaps the `as_i64`/`as_f64`
10331 /// order so `42.0` sinks to [`Self::Float`] instead of
10332 /// [`Self::Int`]) becomes structurally unreachable — there is
10333 /// exactly ONE numeric decode both directions of the round-trip
10334 /// consume.
10335 ///
10336 /// Sibling-lift posture: this method mirrors [`Self::from_lexeme`]
10337 /// on the typed-entry classification axis — that method decodes a
10338 /// bare-atom lexeme (`&str`) into the six-way [`Atom`] taxonomy;
10339 /// THIS method decodes a JSON `Number` into the two-way (
10340 /// [`Self::Int`] / [`Self::Float`]) numeric subtaxonomy on the SAME
10341 /// algebra. Together with the seven typed-EXIT projections on
10342 /// [`Atom`] ([`fmt::Display for Atom`], [`Self::to_json`],
10343 /// `Atom::to_iac_forge_sexpr` (removed), [`Self::label`],
10344 /// [`Self::sexp_shape`], [`Self::hash_discriminator`],
10345 /// [`Self::bool_literal`]) and the two typed-ENTRY projections on
10346 /// [`Atom`] ([`Self::from_lexeme`], THIS method) the algebra's
10347 /// canonical-form bidirectional sweep is complete across every
10348 /// production-site rendering + parsing surface — every consumer's
10349 /// (`Atom` variant, canonical rendering) OR (canonical source,
10350 /// `Atom` variant) pairing binds at ONE method per direction per
10351 /// surface on the closed-set algebra rather than at inline arms
10352 /// scattered across per-consumer sites.
10353 ///
10354 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
10355 /// (JSON `Number` → typed [`Atom`] numeric variant) projection IS
10356 /// the typed-entry gate on the JSON numeric axis. Placing the
10357 /// paired forward (`Atom::to_json`'s [`Self::Int`] / [`Self::Float`]
10358 /// arms) AND inverse (THIS method) on the [`Atom`] algebra closes
10359 /// the round-trip closure at ONE algebra layer — future numeric
10360 /// taxonomy extensions (e.g. `u64`-fitting arm for the
10361 /// `serde-preserve-order` feature's `arbitrary_precision` mode, a
10362 /// [`Self::Bigint`] variant for arbitrary-precision integers, a
10363 /// [`Self::Rational`] variant for [`num_rational::Rational64`])
10364 /// extend the algebra ONCE at [`Self::to_json`]'s match AND ONCE at
10365 /// this method's cascade — both edits land on the SAME algebra
10366 /// rather than across the `Atom` module AND the `Sexp` module
10367 /// boundary. THEORY.md §V.1 — knowable platform; the numeric
10368 /// inverse projection becomes a NAMED primitive on the substrate's
10369 /// [`Atom`] algebra rather than an inline three-arm cascade at the
10370 /// `Sexp::from_json` consumer. THEORY.md §II.1 invariant 5 —
10371 /// composition preserves proofs; the round-trip law
10372 /// `Atom::from_json_number(&Atom::Int(n).to_json_as_number()) ==
10373 /// Atom::Int(n)` (pinned at
10374 /// `atom_from_json_number_round_trips_atom_to_json_int_arm`) is a
10375 /// coherence proof BETWEEN the paired projections on ONE algebra —
10376 /// a regression that drifts either side surfaces at the pin
10377 /// rather than as a silent Sexp ↔ JSON round-trip drift.
10378 ///
10379 /// Frontier inspiration: MLIR's `mlir::parseAttribute(str, ctx)` —
10380 /// the typed-IR parser inverse of `printAttribute` lives on the
10381 /// SAME `Attribute` algebra as its printer dual; the substrate's
10382 /// [`Self::from_json_number`] is the unstructured-Rust peer on the
10383 /// [`Atom`] algebra for the JSON-numeric canonical-form inverse,
10384 /// paired with [`Self::to_json`]'s numeric arms as the closed
10385 /// numeric-axis round-trip. Racket's
10386 /// `(json->racket (racket->json v))` numeric identity — the
10387 /// round-trip law that a JSON-projected numeric datum recovers to
10388 /// its source Racket numeric primitive; THIS method's round-trip
10389 /// pins are the Rust-typed peer on the [`Atom`] algebra with the
10390 /// closed-set numeric taxonomy ([`Self::Int`] / [`Self::Float`])
10391 /// standing in for Racket's numeric tower.
10392 #[must_use]
10393 pub fn from_json_number(n: &serde_json::Number) -> Self {
10394 if let Some(i) = n.as_i64() {
10395 Self::Int(i)
10396 } else if let Some(f) = n.as_f64() {
10397 Self::Float(f)
10398 } else {
10399 Self::Int(0)
10400 }
10401 }
10402
10403 // REMOVED (consolidation phase 2 step 8): `Atom::to_iac_forge_sexpr`
10404 // and the `crate::interop` module it mirrored. Both were gated on a
10405 // `iac-forge` Cargo feature this crate never declared, against an
10406 // `iac_forge` dependency it never had — unconditionally dead, and
10407 // uncompilable had the gate ever opened. Re-introducing the bridge
10408 // needs a crates.io-published `iac-forge` first; see
10409 // TATARA-LISP-CONSOLIDATION.md open question 7.
10410 //
10411 // NOT part of this deletion: the `iac_forge_tag` / `IAC_FORGE_TAGS`
10412 // / `from_iac_forge_tag` family on `QuoteForm`, `SexpShape`,
10413 // `UnquoteForm` and `Sexp`. Those are pure `&'static str` closed-set
10414 // projections with no dependency on the `iac-forge` crate; they stay
10415 // live and tested.
10416
10417 /// Classify a bare reader-token lexeme into its typed [`Atom`]
10418 /// variant — the typed-ENTRY mirror of the three typed-EXIT
10419 /// projections on the [`Atom`] algebra ([`fmt::Display for Atom`],
10420 /// [`Self::to_json`], `Atom::to_iac_forge_sexpr` (removed)). Lifts the
10421 /// five-statement classification cascade that lived inline at the
10422 /// reader's private `atom_from_str` helper onto ONE typed-algebra
10423 /// method on the closed-set [`Atom`] algebra; the reader's
10424 /// `Token::Atom(s)` arm collapses to `Sexp::Atom(Atom::from_lexeme(&s))`.
10425 /// Completes the bidirectional sweep across the four production-site
10426 /// per-`Atom`-variant projection shapes (typed-exit Display, JSON,
10427 /// iac-forge canonical attestation, AND now typed-entry
10428 /// classification) onto the algebra.
10429 ///
10430 /// Classification rule (byte-identical to the pre-lift reader
10431 /// `atom_from_str` cascade):
10432 /// 1. `"#t"`/`"#f"` → [`Self::Bool`] — the Scheme bool spellings;
10433 /// bare `true`/`false` re-read as [`Self::Symbol`] (the
10434 /// CLAUDE.md "Lisp bools" warning — every `:values-overlay`
10435 /// payload depends on this for `Value::Bool` round-trip).
10436 /// 2. `:foo` (leading `:`) → [`Self::Keyword`] — strips the `:`
10437 /// so the inverse [`fmt::Display`] rule (`Keyword(s) →
10438 /// ":{s}"`) round-trips.
10439 /// 3. `i64::from_str` succeeds → [`Self::Int`] — load-bearing
10440 /// ORDERING: tried BEFORE `f64` so `"1"` classifies as
10441 /// [`Self::Int`]`(1)`, NOT [`Self::Float`]`(1.0)`. Typed-int-
10442 /// vs-typed-float distinction at the Display→read boundary
10443 /// is the dual of `fmt_float`'s `.0`-suffix discipline.
10444 /// 4. `f64::from_str` succeeds → [`Self::Float`].
10445 /// 5. Default → [`Self::Symbol`].
10446 ///
10447 /// Composition laws (pinned by tests below):
10448 /// * `Atom::from_lexeme(&a.to_string()) == a` for every variant
10449 /// EXCEPT [`Self::Str`] (Display renders Str with quote marks
10450 /// — strings take the reader's `"`-quoted tokenizer branch,
10451 /// NOT the bare-atom branch).
10452 /// * `read(s)` for every canonical bare-atom source lexeme
10453 /// equals `vec![Sexp::Atom(Atom::from_lexeme(s))]` (pinned by
10454 /// `reader_atom_token_arm_routes_through_atom_from_lexeme_for_
10455 /// every_kind` in [`crate::reader::tests`]).
10456 ///
10457 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry;
10458 /// `atom_from_str` was the typed-entry gate as a free function in
10459 /// `reader.rs`, outside the typed `Atom` algebra. Naming it on the
10460 /// algebra brings the typed-entry side INTO the same closed-set
10461 /// match family the typed-exit projections live on, so a future
10462 /// seventh atomic kind (e.g. `Char` for `#\x` reader syntax) lands
10463 /// at ONE [`AtomKind::ALL`] entry plus ONE arm here plus ONE arm
10464 /// per typed-exit projection — exhaustively checked by rustc
10465 /// across all FOUR per-variant projection families. THEORY.md
10466 /// §II.1 invariant 2 — free middle; FOUR consumers (typed-entry
10467 /// classification, Display rendering, JSON projection, canonical-
10468 /// attestation-form projection) now route through ONE
10469 /// per-`Atom`-variant projection family on the closed-set algebra.
10470 /// THEORY.md §VI.1 — generation over composition; this lift
10471 /// completes the bidirectional sweep across the four production
10472 /// surfaces the prior runs in this series named.
10473 ///
10474 /// Frontier inspiration: Racket's `(read-syntax …)` dispatches a
10475 /// bare-atom lexeme through a closed-set classifier keyed on
10476 /// prefix + parse-as-numeric cascade; `Atom::from_lexeme` is the
10477 /// substrate's typed-Rust peer, with [`AtomKind`] standing in for
10478 /// Racket's datum-prim taxonomy. MLIR's
10479 /// `mlir::AsmParser::parseAttribute` dispatches on the closed-set
10480 /// `AttributeKind` so every parser body for a kind lives at ONE
10481 /// implementation site; `Atom::from_lexeme` is the
10482 /// unstructured-Rust peer on the [`Atom`] algebra for the
10483 /// typed-entry classification surface.
10484 #[must_use]
10485 pub fn from_lexeme(s: &str) -> Self {
10486 if s == Self::bool_literal(true) {
10487 return Self::Bool(true);
10488 }
10489 if s == Self::bool_literal(false) {
10490 return Self::Bool(false);
10491 }
10492 if let Some(rest) = s.strip_prefix(Self::KEYWORD_MARKER) {
10493 return Self::Keyword(rest.to_owned());
10494 }
10495 if let Ok(n) = s.parse::<i64>() {
10496 return Self::Int(n);
10497 }
10498 if let Ok(n) = s.parse::<f64>() {
10499 return Self::Float(n);
10500 }
10501 Self::Symbol(s.to_owned())
10502 }
10503
10504 /// Soft projection onto the [`Self::Symbol`] payload — `Some(&str)`
10505 /// iff this is a [`Self::Symbol`] variant, `None` for every other
10506 /// atomic kind (`Keyword`, `Str`, `Int`, `Float`, `Bool`).
10507 ///
10508 /// FIRST of the six per-variant soft-projection methods on the typed
10509 /// [`Atom`] algebra — the typed-EXIT *soft*-projection peer of the
10510 /// typed-EXIT canonical-form projections ([`fmt::Display for Atom`],
10511 /// [`Self::to_json`], `Atom::to_iac_forge_sexpr` (removed)) and the typed-ENTRY
10512 /// classifier ([`Self::from_lexeme`]). Where the canonical-form trio
10513 /// projects the atomic payload to a *rendered* canonical surface
10514 /// (string / JSON / iac-forge SExpr) and the classifier projects a
10515 /// lexeme to the typed `Atom`, this method projects the typed `Atom`
10516 /// to its inner payload — the soft-decomposition face of the closed
10517 /// set, completing the algebra surface across BOTH bidirectional axes
10518 /// (canonical-form rendering + classification on the typed-ENTRY/
10519 /// typed-EXIT axis; soft decomposition on the typed-EXIT side at the
10520 /// payload axis).
10521 ///
10522 /// Sibling soft-projection peer of [`Sexp::as_quote_form`]: where
10523 /// `as_quote_form` soft-decomposes the four homoiconic prefix
10524 /// wrappers into `Option<(QuoteForm, &Sexp)>`, this method (and its
10525 /// five `as_*` siblings on [`Atom`]) soft-decompose the six atomic
10526 /// payloads into `Option<&str>` / `Option<i64>` / `Option<f64>` /
10527 /// `Option<bool>` — there is no inner-sexp body to surface, so the
10528 /// projection's return type is just the payload. The
10529 /// `Sexp::as_symbol` consumer at the `Sexp` algebra layer composes
10530 /// this projection with [`Sexp::as_atom`] (the structural lift to
10531 /// the inner [`Atom`]) — `Sexp::as_symbol(self) ==
10532 /// self.as_atom().and_then(Atom::as_symbol)` — so the per-`Atom`-
10533 /// variant soft-projection binds at ONE method on the typed algebra
10534 /// rather than at six inline `Self::Atom(Atom::X(s)) => Some(s)` arms
10535 /// inside the `Sexp` consumer.
10536 ///
10537 /// Lifts the inline `Self::Atom(Atom::Symbol(s)) => Some(s)` arm at
10538 /// [`Sexp::as_symbol`]'s match body onto ONE typed-algebra projection
10539 /// the `Sexp` consumer routes through via the structural lift
10540 /// [`Sexp::as_atom`]. Sibling-shape lift to the typed-EXIT
10541 /// canonical-form projections (`Display for Atom`, `Atom::to_json`,
10542 /// `Atom::to_iac_forge_sexpr`) and the typed-ENTRY classifier
10543 /// (`Atom::from_lexeme`) — every per-`Atom`-variant projection
10544 /// across both the rendering surfaces AND the soft-decomposition
10545 /// surface now binds at ONE method on the closed-set algebra rather
10546 /// than at inline arms inside its consumer.
10547 ///
10548 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
10549 /// (Atom variant, downstream-consumer-payload) pairing now binds at
10550 /// ONE typed projection per consumer surface (six canonical-form
10551 /// surfaces — `Display`, JSON, iac-forge, plus the soft-projection
10552 /// FAMILY this method opens), regardless of which consumer reaches
10553 /// in. THEORY.md §VI.1 — generation over composition; the six inline
10554 /// `Self::Atom(Atom::X(s)) => Some(_)` arms at `Sexp::as_X` sites
10555 /// (well past the ≥2 PRIME-DIRECTIVE trigger once the structural
10556 /// shape is named) collapse onto the closed-set `Atom` algebra so a
10557 /// future seventh atomic kind (e.g. `Char` for `#\x` reader syntax,
10558 /// `Bigint` for arbitrary-precision integers) extends `Atom::ALL` +
10559 /// the per-variant soft-projection method ONCE and rustc enforces
10560 /// matching across every consumer through the closed-set match.
10561 /// THEORY.md §V.1 — knowable platform; the (Atom variant, payload)
10562 /// pairing becomes a TYPE projection on the substrate algebra
10563 /// rather than six inline arms at the `Sexp` consumer. A typo or
10564 /// swap at the soft-projection site is no longer a runtime drift
10565 /// but a compile error against the typed projection.
10566 ///
10567 /// Frontier inspiration: Racket's `(symbol? v)` / `(symbol->string
10568 /// v)` pair — the typed-predicate + typed-projection pair at the
10569 /// atomic-payload layer; this method (and its five `as_*` siblings)
10570 /// is the substrate's typed soft-projection peer on the closed-set
10571 /// `Atom` algebra, with `Option<&str>` standing in for the
10572 /// predicate-AND-projection pair Racket carries as two functions.
10573 /// MLIR's `mlir::dyn_cast<SymbolAttribute>(attr)` — the typed-IR
10574 /// soft-downcast onto a closed-set attribute family; `Atom::as_symbol`
10575 /// is the unstructured-Rust peer on the `Atom` algebra for the
10576 /// soft-projection face, with the closed-set `AtomKind` standing in
10577 /// for MLIR's `AttributeKind` taxonomy.
10578 #[must_use]
10579 pub fn as_symbol(&self) -> Option<&str> {
10580 match self {
10581 Self::Symbol(s) => Some(s),
10582 _ => None,
10583 }
10584 }
10585
10586 /// Soft projection onto the [`Self::Keyword`] payload — `Some(&str)`
10587 /// iff this is a [`Self::Keyword`] variant, `None` for every other
10588 /// atomic kind. The returned `&str` is the payload AFTER the `:`
10589 /// prefix has been stripped at the typed-ENTRY classifier
10590 /// boundary ([`Self::from_lexeme`] strips `:` when constructing a
10591 /// `Keyword`; this projection surfaces the bare identifier).
10592 /// SECOND of the six per-variant soft-projection methods on the
10593 /// typed [`Atom`] algebra — see [`Self::as_symbol`] for the
10594 /// algebra-level docstring.
10595 #[must_use]
10596 pub fn as_keyword(&self) -> Option<&str> {
10597 match self {
10598 Self::Keyword(s) => Some(s),
10599 _ => None,
10600 }
10601 }
10602
10603 /// Soft projection onto the [`Self::Str`] payload — `Some(&str)` iff
10604 /// this is a [`Self::Str`] variant (the typed `"…"`-quoted string
10605 /// literal payload at the reader's [`crate::reader::Token::Str`]
10606 /// branch), `None` for every other atomic kind. THIRD of the six
10607 /// per-variant soft-projection methods — named `as_string` at the
10608 /// `Sexp` consumer for consumer-vocabulary continuity with the
10609 /// pre-lift `Sexp::as_string` projection (the typed payload variant
10610 /// is `Str` for `String` shortening; the consumer-facing method
10611 /// keeps `string` for symmetry with the `ExpectedKwargShape::String`
10612 /// label and the [`SexpShape::String`] outer-shape marker).
10613 #[must_use]
10614 pub fn as_string(&self) -> Option<&str> {
10615 match self {
10616 Self::Str(s) => Some(s),
10617 _ => None,
10618 }
10619 }
10620
10621 /// Soft projection onto the [`Self::Int`] payload — `Some(i64)` iff
10622 /// this is a [`Self::Int`] variant, `None` for every other atomic
10623 /// kind. FOURTH of the six per-variant soft-projection methods.
10624 /// The `i64` is returned by value (the payload is `Copy`); contrast
10625 /// with [`Self::as_symbol`] / [`Self::as_keyword`] / [`Self::as_string`]
10626 /// which borrow the underlying `String` payload as `&str` because
10627 /// `String` is not `Copy`.
10628 ///
10629 /// Strict typed identity: this method projects `Atom::Int(n)` to
10630 /// `Some(n)` only. The `Sexp::as_float` consumer at the `Sexp`
10631 /// algebra layer widens `Int` to `Float` (`Atom::Int(n) → Some(n as
10632 /// f64)`) for caller convenience at the numeric-kwarg boundary; the
10633 /// `Atom`-level projection here stays strict so the typed-identity
10634 /// distinction `Int(1)` vs `Float(1.0)` (the load-bearing typed
10635 /// identity at the [`Self::from_lexeme`] ⇄ Display round-trip
10636 /// boundary, dual of [`fmt_float`]'s `.0`-suffix discipline) is
10637 /// preserved at the algebra layer. The widening lives at the
10638 /// `Sexp::as_float` consumer (`a.as_float().or_else(|| a.as_int()
10639 /// .map(|n| n as f64))`) where the convenience is wanted, not at
10640 /// the algebra-level projection where the typed identity is
10641 /// load-bearing.
10642 #[must_use]
10643 pub fn as_int(&self) -> Option<i64> {
10644 match self {
10645 Self::Int(n) => Some(*n),
10646 _ => None,
10647 }
10648 }
10649
10650 /// Soft projection onto the [`Self::Float`] payload — `Some(f64)`
10651 /// iff this is a [`Self::Float`] variant, `None` for every other
10652 /// atomic kind. FIFTH of the six per-variant soft-projection
10653 /// methods.
10654 ///
10655 /// Strict typed identity: `Atom::Int(n)` does NOT project through
10656 /// this method (it stays `None`). The [`Sexp::as_float`] consumer
10657 /// widens `Int` to `Float` at the `Sexp` algebra layer for caller
10658 /// convenience; this algebra-level projection stays strict. See
10659 /// [`Self::as_int`]'s docstring for the typed-identity contract.
10660 #[must_use]
10661 pub fn as_float(&self) -> Option<f64> {
10662 match self {
10663 Self::Float(n) => Some(*n),
10664 _ => None,
10665 }
10666 }
10667
10668 /// Soft projection onto the [`Self::Bool`] payload — `Some(bool)`
10669 /// iff this is a [`Self::Bool`] variant, `None` for every other
10670 /// atomic kind. SIXTH and LAST of the six per-variant soft-projection
10671 /// methods on the typed [`Atom`] algebra; together with the five
10672 /// siblings ([`Self::as_symbol`], [`Self::as_keyword`],
10673 /// [`Self::as_string`], [`Self::as_int`], [`Self::as_float`]) the
10674 /// per-`Atom`-variant soft-projection family is complete across all
10675 /// six closed-set arms. The CLAUDE.md-pinned `"#t"` / `"#f"` Scheme
10676 /// bool spellings the reader's typed-ENTRY classifier
10677 /// [`Self::from_lexeme`] dispatches on bind the lexeme → typed
10678 /// [`Self::Bool`] direction; this method binds the typed
10679 /// [`Self::Bool`] → payload direction at the soft-decomposition
10680 /// face.
10681 #[must_use]
10682 pub fn as_bool(&self) -> Option<bool> {
10683 match self {
10684 Self::Bool(b) => Some(*b),
10685 _ => None,
10686 }
10687 }
10688
10689 /// Soft projection onto the *symbol-or-string* union — `Some(&str)` iff
10690 /// this is a [`Self::Symbol`] variant OR a [`Self::Str`] variant, `None`
10691 /// for every other atomic kind (`Keyword`, `Int`, `Float`, `Bool`).
10692 /// The atomic-payload peer of [`Sexp::as_symbol_or_string`] —
10693 /// disjunctive composition of [`Self::as_symbol`] + [`Self::as_string`]
10694 /// at the typed [`Atom`] algebra rather than at the [`Sexp`] consumer
10695 /// layer where the union previously composed two distinct
10696 /// [`Sexp::as_atom`] traversals.
10697 ///
10698 /// Sibling soft-projection peer of the six per-variant projections
10699 /// ([`Self::as_symbol`], [`Self::as_keyword`], [`Self::as_string`],
10700 /// [`Self::as_int`], [`Self::as_float`], [`Self::as_bool`]) — this
10701 /// union projection completes the soft-decomposition family on the
10702 /// closed-set [`Atom`] algebra by naming the (Symbol ⊎ Str) union
10703 /// the substrate's named-form NAME gate ([`crate::compile::split_name_slot`]
10704 /// via [`Sexp::as_symbol_or_string`]) keys on. Both NAME-author
10705 /// surfaces (`(defcompiler my-name …)` — bare symbol; `(defcompiler
10706 /// "my-name" …)` — quoted string) project to `Some("my-name")`
10707 /// through one method on the algebra.
10708 ///
10709 /// Composition law binding it to [`Sexp::as_symbol_or_string`]: for
10710 /// every [`Sexp`] `s`,
10711 /// `s.as_symbol_or_string() == s.as_atom().and_then(Atom::as_symbol_or_string)`
10712 /// — the same structural-lift composition pattern [`Sexp::as_symbol`]
10713 /// / [`Sexp::as_keyword`] / [`Sexp::as_string`] / [`Sexp::as_int`] /
10714 /// [`Sexp::as_bool`] route through on the six per-variant axis.
10715 /// Lifts the `self.as_symbol().or_else(|| self.as_string())`
10716 /// disjunctive composition at [`Sexp::as_symbol_or_string`]'s body
10717 /// (TWO `Sexp::as_atom` traversals pre-lift) onto ONE typed-algebra
10718 /// projection the `Sexp` consumer routes through via the structural
10719 /// lift [`Sexp::as_atom`] (ONE `Sexp::as_atom` traversal post-lift).
10720 ///
10721 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
10722 /// (Symbol ⊎ Str) union projection now binds at ONE method on the
10723 /// closed-set [`Atom`] algebra regardless of which consumer reaches
10724 /// in. THEORY.md §VI.1 — generation over composition; the
10725 /// disjunctive `as_symbol().or_else(|| as_string())` composition at
10726 /// [`Sexp::as_symbol_or_string`]'s body collapses onto a SINGLE
10727 /// structural lift through [`Sexp::as_atom`] + the algebra-level
10728 /// union projection, eliminating the double-traversal redundancy
10729 /// the pre-lift consumer-layer composition carried. THEORY.md §V.1
10730 /// — knowable platform; the (Symbol-or-Str) NAME-slot union becomes
10731 /// a TYPE projection on the substrate algebra rather than a
10732 /// disjunctive composition at every NAME-gate consumer.
10733 ///
10734 /// Frontier inspiration: Racket's `(or/c symbol? string?)`
10735 /// contract — a typed disjunctive predicate the consumer binds to
10736 /// in one place rather than re-deriving the disjunction at every
10737 /// callsite; [`Self::as_symbol_or_string`] is the substrate's
10738 /// unstructured-Rust peer with the typed projection (`Option<&str>`)
10739 /// surfacing the underlying payload alongside the predicate face.
10740 /// MLIR's `mlir::dyn_cast<StringLike>(attr)` — typed soft-downcast
10741 /// onto a closed-set attribute union; [`Self::as_symbol_or_string`]
10742 /// is the substrate's [`Atom`]-algebra peer for the
10743 /// (Symbol ⊎ Str) union, with `Option<&str>` standing in for MLIR's
10744 /// typed downcast result.
10745 #[must_use]
10746 pub fn as_symbol_or_string(&self) -> Option<&str> {
10747 self.as_symbol().or_else(|| self.as_string())
10748 }
10749}
10750
10751/// Closed-set typed discriminator for the six [`Atom`] payload variants —
10752/// `Symbol(String)`, `Keyword(String)`, `Str(String)`, `Int(i64)`,
10753/// `Float(f64)`, `Bool(bool)` — paired with the projections every
10754/// per-atom-kind consumer keys on ([`Self::hash_discriminator`] for
10755/// [`Hash for Atom`]'s cache-key bytes, [`Self::sexp_shape`] for
10756/// [`crate::domain::sexp_shape`]'s atom-arm collapse, [`Self::label`]
10757/// for the operator-facing diagnostic vocabulary, [`Self::FromStr`]
10758/// for the typed-inverse decode that lets LSP / REPL / metric-aggregator
10759/// consumers round-trip a rendered diagnostic label back into the typed
10760/// discriminator).
10761///
10762/// Atomic-payload peer of [`QuoteForm`] (the four homoiconic prefix
10763/// wrappers — `Sexp::{Quote, Quasiquote, Unquote, UnquoteSplice}`):
10764/// where `QuoteForm` carves the closed set on `Sexp`'s wrapper-variant
10765/// axis, `AtomKind` carves the closed set on `Sexp`'s atomic-payload
10766/// axis. Together the two closed-set discriminators cover every reachable
10767/// `Sexp` outermost shape except `Nil` and `List` (the structural
10768/// constructors `()` and `(…)`) — every other shape is either an
10769/// `Atom(_)` projecting through this enum's [`Self::sexp_shape`] arm or a
10770/// quote-family wrapper projecting through [`QuoteForm::sexp_shape`].
10771/// After this lift the two enums' [`Self::sexp_shape`] arms own ALL TEN
10772/// of [`SexpShape`]'s twelve canonical labels through ONE typed
10773/// composition each rather than through per-callsite arm-pairing in
10774/// [`crate::domain::sexp_shape`].
10775///
10776/// Mirror at the atomic-payload boundary of the prior-run [`QuoteForm`]
10777/// (homoiconic-prefix-wrapper closed set, 4 variants), the cross-crate
10778/// `tatara-process` closed-set family
10779/// (`ConditionKind::ALL`, `ProcessPhase::ALL`, `ProcessSignal::ALL`,
10780/// `ChannelKind::ALL`, `IntentKind::ALL`, `LifetimeKind::ALL`,
10781/// `RequestorKind::ALL`, `ReceiptKind::ALL`, …) and this crate's own
10782/// [`SexpShape`] (the twelve reachable Sexp outermost shapes — the
10783/// SUPERSET this enum projects into via [`Self::sexp_shape`]) and
10784/// [`UnquoteForm`] (the two template-substitution markers) closed-set
10785/// lifts: those enums key their respective rejection or projection
10786/// variants on a typed identity carried inside the variant's data shape;
10787/// this enum keys the SIX [`Atom`] payload variants on a typed
10788/// discriminator identity threaded through ALL THREE per-atom-kind
10789/// dispatch sites ([`Hash for Atom`]'s six byte literals,
10790/// [`crate::domain::sexp_shape`]'s six atom arms, AND the
10791/// diagnostic-label vocabulary [`SexpShape::label`] publishes for the
10792/// atom subset). Adding a hypothetical seventh atomic kind (e.g. a
10793/// `Char` literal for `#\x` reader syntax, a `Bigint` for arbitrary-
10794/// precision integers, a `Symbol2` for namespaced symbols) requires
10795/// extending this enum, which rustc-enforces matching at every
10796/// projection site ([`Self::label`], [`Self::hash_discriminator`],
10797/// [`Self::sexp_shape`], [`Atom::kind`], the [`Hash for Atom`] inner
10798/// match, and the [`Self::FromStr`] sweep keyed on [`Self::ALL`]) — the
10799/// closed set becomes a TYPE rather than six `&'static str` / `u8`
10800/// / `SexpShape` literals that could drift independently across the
10801/// substrate's three per-atom-kind consumer surfaces.
10802///
10803/// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
10804/// atomic-payload discriminator at a typed-entry rejection IS part of
10805/// the proof of WHAT the gate observed, and naming its closed-set
10806/// identity lifts the discriminator from per-site literal-pair
10807/// discipline (a byte at the Hash site, a SexpShape variant at the
10808/// `sexp_shape` site, a `&'static str` at any future LSP completion
10809/// site) to ONE typed enum the substrate's diagnostic + cache-key
10810/// surfaces both bind against. THEORY.md §II.1 invariant 2 — free
10811/// middle; THREE consumers ([`Hash for Atom`],
10812/// [`crate::domain::sexp_shape`], and the future diagnostic /
10813/// completion surface) route through ONE typed closed-set match
10814/// family, so a regression that drifts ONE consumer's pairing from the
10815/// others cannot reach the substrate's runtime. THEORY.md §V.1 —
10816/// knowable platform; the closed set of atomic payload kinds becomes a
10817/// TYPE rather than six byte literals (Hash) + six SexpShape literals
10818/// (`sexp_shape`) scattered across distinct files — a typo in any one
10819/// site is no longer a runtime drift but a compile error against the
10820/// typed projection. THEORY.md §VI.1 — generation over composition;
10821/// the (Atom variant, label, discriminator-byte, SexpShape variant)
10822/// quadruple appeared inline at THREE sites (`Hash for Atom`'s six
10823/// byte arms, `domain::sexp_shape`'s six atom arms, plus implicit
10824/// pairing across `SexpShape::label`'s six atom-subset arms) — well
10825/// past the ≥2 PRIME-DIRECTIVE trigger once the structural shape is
10826/// named.
10827#[derive(Debug, Clone, Copy, PartialEq, Eq, tatara_closed_set::DeriveClosedSet)]
10828#[closed_set(via = "label", display, generate_unknown = "atom kind")]
10829pub enum AtomKind {
10830 /// `Atom::Symbol(_)` — `"symbol"` diagnostic label, byte `0u8`
10831 /// hash discriminator, projects to [`SexpShape::Symbol`].
10832 Symbol,
10833 /// `Atom::Keyword(_)` — `"keyword"` diagnostic label, byte `1u8`
10834 /// hash discriminator, projects to [`SexpShape::Keyword`].
10835 Keyword,
10836 /// `Atom::Str(_)` — `"string"` diagnostic label, byte `2u8` hash
10837 /// discriminator, projects to [`SexpShape::String`].
10838 Str,
10839 /// `Atom::Int(_)` — `"int"` diagnostic label, byte `3u8` hash
10840 /// discriminator, projects to [`SexpShape::Int`].
10841 Int,
10842 /// `Atom::Float(_)` — `"float"` diagnostic label, byte `4u8` hash
10843 /// discriminator, projects to [`SexpShape::Float`].
10844 Float,
10845 /// `Atom::Bool(_)` — `"bool"` diagnostic label, byte `5u8` hash
10846 /// discriminator, projects to [`SexpShape::Bool`].
10847 Bool,
10848}
10849
10850impl AtomKind {
10851 /// The closed set of six atomic [`Atom`] payload kinds — single
10852 /// source of truth that drives every per-kind projection
10853 /// ([`Self::label`] / [`fmt::Display`], [`Self::hash_discriminator`],
10854 /// [`Self::sexp_shape`], and the [`Self::FromStr`] decode sweep
10855 /// keyed on [`Self::label`]).
10856 ///
10857 /// Adding a hypothetical seventh atomic kind (e.g. `Char` for
10858 /// `#\x` reader syntax, `Bigint` for arbitrary-precision
10859 /// integers) lands at one [`Self::ALL`] entry plus one arm per
10860 /// projection — exhaustively checked by the compiler (the
10861 /// `[Self; 6]` array literal forces the arity) AND by the
10862 /// per-variant truth-table tests below.
10863 ///
10864 /// Sibling closed-set lift to every other typed-shape enum the
10865 /// substrate carries: this crate's own [`SexpShape::ALL`] (the
10866 /// twelve reachable outer shapes — superset of this kind's six),
10867 /// [`QuoteForm`] (the four homoiconic prefix wrappers — peer
10868 /// projection on the SAME `Sexp` algebra), [`UnquoteForm`] (the
10869 /// two template-substitution markers — proper subset of
10870 /// `QuoteForm`), and the cross-crate `tatara-process` family
10871 /// (`ConditionKind::ALL`, `ProcessPhase::ALL`,
10872 /// `ProcessSignal::ALL`, `ChannelKind::ALL`, `IntentKind::ALL`,
10873 /// …) every one of which paired its typed projection with `ALL`
10874 /// before this lift.
10875 ///
10876 /// Future consumers that compose against `ALL`: LSP / REPL
10877 /// completion for the operator-facing rendered atom-kind label
10878 /// (every `expected X, got Y` substring in `LispError`'s rendered
10879 /// diagnostics for an atomic witness keys on this set's projection
10880 /// through [`Self::label`]); `tatara-check` coverage assertions
10881 /// over which atomic kinds reach a `TypeMismatch.got` arm at all
10882 /// — the typed sweep replaces a per-callsite vocabulary of six
10883 /// `&'static str` literals; any future audit-trail metric jointly
10884 /// labeled by [`Self::label`] (e.g.
10885 /// `tatara_lisp_atom_type_mismatch_total{got="symbol"}`) — the
10886 /// metric label set IS [`Self::ALL`] mapped through
10887 /// [`Self::label`]; any future structural rewriter (typed
10888 /// analogue of MLIR's `op.walk<AtomKind::Symbol>()`) that wants
10889 /// to sweep over every atomic kind in a typed sequence.
10890 pub const ALL: [Self; 6] = [
10891 Self::Symbol,
10892 Self::Keyword,
10893 Self::Str,
10894 Self::Int,
10895 Self::Float,
10896 Self::Bool,
10897 ];
10898
10899 /// Canonical `&'static str` bytes for the [`Self::Symbol`] atomic-
10900 /// payload marker — aliases [`SexpShape::SYMBOL_LABEL`] on the
10901 /// AtomKind ⊂ SexpShape carving so the marker-level per-role bytes
10902 /// bind at ONE `pub const` on the parent superset's atomic arm
10903 /// rather than at TWO sites (the per-role `pub const` AND a
10904 /// parallel inline literal). Per-role peer of `Self::Symbol` on the
10905 /// closed-set atomic algebra; consumers reach for
10906 /// `AtomKind::SYMBOL_LABEL` when the caller has a variant in hand
10907 /// at compile time and wants the canonical diagnostic bytes without
10908 /// runtime dispatch through [`Self::label`].
10909 pub const SYMBOL_LABEL: &'static str = SexpShape::SYMBOL_LABEL;
10910
10911 /// Canonical `&'static str` bytes for the [`Self::Keyword`] atomic-
10912 /// payload marker — aliases [`SexpShape::KEYWORD_LABEL`] on the
10913 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Keyword`.
10914 pub const KEYWORD_LABEL: &'static str = SexpShape::KEYWORD_LABEL;
10915
10916 /// Canonical `&'static str` bytes for the [`Self::Str`] atomic-
10917 /// payload marker — aliases [`SexpShape::STRING_LABEL`] on the
10918 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Str`; the
10919 /// `Str → "string"` wire-shape rename matches
10920 /// [`SexpShape::String`]'s label projection so the AtomKind marker
10921 /// and its SexpShape peer emit byte-identical diagnostic bytes.
10922 pub const STRING_LABEL: &'static str = SexpShape::STRING_LABEL;
10923
10924 /// Canonical `&'static str` bytes for the [`Self::Int`] atomic-
10925 /// payload marker — aliases [`SexpShape::INT_LABEL`] on the
10926 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Int`.
10927 pub const INT_LABEL: &'static str = SexpShape::INT_LABEL;
10928
10929 /// Canonical `&'static str` bytes for the [`Self::Float`] atomic-
10930 /// payload marker — aliases [`SexpShape::FLOAT_LABEL`] on the
10931 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Float`.
10932 pub const FLOAT_LABEL: &'static str = SexpShape::FLOAT_LABEL;
10933
10934 /// Canonical `&'static str` bytes for the [`Self::Bool`] atomic-
10935 /// payload marker — aliases [`SexpShape::BOOL_LABEL`] on the
10936 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Bool`.
10937 pub const BOOL_LABEL: &'static str = SexpShape::BOOL_LABEL;
10938
10939 /// Closed-set forced-arity ALL array over the canonical atomic-
10940 /// payload marker `&'static str` bytes, in declaration order
10941 /// matching [`Self::ALL`] element-wise (pinned by
10942 /// `atom_kind_labels_align_with_all_by_index`). Sibling posture to
10943 /// [`SexpShape::LABELS`] (`[&'static str; 12]` — the superset
10944 /// carving this AtomKind subset embeds into),
10945 /// [`crate::error::ExpectedKwargShape::LABELS`] (`[&'static str; 7]`),
10946 /// [`crate::error::KwargPathKind::LABELS`] (`[&'static str; 3]`),
10947 /// [`crate::error::MacroDefHead::KEYWORDS`] (`[&'static str; 3]`),
10948 /// [`Atom::BOOL_LITERALS`] (`[&'static str; 2]`), and
10949 /// [`QuoteForm::PREFIXES`] (`[&'static str; 4]`) — every closed-set
10950 /// outer projection on the substrate that carries an `&'static str`-
10951 /// per-variant label now pins its per-role canonical bytes at ONE
10952 /// `pub const` per role PLUS an ALL array for family-wide consumers.
10953 ///
10954 /// Pre-lift the six atomic-payload marker bytes had NO per-role
10955 /// primitive on this closed-set algebra — a consumer with an
10956 /// `AtomKind` variant in hand at compile time reaching for the
10957 /// canonical diagnostic bytes had to spell
10958 /// `AtomKind::Symbol.label()` (runtime dispatch through the
10959 /// composition [`Self::sexp_shape`] + [`SexpShape::label`]) OR
10960 /// reach across the algebra boundary into
10961 /// [`SexpShape::SYMBOL_LABEL`] and re-derive the AtomKind ⊂
10962 /// SexpShape variant pairing at the call site. Post-lift the SIX
10963 /// canonical bytes bind at ONE `pub const` per role on the typed
10964 /// [`AtomKind`] algebra AND at [`Self::LABELS`] as a family-wide
10965 /// forced-arity array — a future LSP / REPL completion bar keyed on
10966 /// `AtomKind::LABELS`, a `tatara-check` coverage sweep over the
10967 /// atomic-payload arms of a `TypeMismatch.got` corpus, or a Sekiban
10968 /// audit-trail metric jointly labeled by the atomic marker
10969 /// (`tatara_lisp_atom_type_mismatch_total{kind="symbol"}`) reads
10970 /// through the typed constants on this subset algebra without
10971 /// re-deriving the 6-of-12 carving inline.
10972 ///
10973 /// Each entry is byte-for-byte identical to the corresponding
10974 /// [`SexpShape`] atomic arm — an intentional cross-axis overlap
10975 /// pinned by
10976 /// `atom_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
10977 /// so a future label rename on EITHER side (a SexpShape `"string"`
10978 /// → `"str"` drift, or an AtomKind rename that skips the alias)
10979 /// fails-loudly at the alias test rather than as a silent
10980 /// operator-facing vocabulary fracture. Adding a hypothetical
10981 /// seventh atomic kind (e.g. `Char` for `#\x` reader syntax,
10982 /// `Bigint` for arbitrary-precision integers) extends [`Self::ALL`]
10983 /// AND [`Self::LABELS`] AND adds ONE per-role `pub const` alias in
10984 /// lockstep — rustc's forced-arity check on the two `[_; N]` arrays
10985 /// fails compilation if EITHER ALL array grows without the other.
10986 ///
10987 /// Theory anchor: THEORY.md §III — the typescape; the six canonical
10988 /// atomic-payload marker bytes bind at ONE typed
10989 /// `[&'static str; 6]` array on the closed-set AtomKind algebra
10990 /// rather than at zero-primitive-on-this-subset-plus-six-inline-
10991 /// lookups scattered across the substrate. THEORY.md §V.1 —
10992 /// knowable platform; the family's cardinality becomes a TYPE-level
10993 /// constant on the substrate algebra rather than a per-consumer
10994 /// runtime dispatch through the composition. THEORY.md §VI.1 —
10995 /// generation over composition; the family-wide contract sweeps
10996 /// (alignment with `ALL`, pairwise disjointness, membership through
10997 /// [`Self::label`]) emerge from the composition of TWO substrate
10998 /// primitives (this `pub const` array + the six per-role
10999 /// `pub const *_LABEL` aliases) rather than as per-variant inline
11000 /// assertions duplicated at each call site.
11001 pub const LABELS: [&'static str; 6] = [
11002 Self::SYMBOL_LABEL,
11003 Self::KEYWORD_LABEL,
11004 Self::STRING_LABEL,
11005 Self::INT_LABEL,
11006 Self::FLOAT_LABEL,
11007 Self::BOOL_LABEL,
11008 ];
11009
11010 /// Canonical `u8` cache-key byte for [`Self::Symbol`]'s
11011 /// [`Self::hash_discriminator`] arm — `0`. Per-role peer of
11012 /// [`Self::Symbol`] on the closed-set atomic-payload cache-key-byte
11013 /// axis; consumers reach for `AtomKind::SYMBOL_HASH_DISCRIMINATOR`
11014 /// when the caller has a variant in hand at compile time and wants
11015 /// the canonical byte without runtime dispatch through
11016 /// [`Self::hash_discriminator`]. The byte is load-bearing because
11017 /// the macro-expansion cache ([`crate::macro_expand::Expander`]'s
11018 /// cache) keys on [`Hash for Atom`], and any renumbering silently
11019 /// invalidates every cached expansion — post-lift the six canonical
11020 /// bytes bind at ONE `pub(crate) const` per role rather than at
11021 /// six inline `u8` literals scattered across
11022 /// [`Self::hash_discriminator`]'s match arms.
11023 ///
11024 /// Sibling posture to [`crate::error::QuoteForm::QUOTE_HASH_DISCRIMINATOR`]
11025 /// on the quote-family sub-carving — both close their respective
11026 /// closed-set cache-key algebras at ONE per-role constant per
11027 /// variant PLUS a family-wide [`Self::HASH_DISCRIMINATORS`] array.
11028 /// The two families partition their respective cache-key spaces
11029 /// independently: `AtomKind` at `{0..=5}` NESTED inside
11030 /// [`crate::ast::Sexp::Atom`]'s outer `1u8` byte (`Hash for Atom`
11031 /// runs on the [`Atom`] type, not [`Sexp`]), `QuoteForm` at
11032 /// `{3..=6}` at the outer [`Sexp`] cache-key space itself.
11033 ///
11034 /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
11035 /// preserves proofs; the alias-chain composition law
11036 /// `AtomKind::HASH_DISCRIMINATORS[i] ==
11037 /// AtomKind::ALL[i].hash_discriminator()` binds the family-wide
11038 /// array to the projection method at rustc time, pinned by byte
11039 /// equality. THEORY.md §III — the typescape; the six canonical
11040 /// cache-key bytes bind at ONE `pub(crate) const` per role on the
11041 /// typed algebra rather than as inline `u8` literals in the
11042 /// `hash_discriminator` match arms.
11043 pub(crate) const SYMBOL_HASH_DISCRIMINATOR: u8 = 0;
11044
11045 /// Canonical `u8` cache-key byte for [`Self::Keyword`]'s
11046 /// [`Self::hash_discriminator`] arm — `1`. Sibling of
11047 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11048 /// atomic-payload cache-key-byte axis; see
11049 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] for the algebra-level
11050 /// round-trip + disjointness contracts every sibling shares.
11051 pub(crate) const KEYWORD_HASH_DISCRIMINATOR: u8 = 1;
11052
11053 /// Canonical `u8` cache-key byte for [`Self::Str`]'s
11054 /// [`Self::hash_discriminator`] arm — `2`. Sibling of
11055 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11056 /// atomic-payload cache-key-byte axis.
11057 pub(crate) const STR_HASH_DISCRIMINATOR: u8 = 2;
11058
11059 /// Canonical `u8` cache-key byte for [`Self::Int`]'s
11060 /// [`Self::hash_discriminator`] arm — `3`. Sibling of
11061 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11062 /// atomic-payload cache-key-byte axis.
11063 pub(crate) const INT_HASH_DISCRIMINATOR: u8 = 3;
11064
11065 /// Canonical `u8` cache-key byte for [`Self::Float`]'s
11066 /// [`Self::hash_discriminator`] arm — `4`. Sibling of
11067 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11068 /// atomic-payload cache-key-byte axis.
11069 pub(crate) const FLOAT_HASH_DISCRIMINATOR: u8 = 4;
11070
11071 /// Canonical `u8` cache-key byte for [`Self::Bool`]'s
11072 /// [`Self::hash_discriminator`] arm — `5`. Sibling of
11073 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11074 /// atomic-payload cache-key-byte axis. The HIGHEST byte on the
11075 /// closed set — a future seventh atomic kind (e.g. `Char` for
11076 /// `#\x` reader syntax, `Bigint` for arbitrary-precision integers)
11077 /// would extend the partition to `{0..=6}` and land the new
11078 /// discriminator at `6u8`.
11079 pub(crate) const BOOL_HASH_DISCRIMINATOR: u8 = 5;
11080
11081 /// Closed-set forced-arity ALL array over the canonical atomic-
11082 /// payload cache-key `u8` bytes, in declaration order matching
11083 /// [`Self::ALL`] element-wise (pinned by
11084 /// `atom_kind_hash_discriminators_align_with_all_by_index`).
11085 /// Sibling posture to [`Self::LABELS`] (`[&'static str; 6]` — the
11086 /// diagnostic-label `&'static str` axis on the SAME closed set) and
11087 /// to [`crate::error::QuoteForm::HASH_DISCRIMINATORS`] (`[u8; 4]` —
11088 /// the quote-family sub-carving's cache-key-byte peer). Every
11089 /// closed-set outer projection on the substrate's [`AtomKind`]
11090 /// algebra that carries a `u8` per-variant discriminator now pins
11091 /// its per-role canonical bytes at ONE `pub(crate) const` per role
11092 /// PLUS an ALL array for family-wide consumers.
11093 ///
11094 /// Pre-lift the six cache-key bytes had NO per-role primitive on
11095 /// this closed-set algebra — a consumer with an [`AtomKind`]
11096 /// variant in hand at compile time reaching for the canonical byte
11097 /// had to spell `AtomKind::Str.hash_discriminator()` (runtime
11098 /// dispatch through the match arm) OR reach across into the inline
11099 /// `2u8` at the pre-lift match arm's [`Self::Str`] branch and
11100 /// re-derive the (variant, byte) pairing at the call site.
11101 /// Post-lift the SIX canonical bytes bind at ONE `pub(crate) const`
11102 /// per role on the typed [`AtomKind`] algebra AND at
11103 /// [`Self::HASH_DISCRIMINATORS`] as a family-wide forced-arity
11104 /// array — a future substrate-facing cache-key introspection tool
11105 /// (a `tatara-check` predicate that asserts every atomic arm's
11106 /// discriminator injective on the nested [`Atom`] axis, a Sekiban
11107 /// audit-trail metric jointly labeled by the atomic cache-key
11108 /// partition, a future `TypedRewriter<AtomKindOp>` sweep zipping
11109 /// ALL / LABELS / HASH_DISCRIMINATORS in lockstep for a family-wide
11110 /// (variant, label, byte) triple render) reads through the typed
11111 /// constants without re-deriving the six-arm carving inline.
11112 ///
11113 /// Each entry is byte-for-byte identical to the pre-lift inline
11114 /// `u8` literal at the corresponding [`Self::hash_discriminator`]
11115 /// arm — pinned by
11116 /// `atom_kind_hash_discriminators_pin_legacy_cache_key_bytes` so
11117 /// a regression that drifts ONE `pub(crate) const` from its pre-
11118 /// lift byte silently invalidates every cached expansion of an
11119 /// [`Atom`] participating in [`crate::macro_expand::Expander::cache`],
11120 /// fails-loudly at the alias test rather than at a silent cache
11121 /// mis-hash. Adding a hypothetical seventh atomic kind (e.g.
11122 /// `Char` for `#\x` reader syntax, `Bigint` for arbitrary-
11123 /// precision integers) extends [`Self::ALL`] AND
11124 /// [`Self::HASH_DISCRIMINATORS`] AND adds ONE per-role
11125 /// `pub(crate) const` in lockstep — rustc's forced-arity check on
11126 /// the two `[_; N]` arrays fails compilation if EITHER array grows
11127 /// without the other, closing the extensibility gap that pre-lift
11128 /// silently allowed a discriminator collision on `6u8` (the next
11129 /// free byte on the nested [`Atom`] cache-key space).
11130 ///
11131 /// Theory anchor: THEORY.md §III — the typescape; the six
11132 /// canonical cache-key bytes bind at ONE typed `[u8; 6]` array on
11133 /// the closed-set [`AtomKind`] algebra rather than at zero-
11134 /// primitive-plus-six-inline-`u8`-literals scattered across the
11135 /// [`Self::hash_discriminator`] match arms. THEORY.md §V.1 —
11136 /// knowable platform; the family's cardinality becomes a TYPE-
11137 /// level constant on the substrate algebra rather than a per-
11138 /// consumer runtime dispatch through the match table. THEORY.md
11139 /// §V.3 — three-pillar attestation; the cache-key partition is
11140 /// the substrate's nested [`Atom`] `intent_hash` composition axis
11141 /// for every atomic arm — binding the six bytes on the typed
11142 /// algebra makes attestation-key drift a compile error rather
11143 /// than a silent BLAKE3 mis-hash. THEORY.md §VI.1 — generation
11144 /// over composition; the family-wide contract sweeps (alignment
11145 /// with `ALL`, pairwise disjointness, membership through
11146 /// [`Self::hash_discriminator`]) emerge from the composition of
11147 /// TWO substrate primitives (this `pub(crate) const` array + the
11148 /// six per-role `pub(crate) const *_HASH_DISCRIMINATOR` aliases)
11149 /// rather than as per-variant inline assertions duplicated at each
11150 /// call site.
11151 ///
11152 /// The `#[allow(dead_code)]` posture matches
11153 /// [`crate::error::QuoteForm::HASH_DISCRIMINATORS`]: the substrate's
11154 /// current [`Hash for Atom`] body composes through the per-variant
11155 /// [`Self::hash_discriminator`] projection arm-by-arm rather than
11156 /// sweeping the family-wide array, so no non-test caller currently
11157 /// reaches this ALL array directly. The lift lands the substrate
11158 /// primitive so future consumers keyed on the whole family (a
11159 /// future [`crate::macro_expand::Expander`] cache-warmup pass that
11160 /// hashes the atomic byte-set upfront, a future `tatara-check`
11161 /// predicate `(check-atom-cache-key-partition-injective …)` that
11162 /// verifies the `{0..=5}` partition structurally, a future
11163 /// `TypedRewriter<AtomKindOp>` sweep zipping ALL / LABELS /
11164 /// HASH_DISCRIMINATORS in lockstep for a family-wide (variant,
11165 /// label, byte) triple render) bind to ONE `[u8; 6]` primitive
11166 /// rather than re-deriving the array inline per callsite.
11167 #[allow(dead_code)]
11168 pub(crate) const HASH_DISCRIMINATORS: [u8; 6] = [
11169 Self::SYMBOL_HASH_DISCRIMINATOR,
11170 Self::KEYWORD_HASH_DISCRIMINATOR,
11171 Self::STR_HASH_DISCRIMINATOR,
11172 Self::INT_HASH_DISCRIMINATOR,
11173 Self::FLOAT_HASH_DISCRIMINATOR,
11174 Self::BOOL_HASH_DISCRIMINATOR,
11175 ];
11176
11177 /// Canonical `u8` OUTER-`Sexp` cache-key byte at which ALL SIX
11178 /// atomic-payload shapes collapse when hashed at the outer
11179 /// [`Hash for Sexp`](crate::ast::Sexp) level — `1`. The
11180 /// outer-carve peer of [`Self::HASH_DISCRIMINATORS`] (the six
11181 /// nested INNER cache-key bytes `{0..=5}` that specialise INSIDE
11182 /// [`Hash for Atom`] after the outer marker byte is emitted).
11183 /// The lift moves the byte the outer-Sexp cache-key algebra uses
11184 /// to distinguish [`crate::ast::Sexp::Atom(_)`] from every other
11185 /// outer-Sexp variant off inline `1u8` literals scattered across
11186 /// [`crate::error::SexpShape::hash_discriminator`]'s six-arm
11187 /// atomic collapse + the two structural-carve joint-partition
11188 /// disjointness pins and onto ONE `pub(crate) const` on the
11189 /// [`AtomKind`] algebra it names.
11190 ///
11191 /// Where the byte appears at the outer-Sexp cache-key algebra:
11192 /// [`crate::error::SexpShape::hash_discriminator`]'s atomic-arm
11193 /// collapse `Self::Symbol | Self::Keyword | Self::String |
11194 /// Self::Int | Self::Float | Self::Bool => 1` binds directly to
11195 /// this constant; every one of the six atomic shapes routes
11196 /// through the shape-level projection into the outer-Sexp cache
11197 /// key at THIS byte. The nested inner
11198 /// [`Self::HASH_DISCRIMINATORS`] `{0..=5}` bytes then specialise
11199 /// the atomic payload INSIDE [`Hash for Atom`] via a second
11200 /// discriminator emission (`self.hash_discriminator().hash(h)`
11201 /// on the [`Atom`] value carrier), so the two byte spaces live
11202 /// at different hash-sequence positions and do not collide.
11203 ///
11204 /// Sibling posture to [`crate::error::StructuralKind::HASH_DISCRIMINATORS`]
11205 /// (`[u8; 2]` at `{0, 2}` on the outer-Sexp cache-key space) and
11206 /// to [`crate::error::QuoteForm::HASH_DISCRIMINATORS`] (`[u8; 4]`
11207 /// at `{3, 4, 5, 6}` on the same space) — together with THIS
11208 /// scalar the three sibling carvings' byte spaces jointly
11209 /// partition the outer-Sexp discriminator space `{0..=6}`
11210 /// injectively. Post-lift the outer-Sexp cache-key algebra
11211 /// closes over FOUR typed byte primitives:
11212 /// * [`Self::OUTER_HASH_DISCRIMINATOR`] (this constant) —
11213 /// scalar `1u8` for the atomic-payload outer-carve;
11214 /// * [`crate::error::StructuralKind::HASH_DISCRIMINATORS`] —
11215 /// `{0, 2}` for the structural-residual carve;
11216 /// * [`crate::error::QuoteForm::HASH_DISCRIMINATORS`] —
11217 /// `{3..=6}` for the quote-family carve;
11218 /// * [`Self::HASH_DISCRIMINATORS`] — the nested inner
11219 /// `{0..=5}` byte-set INSIDE [`Hash for Atom`], NOT on the
11220 /// outer-Sexp space.
11221 ///
11222 /// The scalar shape (single `u8`, NOT an array) is intrinsic to
11223 /// the carve: all six atomic-payload arms of
11224 /// [`crate::error::SexpShape`] collapse to the SAME outer byte
11225 /// (the outer-Sexp distinguisher is variant-level: `Sexp::Atom(_)`
11226 /// vs the six sibling `Sexp` variants); per-atom-kind
11227 /// specialisation lives at the nested inner
11228 /// [`Self::HASH_DISCRIMINATORS`] carve inside [`Hash for Atom`].
11229 /// The other two carvings' `HASH_DISCRIMINATORS` are arrays
11230 /// because their shape-level arms each carry a DISTINCT outer
11231 /// byte; the atomic carve is a scalar because its arms carry the
11232 /// SAME outer byte.
11233 ///
11234 /// `pub(crate)` because the byte is an implementation detail of
11235 /// the substrate's `Hash for Sexp` cache-key contract; exposing
11236 /// it publicly would leak the cache-key shape through the API
11237 /// without enabling any external consumer the public projections
11238 /// ([`Self::label`], [`Self::sexp_shape`]) don't already serve.
11239 /// Same posture as [`Self::HASH_DISCRIMINATORS`] +
11240 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] and the sibling carvings'
11241 /// per-role `pub(crate) const` peers.
11242 ///
11243 /// Pre-lift the outer-Atom marker byte lived at THREE sites: the
11244 /// inline `1u8` literal at
11245 /// [`crate::error::SexpShape::hash_discriminator`]'s six-arm
11246 /// atomic collapse; the inline `1u8` literal at
11247 /// `sexp_shape_hash_discriminator_atomic_arms_collapse_to_outer_atom_marker`'s
11248 /// assertion body; the inline `1u8` literal at
11249 /// `sexp_shape_hash_discriminator_partitions_by_three_way_carving_disjointly`'s
11250 /// `expected_atomic` fixture; PLUS a duplicated local `const
11251 /// ATOM_OUTER_CARVE_BYTE: u8 = 1` inside
11252 /// `structural_kind_hash_discriminator_disjoint_from_atom_outer_carve_byte_and_quote_form_hash_discriminator_partition`.
11253 /// The (byte, algebra) pairing had no typed home — a consumer
11254 /// with a typed [`AtomKind`] identity in hand reaching for the
11255 /// outer-Sexp cache-key byte the atomic arm collapses to had to
11256 /// re-derive the byte from the shape-level projection method's
11257 /// atomic collapse arm inline, OR re-derive the pre-lift local
11258 /// `ATOM_OUTER_CARVE_BYTE` fixture at every joint-partition-check
11259 /// site. Post-lift the byte binds at ONE `pub(crate) const` on
11260 /// the closed-set [`AtomKind`] algebra it names; every downstream
11261 /// consumer (the shape-level projection, the joint-partition
11262 /// disjointness pins, the three-way carving image pin, a future
11263 /// `tatara-check` predicate that verifies the outer-Sexp cache-
11264 /// key partition structurally, a future
11265 /// [`crate::macro_expand::Expander`] cache-warmup pass that
11266 /// hashes the outer-Sexp byte-set upfront) picks up the same
11267 /// canonical byte from ONE source of truth.
11268 ///
11269 /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
11270 /// preserves proofs; the (AtomKind ⊂ SexpShape carve, outer-Sexp
11271 /// cache-key byte) pairing binds at rustc time by byte equality
11272 /// against the shape-level projection's atomic collapse arm.
11273 /// THEORY.md §III — the typescape; the outer-Atom cache-key byte
11274 /// binds at ONE `pub(crate) const` on the typed algebra rather
11275 /// than as inline `1u8` literals at every joint-partition + shape-
11276 /// level-collapse site. THEORY.md §V.1 — knowable platform; the
11277 /// outer-Sexp cache-key space's four-way partition (this scalar
11278 /// PLUS the three sibling carvings' arrays) becomes a TYPE-level
11279 /// constant on the substrate algebra rather than a per-callsite
11280 /// hand-rolled `{0, 1, 2, 3, 4, 5, 6}` re-enumeration. THEORY.md
11281 /// §V.3 — three-pillar attestation; the outer-Sexp cache-key
11282 /// partition is the substrate's outer [`Sexp`] `intent_hash`
11283 /// composition axis — binding the four-way partition's atomic-
11284 /// carve byte on the typed algebra makes attestation-key drift a
11285 /// compile error rather than a silent BLAKE3 mis-hash. A future
11286 /// eighth [`Sexp`] variant (e.g. `Vector` for `#(...)` reader
11287 /// syntax, `Map` for `{...}`, `Char` for `#\x`) picks a fresh
11288 /// cache-key byte outside `{0..=6}` (e.g. `7u8`), extends the
11289 /// closed-set [`crate::error::SexpShape`] enum + its shape-level
11290 /// `hash_discriminator` and either an existing carving OR a fresh
11291 /// sub-algebra — the outer-Atom scalar itself stays untouched
11292 /// unless the new variant is also an atomic-payload arm.
11293 pub(crate) const OUTER_HASH_DISCRIMINATOR: u8 = 1;
11294
11295 /// Project the typed marker to the canonical `&'static str`
11296 /// diagnostic label — `"symbol"` for [`Self::Symbol`],
11297 /// `"keyword"` for [`Self::Keyword`], `"string"` for [`Self::Str`]
11298 /// (the wire-shape rename `Str → "string"` matches the
11299 /// [`SexpShape::String`] label projection), `"int"` for
11300 /// [`Self::Int`], `"float"` for [`Self::Float`], `"bool"` for
11301 /// [`Self::Bool`]. Each label is byte-for-byte identical to the
11302 /// corresponding [`SexpShape`] variant's label — and post-lift this
11303 /// agreement is STRUCTURAL rather than two literal-discipline sites
11304 /// pinned by a cross-projection test.
11305 ///
11306 /// Composition law: `AtomKind::label(k) ==
11307 /// AtomKind::sexp_shape(k).label()` for every `k: AtomKind`. The
11308 /// body composes [`Self::sexp_shape`] (the typed projection lifting
11309 /// each AtomKind variant into its peer [`SexpShape`] variant) with
11310 /// [`SexpShape::label`] (the canonical `&'static str` projection on
11311 /// the supeset's twelve-variant closed set), so the six atomic-arm
11312 /// labels live at ONE canonical site ([`SexpShape::label`]) rather
11313 /// than at TWO ([`SexpShape::label`] AND a parallel six-arm match
11314 /// here, pre-lift). Pre-lift the substrate-wide AtomKind ⊂ SexpShape
11315 /// label-vocabulary agreement was enforced by literal discipline at
11316 /// the two sites + a cross-projection test
11317 /// (`atom_kind_label_agrees_with_sexp_shape_label_for_every_atom_arm`);
11318 /// post-lift the agreement is a TYPED CONSEQUENCE of the composition
11319 /// — a typo in `SexpShape::label`'s atomic arms is a typo in BOTH
11320 /// projections, and the cross-projection test is true by
11321 /// construction. Same lift posture as the prior-run
11322 /// `Atom::as_X → Atom::as_X` algebra-lift commit (6935416), the
11323 /// `from_lexeme` reader-atom lift commit (9b95e64), and the
11324 /// `to_iac_forge_sexpr` Atom-arm lift commit (418be51): the typed
11325 /// projection sits on the value, and the consumer composes through
11326 /// the existing structural pairing rather than re-deriving the
11327 /// per-variant literal.
11328 ///
11329 /// The `&'static str` lifetime is load-bearing: it lets the
11330 /// variant project through this method without an allocation,
11331 /// parallel to how [`SexpShape::label`], [`QuoteForm::prefix`],
11332 /// [`QuoteForm::iac_forge_tag`], [`UnquoteForm::marker`], and
11333 /// [`crate::error::ExpectedKwargShape::label`] project their
11334 /// respective closed-set surfaces. The composition preserves the
11335 /// no-allocation contract: [`Self::sexp_shape`] returns a `Copy`
11336 /// value and [`SexpShape::label`] yields `&'static str`, so the
11337 /// `&'static str` projection through the composition allocates
11338 /// nothing at runtime.
11339 ///
11340 /// The bidirectional contract is anchored by tests:
11341 /// `atom_kind_label_renders_canonical_string_for_every_variant`
11342 /// pins each variant's canonical literal so a typo in
11343 /// [`SexpShape::label`]'s atomic arms fails-loudly through this
11344 /// projection too, `atom_kind_display_matches_label_for_every_variant`
11345 /// pins Display-equals-label so any future
11346 /// `#[error("... got {got}")]` annotation that threads through
11347 /// this projection projects byte-for-byte, and
11348 /// `atom_kind_label_round_trips_through_from_str` pins the
11349 /// `label` ↔ [`Self::FromStr`] round-trip for every variant in
11350 /// [`Self::ALL`] so the typed surface and the rendered diagnostic
11351 /// literal cannot drift. The post-lift composition contract is
11352 /// pinned by
11353 /// `atom_kind_label_routes_through_sexp_shape_label_via_sexp_shape_projection`
11354 /// — a regression that re-inlines the six atomic-arm literals here
11355 /// and silently drifts ONE arm from the [`SexpShape::label`] axis
11356 /// fails the routing pin loudly without needing a per-variant
11357 /// cross-axis literal sweep.
11358 ///
11359 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
11360 /// AtomKind ⊂ SexpShape label-vocabulary containment becomes a
11361 /// TYPED CONSEQUENCE of the [`Self::sexp_shape`] + [`SexpShape::label`]
11362 /// composition rather than literal discipline at two sites. THEORY.md
11363 /// §VI.1 — generation over composition; the six atomic-arm labels
11364 /// live at ONE canonical site ([`SexpShape::label`]) and this method
11365 /// generates its identity through the typed-projection composition.
11366 /// THEORY.md §II.1 invariant 2 — free middle; FOUR consumers of the
11367 /// [`AtomKind`] algebra ([`Hash for Atom`] via
11368 /// [`Self::hash_discriminator`], [`crate::domain::sexp_shape`] via
11369 /// [`Self::sexp_shape`], the diagnostic-rendering surface via this
11370 /// method, and the `ClosedSet`-trait FromStr/Display surface via
11371 /// `#[closed_set(via = "label")]`) now route through ONE typed
11372 /// closed-set projection family with no per-consumer literal
11373 /// duplication.
11374 #[must_use]
11375 pub fn label(self) -> &'static str {
11376 self.sexp_shape().label()
11377 }
11378
11379 /// Stable, per-variant byte discriminator that paired with the
11380 /// recursive payload hash builds the substrate's [`Hash for Atom`]
11381 /// projection — `0u8` for [`Self::Symbol`], `1u8` for
11382 /// [`Self::Keyword`], `2u8` for [`Self::Str`], `3u8` for
11383 /// [`Self::Int`], `4u8` for [`Self::Float`], `5u8` for
11384 /// [`Self::Bool`]. The byte values are load-bearing because the
11385 /// macro-expansion cache ([`crate::macro_expand::Expander`]'s
11386 /// cache) keys on the hash of `(macro_name, args)`, and any
11387 /// `Atom` participates in that hash — changing a discriminator
11388 /// silently invalidates every cached expansion across the
11389 /// substrate.
11390 ///
11391 /// The closed set ensures the six arms partition `{0, 1, 2, 3,
11392 /// 4, 5}` injectively. Disjointness from [`QuoteForm`]'s
11393 /// `{3, 4, 5, 6}` is structural rather than overlap-induced
11394 /// hash collision: [`Hash for Atom`] and the quote-family arms of
11395 /// [`Hash for Sexp`] hash DISTINCT types (`Atom` vs `Sexp`), and
11396 /// `Atom`'s discriminator lives nested INSIDE `Sexp::Atom`'s outer
11397 /// `1u8` discriminator — the prefix-uniqueness contract that the
11398 /// `Hash for Sexp` outer match maintains independently. A future
11399 /// quote-family or atomic-kind extension must extend BOTH bodies'
11400 /// arms in lockstep, with rustc binding the consistency through
11401 /// exhaustiveness over BOTH closed enums.
11402 ///
11403 /// `pub(crate)` because the byte-discriminator surface is an
11404 /// implementation detail of the substrate's [`Hash for Atom`]
11405 /// cache-key contract; exposing it publicly would leak the
11406 /// cache-key shape through the API without enabling any external
11407 /// consumer the public projections ([`Atom::kind`], [`Self::label`],
11408 /// [`Self::sexp_shape`]) don't already serve. Same posture as
11409 /// [`QuoteForm::hash_discriminator`] and its outer-value peer
11410 /// [`Atom::hash_discriminator`] (the outer-`Atom` projection that
11411 /// composes through this method via `self.kind().hash_discriminator()`
11412 /// so the [`Hash for Atom`] callsite binds at ONE site on the
11413 /// outer-`Atom` algebra rather than at the two-hop
11414 /// `.kind().hash_discriminator()` chain).
11415 #[must_use]
11416 pub(crate) fn hash_discriminator(self) -> u8 {
11417 match self {
11418 Self::Symbol => Self::SYMBOL_HASH_DISCRIMINATOR,
11419 Self::Keyword => Self::KEYWORD_HASH_DISCRIMINATOR,
11420 Self::Str => Self::STR_HASH_DISCRIMINATOR,
11421 Self::Int => Self::INT_HASH_DISCRIMINATOR,
11422 Self::Float => Self::FLOAT_HASH_DISCRIMINATOR,
11423 Self::Bool => Self::BOOL_HASH_DISCRIMINATOR,
11424 }
11425 }
11426
11427 /// Canonical [`SexpShape`] embed target for the [`Self::Symbol`]
11428 /// atomic-payload arm on the AtomKind ⊂ SexpShape 6-of-12 carving —
11429 /// [`SexpShape::Symbol`]. Per-role peer of `Self::Symbol` on the
11430 /// closed-set atomic-payload → outer-shape embed axis; consumers
11431 /// reach for `AtomKind::SYMBOL_SHAPE` when the caller has a variant
11432 /// in hand at compile time and wants the canonical outer-shape
11433 /// identity without runtime dispatch through [`Self::sexp_shape`].
11434 ///
11435 /// Sibling posture to the six pre-existing per-role LABEL /
11436 /// HASH_DISCRIMINATOR aliases on this same closed-set algebra
11437 /// ([`Self::SYMBOL_LABEL`], [`Self::SYMBOL_HASH_DISCRIMINATOR`]) —
11438 /// each closes a distinct per-role sub-vocabulary axis on the
11439 /// AtomKind carving. This constant closes the THIRD per-role
11440 /// axis on [`AtomKind`] (the `SexpShape`-embed axis, paired with
11441 /// the pre-existing `&'static str` diagnostic-label axis + the
11442 /// `u8` cache-key axis) at ONE typed alias through the peer
11443 /// superset variant on the [`SexpShape`] closed set.
11444 pub const SYMBOL_SHAPE: SexpShape = SexpShape::Symbol;
11445
11446 /// Canonical [`SexpShape`] embed target for the [`Self::Keyword`]
11447 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11448 /// [`SexpShape::Keyword`]. Per-role peer of `Self::Keyword`.
11449 pub const KEYWORD_SHAPE: SexpShape = SexpShape::Keyword;
11450
11451 /// Canonical [`SexpShape`] embed target for the [`Self::Str`]
11452 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11453 /// [`SexpShape::String`]. Per-role peer of `Self::Str`; the
11454 /// `Str → String` wire-shape rename matches the peer
11455 /// [`Self::STRING_LABEL`] alias (both bind the AtomKind subset's
11456 /// `Str` variant to the SexpShape superset's `String` variant on
11457 /// their respective per-role sub-vocabulary axes).
11458 pub const STR_SHAPE: SexpShape = SexpShape::String;
11459
11460 /// Canonical [`SexpShape`] embed target for the [`Self::Int`]
11461 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11462 /// [`SexpShape::Int`]. Per-role peer of `Self::Int`.
11463 pub const INT_SHAPE: SexpShape = SexpShape::Int;
11464
11465 /// Canonical [`SexpShape`] embed target for the [`Self::Float`]
11466 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11467 /// [`SexpShape::Float`]. Per-role peer of `Self::Float`.
11468 pub const FLOAT_SHAPE: SexpShape = SexpShape::Float;
11469
11470 /// Canonical [`SexpShape`] embed target for the [`Self::Bool`]
11471 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11472 /// [`SexpShape::Bool`]. Per-role peer of `Self::Bool`.
11473 pub const BOOL_SHAPE: SexpShape = SexpShape::Bool;
11474
11475 /// Closed-set forced-arity ALL array over the canonical
11476 /// [`SexpShape`] embed targets on the AtomKind ⊂ SexpShape
11477 /// 6-of-12 carving, in declaration order matching [`Self::ALL`]
11478 /// element-wise (pinned by
11479 /// `atom_kind_shapes_align_with_all_by_index`). Sibling posture
11480 /// to [`Self::LABELS`] (`[&'static str; 6]` — per-role diagnostic
11481 /// bytes) and [`Self::HASH_DISCRIMINATORS`] (`[u8; 6]` — per-role
11482 /// nested-Atom cache-key bytes) on the SAME closed-set AtomKind
11483 /// algebra; where those two arrays lift the per-role
11484 /// `&'static str` and `u8` sub-vocabularies onto the substrate,
11485 /// this array lifts the per-role [`SexpShape`] embed-target
11486 /// sub-vocabulary at the same `[_; 6]` forced arity.
11487 ///
11488 /// Pre-lift the six [`SexpShape`] embed targets had NO per-role
11489 /// primitive on this closed-set algebra — a consumer with an
11490 /// `AtomKind` variant in hand at compile time reaching for the
11491 /// canonical embed target had to spell
11492 /// `AtomKind::Symbol.sexp_shape()` (runtime dispatch through the
11493 /// six-arm match body) OR re-derive the AtomKind ⊂ SexpShape
11494 /// variant pairing at the call site by importing both enums and
11495 /// spelling `SexpShape::Symbol` inline. Post-lift the SIX
11496 /// canonical embed targets bind at ONE `pub const` per role on
11497 /// the typed [`AtomKind`] algebra AND at [`Self::SHAPES`] as a
11498 /// family-wide forced-arity array — a future LSP / REPL
11499 /// completion bar keyed on `AtomKind::SHAPES` for the "which
11500 /// SexpShape does this AtomKind embed into?" outer-shape column,
11501 /// a `tatara-check` coverage sweep zipping `AtomKind::ALL` /
11502 /// `LABELS` / `HASH_DISCRIMINATORS` / `SHAPES` in lockstep for a
11503 /// family-wide (variant, label, byte, embed-target) quadruple
11504 /// render, or a Sekiban audit-trail metric jointly labeled by
11505 /// the embed-target's SexpShape identity reads through the typed
11506 /// constants on this subset algebra without re-deriving the
11507 /// 6-of-12 carving inline.
11508 ///
11509 /// Round-trip identity with the inverse projection
11510 /// [`crate::error::SexpShape::as_atom_kind`]: for every index `i`,
11511 /// `Self::SHAPES[i].as_atom_kind() == Some(Self::ALL[i])`
11512 /// (pinned by
11513 /// `atom_kind_shapes_align_with_all_by_index_through_as_atom_kind`) —
11514 /// the embed / project section closes as a family-wide array-
11515 /// indexed law rather than as a per-variant assertion sweep.
11516 /// Adding a hypothetical seventh atomic kind (e.g. `Char` for
11517 /// `#\x` reader syntax, `Bigint` for arbitrary-precision
11518 /// integers) extends [`Self::ALL`] AND [`Self::SHAPES`] AND
11519 /// [`SexpShape::ALL`] AND adds ONE per-role `pub const *_SHAPE`
11520 /// in lockstep — rustc's forced-arity check on the two `[_; N]`
11521 /// arrays fails compilation if EITHER ALL array grows without
11522 /// the other, AND the peer [`SexpShape::as_atom_kind`] arm must
11523 /// grow in lockstep to preserve the round-trip identity.
11524 ///
11525 /// Theory anchor: THEORY.md §III — the typescape; the six
11526 /// canonical [`SexpShape`] embed targets bind at ONE typed
11527 /// `[SexpShape; 6]` array on the closed-set AtomKind algebra
11528 /// rather than at zero-primitive-on-this-subset-plus-six-inline-
11529 /// lookups scattered across the substrate. THEORY.md §V.1 —
11530 /// knowable platform; the family's cardinality becomes a TYPE-
11531 /// level constant on the substrate algebra rather than a per-
11532 /// consumer runtime dispatch through the composition. THEORY.md
11533 /// §II.1 invariant 2 — free middle; the (embed, project) pair
11534 /// binds at THREE typed sites now — the projection method
11535 /// [`Self::sexp_shape`], this family-wide array, AND the peer
11536 /// inverse [`crate::error::SexpShape::as_atom_kind`] — with
11537 /// rustc-enforced consistency across all three. THEORY.md §VI.1
11538 /// — generation over composition; the family-wide contract
11539 /// sweeps (alignment with `ALL`, round-trip through
11540 /// `as_atom_kind`, membership through `sexp_shape`) emerge from
11541 /// the composition of TWO substrate primitives (this `pub const`
11542 /// array + the six per-role `pub const *_SHAPE` aliases) rather
11543 /// than as per-variant inline assertions duplicated at each call
11544 /// site.
11545 pub const SHAPES: [SexpShape; 6] = [
11546 Self::SYMBOL_SHAPE,
11547 Self::KEYWORD_SHAPE,
11548 Self::STR_SHAPE,
11549 Self::INT_SHAPE,
11550 Self::FLOAT_SHAPE,
11551 Self::BOOL_SHAPE,
11552 ];
11553
11554 /// Project the typed marker into its matching [`SexpShape`]
11555 /// variant — `Symbol → SexpShape::Symbol`, `Keyword →
11556 /// SexpShape::Keyword`, `Str → SexpShape::String`, `Int →
11557 /// SexpShape::Int`, `Float → SexpShape::Float`, `Bool →
11558 /// SexpShape::Bool`. ONE projection on the closed-set atomic-
11559 /// payload algebra that [`crate::domain::sexp_shape`]'s outer-shape
11560 /// projection routes through for the six atom arms — so the
11561 /// (Atom variant, SexpShape variant) pairing binds at ONE site on
11562 /// the typed algebra rather than at six byte-identical inline arms
11563 /// in [`crate::domain::sexp_shape`]. Direct sibling to
11564 /// [`QuoteForm::sexp_shape`] — that closed enum carves the
11565 /// quote-family arms of [`SexpShape`]'s twelve-variant closed set,
11566 /// while this enum carves the atomic-payload arms.
11567 ///
11568 /// Each arm routes through the per-role `pub const` on `impl Self`
11569 /// ([`Self::SYMBOL_SHAPE`], [`Self::KEYWORD_SHAPE`],
11570 /// [`Self::STR_SHAPE`], [`Self::INT_SHAPE`], [`Self::FLOAT_SHAPE`],
11571 /// [`Self::BOOL_SHAPE`]) so the six canonical embed targets bind
11572 /// at ONE typed source of truth per role rather than as inline
11573 /// `SexpShape::X` literals scattered across the `match` body.
11574 /// Sibling posture to [`Self::label`]'s composition through
11575 /// [`Self::sexp_shape().label()`] and [`Self::hash_discriminator`]'s
11576 /// per-role routing through [`Self::SYMBOL_HASH_DISCRIMINATOR`] …
11577 /// [`Self::BOOL_HASH_DISCRIMINATOR`] — the three per-role axes on
11578 /// the AtomKind algebra (embed target, diagnostic label, cache-key
11579 /// byte) each surface their per-role bytes through the SAME
11580 /// per-role `pub const` shape.
11581 ///
11582 /// Composition law: for every [`Atom`] `a`,
11583 /// `crate::domain::sexp_shape(&Sexp::Atom(a.clone())) ==
11584 /// a.kind().sexp_shape()`. Pinned by the cross-projection round-trip
11585 /// test in this module, so a regression that drifts either side
11586 /// of the typed algebra (an [`Atom::kind`] arm or this
11587 /// [`Self::sexp_shape`] arm) surfaces immediately rather than as a
11588 /// silent operator-facing diagnostic drift at every
11589 /// `LispError::TypeMismatch.got` slot for an atomic witness.
11590 ///
11591 /// Post-lift routing pin
11592 /// `atom_kind_sexp_shape_routes_through_typed_per_role_constants`
11593 /// catches a regression that re-inlines the six `SexpShape::X`
11594 /// arm literals here and silently drifts ONE arm from the per-role
11595 /// `pub const` alias — the routing agreement is a TYPED CONSEQUENCE
11596 /// of the composition rather than literal discipline at two sites.
11597 ///
11598 /// Bidirectional dual: the inverse projection
11599 /// [`crate::error::SexpShape::as_atom_kind`] (12→6, partial)
11600 /// covers the 6-of-12 carving of [`SexpShape`] this embed
11601 /// reaches. The pair `(AtomKind::sexp_shape,
11602 /// SexpShape::as_atom_kind)` forms an `Iso(AtomKind, AtomShape ⊂
11603 /// SexpShape)`: every typed marker round-trips through the embed
11604 /// (`AtomKind::sexp_shape(k).as_atom_kind() == Some(k)` for every
11605 /// `k: AtomKind`), every atom-shape pre-image recovers the typed
11606 /// marker. The non-atom shapes (`Nil`, `List`, every quote-family
11607 /// wrapper) form the kernel of the inverse — `as_atom_kind`
11608 /// returns `None` for them. See [`crate::error::SexpShape::as_atom_kind`]'s
11609 /// docstring for the composition law's other direction +
11610 /// disjointness with the quote-family sibling
11611 /// `SexpShape::as_quote_form`.
11612 ///
11613 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (Atom
11614 /// variant, SexpShape variant) pairing becomes a TYPE projection
11615 /// on the substrate algebra rather than six inline arms in
11616 /// [`crate::domain::sexp_shape`]. A typo or swap at the shape-
11617 /// projection site is no longer a runtime drift but a compile
11618 /// error against the typed projection. THEORY.md §II.1 invariant
11619 /// 2 — free middle; THREE consumers ([`Hash for Atom`] via
11620 /// [`Self::hash_discriminator`], [`crate::domain::sexp_shape`]
11621 /// via this method, and the future diagnostic / completion surface
11622 /// via [`Self::label`]) now route through ONE typed closed-set
11623 /// match family, so a regression that drifts ONE consumer's
11624 /// pairing from the others cannot reach the substrate's runtime.
11625 #[must_use]
11626 pub fn sexp_shape(self) -> SexpShape {
11627 match self {
11628 Self::Symbol => Self::SYMBOL_SHAPE,
11629 Self::Keyword => Self::KEYWORD_SHAPE,
11630 Self::Str => Self::STR_SHAPE,
11631 Self::Int => Self::INT_SHAPE,
11632 Self::Float => Self::FLOAT_SHAPE,
11633 Self::Bool => Self::BOOL_SHAPE,
11634 }
11635 }
11636}
11637
11638// `impl fmt::Display for AtomKind` + `impl std::str::FromStr for AtomKind`
11639// + `impl tatara_closed_set::ClosedSet for AtomKind` + `pub struct UnknownAtomKind(pub
11640// String)` are generated by `#[derive(tatara_closed_set::DeriveClosedSet)]` on
11641// the enum declaration above. `label` delegates to the inherent
11642// `AtomKind::label` via `#[closed_set(via = "label")]` so the
11643// domain-canonical lowercase-vocabulary projection stays load-bearing (the
11644// six labels `"symbol" / "keyword" / "string" / "int" / "float" / "bool"`
11645// match the `SexpShape` atomic-subset labels byte-for-byte AND the
11646// diagnostic-rendering shape `LispError::TypeMismatch.got` keys on
11647// verbatim). The `display` flag emits the substrate-wide
11648// `f.write_str(Self::label(*self))` block. `#[closed_set(generate_unknown =
11649// "atom kind")]` emits the typed parse-rejection carrier with the
11650// substrate-wide `Debug + Clone + PartialEq + Eq + thiserror::Error`
11651// derives and the `#[error("unknown atom kind: {0}")]` annotation
11652// byte-for-byte; the explicit label pins the pre-lift wording even though
11653// the auto-derived `pascal_to_spaced_lowercase("AtomKind")` projects to
11654// the same `"atom kind"` literal.
11655
11656impl Sexp {
11657 /// Canonical `(` char that opens a [`Self::List`] rendering AND
11658 /// (paired with [`Self::LIST_CLOSE`]) the empty [`Self::Nil`]
11659 /// rendering `()`. Outer-structural peer of [`Atom::STR_DELIMITER`]
11660 /// on the atomic-payload delimiter axis: where `STR_DELIMITER` is
11661 /// the ONE `"` byte the reader's tokenizer's FOUR
11662 /// `Token::Str`-round-trip sites bind to on the closed-set [`Atom`]
11663 /// algebra, `LIST_OPEN` is the ONE `(` byte the reader's tokenizer's
11664 /// `Token::LParen` outer-dispatch arm AND the bare-atom terminator
11665 /// disjunct AND [`fmt::Display for Sexp`]'s list-opening emission
11666 /// AND [`Self::Nil`]'s two-char `()` rendering all bind to on the
11667 /// closed-set outer [`Sexp`] algebra.
11668 ///
11669 /// Pre-lift the same `'('` byte lived inline at FOUR sites: two
11670 /// outer-match arms in `crate::reader::tokenize` (the
11671 /// `Token::LParen` construction arm AND the bare-atom terminator's
11672 /// `|| ch == '('` disjunct), and two Display arms in [`fmt::Display
11673 /// for Sexp`] (the `Self::List(_)` opener AND the `Self::Nil`
11674 /// two-char `()` rendering's left char). Post-lift the (typed
11675 /// structural role, canonical byte) pairing binds at ONE constant
11676 /// on the [`Sexp`] algebra that every consumer routes through; a
11677 /// refactor that swaps the byte (e.g. a Racket-style port to `[`
11678 /// for square-bracket list literals, an S-expression-DSL port to
11679 /// `{` for brace-list syntax) touches ONE constant rather than
11680 /// four inline bytes that would silently drift out of round-trip
11681 /// agreement if one was updated without the others.
11682 ///
11683 /// Load-bearing paired-delimiter contract:
11684 /// `Sexp::LIST_OPEN` MUST pair section-for-retraction with
11685 /// [`Self::LIST_CLOSE`] at every round-trip site — the reader's
11686 /// `Token::LParen` (from `LIST_OPEN`) MUST be closed by a
11687 /// `Token::RParen` (from `LIST_CLOSE`) for a well-formed list,
11688 /// and the Display impl's `Self::List(_)` arm MUST emit
11689 /// `LIST_OPEN` at the opener AND `LIST_CLOSE` at the closer for
11690 /// the reader-then-Display round trip
11691 /// `parse(display(list)) == list` to hold. Guards the paired
11692 /// disjointness across the closed-set outer [`Sexp`] algebra so a
11693 /// future refactor that renames one constant without updating the
11694 /// other fails at rustc / test time rather than as a silent list-
11695 /// rendering asymmetry.
11696 ///
11697 /// Cross-axis disjointness with the sibling delimiters (pinned
11698 /// structurally at
11699 /// `sexp_list_delimiters_distinct_from_every_other_algebra_marker`):
11700 /// `LIST_OPEN`'s byte MUST differ from [`Atom::STR_DELIMITER`]
11701 /// (`'"'`), [`Atom::KEYWORD_MARKER`] (`":"`), the two
11702 /// [`Atom::bool_literal`] spellings (`"#t"` / `"#f"`) AND every
11703 /// [`QuoteForm::lead_char`] projection (`'\''`, `` '`' ``, `','`)
11704 /// on the substrate's outer-marker axes. Otherwise the reader's
11705 /// `Token::LParen` outer-dispatch arm would ambiguously route
11706 /// through a sibling algebra's arm.
11707 ///
11708 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
11709 /// (`Self::List` outer structure, canonical `(` opener) pairing
11710 /// now binds at ONE constant on the closed-set outer [`Sexp`]
11711 /// algebra regardless of which of the four consumer surfaces
11712 /// reaches in. THEORY.md §VI.1 — generation over composition;
11713 /// four byte-identical inline `'('` char literals across two
11714 /// substrate files collapse onto ONE named constant, matching
11715 /// the substrate's three-times rule. THEORY.md §V.1 — knowable
11716 /// platform; the canonical list-opener byte becomes a
11717 /// TYPE-level constant on the outer substrate algebra rather
11718 /// than four inline bytes at four consumer surfaces across two
11719 /// substrate files (`crate::reader` and `crate::ast`).
11720 pub const LIST_OPEN: char = '(';
11721
11722 /// Canonical `)` char that closes a [`Self::List`] rendering AND
11723 /// (paired with [`Self::LIST_OPEN`]) the empty [`Self::Nil`]
11724 /// rendering `()`. See [`Self::LIST_OPEN`] for the substrate-wide
11725 /// paired-delimiter contract, cross-axis disjointness, and theory
11726 /// anchors — this constant is its section-for-retraction sibling
11727 /// on the closer axis. Four consumer sites (the reader's
11728 /// `Token::RParen` outer-dispatch arm, the bare-atom terminator's
11729 /// `|| ch == ')'` disjunct, [`fmt::Display for Sexp`]'s
11730 /// `Self::List(_)` closer, [`Self::Nil`]'s two-char `()`
11731 /// rendering's right char) all bind here.
11732 pub const LIST_CLOSE: char = ')';
11733
11734 /// Canonical paired list-delimiter closed-set ALL array — composes
11735 /// [`Self::LIST_OPEN`] followed by [`Self::LIST_CLOSE`] in canonical
11736 /// declaration order, forced-arity `[char; 2]` so a hypothetical
11737 /// third list-delimiter row would extend this array + one algebra
11738 /// constant in lockstep. Peer to [`Atom::SELF_ESCAPE_TABLE`]
11739 /// (`[char; 2]` on the Str-payload self-escape sub-vocabulary axis
11740 /// of the closed-set [`Atom`] algebra): where `SELF_ESCAPE_TABLE`
11741 /// closes the two pattern-EQUALS-value inner-tokenizer arms of
11742 /// `Atom::decode_str_escape` as ONE typed forced-arity array,
11743 /// `LIST_DELIMITERS` closes the two outer-structural list-delimiter
11744 /// arms of the reader's outer-dispatch as the analogous typed
11745 /// forced-arity array one axis over on the closed-set outer
11746 /// [`Sexp`] algebra.
11747 ///
11748 /// Pre-lift the two-element `[Self::LIST_OPEN, Self::LIST_CLOSE]`
11749 /// composition lived inline at TWO sites: the two `|| ch ==
11750 /// Self::LIST_{OPEN,CLOSE}` disjuncts inside
11751 /// [`Self::is_bare_atom_boundary`]'s reader-boundary projection
11752 /// (spanning TWO of the SIX categories the projection carries), AND
11753 /// the `[Sexp::LIST_OPEN, Sexp::LIST_CLOSE].iter().collect()` array
11754 /// literal at the `Nil` Display composition-pin test that binds the
11755 /// two-char `()` rendering to the two typed constants. Post-lift
11756 /// the sub-vocabulary sweep binds at ONE typed forced-arity array
11757 /// on the closed-set outer [`Sexp`] algebra rather than at two
11758 /// inline algebra-constant enumerations per consumer. Adding a
11759 /// hypothetical Racket-compat square-bracket list mode
11760 /// (`[Self::LIST_OPEN, Self::LIST_CLOSE, Self::VEC_OPEN,
11761 /// Self::VEC_CLOSE]`) would extend `LIST_DELIMITERS` ONCE +
11762 /// `Self::is_bare_atom_boundary`'s sub-vocabulary sweep ONCE + two
11763 /// new algebra constants (opener + closer) in lockstep; rustc's
11764 /// forced-arity check on `[char; N]` binds the extension through
11765 /// the array declaration site.
11766 ///
11767 /// Structural invariant carried at the SHAPE level: `[char; 2]`
11768 /// pairs section-for-retraction one-to-one with
11769 /// [`Atom::SELF_ESCAPE_TABLE`]'s `[char; 2]` — the two arrays
11770 /// sit on distinct closed-set algebras (outer-structural
11771 /// list-delimiter vocabulary on [`Sexp`]; inner-Str-payload
11772 /// self-escape vocabulary on [`Atom`]) but share the same
11773 /// forced-arity shape at their respective sub-vocabulary
11774 /// axes. A consumer that reaches for one of the two arrays
11775 /// encodes its vocabulary's paired-role identity in the SHAPE
11776 /// it iterates rather than in a per-site convention.
11777 ///
11778 /// Composition law (round-trip): `LIST_DELIMITERS[0] ==
11779 /// Self::LIST_OPEN` AND `LIST_DELIMITERS[1] == Self::LIST_CLOSE`
11780 /// AND `LIST_DELIMITERS.len() == 2`. The forced-arity + canonical
11781 /// declaration order together pin every downstream index-sweep
11782 /// consumer to the (opener, closer) pairing at rustc time; a
11783 /// reorder without reordering the underlying algebra constants
11784 /// fails at the composition pin below.
11785 ///
11786 /// Cross-axis disjointness pinned structurally at
11787 /// [`sexp_list_delimiters_distinct_from_every_other_algebra_marker`]:
11788 /// neither element aliases any sibling outer-marker char on the
11789 /// substrate's other closed-set algebras — the Str-payload
11790 /// delimiter (`Atom::STR_DELIMITER`), the Keyword-marker prefix
11791 /// (`Atom::KEYWORD_MARKER_LEAD`), the Comment-lead byte
11792 /// (`Self::COMMENT_LEAD`), every quote-family lead char
11793 /// (`QuoteForm::lead_char`), and every Bool-literal spelling's
11794 /// first char.
11795 ///
11796 /// Theory anchor: THEORY.md §III — the typescape; the paired
11797 /// (opener, closer) list-delimiter sub-vocabulary becomes a typed
11798 /// forced-arity ALL array on the closed-set outer [`Sexp`]
11799 /// algebra rather than as two inline algebra-constant
11800 /// enumerations at every consumer that iterates the paired
11801 /// delimiter axis. THEORY.md §V.1 — knowable platform; the
11802 /// paired-delimiter sub-vocabulary now binds as load-bearing
11803 /// typed data at the algebra level rather than as two per-site
11804 /// disjuncts. THEORY.md §VI.1 — generation over composition; the
11805 /// paired-delimiter (opener + closer) composition regenerates
11806 /// identically through this ONE typed forced-arity array rather
11807 /// than through two inline algebra-constant enumerations per
11808 /// consumer.
11809 pub const LIST_DELIMITERS: [char; 2] = [Self::LIST_OPEN, Self::LIST_CLOSE];
11810
11811 /// Canonical `;` char that begins a line-comment run in the reader's
11812 /// tokenizer AND (as a bare-atom terminator disjunct) breaks a
11813 /// `Token::Atom` accumulator when the byte is encountered mid-lexeme.
11814 /// Outer-structural peer of [`Self::LIST_OPEN`] / [`Self::LIST_CLOSE`]
11815 /// on the reader-discard axis: where `LIST_OPEN` / `LIST_CLOSE` are
11816 /// the paired-delimiter constants that shape a `Sexp::List` payload
11817 /// on the closed-set outer [`Sexp`] algebra, `COMMENT_LEAD` is the
11818 /// ONE `;` byte the reader's tokenizer's TWO comment-boundary sites
11819 /// bind to on the same outer algebra — the outer-dispatch arm that
11820 /// begins a line-comment run (consuming through the trailing `\n`
11821 /// which is itself absorbed by the whitespace disjunct in the outer
11822 /// match) AND the bare-atom terminator disjunct that ends a
11823 /// `Token::Atom` accumulator when it encounters this byte mid-lexeme
11824 /// so a bare `foo;bar` source tokenizes as `Token::Atom("foo") @ 0`
11825 /// followed by a discarded line-comment run rather than as ONE
11826 /// `Token::Atom("foo;bar")` payload.
11827 ///
11828 /// Pre-lift the same `';'` byte lived inline at TWO sites in
11829 /// `crate::reader::tokenize`: the outer-match `';' => { … }`
11830 /// line-comment arm AND the bare-atom terminator's `|| ch == ';'`
11831 /// disjunct. Post-lift the (reader-discard role, canonical byte)
11832 /// pairing binds at ONE constant on the [`Sexp`] algebra that both
11833 /// consumer sites route through; a refactor that swaps the byte
11834 /// (e.g. a Scheme R7RS-style port to `#;` datum-comment syntax, an
11835 /// Emacs-style port to `#!` shebang-comment syntax) touches ONE
11836 /// constant rather than two inline bytes that would silently drift
11837 /// out of tokenizer agreement if one was updated without the other.
11838 ///
11839 /// Reader-discard contract: `Sexp::COMMENT_LEAD` MUST NOT surface
11840 /// as an atomic payload in any parsed [`Sexp`] — the outer-dispatch
11841 /// arm consumes the byte AND every char up to (but not past) the
11842 /// trailing `\n`, emitting NO token. The bare-atom terminator
11843 /// disjunct breaks the `Token::Atom` accumulator EXACTLY on this
11844 /// byte so the subsequent line-comment run reaches the outer arm
11845 /// with its byte-offset intact. Both sites bind to ONE constant so
11846 /// a regression that drifts ONE of the two disjuncts (e.g.
11847 /// re-inlines `';'` at the outer arm while migrating the terminator
11848 /// to a different byte) fails at rustc / test time rather than as
11849 /// a silent tokenizer misclassification.
11850 ///
11851 /// Cross-axis disjointness with the sibling markers (pinned
11852 /// structurally at
11853 /// `sexp_comment_lead_distinct_from_every_other_algebra_marker`):
11854 /// `COMMENT_LEAD`'s byte MUST differ from [`Self::LIST_OPEN`]
11855 /// (`'('`), [`Self::LIST_CLOSE`] (`')'`), [`Atom::STR_DELIMITER`]
11856 /// (`'"'`), [`Atom::KEYWORD_MARKER`]'s lead byte (`':'`), the two
11857 /// [`Atom::bool_literal`] spellings' lead byte (`'#'`) AND every
11858 /// [`QuoteForm::lead_char`] projection (`'\''`, `` '`' ``, `','`)
11859 /// on the substrate's outer-marker axes. Otherwise a bare `;foo`
11860 /// lexeme would ambiguously route through the line-comment arm AND
11861 /// a sibling algebra's arm at the reader's outer dispatch.
11862 ///
11863 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
11864 /// (reader-discard role, canonical `;` byte) pairing binds at ONE
11865 /// constant on the closed-set outer [`Sexp`] algebra regardless of
11866 /// which of the two consumer sites reaches in. THEORY.md §VI.1 —
11867 /// generation over composition; two byte-identical inline `';'`
11868 /// char literals across ONE substrate file collapse onto ONE named
11869 /// constant, matching the substrate's three-times rule
11870 /// (`\geq 2` PRIME-DIRECTIVE trigger). THEORY.md §V.1 — knowable
11871 /// platform; the canonical comment-lead byte becomes a TYPE-level
11872 /// constant on the outer substrate algebra rather than two inline
11873 /// bytes at two consumer surfaces in `crate::reader`.
11874 pub const COMMENT_LEAD: char = ';';
11875
11876 /// Canonical `\n` char that terminates a line-comment run in the
11877 /// reader's tokenizer — the section-for-retraction sibling of
11878 /// [`Self::COMMENT_LEAD`] on the reader-discard axis. Paired
11879 /// opener/terminator peer of [`Self::LIST_OPEN`] /
11880 /// [`Self::LIST_CLOSE`] on the outer-structural axis: where
11881 /// [`Self::LIST_OPEN`] / [`Self::LIST_CLOSE`] are the two typed
11882 /// constants that shape a `Sexp::List` payload, [`Self::COMMENT_LEAD`]
11883 /// / [`Self::COMMENT_TERM`] are the two typed constants that shape
11884 /// the reader's line-comment discard run. Both pairs live on the
11885 /// closed-set outer [`Sexp`] algebra so the reader-discard axis
11886 /// carries the same opener/closer discipline the outer-structural
11887 /// axis has carried since the initial [`Self::LIST_OPEN`] /
11888 /// [`Self::LIST_CLOSE`] lift.
11889 ///
11890 /// Pre-lift the same `'\n'` byte lived inline at ONE site in
11891 /// `crate::reader::tokenize` — the line-comment discard loop's
11892 /// terminator check `if ch == '\n' { break; }`. Post-lift the
11893 /// (reader-discard terminator role, canonical byte) pairing binds
11894 /// at ONE constant on the [`Sexp`] algebra that the consumer site
11895 /// routes through; a refactor that ports the reader to a different
11896 /// line-break convention (e.g. Scheme R7RS `#;` datum-comment
11897 /// terminated at the next well-formed datum, an Emacs-style port
11898 /// with `\r\n` CRLF sequences, a Common-Lisp-style `#|…|#` block
11899 /// comment closed by `|#`) touches ONE constant (or extends the
11900 /// algebra by ONE peer method) rather than an inline byte.
11901 ///
11902 /// Reader-discard contract: `Sexp::COMMENT_TERM` MUST NOT surface
11903 /// as an atomic payload in any parsed [`Sexp`] — the reader's
11904 /// [`Self::COMMENT_LEAD`] outer-dispatch arm consumes every byte
11905 /// (INCLUDING this terminator) up to and including the FIRST
11906 /// occurrence of [`Self::COMMENT_TERM`], emitting NO token. The
11907 /// (lead, term) pair carries the SAME reader-discard invariant as
11908 /// (LIST_OPEN, LIST_CLOSE) does on the outer-structural axis: both
11909 /// bytes are structural markers that never appear in a token
11910 /// payload.
11911 ///
11912 /// Cross-axis disjointness with the sibling closed-set markers
11913 /// (pinned structurally at
11914 /// `sexp_comment_term_distinct_from_every_non_whitespace_algebra_marker`):
11915 /// `COMMENT_TERM`'s byte MUST differ from every NON-whitespace
11916 /// outer-marker char — [`Self::LIST_OPEN`], [`Self::LIST_CLOSE`],
11917 /// [`Self::COMMENT_LEAD`], [`Atom::STR_DELIMITER`],
11918 /// [`Atom::STR_ESCAPE_LEAD`], [`Atom::KEYWORD_MARKER`]'s lead byte,
11919 /// the two [`Atom::bool_literal`] spellings' lead byte, AND every
11920 /// [`QuoteForm::lead_char`] projection. The terminator IS a
11921 /// whitespace char (it satisfies `char::is_whitespace`) so the
11922 /// disjointness test explicitly excludes the whitespace-family
11923 /// axis: the terminator's role IS to be whitespace-family, so the
11924 /// disjointness contract binds only against the non-whitespace
11925 /// outer-marker axes.
11926 ///
11927 /// Interaction with the escape-decode codomain axis: the terminator
11928 /// byte `'\n'` COINCIDES with the C0 control byte
11929 /// [`Atom::decode_str_escape`]`('n')` produces — the two roles are
11930 /// distinct algebraic axes (reader-discard structural marker on
11931 /// the outer [`Sexp`] algebra vs. Str-escape shorthand codomain
11932 /// value on the inner [`Atom`] algebra) so the collision at the
11933 /// byte level is by design, NOT a disjointness violation. A `\n`
11934 /// byte APPEARING inside a `Token::Str` payload's decoded output
11935 /// (via `\n` shorthand) is orthogonal to a `\n` byte APPEARING as
11936 /// the line-comment terminator at the reader's outer-dispatch
11937 /// discard loop.
11938 ///
11939 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
11940 /// (reader-discard terminator role, canonical `\n` byte) pairing
11941 /// binds at ONE constant on the closed-set outer [`Sexp`] algebra
11942 /// regardless of which consumer reaches in. THEORY.md §II.1
11943 /// invariant 5 — composition preserves proofs; the paired
11944 /// (COMMENT_LEAD, COMMENT_TERM) shape now lives at the algebra
11945 /// alongside (LIST_OPEN, LIST_CLOSE), so the reader-discard axis
11946 /// carries the SAME opener/closer discipline as the outer-
11947 /// structural axis. THEORY.md §VI.1 — generation over composition;
11948 /// the reader's inline `'\n'` char literal at the line-comment
11949 /// discard loop's terminator check collapses onto ONE named
11950 /// constant on the substrate algebra. THEORY.md §V.1 — knowable
11951 /// platform; the canonical line-comment terminator byte becomes
11952 /// a TYPE-level constant on the outer substrate algebra rather
11953 /// than an inline `'\n'` at one consumer surface in
11954 /// `crate::reader::tokenize`.
11955 pub const COMMENT_TERM: char = '\n';
11956
11957 /// Canonical paired line-comment delimiter closed-set ALL array —
11958 /// composes [`Self::COMMENT_LEAD`] followed by [`Self::COMMENT_TERM`]
11959 /// in canonical (opener, terminator) declaration order, forced-arity
11960 /// `[char; 2]` so a hypothetical alternative comment convention (a
11961 /// Scheme R7RS `#;` datum-comment lead paired with a next-datum
11962 /// terminator, an Emacs-style CRLF paired terminator, a Common-Lisp
11963 /// `#|…|#` block comment closed by `|#`) would extend this array +
11964 /// one algebra constant per new row in lockstep — rustc's forced-
11965 /// arity check on `[char; N]` binds the extension through the array
11966 /// declaration site.
11967 ///
11968 /// Cross-axis peer to [`Self::LIST_DELIMITERS`] (`[char; 2]` on the
11969 /// outer-structural paired-delimiter axis of the SAME closed-set
11970 /// outer [`Sexp`] algebra) — both close a paired-role byte
11971 /// sub-vocabulary at the SAME shape (`[char; 2]`, `(opener,
11972 /// closer_or_terminator)` canonical declaration order) but on
11973 /// DISTINCT roles: [`Self::LIST_DELIMITERS`] closes the two typed
11974 /// constants that shape a [`Self::List`] payload
11975 /// ([`Self::LIST_OPEN`] / [`Self::LIST_CLOSE`], BOTH of which
11976 /// classify as bare-atom boundaries and both non-whitespace);
11977 /// [`Self::COMMENT_DELIMITERS`] closes the two typed constants that
11978 /// shape the reader's line-comment discard run
11979 /// ([`Self::COMMENT_LEAD`] / [`Self::COMMENT_TERM`], where the LEAD
11980 /// row is a bare-atom boundary AND non-whitespace, and the TERM row
11981 /// is a whitespace-family char absorbed by the reader's
11982 /// `ch.is_whitespace()` outer-dispatch arm rather than a distinct
11983 /// bare-atom boundary). The two arrays partition the SIX-category
11984 /// outer-dispatch arm-set into a per-axis paired shape (2 rows for
11985 /// list delimiters + 2 rows for comment delimiters + 1 row for
11986 /// [`Atom::STR_DELIMITER`] + 3-of-4 rows for [`QuoteForm::LEADS`] +
11987 /// the residual whitespace family), each axis carrying its own
11988 /// forced-arity ALL array.
11989 ///
11990 /// Also sibling-shape to [`Atom::SELF_ESCAPE_TABLE`] (`[char; 2]` on
11991 /// the inner-Str-payload self-escape sub-vocabulary axis of the
11992 /// closed-set [`Atom`] algebra), [`Atom::BOOL_LITERALS`]
11993 /// (`[&'static str; 2]` on the Scheme-bool spelling axis), and
11994 /// [`crate::error::UnquoteForm::MARKERS`] / [`crate::error::UnquoteForm::IAC_FORGE_TAGS`]
11995 /// (`[&'static str; 2]` on the template-substitution subset algebra
11996 /// — the 2-of-4 subset carving of [`QuoteForm`]) — every closed-set
11997 /// outer projection on the substrate that carries a paired-role
11998 /// two-row axis now pins its canonical bytes at ONE `pub const` per
11999 /// role plus a forced-arity ALL array for family-wide consumers.
12000 ///
12001 /// Pre-lift the two-element `[Self::COMMENT_LEAD, Self::COMMENT_TERM]`
12002 /// composition had NO typed source of truth on the substrate — the
12003 /// two constants each existed independently on the algebra
12004 /// ([`Self::COMMENT_LEAD`] shipped in the initial reader-discard
12005 /// lift; [`Self::COMMENT_TERM`] shipped in the follow-on paired-
12006 /// terminator lift `bb1bd5e`) and consumers that wanted the
12007 /// (opener, terminator) shape had to reach across the algebra
12008 /// through TWO `pub const` sites. Post-lift the paired-role
12009 /// sub-vocabulary binds at ONE forced-arity ALL array on the closed-
12010 /// set outer [`Sexp`] algebra alongside the peer
12011 /// [`Self::LIST_DELIMITERS`] array on the outer-structural axis; a
12012 /// consumer that walks EITHER axis of the outer-structural /
12013 /// reader-discard cross-product reads the paired-role identity off
12014 /// the shared `[char; 2]` shape.
12015 ///
12016 /// Structural invariant carried at the SHAPE level: [`char; 2`]
12017 /// pairs section-for-retraction one-to-one with
12018 /// [`Self::LIST_DELIMITERS`]'s `[char; 2]` — the two arrays sit at
12019 /// distinct roles on the SAME closed-set outer [`Sexp`] algebra
12020 /// (outer-structural payload-delimiter role for `LIST_DELIMITERS`;
12021 /// reader-discard opener/terminator role for `COMMENT_DELIMITERS`)
12022 /// but share the same forced-arity shape at their respective axes.
12023 /// A consumer that reaches for one of the two arrays encodes its
12024 /// axis's paired-role identity in the SHAPE it iterates rather than
12025 /// in a per-site convention.
12026 ///
12027 /// Composition law (round-trip): `COMMENT_DELIMITERS[0] ==
12028 /// Self::COMMENT_LEAD` AND `COMMENT_DELIMITERS[1] ==
12029 /// Self::COMMENT_TERM` AND `COMMENT_DELIMITERS.len() == 2`. The
12030 /// forced-arity + canonical declaration order together pin every
12031 /// downstream index-sweep consumer to the (opener, terminator)
12032 /// pairing at rustc time; a reorder without reordering the
12033 /// underlying algebra constants fails at the composition pin below.
12034 ///
12035 /// Path-uniformity contract pinned per-row: `COMMENT_DELIMITERS[0]`
12036 /// (the LEAD row) MUST classify as a bare-atom boundary via
12037 /// [`Self::is_bare_atom_boundary`] (the reader's outer-dispatch's
12038 /// dedicated line-comment arm is one of the SIX categories that
12039 /// projection covers), AND `COMMENT_DELIMITERS[1]` (the TERM row)
12040 /// MUST classify as a whitespace-family char via
12041 /// [`char::is_whitespace`] (the reader's `ch.is_whitespace()` arm
12042 /// absorbs the terminator so the discard loop's post-loop hand-off
12043 /// to the outer-dispatch fires the whitespace arm rather than a
12044 /// distinct comment-terminator arm). The per-row asymmetry is
12045 /// LOAD-BEARING and structurally distinct from
12046 /// [`Self::LIST_DELIMITERS`]'s BOTH-rows-are-bare-atom-boundaries
12047 /// contract (both `(` and `)` are non-whitespace outer-dispatch
12048 /// arms). Pinned by
12049 /// `sexp_comment_delimiters_lead_row_is_bare_atom_boundary` +
12050 /// `sexp_comment_delimiters_term_row_is_whitespace_family_char`.
12051 ///
12052 /// Cross-axis disjointness pinned structurally at
12053 /// `sexp_comment_delimiters_disjoint_from_list_delimiters`: no row
12054 /// of `COMMENT_DELIMITERS` aliases any row of
12055 /// [`Self::LIST_DELIMITERS`] — the reader-discard sub-vocabulary
12056 /// and the outer-structural list-delimiter sub-vocabulary partition
12057 /// their respective bytes disjointly on the SAME closed-set outer
12058 /// [`Sexp`] algebra. Cross-algebra disjointness pinned at
12059 /// `sexp_comment_delimiters_disjoint_from_str_delimiter`: no row
12060 /// aliases [`Atom::STR_DELIMITER`] — the reader-discard arm and the
12061 /// Str-payload arm partition their bytes across the two closed-set
12062 /// algebras disjointly.
12063 ///
12064 /// Future consumers that compose against [`Self::COMMENT_DELIMITERS`]:
12065 /// a hypothetical `tatara_lisp_comment_delimiter_total{delimiter=";"|"\n"}`
12066 /// Sekiban metric surface at Prometheus recording time — the
12067 /// label-set generator sweeps this array verbatim rather than
12068 /// re-typing the two paired bytes inline at each recorder, and
12069 /// rustc-binds the metric-label set to the closed set through the
12070 /// forced-arity ALL array; an LSP / structural-editor that
12071 /// highlights line-comment runs — the (opener, terminator) pair the
12072 /// editor spans over IS this array's two rows; a hypothetical
12073 /// `Sexp::BLOCK_COMMENT_DELIMITERS` peer array for a future
12074 /// `#|…|#` block-comment mode would follow the same shape
12075 /// mechanically, extending the reader-discard axis by ONE peer
12076 /// array without touching this one's shape.
12077 ///
12078 /// Theory anchor: THEORY.md §III — the typescape; the paired
12079 /// (opener, terminator) reader-discard sub-vocabulary of the
12080 /// reader's outer-dispatch arm-set now binds at ONE typed `[char;
12081 /// 2]` array on the closed-set outer [`Sexp`] algebra rather than
12082 /// as two independent algebra constants (`Self::COMMENT_LEAD`,
12083 /// `Self::COMMENT_TERM`) accessed independently at every consumer
12084 /// that wants the paired-role shape. The shared `[char; 2]` shape
12085 /// with [`Self::LIST_DELIMITERS`] encodes the paired-role identity
12086 /// relation across the two axes of the SAME closed-set algebra at
12087 /// the type system level. THEORY.md §V.1 — knowable platform; the
12088 /// paired-discard-delimiter sub-vocabulary becomes load-bearing
12089 /// typed data on the closed-set outer [`Sexp`] algebra. THEORY.md
12090 /// §VI.1 — generation over composition; the paired-delimiter
12091 /// (opener + terminator) composition regenerates identically
12092 /// through this ONE typed forced-arity array rather than through
12093 /// two independent algebra constants at every consumer. THEORY.md
12094 /// §II.1 invariant 5 — composition preserves proofs; the two-axis
12095 /// (outer-structural, reader-discard) cross-product on the closed-
12096 /// set outer [`Sexp`] algebra now carries the SAME opener/closer
12097 /// discipline on BOTH axes through two forced-arity ALL arrays
12098 /// with byte-identical shape.
12099 pub const COMMENT_DELIMITERS: [char; 2] = [Self::COMMENT_LEAD, Self::COMMENT_TERM];
12100
12101 /// Closed-set forced-arity ALL array over the SEVEN non-whitespace
12102 /// category-leading chars the reader's outer-dispatch cascade
12103 /// specialises on — the reader-level boundary sub-vocabulary that
12104 /// paired with `char::is_whitespace()` closes the six-clause
12105 /// [`Self::is_bare_atom_boundary`] disjunction. Composes through
12106 /// seven typed `pub const` primitives spanning THREE type namespaces
12107 /// on the SAME reader-outer-dispatch axis of the substrate:
12108 /// * [`Self::LIST_OPEN`] (`'('`) — the outer-structural list-opening
12109 /// delimiter on the outer [`Sexp`] algebra;
12110 /// * [`Self::LIST_CLOSE`] (`')'`) — the outer-structural list-closing
12111 /// delimiter on the outer [`Sexp`] algebra;
12112 /// * [`QuoteForm::QUOTE_LEAD`] (`'\''`) — the [`QuoteForm::Quote`]
12113 /// reader-punctuation lead byte on the quote-family sub-algebra;
12114 /// * [`QuoteForm::QUASIQUOTE_LEAD`] (`` '`' ``) — the
12115 /// [`QuoteForm::Quasiquote`] reader-punctuation lead byte;
12116 /// * [`QuoteForm::UNQUOTE_LEAD`] (`','`) — the shared
12117 /// [`QuoteForm::Unquote`] / [`QuoteForm::UnquoteSplice`] reader-
12118 /// punctuation lead byte (disambiguated at the second-char peek
12119 /// via [`QuoteForm::promote_via_next_char`]);
12120 /// * [`Atom::STR_DELIMITER`] (`'"'`) — the Str-payload opening /
12121 /// closing delimiter on the closed-set [`Atom`] algebra;
12122 /// * [`Self::COMMENT_LEAD`] (`';'`) — the line-comment opener on
12123 /// the outer [`Sexp`] algebra (paired with [`Self::COMMENT_TERM`]
12124 /// inside the discard loop — but the TERM is a run-boundary
12125 /// marker inside a comment run, NOT a reader-outer-dispatch
12126 /// category-leading char, so it is intentionally omitted from
12127 /// this ALL array).
12128 ///
12129 /// Cross-axis peer to [`Self::LIST_DELIMITERS`] (`[char; 2]` on the
12130 /// outer-structural payload-delimiter axis) and [`Self::COMMENT_DELIMITERS`]
12131 /// (`[char; 2]` on the reader-discard opener/terminator axis) at
12132 /// ONE algebra level up: those two arrays close their respective
12133 /// paired-role sub-vocabularies (opener + closer, opener + terminator)
12134 /// on the reader-INNER axis of the outer [`Sexp`] algebra; this
12135 /// array closes the reader-OUTER-dispatch category-leading char
12136 /// sub-vocabulary across the SAME closed-set outer [`Sexp`] algebra
12137 /// PLUS its two sibling sub-algebras ([`QuoteForm`], [`Atom`]) at
12138 /// ONE family-wide `[char; 7]` primitive. Sibling-shape peer of the
12139 /// intra-algebra sub-vocabulary array [`QuoteForm::LEADS`]
12140 /// (`[char; 3]` — the three DISTINCT quote-family reader-lead
12141 /// bytes) which this array embeds as its middle three positions:
12142 /// where [`QuoteForm::LEADS`] closes the quote-family sub-carving's
12143 /// reader-lead sub-vocabulary at ONE forced-arity array on ONE
12144 /// closed-set algebra, this array closes the FULL reader-outer-
12145 /// dispatch non-whitespace category-leading char sub-vocabulary at
12146 /// ONE forced-arity array on the outer [`Sexp`] algebra by
12147 /// composing through the three-arm quote-family sub-carving + the
12148 /// two-arm structural-delimiter sub-carving + the two-arm atomic-
12149 /// carve delimiter + comment-lead singletons.
12150 ///
12151 /// Pre-lift the seven category-leading chars had NO family-wide
12152 /// array on the outer [`Sexp`] algebra — [`Self::is_bare_atom_boundary`]
12153 /// carried them as three sub-expressions (`Self::LIST_DELIMITERS.contains(&ch)`
12154 /// on the structural-delimiter axis, `QuoteForm::from_lead_char(ch).is_some()`
12155 /// on the quote-family axis, `ch == Atom::STR_DELIMITER || ch ==
12156 /// Self::COMMENT_LEAD` on the singleton axes) unified through boolean
12157 /// disjunction. Post-lift the WHOLE non-whitespace terminator sub-
12158 /// vocabulary binds at ONE `pub const [char; 7]` array on the outer
12159 /// [`Sexp`] algebra so [`Self::is_bare_atom_boundary`] collapses to
12160 /// `ch.is_whitespace() || Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch)`
12161 /// — TWO clauses (one whitespace-partial predicate + one
12162 /// contains-check on the family-wide ARRAY) rather than five
12163 /// sub-clauses spanning three type namespaces. Consumers keyed on
12164 /// the whole family (a `tatara-check` predicate `(check-reader-
12165 /// outer-dispatch-terminator-partition-injective …)` that verifies
12166 /// the seven-arm partition structurally, a future REPL / LSP
12167 /// tokenizer-boundary hint that scans the source for the reader-
12168 /// outer-dispatch category-leading chars upfront, a future
12169 /// completion generator that suggests the seven bytes at every
12170 /// bare-atom-lexeme insertion site) read through
12171 /// [`Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS`] without re-deriving
12172 /// the three-part sub-expression composition inline.
12173 ///
12174 /// Composition law (forward): for every `ch: char`,
12175 /// `Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch) ==
12176 /// (Self::LIST_DELIMITERS.contains(&ch) ||
12177 /// QuoteForm::from_lead_char(ch).is_some() ||
12178 /// ch == Atom::STR_DELIMITER || ch == Self::COMMENT_LEAD)` — pinned
12179 /// by `sexp_non_whitespace_bare_atom_terminators_agree_with_pre_lift_sub_expression_disjunction`.
12180 /// Boundary-predicate composition law:
12181 /// `Self::is_bare_atom_boundary(ch) == (ch.is_whitespace() ||
12182 /// Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch))` —
12183 /// pinned by `sexp_is_bare_atom_boundary_agrees_with_terminators_array_on_non_whitespace_partition`.
12184 ///
12185 /// Adding a hypothetical seventh reader-outer-dispatch category
12186 /// (e.g. `#|…|#` block-comment lead byte, `#\` char-literal prefix,
12187 /// `#[` vector-literal prefix — each pinning a new lead byte on
12188 /// [`Self`] or a fresh sub-algebra) extends this array AND
12189 /// [`Self::is_bare_atom_boundary`]'s indirect coverage in
12190 /// LOCKSTEP — rustc's forced-arity check on `[char; 7]` fails
12191 /// compilation if the algebra grows without the array (or the
12192 /// array without the algebra).
12193 ///
12194 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
12195 /// (reader-outer-dispatch category, canonical char) family-wide
12196 /// pairing binds at ONE typed `[char; 7]` array on the outer
12197 /// [`Sexp`] algebra regardless of which of the three sub-algebras
12198 /// the individual chars name their per-role `pub const` primitive
12199 /// on. THEORY.md §III — the typescape; the seven canonical
12200 /// reader-outer-dispatch category-leading bytes bind at ONE typed
12201 /// `[char; 7]` array on the outer [`Sexp`] algebra rather than at
12202 /// three-part sub-expression composition inline at
12203 /// [`Self::is_bare_atom_boundary`]. THEORY.md §V.1 — knowable
12204 /// platform; the family's cardinality becomes a TYPE-level constant
12205 /// on the substrate algebra rather than a per-consumer hand-rolled
12206 /// enumeration of the seven chars. THEORY.md §VI.1 — generation over
12207 /// composition; the family-wide contract sweeps (routing through
12208 /// typed sub-algebra `pub const` primitives, pairwise distinctness,
12209 /// agreement with the pre-lift sub-expression disjunction) emerge
12210 /// from the composition of EIGHT substrate primitives (this
12211 /// `pub const [char; 7]` array + the seven sub-algebra `pub const`
12212 /// primitives) rather than as inline disjunctions at each call site.
12213 pub const NON_WHITESPACE_BARE_ATOM_TERMINATORS: [char; 7] = [
12214 Self::LIST_OPEN,
12215 Self::LIST_CLOSE,
12216 QuoteForm::QUOTE_LEAD,
12217 QuoteForm::QUASIQUOTE_LEAD,
12218 QuoteForm::UNQUOTE_LEAD,
12219 Atom::STR_DELIMITER,
12220 Self::COMMENT_LEAD,
12221 ];
12222
12223 /// Reader-level boundary predicate — returns `true` iff `ch` is one
12224 /// of the SIX outer-dispatch category-leading chars the reader's
12225 /// tokenizer specialises on: whitespace, [`Self::LIST_OPEN`],
12226 /// [`Self::LIST_CLOSE`], any [`QuoteForm::lead_char`] (via the
12227 /// closed-set [`QuoteForm::from_lead_char`] decode),
12228 /// [`Atom::STR_DELIMITER`], AND [`Self::COMMENT_LEAD`]. The ONE
12229 /// typed projection on the outer [`Sexp`] algebra that names the
12230 /// disjunction of "the char would start a NEW reader-level token
12231 /// (or a discarded run) rather than feed the current bare-atom
12232 /// accumulator."
12233 ///
12234 /// Structural dual of the reader's outer-dispatch cascade in
12235 /// `crate::reader::tokenize`: the outer-dispatch has FIVE specific
12236 /// arms (`ws if ws.is_whitespace()`, `Self::COMMENT_LEAD`,
12237 /// `Self::LIST_OPEN`, `Self::LIST_CLOSE`, `Atom::STR_DELIMITER`)
12238 /// plus ONE pre-match `QuoteForm::from_lead_char(c).is_some()`
12239 /// gate — SIX categories in total. The default `_ => { …
12240 /// bare-atom accumulator … }` arm fires EXACTLY when every specific
12241 /// arm rejects. This method is the typed projection of that
12242 /// implicit disjunction: `Sexp::is_bare_atom_boundary(ch) == true`
12243 /// iff `ch` would trigger one of the SIX specific arms, and
12244 /// `false` iff `ch` would fall through to the bare-atom
12245 /// accumulator's default arm. The two consumer sites in
12246 /// `crate::reader::tokenize` — the outer-dispatch's implicit "no
12247 /// specific arm fires" residual predicate AND the bare-atom
12248 /// accumulator's terminator disjunct — now share ONE typed source
12249 /// of truth on the closed-set outer [`Sexp`] algebra.
12250 ///
12251 /// Pre-lift the SIX-clause boolean chain
12252 /// (`ch.is_whitespace() || ch == Sexp::LIST_OPEN || ch ==
12253 /// Sexp::LIST_CLOSE || QuoteForm::from_lead_char(ch).is_some() ||
12254 /// ch == Atom::STR_DELIMITER || ch == Sexp::COMMENT_LEAD`) lived
12255 /// inline at the bare-atom accumulator's terminator gate in
12256 /// `crate::reader::tokenize`, spanning THREE type namespaces
12257 /// ([`Sexp`], [`Atom`], [`QuoteForm`]) at ONE consumer site.
12258 /// Post-lift the WHOLE disjunction binds at ONE typed projection
12259 /// on the outer [`Sexp`] algebra so a refactor that adds a
12260 /// SEVENTH outer-dispatch category (e.g. `#|…|#` block-comment
12261 /// lead byte, `#\` char-literal prefix, `#[` vector-literal
12262 /// prefix) extends the algebra ONCE (via a new arm on THIS method
12263 /// AND a matching outer-dispatch arm in the reader) rather than
12264 /// mutating an inline six-clause boolean chain that would silently
12265 /// drift out of tokenizer agreement if one clause was added
12266 /// without the other. Sibling-shape peer of the outer-dispatch's
12267 /// closed-set per-category projections
12268 /// ([`QuoteForm::from_lead_char`] on the quote-family axis;
12269 /// [`Atom::decode_str_escape`] on the Str-escape axis): where those
12270 /// two methods each lift ONE outer-dispatch category's decode onto
12271 /// its typed algebra, THIS method lifts the DISJUNCTION of ALL SIX
12272 /// outer-dispatch categories onto the outer [`Sexp`] algebra as
12273 /// a bool predicate.
12274 ///
12275 /// Composition law (forward): for every char `ch` and every
12276 /// substrate-marker enumeration
12277 /// `Self::{LIST_OPEN, LIST_CLOSE, COMMENT_LEAD}`,
12278 /// `Atom::STR_DELIMITER`, `QuoteForm::from_lead_char(ch).is_some()`,
12279 /// `is_bare_atom_boundary` returns `true`; for every char that
12280 /// isn't whitespace AND isn't listed on any marker axis, returns
12281 /// `false`. Reader-level composition law:
12282 /// `read(format!("foo{ch}"))` tokenizes as `[Token::Atom("foo"),
12283 /// …trailing token(s) from `ch`]` when
12284 /// `Self::is_bare_atom_boundary(ch)` is `true`, and as
12285 /// `[Token::Atom(format!("foo{ch}"))]` (ONE token) when it is
12286 /// `false`.
12287 ///
12288 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
12289 /// (reader-level boundary role, canonical char) pairing binds at
12290 /// ONE typed projection on the outer [`Sexp`] algebra regardless
12291 /// of which of the SIX outer-dispatch category-leading chars is
12292 /// under test. THEORY.md §VI.1 — generation over composition; a
12293 /// SIX-clause inline boolean disjunction spanning THREE type
12294 /// namespaces collapses onto ONE named method — the substrate's
12295 /// three-times rule saturated at the outer-dispatch's disjunction.
12296 /// THEORY.md §V.1 — knowable platform; the canonical reader-level
12297 /// boundary predicate becomes a TYPE-level method on the outer
12298 /// substrate algebra rather than an inline six-clause boolean
12299 /// chain at ONE consumer site inside `crate::reader::tokenize`.
12300 #[must_use]
12301 pub fn is_bare_atom_boundary(ch: char) -> bool {
12302 ch.is_whitespace() || Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch)
12303 }
12304
12305 /// Canonical [`Self::Atom`]-[`Atom::Symbol`] outer constructor —
12306 /// composes [`Atom::symbol`] (the typed-construct method on the
12307 /// closed-set [`Atom`] algebra) under the [`Self::Atom`] outer
12308 /// wrapper. The first of six `Self::Atom(Atom::X(_))` outer
12309 /// constructors all routing through the typed [`Atom`] construct
12310 /// family at the inner algebra so the `.into()` coercion + tuple-
12311 /// variant constructor pair lives at ONE site per kind on the
12312 /// [`Atom`] algebra rather than at this outer constructor's body.
12313 /// Sibling-shape lift to the [`Atom::as_X`] /
12314 /// [`Self::as_X`] composition through [`Self::as_atom`] on the
12315 /// projection axis: where projections route OUTER `Self::as_X`
12316 /// through `self.as_atom().and_then(Atom::as_X)`, constructions
12317 /// route OUTER `Self::X` through `Self::Atom(Atom::X(payload))`.
12318 ///
12319 /// Composition law (forward): `Sexp::symbol(s) ==
12320 /// Sexp::Atom(Atom::symbol(s))` for every `s: impl Into<String>`.
12321 /// Round-trip law (with the soft-projection sibling): for every
12322 /// `s: &str`, `Sexp::symbol(s).as_symbol() == Some(s)` — the inner
12323 /// algebra's section-for-retraction surfaces through the outer
12324 /// algebra without re-derivation. Same posture across the six
12325 /// sibling pairs.
12326 #[must_use]
12327 pub fn symbol(s: impl Into<String>) -> Self {
12328 Self::Atom(Atom::symbol(s))
12329 }
12330 /// Canonical [`Self::Atom`]-[`Atom::Keyword`] outer constructor —
12331 /// composes [`Atom::keyword`] under [`Self::Atom`]. See
12332 /// [`Self::symbol`] for the outer-algebra docstring.
12333 #[must_use]
12334 pub fn keyword(s: impl Into<String>) -> Self {
12335 Self::Atom(Atom::keyword(s))
12336 }
12337 /// Canonical [`Self::Atom`]-[`Atom::Str`] outer constructor —
12338 /// composes [`Atom::string`] under [`Self::Atom`].
12339 #[must_use]
12340 pub fn string(s: impl Into<String>) -> Self {
12341 Self::Atom(Atom::string(s))
12342 }
12343 /// Canonical [`Self::Atom`]-[`Atom::Int`] outer constructor —
12344 /// composes [`Atom::int`] under [`Self::Atom`].
12345 #[must_use]
12346 pub fn int(n: i64) -> Self {
12347 Self::Atom(Atom::int(n))
12348 }
12349 /// Canonical [`Self::Atom`]-[`Atom::Float`] outer constructor —
12350 /// composes [`Atom::float`] under [`Self::Atom`].
12351 #[must_use]
12352 pub fn float(n: f64) -> Self {
12353 Self::Atom(Atom::float(n))
12354 }
12355 /// Canonical [`Self::Atom`]-[`Atom::Bool`] outer constructor —
12356 /// composes [`Atom::boolean`] under [`Self::Atom`].
12357 #[must_use]
12358 pub fn boolean(b: bool) -> Self {
12359 Self::Atom(Atom::boolean(b))
12360 }
12361
12362 /// Canonical [`Self::Quote`] outer constructor — composes
12363 /// [`QuoteForm::wrap`] on the [`QuoteForm::Quote`] marker so the
12364 /// `Box::new(inner)` allocation + tuple-variant pair lives at ONE
12365 /// site on the closed-set [`QuoteForm`] algebra rather than at
12366 /// this outer-constructor body. The first of four `Self::Quote*`
12367 /// outer constructors all routing through the typed
12368 /// [`QuoteForm::wrap`] family at the inner algebra — the
12369 /// quote-family-axis section peer of the six `Self::Atom(Atom::X(_))`
12370 /// outer constructors ([`Self::symbol`], [`Self::keyword`],
12371 /// [`Self::string`], [`Self::int`], [`Self::float`],
12372 /// [`Self::boolean`]) all routing through the typed [`Atom`]
12373 /// construct family on the atomic-payload axis. Sibling-shape lift
12374 /// to the [`Self::as_quote_form`] soft-projection sibling on the
12375 /// projection axis: where the projection soft-decomposes a
12376 /// quote-family wrapper into `Option<(QuoteForm, &Sexp)>` (surfacing
12377 /// the typed marker alongside the borrowed inner body), each of
12378 /// these four typed constructors embeds a fresh inner body under
12379 /// the typed marker into the matching tuple-variant wrapper.
12380 ///
12381 /// Composition law (forward): `Sexp::quote(inner) ==
12382 /// QuoteForm::Quote.wrap(inner) == Sexp::Quote(Box::new(inner))`
12383 /// for every `inner: Sexp`. Round-trip law (section-for-retraction
12384 /// with the soft-projection sibling): `Sexp::quote(inner)
12385 /// .as_quote_form() == Some((QuoteForm::Quote, &inner))` for every
12386 /// `inner: Sexp` — the inner algebra's typed constructor pairs
12387 /// section-for-retraction with the outer algebra's soft
12388 /// projection, and the marker + inner body cross-projection
12389 /// preserves identity. Same posture across the four sibling
12390 /// pairs (`Sexp::quote` / `Sexp::quasiquote` / `Sexp::unquote` /
12391 /// `Sexp::unquote_splice`).
12392 ///
12393 /// Pre-lift the `Self::Quote(Box::new(inner))` welded triple
12394 /// (`Self::Quote`, `Box::new`, `inner`) appeared inline at every
12395 /// consumer that builds a quote-family wrapper — well past the ≥2
12396 /// PRIME-DIRECTIVE trigger once the structural shape is named. The
12397 /// welded triple already lives at ONE site on the closed-set
12398 /// [`QuoteForm::wrap`] algebra for the marker-driven consumer path;
12399 /// this outer constructor binds the per-variant `Sexp::X(Box::new(
12400 /// inner))` welded triple to ONE typed-algebra method per marker on
12401 /// the outer [`Sexp`] algebra, so consumers that know the marker at
12402 /// compile time bind to the typed method directly rather than
12403 /// re-deriving the `Self::X(Box::new(_))` pair inline. A future
12404 /// allocation-policy change (e.g. arena-allocated wrappers for
12405 /// span-aware [`Sexp`]) lands as ONE edit at [`QuoteForm::wrap`]
12406 /// (the single site the allocation composition lives) and
12407 /// propagates through these four typed constructors byte-for-byte.
12408 ///
12409 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
12410 /// (QuoteForm variant, [`Sexp`] tuple-variant constructor) pairing
12411 /// binds at ONE typed-algebra method per marker on the outer
12412 /// [`Sexp`] algebra regardless of which consumer reaches in.
12413 /// THEORY.md §VI.1 — generation over composition; the welded
12414 /// `Self::X(Box::new(_))` triple at every quote-family construct
12415 /// site regenerates through `QuoteForm::X.wrap(_)` composition over
12416 /// the typed algebra rather than per-site re-derivation. THEORY.md
12417 /// §V.1 — knowable platform; the typed-construct family becomes a
12418 /// TYPE projection on the substrate's outer [`Sexp`] algebra sitting
12419 /// next to the typed-project family [`Self::as_quote_form`] rather
12420 /// than as bare tuple-variant constructor + per-site `Box::new`
12421 /// discipline. A future fifth homoiconic prefix syntax (e.g. syntax
12422 /// quotation `#'x` for hygienic macros) extends [`QuoteForm::ALL`] +
12423 /// [`QuoteForm::wrap`]'s arm + this construct family in lockstep,
12424 /// rustc-enforced through the closed-set exhaustiveness.
12425 ///
12426 /// Frontier inspiration: Racket's `(quote x)` /
12427 /// `(quasiquote x)` / `(unquote x)` / `(unquote-splicing x)` typed
12428 /// syntactic-form construct face paired one-for-one with the
12429 /// [`Self::as_quote_form`] closed-set soft-projection sibling on
12430 /// the outer syntax algebra — the typed-construct + typed-project
12431 /// algebra dual is closed at one method per direction per marker
12432 /// on Racket's surface, and the [`Self::quote`] /
12433 /// [`Self::quasiquote`] / [`Self::unquote`] / [`Self::unquote_splice`]
12434 /// family is the Rust-typed peer on the closed-set outer [`Sexp`]
12435 /// algebra with [`QuoteForm::wrap`] standing in for Racket's typed
12436 /// dispatch face. MLIR's `mlir::OpBuilder::create<QuoteOp>(loc,
12437 /// inner)` typed-IR wrapper construction paired with
12438 /// `mlir::dyn_cast<QuoteOp>(op)` on the projection face — the typed
12439 /// factory + typed downcast pair the IR algebra closes over on
12440 /// every wrapper op; [`Self::quote`] / [`Self::as_quote_form`] is
12441 /// the Rust-typed peer on the outer [`Sexp`] algebra with the
12442 /// closed-set [`QuoteForm`] standing in for MLIR's `OperationName`
12443 /// taxonomy over the wrapper-op family.
12444 #[must_use]
12445 pub fn quote(inner: Sexp) -> Self {
12446 QuoteForm::Quote.wrap(inner)
12447 }
12448 /// Canonical [`Self::Quasiquote`] outer constructor — composes
12449 /// [`QuoteForm::wrap`] on the [`QuoteForm::Quasiquote`] marker.
12450 /// See [`Self::quote`] for the outer-algebra docstring.
12451 #[must_use]
12452 pub fn quasiquote(inner: Sexp) -> Self {
12453 QuoteForm::Quasiquote.wrap(inner)
12454 }
12455 /// Canonical [`Self::Unquote`] outer constructor — composes
12456 /// [`QuoteForm::wrap`] on the [`QuoteForm::Unquote`] marker.
12457 /// See [`Self::quote`] for the outer-algebra docstring.
12458 #[must_use]
12459 pub fn unquote(inner: Sexp) -> Self {
12460 QuoteForm::Unquote.wrap(inner)
12461 }
12462 /// Canonical [`Self::UnquoteSplice`] outer constructor — composes
12463 /// [`QuoteForm::wrap`] on the [`QuoteForm::UnquoteSplice`] marker.
12464 /// See [`Self::quote`] for the outer-algebra docstring.
12465 #[must_use]
12466 pub fn unquote_splice(inner: Sexp) -> Self {
12467 QuoteForm::UnquoteSplice.wrap(inner)
12468 }
12469
12470 /// Canonical marker-driven quote-family outer constructor — routes
12471 /// through [`QuoteForm::wrap`] on the caller-supplied [`QuoteForm`]
12472 /// marker at ONE site on the closed-set [`Sexp`] algebra. The outer-
12473 /// algebra section-for-retraction sibling of the existing
12474 /// [`Self::as_quote_form`] soft-projection ([`Option<(QuoteForm,
12475 /// &Sexp)>`]): where the projection soft-decomposes a quote-family
12476 /// wrapper into its typed [`QuoteForm`] marker + borrowed inner body
12477 /// on the (marker, borrowed-inner) product, this constructor embeds
12478 /// a typed [`QuoteForm`] marker + owned inner body pair into the
12479 /// matching tuple-variant wrapper on the (marker, owned-inner)
12480 /// product. Marker-driven parent of the four per-variant siblings
12481 /// [`Self::quote`] / [`Self::quasiquote`] / [`Self::unquote`] /
12482 /// [`Self::unquote_splice`] — each of the four is `Self::quote_form(
12483 /// QuoteForm::X, inner)` restricted to a compile-time-known marker;
12484 /// this constructor is the marker-abstracted parent every consumer
12485 /// that binds the marker as a runtime value routes through.
12486 ///
12487 /// Sibling posture across the outer-algebra construct-family layer:
12488 /// where [`Self::call`](Self::call) and [`Self::named_call`](Self::named_call)
12489 /// close the (construct, project) dual on the call-form + named-
12490 /// call-form typed decompositions of the residual-axis List arm,
12491 /// and [`Self::list`](Self::list) closes it on the residual-axis
12492 /// List arm itself, this constructor closes it on the quote-family-
12493 /// axis wrapper decomposition — the outer [`Sexp`] algebra now
12494 /// carries a (marker, project) construct-family dual pair for every
12495 /// axis of the [`SexpShape`] closed set at ONE typed method per
12496 /// corner, with `Sexp::quote_form(qf, inner)` as the marker-driven
12497 /// quote-family construct entry and [`Self::as_quote_form`] as its
12498 /// marker-recovering projection sibling.
12499 ///
12500 /// Composition law (forward): `Sexp::quote_form(marker, inner) ==
12501 /// marker.wrap(inner)` for every `marker: QuoteForm` and every
12502 /// `inner: Sexp`. The body routes through the SAME closed-set
12503 /// `QuoteForm::wrap` method the four per-variant siblings
12504 /// ([`Self::quote`] / [`Self::quasiquote`] / [`Self::unquote`] /
12505 /// [`Self::unquote_splice`]) already reach for, so the (marker,
12506 /// [`Sexp`] tuple-variant constructor) pairing binds at ONE closed-
12507 /// set match on the substrate algebra — a regression that drifts
12508 /// one consumer's marker→wrapper mapping from the others (e.g. a
12509 /// copy-edit that pairs [`QuoteForm::Quote`] with the
12510 /// [`Sexp::Quasiquote`] tuple variant, or that drops a
12511 /// [`QuoteForm::UnquoteSplice`] value through the
12512 /// [`Sexp::Unquote`] tuple variant) cannot reach the substrate's
12513 /// runtime.
12514 ///
12515 /// Round-trip law (section-for-retraction with the outer-algebra
12516 /// soft-projection): for every `marker: QuoteForm` and every
12517 /// `inner: Sexp`, `Sexp::quote_form(marker, inner.clone())
12518 /// .as_quote_form() == Some((marker, &inner))` — the outer
12519 /// algebra's marker-driven quote-family constructor pairs section-
12520 /// for-retraction with the outer algebra's soft quote-family
12521 /// projection, and the (marker, inner body) cross-projection
12522 /// preserves identity for every `QuoteForm` variant.
12523 ///
12524 /// Marker-recovering projection composition: `Sexp::quote_form(
12525 /// marker, inner).as_quote_form_marker() == Some(marker)` for every
12526 /// input — the marker-only projection sibling
12527 /// ([`Self::as_quote_form_marker`]) recovers the constructor's
12528 /// marker byte-for-byte. Outer-shape composition law:
12529 /// `Sexp::quote_form(marker, inner).shape() == marker.sexp_shape()`
12530 /// — the outer-shape identity binds through the typed-shape lattice
12531 /// at ONE arm per [`QuoteForm`] variant, symmetric with the atomic
12532 /// construct family's `Sexp::X_atom(payload).shape() ==
12533 /// AtomKind::X.sexp_shape()` composition and the residual construct
12534 /// family's `Sexp::list(items).shape() == SexpShape::List`
12535 /// composition.
12536 ///
12537 /// Per-variant restriction laws (structural identity between the
12538 /// marker-driven parent + the four per-variant siblings):
12539 /// * `Sexp::quote_form(QuoteForm::Quote, inner) == Sexp::quote(inner)`
12540 /// * `Sexp::quote_form(QuoteForm::Quasiquote, inner) == Sexp::quasiquote(inner)`
12541 /// * `Sexp::quote_form(QuoteForm::Unquote, inner) == Sexp::unquote(inner)`
12542 /// * `Sexp::quote_form(QuoteForm::UnquoteSplice, inner) == Sexp::unquote_splice(inner)`
12543 ///
12544 /// The four per-variant constructors ARE the marker-driven parent
12545 /// specialized on a compile-time-known marker; the marker-abstracted
12546 /// parent binds every consumer that routes a runtime `QuoteForm`
12547 /// value through a quote-family construct to ONE typed method on
12548 /// the outer [`Sexp`] algebra rather than a four-arm inline
12549 /// `match qf { QuoteForm::X => Sexp::x(inner), … }` dispatch.
12550 ///
12551 /// Pre-lift consumers with a runtime `QuoteForm` marker routed
12552 /// through `marker.wrap(inner)` directly (the reader's
12553 /// `read_quoted` production consumer at `reader.rs`, the domain
12554 /// module's quote-family round-trip test site) — well past the ≥2
12555 /// PRIME-DIRECTIVE trigger once the marker-driven pattern is
12556 /// named. Post-lift consumers bind to ONE typed-algebra method on
12557 /// the outer [`Sexp`] algebra sitting next to the typed-project
12558 /// family ([`Self::as_quote_form`] / [`Self::as_quote_form_marker`])
12559 /// rather than reaching into the closed-set [`QuoteForm::wrap`]
12560 /// method directly. A future allocation-policy change (e.g. arena-
12561 /// allocated wrappers for span-aware [`Sexp`]) lands as ONE edit at
12562 /// the single [`QuoteForm::wrap`] composition site and propagates
12563 /// through this constructor byte-for-byte.
12564 ///
12565 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
12566 /// (typed [`QuoteForm`] marker, owned inner body, [`QuoteForm::wrap`]
12567 /// composition) triple binds at ONE typed-algebra method on the
12568 /// outer [`Sexp`] algebra, closing the marker-driven quote-family
12569 /// (construct, project) algebra dual pair with
12570 /// [`Self::as_quote_form`] on the projection side. THEORY.md §II.1
12571 /// invariant 2 — free middle; every consumer that has a runtime
12572 /// [`QuoteForm`] marker + an owned inner body and wants to build a
12573 /// quote-family wrapper routes through the SAME typed method, so a
12574 /// regression that drifts one consumer's marker→wrapper mapping
12575 /// cannot reach the substrate's runtime. THEORY.md §V.1 — knowable
12576 /// platform; the marker-driven quote-family typed-construct becomes
12577 /// a TYPE projection on the substrate's outer [`Sexp`] algebra
12578 /// sitting next to the typed-project family
12579 /// [`Self::as_quote_form`] / [`Self::as_quote_form_marker`] rather
12580 /// than the closed-set [`QuoteForm::wrap`] method threaded as a
12581 /// method call on a bound-marker value. THEORY.md §VI.1 —
12582 /// generation over composition; the marker-driven quote-family
12583 /// pair emerges from ONE typed-algebra composition through
12584 /// [`QuoteForm::wrap`] rather than from per-consumer marker→wrapper
12585 /// dispatch literals; a future fifth homoiconic prefix syntax
12586 /// (e.g. `#'x` for hygienic macros) extends [`QuoteForm::ALL`] +
12587 /// [`QuoteForm::wrap`]'s match arm + [`Self::as_quote_form`]'s
12588 /// match arm in lockstep — rustc-enforced through the closed-set
12589 /// exhaustiveness — with THIS constructor inheriting the extension
12590 /// through the [`QuoteForm::wrap`] composition site without a per-
12591 /// site edit.
12592 ///
12593 /// Frontier inspiration: Racket's `(datum->syntax stx (list #'qf
12594 /// inner))` marker-abstracted quote-family construct paired one-
12595 /// for-one with `syntax-e` on the projection face — the typed-
12596 /// construct + typed-project algebra dual is closed on Racket's
12597 /// syntax algebra at one method per direction, and
12598 /// `Sexp::quote_form` / `Sexp::as_quote_form` is the Rust-typed peer
12599 /// on the closed-set outer [`Sexp`] algebra with [`QuoteForm`]
12600 /// standing in for Racket's syntactic-form taxonomy over the four
12601 /// homoiconic prefix wrappers. MLIR's typed-IR
12602 /// `mlir::OpBuilder::create(loc, OperationName, operands)` marker-
12603 /// driven op construction paired with `mlir::Operation::getName()`
12604 /// on the projection face — the typed factory + typed downcast pair
12605 /// the IR algebra closes over on every op kind at one method per
12606 /// direction; `Sexp::quote_form` / [`Self::as_quote_form_marker`]
12607 /// is the Rust-typed peer on the outer [`Sexp`] algebra with the
12608 /// closed-set [`QuoteForm`] standing in for MLIR's `OperationName`
12609 /// taxonomy over the four homoiconic prefix-wrapper op kinds.
12610 #[must_use]
12611 pub fn quote_form(marker: QuoteForm, inner: Sexp) -> Self {
12612 marker.wrap(inner)
12613 }
12614
12615 /// Canonical marker-driven template-substitution outer constructor —
12616 /// routes through [`UnquoteForm::wrap`] on the caller-supplied
12617 /// [`UnquoteForm`] marker at ONE site on the closed-set [`Sexp`]
12618 /// algebra. Subset-algebra peer of the marker-driven quote-family
12619 /// parent [`Self::quote_form`]: where [`Self::quote_form`] embeds a
12620 /// caller-supplied [`QuoteForm`] marker + owned inner body on the
12621 /// 4-of-12 quote-family carving through the closed-set
12622 /// [`QuoteForm::wrap`] composition site, THIS constructor embeds a
12623 /// caller-supplied [`UnquoteForm`] marker + owned inner body on the
12624 /// 2-of-12 template-substitution subset carving through the
12625 /// [`UnquoteForm::wrap`] composition site (which itself composes
12626 /// [`UnquoteForm::to_quote_form`] then [`QuoteForm::wrap`], so the
12627 /// welded `Sexp::X(Box::new(_))` triple ultimately still binds at
12628 /// the ONE canonical [`QuoteForm::wrap`] site the four per-variant
12629 /// siblings [`Self::quote`] / [`Self::quasiquote`] / [`Self::unquote`]
12630 /// / [`Self::unquote_splice`] and the marker-driven parent
12631 /// [`Self::quote_form`] all route through). Closes the (construct,
12632 /// project) algebra dual on the (`UnquoteForm`, `Sexp`) product
12633 /// against the pre-existing projection sibling [`Self::as_unquote`]
12634 /// (soft-decomposition into `Option<(UnquoteForm, &Sexp)>`) and its
12635 /// marker-only peer [`Self::as_unquote_form`] (soft-decomposition
12636 /// into `Option<UnquoteForm>`) — post-lift the outer [`Sexp`]
12637 /// algebra carries a marker-driven (construct, project) dual pair
12638 /// `Sexp::unquote_form` / `Sexp::as_unquote` at ONE typed method per
12639 /// direction on the template-substitution subset alongside the
12640 /// marker-only projection sibling [`Self::as_unquote_form`],
12641 /// symmetric with the pair `Sexp::quote_form` / `Sexp::as_quote_form`
12642 /// / `Sexp::as_quote_form_marker` the superset carries.
12643 ///
12644 /// Sibling posture across the outer-algebra construct-family layer:
12645 /// where [`Self::call`](Self::call) and [`Self::named_call`](Self::named_call)
12646 /// close the (construct, project) dual on the call-form + named-call-
12647 /// form typed decompositions of the residual-axis List arm,
12648 /// [`Self::list`](Self::list) closes it on the residual-axis List
12649 /// arm itself, and [`Self::quote_form`](Self::quote_form) closes it
12650 /// on the quote-family-axis marker-driven decomposition (the parent
12651 /// 4-of-12 quote-family carving), THIS constructor closes it on the
12652 /// template-substitution-subset marker-driven decomposition (the
12653 /// 2-of-4 subset of the quote-family carving, equivalently the
12654 /// 2-of-12 substitution carving of the outer [`SexpShape`] closed
12655 /// set) — the outer [`Sexp`] algebra now carries a marker-driven
12656 /// construct-family dual pair for every closed-set carving on the
12657 /// quote-family axis at ONE typed method per corner.
12658 ///
12659 /// Composition law (forward): `Sexp::unquote_form(marker, inner) ==
12660 /// marker.wrap(inner)` for every `marker: UnquoteForm` and every
12661 /// `inner: Sexp`. The body routes through the SAME
12662 /// [`UnquoteForm::wrap`] method the subset-algebra consumer path
12663 /// already reaches for, so the (subset marker, [`Sexp`] tuple-variant
12664 /// wrapper) pairing binds at ONE closed-set composition site on the
12665 /// substrate — a regression that drifts one consumer's subset
12666 /// marker → wrapper mapping from the others (e.g. a copy-edit that
12667 /// pairs [`UnquoteForm::Unquote`] with the [`Sexp::UnquoteSplice`]
12668 /// tuple variant, or that drops a [`UnquoteForm::Splice`] value
12669 /// through the [`Sexp::Unquote`] tuple variant) cannot reach the
12670 /// substrate's runtime.
12671 ///
12672 /// Round-trip law (section-for-retraction with the outer-algebra
12673 /// soft-projection): for every `marker: UnquoteForm` and every
12674 /// `inner: Sexp`, `Sexp::unquote_form(marker, inner.clone())
12675 /// .as_unquote() == Some((marker, &inner))` — the outer algebra's
12676 /// marker-driven template-substitution constructor pairs section-
12677 /// for-retraction with the outer algebra's soft template-substitution
12678 /// projection, and the (subset marker, inner body) cross-projection
12679 /// preserves identity for every [`UnquoteForm`] variant.
12680 ///
12681 /// Marker-recovering projection composition: `Sexp::unquote_form(
12682 /// marker, inner).as_unquote_form() == Some(marker)` for every input
12683 /// — the marker-only projection sibling [`Self::as_unquote_form`]
12684 /// recovers the constructor's subset marker byte-for-byte. Outer-
12685 /// shape composition law: `Sexp::unquote_form(marker, inner).shape()
12686 /// == marker.sexp_shape()` — the outer-shape identity binds through
12687 /// the typed-shape lattice at ONE arm per [`UnquoteForm`] variant,
12688 /// symmetric with the quote-family construct family's
12689 /// `Sexp::quote_form(marker, inner).shape() == marker.sexp_shape()`
12690 /// composition and the atomic construct family's
12691 /// `Sexp::X_atom(payload).shape() == AtomKind::X.sexp_shape()`
12692 /// composition. Superset-routing composition law:
12693 /// `Sexp::unquote_form(marker, inner) == Sexp::quote_form(
12694 /// marker.to_quote_form(), inner)` for every input — the subset-
12695 /// algebra construct routes through the same closed-set
12696 /// [`QuoteForm::wrap`] composition site the superset construct
12697 /// routes through, threaded via the typed 2-of-4 subset → superset
12698 /// projection [`UnquoteForm::to_quote_form`]. A regression that
12699 /// drifts either direction of this composition fails at the
12700 /// superset-routing pin.
12701 ///
12702 /// Per-variant restriction laws (structural identity between the
12703 /// marker-driven parent + the two per-variant siblings):
12704 /// * `Sexp::unquote_form(UnquoteForm::Unquote, inner) == Sexp::unquote(inner)`
12705 /// * `Sexp::unquote_form(UnquoteForm::Splice, inner) == Sexp::unquote_splice(inner)`
12706 ///
12707 /// The two per-variant constructors ARE the marker-driven parent
12708 /// specialized on a compile-time-known subset marker; the marker-
12709 /// abstracted parent binds every consumer that routes a runtime
12710 /// [`UnquoteForm`] value through a template-substitution construct
12711 /// to ONE typed method on the outer [`Sexp`] algebra rather than a
12712 /// two-arm inline `match uf { UnquoteForm::Unquote => Sexp::unquote(
12713 /// inner), UnquoteForm::Splice => Sexp::unquote_splice(inner) }`
12714 /// dispatch.
12715 ///
12716 /// Pre-lift consumers with a runtime `UnquoteForm` marker routed
12717 /// through `marker.wrap(inner)` directly (reaching into the
12718 /// [`UnquoteForm::wrap`] subset-algebra method) OR through the two-
12719 /// step `Sexp::quote_form(marker.to_quote_form(), inner)`
12720 /// composition (routing via the superset marker-driven parent).
12721 /// Post-lift consumers bind to ONE typed-algebra method on the outer
12722 /// [`Sexp`] algebra sitting next to the typed-project family
12723 /// ([`Self::as_unquote`] / [`Self::as_unquote_form`]) rather than
12724 /// reaching into the closed-set [`UnquoteForm::wrap`] method
12725 /// directly or composing the superset parent with the subset →
12726 /// superset projection. A future allocation-policy change (e.g.
12727 /// arena-allocated wrappers for span-aware [`Sexp`]) lands as ONE
12728 /// edit at the single [`QuoteForm::wrap`] composition site and
12729 /// propagates through this constructor byte-for-byte (via the
12730 /// [`UnquoteForm::wrap`] composition).
12731 ///
12732 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
12733 /// (typed [`UnquoteForm`] marker, owned inner body,
12734 /// [`UnquoteForm::wrap`] composition) triple binds at ONE typed-
12735 /// algebra method on the outer [`Sexp`] algebra, closing the marker-
12736 /// driven template-substitution (construct, project) algebra dual
12737 /// pair with [`Self::as_unquote`] on the projection side. THEORY.md
12738 /// §II.1 invariant 2 — free middle; every consumer that has a
12739 /// runtime [`UnquoteForm`] marker + an owned inner body and wants to
12740 /// build a template-substitution wrapper routes through the SAME
12741 /// typed method, so a regression that drifts one consumer's subset
12742 /// marker → wrapper mapping cannot reach the substrate's runtime.
12743 /// THEORY.md §V.1 — knowable platform; the marker-driven template-
12744 /// substitution typed-construct becomes a TYPE projection on the
12745 /// substrate's outer [`Sexp`] algebra sitting next to the typed-
12746 /// project family [`Self::as_unquote`] / [`Self::as_unquote_form`]
12747 /// rather than the closed-set [`UnquoteForm::wrap`] method threaded
12748 /// as a method call on a bound-marker value or the two-step subset
12749 /// → superset then [`Self::quote_form`] composition. THEORY.md
12750 /// §VI.1 — generation over composition; the marker-driven template-
12751 /// substitution pair emerges from ONE typed-algebra composition
12752 /// through [`UnquoteForm::wrap`] rather than from per-consumer
12753 /// subset-marker → wrapper dispatch literals; a future third
12754 /// template-substitution marker (e.g. a `,~` reverse-unquote)
12755 /// extends [`UnquoteForm::ALL`] + [`UnquoteForm::to_quote_form`]'s
12756 /// dispatch table in lockstep — rustc-enforced through the closed-
12757 /// set exhaustiveness — with THIS constructor inheriting the
12758 /// extension through the [`UnquoteForm::wrap`] composition site
12759 /// without a per-site edit.
12760 ///
12761 /// Frontier inspiration: Racket's `(datum->syntax stx (list #'uf
12762 /// inner))` marker-abstracted template-substitution construct
12763 /// restricted to the substitution-subset of syntactic-form kinds,
12764 /// paired one-for-one with `syntax-e` on the projection face — the
12765 /// typed-construct + typed-project algebra dual is closed on
12766 /// Racket's syntax algebra at one method per direction per subset,
12767 /// and `Sexp::unquote_form` / `Sexp::as_unquote` is the Rust-typed
12768 /// peer on the closed-set outer [`Sexp`] algebra with
12769 /// [`UnquoteForm`] standing in for Racket's substitution-subset
12770 /// syntactic-form taxonomy. MLIR's typed factory
12771 /// `mlir::OpBuilder::create<UnquoteFamilyOp>(loc, marker, operands)`
12772 /// paired with the projection sibling
12773 /// `mlir::dyn_cast<UnquoteFamilyOp>(op)` — the typed factory + typed
12774 /// downcast pair the IR algebra closes over on every op-family
12775 /// subset at one method per direction; `Sexp::unquote_form` /
12776 /// [`Self::as_unquote_form`] is the Rust-typed peer on the outer
12777 /// [`Sexp`] algebra with the closed-set [`UnquoteForm`] standing in
12778 /// for MLIR's `OperationName` subset taxonomy over the template-
12779 /// substitution op family.
12780 #[must_use]
12781 pub fn unquote_form(marker: UnquoteForm, inner: Sexp) -> Self {
12782 marker.wrap(inner)
12783 }
12784
12785 pub fn is_list(&self) -> bool {
12786 matches!(self, Self::List(_))
12787 }
12788 pub fn as_list(&self) -> Option<&[Sexp]> {
12789 match self {
12790 Self::List(xs) => Some(xs),
12791 _ => None,
12792 }
12793 }
12794
12795 /// Canonical [`Self::List`] outer constructor — collects an
12796 /// `impl IntoIterator<Item = Sexp>` into the tuple-variant payload
12797 /// `Vec<Sexp>` at ONE site on the closed-set [`Sexp`] algebra. The
12798 /// residual-axis section-for-retraction sibling of the existing
12799 /// [`Self::as_list`] soft-projection ([`Option<&[Sexp]>`]): where
12800 /// the projection soft-decomposes a [`Self::List`] arm into its
12801 /// borrowed inner slice, this constructor embeds a fresh owned
12802 /// item sequence into the matching tuple-variant wrapper. Sibling
12803 /// of the atomic-payload construct family ([`Self::symbol`],
12804 /// [`Self::keyword`], [`Self::string`], [`Self::int`],
12805 /// [`Self::float`], [`Self::boolean`] — all routing through the
12806 /// typed [`Atom`] construct family on the 6-of-12 atomic-payload
12807 /// carving) and the quote-family construct family ([`Self::quote`],
12808 /// [`Self::quasiquote`], [`Self::unquote`], [`Self::unquote_splice`]
12809 /// — all routing through the typed [`QuoteForm::wrap`] family on
12810 /// the 4-of-12 quote-family carving); closes the (construct,
12811 /// project) algebra dual on the third and final structural carving
12812 /// of the outer [`Sexp`] closed set — the 2-of-12 residual axis
12813 /// covering [`Self::Nil`] and [`Self::List`]. [`Self::Nil`] is a
12814 /// unit variant carrying no payload — the residual-axis
12815 /// construct family closes at ONE constructor (this method) for
12816 /// the sole payload-bearing residual arm.
12817 ///
12818 /// Composition law (forward): `Sexp::list(items) ==
12819 /// Sexp::List(items.into_iter().collect::<Vec<Sexp>>())` for every
12820 /// `items: impl IntoIterator<Item = Sexp>`. Round-trip law
12821 /// (section-for-retraction with the soft-projection sibling): for
12822 /// every `items: Vec<Sexp>`, `Sexp::list(items.clone()).as_list()
12823 /// == Some(items.as_slice())` — the outer algebra's typed
12824 /// constructor pairs section-for-retraction with the outer
12825 /// algebra's soft projection, and the borrowed-slice cross-
12826 /// projection preserves identity. Sibling posture across the
12827 /// three axis-construct families on the outer [`Sexp`] algebra
12828 /// (atomic + quote-family + residual).
12829 ///
12830 /// Outer-shape composition law: `Sexp::list(items).shape() ==
12831 /// SexpShape::List` for every `items: impl IntoIterator<Item =
12832 /// Sexp>` — the residual-arm outer-shape identity binds through
12833 /// the typed-shape lattice at ONE arm, symmetric with the
12834 /// quote-family construct family's outer-shape composition
12835 /// `Sexp::X_variant(inner).shape() == QuoteForm::X.sexp_shape()`
12836 /// and the atomic construct family's `Sexp::X_atom(payload).shape()
12837 /// == AtomKind::X.sexp_shape()`. Structural-carving-marker
12838 /// composition law: `Sexp::list(items).as_structural_kind() ==
12839 /// Some(StructuralKind::List)` for every `items: impl
12840 /// IntoIterator<Item = Sexp>` — the residual-axis carving marker
12841 /// binds through the closed-set [`StructuralKind`] algebra at ONE
12842 /// arm, symmetric with the atomic-axis's `Sexp::X_atom(payload)
12843 /// .as_atom_kind() == Some(AtomKind::X)` marker composition.
12844 ///
12845 /// Pre-lift the [`Self::List(Vec<Sexp>)`] welded pair
12846 /// ([`Self::List`] tuple-variant constructor + `Vec<Sexp>`
12847 /// payload) appeared inline at every consumer that builds a
12848 /// list-shaped [`Sexp`] value — well past the ≥2 PRIME-DIRECTIVE
12849 /// trigger once the structural shape is named. Post-lift the
12850 /// welded pair binds at ONE typed-algebra method on the outer
12851 /// [`Sexp`] algebra with an `impl IntoIterator<Item = Sexp>`
12852 /// bound so consumers that have a `Vec<Sexp>`, a `[Sexp; N]`
12853 /// array, an `iter().cloned()` sequence, a
12854 /// `.map(...).collect()`-worthy chain, or a
12855 /// `once(head).chain(tail)` composition can hand the sequence
12856 /// directly to the algebra without a per-site `.collect::<Vec<
12857 /// Sexp>>()` coercion. A future allocation-policy change (e.g.
12858 /// arena-allocated lists for span-aware [`Sexp`]) lands as ONE
12859 /// edit at this method site and propagates through consumers
12860 /// byte-for-byte.
12861 ///
12862 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
12863 /// (list-shaped inner sequence, [`Self::List`] tuple-variant
12864 /// constructor) pairing binds at ONE typed-algebra method on the
12865 /// outer [`Sexp`] algebra, closing the outer-algebra construct
12866 /// family across ALL THREE structural carvings of the [`SexpShape`]
12867 /// closed set (atomic-payload + quote-family + residual). THEORY.md
12868 /// §II.1 invariant 2 — free middle; every consumer that has an
12869 /// owned or iterable sequence of [`Sexp`] and wants to build a
12870 /// list-shaped wrapper routes through the SAME typed method, so a
12871 /// regression that drifts one consumer's construction from the
12872 /// others cannot reach the substrate's runtime. THEORY.md §V.1 —
12873 /// knowable platform; the typed-construct family becomes a TYPE
12874 /// projection on the substrate's outer [`Sexp`] algebra sitting
12875 /// next to the typed-project family [`Self::as_list`] rather than
12876 /// bare tuple-variant constructor + per-site `Vec<Sexp>` discipline.
12877 /// THEORY.md §VI.1 — generation over composition; the residual-
12878 /// arm outer-shape + carving-marker pairings emerge from ONE
12879 /// typed-algebra composition on the outer [`Sexp`] algebra rather
12880 /// than from per-consumer per-variant literals.
12881 ///
12882 /// Frontier inspiration: Racket's `(list x y z)` typed list-
12883 /// construct primitive paired one-for-one with `(list? v)` /
12884 /// `(car v)` / `(cdr v)` predicate/projection siblings on the
12885 /// same closed-set list shape — the typed-construct + typed-
12886 /// project algebra dual is closed at one method per direction on
12887 /// Racket's surface, and [`Self::list`] / [`Self::as_list`] is
12888 /// the Rust-typed peer on the closed-set outer [`Sexp`] algebra
12889 /// with `impl IntoIterator<Item = Sexp>` standing in for Racket's
12890 /// variadic collect face. MLIR's `mlir::OpBuilder::create<
12891 /// ListOp>(loc, elements)` typed-IR list-op construction paired
12892 /// with `mlir::dyn_cast<ListOp>(op)` on the projection face —
12893 /// the typed factory + typed downcast pair the IR algebra closes
12894 /// over on every list-shaped op; [`Self::list`] / [`Self::as_list`]
12895 /// is the Rust-typed peer on the outer [`Sexp`] algebra with
12896 /// [`StructuralKind::List`] standing in for MLIR's `OperationName`
12897 /// taxonomy over the list-shaped op family.
12898 #[must_use]
12899 pub fn list<I: IntoIterator<Item = Sexp>>(items: I) -> Self {
12900 Self::List(items.into_iter().collect())
12901 }
12902
12903 /// Soft projection onto the closed-set [`StructuralKind`] residual
12904 /// carving marker — the 2-of-12 carving of the [`SexpShape`] algebra
12905 /// covering [`Self::Nil`] and [`Self::List`] (the outer shapes that
12906 /// lie OUTSIDE both the atomic-payload carving
12907 /// [`AtomKind`](crate::error::SexpShape::as_atom_kind) and the
12908 /// quote-family carving
12909 /// [`QuoteForm`](crate::error::SexpShape::as_quote_form)). Returns
12910 /// `Some(StructuralKind::Nil)` for [`Self::Nil`],
12911 /// `Some(StructuralKind::List)` for [`Self::List`], `None` for
12912 /// every other outer shape (every [`Self::Atom`] variant, every
12913 /// quote-family wrapper: [`Self::Quote`], [`Self::Quasiquote`],
12914 /// [`Self::Unquote`], [`Self::UnquoteSplice`]).
12915 ///
12916 /// Sibling soft-projection peer of [`Self::as_quote_form`] (the
12917 /// soft-decomposition of the four homoiconic prefix wrappers into
12918 /// `(QuoteForm, &Sexp)`) and [`Self::as_unquote`] (the
12919 /// soft-decomposition of the two template-substitution wrappers
12920 /// into `(UnquoteForm, &Sexp)`). Direct value-level peer of the
12921 /// shape-level projection
12922 /// [`SexpShape::as_structural_kind`](crate::error::SexpShape::as_structural_kind)
12923 /// — the pair `(Sexp::as_structural_kind, SexpShape::as_structural_kind)`
12924 /// binds the (Sexp value, StructuralKind carving marker) pairing at
12925 /// ONE typed method on each algebra, symmetric with the existing
12926 /// (Sexp value → AtomKind via
12927 /// `Sexp::as_atom().map(Atom::kind)`) atomic-axis composition and
12928 /// the direct (Sexp value → QuoteForm) marker projection
12929 /// [`Self::as_quote_form`] returns.
12930 ///
12931 /// Composition law: `s.as_structural_kind() ==
12932 /// s.shape().as_structural_kind()` for every `s: &Sexp`. Pre-lift
12933 /// the residual-carving marker at the value level was reachable
12934 /// only via the two-step composition
12935 /// `s.shape().as_structural_kind()` (walking through the full
12936 /// 12-variant [`SexpShape`] closed set to arrive at the 2-of-12
12937 /// carving marker); post-lift the composition lands at ONE typed
12938 /// method on the value algebra — the Nil arm returns `Some(Nil)`
12939 /// directly and the List arm returns `Some(List)` directly,
12940 /// matching the residual-carving membership at the value level.
12941 /// The composition law is pinned by
12942 /// `sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant`
12943 /// in this module, so a regression that drifts either projection
12944 /// from the other surfaces immediately.
12945 ///
12946 /// Sibling-shape lift to [`Self::is_list`] (the bare List-arm
12947 /// predicate) and [`Self::is_kwargs_list`] (the narrower
12948 /// kwargs-shaped List cohort predicate): where `is_list` returns
12949 /// `true` iff the value inhabits the List arm of the residual
12950 /// carving, `as_structural_kind` returns the typed carving marker
12951 /// that binds BOTH residual arms (Nil and List) at ONE typed
12952 /// projection — the operator answering "which residual arm?"
12953 /// rather than the bare "is this the List arm?" predicate.
12954 ///
12955 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
12956 /// (Sexp variant, StructuralKind carving marker) pairing becomes a
12957 /// TYPE projection on the substrate `Sexp` algebra rather than a
12958 /// two-step composition through the shape-level projection. A typo
12959 /// or swap at the value-projection site is no longer a runtime
12960 /// drift but a compile error against the typed projection.
12961 /// THEORY.md §VI.1 — generation over composition; the
12962 /// residual-carving marker projection now lives on the typed
12963 /// `Sexp` algebra alongside [`Self::as_atom`], [`Self::as_list`],
12964 /// [`Self::as_quote_form`], [`Self::as_unquote`], completing the
12965 /// (Sexp value → closed-set carving marker) family at the residual
12966 /// axis. THEORY.md §II.1 invariant 2 — free middle; every consumer
12967 /// that needs the residual-carving marker at the value level (a
12968 /// future `tatara-check` predicate keyed on the Nil/List cohort, a
12969 /// future LSP structural-navigation filter that keys on the
12970 /// residual carving, a future typed-rewriter walk over the
12971 /// residual arm) binds to ONE typed method on the value algebra
12972 /// rather than a two-step composition through the shape-level
12973 /// projection.
12974 ///
12975 /// Frontier inspiration: MLIR's `mlir::dyn_cast<StructuralOp>(val)`
12976 /// typed soft-downcast on the residual carving of a closed-set
12977 /// value algebra — the (value, typed carving marker) pairing lives
12978 /// at ONE typed projection on the outer value-algebra sibling. The
12979 /// Rust-typed peer here uses the substrate's outer `Sexp` algebra
12980 /// with `Sexp::as_structural_kind` closing the residual-carving
12981 /// cell of the value-level soft-projection surface, symmetric with
12982 /// the atomic-axis composition through [`Self::as_atom`] and the
12983 /// quote-family projection [`Self::as_quote_form`].
12984 #[must_use]
12985 pub fn as_structural_kind(&self) -> Option<StructuralKind> {
12986 match self {
12987 Self::Nil => Some(StructuralKind::Nil),
12988 Self::List(_) => Some(StructuralKind::List),
12989 _ => None,
12990 }
12991 }
12992
12993 /// Structural-shape predicate — `true` iff this is a [`Self::List`]
12994 /// whose items form a non-empty, even-length `(:k v :k v …)` kwargs
12995 /// sequence with every even-indexed item being an [`Atom::Keyword`].
12996 /// `false` for every other outer shape ([`Self::Nil`], every
12997 /// [`Self::Atom`] variant, every quote-family wrapper) and for every
12998 /// [`Self::List`] that fails the kwargs convention (empty list, odd
12999 /// length, or any even-indexed non-keyword).
13000 ///
13001 /// The structural witness that [`Self::to_json`] will project this
13002 /// value as [`serde_json::Value::Object`] rather than
13003 /// [`serde_json::Value::Array`] at the [`Self::List`] arm — the
13004 /// `(Sexp variant + kwargs shape, JSON canonical-form)` pairing
13005 /// binds at ONE inherent method on the algebra rather than at a
13006 /// free function consumers must reach into the `domain` module
13007 /// path to invoke. Inverse round-trip law: every
13008 /// [`Self::from_json`] projection of a [`serde_json::Value::Object`]
13009 /// satisfies this predicate (the [`Self::List`] arm
13010 /// [`Self::from_json`] builds for an `Object` is non-empty by the
13011 /// `Object`'s non-empty-keys invariant when present, even-length by
13012 /// the alternating `:k v` build, and keyword-headed at every even
13013 /// index by the `Self::keyword(camel_to_kebab(k))` build — except
13014 /// for the structurally degenerate empty `Object` which projects to
13015 /// `Sexp::List(vec![])` and returns `false` here, matching
13016 /// [`Self::to_json`]'s "empty-list ↛ kwargs" gate).
13017 ///
13018 /// Composes through [`Self::as_list`] (the structural soft-projection
13019 /// onto `&[Sexp]`) and [`Atom::as_keyword`] (the typed soft-projection
13020 /// onto the keyword payload from the [`Atom`] algebra) — the predicate
13021 /// is rebuilt from already-lifted algebra primitives rather than
13022 /// inline-matching the [`Self::List`] arm. Sibling-shape predicate
13023 /// peer of [`Self::is_list`] (the unconditional [`Self::List`]-arm
13024 /// predicate), with this method narrowing the structural witness to
13025 /// the kwargs-shaped sub-cohort. The two predicates partition the
13026 /// list-typed cell of the algebra: every [`Self::List`] either
13027 /// satisfies `is_kwargs_list` (projects as [`serde_json::Value::Object`]
13028 /// through [`Self::to_json`]) or does not (projects as
13029 /// [`serde_json::Value::Array`]).
13030 ///
13031 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
13032 /// kwargs-shape predicate, previously a `pub(crate)` free function in
13033 /// `domain.rs` reached across the module boundary by [`Self::to_json`],
13034 /// is lifted ONE algebra level higher onto the inherent method on
13035 /// the [`Sexp`] algebra — completing the structural-predicate family
13036 /// alongside [`Self::is_list`] and the soft-projection family
13037 /// ([`Self::as_atom`], [`Self::as_list`], [`Self::as_quote_form`]).
13038 /// THEORY.md §II.1 invariant 2 — free middle; every consumer that
13039 /// queries "would [`Self::to_json`] project this as `Object`?" (the
13040 /// `Self::to_json` arm itself, future authoring-tool diagnostics, a
13041 /// future LSP completion fallback, a future REPL pretty-printer that
13042 /// chooses between `(…)` and `{…}` rendering, a future `tatara-check`
13043 /// typed-pattern matcher) routes through ONE inherent algebra method
13044 /// rather than reaching into the `domain` module path for a free
13045 /// function. THEORY.md §V.1 — knowable platform; the JSON-format
13046 /// witness becomes a TYPE projection on the substrate `Sexp` algebra
13047 /// next to its sibling `Sexp::is_list` / `Sexp::as_list` pair rather
13048 /// than living in a `domain.rs` `pub(crate)` helper consumers must
13049 /// import via module path.
13050 ///
13051 /// Frontier inspiration: MLIR's `mlir::Operation::hasTrait<T>()` —
13052 /// typed-IR operations carry their structural traits as inherent
13053 /// methods on the operation algebra rather than as free functions
13054 /// in a sibling module; `Sexp::is_kwargs_list` is the
13055 /// unstructured-Rust peer on the `Sexp` algebra for the
13056 /// "would-this-project-as-Object" structural trait. Racket's
13057 /// `(keyword-apply-procedure? stx)` — the syntax-class predicate
13058 /// that gates a kwargs-style application form's printer / expander
13059 /// path on the syntax algebra; `Sexp::is_kwargs_list` is the
13060 /// substrate's peer at the [`Sexp`] layer, with the `as_list().
13061 /// is_some_and(…)` composition standing in for Racket's
13062 /// `syntax-parse` pattern matcher.
13063 #[must_use]
13064 pub fn is_kwargs_list(&self) -> bool {
13065 self.as_list().is_some_and(|items| {
13066 !items.is_empty()
13067 && items.len().is_multiple_of(2)
13068 && items.iter().step_by(2).all(|s| s.as_keyword().is_some())
13069 })
13070 }
13071
13072 /// Soft projection onto the inner [`Atom`] payload — `Some(&Atom)`
13073 /// iff this is a [`Self::Atom`] variant, `None` for every other
13074 /// outer shape (`Nil`, `List`, `Quote`, `Quasiquote`, `Unquote`,
13075 /// `UnquoteSplice`). The structural-lift face of the per-atomic-
13076 /// payload soft-projection family — composes with the typed
13077 /// [`Atom::as_symbol`] / [`Atom::as_keyword`] / [`Atom::as_string`]
13078 /// / [`Atom::as_int`] / [`Atom::as_float`] / [`Atom::as_bool`]
13079 /// projections to give the six `Sexp::as_X` consumers ONE typed
13080 /// boundary instead of six inline `Self::Atom(Atom::X(s)) => Some(s)`
13081 /// arms.
13082 ///
13083 /// Sibling soft-projection peer of [`Self::as_quote_form`] (the
13084 /// soft-decomposition of the four homoiconic prefix wrappers into
13085 /// `(QuoteForm, &Sexp)`) and [`Self::as_list`] (the soft-decomposition
13086 /// of the structural list constructor into `&[Sexp]`). Together the
13087 /// three projections (`as_atom`, `as_list`, `as_quote_form`) and
13088 /// their nullary peer ([`Self::Nil`] via `matches!(self, Self::Nil)`)
13089 /// cover every outer-shape arm of the `Sexp` algebra: Nil + Atom +
13090 /// List + 4 quote-family arms = 7 outer shapes, with the typed-
13091 /// projection set partitioning them by structural axis.
13092 ///
13093 /// Composition law binding `Sexp::as_X` to the typed `Atom` algebra:
13094 /// for every [`Sexp`] `s`,
13095 /// `s.as_symbol()` (and each `as_keyword` / `as_string` / `as_int` /
13096 /// `as_bool` sibling) `== s.as_atom().and_then(Atom::as_<variant>)`.
13097 /// The `Sexp::as_float` consumer specializes through the widening
13098 /// inline composition `s.as_atom().and_then(|a| a.as_float()
13099 /// .or_else(|| a.as_int().map(|n| n as f64)))` so the algebra-level
13100 /// `Atom::as_float` stays strict and the typed-identity
13101 /// distinction `Int(1)` vs `Float(1.0)` is preserved at the algebra
13102 /// layer (see [`Atom::as_int`]'s docstring for the discipline).
13103 ///
13104 /// Theory anchor: THEORY.md §VI.1 — generation over composition;
13105 /// the six inline `Self::Atom(Atom::X(s)) => Some(_)` arms across
13106 /// the `Sexp::as_X` family is past the three-times rule. THEORY.md
13107 /// §II.1 invariant 2 — free middle; SIX consumers (`as_symbol`,
13108 /// `as_keyword`, `as_string`, `as_int`, `as_float`, `as_bool`) now
13109 /// route through ONE typed structural lift (this method) AND ONE
13110 /// per-variant projection family on the closed-set `Atom` algebra
13111 /// rather than six byte-identical outer-arm matches each.
13112 /// THEORY.md §V.1 — knowable platform; the (Sexp variant, inner
13113 /// payload kind) pairing becomes a TYPE projection on the substrate
13114 /// algebra rather than six inline arms scattered across the six
13115 /// `Sexp::as_X` consumers.
13116 #[must_use]
13117 pub fn as_atom(&self) -> Option<&Atom> {
13118 match self {
13119 Self::Atom(a) => Some(a),
13120 _ => None,
13121 }
13122 }
13123
13124 /// Soft projection onto the closed-set [`AtomKind`] atomic-payload
13125 /// carving marker — the 6-of-12 carving of the [`SexpShape`] algebra
13126 /// covering [`Self::Atom`]'s six per-payload variants ([`Atom::Symbol`],
13127 /// [`Atom::Keyword`], [`Atom::Str`], [`Atom::Int`], [`Atom::Float`],
13128 /// [`Atom::Bool`]). Returns `Some(a.kind())` iff this is a
13129 /// [`Self::Atom`] variant, `None` for every other outer shape
13130 /// ([`Self::Nil`], [`Self::List`], every quote-family wrapper:
13131 /// [`Self::Quote`], [`Self::Quasiquote`], [`Self::Unquote`],
13132 /// [`Self::UnquoteSplice`]).
13133 ///
13134 /// Direct value-level peer of the shape-level projection
13135 /// [`SexpShape::as_atom_kind`](crate::error::SexpShape::as_atom_kind)
13136 /// — the pair `(Sexp::as_atom_kind, SexpShape::as_atom_kind)` binds
13137 /// the (Sexp value, AtomKind carving marker) pairing at ONE typed
13138 /// method on each algebra, closing the atomic-axis cell of the
13139 /// (Sexp value → carving marker) matrix. Sibling soft-projection
13140 /// peer of [`Self::as_structural_kind`] (the 2-of-12 residual
13141 /// carving returning `Option<StructuralKind>`) and
13142 /// [`Self::as_quote_form`] (the 4-of-12 quote-family carving
13143 /// returning `Option<(QuoteForm, &Sexp)>`) — post-lift ALL THREE
13144 /// carvings that partition the twelve outer shapes of the
13145 /// [`SexpShape`] algebra have a marker-only value-level projection
13146 /// on `Sexp`: `as_atom_kind` (atomic axis), `as_quote_form`
13147 /// (quote-family axis, marker + inner), `as_structural_kind`
13148 /// (residual axis). The `Sexp::as_atom` projection stays available
13149 /// for consumers that need the inner [`Atom`] payload for further
13150 /// per-variant typed projection ([`Atom::as_symbol`] et al.); this
13151 /// projection is the shortcut for consumers that only need the
13152 /// carving-marker identity.
13153 ///
13154 /// Composition laws (dual bindings): `s.as_atom_kind() ==
13155 /// s.as_atom().map(Atom::kind) == s.shape().as_atom_kind()` for
13156 /// every `s: &Sexp`. Pre-lift the atomic carving marker at the
13157 /// value level was reachable only via one of these two-step
13158 /// compositions — either through the [`Atom`] algebra
13159 /// (`as_atom().map(Atom::kind)`) or through the shape algebra
13160 /// (`shape().as_atom_kind()`). Post-lift the projection lands at
13161 /// ONE typed method on the value algebra, and both compositions
13162 /// are pinned as agreement laws (see
13163 /// `sexp_as_atom_kind_agrees_with_as_atom_map_kind_for_every_variant`
13164 /// and
13165 /// `sexp_as_atom_kind_agrees_with_shape_as_atom_kind_for_every_variant`
13166 /// in this module). A regression that drifts any of the three
13167 /// projections from the others surfaces immediately.
13168 ///
13169 /// Symmetric with [`Self::as_structural_kind`]'s shape (returns
13170 /// just the marker, no inner-payload borrow) — where
13171 /// [`Self::as_quote_form`] and [`Self::as_unquote`] surface both
13172 /// the marker AND the wrapped inner `&Sexp` (because the four
13173 /// quote-family arms and the two substitution arms structurally
13174 /// carry a boxed inner value), `as_atom_kind` and
13175 /// `as_structural_kind` return marker-only projections (the atomic
13176 /// arm's inner payload is heterogeneous across the six variants —
13177 /// `String` / `i64` / `f64` / `bool` — and the residual arms
13178 /// carry no or list-heterogeneous payload). Consumers that need
13179 /// the payload compose through [`Self::as_atom`] +
13180 /// [`Atom::as_symbol`] et al. (atomic axis) or [`Self::as_list`]
13181 /// (residual axis); this projection is the payload-agnostic
13182 /// carving-marker cell.
13183 ///
13184 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (Sexp
13185 /// variant, AtomKind carving marker) pairing becomes a TYPE
13186 /// projection on the substrate `Sexp` algebra rather than a
13187 /// two-step composition through either the [`Atom`] algebra or the
13188 /// shape algebra. A typo or swap at the value-projection site is
13189 /// no longer a runtime drift but a compile error against the
13190 /// typed projection. THEORY.md §VI.1 — generation over composition;
13191 /// the atomic-carving marker projection now lives on the typed
13192 /// `Sexp` algebra alongside [`Self::as_atom`], [`Self::as_list`],
13193 /// [`Self::as_quote_form`], [`Self::as_unquote`],
13194 /// [`Self::as_structural_kind`], completing the (Sexp value →
13195 /// closed-set carving marker) family across ALL THREE axes
13196 /// (atomic + quote-family + structural-residual). THEORY.md §II.1
13197 /// invariant 2 — free middle; every consumer that needs the
13198 /// atomic-carving marker at the value level (a future
13199 /// `tatara-check` predicate keyed on the atomic cohort, a future
13200 /// LSP structural-navigation filter that keys on the atomic
13201 /// carving, a future typed-rewriter walk over the atomic arm)
13202 /// binds to ONE typed method on the value algebra rather than a
13203 /// two-step composition.
13204 ///
13205 /// Sibling posture across the value-level marker family — the
13206 /// three projections (`as_atom_kind`, `as_quote_form`,
13207 /// `as_structural_kind`) form a partition of the seven outer-shape
13208 /// variants of the `Sexp` algebra: for every `s: &Sexp`, EXACTLY
13209 /// ONE returns `Some(_)` (pinned by the joint sweep
13210 /// `sexp_as_atom_kind_partitions_outer_shapes_jointly_with_as_quote_form_and_as_structural_kind`
13211 /// in this module, sibling to the pre-existing partition sweep
13212 /// keyed on `as_atom` rather than `as_atom_kind`). The value-level
13213 /// partition-total invariant across the three carvings is the
13214 /// value-level peer of the shape-level partition-total invariant
13215 /// (`sexp_shape_partition_is_total_across_atom_quote_structural_carvings`
13216 /// in error.rs); each axis has BOTH invariants pinned.
13217 ///
13218 /// Frontier inspiration: MLIR's `mlir::dyn_cast<AtomOp>(val)` typed
13219 /// soft-downcast onto the atomic carving of a closed-set value
13220 /// algebra — the (value, typed carving marker) pairing lives at
13221 /// ONE typed projection on the outer value-algebra sibling. The
13222 /// Rust-typed peer here uses the substrate's outer `Sexp` algebra
13223 /// with `Sexp::as_atom_kind` closing the atomic-carving cell of
13224 /// the value-level soft-projection surface, symmetric with the
13225 /// residual-carving projection [`Self::as_structural_kind`] and
13226 /// the quote-family projection [`Self::as_quote_form`]. Racket's
13227 /// `(atom? stx)` predicate paired with `(syntax->datum stx)` on
13228 /// the atomic branch — the substrate's `as_atom_kind` surfaces the
13229 /// typed witness (`AtomKind`) alongside the predicate verdict in
13230 /// ONE `Option<AtomKind>` projection.
13231 #[must_use]
13232 pub fn as_atom_kind(&self) -> Option<AtomKind> {
13233 self.as_atom().map(Atom::kind)
13234 }
13235
13236 /// Project this [`Sexp`] to its closed-set [`SexpShape`] outer-shape
13237 /// marker — `Nil → SexpShape::Nil`, `Atom(a) → a.kind().sexp_shape()`,
13238 /// `List(_) → SexpShape::List`, and each quote-family wrapper routes
13239 /// through `as_quote_form().map(|(qf, _)| qf.sexp_shape())`. The
13240 /// outer-shape peer on the [`Sexp`] algebra of [`Atom::kind`] (the
13241 /// atomic-payload axis) and [`QuoteForm::sexp_shape`] (the
13242 /// quote-family axis) — completes the substrate's Sexp-shape
13243 /// projection family by lifting the free-function dispatcher
13244 /// [`crate::domain::sexp_shape`] onto the typed `Sexp` algebra
13245 /// alongside its [`Atom`] / [`QuoteForm`] peers.
13246 ///
13247 /// Composition law: `s.shape() == crate::domain::sexp_shape(s)` for
13248 /// every `s: &Sexp`. The free function continues to exist as a thin
13249 /// delegate (its callers in `domain.rs`'s diagnostic-builder paths,
13250 /// `compile.rs`'s `TypeMismatch.got` builder, and downstream tests
13251 /// route through `s.shape()` after this lift), so the (Sexp variant,
13252 /// SexpShape variant) pairing now binds at ONE inherent method on
13253 /// the algebra rather than at a free function `domain` consumers
13254 /// must reach into the module path to invoke.
13255 ///
13256 /// Sibling-shape lift to the typed-EXIT projection trio on [`Atom`]
13257 /// ([`fmt::Display for Atom`], [`Atom::to_json`],
13258 /// `Atom::to_iac_forge_sexpr` (removed)) and the typed-ENTRY classifier
13259 /// ([`Atom::from_lexeme`]): where the atomic-payload algebra carries
13260 /// its own per-variant projection family at the atomic-payload
13261 /// level, the `Sexp` algebra carries this single outer-shape
13262 /// projection that composes through [`Self::as_atom`] +
13263 /// [`Atom::kind`] (atomic axis) and [`Self::as_quote_form`] (quote-
13264 /// family axis) — every other arm (`Nil`, `List`) projects to its
13265 /// own [`SexpShape`] variant directly.
13266 ///
13267 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
13268 /// (Sexp variant, SexpShape variant) pairing becomes an inherent
13269 /// algebra projection rather than a free function in `domain.rs`,
13270 /// so the projection sits next to the rest of the typed `Sexp`
13271 /// algebra ([`Self::as_atom`], [`Self::as_list`],
13272 /// [`Self::as_quote_form`], [`Self::head_symbol`],
13273 /// [`Self::as_call`]) the substrate carries. THEORY.md §II.1
13274 /// invariant 2 — free middle; every consumer that needs the
13275 /// outer shape (diagnostic builders at
13276 /// [`crate::domain::sexp_witness`] / [`crate::domain::missing_head_err`],
13277 /// [`crate::compile`]'s `TypeMismatch.got` projection, future LSP /
13278 /// REPL / `tatara-check` typed-pattern matchers) now reaches a
13279 /// method on the value rather than a free function imported from
13280 /// `domain`. THEORY.md §VI.1 — generation over composition; the
13281 /// inline dispatch lifted to [`crate::domain::sexp_shape`] is now
13282 /// lifted ONE algebra level higher — from the free function to
13283 /// the inherent method — so a future `Sexp` variant lands at the
13284 /// algebra's match site without a module-path indirection. A
13285 /// future extension (e.g. `Sexp::Vector` for `#(...)` reader
13286 /// syntax, `Sexp::Map` for `{...}`) extends THIS method + the
13287 /// `SexpShape` algebra + the free function's delegation in
13288 /// lockstep — exhaustively checked by rustc across the `Sexp`
13289 /// match.
13290 ///
13291 /// Frontier inspiration: MLIR's `mlir::Operation::getName()` —
13292 /// the typed-IR operation projects through an inherent method
13293 /// to its closed-set name on the operation algebra; `Sexp::shape`
13294 /// is the unstructured-Rust peer on the [`Sexp`] algebra for the
13295 /// outer-shape projection surface, with [`SexpShape`] standing in
13296 /// for MLIR's `OperationName` taxonomy. Racket's `(syntax-e stx)`
13297 /// composed with a datum-prim classifier on the closed-set
13298 /// syntax-taxonomy projects a syntax object to its outer shape via
13299 /// a single primitive on the syntax algebra; `Sexp::shape` is the
13300 /// substrate's typed-Rust peer.
13301 #[must_use]
13302 pub fn shape(&self) -> SexpShape {
13303 // Each variant routes through its closed-set carving-marker's
13304 // `sexp_shape` projection — the atomic-payload carving via
13305 // `AtomKind::sexp_shape`, the structural-residual carving via
13306 // `StructuralKind::sexp_shape`, the quote-family carving via
13307 // `QuoteForm::sexp_shape`. Post-lift the twelve outer-shape
13308 // arms of the SexpShape closed set are reached through THREE
13309 // carving-marker `sexp_shape` projections (6 + 2 + 4 = 12),
13310 // symmetric across the partition — no arm hits a raw
13311 // `SexpShape::*` literal here. A future thirteenth variant
13312 // (e.g. `Sexp::Vector` for `#(...)` reader syntax) extends the
13313 // carving-marker family the same way and lands at one arm
13314 // here + one carving-marker `sexp_shape` arm in lockstep.
13315 match self {
13316 Self::Nil => StructuralKind::Nil.sexp_shape(),
13317 Self::Atom(a) => a.kind().sexp_shape(),
13318 Self::List(_) => StructuralKind::List.sexp_shape(),
13319 Self::Quote(_) | Self::Quasiquote(_) | Self::Unquote(_) | Self::UnquoteSplice(_) => {
13320 let (qf, _) = self.expect_quote_form();
13321 qf.sexp_shape()
13322 }
13323 }
13324 }
13325
13326 /// Project this `Sexp` to its [`SexpWitness`] — the typed joint
13327 /// identity pairing the structural [`SexpShape`] with the
13328 /// renderable [`Sexp::Display`] projection in ONE owned value.
13329 /// The joint-identity peer on the [`Sexp`] algebra of
13330 /// [`Self::shape`] (the structural-shape-only projection) and
13331 /// [`fmt::Display for Sexp`] (the rendered-literal-only
13332 /// projection) — completes the substrate's Sexp-projection
13333 /// family by lifting the free-function dispatcher
13334 /// [`crate::domain::sexp_witness`] onto the typed `Sexp` algebra
13335 /// alongside its [`Self::shape`] peer.
13336 ///
13337 /// Composition law: `s.witness() ==
13338 /// crate::domain::sexp_witness(s)` for every `s: &Sexp`. The
13339 /// free function continues to exist as a thin delegate (its
13340 /// callers in `macro_expand.rs`'s 8 typed-entry rejection
13341 /// builders, `domain.rs`'s `missing_head_err` caller +
13342 /// `rewriter_non_list_err` typed-exit builder, and downstream
13343 /// tests route through `s.witness()` after this lift), so the
13344 /// (Sexp variant, SexpWitness identity) pairing now binds at
13345 /// ONE inherent method on the algebra rather than at a free
13346 /// function `domain` consumers must reach into the module path
13347 /// to invoke. Body composes the two algebra-level projections
13348 /// — `self.shape()` for the structural identity, `self.to_string()`
13349 /// for the renderable identity — into ONE
13350 /// [`SexpWitness::new`] call. Pre-lift the dispatcher lived as
13351 /// a free function in `domain.rs`; post-lift the canonical site
13352 /// is the inherent method and the free function delegates
13353 /// (mirrors the [`Self::shape`] lift in 121bb60 exactly).
13354 ///
13355 /// Sibling-shape lift to [`Self::shape`] (the structural-shape
13356 /// projection): where `shape()` carries the typed-shape axis on
13357 /// the `Sexp` algebra, `witness()` carries the JOINT typed-shape
13358 /// and renderable-literal axis — the typed identity an authoring
13359 /// tool diagnostic owes the operator AT the typed-entry or
13360 /// typed-exit rejection boundary. Every rejection-builder
13361 /// helper in `macro_expand.rs` that previously projected `&Sexp`
13362 /// through `crate::domain::sexp_witness(_)` at the variant
13363 /// boundary now reaches a method on the value rather than a
13364 /// free function imported from `domain`.
13365 ///
13366 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
13367 /// (Sexp variant, SexpWitness identity) pairing becomes an
13368 /// inherent algebra projection rather than a free function in
13369 /// `domain.rs`, so the projection sits next to the rest of the
13370 /// typed `Sexp` algebra ([`Self::shape`], [`Self::as_atom`],
13371 /// [`Self::as_list`], [`Self::as_quote_form`],
13372 /// [`Self::head_symbol`], [`Self::as_call`]) the substrate
13373 /// carries. THEORY.md §II.1 invariant 2 — free middle; every
13374 /// consumer that needs the typed joint identity at a
13375 /// rejection-boundary slot (`NonSymbolUnquoteTarget.got`,
13376 /// `SpliceOutsideList.got`, `NonSymbolParam.got`,
13377 /// `RestParamMissingName.got`, `RestParamTrailingTokens.first`,
13378 /// `OptionalParamMalformed.got`, `DefmacroNonSymbolName.got`,
13379 /// `DefmacroNonListParams.got`, `MissingHeadSymbol.got`,
13380 /// `RewriterNonList.got`, future LSP / REPL / `tatara-check`
13381 /// typed-pattern matchers) now reaches a method on the value
13382 /// rather than a free function imported from `domain`.
13383 /// THEORY.md §VI.1 — generation over composition; the inline
13384 /// dispatch lifted to [`crate::domain::sexp_witness`] is now
13385 /// lifted ONE algebra level higher — from the free function
13386 /// to the inherent method — completing the Sexp-projection
13387 /// family alongside [`Self::shape`]. A future `Sexp` variant
13388 /// extension (e.g. `Sexp::Vector` for `#(...)` reader syntax,
13389 /// `Sexp::Map` for `{...}`) reaches this method through the
13390 /// already-lifted [`Self::shape`] + [`fmt::Display for Sexp`]
13391 /// pair — no new arm needed here.
13392 ///
13393 /// Frontier inspiration: MLIR's diagnostic builder pattern —
13394 /// `op.emitOpError() << op` projects the offending operation
13395 /// through inherent methods (`getName()`, `print()`) into ONE
13396 /// diagnostic value; `Sexp::witness` is the unstructured-Rust
13397 /// peer on the [`Sexp`] algebra for the joint typed-shape +
13398 /// renderable-literal projection surface, with [`SexpWitness`]
13399 /// standing in for MLIR's `InFlightDiagnostic` typed payload.
13400 #[must_use]
13401 pub fn witness(&self) -> SexpWitness {
13402 SexpWitness::new(self.shape(), self.to_string())
13403 }
13404
13405 /// Project this `Sexp` to its stable, human-readable outer-shape
13406 /// label — the `&'static str` axis on the [`Sexp`] algebra. Lifts
13407 /// the free-function dispatcher [`crate::domain::sexp_type_name`]
13408 /// onto the typed `Sexp` algebra alongside its [`Self::shape`] /
13409 /// [`Self::witness`] / [`Self::to_json`] / [`Self::from_json`]
13410 /// sibling projections, completing the substrate's
13411 /// Sexp-projection family at the canonical-label axis the way
13412 /// [`Self::shape`] completes the typed-shape axis and
13413 /// [`fmt::Display for Sexp`] completes the canonical-string axis.
13414 ///
13415 /// Composition law: `s.type_name() == s.shape().label() ==
13416 /// crate::domain::sexp_type_name(s)` for every `s: &Sexp`.
13417 /// Pre-lift the projection lived as a free function in
13418 /// `domain.rs` consumers (in particular the `LispError::TypeMismatch`
13419 /// `got` slot in `compile.rs` and the legacy substring-grep
13420 /// rejection-message tests) reached across module boundaries to
13421 /// invoke; post-lift the canonical site is the inherent method on
13422 /// the [`Sexp`] algebra and the free function delegates so existing
13423 /// callers continue to compile. Body composes through
13424 /// [`Self::shape`] + [`SexpShape::label`] so a future `Sexp`
13425 /// variant (e.g. `Sexp::Vector` for `#(...)` reader syntax,
13426 /// `Sexp::Map` for `{...}`) lands at one extension site
13427 /// ([`Self::shape`]'s exhaustive arm) rather than a parallel
13428 /// `&'static str` match — the projection is structurally derived,
13429 /// not duplicated.
13430 ///
13431 /// Sibling-shape lift to [`Self::shape`] (the typed-shape
13432 /// projection): where `shape()` carries the typed
13433 /// [`SexpShape`] identity (matchable, exhaustive across `Sexp`
13434 /// variants), `type_name()` carries the `&'static str` literal
13435 /// the rendered diagnostic surface wants (still derived from
13436 /// the typed identity, but flattened through
13437 /// [`SexpShape::label`] for substring-grep callers and the
13438 /// `TypeMismatch.got` slot). The `&'static str` lifetime makes
13439 /// the projection cheap to embed in any error variant without
13440 /// allocation.
13441 ///
13442 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
13443 /// (Sexp variant, `&'static str` label) pairing becomes an
13444 /// inherent algebra projection rather than a free function in
13445 /// `domain.rs`, so the projection sits next to the rest of the
13446 /// typed `Sexp` algebra ([`Self::shape`], [`Self::witness`],
13447 /// [`Self::to_json`], [`Self::from_json`], [`Self::as_atom`],
13448 /// [`Self::as_list`], [`Self::as_quote_form`],
13449 /// [`Self::head_symbol`], [`Self::as_call`]) the substrate
13450 /// carries. THEORY.md §II.1 invariant 2 — free middle; every
13451 /// consumer that needs the outer-shape label
13452 /// (`LispError::TypeMismatch.got` projection in `compile.rs`,
13453 /// legacy substring-grep rejection-message tests, future LSP /
13454 /// REPL diagnostic surfaces) now reaches a method on the value
13455 /// rather than a free function imported from `domain`.
13456 /// THEORY.md §VI.1 — generation over composition; the inline
13457 /// `s.shape().label()` recipe lifted to
13458 /// [`crate::domain::sexp_type_name`] is now lifted ONE algebra
13459 /// level higher — from the free function to the inherent
13460 /// method — completing the Sexp-projection family alongside
13461 /// [`Self::shape`] / [`Self::witness`] / [`Self::to_json`] /
13462 /// [`Self::from_json`]. The `domain.rs` `sexp_*` free-function
13463 /// namespace is now structurally reserved for free functions
13464 /// that genuinely need a `domain`-module reach (registry
13465 /// dispatch, kwargs gates, registry suggestions), not
13466 /// algebra-layer projections.
13467 ///
13468 /// Frontier inspiration: MLIR's `mlir::Operation::getName()`
13469 /// composed with `OperationName::getStringRef()` — the typed-IR
13470 /// operation projects through inherent methods to its closed-set
13471 /// label on the operation algebra; `Sexp::type_name` is the
13472 /// unstructured-Rust peer on the [`Sexp`] algebra for the
13473 /// canonical-label projection surface, with [`SexpShape::label`]
13474 /// standing in for MLIR's `OperationName::getStringRef` second
13475 /// hop. Racket's `(syntax-name stx)` — the typed inverse of
13476 /// `(syntax-e stx)` on the syntax algebra; `Sexp::type_name`
13477 /// composes the typed-shape projection with its closed-set
13478 /// label projection at the inherent-method site rather than
13479 /// the typeclass-method site, matching pleme-io's
13480 /// "rust-typed, not trait-typed" idiom for closed-set algebras.
13481 #[must_use]
13482 pub fn type_name(&self) -> &'static str {
13483 self.shape().label()
13484 }
13485
13486 /// Project this `Sexp` to its canonical [`serde_json::Value`]
13487 /// rendering — the typed-algebra peer of [`Atom::to_json`] at the
13488 /// `Sexp` layer. Lifts the free-function dispatcher
13489 /// [`crate::domain::sexp_to_json`] onto the typed `Sexp` algebra
13490 /// alongside its [`Self::shape`] / [`Self::witness`] sibling
13491 /// projections, completing the JSON-projection axis at the
13492 /// algebra layer the way [`fmt::Display for Sexp`] completes the
13493 /// canonical-string axis. The free function continues to exist
13494 /// as a thin delegate (its callers in `tatara-lisp-derive`'s
13495 /// derive output route through it via the
13496 /// `crate::domain::sexp_to_json` import); the
13497 /// `from_value_with_path` private helper in `domain.rs` and the
13498 /// recursive sub-calls inside this method route through the
13499 /// inherent method directly so the canonical-site indirection
13500 /// disappears at every internal callsite.
13501 ///
13502 /// Rules (preserve byte-identical pre-lift behavior at the
13503 /// `sexp_to_json` callsite):
13504 /// - [`Self::Nil`] → [`serde_json::Value::Null`].
13505 /// - [`Self::Atom`] → [`Atom::to_json`] (the typed-algebra
13506 /// peer at the atomic-payload layer; pinned by
13507 /// `sexp_to_json_atom_arms_route_through_atom_to_json` in
13508 /// `domain.rs`).
13509 /// - [`Self::List`] with kwargs shape `(:k v :k v …)` →
13510 /// [`serde_json::Value::Object`] keyed by
13511 /// [`crate::domain::kebab_to_camel`] of each `:k`'s name.
13512 /// A duplicate kebab→camel key inside any nested kwargs-list
13513 /// fails with [`crate::domain::duplicate_kwarg`] — same
13514 /// typed-entry posture
13515 /// [`crate::domain::parse_kwargs`] takes at the top level.
13516 /// - [`Self::List`] otherwise → [`serde_json::Value::Array`]
13517 /// mapping each element through this method recursively.
13518 /// - [`Self::Quote`] / [`Self::Quasiquote`] / [`Self::Unquote`]
13519 /// / [`Self::UnquoteSplice`] → recurse on the inner via
13520 /// [`Self::expect_quote_form`] (strips the wrapper; the
13521 /// round-trip via [`crate::domain::json_to_sexp`] re-emits
13522 /// the inner without an enclosing wrapper). All four arms
13523 /// route through ONE [`Self::as_quote_form`]-derived
13524 /// projection so the per-variant pairing binds at ONE site
13525 /// on the [`QuoteForm`] algebra rather than four
13526 /// byte-identical inline arms.
13527 ///
13528 /// Composition law: `s.to_json() == crate::domain::sexp_to_json(s)`
13529 /// for every `s: &Sexp`. Pre-lift the dispatcher lived as a free
13530 /// function in `domain.rs`; post-lift the canonical site is the
13531 /// inherent method and the free function delegates (same lift
13532 /// posture as [`Self::shape`] in 121bb60 and [`Self::witness`]
13533 /// in a427e3b).
13534 ///
13535 /// Sibling-shape lift to [`Self::shape`] (the structural-shape
13536 /// projection), [`Self::witness`] (the joint structural-shape +
13537 /// renderable-literal projection), and [`fmt::Display for Sexp`]
13538 /// (the renderable-literal projection): where those three carry
13539 /// the Lisp-canonical-form / structural-identity axes on the
13540 /// algebra, `to_json` carries the JSON canonical-form axis. The
13541 /// substrate's `Sexp` algebra now binds ALL THREE canonical-form
13542 /// projection surfaces (Lisp Display, JSON, and the feature-gated
13543 /// iac-forge `From<&Sexp> for SExpr`) at the algebra layer, with
13544 /// per-variant atomic rendering composed through the corresponding
13545 /// [`Atom`] projection family (`Atom::Display`, [`Atom::to_json`],
13546 /// `Atom::to_iac_forge_sexpr`).
13547 ///
13548 /// Theory anchor: THEORY.md §VI.1 — generation over composition;
13549 /// the inline dispatch the prior runs lifted onto
13550 /// [`crate::domain::sexp_to_json`] (the free function) is now
13551 /// lifted ONE algebra level higher — from the free function to
13552 /// the inherent method — completing the Sexp-projection family
13553 /// alongside [`Self::shape`] and [`Self::witness`]. THEORY.md
13554 /// §II.1 invariant 2 — free middle; the typed-exit JSON
13555 /// projection (every consumer that round-trips a Sexp through
13556 /// `serde_json::from_value::<T>` for typed-domain
13557 /// deserialization, the typed-rewriter at
13558 /// [`crate::domain::TypedRewriter`], the derive macro's
13559 /// `compile_from_args` fallthrough, and any future canonical-
13560 /// form surface) all route through ONE inherent algebra method
13561 /// rather than reach into the `domain` module path for a free
13562 /// function. THEORY.md §V.1 — knowable platform; a future
13563 /// `Sexp` variant extension (e.g. `Sexp::Vector` for `#(...)`
13564 /// reader syntax, `Sexp::Map` for `{...}`) reaches this method
13565 /// through the already-lifted [`Self::as_quote_form`] +
13566 /// [`Atom::to_json`] pair — one arm added here for the new
13567 /// outer-shape variant; rustc enforces the per-variant body is
13568 /// named.
13569 ///
13570 /// Frontier inspiration: MLIR's `mlir::AsmPrinter::printOp` —
13571 /// the typed-IR printer dispatches on the closed-set `Op` so
13572 /// every printer body for an op lives at ONE implementation site;
13573 /// `Sexp::to_json` is the unstructured-Rust peer on the `Sexp`
13574 /// algebra for the JSON canonical-form surface (where
13575 /// [`fmt::Display for Sexp`] is the Lisp-canonical-form peer
13576 /// and `From<&Sexp> for iac_forge::SExpr` is the
13577 /// canonical-attestation-form peer). Racket's `(syntax->datum
13578 /// stx)` then a serializer over the datum prim — `to_json` is
13579 /// the substrate's serializer at the Sexp layer composed
13580 /// through [`Atom::to_json`] at the atomic-payload layer, with
13581 /// the closed-set [`AtomKind`] standing in for Racket's
13582 /// datum-prim taxonomy.
13583 pub fn to_json(&self) -> crate::error::Result<serde_json::Value> {
13584 Ok(match self {
13585 Self::Nil => serde_json::Value::Null,
13586 Self::Atom(a) => a.to_json(),
13587 Self::List(items) => {
13588 if self.is_kwargs_list() {
13589 let mut map = serde_json::Map::with_capacity(items.len() / 2);
13590 let mut i = 0;
13591 while i + 1 < items.len() {
13592 if let Some(k) = items[i].as_keyword() {
13593 let value = items[i + 1].to_json()?;
13594 if map
13595 .insert(crate::domain::kebab_to_camel(k), value)
13596 .is_some()
13597 {
13598 return Err(crate::domain::duplicate_kwarg(k));
13599 }
13600 i += 2;
13601 } else {
13602 break;
13603 }
13604 }
13605 serde_json::Value::Object(map)
13606 } else {
13607 serde_json::Value::Array(
13608 items
13609 .iter()
13610 .map(Self::to_json)
13611 .collect::<crate::error::Result<Vec<_>>>()?,
13612 )
13613 }
13614 }
13615 Self::Quote(_) | Self::Quasiquote(_) | Self::Unquote(_) | Self::UnquoteSplice(_) => {
13616 let (_, inner) = self.expect_quote_form();
13617 inner.to_json()?
13618 }
13619 })
13620 }
13621
13622 /// Inverse of [`Self::to_json`] — project a [`serde_json::Value`] back
13623 /// onto a [`Sexp`]. The closed-set [`serde_json::Value`] discriminator
13624 /// maps directly onto the corresponding [`Sexp`] constructor:
13625 ///
13626 /// - [`serde_json::Value::Null`] → [`Self::Nil`].
13627 /// - [`serde_json::Value::Bool`] → [`Self::boolean`].
13628 /// - [`serde_json::Value::Number`] → [`Self::int`] when the value
13629 /// fits an [`i64`], otherwise [`Self::float`] when it fits an
13630 /// [`f64`]; the structural impossibility "neither i64 nor f64"
13631 /// collapses to [`Self::int(0)`](Self::int) as a typed floor —
13632 /// [`serde_json::Number`]'s closed-set discriminator excludes
13633 /// this case in practice (every [`serde_json::Number`] is either
13634 /// i64-fitting, u64-fitting projected through f64, or f64-fitting
13635 /// directly), but the typed floor stays explicit so a future
13636 /// `serde_json` extension does not silently misroute. Mirror of
13637 /// [`Atom::to_json`]'s [`Self::int`] / [`Self::float`] bifurcation.
13638 /// - [`serde_json::Value::String`] → [`Self::string`]. The
13639 /// `serde_json::Value::String` discriminator is type-erased — a
13640 /// serde-projected symbol AND a serde-projected keyword AND a
13641 /// genuine string literal ALL inhabit it on the JSON side — so
13642 /// the back-projection chooses [`Self::string`] as the lossless
13643 /// floor for the `Atom::Symbol` / `Atom::Keyword` / `Atom::Str`
13644 /// three-way collapse. Consumers that need the symbol-vs-string
13645 /// distinction must preserve it BEFORE the JSON round-trip
13646 /// (e.g. through a typed enum's serde projection rather than a
13647 /// raw `Sexp`-to-`JValue` round-trip).
13648 /// - [`serde_json::Value::Array`] → [`Self::List`] mapping each
13649 /// element through this method recursively.
13650 /// - [`serde_json::Value::Object`] → [`Self::List`] of alternating
13651 /// `:key value` pairs in [`serde_json::Map`]'s iteration order
13652 /// (sorted by key under `serde_json`'s default `BTreeMap`
13653 /// backing; insertion order under the optional `preserve_order`
13654 /// feature, which the substrate does NOT enable today), with
13655 /// each JSON key projected through
13656 /// [`crate::domain::camel_to_kebab`] to recover the `:k`'s
13657 /// kebab-case authoring shape and each JSON value recursed
13658 /// through this method. Inverse of [`Self::to_json`]'s
13659 /// [`Self::List`] kwargs-shape arm: that arm projects
13660 /// `:k v :k v …` into a JSON object via
13661 /// [`crate::domain::kebab_to_camel`]; this arm projects the
13662 /// object back into a `Self::List` of alternating keyword /
13663 /// value via the inverse [`crate::domain::camel_to_kebab`].
13664 ///
13665 /// Composition law: `Self::from_json(&s.to_json()?)` projects back
13666 /// to a `Sexp` whose [`Self::to_json`] re-projection produces the
13667 /// SAME `JValue` (modulo the lossy `Symbol` / `Keyword` / `Str`
13668 /// three-way collapse documented above; for the round-trippable
13669 /// subset, `Sexp::Nil`, the six [`Atom`] kinds within their
13670 /// discriminator class, and recursively `Sexp::List` of round-
13671 /// trippable elements, the law holds byte-for-byte).
13672 ///
13673 /// Sibling-lift posture: this method mirrors the prior
13674 /// [`crate::domain::sexp_to_json`] → [`Self::to_json`] (commit
13675 /// 875ee3b) / [`crate::domain::sexp_shape`] → [`Self::shape`]
13676 /// (commit 121bb60) / [`crate::domain::sexp_witness`] →
13677 /// [`Self::witness`] (commit a427e3b) family of lifts, all of which
13678 /// promoted a free function in `domain.rs` to the inherent-method
13679 /// canonical site on the [`Sexp`] algebra. Pre-lift the
13680 /// `json_to_sexp` dispatcher lived in `domain.rs` as the canonical
13681 /// site; post-lift this inherent method is the canonical site and
13682 /// the free function delegates so every existing caller continues
13683 /// to compile.
13684 ///
13685 /// Sibling-shape lift on the round-trip closure: the substrate's
13686 /// `Sexp` ↔ `serde_json::Value` round-trip now lives entirely as
13687 /// two inherent methods on the [`Sexp`] algebra — [`Self::to_json`]
13688 /// (forward) and [`Self::from_json`] (inverse). Consumers that
13689 /// previously round-tripped a typed value through Lisp forms via
13690 /// `domain::sexp_to_json` + `domain::json_to_sexp` now bind to ONE
13691 /// algebra (the inherent-method family) rather than reaching across
13692 /// the `domain` module path for two free functions. A future
13693 /// canonical-form surface (e.g., a YAML round-trip via
13694 /// [`serde_yaml`], a Nix-expression round-trip via the typed Nix
13695 /// surface in `tatara-nix`) hangs off the SAME `Sexp` algebra at
13696 /// `Self::to_yaml` / `Self::from_yaml` / `Self::to_nix` /
13697 /// `Self::from_nix` — the naming pattern is now structurally
13698 /// established by this pair.
13699 ///
13700 /// Theory anchor: THEORY.md §VI.1 — generation over composition;
13701 /// the inline `json_to_sexp` dispatcher in `domain.rs` is lifted
13702 /// ONE algebra level higher (from free function to inherent
13703 /// method), completing the Sexp ↔ JValue round-trip closure
13704 /// alongside [`Self::to_json`]. THEORY.md §V.1 — knowable
13705 /// platform; the inverse projection becomes a NAMED primitive on
13706 /// the substrate's `Sexp` algebra rather than a `domain`-module
13707 /// free function consumers reach across module boundaries to call.
13708 /// THEORY.md §II.1 invariant 2 — free middle; every consumer that
13709 /// round-trips through JSON (the typed-rewriter at
13710 /// [`crate::domain::TypedRewriter`], the derive macro's
13711 /// `compile_from_args` JSON fallthrough, the test round-trip
13712 /// fixtures) routes through ONE inherent algebra method — the
13713 /// typed round-trip closure is structurally complete on the
13714 /// `Sexp` algebra.
13715 ///
13716 /// Frontier inspiration: MLIR's `mlir::parseAttribute(str, ctx)` —
13717 /// the typed-IR parser inverse of `printAttribute` lives on the
13718 /// same `Attribute` algebra as its printer dual; the substrate's
13719 /// [`Self::from_json`] is the unstructured-Rust peer on the
13720 /// `Sexp` algebra for the JSON canonical-form inverse, paired
13721 /// with [`Self::to_json`] as the closed round-trip. Racket's
13722 /// `(datum->syntax stx datum)` — the round-trip inverse of
13723 /// `(syntax->datum stx)`, projected at the `datum` algebra layer;
13724 /// `Self::from_json` is the substrate's peer at the `Sexp` layer
13725 /// (one algebra level lower than Racket's `syntax` wrapper).
13726 #[must_use]
13727 pub fn from_json(v: &serde_json::Value) -> Self {
13728 match v {
13729 serde_json::Value::Null => Self::Nil,
13730 serde_json::Value::Bool(b) => Self::boolean(*b),
13731 // Numeric arm — the (JSON `Number` → typed [`Atom`]
13732 // numeric variant) bifurcation lifts onto the closed-set
13733 // [`Atom`] algebra via [`Atom::from_json_number`]. Pre-lift
13734 // this arm carried its own inline three-branch cascade
13735 // (`n.as_i64()` sink to [`Self::int`] then `n.as_f64()`
13736 // sink to [`Self::float`] then a `Self::int(0)` typed
13737 // floor for the structural-impossibility residual);
13738 // post-lift the WHOLE cascade binds at ONE typed projection
13739 // on the algebra so a delimiter swap of the numeric axis
13740 // (e.g. adding a `u64`-fitting arm for the
13741 // `serde-preserve-order` feature's `arbitrary_precision`
13742 // mode, extending [`Atom`] with a `Bigint` variant) extends
13743 // [`Atom::from_json_number`] ONCE — the outer [`Self::from_json`]
13744 // arm here delegates through the algebra with zero edits.
13745 // Structural dual of [`Atom::to_json`]'s [`Atom::Int`] /
13746 // [`Atom::Float`] arms one algebra layer over: the paired
13747 // FORWARD (typed variant → `JValue::Number`) AND INVERSE
13748 // (`JValue::Number` → typed variant) numeric-axis
13749 // projections both live on the closed-set [`Atom`] algebra,
13750 // and this outer [`Self::from_json`] arm binds to the
13751 // inverse peer at ONE typed method. Sibling-shape pin to
13752 // [`Self::Atom`]'s general delegation posture — the outer
13753 // `Sexp` layer wraps the atomic algebra's typed projection
13754 // via [`Self::Atom`] rather than re-deriving the
13755 // per-variant construction at this consumer.
13756 serde_json::Value::Number(n) => Self::Atom(Atom::from_json_number(n)),
13757 serde_json::Value::String(s) => Self::string(s.clone()),
13758 serde_json::Value::Array(items) => {
13759 Self::List(items.iter().map(Self::from_json).collect())
13760 }
13761 serde_json::Value::Object(map) => {
13762 let mut out = Vec::with_capacity(map.len() * 2);
13763 for (k, v) in map {
13764 out.push(Self::keyword(crate::domain::camel_to_kebab(k)));
13765 out.push(Self::from_json(v));
13766 }
13767 Self::List(out)
13768 }
13769 }
13770 }
13771
13772 pub fn as_symbol(&self) -> Option<&str> {
13773 self.as_atom().and_then(Atom::as_symbol)
13774 }
13775 pub fn as_keyword(&self) -> Option<&str> {
13776 self.as_atom().and_then(Atom::as_keyword)
13777 }
13778 pub fn as_string(&self) -> Option<&str> {
13779 self.as_atom().and_then(Atom::as_string)
13780 }
13781 pub fn as_int(&self) -> Option<i64> {
13782 self.as_atom().and_then(Atom::as_int)
13783 }
13784 /// `Some(f)` for `Atom::Float(f)`, AND `Some(n as f64)` for
13785 /// `Atom::Int(n)` — caller convenience at the numeric-kwarg
13786 /// boundary. The Int-widening face lives at this consumer layer
13787 /// rather than at [`Atom::as_float`] (strict per the typed-identity
13788 /// discipline pinned at [`Atom::as_int`]'s docstring); the typed
13789 /// soft-projection algebra on `Atom` stays strict, and the
13790 /// `Sexp::as_float` consumer composes the strict typed projection
13791 /// with a fallback widening branch on `Atom::as_int`.
13792 pub fn as_float(&self) -> Option<f64> {
13793 let a = self.as_atom()?;
13794 a.as_float().or_else(|| a.as_int().map(|n| n as f64))
13795 }
13796 pub fn as_bool(&self) -> Option<bool> {
13797 self.as_atom().and_then(Atom::as_bool)
13798 }
13799 /// `foo` or `"foo"` — useful for names that may be authored either way.
13800 ///
13801 /// Structural-lift composition: routes through [`Sexp::as_atom`] + the
13802 /// algebra-level [`Atom::as_symbol_or_string`] union projection — the
13803 /// same `as_atom().and_then(Atom::as_X)` composition pattern
13804 /// [`Sexp::as_symbol`] / [`Sexp::as_keyword`] / [`Sexp::as_string`] /
13805 /// [`Sexp::as_int`] / [`Sexp::as_bool`] route through on the
13806 /// per-variant axis. Lifts the disjunctive
13807 /// `self.as_symbol().or_else(|| self.as_string())` composition at this
13808 /// site's pre-lift body (TWO `Sexp::as_atom` traversals — one per
13809 /// per-variant projection) onto ONE typed-algebra union projection
13810 /// reached via ONE `Sexp::as_atom` traversal.
13811 ///
13812 /// Composition law: `s.as_symbol_or_string() == s.as_atom().and_then(Atom::as_symbol_or_string)`
13813 /// for every [`Sexp`] `s`. See [`Atom::as_symbol_or_string`] for the
13814 /// algebra-level peer's docstring (per-variant family completion +
13815 /// theory grounding).
13816 pub fn as_symbol_or_string(&self) -> Option<&str> {
13817 self.as_atom().and_then(Atom::as_symbol_or_string)
13818 }
13819
13820 /// The symbol in operator position — `Some(s)` iff this is a non-empty
13821 /// list whose first element is a symbol (`(defpoint …)` → `Some("defpoint")`).
13822 /// `None` for every other shape: a non-list (`foo`, `5`, `:kw`), the
13823 /// empty list `()`, and a list whose head is not a symbol (`(5 …)`,
13824 /// `(:kw …)`, `((nested) …)`).
13825 ///
13826 /// This is the *operator-position projection* — the structural query
13827 /// every form-dispatch site in the substrate keys on: "what operator
13828 /// does this form invoke?" Macroexpansion (`Expander::expand` looks up
13829 /// the head against the macro table; `macro_def_from` reads it to
13830 /// recognize a `defmacro` head) and the typed compilers
13831 /// (`compile_typed` / `compile_named_from_forms` match it against
13832 /// `T::KEYWORD`) all asked the same `self.as_list()?.first()?.as_symbol()`
13833 /// question inline. Naming it once makes "operator position" a primitive
13834 /// of the `Sexp` algebra rather than four byte-identical inline chains.
13835 ///
13836 /// This is the SOFT face of operator-position dispatch — it answers
13837 /// "is this form an invocation of some operator?" and yields `None`
13838 /// (skip / fall through) for everything that isn't, with no diagnostic.
13839 /// Its STRICT sibling is `TataraDomain::compile_from_sexp`, which on a
13840 /// matched-arity form distinguishes the empty-list and
13841 /// present-but-not-a-symbol head sub-modes to emit a rich
13842 /// `MissingHeadSymbol` rejection. The two are the dispatch (`head_symbol`)
13843 /// and the gate (`compile_from_sexp`) faces of the same projection;
13844 /// keeping both lets a site choose "skip silently" or "reject loudly"
13845 /// without re-deriving the head.
13846 ///
13847 /// `head_symbol` is the operator projection of [`Sexp::as_call`]: it
13848 /// keeps the head and discards the argument tail. The
13849 /// `as_list()?.first()?.as_symbol()` chain lives in ONE place
13850 /// (`as_call`); this is its first component.
13851 pub fn head_symbol(&self) -> Option<&str> {
13852 self.as_call().map(|(head, _)| head)
13853 }
13854
13855 /// Decompose a call form into its operator and argument tail —
13856 /// `Some((op, args))` iff this is a non-empty list whose first element
13857 /// is a symbol, where `op` is that head symbol and `args` is the
13858 /// remaining elements (`&self[1..]`, possibly empty). `None` for every
13859 /// shape `head_symbol` rejects: a non-list, the empty list, and a list
13860 /// whose head is present but not a symbol.
13861 ///
13862 /// This is the *call-form decomposition* — the structural shape of a
13863 /// Lisp invocation: an operator applied to an argument tail. It pairs
13864 /// the operator-position projection (`head_symbol`) with the argument
13865 /// tail every dispatch site reads immediately after matching the
13866 /// operator. Macroexpansion (`Expander::expand`) applies the matched
13867 /// macro to `&list[1..]`; the typed compilers (`compile_typed`,
13868 /// `compile_named_from_forms`) feed `&list[1..]` into
13869 /// `T::compile_from_args`. Before this query each site bound
13870 /// `as_list()` for the tail AND independently called `head_symbol()`
13871 /// (which itself re-derives `as_list().first()`) for the operator —
13872 /// two traversals of the same list, two projections. `as_call` yields
13873 /// both from one match, so the operator and its arguments can never
13874 /// drift out of agreement at a dispatch site.
13875 ///
13876 /// Soft face, like `head_symbol`: it answers "is this an invocation of
13877 /// some operator, and what are its arguments?" and yields `None` (skip
13878 /// / fall through) for everything that isn't, with no diagnostic. The
13879 /// strict gate sibling is `TataraDomain::compile_from_sexp`, which
13880 /// distinguishes the empty-list and non-symbol-head sub-modes to reject
13881 /// loudly.
13882 pub fn as_call(&self) -> Option<(&str, &[Sexp])> {
13883 let list = self.as_list()?;
13884 let head = list.first()?.as_symbol()?;
13885 Some((head, &list[1..]))
13886 }
13887
13888 /// Canonical call-form outer constructor — composes the atomic-
13889 /// payload construct family's [`Self::symbol`] (the head-position
13890 /// construct on the 6-of-12 atomic carving of [`SexpShape`]) with
13891 /// the residual-axis construct family's [`Self::list`] (via
13892 /// `std::iter::once(head_sexp).chain(args)`) to build a symbol-
13893 /// headed list-shaped [`Sexp`] value at ONE site on the closed-set
13894 /// [`Sexp`] algebra. The call-form section-for-retraction sibling
13895 /// of the existing [`Self::as_call`] soft-projection ([`Option<(&
13896 /// str, &[Sexp])>`]): where the projection soft-decomposes a
13897 /// symbol-headed list into its head symbol and argument tail, this
13898 /// constructor embeds a fresh (head string, item sequence) pair
13899 /// into the matching call-shaped wrapper.
13900 ///
13901 /// Composition sibling of the atomic-payload construct family
13902 /// ([`Self::symbol`], [`Self::keyword`], [`Self::string`],
13903 /// [`Self::int`], [`Self::float`], [`Self::boolean`] — routing
13904 /// through the typed [`Atom`] family on the 6-of-12 atomic carving),
13905 /// the quote-family construct family ([`Self::quote`],
13906 /// [`Self::quasiquote`], [`Self::unquote`], [`Self::unquote_splice`]
13907 /// — routing through the typed [`QuoteForm::wrap`] family on the
13908 /// 4-of-12 quote-family carving), and the residual-axis construct
13909 /// [`Self::list`] (routing owned or iterable item sequences into
13910 /// the tuple-variant on the 2-of-12 residual carving): those close
13911 /// the (construct, project) algebra dual on their respective
13912 /// STRUCTURAL carvings; this closes the (construct, project)
13913 /// algebra dual on the SYMBOL-HEADED-LIST TYPED DECOMPOSITION — the
13914 /// load-bearing shape every Lisp invocation, every `(defX …)`
13915 /// typed-domain call form, and every macroexpander template head
13916 /// takes on the outer [`Sexp`] algebra.
13917 ///
13918 /// Composition law (forward, through the outer algebra's atomic +
13919 /// residual construct families): `Sexp::call(head, args) ==
13920 /// Sexp::list(std::iter::once(Sexp::symbol(head)).chain(args))` for
13921 /// every `head: impl Into<String>` + `args: impl IntoIterator<Item
13922 /// = Sexp>`. The body binds through the SAME two construct methods
13923 /// consumers already reach for when threading a head-then-rest
13924 /// sequence into a call form — the composition law lifts that
13925 /// two-method inline pattern to ONE named query on the outer
13926 /// [`Sexp`] algebra.
13927 ///
13928 /// Round-trip law (section-for-retraction with the soft-projection
13929 /// sibling): for every `head: &str` + `args: Vec<Sexp>`,
13930 /// `Sexp::call(head, args.clone()).as_call() == Some((head,
13931 /// args.as_slice()))` — the outer algebra's call-form typed
13932 /// constructor pairs section-for-retraction with the outer
13933 /// algebra's soft call-form projection, and the (head symbol,
13934 /// args slice) cross-projection preserves identity. Keyword-
13935 /// matched round-trip law: for every `head: &str` + `args:
13936 /// Vec<Sexp>`, `Sexp::call(head, args.clone()).as_call_to(head) ==
13937 /// Some(args.as_slice())` — the keyword-typed projection recovers
13938 /// the args tail iff its argument keyword matches the constructor's
13939 /// head. Head-symbol composition law: `Sexp::call(head,
13940 /// args).head_symbol() == Some(head.as_str())` for every `head:
13941 /// impl Into<String>` + `args: impl IntoIterator<Item = Sexp>` —
13942 /// the head-position projection recovers the constructor's head
13943 /// byte-for-byte.
13944 ///
13945 /// Outer-shape composition law: `Sexp::call(head, args).shape() ==
13946 /// SexpShape::List` for every input — a call form is a list-shaped
13947 /// [`Sexp`], and the outer-shape identity binds through the typed-
13948 /// shape lattice at the residual arm. Structural-carving-marker
13949 /// composition law: `Sexp::call(head, args).as_structural_kind()
13950 /// == Some(StructuralKind::List)` — the residual-axis carving
13951 /// marker binds through the closed-set [`StructuralKind`] algebra
13952 /// at ONE arm, symmetric with the atomic-axis's `Sexp::X_atom(
13953 /// payload).as_atom_kind() == Some(AtomKind::X)` marker
13954 /// composition.
13955 ///
13956 /// Pre-lift the `Sexp::List(std::iter::once(Sexp::symbol(head))
13957 /// .chain(args).collect())` composition (or equivalently the
13958 /// `Sexp::List(vec![Sexp::symbol(head), args...])` welded triple)
13959 /// appeared inline at every consumer that builds a call-shaped
13960 /// [`Sexp`] value — well past the ≥2 PRIME-DIRECTIVE trigger once
13961 /// the call-form shape is named. Post-lift consumers that have a
13962 /// head string + an owned or iterable sequence of args bind to ONE
13963 /// typed-algebra method on the outer [`Sexp`] algebra with the
13964 /// `impl Into<String>` bound on the head absorbing `&str` /
13965 /// `String` / `&String` and the `impl IntoIterator<Item = Sexp>`
13966 /// bound on the args absorbing `Vec<Sexp>` / `[Sexp; N]` /
13967 /// `.map(...)` chains without a per-site `.collect::<Vec<Sexp>>()`
13968 /// coercion.
13969 ///
13970 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
13971 /// (head string, args sequence, [`Self::List`] tuple-variant
13972 /// constructor) triple binds at ONE typed-algebra method on the
13973 /// outer [`Sexp`] algebra, closing the call-form (construct,
13974 /// project) algebra dual pair with [`Self::as_call`] /
13975 /// [`Self::as_call_to`] / [`Self::head_symbol`]. THEORY.md §II.1
13976 /// invariant 2 — free middle; every consumer that has a head
13977 /// string + an owned or iterable sequence of args and wants to
13978 /// build a call-shaped [`Sexp`] routes through the SAME typed
13979 /// method, so a regression that drifts one consumer's construction
13980 /// from the others (e.g. a copy-edit that emits `Sexp::keyword(
13981 /// head)` for the head position, or that swaps in a `Sexp::string`
13982 /// head that [`Self::as_call`] then rejects at the projection
13983 /// site) cannot reach the substrate's runtime. THEORY.md §V.1 —
13984 /// knowable platform; the call-form typed-construct becomes a TYPE
13985 /// projection on the substrate's outer [`Sexp`] algebra sitting
13986 /// next to the typed-project family [`Self::as_call`] /
13987 /// [`Self::as_call_to`] rather than bare tuple-variant constructor
13988 /// paired with per-site `Sexp::List(vec![Sexp::symbol(...), ...])`
13989 /// discipline. THEORY.md §VI.1 — generation over composition; the
13990 /// call-form pair emerges from ONE typed-algebra composition
13991 /// through [`Self::list`] composed with [`Self::symbol`] rather
13992 /// than from per-consumer per-callsite literals; a future call-
13993 /// form shape extension (e.g. a keyword-headed call form for a
13994 /// Kernel-style applicative-vs-operative split) lands as ONE peer
13995 /// constructor on this algebra alongside the residual, quote-
13996 /// family, and atomic-payload construct families.
13997 #[must_use]
13998 pub fn call<H, I>(head: H, args: I) -> Self
13999 where
14000 H: Into<String>,
14001 I: IntoIterator<Item = Sexp>,
14002 {
14003 Self::list(std::iter::once(Self::symbol(head)).chain(args))
14004 }
14005
14006 /// Canonical named-call-form outer constructor — composes the call-
14007 /// form typed constructor [`Self::call`] with the atomic-payload
14008 /// construct family's [`Self::symbol`] (for the NAME slot) via
14009 /// `std::iter::once(Self::symbol(name)).chain(spec_args)` to build a
14010 /// `(head NAME spec_args…)` symbol-headed named list-shaped [`Sexp`]
14011 /// value at ONE site on the closed-set [`Sexp`] algebra. The named-
14012 /// call-form section-for-retraction sibling of the existing
14013 /// [`Self::as_named_call_to`] soft-projection ([`Option<crate::error::
14014 /// Result<(&str, &[Sexp])>>`]): where the projection soft-decomposes
14015 /// a `(<keyword> NAME spec_args…)` symbol-headed list into its NAME
14016 /// symbol and spec args tail through the named-form gate
14017 /// ([`crate::compile::split_name_slot`]), this constructor embeds a
14018 /// fresh `(head string, name string, spec_args sequence)` triple
14019 /// into the matching named-call-shaped wrapper. Composition sibling
14020 /// of the call-form construct [`Self::call`] on the outer algebra:
14021 /// where [`Self::call`] closes the (construct, project) dual on the
14022 /// CALL-FORM TYPED DECOMPOSITION (`(head args…)`), this closes the
14023 /// dual on the NAMED-CALL-FORM TYPED DECOMPOSITION (`(head NAME
14024 /// spec_args…)`) — the load-bearing shape every `(defX NAME …)`
14025 /// typed-domain named authoring form takes on the outer [`Sexp`]
14026 /// algebra, and the section-for-retraction dual of the
14027 /// [`crate::compile::split_name_slot`] gate at the value level.
14028 ///
14029 /// Composition law (forward, through the call-form + atomic-payload
14030 /// construct families): `Sexp::named_call(head, name, spec_args) ==
14031 /// Sexp::call(head, std::iter::once(Sexp::symbol(name)).chain(
14032 /// spec_args))` for every `head: impl Into<String>` + `name: impl
14033 /// Into<String>` + `spec_args: impl IntoIterator<Item = Sexp>`. The
14034 /// body binds through the SAME two construct methods consumers
14035 /// already reach for when threading a head-then-name-then-rest
14036 /// sequence into a named call form — the composition law lifts that
14037 /// two-method inline pattern to ONE named query on the outer
14038 /// [`Sexp`] algebra.
14039 ///
14040 /// Round-trip law (section-for-retraction with the named-form soft-
14041 /// projection): for every `head: &'static str` + `name: &str` +
14042 /// `spec_args: Vec<Sexp>`, `Sexp::named_call(head, name, spec_args
14043 /// .clone()).as_named_call_to(head) == Some(Ok((name, spec_args
14044 /// .as_slice())))` — the outer algebra's named-call-form typed
14045 /// constructor pairs section-for-retraction with the outer
14046 /// algebra's soft named-call-form projection, and the (head symbol,
14047 /// NAME symbol, spec args slice) cross-projection preserves
14048 /// identity. Call-form projection composition: `Sexp::named_call(
14049 /// head, name, spec_args).as_call() == Some((head,
14050 /// once(Sexp::symbol(name)).chain(spec_args).collect().as_slice()
14051 /// ))` — the call-form soft-projection recovers `(head, [name,
14052 /// spec_args…])` with the NAME symbol as the first arg, mirroring
14053 /// the [`Self::call`] round-trip on the encompassing call algebra.
14054 /// Keyword-matched round-trip law: for every `head: &'static str` +
14055 /// `name: &str` + `spec_args: Vec<Sexp>`, `Sexp::named_call(head,
14056 /// name, spec_args.clone()).as_call_to(head) == Some(
14057 /// [Sexp::symbol(name), spec_args…].as_slice())` — the keyword-
14058 /// typed projection recovers the NAME-headed args tail iff its
14059 /// argument keyword matches the constructor's head. Head-symbol
14060 /// composition law: `Sexp::named_call(head, name, spec_args)
14061 /// .head_symbol() == Some(head.as_str())` — the head-position
14062 /// projection recovers the constructor's head byte-for-byte.
14063 ///
14064 /// Outer-shape composition law: `Sexp::named_call(head, name,
14065 /// spec_args).shape() == SexpShape::List` for every input — a
14066 /// named call form is a list-shaped [`Sexp`], the outer-shape
14067 /// identity binds through the typed-shape lattice at the residual
14068 /// arm. Structural-carving-marker composition law: `Sexp::
14069 /// named_call(head, name, spec_args).as_structural_kind() ==
14070 /// Some(StructuralKind::List)` — the residual-axis carving marker
14071 /// binds through the closed-set [`StructuralKind`] algebra at ONE
14072 /// arm, symmetric with [`Self::call`]'s residual-arm marker
14073 /// composition.
14074 ///
14075 /// Named-form gate composition law: `crate::compile::split_name_slot(
14076 /// &Sexp::named_call(head, name, spec_args).as_call_to(head)
14077 /// .unwrap(), head) == Ok((name, spec_args.as_slice()))` — the
14078 /// substrate's named-form arity + NAME-shape gate accepts every
14079 /// output of this constructor byte-for-byte, closing the section-
14080 /// for-retraction pair at the gate level as well as at the
14081 /// projection level. A constructor emission that drifts into a
14082 /// missing-NAME shape (empty spec_args yields `(head)`, which the
14083 /// call-form projection recovers but the named-form gate rejects
14084 /// with `NamedFormMissingName`) or a non-symbol-NAME shape
14085 /// (`Sexp::keyword(name)` for the NAME position, which the gate
14086 /// rejects with `NamedFormNonSymbolName`) becomes structurally
14087 /// impossible — the `impl Into<String>` NAME bound admits string
14088 /// payloads only, and the [`Self::symbol`] wrap routes to the
14089 /// symbol atom variant `as_symbol_or_string` accepts.
14090 ///
14091 /// Pre-lift the `Sexp::call(head, std::iter::once(Sexp::symbol(
14092 /// name)).chain(spec_args))` composition (or equivalently the
14093 /// `Sexp::List(vec![Sexp::symbol(head), Sexp::symbol(name),
14094 /// spec_args...])` welded quadruple) appeared inline at every
14095 /// consumer that builds a `(defX NAME …)`-shaped [`Sexp`] value
14096 /// — well past the ≥2 PRIME-DIRECTIVE trigger once the named
14097 /// call-form shape is named. Post-lift consumers that have a head
14098 /// string + a NAME string + an owned or iterable sequence of spec
14099 /// args bind to ONE typed-algebra method on the outer [`Sexp`]
14100 /// algebra with the two `impl Into<String>` bounds absorbing `&str`
14101 /// / `String` / `&String` on both string positions and the
14102 /// `impl IntoIterator<Item = Sexp>` bound on the spec args
14103 /// absorbing `Vec<Sexp>` / `[Sexp; N]` / `.map(...)` chains without
14104 /// a per-site `.collect::<Vec<Sexp>>()` coercion.
14105 ///
14106 /// Frontier inspiration: Racket's `syntax-parse`
14107 /// `(~datum keyword) name:id spec ...` pattern binds the NAME slot
14108 /// through the `name:id` capture binder and consumers reference it
14109 /// downstream; the constructor peer on the same surface is
14110 /// `syntax-e` composed with `datum->syntax` wrapping a
14111 /// `(list #'keyword name-id spec-list ...)` triple. `Sexp::
14112 /// named_call` is the unstructured-Rust peer — a section-for-
14113 /// retraction constructor on the outer algebra that mirrors the
14114 /// `~datum keyword name:id spec ...` pattern's NAME capture on the
14115 /// construct side. Tree-sitter's `query`-matched named captures
14116 /// have the same shape on the tree side: the query pattern
14117 /// binds a NAME capture, the constructor peer (`ts_node_new`
14118 /// composed with `ts_node_field_set`) embeds a fresh NAME child at
14119 /// the corresponding field slot. The typed structural rejection
14120 /// chain the substrate's named-form gate emits
14121 /// ([`crate::error::LispError::NamedFormMissingName`],
14122 /// [`crate::error::LispError::NamedFormNonSymbolName`]) is
14123 /// preserved by construction — the constructor cannot emit a
14124 /// value the gate rejects, symmetric with the `~datum` /
14125 /// `name:id` reader-side rejection that fires BEFORE any
14126 /// downstream binding sees the drifted shape.
14127 ///
14128 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
14129 /// (head string, NAME string, spec args sequence, [`Self::call`]
14130 /// call-form constructor) quadruple binds at ONE typed-algebra
14131 /// method on the outer [`Sexp`] algebra, closing the named-call-
14132 /// form (construct, project) algebra dual pair with
14133 /// [`Self::as_named_call_to`] / [`Self::as_named_call_to_any`] on
14134 /// the projection side and [`crate::compile::split_name_slot`] on
14135 /// the gate side. THEORY.md §II.1 invariant 2 — free middle;
14136 /// every consumer that has a head + NAME + spec args and wants to
14137 /// build a named-call-shaped [`Sexp`] routes through the SAME
14138 /// typed method, so a regression that drifts one consumer's
14139 /// construction from the others (e.g. a copy-edit that emits
14140 /// `Sexp::keyword(name)` for the NAME position, which the
14141 /// named-form gate would reject with `NamedFormNonSymbolName`)
14142 /// cannot reach the substrate's runtime. THEORY.md §V.1 —
14143 /// knowable platform; the named-call-form typed-construct becomes
14144 /// a TYPE projection on the substrate's outer [`Sexp`] algebra
14145 /// sitting next to the typed-project family [`Self::
14146 /// as_named_call_to`] / [`Self::as_named_call_to_any`] +
14147 /// [`crate::ast::iter_named_calls_to`] /
14148 /// [`crate::ast::iter_named_calls_to_any`] rather than a per-site
14149 /// inline composition. THEORY.md §VI.1 — generation over
14150 /// composition; the named-call-form pair emerges from ONE typed-
14151 /// algebra composition through [`Self::call`] composed with
14152 /// [`Self::symbol`] rather than from per-consumer per-callsite
14153 /// literals; a future named-form shape extension (e.g. a
14154 /// dotted-NAME form, or a typed-NAME form where the NAME slot
14155 /// carries a compile-time-decoded typed witness) lands as ONE
14156 /// peer constructor on this algebra alongside the call-form,
14157 /// residual, quote-family, and atomic-payload construct
14158 /// families.
14159 #[must_use]
14160 pub fn named_call<H, N, I>(head: H, name: N, spec_args: I) -> Self
14161 where
14162 H: Into<String>,
14163 N: Into<String>,
14164 I: IntoIterator<Item = Sexp>,
14165 {
14166 Self::call(head, std::iter::once(Self::symbol(name)).chain(spec_args))
14167 }
14168
14169 /// Decompose a call form into its argument tail IFF the head matches the
14170 /// supplied `keyword` — `Some(args)` iff this is a non-empty list whose
14171 /// first element is a symbol equal to `keyword`, where `args` is the
14172 /// remaining elements (`&self[1..]`, possibly empty). `None` for every
14173 /// shape `as_call` rejects AND for every call whose head is present but
14174 /// differs from `keyword`.
14175 ///
14176 /// This is the *keyword-typed call decomposition* — the natural
14177 /// extension of [`Sexp::as_call`] for the "is this a call to ONE
14178 /// specific operator?" question every typed-domain dispatch site asks
14179 /// after macroexpansion. [`compile_typed`](crate::compile::compile_typed)
14180 /// and [`compile_named_from_forms`](crate::compile::compile_named_from_forms)
14181 /// both opened the same two-step chain inline —
14182 /// `if let Some((head, args)) = form.as_call() { if head == T::KEYWORD { … } }`
14183 /// — at every form they walked; the chain IS this projection. Naming
14184 /// it lifts "is this form a call to T?" from a two-step inline pattern
14185 /// to ONE structural query on the `Sexp` algebra. A regression that
14186 /// drifts one consumer's comparison from `==` to `!= `, or that
14187 /// compares against a different label than `T::KEYWORD` (e.g.
14188 /// substring-grepping the rendered head), becomes structurally
14189 /// impossible: there is exactly one implementation both dispatchers
14190 /// route through.
14191 ///
14192 /// Soft face, like the rest of the `as_*` family: it answers "is this
14193 /// a call to `keyword`, and what are its arguments?" and yields `None`
14194 /// for everything that isn't (skip / fall through), with no
14195 /// diagnostic. The strict gate sibling is
14196 /// `TataraDomain::compile_from_sexp`, which distinguishes the
14197 /// not-a-list / empty-list / non-symbol-head / wrong-keyword
14198 /// sub-modes to reject loudly. The two are the dispatch
14199 /// (`as_call_to`) and the gate (`compile_from_sexp`) faces of the
14200 /// same projection; keeping both lets a site choose "skip silently"
14201 /// or "reject loudly" without re-deriving the head.
14202 ///
14203 /// Structural identity binding it to its siblings:
14204 /// * `as_call_to(keyword) == as_call().and_then(|(h, args)| (h == keyword).then_some(args))`
14205 /// * `as_call_to(keyword).is_some() == (head_symbol() == Some(keyword))`
14206 ///
14207 /// The returned `&[Sexp]` borrows from the list's tail verbatim — no
14208 /// copy, no allocation, same lifetime as [`Sexp::as_call`]'s tail.
14209 ///
14210 /// Slice-side sibling: [`iter_calls_to`] lifts this per-form projection
14211 /// onto a `&[Sexp]`, yielding the args slices of every matching form in
14212 /// source order — the substrate's typed-keyword filter over a batch of
14213 /// forms, structurally bound to this per-form projection via the
14214 /// closed-form composition
14215 /// `iter_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_call_to(k))`.
14216 pub fn as_call_to(&self, keyword: &str) -> Option<&[Sexp]> {
14217 let (head, args) = self.as_call()?;
14218 (head == keyword).then_some(args)
14219 }
14220
14221 /// Decompose a call form whose head decodes through a caller-supplied
14222 /// classifier — `Some((decoded, args))` iff this is a non-empty list
14223 /// whose first element is a symbol AND `decode(head)` returns
14224 /// `Some(decoded)`, where `args` is the remaining elements
14225 /// (`&self[1..]`, possibly empty). `None` for every shape
14226 /// [`Sexp::as_call`] rejects AND for every call whose head is present
14227 /// but `decode` rejects.
14228 ///
14229 /// This is the *typed-decoded call decomposition* — the closure-typed
14230 /// extension of [`Sexp::as_call_to`] for the "is this a call whose head
14231 /// belongs to a CLOSED SET (or a LIVE REGISTRY) that decodes to a typed
14232 /// witness?" question. Where [`Sexp::as_call_to`] filters by ONE
14233 /// constant keyword, `as_call_to_any` filters AND TYPES by a caller-
14234 /// supplied projection — every dispatch site that asks "is this form
14235 /// an invocation of any of N operators, decoded as a typed enum or
14236 /// resolved against a runtime table?" binds to ONE structural query
14237 /// on the `Sexp` algebra. Two consumers route through it:
14238 ///
14239 /// * The macro-expander's `macro_def_from` — closed-set classifier:
14240 /// `as_call_to_any(MacroDefHead::from_keyword)` decides which of
14241 /// `{defmacro, defpoint-template, defcheck}` a top-level form
14242 /// invokes, decoded to the typed `MacroDefHead` enum. Pre-lift the
14243 /// site opened the same three-step chain inline — `let Some(list)
14244 /// = form.as_list()…; let Some(head) = form.head_symbol()…; let
14245 /// Some(decoded) = MacroDefHead::from_keyword(head)…`.
14246 /// * The macro-expander's `Expander::expand` — live-registry
14247 /// classifier: `as_call_to_any(|h| self.macros.get(h))` decides
14248 /// which of the registered macros (a `HashMap<String, MacroDef>`
14249 /// populated by `expand_program`'s `defmacro` recognition) a form
14250 /// invokes, decoded to `&MacroDef`. Pre-lift the site opened the
14251 /// same `as_list() + as_call() + self.macros.get(head)` chain
14252 /// inline — `as_list()` for the children-walk fallthrough,
14253 /// `as_call()` for the (head, args) pair (which itself re-derives
14254 /// `as_list()` internally), and `self.macros.get(head)` for the
14255 /// registry lookup.
14256 ///
14257 /// Naming the projection lifts "is this form a call to any of N
14258 /// operators, decoded to T?" from the three-step inline pattern to
14259 /// ONE structural query — closed-set enum classifier OR live-registry
14260 /// HashMap classifier, the family primitive is uniform under both.
14261 ///
14262 /// Soft face, like the rest of the `as_*` family: it answers "is this
14263 /// a call whose head decodes through `F`, and what are its arguments?"
14264 /// and yields `None` for everything that isn't (skip / fall through),
14265 /// with no diagnostic. The strict gate sibling stays
14266 /// `TataraDomain::compile_from_sexp` — that distinguishes the
14267 /// not-a-list / empty-list / non-symbol-head / wrong-keyword sub-modes
14268 /// to reject loudly for a single-keyword consumer. The two are the
14269 /// closed-set-decoded dispatch (`as_call_to_any`) and the
14270 /// single-keyword gate (`compile_from_sexp`) faces of the typed-domain
14271 /// recognition problem; keeping both lets a site choose "skip
14272 /// silently if the head isn't ours" or "reject loudly if the head
14273 /// isn't the exact keyword" without re-deriving the head.
14274 ///
14275 /// Structural identity binding it to its siblings:
14276 /// * `as_call_to_any(decode) == as_call().and_then(|(h, args)| decode(h).map(|d| (d, args)))`
14277 /// * `as_call_to(k) == as_call_to_any(|h| (h == k).then_some(())).map(|(_, a)| a)` (modulo the discarded `()`)
14278 /// * `as_call_to_any(decode).is_some() == as_call().map_or(false, |(h, _)| decode(h).is_some())`
14279 ///
14280 /// The returned `&[Sexp]` borrows from the list's tail verbatim — no
14281 /// copy, no allocation, same lifetime as [`Sexp::as_call`]'s tail.
14282 /// `T` is owned because `decode` is `FnOnce(&str) -> Option<T>` and a
14283 /// `&'_ str` borrow into the head symbol would not outlive the helper
14284 /// boundary; consumers projecting to a typed `Copy` enum (e.g.
14285 /// `MacroDefHead`) get the value directly, consumers projecting to a
14286 /// borrowed `&'static str` (a closed-set head) project to
14287 /// `&'static str` and inherit the static lifetime through the
14288 /// classifier.
14289 ///
14290 /// Slice-side sibling: [`iter_calls_to_any`] lifts this per-form
14291 /// projection onto a `&[Sexp]`, yielding the `(decoded, &[Sexp])`
14292 /// pair of every matching form in source order — the substrate's
14293 /// typed-decoded filter over a batch of forms, structurally bound
14294 /// to this per-form projection via the closed-form composition
14295 /// `iter_calls_to_any(forms, decode) == forms.iter().filter_map(|f|
14296 /// f.as_call_to_any(&mut decode))`. The slice-side primitive
14297 /// promotes the closure constraint from [`FnOnce`] (per-form, one
14298 /// call per invocation) to [`FnMut`] (slice-side, one call per
14299 /// element) so a decoder that captures mutable state (a counter, a
14300 /// registry cache) maintains state across the batch walk.
14301 pub fn as_call_to_any<F, T>(&self, decode: F) -> Option<(T, &[Sexp])>
14302 where
14303 F: FnOnce(&str) -> Option<T>,
14304 {
14305 let (head, args) = self.as_call()?;
14306 decode(head).map(|d| (d, args))
14307 }
14308
14309 /// Decompose a named call form (a `(<keyword> NAME :k v …)` shape) whose
14310 /// head decodes through a caller-supplied classifier — `Some(Ok((decoded,
14311 /// name, spec_args)))` iff this is a non-empty list whose first element
14312 /// is a symbol AND `decode(head)` returns `Some((decoded, kw))` AND the
14313 /// remaining elements split cleanly into a NAME slot (symbol or string
14314 /// at position 1) and a spec args tail (position 2..), `Some(Err(…))` iff
14315 /// the head decodes but the NAME slot is missing
14316 /// ([`LispError::NamedFormMissingName`]) or non-symbol-or-string
14317 /// ([`LispError::NamedFormNonSymbolName`]), `None` for every shape
14318 /// [`Sexp::as_call_to_any`] rejects AND for every call whose head is
14319 /// present but `decode` returns `None` for.
14320 ///
14321 /// This is the *per-form named-classifier projection* — the per-form
14322 /// peer of [`iter_named_calls_to_any`] on the slice algebra and of
14323 /// [`crate::macro_expand::Expander::expand_and_collect_named_calls_to_any`]
14324 /// on the expander surface. Closes the (per-form × classifier × named)
14325 /// corner of the soft-dispatch cube the substrate's per-form algebra
14326 /// (`as_call_to{,_any}`) and slice algebra (`iter_calls_to{,_any}`,
14327 /// `iter_named_calls_to{,_any}`) collectively shape — pre-lift the cube
14328 /// at the per-form × named corner was "(composed inline at each named
14329 /// consumer)" (the documented gap the cube table inside
14330 /// [`iter_named_calls_to_any`] called out), post-lift the per-form ×
14331 /// named row binds to ONE primitive every per-form named consumer
14332 /// composes through:
14333 ///
14334 /// | | bare-kwargs | named NAME-then-kwargs |
14335 /// |----------------|------------------------------|--------------------------------------|
14336 /// | per-form | [`Sexp::as_call_to_any`] | `as_named_call_to_any` (this) |
14337 /// | slice | [`iter_calls_to_any`] | [`iter_named_calls_to_any`] |
14338 /// | expander | `expand_and_collect_calls_to_any` | `expand_and_collect_named_calls_to_any` |
14339 ///
14340 /// The slice-side [`iter_named_calls_to_any`] now routes through THIS
14341 /// per-form primitive via the SAME `forms.iter().filter_map(_)`
14342 /// skeleton [`iter_calls_to_any`] uses to route through
14343 /// [`Sexp::as_call_to_any`], so a regression that drifts ONE row's
14344 /// instrumentation, span-aware borrow walker, or fused-iterator
14345 /// invariant from the bare row to the named row (or vice versa) is
14346 /// structurally impossible.
14347 ///
14348 /// Composes [`Sexp::as_call_to_any`] with
14349 /// [`crate::compile::split_name_slot`]: the classifier filter precedes
14350 /// the named gate, mirroring how `split_name_slot` is composed AFTER
14351 /// the classifier-decoded args tail is already in hand inside
14352 /// [`iter_named_calls_to_any`]. Decoder signature `FnOnce(&str) ->
14353 /// Option<(T, &'static str)>` pairs the typed witness `T` with the
14354 /// canonical static keyword threaded through the
14355 /// `NamedFormMissingName.keyword` / `NamedFormNonSymbolName.keyword`
14356 /// slots of the named-form gate — the `&'static` constraint pins the
14357 /// same compile-time discipline [`crate::compile::split_name_slot`]'s
14358 /// `keyword: &'static str` parameter pins at the slice-side boundary,
14359 /// AND that the slice-side decoder signature pins on the slice
14360 /// algebra.
14361 ///
14362 /// Three-arm result shape — `Option<Result<…>>` — preserves both the
14363 /// classifier filter face (`None` for "not our head, skip silently",
14364 /// matching the per-form soft-projection posture of every other `as_*`
14365 /// method on `Sexp`) AND the named gate face (`Err` for "matched head
14366 /// but malformed NAME", surfacing the typed structural-rejection
14367 /// variants `LispError::NamedFormMissingName` /
14368 /// `LispError::NamedFormNonSymbolName` the slice-side and expander-
14369 /// surface consumers already short-circuit on). A consumer that wants
14370 /// "fold over every per-form result, short-circuiting on the first
14371 /// malformed NAME" composes `.transpose()` (yielding
14372 /// `Result<Option<…>>`) and `?`-routes the outer `Result`; a consumer
14373 /// that wants "skip every non-matching form AND every malformed
14374 /// matched form" composes `.and_then(|res| res.ok())` (yielding
14375 /// `Option<(T, &str, &[Sexp])>`); a consumer that wants the raw
14376 /// three-arm shape pattern-matches directly.
14377 ///
14378 /// Two plausible future consumer shapes the per-form named-classifier
14379 /// projection admits with no boilerplate:
14380 /// * **LSP hover tooltip** — an authoring tool that surfaces a
14381 /// tooltip on the symbol under the cursor wants to ask "is THIS
14382 /// form (the one I just resolved to under the cursor) a named
14383 /// call to any registered domain, decoded to a typed kind, with
14384 /// the borrowed NAME slot extracted for the tooltip body?". Pre-
14385 /// lift the tool would re-derive `form.as_call_to_any(decode)
14386 /// .and_then(|((kind, kw), args)| split_name_slot(args,
14387 /// kw).ok().map(|(name, rest)| (kind, name, rest)))` inline;
14388 /// post-lift the tool binds to ONE primitive.
14389 /// * **REPL single-form dispatcher** — a `:dispatch <classifier>
14390 /// <form>` command that walks a single form through the
14391 /// registry classifier, reporting the typed kind AND the NAME
14392 /// slot (for "you said `(defmonitor my-monitor …)`, I see
14393 /// `Monitor` named `my-monitor` with 3 spec args"). Pre-lift
14394 /// the REPL would re-derive the same inline composition; post-
14395 /// lift the REPL binds to ONE primitive, sibling shape to how
14396 /// [`Sexp::as_call_to_any`] backs the slice-side dispatcher
14397 /// [`iter_calls_to_any`].
14398 ///
14399 /// Structural identity binding it to its siblings:
14400 /// * `as_named_call_to_any(decode) == as_call_to_any(decode).map(|((d, kw), args)| split_name_slot(args, kw).map(|(name, rest)| (d, name, rest)))`
14401 /// * `as_named_call_to(k) == as_named_call_to_any(|h| (h == k).then_some(((), k))).map(|res| res.map(|(_, name, rest)| (name, rest)))`
14402 /// * `as_named_call_to_any(decode).is_none() == as_call_to_any(decode).is_none()` (the classifier filter face is identical to the bare-kwargs sibling's)
14403 ///
14404 /// The returned `&str` NAME slot and `&[Sexp]` spec args tail borrow
14405 /// from `&self` verbatim — no copy, no allocation, same lifetime as
14406 /// [`Sexp::as_call_to_any`]'s tail AND [`crate::compile::split_name_slot`]'s
14407 /// pair. `T` is owned because the underlying [`Sexp::as_call_to_any`]
14408 /// classifier is `FnOnce(&str) -> Option<(T, &'static str)>` and `T`
14409 /// must outlive the helper boundary; consumers projecting to a typed
14410 /// `Copy` enum (e.g. a closed-set `Kind`) get the value directly,
14411 /// consumers projecting to a borrowed `&'static str` (a closed-set
14412 /// head sourced from `ClosedSet::ALL.label()`) project to `&'static
14413 /// str` and inherit the static lifetime through the classifier.
14414 ///
14415 /// Soft face on the classifier filter, strict face on the named gate:
14416 /// "is this a named call whose head decodes through `F`, and what
14417 /// are its NAME and spec args?" yielding `None` for "not our head"
14418 /// (skip / fall through, no diagnostic) AND `Some(Err(…))` for "our
14419 /// head but malformed NAME" (reject loudly, structural variant). The
14420 /// soft-classifier-then-strict-named composition matches the
14421 /// slice-side `iter_named_calls_to_any` yielded `Result` shape (with
14422 /// non-matching forms skipped by the iterator filter) and the
14423 /// expander-surface `expand_and_collect_named_calls_to_any` collect
14424 /// shape (with `Result::collect` short-circuiting on the first
14425 /// malformed NAME) — every layer of the cube preserves both faces.
14426 ///
14427 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
14428 /// per-form × classifier × named cell of the soft-dispatch cube is a
14429 /// CONSEQUENCE of [`Sexp::as_call_to_any`] + [`crate::compile::split_name_slot`],
14430 /// named on the substrate's `Sexp` algebra rather than re-derived
14431 /// inline at every per-form named consumer site. THEORY.md §V.1 —
14432 /// knowable platform; the per-form named-classifier projection
14433 /// becomes a NAMED primitive on the `Sexp` algebra, discoverable by
14434 /// any future authoring tool (LSP, REPL, `tatara-check`) that holds
14435 /// a single form in isolation. THEORY.md §II.1 invariant 2 — free
14436 /// middle; the slice-side sibling [`iter_named_calls_to_any`] now
14437 /// routes through this per-form primitive via the same
14438 /// `forms.iter().filter_map(_)` skeleton the bare-kwargs row uses
14439 /// to route through [`Sexp::as_call_to_any`], so the bare and named
14440 /// rows share ONE filter-and-fuse implementation skeleton on the
14441 /// `Sexp`/`&[Sexp]` algebras.
14442 ///
14443 /// Frontier inspiration: MLIR's `mlir::dyn_cast<NamedOpInterface>(op)`
14444 /// — the typed downcast from a polymorphic IR node onto a NAMED-op
14445 /// interface that exposes both the typed witness AND the
14446 /// symbol-name accessor is the MLIR idiom; `as_named_call_to_any` is
14447 /// the unstructured-Rust peer on the substrate's `Sexp` algebra,
14448 /// with `Option<Result<(T, &str, &[Sexp])>>` standing in for MLIR's
14449 /// typed-downcast-then-name-accessor pair, and the `Result` face
14450 /// carrying the typed structural rejection MLIR encodes via verifier
14451 /// diagnostics. Racket's `syntax-parse` `~or* ((~datum defX) name:id
14452 /// arg ...) ((~datum defY) name:id arg ...)` on a single syntax
14453 /// object — typed named-form decomposition with `name:id` capture
14454 /// binding is the Racket idiom; this method is the per-form
14455 /// Rust-typed peer with the typed structural rejection
14456 /// (`NamedFormMissingName` / `NamedFormNonSymbolName`) preserved
14457 /// across the boundary.
14458 pub fn as_named_call_to_any<F, T>(
14459 &self,
14460 decode: F,
14461 ) -> Option<crate::error::Result<(T, &str, &[Sexp])>>
14462 where
14463 F: FnOnce(&str) -> Option<(T, &'static str)>,
14464 {
14465 self.as_call_to_any(decode).map(|((decoded, kw), args)| {
14466 let (name, spec_args) = crate::compile::split_name_slot(args, kw)?;
14467 Ok((decoded, name, spec_args))
14468 })
14469 }
14470
14471 /// Decompose a named call form whose head matches a constant
14472 /// `keyword` — `Some(Ok((name, spec_args)))` iff this is a non-empty
14473 /// list whose first element is the symbol `keyword` AND the remaining
14474 /// elements split cleanly into a NAME slot and a spec args tail,
14475 /// `Some(Err(…))` iff the head matches but the NAME slot is missing
14476 /// or non-symbol-or-string, `None` for every shape
14477 /// [`Sexp::as_call_to`] rejects.
14478 ///
14479 /// Constant-keyword sibling of [`Sexp::as_named_call_to_any`] and
14480 /// per-form sibling of [`iter_named_calls_to`] on the slice algebra.
14481 /// Routes through the typed-decoded sibling with a constant-classifier
14482 /// decoder (`|h| (h == keyword).then_some(((), keyword))`) — the same
14483 /// constant-classifier composition [`Sexp::as_call_to`] uses to route
14484 /// through [`Sexp::as_call_to_any`] on the bare-kwargs axis, and that
14485 /// [`iter_named_calls_to`] uses to route through
14486 /// [`iter_named_calls_to_any`] on the slice algebra. The discarded
14487 /// `()` typed witness (`then_some(((), keyword))`) is consumed by the
14488 /// wrapper projection so the consumer's per-form mapper sees only the
14489 /// `(name, spec_args)` borrowed pair, matching the bare projection
14490 /// signature on the named axis.
14491 ///
14492 /// `keyword: &'static str` threads verbatim through the
14493 /// `NamedFormMissingName.keyword` / `NamedFormNonSymbolName.keyword`
14494 /// slots of the named-form gate — same `&'static` discipline
14495 /// [`crate::compile::split_name_slot`] pins at its boundary, AND that
14496 /// [`iter_named_calls_to`] pins on the slice algebra. Consumers that
14497 /// want a runtime keyword whose lifetime is shorter use
14498 /// [`Sexp::as_named_call_to_any`] directly with a constant-classifier
14499 /// decoder that converts post-resolution.
14500 ///
14501 /// Structural identity binding it to its siblings:
14502 /// * `as_named_call_to(k) == as_named_call_to_any(|h| (h == k).then_some(((), k))).map(|res| res.map(|(_, name, rest)| (name, rest)))`
14503 /// * `as_named_call_to(k).is_none() == as_call_to(k).is_none()`
14504 /// * `iter_named_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_named_call_to(k))`
14505 ///
14506 /// Theory anchor: see [`Sexp::as_named_call_to_any`] — the constant-
14507 /// keyword sibling shares the same lift posture, threading the
14508 /// `&'static str` keyword constraint through the named-form gate's
14509 /// canonical-keyword slot rather than admitting an arbitrary runtime
14510 /// keyword.
14511 pub fn as_named_call_to(
14512 &self,
14513 keyword: &'static str,
14514 ) -> Option<crate::error::Result<(&str, &[Sexp])>> {
14515 self.as_named_call_to_any(move |h| (h == keyword).then_some(((), keyword)))
14516 .map(|res| res.map(|(_, name, rest)| (name, rest)))
14517 }
14518
14519 /// Decompose an unquote-family form into its typed marker and inner
14520 /// expression — `Some((UnquoteForm::Unquote, inner))` iff this is `,x`
14521 /// (a [`Sexp::Unquote`] wrapper), `Some((UnquoteForm::Splice, inner))`
14522 /// iff this is `,@x` (a [`Sexp::UnquoteSplice`] wrapper), `None` for
14523 /// every other shape (Quote, Quasiquote, Nil, Atom, List).
14524 ///
14525 /// This is the *unquote-family projection* — the typed-marker peer of
14526 /// [`Sexp::as_call`] for the macro-template substitution surface. Where
14527 /// [`Sexp::as_call`] decomposes `(op args …)` into a `(head, args)`
14528 /// pair, `as_unquote` decomposes `,x` / `,@x` into a `(form, inner)`
14529 /// pair where `form: UnquoteForm` is the closed-set typed marker
14530 /// (`Unquote` for `,`, `Splice` for `,@`) and `inner: &Sexp` is the
14531 /// borrowed body. The pairing of `Sexp::Unquote ↔ UnquoteForm::Unquote`
14532 /// and `Sexp::UnquoteSplice ↔ UnquoteForm::Splice` is the structural
14533 /// invariant the macro-expander's substitution path keys every
14534 /// rejection on — naming the projection lifts the pair from
14535 /// per-callsite discipline (two `Sexp::Unquote(inner)` arms paired
14536 /// with two `UnquoteForm::Unquote` literals at distinct sites, two
14537 /// `Sexp::UnquoteSplice(inner)` arms paired with two
14538 /// `UnquoteForm::Splice` literals at distinct sites) into ONE typed
14539 /// projection both expansion strategies route through.
14540 ///
14541 /// Three consumers in [`macro_expand`](crate::macro_expand) route
14542 /// through this primitive:
14543 /// * `compile_node` (bytecode-template compile path) — `,x` becomes
14544 /// `TemplateOp::Subst(idx)`, `,@x` becomes `TemplateOp::Splice(idx)`;
14545 /// both arms share the gate-1+gate-2 composition
14546 /// `resolve_unquote_in_params(inner, params, form)?` keyed on the
14547 /// typed `form` projection.
14548 /// * `substitute` top-level (substitute fallback path) — `,x` resolves
14549 /// to its bound value, `,@x` rejects with
14550 /// `LispError::SpliceOutsideList` (a splice form with no containing
14551 /// list to flatten into).
14552 /// * `substitute` list-inner (substitute fallback path's per-item
14553 /// walk) — `,@x` items splice their bound list/nil/scalar value
14554 /// into the assembled list builder via
14555 /// [`crate::macro_expand::splice_value_into`]; non-splice items
14556 /// recurse into `substitute`.
14557 ///
14558 /// Pre-lift each site opened the same per-variant match arms —
14559 /// `Sexp::Unquote(inner) => … UnquoteForm::Unquote …` and
14560 /// `Sexp::UnquoteSplice(inner) => … UnquoteForm::Splice …` —
14561 /// independently. The (Sexp variant, UnquoteForm variant) pairing was
14562 /// load-bearing across distinct sites yet only enforced by callsite
14563 /// discipline. Post-lift the pair binds at ONE projection function the
14564 /// type system threads through `(UnquoteForm, &Sexp)`: a regression
14565 /// that drifts ONE site's pairing (e.g. a future emitter that matches
14566 /// `Sexp::Unquote(_)` but threads `UnquoteForm::Splice` into
14567 /// `unquote_target_symbol` — type-checks but renders a misleading
14568 /// diagnostic) becomes structurally impossible.
14569 ///
14570 /// Soft face, like the rest of the `as_*` family on `Sexp`: it answers
14571 /// "is this form an unquote-family marker, and what does it wrap?" and
14572 /// yields `None` for everything that isn't (skip / fall through), with
14573 /// no diagnostic. The strict siblings —
14574 /// [`crate::macro_expand::splice_value_into`] for the bound-list
14575 /// coercion, `non_symbol_unquote_target` /
14576 /// `splice_outside_list` for the per-failure-mode rejections — keep
14577 /// their loud-reject posture; this projection is the dispatch face the
14578 /// soft pre-rejection walk binds to.
14579 ///
14580 /// Structural identity binding it to the unquote-family variants:
14581 /// * `as_unquote() == Some((UnquoteForm::Unquote, inner))` iff `self == Sexp::Unquote(inner)`
14582 /// * `as_unquote() == Some((UnquoteForm::Splice, inner))` iff `self == Sexp::UnquoteSplice(inner)`
14583 /// * `as_unquote().is_some() == matches!(self, Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
14584 ///
14585 /// The returned `&Sexp` borrows the inner box's body verbatim — no
14586 /// clone, no allocation — same lifetime as `&self`. The closed-set
14587 /// guarantee on [`UnquoteForm`] (exactly `Unquote ⊎ Splice`) is
14588 /// threaded through this projection's return tuple, so consumers that
14589 /// pattern-match on `form: UnquoteForm` get rustc-enforced
14590 /// exhaustiveness — a future `Sexp` variant must extend `UnquoteForm`
14591 /// AND this match arm together (or stay outside the unquote family
14592 /// and project to `None`), eliminating the silent two-site
14593 /// extension-drift this lift was already designed to forbid.
14594 ///
14595 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
14596 /// `(Sexp::Unquote, UnquoteForm::Unquote)` and
14597 /// `(Sexp::UnquoteSplice, UnquoteForm::Splice)` pairings appear ≥3
14598 /// times across `compile_node` (2 arms) + `substitute` (top-level +
14599 /// list-inner) — past the PRIME-DIRECTIVE trigger once the structural
14600 /// shape is named. THEORY.md §V.1 — knowable platform; the
14601 /// unquote-family projection becomes a NAMED primitive on the
14602 /// substrate's `Sexp` algebra rather than per-site `Sexp::Unquote(_)
14603 /// | Sexp::UnquoteSplice(_)` inline matches paired with per-site
14604 /// `UnquoteForm::Unquote` / `UnquoteForm::Splice` literals.
14605 /// THEORY.md §II.1 invariant 1 — typed entry; the macro-template
14606 /// substitution surface's typed-marker projection IS the rust-level
14607 /// typed-entry gate's structural component, lifted from per-site
14608 /// duplication onto ONE rust method the substrate's diagnostic
14609 /// promotions hang off of. THEORY.md §II.1 invariant 2 — free middle;
14610 /// both expansion strategies (bytecode `compile_node` and substitute
14611 /// fallback `substitute`) route through the SAME projection, so a
14612 /// regression that drifts ONE strategy's (Sexp variant, UnquoteForm
14613 /// variant) pairing from the other cannot reach the substrate's
14614 /// runtime — the type system binds both strategies to the
14615 /// projection's single emission shape.
14616 ///
14617 /// Frontier inspiration: Racket's `syntax-parse` `~or* (~unquote stx)
14618 /// (~unquote-splice stx)` pattern — every macro-template pattern over
14619 /// `,id` / `,@id` binds to ONE typed decomposition that surfaces the
14620 /// marker identity alongside the inner expression; the substrate's
14621 /// `as_unquote` is the Rust-typed peer of that pattern, lifted onto
14622 /// the `Sexp` algebra with [`UnquoteForm`] standing in for Racket's
14623 /// pattern-class identity. MLIR's typed-IR projection
14624 /// `mlir::dyn_cast<UnquoteFamilyOp>(op)` — the typed downcast from a
14625 /// polymorphic IR node onto a closed-set op family is the MLIR idiom;
14626 /// `as_unquote` is the unstructured-projection peer on the substrate's
14627 /// `Sexp` algebra, with `Option<(UnquoteForm, &Sexp)>` standing in for
14628 /// MLIR's typed downcast result.
14629 pub fn as_unquote(&self) -> Option<(UnquoteForm, &Sexp)> {
14630 let (qf, inner) = self.as_quote_form()?;
14631 qf.as_unquote_form().map(|uf| (uf, inner))
14632 }
14633
14634 /// Soft projection onto the closed-set [`UnquoteForm`] template-
14635 /// substitution carving marker — the 2-of-12 carving of the
14636 /// [`SexpShape`](crate::error::SexpShape) algebra covering the two
14637 /// homoiconic template-substitution wrappers ([`Self::Unquote`] and
14638 /// [`Self::UnquoteSplice`]), which is itself a 2-of-4 subset of the
14639 /// quote-family carving ([`QuoteForm`]). Returns
14640 /// `Some(UnquoteForm::Unquote)` iff this is `,x` (a [`Self::Unquote`]
14641 /// wrapper), `Some(UnquoteForm::Splice)` iff this is `,@x` (a
14642 /// [`Self::UnquoteSplice`] wrapper), `None` for every other outer
14643 /// shape ([`Self::Nil`], every [`Self::Atom`] variant, [`Self::List`],
14644 /// and the two non-substitution quote-family wrappers [`Self::Quote`]
14645 /// and [`Self::Quasiquote`]).
14646 ///
14647 /// Direct value-level peer of the shape-level projection
14648 /// [`SexpShape::as_unquote_form`](crate::error::SexpShape::as_unquote_form)
14649 /// — the pair `(Sexp::as_unquote_form, SexpShape::as_unquote_form)`
14650 /// binds the (Sexp value, UnquoteForm carving marker) pairing at ONE
14651 /// typed method on each algebra, closing the unquote-subset cell of
14652 /// the (Sexp value → carving marker) matrix. Marker-only sibling of
14653 /// [`Self::as_unquote`] (which returns
14654 /// `Option<(UnquoteForm, &Sexp)>` — marker + wrapped inner) and
14655 /// direct 2-of-4 subset peer of [`Self::as_quote_form`] (which
14656 /// covers the 4-of-12 quote-family carving with `Option<(QuoteForm,
14657 /// &Sexp)>`). Post-lift the substrate's value-level marker-only
14658 /// carving-marker matrix closes ONE more cell: the atomic axis via
14659 /// [`Self::as_atom_kind`] (6-of-12), the residual axis via
14660 /// [`Self::as_structural_kind`] (2-of-12), the quote-family axis via
14661 /// `Self::as_quote_form().map(|(qf, _)| qf)` (4-of-12, marker + inner
14662 /// available via the pre-existing method), and now the unquote-
14663 /// subset axis via `Self::as_unquote_form` (2-of-12, marker only) —
14664 /// symmetric with the shape-level marker-only projection family on
14665 /// [`SexpShape`](crate::error::SexpShape).
14666 ///
14667 /// Composition laws (three-way agreement — bindings): for every
14668 /// `s: &Sexp`,
14669 /// `s.as_unquote_form() == s.as_unquote().map(|(uf, _)| uf) ==
14670 /// s.shape().as_unquote_form() ==
14671 /// s.as_quote_form().and_then(|(qf, _)| qf.as_unquote_form())`.
14672 /// Pre-lift the unquote-subset carving marker at the value level
14673 /// was reachable only via one of these three-step compositions —
14674 /// either through the parent [`Self::as_unquote`] projection
14675 /// (discarding the inner), through the shape algebra
14676 /// (`shape().as_unquote_form()`), or through the parent quote-family
14677 /// projection composed with the 2-of-4 subset gate
14678 /// [`QuoteForm::as_unquote_form`]. Post-lift the projection lands at
14679 /// ONE typed method on the value algebra, and all three compositions
14680 /// are pinned as agreement laws (see
14681 /// `sexp_as_unquote_form_agrees_with_as_unquote_map_marker_for_every_variant`,
14682 /// `sexp_as_unquote_form_agrees_with_shape_as_unquote_form_for_every_variant`,
14683 /// and
14684 /// `sexp_as_unquote_form_agrees_with_as_quote_form_and_quote_form_as_unquote_form_for_every_variant`
14685 /// in this module). A regression that drifts any of the four
14686 /// projections from the others surfaces immediately.
14687 ///
14688 /// Symmetric with [`Self::as_atom_kind`] and [`Self::as_structural_kind`]
14689 /// on the marker-only shape (returns just the closed-set marker, no
14690 /// inner-payload borrow) — where [`Self::as_quote_form`] and
14691 /// [`Self::as_unquote`] surface both the marker AND the wrapped
14692 /// inner `&Sexp` (because the four quote-family arms and the two
14693 /// substitution arms structurally carry a boxed inner value),
14694 /// `as_unquote_form` returns a marker-only projection: consumers that
14695 /// need the wrapped inner reach the marker-plus-inner sibling
14696 /// [`Self::as_unquote`], while consumers that only need the closed-
14697 /// set carving-marker identity (typed-pattern matchers, diagnostic
14698 /// filters, coverage sweeps, LSP/REPL structural-navigation gates)
14699 /// reach this projection and never allocate the tuple.
14700 ///
14701 /// Composes cleanly with [`UnquoteForm::marker`] to project the value-
14702 /// level substitution carving membership onto its canonical marker
14703 /// string (`,` / `,@`):
14704 /// `s.as_unquote_form().map(UnquoteForm::marker)` — the marker-string
14705 /// witness for the substitution subset, sibling to
14706 /// `s.as_atom_kind().map(AtomKind::label)` on the atomic axis, both
14707 /// routing through the closed-set marker enum's canonical-vocabulary
14708 /// projection at ONE canonical site (`UnquoteForm::marker` —
14709 /// itself composed through `QuoteForm::prefix`).
14710 ///
14711 /// Structural identity (pinned as a truth-table by
14712 /// `sexp_as_unquote_form_projects_each_variant_to_canonical_unquote_form`
14713 /// and `sexp_as_unquote_form_rejects_non_unquote_subset_outer_shapes`):
14714 /// * `as_unquote_form() == Some(UnquoteForm::Unquote)` iff `matches!(self, Sexp::Unquote(_))`
14715 /// * `as_unquote_form() == Some(UnquoteForm::Splice)` iff `matches!(self, Sexp::UnquoteSplice(_))`
14716 /// * `as_unquote_form() == None` iff `!matches!(self, Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
14717 ///
14718 /// Theory anchor: THEORY.md §V.1 — knowable platform; the substitution-
14719 /// subset carving marker at the value level becomes a NAMED
14720 /// primitive on the substrate's `Sexp` algebra rather than a per-
14721 /// site composition through either [`Self::as_unquote`] (discarding
14722 /// its `&Sexp` inner) or [`Self::shape`] (walking through the full
14723 /// 12-variant `SexpShape` closed set to arrive at the 2-of-12
14724 /// carving marker) or the parent [`Self::as_quote_form`] combined
14725 /// with [`QuoteForm::as_unquote_form`] (the 2-of-4 subset gate).
14726 /// THEORY.md §II.1 invariant 2 — free middle; every consumer that
14727 /// wants the substitution-subset carving identity without needing
14728 /// the wrapped inner (a future `tatara-check` predicate
14729 /// `(check-value-projects-to-unquote-subset …)` that filters
14730 /// diagnostics keyed on the substitution-subset cohort; a future LSP
14731 /// structural-navigation filter that keys on the substitution-subset
14732 /// carving membership at the value level; a future
14733 /// `TypedRewriter<TemplateOp>` sweep that walks `Sexp` values whose
14734 /// substitution-arm identity is `Some(UnquoteForm::_)` regardless of
14735 /// inner payload identity; a future REPL pretty-printer that chooses
14736 /// rendering paths keyed on the value-level substitution carving
14737 /// marker without needing the inner payload) binds to ONE typed
14738 /// method on the value algebra. THEORY.md §VI.1 — generation over
14739 /// composition; the (Sexp variant, UnquoteForm variant) pairing
14740 /// binds at ONE inherent method on the algebra rather than at three
14741 /// parallel compositions (`as_unquote().map(…)`, `shape()
14742 /// .as_unquote_form()`, `as_quote_form().and_then(|(qf, _)|
14743 /// qf.as_unquote_form())`), so a regression that drifts ONE
14744 /// composition's pairing from the others cannot reach the substrate's
14745 /// runtime — the type system binds all three compositions to the
14746 /// projection's single emission shape.
14747 ///
14748 /// Frontier inspiration: MLIR's `mlir::dyn_cast<UnquoteFamilyOp>(op)
14749 /// .map(|op| op.marker())` — every typed rewriter that only needs
14750 /// the op-family identity (without the op's operands) binds to the
14751 /// typed-downcast projection composed with an operand-discarding
14752 /// marker extract; `Sexp::as_unquote_form` is the marker-only peer
14753 /// on the substrate's `Sexp` algebra, with `Option<UnquoteForm>`
14754 /// standing in for MLIR's `Optional<OperationName>` marker-only
14755 /// downcast result. Racket's `syntax-parse` `~or* (~unquote _)
14756 /// (~unquote-splice _)` — every syntax-class pattern that keys on
14757 /// the substitution-subset marker identity without binding the
14758 /// inner form; `Sexp::as_unquote_form` is the Rust-typed peer that
14759 /// surfaces the marker identity through a single primitive on the
14760 /// syntax algebra.
14761 #[must_use]
14762 pub fn as_unquote_form(&self) -> Option<UnquoteForm> {
14763 self.as_unquote().map(|(uf, _)| uf)
14764 }
14765
14766 /// Decompose a quote-family form into its typed marker and inner
14767 /// expression — `Some((QuoteForm::Quote, inner))` iff this is `'x`
14768 /// (a [`Sexp::Quote`] wrapper), `Some((QuoteForm::Quasiquote, inner))`
14769 /// iff this is `` `x `` (a [`Sexp::Quasiquote`] wrapper),
14770 /// `Some((QuoteForm::Unquote, inner))` iff this is `,x` (a
14771 /// [`Sexp::Unquote`] wrapper), `Some((QuoteForm::UnquoteSplice, inner))`
14772 /// iff this is `,@x` (a [`Sexp::UnquoteSplice`] wrapper), `None` for
14773 /// every other shape (Nil, Atom, List).
14774 ///
14775 /// This is the *quote-family projection* — the typed-marker peer of
14776 /// [`Sexp::as_unquote`] generalized across all four homoiconic
14777 /// prefix-wrappers. Where [`Sexp::as_unquote`] keys the macro-template
14778 /// SUBSTITUTION surface on the closed pair `{Unquote, Splice}` (the
14779 /// two prefixes whose template-time semantic is substitution),
14780 /// `as_quote_form` keys the WIRE-SHAPE surfaces (Display rendering,
14781 /// Hash discrimination, canonical-form interop) on the closed superset
14782 /// `{Quote, Quasiquote, Unquote, UnquoteSplice}` — all four prefixes
14783 /// the reader can tokenize and the writer must round-trip. The
14784 /// `Sexp::as_unquote` projection now derives structurally from
14785 /// `as_quote_form`'s output via [`QuoteForm::as_unquote_form`] — the
14786 /// 2-of-4 subset gate — so the two projections share a SINGLE
14787 /// implementation site on the `Sexp` algebra and the
14788 /// (Sexp variant, QuoteForm variant) pairing binds at ONE rust
14789 /// function regardless of whether the consumer wants the substitution
14790 /// subset or the wire-shape superset.
14791 ///
14792 /// Three consumers in this file route through this primitive:
14793 /// * `Hash for Sexp` — the four `Quote`/`Quasiquote`/`Unquote`/
14794 /// `UnquoteSplice` arms (pre-lift each carrying its own
14795 /// `<discr>.hash(h); inner.hash(h)` body) collapse to ONE arm
14796 /// that routes through `as_quote_form` and reads the
14797 /// discriminator via [`QuoteForm::hash_discriminator`].
14798 /// * `Display for Sexp` — the four `write!(f, "<prefix>{inner}")`
14799 /// arms (pre-lift each carrying its own literal prefix string)
14800 /// collapse to ONE arm that routes through `as_quote_form` and
14801 /// reads the prefix via [`QuoteForm::prefix`].
14802 /// * [`Sexp::as_unquote`] — derives `Option<(UnquoteForm, &Sexp)>`
14803 /// by composing `as_quote_form` with [`QuoteForm::as_unquote_form`]
14804 /// (the 2-of-4 subset projection), so the macro-template
14805 /// substitution surface inherits the (Sexp variant, marker)
14806 /// pairing through this projection's typed dispatch rather than
14807 /// re-deriving its own arm-based match.
14808 ///
14809 /// The closed-set guarantee on [`QuoteForm`] (exactly
14810 /// `Quote ⊎ Quasiquote ⊎ Unquote ⊎ UnquoteSplice`) is threaded through
14811 /// this projection's return tuple, so consumers that pattern-match on
14812 /// `form: QuoteForm` get rustc-enforced exhaustiveness — a future
14813 /// `Sexp` wrapper variant must extend `QuoteForm` AND this match arm
14814 /// together (or stay outside the quote family and project to `None`),
14815 /// eliminating the silent multi-site extension-drift this lift was
14816 /// designed to forbid.
14817 ///
14818 /// Soft face, like the rest of the `as_*` family on `Sexp`: it
14819 /// answers "is this form a quote-family marker, and what does it
14820 /// wrap?" and yields `None` for everything that isn't (skip / fall
14821 /// through), with no diagnostic.
14822 ///
14823 /// Structural identity binding it to the quote-family variants and
14824 /// its `as_unquote` subset sibling:
14825 /// * `as_quote_form() == Some((QuoteForm::Quote, inner))` iff `self == Sexp::Quote(inner)`
14826 /// * `as_quote_form() == Some((QuoteForm::Quasiquote, inner))` iff `self == Sexp::Quasiquote(inner)`
14827 /// * `as_quote_form() == Some((QuoteForm::Unquote, inner))` iff `self == Sexp::Unquote(inner)`
14828 /// * `as_quote_form() == Some((QuoteForm::UnquoteSplice, inner))` iff `self == Sexp::UnquoteSplice(inner)`
14829 /// * `as_quote_form().is_some() == matches!(self, Sexp::Quote(_) | Sexp::Quasiquote(_) | Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
14830 /// * `as_unquote() == as_quote_form().and_then(|(qf, inner)| qf.as_unquote_form().map(|uf| (uf, inner)))`
14831 ///
14832 /// The returned `&Sexp` borrows the inner box's body verbatim — no
14833 /// clone, no allocation — same lifetime as `&self` and same posture
14834 /// as [`Sexp::as_unquote`]'s tail.
14835 ///
14836 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
14837 /// quote-family (Sexp variant, prefix string, hash discriminator)
14838 /// triple appeared inline at three sites (`Hash for Sexp`,
14839 /// `Display for Sexp`, `as_unquote`) — well past the ≥2 PRIME-DIRECTIVE
14840 /// trigger once the structural shape is named. THEORY.md §V.1 —
14841 /// knowable platform; the quote-family typed-marker projection becomes
14842 /// a NAMED primitive on the substrate's `Sexp` algebra rather than
14843 /// per-site inline matches paired with per-site discriminator literals
14844 /// and prefix literals. THEORY.md §II.1 invariant 1 — typed entry; the
14845 /// reader's prefix-to-variant dispatch ([`crate::reader::read_quoted`])
14846 /// AND the Display impl's variant-to-prefix dispatch are dual
14847 /// typed-entry / typed-exit gates over the same closed set; the
14848 /// `QuoteForm` algebra threads BOTH gates through ONE typed enum so a
14849 /// regression that drifts one side's prefix from the other (e.g. the
14850 /// reader gains a fifth prefix but the Display impl doesn't) is no
14851 /// longer a silent two-site divergence — rustc binds both sides to
14852 /// the same closed-set enum. THEORY.md §II.1 invariant 2 — free
14853 /// middle; the three consumers (Hash, Display, `as_unquote`) route
14854 /// through the SAME projection, so a regression that drifts ONE
14855 /// consumer's (Sexp variant, marker) pairing from the others cannot
14856 /// reach the substrate's runtime.
14857 ///
14858 /// Frontier inspiration: Racket's `syntax-parse` `~or* (~quote stx)
14859 /// (~quasiquote stx) (~unquote stx) (~unquote-splice stx)` pattern —
14860 /// every macro-template pattern over `'`/`` ` ``/`,`/`,@` binds to
14861 /// ONE typed decomposition that surfaces the marker identity
14862 /// alongside the inner expression; the substrate's `as_quote_form` is
14863 /// the Rust-typed peer of that pattern, lifted onto the `Sexp`
14864 /// algebra with `QuoteForm` standing in for Racket's pattern-class
14865 /// identity at the homoiconic prefix surface. MLIR's typed-IR
14866 /// projection `mlir::dyn_cast<QuoteFamilyOp>(op)` — the typed downcast
14867 /// from a polymorphic IR node onto a closed-set op family is the MLIR
14868 /// idiom; `as_quote_form` is the unstructured-projection peer on the
14869 /// substrate's `Sexp` algebra, with `Option<(QuoteForm, &Sexp)>`
14870 /// standing in for MLIR's typed downcast result.
14871 pub fn as_quote_form(&self) -> Option<(QuoteForm, &Sexp)> {
14872 match self {
14873 Self::Quote(inner) => Some((QuoteForm::Quote, inner)),
14874 Self::Quasiquote(inner) => Some((QuoteForm::Quasiquote, inner)),
14875 Self::Unquote(inner) => Some((QuoteForm::Unquote, inner)),
14876 Self::UnquoteSplice(inner) => Some((QuoteForm::UnquoteSplice, inner)),
14877 _ => None,
14878 }
14879 }
14880
14881 /// Soft projection onto the closed-set [`QuoteForm`] quote-family
14882 /// carving marker — the 4-of-12 carving of the [`SexpShape`] algebra
14883 /// covering the four homoiconic prefix-wrappers ([`Self::Quote`],
14884 /// [`Self::Quasiquote`], [`Self::Unquote`], [`Self::UnquoteSplice`]).
14885 /// Returns `Some(QuoteForm::Quote)` iff this is `'x` (a
14886 /// [`Self::Quote`] wrapper), `Some(QuoteForm::Quasiquote)` iff this
14887 /// is `` `x `` (a [`Self::Quasiquote`] wrapper),
14888 /// `Some(QuoteForm::Unquote)` iff this is `,x` (a [`Self::Unquote`]
14889 /// wrapper), `Some(QuoteForm::UnquoteSplice)` iff this is `,@x` (a
14890 /// [`Self::UnquoteSplice`] wrapper), `None` for every other outer
14891 /// shape ([`Self::Nil`], every [`Self::Atom`] variant, [`Self::List`]).
14892 ///
14893 /// Direct value-level peer of the shape-level projection
14894 /// [`SexpShape::as_quote_form`](crate::error::SexpShape::as_quote_form)
14895 /// — the pair `(Sexp::as_quote_form_marker, SexpShape::as_quote_form)`
14896 /// binds the (Sexp value, QuoteForm carving marker) pairing at ONE
14897 /// typed method on each algebra, closing the quote-family cell of
14898 /// the (Sexp value → carving marker) matrix at the marker-only
14899 /// value-level projection surface. Marker-only sibling of
14900 /// [`Self::as_quote_form`] (which returns `Option<(QuoteForm, &Sexp)>`
14901 /// — marker + wrapped inner). Post-lift the substrate's value-level
14902 /// marker-only carving-marker matrix closes its FINAL cell: the
14903 /// atomic axis via [`Self::as_atom_kind`] (6-of-12), the residual
14904 /// axis via [`Self::as_structural_kind`] (2-of-12), the unquote-
14905 /// subset axis via [`Self::as_unquote_form`] (2-of-12), and NOW the
14906 /// quote-family axis via `Self::as_quote_form_marker` (4-of-12) —
14907 /// symmetric with the shape-level marker-only projection family on
14908 /// [`SexpShape`](crate::error::SexpShape).
14909 ///
14910 /// Composition laws (two-way agreement — bindings): for every
14911 /// `s: &Sexp`,
14912 /// `s.as_quote_form_marker() == s.as_quote_form().map(|(qf, _)| qf)
14913 /// == s.shape().as_quote_form()`. Pre-lift the quote-family carving
14914 /// marker at the value level was reachable only via one of these
14915 /// two-step compositions — either through the parent
14916 /// [`Self::as_quote_form`] projection (discarding the wrapped inner
14917 /// via `.map(|(qf, _)| qf)`) or through the shape algebra
14918 /// (`s.shape().as_quote_form()`, walking the full 12-variant
14919 /// [`SexpShape`](crate::error::SexpShape) closed set to arrive at
14920 /// the 4-of-12 carving marker). Post-lift the projection lands at
14921 /// ONE typed method on the value algebra, and both compositions
14922 /// are pinned as agreement laws (see
14923 /// `sexp_as_quote_form_marker_agrees_with_as_quote_form_map_marker_for_every_variant`
14924 /// and
14925 /// `sexp_as_quote_form_marker_agrees_with_shape_as_quote_form_for_every_variant`
14926 /// in this module).
14927 ///
14928 /// Superset-gate contract with [`Self::as_unquote_form`]: for every
14929 /// `s: &Sexp`, `s.as_unquote_form().is_some()` implies
14930 /// `s.as_quote_form_marker().is_some()` (the 2-of-12 substitution
14931 /// subset is a proper subset of the 4-of-12 quote family). The two
14932 /// non-substitution quote-family wrappers ([`Self::Quote`] and
14933 /// [`Self::Quasiquote`]) satisfy `as_quote_form_marker().is_some()`
14934 /// AND `as_unquote_form().is_none()` — the value-level image of the
14935 /// 2-of-4 subset gate [`QuoteForm::as_unquote_form`]. Pinned by
14936 /// `sexp_as_quote_form_marker_extends_as_unquote_form_to_full_quote_family`.
14937 ///
14938 /// Structural identity binding it to the quote-family variants:
14939 /// * `as_quote_form_marker() == Some(QuoteForm::Quote)` iff `matches!(self, Sexp::Quote(_))`
14940 /// * `as_quote_form_marker() == Some(QuoteForm::Quasiquote)` iff `matches!(self, Sexp::Quasiquote(_))`
14941 /// * `as_quote_form_marker() == Some(QuoteForm::Unquote)` iff `matches!(self, Sexp::Unquote(_))`
14942 /// * `as_quote_form_marker() == Some(QuoteForm::UnquoteSplice)` iff `matches!(self, Sexp::UnquoteSplice(_))`
14943 /// * `as_quote_form_marker() == None` iff `!matches!(self, Sexp::Quote(_) | Sexp::Quasiquote(_) | Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
14944 ///
14945 /// Theory anchor: THEORY.md §V.1 — knowable platform; the quote-
14946 /// family carving marker at the value level becomes a NAMED
14947 /// primitive on the substrate's `Sexp` algebra rather than a per-
14948 /// site two-step composition through either [`Self::as_quote_form`]
14949 /// (discarding its `&Sexp` inner) or [`Self::shape`] (walking through
14950 /// the full 12-variant [`SexpShape`](crate::error::SexpShape) closed
14951 /// set to arrive at the 4-of-12 carving marker). THEORY.md §II.1
14952 /// invariant 2 — free middle; every consumer that wants the quote-
14953 /// family carving identity without needing the wrapped inner (a
14954 /// future `tatara-check` predicate `(check-value-projects-to-quote-
14955 /// family …)` that filters diagnostics keyed on the quote-family
14956 /// cohort; a future LSP structural-navigation filter that keys on
14957 /// the quote-family carving membership at the value level; a
14958 /// future `TypedRewriter<QuoteFamilyOp>` sweep that walks `Sexp`
14959 /// values whose quote-family arm identity is `Some(QuoteForm::_)`
14960 /// regardless of inner payload identity; a future REPL pretty-
14961 /// printer that chooses rendering paths keyed on the value-level
14962 /// quote-family carving marker without needing the inner payload)
14963 /// routes through ONE typed method rather than reaching into one of
14964 /// the two composition sites, and both compositions are pinned as
14965 /// agreement laws so a regression that drifts ONE composition's
14966 /// pairing from the other cannot reach the substrate's runtime.
14967 /// THEORY.md §VI.1 — generation over composition; the (Sexp variant,
14968 /// QuoteForm variant) pairing binds at ONE inherent method on the
14969 /// algebra rather than at two parallel compositions, so a future
14970 /// extension (e.g. a fifth `Sexp` quote-family wrapper) lands at
14971 /// ONE match arm in the parent `as_quote_form` projection and
14972 /// inherits through this method's structural composition.
14973 ///
14974 /// Frontier inspiration: MLIR's `mlir::dyn_cast<QuoteFamilyOp>(op)
14975 /// .map(|op| op.marker())` — every typed rewriter that only needs
14976 /// the op-family identity (without the op's operands) binds to the
14977 /// typed-downcast projection composed with an operand-discarding
14978 /// marker extract; `Sexp::as_quote_form_marker` is the marker-only
14979 /// peer on the substrate's `Sexp` algebra, with
14980 /// `Option<QuoteForm>` standing in for MLIR's
14981 /// `Optional<OperationName>` marker-only downcast result. Racket's
14982 /// `syntax-parse` `~or* (~quote _) (~quasiquote _) (~unquote _)
14983 /// (~unquote-splice _)` — every syntax-class pattern that keys on
14984 /// the quote-family marker identity without binding the inner form;
14985 /// `Sexp::as_quote_form_marker` is the Rust-typed peer that
14986 /// surfaces the marker identity through a single primitive on the
14987 /// syntax algebra.
14988 #[must_use]
14989 pub fn as_quote_form_marker(&self) -> Option<QuoteForm> {
14990 self.as_quote_form().map(|(qf, _)| qf)
14991 }
14992
14993 /// Quote-family projection, asserted-total face of [`Sexp::as_quote_form`].
14994 /// Returns `(QuoteForm, &Sexp)` verbatim — same borrowed-inner posture,
14995 /// same closed-set marker — but panics with [`QUOTE_FAMILY_PROJECTION_INVARIANT`]
14996 /// instead of yielding `None` for non-quote-family variants. Use AFTER
14997 /// an outer pattern match has narrowed the discriminant union to the
14998 /// quote family (`Sexp::Quote(_) | Sexp::Quasiquote(_) | Sexp::Unquote(_) |
14999 /// Sexp::UnquoteSplice(_)`); the panic message states the invariant the
15000 /// caller's outer pattern already proves.
15001 ///
15002 /// Pre-lift the five production-site quote-family-arm consumers —
15003 /// `Hash for Sexp::hash_discriminator`, `Display for Sexp::prefix`,
15004 /// `domain::sexp_shape`, `domain::sexp_to_json`, `interop::iac_forge_tag` —
15005 /// each carried a verbatim copy of the 4-arm wildcard pattern AND a
15006 /// verbatim copy of the inline
15007 /// `.as_quote_form().expect("matched quote-family variant must project
15008 /// to Some via as_quote_form")` re-projection. The `(pattern, expect
15009 /// message)` pair appeared bit-for-bit at FIVE sites. Post-lift the
15010 /// expect message lives at ONE named const and the projection-with-
15011 /// assertion lives at ONE primitive on the `Sexp` algebra; the five
15012 /// callsites collapse to ONE typed query each. A future quote-family
15013 /// extension that drifts ONE site's panic text from the others becomes
15014 /// structurally impossible (one const, one method); a future site that
15015 /// needs the same "outer-narrowed, total projection" shape lands on
15016 /// this primitive directly without re-deriving the expect literal.
15017 ///
15018 /// `#[track_caller]` ensures a panic surfaces the consumer's source
15019 /// position, not this projection's — so the diagnostic stays
15020 /// load-bearing under the lift.
15021 ///
15022 /// Sibling posture to the `expect_*` family of typed-projection
15023 /// asserted-total faces across the substrate's closed-set algebras
15024 /// (`Option::expect`, `Result::expect`) — the assertion is the same
15025 /// shape, the message is named on the algebra it asserts about.
15026 ///
15027 /// # Panics
15028 ///
15029 /// Panics with [`QUOTE_FAMILY_PROJECTION_INVARIANT`] if `self` is not
15030 /// a quote-family variant. The outer pattern match at every caller
15031 /// site is the proof of the invariant; the panic is the static
15032 /// fall-through for a regression that drifts that proof.
15033 ///
15034 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
15035 /// (4-arm wildcard pattern, expect re-projection) pair appeared bit-
15036 /// for-bit at five production sites — well past the ≥2 PRIME-DIRECTIVE
15037 /// trigger. THEORY.md §V.1 — knowable platform; the panic message and
15038 /// the projection-with-assertion are now ONE named primitive on the
15039 /// substrate's `Sexp` algebra, structurally binding the invariant
15040 /// across every consumer that asserts an outer narrowing.
15041 #[must_use]
15042 #[track_caller]
15043 pub fn expect_quote_form(&self) -> (QuoteForm, &Sexp) {
15044 self.as_quote_form()
15045 .expect(QUOTE_FAMILY_PROJECTION_INVARIANT)
15046 }
15047
15048 /// Stable, per-outer-variant byte discriminator for the substrate's
15049 /// [`Hash for Sexp`] cache-key projection — `0` for [`Self::Nil`],
15050 /// `1` for [`Self::Atom`], `2` for [`Self::List`], `3` for
15051 /// [`Self::Quote`], `4` for [`Self::Quasiquote`], `5` for
15052 /// [`Self::Unquote`], `6` for [`Self::UnquoteSplice`]. Composes
15053 /// through [`Self::shape`] into
15054 /// [`crate::error::SexpShape::hash_discriminator`], which in turn
15055 /// composes through the three closed-set sub-carvings' discriminator
15056 /// methods: [`crate::error::StructuralKind::hash_discriminator`] for
15057 /// the two structural-residual arms `{0, 2}`,
15058 /// [`crate::error::QuoteForm::hash_discriminator`] for the four
15059 /// quote-family arms `{3..=6}`, and the outer atomic marker byte
15060 /// `1u8` for the six atomic-payload arms (whose inner
15061 /// [`crate::ast::AtomKind::hash_discriminator`] `{0..=5}` partition
15062 /// nests inside [`Hash for Atom`] rather than surfacing here). The
15063 /// outer-`Sexp` cache-key algebra now closes at FIVE typed layers
15064 /// (outer `Sexp` → [`crate::error::SexpShape`] → three sub-carvings)
15065 /// with rustc-enforced consistency across each.
15066 ///
15067 /// The byte values are load-bearing because the macro-expansion cache
15068 /// ([`crate::macro_expand::Expander`]'s cache) keys on the hash of
15069 /// `(macro_name, args)` — changing a discriminator silently
15070 /// invalidates every cached expansion across the substrate.
15071 ///
15072 /// The seven outer-variant arms partition `{0, 1, 2, 3, 4, 5, 6}`
15073 /// injectively — closed-set-typed intra-Sexp injectivity that
15074 /// composes through the intermediate
15075 /// [`crate::error::SexpShape::hash_discriminator`] shape-level
15076 /// projection (12 arms → 7 bytes; the six atomic-shape arms
15077 /// collapse to the outer Atom marker byte `1u8`; the two
15078 /// structural-residual arms surface `{0, 2}`; the four quote-family
15079 /// arms surface `{3..=6}`). Together the four sub-algebras (this
15080 /// outer method + shape + three sub-carvings) jointly cover the
15081 /// entire outer-Sexp discriminator space through ONE typed method
15082 /// per algebra layer. A future eighth `Sexp` variant (e.g. a
15083 /// hypothetical `Vector` for `#(...)` reader syntax, `Map` for
15084 /// `{...}`, or `Char` for `#\x`) picks a fresh cache-key byte
15085 /// outside `{0..=6}` (e.g. `7u8`), extends the closed-set
15086 /// [`crate::error::SexpShape`] enum + its
15087 /// `hash_discriminator` (plus either [`crate::error::StructuralKind`]
15088 /// or a fresh sub-algebra) in lockstep — rustc binds the
15089 /// consistency through exhaustiveness over each closed enum.
15090 ///
15091 /// Pre-lift this outer method dispatched over the seven `Sexp`
15092 /// variants directly and routed the three structural + four
15093 /// quote-family arms into the two sub-carvings' discriminator
15094 /// methods with the Atom arm inline at `1u8` — the intermediate
15095 /// [`crate::error::SexpShape`] shape-level projection did not
15096 /// exist, so a consumer with a typed [`crate::error::SexpShape`]
15097 /// identity in hand had to re-embed into a `Sexp` value to reach
15098 /// the outer cache-key byte. Post-lift the outer method routes
15099 /// through [`Self::shape`] into
15100 /// [`crate::error::SexpShape::hash_discriminator`]; the shape-level
15101 /// projection is the missing algebra layer between the outer `Sexp`
15102 /// and the three sub-carvings, and consumers with a typed shape
15103 /// identity now reach the outer cache-key byte at ONE typed method
15104 /// per algebra layer without a re-embed.
15105 ///
15106 /// `pub(crate)` because the byte-discriminator surface is an
15107 /// implementation detail of the substrate's `Hash for Sexp` cache-
15108 /// key contract; exposing it publicly would leak the cache-key shape
15109 /// through the API without enabling any external consumer the public
15110 /// projections ([`Self::as_atom`], [`Self::as_list`],
15111 /// [`Self::as_quote_form`]) don't already serve. Same posture as
15112 /// [`crate::error::SexpShape::hash_discriminator`],
15113 /// [`crate::ast::AtomKind::hash_discriminator`],
15114 /// [`crate::error::QuoteForm::hash_discriminator`], and
15115 /// [`crate::error::StructuralKind::hash_discriminator`].
15116 #[must_use]
15117 pub(crate) fn hash_discriminator(&self) -> u8 {
15118 self.shape().hash_discriminator()
15119 }
15120
15121 /// Cross-crate canonical iac-forge tag for the outer [`Sexp`] value —
15122 /// the OUTER-VALUE peer of the shape-level [`crate::error::SexpShape
15123 /// ::iac_forge_tag`] one algebra layer down. `Some(&'static str)` for
15124 /// the four homoiconic prefix-wrapper arms — `Self::Quote →
15125 /// Some("quote")`, `Self::Quasiquote → Some("quasiquote")`,
15126 /// `Self::Unquote → Some("unquote")`, `Self::UnquoteSplice →
15127 /// Some("unquote-splicing")` — and `None` for the outer atomic-payload
15128 /// arm ([`Self::Atom`]) AND the two structural-residual arms
15129 /// ([`Self::Nil`], [`Self::List`]). The 4-of-7 partial projection on
15130 /// the outer-`Sexp` algebra surfaces
15131 /// [`crate::ast::QuoteForm::iac_forge_tag`]'s cross-crate canonical-
15132 /// form tag surface at the outermost value-carrier algebra level,
15133 /// composed through the pre-existing [`Self::shape`] projection and
15134 /// [`crate::error::SexpShape::iac_forge_tag`]'s shape-level partial
15135 /// projection.
15136 ///
15137 /// Composition law: `sexp.iac_forge_tag() ==
15138 /// sexp.shape().iac_forge_tag()` for every `sexp: &Sexp` — the outer-
15139 /// `Sexp` cross-crate canonical-form tag surface routes through
15140 /// [`Self::shape`] into the shape-level partial projection, which in
15141 /// turn composes through [`crate::error::SexpShape::as_quote_form`]
15142 /// with [`crate::ast::QuoteForm::iac_forge_tag`]'s canonical 4-of-4
15143 /// closed-set tag projection. Post-lift the outer-`Sexp` cross-crate
15144 /// canonical-form tag surface closes at FOUR typed layers: outer
15145 /// [`Self::iac_forge_tag`] (7-arm outer dispatch on the outer
15146 /// [`Sexp`] algebra, this method) → shape-level
15147 /// [`crate::error::SexpShape::iac_forge_tag`] (12-arm shape-level
15148 /// dispatch on the [`crate::error::SexpShape`] algebra) →
15149 /// quote-family carving [`crate::error::SexpShape::as_quote_form`]
15150 /// (4-of-12 quote-family sub-carving) → sub-carving tag
15151 /// [`crate::ast::QuoteForm::iac_forge_tag`] (4-arm quote-family
15152 /// sub-carving's canonical-form tag projection).
15153 ///
15154 /// Pre-lift a consumer with a typed [`Sexp`] value in hand (a
15155 /// generation-side canonical-form emitter, a downstream iac-forge
15156 /// attestation site, an LSP / REPL / audit-trail metric keyed on the
15157 /// observed outer value) wanting the cross-crate iac-forge canonical
15158 /// tag string had to spell the two-step composition
15159 /// `sexp.shape().iac_forge_tag()` at every callsite, or route through
15160 /// [`Self::as_quote_form_marker`] composed with
15161 /// [`crate::ast::QuoteForm::iac_forge_tag`] via `map` as the
15162 /// `crate::interop` (removed) `From<&Sexp> for iac_forge::SExpr` impl does
15163 /// for its four quote-family arms via [`Self::expect_quote_form`]
15164 /// composed with [`crate::ast::QuoteForm::iac_forge_tag`]. Post-lift
15165 /// the outer-`Sexp` canonical-form tag projection binds at ONE
15166 /// typed-algebra method on the outer value-carrier — the SEVENTH
15167 /// consumer of the outer-`Sexp` projection surface (sibling of
15168 /// [`Self::shape`], [`Self::type_name`],
15169 /// [`Self::hash_discriminator`], [`Self::as_atom`], [`Self::as_list`],
15170 /// [`Self::as_quote_form`], [`Self::as_quote_form_marker`],
15171 /// [`Self::as_unquote`], [`Self::as_unquote_form`]), matching the
15172 /// same shape-composition posture [`Self::hash_discriminator`] takes
15173 /// through the outer → shape one-step delegation.
15174 ///
15175 /// The `Option<&'static str>` return shape mirrors
15176 /// [`crate::error::SexpShape::iac_forge_tag`]'s partial-projection
15177 /// shape one algebra level down — the outer-`Sexp` seven-arm closed
15178 /// set's projection PARTIALIZES on the three non-quote-family shapes
15179 /// (`Nil`, `Atom`, `List`) exactly as the shape-level twelve-arm
15180 /// closed set's projection PARTIALIZES on the eight non-quote-family
15181 /// shapes. The kernel's outer cardinality (three: `Nil` / `Atom` /
15182 /// `List`) matches the shape-level kernel's cardinality (eight)
15183 /// through [`Self::shape`]'s six-atomic-arms → outer `Atom` collapse
15184 /// — the outer three-arm kernel `{Nil, Atom, List}` corresponds to
15185 /// the shape-level eight-arm kernel `{Nil, Symbol, Keyword, String,
15186 /// Int, Float, Bool, List}` under the outer → shape projection.
15187 ///
15188 /// The `&'static str` lifetime is load-bearing: every iac-forge
15189 /// consumer projects through this method into the canonical
15190 /// 2-element-list head without an allocation, parallel to how
15191 /// [`crate::ast::QuoteForm::iac_forge_tag`] on the sub-carving,
15192 /// [`crate::error::SexpShape::iac_forge_tag`] on the shape-level
15193 /// projection, and [`crate::error::UnquoteForm::iac_forge_tag`] on
15194 /// the template-substitution subset project their respective closed
15195 /// sets. A future eighth [`Sexp`] variant (e.g. a hypothetical
15196 /// `Vector` for `#(...)` reader syntax, `Map` for `{...}`, `Char` for
15197 /// `#\x`) extends [`crate::error::SexpShape`] (adding a `None`-arm
15198 /// non-quote-family shape) — this method picks up the new arm's
15199 /// `None` mechanically through the shape composition, with rustc's
15200 /// exhaustiveness binding the extension end-to-end at
15201 /// [`crate::error::SexpShape::as_quote_form`]'s closed match.
15202 ///
15203 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (outer
15204 /// `Sexp` variant, canonical iac-forge tag) pairing becomes a TYPE
15205 /// projection on the outermost value-carrier algebra rather than an
15206 /// inline `.shape().iac_forge_tag()` two-step at every consumer. A
15207 /// typo or swap at the projection site is no longer a runtime tag
15208 /// drift but a compile error against the typed composition — the
15209 /// `Sexp` ↔ `SexpShape` ↔ `QuoteForm` ↔ tag string chain is rustc-
15210 /// enforced end-to-end. THEORY.md §II.1 invariant 2 — free middle;
15211 /// the (outer value, canonical iac-forge tag) pairing now binds at
15212 /// ONE site on the outer-`Sexp` algebra, composing through the pre-
15213 /// existing shape-level partial projection rather than duplicating
15214 /// the four-arm match here. THEORY.md §VI.1 — generation over
15215 /// composition; the outer-`Sexp` cross-crate canonical-form tag
15216 /// surface closes at FOUR typed layers (outer → shape → carving →
15217 /// sub-carving-tag), each keyed on the SAME canonical-form tag
15218 /// projection carried at the closed-set sub-carving level.
15219 ///
15220 /// Frontier inspiration: MLIR's `mlir::Operation::getName()` typed
15221 /// projection composed with `mlir::OperationName::getStringRef()` —
15222 /// narrowing an operation-carrier value through its typed op-name
15223 /// identity yields the canonical cross-boundary string identity in
15224 /// ONE typed composition. [`Self::iac_forge_tag`] is the Rust-typed
15225 /// peer where the "project to shape" step ([`Self::shape`]) composes
15226 /// with the "read the shape's canonical tag" step
15227 /// ([`crate::error::SexpShape::iac_forge_tag`]) into ONE outer-value
15228 /// projection.
15229 #[must_use]
15230 pub fn iac_forge_tag(&self) -> Option<&'static str> {
15231 self.shape().iac_forge_tag()
15232 }
15233
15234 /// Canonical reader-punctuation prefix for the outer [`Sexp`] value —
15235 /// the OUTER-VALUE peer of the shape-level
15236 /// [`crate::error::SexpShape::prefix`] one algebra layer down.
15237 /// `Some(&'static str)` for the four homoiconic prefix-wrapper arms —
15238 /// `Self::Quote → Some("'")`, `Self::Quasiquote → Some("`")`,
15239 /// `Self::Unquote → Some(",")`, `Self::UnquoteSplice → Some(",@")` —
15240 /// and `None` for the outer atomic-payload arm ([`Self::Atom`]) AND
15241 /// the two structural-residual arms ([`Self::Nil`], [`Self::List`]).
15242 /// The 4-of-7 partial projection on the outer-`Sexp` algebra surfaces
15243 /// [`crate::ast::QuoteForm::prefix`]'s reader-punctuation surface at
15244 /// the outermost value-carrier algebra level, composed through the
15245 /// pre-existing [`Self::shape`] projection and
15246 /// [`crate::error::SexpShape::prefix`]'s shape-level partial
15247 /// projection.
15248 ///
15249 /// Composition law: `sexp.prefix() == sexp.shape().prefix()` for
15250 /// every `sexp: &Sexp` — the outer-`Sexp` reader-punctuation surface
15251 /// routes through [`Self::shape`] into the shape-level partial
15252 /// projection, which in turn composes through
15253 /// [`crate::error::SexpShape::as_quote_form`] with
15254 /// [`crate::ast::QuoteForm::prefix`]'s canonical 4-of-4 closed-set
15255 /// prefix projection. Post-lift the outer-`Sexp` reader-punctuation
15256 /// surface closes at FOUR typed layers: outer [`Self::prefix`]
15257 /// (7-arm outer dispatch on the outer [`Sexp`] algebra, this method)
15258 /// → shape-level [`crate::error::SexpShape::prefix`] (12-arm shape-
15259 /// level dispatch on the [`crate::error::SexpShape`] algebra) →
15260 /// quote-family carving [`crate::error::SexpShape::as_quote_form`]
15261 /// (4-of-12 quote-family sub-carving) → sub-carving prefix
15262 /// [`crate::ast::QuoteForm::prefix`] (4-arm quote-family sub-
15263 /// carving's canonical reader-punctuation projection).
15264 ///
15265 /// Pre-lift a consumer with a typed [`Sexp`] value in hand (an
15266 /// [`fmt::Display for Sexp`] impl that renders the four quote-family
15267 /// arms as `<prefix><inner>`, an LSP hover / REPL completion that
15268 /// echoes the source-punctuation prefix of a wrapper value, an
15269 /// audit-trail metric keyed on the observed outer value) wanting
15270 /// the canonical reader-punctuation prefix string had to spell the
15271 /// two-step composition `sexp.shape().prefix()` at every callsite,
15272 /// or route through [`Self::as_quote_form_marker`] composed with
15273 /// [`crate::ast::QuoteForm::prefix`] via `map` as the
15274 /// [`fmt::Display for Sexp`] impl does for its four quote-family
15275 /// arms via [`Self::expect_quote_form`] composed with
15276 /// [`crate::ast::QuoteForm::prefix`]. Post-lift the outer-`Sexp`
15277 /// reader-punctuation projection binds at ONE typed-algebra method
15278 /// on the outer value-carrier — the natural next rung on the
15279 /// trajectory mirroring the [`Self::iac_forge_tag`] →
15280 /// [`crate::error::SexpShape::iac_forge_tag`] ladder one vocabulary
15281 /// axis over, matching the same shape-composition posture
15282 /// [`Self::hash_discriminator`] and [`Self::iac_forge_tag`] take
15283 /// through the outer → shape one-step delegation.
15284 ///
15285 /// The `Option<&'static str>` return shape mirrors
15286 /// [`crate::error::SexpShape::prefix`]'s partial-projection shape one
15287 /// algebra level down — the outer-`Sexp` seven-arm closed set's
15288 /// projection PARTIALIZES on the three non-quote-family shapes
15289 /// (`Nil`, `Atom`, `List`) exactly as the shape-level twelve-arm
15290 /// closed set's projection PARTIALIZES on the eight non-quote-family
15291 /// shapes. The kernel's outer cardinality (three: `Nil` / `Atom` /
15292 /// `List`) matches the shape-level kernel's cardinality (eight)
15293 /// through [`Self::shape`]'s six-atomic-arms → outer `Atom` collapse
15294 /// — the outer three-arm kernel `{Nil, Atom, List}` corresponds to
15295 /// the shape-level eight-arm kernel `{Nil, Symbol, Keyword, String,
15296 /// Int, Float, Bool, List}` under the outer → shape projection.
15297 ///
15298 /// The reader-punctuation vocabulary this method projects (`"'"` /
15299 /// `` "`" `` / `","` / `",@"`) is INTENTIONALLY DISJOINT from the
15300 /// two sibling `&'static str` outer-value projection axes:
15301 ///
15302 /// * [`Self::iac_forge_tag`] — cross-crate canonical form
15303 /// (`"quote"` / `"quasiquote"` / `"unquote"` /
15304 /// `"unquote-splicing"`), BLAKE3 attestation keys, render-cache
15305 /// shape (load-bearing for byte-identical inter-crate compatibility
15306 /// with the iac-forge ecosystem);
15307 /// * [`Self::type_name`] — operator-facing diagnostic label
15308 /// (`"nil"` / `"atom"` / `"list"` / `"quote"` / `"quasiquote"` /
15309 /// `"unquote"` / `"unquote-splice"`) on the outer 7-arm surface,
15310 /// [`crate::error::LispError::TypeMismatch`]'s `got` rendering,
15311 /// REPL / LSP shape-of-witness surface.
15312 ///
15313 /// This method projects the reader's SOURCE-TEXT vocabulary — the
15314 /// four punctuation characters that appear literally in Lisp source
15315 /// at each variant's homoiconic prefix. The three outer-value
15316 /// closed-set projections key the SAME seven-arm outer algebra on
15317 /// THREE distinct `&'static str` vocabularies (source-punctuation,
15318 /// diagnostic-label, cross-crate canonical-form); consolidating any
15319 /// two would silently break either the reader round-trip, the
15320 /// operator-facing diagnostic surface, OR the iac-forge attestation
15321 /// pipeline. The three vocabularies' distinctness is pinned bit-for-
15322 /// bit through the composition law across the closed-set typed
15323 /// algebra.
15324 ///
15325 /// The `&'static str` lifetime is load-bearing: every reader / LSP
15326 /// / REPL / [`fmt::Display for Sexp`] consumer projects through this
15327 /// method into the canonical prefix character without an allocation,
15328 /// parallel to how [`crate::ast::QuoteForm::prefix`] on the sub-
15329 /// carving, [`crate::error::SexpShape::prefix`] on the shape-level
15330 /// projection, [`Self::iac_forge_tag`] on the cross-crate canonical-
15331 /// form axis, and [`crate::error::UnquoteForm::marker`] on the
15332 /// template-marker axis project their respective closed sets. A
15333 /// future eighth [`Sexp`] variant (e.g. a hypothetical `Vector` for
15334 /// `#(...)` reader syntax, `Map` for `{...}`, `Char` for `#\x`)
15335 /// extends [`crate::error::SexpShape`] (adding a `None`-arm non-
15336 /// quote-family shape) — this method picks up the new arm's `None`
15337 /// mechanically through the shape composition, with rustc's
15338 /// exhaustiveness binding the extension end-to-end at
15339 /// [`crate::error::SexpShape::as_quote_form`]'s closed match.
15340 ///
15341 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (outer
15342 /// `Sexp` variant, reader-punctuation prefix) pairing becomes a
15343 /// TYPE projection on the outermost value-carrier algebra rather
15344 /// than an inline `.shape().prefix()` two-step at every consumer.
15345 /// A typo or swap at the projection site is no longer a runtime
15346 /// prefix drift but a compile error against the typed composition
15347 /// — the `Sexp` ↔ `SexpShape` ↔ `QuoteForm` ↔ prefix character
15348 /// chain is rustc-enforced end-to-end. THEORY.md §II.1 invariant 2
15349 /// — free middle; the (outer value, reader-punctuation prefix)
15350 /// pairing now binds at ONE site on the outer-`Sexp` algebra,
15351 /// composing through the pre-existing shape-level partial
15352 /// projection rather than duplicating the four-arm match here.
15353 /// THEORY.md §VI.1 — generation over composition; the outer-`Sexp`
15354 /// reader-punctuation surface closes at FOUR typed layers (outer
15355 /// → shape → carving → sub-carving-prefix), each keyed on the SAME
15356 /// reader-punctuation projection carried at the closed-set sub-
15357 /// carving level.
15358 ///
15359 /// Frontier inspiration: MLIR's `mlir::Operation::getName()` typed
15360 /// projection composed with `mlir::OperationName::getStringRef()`
15361 /// — narrowing an operation-carrier value through its typed op-name
15362 /// identity yields the canonical cross-boundary string identity in
15363 /// ONE typed composition. [`Self::prefix`] is the Rust-typed peer
15364 /// where the "project to shape" step ([`Self::shape`]) composes
15365 /// with the "read the shape's canonical reader-punctuation" step
15366 /// ([`crate::error::SexpShape::prefix`]) into ONE outer-value
15367 /// projection — sibling of [`Self::iac_forge_tag`] one vocabulary
15368 /// axis over on the cross-crate canonical-form surface.
15369 #[must_use]
15370 pub fn prefix(&self) -> Option<&'static str> {
15371 self.shape().prefix()
15372 }
15373
15374 /// Total structural node count of the outer [`Sexp`] value — one
15375 /// node per outer-algebra arm plus the recursive node count of
15376 /// each child. [`Self::Nil`] and [`Self::Atom`] contribute one
15377 /// node apiece (the outer arm itself); [`Self::List`] contributes
15378 /// one node for the outer arm plus the summed node count of each
15379 /// child element; the four homoiconic wrapper arms
15380 /// ([`Self::Quote`], [`Self::Quasiquote`], [`Self::Unquote`],
15381 /// [`Self::UnquoteSplice`]) each contribute one node for the
15382 /// outer arm plus the node count of the wrapped inner form. The
15383 /// projection is a structural size on the AST — every closed-set
15384 /// arm counts as one, so the count is well-defined on ANY
15385 /// [`Sexp`] value regardless of how it was constructed.
15386 ///
15387 /// Load-bearing arithmetic identities:
15388 /// * `Sexp::Nil.node_count() == 1`
15389 /// * `Sexp::Atom(_).node_count() == 1`
15390 /// * `Sexp::list(items).node_count() == 1 + sum(item.node_count())`
15391 /// * `Sexp::quote(inner).node_count() == 1 + inner.node_count()`
15392 /// * (peer identity for each other quote-family arm)
15393 ///
15394 /// The identities compose: `node_count` is monotone in tree
15395 /// growth (a strictly-larger tree — one containing more arms —
15396 /// has a strictly-larger count), so a resource ceiling keyed on
15397 /// `node_count` bounds the total AST arm-count reachable at that
15398 /// ceiling. A future [`Self::UnquoteSplice`] wrapper appearing
15399 /// inside a list contributes one node for the wrapper AND one
15400 /// node for the outer list arm plus the node count of the
15401 /// wrapper's inner form — the identity holds compositionally
15402 /// through the wrapper's `Box<Sexp>` payload.
15403 ///
15404 /// Consumers so far: [`crate::macro_expand::Expander`]'s
15405 /// `max_expansion_size` ceiling — the RESOURCE-axis peer of the
15406 /// `max_expansion_depth` (recursion length) and
15407 /// `max_cache_entries` (memoization width) ceilings — projects
15408 /// the freshly-applied macro expansion through `node_count` to
15409 /// decide whether the result crosses the "expansion bomb"
15410 /// threshold on the OUTPUT-SIZE axis. A `#[derive(TataraDomain)]`
15411 /// consumer that wants to reject "this macro produced a giant
15412 /// blob" at the expander boundary now inherits the projection
15413 /// mechanically through the ceiling; no per-consumer walker
15414 /// discipline required.
15415 ///
15416 /// The `usize` return shape is the natural resource-count
15417 /// carrier — sibling to `HashMap::len` (the cache-width ceiling
15418 /// consumes) and to the `usize` depth counter (the recursion
15419 /// ceiling consumes) — so all three ceilings compose against a
15420 /// single arithmetic type without cross-cast overhead.
15421 ///
15422 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
15423 /// structural size of a value on the outer [`Sexp`] algebra
15424 /// becomes a first-class TYPED projection rather than a
15425 /// per-consumer hand-rolled walker. THEORY.md §VI.1 — generation
15426 /// over composition; a future ceiling on a subtree count (e.g.
15427 /// "reject any expansion whose LIST arms exceed N") emerges
15428 /// naturally as a peer projection on the same closed-set
15429 /// algebra, not as a separate walker.
15430 #[must_use]
15431 pub fn node_count(&self) -> usize {
15432 match self {
15433 Self::Nil | Self::Atom(_) => 1,
15434 Self::List(items) => 1 + items.iter().map(Self::node_count).sum::<usize>(),
15435 Self::Quote(inner)
15436 | Self::Quasiquote(inner)
15437 | Self::Unquote(inner)
15438 | Self::UnquoteSplice(inner) => 1 + inner.node_count(),
15439 }
15440 }
15441}
15442
15443/// Static panic message for [`Sexp::expect_quote_form`]'s asserted-total
15444/// face of the quote-family projection. Pre-lift this literal appeared
15445/// inline at five `.expect(...)` callsites (`Hash for Sexp`,
15446/// `Display for Sexp`, `domain::sexp_shape`, `domain::sexp_to_json`,
15447/// `interop::iac_forge_tag`); post-lift it lives at ONE named const so a
15448/// regression that drifts the diagnostic at one site silently from the
15449/// others becomes structurally impossible. Sibling to the per-projection
15450/// asserted-total faces across the substrate's typed algebras — the
15451/// message names the invariant the outer pattern proves, not the
15452/// substring grep'able by tests.
15453pub const QUOTE_FAMILY_PROJECTION_INVARIANT: &str =
15454 "matched quote-family variant must project to Some via as_quote_form";
15455
15456/// Closed-set typed identifier for the four homoiconic prefix-wrappers in
15457/// the substrate's `Sexp` algebra — `'x` ([`Sexp::Quote`]), `` `x ``
15458/// ([`Sexp::Quasiquote`]), `,x` ([`Sexp::Unquote`]), `,@x`
15459/// ([`Sexp::UnquoteSplice`]) — paired with the projections each consumer
15460/// surface needs ([`Self::prefix`] for [`crate::ast::Sexp`]'s `Display`
15461/// impl AND the reader's prefix dispatch dual, [`Self::hash_discriminator`]
15462/// for [`crate::ast::Sexp`]'s `Hash` impl, [`Self::as_unquote_form`] for
15463/// the 2-of-4 subset gate the template-substitution surface keys on).
15464///
15465/// Mirror at the homoiconic-prefix-wrapper boundary of the prior-run
15466/// `UnquoteForm` (template-marker subset, 2 variants),
15467/// `CompilerSpecIoStage` (disk-persistence surface),
15468/// `TemplateInvariantKind` (bytecode-runtime surface), `MacroDefHead`
15469/// (macro-definition-head closed set), and `KwargPath` (kwargs-path-shape
15470/// surface) closed-set lifts: those enums key their respective rejection
15471/// or projection variants on a typed identity carried inside the variant's
15472/// data shape; this enum keys the FOUR distinct quote-family rendering /
15473/// hashing / template-substitution sites on a typed marker identity.
15474/// Adding a fifth homoiconic prefix-wrapper (e.g., a hypothetical `,~`
15475/// reverse-unquote) requires extending this enum, which rustc-enforces
15476/// matching at every projection site (`prefix`, `hash_discriminator`,
15477/// `as_unquote_form`, plus `Sexp::as_quote_form`'s match arm) — the closed
15478/// set becomes a TYPE rather than four `&'static str` / `u8` literals that
15479/// could drift independently across `Sexp::Display`'s prefix arm and
15480/// `Sexp::Hash`'s discriminator arm and the reader's prefix dispatch.
15481///
15482/// Subset-gate relationship to [`UnquoteForm`]: the template-substitution
15483/// surface's [`Sexp::as_unquote`] is now `as_quote_form().and_then(|(qf,
15484/// inner)| qf.as_unquote_form().map(|uf| (uf, inner)))` — the 2-of-4
15485/// projection lives at ONE site on this algebra ([`Self::as_unquote_form`])
15486/// rather than being re-derived at every consumer that wants only the
15487/// `{Unquote, UnquoteSplice}` subset. A future enum variant that joins
15488/// the template-substitution subset (e.g. a typed `defalias`-projected
15489/// fifth marker) extends [`UnquoteForm`] AND
15490/// [`Self::as_unquote_form`]'s arm together, with rustc binding the
15491/// extension through the projection's `Option` return type.
15492///
15493/// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
15494/// homoiconic-prefix-wrapper dispatch (the reader's prefix-to-variant
15495/// gate AND the Display impl's variant-to-prefix dual) IS the rust-level
15496/// typed-entry / typed-exit gate, and naming its closed-set identity
15497/// lifts the gate from per-site literal-pair discipline to ONE typed
15498/// enum the substrate's diagnostic promotions hang off of.
15499/// THEORY.md §V.1 — knowable platform; the closed set of homoiconic
15500/// prefix-wrappers becomes a TYPE rather than four `&'static str` / `u8`
15501/// literals scattered across Hash / Display / interop / sexp_shape — a
15502/// typo in any one site is no longer a runtime drift but a compile error
15503/// against the typed projection. THEORY.md §VI.1 — generation over
15504/// composition; the typed enum lands the structural-completeness floor
15505/// for the quote-family surface, parallel to how `UnquoteForm` lands it
15506/// for the template-marker subset and `MacroDefHead` for the
15507/// macro-definition-head surface.
15508#[derive(Debug, Clone, Copy, PartialEq, Eq, tatara_closed_set::DeriveClosedSet)]
15509#[closed_set(via = "prefix", display, generate_unknown = "quote form")]
15510pub enum QuoteForm {
15511 /// `'x` — literal-quote prefix. The `'` marker; the inner expression
15512 /// is NOT subject to macro substitution. Projects to NO
15513 /// `UnquoteForm` (the template-substitution surface ignores quote).
15514 Quote,
15515 /// `` `x `` — quasi-quote prefix. The `` ` `` marker; the inner
15516 /// expression is the template body inside which `,` and `,@` mark
15517 /// substitution points. Projects to NO `UnquoteForm` (a quasi-quote
15518 /// is the substitution SCOPE, not a substitution itself).
15519 Quasiquote,
15520 /// `,x` — single-value substitution. The `,` marker; the inner
15521 /// symbol is substituted with its bound value at template
15522 /// expansion. Projects to `UnquoteForm::Unquote` for the
15523 /// template-substitution surface.
15524 Unquote,
15525 /// `,@x` — list-splice substitution. The `,@` marker; the inner
15526 /// symbol must be bound to a list, whose elements are flattened
15527 /// into the containing list at template expansion. Projects to
15528 /// `UnquoteForm::Splice` for the template-substitution surface.
15529 UnquoteSplice,
15530}
15531
15532impl QuoteForm {
15533 /// The closed set of four homoiconic prefix-wrappers — single
15534 /// source of truth that drives every per-variant projection
15535 /// ([`Self::prefix`] / [`fmt::Display`], [`Self::hash_discriminator`],
15536 /// [`Self::as_unquote_form`], [`Self::iac_forge_tag`],
15537 /// [`Self::sexp_shape`], [`Self::wrap`], and the [`Self::FromStr`]
15538 /// decode sweep keyed on [`Self::prefix`]).
15539 ///
15540 /// Adding a hypothetical fifth homoiconic prefix-wrapper (e.g.
15541 /// a `,~` reverse-unquote, a `,?` conditional-unquote, or a
15542 /// `#'` Common-Lisp function-quote literal) lands at one
15543 /// [`Self::ALL`] entry plus one arm per projection — exhaustively
15544 /// checked by the compiler (the `[Self; 4]` array literal forces
15545 /// the arity) AND by the per-variant truth-table tests below.
15546 ///
15547 /// Sibling closed-set lift to every other typed-shape enum the
15548 /// substrate carries: this crate's own
15549 /// [`crate::error::SexpShape::ALL`] (the twelve reachable outer
15550 /// shapes — superset of this enum's four via [`Self::sexp_shape`]),
15551 /// [`AtomKind::ALL`] (the six atomic-payload kinds — peer axis
15552 /// on the same algebra, also a 6-of-12 carving of `SexpShape`),
15553 /// [`crate::error::UnquoteForm::ALL`] (the two template-substitution
15554 /// markers — proper 2-of-4 subset of THIS enum via
15555 /// [`Self::as_unquote_form`]), and the cross-crate `tatara-process`
15556 /// family (`ConditionKind::ALL`, `ProcessPhase::ALL`,
15557 /// `ProcessSignal::ALL`, `ChannelKind::ALL`, `IntentKind::ALL`,
15558 /// `LifetimeKind::ALL`, `RequestorKind::ALL`, `ReceiptKind::ALL`,
15559 /// …) every one of which paired its typed projection with `ALL`
15560 /// before this lift.
15561 ///
15562 /// Future consumers that compose against `ALL`: LSP / REPL
15563 /// completion for the operator-facing rendered homoiconic prefix
15564 /// (every `'`/`` ` ``/`,`/`,@` substring an authoring tool would
15565 /// surface in a completion list keys on this set's projection
15566 /// through [`Self::prefix`]); `tatara-check` coverage assertions
15567 /// over which quote-family wrappers reach a `Sexp::Display` /
15568 /// `Hash for Sexp` / `as_unquote_form` consumer arm at all — the
15569 /// typed sweep replaces a per-callsite vocabulary of four
15570 /// `&'static str` / `u8` literals; any future audit-trail metric
15571 /// jointly labeled by [`Self::prefix`] (e.g.
15572 /// `tatara_lisp_quote_family_total{prefix="'"}`) — the metric
15573 /// label set IS [`Self::ALL`] mapped through [`Self::prefix`];
15574 /// any future structural rewriter (typed analogue of MLIR's
15575 /// `op.walk<QuoteFormOp>()`) that wants to sweep over every
15576 /// quote-family wrapper in a typed sequence.
15577 pub const ALL: [Self; 4] = [
15578 Self::Quote,
15579 Self::Quasiquote,
15580 Self::Unquote,
15581 Self::UnquoteSplice,
15582 ];
15583
15584 /// Canonical `&'static str` reader-prefix of [`Self::Quote`] —
15585 /// `"'"`. The ONE canonical bytes-payload on the closed-set
15586 /// [`QuoteForm`] algebra shared by [`Self::prefix`]'s [`Self::Quote`]
15587 /// arm AND the [`crate::ast::Sexp`] `Display` arm the arm feeds.
15588 ///
15589 /// Sibling posture to the closed set of per-role `pub const`
15590 /// bytes on the substrate's other closed-set outer algebras:
15591 /// [`crate::error::MacroDefHead::DEFMACRO_KEYWORD`] /
15592 /// [`crate::error::MacroDefHead::DEFPOINT_TEMPLATE_KEYWORD`] /
15593 /// [`crate::error::MacroDefHead::DEFCHECK_KEYWORD`] (per-role
15594 /// head-keyword algebra on the CL macro-definition surface),
15595 /// [`crate::ast::Atom::TRUE_LITERAL`] /
15596 /// [`crate::ast::Atom::FALSE_LITERAL`] (per-role Scheme-bool
15597 /// spelling algebra on the atomic-payload surface),
15598 /// [`crate::macro_expand::MacroParams::REST_MARKER`] /
15599 /// [`crate::macro_expand::MacroParams::OPTIONAL_MARKER`] (per-role
15600 /// CL lambda-list-keyword algebra on the macro-param surface).
15601 ///
15602 /// The `char`-level peer of THIS `&'static str` constant is
15603 /// [`Self::QUOTE_LEAD`] — the first (and only) char of this
15604 /// prefix. The structural round-trip law between the two is
15605 /// `Self::QUOTE_PREFIX.chars().next() == Some(Self::QUOTE_LEAD)`
15606 /// AND `Self::QUOTE_PREFIX.len() ==
15607 /// Self::QUOTE_LEAD.len_utf8()` — the ONE `char` byte
15608 /// composes the ONE `&'static str` prefix. Pinned by
15609 /// `quote_form_per_role_prefixes_route_through_matching_lead_char_for_single_char_prefixes`.
15610 ///
15611 /// A regression that inlines the `"'"` literal at
15612 /// [`Self::prefix`]'s [`Self::Quote`] arm and drifts the constant
15613 /// silently (e.g. an ELisp-compat port of the quote prefix to
15614 /// `#'`, a hypothetical Racket-compat swap to a distinct byte)
15615 /// fails at the algebra's `prefix()` path-uniformity pin
15616 /// (`quote_form_prefix_routes_through_typed_per_role_constants`)
15617 /// rather than at silent reader-family drift where the
15618 /// [`Sexp::Display`] round-trip breaks.
15619 pub const QUOTE_PREFIX: &'static str = "'";
15620
15621 /// Canonical `&'static str` reader-prefix of [`Self::Quasiquote`]
15622 /// — `` "`" ``. Sibling of [`Self::QUOTE_PREFIX`] on the closed-set
15623 /// per-role quote-family prefix-bytes axis; see
15624 /// [`Self::QUOTE_PREFIX`] for the algebra-level round-trip +
15625 /// disjointness contracts every sibling shares. The `char`-level
15626 /// peer is [`Self::QUASIQUOTE_LEAD`].
15627 pub const QUASIQUOTE_PREFIX: &'static str = "`";
15628
15629 /// Canonical `&'static str` reader-prefix of [`Self::Unquote`] —
15630 /// `","`. Sibling of [`Self::QUOTE_PREFIX`] on the closed-set
15631 /// per-role quote-family prefix-bytes axis; see
15632 /// [`Self::QUOTE_PREFIX`] for the algebra-level round-trip +
15633 /// disjointness contracts every sibling shares. The `char`-level
15634 /// peer is [`Self::UNQUOTE_LEAD`] (shared with
15635 /// [`Self::UNQUOTE_SPLICE_PREFIX`]'s lead byte — see the
15636 /// [`Self::UNQUOTE_LEAD`] docstring for the shared-lead-char
15637 /// discipline the two prefixes disambiguate on).
15638 pub const UNQUOTE_PREFIX: &'static str = ",";
15639
15640 /// Canonical `&'static str` reader-prefix of [`Self::UnquoteSplice`]
15641 /// — `",@"`. The ONLY two-char prefix on the closed set; every
15642 /// other [`Self::PREFIXES`] entry is a single `char` rendered as
15643 /// `&'static str`. Sibling of [`Self::QUOTE_PREFIX`] on the closed-
15644 /// set per-role quote-family prefix-bytes axis.
15645 ///
15646 /// Structural composition law: `Self::UNQUOTE_SPLICE_PREFIX ==
15647 /// format!("{}{}", Self::UNQUOTE_LEAD, Self::SPLICE_DISCRIMINATOR)`
15648 /// — the two-char prefix decomposes cleanly into the ONE shared
15649 /// lead byte [`Self::UNQUOTE_LEAD`] + the ONE splice-promotion
15650 /// discriminator [`Self::SPLICE_DISCRIMINATOR`], both `char`-level
15651 /// constants on this algebra. Pinned by
15652 /// `quote_form_unquote_splice_prefix_constant_composes_from_unquote_lead_and_splice_discriminator`
15653 /// (byte-level composition through the per-role `pub const`) as a
15654 /// section-for-retraction peer of the pre-existing
15655 /// `quote_form_unquote_splice_prefix_composes_from_unquote_lead_and_splice_discriminator`
15656 /// pin (byte-level composition through the [`Self::prefix`]
15657 /// method).
15658 pub const UNQUOTE_SPLICE_PREFIX: &'static str = ",@";
15659
15660 /// The closed-set forced-arity ALL array over the quote-family
15661 /// reader-prefix `&'static str` bytes in canonical declaration
15662 /// order matching [`Self::ALL`] element-wise. Sibling posture to
15663 /// [`crate::error::MacroDefHead::KEYWORDS`] (`[&'static str; 3]`
15664 /// on the CL macro-definition head algebra),
15665 /// [`crate::ast::Atom::BOOL_LITERALS`] (`[&'static str; 2]` on the
15666 /// Scheme-bool spelling algebra), and
15667 /// [`crate::macro_expand::MacroParams::LAMBDA_LIST_KEYWORDS`]
15668 /// (`[&'static str; 2]` on the CL lambda-list-keyword algebra) —
15669 /// every closed-set outer projection on the substrate now pins
15670 /// its canonical bytes at ONE `pub const` per role plus an ALL
15671 /// array for family-wide consumers.
15672 ///
15673 /// Adding a hypothetical fifth homoiconic prefix (a `,~`
15674 /// reverse-unquote, a `,?` conditional-unquote, a `#'` Common-
15675 /// Lisp function-quote) extends [`Self::ALL`] AND
15676 /// [`Self::PREFIXES`] AND [`Self::prefix`]'s arm AND one new
15677 /// per-role `pub const` in lockstep — rustc's forced-arity check
15678 /// on `[&'static str; N]` fails compilation if either ALL array
15679 /// grows without the other.
15680 ///
15681 /// Future consumers that compose against [`Self::PREFIXES`]:
15682 /// - LSP / REPL completion for the operator-facing rendered
15683 /// homoiconic prefix bar — the completion set IS
15684 /// [`Self::PREFIXES`] rather than four hand-enumerated
15685 /// `&'static str` literals per completion provider.
15686 /// - `tatara-check` coverage assertions that sweep workspace
15687 /// `.lisp` files for every canonical quote-family prefix — the
15688 /// typed sweep replaces per-consumer inline enumeration of the
15689 /// four literals.
15690 /// - Any future audit-trail metric jointly labeled by
15691 /// [`Self::prefix`] (e.g.
15692 /// `tatara_lisp_quote_family_total{prefix="'"}`) — the metric
15693 /// label set IS [`Self::PREFIXES`] mapped through
15694 /// [`Self::prefix`].
15695 pub const PREFIXES: [&'static str; 4] = [
15696 Self::QUOTE_PREFIX,
15697 Self::QUASIQUOTE_PREFIX,
15698 Self::UNQUOTE_PREFIX,
15699 Self::UNQUOTE_SPLICE_PREFIX,
15700 ];
15701
15702 /// Canonical `&'static str` prefix that paired with the variant
15703 /// renders the homoiconic form — [`Self::QUOTE_PREFIX`] for
15704 /// [`Self::Quote`], [`Self::QUASIQUOTE_PREFIX`] for
15705 /// [`Self::Quasiquote`], [`Self::UNQUOTE_PREFIX`] for
15706 /// [`Self::Unquote`], [`Self::UNQUOTE_SPLICE_PREFIX`] for
15707 /// [`Self::UnquoteSplice`]. Threaded through
15708 /// [`crate::ast::Sexp`]'s `Display` impl so the per-variant prefix
15709 /// rendering lives at ONE site on this algebra rather than four
15710 /// inline literal strings across the Display arms.
15711 ///
15712 /// Post-lift the four arms route through the per-role `pub const`
15713 /// bytes on the closed-set [`QuoteForm`] algebra rather than
15714 /// inline `&'static str` literals — so a rename of ONE canonical
15715 /// prefix bytes (an ELisp-compat port of `Quote` to `"#'"`, a
15716 /// hypothetical Racket-compat swap of `Quasiquote`, a Common-Lisp-
15717 /// standard rename of `UnquoteSplice` to `",."`) lands as ONE
15718 /// edit to the matching `pub const` — every downstream consumer
15719 /// that binds to the algebra ([`crate::ast::Sexp`]'s `Display`
15720 /// impl, the reader's tokenizer round-trip law, the future
15721 /// canonical-form taggers) inherits the rename mechanically.
15722 ///
15723 /// Structural dual of the reader's [`crate::reader::read_quoted`]
15724 /// dispatch: the reader maps prefix-tokens to `Sexp::{Quote,
15725 /// Quasiquote, Unquote, UnquoteSplice}` constructors; this method
15726 /// maps the typed `QuoteForm` marker back to its canonical prefix
15727 /// string. Adding a fifth prefix extends both sides — the reader's
15728 /// tokenizer + dispatch AND this method — with rustc enforcing
15729 /// the pair through the closed-set enum. Round-trip:
15730 /// `read(format!("{}{inner}", qf.prefix()))` produces the
15731 /// `Sexp::*` variant matching `qf`, by construction.
15732 ///
15733 /// The `&'static str` lifetime is load-bearing: it lets every
15734 /// consumer (Display arm, future format strings, future interop
15735 /// canonical-form taggers) project through this method without
15736 /// an allocation, parallel to how [`UnquoteForm::marker`]
15737 /// projects its 2-of-4 subset surface.
15738 #[must_use]
15739 pub fn prefix(self) -> &'static str {
15740 match self {
15741 Self::Quote => Self::QUOTE_PREFIX,
15742 Self::Quasiquote => Self::QUASIQUOTE_PREFIX,
15743 Self::Unquote => Self::UNQUOTE_PREFIX,
15744 Self::UnquoteSplice => Self::UNQUOTE_SPLICE_PREFIX,
15745 }
15746 }
15747
15748 /// Canonical `'` LEAD `char` of [`Self::Quote`]'s [`Self::prefix`]
15749 /// (`"'"`) — the ONE canonical `char` on the [`QuoteForm`] algebra the
15750 /// substrate's Quote-family single-quote lead-byte disjointness
15751 /// contract binds to.
15752 ///
15753 /// Sibling posture to the closed set of `pub const` reader-punctuation
15754 /// canonical `char` bytes on the substrate:
15755 /// [`Self::SPLICE_DISCRIMINATOR`] (`'@'`),
15756 /// [`crate::ast::Atom::STR_DELIMITER`] (`'"'`),
15757 /// [`crate::ast::Atom::STR_ESCAPE_LEAD`] (`'\\'`),
15758 /// [`crate::ast::Atom::KEYWORD_MARKER_LEAD`] (`':'`),
15759 /// [`crate::ast::Atom::BOOL_LITERAL_LEAD`] (`'#'`),
15760 /// [`crate::ast::Sexp::LIST_OPEN`] (`'('`),
15761 /// [`crate::ast::Sexp::LIST_CLOSE`] (`')'`),
15762 /// [`crate::ast::Sexp::COMMENT_LEAD`] (`';'`),
15763 /// [`crate::ast::Sexp::COMMENT_TERM`] (`'\n'`) — every canonical per-
15764 /// role byte the reader's tokenizer specialises on is a `pub const`
15765 /// on its owning closed-set algebra. This constant closes the Quote-
15766 /// family single-quote lead byte at the SAME algebra as the
15767 /// [`Self::lead_char`] projection (whose [`Self::Quote`] arm returns
15768 /// this byte) AND the [`Self::from_lead_char`] inverse (whose match
15769 /// arm decodes this byte back to [`Self::Quote`]).
15770 ///
15771 /// Structural round-trip contract:
15772 /// `Self::from_lead_char(Self::QUOTE_LEAD) == Some(Self::Quote)`
15773 /// AND `Self::Quote.lead_char() == Self::QUOTE_LEAD` — pinned by
15774 /// `quote_form_lead_constants_round_trip_through_lead_char_projections`.
15775 /// A regression that drifts EITHER the constant OR the paired
15776 /// projection surfaces at the pin rather than at a silent tokenizer
15777 /// drift where `'foo` classifies as a bare atom instead of
15778 /// [`crate::ast::Sexp::Quote`].
15779 ///
15780 /// Disjointness contract: `QUOTE_LEAD`'s byte MUST differ from
15781 /// [`Self::QUASIQUOTE_LEAD`], [`Self::UNQUOTE_LEAD`],
15782 /// [`Self::SPLICE_DISCRIMINATOR`],
15783 /// [`crate::ast::Atom::STR_DELIMITER`],
15784 /// [`crate::ast::Atom::STR_ESCAPE_LEAD`],
15785 /// [`crate::ast::Atom::KEYWORD_MARKER_LEAD`],
15786 /// [`crate::ast::Atom::BOOL_LITERAL_LEAD`],
15787 /// [`crate::ast::Sexp::LIST_OPEN`], [`crate::ast::Sexp::LIST_CLOSE`],
15788 /// [`crate::ast::Sexp::COMMENT_LEAD`], and
15789 /// [`crate::ast::Sexp::COMMENT_TERM`] — every other closed-set outer-
15790 /// marker byte the reader's tokenizer specialises on. A collision
15791 /// would silently break the reader's outer dispatch. Pinned by
15792 /// `quote_form_lead_constants_distinct_from_every_other_algebra_marker_char`.
15793 ///
15794 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
15795 /// (Quote-family single-quote lead byte, canonical `'\''`) pairing
15796 /// binds at ONE constant on the closed-set [`QuoteForm`] algebra
15797 /// regardless of which paired projection reaches in. THEORY.md §V.1
15798 /// — knowable platform; the canonical Quote-family lead byte becomes
15799 /// a TYPE-level constant on the substrate algebra rather than an
15800 /// inline `'\''` char literal at [`Self::lead_char`]'s [`Self::Quote`]
15801 /// arm AND at [`Self::from_lead_char`]'s decode arm.
15802 pub const QUOTE_LEAD: char = '\'';
15803
15804 /// Canonical `` ` `` LEAD `char` of [`Self::Quasiquote`]'s
15805 /// [`Self::prefix`] (`` "`" ``) — sibling of [`Self::QUOTE_LEAD`] on
15806 /// the closed-set quote-family lead-byte axis. See
15807 /// [`Self::QUOTE_LEAD`] for the algebra-level round-trip +
15808 /// disjointness contracts every sibling shares. Bound by
15809 /// [`Self::lead_char`]'s [`Self::Quasiquote`] arm AND
15810 /// [`Self::from_lead_char`]'s decode arm.
15811 pub const QUASIQUOTE_LEAD: char = '`';
15812
15813 /// Canonical `,` LEAD `char` SHARED by [`Self::Unquote`]'s
15814 /// [`Self::prefix`] (`","`) AND [`Self::UnquoteSplice`]'s
15815 /// [`Self::prefix`] (`",@"`) — the splice's two-char prefix opens
15816 /// with this byte and disambiguates on the peek-then-consume
15817 /// [`Self::SPLICE_DISCRIMINATOR`] second char inside
15818 /// [`crate::reader::tokenize`]. Sibling of [`Self::QUOTE_LEAD`] on
15819 /// the closed-set quote-family lead-byte axis; see
15820 /// [`Self::QUOTE_LEAD`] for the algebra-level round-trip +
15821 /// disjointness contracts every sibling shares. Bound by
15822 /// [`Self::lead_char`]'s `Self::Unquote | Self::UnquoteSplice` merged
15823 /// arm AND [`Self::from_lead_char`]'s decode arm.
15824 ///
15825 /// Composition identity with [`Self::SPLICE_DISCRIMINATOR`]:
15826 /// `format!("{}{}", Self::UNQUOTE_LEAD, Self::SPLICE_DISCRIMINATOR)
15827 /// == Self::UnquoteSplice.prefix()` — the two byte-level constants
15828 /// on the closed-set [`QuoteForm`] algebra compose the ONLY two-char
15829 /// [`Self::prefix`] in the closed set. Pinned by
15830 /// `quote_form_unquote_splice_prefix_composes_from_unquote_lead_and_splice_discriminator`.
15831 /// A regression that renames EITHER constant without touching the
15832 /// paired [`Self::UnquoteSplice`]'s [`Self::prefix`] arm surfaces
15833 /// here rather than as a silent `,@` reader drift.
15834 pub const UNQUOTE_LEAD: char = ',';
15835
15836 /// The closed-set forced-arity ALL array over the quote-family
15837 /// DISTINCT reader-lead-byte `char`s in canonical declaration
15838 /// order matching [`Self::ALL`]'s three-of-four distinct-lead-
15839 /// byte projection through [`Self::lead_char`] — [`Self::QUOTE_LEAD`]
15840 /// (`'\''` — the [`Self::Quote`] lead byte), [`Self::QUASIQUOTE_LEAD`]
15841 /// (`` '`' `` — the [`Self::Quasiquote`] lead byte),
15842 /// [`Self::UNQUOTE_LEAD`] (`','` — the SHARED lead byte of BOTH
15843 /// [`Self::Unquote`] AND [`Self::UnquoteSplice`], with the splice
15844 /// promotion living at the reader's peek-then-consume
15845 /// [`Self::SPLICE_DISCRIMINATOR`] second-char arm rather than at a
15846 /// distinct lead byte).
15847 ///
15848 /// The `[char; 3]` cardinality (vs the peer [`Self::PREFIXES`]
15849 /// `[&'static str; 4]`) IS the structural axis distinguishing the
15850 /// DISTINCT-lead-byte sub-vocabulary from the PER-VARIANT-prefix
15851 /// sub-vocabulary — three-of-four distinct-lead-byte collapse is
15852 /// definitional (only [`Self::UnquoteSplice`]'s two-char `,@`
15853 /// prefix shares its lead byte with a sibling variant; every other
15854 /// variant owns its lead byte outright). The shape asymmetry
15855 /// between the two ALL arrays encodes the shared-lead-byte
15856 /// collapse identity on the closed-set [`QuoteForm`] algebra at
15857 /// the type-system level: a consumer that reaches for
15858 /// [`Self::LEADS`] reads the distinct-lead-byte cardinality
15859 /// directly off the array's forced arity rather than through a
15860 /// per-consumer `HashSet`-then-count over [`Self::PREFIXES`]'s
15861 /// first chars.
15862 ///
15863 /// Sibling posture to [`Self::PREFIXES`] (`[&'static str; 4]` on
15864 /// the per-variant reader-prefix axis) AND [`Self::IAC_FORGE_TAGS`]
15865 /// (`[&'static str; 4]` on the per-variant canonical-form tag
15866 /// axis) — those two ALL arrays close the per-variant axes of the
15867 /// outer-tokenizer `QuoteForm` closed set; this ALL array closes
15868 /// the peer DISTINCT-lead-byte axis at the SHAPE-ASYMMETRIC
15869 /// `[char; N]` cardinality. Also sibling-shape to
15870 /// [`crate::ast::Sexp::LIST_DELIMITERS`] (`[char; 2]` on the outer-
15871 /// structural paired-delimiter axis), [`Atom::SELF_ESCAPE_TABLE`]
15872 /// (`[char; 2]` on the inner-Str-payload self-escape axis), and
15873 /// [`Atom::BOOL_LITERALS`] (`[&'static str; 2]` on the Scheme-bool
15874 /// spelling axis) — every closed-set outer projection on the
15875 /// substrate now pins its canonical bytes at ONE `pub const` per
15876 /// role plus an ALL array for family-wide consumers.
15877 ///
15878 /// Adding a hypothetical fifth homoiconic prefix with a DISTINCT
15879 /// lead byte (a `~` reverse-unquote, a `?` conditional-unquote, a
15880 /// `#` reader-macro-lead) extends [`Self::ALL`] AND
15881 /// [`Self::PREFIXES`] AND [`Self::LEADS`] AND [`Self::lead_char`]'s
15882 /// arm AND [`Self::from_lead_char`]'s arm AND one new per-role
15883 /// `pub const` in lockstep — rustc's forced-arity check on
15884 /// `[char; N]` fails compilation if the LEADS array grows without
15885 /// the paired algebra constant, and the paired PREFIXES /
15886 /// IAC_FORGE_TAGS arrays extend by ONE row each in lockstep. A
15887 /// fifth prefix that SHARES its lead byte with an existing variant
15888 /// (like the splice's `,@` sharing with unquote's `,`) leaves
15889 /// [`Self::LEADS`]'s cardinality unchanged — the DISTINCT-lead-
15890 /// byte set is invariant under such an extension, closing the
15891 /// splice-family promotion pattern at the ALL-array level.
15892 ///
15893 /// Future consumers that compose against [`Self::LEADS`]:
15894 /// - LSP / REPL completion for the operator-facing reader-entry
15895 /// lead-byte set — the completion set IS [`Self::LEADS`] rather
15896 /// than three hand-enumerated `char` literals per completion
15897 /// provider.
15898 /// - The reader's outer tokenizer pre-match check that gates the
15899 /// quote-family dispatch — the check IS
15900 /// `Self::LEADS.contains(&ch)` rather than three inline
15901 /// `ch == Self::QUOTE_LEAD || ch == Self::QUASIQUOTE_LEAD ||
15902 /// ch == Self::UNQUOTE_LEAD` disjuncts, and the sweep binds
15903 /// through the ALL array's forced arity.
15904 /// - A hypothetical `tatara_lisp_quote_family_lead_total{lead="'"}`
15905 /// metric surface — the label-set generator sweeps
15906 /// [`Self::LEADS`] verbatim rather than re-typing the three
15907 /// distinct-lead bytes inline at each recorder.
15908 /// - Any future syntax-highlighter / structural editor that needs
15909 /// the reader-entry lead-byte set for classification — the
15910 /// editor's per-char classifier binds through [`Self::LEADS`]
15911 /// rather than three parallel `char`-literal patterns.
15912 ///
15913 /// Theory anchor: THEORY.md §III — the typescape; the three
15914 /// distinct quote-family reader-lead bytes now bind at ONE typed
15915 /// `[char; 3]` array on the closed-set [`QuoteForm`] algebra
15916 /// rather than as three inline algebra-constant enumerations at
15917 /// every consumer that iterates the distinct-lead-byte sub-
15918 /// vocabulary. THEORY.md §V.1 — knowable platform; the distinct-
15919 /// lead-byte sub-vocabulary becomes load-bearing typed data on
15920 /// the closed-set outer [`QuoteForm`] algebra. THEORY.md §VI.1 —
15921 /// generation over composition; the shared-lead-byte collapse
15922 /// identity (four variants → three distinct lead bytes)
15923 /// composes at ONE typed ALL array whose shape-asymmetric
15924 /// cardinality (3 vs [`Self::PREFIXES`]'s 4) IS the collapse
15925 /// invariant carried at the type-system level.
15926 pub const LEADS: [char; 3] = [Self::QUOTE_LEAD, Self::QUASIQUOTE_LEAD, Self::UNQUOTE_LEAD];
15927
15928 /// Canonical FIRST-char of [`Self::prefix`] — [`Self::QUOTE_LEAD`]
15929 /// for [`Self::Quote`], [`Self::QUASIQUOTE_LEAD`] for
15930 /// [`Self::Quasiquote`], [`Self::UNQUOTE_LEAD`] for BOTH
15931 /// [`Self::Unquote`] AND [`Self::UnquoteSplice`] (the splice's two-
15932 /// char `,@` prefix shares its lead byte with bare unquote and
15933 /// disambiguates on the peek-then-consume `@` second char inside
15934 /// [`crate::reader::tokenize`]).
15935 /// The three-of-four collapse onto three distinct lead chars is
15936 /// structurally fixed — the reader's outer tokenizer dispatch
15937 /// selects between quote-family entry and every non-quote-family
15938 /// arm on lead char alone, with the `,`-vs-`,@` disambiguation
15939 /// falling out of the reader's second-char peek.
15940 ///
15941 /// Structural dual of [`Self::from_lead_char`]: this method projects
15942 /// the closed-set marker to its canonical reader-punctuation lead
15943 /// char; the sibling projects the lead char back to the DEFAULT
15944 /// marker on that char (`,` → [`Self::Unquote`], with the splice
15945 /// promotion living at the reader's peek arm rather than at
15946 /// [`Self::from_lead_char`]'s decode). Every variant round-trips
15947 /// through the composition `Self::from_lead_char(qf.lead_char())`,
15948 /// with the `{Unquote, UnquoteSplice}` two-of-four collapsing onto
15949 /// `Some(Unquote)` per the shared-lead-char structural identity.
15950 ///
15951 /// The `const` qualifier is load-bearing: [`crate::reader::tokenize`]
15952 /// binds its outer-match quote-family dispatch to this projection
15953 /// via a pre-match `Self::from_lead_char` check, and future consumer
15954 /// sites (e.g. `const` array literals of every reader-recognized
15955 /// lead byte the tokenizer could dispatch on, LSP completion
15956 /// generators that pre-materialize the lead-char set) route through
15957 /// this projection at compile time. Sibling posture to
15958 /// [`crate::ast::Atom::STR_DELIMITER`] one axis over on the same
15959 /// closed-set-lead-char algebra — that constant is the ONE `char`
15960 /// the `Token::Str` open/close/self-escape/bare-atom-terminator
15961 /// FOUR sites in the reader pair with; this method is the ONE
15962 /// projection the `Token::Quoted(QuoteForm)` outer-dispatch AND
15963 /// the same bare-atom-terminator disjunct pair with across FOUR
15964 /// per-variant lead chars.
15965 ///
15966 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
15967 /// reader's per-char quote-family dispatch IS the typed-entry gate,
15968 /// and lifting its (char, `QuoteForm`) pairing to ONE projection
15969 /// method plus one inverse (see [`Self::from_lead_char`]) closes
15970 /// the tokenizer's outer-arm entry surface onto the closed-set
15971 /// algebra rather than four inline `char` literals scattered across
15972 /// three tokenizer arms (`'\''` / `` '`' `` / `','` outer-match arms)
15973 /// AND three bare-atom-terminator disjuncts (`ch == '\''` / `ch ==
15974 /// '`'` / `ch == ','`). THEORY.md §V.1 — knowable platform; the
15975 /// closed set of quote-family lead chars becomes a TYPE (the
15976 /// enum's arms projected through this method) rather than four
15977 /// literal `char` values scattered across the reader's outer
15978 /// dispatch AND its bare-atom terminator. THEORY.md §VI.1 —
15979 /// generation over composition; a fifth homoiconic prefix
15980 /// (hypothetical `,~` reverse-unquote, `#'` function-quote,
15981 /// `#[…]` vector-quote) extends [`Self`] AND this method AND
15982 /// [`Self::from_lead_char`] AND the tokenizer's pre-match check
15983 /// in lockstep, with rustc binding the extension through
15984 /// exhaustiveness over the closed enum.
15985 #[must_use]
15986 pub const fn lead_char(self) -> char {
15987 match self {
15988 Self::Quote => Self::QUOTE_LEAD,
15989 Self::Quasiquote => Self::QUASIQUOTE_LEAD,
15990 Self::Unquote | Self::UnquoteSplice => Self::UNQUOTE_LEAD,
15991 }
15992 }
15993
15994 /// Inverse of [`Self::lead_char`] on the three-of-four distinct
15995 /// lead chars — `'\''` decodes to `Some(Self::Quote)`, `` '`' ``
15996 /// decodes to `Some(Self::Quasiquote)`, `','` decodes to
15997 /// `Some(Self::Unquote)` (the DEFAULT variant on the shared `,`
15998 /// lead char; the two-char `,@` splice promotion lives at
15999 /// [`crate::reader::tokenize`]'s peek-then-consume `@` disambiguator
16000 /// rather than at this decode). Every other `char` yields `None` —
16001 /// the closed-set guarantee on [`Self`] AND on the tokenizer's
16002 /// outer-arm set (whitespace, `(`, `)`, [`crate::ast::Atom::STR_DELIMITER`],
16003 /// `;`, bare atom) ensures the four typed markers partition the
16004 /// three distinct lead chars injectively against every other
16005 /// tokenizer-recognized entry char.
16006 ///
16007 /// ONE consumer entrypoint the reader's `tokenize` binds against:
16008 /// the outer-match quote-family dispatch was pre-lift a hand-rolled
16009 /// three-arm cascade (`'\''` / `` '`' `` / `','`) with per-arm
16010 /// `Token::Quoted(QuoteForm::*)` construction and a fourth
16011 /// `Token::Quoted(QuoteForm::UnquoteSplice)` arm buried inside the
16012 /// `','`-arm's peek branch; post-lift the tokenizer pre-checks
16013 /// `Self::from_lead_char(c)` before the outer match, promotes the
16014 /// returned `Self::Unquote` to `Self::UnquoteSplice` on second-char
16015 /// `@`, and emits ONE `Token::Quoted(final_qf)` — the (lead char,
16016 /// [`Self`] marker) pairing binds at ONE site on the closed-set
16017 /// algebra rather than at three inline `char` literals across
16018 /// three outer-match arms. The bare-atom terminator disjunct at
16019 /// the reader's `Token::Atom` accumulator loop routes through
16020 /// `Self::from_lead_char(ch).is_some()` so the three
16021 /// quote-family-lead disjuncts (`ch == '\''` / `ch == '`'` /
16022 /// `ch == ','`) collapse to ONE gate — a regression that drifts
16023 /// one bare-atom-terminator disjunct from the outer-dispatch's
16024 /// quote-family arm becomes structurally impossible because
16025 /// there is exactly ONE decode both sites consume.
16026 ///
16027 /// The two-of-four collapse onto `Some(Self::Unquote)` for the
16028 /// `,` lead char is INTENTIONAL: `Self::UnquoteSplice` has NO
16029 /// distinct lead char; the tokenizer must see two consecutive
16030 /// chars (`,` then `@`) to promote the decoded `Self::Unquote`
16031 /// to `Self::UnquoteSplice`. Placing the promotion at the
16032 /// reader's peek arm rather than at this decode keeps the
16033 /// (char → marker) projection at the closed-set algebra's
16034 /// character-boundary surface (one char in, one variant out)
16035 /// and the (two-char sequence → splice) promotion at the
16036 /// tokenizer's streaming surface (peek and consume the second
16037 /// char). This split parallels the reader's split of `Token::Str`
16038 /// into open-delimiter dispatch ([`crate::ast::Atom::STR_DELIMITER`])
16039 /// AND inner-payload accumulation — the closed-set char algebra
16040 /// decodes the entry char; the streaming reader handles multi-
16041 /// char follow-through.
16042 ///
16043 /// Sibling to [`crate::ast::Atom::from_lexeme`] one axis over on
16044 /// the same typed-entry family — that method decodes a bare-atom
16045 /// lexeme into a typed [`crate::ast::Atom`] variant; this method
16046 /// decodes a single lead char into a typed [`Self`] variant. Both
16047 /// map the reader's per-char / per-lexeme classification surface
16048 /// onto the substrate's closed-set algebra so the reader's outer
16049 /// dispatch binds through ONE typed decode rather than through
16050 /// scattered per-arm `char` / `&str` literal patterns.
16051 ///
16052 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
16053 /// reader's per-char quote-family classification IS the typed-entry
16054 /// gate. THEORY.md §V.1 — knowable platform; the reader's outer
16055 /// dispatch AND the bare-atom terminator each route through ONE
16056 /// typed decode against the closed-set algebra rather than through
16057 /// three (or four) parallel `char`-literal patterns that could
16058 /// drift independently — a regression that renames one lead char
16059 /// without updating the sibling site fails at rustc / test time
16060 /// rather than as a silent tokenizer drift.
16061 #[must_use]
16062 pub const fn from_lead_char(c: char) -> Option<Self> {
16063 match c {
16064 Self::QUOTE_LEAD => Some(Self::Quote),
16065 Self::QUASIQUOTE_LEAD => Some(Self::Quasiquote),
16066 Self::UNQUOTE_LEAD => Some(Self::Unquote),
16067 _ => None,
16068 }
16069 }
16070
16071 /// Canonical SECOND char of [`Self::UnquoteSplice`]'s two-char `,@`
16072 /// [`Self::prefix`] — the ONE `'@'` byte the reader's peek-then-
16073 /// consume splice-promotion arm inside [`crate::reader::tokenize`]
16074 /// disambiguates on. Sibling posture to [`crate::ast::Atom::STR_DELIMITER`]
16075 /// (one-char Str-payload delimiter shared across four `"`-round-
16076 /// trip sites) AND to [`crate::ast::Sexp::COMMENT_LEAD`] (one-char
16077 /// line-comment lead shared across two `;`-boundary sites) — those
16078 /// two constants project a single byte onto their respective closed-
16079 /// set algebras (`Atom` and outer-`Sexp`); this constant projects
16080 /// the single byte that composes the `,` [`Self::Unquote`]
16081 /// [`Self::lead_char`] into the two-char [`Self::UnquoteSplice`]
16082 /// [`Self::prefix`] onto the same closed-set [`Self`] algebra.
16083 ///
16084 /// The `,@` splice is the ONLY multi-char [`Self::prefix`] in the
16085 /// closed set — [`Self::Quote`] / [`Self::Quasiquote`] / [`Self::Unquote`]
16086 /// each render as a single [`Self::lead_char`] byte; only
16087 /// [`Self::UnquoteSplice`] appends this discriminator. The
16088 /// composition [`Self::Unquote::prefix()`] + `SPLICE_DISCRIMINATOR`
16089 /// == [`Self::UnquoteSplice::prefix()`] IS the structural identity
16090 /// the reader's peek arm depends on — pinned by
16091 /// `quote_form_unquote_splice_prefix_composes_from_unquote_prefix_and_splice_discriminator`.
16092 /// A future hypothetical fifth homoiconic prefix with its own two-
16093 /// char extension (e.g. `,~` reverse-unquote via a `~` discriminator,
16094 /// `#'` function-quote via a `'` discriminator) extends [`Self`]
16095 /// AND a per-variant promotion peer (extending
16096 /// [`Self::promote_via_next_char`]) in lockstep — rustc binds the
16097 /// extension through exhaustiveness over the closed enum.
16098 ///
16099 /// The `const` qualifier is load-bearing: [`Self::promote_via_next_char`]'s
16100 /// body binds through this constant in a `const fn` context so the
16101 /// reader's peek arm consumes the promotion table at compile time.
16102 /// Sibling posture to [`crate::ast::Atom::STR_DELIMITER`],
16103 /// [`crate::ast::Atom::KEYWORD_MARKER`], [`crate::ast::Sexp::LIST_OPEN`],
16104 /// [`crate::ast::Sexp::LIST_CLOSE`], [`crate::ast::Sexp::COMMENT_LEAD`] —
16105 /// every canonical reader-punctuation constant on the substrate is a
16106 /// `pub const` on its owning closed-set algebra.
16107 ///
16108 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
16109 /// reader's two-char splice-promotion gate IS the typed-entry gate
16110 /// on the `,@` boundary, and lifting the `@` discriminator to ONE
16111 /// canonical byte on the closed-set algebra closes the gate's
16112 /// entry-char identity onto the algebra rather than at an inline
16113 /// `char` literal at the reader's peek arm. THEORY.md §V.1 —
16114 /// knowable platform; the splice-promotion discriminator becomes a
16115 /// TYPED byte on the substrate algebra rather than an inline `'@'`
16116 /// scattered across the reader — a regression that renames the byte
16117 /// without updating the sibling promotion peer fails at rustc /
16118 /// test time rather than as a silent tokenizer drift where `,@xs`
16119 /// forms silently degrade to `,` + `@xs` two-token sequences.
16120 pub const SPLICE_DISCRIMINATOR: char = '@';
16121
16122 /// Closed-set forced-arity ALL array over the canonical promotion
16123 /// triples on the substrate's quote-family algebra —
16124 /// `(head_variant, next_char_discriminator, promoted_variant)` for
16125 /// every `(head, next)` pair whose [`Self::promote_via_next_char`]
16126 /// projection yields `Some(promoted)`. Post-lift the promotion
16127 /// algebra's canonical triples bind at ONE typed
16128 /// `[(Self, char, Self); N]` array on the closed-set [`QuoteForm`]
16129 /// algebra rather than at zero-primitive-plus-inline-arm-literals
16130 /// inside [`Self::promote_via_next_char`]'s match body.
16131 ///
16132 /// The substrate's current promotion algebra is the singleton
16133 /// `[(Self::Unquote, Self::SPLICE_DISCRIMINATOR, Self::UnquoteSplice)]`
16134 /// — the ONLY (variant, next-char) → longer-variant mapping the
16135 /// reader's peek-then-consume `@` promotion arm depends on. Its
16136 /// forced-arity `1` is INTENTIONAL and load-bearing: [`Self::UnquoteSplice`]
16137 /// is the ONLY variant with a two-char [`Self::prefix`], so the
16138 /// promotion table has exactly ONE `Some` arm and every other
16139 /// pairing rejects — the closed set of promotions is the singleton
16140 /// `{(Unquote, '@') → UnquoteSplice}` on the `Self × char →
16141 /// Option<Self>` product. Pinned bit-for-bit by
16142 /// `quote_form_promotions_has_expected_cardinality` (forced-arity)
16143 /// AND `quote_form_promotions_pin_legacy_splice_promotion_triple`
16144 /// (identity of the singleton entry). A future fifth homoiconic
16145 /// prefix with its own two-char extension (a hypothetical `,~`
16146 /// reverse-unquote via a `~` discriminator, a `#'` function-quote
16147 /// via a `'` discriminator, a `,?` conditional-unquote via a `?`
16148 /// discriminator) extends [`Self::ALL`] AND appends ONE new
16149 /// promotion triple to [`Self::PROMOTIONS`] AND extends
16150 /// [`Self::promote_via_next_char`]'s match body in lockstep —
16151 /// rustc's forced-arity check on the `[(Self, char, Self); N]`
16152 /// array fails compilation if the array's cardinality grows
16153 /// without a matching arm on the projection method.
16154 ///
16155 /// Sibling posture to the closed-set forced-arity ALL arrays across
16156 /// the substrate's [`QuoteForm`] algebra — [`Self::ALL`]
16157 /// (`[Self; 4]` — the closed set of variants),
16158 /// [`Self::PREFIXES`] (`[&'static str; 4]` — the reader-prefix
16159 /// `&'static str` axis), [`Self::LABELS`] (`[&'static str; 4]` —
16160 /// the diagnostic-label `&'static str` axis),
16161 /// [`Self::IAC_FORGE_TAGS`] (`[&'static str; 4]` — the iac-forge
16162 /// canonical-form tag `&'static str` axis),
16163 /// [`Self::LEADS`] (`[char; 3]` — the reader-lead `char` axis with
16164 /// shape-asymmetric cardinality reflecting the shared-lead-byte
16165 /// collapse of the `,` prefix across [`Self::Unquote`] AND
16166 /// [`Self::UnquoteSplice`]), and [`Self::HASH_DISCRIMINATORS`]
16167 /// (`[u8; 4]` — the outer-Sexp cache-key byte axis). This lift adds
16168 /// the SEVENTH per-family axis on the algebra — the (head, disc,
16169 /// promoted) triple axis on the closed-set promotion product.
16170 /// Each of the seven axes now pins its per-role canonical data at
16171 /// ONE `pub const` per role PLUS an ALL array for family-wide
16172 /// consumers, across the same closed set of four variants (or
16173 /// three-of-four for the shape-asymmetric [`Self::LEADS`] axis's
16174 /// shared-lead-char collapse, or one-of-four for the promotion-
16175 /// asymmetric [`Self::PROMOTIONS`] axis's single-arm collapse).
16176 ///
16177 /// Composition identity (pinned by
16178 /// `quote_form_promotions_align_with_promote_via_next_char_for_every_entry`):
16179 /// for every `(head, disc, promoted)` in [`Self::PROMOTIONS`],
16180 /// `head.promote_via_next_char(disc) == Some(promoted)`. The
16181 /// projection's Some-arm binds through [`Self::PROMOTIONS`]`[i].2`
16182 /// (the promoted-variant column of the ONE promotion triple) — a
16183 /// regression that drifts the triple's promoted-variant column
16184 /// silently redirects every reader `,@` sequence to a phantom
16185 /// variant AND fails the alignment pin at rustc / test time
16186 /// rather than at silent tokenizer drift where every `,@xs` form
16187 /// tokenizes to the wrong closed-set marker.
16188 ///
16189 /// Rejection contract (pinned by
16190 /// `quote_form_promotions_close_promote_via_next_char_against_every_non_promotion_pair`):
16191 /// for every `(head, next)` pair NOT in [`Self::PROMOTIONS`]'s
16192 /// projection to `(Self × char)`, `head.promote_via_next_char(next)
16193 /// == None`. Sweeps the [`Self::ALL`] × (rejection-char sweep)
16194 /// product against the promotion set's complement — a regression
16195 /// that widened the promotion algebra (e.g. phantom-promoted
16196 /// [`Self::Quote`] on `'@'` after a copy-paste drift on the match
16197 /// arm) surfaces at test time rather than at silent tokenizer
16198 /// drift where bare `'@xs` forms silently degrade to a phantom
16199 /// [`Self::UnquoteSplice`]-shaped sequence.
16200 ///
16201 /// Composition law (rendered-prefix identity, pinned by
16202 /// `quote_form_promotions_compose_prefix_from_source_prefix_and_discriminator_for_every_entry`):
16203 /// for every `(head, disc, promoted)` in [`Self::PROMOTIONS`],
16204 /// `format!("{}{}", head.prefix(), disc) == promoted.prefix()`.
16205 /// The (head prefix + discriminator) source-text composition
16206 /// agrees byte-for-byte with the promoted variant's rendered
16207 /// prefix — the reader's peek-then-consume arm's rendered
16208 /// prefix identity closes the read↔write duality across the
16209 /// promotion algebra. Sibling to the pre-existing
16210 /// `quote_form_promote_via_next_char_composes_prefix_from_source_prefix_and_next_char`
16211 /// which pins the same law through the projection method rather
16212 /// than through the triple's data directly — this pin closes the
16213 /// law at the constant, that pin closes it at the projection.
16214 ///
16215 /// `pub(crate)` because the promotion algebra is an implementation
16216 /// detail of the substrate's reader — exposing it publicly would
16217 /// leak the promotion-table shape through the API without enabling
16218 /// any external consumer the public projections
16219 /// ([`Self::promote_via_next_char`], [`Self::prefix`],
16220 /// [`Self::from_lead_char`]) don't already serve — same visibility
16221 /// rationale as [`Self::HASH_DISCRIMINATORS`] on the sibling
16222 /// cache-key axis.
16223 ///
16224 /// The `#[allow(dead_code)]` posture matches
16225 /// [`Self::HASH_DISCRIMINATORS`] / [`AtomKind::HASH_DISCRIMINATORS`]:
16226 /// the substrate's current [`Self::promote_via_next_char`] body
16227 /// dispatches through ONE match arm bound to the ONE promotion
16228 /// triple's promoted-variant column ([`Self::PROMOTIONS`]`[0].2`),
16229 /// with the head-pattern + discriminator-pattern arm literals
16230 /// preserved for the const-fn match's pattern surface (patterns
16231 /// cannot be array-indexing expressions in the current const-fn
16232 /// grammar). The lift lands the substrate primitive so future
16233 /// consumers keyed on the whole promotion algebra (a future
16234 /// `tatara-check` predicate `(check-promotion-algebra-injective …)`
16235 /// that verifies each `(head, disc)` pair projects to a unique
16236 /// promoted variant, a future LSP structural-navigation filter
16237 /// that keys on the promotion algebra's cardinality, a future
16238 /// `TypedRewriter<PromotionOp>` sweep zipping ALL / PREFIXES /
16239 /// LABELS / IAC_FORGE_TAGS / HASH_DISCRIMINATORS / PROMOTIONS in
16240 /// lockstep for a family-wide render) bind to ONE `[(Self, char,
16241 /// Self); N]` primitive rather than re-deriving the promotion
16242 /// triples inline per callsite. Matches the preemptive-primitive
16243 /// posture the prior-run [`Self::HASH_DISCRIMINATORS`] +
16244 /// [`AtomKind::HASH_DISCRIMINATORS`] +
16245 /// [`crate::error::StructuralKind::HASH_DISCRIMINATORS`] lifts
16246 /// carried before their downstream consumers materialized.
16247 ///
16248 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
16249 /// reader's two-char quote-family classification IS the typed-
16250 /// entry gate on the `,@` boundary, and lifting the promotion
16251 /// algebra's canonical triples to ONE typed `[(Self, char, Self);
16252 /// N]` primitive on the closed-set algebra closes the gate's
16253 /// two-char entry surface onto the algebra rather than at inline
16254 /// arm literals scattered across the const-fn match body.
16255 /// THEORY.md §III — the typescape; the singleton promotion triple
16256 /// binds at ONE typed `pub const` on the closed-set [`QuoteForm`]
16257 /// algebra rather than at zero-primitive-plus-inline-arm-literals
16258 /// at [`Self::promote_via_next_char`]'s match arm. THEORY.md §V.1
16259 /// — knowable platform; the family's cardinality becomes a
16260 /// TYPE-level constant on the substrate algebra rather than a
16261 /// per-consumer runtime dispatch through the match table.
16262 /// THEORY.md §VI.1 — generation over composition; the family-wide
16263 /// contract sweeps (alignment with
16264 /// [`Self::promote_via_next_char`], pairwise disjointness across
16265 /// the head-discriminator product, rendered-prefix composition
16266 /// identity) emerge from the composition of ONE substrate
16267 /// primitive (this `pub(crate) const` array) rather than as
16268 /// per-arm inline assertions duplicated at each call site.
16269 ///
16270 /// Frontier inspiration: MLIR's typed rewriter registry
16271 /// (`mlir::PatternApplicator`) carries a per-op-family
16272 /// `[(source_pattern, matcher, rewritten_op)]` static rewrite
16273 /// table at the closed-set boundary — the (source, matcher,
16274 /// target) triple axis becomes typed data on the IR algebra
16275 /// rather than dispatch tables scattered across per-pattern
16276 /// callsites. Translated through the substrate's [`QuoteForm`]
16277 /// closed-set marker, the reader's promotion rewrite table
16278 /// becomes ONE typed `[(head_variant, next_char, promoted_variant);
16279 /// N]` array on the algebra. Where MLIR's registry carries the
16280 /// rewrite table dynamically on the pattern applicator's runtime
16281 /// state, this substrate carries it statically as `pub const` on
16282 /// the closed-set marker — the pattern-matching evaluation lands
16283 /// at rustc-time through const-fn match arm binding to
16284 /// [`Self::PROMOTIONS`]`[i].2` rather than at runtime through a
16285 /// dynamic registry lookup.
16286 #[allow(dead_code)]
16287 pub(crate) const PROMOTIONS: [(Self, char, Self); 1] = [(
16288 Self::Unquote,
16289 Self::SPLICE_DISCRIMINATOR,
16290 Self::UnquoteSplice,
16291 )];
16292
16293 /// Promotion table on the closed-set quote-family algebra —
16294 /// `Some(Self::UnquoteSplice)` iff `self == Self::Unquote &&
16295 /// next == Self::SPLICE_DISCRIMINATOR`, else `None`. Encodes the
16296 /// substrate's ONE (variant, next-char) → longer-variant mapping —
16297 /// `,` [`Self::Unquote`] followed by `@` [`Self::SPLICE_DISCRIMINATOR`]
16298 /// promotes to `,@` [`Self::UnquoteSplice`]. Every other pairing
16299 /// (including [`Self::Quote`] / [`Self::Quasiquote`] / [`Self::UnquoteSplice`]
16300 /// on ANY next-char, AND [`Self::Unquote`] on any non-discriminator
16301 /// next-char) yields `None` — the closed set of promotions is the
16302 /// singleton `{(Unquote, '@') → UnquoteSplice}` on the `Self × char
16303 /// → Option<Self>` product.
16304 ///
16305 /// Structural sibling of [`Self::from_lead_char`] one axis over on
16306 /// the same typed-entry family: [`Self::from_lead_char`] decodes
16307 /// ONE lead char to its DEFAULT variant on that char; this method
16308 /// decodes ONE (default variant, second char) pair to its PROMOTED
16309 /// variant. Together the two methods close the reader's outer
16310 /// quote-family entry surface onto the algebra: the tokenizer
16311 /// consumes ONE lead char through [`Self::from_lead_char`], then
16312 /// OPTIONALLY consumes one second char through this method — the
16313 /// (lead char, second char) → typed marker projection binds at TWO
16314 /// typed decodes rather than at inline `char`-literal patterns
16315 /// scattered across the outer-match dispatch arm.
16316 ///
16317 /// ONE consumer entrypoint the reader's `tokenize` binds against:
16318 /// the peek-then-consume `@` promotion inside the outer-match
16319 /// quote-family dispatch was pre-lift a hand-rolled inline check
16320 /// `matches!(qf_head, QuoteForm::Unquote) &&
16321 /// chars.peek().map(|&(_, c)| c) == Some('@')` paired with a
16322 /// per-branch `QuoteForm::UnquoteSplice` construction. The pairing
16323 /// was load-bearing yet only enforced by callsite discipline at a
16324 /// SEVENTH consumer site (alongside `Hash`, `Display`, `sexp_shape`,
16325 /// `wrap`, `iac_forge_tag`, `as_unquote_form`) the prior closed-set
16326 /// `QuoteForm` lifts did not reach. Post-lift the reader's peek
16327 /// arm routes through this method, so the (Unquote, '@') →
16328 /// UnquoteSplice promotion binds at ONE site on the typed algebra.
16329 /// A regression that drifts the promotion table (e.g. re-inlines
16330 /// `matches!(qf_head, QuoteForm::Quote)` at the peek arm and
16331 /// silently promotes bare `'` to a phantom variant) becomes a
16332 /// typed compile error against the `Option<Self>` return type.
16333 ///
16334 /// The single-promotion collapse (only `(Unquote, '@')` triggers)
16335 /// is INTENTIONAL: [`Self::UnquoteSplice`] is the ONLY variant with
16336 /// a two-char [`Self::prefix`], so the promotion table has exactly
16337 /// ONE `Some` arm and every other pairing rejects. Placing the
16338 /// promotion at the closed-set algebra rather than at the reader's
16339 /// peek arm keeps the streaming reader's two-char peek-then-consume
16340 /// shape at ONE site (the reader) while the (variant × second
16341 /// char) → promoted variant projection lives on the substrate
16342 /// algebra — parallel to the split that [`Self::from_lead_char`]
16343 /// closes for the one-char entry surface. This split parallels the
16344 /// reader's split of `Token::Str` into open-delimiter dispatch
16345 /// ([`crate::ast::Atom::STR_DELIMITER`]) AND inner-payload
16346 /// accumulation — the closed-set char algebra decodes the entry
16347 /// chars; the streaming reader handles the peek-and-consume
16348 /// follow-through.
16349 ///
16350 /// Composition identity: for every `qf: QuoteForm` and every
16351 /// `c: char`, if `qf.promote_via_next_char(c) == Some(promoted)`
16352 /// then `format!("{}{}", qf.prefix(), c) == promoted.prefix()`.
16353 /// Pinned by
16354 /// `quote_form_promote_via_next_char_composes_prefix_from_source_prefix_and_next_char`
16355 /// across the singleton promotion arm — the pin asserts the
16356 /// (variant, next char) → promoted-variant projection agrees with
16357 /// the reader's rendered [`Self::prefix`] composition, so a
16358 /// regression that drifts one side of the identity (a promotion
16359 /// arm rerouted through the wrong variant, or a prefix renamed
16360 /// without updating the promotion table) surfaces immediately.
16361 ///
16362 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
16363 /// reader's two-char quote-family classification IS the typed-
16364 /// entry gate on the `,@` boundary. THEORY.md §V.1 — knowable
16365 /// platform; the (variant, next char) → promoted variant table
16366 /// becomes a TYPE projection on the substrate algebra rather than
16367 /// at an inline `matches!(qf, Unquote) && c == '@'` pattern
16368 /// scattered at the reader's peek arm. THEORY.md §VI.1 —
16369 /// generation over composition; a fifth homoiconic prefix with its
16370 /// own two-char extension extends [`Self`] AND this method's match
16371 /// arm in lockstep — rustc binds the extension through
16372 /// exhaustiveness over the closed enum, and the `Option<Self>`
16373 /// return shape leaves the promotion table structurally open for
16374 /// future variants to append their own `Some` arms without
16375 /// touching the existing arms' semantics.
16376 ///
16377 /// Frontier inspiration: Racket's `read-syntax` two-char
16378 /// discriminator table (`(quote-abbrev-mapping char) → syntax`)
16379 /// that maps `(#\' → 'quote)`, `(#\` → 'quasiquote)`, `(#\, →
16380 /// 'unquote)`, `(#\, #\@) → 'unquote-splicing)` on the reader's
16381 /// typed abbreviation surface. Translated through the substrate's
16382 /// [`QuoteForm`] outer-marker algebra, the `(#\, #\@) → 'unquote-
16383 /// splicing)` two-char arm becomes ONE typed `(Self::Unquote, '@')
16384 /// → Some(Self::UnquoteSplice)` promotion on the closed-set
16385 /// algebra with rustc binding the promotion identity through
16386 /// exhaustiveness. Where Racket carries the promotion table
16387 /// dynamically on the reader's abbreviation-mapping parameter,
16388 /// this substrate carries it statically as `pub const fn` on the
16389 /// closed-set marker.
16390 #[must_use]
16391 pub const fn promote_via_next_char(self, next: char) -> Option<Self> {
16392 // The Some-arm's promoted-variant column routes through
16393 // `Self::PROMOTIONS[0].2` — the ONE promotion triple on the
16394 // closed-set algebra — so a regression that drifts the
16395 // promoted-variant column of the singleton triple silently
16396 // redirects every reader `,@` sequence to a phantom variant AND
16397 // fails the alignment pin
16398 // `quote_form_promotions_align_with_promote_via_next_char_for_every_entry`
16399 // at rustc / test time rather than at silent tokenizer drift.
16400 // The head-pattern (`Self::Unquote`) + discriminator-pattern
16401 // (`Self::SPLICE_DISCRIMINATOR`) arm literals stay inline
16402 // because patterns cannot be array-indexing expressions in the
16403 // current const-fn grammar; the alignment pin catches head /
16404 // discriminator column drift by construction.
16405 match (self, next) {
16406 (Self::Unquote, Self::SPLICE_DISCRIMINATOR) => Some(Self::PROMOTIONS[0].2),
16407 _ => None,
16408 }
16409 }
16410
16411 /// Canonical `u8` cache-key byte for [`Self::Quote`]'s
16412 /// [`Self::hash_discriminator`] arm — `3`. ONE canonical byte on
16413 /// the closed-set [`QuoteForm`] algebra shared by
16414 /// [`Self::hash_discriminator`]'s [`Self::Quote`] arm AND every
16415 /// downstream consumer (the [`Hash for Sexp`](crate::ast::Sexp)
16416 /// cache-key body, the expansion cache
16417 /// (`crate::macro_expand::Expander::cache`) that keys on that hash).
16418 ///
16419 /// Sibling posture to the closed set of per-role `pub(crate) const`
16420 /// / `pub const` bytes on the substrate's other closed-set outer
16421 /// algebras: [`Self::QUOTE_PREFIX`] / [`Self::QUASIQUOTE_PREFIX`]
16422 /// / [`Self::UNQUOTE_PREFIX`] / [`Self::UNQUOTE_SPLICE_PREFIX`]
16423 /// (per-role reader-prefix `&'static str` algebra on the SAME
16424 /// [`QuoteForm`] closed set — commit a08e61f),
16425 /// [`Self::QUOTE_LABEL`] / [`Self::QUASIQUOTE_LABEL`] /
16426 /// [`Self::UNQUOTE_LABEL`] / [`Self::UNQUOTE_SPLICE_LABEL`] (per-
16427 /// role diagnostic-label `&'static str` algebra on the SAME closed
16428 /// set — commit 70be157), [`Self::QUOTE_IAC_FORGE_TAG`] /
16429 /// [`Self::QUASIQUOTE_IAC_FORGE_TAG`] / [`Self::UNQUOTE_IAC_FORGE_TAG`]
16430 /// / [`Self::UNQUOTE_SPLICE_IAC_FORGE_TAG`] (per-role iac-forge
16431 /// canonical-form tag `&'static str` algebra on the SAME closed set
16432 /// — commit bdd624b). This constant closes the FOURTH per-role
16433 /// axis on [`QuoteForm`] — the `u8` cache-key axis paired with the
16434 /// three pre-existing `&'static str` axes.
16435 ///
16436 /// The FOUR canonical bytes `{3, 4, 5, 6}` partition the outer-
16437 /// [`crate::ast::Sexp`] `Hash` body's quote-family arm-set against
16438 /// the reserved bytes the non-quote-family arms use (`0u8` for
16439 /// [`crate::error::StructuralKind::Nil`] via
16440 /// [`crate::error::StructuralKind::hash_discriminator`], `1u8` for
16441 /// [`crate::ast::Sexp::Atom`]'s outer-carve marker via the
16442 /// pre-existing inline `1u8` at [`Hash for Sexp`](crate::ast::Sexp)'s
16443 /// atom arm, `2u8` for [`crate::error::StructuralKind::List`] via
16444 /// [`crate::error::StructuralKind::hash_discriminator`]) — the
16445 /// three carvings of the outer-[`crate::ast::Sexp`] cache-key
16446 /// space jointly cover the `{0, 1, 2, 3, 4, 5, 6}` byte-set with
16447 /// no gaps AND no overlaps.
16448 ///
16449 /// A regression that inlines the `3` literal at
16450 /// [`Self::hash_discriminator`]'s [`Self::Quote`] arm and drifts
16451 /// the constant silently (e.g. a re-numbering that collides with
16452 /// the reserved `2u8` for [`crate::error::StructuralKind::List`],
16453 /// silently mis-hashing every cached expansion across the substrate)
16454 /// fails at the algebra's `hash_discriminator()` path-uniformity
16455 /// pin
16456 /// (`quote_form_hash_discriminator_routes_through_typed_per_role_constants`)
16457 /// rather than at silent cache-key drift where
16458 /// `crate::macro_expand::Expander::cache` mis-collides live
16459 /// expansions.
16460 ///
16461 /// `pub(crate)` because the byte-discriminator surface is an
16462 /// implementation detail of the substrate's [`Hash for Sexp`](crate::ast::Sexp)
16463 /// cache-key contract; exposing it publicly would leak the cache-
16464 /// key shape through the API without enabling any external
16465 /// consumer the public projections ([`Self::as_quote_form`],
16466 /// [`Self::prefix`], [`Self::as_unquote_form`]) don't already
16467 /// serve — same visibility rationale as [`Self::hash_discriminator`]
16468 /// itself.
16469 ///
16470 /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
16471 /// preserves proofs; the alias-chain composition law
16472 /// `QuoteForm::HASH_DISCRIMINATORS[i] ==
16473 /// QuoteForm::ALL[i].hash_discriminator()` binds the family-wide
16474 /// array to the projection method at rustc time, pinned by byte
16475 /// equality. THEORY.md §III — the typescape; the four canonical
16476 /// cache-key bytes bind at ONE `pub(crate) const` per role on the
16477 /// typed algebra rather than as inline `u8` literals in the
16478 /// `hash_discriminator` match arms.
16479 pub(crate) const QUOTE_HASH_DISCRIMINATOR: u8 = 3;
16480
16481 /// Canonical `u8` cache-key byte for [`Self::Quasiquote`]'s
16482 /// [`Self::hash_discriminator`] arm — `4`. Sibling of
16483 /// [`Self::QUOTE_HASH_DISCRIMINATOR`] on the closed-set per-role
16484 /// quote-family cache-key-byte axis; see
16485 /// [`Self::QUOTE_HASH_DISCRIMINATOR`] for the algebra-level round-
16486 /// trip + disjointness contracts every sibling shares.
16487 pub(crate) const QUASIQUOTE_HASH_DISCRIMINATOR: u8 = 4;
16488
16489 /// Canonical `u8` cache-key byte for [`Self::Unquote`]'s
16490 /// [`Self::hash_discriminator`] arm — `5`. Sibling of
16491 /// [`Self::QUOTE_HASH_DISCRIMINATOR`] on the closed-set per-role
16492 /// quote-family cache-key-byte axis. Byte-for-byte distinct from
16493 /// [`Self::UNQUOTE_SPLICE_HASH_DISCRIMINATOR`] — the two template-
16494 /// substitution arms partition `{5, 6}` on the outer-Sexp cache-
16495 /// key space.
16496 pub(crate) const UNQUOTE_HASH_DISCRIMINATOR: u8 = 5;
16497
16498 /// Canonical `u8` cache-key byte for [`Self::UnquoteSplice`]'s
16499 /// [`Self::hash_discriminator`] arm — `6`. Sibling of
16500 /// [`Self::QUOTE_HASH_DISCRIMINATOR`] on the closed-set per-role
16501 /// quote-family cache-key-byte axis. The HIGHEST byte on the
16502 /// closed set — a future fifth quote-family variant would extend
16503 /// the partition to `{3, 4, 5, 6, 7}` and land the new
16504 /// discriminator at `7u8`.
16505 pub(crate) const UNQUOTE_SPLICE_HASH_DISCRIMINATOR: u8 = 6;
16506
16507 /// Closed-set forced-arity ALL array over the canonical cache-key
16508 /// `u8` bytes, in declaration order matching [`Self::ALL`] element-
16509 /// wise (pinned by `quote_form_hash_discriminators_align_with_all_by_index`).
16510 /// Sibling posture to [`Self::PREFIXES`] (`[&'static str; 4]` —
16511 /// the reader-prefix `&'static str` axis on the SAME closed set),
16512 /// [`Self::LABELS`] (`[&'static str; 4]` — the diagnostic-label
16513 /// `&'static str` axis), [`Self::IAC_FORGE_TAGS`] (`[&'static str;
16514 /// 4]` — the iac-forge canonical-form tag `&'static str` axis) —
16515 /// every closed-set outer projection on the substrate's
16516 /// [`QuoteForm`] algebra now pins its per-role canonical bytes at
16517 /// ONE `pub(crate) const` / `pub const` per role PLUS an ALL array
16518 /// for family-wide consumers, across ALL FOUR production
16519 /// vocabularies the closed set carries (reader prefix, diagnostic
16520 /// label, iac-forge canonical-form tag, outer-Sexp cache-key byte).
16521 ///
16522 /// Pre-lift the four cache-key bytes had NO per-role primitive on
16523 /// this closed-set algebra — a consumer with a [`QuoteForm`]
16524 /// variant in hand at compile time reaching for the canonical byte
16525 /// had to spell `QuoteForm::Unquote.hash_discriminator()` (runtime
16526 /// dispatch through the match arm) OR reach across into the inline
16527 /// `5u8` at the pre-lift match arm's [`Self::Unquote`] branch and
16528 /// re-derive the (variant, byte) pairing at the call site.
16529 /// Post-lift the FOUR canonical bytes bind at ONE `pub(crate) const`
16530 /// per role on the typed [`QuoteForm`] algebra AND at
16531 /// [`Self::HASH_DISCRIMINATORS`] as a family-wide forced-arity
16532 /// array — a future substrate-facing cache-key introspection tool
16533 /// (a `tatara-check` predicate that asserts every quote-family
16534 /// arm's discriminator disjoint from the reserved
16535 /// [`crate::ast::Sexp::Atom`] byte, a Sekiban audit-trail metric
16536 /// jointly labeled by the cache-key partition, a future
16537 /// `TypedRewriter<QuoteFormOp>` sweep zipping ALL / PREFIXES /
16538 /// LABELS / IAC_FORGE_TAGS / HASH_DISCRIMINATORS in lockstep for a
16539 /// family-wide (variant, four-vocabulary quadruple) render) reads
16540 /// through the typed constants without re-deriving the four-arm
16541 /// carving inline.
16542 ///
16543 /// Each entry is byte-for-byte identical to the pre-lift inline
16544 /// `u8` literal at the corresponding [`Self::hash_discriminator`]
16545 /// arm — pinned by
16546 /// `quote_form_hash_discriminators_pin_legacy_cache_key_bytes` so
16547 /// a regression that drifts ONE `pub(crate) const` from its pre-
16548 /// lift byte silently invalidates every cached expansion AND mis-
16549 /// collides with the reserved bytes the non-quote-family arms use,
16550 /// fails-loudly at the alias test rather than at a silent
16551 /// [`crate::macro_expand::Expander::cache`] mis-hash. Adding a
16552 /// hypothetical fifth homoiconic prefix (a `,~` reverse-unquote, a
16553 /// `,?` conditional-unquote) extends [`Self::ALL`] AND
16554 /// [`Self::HASH_DISCRIMINATORS`] AND adds ONE per-role
16555 /// `pub(crate) const` in lockstep — rustc's forced-arity check on
16556 /// the two `[_; N]` arrays fails compilation if EITHER array grows
16557 /// without the other, closing the extensibility gap that pre-lift
16558 /// silently allowed a discriminator collision on `7u8` (the next
16559 /// free byte).
16560 ///
16561 /// Theory anchor: THEORY.md §III — the typescape; the four
16562 /// canonical cache-key bytes bind at ONE typed `[u8; 4]` array on
16563 /// the closed-set [`QuoteForm`] algebra rather than at zero-
16564 /// primitive-plus-four-inline-`u8`-literals scattered across the
16565 /// [`Self::hash_discriminator`] match arms. THEORY.md §V.1 —
16566 /// knowable platform; the family's cardinality becomes a TYPE-
16567 /// level constant on the substrate algebra rather than a per-
16568 /// consumer runtime dispatch through the match table. THEORY.md
16569 /// §V.3 — three-pillar attestation; the cache-key partition is
16570 /// the substrate's outer-Sexp `intent_hash` composition axis for
16571 /// every quote-family arm — binding the four bytes on the typed
16572 /// algebra makes attestation-key drift a compile error rather
16573 /// than a silent BLAKE3 mis-hash. THEORY.md §VI.1 — generation
16574 /// over composition; the family-wide contract sweeps (alignment
16575 /// with `ALL`, pairwise disjointness, membership through
16576 /// [`Self::hash_discriminator`]) emerge from the composition of
16577 /// TWO substrate primitives (this `pub(crate) const` array + the
16578 /// four per-role `pub(crate) const *_HASH_DISCRIMINATOR` aliases)
16579 /// rather than as per-variant inline assertions duplicated at
16580 /// each call site.
16581 ///
16582 /// The `#[allow(dead_code)]` posture is deliberate: the substrate's
16583 /// current [`Hash for Sexp`](crate::ast::Sexp) body composes
16584 /// through the per-variant [`Self::hash_discriminator`] projection
16585 /// arm-by-arm rather than sweeping the family-wide array, so no
16586 /// non-test caller currently reaches this ALL array directly. The
16587 /// lift lands the substrate primitive so future consumers keyed
16588 /// on the whole family (a future
16589 /// [`crate::macro_expand::Expander`] cache-warmup pass that hashes
16590 /// the quote-family byte-set upfront, a future `tatara-check`
16591 /// predicate `(check-cache-key-partition-disjoint …)` that
16592 /// verifies the `{3, 4, 5, 6}` partition against the reserved
16593 /// `{0, 1, 2}` bytes structurally, a future
16594 /// `TypedRewriter<QuoteFormOp>` sweep zipping ALL / PREFIXES /
16595 /// LABELS / IAC_FORGE_TAGS / HASH_DISCRIMINATORS in lockstep for a
16596 /// family-wide (variant, four-vocabulary quadruple) render) bind
16597 /// to ONE `[u8; 4]` primitive rather than re-deriving the array
16598 /// inline per callsite. Matches the preemptive-primitive posture
16599 /// the prior-run [`crate::error::UnquoteForm::hash_discriminator`]
16600 /// lift carried before its downstream consumers materialized.
16601 #[allow(dead_code)]
16602 pub(crate) const HASH_DISCRIMINATORS: [u8; 4] = [
16603 Self::QUOTE_HASH_DISCRIMINATOR,
16604 Self::QUASIQUOTE_HASH_DISCRIMINATOR,
16605 Self::UNQUOTE_HASH_DISCRIMINATOR,
16606 Self::UNQUOTE_SPLICE_HASH_DISCRIMINATOR,
16607 ];
16608
16609 /// Stable, per-variant byte discriminator that paired with the
16610 /// recursive inner hash builds the substrate's `Hash for Sexp`
16611 /// projection — `3` for [`Self::Quote`], `4` for
16612 /// [`Self::Quasiquote`], `5` for [`Self::Unquote`], `6` for
16613 /// [`Self::UnquoteSplice`]. The byte values are load-bearing
16614 /// because the expansion cache (`Expander::cache`) keys on the
16615 /// hash of `(macro_name, args)` — changing a discriminator silently
16616 /// invalidates every cached expansion AND mis-collides with the
16617 /// reserved bytes the non-quote-family Hash arms use (`0` for
16618 /// `Nil`, `1` for `Atom`, `2` for `List`). The closed set ensures
16619 /// the four arms partition `{3, 4, 5, 6}` injectively against the
16620 /// reserved bytes — a future quote-family extension must extend
16621 /// this method AND the non-quote-family arms in lockstep, with
16622 /// rustc binding the consistency through exhaustiveness over the
16623 /// closed enum.
16624 ///
16625 /// Post-lift the four arms route through the per-role
16626 /// `pub(crate) const` bytes on the closed-set [`QuoteForm`]
16627 /// algebra ([`Self::QUOTE_HASH_DISCRIMINATOR`],
16628 /// [`Self::QUASIQUOTE_HASH_DISCRIMINATOR`],
16629 /// [`Self::UNQUOTE_HASH_DISCRIMINATOR`],
16630 /// [`Self::UNQUOTE_SPLICE_HASH_DISCRIMINATOR`]) rather than
16631 /// inline `u8` literals — so a re-numbering that would silently
16632 /// invalidate every cached expansion lands as ONE edit to the
16633 /// matching `pub(crate) const` rather than at four scattered
16634 /// arm-literals. Every downstream consumer that binds to the
16635 /// algebra ([`Hash for Sexp`](crate::ast::Sexp)'s outer sweep,
16636 /// the [`crate::macro_expand::Expander::cache`] cache-key
16637 /// composition, the future coverage-tool sweeps) inherits the
16638 /// rename mechanically.
16639 ///
16640 /// `pub(crate)` because the byte-discriminator surface is an
16641 /// implementation detail of the substrate's `Hash for Sexp` cache-
16642 /// key contract; exposing it publicly would leak the cache-key
16643 /// shape through the API without enabling any external consumer
16644 /// the public projections (`Sexp::as_quote_form`, `Self::prefix`,
16645 /// `Self::as_unquote_form`) don't already serve.
16646 #[must_use]
16647 pub(crate) fn hash_discriminator(self) -> u8 {
16648 match self {
16649 Self::Quote => Self::QUOTE_HASH_DISCRIMINATOR,
16650 Self::Quasiquote => Self::QUASIQUOTE_HASH_DISCRIMINATOR,
16651 Self::Unquote => Self::UNQUOTE_HASH_DISCRIMINATOR,
16652 Self::UnquoteSplice => Self::UNQUOTE_SPLICE_HASH_DISCRIMINATOR,
16653 }
16654 }
16655
16656 /// Project the 4-of-4 quote-family marker into the 2-of-4
16657 /// template-substitution subset — `Some(UnquoteForm::Unquote)` for
16658 /// [`Self::Unquote`], `Some(UnquoteForm::Splice)` for
16659 /// [`Self::UnquoteSplice`], `None` for [`Self::Quote`] /
16660 /// [`Self::Quasiquote`] (the literal-quote and quasi-quote
16661 /// prefixes are wrappers, NOT substitution points). ONE projection
16662 /// on this algebra the [`crate::ast::Sexp::as_unquote`] derivation
16663 /// routes through — the (Sexp variant, UnquoteForm marker) pairing
16664 /// now binds at the typed [`crate::ast::Sexp::as_quote_form`]
16665 /// projection's output composed with this method's output, instead
16666 /// of being re-derived per-arm inside `Sexp::as_unquote`.
16667 ///
16668 /// The closed-set guarantee on [`UnquoteForm`] (exactly
16669 /// `Unquote ⊎ Splice`) AND on [`Self`] (exactly
16670 /// `Quote ⊎ Quasiquote ⊎ Unquote ⊎ UnquoteSplice`) ensures that the
16671 /// 2-of-4 subset is structurally fixed: a future variant joining
16672 /// the template-substitution surface extends both enums AND this
16673 /// method's match arm together, with rustc binding the extension
16674 /// through the projection's `Option` return type.
16675 #[must_use]
16676 pub fn as_unquote_form(self) -> Option<UnquoteForm> {
16677 match self {
16678 Self::Unquote => Some(UnquoteForm::Unquote),
16679 Self::UnquoteSplice => Some(UnquoteForm::Splice),
16680 Self::Quote | Self::Quasiquote => None,
16681 }
16682 }
16683
16684 /// Canonical `&'static str` iac-forge canonical-form tag of
16685 /// [`Self::Quote`] — `"quote"`. The ONE canonical bytes-payload on
16686 /// the closed-set [`QuoteForm`] algebra shared by [`Self::iac_forge_tag`]'s
16687 /// [`Self::Quote`] arm AND the `crate::interop` (removed) `From<&Sexp> for
16688 /// iac_forge::sexpr::SExpr` arm the projection feeds.
16689 ///
16690 /// Sibling posture to the closed set of per-role `pub const` bytes
16691 /// on the substrate's other closed-set outer algebras:
16692 /// [`Self::QUOTE_PREFIX`] / [`Self::QUASIQUOTE_PREFIX`] /
16693 /// [`Self::UNQUOTE_PREFIX`] / [`Self::UNQUOTE_SPLICE_PREFIX`] (per-
16694 /// role reader-prefix algebra on the SAME [`QuoteForm`] closed set),
16695 /// [`crate::error::MacroDefHead::DEFMACRO_KEYWORD`] /
16696 /// [`crate::error::MacroDefHead::DEFPOINT_TEMPLATE_KEYWORD`] /
16697 /// [`crate::error::MacroDefHead::DEFCHECK_KEYWORD`] (per-role
16698 /// head-keyword algebra on the CL macro-definition surface),
16699 /// [`Atom::TRUE_LITERAL`] / [`Atom::FALSE_LITERAL`] (per-role
16700 /// Scheme-bool spelling algebra on the atomic-payload surface).
16701 ///
16702 /// The (canonical iac-forge tag) axis lives ORTHOGONAL to the
16703 /// (canonical reader prefix) axis: `Self::QUOTE_PREFIX` (`"'"`) and
16704 /// `Self::QUOTE_IAC_FORGE_TAG` (`"quote"`) both project the same
16705 /// variant but through two distinct byte vocabularies — the reader
16706 /// axis for the Lisp source-code surface, the iac-forge axis for
16707 /// the cross-crate canonical-form surface (BLAKE3 attestation,
16708 /// render cache). A regression that inlines the `"quote"` literal
16709 /// at [`Self::iac_forge_tag`]'s [`Self::Quote`] arm and drifts the
16710 /// constant silently (e.g. a hypothetical rename to `"literal-quote"`
16711 /// on the iac-forge side while leaving the prefix `"'"` intact)
16712 /// fails at the algebra's `iac_forge_tag()` path-uniformity pin
16713 /// (`quote_form_iac_forge_tag_routes_through_typed_per_role_constants`)
16714 /// rather than at silent canonical-form drift where downstream
16715 /// BLAKE3 attestation keys silently mis-hash.
16716 pub const QUOTE_IAC_FORGE_TAG: &'static str = "quote";
16717
16718 /// Canonical `&'static str` iac-forge canonical-form tag of
16719 /// [`Self::Quasiquote`] — `"quasiquote"`. Sibling of
16720 /// [`Self::QUOTE_IAC_FORGE_TAG`] on the closed-set per-role
16721 /// quote-family iac-forge tag-bytes axis; see
16722 /// [`Self::QUOTE_IAC_FORGE_TAG`] for the algebra-level round-trip +
16723 /// disjointness contracts every sibling shares.
16724 pub const QUASIQUOTE_IAC_FORGE_TAG: &'static str = "quasiquote";
16725
16726 /// Canonical `&'static str` iac-forge canonical-form tag of
16727 /// [`Self::Unquote`] — `"unquote"`. Sibling of
16728 /// [`Self::QUOTE_IAC_FORGE_TAG`] on the closed-set per-role
16729 /// quote-family iac-forge tag-bytes axis.
16730 ///
16731 /// Byte-identical to the substrate's shorter diagnostic label
16732 /// [`crate::error::SexpShape::Unquote`]'s label projection
16733 /// (`SexpShape::label` returns `"unquote"` for this variant) — the
16734 /// two projections happen to agree on this variant's bytes but
16735 /// live at distinct algebraic layers (iac-forge canonical form vs
16736 /// substrate diagnostic surface); the divergence is load-bearing on
16737 /// the [`Self::UnquoteSplice`] arm (`"unquote-splicing"` vs
16738 /// `"unquote-splice"`) and this byte-level agreement here does not
16739 /// license a consolidation of the two axes. Pinned by
16740 /// `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`.
16741 pub const UNQUOTE_IAC_FORGE_TAG: &'static str = "unquote";
16742
16743 /// Canonical `&'static str` iac-forge canonical-form tag of
16744 /// [`Self::UnquoteSplice`] — `"unquote-splicing"`. The Common-Lisp-
16745 /// canonical spelling: a `,@x` form encodes as `(unquote-splicing x)`
16746 /// rather than `(unquote-splice x)`. That tag-string choice is
16747 /// INTENTIONALLY DISTINCT from the substrate's shorter diagnostic
16748 /// label projected by [`crate::error::SexpShape::label`] (which
16749 /// renders `[`Self::UnquoteSplice`]` as `"unquote-splice"` — the
16750 /// shorter idiom appropriate for `expected …, got unquote-splice`
16751 /// error surfaces). The two projections key the SAME closed set on
16752 /// TWO distinct boundaries — pinning the divergence at the typed
16753 /// per-role `pub const` documents the intent structurally: a
16754 /// future "consolidation" PR that homogenizes them would have to
16755 /// touch this constant explicitly, surfacing the boundary-distinct
16756 /// invariant at code-review time rather than silently.
16757 ///
16758 /// Sibling of [`Self::QUOTE_IAC_FORGE_TAG`] on the closed-set
16759 /// per-role quote-family iac-forge tag-bytes axis. The ONLY entry
16760 /// on this axis whose bytes disagree with the peer-axis
16761 /// [`crate::error::SexpShape::label`] projection — pinned by
16762 /// `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`
16763 /// alongside the three matched-arm agreements.
16764 pub const UNQUOTE_SPLICE_IAC_FORGE_TAG: &'static str = "unquote-splicing";
16765
16766 /// The closed-set forced-arity ALL array over the quote-family
16767 /// iac-forge canonical-form tag `&'static str` bytes in canonical
16768 /// declaration order matching [`Self::ALL`] element-wise. Sibling
16769 /// posture to [`Self::PREFIXES`] (`[&'static str; 4]` on the
16770 /// reader-prefix axis of the SAME [`QuoteForm`] closed set),
16771 /// [`crate::error::MacroDefHead::KEYWORDS`] (`[&'static str; 3]`
16772 /// on the CL macro-definition head algebra),
16773 /// [`Atom::BOOL_LITERALS`] (`[&'static str; 2]` on the Scheme-bool
16774 /// spelling algebra), and
16775 /// [`crate::macro_expand::MacroParams::LAMBDA_LIST_KEYWORDS`]
16776 /// (`[&'static str; 2]` on the CL lambda-list-keyword algebra) —
16777 /// every closed-set outer projection on the substrate now pins its
16778 /// canonical bytes at ONE `pub const` per role plus an ALL array
16779 /// for family-wide consumers.
16780 ///
16781 /// The (canonical iac-forge tag) axis + the (canonical reader
16782 /// prefix) axis together span the two production byte-vocabularies
16783 /// the [`QuoteForm`] closed set carries — [`Self::PREFIXES`] holds
16784 /// the Lisp source-code prefixes (`"'"`, `` "`" ``, `","`, `",@"`)
16785 /// the reader tokenizes on, [`Self::IAC_FORGE_TAGS`] holds the
16786 /// cross-crate canonical-form tag strings (`"quote"`,
16787 /// `"quasiquote"`, `"unquote"`, `"unquote-splicing"`) the
16788 /// iac-forge interop layer round-trips through. Adding a
16789 /// hypothetical fifth homoiconic prefix (a `,~` reverse-unquote, a
16790 /// `,?` conditional-unquote, a `#'` Common-Lisp function-quote)
16791 /// extends [`Self::ALL`] AND [`Self::PREFIXES`] AND
16792 /// [`Self::IAC_FORGE_TAGS`] AND [`Self::prefix`]'s arm AND
16793 /// [`Self::iac_forge_tag`]'s arm AND two new per-role `pub const`s
16794 /// (one on each axis) in lockstep — rustc's forced-arity check on
16795 /// `[&'static str; N]` fails compilation if any of the three ALL
16796 /// arrays grows without the others.
16797 ///
16798 /// Future consumers that compose against [`Self::IAC_FORGE_TAGS`]:
16799 /// - Cross-crate canonical-form completion (an authoring tool
16800 /// surfacing every legal iac-forge tag in a `(<tag> <inner>)`
16801 /// template — the completion set IS [`Self::IAC_FORGE_TAGS`]
16802 /// rather than four hand-enumerated `&'static str` literals per
16803 /// completion provider).
16804 /// - `tatara-check` coverage assertions that sweep workspace
16805 /// attestation payloads for every canonical iac-forge tag —
16806 /// the typed sweep replaces per-consumer inline enumeration of
16807 /// the four literals.
16808 /// - Any future audit-trail metric jointly labeled by
16809 /// [`Self::iac_forge_tag`] (e.g.
16810 /// `tatara_lisp_iac_forge_tag_total{tag="quote"}`) — the metric
16811 /// label set IS [`Self::IAC_FORGE_TAGS`] mapped through
16812 /// [`Self::iac_forge_tag`].
16813 pub const IAC_FORGE_TAGS: [&'static str; 4] = [
16814 Self::QUOTE_IAC_FORGE_TAG,
16815 Self::QUASIQUOTE_IAC_FORGE_TAG,
16816 Self::UNQUOTE_IAC_FORGE_TAG,
16817 Self::UNQUOTE_SPLICE_IAC_FORGE_TAG,
16818 ];
16819
16820 /// Canonical iac-forge interop tag — the symbol head the canonical
16821 /// 2-element-list encoding of a quote-family wrapper uses when
16822 /// projecting `tatara_lisp::Sexp` into `iac_forge::sexpr::SExpr`:
16823 /// `"quote"` for [`Self::Quote`], `"quasiquote"` for
16824 /// [`Self::Quasiquote`], `"unquote"` for [`Self::Unquote`],
16825 /// `"unquote-splicing"` for [`Self::UnquoteSplice`].
16826 ///
16827 /// The mapping is Common-Lisp-canonical: a `,@x` form encodes as
16828 /// `(unquote-splicing x)` rather than `(unquote-splice x)`. That
16829 /// tag-string choice is INTENTIONALLY DISTINCT from the substrate's
16830 /// shorter diagnostic label projected by
16831 /// [`crate::error::SexpShape::label`] (which renders
16832 /// `[`Self::UnquoteSplice`]` as `"unquote-splice"` — the shorter
16833 /// idiom appropriate for `expected …, got unquote-splice` error
16834 /// surfaces). The two projections key the SAME closed set on TWO
16835 /// distinct boundaries:
16836 ///
16837 /// * `iac_forge_tag` — cross-crate canonical form, BLAKE3 attestation
16838 /// keys, render-cache shape (load-bearing for byte-identical
16839 /// inter-crate compatibility with the iac-forge ecosystem).
16840 /// * `SexpShape::label` — operator-facing diagnostic label,
16841 /// `LispError::TypeMismatch.got` rendering, REPL/LSP
16842 /// shape-of-witness surface.
16843 ///
16844 /// Pre-lift the four canonical iac-forge tag strings lived inline
16845 /// across four arms in `crate::interop` (removed)'s
16846 /// `From<&Sexp> for iac_forge::sexpr::SExpr` impl, paired with the
16847 /// matching `Sexp::{Quote, Quasiquote, Unquote, UnquoteSplice}`
16848 /// patterns. The pairing was load-bearing yet only enforced by
16849 /// callsite discipline at a FOURTH consumer site (alongside `Hash`,
16850 /// `Display`, and `Sexp::as_unquote`) the prior closed-set
16851 /// `QuoteForm` lift did not reach (the `iac-forge` feature gate
16852 /// kept that site's drift risk silent in the default build). After
16853 /// this lift the interop arms collapse to ONE arm routing through
16854 /// [`crate::ast::Sexp::as_quote_form`] + this method, so the
16855 /// (Sexp variant, canonical tag string) pairing binds at ONE site
16856 /// on the substrate algebra regardless of which consumer surface
16857 /// (`Hash`, `Display`, `Sexp::as_unquote`, iac-forge interop)
16858 /// needs it.
16859 ///
16860 /// The `&'static str` lifetime is load-bearing: every iac-forge
16861 /// consumer projects through this method into the canonical
16862 /// 2-element-list head without an allocation, parallel to how
16863 /// [`Self::prefix`], [`UnquoteForm::marker`], and
16864 /// [`crate::error::SexpShape::label`] project their respective
16865 /// closed-set surfaces. A future homoiconic prefix-wrapper (e.g.
16866 /// hypothetical `,~` reverse-unquote) extends [`Self`] AND this
16867 /// method's match arm together — rustc binds the iac-forge
16868 /// canonical-form surface to the algebra through exhaustiveness.
16869 ///
16870 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
16871 /// quote-family canonical-form tag set becomes a TYPE projection
16872 /// on the substrate algebra rather than four `&'static str`
16873 /// literals scattered across the `interop` arms (parallel to how
16874 /// `Self::prefix` lifts the Display↔reader prefix and
16875 /// `Self::hash_discriminator` lifts the cache-key bytes).
16876 /// THEORY.md §VI.1 — generation over composition; the (Sexp
16877 /// variant, iac-forge tag) pairing appeared at the four
16878 /// `interop.rs` arms — past the ≥2 PRIME-DIRECTIVE trigger once
16879 /// the structural shape is named. THEORY.md §II.1 invariant 1 —
16880 /// typed entry; the cross-crate canonical-form projection IS the
16881 /// typed-exit gate at the iac-forge boundary, and naming its
16882 /// closed-set tag identity lifts the gate from per-site literal
16883 /// discipline to ONE method the iac-forge round-trip discipline
16884 /// binds against.
16885 #[must_use]
16886 pub fn iac_forge_tag(self) -> &'static str {
16887 match self {
16888 Self::Quote => Self::QUOTE_IAC_FORGE_TAG,
16889 Self::Quasiquote => Self::QUASIQUOTE_IAC_FORGE_TAG,
16890 Self::Unquote => Self::UNQUOTE_IAC_FORGE_TAG,
16891 Self::UnquoteSplice => Self::UNQUOTE_SPLICE_IAC_FORGE_TAG,
16892 }
16893 }
16894
16895 /// Inverse of [`Self::iac_forge_tag`] on the four-arm canonical CL
16896 /// tag closed set — `"quote"` decodes to `Some(Self::Quote)`,
16897 /// `"quasiquote"` decodes to `Some(Self::Quasiquote)`, `"unquote"`
16898 /// decodes to `Some(Self::Unquote)`, `"unquote-splicing"` decodes to
16899 /// `Some(Self::UnquoteSplice)`. Every other `tag` (empty string,
16900 /// PascalCase drift, the shorter substrate diagnostic label
16901 /// `"unquote-splice"` — which is INTENTIONALLY distinct from the
16902 /// CL canonical `"unquote-splicing"` per the substrate's
16903 /// two-vocabulary axis pinned by
16904 /// `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`,
16905 /// every arbitrary word not in the four-arm image) yields `None`.
16906 ///
16907 /// Structural roundtrip law (pinned by
16908 /// `quote_form_iac_forge_tag_round_trips_through_from_iac_forge_tag`):
16909 /// for every `qf: QuoteForm`,
16910 /// `Self::from_iac_forge_tag(qf.iac_forge_tag()) == Some(qf)`.
16911 /// Sibling posture to [`Self::from_lead_char`]'s inverse-of-
16912 /// [`Self::lead_char`] contract on the reader-lead-char axis —
16913 /// both name the typed inverse decoder on a per-role projection
16914 /// axis of the same closed set, with `Option<Self>` shape mirroring
16915 /// the partial decode over an unbounded string codomain into the
16916 /// four-arm typed closed set.
16917 ///
16918 /// Load-bearing use case: cross-crate iac-forge canonical-form
16919 /// inbound decoding. Pre-lift a consumer parsing a canonical
16920 /// `(<tag> <inner>)` list from an `iac_forge::sexpr::SExpr` (a
16921 /// downstream deserialization codepath, an LSP quick-fix that
16922 /// completes an iac-forge canonical-form skeleton, a
16923 /// `tatara-check` predicate that reads back an attested
16924 /// canonical form and re-typed-witnesses its shape) would have
16925 /// to hand-roll `match tag { "quote" => QuoteForm::Quote,
16926 /// "quasiquote" => …, "unquote-splicing" => …, _ => return None
16927 /// }` at each callsite; post-lift the (tag, typed variant)
16928 /// decode binds at ONE typed method on the substrate algebra
16929 /// composed as a linear sweep over [`Self::ALL`] keyed on
16930 /// [`Self::iac_forge_tag`]. The (tag literals, decode arms)
16931 /// pairing lives at ONE canonical site (the four
16932 /// [`Self::QUOTE_IAC_FORGE_TAG`] / [`Self::QUASIQUOTE_IAC_FORGE_TAG`]
16933 /// / [`Self::UNQUOTE_IAC_FORGE_TAG`] /
16934 /// [`Self::UNQUOTE_SPLICE_IAC_FORGE_TAG`] per-role constants
16935 /// [`Self::iac_forge_tag`]'s outbound arms bind to) rather than
16936 /// at TWO — the outbound projection (existing) plus a hand-rolled
16937 /// inbound decoder duplicated per callsite.
16938 ///
16939 /// Boundary distinction with [`Self::from_str`] (the substrate's
16940 /// [`FromStr`] impl derived via `#[closed_set(via = "prefix")]`):
16941 /// [`Self::from_str`] decodes the reader-punctuation vocabulary
16942 /// (`"'"`, `` "`" ``, `","`, `",@"`); THIS method decodes the
16943 /// cross-crate iac-forge canonical-form vocabulary (`"quote"`,
16944 /// `"quasiquote"`, `"unquote"`, `"unquote-splicing"`). The two
16945 /// closed-set inverse decoders key the SAME four-arm outer set
16946 /// through TWO orthogonal byte vocabularies — pinning them at
16947 /// distinct methods documents the axis-orthogonality
16948 /// [`Self::PREFIXES`] vs [`Self::IAC_FORGE_TAGS`] carries at the
16949 /// per-role forced-arity ALL array level. A consumer with a
16950 /// reader-punctuation byte in hand routes through [`FromStr`];
16951 /// a consumer with an iac-forge canonical tag in hand routes
16952 /// through THIS method — the vocabulary axis binds at the
16953 /// decoder-method boundary rather than at per-consumer inline
16954 /// dispatch.
16955 ///
16956 /// Case-sensitive by design — matches the case-sensitive
16957 /// [`FromStr`] posture (which decodes reader punctuation) and
16958 /// every other closed-set FromStr on the substrate. Non-const
16959 /// because `&str` equality is not const-evaluable on stable at
16960 /// substrate MSRV (parallel to how [`Self::FromStr`]'s decode
16961 /// body is non-const while [`Self::from_lead_char`] is
16962 /// `const fn` because `char` equality IS const-evaluable);
16963 /// callers that need a decode-at-compile-time surface stay on
16964 /// the reader-lead-char decoder.
16965 ///
16966 /// Post-lift the (iac-forge tag, typed variant) inverse decoder
16967 /// closes the FIFTH inverse-projection axis on the outer-`QuoteForm`
16968 /// algebra alongside [`Self::from_lead_char`] (the reader-lead-char
16969 /// axis inverse), [`Self::FromStr`] (the reader-prefix axis
16970 /// inverse, derived via `#[closed_set(via = "prefix")]`),
16971 /// [`crate::error::SexpShape::as_quote_form`] (the outer-shape
16972 /// carving inverse embedding), and
16973 /// [`crate::error::UnquoteForm::to_quote_form`] (the
16974 /// substitution-subset embedding inverse). The full outer
16975 /// quote-family algebra now closes ALL FIVE inverse-projection
16976 /// axes matched with their forward-projection siblings
16977 /// ([`Self::lead_char`] / [`Self::prefix`] / [`Self::sexp_shape`]
16978 /// / [`Self::as_unquote_form`] on the forward side).
16979 ///
16980 /// Theory anchor: THEORY.md §II.1 invariant 3 — typed exit; the
16981 /// inbound iac-forge canonical-form decode surface becomes a
16982 /// TYPE projection on the closed-set [`QuoteForm`] algebra
16983 /// rather than an inline match at every downstream consumer.
16984 /// THEORY.md §V.1 — knowable platform; the closed set of
16985 /// canonical CL tags becomes a decoder codomain rather than
16986 /// four inline `&'static str` literals scattered across future
16987 /// consumers that could drift independently. THEORY.md §VI.1 —
16988 /// generation over composition; the (tag, typed variant)
16989 /// pairing decodes at ONE typed method on the algebra composed
16990 /// from the pre-existing [`Self::ALL`] typed set and the
16991 /// [`Self::iac_forge_tag`] outbound projection — no new
16992 /// per-role primitive, the decode is a typed CONSEQUENCE of
16993 /// the existing family-wide primitives. Sibling posture to
16994 /// [`Self::from_lead_char`] which similarly composes as an
16995 /// inverse over [`Self::lead_char`] without introducing a new
16996 /// per-role primitive.
16997 ///
16998 /// Frontier inspiration: MLIR's typed-attribute
16999 /// `parseType(str) -> Optional<Type>` factory on the closed-set
17000 /// typed-attribute registry — the same inverse-decode shape on
17001 /// a Rust closed-set enum, where the (tag, typed variant)
17002 /// decode binds at ONE typed factory rather than at every
17003 /// downstream operation's parseAttribute callback. Racket's
17004 /// `(assq tag tag-alist)` typed lookup over a closed
17005 /// association list — the inverse decode projects through the
17006 /// ALL array without hand-rolling a per-tag match; `Self::ALL
17007 /// .iter().find(qf.iac_forge_tag() == tag)` is the Rust-typed
17008 /// peer on the closed-set outer-[`QuoteForm`] algebra with the
17009 /// ALL array standing in for Racket's typed association-list
17010 /// spine.
17011 #[must_use]
17012 pub fn from_iac_forge_tag(tag: &str) -> Option<Self> {
17013 Self::ALL
17014 .iter()
17015 .copied()
17016 .find(|qf| qf.iac_forge_tag() == tag)
17017 }
17018
17019 /// Project the typed marker into its matching [`crate::error::SexpShape`]
17020 /// variant — `Quote → SexpShape::Quote`, `Quasiquote → SexpShape::Quasiquote`,
17021 /// `Unquote → SexpShape::Unquote`, `UnquoteSplice → SexpShape::UnquoteSplice`.
17022 /// ONE projection on the closed-set quote-family algebra the substrate's
17023 /// outer-shape projection ([`crate::domain::sexp_shape`]) routes through
17024 /// for the four quote-family arms — so the (Sexp variant, SexpShape
17025 /// variant) pairing binds at ONE site on the typed algebra rather than
17026 /// at four byte-identical inline arms in [`crate::domain::sexp_shape`].
17027 ///
17028 /// The SIXTH consumer of the closed-set [`QuoteForm`] algebra, sibling
17029 /// of [`Self::prefix`] (Display / reader prefix-string surface),
17030 /// [`Self::hash_discriminator`] (Hash cache-key bytes surface),
17031 /// [`Self::as_unquote_form`] (2-of-4 template-substitution subset gate),
17032 /// [`Self::iac_forge_tag`] (cross-crate canonical-form tag surface), and
17033 /// [`Self::wrap`] (reader's marker → `Sexp::*` constructor surface).
17034 /// Composes with [`SexpShape::label`] to yield the short diagnostic
17035 /// label string the substrate's `LispError::TypeMismatch.got` slot
17036 /// renders — the (QuoteForm variant, SexpShape variant, short label)
17037 /// triple binds end-to-end through the typed algebra so a regression
17038 /// that drifts the short label silently between the typed marker and
17039 /// the diagnostic surface is structurally impossible.
17040 ///
17041 /// Bidirectional dual: the inverse projection
17042 /// [`crate::error::SexpShape::as_quote_form`] (12→4, partial)
17043 /// covers the 4-of-12 carving of [`SexpShape`] this embed reaches.
17044 /// The pair `(QuoteForm::sexp_shape,
17045 /// SexpShape::as_quote_form)` forms an `Iso(QuoteForm, QuoteShape ⊂
17046 /// SexpShape)`: every typed marker round-trips through the embed
17047 /// (`QuoteForm::sexp_shape(qf).as_quote_form() == Some(qf)` for
17048 /// every `qf: QuoteForm`), every quote-shape pre-image recovers
17049 /// the typed marker. The non-quote-family shapes (`Nil`, `List`,
17050 /// every atomic-payload variant) form the kernel of the inverse —
17051 /// `as_quote_form` returns `None` for them. See
17052 /// [`crate::error::SexpShape::as_quote_form`]'s docstring for the
17053 /// composition law's other direction + disjointness with the
17054 /// atomic-payload sibling `SexpShape::as_atom_kind`.
17055 ///
17056 /// Canonical [`SexpShape`] embed target for the [`Self::Quote`]
17057 /// quote-family arm on the QuoteForm ⊂ SexpShape carving —
17058 /// [`SexpShape::Quote`]. Per-role peer of `Self::Quote` on the
17059 /// closed-set outer-shape embed axis; consumers with a `QuoteForm`
17060 /// variant in hand at compile time bind the canonical embed target
17061 /// through ONE typed `pub const` per role rather than through
17062 /// runtime dispatch via [`Self::sexp_shape`] or by re-deriving the
17063 /// QuoteForm ⊂ SexpShape variant pairing inline.
17064 ///
17065 /// Sibling posture to [`Self::QUOTE_LABEL`] (the per-role
17066 /// diagnostic label alias) and [`Self::QUOTE_HASH_DISCRIMINATOR`]
17067 /// (the per-role outer-Sexp cache-key byte) on the closed-set
17068 /// QuoteForm algebra — each closes a distinct per-role
17069 /// sub-vocabulary axis on the QuoteForm carving. This constant
17070 /// closes the FOURTH per-role axis on [`QuoteForm`] (the
17071 /// `SexpShape`-embed axis, paired with the pre-existing
17072 /// `&'static str` reader-prefix + diagnostic-label +
17073 /// cross-crate iac-forge-tag axes AND the `u8` cache-key axis) at
17074 /// ONE typed alias through the peer superset variant on the
17075 /// [`SexpShape`] closed set.
17076 ///
17077 /// Sibling posture to the peer 6-of-12 atomic-payload carving's
17078 /// per-role SHAPE aliases ([`AtomKind::SYMBOL_SHAPE`] …
17079 /// [`AtomKind::BOOL_SHAPE`] — every one an alias of its
17080 /// [`SexpShape`] peer on the AtomKind ⊂ SexpShape 6-of-12
17081 /// carving). Post-lift the SexpShape's per-carving embed-target
17082 /// axis is uniformly surfaced through per-role `pub const *_SHAPE`
17083 /// aliases on every sub-carving that carries a bidirectional
17084 /// (embed, project) `Iso(_, _ ⊂ SexpShape)` — first `AtomKind` (6),
17085 /// now `QuoteForm` (4).
17086 pub const QUOTE_SHAPE: SexpShape = SexpShape::Quote;
17087
17088 /// Canonical [`SexpShape`] embed target for the [`Self::Quasiquote`]
17089 /// quote-family arm on the QuoteForm ⊂ SexpShape carving —
17090 /// [`SexpShape::Quasiquote`]. Per-role peer of `Self::Quasiquote`.
17091 /// See [`Self::QUOTE_SHAPE`] for the alias-chain shape every
17092 /// sibling shares.
17093 pub const QUASIQUOTE_SHAPE: SexpShape = SexpShape::Quasiquote;
17094
17095 /// Canonical [`SexpShape`] embed target for the [`Self::Unquote`]
17096 /// quote-family arm on the QuoteForm ⊂ SexpShape carving —
17097 /// [`SexpShape::Unquote`]. Per-role peer of `Self::Unquote`.
17098 pub const UNQUOTE_SHAPE: SexpShape = SexpShape::Unquote;
17099
17100 /// Canonical [`SexpShape`] embed target for the [`Self::UnquoteSplice`]
17101 /// quote-family arm on the QuoteForm ⊂ SexpShape carving —
17102 /// [`SexpShape::UnquoteSplice`]. Per-role peer of
17103 /// `Self::UnquoteSplice`.
17104 pub const UNQUOTE_SPLICE_SHAPE: SexpShape = SexpShape::UnquoteSplice;
17105
17106 /// Closed-set forced-arity ALL array over the canonical
17107 /// [`SexpShape`] embed targets on the QuoteForm ⊂ SexpShape
17108 /// 4-of-12 carving, in declaration order matching [`Self::ALL`]
17109 /// element-wise (pinned by
17110 /// `quote_form_shapes_align_with_all_by_index`). Sibling posture
17111 /// to [`Self::LABELS`] (`[&'static str; 4]` — per-role diagnostic
17112 /// bytes), [`Self::PREFIXES`] (`[&'static str; 4]` — per-role
17113 /// reader-punctuation bytes), [`Self::IAC_FORGE_TAGS`] (`[&'static
17114 /// str; 4]` — cross-crate iac-forge canonical-form tag bytes), and
17115 /// [`Self::HASH_DISCRIMINATORS`] (`[u8; 4]` — per-role outer-Sexp
17116 /// cache-key bytes) on the SAME closed-set QuoteForm algebra;
17117 /// where those four arrays lift per-role `&'static str` and `u8`
17118 /// sub-vocabularies onto the substrate, this array lifts the
17119 /// per-role [`SexpShape`] embed-target sub-vocabulary at the same
17120 /// `[_; 4]` forced arity.
17121 ///
17122 /// Sibling posture to [`AtomKind::SHAPES`] (`[SexpShape; 6]`) —
17123 /// the peer atomic-payload carving's family-wide embed-target
17124 /// array on the AtomKind ⊂ SexpShape 6-of-12 carving. Together the
17125 /// two `SHAPES` arrays cover the TWO bidirectional sub-carvings of
17126 /// [`SexpShape`] (`Iso(AtomKind, AtomShape ⊂ SexpShape)` + `Iso(QuoteForm,
17127 /// QuoteShape ⊂ SexpShape)`) — a family-wide sweep zipping every
17128 /// carving's `ALL` + `SHAPES` in lockstep now closes over TWO
17129 /// carvings' 10-of-12 embed targets at ONE typed pair-of-arrays
17130 /// each.
17131 ///
17132 /// Pre-lift the four [`SexpShape`] embed targets had NO per-role
17133 /// primitive on this closed-set algebra — a consumer with a
17134 /// `QuoteForm` variant in hand at compile time reaching for the
17135 /// canonical embed target had to spell
17136 /// `QuoteForm::Quote.sexp_shape()` (runtime dispatch through the
17137 /// four-arm match body) OR re-derive the QuoteForm ⊂ SexpShape
17138 /// variant pairing at the call site by importing both enums and
17139 /// spelling `SexpShape::Quote` inline. Post-lift the FOUR canonical
17140 /// embed targets bind at ONE `pub const` per role on the typed
17141 /// [`QuoteForm`] algebra AND at [`Self::SHAPES`] as a family-wide
17142 /// forced-arity array — a future LSP / REPL completion bar keyed
17143 /// on `QuoteForm::SHAPES` for the "which SexpShape does this
17144 /// QuoteForm embed into?" outer-shape column, a `tatara-check`
17145 /// coverage sweep zipping `QuoteForm::ALL` / `LABELS` / `PREFIXES`
17146 /// / `IAC_FORGE_TAGS` / `HASH_DISCRIMINATORS` / `SHAPES` in
17147 /// lockstep for a family-wide (variant, label, prefix, iac-forge
17148 /// tag, byte, embed-target) sextuple render, or a Sekiban
17149 /// audit-trail metric jointly labeled by the embed-target's
17150 /// SexpShape identity reads through the typed constants on this
17151 /// subset algebra without re-deriving the 4-of-12 carving inline.
17152 ///
17153 /// Round-trip identity with the inverse projection
17154 /// [`crate::error::SexpShape::as_quote_form`]: for every index `i`,
17155 /// `Self::SHAPES[i].as_quote_form() == Some(Self::ALL[i])`
17156 /// (pinned by
17157 /// `quote_form_shapes_align_with_all_by_index_through_as_quote_form`) —
17158 /// the embed / project section closes as a family-wide array-
17159 /// indexed law rather than as a per-variant assertion sweep.
17160 /// Adding a hypothetical fifth quote-family wrapper (e.g. `,~`
17161 /// reverse-unquote, `,?` conditional-unquote, `#'` Common-Lisp
17162 /// function-quote) extends [`Self::ALL`] AND [`Self::SHAPES`] AND
17163 /// [`SexpShape::ALL`] AND adds ONE per-role `pub const *_SHAPE` in
17164 /// lockstep — rustc's forced-arity check on the two `[_; N]`
17165 /// arrays fails compilation if EITHER ALL array grows without the
17166 /// other, AND the peer [`SexpShape::as_quote_form`] arm must grow
17167 /// in lockstep to preserve the round-trip identity.
17168 ///
17169 /// Theory anchor: THEORY.md §III — the typescape; the four
17170 /// canonical [`SexpShape`] embed targets bind at ONE typed
17171 /// `[SexpShape; 4]` array on the closed-set QuoteForm algebra
17172 /// rather than at zero-primitive-on-this-subset-plus-four-inline-
17173 /// lookups scattered across the substrate. Closes the FOURTH
17174 /// per-role `pub const` axis on the QuoteForm carving alongside
17175 /// the pre-existing LABELS + PREFIXES + IAC_FORGE_TAGS +
17176 /// HASH_DISCRIMINATORS axes. THEORY.md §V.1 — knowable platform;
17177 /// the family's cardinality becomes a TYPE-level constant on the
17178 /// substrate algebra rather than a per-consumer runtime dispatch
17179 /// through the composition. THEORY.md §II.1 invariant 2 — free
17180 /// middle; the (embed, project) pair binds at THREE typed sites
17181 /// now — the projection method [`Self::sexp_shape`], this family-
17182 /// wide array, AND the peer inverse
17183 /// [`crate::error::SexpShape::as_quote_form`] — with rustc-enforced
17184 /// consistency across all three. THEORY.md §VI.1 — generation
17185 /// over composition; the family-wide contract sweeps (alignment
17186 /// with `ALL`, round-trip through `as_quote_form`, membership
17187 /// through `sexp_shape`, pairwise injectivity across the four
17188 /// embed targets) emerge from the composition of TWO substrate
17189 /// primitives (this `pub const` array + the four per-role
17190 /// `pub const *_SHAPE` aliases) rather than as per-variant inline
17191 /// assertions duplicated at each call site.
17192 pub const SHAPES: [SexpShape; 4] = [
17193 Self::QUOTE_SHAPE,
17194 Self::QUASIQUOTE_SHAPE,
17195 Self::UNQUOTE_SHAPE,
17196 Self::UNQUOTE_SPLICE_SHAPE,
17197 ];
17198
17199 /// Project the typed marker into its matching [`crate::error::SexpShape`]
17200 /// variant — `Quote → SexpShape::Quote`, `Quasiquote → SexpShape::Quasiquote`,
17201 /// `Unquote → SexpShape::Unquote`, `UnquoteSplice → SexpShape::UnquoteSplice`.
17202 /// ONE projection on the closed-set quote-family algebra the substrate's
17203 /// outer-shape projection ([`crate::domain::sexp_shape`]) routes through
17204 /// for the four quote-family arms — so the (Sexp variant, SexpShape
17205 /// variant) pairing binds at ONE site on the typed algebra rather than
17206 /// at four byte-identical inline arms in [`crate::domain::sexp_shape`].
17207 ///
17208 /// The SIXTH consumer of the closed-set [`QuoteForm`] algebra, sibling
17209 /// of [`Self::prefix`] (Display / reader prefix-string surface),
17210 /// [`Self::hash_discriminator`] (Hash cache-key bytes surface),
17211 /// [`Self::as_unquote_form`] (2-of-4 template-substitution subset gate),
17212 /// [`Self::iac_forge_tag`] (cross-crate canonical-form tag surface), and
17213 /// [`Self::wrap`] (reader's marker → `Sexp::*` constructor surface).
17214 /// Composes with [`SexpShape::label`] to yield the short diagnostic
17215 /// label string the substrate's `LispError::TypeMismatch.got` slot
17216 /// renders — the (QuoteForm variant, SexpShape variant, short label)
17217 /// triple binds end-to-end through the typed algebra so a regression
17218 /// that drifts the short label silently between the typed marker and
17219 /// the diagnostic surface is structurally impossible.
17220 ///
17221 /// Each arm routes through the per-role `pub const` on `impl Self`
17222 /// ([`Self::QUOTE_SHAPE`], [`Self::QUASIQUOTE_SHAPE`],
17223 /// [`Self::UNQUOTE_SHAPE`], [`Self::UNQUOTE_SPLICE_SHAPE`]) so the
17224 /// four canonical embed targets bind at ONE typed source of truth
17225 /// per role rather than as inline `SexpShape::X` literals scattered
17226 /// across the `match` body. Sibling posture to
17227 /// [`AtomKind::sexp_shape`]'s post-lift routing through
17228 /// [`AtomKind::SYMBOL_SHAPE`] … [`AtomKind::BOOL_SHAPE`] on the peer
17229 /// 6-of-12 atomic-payload carving — the per-role `pub const *_SHAPE`
17230 /// routing is now uniform across every sub-carving of [`SexpShape`]
17231 /// that has a bidirectional (embed, project) isomorphism, closing
17232 /// the (embed-target constant, embed-target array, projection
17233 /// method) trio on each sub-carving in lockstep.
17234 ///
17235 /// Post-lift routing pin
17236 /// `quote_form_sexp_shape_routes_through_typed_per_role_constants`
17237 /// catches a regression that re-inlines the four `SexpShape::X` arm
17238 /// literals here and silently drifts ONE arm from the per-role
17239 /// `pub const` alias — the routing agreement is a TYPED CONSEQUENCE
17240 /// of the composition rather than literal discipline at two sites.
17241 ///
17242 /// Bidirectional dual: the inverse projection
17243 /// [`crate::error::SexpShape::as_quote_form`] (12→4, partial)
17244 /// covers the 4-of-12 carving of [`SexpShape`] this embed reaches.
17245 /// The pair `(QuoteForm::sexp_shape,
17246 /// SexpShape::as_quote_form)` forms an `Iso(QuoteForm, QuoteShape ⊂
17247 /// SexpShape)`: every typed marker round-trips through the embed
17248 /// (`QuoteForm::sexp_shape(qf).as_quote_form() == Some(qf)` for
17249 /// every `qf: QuoteForm`), every quote-shape pre-image recovers
17250 /// the typed marker. The non-quote-family shapes (`Nil`, `List`,
17251 /// every atomic-payload variant) form the kernel of the inverse —
17252 /// `as_quote_form` returns `None` for them. See
17253 /// [`crate::error::SexpShape::as_quote_form`]'s docstring for the
17254 /// composition law's other direction + disjointness with the
17255 /// atomic-payload sibling `SexpShape::as_atom_kind`.
17256 ///
17257 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (QuoteForm
17258 /// variant, SexpShape variant) pairing becomes a TYPE projection on
17259 /// the substrate algebra rather than four inline arms in
17260 /// [`crate::domain::sexp_shape`]. A typo or swap at the shape-projection
17261 /// site is no longer a runtime drift but a compile error against the
17262 /// typed projection. THEORY.md §II.1 invariant 2 — free middle; SIX
17263 /// consumers of the [`QuoteForm`] algebra now route through ONE typed
17264 /// closed-set match family, so a regression that drifts ONE consumer's
17265 /// pairing from the others cannot reach the substrate's runtime.
17266 /// THEORY.md §VI.1 — generation over composition; the (Sexp variant,
17267 /// SexpShape variant) pairing appeared at four arms in `sexp_shape` —
17268 /// past the ≥2 PRIME-DIRECTIVE trigger once the structural shape is
17269 /// named.
17270 #[must_use]
17271 pub fn sexp_shape(self) -> SexpShape {
17272 match self {
17273 Self::Quote => Self::QUOTE_SHAPE,
17274 Self::Quasiquote => Self::QUASIQUOTE_SHAPE,
17275 Self::Unquote => Self::UNQUOTE_SHAPE,
17276 Self::UnquoteSplice => Self::UNQUOTE_SPLICE_SHAPE,
17277 }
17278 }
17279
17280 /// Project the typed marker to its canonical short diagnostic label —
17281 /// `"quote"` for [`Self::Quote`], `"quasiquote"` for
17282 /// [`Self::Quasiquote`], `"unquote"` for [`Self::Unquote`],
17283 /// `"unquote-splice"` for [`Self::UnquoteSplice`]. Body composes
17284 /// through `self.sexp_shape().label()` — routing through
17285 /// [`Self::sexp_shape`] (the typed 4-of-12 outer-value → SexpShape
17286 /// projection) then [`SexpShape::label`] (the canonical 12-arm
17287 /// diagnostic-label projection) so the (QuoteForm variant, short
17288 /// diagnostic string) pairing lives at ONE canonical site
17289 /// ([`SexpShape::label`]'s four quote-family arms in `error.rs`)
17290 /// rather than at four inline `&'static str` arms on the closed-set
17291 /// `QuoteForm` algebra.
17292 ///
17293 /// The outer-shape peer of [`crate::ast::Sexp::type_name`] one
17294 /// algebra layer up (`self.shape().label()` on outer-`Sexp`) and of
17295 /// [`crate::ast::Atom::label`] one algebra layer down
17296 /// (`self.kind().label()` on outer-`Atom` through [`AtomKind`]).
17297 /// Where `Atom::label` composes through the atomic-payload 6-of-12
17298 /// carving via [`AtomKind`] into [`SexpShape::label`], this method
17299 /// composes through the quote-family 4-of-12 carving directly onto
17300 /// [`SexpShape::label`] — the (label, sexp_shape, hash_discriminator)
17301 /// trio the outer-`Atom` algebra closed one lift back
17302 /// (`Atom::hash_discriminator`, e49f550) is now mirrored on the
17303 /// `QuoteForm` algebra: `prefix` (reader punctuation) and
17304 /// `iac_forge_tag` (CL canonical form) key the SAME closed set on
17305 /// their own boundaries, and `label` keys it on the substrate's
17306 /// operator-facing diagnostic boundary.
17307 ///
17308 /// Composition law: `qf.label() == qf.sexp_shape().label()` for every
17309 /// `qf: QuoteForm`. Pinned by
17310 /// `quote_form_label_composes_through_sexp_shape_label_for_every_variant`
17311 /// across all four variants — the pin asserts pointer-equality on the
17312 /// returned `&'static str` so a regression that re-inlines the four
17313 /// literals here (and gains its own drift surface separate from the
17314 /// canonical [`SexpShape::label`] site) surfaces immediately. Sibling
17315 /// of `atom_label_composes_through_kind_label_for_every_variant` one
17316 /// algebra layer down (on the outer-`Atom` value / `AtomKind` marker
17317 /// pair) and
17318 /// `sexp_type_name_method_composes_through_shape_label_for_every_outer_shape`
17319 /// one algebra layer up (on the outer-`Sexp` value / `SexpShape`
17320 /// marker pair).
17321 ///
17322 /// Cross-algebra agreement law: for every `qf: QuoteForm` and every
17323 /// `inner: Sexp`, `qf.label() == qf.wrap(inner).type_name()`. The
17324 /// (QuoteForm variant, canonical label) pairing lands at the SAME
17325 /// `&'static str` regardless of whether the consumer holds the typed
17326 /// marker directly or an outer-`Sexp` wrapper produced from
17327 /// [`Self::wrap`] — so a regression that drifts one algebra layer's
17328 /// label from the other (a `QuoteForm::label` re-inlined onto a
17329 /// different literal, a `Sexp::type_name` re-routed through a stale
17330 /// shape projection, a `QuoteForm::sexp_shape` arm that swaps two
17331 /// markers) fails-loudly here rather than as a silent operator-facing
17332 /// diagnostic drift at every consumer that pattern-matches on the
17333 /// outer-`Sexp` label vs the outer-`QuoteForm` label independently.
17334 /// Pinned by `quote_form_label_agrees_with_sexp_type_name_at_every_quote_form_arm`.
17335 ///
17336 /// Divergence law (boundary distinction with [`Self::iac_forge_tag`]):
17337 /// at the [`Self::UnquoteSplice`] arm, `qf.label() == "unquote-splice"`
17338 /// while `qf.iac_forge_tag() == "unquote-splicing"`. The two
17339 /// projections key the SAME closed-set on TWO distinct boundaries
17340 /// (substrate diagnostic surface vs cross-crate CL canonical form)
17341 /// and their intentional divergence at the `Splice` arm is pinned by
17342 /// `quote_form_label_diverges_from_iac_forge_tag_for_unquote_splice`
17343 /// — sibling posture to
17344 /// `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`
17345 /// which pinned the divergence at the `sexp_shape().label()`
17346 /// composition; this pin lifts the divergence contract onto the new
17347 /// typed peer.
17348 ///
17349 /// The `&'static str` lifetime is load-bearing: every future consumer
17350 /// with a `QuoteForm` in hand wanting the substrate's short
17351 /// diagnostic string projects through this method into the
17352 /// `LispError::TypeMismatch.got` slot / REPL / LSP surface without an
17353 /// allocation, parallel to how [`Self::prefix`] projects the reader
17354 /// punctuation and [`Self::iac_forge_tag`] projects the CL canonical
17355 /// tag. A future homoiconic prefix-wrapper (e.g. hypothetical `,~`
17356 /// reverse-unquote) extends [`Self`] AND [`SexpShape::label`]
17357 /// together — rustc binds the diagnostic surface to the algebra
17358 /// through the closed-set composition without touching this method.
17359 ///
17360 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (QuoteForm
17361 /// variant, canonical short label) pairing becomes a TYPE projection
17362 /// on the substrate algebra composed through the pre-existing outer-
17363 /// shape projection, rather than at a per-callsite
17364 /// `.sexp_shape().label()` two-hop the load-bearing pin already
17365 /// carries as a composition-law contract. THEORY.md §II.1 invariant 2
17366 /// — free middle; the outer-`QuoteForm` diagnostic-label algebra now
17367 /// closes over THREE typed layers (`QuoteForm` → [`SexpShape`] →
17368 /// `&'static str`) with rustc-enforced consistency across each — a
17369 /// regression that drifts ONE layer's mapping from the others cannot
17370 /// reach the substrate's runtime typed-witness surface,
17371 /// `LispError::TypeMismatch.got` slot, or [`crate::error::SexpWitness::shape`]
17372 /// projection. THEORY.md §VI.1 — generation over composition; the
17373 /// outer-value diagnostic-label projection is the missing algebra
17374 /// layer between the outer `QuoteForm` and the pre-existing marker-
17375 /// level label projection — the two pre-existing typed layers become
17376 /// a full THREE-layer typed composition through ONE new named
17377 /// projection, closing the (prefix, iac_forge_tag, sexp_shape,
17378 /// hash_discriminator, label) quintet on the outer-`QuoteForm`
17379 /// algebra.
17380 ///
17381 /// Frontier inspiration: MLIR's `mlir::OperationName::getStringRef()`
17382 /// composed with an op-family typed projection — narrowing a
17383 /// closed-set op-family value through its typed identity yields the
17384 /// canonical diagnostic string identity in ONE typed composition on
17385 /// the op-family algebra. Translated through the substrate's
17386 /// [`QuoteForm`] outer-marker algebra, `qf.sexp_shape().label()`
17387 /// closes the (typed marker, canonical diagnostic label) pairing at
17388 /// ONE typed projection on the marker algebra composed through the
17389 /// outer-shape's per-carving canonical site. Racket's `(quote-kind
17390 /// qf)` composed with `(kind-label kind)` on the quote-family
17391 /// taxonomy — the typed diagnostic label emerges from a two-hop
17392 /// composition on the closed-set marker through the typed outer-shape
17393 /// identity. `QuoteForm::label` is the Rust-typed peer on the
17394 /// closed-set outer-[`QuoteForm`] algebra with [`SexpShape`] standing
17395 /// in for Racket's quote-family taxonomy.
17396 #[must_use]
17397 pub fn label(self) -> &'static str {
17398 self.sexp_shape().label()
17399 }
17400
17401 /// Canonical `&'static str` bytes for the [`Self::Quote`] quote-family
17402 /// marker — aliases [`SexpShape::QUOTE_LABEL`] on the QuoteForm ⊂
17403 /// SexpShape carving so the marker-level per-role bytes bind at ONE
17404 /// `pub const` on the parent superset's quote-family arm rather than
17405 /// at TWO sites (the per-role `pub const` AND a parallel inline
17406 /// literal). Per-role peer of `Self::Quote` on the closed-set quote-
17407 /// family algebra; consumers reach for `QuoteForm::QUOTE_LABEL` when
17408 /// the caller has a variant in hand at compile time and wants the
17409 /// canonical diagnostic bytes without runtime dispatch through
17410 /// [`Self::label`].
17411 ///
17412 /// Sibling posture to the peer 6-of-12 atomic-payload carving's per-
17413 /// role LABEL aliases ([`crate::ast::AtomKind::SYMBOL_LABEL`] …
17414 /// [`crate::ast::AtomKind::BOOL_LABEL`] — every one an alias of its
17415 /// [`SexpShape`] peer) and the peer 2-of-12 structural-residual
17416 /// carving's per-role LABEL aliases
17417 /// ([`crate::error::StructuralKind::NIL_LABEL`] +
17418 /// [`crate::error::StructuralKind::LIST_LABEL`]) — this closes the
17419 /// fourth and final closed-set sub-carving of [`SexpShape`] whose
17420 /// per-role diagnostic-label bytes are surfaced through the same
17421 /// alias-chain shape rather than reachable only through the
17422 /// composition [`Self::sexp_shape`] + [`SexpShape::label`]. Every
17423 /// SexpShape sub-carving (atomic payload, quote family, structural
17424 /// residual) now exposes its per-role LABEL bytes at ONE `pub const`
17425 /// per role on its subset algebra AS WELL AS at the parent
17426 /// superset's `SexpShape::*_LABEL`.
17427 ///
17428 /// The prefix-family peer of THIS `&'static str` constant is
17429 /// [`Self::QUOTE_PREFIX`] (`"'"` — reader-punctuation byte); the
17430 /// canonical-form peer is [`Self::QUOTE_IAC_FORGE_TAG`] (`"quote"` —
17431 /// cross-crate iac-forge tag). At the `Quote` arm the label and
17432 /// the iac-forge tag agree byte-for-byte (`"quote"`); the divergence
17433 /// axis lives at [`Self::UNQUOTE_SPLICE_LABEL`] (`"unquote-splice"`)
17434 /// vs [`Self::UNQUOTE_SPLICE_IAC_FORGE_TAG`] (`"unquote-splicing"`).
17435 /// The three parallel per-role `pub const` families (prefix, label,
17436 /// iac-forge tag) close the (reader, diagnostic, canonical-form)
17437 /// triple on the outer-`QuoteForm` algebra.
17438 pub const QUOTE_LABEL: &'static str = SexpShape::QUOTE_LABEL;
17439
17440 /// Canonical `&'static str` bytes for the [`Self::Quasiquote`] quote-
17441 /// family marker — aliases [`SexpShape::QUASIQUOTE_LABEL`] on the
17442 /// QuoteForm ⊂ SexpShape carving. Per-role peer of `Self::Quasiquote`.
17443 /// See [`Self::QUOTE_LABEL`] for the alias-chain shape every sibling
17444 /// shares.
17445 pub const QUASIQUOTE_LABEL: &'static str = SexpShape::QUASIQUOTE_LABEL;
17446
17447 /// Canonical `&'static str` bytes for the [`Self::Unquote`] quote-
17448 /// family marker — aliases [`SexpShape::UNQUOTE_LABEL`] on the
17449 /// QuoteForm ⊂ SexpShape carving. Per-role peer of `Self::Unquote`.
17450 /// See [`Self::QUOTE_LABEL`] for the alias-chain shape every sibling
17451 /// shares.
17452 pub const UNQUOTE_LABEL: &'static str = SexpShape::UNQUOTE_LABEL;
17453
17454 /// Canonical `&'static str` bytes for the [`Self::UnquoteSplice`]
17455 /// quote-family marker — aliases [`SexpShape::UNQUOTE_SPLICE_LABEL`]
17456 /// on the QuoteForm ⊂ SexpShape carving. Per-role peer of
17457 /// `Self::UnquoteSplice`; the `"unquote-splice"` short label matches
17458 /// [`SexpShape::UNQUOTE_SPLICE_LABEL`] byte-for-byte and diverges
17459 /// INTENTIONALLY from [`Self::UNQUOTE_SPLICE_IAC_FORGE_TAG`]
17460 /// (`"unquote-splicing"`) — the two projections key the SAME closed
17461 /// set on TWO distinct boundaries (substrate diagnostic surface vs
17462 /// cross-crate Common-Lisp canonical form). The divergence is pinned
17463 /// by `quote_form_label_diverges_from_iac_forge_tag_for_unquote_splice`
17464 /// on the runtime projection and by the byte-equality pins on THIS
17465 /// constant vs its iac-forge peer on the per-role `pub const`
17466 /// surface. See [`Self::QUOTE_LABEL`] for the alias-chain shape
17467 /// every sibling shares.
17468 pub const UNQUOTE_SPLICE_LABEL: &'static str = SexpShape::UNQUOTE_SPLICE_LABEL;
17469
17470 /// Closed-set forced-arity ALL array over the canonical quote-family
17471 /// marker `&'static str` bytes, in declaration order matching
17472 /// [`Self::ALL`] element-wise (pinned by
17473 /// `quote_form_labels_align_with_all_by_index`). Sibling posture to
17474 /// [`crate::error::SexpShape::LABELS`] (`[&'static str; 12]` — the
17475 /// superset carving this QuoteForm subset embeds into),
17476 /// [`crate::ast::AtomKind::LABELS`] (`[&'static str; 6]` — the peer
17477 /// 6-of-12 atomic-payload carving's ALL array),
17478 /// [`crate::error::StructuralKind::LABELS`] (`[&'static str; 2]` —
17479 /// the peer 2-of-12 structural-residual carving's ALL array),
17480 /// [`Self::PREFIXES`] (`[&'static str; 4]` — reader-prefix axis on
17481 /// this same algebra), and [`Self::IAC_FORGE_TAGS`]
17482 /// (`[&'static str; 4]` — canonical-form tag axis on this same
17483 /// algebra) — every closed-set outer projection on the substrate
17484 /// that carries an `&'static str`-per-variant label now pins its
17485 /// per-role canonical bytes at ONE `pub const` per role PLUS an ALL
17486 /// array for family-wide consumers.
17487 ///
17488 /// Pre-lift the four quote-family marker labels had NO per-role
17489 /// primitive on this closed-set algebra — a consumer with a
17490 /// [`QuoteForm`] variant in hand at compile time reaching for the
17491 /// canonical diagnostic bytes had to spell `QuoteForm::Quote.label()`
17492 /// (runtime dispatch through the composition [`Self::sexp_shape`] +
17493 /// [`SexpShape::label`]) OR reach across the algebra boundary into
17494 /// [`SexpShape::QUOTE_LABEL`] and re-derive the QuoteForm ⊂
17495 /// SexpShape variant pairing at the call site. Post-lift the FOUR
17496 /// canonical labels bind at ONE `pub const` per role on the typed
17497 /// [`QuoteForm`] algebra AND at [`Self::LABELS`] as a family-wide
17498 /// forced-arity array — a future LSP / REPL completion bar keyed on
17499 /// `QuoteForm::LABELS` for the "quote-family" carving-axis column,
17500 /// a `tatara-check` coverage sweep over the quote-family arms of a
17501 /// `TypeMismatch.got` corpus, or a Sekiban audit-trail metric
17502 /// jointly labeled by the quote-family marker
17503 /// (`tatara_lisp_quote_family_label_total{label="quote"}`) reads
17504 /// through the typed constants on this subset algebra without re-
17505 /// deriving the 4-of-12 carving inline OR reaching across into the
17506 /// superset's twelve-entry `SexpShape::LABELS` array + filtering.
17507 ///
17508 /// Each entry is byte-for-byte identical to the corresponding
17509 /// [`SexpShape`] quote-family arm — an intentional cross-axis
17510 /// overlap pinned by
17511 /// `quote_form_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
17512 /// so a future label rename on EITHER side (a `SexpShape`
17513 /// `"quote"` → `"cite"` drift, a `QuoteForm` rename that skips the
17514 /// alias, a hypothetical Racket-compat swap of `"quasiquote"`)
17515 /// fails-loudly at the alias test rather than as a silent operator-
17516 /// facing vocabulary fracture. Adding a hypothetical fifth
17517 /// homoiconic prefix-wrapper (a `,~` reverse-unquote, a `,?`
17518 /// conditional-unquote, a `#'` Common-Lisp function-quote) extends
17519 /// [`Self::ALL`] AND [`Self::LABELS`] AND adds ONE per-role
17520 /// `pub const` alias in lockstep — rustc's forced-arity check on
17521 /// the two `[_; N]` arrays fails compilation if EITHER ALL array
17522 /// grows without the other.
17523 ///
17524 /// Theory anchor: THEORY.md §III — the typescape; the four
17525 /// canonical quote-family marker labels bind at ONE typed
17526 /// `[&'static str; 4]` array on the closed-set [`QuoteForm`]
17527 /// algebra rather than at zero-primitive-on-this-subset-plus-four-
17528 /// inline-lookups scattered across the substrate. Closes the
17529 /// fourth SexpShape sub-carving's per-role LABEL parity with
17530 /// [`AtomKind`] and [`crate::error::StructuralKind`]. THEORY.md
17531 /// §V.1 — knowable platform; the family's cardinality becomes a
17532 /// TYPE-level constant on the substrate algebra rather than a per-
17533 /// consumer runtime dispatch through the composition. The alias-
17534 /// chain shape is load-bearing: a [`SexpShape`]-side rename
17535 /// propagates through the const-eval alias chain byte-for-byte
17536 /// without silent drift. THEORY.md §VI.1 — generation over
17537 /// composition; the family-wide contract sweeps (alignment with
17538 /// [`Self::ALL`], pairwise disjointness, membership through
17539 /// [`Self::label`]) emerge from the composition of TWO substrate
17540 /// primitives (this `pub const` array + the four per-role
17541 /// `pub const *_LABEL` aliases) rather than as per-variant inline
17542 /// assertions duplicated at each call site. THEORY.md §II.1
17543 /// invariant 5 — composition preserves proofs; the alias-chain
17544 /// composition law `QuoteForm::LABELS[i] ==
17545 /// QuoteForm::ALL[i].sexp_shape().label()` binds the family-wide
17546 /// array to the composition through [`Self::sexp_shape`] +
17547 /// [`SexpShape::label`] at rustc time.
17548 pub const LABELS: [&'static str; 4] = [
17549 Self::QUOTE_LABEL,
17550 Self::QUASIQUOTE_LABEL,
17551 Self::UNQUOTE_LABEL,
17552 Self::UNQUOTE_SPLICE_LABEL,
17553 ];
17554
17555 /// Project the typed marker back into its matching `Sexp::*` wrapper
17556 /// variant applied to `inner` — the structural inverse of
17557 /// [`crate::ast::Sexp::as_quote_form`]. [`Self::Quote`] yields
17558 /// [`Sexp::Quote`], [`Self::Quasiquote`] yields [`Sexp::Quasiquote`],
17559 /// [`Self::Unquote`] yields [`Sexp::Unquote`], [`Self::UnquoteSplice`]
17560 /// yields [`Sexp::UnquoteSplice`], each boxing `inner` into the
17561 /// corresponding tuple-variant constructor (`fn(Box<Sexp>) -> Sexp`).
17562 ///
17563 /// Round-trip identity with [`crate::ast::Sexp::as_quote_form`] — the
17564 /// structural law every consumer can pin against:
17565 ///
17566 /// ```ignore
17567 /// // for every (qf, inner): qf.wrap(inner.clone()).as_quote_form() == Some((qf, &inner))
17568 /// // for every Sexp s matching the quote family:
17569 /// // let (qf, inner) = s.as_quote_form().unwrap();
17570 /// // qf.wrap(inner.clone()) == s
17571 /// ```
17572 ///
17573 /// Consumer: [`crate::reader::read_quoted`] — the FIFTH consumer site
17574 /// of the closed-set `QuoteForm` algebra (sibling to `Hash for Sexp`'s
17575 /// `hash_discriminator` arm, `Display for Sexp`'s `prefix` arm,
17576 /// `Sexp::as_unquote`'s `as_unquote_form` subset-gate composition, and
17577 /// the feature-gated `From<&Sexp> for iac_forge::SExpr`'s
17578 /// `iac_forge_tag` arm). Pre-lift the reader's parse dispatch carried
17579 /// its own parallel closed set: a local `Token::{Quote, Quasiquote,
17580 /// Unquote, UnquoteSplice}` enum paired with the matching `Sexp::*`
17581 /// tuple-variant constructors threaded as `fn(Box<Sexp>) -> Sexp`
17582 /// arguments to `read_quoted`. The (Token variant, Sexp::* constructor)
17583 /// pairing was load-bearing yet only enforced by callsite discipline
17584 /// at the FIFTH consumer site the prior `QuoteForm` lifts did not
17585 /// reach — a regression that swapped `Sexp::Quote` and
17586 /// `Sexp::Quasiquote` between the parser arms type-checked but
17587 /// silently corrupted every program's quote-family parse.
17588 ///
17589 /// Post-lift the reader's `Token` collapses to ONE typed variant
17590 /// `Token::Quoted(QuoteForm)`, the parser's four prefix arms collapse
17591 /// to ONE arm `Some((Token::Quoted(qf), _)) => read_quoted(it,
17592 /// eof_pos, qf)`, and `read_quoted` routes through this projection to
17593 /// produce the matching `Sexp::*` variant. The (QuoteForm variant,
17594 /// Sexp::* constructor) pairing now binds at ONE site on the typed
17595 /// algebra — rustc enforces exhaustiveness across [`Self`]'s closed
17596 /// set, so a regression that drifts the (marker, constructor) pair
17597 /// becomes a typed compile error rather than a silent program-text
17598 /// corruption.
17599 ///
17600 /// The `Sexp` (owned) return type complements [`Sexp::as_quote_form`]'s
17601 /// `&Sexp` (borrowed) — `wrap` consumes the inner body to build the
17602 /// new wrapper, `as_quote_form` borrows the inner body from the
17603 /// existing wrapper. The asymmetry is intentional: at the reader's
17604 /// parse-then-wrap boundary the inner is fresh from `parse(...)?` and
17605 /// has no caller-owned binding; the typed `Box::new(inner)` allocation
17606 /// lives at ONE site rather than four (one per pre-lift parser arm),
17607 /// so a future allocation-policy change (e.g. arena-allocated wrappers
17608 /// for span-aware Sexp) lands as ONE edit.
17609 ///
17610 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
17611 /// reader's prefix-token → Sexp-wrapper gate IS the rust-level
17612 /// typed-entry gate at the source-text boundary, and naming the
17613 /// typed projection from [`QuoteForm`] back to the `Sexp::*` wrapper
17614 /// lifts the gate from per-arm constructor literals to ONE method
17615 /// the closed-set algebra owns — parallel to how [`Self::prefix`]
17616 /// lifts the Display↔reader prefix-string surface. THEORY.md §II.1
17617 /// invariant 2 — free middle; ALL FIVE consumers (Hash, Display,
17618 /// as_unquote, iac-forge interop, reader's parse) now route through
17619 /// the SAME closed-set algebra so a regression that drifts ONE
17620 /// consumer's pairing from the others cannot reach the substrate's
17621 /// runtime. THEORY.md §V.1 — knowable platform; the (QuoteForm
17622 /// variant, Sexp::* constructor) pairing becomes a TYPE projection on
17623 /// the substrate algebra rather than four `fn(Box<Sexp>) -> Sexp`
17624 /// function pointers threaded as call arguments. A typo or
17625 /// swap is no longer a runtime drift but a compile error against the
17626 /// typed projection. THEORY.md §VI.1 — generation over composition;
17627 /// the (QuoteForm variant, Sexp::* constructor) pairing appeared at
17628 /// the four reader arms — past the ≥2 PRIME-DIRECTIVE trigger once
17629 /// the structural shape is named. The typed projection lands the
17630 /// structural-completeness floor for the reader's quote-family
17631 /// surface, completing the FIVE-consumer closure of the
17632 /// `QuoteForm` algebra.
17633 #[must_use]
17634 pub fn wrap(self, inner: Sexp) -> Sexp {
17635 let boxed = Box::new(inner);
17636 match self {
17637 Self::Quote => Sexp::Quote(boxed),
17638 Self::Quasiquote => Sexp::Quasiquote(boxed),
17639 Self::Unquote => Sexp::Unquote(boxed),
17640 Self::UnquoteSplice => Sexp::UnquoteSplice(boxed),
17641 }
17642 }
17643}
17644
17645// `impl fmt::Display for QuoteForm` is generated by
17646// `#[derive(tatara_closed_set::DeriveClosedSet)]` + `#[closed_set(display)]` on
17647// the enum declaration above — emits the substrate-wide
17648// `f.write_str(Self::prefix(*self))` block byte-for-byte.
17649
17650// `impl std::str::FromStr for QuoteForm` + `impl tatara_closed_set::ClosedSet for
17651// QuoteForm` + `pub struct UnknownQuoteForm(pub String)` are generated by
17652// `#[derive(tatara_closed_set::DeriveClosedSet)]` on the enum declaration
17653// above. `label` delegates to the inherent `QuoteForm::prefix` via
17654// `#[closed_set(via = "prefix")]` so the domain-canonical
17655// reader-punctuation projection (`"'" / "`" / "," / ",@"`) stays
17656// load-bearing at the inherent surface while the trait surface unifies
17657// every closed-set implementor's projection name onto `label`.
17658// `#[closed_set(generate_unknown = "quote form")]` emits the typed
17659// parse-rejection carrier with the substrate-wide `Debug + Clone +
17660// PartialEq + Eq + thiserror::Error` derives and the `#[error("unknown
17661// quote form: {0}")]` annotation byte-for-byte; the explicit label pins
17662// the pre-lift wording even though the auto-derived
17663// `pascal_to_spaced_lowercase("QuoteForm")` projects to the same
17664// `"quote form"` literal. The FromStr decode is a linear sweep over
17665// `QuoteForm::ALL` keyed on `prefix`: every successful decode round-trips
17666// through `prefix()`, cross-axis labels from `SexpShape` (`"quote" /
17667// "quasiquote" / ...`) and `iac_forge_tag` (`"unquote-splicing"`) reject —
17668// pinned by `quote_form_prefix_round_trips_through_from_str` +
17669// `quote_form_from_str_rejects_sexp_shape_labels_on_homoiconic_prefix_axis`.
17670
17671/// Iterate over the argument tails of every form in `forms` whose call head
17672/// matches `keyword` — the *slice-side* sibling of [`Sexp::as_call_to`].
17673/// Where [`Sexp::as_call_to`] answers "is THIS form a call to `K`, and what
17674/// are its arguments?" on ONE form, `iter_calls_to` answers "which forms
17675/// in this SLICE are calls to `K`, and what are their arguments?" on a
17676/// `&[Sexp]`. Yields `&[Sexp]` for each matching form's argument tail
17677/// (`&form_list[1..]`, the empty slice for a singleton call like `(K)`);
17678/// non-matching forms — every shape [`Sexp::as_call_to`] rejects — are
17679/// skipped silently, matching the soft-projection posture the per-form
17680/// sibling carries.
17681///
17682/// Two consumers in [`compile.rs`](crate::compile) route through this
17683/// primitive:
17684/// * [`compile_typed::<T>`](crate::compile::compile_typed) — walks every
17685/// expanded top-level form and compiles every `(T::KEYWORD :k v …)`
17686/// form into a typed `T`.
17687/// * [`compile_named_from_forms::<T>`](crate::compile::compile_named_from_forms)
17688/// — walks every expanded form and compiles every
17689/// `(T::KEYWORD NAME :k v …)` form into a [`NamedDefinition<T>`](crate::compile::NamedDefinition).
17690///
17691/// Before this lift both consumers opened the same `for form in &expanded
17692/// { if let Some(args) = form.as_call_to(T::KEYWORD) { … } }` walk inline
17693/// — well past the ≥2 PRIME-DIRECTIVE trigger once the per-form sibling
17694/// had a name. After this lift the walk lives in ONE function the two
17695/// dispatchers route through; a regression that drifts ONE consumer's
17696/// walk from the other (a future emitter that inlines a partial filter,
17697/// a debug-mode logger that loses track of non-matching forms, a span-
17698/// aware walk that threads a borrowed `&Sexp` position alongside the
17699/// tail) becomes structurally impossible because there is exactly ONE
17700/// implementation both dispatchers consume. A future authoring tool
17701/// (LSP / REPL / `tatara-check`) that wants to surface "which forms in
17702/// this program invoke `K`?" binds to ONE function on the slice algebra
17703/// instead of re-deriving the walk per consumer.
17704///
17705/// Closes the soft-dispatch family at the slice level: the per-form
17706/// projections `{head_symbol, as_call, as_call_to, as_call_to_any}` each
17707/// answer "what does THIS form's head say?", and the slice-side
17708/// `iter_calls_to` extends them to "what do THESE forms' heads say,
17709/// projected through one keyword?". Typed-decoded sibling on the
17710/// slice algebra: [`iter_calls_to_any`] — the closure-typed extension
17711/// of THIS function the same way [`Sexp::as_call_to_any`] extends
17712/// [`Sexp::as_call_to`] on the per-form algebra. The (per-form,
17713/// slice-side) × (keyword, classifier) 2×2 of soft-dispatch
17714/// primitives is closed at the slice corner this lift establishes;
17715/// the closed-form composition binding the slice-side projection to
17716/// its per-form sibling is the structural identity every consumer
17717/// can pin against:
17718///
17719/// ```ignore
17720/// iter_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_call_to(k))
17721/// ```
17722///
17723/// Post-lift `iter_calls_to`'s body composes
17724/// [`iter_calls_to_any`] with a keyword-equality decoder
17725/// (`|h| (h == keyword).then_some(())`) and drops the decoded unit, so
17726/// the keyword-typed slice walk IS the typed-decoded slice walk
17727/// restricted to a constant-keyword classifier. The (slice-side
17728/// keyword projection, slice-side typed-decoded projection) pair
17729/// binds at ONE filter-and-fuse implementation on the algebra
17730/// rather than at two parallel `forms.iter().filter_map(_)` triples
17731/// that the type system would not catch when one drifts from the
17732/// other (a future emitter that adds debug logging at one site but
17733/// not the other, a future span-aware walk that threads borrowed
17734/// positional metadata through one site but skips the other).
17735///
17736/// The yielded `&[Sexp]` slices borrow `&forms[i][1..]` verbatim — no
17737/// copy, no allocation, same lifetime as [`Sexp::as_call_to`]'s tail.
17738/// The iterator's lifetime `'a` is the unified outer lifetime of `forms`
17739/// AND `keyword`: the keyword string must outlive the iterator's borrow
17740/// of the slice (typical caller passes `T::KEYWORD: &'static str`, which
17741/// unifies trivially; a caller passing a locally-allocated `&str` ties
17742/// the iterator to that local). The closure captures `keyword` by move
17743/// (the `move` keyword on the `filter_map` closure), so each invocation
17744/// re-derives the head comparison via [`Sexp::as_call_to`]'s `head ==
17745/// keyword` check at every form — no shared-state, fully Iterator-fused.
17746///
17747/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
17748/// two-site `for + as_call_to` inline walk is past the ≥2 PRIME-DIRECTIVE
17749/// trigger once the per-form sibling has a name. THEORY.md §V.1 —
17750/// knowable platform / "make invalid states unrepresentable"; the
17751/// slice-side projection becomes a NAMED primitive on the substrate's
17752/// `&[Sexp]` algebra rather than a re-derived for-loop at every consumer
17753/// site, so authoring tools (REPL, LSP, `tatara-check`) bind to ONE
17754/// function instead of re-implementing the walk. THEORY.md §II.1
17755/// invariant 1 — typed entry; the typed-keyword filter on a slice IS the
17756/// rust-level typed-entry-batch gate (the batch sibling of `as_call_to`'s
17757/// per-form gate), and naming its single shape lifts the gate from
17758/// two-site duplication to one rust function the substrate's diagnostic
17759/// promotions hang off of. THEORY.md §II.1 invariant 2 — free middle;
17760/// both dispatchers route through the SAME projection, so a regression
17761/// that drifts one consumer's walk from the other cannot reach the
17762/// substrate's runtime: the type system binds every consumer to the
17763/// projection's single emission shape.
17764///
17765/// Frontier inspiration: MLIR's `op.getOps<NamedOp>()` — every rewrite
17766/// pattern over a typed-op block binds to ONE typed-filter iterator
17767/// regardless of whether it's matching one op kind or batching across a
17768/// region's contents; the substrate's `iter_calls_to` is the
17769/// unstructured-projection peer of that iterator, lifted onto the
17770/// substrate's typed `&[Sexp]` algebra. Racket's `syntax-parse`
17771/// `~seq (defmacro id args …) …` ellipsis-form — the slice-level
17772/// matched-keyword filter is the closed-form sibling of `~seq`'s
17773/// repeated-pattern matcher, translated through pleme-io primitives as
17774/// ONE `iter_calls_to(forms, keyword)` projection. Tree-sitter's
17775/// `Query::matches` over a node sequence — the same "iterate the
17776/// matched forms in a parent" projection, inherited here for the typed
17777/// `Sexp` algebra without a new IR layer.
17778pub fn iter_calls_to<'a>(
17779 forms: &'a [Sexp],
17780 keyword: &'a str,
17781) -> impl Iterator<Item = &'a [Sexp]> + 'a {
17782 iter_calls_to_any(forms, move |h| (h == keyword).then_some(())).map(|(_, args)| args)
17783}
17784
17785/// Iterate over the `(decoded, args)` pairs of every form in `forms` whose
17786/// call head decodes through `decode` — the *slice-side* sibling of
17787/// [`Sexp::as_call_to_any`]. Where [`Sexp::as_call_to_any`] answers "is
17788/// THIS form a call whose head decodes through `F`, and what are its
17789/// arguments?" on ONE form, `iter_calls_to_any` answers "which forms in
17790/// this SLICE are calls whose heads decode through `F`, and what do they
17791/// decode to alongside their arguments?" on a `&[Sexp]`. Yields
17792/// `(decoded, &[Sexp])` for each matching form — the decoded typed
17793/// witness alongside the matched form's argument tail (`&form_list[1..]`,
17794/// the empty slice for a singleton call like `(K)`); non-matching forms
17795/// — every shape [`Sexp::as_call_to_any`] rejects, including calls whose
17796/// head is present but `decode` returns `None` for — are skipped silently,
17797/// matching the soft-projection posture the per-form sibling carries.
17798///
17799/// Closes the soft-dispatch family at the slice corner this lift
17800/// establishes — the (per-form, slice-side) × (keyword, classifier) 2×2
17801/// of soft-dispatch primitives on the `Sexp`/`&[Sexp]` algebras:
17802///
17803/// | | per-form | slice-side |
17804/// |----------------|-----------------------|--------------------------|
17805/// | keyword | [`Sexp::as_call_to`] | [`iter_calls_to`] |
17806/// | classifier `F` | [`Sexp::as_call_to_any`] | `iter_calls_to_any` (this) |
17807///
17808/// The keyword corner is the constant-classifier projection of the
17809/// classifier corner: [`iter_calls_to`] now composes through THIS
17810/// primitive with a `move |h| (h == keyword).then_some(())` decoder
17811/// and drops the decoded unit, parallel to how
17812/// `Sexp::as_call_to(k) == Sexp::as_call_to_any(|h| (h ==
17813/// k).then_some(())).map(|(_, a)| a)` (modulo the discarded `()`) on
17814/// the per-form algebra. The slice-side filter-and-fuse implementation
17815/// now lives at ONE site, so a regression that drifts a debug-logging
17816/// instrumentation, span-aware borrow threading, or fused-iterator
17817/// invariant from one slice consumer to the other becomes
17818/// structurally impossible.
17819///
17820/// Two plausible future consumer shapes the typed-decoded slice walk
17821/// admits with no boilerplate:
17822/// * **Closed-set classifier** — `iter_calls_to_any(forms,
17823/// MacroDefHead::from_keyword)` walks a slice yielding `(head: MacroDefHead,
17824/// args: &[Sexp])` for every `(defmacro …)` / `(defpoint-template …)`
17825/// / `(defcheck …)` form, decoded to the typed `MacroDefHead` enum.
17826/// Future LSP / `tatara-check` consumers that surface "every
17827/// defmacro-family form in this buffer with its kind tag" bind to
17828/// ONE projection rather than a hand-rolled
17829/// `forms.iter().filter_map(|f| f.as_call_to_any(MacroDefHead::from_keyword))`
17830/// triple at each consumer site.
17831/// * **Live-registry classifier** — `iter_calls_to_any(forms, |h|
17832/// registry.get(h))` walks a slice yielding `(handler: &Handler,
17833/// args: &[Sexp])` for every form whose head matches a runtime
17834/// registry. Future REPL / `tatara-check` consumers that route
17835/// every form through a registry dispatcher bind to ONE
17836/// projection rather than re-deriving the `filter_map` pattern
17837/// per consumer surface — sibling shape to
17838/// [`Expander::expand`](crate::macro_expand::Expander::expand)'s
17839/// per-form `as_call_to_any(|h| self.macros.get(h))` macro-call
17840/// dispatch, lifted onto the slice algebra so a batch walk picks
17841/// up the same dispatch shape without re-derivation.
17842///
17843/// The closed-form composition binding the slice-side projection to
17844/// its per-form sibling is the structural identity every consumer can
17845/// pin against:
17846///
17847/// ```ignore
17848/// iter_calls_to_any(forms, decode) ==
17849/// forms.iter().filter_map(|f| f.as_call_to_any(&mut decode))
17850/// ```
17851///
17852/// The yielded `&[Sexp]` slices borrow `&forms[i][1..]` verbatim — no
17853/// copy, no allocation, same lifetime as [`Sexp::as_call_to_any`]'s
17854/// tail. `T` is owned because `decode` is `FnMut(&str) -> Option<T>`
17855/// and a `&'_ str` borrow into the head symbol would not outlive the
17856/// helper boundary; consumers projecting to a typed `Copy` enum
17857/// (e.g. `MacroDefHead`) get the value directly per form, consumers
17858/// projecting to a borrowed `&'static str` (a closed-set head)
17859/// project to `&'static str` and inherit the static lifetime through
17860/// the classifier. The closure is `FnMut` (rather than the per-form
17861/// sibling's `FnOnce`) because the slice walk calls it once per form
17862/// — a closure that captures mutable state (a counter, a registry
17863/// cache) maintains that state across the batch walk; a closure with
17864/// no mutable state is admitted trivially.
17865///
17866/// The iterator's lifetime `'a` unifies `forms`'s borrow lifetime
17867/// with the closure `F`'s captures lifetime: the decoder must outlive
17868/// the iterator's borrow of the slice, the typical caller passes a
17869/// `'static` decoder (a `fn` item like `MacroDefHead::from_keyword`,
17870/// or a closure capturing nothing) which unifies trivially. The
17871/// closure captures `decode` by move (the `move` keyword on the
17872/// `filter_map` closure), so each invocation re-borrows it as
17873/// `&mut decode` and calls [`Sexp::as_call_to_any`] with a fresh
17874/// `FnOnce`-coerced borrow — no shared-state hazard, fully
17875/// Iterator-fused.
17876///
17877/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
17878/// per-form classifier sibling [`Sexp::as_call_to_any`] has two
17879/// production consumers (`macro_def_from` via closed-set classifier
17880/// `MacroDefHead::from_keyword`, `Expander::expand` via live-registry
17881/// classifier `|h| self.macros.get(h)`) — past the ≥2 PRIME-DIRECTIVE
17882/// trigger once the slice-side projection is named. Future
17883/// authoring-tool surfaces (LSP buffer walks, `tatara-check` batch
17884/// dispatchers, REPL exhaustive listers) join the family without
17885/// re-deriving the `filter_map(|f| f.as_call_to_any(_))` triple per
17886/// consumer. THEORY.md §V.1 — knowable platform; the slice-side
17887/// typed-decoded projection becomes a NAMED primitive on the
17888/// substrate's `&[Sexp]` algebra, closing the 2×2 of soft-dispatch
17889/// primitives the per-form algebra already establishes. THEORY.md
17890/// §II.1 invariant 2 — free middle; the slice-side keyword filter
17891/// ([`iter_calls_to`]) now routes through the slice-side classifier
17892/// filter (THIS function) via the constant-classifier composition, so
17893/// a regression that drifts the keyword filter's instrumentation
17894/// from the classifier filter's instrumentation becomes structurally
17895/// impossible.
17896///
17897/// Frontier inspiration: MLIR's
17898/// `op.walk<OpInterface, OpInterface2, …>([&](auto op) { … })` — the
17899/// typed-IR walk over a region yielding ops decoded to their typed
17900/// interface witness IS the slice-side typed-decoded projection on
17901/// MLIR's op algebra; `iter_calls_to_any` is the unstructured-Rust
17902/// peer on the substrate's typed `&[Sexp]` algebra, with `decode:
17903/// FnMut(&str) -> Option<T>` standing in for MLIR's typed-interface
17904/// dyn-cast bag. Racket's `syntax-parse` `~or* (~datum defmacro)
17905/// (~datum defpoint-template) (~datum defcheck) (head args …)` over
17906/// an ellipsis-form — the slice-level matched-set filter decoded to
17907/// a typed witness is the closed-form sibling of `~or*`'s
17908/// typed-choice repeater, translated through pleme-io primitives as
17909/// ONE `iter_calls_to_any(forms, F)` projection.
17910pub fn iter_calls_to_any<'a, F, T>(
17911 forms: &'a [Sexp],
17912 mut decode: F,
17913) -> impl Iterator<Item = (T, &'a [Sexp])> + 'a
17914where
17915 F: FnMut(&str) -> Option<T> + 'a,
17916 T: 'a,
17917{
17918 forms
17919 .iter()
17920 .filter_map(move |f| f.as_call_to_any(&mut decode))
17921}
17922
17923/// Iterate over the `Result<(decoded, NAME, spec_args)>` triples of every
17924/// form in `forms` whose call head decodes through `decode` AND carries a
17925/// positional NAME slot — the *slice-side* sibling of
17926/// [`Sexp::as_call_to_any`] specialized to the named NAME-then-kwargs
17927/// form shape, with the named-form structural gate
17928/// [`crate::compile::split_name_slot`] composed in. Where
17929/// [`iter_calls_to_any`] answers "which forms in this SLICE are calls
17930/// whose heads decode through `F`, and what do they decode to alongside
17931/// their args tail?" on a `&[Sexp]`, `iter_named_calls_to_any` answers
17932/// the same question AND extracts the borrowed NAME slot AND the
17933/// remaining spec args tail in ONE projection per matched form, lifting
17934/// the named-form gate from inside the projection at every consumer
17935/// site to the slice algebra itself.
17936///
17937/// The yielded `Result<(T, &'a str, &'a [Sexp])>` shape carries the
17938/// classifier's typed witness `T` alongside the BORROWED NAME slot AND
17939/// the BORROWED spec args tail. Non-matching forms (every shape
17940/// [`Sexp::as_call_to_any`] rejects, AND every call whose head is
17941/// present but `decode` returns `None` for) are skipped silently — the
17942/// classifier filter precedes the named gate, mirroring how
17943/// [`crate::compile::split_name_slot`] is composed into the projection
17944/// AFTER the classifier-decoded args tail is already in hand. Matched
17945/// forms whose NAME slot is missing yield `Err(NamedFormMissingName {
17946/// keyword })` carrying the classifier-supplied keyword; matched forms
17947/// whose NAME slot is a non-symbol-or-string yield `Err(NamedFormNonSymbolName
17948/// { keyword, got })` carrying the same keyword and the typed
17949/// [`SexpShape`](crate::error::SexpShape) projection of the offending
17950/// slot. Consumers `.collect::<Result<Vec<_>, _>>()` to short-circuit
17951/// at the first malformed NAME slot, exactly as
17952/// [`Expander::expand_and_collect_named_calls_to_any`](crate::macro_expand::Expander::expand_and_collect_named_calls_to_any)
17953/// short-circuits today via the same `split_name_slot` gate composed
17954/// inside its projection closure.
17955///
17956/// Decoder signature `FnMut(&str) -> Option<(T, &'static str)>` pairs
17957/// the typed witness `T` with the canonical static keyword threaded
17958/// through the `NamedFormMissingName.keyword` /
17959/// `NamedFormNonSymbolName.keyword` slots of the named-form gate — the
17960/// `&'static` constraint pins the same compile-time discipline
17961/// [`crate::compile::split_name_slot`]'s `keyword: &'static str`
17962/// parameter pins at its boundary. A classifier consumer that wants
17963/// "filter forms by a constant keyword" supplies a constant-classifier
17964/// decoder `|h| (h == keyword).then_some(((), keyword))`; the
17965/// [`iter_named_calls_to`] sibling below is exactly that specialization.
17966///
17967/// Closes the (per-form, slice-side) × (keyword, classifier) × (bare,
17968/// named) 2×2×2 cube of soft-dispatch primitives on the substrate's
17969/// `Sexp`/`&[Sexp]` algebras at the slice-side × classifier × named
17970/// corner — the cube the per-form algebra
17971/// (`as_call_to{,_any}`), the slice algebra
17972/// (`iter_calls_to{,_any}`), and the Expander surface
17973/// (`expand_and_collect_calls_to{,_any}` /
17974/// `expand_and_collect_named_calls_to{,_any}`) collectively shape:
17975///
17976/// | | bare-kwargs | named NAME-then-kwargs |
17977/// |----------------|--------------------------|--------------------------------------------------|
17978/// | per-form | [`Sexp::as_call_to_any`] | [`Sexp::as_named_call_to_any`] |
17979/// | slice | [`iter_calls_to_any`] | `iter_named_calls_to_any` (this) |
17980/// | expander | `expand_and_collect_calls_to_any` | `expand_and_collect_named_calls_to_any` |
17981///
17982/// Pre-lift the bare expander surface (`expand_and_collect_calls_to_any`)
17983/// routed through the slice primitive ([`iter_calls_to_any`]) via a
17984/// uniform `expand_program + iter_calls_to_any + map + collect`
17985/// pipeline; the named expander surface
17986/// (`expand_and_collect_named_calls_to_any`) routed through the
17987/// BARE expander surface and welded
17988/// [`crate::compile::split_name_slot`] INSIDE the projection closure —
17989/// the named gate composition lived at the expander level rather than
17990/// at the slice level the bare row sat at. Post-lift the named expander
17991/// surface routes through THIS slice primitive via the SAME
17992/// `expand_program + iter_named_calls_to_any + map + collect`
17993/// pipeline shape, so both rows now share the same composition skeleton
17994/// on the slice algebra — a regression that drifts a future debug-mode
17995/// logger, span-aware borrow walker, or fused-iterator invariant from
17996/// one row to the other becomes structurally impossible at the slice
17997/// boundary.
17998///
17999/// Two plausible future consumer shapes the slice-side named-classifier
18000/// walk admits with no boilerplate:
18001/// * **Closed-set classifier** — `iter_named_calls_to_any(forms, |h|
18002/// match h { "defmonitor" => Some((Kind::Monitor, "defmonitor")),
18003/// "defalertpolicy" => Some((Kind::Alert, "defalertpolicy")), _ =>
18004/// None }).collect::<Result<Vec<_>, _>>()?` walks a slice of
18005/// already-expanded forms, yielding the `(typed Kind, NAME, spec
18006/// args)` triple for every `(defmonitor NAME …)` / `(defalertpolicy
18007/// NAME …)` form. Future `tatara-check` consumers that already hold
18008/// expanded forms (the workspace coherence checker walks
18009/// `checks.lisp`'s post-expansion top-level) bind to ONE projection
18010/// on the slice algebra rather than re-deriving the
18011/// `iter_calls_to_any(forms, decode).map(|(decoded, args)| {
18012/// split_name_slot(args, kw).map(|(name, rest)| (decoded, name,
18013/// rest)) })` four-step inline composition.
18014/// * **Live-registry classifier** — `iter_named_calls_to_any(forms,
18015/// |h| registry.lookup(h).map(|h| (h, h.canonical_label())))` walks
18016/// a slice of expanded forms, yielding the `(handler reference, NAME,
18017/// spec args)` triple for every form whose head matches a runtime
18018/// registry. Future REPL / authoring-tool surfaces that dispatch
18019/// named forms through a live registry bind to ONE projection,
18020/// sibling shape to how the macro expander already routes through
18021/// a live-registry classifier via
18022/// [`Sexp::as_call_to_any`].
18023///
18024/// The closed-form composition binding this slice primitive to its
18025/// per-form sibling AND to the bare-kwargs slice primitive is the
18026/// structural identity every consumer can pin against:
18027///
18028/// ```ignore
18029/// iter_named_calls_to_any(forms, decode) ==
18030/// iter_calls_to_any(forms, decode).map(|(decoded, args)| {
18031/// let kw = /* keyword the decoder returned alongside decoded */;
18032/// split_name_slot(args, kw).map(|(name, rest)| (decoded, name, rest))
18033/// })
18034/// ```
18035///
18036/// The yielded `&'a str` NAME slot and `&'a [Sexp]` spec args tail
18037/// borrow from `&forms[i]` verbatim — no copy, no allocation, same
18038/// lifetime as [`Sexp::as_call_to_any`]'s tail. Consumers that need
18039/// owned ownership of the NAME (`NamedDefinition.name: String`,
18040/// JSON-serialized payloads) `.to_string()` themselves — pushing the
18041/// clone to the consumer boundary keeps the primitive allocation-free.
18042///
18043/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
18044/// named-form gate composition lived at the Expander level pre-lift
18045/// (inside `expand_and_collect_named_calls_to_any`'s projection
18046/// closure); the slice algebra had no named sibling to the bare
18047/// [`iter_calls_to_any`]. Post-lift the slice algebra closes at the
18048/// named corner, and the Expander surface routes through it via the
18049/// SAME `expand_program + iter + map + collect` pipeline the bare
18050/// expander surface uses. THEORY.md §V.1 — knowable platform; the
18051/// slice-side named-classifier walk becomes a NAMED primitive on the
18052/// substrate's `&[Sexp]` algebra, discoverable by any future authoring
18053/// tool (LSP, REPL, `tatara-check`) that already holds expanded forms.
18054/// THEORY.md §II.1 invariant 2 — free middle; the bare and named slice
18055/// projections share the same `forms.iter().filter_map(_)` skeleton, so
18056/// a regression that drifts ONE row's instrumentation from the other
18057/// becomes structurally impossible.
18058///
18059/// Frontier inspiration: MLIR's
18060/// `region.walk<NamedOp>([&](auto op) { auto name = op.getName(); … })`
18061/// — the typed-IR walk over a region yielding ops decoded to their
18062/// typed kind with the NAMED-symbol accessor pre-extracted is the MLIR
18063/// idiom for a named-op visitor; `iter_named_calls_to_any` is the
18064/// unstructured-Rust peer on the substrate's `&[Sexp]` algebra, with
18065/// `decode: FnMut(&str) -> Option<(T, &'static str)>` standing in for
18066/// MLIR's typed-interface dyn-cast bag AND `split_name_slot` standing
18067/// in for the named accessor. Racket's `syntax-parse` `~or* ((~datum
18068/// defX) name:id arg ...) ((~datum defY) name:id arg ...)` over an
18069/// ellipsis-form — the slice-level matched-set named-form filter
18070/// decoded to a typed witness is the closed-form sibling of `~or*`'s
18071/// typed-choice repeater with the `name:id` capture binder, translated
18072/// through pleme-io primitives as ONE projection on the `&[Sexp]`
18073/// algebra.
18074pub fn iter_named_calls_to_any<'a, F, T>(
18075 forms: &'a [Sexp],
18076 mut decode: F,
18077) -> impl Iterator<Item = crate::error::Result<(T, &'a str, &'a [Sexp])>> + 'a
18078where
18079 F: FnMut(&str) -> Option<(T, &'static str)> + 'a,
18080 T: 'a,
18081{
18082 forms
18083 .iter()
18084 .filter_map(move |f| f.as_named_call_to_any(&mut decode))
18085}
18086
18087/// Iterate over the `Result<(NAME, spec_args)>` pairs of every form in
18088/// `forms` whose call head matches `keyword` AND carries a positional
18089/// NAME slot — the *slice-side* sibling of [`Sexp::as_call_to`]
18090/// specialized to the named NAME-then-kwargs form shape, with the
18091/// named-form structural gate [`crate::compile::split_name_slot`]
18092/// composed in. Where [`iter_calls_to`] answers "which forms in this
18093/// SLICE are calls to `K`, and what are their args tails?" on a
18094/// `&[Sexp]`, `iter_named_calls_to` answers the same question AND
18095/// extracts the borrowed NAME slot AND the remaining spec args tail in
18096/// ONE projection per matched form.
18097///
18098/// Routes through the typed-decoded sibling [`iter_named_calls_to_any`]
18099/// with a constant-classifier decoder — the same constant-classifier
18100/// composition [`iter_calls_to`] uses to route through
18101/// [`iter_calls_to_any`] on the bare-kwargs axis, and that
18102/// [`crate::macro_expand::Expander::expand_and_collect_named_calls_to`]
18103/// uses to route through
18104/// [`crate::macro_expand::Expander::expand_and_collect_named_calls_to_any`]
18105/// on the Expander surface. The discarded `()` typed witness
18106/// (`then_some(((), keyword))`) is consumed by the wrapper projection so
18107/// the consumer's per-form mapper sees only the `(name, spec_args)`
18108/// borrowed pair, matching the bare projection signature on the named
18109/// axis.
18110///
18111/// `keyword: &'static str` threads verbatim through the
18112/// `NamedFormMissingName.keyword` / `NamedFormNonSymbolName.keyword`
18113/// slots of the named-form gate — same `&'static` discipline
18114/// [`crate::compile::split_name_slot`] pins at its boundary. Consumers
18115/// that want a runtime keyword whose lifetime is `&'static` (typical:
18116/// `T::KEYWORD` of a typed-domain witness, a hardcoded literal like
18117/// `"defcheck"`) bind to this primitive; consumers that want a runtime
18118/// keyword whose lifetime is shorter use [`iter_named_calls_to_any`]
18119/// directly with a constant-classifier decoder that converts
18120/// post-resolution.
18121///
18122/// Closes the (slice-side × constant-keyword × named) corner of the
18123/// soft-dispatch cube — see [`iter_named_calls_to_any`]'s docstring for
18124/// the cube shape. The closed-form composition binding this primitive
18125/// to the typed-decoded sibling is the structural identity every
18126/// consumer can pin against:
18127///
18128/// ```ignore
18129/// iter_named_calls_to(forms, k) ==
18130/// iter_named_calls_to_any(forms, |h| (h == k).then_some(((), k)))
18131/// .map(|maybe_triple| maybe_triple.map(|(_, name, args)| (name, args)))
18132/// ```
18133///
18134/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
18135/// constant-keyword named slice projection is a CONSEQUENCE of the
18136/// typed-decoded named slice projection + a constant-classifier
18137/// decoder, parallel to how [`iter_calls_to`] is a consequence of
18138/// [`iter_calls_to_any`] on the bare-kwargs axis. THEORY.md §II.1
18139/// invariant 2 — free middle; both rows of the slice algebra
18140/// (bare-kwargs, named) route through their classifier sibling via
18141/// constant-classifier composition, so a regression that drifts ONE
18142/// row's pipeline from the other becomes structurally impossible.
18143pub fn iter_named_calls_to<'a>(
18144 forms: &'a [Sexp],
18145 keyword: &'static str,
18146) -> impl Iterator<Item = crate::error::Result<(&'a str, &'a [Sexp])>> + 'a {
18147 iter_named_calls_to_any(forms, move |h| (h == keyword).then_some(((), keyword)))
18148 .map(|maybe_triple| maybe_triple.map(|(_, name, args)| (name, args)))
18149}
18150
18151/// Render an `Atom::Float`'s `f64` value to a form that re-reads as
18152/// `Atom::Float` — preserves the float-vs-int typed identity across the
18153/// `Sexp::Display` → [`crate::reader::read`] round-trip.
18154///
18155/// Rust's stdlib `Display` impl for `f64` elides the trailing `.0` for
18156/// finite integral values: `format!("{}", 1.0_f64) == "1"`,
18157/// `format!("{}", 100.0_f64) == "100"`. The substrate's reader
18158/// (via the typed-entry classifier [`Atom::from_lexeme`]) tries
18159/// `i64::parse` BEFORE `f64::parse`, so a bare `1` re-reads as
18160/// `Atom::Int(1)` — NOT as `Atom::Float(1.0)`. The default Display rendering therefore drifts the
18161/// typed identity at the Display→read boundary: `Float(1.0)` round-trips
18162/// to `Int(1)` and a regression silently coerces an authoring-surface
18163/// `1.0` slot into the typed `Int` track.
18164///
18165/// This helper emits `1.0` for `1.0_f64` and `1.5` for `1.5_f64` — the
18166/// `.0` suffix is appended IFF the value is finite AND already integral
18167/// (`n == n.trunc()`). Non-integral values render through the default
18168/// `f64` Display impl, which already preserves the fractional component
18169/// (`1.5`, `0.99`, etc.) round-trippably. Non-finite values (`NaN`,
18170/// `inf`, `-inf`) also fall through to the default impl — they cannot be
18171/// reliably round-tripped through the reader regardless (the Hash impl
18172/// already warns about NaN's PartialEq irregularity at the cache-key
18173/// boundary), so the helper does not paper over that prior limitation.
18174///
18175/// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
18176/// substrate's typed-entry gate distinguishes `Atom::Int` from
18177/// `Atom::Float`, and the Display→read round-trip is the typed-exit-side
18178/// mirror that must preserve the distinction. Pre-lift the
18179/// `Float(integral) → Int(integral)` collapse silently violated the
18180/// invariant at the round-trip boundary; post-lift the typed identity is
18181/// preserved. THEORY.md §V.1 — knowable platform; diagnostics that
18182/// project a `Float(1.0)` slot through `SexpWitness::display` (sourced
18183/// from `Sexp::to_string()`) used to surface as `got 1` — confusingly
18184/// identical to the typed `Int(1)` projection. Post-lift the diagnostic
18185/// shape names the offender's typed identity (`got 1.0`) so operators
18186/// distinguish "you wrote 1.0 in an int slot" from "you wrote 1 in a
18187/// kwarg slot the kwarg gate rejected" without re-reading source.
18188fn fmt_float(n: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18189 if n.is_finite() && n == n.trunc() {
18190 write!(f, "{n}.0")
18191 } else {
18192 write!(f, "{n}")
18193 }
18194}
18195
18196/// Canonical reader-round-trippable rendering of a single atomic payload —
18197/// `Symbol(s) → "{s}"`, `Keyword(s) → ":{s}"`, `Str(s) → "{s:?}"` (the
18198/// debug-quoted form: `\"…\"` with embedded `"` and `\` escaped), `Int(n)
18199/// → "{n}"`, `Float(n)` through [`fmt_float`] so integral values render
18200/// with the `.0` suffix that preserves the typed-`Float`-vs-typed-`Int`
18201/// distinction at the Display→read boundary, `Bool(true) → "#t"`,
18202/// `Bool(false) → "#f"` (the Scheme bool spellings the reader's
18203/// typed-entry classifier [`Atom::from_lexeme`] dispatches on — `true`
18204/// / `false` re-read as symbols, NOT as bools — see the CLAUDE.md
18205/// "Lisp bools" warning).
18206///
18207/// This is the *atomic-payload Display surface* — the typed-exit-side
18208/// peer of [`Atom::from_lexeme`]'s atomic-payload typed-entry surface
18209/// (the FOURTH and LAST of the per-`Atom`-variant projection sites
18210/// lifted onto the closed-set algebra, after the typed-exit Display
18211/// [this impl], JSON [`Atom::to_json`], and iac-forge canonical
18212/// attestation `Atom::to_iac_forge_sexpr` (removed) projections — completing
18213/// the bidirectional typed-entry/typed-exit sweep). Before this lift
18214/// the per-variant rendering arms
18215/// lived inline at the `Sexp::Atom(a) => match a { … }` arm of
18216/// [`fmt::Display for Sexp`]; routing the outer arm through this impl
18217/// lifts the seven inline sub-arms (the Bool variant splits into
18218/// `true`/`false` to short-circuit the `if-else` branch) into ONE
18219/// typed-algebra method the `Sexp` Display arm calls into via
18220/// `fmt::Display::fmt(a, f)`. Sibling closed-set lift to
18221/// [`QuoteForm::prefix`] (the four homoiconic prefix wrappers) and
18222/// [`AtomKind::label`] (the six diagnostic labels) — those name the
18223/// quote-family and atomic-discriminator pairings at the `Sexp` and
18224/// `Atom` algebras respectively; this names the atomic-payload
18225/// rendering at the `Atom` algebra so future consumers of "render a
18226/// bare atom" land on this impl directly without unwrapping through
18227/// `Sexp::Atom(_).to_string()` and stripping the outer wrap.
18228///
18229/// Three production-site sibling shapes the substrate carries that
18230/// route through a per-`Atom`-variant projection, all 6/7-arm inline
18231/// matches pre-lift:
18232/// * [`fmt::Display for Sexp`]'s atom arm — 7 sub-arms (Bool splits),
18233/// produces a `fmt::Formatter` body. Post-lift collapses to
18234/// ONE `fmt::Display::fmt(a, f)` delegation.
18235/// * [`crate::domain::sexp_to_json`]'s atom arms — 6 inline arms
18236/// producing `serde_json::Value`. Now lifted onto [`Atom::to_json`]
18237/// in the sibling pattern this impl's docstring named; the
18238/// `sexp_to_json` site collapses to ONE `Sexp::Atom(a) =>
18239/// a.to_json()` arm.
18240/// * `crate::interop`'s `From<&Sexp> for SExpr` (removed)'s
18241/// atom arm (feature-gated `iac-forge`) — 6 inline arms producing
18242/// `iac_forge::sexpr::SExpr`. Now lifted onto
18243/// `Atom::to_iac_forge_sexpr` (removed) in the sibling pattern this impl's
18244/// docstring named; the interop site collapses to ONE
18245/// `Sexp::Atom(a) => a.to_iac_forge_sexpr()` arm. THIRD and LAST
18246/// of the three production-site atom-arm shapes lifted onto the
18247/// typed `Atom` algebra; the sweep across the Lisp / JSON /
18248/// iac-forge canonical-form surfaces is complete.
18249///
18250/// The (Atom variant, rendered prefix/suffix/body) quadruple now lives
18251/// at ONE typed-algebra Display impl rather than at seven inline
18252/// sub-arms inside `Display for Sexp`'s outer Atom arm. A regression
18253/// that drifts the Bool spelling (`#t`/`#f` vs `true`/`false`) — the
18254/// CLAUDE.md-pinned reader-round-trip invariant — now lands at ONE
18255/// site, and the test surface pins each variant's canonical rendering
18256/// AND the round-trip identity through the reader at the Atom level
18257/// directly (no Sexp wrap required to exercise the round-trip).
18258///
18259/// Bidirectional contract anchored by tests in this module:
18260/// * `atom_display_renders_each_variant_to_canonical_form` —
18261/// sweeps `AtomKind::ALL` and pins each variant's canonical
18262/// rendering byte-for-byte against the pre-lift inline literal,
18263/// so a future regression that drifts ONE arm (e.g. swaps
18264/// `#t`/`#f` for `true`/`false`, or strips `Str`'s quote marks)
18265/// fails loudly.
18266/// * `sexp_atom_display_arm_routes_through_atom_display_for_every_variant`
18267/// — pins the lifted boundary: `Sexp::Atom(a).to_string() ==
18268/// a.to_string()` for every atomic payload variant, AND that
18269/// both equal the legacy inline rendering. Catches a future
18270/// drift where one surface's per-variant body changes without
18271/// the other.
18272/// * `atom_display_round_trips_through_reader_preserving_typed_identity`
18273/// — sweeps a representative atom of each variant, renders it
18274/// via `Atom::Display`, parses the rendering through
18275/// [`crate::reader::read`], and pins the parsed atom equals
18276/// the seed atom (modulo `Str`'s debug-quoted spelling — pinned
18277/// separately because the reader expects unquoted source-level
18278/// `"foo"`). Pins that the (`Atom::Display`, reader) pair forms
18279/// a typed round-trip at the atom layer, the same invariant
18280/// [`fmt_float`]'s `.0` suffix preserves for the float-vs-int
18281/// distinction at the Sexp layer.
18282///
18283/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
18284/// (Atom variant, canonical rendering) pair appeared inline at THREE
18285/// production sites (`Display for Sexp`'s 7-sub-arm atom arm,
18286/// `sexp_to_json`'s 6 atom arms, `From<&Sexp> for SExpr`'s 6 atom arms)
18287/// — well past the ≥2 PRIME-DIRECTIVE trigger once the structural
18288/// shape is named. THIS lift retires the Display-surface site by
18289/// naming the typed primitive on the `Atom` algebra; future runs route
18290/// the JSON and iac-forge sites through parallel sibling projections
18291/// (`Atom::to_json`, `Atom::to_iac_forge_sexpr`) the same pattern
18292/// names. THEORY.md §II.1 invariant 1 — typed entry; the substrate's
18293/// [`Atom::from_lexeme`] is the typed-entry gate at the atomic-payload
18294/// boundary (lifted onto the typed [`Atom`] algebra from the reader's
18295/// pre-lift free function), and this impl is the typed-exit-side
18296/// mirror — the closed-set [`AtomKind`] algebra now threads BOTH gates
18297/// through ONE projection family, so a regression that drifts one side's
18298/// per-variant rendering from the other (e.g. extends `Atom` with a
18299/// `Char` variant the reader accepts but the writer can't emit) is no
18300/// longer a silent two-site divergence — rustc binds both sides to
18301/// the same closed-set enum. THEORY.md §II.1 invariant 2 — free middle;
18302/// the typed-exit rendering, the reader, the diagnostic surface
18303/// (`LispError::TypeMismatch.got` slot rendering an atomic witness),
18304/// and any future authoring tool (LSP / REPL pretty-printer) all
18305/// route through ONE per-variant rendering rather than per-callsite
18306/// re-derivation.
18307///
18308/// Frontier inspiration: Racket's `(syntax->datum stx)` / `write` pair
18309/// — where `syntax->datum` unwraps the homoiconic surface to its
18310/// atomic-payload layer and `write` emits the canonical S-expression
18311/// rendering bound to the reader's `read` inverse; `Atom::Display`
18312/// is the substrate's typed-algebra peer at the atomic-payload boundary,
18313/// with the closed-set [`AtomKind`] standing in for Racket's
18314/// datum-prim taxonomy. MLIR's `mlir::AsmPrinter::printAttribute` — the
18315/// typed-IR attribute printer dispatches on the closed-set
18316/// `AttributeKind` so every printer body for a kind lives at ONE
18317/// implementation site; `Atom::Display` is the unstructured Rust peer
18318/// for the `Sexp`/`Atom` algebra, with `fmt::Display` standing in for
18319/// MLIR's `AsmPrinter` interface.
18320impl fmt::Display for Atom {
18321 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18322 match self {
18323 Self::Symbol(s) => f.write_str(s),
18324 Self::Keyword(s) => write!(f, "{}{s}", Self::KEYWORD_MARKER),
18325 // NOT `{s:?}`. Rust's `str` Debug escapes NUL as `\0`, and this
18326 // reader deliberately decodes `\0` as the DIGIT `0` — see
18327 // `decode_str_escape`'s passthrough arm and the test pinning it.
18328 // So `{s:?}` emitted an escape that means something else here, and
18329 // `"nul\0byte"` round-tripped to `"nul0byte"`: silent corruption
18330 // from two locally-correct decisions disagreeing.
18331 //
18332 // This escaper emits exactly what THIS reader decodes: the five
18333 // named/self escapes, and `\u{…}` for anything else non-printable
18334 // (which the reader gained an arm for alongside this fix). Display
18335 // and read are inverses again.
18336 Self::Str(s) => write!(f, "{}", Self::escape_str_payload(s)),
18337 Self::Int(n) => write!(f, "{n}"),
18338 Self::Float(n) => fmt_float(*n, f),
18339 // Bool arm collapses to ONE branch routing through
18340 // `Self::bool_literal` — the closed-set `bool` fork happens
18341 // at the typed projection on the [`Atom`] algebra, not at
18342 // this consumer's match body. Sibling lift to the Keyword
18343 // arm, which routes through `Self::KEYWORD_MARKER` at the
18344 // same algebra layer.
18345 Self::Bool(b) => f.write_str(Self::bool_literal(*b)),
18346 }
18347 }
18348}
18349
18350impl fmt::Display for Sexp {
18351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18352 match self {
18353 // The empty-list rendering `()` composes BOTH structural
18354 // delimiters — [`Self::LIST_OPEN`] followed by
18355 // [`Self::LIST_CLOSE`] — on the closed-set outer [`Sexp`]
18356 // algebra. Pre-lift the same two bytes lived inline as one
18357 // `"()"` string literal at this arm; post-lift each byte
18358 // binds to its typed constant, so a delimiter swap flips
18359 // both this arm AND the `Self::List(_)` opener/closer arm
18360 // below AND the reader's `Token::LParen` / `Token::RParen`
18361 // outer-dispatch arms in lockstep — at ONE constant per
18362 // side rather than at four inline bytes across two files.
18363 Self::Nil => {
18364 f.write_char(Self::LIST_OPEN)?;
18365 f.write_char(Self::LIST_CLOSE)
18366 }
18367 // The atomic-payload rendering lives at the typed
18368 // [`fmt::Display for Atom`] impl above — the seven inline
18369 // sub-arms `Symbol → s`, `Keyword → ":{s}"`, `Str → "{s:?}"`,
18370 // `Int → "{n}"`, `Float → fmt_float`, `Bool(true) → "#t"`,
18371 // `Bool(false) → "#f"` all bind at ONE site on the closed-set
18372 // `Atom` algebra rather than at this outer arm. A future
18373 // atomic-kind extension (e.g. `Char` for `#\x` reader syntax,
18374 // `Bigint` for arbitrary-precision integers) extends `Atom`'s
18375 // Display impl once and this arm picks up the new variant
18376 // for free.
18377 Self::Atom(a) => fmt::Display::fmt(a, f),
18378 // The `Self::List(_)` opener AND closer arms bind to
18379 // [`Self::LIST_OPEN`] AND [`Self::LIST_CLOSE`] on the
18380 // closed-set outer [`Sexp`] algebra — the SAME two typed
18381 // constants the reader's `Token::LParen` / `Token::RParen`
18382 // outer-dispatch arms AND the `Self::Nil` two-char
18383 // rendering all route through. Adding a fifth structural
18384 // outer-shape (e.g. an eventual `Self::Vector` for `[…]`
18385 // reader syntax) lands as ONE new pair of `Sexp::VEC_OPEN`
18386 // / `Sexp::VEC_CLOSE` constants with the reader arms +
18387 // Display arms binding through them, extending the
18388 // outer-structural algebra by ONE axis without touching
18389 // this arm's `LIST_OPEN` / `LIST_CLOSE` binding.
18390 Self::List(xs) => {
18391 f.write_char(Self::LIST_OPEN)?;
18392 for (i, x) in xs.iter().enumerate() {
18393 if i > 0 {
18394 f.write_str(" ")?;
18395 }
18396 write!(f, "{x}")?;
18397 }
18398 f.write_char(Self::LIST_CLOSE)
18399 }
18400 // The four quote-family variants share the
18401 // `write!(f, "<prefix>{inner}")` Display shape — all route
18402 // through `as_quote_form`'s typed-marker projection so the
18403 // per-variant prefix (`'`, `` ` ``, `,`, `,@`) binds at ONE
18404 // site on the closed-set `QuoteForm` algebra and the
18405 // recursive `inner` rendering composes through the unified
18406 // Display arm. The (prefix, variant) pairing IS the structural
18407 // dual of the reader's `read_quoted` (prefix, variant-ctor)
18408 // dispatch — naming it once threads the round-trip discipline
18409 // through ONE rust function the reader and the Display impl
18410 // both bind against.
18411 Self::Quote(_) | Self::Quasiquote(_) | Self::Unquote(_) | Self::UnquoteSplice(_) => {
18412 let (qf, inner) = self.expect_quote_form();
18413 write!(f, "{}{inner}", qf.prefix())
18414 }
18415 }
18416 }
18417}
18418
18419#[cfg(test)]
18420mod tests {
18421 use super::*;
18422
18423 // ── head_symbol: the operator-position projection ───────────────────
18424 //
18425 // `head_symbol` lifts the `self.as_list()?.first()?.as_symbol()` chain
18426 // that recurred at four soft-dispatch sites (compile.rs `compile_typed`
18427 // + `compile_named_from_forms`, macro_expand.rs `Expander::expand` +
18428 // `macro_def_from`) into ONE named query on the Sexp algebra. These
18429 // tests pin its contract directly; the existing dispatch tests in
18430 // compile.rs / macro_expand.rs are the path-uniformity guards proving
18431 // the four sites route through it without behavior drift.
18432
18433 #[test]
18434 fn head_symbol_returns_operator_for_list_form() {
18435 // `(defpoint obs :class x)` — the operator is the head symbol.
18436 let form = Sexp::List(vec![
18437 Sexp::symbol("defpoint"),
18438 Sexp::symbol("obs"),
18439 Sexp::keyword("class"),
18440 Sexp::symbol("x"),
18441 ]);
18442 assert_eq!(form.head_symbol(), Some("defpoint"));
18443 }
18444
18445 #[test]
18446 fn head_symbol_none_for_non_list_shapes() {
18447 // A bare atom is not an invocation — there is no operator position.
18448 assert_eq!(Sexp::symbol("foo").head_symbol(), None);
18449 assert_eq!(Sexp::int(5).head_symbol(), None);
18450 assert_eq!(Sexp::keyword("k").head_symbol(), None);
18451 assert_eq!(Sexp::string("s").head_symbol(), None);
18452 assert_eq!(Sexp::boolean(true).head_symbol(), None);
18453 assert_eq!(Sexp::float(1.5).head_symbol(), None);
18454 assert_eq!(Sexp::Nil.head_symbol(), None);
18455 // Quote-family wrappers are not lists at the outer layer either.
18456 assert_eq!(Sexp::Quote(Box::new(Sexp::symbol("x"))).head_symbol(), None);
18457 }
18458
18459 #[test]
18460 fn head_symbol_none_for_empty_list() {
18461 // `()` has no first element to read an operator from.
18462 assert_eq!(Sexp::List(vec![]).head_symbol(), None);
18463 }
18464
18465 #[test]
18466 fn head_symbol_none_for_non_symbol_head() {
18467 // A list whose head is present but not a symbol is not a dispatchable
18468 // invocation — the soft projection yields None (the STRICT sibling
18469 // `compile_from_sexp` is the one that rejects these loudly).
18470 assert_eq!(
18471 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]).head_symbol(),
18472 None
18473 );
18474 assert_eq!(
18475 Sexp::List(vec![Sexp::keyword("kw"), Sexp::symbol("a")]).head_symbol(),
18476 None
18477 );
18478 assert_eq!(
18479 Sexp::List(vec![Sexp::string("s"), Sexp::symbol("a")]).head_symbol(),
18480 None
18481 );
18482 assert_eq!(
18483 Sexp::List(vec![
18484 Sexp::List(vec![Sexp::symbol("nested")]),
18485 Sexp::symbol("a")
18486 ])
18487 .head_symbol(),
18488 None
18489 );
18490 assert_eq!(
18491 Sexp::List(vec![Sexp::Nil, Sexp::symbol("a")]).head_symbol(),
18492 None
18493 );
18494 }
18495
18496 #[test]
18497 fn head_symbol_reads_singleton_list_operator() {
18498 // `(defcompiler)` — a keyword-only form still has an operator head;
18499 // this is exactly the arity-gate input compile_named dispatches on
18500 // before rejecting the missing NAME.
18501 assert_eq!(
18502 Sexp::List(vec![Sexp::symbol("defcompiler")]).head_symbol(),
18503 Some("defcompiler")
18504 );
18505 }
18506
18507 #[test]
18508 fn head_symbol_borrows_the_actual_head_string() {
18509 // The returned &str borrows the head atom's contents verbatim — no
18510 // copy, no normalization. Pin that a multi-segment symbol round-trips
18511 // unchanged so the dispatch comparison against `T::KEYWORD` is exact.
18512 let form = Sexp::List(vec![Sexp::symbol("defalert-policy"), Sexp::symbol("p")]);
18513 assert_eq!(form.head_symbol(), Some("defalert-policy"));
18514 }
18515
18516 // ── as_call: the call-form decomposition ────────────────────────────
18517 //
18518 // `as_call` pairs `head_symbol` (the operator projection) with the
18519 // argument tail every dispatch site reads right after matching the
18520 // operator — `Some((op, &args))` for a symbol-headed list, `None` for
18521 // everything else. It lifts the `as_list()`-for-the-tail +
18522 // `head_symbol()`-for-the-operator pairing that recurred at the three
18523 // soft-dispatch sites (compile.rs `compile_typed` + `compile_named_
18524 // from_forms`, macro_expand.rs `Expander::expand`) into ONE match.
18525 // `head_symbol` now delegates to it, so the `as_list()?.first()?.
18526 // as_symbol()` chain lives in exactly one place. These tests pin the
18527 // decomposition's contract directly; the existing dispatch tests in
18528 // compile.rs / macro_expand.rs are the path-uniformity guards proving
18529 // the three sites route through it without behavior drift.
18530
18531 #[test]
18532 fn as_call_decomposes_list_form_into_operator_and_args() {
18533 // `(defpoint obs :class x)` — the operator is the head symbol and
18534 // the args are everything after it.
18535 let args = [
18536 Sexp::symbol("obs"),
18537 Sexp::keyword("class"),
18538 Sexp::symbol("x"),
18539 ];
18540 let form = Sexp::List(
18541 std::iter::once(Sexp::symbol("defpoint"))
18542 .chain(args.iter().cloned())
18543 .collect(),
18544 );
18545 assert_eq!(form.as_call(), Some(("defpoint", &args[..])));
18546 }
18547
18548 #[test]
18549 fn as_call_none_for_non_call_shapes() {
18550 // Every shape `head_symbol` rejects, `as_call` rejects identically:
18551 // non-lists, the empty list, and non-symbol heads have no operator
18552 // to apply, hence no call decomposition.
18553 assert_eq!(Sexp::symbol("foo").as_call(), None);
18554 assert_eq!(Sexp::int(5).as_call(), None);
18555 assert_eq!(Sexp::keyword("k").as_call(), None);
18556 assert_eq!(Sexp::string("s").as_call(), None);
18557 assert_eq!(Sexp::Nil.as_call(), None);
18558 assert_eq!(Sexp::Quote(Box::new(Sexp::symbol("x"))).as_call(), None);
18559 assert_eq!(Sexp::List(vec![]).as_call(), None);
18560 assert_eq!(
18561 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]).as_call(),
18562 None
18563 );
18564 assert_eq!(
18565 Sexp::List(vec![Sexp::keyword("kw"), Sexp::symbol("a")]).as_call(),
18566 None
18567 );
18568 }
18569
18570 #[test]
18571 fn as_call_yields_empty_args_for_singleton_list() {
18572 // `(defcompiler)` — a keyword-only form decomposes to its operator
18573 // with an EMPTY argument tail. This is exactly the arity-gate input
18574 // `compile_named_from_forms` dispatches on before rejecting the
18575 // missing NAME via `rest.split_first()` returning `None`.
18576 assert_eq!(
18577 Sexp::List(vec![Sexp::symbol("defcompiler")]).as_call(),
18578 Some(("defcompiler", &[][..]))
18579 );
18580 }
18581
18582 #[test]
18583 fn as_call_args_are_exactly_the_tail_after_the_operator() {
18584 // The args slice borrows `&list[1..]` verbatim — the head is
18585 // excluded, every following element is included in order.
18586 let form = Sexp::List(vec![
18587 Sexp::symbol("defmonitor"),
18588 Sexp::symbol("cpu"),
18589 Sexp::keyword("threshold"),
18590 Sexp::int(90),
18591 ]);
18592 let (op, args) = form.as_call().expect("symbol-headed list decomposes");
18593 assert_eq!(op, "defmonitor");
18594 assert_eq!(args.len(), 3);
18595 assert_eq!(args[0], Sexp::symbol("cpu"));
18596 assert_eq!(args[2], Sexp::int(90));
18597 }
18598
18599 #[test]
18600 fn head_symbol_is_the_operator_projection_of_as_call() {
18601 // The structural relationship the lift establishes: `head_symbol`
18602 // is `as_call().map(|(h, _)| h)`. Pin it across every shape so a
18603 // regression that drifts one query's head-recognition from the
18604 // other — e.g. `as_call` accepting a keyword head that `head_symbol`
18605 // still rejects — fails loudly. The two share ONE chain.
18606 let shapes = [
18607 Sexp::symbol("foo"),
18608 Sexp::int(5),
18609 Sexp::keyword("k"),
18610 Sexp::Nil,
18611 Sexp::List(vec![]),
18612 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18613 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::symbol("p")]),
18614 Sexp::List(vec![Sexp::symbol("solo")]),
18615 ];
18616 for s in &shapes {
18617 assert_eq!(
18618 s.head_symbol(),
18619 s.as_call().map(|(h, _)| h),
18620 "head_symbol must equal the operator component of as_call for {s}"
18621 );
18622 }
18623 }
18624
18625 // ── as_call_to: the keyword-typed call decomposition ────────────────
18626 //
18627 // `as_call_to(keyword)` answers "is this a call to ONE specific
18628 // operator, and what are its arguments?" — the keyword-aware sibling
18629 // of `as_call`. It lifts the `as_call() + head == T::KEYWORD` two-step
18630 // chain that recurred at the two `compile.rs` dispatch sites
18631 // (`compile_typed` and `compile_named_from_forms`) into ONE structural
18632 // query on the Sexp algebra. The tests below pin its contract
18633 // directly; the existing `compile_*` tests are the path-uniformity
18634 // guards proving the two production sites route through it without
18635 // behavior drift.
18636
18637 #[test]
18638 fn as_call_to_returns_args_for_matching_head() {
18639 // `(defmonitor :name "x")` — head is the exact symbol `defmonitor`,
18640 // so `as_call_to("defmonitor")` returns `Some(args)` with the tail
18641 // after the head verbatim.
18642 let form = Sexp::List(vec![
18643 Sexp::symbol("defmonitor"),
18644 Sexp::keyword("name"),
18645 Sexp::string("x"),
18646 ]);
18647 let args = form
18648 .as_call_to("defmonitor")
18649 .expect("matching head must yield Some(args)");
18650 assert_eq!(args.len(), 2);
18651 assert_eq!(args[0], Sexp::keyword("name"));
18652 assert_eq!(args[1], Sexp::string("x"));
18653 }
18654
18655 #[test]
18656 fn as_call_to_returns_none_for_mismatched_head() {
18657 // `(defmonitor …)` against keyword `"defpoint"` — same form is a
18658 // call (so `as_call().is_some()`), but the head doesn't equal the
18659 // requested keyword. `as_call_to` is the keyword-typed projection,
18660 // so it yields `None` exactly when the head doesn't match. Pin the
18661 // gate: the two pre-lift inline sites both rejected this case via
18662 // `if head != T::KEYWORD { continue }` / `if head == T::KEYWORD`,
18663 // and the lifted primitive must reject identically.
18664 let form = Sexp::List(vec![
18665 Sexp::symbol("defmonitor"),
18666 Sexp::keyword("name"),
18667 Sexp::string("x"),
18668 ]);
18669 assert!(form.as_call().is_some());
18670 assert_eq!(form.as_call_to("defpoint"), None);
18671 assert_eq!(form.as_call_to(""), None);
18672 assert_eq!(form.as_call_to("DEFMONITOR"), None);
18673 }
18674
18675 #[test]
18676 fn as_call_to_yields_empty_args_for_singleton_matching_call() {
18677 // `(defcompiler)` against keyword `"defcompiler"` — the head
18678 // matches and the argument tail is the empty slice. Pin the
18679 // empty-tail posture: this is exactly the input
18680 // `compile_named_from_forms` dispatches on before rejecting the
18681 // missing NAME via `rest.split_first()` returning `None`, so the
18682 // lifted primitive must yield `Some(&[])` here (NOT `None`) so
18683 // the downstream split-first gate fires structurally.
18684 let form = Sexp::List(vec![Sexp::symbol("defcompiler")]);
18685 assert_eq!(form.as_call_to("defcompiler"), Some(&[][..]));
18686 }
18687
18688 #[test]
18689 fn as_call_to_returns_none_for_non_call_shapes() {
18690 // Every shape `as_call` rejects, `as_call_to` rejects identically
18691 // regardless of the requested keyword: non-lists, the empty list,
18692 // and non-symbol heads have no operator to compare to. Pin
18693 // path-uniformity with the `as_call` sibling so a regression that
18694 // narrows the keyword-typed projection to admit a shape the bare
18695 // soft projection rejected (e.g. accepting a keyword head when
18696 // `keyword` matches the keyword's symbol-string projection) fails
18697 // here.
18698 let shapes = [
18699 Sexp::symbol("foo"),
18700 Sexp::int(5),
18701 Sexp::keyword("k"),
18702 Sexp::string("s"),
18703 Sexp::boolean(true),
18704 Sexp::float(1.5),
18705 Sexp::Nil,
18706 Sexp::Quote(Box::new(Sexp::symbol("foo"))),
18707 Sexp::List(vec![]),
18708 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18709 Sexp::List(vec![Sexp::keyword("foo"), Sexp::symbol("a")]),
18710 Sexp::List(vec![Sexp::string("foo"), Sexp::symbol("a")]),
18711 ];
18712 for s in &shapes {
18713 assert_eq!(
18714 s.as_call_to("foo"),
18715 None,
18716 "non-call shape must yield None for any keyword, got Some for {s}"
18717 );
18718 assert_eq!(s.as_call_to("anything"), None);
18719 }
18720 }
18721
18722 #[test]
18723 fn as_call_to_args_borrow_is_same_pointer_as_as_call_tail() {
18724 // The structural identity binding `as_call_to` to its `as_call`
18725 // sibling: on the matching-head path, the returned `args` slice IS
18726 // the same `&[Sexp]` slice `as_call` would return as the tail
18727 // component. Pin pointer equality so a regression that
18728 // re-allocates or copies the tail in the keyword-typed projection
18729 // fails loudly — the soft-projection contract is borrow, not
18730 // clone, AND `as_call_to` inherits the contract verbatim from
18731 // `as_call`.
18732 let form = Sexp::List(vec![
18733 Sexp::symbol("defmonitor"),
18734 Sexp::keyword("name"),
18735 Sexp::string("x"),
18736 ]);
18737 let (_, via_as_call) = form.as_call().expect("call shape");
18738 let via_as_call_to = form
18739 .as_call_to("defmonitor")
18740 .expect("matching keyword shape");
18741 assert!(
18742 std::ptr::eq(via_as_call.as_ptr(), via_as_call_to.as_ptr()),
18743 "as_call_to args must borrow the SAME slice as as_call's tail"
18744 );
18745 assert_eq!(via_as_call.len(), via_as_call_to.len());
18746 }
18747
18748 #[test]
18749 fn as_call_to_is_the_keyword_typed_projection_of_as_call() {
18750 // The structural identity the lift establishes:
18751 // `as_call_to(k) == as_call().and_then(|(h, args)| (h == k).then_some(args))`
18752 // `as_call_to(k).is_some() == (head_symbol() == Some(k))`
18753 // Pin both across every shape so a regression that drifts the
18754 // keyword-typed projection from its closed-form definition fails
18755 // loudly. The three soft-projection primitives — `head_symbol`,
18756 // `as_call`, `as_call_to` — must agree on operator-position
18757 // recognition at every shape they share.
18758 let shapes = [
18759 Sexp::symbol("foo"),
18760 Sexp::int(5),
18761 Sexp::keyword("k"),
18762 Sexp::Nil,
18763 Sexp::List(vec![]),
18764 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18765 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::symbol("p")]),
18766 Sexp::List(vec![Sexp::symbol("defmonitor"), Sexp::keyword("name")]),
18767 Sexp::List(vec![Sexp::symbol("solo")]),
18768 ];
18769 for s in &shapes {
18770 for k in ["defpoint", "defmonitor", "solo", "foo", ""] {
18771 let via_chain = s.as_call().and_then(|(h, args)| (h == k).then_some(args));
18772 assert_eq!(
18773 s.as_call_to(k),
18774 via_chain,
18775 "as_call_to({k:?}) must equal as_call+filter for {s}"
18776 );
18777 assert_eq!(
18778 s.as_call_to(k).is_some(),
18779 s.head_symbol() == Some(k),
18780 "as_call_to({k:?}).is_some() must equal (head_symbol() == Some({k:?})) for {s}"
18781 );
18782 }
18783 }
18784 }
18785
18786 // ── as_call_to_any: the typed-decoded call decomposition ────────────
18787 //
18788 // `as_call_to_any(decode)` answers "is this a call whose head decodes
18789 // through `decode`, and what are its arguments?" — the closure-typed
18790 // sibling of `as_call_to`. It lifts the
18791 // `as_list() + head_symbol() + decode(head)` three-step chain that
18792 // recurred at the macro-expander's `macro_def_from` site (the typed
18793 // `MacroDefHead::from_keyword` dispatch surface) into ONE structural
18794 // query on the Sexp algebra. The tests below pin its contract
18795 // directly; the existing macro-expansion tests are the path-
18796 // uniformity guards proving the production site routes through it
18797 // without behavior drift.
18798 //
18799 // The test classifier `Op::from_keyword` mirrors `MacroDefHead::from_keyword`
18800 // — a closed-set typed enum projection from a `&str` head — so the
18801 // tests cover the macro-expander's real consumer shape rather than a
18802 // synthetic predicate.
18803 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
18804 enum Op {
18805 Quote,
18806 If,
18807 Let,
18808 }
18809 impl Op {
18810 fn from_keyword(head: &str) -> Option<Self> {
18811 match head {
18812 "quote" => Some(Self::Quote),
18813 "if" => Some(Self::If),
18814 "let" => Some(Self::Let),
18815 _ => None,
18816 }
18817 }
18818 }
18819
18820 #[test]
18821 fn as_call_to_any_returns_decoded_head_and_args_for_matching_head() {
18822 // `(if c t e)` — head `if` decodes to `Op::If`, args are the
18823 // three-element tail verbatim. Pin both halves of the returned
18824 // tuple: the decoded typed witness AND the borrowed args slice.
18825 let form = Sexp::List(vec![
18826 Sexp::symbol("if"),
18827 Sexp::symbol("c"),
18828 Sexp::symbol("t"),
18829 Sexp::symbol("e"),
18830 ]);
18831 let (op, args) = form
18832 .as_call_to_any(Op::from_keyword)
18833 .expect("matching head must yield Some((decoded, args))");
18834 assert_eq!(op, Op::If);
18835 assert_eq!(args.len(), 3);
18836 assert_eq!(args[0], Sexp::symbol("c"));
18837 assert_eq!(args[2], Sexp::symbol("e"));
18838 }
18839
18840 #[test]
18841 fn as_call_to_any_returns_none_when_decoder_rejects_head() {
18842 // `(defmonitor :name "x")` — head `defmonitor` is a valid symbol
18843 // (so `as_call().is_some()`), but `Op::from_keyword` rejects it
18844 // (it's not one of the closed `{quote, if, let}` set). Pin the
18845 // gate: `as_call_to_any` yields `None` exactly when the decoder
18846 // rejects the head, mirroring how the pre-lift inline chain in
18847 // `macro_def_from` returned `Ok(None)` when
18848 // `MacroDefHead::from_keyword(head_str)` returned `None`.
18849 let form = Sexp::List(vec![
18850 Sexp::symbol("defmonitor"),
18851 Sexp::keyword("name"),
18852 Sexp::string("x"),
18853 ]);
18854 assert!(form.as_call().is_some());
18855 assert!(form.as_call_to_any(Op::from_keyword).is_none());
18856 }
18857
18858 #[test]
18859 fn as_call_to_any_yields_empty_args_for_singleton_decoded_call() {
18860 // `(quote)` against the classifier — head decodes to `Op::Quote`
18861 // and the argument tail is the empty slice. Pin the empty-tail
18862 // posture: a downstream arity gate (analogous to
18863 // `if list.len() < 4` inside `macro_def_from`) dispatches on
18864 // `args.is_empty()` AFTER the decoder accepts the head; the
18865 // helper must yield `Some((decoded, &[]))` (NOT `None`) so that
18866 // gate fires structurally.
18867 let form = Sexp::List(vec![Sexp::symbol("quote")]);
18868 let (op, args) = form
18869 .as_call_to_any(Op::from_keyword)
18870 .expect("singleton matching call must decompose");
18871 assert_eq!(op, Op::Quote);
18872 assert_eq!(args.len(), 0);
18873 }
18874
18875 #[test]
18876 fn as_call_to_any_returns_none_for_non_call_shapes() {
18877 // Every shape `as_call` rejects, `as_call_to_any` rejects
18878 // identically regardless of the decoder: non-lists, the empty
18879 // list, and non-symbol heads have no operator string to feed
18880 // the decoder. Pin path-uniformity with the `as_call` sibling so
18881 // a regression that admits a non-call shape (e.g. accepting a
18882 // bare symbol via a permissive decoder) fails here. Pass
18883 // `Some` for every input to prove the call-shape gate fires
18884 // BEFORE the decoder runs — the decoder cannot rescue a
18885 // non-call.
18886 let shapes = [
18887 Sexp::symbol("foo"),
18888 Sexp::int(5),
18889 Sexp::keyword("k"),
18890 Sexp::string("s"),
18891 Sexp::boolean(true),
18892 Sexp::float(1.5),
18893 Sexp::Nil,
18894 Sexp::Quote(Box::new(Sexp::symbol("foo"))),
18895 Sexp::List(vec![]),
18896 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18897 Sexp::List(vec![Sexp::keyword("foo"), Sexp::symbol("a")]),
18898 Sexp::List(vec![Sexp::string("foo"), Sexp::symbol("a")]),
18899 ];
18900 for s in &shapes {
18901 // The promiscuous decoder accepts every &str head, so the
18902 // only way to see `None` here is if the call-shape gate
18903 // rejects the shape upstream of the decoder.
18904 assert_eq!(
18905 s.as_call_to_any(|h: &str| Some(h.to_string())),
18906 None,
18907 "non-call shape must yield None even for a promiscuous decoder, got Some for {s}"
18908 );
18909 }
18910 }
18911
18912 #[test]
18913 fn as_call_to_any_args_borrow_is_same_pointer_as_as_call_tail() {
18914 // The structural identity binding `as_call_to_any` to its
18915 // `as_call` sibling: on the decoded path, the returned `args`
18916 // slice IS the same `&[Sexp]` slice `as_call` would return as
18917 // the tail component. Pin pointer equality so a regression that
18918 // re-allocates or copies the tail in the typed-decoded
18919 // projection fails loudly — the soft-projection contract is
18920 // borrow, not clone, AND `as_call_to_any` inherits the contract
18921 // verbatim from `as_call`. Parallel to the
18922 // `as_call_to_args_borrow_is_same_pointer_as_as_call_tail` pin
18923 // for `as_call_to`.
18924 let form = Sexp::List(vec![
18925 Sexp::symbol("if"),
18926 Sexp::symbol("c"),
18927 Sexp::symbol("t"),
18928 ]);
18929 let (_, via_as_call) = form.as_call().expect("call shape");
18930 let (_, via_as_call_to_any) = form
18931 .as_call_to_any(Op::from_keyword)
18932 .expect("decoded shape");
18933 assert!(
18934 std::ptr::eq(via_as_call.as_ptr(), via_as_call_to_any.as_ptr()),
18935 "as_call_to_any args must borrow the SAME slice as as_call's tail"
18936 );
18937 assert_eq!(via_as_call.len(), via_as_call_to_any.len());
18938 }
18939
18940 #[test]
18941 fn as_call_to_any_is_the_decoded_projection_of_as_call() {
18942 // The structural identity the lift establishes:
18943 // `as_call_to_any(decode) == as_call().and_then(|(h, args)| decode(h).map(|d| (d, args)))`
18944 // `as_call_to_any(decode).is_some() == as_call().map_or(false, |(h, _)| decode(h).is_some())`
18945 // Pin both across every shape so a regression that drifts the
18946 // typed-decoded projection from its closed-form definition fails
18947 // loudly. The four soft-projection primitives — `head_symbol`,
18948 // `as_call`, `as_call_to`, `as_call_to_any` — must agree on
18949 // operator-position recognition at every shape they share.
18950 let shapes = [
18951 Sexp::symbol("foo"),
18952 Sexp::int(5),
18953 Sexp::keyword("k"),
18954 Sexp::Nil,
18955 Sexp::List(vec![]),
18956 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18957 Sexp::List(vec![Sexp::symbol("if"), Sexp::symbol("c")]),
18958 Sexp::List(vec![Sexp::symbol("quote"), Sexp::symbol("x")]),
18959 Sexp::List(vec![Sexp::symbol("let"), Sexp::List(vec![])]),
18960 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::symbol("p")]),
18961 Sexp::List(vec![Sexp::symbol("solo")]),
18962 ];
18963 for s in &shapes {
18964 let via_chain = s
18965 .as_call()
18966 .and_then(|(h, args)| Op::from_keyword(h).map(|d| (d, args)));
18967 assert_eq!(
18968 s.as_call_to_any(Op::from_keyword),
18969 via_chain,
18970 "as_call_to_any(Op::from_keyword) must equal as_call+decode for {s}"
18971 );
18972 }
18973 }
18974
18975 #[test]
18976 fn as_call_to_any_subsumes_as_call_to_via_unit_decoder() {
18977 // The closed-form composition `as_call_to(k) == as_call_to_any
18978 // (|h| (h == k).then_some(())).map(|(_, a)| a)` (modulo the
18979 // discarded `()` decoded witness). Pin it across every shape ×
18980 // keyword pair so a regression that drifts the typed-decoded
18981 // projection from its single-keyword sibling fails loudly. This
18982 // makes the family closure: `as_call_to` is the trivial-decoder
18983 // instance of `as_call_to_any`, and naming both lets each
18984 // consumer pick the projection that fits its call site.
18985 let shapes = [
18986 Sexp::symbol("foo"),
18987 Sexp::Nil,
18988 Sexp::List(vec![]),
18989 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18990 Sexp::List(vec![Sexp::symbol("if"), Sexp::symbol("c")]),
18991 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::symbol("p")]),
18992 ];
18993 for s in &shapes {
18994 for k in ["if", "defpoint", "let", "foo", "", "DEFPOINT"] {
18995 let via_unit_decoder = s
18996 .as_call_to_any(|h: &str| (h == k).then_some(()))
18997 .map(|(_, args)| args);
18998 assert_eq!(
18999 s.as_call_to(k),
19000 via_unit_decoder,
19001 "as_call_to({k:?}) must equal as_call_to_any+unit-decoder for {s}"
19002 );
19003 }
19004 }
19005 }
19006
19007 // ── iter_calls_to: the slice-side projection of as_call_to ──────────
19008 //
19009 // `iter_calls_to(forms, keyword)` lifts the per-form projection
19010 // `as_call_to` onto a `&[Sexp]`, yielding the args tails of every
19011 // matching form in source order — the substrate's typed-keyword
19012 // filter over a batch of forms. The two inline `for form in
19013 // &expanded { if let Some(args) = form.as_call_to(T::KEYWORD) { … } }`
19014 // walks at the `compile_typed` + `compile_named_from_forms` dispatch
19015 // sites (compile.rs) collapse to ONE `iter_calls_to(&expanded,
19016 // T::KEYWORD)` call. Tests pin the slice-side primitive's contract
19017 // directly; the existing dispatch tests in compile.rs are the
19018 // path-uniformity guards proving the two consumers route through it
19019 // without behavior drift.
19020
19021 #[test]
19022 fn iter_calls_to_yields_args_for_every_matching_form_in_slice() {
19023 // Three forms: two match "defmonitor", one matches "defalert".
19024 // `iter_calls_to("defmonitor")` yields the two matching args
19025 // slices in source order — the matched forms' tails verbatim,
19026 // skipping the non-matching `defalert` form silently.
19027 let forms = vec![
19028 Sexp::List(vec![
19029 Sexp::symbol("defmonitor"),
19030 Sexp::keyword("name"),
19031 Sexp::string("a"),
19032 ]),
19033 Sexp::List(vec![
19034 Sexp::symbol("defalert"),
19035 Sexp::keyword("name"),
19036 Sexp::string("p"),
19037 ]),
19038 Sexp::List(vec![
19039 Sexp::symbol("defmonitor"),
19040 Sexp::keyword("name"),
19041 Sexp::string("b"),
19042 ]),
19043 ];
19044 let args: Vec<&[Sexp]> = iter_calls_to(&forms, "defmonitor").collect();
19045 assert_eq!(args.len(), 2);
19046 assert_eq!(args[0], &[Sexp::keyword("name"), Sexp::string("a")][..]);
19047 assert_eq!(args[1], &[Sexp::keyword("name"), Sexp::string("b")][..]);
19048 }
19049
19050 #[test]
19051 fn iter_calls_to_skips_every_non_call_shape_silently() {
19052 // Every shape `as_call_to` rejects, `iter_calls_to` skips: non-
19053 // lists (atoms across all 6 atom kinds, Nil, quote-family
19054 // wrapper), the empty list, and non-symbol-head lists. Pin
19055 // path-uniformity with the per-form sibling: passing ANY keyword
19056 // against a slice of non-call shapes yields zero items. Closes
19057 // the soft-projection posture at the slice level — a regression
19058 // that admits a non-call shape (e.g. accepting a bare symbol
19059 // whose name matches the keyword) fails here.
19060 let forms = vec![
19061 Sexp::symbol("foo"),
19062 Sexp::int(5),
19063 Sexp::keyword("k"),
19064 Sexp::string("s"),
19065 Sexp::boolean(true),
19066 Sexp::float(1.5),
19067 Sexp::Nil,
19068 Sexp::Quote(Box::new(Sexp::symbol("foo"))),
19069 Sexp::List(vec![]),
19070 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
19071 Sexp::List(vec![Sexp::keyword("foo"), Sexp::symbol("a")]),
19072 ];
19073 for k in ["foo", "anything", "", "defpoint"] {
19074 let args: Vec<&[Sexp]> = iter_calls_to(&forms, k).collect();
19075 assert!(
19076 args.is_empty(),
19077 "non-call slice must yield zero items for keyword {k:?}, got {} items",
19078 args.len()
19079 );
19080 }
19081 }
19082
19083 #[test]
19084 fn iter_calls_to_yields_empty_args_slice_for_singleton_matching_call() {
19085 // `(defcompiler)` — the head matches and the args tail is the
19086 // empty slice. Pin the empty-tail posture: `iter_calls_to` must
19087 // yield `Some(&[])` for the matching singleton (NOT skip it),
19088 // mirroring `as_call_to`'s contract — the (possibly-empty) args
19089 // slice on a match, NOT `None` on an empty tail. This is exactly
19090 // the input `compile_named_from_forms` dispatches on before
19091 // rejecting the missing NAME via `rest.split_first()`'s `None`.
19092 let forms = vec![Sexp::List(vec![Sexp::symbol("defcompiler")])];
19093 let args: Vec<&[Sexp]> = iter_calls_to(&forms, "defcompiler").collect();
19094 assert_eq!(args.len(), 1);
19095 assert_eq!(args[0], &[][..]);
19096 }
19097
19098 #[test]
19099 fn iter_calls_to_yields_nothing_for_empty_slice() {
19100 // An empty forms slice yields zero items regardless of keyword.
19101 // Pin the slice-side primitive's degenerate boundary: empty in,
19102 // empty out — the iterator is fused-empty without consulting
19103 // `as_call_to` at all.
19104 let forms: Vec<Sexp> = vec![];
19105 let mut iter = iter_calls_to(&forms, "anything");
19106 assert!(iter.next().is_none());
19107 }
19108
19109 #[test]
19110 fn iter_calls_to_yields_nothing_when_keyword_matches_no_form() {
19111 // A slice of valid call forms whose heads none match the
19112 // requested keyword yields zero items. Pin path-uniformity with
19113 // the per-form sibling: every form's `as_call_to(missing)` is
19114 // `None`, so the slice-side iterator yields nothing — the filter
19115 // fires uniformly across the batch.
19116 let forms = vec![
19117 Sexp::List(vec![Sexp::symbol("defmonitor"), Sexp::int(1)]),
19118 Sexp::List(vec![Sexp::symbol("defalert"), Sexp::int(2)]),
19119 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::int(3)]),
19120 ];
19121 let args: Vec<&[Sexp]> = iter_calls_to(&forms, "missing").collect();
19122 assert!(args.is_empty());
19123 }
19124
19125 #[test]
19126 fn iter_calls_to_args_borrow_is_same_pointer_as_per_form_as_call_to_tail() {
19127 // The structural identity binding `iter_calls_to` to its per-form
19128 // sibling: each yielded `&[Sexp]` IS the same slice `as_call_to`
19129 // would return as the tail component for the corresponding form
19130 // (pinned via `std::ptr::eq` on `as_ptr()`). The soft-projection
19131 // contract is borrow, not clone, AND `iter_calls_to` inherits the
19132 // contract verbatim from `as_call_to`. Parallel to the
19133 // `as_call_to_args_borrow_is_same_pointer_as_as_call_tail` pin
19134 // for `as_call_to`.
19135 let forms = vec![Sexp::List(vec![
19136 Sexp::symbol("defmonitor"),
19137 Sexp::keyword("name"),
19138 Sexp::string("a"),
19139 ])];
19140 let via_iter: &[Sexp] = iter_calls_to(&forms, "defmonitor")
19141 .next()
19142 .expect("one match");
19143 let via_per_form: &[Sexp] = forms[0].as_call_to("defmonitor").expect("one match");
19144 assert!(
19145 std::ptr::eq(via_iter.as_ptr(), via_per_form.as_ptr()),
19146 "iter_calls_to args must borrow the SAME slice as as_call_to's tail"
19147 );
19148 assert_eq!(via_iter.len(), via_per_form.len());
19149 }
19150
19151 #[test]
19152 fn iter_calls_to_is_the_slice_side_projection_of_as_call_to() {
19153 // The structural identity the lift establishes:
19154 // `iter_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_call_to(k))`
19155 // Pin shape AND ordering AND pointer-identity across mixed inputs
19156 // and a range of keywords (including matching, non-matching, and
19157 // edge-case empty/case-mismatched keywords) so a regression that
19158 // drifts the slice-side projection from its closed-form
19159 // definition fails loudly. The five soft-projection primitives —
19160 // `head_symbol`, `as_call`, `as_call_to`, `as_call_to_any`, AND
19161 // `iter_calls_to` — must agree on operator-position recognition
19162 // at every shape/slice they share.
19163 let forms = vec![
19164 Sexp::symbol("foo"),
19165 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
19166 Sexp::Nil,
19167 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(2)]),
19168 Sexp::int(99),
19169 Sexp::List(vec![Sexp::symbol("b"), Sexp::int(3)]),
19170 Sexp::List(vec![Sexp::symbol("a")]),
19171 Sexp::List(vec![]),
19172 Sexp::List(vec![Sexp::keyword("a"), Sexp::int(4)]),
19173 ];
19174 for k in ["a", "b", "c", "", "A"] {
19175 let via_iter: Vec<&[Sexp]> = iter_calls_to(&forms, k).collect();
19176 let via_chain: Vec<&[Sexp]> = forms.iter().filter_map(|f| f.as_call_to(k)).collect();
19177 assert_eq!(
19178 via_iter.len(),
19179 via_chain.len(),
19180 "len drift for keyword {k:?}"
19181 );
19182 for (a, b) in via_iter.iter().zip(via_chain.iter()) {
19183 assert!(
19184 std::ptr::eq(a.as_ptr(), b.as_ptr()),
19185 "ptr drift at keyword {k:?}: iter slice does not borrow the SAME tail as the per-form chain"
19186 );
19187 assert_eq!(a.len(), b.len(), "len drift at keyword {k:?}");
19188 }
19189 }
19190 }
19191
19192 // ── iter_calls_to_any: the typed-decoded slice-side projection ──────
19193 //
19194 // `iter_calls_to_any(forms, decode)` lifts the per-form projection
19195 // `as_call_to_any` onto a `&[Sexp]`, yielding the `(decoded,
19196 // &[Sexp])` pair of every form whose head decodes through `decode`
19197 // — the substrate's typed-decoded filter over a batch of forms,
19198 // closing the (per-form, slice-side) × (keyword, classifier) 2×2
19199 // of soft-dispatch primitives at the slice-side classifier corner.
19200 // The slice-side keyword projection `iter_calls_to` now routes
19201 // through THIS primitive with a constant-keyword decoder, so the
19202 // filter-and-fuse implementation lives at ONE site on the slice
19203 // algebra. Tests pin the slice-side primitive's contract directly
19204 // alongside the (slice-side keyword, slice-side classifier)
19205 // composition law that the keyword projection's re-routing
19206 // establishes.
19207
19208 #[test]
19209 fn iter_calls_to_any_yields_decoded_pair_for_every_matching_form_in_slice() {
19210 // Three forms: two decode through `Op::from_keyword`, one does
19211 // not (the head `"defalert"` is outside the closed set). The
19212 // typed-decoded slice walk yields the `(decoded, args)` pair
19213 // for each matching form in source order, skipping non-decoding
19214 // forms silently — parallel to how `iter_calls_to` yields ONLY
19215 // the args slice for keyword-matching forms.
19216 #[derive(Debug, PartialEq, Eq)]
19217 enum Op {
19218 Defmonitor,
19219 Defpoint,
19220 }
19221 impl Op {
19222 fn from_keyword(h: &str) -> Option<Self> {
19223 match h {
19224 "defmonitor" => Some(Self::Defmonitor),
19225 "defpoint" => Some(Self::Defpoint),
19226 _ => None,
19227 }
19228 }
19229 }
19230 let forms = vec![
19231 Sexp::List(vec![
19232 Sexp::symbol("defmonitor"),
19233 Sexp::keyword("name"),
19234 Sexp::string("a"),
19235 ]),
19236 Sexp::List(vec![
19237 Sexp::symbol("defalert"),
19238 Sexp::keyword("name"),
19239 Sexp::string("p"),
19240 ]),
19241 Sexp::List(vec![
19242 Sexp::symbol("defpoint"),
19243 Sexp::keyword("name"),
19244 Sexp::string("b"),
19245 ]),
19246 ];
19247 let decoded: Vec<(Op, &[Sexp])> = iter_calls_to_any(&forms, Op::from_keyword).collect();
19248 assert_eq!(decoded.len(), 2);
19249 assert_eq!(decoded[0].0, Op::Defmonitor);
19250 assert_eq!(
19251 decoded[0].1,
19252 &[Sexp::keyword("name"), Sexp::string("a")][..]
19253 );
19254 assert_eq!(decoded[1].0, Op::Defpoint);
19255 assert_eq!(
19256 decoded[1].1,
19257 &[Sexp::keyword("name"), Sexp::string("b")][..]
19258 );
19259 }
19260
19261 #[test]
19262 fn iter_calls_to_any_skips_every_shape_per_form_sibling_rejects() {
19263 // Every shape `as_call_to_any` rejects, `iter_calls_to_any`
19264 // skips: non-list shapes, the empty list, non-symbol-head
19265 // lists, AND lists whose head is a symbol the decoder rejects.
19266 // Pin the soft-projection contract at the slice level —
19267 // parallel to `iter_calls_to_skips_every_non_call_shape_silently`
19268 // but with the decoder rejection axis added so the per-form
19269 // sibling's two rejection sources (shape-level + decoder-level)
19270 // both route through the slice-side filter uniformly.
19271 let forms = vec![
19272 Sexp::symbol("foo"),
19273 Sexp::int(5),
19274 Sexp::keyword("k"),
19275 Sexp::string("s"),
19276 Sexp::boolean(true),
19277 Sexp::float(1.5),
19278 Sexp::Nil,
19279 Sexp::Quote(Box::new(Sexp::symbol("foo"))),
19280 Sexp::List(vec![]),
19281 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
19282 Sexp::List(vec![Sexp::keyword("foo"), Sexp::symbol("a")]),
19283 // A call whose head IS a symbol but the decoder rejects —
19284 // this is the decoder-level rejection axis the per-form
19285 // sibling's classifier closure adds beyond the keyword
19286 // sibling's `head == k` axis.
19287 Sexp::List(vec![Sexp::symbol("unknown-head"), Sexp::int(1)]),
19288 ];
19289 let decoded: Vec<(&'static str, &[Sexp])> =
19290 iter_calls_to_any(&forms, |_h: &str| None::<&'static str>).collect();
19291 assert!(
19292 decoded.is_empty(),
19293 "non-call / decoder-rejecting slice must yield zero items, got {} items",
19294 decoded.len()
19295 );
19296 }
19297
19298 #[test]
19299 fn iter_calls_to_any_yields_empty_args_slice_for_singleton_decoded_call() {
19300 // `(defcompiler)` decoded through a classifier that accepts
19301 // the head — the args tail is the empty slice. Pin the
19302 // empty-tail posture: the typed-decoded slice walk must yield
19303 // `(decoded, &[])` for the matching singleton (NOT skip it),
19304 // mirroring the per-form sibling's contract — the
19305 // (possibly-empty) args slice on a decoded match, NOT `None`
19306 // on an empty tail. Parallel to
19307 // `iter_calls_to_yields_empty_args_slice_for_singleton_matching_call`
19308 // for the keyword sibling and
19309 // `as_call_to_any_yields_empty_args_for_singleton_decoded_call`
19310 // for the per-form sibling.
19311 let forms = vec![Sexp::List(vec![Sexp::symbol("defcompiler")])];
19312 let decoded: Vec<(&'static str, &[Sexp])> = iter_calls_to_any(&forms, |h: &str| {
19313 (h == "defcompiler").then_some("defcompiler")
19314 })
19315 .collect();
19316 assert_eq!(decoded.len(), 1);
19317 assert_eq!(decoded[0].0, "defcompiler");
19318 assert_eq!(decoded[0].1, &[][..]);
19319 }
19320
19321 #[test]
19322 fn iter_calls_to_any_yields_nothing_for_empty_slice() {
19323 // An empty forms slice yields zero items regardless of
19324 // decoder. Pin the slice-side primitive's degenerate boundary:
19325 // empty in, empty out — the iterator is fused-empty without
19326 // consulting `as_call_to_any` at all. The decoder's body must
19327 // never run (we assert with an explicitly-panicking closure
19328 // body to prove the fused-empty contract holds before the
19329 // per-form sibling is consulted). Parallel to
19330 // `iter_calls_to_yields_nothing_for_empty_slice` for the
19331 // keyword sibling.
19332 let forms: Vec<Sexp> = vec![];
19333 let mut iter = iter_calls_to_any(&forms, |_h: &str| -> Option<()> {
19334 panic!("decoder must not run on an empty forms slice")
19335 });
19336 assert!(iter.next().is_none());
19337 }
19338
19339 #[test]
19340 fn iter_calls_to_any_args_borrow_is_same_pointer_as_per_form_as_call_to_any_tail() {
19341 // The structural identity binding `iter_calls_to_any` to its
19342 // per-form sibling: each yielded `&[Sexp]` IS the same slice
19343 // `as_call_to_any` would return as the tail component for the
19344 // corresponding form (pinned via `std::ptr::eq` on `as_ptr()`).
19345 // The soft-projection contract is borrow, not clone, AND
19346 // `iter_calls_to_any` inherits the contract verbatim from
19347 // `as_call_to_any`. Parallel to the
19348 // `iter_calls_to_args_borrow_is_same_pointer_as_per_form_as_call_to_tail`
19349 // pin for the keyword sibling and the
19350 // `as_call_to_any_args_borrow_is_same_pointer_as_as_call_tail`
19351 // pin for the per-form sibling.
19352 let forms = vec![Sexp::List(vec![
19353 Sexp::symbol("defmonitor"),
19354 Sexp::keyword("name"),
19355 Sexp::string("a"),
19356 ])];
19357 let (_, via_iter): (&'static str, &[Sexp]) = iter_calls_to_any(&forms, |h: &str| {
19358 (h == "defmonitor").then_some("defmonitor")
19359 })
19360 .next()
19361 .expect("one decoded match");
19362 let (_, via_per_form): (&'static str, &[Sexp]) = forms[0]
19363 .as_call_to_any(|h: &str| (h == "defmonitor").then_some("defmonitor"))
19364 .expect("one decoded match");
19365 assert!(
19366 std::ptr::eq(via_iter.as_ptr(), via_per_form.as_ptr()),
19367 "iter_calls_to_any args must borrow the SAME slice as as_call_to_any's tail"
19368 );
19369 assert_eq!(via_iter.len(), via_per_form.len());
19370 }
19371
19372 #[test]
19373 fn iter_calls_to_any_is_the_slice_side_projection_of_as_call_to_any() {
19374 // The structural identity the lift establishes:
19375 // iter_calls_to_any(forms, decode) ==
19376 // forms.iter().filter_map(|f| f.as_call_to_any(&mut decode))
19377 // Pin shape AND ordering AND pointer-identity across mixed
19378 // inputs and a range of decoders (closed-set classifier,
19379 // always-accept identity, always-reject `None`, partial
19380 // closed-set on a single head) so a regression that drifts
19381 // the slice-side projection from its closed-form definition
19382 // fails loudly. The six soft-projection primitives —
19383 // `head_symbol`, `as_call`, `as_call_to`, `as_call_to_any`,
19384 // `iter_calls_to`, AND `iter_calls_to_any` — must agree on
19385 // operator-position recognition at every shape/slice they
19386 // share. Parallel to
19387 // `iter_calls_to_is_the_slice_side_projection_of_as_call_to`
19388 // for the keyword sibling.
19389 let forms = vec![
19390 Sexp::symbol("foo"),
19391 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
19392 Sexp::Nil,
19393 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(2)]),
19394 Sexp::int(99),
19395 Sexp::List(vec![Sexp::symbol("b"), Sexp::int(3)]),
19396 Sexp::List(vec![Sexp::symbol("a")]),
19397 Sexp::List(vec![]),
19398 Sexp::List(vec![Sexp::keyword("a"), Sexp::int(4)]),
19399 Sexp::List(vec![Sexp::symbol("c"), Sexp::int(5)]),
19400 ];
19401 // Closed-set classifier: accept "a" and "c", reject everything
19402 // else (including the call whose head is "b", to pin the
19403 // decoder-level rejection axis the keyword sibling does not
19404 // have).
19405 let decode_set =
19406 |h: &str| -> Option<&'static str> { matches!(h, "a" | "c").then_some("ac") };
19407 let via_iter: Vec<(&'static str, &[Sexp])> =
19408 iter_calls_to_any(&forms, decode_set).collect();
19409 let via_chain: Vec<(&'static str, &[Sexp])> = forms
19410 .iter()
19411 .filter_map(|f| f.as_call_to_any(decode_set))
19412 .collect();
19413 assert_eq!(
19414 via_iter.len(),
19415 via_chain.len(),
19416 "len drift between slice-side and per-form-chain"
19417 );
19418 for (a, b) in via_iter.iter().zip(via_chain.iter()) {
19419 assert_eq!(a.0, b.0, "decoded drift");
19420 assert!(
19421 std::ptr::eq(a.1.as_ptr(), b.1.as_ptr()),
19422 "ptr drift: slice-side does not borrow the SAME tail as the per-form chain"
19423 );
19424 assert_eq!(a.1.len(), b.1.len(), "len drift");
19425 }
19426 }
19427
19428 #[test]
19429 fn iter_calls_to_routes_through_iter_calls_to_any_via_constant_classifier_composition() {
19430 // The post-lift composition law binding the slice-side
19431 // keyword projection to the slice-side classifier projection:
19432 //
19433 // iter_calls_to(forms, k) ==
19434 // iter_calls_to_any(forms, |h| (h == k).then_some(())).map(|(_, a)| a)
19435 //
19436 // Pin shape AND ordering AND pointer-identity across a mixed
19437 // slice and three representative keywords (matching some,
19438 // matching none, edge-case empty string) so a regression that
19439 // drifts `iter_calls_to`'s body away from the typed-decoded
19440 // routing (e.g. re-inlines the `forms.iter().filter_map(|f|
19441 // f.as_call_to(keyword))` triple directly) fails loudly even
19442 // though the rendered slice-of-slices would still match the
19443 // keyword sibling's output. The pointer-equality axis is
19444 // load-bearing: a regression that re-derives the filter at
19445 // both sites would yield byte-identical slices but with
19446 // distinct closure-capture state, which the
19447 // pointer-identity check rejects only because both routes
19448 // share the SAME underlying form-tail borrow chain.
19449 //
19450 // Sibling-shape lift to prior-run `UnquoteForm::marker` ⊂
19451 // `to_quote_form().prefix()` composition (commit 250c001) and
19452 // `AtomKind::label` ⊂ `sexp_shape().label()` composition
19453 // (commit 1db697f): both pin the invariant that a typed
19454 // subset/keyword projection is structurally derived from its
19455 // parent superset/classifier projection, not a parallel
19456 // implementation the type system happens to not catch when
19457 // the two drift.
19458 let forms = vec![
19459 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
19460 Sexp::List(vec![Sexp::symbol("b"), Sexp::int(2)]),
19461 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(3)]),
19462 Sexp::List(vec![Sexp::symbol("c"), Sexp::int(4)]),
19463 Sexp::int(99),
19464 ];
19465 for k in ["a", "missing", ""] {
19466 let via_keyword: Vec<&[Sexp]> = iter_calls_to(&forms, k).collect();
19467 let via_classifier: Vec<&[Sexp]> =
19468 iter_calls_to_any(&forms, |h: &str| (h == k).then_some(()))
19469 .map(|(_, a)| a)
19470 .collect();
19471 assert_eq!(
19472 via_keyword.len(),
19473 via_classifier.len(),
19474 "len drift between keyword projection and classifier composition for k={k:?}"
19475 );
19476 for (a, b) in via_keyword.iter().zip(via_classifier.iter()) {
19477 assert!(
19478 std::ptr::eq(a.as_ptr(), b.as_ptr()),
19479 "ptr drift at k={k:?}: keyword projection does not share the SAME borrow with the classifier composition"
19480 );
19481 assert_eq!(a.len(), b.len(), "len drift at k={k:?}");
19482 }
19483 }
19484 }
19485
19486 #[test]
19487 fn iter_calls_to_any_admits_fnmut_classifier_maintaining_state_across_batch_walk() {
19488 // The slice-side primitive's `FnMut` constraint (vs the
19489 // per-form sibling's `FnOnce`) admits a classifier that
19490 // captures mutable state — a counter, a registry cache, a
19491 // visited-set. Pin the mutable-state contract: a counter
19492 // closure increments once per matching form (NOT once per
19493 // call to `f.as_call_to_any(decode)` at every form, since
19494 // `as_call_to_any` short-circuits before running `decode` on
19495 // non-list / empty-list / non-symbol-head shapes — only forms
19496 // that pass the shape gate reach the decoder). The counter's
19497 // post-walk value pins the exact number of forms that
19498 // (a) passed the shape gate AND (b) had a head matching the
19499 // classifier's predicate.
19500 let forms = vec![
19501 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
19502 Sexp::int(99), // not a call — `as_call_to_any` short-circuits, decoder never runs
19503 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(2)]),
19504 Sexp::List(vec![Sexp::symbol("b"), Sexp::int(3)]),
19505 Sexp::List(vec![]), // empty list — `as_call_to_any` short-circuits before decoder
19506 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(4)]),
19507 ];
19508 let mut decoder_calls = 0usize;
19509 // Consume the iterator into a count (NOT a Vec) so the closure
19510 // capture of `decoder_calls` is dropped at the iterator's end,
19511 // releasing the mutable borrow before the post-walk assertions
19512 // re-read `decoder_calls` immutably. A `Vec<((), &[Sexp])>`
19513 // collection would inherit the closure's `'a` lifetime through
19514 // the `iter_calls_to_any` return type's unified lifetime
19515 // parameter and keep the mutable borrow live across the assert
19516 // (the rust-borrow-checker contract — `decoded`'s lifetime
19517 // ties to `min(forms, closure)` even though the items
19518 // themselves only borrow from `forms`).
19519 let decoded_count = iter_calls_to_any(&forms, |h: &str| {
19520 decoder_calls += 1;
19521 (h == "a").then_some(())
19522 })
19523 .count();
19524 // Three forms have head "a"; one form has head "b"; the
19525 // non-call shapes (Int + empty list) short-circuit before the
19526 // decoder runs. Decoder is called 4 times (the 4 shape-gate-
19527 // passing forms); yields 3 matches.
19528 assert_eq!(
19529 decoder_calls, 4,
19530 "decoder must run once per shape-gate-passing form"
19531 );
19532 assert_eq!(
19533 decoded_count, 3,
19534 "three forms decode through the classifier"
19535 );
19536 }
19537
19538 // ── iter_named_calls_to_any / iter_named_calls_to: slice-side closure
19539 // of the (slice × classifier × named) and (slice × constant × named)
19540 // corners of the soft-dispatch cube. Pre-lift the named gate
19541 // composition (`split_name_slot` over a classifier-decoded args
19542 // tail) lived ONLY inside `Expander::expand_and_collect_named_calls_to_any`'s
19543 // projection closure — the slice algebra had no named sibling to
19544 // the bare [`iter_calls_to_any`]. Post-lift the gate is composed
19545 // at the slice level and the Expander surface routes through it
19546 // via the SAME `expand_program + iter + map + collect` pipeline
19547 // the bare expander surface uses. The tests below pin the slice
19548 // primitive's contract DIRECTLY — independent of the Expander
19549 // surface — so a classifier-NAME consumer that already holds
19550 // expanded forms (a `tatara-check` runner, an LSP buffer walker,
19551 // a REPL exhaustive lister) sees the SAME `NamedFormMissingName`
19552 // / `NamedFormNonSymbolName` rejection chain the Expander
19553 // consumer sees through the surface method.
19554
19555 #[test]
19556 fn iter_named_calls_to_any_yields_decoded_triple_for_every_matching_named_form_in_slice() {
19557 // Closed-set classifier (`Kind::{Foo, Bar}`) that rejects one head
19558 // out of three on a slice. Every matching form yields a
19559 // `Result<(Kind, &str, &[Sexp])>` triple in source order; the
19560 // unmatched form is skipped silently (NOT yielded as `Err`).
19561 // Fail-before-pass-after: this assert requires the slice
19562 // primitive to exist AND to yield the typed witness ALONGSIDE
19563 // the borrowed NAME slot AND the borrowed spec args tail — pre-
19564 // lift the slice algebra had no named sibling; consumers had
19565 // to re-derive the four-step `iter_calls_to_any(forms,
19566 // decode).map(|(d, args)| split_name_slot(args, k).map(|(n,
19567 // r)| (d, n, r)))` composition at their call site.
19568 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
19569 enum Kind {
19570 Foo,
19571 Bar,
19572 }
19573 let forms = crate::reader::read(
19574 "(deffoo alpha 1) (defbaz gamma 2) (defbar beta 3) (deffoo delta 4)",
19575 )
19576 .unwrap();
19577 let yielded: Vec<(Kind, String, usize)> =
19578 super::iter_named_calls_to_any(&forms, |h: &str| match h {
19579 "deffoo" => Some((Kind::Foo, "deffoo")),
19580 "defbar" => Some((Kind::Bar, "defbar")),
19581 _ => None,
19582 })
19583 .map(|maybe_triple| {
19584 maybe_triple.map(|(kind, name, args)| (kind, name.to_string(), args.len()))
19585 })
19586 .collect::<crate::error::Result<Vec<_>>>()
19587 .expect("slice-side named-classifier walk must succeed on well-formed forms");
19588 assert_eq!(
19589 yielded,
19590 vec![
19591 (Kind::Foo, "alpha".to_string(), 1),
19592 (Kind::Bar, "beta".into(), 1),
19593 (Kind::Foo, "delta".into(), 1),
19594 ],
19595 "iter_named_calls_to_any must yield (decoded, NAME, args_len) in source order, skipping defbaz",
19596 );
19597 }
19598
19599 #[test]
19600 fn iter_named_calls_to_any_skips_every_non_matching_form_shape_silently() {
19601 // Soft-projection contract: the slice primitive must skip every
19602 // shape the classifier rejects — non-list atoms, empty lists,
19603 // lists with non-symbol heads, lists with unrecognized symbol
19604 // heads — WITHOUT emitting the `NamedFormMissingName` /
19605 // `NamedFormNonSymbolName` variants. The named gate fires ONLY
19606 // for matched-keyword forms whose NAME slot is malformed, NEVER
19607 // for forms the classifier filtered out first.
19608 let forms = crate::reader::read(r#":kw "str" 42 () (unrecognized x) (5 y)"#).unwrap();
19609 let yielded: Vec<()> = super::iter_named_calls_to_any(&forms, |h: &str| {
19610 (h == "deffoo").then_some(((), "deffoo"))
19611 })
19612 .map(|maybe_triple| maybe_triple.map(|_| ()))
19613 .collect::<crate::error::Result<Vec<_>>>()
19614 .expect("slice-side named-classifier walk must succeed when zero forms match");
19615 assert!(
19616 yielded.is_empty(),
19617 "slice-side named-classifier walk must yield empty Vec when zero forms match",
19618 );
19619 }
19620
19621 #[test]
19622 fn iter_named_calls_to_any_emits_named_form_missing_name_for_matched_form_with_no_name_slot() {
19623 // `(deffoo)` — head matches the classifier (yielding the typed
19624 // witness AND the classifier-supplied static keyword), but the
19625 // NAME slot is missing. `split_name_slot`'s arity gate fires
19626 // inside the slice primitive and emits `NamedFormMissingName {
19627 // keyword: "deffoo" }`. Pin that the keyword threaded through
19628 // is the CLASSIFIER-supplied keyword (NOT a hardcoded fallback,
19629 // NOT the form's head symbol) — a regression that drifted the
19630 // keyword binding from `decode`'s tuple's second element to the
19631 // head symbol or to a constant would fail loudly here.
19632 let forms = crate::reader::read("(deffoo)").unwrap();
19633 let mut iter = super::iter_named_calls_to_any(&forms, |h: &str| {
19634 (h == "deffoo").then_some(((), "deffoo"))
19635 });
19636 let first = iter.next().expect("matched form must yield an item");
19637 let err = first.expect_err("matched form with missing NAME must yield Err");
19638 assert!(
19639 matches!(
19640 err,
19641 crate::error::LispError::NamedFormMissingName { keyword: "deffoo" }
19642 ),
19643 "expected NamedFormMissingName {{ keyword: \"deffoo\" }} through slice primitive, got: {err:?}"
19644 );
19645 }
19646
19647 #[test]
19648 fn iter_named_calls_to_any_emits_named_form_non_symbol_name_for_matched_form_with_int_name() {
19649 // `(deffoo 42)` — head matches and the NAME-slot arity gate
19650 // passes, but the NAME slot's shape gate rejects the int
19651 // literal. Pin that BOTH the classifier-supplied keyword AND
19652 // the typed `SexpShape::Int` projection flow into the
19653 // structural variant, identically to how
19654 // `Expander::expand_and_collect_named_calls_to_any` emits the
19655 // same variant when its projection composes the same gate.
19656 let forms = crate::reader::read("(deffoo 42)").unwrap();
19657 let mut iter = super::iter_named_calls_to_any(&forms, |h: &str| {
19658 (h == "deffoo").then_some(((), "deffoo"))
19659 });
19660 let first = iter.next().expect("matched form must yield an item");
19661 let err = first.expect_err("matched form with non-symbol NAME must yield Err");
19662 assert!(
19663 matches!(
19664 err,
19665 crate::error::LispError::NamedFormNonSymbolName {
19666 keyword: "deffoo",
19667 got: crate::error::SexpShape::Int,
19668 }
19669 ),
19670 "expected NamedFormNonSymbolName {{ keyword: \"deffoo\", got: Int }} through slice primitive, got: {err:?}"
19671 );
19672 }
19673
19674 #[test]
19675 fn iter_named_calls_to_any_emits_named_form_non_symbol_name_for_matched_form_with_keyword_name()
19676 {
19677 // `(deffoo :name)` — sibling shape pin to the int case: a
19678 // matched form whose NAME slot is a keyword. Together with the
19679 // int case this closes path-uniformity across distinct
19680 // non-symbol-or-string `SexpShape` cells at the slice primitive
19681 // boundary — every consumer routes through the SAME gate
19682 // composition regardless of the offending shape.
19683 let forms = crate::reader::read("(deffoo :name)").unwrap();
19684 let mut iter = super::iter_named_calls_to_any(&forms, |h: &str| {
19685 (h == "deffoo").then_some(((), "deffoo"))
19686 });
19687 let first = iter.next().expect("matched form must yield an item");
19688 let err = first.expect_err("matched form with keyword NAME must yield Err");
19689 assert!(
19690 matches!(
19691 err,
19692 crate::error::LispError::NamedFormNonSymbolName {
19693 keyword: "deffoo",
19694 got: crate::error::SexpShape::Keyword,
19695 }
19696 ),
19697 "expected NamedFormNonSymbolName {{ keyword: \"deffoo\", got: Keyword }} through slice primitive, got: {err:?}"
19698 );
19699 }
19700
19701 #[test]
19702 fn iter_named_calls_to_any_accepts_string_name_slot_routing_past_the_gate() {
19703 // `(deffoo "quoted-name" :k v)` — NAME slot is a string
19704 // literal, which `as_symbol_or_string` (inside `split_name_slot`)
19705 // accepts alongside symbols. Pin that the slice primitive
19706 // erases the quote-vs-symbol distinction at the boundary so a
19707 // consumer sees ONE `&str` shape regardless of authoring
19708 // choice, matching the equivalent gate in the typed-domain
19709 // consumer downstream of `named_form_projection<T>`.
19710 let forms = crate::reader::read(r#"(deffoo "quoted-name" :k "v")"#).unwrap();
19711 let yielded: Vec<(String, usize)> = super::iter_named_calls_to_any(&forms, |h: &str| {
19712 (h == "deffoo").then_some(((), "deffoo"))
19713 })
19714 .map(|maybe_triple| maybe_triple.map(|(_, name, args)| (name.to_string(), args.len())))
19715 .collect::<crate::error::Result<Vec<_>>>()
19716 .expect("string-author NAME slot must route past gate");
19717 assert_eq!(yielded, vec![("quoted-name".into(), 2)]);
19718 }
19719
19720 #[test]
19721 fn iter_named_calls_to_any_short_circuits_on_first_malformed_name_under_collect() {
19722 // `(deffoo good 1) (deffoo) (deffoo also-good 2)` — three
19723 // matched forms; the SECOND has no NAME slot. Pin that
19724 // `.collect::<Result<Vec<_>, _>>()` short-circuits at the
19725 // second form (yielding `Err`) WITHOUT yielding the third
19726 // form's payload. The iterator's lazy iteration combined with
19727 // `Result::collect`'s short-circuit gives consumers
19728 // first-failure semantics at the slice boundary, identical to
19729 // how `Expander::expand_and_collect_named_calls_to_any` already
19730 // short-circuits.
19731 let forms = crate::reader::read("(deffoo good 1) (deffoo) (deffoo also-good 2)").unwrap();
19732 let collected: crate::error::Result<Vec<()>> =
19733 super::iter_named_calls_to_any(&forms, |h: &str| {
19734 (h == "deffoo").then_some(((), "deffoo"))
19735 })
19736 .map(|maybe_triple| maybe_triple.map(|_| ()))
19737 .collect();
19738 let err = collected.expect_err("collect must surface the first failure");
19739 assert!(
19740 matches!(
19741 err,
19742 crate::error::LispError::NamedFormMissingName { keyword: "deffoo" }
19743 ),
19744 "expected NamedFormMissingName at the first malformed NAME, got: {err:?}"
19745 );
19746 }
19747
19748 #[test]
19749 fn iter_named_calls_to_yields_name_and_spec_args_for_every_matching_form_in_slice() {
19750 // Constant-keyword sibling of `iter_named_calls_to_any` —
19751 // discards the `()` typed witness and yields `Result<(&str,
19752 // &[Sexp])>` per matching form. Pin that the constant-keyword
19753 // primitive yields the SAME source-ordered set of triples the
19754 // typed-decoded sibling does on the same source, modulo the
19755 // discarded typed witness.
19756 let forms =
19757 crate::reader::read("(defcheck alpha 1) (other beta) (defcheck gamma 2 3)").unwrap();
19758 let yielded: Vec<(String, usize)> = super::iter_named_calls_to(&forms, "defcheck")
19759 .map(|maybe_pair| maybe_pair.map(|(name, args)| (name.to_string(), args.len())))
19760 .collect::<crate::error::Result<Vec<_>>>()
19761 .expect("constant-keyword named slice walk must succeed on well-formed forms");
19762 assert_eq!(
19763 yielded,
19764 vec![("alpha".into(), 1), ("gamma".into(), 2)],
19765 "iter_named_calls_to must yield (NAME, args_len) in source order, skipping unrelated forms",
19766 );
19767 }
19768
19769 #[test]
19770 fn iter_named_calls_to_routes_through_iter_named_calls_to_any_via_constant_classifier_composition(
19771 ) {
19772 // Pin the closed-form composition law binding the constant-
19773 // keyword named cell to the typed-decoded named-classifier cell
19774 // at the slice algebra boundary:
19775 //
19776 // iter_named_calls_to(forms, k) ==
19777 // iter_named_calls_to_any(forms, |h| (h == k).then_some(((), k)))
19778 // .map(|maybe| maybe.map(|(_, n, a)| (n, a)))
19779 //
19780 // This makes the typed-decoded named-classifier slice primitive
19781 // the CANONICAL composition point the constant-keyword sibling
19782 // routes through — parallel to how `iter_calls_to` /
19783 // `iter_calls_to_any` bind their composition law on the bare-
19784 // kwargs axis at the slice level. A regression that drifts ONE
19785 // sibling's pipeline from the other becomes loudly visible at
19786 // this assertion.
19787 let forms =
19788 crate::reader::read("(defcheck alpha 1) (other beta) (defcheck gamma 2 3)").unwrap();
19789 let via_constant: Vec<(String, usize)> = super::iter_named_calls_to(&forms, "defcheck")
19790 .map(|maybe| maybe.map(|(name, args)| (name.to_string(), args.len())))
19791 .collect::<crate::error::Result<Vec<_>>>()
19792 .expect("constant-keyword named slice walk must succeed");
19793 let via_classifier: Vec<(String, usize)> =
19794 super::iter_named_calls_to_any(&forms, |h: &str| {
19795 (h == "defcheck").then_some(((), "defcheck"))
19796 })
19797 .map(|maybe| maybe.map(|(_, name, args)| (name.to_string(), args.len())))
19798 .collect::<crate::error::Result<Vec<_>>>()
19799 .expect("typed-decoded named slice walk with constant-classifier decoder must succeed");
19800 assert_eq!(
19801 via_constant, via_classifier,
19802 "iter_named_calls_to(forms, k) must yield byte-identical payload to iter_named_calls_to_any(forms, |h| (h == k).then_some(((), k))).map(strip)",
19803 );
19804 }
19805
19806 #[test]
19807 fn iter_named_calls_to_threads_static_keyword_through_missing_variant() {
19808 // Path-uniformity at the constant-keyword slice primitive
19809 // boundary: a static `&'static str` keyword threaded into the
19810 // primitive routes verbatim through the
19811 // `NamedFormMissingName.keyword` slot when a matched form has
19812 // no NAME — same threading discipline `split_name_slot` pins at
19813 // its boundary. Pin three distinct keywords ALL round-trip
19814 // through the variant's keyword slot.
19815 for keyword in ["defmonitor", "defalertpolicy", "defcheck"] {
19816 let src = format!("({keyword})");
19817 let forms = crate::reader::read(&src).unwrap();
19818 let mut iter = super::iter_named_calls_to(&forms, keyword);
19819 let first = iter.next().expect("matched form must yield an item");
19820 let err = first.expect_err("matched form with missing NAME must yield Err");
19821 match err {
19822 crate::error::LispError::NamedFormMissingName { keyword: got } => {
19823 assert_eq!(
19824 got, keyword,
19825 "constant-keyword slice primitive must thread keyword verbatim"
19826 );
19827 }
19828 other => {
19829 panic!("expected NamedFormMissingName for keyword {keyword:?}, got: {other:?}")
19830 }
19831 }
19832 }
19833 }
19834
19835 #[test]
19836 fn iter_named_calls_to_any_admits_fnmut_classifier_maintaining_state_across_batch_walk() {
19837 // The slice-side typed-decoded named primitive's `FnMut`
19838 // classifier constraint admits a closure that captures mutable
19839 // state across the batch walk — counter, registry cache,
19840 // visited-set — matching the bare-kwargs slice sibling's
19841 // contract. Pin: a counter-bumping decoder increments once per
19842 // shape-gate-passing form (NOT once per slice element, since
19843 // `iter_calls_to_any` short-circuits before the decoder on
19844 // non-list / empty-list / non-symbol-head shapes), and the
19845 // post-walk counter equals the number of forms that reached
19846 // the decoder.
19847 let forms =
19848 crate::reader::read("(deffoo a 1) 42 (deffoo b 2) () (defbar c 3) (deffoo d 4)")
19849 .unwrap();
19850 let mut decoder_calls = 0usize;
19851 let yielded: Vec<String> = super::iter_named_calls_to_any(&forms, |h: &str| {
19852 decoder_calls += 1;
19853 (h == "deffoo").then_some(((), "deffoo"))
19854 })
19855 .map(|maybe| maybe.map(|(_, name, _)| name.to_string()))
19856 .collect::<crate::error::Result<Vec<_>>>()
19857 .expect("FnMut classifier dispatch must succeed on well-formed NAME slots");
19858 // Four (defX …) call forms in the slice pass the shape gate;
19859 // the int atom and empty list short-circuit before the
19860 // decoder. Three of the four pass-through-decoder forms
19861 // dispatch to deffoo; one dispatches to defbar (rejected by
19862 // the decoder).
19863 assert_eq!(
19864 decoder_calls, 4,
19865 "FnMut decoder must run once per shape-gate-passing form (4 call forms)"
19866 );
19867 assert_eq!(
19868 yielded,
19869 vec!["a".to_string(), "b".into(), "d".into()],
19870 "three (deffoo …) forms match; one (defbar …) form is rejected by the decoder",
19871 );
19872 }
19873
19874 #[test]
19875 fn iter_named_calls_to_any_yields_borrowed_name_and_args_with_form_lifetime() {
19876 // Pin the borrow-lifetime contract at the slice primitive
19877 // boundary: the yielded `&'a str` NAME slot and `&'a [Sexp]`
19878 // spec args tail must borrow from the input slice verbatim —
19879 // no copy, no allocation. A consumer that holds the iterator's
19880 // yields alongside the input slice borrow can use the NAME as
19881 // a lookup key against a registry without paying for a clone.
19882 let forms = crate::reader::read("(deffoo my-name :k 1 :j 2)").unwrap();
19883 let mut iter = super::iter_named_calls_to_any(&forms, |h: &str| {
19884 (h == "deffoo").then_some(((), "deffoo"))
19885 });
19886 let (_, name, spec_args) = iter
19887 .next()
19888 .expect("matched form must yield an item")
19889 .expect("well-formed NAME slot must split");
19890 // Identity-check the NAME borrow: it must point at the same
19891 // bytes the form's NAME slot symbol borrows from.
19892 let form_list = forms[0].as_list().expect("form must be a list");
19893 let form_name = form_list[1]
19894 .as_symbol()
19895 .expect("form NAME must be a symbol");
19896 assert!(
19897 std::ptr::eq(name.as_ptr(), form_name.as_ptr()),
19898 "iter_named_calls_to_any must yield the borrowed NAME, NOT an allocated copy"
19899 );
19900 // Spec args tail must borrow from the form's tail starting at
19901 // index 2 (after the NAME slot at index 1).
19902 assert!(
19903 std::ptr::eq(spec_args.as_ptr(), &form_list[2] as *const Sexp),
19904 "iter_named_calls_to_any must yield the borrowed spec args tail, NOT an allocated copy"
19905 );
19906 assert_eq!(spec_args.len(), 4);
19907 }
19908
19909 // ── as_named_call_to_any / as_named_call_to: per-form × named cell ──
19910 //
19911 // The per-form × named corner of the soft-dispatch cube the slice
19912 // primitive `iter_named_calls_to_any`'s docstring table identified as
19913 // the documented gap pre-lift ("(composed inline at each named
19914 // consumer)"). Post-lift the per-form × named row binds to ONE
19915 // primitive every per-form named consumer composes through, and the
19916 // slice-side `iter_named_calls_to_any` routes through it via the SAME
19917 // `forms.iter().filter_map(_)` skeleton `iter_calls_to_any` uses to
19918 // route through `as_call_to_any`. These tests pin: (a) the three-arm
19919 // result shape (None for non-match, Some(Ok) for matched-and-
19920 // well-formed, Some(Err) for matched-but-malformed-NAME) across each
19921 // distinct shape, (b) the constant-keyword sibling routes through
19922 // the typed-decoded sibling via constant-classifier composition, (c)
19923 // the slice-side `iter_named_calls_to_any` IS the
19924 // `forms.iter().filter_map(|f| f.as_named_call_to_any(_))` projection
19925 // — the structural identity binding the per-form to the slice row.
19926
19927 #[test]
19928 fn as_named_call_to_any_returns_decoded_triple_for_matched_well_formed_form() {
19929 // `(deffoo my-name :k 1)` — head matches the classifier's `deffoo`
19930 // arm, NAME slot is the symbol `my-name`, spec args tail is the
19931 // two-element `:k 1` pair. Pin Some(Ok((decoded, name, args))).
19932 // Fail-before-pass-after: this assert requires the per-form
19933 // method to exist AND to thread the typed witness + borrowed
19934 // NAME + borrowed spec args through ONE projection.
19935 #[derive(Debug, PartialEq, Eq)]
19936 enum Kind {
19937 Foo,
19938 }
19939 let form = crate::reader::read("(deffoo my-name :k 1)").unwrap()[0].clone();
19940 let res = form
19941 .as_named_call_to_any(|h: &str| match h {
19942 "deffoo" => Some((Kind::Foo, "deffoo")),
19943 _ => None,
19944 })
19945 .expect("matched head must yield Some(_)")
19946 .expect("well-formed NAME slot must split");
19947 assert_eq!(res.0, Kind::Foo);
19948 assert_eq!(res.1, "my-name");
19949 assert_eq!(res.2.len(), 2);
19950 }
19951
19952 #[test]
19953 fn as_named_call_to_any_returns_none_when_decoder_rejects_head() {
19954 // `(unrelated my-name :k 1)` — head is a symbol, but the
19955 // classifier returns `None`. Pin: the classifier filter face is
19956 // identical to `as_call_to_any` — `None` short-circuits BEFORE
19957 // the named gate runs, so a non-matching head with a malformed
19958 // NAME slot still yields `None`, NOT `Some(Err)`. The soft-
19959 // filter face is preserved across the cube row.
19960 let form = crate::reader::read("(unrelated my-name :k 1)").unwrap()[0].clone();
19961 assert!(form
19962 .as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
19963 .is_none());
19964 }
19965
19966 #[test]
19967 fn as_named_call_to_any_returns_none_for_non_call_shapes() {
19968 // Every shape `as_call_to_any` rejects, `as_named_call_to_any`
19969 // rejects identically — atom, keyword, empty list, list with
19970 // non-symbol head. The classifier-filter face is uniformly the
19971 // soft per-form posture of every other `as_*` method on `Sexp`.
19972 let shapes: Vec<Sexp> = vec![
19973 Sexp::int(5),
19974 Sexp::keyword("deffoo"),
19975 Sexp::Nil,
19976 Sexp::List(vec![]),
19977 Sexp::List(vec![Sexp::int(1), Sexp::symbol("my-name")]),
19978 ];
19979 for s in shapes {
19980 assert!(
19981 s.as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
19982 .is_none(),
19983 "non-call shape must yield None for as_named_call_to_any: {s}"
19984 );
19985 }
19986 }
19987
19988 #[test]
19989 fn as_named_call_to_any_returns_some_err_for_matched_head_with_no_name_slot() {
19990 // `(deffoo)` — head matches the classifier's `deffoo` arm but
19991 // the form is a singleton: NO NAME slot at all. The named gate
19992 // (`split_name_slot`'s arity gate) fires structurally, yielding
19993 // `Some(Err(LispError::NamedFormMissingName { keyword: "deffoo" }))`.
19994 // Pin the strict-gate face on the named row: matched-and-
19995 // malformed yields the typed structural rejection variant, NOT
19996 // `None` (which would conflate "not our head" with "our head
19997 // but missing NAME" and break the cube's strict-vs-soft split).
19998 let form = crate::reader::read("(deffoo)").unwrap()[0].clone();
19999 let err = form
20000 .as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
20001 .expect("matched head must yield Some(_)")
20002 .expect_err("missing NAME slot must yield Err");
20003 assert!(
20004 matches!(
20005 err,
20006 crate::error::LispError::NamedFormMissingName { keyword: "deffoo" }
20007 ),
20008 "expected NamedFormMissingName through per-form primitive, got: {err:?}"
20009 );
20010 }
20011
20012 #[test]
20013 fn as_named_call_to_any_returns_some_err_for_matched_head_with_non_symbol_name() {
20014 // `(deffoo 5 :k 1)` — head matches but NAME slot is an int
20015 // literal. The named gate's `as_symbol_or_string` shape gate
20016 // fires, yielding `Some(Err(LispError::NamedFormNonSymbolName
20017 // { keyword: "deffoo", got: SexpShape::Int }))`. Pin the strict-
20018 // gate face for the second structural rejection variant of the
20019 // named gate AND the typed `SexpShape` projection of the
20020 // offending slot.
20021 let form = crate::reader::read("(deffoo 5 :k 1)").unwrap()[0].clone();
20022 let err = form
20023 .as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
20024 .expect("matched head must yield Some(_)")
20025 .expect_err("non-symbol NAME slot must yield Err");
20026 assert!(
20027 matches!(
20028 err,
20029 crate::error::LispError::NamedFormNonSymbolName {
20030 keyword: "deffoo",
20031 got: crate::error::SexpShape::Int,
20032 }
20033 ),
20034 "expected NamedFormNonSymbolName through per-form primitive, got: {err:?}"
20035 );
20036 }
20037
20038 #[test]
20039 fn as_named_call_to_constant_keyword_routes_through_as_named_call_to_any() {
20040 // Pin the closed-form composition binding the constant-keyword
20041 // sibling to the typed-decoded sibling:
20042 // as_named_call_to(k) ==
20043 // as_named_call_to_any(|h| (h == k).then_some(((), k)))
20044 // .map(|res| res.map(|(_, name, rest)| (name, rest)))
20045 // across every shape in the test fixture set. A regression
20046 // that re-implements the constant-keyword sibling without
20047 // routing through the classifier sibling fails this assertion
20048 // for the matched-and-well-formed AND matched-but-malformed
20049 // AND non-match arms simultaneously.
20050 let shapes: Vec<Sexp> = vec![
20051 crate::reader::read("(defcompiler my-comp :a 1)").unwrap()[0].clone(),
20052 crate::reader::read("(defcompiler)").unwrap()[0].clone(),
20053 crate::reader::read("(defcompiler 5)").unwrap()[0].clone(),
20054 crate::reader::read("(unrelated my-name :k 1)").unwrap()[0].clone(),
20055 Sexp::int(99),
20056 Sexp::List(vec![]),
20057 ];
20058 // `LispError` is not `PartialEq` (it transitively wraps `Sexp`,
20059 // which carries an `Atom::Float` whose `f64` is not `Eq`).
20060 // Compare via formatted-debug strings on the Err arm; Ok arms and
20061 // None arm compare structurally. The closed-form composition
20062 // `as_named_call_to(k) == as_named_call_to_any+unit-decoder` is
20063 // pinned across all three arms.
20064 for s in &shapes {
20065 let via_constant = s.as_named_call_to("defcompiler").map(|res| {
20066 res.map(|(name, rest)| (name.to_string(), rest.len()))
20067 .map_err(|e| format!("{e:?}"))
20068 });
20069 let via_classifier = s
20070 .as_named_call_to_any(|h: &str| (h == "defcompiler").then_some(((), "defcompiler")))
20071 .map(|res| {
20072 res.map(|(_, name, rest)| (name.to_string(), rest.len()))
20073 .map_err(|e| format!("{e:?}"))
20074 });
20075 assert_eq!(
20076 via_constant, via_classifier,
20077 "as_named_call_to(k) must equal as_named_call_to_any+unit-decoder for {s}"
20078 );
20079 }
20080 }
20081
20082 #[test]
20083 fn iter_named_calls_to_any_is_the_slice_side_filter_map_of_as_named_call_to_any() {
20084 // Pin the structural identity binding the slice algebra to the
20085 // per-form algebra:
20086 // iter_named_calls_to_any(forms, decode) ==
20087 // forms.iter().filter_map(|f| f.as_named_call_to_any(&mut decode))
20088 // Both sides must yield the SAME Result shape per element in
20089 // source order — `Ok(triple)` for matched-and-well-formed,
20090 // `Err(LispError)` for matched-but-malformed, with non-matches
20091 // skipped by the filter_map. Sibling pin to
20092 // `iter_calls_to_any_is_the_slice_side_projection_of_as_call_to_any`
20093 // on the bare-kwargs row — both rows now share ONE
20094 // `forms.iter().filter_map(_)` skeleton.
20095 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
20096 enum Kind {
20097 Foo,
20098 Bar,
20099 }
20100 let src = "(deffoo a :k 1)
20101 (other thing)
20102 (defbar 7 :j 2)
20103 (deffoo b)
20104 (defbaz c :m 3)";
20105 let forms = crate::reader::read(src).unwrap();
20106 let decode = |h: &str| match h {
20107 "deffoo" => Some((Kind::Foo, "deffoo")),
20108 "defbar" => Some((Kind::Bar, "defbar")),
20109 _ => None,
20110 };
20111 let via_iter: Vec<crate::error::Result<(Kind, String, usize)>> =
20112 super::iter_named_calls_to_any(&forms, decode)
20113 .map(|res| res.map(|(k, name, args)| (k, name.to_string(), args.len())))
20114 .collect();
20115 let via_filter_map: Vec<crate::error::Result<(Kind, String, usize)>> = forms
20116 .iter()
20117 .filter_map(|f| f.as_named_call_to_any(decode))
20118 .map(|res| res.map(|(k, name, args)| (k, name.to_string(), args.len())))
20119 .collect();
20120 assert_eq!(
20121 via_iter.len(),
20122 via_filter_map.len(),
20123 "slice-side iter must yield the same number of items as the per-form filter_map",
20124 );
20125 for (a, b) in via_iter.iter().zip(via_filter_map.iter()) {
20126 match (a, b) {
20127 (Ok(ta), Ok(tb)) => assert_eq!(ta, tb),
20128 (Err(ea), Err(eb)) => assert_eq!(format!("{ea:?}"), format!("{eb:?}")),
20129 _ => panic!(
20130 "variant drift between slice iter and per-form filter_map: {a:?} vs {b:?}"
20131 ),
20132 }
20133 }
20134 // Concretely: 3 matched forms (deffoo a, defbar 7, deffoo b);
20135 // `defbar 7` yields Err (int NAME), other two yield Ok.
20136 assert_eq!(via_iter.len(), 3);
20137 assert!(via_iter[0].is_ok());
20138 assert!(via_iter[1].is_err());
20139 assert!(via_iter[2].is_ok());
20140 }
20141
20142 #[test]
20143 fn as_named_call_to_any_borrows_name_and_spec_args_from_form_verbatim() {
20144 // Pin the borrow-lifetime contract at the per-form primitive
20145 // boundary: the yielded `&str` NAME slot and `&[Sexp]` spec
20146 // args tail must borrow from the underlying form verbatim — no
20147 // copy, no allocation. Sibling pin to
20148 // `iter_named_calls_to_any_yields_borrowed_name_and_args_with_form_lifetime`
20149 // on the slice algebra — both rows preserve the borrow
20150 // contract.
20151 let forms = crate::reader::read("(deffoo my-name :k 1 :j 2)").unwrap();
20152 let form = &forms[0];
20153 let (_, name, spec_args) = form
20154 .as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
20155 .expect("matched head must yield Some(_)")
20156 .expect("well-formed NAME slot must split");
20157 let form_list = form.as_list().expect("form must be a list");
20158 let form_name = form_list[1]
20159 .as_symbol()
20160 .expect("form NAME must be a symbol");
20161 assert!(
20162 std::ptr::eq(name.as_ptr(), form_name.as_ptr()),
20163 "as_named_call_to_any must yield the borrowed NAME, NOT an allocated copy"
20164 );
20165 assert!(
20166 std::ptr::eq(spec_args.as_ptr(), &form_list[2] as *const Sexp),
20167 "as_named_call_to_any must yield the borrowed spec args tail, NOT an allocated copy"
20168 );
20169 assert_eq!(spec_args.len(), 4);
20170 }
20171
20172 // ── as_unquote: the unquote-family projection ───────────────────────
20173 //
20174 // `as_unquote` lifts the per-callsite `Sexp::Unquote(inner) /
20175 // Sexp::UnquoteSplice(inner)` arms paired with their `UnquoteForm::
20176 // Unquote / UnquoteForm::Splice` literals — three sites pre-lift
20177 // (`compile_node` 2 arms + `substitute` top-level + `substitute`
20178 // list-inner) — into ONE typed projection on the `Sexp` algebra.
20179 // These tests pin its contract; the existing path tests in
20180 // macro_expand.rs are the path-uniformity guards proving the three
20181 // sites route through it without behavior drift.
20182
20183 #[test]
20184 fn as_unquote_decomposes_unquote_into_typed_marker_and_inner() {
20185 // `,x` — Sexp::Unquote wrapping a symbol. Pin Some((Unquote, &inner)).
20186 let inner = Sexp::symbol("x");
20187 let form = Sexp::Unquote(Box::new(inner.clone()));
20188 let (marker, body) = form
20189 .as_unquote()
20190 .expect("`,x` must project to Some((Unquote, _))");
20191 assert_eq!(marker, UnquoteForm::Unquote);
20192 assert_eq!(body, &inner);
20193 }
20194
20195 #[test]
20196 fn as_unquote_decomposes_unquote_splice_into_typed_marker_and_inner() {
20197 // `,@xs` — Sexp::UnquoteSplice wrapping a symbol. Pin
20198 // Some((Splice, &inner)). Sibling positive control to the Unquote
20199 // arm: pins BOTH unquote-family variants project to their typed
20200 // closed-set UnquoteForm pair through ONE projection function.
20201 let inner = Sexp::symbol("xs");
20202 let form = Sexp::UnquoteSplice(Box::new(inner.clone()));
20203 let (marker, body) = form
20204 .as_unquote()
20205 .expect("`,@xs` must project to Some((Splice, _))");
20206 assert_eq!(marker, UnquoteForm::Splice);
20207 assert_eq!(body, &inner);
20208 }
20209
20210 #[test]
20211 fn as_unquote_none_for_non_unquote_shapes() {
20212 // Every Sexp shape OUTSIDE the unquote family — atoms, lists, nil,
20213 // and the OTHER quote-family variants (Quote `'x`, Quasiquote ``x`) —
20214 // yields None. Pins the projection's exhaustive negative coverage:
20215 // a regression that drifts the matched-variant set (e.g. a future
20216 // emitter that projects `'x` into Some((Unquote, _))) would fail
20217 // here, even before any downstream dispatcher tests fire.
20218 assert_eq!(Sexp::symbol("foo").as_unquote(), None);
20219 assert_eq!(Sexp::int(5).as_unquote(), None);
20220 assert_eq!(Sexp::keyword("k").as_unquote(), None);
20221 assert_eq!(Sexp::string("s").as_unquote(), None);
20222 assert_eq!(Sexp::boolean(true).as_unquote(), None);
20223 assert_eq!(Sexp::float(1.5).as_unquote(), None);
20224 assert_eq!(Sexp::Nil.as_unquote(), None);
20225 assert_eq!(Sexp::List(vec![]).as_unquote(), None);
20226 assert_eq!(
20227 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]).as_unquote(),
20228 None
20229 );
20230 // `'x` — Quote-family but NOT unquote-family. The closed-set
20231 // UnquoteForm projection covers only `,` and `,@`; `'` and `` ` ``
20232 // are siblings that this projection does NOT match.
20233 assert_eq!(Sexp::Quote(Box::new(Sexp::symbol("x"))).as_unquote(), None);
20234 assert_eq!(
20235 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))).as_unquote(),
20236 None
20237 );
20238 }
20239
20240 #[test]
20241 fn as_unquote_is_some_iff_matches_unquote_family() {
20242 // Structural identity: as_unquote().is_some() agrees with the
20243 // pre-lift `matches!(self, Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
20244 // discriminant across the closed Sexp variant set. Sweep every
20245 // representative Sexp shape and pin equality of the two discriminants
20246 // — a regression that drifts ONE shape's projection (e.g. adds
20247 // Quasiquote to the matched set) becomes a typed test failure.
20248 let shapes: Vec<(&str, Sexp, bool)> = vec![
20249 ("nil", Sexp::Nil, false),
20250 ("symbol", Sexp::symbol("x"), false),
20251 ("keyword", Sexp::keyword("k"), false),
20252 ("string", Sexp::string("s"), false),
20253 ("int", Sexp::int(7), false),
20254 ("float", Sexp::float(2.5), false),
20255 ("bool", Sexp::boolean(true), false),
20256 ("empty list", Sexp::List(vec![]), false),
20257 (
20258 "non-empty list",
20259 Sexp::List(vec![Sexp::symbol("op")]),
20260 false,
20261 ),
20262 ("quote", Sexp::Quote(Box::new(Sexp::symbol("x"))), false),
20263 (
20264 "quasiquote",
20265 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
20266 false,
20267 ),
20268 ("unquote", Sexp::Unquote(Box::new(Sexp::symbol("x"))), true),
20269 (
20270 "unquote-splice",
20271 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
20272 true,
20273 ),
20274 ];
20275 for (label, sexp, expect_some) in &shapes {
20276 let via_proj = sexp.as_unquote().is_some();
20277 let via_pat = matches!(sexp, Sexp::Unquote(_) | Sexp::UnquoteSplice(_));
20278 assert_eq!(
20279 via_proj, *expect_some,
20280 "as_unquote().is_some() drifted from expected at {label}"
20281 );
20282 assert_eq!(
20283 via_proj, via_pat,
20284 "as_unquote().is_some() != pre-lift `matches!(_, Unquote | UnquoteSplice)` at {label}"
20285 );
20286 }
20287 }
20288
20289 #[test]
20290 fn as_unquote_inner_pointer_is_the_boxed_body() {
20291 // The returned `&Sexp` borrows the inner box's body verbatim — no
20292 // clone, no allocation, same lifetime as `&self`. Pin pointer
20293 // identity: the returned `&Sexp` shares its address with the
20294 // contents of the original Box, proving no intermediate copy fires
20295 // at the projection boundary (so consumers walking deeply nested
20296 // template bodies pay zero allocation per unquote node).
20297 let inner = Sexp::symbol("payload");
20298 let boxed = Box::new(inner);
20299 let inner_ptr: *const Sexp = boxed.as_ref();
20300 let form = Sexp::Unquote(boxed);
20301 let (_, body) = form
20302 .as_unquote()
20303 .expect("Sexp::Unquote must project to Some");
20304 assert!(
20305 std::ptr::eq(body, inner_ptr),
20306 "as_unquote inner pointer drifted from the boxed body — projection allocates or clones"
20307 );
20308
20309 let inner_splice = Sexp::symbol("payload-splice");
20310 let boxed_splice = Box::new(inner_splice);
20311 let inner_splice_ptr: *const Sexp = boxed_splice.as_ref();
20312 let form_splice = Sexp::UnquoteSplice(boxed_splice);
20313 let (_, body_splice) = form_splice
20314 .as_unquote()
20315 .expect("Sexp::UnquoteSplice must project to Some");
20316 assert!(
20317 std::ptr::eq(body_splice, inner_splice_ptr),
20318 "as_unquote inner pointer drifted from the boxed body (splice arm)"
20319 );
20320 }
20321
20322 // ── fmt_float: Display→read round-trip preserves Float identity ──────
20323 //
20324 // Rust's stdlib Display for f64 elides trailing `.0` on integral
20325 // floats — `format!("{}", 1.0_f64) == "1"` — and the substrate's
20326 // reader tries `i64::parse` before `f64::parse`, so a bare `1` re-reads
20327 // as `Atom::Int(1)`, NOT `Atom::Float(1.0)`. The Display→read
20328 // round-trip pre-lift dropped the typed Float identity on every
20329 // integral float: `Float(1.0)` displayed as `"1"`, re-read as `Int(1)`,
20330 // and downstream consumers silently typed the slot as Int. The
20331 // `fmt_float` helper appends `.0` for finite integral values so the
20332 // round-trip preserves the typed identity. Tests below pin:
20333 // (a) Display of `Float(1.0)` is `"1.0"` (fail-before-pass-after);
20334 // (b) the Display→read round-trip lands as `Float(1.0)`, NOT
20335 // `Int(1)` (the typed-identity preservation contract);
20336 // (c) non-integral floats render unchanged through the default
20337 // impl (`Float(1.5)` is still `"1.5"`);
20338 // (d) negative integral floats inherit the `.0` suffix
20339 // (`Float(-2.0)` is `"-2.0"`);
20340 // (e) integer Display is unaffected (`Int(1)` is still `"1"`) —
20341 // pin path-uniformity so the helper is precisely scoped to
20342 // the Float arm.
20343
20344 #[test]
20345 fn fmt_float_renders_integral_float_with_trailing_zero() {
20346 // Fail-before-pass-after: pre-lift `Sexp::float(1.0).to_string()`
20347 // was `"1"`; post-lift the typed Float identity is preserved by
20348 // the `.0` suffix.
20349 assert_eq!(Sexp::float(1.0).to_string(), "1.0");
20350 assert_eq!(Sexp::float(100.0).to_string(), "100.0");
20351 assert_eq!(Sexp::float(0.0).to_string(), "0.0");
20352 }
20353
20354 #[test]
20355 fn fmt_float_round_trips_integral_float_through_reader_as_float() {
20356 // The structural contract the lift establishes: a `Float`
20357 // serialized via `Display` re-reads as `Float`, NOT `Int`. Pin
20358 // the round-trip via the reader so a regression that drops the
20359 // `.0` suffix (or that re-orders the reader's i64/f64 parse
20360 // attempts to drop the float arm) surfaces here.
20361 let orig = Sexp::float(1.0);
20362 let rendered = orig.to_string();
20363 let forms =
20364 crate::reader::read(&rendered).expect("integral float must round-trip through reader");
20365 assert_eq!(forms.len(), 1);
20366 match &forms[0] {
20367 Sexp::Atom(Atom::Float(n)) => assert_eq!(*n, 1.0),
20368 other => panic!("Display->read round-trip dropped the Float identity, got: {other:?}"),
20369 }
20370 // Sibling-shape control: a SECOND integral magnitude reinforces
20371 // that the round-trip preserves the value, not only the type.
20372 let orig2 = Sexp::float(-42.0);
20373 let rendered2 = orig2.to_string();
20374 let forms2 = crate::reader::read(&rendered2)
20375 .expect("negative integral float must round-trip through reader");
20376 match &forms2[0] {
20377 Sexp::Atom(Atom::Float(n)) => assert_eq!(*n, -42.0),
20378 other => panic!(
20379 "Display->read of negative integral float dropped Float identity, got: {other:?}"
20380 ),
20381 }
20382 }
20383
20384 #[test]
20385 fn fmt_float_preserves_non_integral_float_display() {
20386 // Path-uniformity: non-integral floats (the case the stdlib impl
20387 // already handled correctly) must render unchanged. A regression
20388 // that always-appends `.0` would write `"1.5.0"` and fail
20389 // here AND fail the reader round-trip below.
20390 assert_eq!(Sexp::float(1.5).to_string(), "1.5");
20391 assert_eq!(Sexp::float(0.99).to_string(), "0.99");
20392 assert_eq!(Sexp::float(-2.75).to_string(), "-2.75");
20393
20394 // Round-trip control for the non-integral case stays valid: the
20395 // helper is precisely scoped, so the fractional component is
20396 // preserved verbatim through the reader.
20397 let orig = Sexp::float(0.99);
20398 let forms = crate::reader::read(&orig.to_string())
20399 .expect("non-integral float must round-trip through reader");
20400 match &forms[0] {
20401 Sexp::Atom(Atom::Float(n)) => assert_eq!(*n, 0.99),
20402 other => panic!("non-integral float round-trip drift, got: {other:?}"),
20403 }
20404 }
20405
20406 // ── QuoteForm + as_quote_form: closed-set quote-family projection ─────
20407 //
20408 // `as_quote_form` lifts the per-callsite `Sexp::Quote(inner)
20409 // / Sexp::Quasiquote(inner) / Sexp::Unquote(inner) /
20410 // Sexp::UnquoteSplice(inner)` arm-set paired with their
20411 // per-variant prefix string (`'`, `` ` ``, `,`, `,@`) and
20412 // discriminator byte (3, 4, 5, 6) into ONE typed projection on
20413 // the `Sexp` algebra. Three consumers in this file route through
20414 // it (`Hash for Sexp`, `Display for Sexp`, `Sexp::as_unquote`)
20415 // so the (Sexp variant, marker, prefix, discriminator) tuple
20416 // binds at ONE site. Tests below pin:
20417 // (a) the projection lands `Some((QuoteForm::*, inner))` for
20418 // each of the four wrapper variants AND `None` for every
20419 // non-quote-family shape;
20420 // (b) `QuoteForm::prefix` returns the canonical reader-token
20421 // prefix for each variant — load-bearing for the round-trip
20422 // property the `Display`→reader dual encodes;
20423 // (c) `QuoteForm::hash_discriminator` returns the same byte
20424 // values the pre-lift Hash arms emitted (3, 4, 5, 6) — pin
20425 // the cache-key contract so a regression that drifts a
20426 // discriminator silently invalidates every cached expansion
20427 // fails loudly here;
20428 // (d) `QuoteForm::as_unquote_form` projects the 2-of-4 subset
20429 // `{Unquote → UnquoteForm::Unquote, UnquoteSplice →
20430 // UnquoteForm::Splice}` and yields `None` for `{Quote,
20431 // Quasiquote}` — the structural-subset gate the
20432 // `Sexp::as_unquote` derivation routes through;
20433 // (e) `Sexp::as_unquote` derived from `as_quote_form +
20434 // QuoteForm::as_unquote_form` agrees with the pre-lift
20435 // arm-based semantic across every Sexp shape — path
20436 // uniformity across the subset gate;
20437 // (f) the four homoiconic prefixes round-trip through the
20438 // reader via `read(format!("{prefix}{inner}"))` into the
20439 // matching `Sexp::*` variant — the typed dual of the
20440 // reader's prefix dispatch, pinned end-to-end on the four
20441 // wrappers (sibling to `fmt_float`'s Float round-trip pin
20442 // at the Display→read boundary).
20443
20444 #[test]
20445 fn as_quote_form_projects_each_wrapper_variant_to_typed_marker_and_inner() {
20446 // `'foo` — Sexp::Quote wrapping a symbol. Pin Some((Quote, &inner))
20447 // with the typed marker AND the borrowed inner body.
20448 let inner = Sexp::symbol("foo");
20449 let form = Sexp::Quote(Box::new(inner.clone()));
20450 let (qf, body) = form.as_quote_form().expect("Sexp::Quote must project");
20451 assert_eq!(qf, QuoteForm::Quote);
20452 assert_eq!(body, &inner);
20453
20454 // `` `foo `` — Sexp::Quasiquote wrapping a symbol.
20455 let form_qq = Sexp::Quasiquote(Box::new(inner.clone()));
20456 let (qf_qq, body_qq) = form_qq
20457 .as_quote_form()
20458 .expect("Sexp::Quasiquote must project");
20459 assert_eq!(qf_qq, QuoteForm::Quasiquote);
20460 assert_eq!(body_qq, &inner);
20461
20462 // `,foo` — Sexp::Unquote wrapping a symbol.
20463 let form_u = Sexp::Unquote(Box::new(inner.clone()));
20464 let (qf_u, body_u) = form_u.as_quote_form().expect("Sexp::Unquote must project");
20465 assert_eq!(qf_u, QuoteForm::Unquote);
20466 assert_eq!(body_u, &inner);
20467
20468 // `,@xs` — Sexp::UnquoteSplice wrapping a symbol.
20469 let form_us = Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs")));
20470 let (qf_us, body_us) = form_us
20471 .as_quote_form()
20472 .expect("Sexp::UnquoteSplice must project");
20473 assert_eq!(qf_us, QuoteForm::UnquoteSplice);
20474 assert_eq!(body_us, &Sexp::symbol("xs"));
20475 }
20476
20477 #[test]
20478 fn as_quote_form_none_for_non_quote_family_shapes() {
20479 // Every shape OUTSIDE the closed quote-family must project to
20480 // None: Nil, every Atom variant, and List (empty + populated).
20481 // Pin the closed-set boundary so a regression that accidentally
20482 // promotes a non-wrapper variant into the quote family becomes
20483 // a typed test failure.
20484 assert_eq!(Sexp::Nil.as_quote_form(), None);
20485 assert_eq!(Sexp::symbol("x").as_quote_form(), None);
20486 assert_eq!(Sexp::keyword("k").as_quote_form(), None);
20487 assert_eq!(Sexp::string("s").as_quote_form(), None);
20488 assert_eq!(Sexp::int(7).as_quote_form(), None);
20489 assert_eq!(Sexp::float(2.5).as_quote_form(), None);
20490 assert_eq!(Sexp::boolean(true).as_quote_form(), None);
20491 assert_eq!(Sexp::List(vec![]).as_quote_form(), None);
20492 assert_eq!(
20493 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]).as_quote_form(),
20494 None
20495 );
20496 }
20497
20498 #[test]
20499 fn as_quote_form_inner_pointer_is_the_boxed_body() {
20500 // The returned `&Sexp` borrows the inner box's body verbatim —
20501 // no clone, no allocation, same lifetime as `&self`. Pin
20502 // pointer identity for each of the four wrapper variants so a
20503 // regression that adds an intermediate copy at the projection
20504 // boundary surfaces here. Same posture as
20505 // `as_unquote_inner_pointer_is_the_boxed_body` for its 2-of-4
20506 // subset.
20507 let payload = Sexp::symbol("payload");
20508 let boxed = Box::new(payload);
20509 let inner_ptr: *const Sexp = boxed.as_ref();
20510 let form = Sexp::Quote(boxed);
20511 let (_, body) = form.as_quote_form().expect("Sexp::Quote must project");
20512 assert!(
20513 std::ptr::eq(body, inner_ptr),
20514 "as_quote_form inner pointer drifted from the boxed body — projection allocates or clones"
20515 );
20516
20517 let payload_qq = Sexp::symbol("payload-qq");
20518 let boxed_qq = Box::new(payload_qq);
20519 let inner_ptr_qq: *const Sexp = boxed_qq.as_ref();
20520 let form_qq = Sexp::Quasiquote(boxed_qq);
20521 let (_, body_qq) = form_qq
20522 .as_quote_form()
20523 .expect("Sexp::Quasiquote must project");
20524 assert!(
20525 std::ptr::eq(body_qq, inner_ptr_qq),
20526 "as_quote_form inner pointer drifted (quasiquote arm)"
20527 );
20528 }
20529
20530 #[test]
20531 fn expect_quote_form_projects_each_quote_family_variant_identically_to_as_quote_form() {
20532 // ASSERTED-TOTAL-FACE CONTRACT: `expect_quote_form` is the
20533 // asserted-total face of `as_quote_form` — for every quote-family
20534 // variant it MUST yield the same `(QuoteForm, &Sexp)` projection
20535 // that `as_quote_form` yields wrapped in `Some`. A regression
20536 // that drifts the two projections (e.g. a future variant
20537 // extension that updates `as_quote_form` but forgets to align
20538 // `expect_quote_form`'s body) surfaces here.
20539 let inner = Sexp::symbol("payload");
20540 for variant in [
20541 Sexp::Quote(Box::new(inner.clone())),
20542 Sexp::Quasiquote(Box::new(inner.clone())),
20543 Sexp::Unquote(Box::new(inner.clone())),
20544 Sexp::UnquoteSplice(Box::new(inner.clone())),
20545 ] {
20546 let via_total = variant.expect_quote_form();
20547 let via_soft = variant.as_quote_form().expect("variant is quote-family");
20548 assert_eq!(
20549 via_total.0, via_soft.0,
20550 "expect_quote_form's QuoteForm drifted from as_quote_form's at {variant}"
20551 );
20552 assert!(
20553 std::ptr::eq(via_total.1, via_soft.1),
20554 "expect_quote_form's inner pointer drifted from as_quote_form's at {variant}"
20555 );
20556 }
20557 }
20558
20559 #[test]
20560 fn expect_quote_form_panics_with_invariant_const_on_non_quote_family_variants() {
20561 // STATIC-INVARIANT CONTRACT: every non-quote-family variant
20562 // (Nil, every Atom subkind, List empty + populated) MUST trigger
20563 // the asserted-total panic with the named
20564 // `QUOTE_FAMILY_PROJECTION_INVARIANT` message. The const-vs-
20565 // panic-payload pin catches a future drift where the const is
20566 // edited without the projection picking it up (or vice versa).
20567 for variant in [
20568 Sexp::Nil,
20569 Sexp::symbol("x"),
20570 Sexp::keyword("k"),
20571 Sexp::string("s"),
20572 Sexp::int(7),
20573 Sexp::float(2.5),
20574 Sexp::boolean(true),
20575 Sexp::List(vec![]),
20576 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
20577 ] {
20578 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
20579 let _ = variant.expect_quote_form();
20580 }));
20581 let payload = result.expect_err("expect_quote_form must panic on non-quote-family");
20582 let msg = payload
20583 .downcast_ref::<String>()
20584 .map(String::as_str)
20585 .or_else(|| payload.downcast_ref::<&'static str>().copied())
20586 .expect("panic payload must be a string");
20587 assert!(
20588 msg.contains(QUOTE_FAMILY_PROJECTION_INVARIANT),
20589 "expect_quote_form panic message {msg:?} did not name \
20590 QUOTE_FAMILY_PROJECTION_INVARIANT at variant {variant:?}"
20591 );
20592 }
20593 }
20594
20595 #[test]
20596 fn quote_family_projection_invariant_const_matches_legacy_inline_literal() {
20597 // CONST-PIN: pre-lift the panic literal "matched quote-family
20598 // variant must project to Some via as_quote_form" appeared inline
20599 // at FIVE production sites (`Hash for Sexp`, `Display for Sexp`,
20600 // `domain::sexp_shape`, `domain::sexp_to_json`,
20601 // `interop::iac_forge_tag`). Pin the lifted const to the legacy
20602 // inline literal bit-for-bit so a regression that drifts the
20603 // const silently from the historical diagnostic string surfaces
20604 // here. Sibling shape to `quote_form_hash_discriminator_pins_
20605 // legacy_cache_key_bytes` for the discriminator-byte algebra.
20606 assert_eq!(
20607 QUOTE_FAMILY_PROJECTION_INVARIANT,
20608 "matched quote-family variant must project to Some via as_quote_form"
20609 );
20610 }
20611
20612 #[test]
20613 fn quote_form_prefix_pins_canonical_reader_tokens_for_every_variant() {
20614 // Pin every prefix string load-bearing for the Display→read
20615 // round-trip. A regression that drifts the prefix (e.g. swaps
20616 // `'` and `` ` `` between Quote and Quasiquote) silently
20617 // re-routes every renderer through the wrong variant; this
20618 // test fails loudly. Sibling-arm sweep so the (variant,
20619 // prefix) pair stays load-bearing under reordering refactors.
20620 assert_eq!(QuoteForm::Quote.prefix(), "'");
20621 assert_eq!(QuoteForm::Quasiquote.prefix(), "`");
20622 assert_eq!(QuoteForm::Unquote.prefix(), ",");
20623 assert_eq!(QuoteForm::UnquoteSplice.prefix(), ",@");
20624 }
20625
20626 // ── `QuoteForm::{QUOTE_PREFIX, QUASIQUOTE_PREFIX, UNQUOTE_PREFIX,
20627 // UNQUOTE_SPLICE_PREFIX, PREFIXES}` — per-role `&'static str`
20628 // reader-prefix algebra on the closed-set outer [`QuoteForm`]. Peer
20629 // of [`crate::error::MacroDefHead::KEYWORDS`] (head-keyword
20630 // algebra), [`Atom::BOOL_LITERALS`] (Scheme-bool spelling algebra),
20631 // and [`crate::macro_expand::MacroParams::LAMBDA_LIST_KEYWORDS`]
20632 // (CL lambda-list-keyword algebra) — every closed-set outer
20633 // projection on the substrate now pins its canonical bytes at ONE
20634 // `pub const` per role plus an ALL array for family-wide consumers.
20635
20636 #[test]
20637 fn quote_form_quote_prefix_projects_canonical_single_quote_bytes() {
20638 // Pin the exact `"'"` bytes at the typed constant. A regression
20639 // that renames the constant to a different byte fails HERE
20640 // rather than at silent reader-family drift where `'foo`
20641 // classifies as a bare atom (or through a different quote-family
20642 // variant) instead of `Sexp::Quote`.
20643 assert_eq!(QuoteForm::QUOTE_PREFIX, "'");
20644 }
20645
20646 #[test]
20647 fn quote_form_quasiquote_prefix_projects_canonical_backtick_bytes() {
20648 // Pin the exact `` "`" `` bytes at the typed constant. Sibling
20649 // posture to `quote_form_quote_prefix_projects_canonical_single_quote_bytes`.
20650 assert_eq!(QuoteForm::QUASIQUOTE_PREFIX, "`");
20651 }
20652
20653 #[test]
20654 fn quote_form_unquote_prefix_projects_canonical_comma_bytes() {
20655 // Pin the exact `","` bytes at the typed constant.
20656 assert_eq!(QuoteForm::UNQUOTE_PREFIX, ",");
20657 }
20658
20659 #[test]
20660 fn quote_form_unquote_splice_prefix_projects_canonical_comma_at_bytes() {
20661 // Pin the exact `",@"` bytes at the typed constant — the ONLY
20662 // two-char prefix on the closed set. A regression that lost the
20663 // `@` discriminator (dropping to just `","`, colliding with
20664 // `UNQUOTE_PREFIX`) surfaces HERE rather than as a silent
20665 // reader classifier collision.
20666 assert_eq!(QuoteForm::UNQUOTE_SPLICE_PREFIX, ",@");
20667 }
20668
20669 #[test]
20670 fn quote_form_prefix_routes_through_typed_per_role_constants() {
20671 // PATH-UNIFORMITY: `Self::prefix(self)` returns the per-role
20672 // `pub const` byte-for-byte per variant, catching a regression
20673 // that reverts ONE arm to an inline `"'"` / `` "`" `` / `","`
20674 // / `",@"` string literal (or drifts one arm's bytes silently).
20675 // Sibling posture to
20676 // `atom_bool_literal_routes_through_typed_per_variant_constants`
20677 // on the Scheme-bool spelling algebra.
20678 for (qf, expected) in [
20679 (QuoteForm::Quote, QuoteForm::QUOTE_PREFIX),
20680 (QuoteForm::Quasiquote, QuoteForm::QUASIQUOTE_PREFIX),
20681 (QuoteForm::Unquote, QuoteForm::UNQUOTE_PREFIX),
20682 (QuoteForm::UnquoteSplice, QuoteForm::UNQUOTE_SPLICE_PREFIX),
20683 ] {
20684 let actual = qf.prefix();
20685 assert_eq!(
20686 actual, expected,
20687 "QuoteForm::{qf:?}.prefix() `{actual}` drifted from \
20688 per-role constant `{expected}` — the arm must route \
20689 through the typed constant rather than an inline literal",
20690 );
20691 }
20692 }
20693
20694 #[test]
20695 fn quote_form_prefixes_has_expected_cardinality() {
20696 // Cardinality contract: `Self::PREFIXES.len() == 4` — pinned at
20697 // the declaration site by rustc's forced-arity check on
20698 // `[&'static str; 4]`. This test surfaces the arity as a
20699 // fail-loud runtime pin so a future refactor that switches the
20700 // array type to `&[&'static str]` (dropping the compile-time
20701 // arity forcing) doesn't silently loosen the closed-set
20702 // discipline the family relies on. Sibling posture to
20703 // `atom_bool_literals_has_expected_cardinality` and
20704 // `macro_def_head_keywords_has_expected_cardinality`.
20705 assert_eq!(
20706 QuoteForm::PREFIXES.len(),
20707 4,
20708 "QuoteForm::PREFIXES cardinality drifted from 4 — the \
20709 closed homoiconic-prefix domain admits exactly four \
20710 wrappers by construction; a fifth extension surfaces here"
20711 );
20712 }
20713
20714 #[test]
20715 fn quote_form_prefixes_align_with_all_by_index() {
20716 // ALIGNMENT CONTRACT: `Self::PREFIXES[i] == Self::ALL[i].prefix()`
20717 // element-wise. Pins that the typed variant ALL and the
20718 // `&'static str` PREFIXES ALL stay in lockstep under any
20719 // reorder — a regression that reorders ONE array without
20720 // reordering the other silently misaligns every `zip(ALL,
20721 // PREFIXES)` consumer (LSP completion providers, metric-label
20722 // emitters, coverage reporters). Sibling posture to
20723 // `macro_def_head_keywords_align_with_all_by_index`.
20724 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
20725 assert_eq!(
20726 QuoteForm::PREFIXES[i],
20727 qf.prefix(),
20728 "QuoteForm::PREFIXES[{i}] `{prefix}` drifted from \
20729 QuoteForm::ALL[{i}] ({qf:?}).prefix() `{via_variant}` \
20730 — the canonical declaration order of the ALL array \
20731 and the prefix projection must match element-wise",
20732 prefix = QuoteForm::PREFIXES[i],
20733 via_variant = qf.prefix(),
20734 );
20735 }
20736 }
20737
20738 #[test]
20739 fn quote_form_prefixes_pairwise_distinct() {
20740 // PAIRWISE DISJOINTNESS: every entry of the `PREFIXES` array
20741 // must differ so the reader-entry classifier (whether inline
20742 // as at [`crate::reader::tokenize`]'s outer arm or via a
20743 // hypothetical future `PREFIXES.iter()` sweep) cannot route
20744 // two homoiconic prefixes through the same arm. Family-wide
20745 // sweep over `PREFIXES × PREFIXES` — supersedes any per-pair
20746 // pin and picks up new prefixes mechanically. Sibling posture
20747 // to `atom_bool_literals_pairwise_distinct` on the Scheme-bool
20748 // algebra AND `macro_def_head_keywords_pairwise_distinct` on
20749 // the head-keyword algebra.
20750 for (i, a) in QuoteForm::PREFIXES.iter().enumerate() {
20751 for (j, b) in QuoteForm::PREFIXES.iter().enumerate() {
20752 if i == j {
20753 continue;
20754 }
20755 assert_ne!(
20756 a, b,
20757 "QuoteForm::PREFIXES[{i}] `{a}` collides with \
20758 QuoteForm::PREFIXES[{j}] `{b}` — the reader-entry \
20759 classifier's cascade would route two homoiconic \
20760 prefixes through the same arm"
20761 );
20762 }
20763 }
20764 }
20765
20766 #[test]
20767 fn quote_form_per_role_prefixes_route_through_matching_lead_char_for_every_variant() {
20768 // CROSS-AXIS ROUND-TRIP: every entry of `Self::PREFIXES` MUST
20769 // start with the corresponding variant's `lead_char()` — the
20770 // per-role `pub const` on the reader-prefix axis composes byte-
20771 // for-byte with the per-role `char` on the reader-lead-byte axis
20772 // ([`Self::QUOTE_LEAD`], [`Self::QUASIQUOTE_LEAD`],
20773 // [`Self::UNQUOTE_LEAD`]) via the [`Self::lead_char`] projection.
20774 // Both `Unquote` AND `UnquoteSplice` start with `UNQUOTE_LEAD`
20775 // (the shared `,` lead byte) — the two-char splice prefix's
20776 // second byte is [`Self::SPLICE_DISCRIMINATOR`], which the
20777 // reader's peek-then-consume arm disambiguates on. Sibling
20778 // posture to `atom_bool_literals_all_route_through_bool_literal_leading_byte`
20779 // on the Scheme-bool spelling algebra.
20780 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
20781 let prefix = QuoteForm::PREFIXES[i];
20782 let expected_lead = qf.lead_char();
20783 let actual_lead = prefix.chars().next().unwrap_or_else(|| {
20784 panic!("QuoteForm::PREFIXES[{i}] `{prefix}` for {qf:?} must have at least one char")
20785 });
20786 assert_eq!(
20787 actual_lead, expected_lead,
20788 "QuoteForm::PREFIXES[{i}] `{prefix}` for {qf:?} — first \
20789 char {actual_lead:?} drifted from lead_char {expected_lead:?} — \
20790 the per-role prefix constant drifted from the shared \
20791 lead byte"
20792 );
20793 }
20794 }
20795
20796 #[test]
20797 fn quote_form_unquote_splice_prefix_constant_composes_from_unquote_lead_and_splice_discriminator(
20798 ) {
20799 // STRUCTURAL COMPOSITION LAW at the `pub const` level:
20800 // [`Self::UNQUOTE_SPLICE_PREFIX`] decomposes cleanly into
20801 // [`Self::UNQUOTE_LEAD`] + [`Self::SPLICE_DISCRIMINATOR`]. The
20802 // ONLY two-char prefix on the closed set composes from the two
20803 // `char`-level constants on the algebra. Section-for-retraction
20804 // peer of the pre-existing
20805 // `quote_form_unquote_splice_prefix_composes_from_unquote_lead_and_splice_discriminator`
20806 // pin (which composes at the [`Self::prefix`] method level);
20807 // where that pin composes through the runtime projection, this
20808 // pin composes at the `pub const` level so a regression that
20809 // drifts the two-char constant WITHOUT drifting the runtime
20810 // projection (unlikely but structurally distinct) surfaces
20811 // here.
20812 let composed = format!(
20813 "{}{}",
20814 QuoteForm::UNQUOTE_LEAD,
20815 QuoteForm::SPLICE_DISCRIMINATOR,
20816 );
20817 assert_eq!(
20818 composed,
20819 QuoteForm::UNQUOTE_SPLICE_PREFIX,
20820 "QuoteForm::UNQUOTE_SPLICE_PREFIX drifted from UNQUOTE_LEAD + \
20821 SPLICE_DISCRIMINATOR — the reader's two-char splice \
20822 promotion identity is broken at the pub-const byte level",
20823 );
20824 }
20825
20826 #[test]
20827 fn quote_form_lead_char_pins_first_char_of_prefix_for_every_variant() {
20828 // Pin the (variant, lead char) pairing threaded through
20829 // [`crate::reader::tokenize`]'s outer quote-family dispatch AND
20830 // its bare-atom terminator disjunct. Quote/Quasiquote's
20831 // singleton chars project to `'\''` / `` '`' `` respectively;
20832 // Unquote AND UnquoteSplice BOTH project to `','` because the
20833 // splice's two-char `,@` prefix shares its lead byte with bare
20834 // unquote and the reader disambiguates on the peek-then-consume
20835 // `@` second char. A regression that split the shared-lead-char
20836 // collapse (e.g. gave UnquoteSplice a distinct lead char)
20837 // silently re-shapes every splice tokenization; this test
20838 // catches the drift at rustc + `cargo test` time rather than
20839 // as an off-by-one reader miscue that surfaces only when a
20840 // `,@xs` form parses wrong.
20841 assert_eq!(QuoteForm::Quote.lead_char(), '\'');
20842 assert_eq!(QuoteForm::Quasiquote.lead_char(), '`');
20843 assert_eq!(QuoteForm::Unquote.lead_char(), ',');
20844 assert_eq!(QuoteForm::UnquoteSplice.lead_char(), ',');
20845 }
20846
20847 #[test]
20848 fn quote_form_lead_char_is_first_char_of_prefix_for_every_variant() {
20849 // COMPOSITION CONTRACT: `lead_char` MUST equal the first char of
20850 // `prefix()` for every variant. Pin the composition so a
20851 // regression that drifts one of the two projections (e.g. a
20852 // rename of `Quote`'s prefix from `"'"` to `"‛"` without
20853 // updating `lead_char`, or vice versa) surfaces immediately.
20854 // The typed composition binds the (`QuoteForm`, lead char,
20855 // full prefix) triple at ONE consistency check across every
20856 // arm of the closed set — a future fifth homoiconic prefix
20857 // extension must extend `prefix` AND `lead_char` in lockstep,
20858 // and this sweep pins the invariant that connects them.
20859 for qf in QuoteForm::ALL {
20860 let prefix_first =
20861 qf.prefix().chars().next().unwrap_or_else(|| {
20862 panic!("QuoteForm::{qf:?} prefix must have at least one char")
20863 });
20864 assert_eq!(
20865 qf.lead_char(),
20866 prefix_first,
20867 "QuoteForm::{qf:?} — lead_char {:?} drifted from first char of prefix {:?}",
20868 qf.lead_char(),
20869 qf.prefix(),
20870 );
20871 }
20872 }
20873
20874 #[test]
20875 fn quote_form_from_lead_char_decodes_every_distinct_lead_char_to_default_variant() {
20876 // Pin the inverse projection at every distinct lead char across
20877 // the closed set. Quote/Quasiquote decode to their singleton
20878 // owners; `,` decodes to `Some(Unquote)` (the DEFAULT variant on
20879 // the shared `,` lead char) — the `,@` splice promotion lives
20880 // at the reader's peek arm, NOT at this decode. Every other
20881 // char yields `None`. Includes the tokenizer's non-quote entry
20882 // chars (`'('`, `')'`, `';'`, `Atom::STR_DELIMITER`, ` `) as
20883 // the rejection sweep — a regression that leaks a quote-family
20884 // variant onto a non-quote lead char silently re-shapes every
20885 // top-level program's tokenization; this rejection sweep
20886 // catches the drift at test time.
20887 assert_eq!(QuoteForm::from_lead_char('\''), Some(QuoteForm::Quote));
20888 assert_eq!(QuoteForm::from_lead_char('`'), Some(QuoteForm::Quasiquote));
20889 assert_eq!(QuoteForm::from_lead_char(','), Some(QuoteForm::Unquote));
20890
20891 // Non-quote reader entry chars must reject.
20892 assert_eq!(QuoteForm::from_lead_char('('), None);
20893 assert_eq!(QuoteForm::from_lead_char(')'), None);
20894 assert_eq!(QuoteForm::from_lead_char(';'), None);
20895 assert_eq!(QuoteForm::from_lead_char(Atom::STR_DELIMITER), None);
20896 assert_eq!(QuoteForm::from_lead_char(' '), None);
20897 assert_eq!(QuoteForm::from_lead_char('a'), None);
20898 assert_eq!(QuoteForm::from_lead_char('@'), None);
20899 assert_eq!(QuoteForm::from_lead_char(':'), None);
20900 assert_eq!(QuoteForm::from_lead_char('#'), None);
20901 }
20902
20903 #[test]
20904 fn quote_form_lead_char_round_trips_through_from_lead_char_with_shared_lead_char_collapse() {
20905 // ROUND-TRIP CONTRACT: for every variant, decoding its
20906 // `lead_char()` back through `from_lead_char` produces
20907 // `Some(default_variant_on_that_lead_char)`. For the three
20908 // variants with singleton lead chars (`Quote`, `Quasiquote`,
20909 // `Unquote`) the round-trip is the identity. For `UnquoteSplice`
20910 // — which shares `,` with `Unquote` — the round-trip yields
20911 // `Some(QuoteForm::Unquote)` because `,` alone cannot signal
20912 // splice; the reader's peek-then-consume `@` disambiguator is
20913 // where the splice promotion happens. Pin this asymmetry so
20914 // a regression that pushed the splice promotion into
20915 // `from_lead_char` (a natural but wrong refactor) surfaces
20916 // here at test time — decoupling the char-level decode from
20917 // the streaming reader's two-char sequence is load-bearing
20918 // for the tokenizer's structure.
20919 for qf in QuoteForm::ALL {
20920 let decoded = QuoteForm::from_lead_char(qf.lead_char());
20921 let expected = match qf {
20922 QuoteForm::Quote => Some(QuoteForm::Quote),
20923 QuoteForm::Quasiquote => Some(QuoteForm::Quasiquote),
20924 // Both `,`-lead-char variants collapse onto Unquote;
20925 // splice promotion lives at the reader's peek arm.
20926 QuoteForm::Unquote | QuoteForm::UnquoteSplice => Some(QuoteForm::Unquote),
20927 };
20928 assert_eq!(
20929 decoded, expected,
20930 "QuoteForm::{qf:?} — from_lead_char(lead_char) round-trip drifted",
20931 );
20932 }
20933 }
20934
20935 #[test]
20936 fn quote_form_from_lead_char_is_const_fn_over_the_closed_set() {
20937 // Pin the `const fn` posture of both projections by binding a
20938 // `const` array literal keyed on the closed set. A regression
20939 // that removed the `const` qualifier (dropping the compile-
20940 // time evaluability the reader's outer dispatch AND future
20941 // static lookup tables key on) fails to compile HERE — the
20942 // `const` context enforces the qualifier without a test-time
20943 // assertion.
20944 const _QUOTE: char = QuoteForm::Quote.lead_char();
20945 const _QUASIQUOTE: char = QuoteForm::Quasiquote.lead_char();
20946 const _UNQUOTE: char = QuoteForm::Unquote.lead_char();
20947 const _SPLICE: char = QuoteForm::UnquoteSplice.lead_char();
20948 const _FROM: Option<QuoteForm> = QuoteForm::from_lead_char(',');
20949 assert_eq!(_QUOTE, '\'');
20950 assert_eq!(_QUASIQUOTE, '`');
20951 assert_eq!(_UNQUOTE, ',');
20952 assert_eq!(_SPLICE, ',');
20953 assert_eq!(_FROM, Some(QuoteForm::Unquote));
20954 }
20955
20956 #[test]
20957 fn quote_form_lead_constants_project_canonical_chars() {
20958 // Pin each constant's byte identity so a typo (`'‛'` for
20959 // `QUOTE_LEAD`, `'’'` for `QUASIQUOTE_LEAD`, `';'` for
20960 // `UNQUOTE_LEAD`) or accidental redefinition surfaces
20961 // immediately. Every canonical per-role reader-punctuation byte
20962 // on the substrate has its own byte-identity pin at its owning
20963 // algebra
20964 // (`atom_str_delimiter_projects_canonical_quote_char`,
20965 // `atom_str_escape_lead_projects_canonical_backslash_char`,
20966 // `atom_keyword_marker_lead_projects_canonical_colon_char`,
20967 // `atom_bool_literal_lead_projects_canonical_hash_char`,
20968 // `sexp_list_open_projects_canonical_char`,
20969 // `sexp_list_close_projects_canonical_char`,
20970 // `sexp_comment_lead_projects_canonical_char`,
20971 // `sexp_comment_term_projects_canonical_char`,
20972 // `quote_form_splice_discriminator_projects_canonical_at_char`);
20973 // this pin closes the three quote-family lead-byte constants at
20974 // the SAME shape.
20975 assert_eq!(QuoteForm::QUOTE_LEAD, '\'');
20976 assert_eq!(QuoteForm::QUASIQUOTE_LEAD, '`');
20977 assert_eq!(QuoteForm::UNQUOTE_LEAD, ',');
20978 }
20979
20980 #[test]
20981 fn quote_form_lead_constants_round_trip_through_lead_char_projections() {
20982 // ROUND-TRIP CONTRACT: for each of the three distinct-lead-byte
20983 // variants (`Quote`, `Quasiquote`, `Unquote`) the (variant →
20984 // constant → variant) triangle closes exactly.
20985 // `Self::from_lead_char(Self::X_LEAD) == Some(Self::X)` AND
20986 // `Self::X.lead_char() == Self::X_LEAD` — the constants ARE the
20987 // canonical per-variant lead byte both projections route
20988 // through. `UnquoteSplice` shares its lead byte with `Unquote`
20989 // (see the merged arm in `lead_char`), so `UnquoteSplice.lead_char()
20990 // == Self::UNQUOTE_LEAD` too — the splice's SECOND-char
20991 // `SPLICE_DISCRIMINATOR` promotion lives at the reader's peek
20992 // arm, not at this decode. Pin the triangle at every variant so
20993 // a regression that drifts EITHER a constant OR one of the two
20994 // projection sites surfaces at test time.
20995 assert_eq!(
20996 QuoteForm::from_lead_char(QuoteForm::QUOTE_LEAD),
20997 Some(QuoteForm::Quote),
20998 );
20999 assert_eq!(QuoteForm::Quote.lead_char(), QuoteForm::QUOTE_LEAD);
21000
21001 assert_eq!(
21002 QuoteForm::from_lead_char(QuoteForm::QUASIQUOTE_LEAD),
21003 Some(QuoteForm::Quasiquote),
21004 );
21005 assert_eq!(
21006 QuoteForm::Quasiquote.lead_char(),
21007 QuoteForm::QUASIQUOTE_LEAD,
21008 );
21009
21010 assert_eq!(
21011 QuoteForm::from_lead_char(QuoteForm::UNQUOTE_LEAD),
21012 Some(QuoteForm::Unquote),
21013 );
21014 assert_eq!(QuoteForm::Unquote.lead_char(), QuoteForm::UNQUOTE_LEAD);
21015 // UnquoteSplice's `lead_char` collapses onto the shared
21016 // `UNQUOTE_LEAD` byte because the splice's `,@` prefix opens
21017 // with `,`; the promotion is a two-char peek in the reader.
21018 assert_eq!(
21019 QuoteForm::UnquoteSplice.lead_char(),
21020 QuoteForm::UNQUOTE_LEAD,
21021 );
21022 }
21023
21024 #[test]
21025 fn quote_form_lead_constants_distinct_from_every_other_algebra_marker_char() {
21026 // CROSS-AXIS DISJOINTNESS CONTRACT: each of the three quote-
21027 // family lead bytes MUST differ from every other canonical
21028 // reader-punctuation constant on the substrate — the other two
21029 // quote-family lead bytes, `SPLICE_DISCRIMINATOR`,
21030 // `Atom::STR_DELIMITER`, `Atom::STR_ESCAPE_LEAD`,
21031 // `Atom::KEYWORD_MARKER_LEAD`, `Atom::BOOL_LITERAL_LEAD`,
21032 // `Sexp::LIST_OPEN`, `Sexp::LIST_CLOSE`, `Sexp::COMMENT_LEAD`,
21033 // and `Sexp::COMMENT_TERM`. Otherwise the reader's outer
21034 // dispatch would ambiguously route a `'` / `` ` `` / `,` lead
21035 // byte through the aliased sibling arm — e.g. if `QUOTE_LEAD`
21036 // aliased `Sexp::LIST_OPEN`, a source `'foo` would ambiguously
21037 // trigger the list-open arm before the quote-family arm ran.
21038 // Sibling-shape peer of
21039 // `quote_form_splice_discriminator_distinct_from_every_algebra_marker_char`
21040 // one axis over.
21041 // The three distinct-lead-byte rows bind through the typed
21042 // [`QuoteForm::LEADS`] ALL array — the sub-vocabulary sweep
21043 // now iterates ONE forced-arity `[char; 3]` array rather than
21044 // three inline algebra-constant enumerations.
21045 let leads = QuoteForm::LEADS;
21046 // Within-family: the three lead bytes are pairwise distinct.
21047 for (i, a) in leads.iter().enumerate() {
21048 for b in &leads[i + 1..] {
21049 assert_ne!(a, b, "quote-family lead bytes must be pairwise distinct",);
21050 }
21051 }
21052 // Cross-family disjointness against every other single-char
21053 // algebra marker on the substrate.
21054 for lead in leads {
21055 assert_ne!(lead, QuoteForm::SPLICE_DISCRIMINATOR);
21056 assert_ne!(lead, Atom::STR_DELIMITER);
21057 assert_ne!(lead, Atom::STR_ESCAPE_LEAD);
21058 assert_ne!(lead, Atom::KEYWORD_MARKER_LEAD);
21059 assert_ne!(lead, Atom::BOOL_LITERAL_LEAD);
21060 assert_ne!(lead, Sexp::LIST_OPEN);
21061 assert_ne!(lead, Sexp::LIST_CLOSE);
21062 assert_ne!(lead, Sexp::COMMENT_LEAD);
21063 assert_ne!(lead, Sexp::COMMENT_TERM);
21064 }
21065 }
21066
21067 #[test]
21068 fn quote_form_unquote_splice_prefix_composes_from_unquote_lead_and_splice_discriminator() {
21069 // STRUCTURAL COMPOSITION LAW: [`QuoteForm::UnquoteSplice`]'s
21070 // two-char prefix `",@"` decomposes cleanly into
21071 // [`QuoteForm::UNQUOTE_LEAD`] (the `,` byte shared with
21072 // [`QuoteForm::Unquote`]) + [`QuoteForm::SPLICE_DISCRIMINATOR`]
21073 // (the `@` byte promoted by the reader's peek arm). The two
21074 // BYTE-LEVEL constants on the closed-set [`QuoteForm`] algebra
21075 // compose the ONLY two-char [`Self::prefix`] in the closed set
21076 // — a stronger form of the pre-existing
21077 // `quote_form_unquote_splice_prefix_composes_from_unquote_prefix_and_splice_discriminator`
21078 // pin (which composes through the `&'static str` prefix of the
21079 // Unquote variant). Where that pin binds the `&'static str`-level
21080 // composition, this pin binds the `char`-level composition; both
21081 // pins fail loudly on any drift of the `,` or `@` bytes. The
21082 // reader's two-char peek-then-consume splice-promotion arm
21083 // reads `Self::UNQUOTE_LEAD` then peeks `Self::SPLICE_DISCRIMINATOR`
21084 // to promote to `UnquoteSplice` — this identity closes the
21085 // read↔write duality at the byte level.
21086 let composed = format!(
21087 "{}{}",
21088 QuoteForm::UNQUOTE_LEAD,
21089 QuoteForm::SPLICE_DISCRIMINATOR,
21090 );
21091 assert_eq!(
21092 composed,
21093 QuoteForm::UnquoteSplice.prefix(),
21094 "UnquoteSplice.prefix() drifted from UNQUOTE_LEAD + \
21095 SPLICE_DISCRIMINATOR — the reader's two-char splice \
21096 promotion identity is broken at the byte level",
21097 );
21098 }
21099
21100 #[test]
21101 fn quote_form_lead_constants_are_const_evaluable_over_the_closed_set() {
21102 // Pin the `const` posture of the three lead constants by binding
21103 // `const` bindings that ROUTE through the `const fn` lead_char /
21104 // from_lead_char projections — the compile-time evaluation
21105 // context enforces the qualifier without a test-time assertion.
21106 // Sibling posture to
21107 // `quote_form_from_lead_char_is_const_fn_over_the_closed_set`.
21108 const _QUOTE: char = QuoteForm::QUOTE_LEAD;
21109 const _QUASIQUOTE: char = QuoteForm::QUASIQUOTE_LEAD;
21110 const _UNQUOTE: char = QuoteForm::UNQUOTE_LEAD;
21111 const _FROM_QUOTE: Option<QuoteForm> = QuoteForm::from_lead_char(QuoteForm::QUOTE_LEAD);
21112 const _FROM_QUASIQUOTE: Option<QuoteForm> =
21113 QuoteForm::from_lead_char(QuoteForm::QUASIQUOTE_LEAD);
21114 const _FROM_UNQUOTE: Option<QuoteForm> = QuoteForm::from_lead_char(QuoteForm::UNQUOTE_LEAD);
21115 assert_eq!(_QUOTE, '\'');
21116 assert_eq!(_QUASIQUOTE, '`');
21117 assert_eq!(_UNQUOTE, ',');
21118 assert_eq!(_FROM_QUOTE, Some(QuoteForm::Quote));
21119 assert_eq!(_FROM_QUASIQUOTE, Some(QuoteForm::Quasiquote));
21120 assert_eq!(_FROM_UNQUOTE, Some(QuoteForm::Unquote));
21121 }
21122
21123 #[test]
21124 fn quote_form_leads_composes_from_algebra_constants_in_declaration_order() {
21125 // FAMILY COMPOSITION LAW: pin that the ALL array's rows are the
21126 // three distinct-lead-byte algebra constants (`Self::QUOTE_LEAD`,
21127 // `Self::QUASIQUOTE_LEAD`, `Self::UNQUOTE_LEAD`) in canonical
21128 // declaration order matching [`QuoteForm::ALL`]'s three-of-four
21129 // distinct-lead-byte projection through [`QuoteForm::lead_char`].
21130 // A reorder of ONE row without reordering the underlying algebra
21131 // constants silently misaligns every index-sweep consumer (a
21132 // hypothetical reader outer-dispatch pre-check that keys on
21133 // `QuoteForm::LEADS[0]`, an LSP completion generator that
21134 // materialises the distinct-lead-byte set in this order). Sibling-
21135 // shape pin to
21136 // `sexp_list_delimiters_composes_from_algebra_constants_in_declaration_order`
21137 // on the peer `[char; 2]` sub-vocabulary at
21138 // [`Sexp::LIST_DELIMITERS`].
21139 assert_eq!(
21140 QuoteForm::LEADS,
21141 [
21142 QuoteForm::QUOTE_LEAD,
21143 QuoteForm::QUASIQUOTE_LEAD,
21144 QuoteForm::UNQUOTE_LEAD,
21145 ],
21146 "QuoteForm::LEADS composition drifted from the canonical \
21147 (QUOTE_LEAD, QUASIQUOTE_LEAD, UNQUOTE_LEAD) triple — the \
21148 distinct-lead-byte sub-vocabulary lift must route through \
21149 the three typed algebra constants in that order.",
21150 );
21151 }
21152
21153 #[test]
21154 fn quote_form_leads_has_expected_cardinality() {
21155 // CARDINALITY PIN: `[char; 3]` at rustc — this assert pins the
21156 // runtime observable so a refactor that loosens the array's type
21157 // to `&[char]` (dropping the compile-time arity forcing) fails
21158 // HERE at the runtime cardinality assertion rather than silently
21159 // allowing a fourth or absent row. The 3 vs [`QuoteForm::PREFIXES`]'s
21160 // 4 shape asymmetry IS the structural axis distinguishing the
21161 // DISTINCT-lead-byte sub-vocabulary from the PER-VARIANT-prefix
21162 // sub-vocabulary — three-of-four distinct-lead-byte collapse is
21163 // definitional (only [`QuoteForm::UnquoteSplice`]'s two-char
21164 // `,@` prefix shares its lead byte with a sibling variant). A
21165 // regression that grew LEADS to 4 without the shared-lead-byte
21166 // collapse breaking (i.e. by adding a fourth distinct-lead-byte
21167 // variant WITHOUT drifting [`QuoteForm::PREFIXES`]'s cardinality
21168 // to 5 in lockstep) fails at this cardinality pin. Sibling-shape
21169 // pin to `sexp_list_delimiters_has_expected_cardinality`.
21170 assert_eq!(
21171 QuoteForm::LEADS.len(),
21172 3,
21173 "QuoteForm::LEADS cardinality drifted from 3 — the distinct-\
21174 lead-byte sub-vocabulary MUST be exactly three rows because \
21175 UnquoteSplice shares its lead byte with Unquote by the \
21176 splice's two-char `,@` prefix construction.",
21177 );
21178 }
21179
21180 #[test]
21181 fn quote_form_leads_pairwise_distinct() {
21182 // PAIRWISE DISJOINTNESS: the three distinct-lead-byte rows MUST
21183 // NOT alias — the closed-set outer [`QuoteForm`] algebra's
21184 // three-of-four collapse identity depends on Quote / Quasiquote /
21185 // Unquote owning distinct lead bytes (only UnquoteSplice shares
21186 // Unquote's lead byte, via the splice's two-char `,@` prefix).
21187 // A regression that collapsed Quote and Quasiquote onto the same
21188 // lead byte would silently break the reader's outer dispatch
21189 // (the tokenizer would ambiguously route both `'foo` and
21190 // `` `foo `` through the same arm). Sibling-shape pin to
21191 // `sexp_list_delimiters_pairwise_distinct` on the peer
21192 // `[char; 2]` sub-vocabulary at [`Sexp::LIST_DELIMITERS`].
21193 for (i, a) in QuoteForm::LEADS.iter().enumerate() {
21194 for (j, b) in QuoteForm::LEADS.iter().enumerate() {
21195 if i == j {
21196 continue;
21197 }
21198 assert_ne!(
21199 a, b,
21200 "QuoteForm::LEADS rows [{i}] and [{j}] share a byte \
21201 ({a:?} == {b:?}) — the distinct-lead-byte contract \
21202 across Quote / Quasiquote / Unquote would collapse.",
21203 );
21204 }
21205 }
21206 }
21207
21208 #[test]
21209 fn quote_form_leads_disjoint_from_splice_discriminator() {
21210 // CROSS-AXIS DISJOINTNESS (splice discriminator): no row of
21211 // [`QuoteForm::LEADS`] may alias
21212 // [`QuoteForm::SPLICE_DISCRIMINATOR`] — otherwise the reader's
21213 // two-char peek-then-consume splice-promotion arm inside
21214 // [`crate::reader::tokenize`] would ambiguously promote on a
21215 // sibling lead byte (e.g. if `SPLICE_DISCRIMINATOR` aliased
21216 // [`QuoteForm::UNQUOTE_LEAD`], a source `,,foo` would silently
21217 // promote to `UnquoteSplice(,foo)` rather than parsing as
21218 // `Unquote(Unquote(foo))`). Sibling-shape pin to
21219 // `sexp_list_delimiters_disjoint_from_str_delimiter`: both close
21220 // the cross-sub-vocabulary disjointness contract at the ALL-array
21221 // level rather than as an inline disjunction per consumer.
21222 for (i, ch) in QuoteForm::LEADS.iter().enumerate() {
21223 assert_ne!(
21224 *ch,
21225 QuoteForm::SPLICE_DISCRIMINATOR,
21226 "QuoteForm::LEADS[{i}] ({ch:?}) aliases \
21227 QuoteForm::SPLICE_DISCRIMINATOR ({:?}) — the reader's \
21228 distinct-lead-byte arm would collide with the two-char \
21229 splice promotion arm at the same byte.",
21230 QuoteForm::SPLICE_DISCRIMINATOR,
21231 );
21232 }
21233 }
21234
21235 #[test]
21236 fn quote_form_lead_char_routes_through_leads_for_every_variant() {
21237 // PATH-UNIFORMITY PIN: every [`QuoteForm::lead_char`] projection
21238 // over the closed set MUST land on a row of [`QuoteForm::LEADS`]
21239 // — the shared-lead-byte collapse identity (Quote / Quasiquote /
21240 // Unquote / UnquoteSplice → three distinct lead bytes) binds
21241 // through the ALL array's `.contains` sweep. A regression that
21242 // reverted `lead_char`'s arms to inline `char` literals AND
21243 // drifted one of the four arms without drifting a paired algebra
21244 // constant fails HERE at the first mismatched variant rather than
21245 // at a distant reader outer-dispatch drift where a quote-family
21246 // form silently routes through a bare-atom arm. Sibling-shape
21247 // pin to
21248 // `sexp_is_bare_atom_boundary_routes_through_list_delimiters_for_every_row`
21249 // on the peer `[char; 2]` sub-vocabulary at
21250 // [`Sexp::LIST_DELIMITERS`], lifted to the four-variant closed
21251 // set: every one of the four variants collapses onto one of the
21252 // three LEADS rows.
21253 for qf in QuoteForm::ALL {
21254 assert!(
21255 QuoteForm::LEADS.contains(&qf.lead_char()),
21256 "QuoteForm::{qf:?}.lead_char() = {:?} is NOT a row of \
21257 QuoteForm::LEADS — the shared-lead-byte collapse drifted \
21258 from the ALL array's distinct-lead-byte sub-vocabulary.",
21259 qf.lead_char(),
21260 );
21261 }
21262 }
21263
21264 #[test]
21265 fn quote_form_from_lead_char_decodes_every_row_of_leads_to_some() {
21266 // INVERSE PATH-UNIFORMITY PIN: every row of [`QuoteForm::LEADS`]
21267 // MUST decode through [`QuoteForm::from_lead_char`] to
21268 // `Some(_variant_)` (the DEFAULT variant on that lead byte —
21269 // Unquote on `,`, Quote on `'`, Quasiquote on `` ` ``). A
21270 // regression that dropped one of the three arms in
21271 // `from_lead_char`'s match without shrinking [`QuoteForm::LEADS`]
21272 // in lockstep fails HERE at the first mismatched row rather than
21273 // as a silent reader outer-dispatch drift where a quote-family
21274 // lead byte silently falls through to the None arm. This pin
21275 // closes the round-trip from the DISTINCT-lead-byte axis back
21276 // through the decode projection at the ALL-array level. Sibling-
21277 // shape pin to
21278 // `atom_decode_str_escape_routes_through_self_escape_table_for_every_row`
21279 // on the peer sub-vocabulary at [`Atom::SELF_ESCAPE_TABLE`].
21280 for (i, ch) in QuoteForm::LEADS.iter().enumerate() {
21281 assert!(
21282 QuoteForm::from_lead_char(*ch).is_some(),
21283 "QuoteForm::LEADS[{i}] ({ch:?}) decodes to None through \
21284 QuoteForm::from_lead_char — the distinct-lead-byte \
21285 sub-vocabulary drifted from the decode projection's \
21286 non-None arm-set.",
21287 );
21288 }
21289 }
21290
21291 #[test]
21292 fn quote_form_leads_matches_dedup_of_prefixes_first_char_set() {
21293 // CROSS-ARRAY IDENTITY: [`QuoteForm::LEADS`] IS the deduplicated
21294 // first-char set of [`QuoteForm::PREFIXES`] — the four per-
21295 // variant prefixes' first chars (`'\''`, `` '`' ``, `','`, `','`)
21296 // deduplicate onto the three DISTINCT lead bytes exactly matching
21297 // [`QuoteForm::LEADS`]'s rows. This pin binds the SHAPE-ASYMMETRIC
21298 // (3 vs 4) relationship between the two ALL arrays: [`QuoteForm::PREFIXES`]
21299 // enumerates the per-variant prefix-bytes axis; [`QuoteForm::LEADS`]
21300 // enumerates the DISTINCT lead-byte axis; the two ALL arrays
21301 // compose through the (first-char, dedup) projection. A regression
21302 // that drifted EITHER array without drifting the other (e.g. an
21303 // ELisp-compat port of Quote's prefix to `"#'"` without updating
21304 // [`QuoteForm::QUOTE_LEAD`]) surfaces here rather than as a
21305 // silent reader outer-dispatch drift. Sibling-shape pin to
21306 // `quote_form_per_role_prefixes_route_through_matching_lead_char_for_every_variant`
21307 // one axis over — that pin binds per-variant; this pin binds
21308 // per-distinct-lead-byte via the dedup composition. Peer at the
21309 // ALL-array level to the pre-existing composition tests between
21310 // NAMED_ESCAPE_TABLE + SELF_ESCAPE_TABLE (which SPAN a total
21311 // arm-set cardinality; this pin composes a DEDUP arm-set
21312 // cardinality).
21313 let mut deduped_first_chars: Vec<char> = QuoteForm::PREFIXES
21314 .iter()
21315 .map(|p| {
21316 p.chars().next().unwrap_or_else(|| {
21317 panic!("QuoteForm::PREFIXES entry `{p}` must have at least one char")
21318 })
21319 })
21320 .collect();
21321 deduped_first_chars.sort_unstable();
21322 deduped_first_chars.dedup();
21323
21324 let mut leads_sorted: Vec<char> = QuoteForm::LEADS.to_vec();
21325 leads_sorted.sort_unstable();
21326
21327 assert_eq!(
21328 deduped_first_chars, leads_sorted,
21329 "QuoteForm::LEADS drifted from the deduplicated first-char \
21330 set of QuoteForm::PREFIXES — the (per-variant prefix axis) \
21331 × (distinct-lead-byte axis) shape-asymmetric composition \
21332 identity is broken.",
21333 );
21334 }
21335
21336 #[test]
21337 fn quote_form_splice_discriminator_projects_canonical_at_char() {
21338 // Pin the constant's byte identity so a typo (`'!'`, `'?'`,
21339 // `'~'`) or accidental redefinition surfaces immediately. The
21340 // reader's peek arm inside [`crate::reader::tokenize`] AND the
21341 // splice-promotion table [`QuoteForm::promote_via_next_char`]
21342 // BOTH bind to this constant; a drift here would silently re-
21343 // shape every `,@xs` tokenization into a `,` + `@xs` two-token
21344 // sequence (or into a phantom promotion on a different
21345 // second-char), and this pin catches the drift at test time.
21346 assert_eq!(QuoteForm::SPLICE_DISCRIMINATOR, '@');
21347 }
21348
21349 #[test]
21350 fn quote_form_splice_discriminator_distinct_from_every_algebra_marker_char() {
21351 // CROSS-AXIS DISJOINTNESS CONTRACT: the splice discriminator
21352 // `@` must NOT alias any other canonical reader-punctuation
21353 // constant on the substrate — every [`QuoteForm::lead_char`]
21354 // projection, [`crate::ast::Atom::STR_DELIMITER`],
21355 // [`crate::ast::Atom::KEYWORD_MARKER`]'s lead byte,
21356 // [`crate::ast::Sexp::LIST_OPEN`], [`crate::ast::Sexp::LIST_CLOSE`],
21357 // [`crate::ast::Sexp::COMMENT_LEAD`], and both
21358 // [`crate::ast::Atom::bool_literal`] spellings' lead bytes.
21359 // Otherwise the two-char `,@` splice-promotion arm inside the
21360 // reader would ambiguously route through the splice arm AND a
21361 // sibling algebra's arm at the outer dispatch — e.g. if
21362 // `SPLICE_DISCRIMINATOR` aliased [`Sexp::LIST_OPEN`], a source
21363 // `,(a b)` would ambiguously promote-and-consume the `(` before
21364 // the list-opening arm ran. Pin the disjointness across every
21365 // sibling constant so a future rename catches the collision at
21366 // test time rather than as a silent tokenizer drift.
21367 assert_ne!(
21368 QuoteForm::SPLICE_DISCRIMINATOR,
21369 QuoteForm::Quote.lead_char()
21370 );
21371 assert_ne!(
21372 QuoteForm::SPLICE_DISCRIMINATOR,
21373 QuoteForm::Quasiquote.lead_char()
21374 );
21375 assert_ne!(
21376 QuoteForm::SPLICE_DISCRIMINATOR,
21377 QuoteForm::Unquote.lead_char()
21378 );
21379 assert_ne!(
21380 QuoteForm::SPLICE_DISCRIMINATOR,
21381 QuoteForm::UnquoteSplice.lead_char()
21382 );
21383 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Atom::STR_DELIMITER);
21384 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Sexp::LIST_OPEN);
21385 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Sexp::LIST_CLOSE);
21386 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Sexp::COMMENT_LEAD);
21387 // The KEYWORD_MARKER prefix's canonical LEAD `char` lives at
21388 // the typed `Atom::KEYWORD_MARKER_LEAD` constant on the closed-
21389 // set outer [`Atom`] algebra. Pre-lift this slot held an inline
21390 // `Atom::KEYWORD_MARKER.chars().next().expect(_)` extraction;
21391 // post-lift the byte lives at ONE named constant that the
21392 // [`Atom::KEYWORD_MARKER`] `&'static str` projects to (pinned
21393 // by `atom_keyword_marker_lead_prefixes_keyword_marker`).
21394 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Atom::KEYWORD_MARKER_LEAD);
21395 // Both `#t` / `#f` spellings share `Atom::BOOL_LITERAL_LEAD`
21396 // (`'#'`) as the lead byte. Pre-lift each spelling was
21397 // extracted via `Atom::bool_literal(b).chars().next().expect(_)`
21398 // — TWO assertions for the SAME byte by construction. Post-
21399 // lift both collapse to ONE assertion routing through the
21400 // typed constant; the structural invariant "both spellings
21401 // share the lead byte" lives at
21402 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`
21403 // rather than as a per-spelling boilerplate duplication here.
21404 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Atom::BOOL_LITERAL_LEAD);
21405 }
21406
21407 #[test]
21408 fn quote_form_promote_via_next_char_only_promotes_unquote_on_splice_discriminator() {
21409 // Pin the closed-set promotion table: EXACTLY the singleton
21410 // `(Unquote, SPLICE_DISCRIMINATOR) → Some(UnquoteSplice)` arm
21411 // triggers; every other `(variant, char)` pairing yields
21412 // `None`. Sweeps every `QuoteForm::ALL` variant against the
21413 // splice discriminator AND against a broad rejection set
21414 // (whitespace, `(`, `)`, `;`, `Atom::STR_DELIMITER`, `a`, `,`,
21415 // `'`, `` ` ``) so a regression that widens the promotion
21416 // table (e.g. promotes `Quote` on `@` to a phantom variant,
21417 // or promotes `Unquote` on `'` after a copy-paste drift)
21418 // surfaces at test time. Sibling to
21419 // `quote_form_from_lead_char_decodes_every_distinct_lead_char_to_default_variant`
21420 // one axis over on the closed-set entry-char algebra — this
21421 // sweep pins the two-char extension of that one-char decode.
21422 for qf in QuoteForm::ALL {
21423 let promoted = qf.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21424 let expected = if matches!(qf, QuoteForm::Unquote) {
21425 Some(QuoteForm::UnquoteSplice)
21426 } else {
21427 None
21428 };
21429 assert_eq!(
21430 promoted, expected,
21431 "QuoteForm::{qf:?} — promotion table drifted on SPLICE_DISCRIMINATOR",
21432 );
21433 // Every non-discriminator char must reject for every variant.
21434 for rejection_char in [
21435 ' ',
21436 '\n',
21437 '\t',
21438 Sexp::LIST_OPEN,
21439 Sexp::LIST_CLOSE,
21440 Sexp::COMMENT_LEAD,
21441 Atom::STR_DELIMITER,
21442 'a',
21443 ',',
21444 '\'',
21445 '`',
21446 '#',
21447 ':',
21448 '!',
21449 '?',
21450 '~',
21451 ] {
21452 assert_eq!(
21453 qf.promote_via_next_char(rejection_char),
21454 None,
21455 "QuoteForm::{qf:?} — promotion table leaked on non-\
21456 discriminator char {rejection_char:?}",
21457 );
21458 }
21459 }
21460 }
21461
21462 #[test]
21463 fn quote_form_promote_via_next_char_is_const_fn_over_the_closed_set() {
21464 // Pin the `const fn` posture by binding a `const` array
21465 // literal of promotions keyed on the closed set. A regression
21466 // that removed the `const` qualifier (dropping the compile-
21467 // time evaluability the reader's peek arm AND future static
21468 // lookup tables key on) fails to compile HERE — the `const`
21469 // context enforces the qualifier without a test-time
21470 // assertion. Sibling posture to
21471 // `quote_form_from_lead_char_is_const_fn_over_the_closed_set`.
21472 const _QUOTE: Option<QuoteForm> =
21473 QuoteForm::Quote.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21474 const _QUASIQUOTE: Option<QuoteForm> =
21475 QuoteForm::Quasiquote.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21476 const _UNQUOTE: Option<QuoteForm> =
21477 QuoteForm::Unquote.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21478 const _SPLICE: Option<QuoteForm> =
21479 QuoteForm::UnquoteSplice.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21480 assert_eq!(_QUOTE, None);
21481 assert_eq!(_QUASIQUOTE, None);
21482 assert_eq!(_UNQUOTE, Some(QuoteForm::UnquoteSplice));
21483 assert_eq!(_SPLICE, None);
21484 }
21485
21486 #[test]
21487 fn quote_form_promote_via_next_char_composes_prefix_from_source_prefix_and_next_char() {
21488 // COMPOSITION IDENTITY: for every `qf: QuoteForm` and every
21489 // `c: char`, if `qf.promote_via_next_char(c) == Some(promoted)`
21490 // then `format!("{}{}", qf.prefix(), c) == promoted.prefix()`.
21491 // Pin the (variant, next char) → promoted-variant projection
21492 // agrees with the reader's rendered `Self::prefix` composition,
21493 // so a regression that drifts one side of the identity
21494 // surfaces immediately. Sibling to
21495 // `quote_form_lead_char_is_first_char_of_prefix_for_every_variant`
21496 // one axis up on the closed-set entry-char algebra.
21497 for qf in QuoteForm::ALL {
21498 if let Some(promoted) = qf.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR) {
21499 let composed = format!("{}{}", qf.prefix(), QuoteForm::SPLICE_DISCRIMINATOR);
21500 assert_eq!(
21501 composed,
21502 promoted.prefix(),
21503 "QuoteForm::{qf:?} — promotion composition drifted from \
21504 promoted prefix ({promoted:?}.prefix() = {:?})",
21505 promoted.prefix(),
21506 );
21507 }
21508 }
21509 }
21510
21511 #[test]
21512 fn quote_form_unquote_splice_prefix_composes_from_unquote_prefix_and_splice_discriminator() {
21513 // STRUCTURAL COMPOSITION LAW: [`QuoteForm::UnquoteSplice`]'s
21514 // two-char prefix `",@"` decomposes cleanly into
21515 // [`QuoteForm::Unquote`]'s prefix `","` + the splice
21516 // discriminator byte `'@'`. Pin the identity directly rather
21517 // than through the promotion table so a regression that
21518 // renamed the discriminator without touching the promotion
21519 // table (or vice versa) surfaces here. This IS the structural
21520 // identity the reader's peek-then-consume arm depends on: the
21521 // tokenizer sees `,` (Unquote lead char), peeks `@`
21522 // (SPLICE_DISCRIMINATOR), and emits UnquoteSplice — the
21523 // rendered prefix identity closes the read↔write duality.
21524 let composed = format!(
21525 "{}{}",
21526 QuoteForm::Unquote.prefix(),
21527 QuoteForm::SPLICE_DISCRIMINATOR,
21528 );
21529 assert_eq!(
21530 composed,
21531 QuoteForm::UnquoteSplice.prefix(),
21532 "UnquoteSplice.prefix() drifted from Unquote.prefix() + \
21533 SPLICE_DISCRIMINATOR — the reader's two-char splice \
21534 promotion identity is broken",
21535 );
21536 }
21537
21538 // ── `QuoteForm::PROMOTIONS` — the closed-set forced-arity array of
21539 // promotion triples on the substrate's quote-family algebra. Pins
21540 // the singleton `(Unquote, SPLICE_DISCRIMINATOR, UnquoteSplice)`
21541 // entry AND its alignment with `promote_via_next_char`'s Some-arm
21542 // AND the family-wide contract sweeps below. Sibling-shape tests to
21543 // the `quote_form_hash_discriminators_*` block on the outer-Sexp
21544 // cache-key axis — that block anchors the `[u8; 4]` byte algebra;
21545 // this block anchors the `[(Self, char, Self); 1]` promotion
21546 // algebra one axis over on the same closed set.
21547
21548 #[test]
21549 fn quote_form_promotions_has_expected_cardinality() {
21550 // FORCED-ARITY CONTRACT: [`QuoteForm::PROMOTIONS`]'s
21551 // cardinality is `1` at the type level — the substrate's
21552 // current promotion algebra has EXACTLY ONE promotion arm
21553 // (`(Unquote, SPLICE_DISCRIMINATOR, UnquoteSplice)`). A
21554 // regression that widened the array without extending
21555 // [`QuoteForm::promote_via_next_char`]'s match body (or vice
21556 // versa) fails compilation because the array literal's arity
21557 // is forced by the `[_; 1]` type annotation. This runtime
21558 // pin closes the same law at a runtime cardinality check
21559 // so a callsite that expects the singleton shape without
21560 // static-arity inference (a dynamic iteration site, an
21561 // audit-log emitter that reports the promotion-algebra size
21562 // for observability) reads through ONE substrate primitive
21563 // rather than through a hand-rolled `1usize` literal.
21564 assert_eq!(
21565 QuoteForm::PROMOTIONS.len(),
21566 1,
21567 "QuoteForm::PROMOTIONS cardinality drifted from the \
21568 substrate's singleton promotion algebra — the closed-set \
21569 array's forced arity is load-bearing on the `(Unquote, \
21570 SPLICE_DISCRIMINATOR) → UnquoteSplice` singleton identity",
21571 );
21572 }
21573
21574 #[test]
21575 fn quote_form_promotions_pin_legacy_splice_promotion_triple() {
21576 // LEGACY-TRIPLE CONTRACT: [`QuoteForm::PROMOTIONS`]'s
21577 // singleton entry is byte-for-byte `(Self::Unquote,
21578 // Self::SPLICE_DISCRIMINATOR, Self::UnquoteSplice)`. Pin each
21579 // column of the triple against its typed source:
21580 // * head == QuoteForm::Unquote (the `,` variant)
21581 // * disc == QuoteForm::SPLICE_DISCRIMINATOR (`'@'`)
21582 // * promoted == QuoteForm::UnquoteSplice (the `,@` variant)
21583 // A regression that drifts ONE column of the triple silently
21584 // redirects the reader's promotion arm to a phantom variant
21585 // AND fails HERE at the per-column identity check rather
21586 // than at silent tokenizer drift where every `,@xs` source
21587 // tokenizes to the wrong closed-set marker.
21588 assert_eq!(
21589 QuoteForm::PROMOTIONS[0].0,
21590 QuoteForm::Unquote,
21591 "QuoteForm::PROMOTIONS[0].0 (head) drifted from Unquote — \
21592 the substrate's singleton promotion arm's head variant \
21593 MUST be Unquote (the only variant whose prefix is the \
21594 lead byte of a longer variant's prefix)",
21595 );
21596 assert_eq!(
21597 QuoteForm::PROMOTIONS[0].1,
21598 QuoteForm::SPLICE_DISCRIMINATOR,
21599 "QuoteForm::PROMOTIONS[0].1 (discriminator) drifted from \
21600 SPLICE_DISCRIMINATOR — the substrate's singleton \
21601 promotion arm's discriminator MUST be the byte the \
21602 reader's peek arm consumes to promote the head variant \
21603 (the ONE `'@'` byte on the closed-set algebra)",
21604 );
21605 assert_eq!(
21606 QuoteForm::PROMOTIONS[0].2,
21607 QuoteForm::UnquoteSplice,
21608 "QuoteForm::PROMOTIONS[0].2 (promoted) drifted from \
21609 UnquoteSplice — the substrate's singleton promotion \
21610 arm's promoted variant MUST be UnquoteSplice (the only \
21611 two-char-prefix variant on the closed-set algebra)",
21612 );
21613 }
21614
21615 #[test]
21616 fn quote_form_promotions_align_with_promote_via_next_char_for_every_entry() {
21617 // ALIGNMENT CONTRACT: sweep [`QuoteForm::PROMOTIONS`] and
21618 // assert that for every `(head, disc, promoted)` entry,
21619 // `head.promote_via_next_char(disc) == Some(promoted)`. This
21620 // is the projection method's forward composition law at the
21621 // closed set — a regression that drifts the promoted-variant
21622 // column of the constant (or the projection method's Some-arm
21623 // return literal) surfaces here rather than as a silent
21624 // tokenizer redirect. Sibling-shape pin to
21625 // `quote_form_hash_discriminators_align_with_all_by_index`
21626 // one axis over on the cache-key byte algebra — that pin
21627 // aligns the `[u8; 4]` array with the projection method's
21628 // per-variant arm; this pin aligns the `[(Self, char, Self);
21629 // 1]` array with the projection method's per-triple arm.
21630 for (head, disc, promoted) in QuoteForm::PROMOTIONS {
21631 let projected = head.promote_via_next_char(disc);
21632 assert_eq!(
21633 projected,
21634 Some(promoted),
21635 "QuoteForm::PROMOTIONS[({head:?}, {disc:?}, \
21636 {promoted:?})] — `promote_via_next_char` drifted \
21637 from the constant's promoted-variant column (got \
21638 {projected:?})",
21639 );
21640 }
21641 }
21642
21643 #[test]
21644 fn quote_form_promotions_compose_prefix_from_source_prefix_and_discriminator_for_every_entry() {
21645 // COMPOSITION LAW (rendered-prefix identity): sweep
21646 // [`QuoteForm::PROMOTIONS`] and assert that for every
21647 // `(head, disc, promoted)` entry, `format!("{}{}",
21648 // head.prefix(), disc) == promoted.prefix()`. The
21649 // (head prefix + discriminator) source-text composition
21650 // agrees byte-for-byte with the promoted variant's rendered
21651 // prefix — the reader's peek-then-consume arm's rendered
21652 // prefix identity closes the read↔write duality across
21653 // every entry in the promotion algebra.
21654 //
21655 // Sibling to the pre-existing
21656 // `quote_form_promote_via_next_char_composes_prefix_from_source_prefix_and_next_char`
21657 // which pins the same law through
21658 // [`QuoteForm::promote_via_next_char`]'s Some-arm rather
21659 // than through the constant's triple directly — this pin
21660 // closes the law at the constant, that pin closes it at the
21661 // projection method. Together the two pins bind the
21662 // rendered-prefix identity to BOTH the substrate primitive
21663 // AND the projection method so a regression that drifts
21664 // ONE side of the identity fails at BOTH pins rather than at
21665 // silent read/write drift where a reader-tokenized `,@xs`
21666 // form's rendered prefix disagrees with its typed marker's
21667 // rendered prefix.
21668 for (head, disc, promoted) in QuoteForm::PROMOTIONS {
21669 let composed = format!("{}{}", head.prefix(), disc);
21670 assert_eq!(
21671 composed,
21672 promoted.prefix(),
21673 "QuoteForm::PROMOTIONS[({head:?}, {disc:?}, \
21674 {promoted:?})] — head.prefix() + disc drifted from \
21675 promoted.prefix() ({:?})",
21676 promoted.prefix(),
21677 );
21678 }
21679 }
21680
21681 #[test]
21682 fn quote_form_promotions_close_promote_via_next_char_against_every_non_promotion_pair() {
21683 // REJECTION CONTRACT: sweep [`QuoteForm::ALL`] × (every
21684 // (head, disc) pair from [`QuoteForm::PROMOTIONS`] plus every
21685 // rejection discriminator distinct from the promotion set's
21686 // discriminator column) and assert that every pair NOT in
21687 // [`QuoteForm::PROMOTIONS`]'s `(head, disc)` projection
21688 // rejects with `None`. A regression that widened the
21689 // promotion algebra (e.g. phantom-promoted [`QuoteForm::Quote`]
21690 // on `'@'` after a copy-paste drift on the match arm's head
21691 // pattern, OR silently promoted `Unquote` on a non-`@`
21692 // discriminator after a drift on the match arm's char
21693 // pattern) fails HERE at the sweep-time rejection assertion
21694 // rather than at silent tokenizer drift where bare `'@xs`
21695 // forms degrade to a phantom `UnquoteSplice`-shaped sequence
21696 // OR bare `,'xs` forms silently promote through the reader.
21697 //
21698 // Sibling-shape pin to the pre-existing
21699 // `quote_form_promote_via_next_char_only_promotes_unquote_on_splice_discriminator`
21700 // which sweeps every variant against SPLICE_DISCRIMINATOR
21701 // AND a hand-rolled rejection char set; this pin extends the
21702 // rejection sweep to compose the rejection set STRUCTURALLY
21703 // from [`QuoteForm::PROMOTIONS`]'s complement rather than
21704 // hand-rolling a rejection char literal list at a callsite
21705 // that would silently drift as the algebra grows.
21706 //
21707 // The rejection char set is composed as: every
21708 // [`QuoteForm::ALL`] variant's [`QuoteForm::lead_char`] (the
21709 // three quote-family lead bytes `{'\'', '`', ','}`), every
21710 // char in [`QuoteForm::PROMOTIONS`]'s discriminator column
21711 // (the ONE `'@'` byte — used to verify that variants NOT in
21712 // the promotion set's head column reject on the same
21713 // discriminator), and a hand-rolled sweep of non-quote-family
21714 // rejection chars (whitespace, structural, reader-punctuation)
21715 // to cover the closed-set-complement rejection surface.
21716 let mut discriminators: Vec<char> =
21717 QuoteForm::ALL.iter().map(|qf| qf.lead_char()).collect();
21718 for (_, disc, _) in QuoteForm::PROMOTIONS {
21719 if !discriminators.contains(&disc) {
21720 discriminators.push(disc);
21721 }
21722 }
21723 for extra in [
21724 ' ',
21725 '\n',
21726 '\t',
21727 Sexp::LIST_OPEN,
21728 Sexp::LIST_CLOSE,
21729 Sexp::COMMENT_LEAD,
21730 Atom::STR_DELIMITER,
21731 'a',
21732 '#',
21733 ':',
21734 '!',
21735 '?',
21736 '~',
21737 ] {
21738 if !discriminators.contains(&extra) {
21739 discriminators.push(extra);
21740 }
21741 }
21742
21743 let promotion_pairs: Vec<(QuoteForm, char)> = QuoteForm::PROMOTIONS
21744 .iter()
21745 .map(|(head, disc, _)| (*head, *disc))
21746 .collect();
21747
21748 for head in QuoteForm::ALL {
21749 for disc in &discriminators {
21750 let in_promotion_set = promotion_pairs.contains(&(head, *disc));
21751 let projected = head.promote_via_next_char(*disc);
21752 if in_promotion_set {
21753 // Positive arms are covered by
21754 // `quote_form_promotions_align_with_promote_via_next_char_for_every_entry`;
21755 // skip here to keep this pin's focus on the
21756 // rejection surface exclusively.
21757 continue;
21758 }
21759 assert_eq!(
21760 projected, None,
21761 "QuoteForm::{head:?}.promote_via_next_char({disc:?}) \
21762 — promotion algebra leaked on a pair NOT in \
21763 QuoteForm::PROMOTIONS (got {projected:?}, \
21764 expected None)",
21765 );
21766 }
21767 }
21768 }
21769
21770 #[test]
21771 fn quote_form_promote_via_next_char_routes_promoted_variant_through_promotions_constant() {
21772 // ROUTING CONTRACT: pin that
21773 // [`QuoteForm::promote_via_next_char`]'s Some-arm return
21774 // BINDS through [`QuoteForm::PROMOTIONS`]`[0].2` rather than
21775 // through an inline [`QuoteForm::UnquoteSplice`] literal.
21776 // Post-lift the projection method's Some-arm reads:
21777 // `Some(Self::PROMOTIONS[0].2)`
21778 // — so a regression that reverts the arm to an inline
21779 // `Some(Self::UnquoteSplice)` fails HERE at the byte-identity
21780 // sweep, WHERE the projected value is compared against
21781 // [`QuoteForm::PROMOTIONS`]`[0].2` directly rather than
21782 // against an inline literal.
21783 //
21784 // Sibling-shape pin to
21785 // `sexp_shape_hash_discriminator_atomic_arms_route_through_atom_kind_outer_hash_discriminator`
21786 // (prior-run 39537b2) one axis over on the cache-key byte
21787 // algebra — that pin binds the shape-level projection's
21788 // atomic-arm collapse to the typed constant; this pin binds
21789 // the promotion projection's Some-arm to the typed triple's
21790 // promoted-variant column. Together the two pins close the
21791 // "projection routes through the substrate primitive" pattern
21792 // across the cache-key axis AND the promotion axis on the
21793 // closed-set algebra.
21794 let projected = QuoteForm::Unquote.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21795 assert_eq!(
21796 projected,
21797 Some(QuoteForm::PROMOTIONS[0].2),
21798 "QuoteForm::Unquote.promote_via_next_char(SPLICE_DISCRIMINATOR) \
21799 — Some-arm return drifted from routing through \
21800 QuoteForm::PROMOTIONS[0].2 (got {projected:?})",
21801 );
21802 }
21803
21804 #[test]
21805 fn quote_form_hash_discriminator_pins_legacy_cache_key_bytes() {
21806 // CACHE-KEY CONTRACT: pre-lift `Hash for Sexp` used the literal
21807 // byte values 3/4/5/6 for Quote/Quasiquote/Unquote/UnquoteSplice
21808 // as the per-variant discriminator. The expansion cache
21809 // (`Expander::cache`) keys on Hash; ANY change to a
21810 // discriminator byte silently invalidates every cached
21811 // expansion across the substrate AND risks collision with the
21812 // reserved bytes the non-quote-family Hash arms use (0=Nil,
21813 // 1=Atom, 2=List). Pin the four legacy values explicitly so a
21814 // regression that re-numbers them surfaces immediately — the
21815 // `QuoteForm` algebra MUST preserve the prior byte mapping
21816 // bit-for-bit.
21817 assert_eq!(QuoteForm::Quote.hash_discriminator(), 3);
21818 assert_eq!(QuoteForm::Quasiquote.hash_discriminator(), 4);
21819 assert_eq!(QuoteForm::Unquote.hash_discriminator(), 5);
21820 assert_eq!(QuoteForm::UnquoteSplice.hash_discriminator(), 6);
21821 }
21822
21823 // ── `QuoteForm::{QUOTE_HASH_DISCRIMINATOR,
21824 // QUASIQUOTE_HASH_DISCRIMINATOR, UNQUOTE_HASH_DISCRIMINATOR,
21825 // UNQUOTE_SPLICE_HASH_DISCRIMINATOR, HASH_DISCRIMINATORS}` —
21826 // per-role `u8` cache-key byte algebra on the closed-set outer
21827 // [`QuoteForm`]. Fourth per-role axis on the algebra alongside the
21828 // reader-prefix (commit a08e61f), diagnostic-label (commit
21829 // 70be157), and iac-forge canonical-form tag (commit bdd624b)
21830 // `&'static str` axes — closes the FOUR production
21831 // byte-vocabularies the closed set carries at ONE `pub(crate)
21832 // const` per (role, vocabulary) pair plus a family-wide ALL array
21833 // per vocabulary.
21834
21835 #[test]
21836 fn quote_form_hash_discriminators_pin_legacy_cache_key_bytes() {
21837 // Pin each per-role `pub(crate) const` at its exact canonical
21838 // `u8` byte. Sibling of
21839 // `quote_form_hash_discriminator_pins_legacy_cache_key_bytes`
21840 // (which pins the method's projection) — this pin asserts the
21841 // `pub(crate) const` value itself, so a regression that drifts
21842 // the constant but leaves the method's arm literal in place
21843 // (unlikely post-lift but structurally distinct) surfaces
21844 // here. The cache-key partition `{3, 4, 5, 6}` is load-bearing
21845 // for the outer-`Sexp` `Hash` body's disjointness contract
21846 // with the reserved bytes `{0, 1, 2}` the non-quote-family
21847 // arms use — a `4u8` drift to `2u8` would silently collide
21848 // with `StructuralKind::List`'s cache-key byte and mis-hash
21849 // every quasi-quote through the list-arm's path.
21850 assert_eq!(QuoteForm::QUOTE_HASH_DISCRIMINATOR, 3);
21851 assert_eq!(QuoteForm::QUASIQUOTE_HASH_DISCRIMINATOR, 4);
21852 assert_eq!(QuoteForm::UNQUOTE_HASH_DISCRIMINATOR, 5);
21853 assert_eq!(QuoteForm::UNQUOTE_SPLICE_HASH_DISCRIMINATOR, 6);
21854 }
21855
21856 #[test]
21857 fn quote_form_hash_discriminator_routes_through_typed_per_role_constants() {
21858 // PATH-UNIFORMITY: `Self::hash_discriminator(self)` returns
21859 // the per-role `pub(crate) const` byte-for-byte per variant,
21860 // catching a regression that reverts ONE arm to an inline
21861 // `3` / `4` / `5` / `6` `u8` literal (or drifts one arm's
21862 // byte silently). Sibling posture to
21863 // `quote_form_prefix_routes_through_typed_per_role_constants`
21864 // on the reader-prefix axis of the SAME closed set.
21865 for (qf, expected) in [
21866 (QuoteForm::Quote, QuoteForm::QUOTE_HASH_DISCRIMINATOR),
21867 (
21868 QuoteForm::Quasiquote,
21869 QuoteForm::QUASIQUOTE_HASH_DISCRIMINATOR,
21870 ),
21871 (QuoteForm::Unquote, QuoteForm::UNQUOTE_HASH_DISCRIMINATOR),
21872 (
21873 QuoteForm::UnquoteSplice,
21874 QuoteForm::UNQUOTE_SPLICE_HASH_DISCRIMINATOR,
21875 ),
21876 ] {
21877 let actual = qf.hash_discriminator();
21878 assert_eq!(
21879 actual, expected,
21880 "QuoteForm::{qf:?}.hash_discriminator() `{actual}` \
21881 drifted from per-role constant `{expected}` — the \
21882 arm must route through the typed `pub(crate) const` \
21883 rather than an inline `u8` literal",
21884 );
21885 }
21886 }
21887
21888 #[test]
21889 fn quote_form_hash_discriminators_has_expected_cardinality() {
21890 // Cardinality contract: `Self::HASH_DISCRIMINATORS.len() == 4`
21891 // — pinned at the declaration site by rustc's forced-arity
21892 // check on `[u8; 4]`. This test surfaces the arity as a
21893 // fail-loud runtime pin so a future refactor that switches
21894 // the array type to `&[u8]` (dropping the compile-time arity
21895 // forcing) doesn't silently loosen the closed-set discipline
21896 // the family relies on. Sibling posture to
21897 // `quote_form_prefixes_has_expected_cardinality`,
21898 // `quote_form_labels_has_expected_cardinality`, and
21899 // `quote_form_iac_forge_tags_has_expected_cardinality` on the
21900 // other three per-role axes of the SAME [`QuoteForm`] closed
21901 // set.
21902 assert_eq!(
21903 QuoteForm::HASH_DISCRIMINATORS.len(),
21904 4,
21905 "QuoteForm::HASH_DISCRIMINATORS cardinality drifted from \
21906 4 — the closed homoiconic-prefix domain admits exactly \
21907 four wrappers by construction; a fifth extension \
21908 surfaces here"
21909 );
21910 }
21911
21912 #[test]
21913 fn quote_form_hash_discriminators_align_with_all_by_index() {
21914 // ALIGNMENT CONTRACT: `Self::HASH_DISCRIMINATORS[i] ==
21915 // Self::ALL[i].hash_discriminator()` element-wise. Pins that
21916 // the typed variant ALL and the `u8` HASH_DISCRIMINATORS ALL
21917 // stay in lockstep under any reorder — a regression that
21918 // reorders ONE array without reordering the other silently
21919 // misaligns every `zip(ALL, HASH_DISCRIMINATORS)` consumer.
21920 // Sibling posture to `quote_form_prefixes_align_with_all_by_index`
21921 // on the reader-prefix axis.
21922 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
21923 assert_eq!(
21924 QuoteForm::HASH_DISCRIMINATORS[i],
21925 qf.hash_discriminator(),
21926 "QuoteForm::HASH_DISCRIMINATORS[{i}] `{disc}` drifted \
21927 from QuoteForm::ALL[{i}] ({qf:?}).hash_discriminator() \
21928 `{via_variant}` — the canonical declaration order of \
21929 the ALL array and the hash_discriminator projection \
21930 must match element-wise",
21931 disc = QuoteForm::HASH_DISCRIMINATORS[i],
21932 via_variant = qf.hash_discriminator(),
21933 );
21934 }
21935 }
21936
21937 #[test]
21938 fn quote_form_hash_discriminators_pairwise_distinct() {
21939 // PAIRWISE DISJOINTNESS: every entry of the
21940 // `HASH_DISCRIMINATORS` array must differ so the outer-`Sexp`
21941 // `Hash` body cannot route two homoiconic prefixes through
21942 // the same cache-key byte — a collision would silently mis-
21943 // hash two structurally-distinct forms to the same
21944 // `Expander::cache` slot. Family-wide sweep over
21945 // `HASH_DISCRIMINATORS × HASH_DISCRIMINATORS` — supersedes
21946 // any per-pair pin and picks up new discriminators
21947 // mechanically. Sibling posture to
21948 // `quote_form_prefixes_pairwise_distinct`.
21949 for (i, a) in QuoteForm::HASH_DISCRIMINATORS.iter().enumerate() {
21950 for (j, b) in QuoteForm::HASH_DISCRIMINATORS.iter().enumerate() {
21951 if i == j {
21952 continue;
21953 }
21954 assert_ne!(
21955 a, b,
21956 "QuoteForm::HASH_DISCRIMINATORS[{i}] `{a}` \
21957 collides with QuoteForm::HASH_DISCRIMINATORS[{j}] \
21958 `{b}` — the outer-Sexp Hash body's cache-key \
21959 partition would route two homoiconic prefixes \
21960 through the same slot"
21961 );
21962 }
21963 }
21964 }
21965
21966 #[test]
21967 fn quote_form_hash_discriminators_disjoint_from_reserved_outer_sexp_bytes() {
21968 // CROSS-AXIS DISJOINTNESS CONTRACT: every entry of
21969 // `Self::HASH_DISCRIMINATORS` must differ from the reserved
21970 // outer-`Sexp` bytes the non-quote-family arms use — `0u8`
21971 // for [`crate::error::StructuralKind::Nil`], `1u8` for the
21972 // [`crate::ast::Sexp::Atom`] outer-carve marker, `2u8` for
21973 // [`crate::error::StructuralKind::List`]. The three carvings
21974 // of the outer-`Sexp` cache-key space jointly cover
21975 // `{0, 1, 2, 3, 4, 5, 6}` with no gaps AND no overlaps; a
21976 // regression that re-numbers a quote-family discriminator
21977 // into the reserved region silently mis-hashes every affected
21978 // form. Pin the disjointness across every reserved byte so a
21979 // future rename catches the collision at test time rather
21980 // than as a silent cache-key drift where
21981 // `Expander::cache` mis-collides live expansions.
21982 let reserved_non_quote_family_bytes: [u8; 3] = [
21983 crate::error::StructuralKind::Nil.hash_discriminator(),
21984 1u8, // Sexp::Atom outer-carve marker (pre-lift inline literal in Hash for Sexp)
21985 crate::error::StructuralKind::List.hash_discriminator(),
21986 ];
21987 for (i, quote_family_byte) in QuoteForm::HASH_DISCRIMINATORS.iter().enumerate() {
21988 for reserved_byte in reserved_non_quote_family_bytes {
21989 assert_ne!(
21990 *quote_family_byte, reserved_byte,
21991 "QuoteForm::HASH_DISCRIMINATORS[{i}] `{quote_family_byte}` \
21992 collides with reserved non-quote-family cache-key byte \
21993 `{reserved_byte}` — the outer-Sexp Hash body's three-\
21994 carving partition is broken"
21995 );
21996 }
21997 }
21998 }
21999
22000 #[test]
22001 fn quote_form_as_unquote_form_projects_two_of_four_subset() {
22002 // The structural-subset gate: only `{Unquote, UnquoteSplice}`
22003 // are template-substitution markers; `{Quote, Quasiquote}` are
22004 // wrappers whose semantic does NOT include substitution. Pin
22005 // the 2-of-4 partition so the `Sexp::as_unquote` derivation's
22006 // closed-set arithmetic stays correct.
22007 assert_eq!(
22008 QuoteForm::Unquote.as_unquote_form(),
22009 Some(UnquoteForm::Unquote)
22010 );
22011 assert_eq!(
22012 QuoteForm::UnquoteSplice.as_unquote_form(),
22013 Some(UnquoteForm::Splice)
22014 );
22015 assert_eq!(QuoteForm::Quote.as_unquote_form(), None);
22016 assert_eq!(QuoteForm::Quasiquote.as_unquote_form(), None);
22017 }
22018
22019 #[test]
22020 fn quote_form_iac_forge_tag_pins_canonical_lisp_tag_strings_for_every_variant() {
22021 // CROSS-CRATE CANONICAL-FORM CONTRACT: the four canonical
22022 // iac-forge tags are load-bearing for inter-crate compatibility
22023 // — `iac_forge::sexpr::SExpr` consumers (BLAKE3 attestation,
22024 // render cache) key on the canonical 2-element-list shape
22025 // `(<tag> <inner>)`. A regression that drifts ONE tag silently
22026 // invalidates every cached canonical form across the substrate
22027 // AND mis-collides with the legacy `SexpShape::label` projection
22028 // that uses the shorter `"unquote-splice"` for the diagnostic
22029 // surface. Pin the four legacy tag values explicitly so a
22030 // regression that re-spells them surfaces immediately.
22031 assert_eq!(QuoteForm::Quote.iac_forge_tag(), "quote");
22032 assert_eq!(QuoteForm::Quasiquote.iac_forge_tag(), "quasiquote");
22033 assert_eq!(QuoteForm::Unquote.iac_forge_tag(), "unquote");
22034 assert_eq!(QuoteForm::UnquoteSplice.iac_forge_tag(), "unquote-splicing");
22035 }
22036
22037 // ── `QuoteForm::{QUOTE_IAC_FORGE_TAG, QUASIQUOTE_IAC_FORGE_TAG,
22038 // UNQUOTE_IAC_FORGE_TAG, UNQUOTE_SPLICE_IAC_FORGE_TAG,
22039 // IAC_FORGE_TAGS}` — per-role `&'static str` iac-forge canonical-
22040 // form tag algebra on the closed-set outer [`QuoteForm`]. Peer of
22041 // the reader-prefix axis's [`QuoteForm::{QUOTE_PREFIX,
22042 // QUASIQUOTE_PREFIX, UNQUOTE_PREFIX, UNQUOTE_SPLICE_PREFIX,
22043 // PREFIXES}`] block above — the same closed set carries TWO
22044 // orthogonal byte vocabularies (the Lisp reader prefixes the
22045 // tokenizer classifies on, the iac-forge canonical-form tags the
22046 // cross-crate attestation layer round-trips through), each now
22047 // pinned at a per-role `pub const` plus a paired ALL array.
22048 #[test]
22049 fn quote_form_per_role_iac_forge_tags_pin_canonical_bytes() {
22050 // Pin each per-role `pub const` at its exact canonical byte
22051 // sequence. Sweeping via a per-variant pair rather than four
22052 // hand-rolled `assert_eq!(QuoteForm::X_IAC_FORGE_TAG, "x")`
22053 // asserts (a) that each constant IS the load-bearing byte
22054 // string, and (b) that the pairing between the const and the
22055 // spelling is enforced at rustc's constant-folding-level so a
22056 // future rename of the const surfaces here as a spelling drift
22057 // rather than as a silent canonical-form regression.
22058 for (label, actual, expected) in [
22059 (
22060 "QUOTE_IAC_FORGE_TAG",
22061 QuoteForm::QUOTE_IAC_FORGE_TAG,
22062 "quote",
22063 ),
22064 (
22065 "QUASIQUOTE_IAC_FORGE_TAG",
22066 QuoteForm::QUASIQUOTE_IAC_FORGE_TAG,
22067 "quasiquote",
22068 ),
22069 (
22070 "UNQUOTE_IAC_FORGE_TAG",
22071 QuoteForm::UNQUOTE_IAC_FORGE_TAG,
22072 "unquote",
22073 ),
22074 (
22075 "UNQUOTE_SPLICE_IAC_FORGE_TAG",
22076 QuoteForm::UNQUOTE_SPLICE_IAC_FORGE_TAG,
22077 "unquote-splicing",
22078 ),
22079 ] {
22080 assert_eq!(
22081 actual, expected,
22082 "QuoteForm::{label} drifted from canonical `{expected}` bytes"
22083 );
22084 }
22085 }
22086
22087 #[test]
22088 fn quote_form_iac_forge_tag_routes_through_typed_per_role_constants() {
22089 // PATH-UNIFORMITY: `Self::iac_forge_tag(self)` returns the
22090 // per-role `pub const` byte-for-byte per variant, catching a
22091 // regression that reverts ONE arm to an inline `"quote"` /
22092 // `"quasiquote"` / `"unquote"` / `"unquote-splicing"` string
22093 // literal (or drifts one arm's bytes silently). Sibling posture
22094 // to `quote_form_prefix_routes_through_typed_per_role_constants`
22095 // on the reader-prefix axis of the SAME closed set.
22096 for (qf, expected) in [
22097 (QuoteForm::Quote, QuoteForm::QUOTE_IAC_FORGE_TAG),
22098 (QuoteForm::Quasiquote, QuoteForm::QUASIQUOTE_IAC_FORGE_TAG),
22099 (QuoteForm::Unquote, QuoteForm::UNQUOTE_IAC_FORGE_TAG),
22100 (
22101 QuoteForm::UnquoteSplice,
22102 QuoteForm::UNQUOTE_SPLICE_IAC_FORGE_TAG,
22103 ),
22104 ] {
22105 let actual = qf.iac_forge_tag();
22106 assert_eq!(
22107 actual, expected,
22108 "QuoteForm::{qf:?}.iac_forge_tag() `{actual}` drifted from \
22109 per-role constant `{expected}` — the arm must route \
22110 through the typed constant rather than an inline literal",
22111 );
22112 }
22113 }
22114
22115 #[test]
22116 fn quote_form_iac_forge_tags_has_expected_cardinality() {
22117 // Cardinality contract: `Self::IAC_FORGE_TAGS.len() == 4` —
22118 // pinned at the declaration site by rustc's forced-arity check
22119 // on `[&'static str; 4]`. This test surfaces the arity as a
22120 // fail-loud runtime pin so a future refactor that switches the
22121 // array type to `&[&'static str]` (dropping the compile-time
22122 // arity forcing) doesn't silently loosen the closed-set
22123 // discipline the family relies on. Sibling posture to
22124 // `quote_form_prefixes_has_expected_cardinality` on the peer
22125 // reader-prefix axis.
22126 assert_eq!(
22127 QuoteForm::IAC_FORGE_TAGS.len(),
22128 4,
22129 "QuoteForm::IAC_FORGE_TAGS cardinality drifted from 4 — the \
22130 closed homoiconic-prefix domain admits exactly four \
22131 wrappers by construction; a fifth extension surfaces here"
22132 );
22133 }
22134
22135 #[test]
22136 fn quote_form_iac_forge_tags_align_with_all_by_index() {
22137 // ALIGNMENT CONTRACT: `Self::IAC_FORGE_TAGS[i] ==
22138 // Self::ALL[i].iac_forge_tag()` element-wise. Pins that the
22139 // typed variant ALL and the `&'static str` IAC_FORGE_TAGS ALL
22140 // stay in lockstep under any reorder — a regression that
22141 // reorders ONE array without reordering the other silently
22142 // misaligns every `zip(ALL, IAC_FORGE_TAGS)` consumer (cross-
22143 // crate attestation renderers, LSP canonical-form completion
22144 // providers, metric-label emitters, coverage reporters).
22145 // Sibling posture to `quote_form_prefixes_align_with_all_by_index`
22146 // on the peer reader-prefix axis.
22147 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
22148 assert_eq!(
22149 QuoteForm::IAC_FORGE_TAGS[i],
22150 qf.iac_forge_tag(),
22151 "QuoteForm::IAC_FORGE_TAGS[{i}] `{tag}` drifted from \
22152 QuoteForm::ALL[{i}] ({qf:?}).iac_forge_tag() `{via_variant}` \
22153 — the canonical declaration order of the ALL array \
22154 and the iac-forge tag projection must match element-wise",
22155 tag = QuoteForm::IAC_FORGE_TAGS[i],
22156 via_variant = qf.iac_forge_tag(),
22157 );
22158 }
22159 }
22160
22161 #[test]
22162 fn quote_form_iac_forge_tags_pairwise_distinct() {
22163 // PAIRWISE DISJOINTNESS: every entry of the `IAC_FORGE_TAGS`
22164 // array must differ so the cross-crate canonical-form decoder
22165 // (any future `IAC_FORGE_TAGS.iter().find(|t| *t == head)`
22166 // sweep, any BLAKE3 attestation key comparison) cannot route
22167 // two homoiconic prefixes through the same tag arm. Family-wide
22168 // sweep over `IAC_FORGE_TAGS × IAC_FORGE_TAGS` — supersedes any
22169 // per-pair pin and picks up new tags mechanically. Sibling
22170 // posture to `quote_form_prefixes_pairwise_distinct` on the
22171 // peer reader-prefix axis.
22172 for (i, a) in QuoteForm::IAC_FORGE_TAGS.iter().enumerate() {
22173 for (j, b) in QuoteForm::IAC_FORGE_TAGS.iter().enumerate() {
22174 if i == j {
22175 continue;
22176 }
22177 assert_ne!(
22178 a, b,
22179 "QuoteForm::IAC_FORGE_TAGS[{i}] `{a}` collides with \
22180 QuoteForm::IAC_FORGE_TAGS[{j}] `{b}` — the \
22181 canonical-form decoder's cascade would route two \
22182 homoiconic prefixes through the same tag arm"
22183 );
22184 }
22185 }
22186 }
22187
22188 #[test]
22189 fn quote_form_iac_forge_tags_diverge_from_prefixes_pairwise() {
22190 // AXIS-ORTHOGONALITY: `IAC_FORGE_TAGS` (the cross-crate
22191 // canonical-form axis) and `PREFIXES` (the Lisp reader-prefix
22192 // axis) span two distinct byte vocabularies on the SAME closed
22193 // set. Every per-variant pair must disagree: the canonical
22194 // tags are word-length identifiers (`"quote"`, `"quasiquote"`,
22195 // `"unquote"`, `"unquote-splicing"`) while the reader prefixes
22196 // are punctuation (`"'"`, `` "`" ``, `","`, `",@"`). A
22197 // regression that collapsed the two axes (a hypothetical
22198 // consolidation PR that reused `iac_forge_tag()`'s bytes at
22199 // the reader prefix site, or vice versa) would silently break
22200 // either the source-code round-trip (readers no longer see
22201 // `"'"`) OR the canonical-form round-trip (attestation keys
22202 // no longer see `"quote"`). Sweep every variant's per-axis
22203 // pair — supersedes any per-variant pin and picks up new
22204 // prefix/tag pairs mechanically.
22205 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
22206 let prefix = QuoteForm::PREFIXES[i];
22207 let tag = QuoteForm::IAC_FORGE_TAGS[i];
22208 assert_ne!(
22209 prefix, tag,
22210 "QuoteForm::{qf:?} — reader prefix `{prefix}` collides \
22211 with iac-forge tag `{tag}`; the two axes must span \
22212 distinct byte vocabularies on the closed set",
22213 );
22214 }
22215 }
22216
22217 #[test]
22218 fn quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice() {
22219 // BOUNDARY-DISTINCT CONTRACT: the iac-forge canonical tag for
22220 // `UnquoteSplice` is `"unquote-splicing"` (Common Lisp idiom,
22221 // load-bearing for canonical-form round-trip with the iac-forge
22222 // ecosystem), distinct from `SexpShape::label`'s shorter
22223 // `"unquote-splice"` (the substrate's diagnostic label idiom).
22224 // The two projections key the SAME closed-set on TWO distinct
22225 // boundaries — pinning the divergence here documents the
22226 // intent: a future "consolidation" PR that homogenizes them
22227 // would silently break either the iac-forge canonical-form
22228 // round-trip OR the operator-facing diagnostic surface. The
22229 // three other variants (Quote, Quasiquote, Unquote) DO match
22230 // across both projections — pin that path-uniformity too so a
22231 // regression that drifts one of the three matched arms surfaces
22232 // immediately. Sibling-arm sweep so the (variant, tag) AND
22233 // (variant, label) pairings stay load-bearing under reordering
22234 // refactors.
22235 use crate::error::SexpShape;
22236 assert_eq!(
22237 QuoteForm::Quote.iac_forge_tag(),
22238 SexpShape::Quote.label(),
22239 "quote tag/label agreement"
22240 );
22241 assert_eq!(
22242 QuoteForm::Quasiquote.iac_forge_tag(),
22243 SexpShape::Quasiquote.label(),
22244 "quasiquote tag/label agreement"
22245 );
22246 assert_eq!(
22247 QuoteForm::Unquote.iac_forge_tag(),
22248 SexpShape::Unquote.label(),
22249 "unquote tag/label agreement"
22250 );
22251 // The intentional divergence — load-bearing for the iac-forge
22252 // canonical form vs the substrate's diagnostic label.
22253 assert_eq!(QuoteForm::UnquoteSplice.iac_forge_tag(), "unquote-splicing");
22254 assert_eq!(SexpShape::UnquoteSplice.label(), "unquote-splice");
22255 assert_ne!(
22256 QuoteForm::UnquoteSplice.iac_forge_tag(),
22257 SexpShape::UnquoteSplice.label(),
22258 "the two projections must disagree at UnquoteSplice — the CL canonical \
22259 form requires '-splicing' while the substrate's diagnostic label uses \
22260 the shorter '-splice'; consolidating them would break either side",
22261 );
22262 }
22263
22264 #[test]
22265 fn quote_form_from_iac_forge_tag_decodes_each_canonical_tag_to_its_variant() {
22266 // TYPED INVERSE CONTRACT: the four canonical CL tag literals
22267 // `"quote"` / `"quasiquote"` / `"unquote"` / `"unquote-splicing"`
22268 // decode through `QuoteForm::from_iac_forge_tag` to their exact
22269 // `QuoteForm` variant — the inbound iac-forge canonical-form
22270 // decode surface's per-arm truth table. Sibling posture to
22271 // `quote_form_iac_forge_tag_pins_canonical_lisp_tag_strings_for_every_variant`
22272 // on the OUTBOUND projection axis: that pin binds each variant to
22273 // its canonical tag; THIS pin binds each canonical tag to its
22274 // variant, closing the (outbound, inbound) roundtrip pair at ONE
22275 // typed method on the algebra rather than at TWO surfaces the
22276 // consumer would have to hand-roll independently.
22277 //
22278 // A regression that drifts ONE arm's inbound decode (a future
22279 // refactor that inlines the four-arm sweep and drops the
22280 // `"quasiquote"` arm, a byte-drifted rename that leaves the
22281 // outbound `iac_forge_tag()` unchanged but breaks the inverse)
22282 // fails-loudly here on the affected arm before any downstream
22283 // iac-forge canonical-form consumer surfaces the drift.
22284 assert_eq!(
22285 QuoteForm::from_iac_forge_tag("quote"),
22286 Some(QuoteForm::Quote),
22287 );
22288 assert_eq!(
22289 QuoteForm::from_iac_forge_tag("quasiquote"),
22290 Some(QuoteForm::Quasiquote),
22291 );
22292 assert_eq!(
22293 QuoteForm::from_iac_forge_tag("unquote"),
22294 Some(QuoteForm::Unquote),
22295 );
22296 assert_eq!(
22297 QuoteForm::from_iac_forge_tag("unquote-splicing"),
22298 Some(QuoteForm::UnquoteSplice),
22299 );
22300 }
22301
22302 #[test]
22303 fn quote_form_from_iac_forge_tag_round_trips_through_iac_forge_tag_for_every_variant() {
22304 // TYPED ROUNDTRIP CONTRACT: for every `qf` in `QuoteForm::ALL`,
22305 // `QuoteForm::from_iac_forge_tag(qf.iac_forge_tag()) == Some(qf)`
22306 // — the composition of the outbound projection with the inbound
22307 // inverse decoder yields the identity on the closed four-arm
22308 // domain. Sibling posture to
22309 // `quote_form_lead_char_round_trips_through_from_lead_char_for_every_variant`
22310 // one axis over on the reader-lead-char inverse decoder — both
22311 // pin the (forward, inverse) composition-identity at the closed
22312 // set's canonical carrier variants without relying on the
22313 // per-arm inbound truth table above (which pins the arm-by-arm
22314 // decoder mapping; this pin binds the closed-set-wide roundtrip
22315 // property that emerges from the arm mapping).
22316 //
22317 // A regression that breaks ONE variant's roundtrip (a future
22318 // refactor that drops `QuoteForm::Unquote` from `Self::ALL`
22319 // silently while leaving the outbound `iac_forge_tag()` arm
22320 // intact, an inbound decoder that returns `Some(Self::Quote)`
22321 // for an unrelated variant's tag) fails-loudly here through the
22322 // closed-set sweep, catching the drift on the affected variant.
22323 for &qf in QuoteForm::ALL.iter() {
22324 let outbound = qf.iac_forge_tag();
22325 let inbound = QuoteForm::from_iac_forge_tag(outbound);
22326 assert_eq!(
22327 inbound,
22328 Some(qf),
22329 "QuoteForm::{qf:?} — outbound iac_forge_tag `{outbound}` \
22330 failed to round-trip through from_iac_forge_tag",
22331 );
22332 }
22333 }
22334
22335 #[test]
22336 fn quote_form_from_iac_forge_tag_rejects_empty_input() {
22337 // EMPTY-INPUT REJECTION: the empty string `""` is structurally
22338 // outside the four-arm canonical CL tag closed set — no variant
22339 // projects to `""` through `iac_forge_tag`, so the inverse
22340 // decode rejects cleanly with `None`. Pins the empty-input
22341 // boundary case operators hit when a canonical-form field is
22342 // absent or blank but the decode is reached anyway (a
22343 // deserialization codepath that reads an empty tag slot, an
22344 // LSP quick-fix that surfaces an empty completion buffer).
22345 // Sibling posture to `parse_label_rejects_empty_input` on the
22346 // closed-set trait's parse-rejection axis one vocabulary over
22347 // (reader-punctuation) — both pin the empty-input rejection so
22348 // no future implementor can drift the decoder's empty-input
22349 // behavior accidentally.
22350 assert_eq!(QuoteForm::from_iac_forge_tag(""), None);
22351 }
22352
22353 #[test]
22354 fn quote_form_from_iac_forge_tag_is_case_sensitive() {
22355 // CASE-SENSITIVE CONTRACT: the four canonical CL tag literals
22356 // are the exact byte sequences the iac-forge canonical form
22357 // renders; case drift between the caller's input and the
22358 // canonical literal is a REJECTION, not a normalization. Pin
22359 // the case-sensitive contract across (a) an all-uppercase
22360 // drift (`"QUOTE"`), (b) a title-case drift (`"Quote"`), and
22361 // (c) an internal-uppercase drift on the multi-word tag
22362 // (`"Unquote-Splicing"`, `"unquote-Splicing"`) — the three
22363 // representative case-drift shapes future operator input
22364 // could reach.
22365 //
22366 // A regression that relaxes the decode to case-insensitive (an
22367 // overzealous normalization pass, an `eq_ignore_ascii_case`
22368 // introduction) silently subsumes the substrate-wide
22369 // case-sensitive convention that binds the iac-forge
22370 // canonical-form bytes to the SexpShape diagnostic-label bytes'
22371 // path-uniformity (the two vocabularies share three of four
22372 // arms byte-for-byte and disagree at UnquoteSplice — any
22373 // future case-insensitive relaxation on ONE axis would silently
22374 // bifurcate the two vocabularies' convention).
22375 assert_eq!(QuoteForm::from_iac_forge_tag("QUOTE"), None);
22376 assert_eq!(QuoteForm::from_iac_forge_tag("Quote"), None);
22377 assert_eq!(QuoteForm::from_iac_forge_tag("Unquote-Splicing"), None);
22378 assert_eq!(QuoteForm::from_iac_forge_tag("unquote-Splicing"), None);
22379 }
22380
22381 #[test]
22382 fn quote_form_from_iac_forge_tag_rejects_substrate_diagnostic_label_bytes() {
22383 // AXIS-BOUNDARY CONTRACT: the SHORTER substrate diagnostic
22384 // label `"unquote-splice"` (which `SexpShape::UnquoteSplice.label()`
22385 // renders) MUST reject through the iac-forge canonical-form
22386 // decoder — the CL canonical form requires the LONGER
22387 // `"unquote-splicing"` per the intentional divergence pinned by
22388 // `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`.
22389 // The two vocabularies are ORTHOGONAL axes on the SAME closed
22390 // four-arm outer set; a decoder keyed on the iac-forge axis
22391 // MUST reject an input on the diagnostic-label axis, or the
22392 // (canonical-form, diagnostic-label) axis-orthogonality
22393 // collapses silently.
22394 //
22395 // A regression that accepts the shorter substrate diagnostic
22396 // label at the iac-forge inbound decode (a hypothetical
22397 // "consolidation" that merges the two vocabularies at the
22398 // decoder boundary, a future decoder that walks BOTH
22399 // `SexpShape::LABELS` AND `Self::IAC_FORGE_TAGS`) would
22400 // silently bifurcate every iac-forge round-trip consumer:
22401 // the outbound `iac_forge_tag()` still renders
22402 // `"unquote-splicing"`, but the inbound decode accepts BOTH
22403 // spellings — a canonical form emitted with the shorter
22404 // substrate diagnostic label would re-decode as
22405 // `QuoteForm::UnquoteSplice` even though it was never a valid
22406 // iac-forge canonical form to begin with. Pin the rejection
22407 // explicitly so the axis boundary stays enforced at the
22408 // decoder itself, not just at the outbound projection.
22409 assert_eq!(QuoteForm::from_iac_forge_tag("unquote-splice"), None);
22410 }
22411
22412 #[test]
22413 fn quote_form_from_iac_forge_tag_rejects_non_canonical_tag_strings() {
22414 // GENERAL-REJECTION CONTRACT: inputs outside the four-arm
22415 // canonical CL tag image reject cleanly with `None`. Pins a
22416 // representative sweep across (a) arbitrary non-tag words that
22417 // share no substring with the canonical vocabulary
22418 // (`"hello"`, `"foo"`), (b) reader-punctuation bytes that
22419 // belong to the ORTHOGONAL `Self::PREFIXES` axis and MUST
22420 // route through `FromStr` (not this decoder) — the vocabulary
22421 // boundary the axis-orthogonality contract carries, (c) the
22422 // SexpShape diagnostic labels for non-quote-family arms
22423 // (`"symbol"`, `"list"`) which project to `None` through
22424 // `SexpShape::iac_forge_tag` and therefore MUST reject here
22425 // too.
22426 //
22427 // A regression that accepts a reader-punctuation byte here (a
22428 // future consolidated decoder that walks BOTH the prefix
22429 // vocabulary AND the iac-forge-tag vocabulary) would silently
22430 // subsume the [`FromStr`] surface's exclusive claim to the
22431 // reader-punctuation axis, breaking every consumer that binds
22432 // decoder identity to vocabulary axis. Pin the rejections
22433 // explicitly so the vocabulary axis stays enforced at the
22434 // decoder boundary itself, not just at the outbound projection.
22435 for input in [
22436 "hello", "foo",
22437 // Reader-punctuation vocabulary — belongs to
22438 // `Self::PREFIXES` / `Self::FromStr`, MUST reject here.
22439 "'", "`", ",", ",@",
22440 // SexpShape labels for non-quote-family shapes — no
22441 // iac-forge tag projection at these variants, MUST reject.
22442 "symbol", "list", "nil",
22443 ] {
22444 assert_eq!(
22445 QuoteForm::from_iac_forge_tag(input),
22446 None,
22447 "QuoteForm::from_iac_forge_tag({input:?}) accepted a \
22448 non-canonical iac-forge tag input — the decoder must \
22449 reject every string outside the four-arm canonical CL \
22450 tag image",
22451 );
22452 }
22453 }
22454
22455 #[test]
22456 fn quote_form_from_iac_forge_tag_composes_through_iac_forge_tags_array() {
22457 // COMPOSITION-LAW CONTRACT: sweeping the family-wide
22458 // `Self::IAC_FORGE_TAGS` array through `from_iac_forge_tag`
22459 // yields the parallel `Self::ALL` array element-wise —
22460 // `from_iac_forge_tag(Self::IAC_FORGE_TAGS[i]) ==
22461 // Some(Self::ALL[i])` for every `i in 0..4`. The composition
22462 // binds the two family-wide forced-arity arrays through the
22463 // typed inverse decoder at ONE typed sweep on the algebra,
22464 // matching the alignment law
22465 // `quote_form_iac_forge_tags_align_with_all_by_index` on the
22466 // outbound projection sibling.
22467 //
22468 // Enforces the (typed variant, canonical tag) forward-and-
22469 // back closure at the array level: a regression that drifts
22470 // ONE array's contents against the other (a future refactor
22471 // that reorders `Self::IAC_FORGE_TAGS` without reordering
22472 // `Self::ALL`, a rename that breaks the alignment) fails-
22473 // loudly here through the composition sweep before any
22474 // downstream `zip(ALL, IAC_FORGE_TAGS)` consumer with an
22475 // inbound decoder in hand surfaces the misalignment.
22476 for (i, &tag) in QuoteForm::IAC_FORGE_TAGS.iter().enumerate() {
22477 let decoded = QuoteForm::from_iac_forge_tag(tag);
22478 let expected = QuoteForm::ALL[i];
22479 assert_eq!(
22480 decoded,
22481 Some(expected),
22482 "QuoteForm::from_iac_forge_tag(IAC_FORGE_TAGS[{i}] = {tag:?}) \
22483 decoded to {decoded:?}, expected Some({expected:?}) \
22484 (the composition of IAC_FORGE_TAGS with from_iac_forge_tag \
22485 must yield ALL element-wise)",
22486 );
22487 }
22488 }
22489
22490 #[test]
22491 fn quote_form_from_iac_forge_tag_is_injective_on_canonical_domain() {
22492 // INJECTIVITY CONTRACT: distinct canonical CL tags in
22493 // `Self::IAC_FORGE_TAGS` decode to distinct typed variants
22494 // through `from_iac_forge_tag` — the inverse decoder is
22495 // injective on the closed four-arm canonical-tag domain.
22496 // Sibling posture to the outbound
22497 // `quote_form_iac_forge_tags_pairwise_distinct` on the same
22498 // closed set: that pin asserts the four canonical-tag literals
22499 // are pairwise distinct; THIS pin asserts the DECODED variants
22500 // are also pairwise distinct — the pair together enforces
22501 // BOTH sides of the two-way injectivity contract that the
22502 // (typed variant, canonical tag) bijection carries on the
22503 // closed set.
22504 //
22505 // A regression that decodes two distinct canonical tags to
22506 // the same typed variant (a future refactor that collapses
22507 // `Quasiquote` and `Quote` decode arms silently, an off-by-
22508 // one in the linear sweep that returns the wrong variant for
22509 // ONE tag) fails-loudly here at the decoded-set cardinality
22510 // check before any downstream consumer surfaces the decoded-
22511 // side collapse.
22512 let decoded: Vec<QuoteForm> = QuoteForm::IAC_FORGE_TAGS
22513 .iter()
22514 .filter_map(|tag| QuoteForm::from_iac_forge_tag(tag))
22515 .collect();
22516 assert_eq!(
22517 decoded.len(),
22518 QuoteForm::ALL.len(),
22519 "QuoteForm::from_iac_forge_tag failed to decode one or more \
22520 canonical tags — the composition of IAC_FORGE_TAGS with \
22521 from_iac_forge_tag lost {} arms (expected {} — the closed \
22522 set's cardinality)",
22523 QuoteForm::ALL.len() - decoded.len(),
22524 QuoteForm::ALL.len(),
22525 );
22526 for i in 0..decoded.len() {
22527 for j in (i + 1)..decoded.len() {
22528 assert_ne!(
22529 decoded[i], decoded[j],
22530 "QuoteForm::from_iac_forge_tag is not injective on the \
22531 canonical-tag domain — decoded[{i}] ({:?}) and \
22532 decoded[{j}] ({:?}) collide",
22533 decoded[i], decoded[j],
22534 );
22535 }
22536 }
22537 }
22538
22539 #[test]
22540 fn quote_form_sexp_shape_pins_canonical_shape_identity_for_every_variant() {
22541 // CLOSED-SET SHAPE-PROJECTION CONTRACT: each `QuoteForm` variant
22542 // projects to its matching `SexpShape` variant — load-bearing for
22543 // the (Sexp variant, SexpShape variant) pairing the substrate's
22544 // outer-shape projection `domain::sexp_shape` routes through.
22545 // Sibling-arm sweep so the four pairings stay load-bearing under
22546 // reordering refactors. A regression that drifts ONE arm (e.g.
22547 // routes `QuoteForm::Quote` to `SexpShape::Quasiquote`) surfaces
22548 // here immediately rather than as a silent operator-facing
22549 // diagnostic drift at every `LispError::TypeMismatch.got` slot
22550 // for a quote-family witness.
22551 use crate::error::SexpShape;
22552 assert_eq!(QuoteForm::Quote.sexp_shape(), SexpShape::Quote);
22553 assert_eq!(QuoteForm::Quasiquote.sexp_shape(), SexpShape::Quasiquote);
22554 assert_eq!(QuoteForm::Unquote.sexp_shape(), SexpShape::Unquote);
22555 assert_eq!(
22556 QuoteForm::UnquoteSplice.sexp_shape(),
22557 SexpShape::UnquoteSplice
22558 );
22559 }
22560
22561 #[test]
22562 fn quote_form_sexp_shape_composes_with_label_for_canonical_short_diagnostic_string() {
22563 // COMPOSITION-LAW CONTRACT: `qf.sexp_shape().label()` is the
22564 // canonical short diagnostic string for the quote-family marker
22565 // — `"quote"`, `"quasiquote"`, `"unquote"`, `"unquote-splice"`.
22566 // The composition law binds the substrate's typed marker
22567 // (`QuoteForm`) to its diagnostic surface (`SexpShape::label`)
22568 // through ONE algebra so a future change to either projection's
22569 // label (e.g. a substrate-wide rename of `"unquote-splice"` to
22570 // `"splice"`) rides through the typed composition rather than
22571 // requiring an inline match at every diagnostic-construction
22572 // site that previously hand-paired the marker with its label.
22573 // Pin the short labels here — DISTINCT from the iac-forge tag's
22574 // `"unquote-splicing"` (load-bearing for the boundary distinction
22575 // already pinned by
22576 // `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`).
22577 assert_eq!(QuoteForm::Quote.sexp_shape().label(), "quote");
22578 assert_eq!(QuoteForm::Quasiquote.sexp_shape().label(), "quasiquote");
22579 assert_eq!(QuoteForm::Unquote.sexp_shape().label(), "unquote");
22580 assert_eq!(
22581 QuoteForm::UnquoteSplice.sexp_shape().label(),
22582 "unquote-splice"
22583 );
22584 }
22585
22586 #[test]
22587 fn quote_form_label_projects_each_variant_to_canonical_diagnostic_label() {
22588 // PER-ARM CONTRACT: pin the outer-`QuoteForm` `Self::label`
22589 // projection produces the FOUR canonical short diagnostic labels
22590 // byte-for-byte across every reachable quote-family variant.
22591 // Pre-lift the outer-`QuoteForm` diagnostic-label projection had
22592 // no typed primitive on the marker algebra — a consumer with a
22593 // `QuoteForm` in hand wanting the canonical short label had to
22594 // spell the two-step composition `qf.sexp_shape().label()` at
22595 // every callsite (a shape pinned as a load-bearing composition
22596 // law by `quote_form_sexp_shape_composes_with_label_for_canonical_short_diagnostic_string`
22597 // one arm above), OR go through `qf.wrap(inner).type_name()`
22598 // which wraps and projects for no runtime purpose. Post-lift the
22599 // FOUR arms bind at ONE typed projection on the outer-`QuoteForm`
22600 // algebra that routes through `SexpShape::label` — the
22601 // (QuoteForm variant, label string) pairing binds at ONE typed
22602 // algebra composition spanning THREE typed layers (`QuoteForm`
22603 // → `SexpShape` → `&'static str`).
22604 //
22605 // Sibling-shape pin to
22606 // `atom_label_projects_each_variant_to_canonical_diagnostic_label`
22607 // one algebra layer down (outer-`Atom` label pin) and
22608 // `sexp_type_name_covers_every_variant` one algebra layer up
22609 // (outer-`Sexp` type_name pin). A regression that drifts ONE
22610 // arm's mapping (e.g. renaming `"unquote-splice"` to `"splice"`
22611 // inline here, dropping the `Unquote → "unquote"` boundary
22612 // rename) fails-loudly at THIS test AND the sibling
22613 // `SexpShape::label` per-arm pin.
22614 assert_eq!(QuoteForm::Quote.label(), "quote");
22615 assert_eq!(QuoteForm::Quasiquote.label(), "quasiquote");
22616 assert_eq!(QuoteForm::Unquote.label(), "unquote");
22617 assert_eq!(QuoteForm::UnquoteSplice.label(), "unquote-splice");
22618 }
22619
22620 #[test]
22621 fn quote_form_label_composes_through_sexp_shape_label_for_every_variant() {
22622 // COMPOSITION-LAW CONTRACT: `qf.label() == qf.sexp_shape().label()`
22623 // for every reachable quote-family marker — the outer-`QuoteForm`
22624 // label projection is structurally derived through `Self::sexp_shape`
22625 // + `SexpShape::label` rather than through a parallel four-arm
22626 // inline match on the outer-`QuoteForm` algebra. Pin the
22627 // composition law so a future refactor that re-inlines the four
22628 // quote-family literals here (and gains its own drift surface
22629 // separate from the `SexpShape::label` canonical site) surfaces
22630 // immediately. The pointer-equality check pins the composition
22631 // produces the SAME `&'static str` (not just a byte-equal copy)
22632 // for every variant — proof the routing hits ONE static literal
22633 // site (`SexpShape::label` via `QuoteForm::sexp_shape().label()`)
22634 // rather than a parallel inline table on the outer-`QuoteForm`
22635 // algebra.
22636 //
22637 // Sibling-shape pin to
22638 // `atom_label_composes_through_kind_label_for_every_variant` on
22639 // the outer-`Atom` value / `AtomKind` marker pair and
22640 // `sexp_type_name_method_composes_through_shape_label_for_every_outer_shape`
22641 // on the outer-`Sexp` value / `SexpShape` marker pair. The three
22642 // routing pins jointly enforce the (outer-value, canonical label)
22643 // pairing stays a full three-layer typed composition on every
22644 // typed-value algebra rather than degrading to a per-layer inline
22645 // literal table.
22646 for qf in QuoteForm::ALL {
22647 let via_label = qf.label();
22648 let via_composition = qf.sexp_shape().label();
22649 assert_eq!(
22650 via_label, via_composition,
22651 "QuoteForm::label() must route through self.sexp_shape().label() \
22652 for {qf:?} — drift here means the lift was reverted to inline arms",
22653 );
22654 assert!(
22655 std::ptr::eq(via_label.as_ptr(), via_composition.as_ptr()),
22656 "QuoteForm::label() must return the SAME `&'static str` as \
22657 self.sexp_shape().label() for {qf:?} — pointer drift means \
22658 the lift composes through a parallel literal table rather \
22659 than routing into the canonical SexpShape::label site",
22660 );
22661 }
22662 }
22663
22664 #[test]
22665 fn quote_form_label_agrees_with_sexp_type_name_at_every_quote_form_arm() {
22666 // CROSS-ALGEBRA AGREEMENT CONTRACT: for every quote-family marker
22667 // `qf` and every inner body `inner`, `qf.label() ==
22668 // qf.wrap(inner.clone()).type_name()`. The agreement is a TYPED
22669 // CONSEQUENCE of the two typed compositions —
22670 // `qf.wrap(inner).type_name()` routes through `Sexp::shape()`'s
22671 // quote-family arms which compose with `SexpShape::label`
22672 // byte-for-byte with `qf.sexp_shape().label()` (which itself IS
22673 // the body of `qf.label()`). A regression that drifts either side
22674 // of the cross-algebra bridge (an outer-`QuoteForm` label
22675 // re-inlined onto a different literal, an outer-`Sexp` quote-arm
22676 // re-routed through a stale shape projection, a
22677 // `QuoteForm::sexp_shape` arm that swaps two markers) fails-
22678 // loudly here rather than as a silent operator-facing diagnostic
22679 // drift at every consumer that pattern-matches on the outer-
22680 // `Sexp` label vs the outer-`QuoteForm` label independently.
22681 //
22682 // Sibling posture to
22683 // `atom_label_agrees_with_sexp_type_name_at_every_atom_arm` on
22684 // the atomic-payload carving — that pin binds the outer-value-
22685 // level vocabulary containment (`Atom::label ==
22686 // Sexp::Atom(_).type_name()`), this pin binds the same
22687 // containment on the quote-family carving (`QuoteForm::label ==
22688 // QuoteForm::wrap(_).type_name()`) so the THREE-layer typed
22689 // composition on the outer-`QuoteForm` algebra and the FOUR-
22690 // layer typed composition on the outer-`Sexp` algebra agree at
22691 // their common quote-family arms.
22692 let inner = Sexp::symbol("x");
22693 for qf in QuoteForm::ALL {
22694 let via_quote_form = qf.label();
22695 let via_sexp = qf.wrap(inner.clone()).type_name();
22696 assert_eq!(
22697 via_quote_form, via_sexp,
22698 "QuoteForm::label() must agree with QuoteForm::wrap(_).type_name() \
22699 for {qf:?} — cross-algebra label drift at the quote-family arms \
22700 would fracture the typed diagnostic vocabulary between the \
22701 outer-QuoteForm and outer-Sexp algebras",
22702 );
22703 assert!(
22704 std::ptr::eq(via_quote_form.as_ptr(), via_sexp.as_ptr()),
22705 "QuoteForm::label() must return the SAME `&'static str` as \
22706 QuoteForm::wrap(_).type_name() for {qf:?} — pointer drift means \
22707 one algebra layer re-inlined the literal rather than routing \
22708 into the canonical `SexpShape::label` site",
22709 );
22710 }
22711 }
22712
22713 #[test]
22714 fn quote_form_label_diverges_from_iac_forge_tag_for_unquote_splice() {
22715 // BOUNDARY-DISTINCT CONTRACT: at the `UnquoteSplice` arm,
22716 // `qf.label() == "unquote-splice"` (the substrate's diagnostic
22717 // label idiom) while `qf.iac_forge_tag() == "unquote-splicing"`
22718 // (the Common-Lisp canonical form, load-bearing for canonical-
22719 // form round-trip with the iac-forge ecosystem). The two
22720 // projections key the SAME closed-set on TWO distinct boundaries
22721 // — pinning the divergence on the NEW typed peer documents the
22722 // intent: a future "consolidation" PR that homogenizes `label`
22723 // and `iac_forge_tag` at the `UnquoteSplice` arm would silently
22724 // break either the iac-forge canonical-form round-trip OR the
22725 // operator-facing diagnostic surface. Sibling-arm posture to
22726 // `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`
22727 // which pinned the divergence at the `qf.sexp_shape().label()`
22728 // composition; this pin lifts the divergence contract onto the
22729 // NEW `QuoteForm::label` typed peer. The three other variants
22730 // (Quote, Quasiquote, Unquote) DO match across both projections
22731 // — pin that path-uniformity too so a regression that drifts one
22732 // of the three matched arms surfaces immediately.
22733 assert_eq!(
22734 QuoteForm::Quote.iac_forge_tag(),
22735 QuoteForm::Quote.label(),
22736 "quote tag/label agreement",
22737 );
22738 assert_eq!(
22739 QuoteForm::Quasiquote.iac_forge_tag(),
22740 QuoteForm::Quasiquote.label(),
22741 "quasiquote tag/label agreement",
22742 );
22743 assert_eq!(
22744 QuoteForm::Unquote.iac_forge_tag(),
22745 QuoteForm::Unquote.label(),
22746 "unquote tag/label agreement",
22747 );
22748 // The intentional divergence — load-bearing for the iac-forge
22749 // canonical form vs the substrate's diagnostic label.
22750 assert_eq!(QuoteForm::UnquoteSplice.iac_forge_tag(), "unquote-splicing");
22751 assert_eq!(QuoteForm::UnquoteSplice.label(), "unquote-splice");
22752 assert_ne!(
22753 QuoteForm::UnquoteSplice.iac_forge_tag(),
22754 QuoteForm::UnquoteSplice.label(),
22755 "the two projections must disagree at UnquoteSplice — the CL canonical \
22756 form requires '-splicing' while the substrate's diagnostic label uses \
22757 the shorter '-splice'; consolidating them would break either side",
22758 );
22759 }
22760
22761 #[test]
22762 fn quote_form_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte() {
22763 // ALIAS CONTRACT: pin every one of the four per-role
22764 // `pub const QuoteForm::*_LABEL` aliases equals the corresponding
22765 // `pub const SexpShape::*_LABEL` byte-for-byte — so the QuoteForm
22766 // ⊂ SexpShape marker-vocabulary containment routes through the
22767 // typed `pub const QuoteForm::V_LABEL: &'static str =
22768 // SexpShape::V_LABEL` alias chain rather than through two
22769 // independent literal-discipline sites. A regression that renames
22770 // the SexpShape side without updating the QuoteForm alias
22771 // pointing at it fails-loudly here with the exact axis identified
22772 // (QUOTE / QUASIQUOTE / UNQUOTE / UNQUOTE_SPLICE); a regression
22773 // that re-inlines the QuoteForm constant to a fresh literal still
22774 // passes this pin but loses the alias-chain typing (which is what
22775 // `quote_form_label_arms_route_through_per_role_labels_for_every_variant`
22776 // + `quote_form_labels_align_with_all_by_index` catch in
22777 // combination).
22778 //
22779 // Sibling-shape pin to
22780 // `atom_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
22781 // on the peer 6-of-12 atomic-payload carving and
22782 // `structural_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
22783 // on the peer 2-of-12 structural-residual carving — this pin
22784 // closes the fourth and final SexpShape sub-carving's alias-chain
22785 // contract at the exact same shape.
22786 assert_eq!(QuoteForm::QUOTE_LABEL, SexpShape::QUOTE_LABEL);
22787 assert_eq!(QuoteForm::QUASIQUOTE_LABEL, SexpShape::QUASIQUOTE_LABEL);
22788 assert_eq!(QuoteForm::UNQUOTE_LABEL, SexpShape::UNQUOTE_LABEL);
22789 assert_eq!(
22790 QuoteForm::UNQUOTE_SPLICE_LABEL,
22791 SexpShape::UNQUOTE_SPLICE_LABEL,
22792 );
22793 }
22794
22795 #[test]
22796 fn quote_form_label_arms_route_through_per_role_labels_for_every_variant() {
22797 // PATH-UNIFORMITY: `QuoteForm::V.label()` MUST equal the per-role
22798 // `pub const QuoteForm::V_LABEL` for every `v: QuoteForm`. Pre-
22799 // lift the four quote-family marker labels were reachable through
22800 // `QuoteForm::label` (the composition `self.sexp_shape().label()`
22801 // — routing into `SexpShape::*_LABEL`) OR through direct
22802 // `SexpShape::*_LABEL` reach-across; post-lift each variant's
22803 // canonical bytes are reachable through the per-role
22804 // `QuoteForm::*_LABEL` alias too. Pin the byte-equality between
22805 // the runtime projection and the compile-time alias so a
22806 // regression that renames the alias without updating the arm (or
22807 // vice versa) fails-loudly at the exact axis.
22808 //
22809 // Sibling-shape pin to
22810 // `atom_kind_label_arms_route_through_per_role_labels_for_every_variant`
22811 // and
22812 // `structural_kind_label_arms_route_through_per_role_labels_for_every_variant`
22813 // — those pin the peer 6-of-12 and 2-of-12 sub-carvings; this pin
22814 // binds the QuoteForm 4-of-12 subset algebra's per-role aliases
22815 // against `QuoteForm::label`'s composition-routed arms so the
22816 // four quote-family marker labels project through ONE aliased
22817 // typed source of truth per role rather than through per-consumer
22818 // inline literals.
22819 assert_eq!(QuoteForm::Quote.label(), QuoteForm::QUOTE_LABEL);
22820 assert_eq!(QuoteForm::Quasiquote.label(), QuoteForm::QUASIQUOTE_LABEL);
22821 assert_eq!(QuoteForm::Unquote.label(), QuoteForm::UNQUOTE_LABEL);
22822 assert_eq!(
22823 QuoteForm::UnquoteSplice.label(),
22824 QuoteForm::UNQUOTE_SPLICE_LABEL,
22825 );
22826 }
22827
22828 #[test]
22829 fn quote_form_labels_has_expected_cardinality() {
22830 // Cardinality pin: `LABELS.len() == 4` matches `ALL.len()` so a
22831 // refactor that loosens the type to `&'static [&'static str]`
22832 // fails HERE (the `[_; 4]` slot cannot be sliced silently), and
22833 // a variant added to `ALL` without a matching `LABELS` row fails
22834 // the pair-arity gate at the array literal itself before this
22835 // test even runs. The pin doubles as an operator-visible mark of
22836 // the family's cardinality across the substrate — four quote-
22837 // family markers, matching the four-arm carving of the parent
22838 // `SexpShape::LABELS` (the quote-family subset of the twelve
22839 // canonical outer-shape labels).
22840 assert_eq!(QuoteForm::LABELS.len(), 4);
22841 assert_eq!(QuoteForm::LABELS.len(), QuoteForm::ALL.len());
22842 }
22843
22844 #[test]
22845 fn quote_form_labels_align_with_all_by_index() {
22846 // ALIGNMENT PIN: sweep `LABELS[i] == ALL[i].label()` so any
22847 // `zip(ALL, LABELS)` consumer reads a coherent (variant, label)
22848 // pair off ONE forced-arity array pair. The declaration-order
22849 // pin makes a family-wide consumer that walks the ALL / LABELS
22850 // pair in lockstep (an LSP completion bar keyed on
22851 // `QuoteForm::LABELS`, a Sekiban metric emitter labeling
22852 // `tatara_lisp_quote_family_label_total{label}` by the per-index
22853 // label) read one canonical (variant, bytes) pair per slot
22854 // rather than routing through per-consumer paired-iteration. A
22855 // regression that reorders LABELS without also reordering ALL
22856 // (or vice versa) fails-loudly at the exact index that drifted.
22857 assert_eq!(QuoteForm::LABELS.len(), QuoteForm::ALL.len());
22858 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
22859 assert_eq!(
22860 QuoteForm::LABELS[i],
22861 qf.label(),
22862 "QuoteForm::LABELS[{i}] `{lbl}` drifted from \
22863 QuoteForm::ALL[{i}].label() `{via_variant}` — the \
22864 canonical ALL ordering and the LABELS ordering must \
22865 match element-wise",
22866 lbl = QuoteForm::LABELS[i],
22867 via_variant = qf.label(),
22868 );
22869 }
22870 }
22871
22872 #[test]
22873 fn quote_form_labels_pairwise_distinct() {
22874 // 4x4 pairwise sweep so a collision between any two labels
22875 // (which would silently degrade two distinct quote-family
22876 // markers to the SAME diagnostic bytes and violate the closed-
22877 // set FromStr round-trip through `SexpShape::from_str`) fails-
22878 // loudly at the exact pair. Distinctness is already enforced
22879 // structurally at the parent superset by
22880 // `sexp_shape_labels_pairwise_distinct` (the twelve-variant
22881 // sweep), but this pin is a secondary guard focused on the per-
22882 // role `pub const` surface of the QuoteForm 4-of-12 subset
22883 // directly rather than the runtime projection through
22884 // `SexpShape::label`.
22885 for (i, a) in QuoteForm::LABELS.iter().enumerate() {
22886 for (j, b) in QuoteForm::LABELS.iter().enumerate() {
22887 if i == j {
22888 continue;
22889 }
22890 assert_ne!(
22891 a, b,
22892 "QuoteForm::LABELS[{i}] ({a:?}) collides with \
22893 QuoteForm::LABELS[{j}] ({b:?}) — two distinct \
22894 quote-family markers cannot share diagnostic bytes",
22895 );
22896 }
22897 }
22898 }
22899
22900 #[test]
22901 fn quote_form_labels_match_sexp_shape_labels_element_wise_via_alias_chain() {
22902 // CROSS-AXIS PIN: `QuoteForm::LABELS[i] ==
22903 // QuoteForm::ALL[i].sexp_shape().label()` for every index. Closes
22904 // the alias-chain identity at the family-wide array level:
22905 // LABELS is NOT a fresh literal table but the projection of ALL
22906 // through the composition, materialized once at declaration time
22907 // through the four per-role aliases. A regression that re-inlines
22908 // LABELS to fresh literals (or that re-inlines each per-role
22909 // constant off the aliased `SexpShape::*_LABEL` source of truth)
22910 // still passes the pairwise-distinct + cardinality + alignment
22911 // pins but drifts the alias chain — this pin catches that drift.
22912 //
22913 // Sibling-shape pin to
22914 // `atom_kind_labels_match_sexp_shape_labels_element_wise_via_alias_chain`
22915 // and
22916 // `structural_kind_labels_match_sexp_shape_labels_element_wise_via_alias_chain`
22917 // — those pin the peer 6-of-12 and 2-of-12 sub-carvings' alias
22918 // chains through their `SexpShape` parents; this pin closes the
22919 // fourth and final SexpShape sub-carving's alias-chain identity
22920 // at the same shape.
22921 assert_eq!(QuoteForm::LABELS.len(), QuoteForm::ALL.len());
22922 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
22923 let via_composition = qf.sexp_shape().label();
22924 assert_eq!(
22925 QuoteForm::LABELS[i],
22926 via_composition,
22927 "QuoteForm::LABELS[{i}] `{lbl}` drifted from \
22928 QuoteForm::ALL[{i}].sexp_shape().label() `{via}` — \
22929 the alias-chain composition law `LABELS[i] == \
22930 ALL[i].sexp_shape().label()` binds the family-wide \
22931 array to the composition through sexp_shape + \
22932 SexpShape::label; a drift here means the per-role \
22933 aliases were re-inlined off their SexpShape source of \
22934 truth",
22935 lbl = QuoteForm::LABELS[i],
22936 via = via_composition,
22937 );
22938 }
22939 }
22940
22941 #[test]
22942 fn quote_form_sexp_shape_paired_with_as_quote_form_preserves_pre_lift_pairing_for_every_sexp() {
22943 // PATH-UNIFORMITY CONTRACT: the (Sexp variant, SexpShape variant)
22944 // pairing the pre-lift `sexp_shape` arms encoded inline is now
22945 // structurally derived via
22946 // `s.as_quote_form().map(|(qf, _)| qf.sexp_shape())` for every
22947 // quote-family `Sexp` shape. Pin the derivation against the
22948 // pre-lift pairing across all four quote-family wrapper variants
22949 // so a regression that drifts ONE side of the typed algebra
22950 // (e.g. a `QuoteForm::Quote → SexpShape::Quasiquote` typo, or a
22951 // `Sexp::as_quote_form` arm that swaps two markers) surfaces
22952 // immediately. Non-quote-family shapes project to `None` from
22953 // `as_quote_form`, which the assertion arm skips — the typed
22954 // closed-set partition is load-bearing for the early-return
22955 // shape of the lifted `domain::sexp_shape`.
22956 use crate::error::SexpShape;
22957 let cases: &[(&str, Sexp, SexpShape)] = &[
22958 (
22959 "quote",
22960 Sexp::Quote(Box::new(Sexp::symbol("x"))),
22961 SexpShape::Quote,
22962 ),
22963 (
22964 "quasiquote",
22965 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
22966 SexpShape::Quasiquote,
22967 ),
22968 (
22969 "unquote",
22970 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
22971 SexpShape::Unquote,
22972 ),
22973 (
22974 "unquote-splice",
22975 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
22976 SexpShape::UnquoteSplice,
22977 ),
22978 ];
22979 for (label, sexp, expected_shape) in cases {
22980 let (qf, _) = sexp
22981 .as_quote_form()
22982 .unwrap_or_else(|| panic!("{label} must project through as_quote_form"));
22983 assert_eq!(
22984 qf.sexp_shape(),
22985 *expected_shape,
22986 "{label} drifted from typed (QuoteForm, SexpShape) pairing"
22987 );
22988 }
22989 }
22990
22991 #[test]
22992 fn as_unquote_derives_from_as_quote_form_composed_with_subset_gate() {
22993 // Path-uniformity: `Sexp::as_unquote` is now derived from
22994 // `as_quote_form().and_then(|(qf, inner)| qf.as_unquote_form()
22995 // .map(|uf| (uf, inner)))`. Pin that the derived semantic
22996 // agrees with the pre-lift arm-based one across the closed
22997 // Sexp variant set — every shape's projection through
22998 // `as_unquote` must equal the manual composition through
22999 // `as_quote_form` + `QuoteForm::as_unquote_form`. A regression
23000 // that drifts ONE projection's posture from the composition
23001 // becomes a typed test failure.
23002 let shapes: Vec<(&str, Sexp)> = vec![
23003 ("nil", Sexp::Nil),
23004 ("symbol", Sexp::symbol("x")),
23005 ("keyword", Sexp::keyword("k")),
23006 ("string", Sexp::string("s")),
23007 ("int", Sexp::int(7)),
23008 ("float", Sexp::float(2.5)),
23009 ("bool", Sexp::boolean(true)),
23010 ("empty list", Sexp::List(vec![])),
23011 ("non-empty list", Sexp::List(vec![Sexp::symbol("op")])),
23012 ("quote", Sexp::Quote(Box::new(Sexp::symbol("x")))),
23013 ("quasiquote", Sexp::Quasiquote(Box::new(Sexp::symbol("x")))),
23014 ("unquote", Sexp::Unquote(Box::new(Sexp::symbol("x")))),
23015 (
23016 "unquote-splice",
23017 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
23018 ),
23019 ];
23020 for (label, sexp) in &shapes {
23021 let via_direct = sexp.as_unquote();
23022 let via_composed = sexp
23023 .as_quote_form()
23024 .and_then(|(qf, inner)| qf.as_unquote_form().map(|uf| (uf, inner)));
23025 assert_eq!(
23026 via_direct, via_composed,
23027 "as_unquote drifted from composed as_quote_form+as_unquote_form at {label}"
23028 );
23029 }
23030 }
23031
23032 #[test]
23033 fn hash_for_sexp_structural_arms_route_through_structural_kind_hash_discriminator() {
23034 // CACHE-KEY CONTRACT (Hash side, structural axis): pin that
23035 // the lifted `Hash for Sexp` impl produces byte-identical
23036 // hashes for the two structural-residual arms (`Sexp::Nil`,
23037 // `Sexp::List(_)`) as the pre-lift implementation, routing
23038 // through `StructuralKind::hash_discriminator` so the
23039 // (Sexp variant, cache-key byte) pairing is structurally
23040 // bound to the algebra rather than threaded through inline
23041 // `0u8` / `2u8` literals. We compute the expected hash via a
23042 // SECOND hasher that manually drives the pre-lift `<discr>
23043 // .hash(h); <rest>.hash(h)` sequence, then compare. A
23044 // regression that drifts the discriminator (e.g. renumbers
23045 // `StructuralKind::List` to `1u8` and collides with the
23046 // atomic-carve outer marker byte) OR re-orders the (discr,
23047 // rest) sequence surfaces here as a hash-value mismatch.
23048 // Sibling arm-sweep to
23049 // `hash_for_sexp_preserves_legacy_quote_family_discriminator_bytes`
23050 // on the quote-family axis (four wrapper variants) — the
23051 // three closed-set carvings' hash arms all route through
23052 // ONE typed method per carving, and this pin binds the
23053 // structural-residual arm's post-lift shape against the
23054 // pre-lift byte-stream.
23055 use crate::error::StructuralKind;
23056 use std::collections::hash_map::DefaultHasher;
23057 use std::hash::{Hash, Hasher};
23058 // (label, sexp, expected-first-discr-byte, extra-hash-sequence
23059 // closure that drives the residual hash body after the
23060 // discriminator byte)
23061 #[allow(clippy::type_complexity)]
23062 let cases: [(&str, Sexp, u8, Box<dyn Fn(&mut DefaultHasher)>); 2] = [
23063 ("nil", Sexp::Nil, 0u8, Box::new(|_h: &mut DefaultHasher| {})),
23064 (
23065 "list",
23066 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
23067 2u8,
23068 Box::new(|h: &mut DefaultHasher| {
23069 let items = vec![Sexp::symbol("a"), Sexp::int(1)];
23070 items.len().hash(h);
23071 for i in &items {
23072 i.hash(h);
23073 }
23074 }),
23075 ),
23076 ];
23077 for (label, sexp, expected_discr, extra) in &cases {
23078 let mut via_impl = DefaultHasher::new();
23079 sexp.hash(&mut via_impl);
23080
23081 let mut via_legacy = DefaultHasher::new();
23082 expected_discr.hash(&mut via_legacy);
23083 extra(&mut via_legacy);
23084
23085 assert_eq!(
23086 via_impl.finish(),
23087 via_legacy.finish(),
23088 "Hash for Sexp drifted from legacy (discr={expected_discr}, rest) sequence at {label}",
23089 );
23090 }
23091 // Composition pin: pointer-independent structural equality —
23092 // the discriminator byte value MUST agree between the typed
23093 // projection and the pre-lift literal, so a regression that
23094 // re-inlines the two arm literals as a parallel match-table
23095 // (`Sexp::Nil => 0u8`, `Sexp::List(_) => 2u8`) still passes
23096 // the hash-value sweep above but drifts if the future
23097 // `StructuralKind::hash_discriminator` is re-numbered — this
23098 // pin binds the composition IDENTITY (not just the value
23099 // equality) between the outer `Hash for Sexp` body and the
23100 // typed algebra.
23101 assert_eq!(StructuralKind::Nil.hash_discriminator(), 0u8);
23102 assert_eq!(StructuralKind::List.hash_discriminator(), 2u8);
23103 }
23104
23105 #[test]
23106 fn hash_for_sexp_preserves_legacy_quote_family_discriminator_bytes() {
23107 // CACHE-KEY CONTRACT (Hash side): pin that the lifted
23108 // `Hash for Sexp` impl produces byte-identical hashes for the
23109 // four quote-family variants as the pre-lift implementation.
23110 // We compute the expected hash via a SECOND hasher that
23111 // manually drives the pre-lift `<discr>.hash(h); inner.hash(h)`
23112 // sequence, then compare. A regression that drifts the
23113 // discriminator OR re-orders the (discr, inner) sequence
23114 // surfaces here as a hash-value mismatch.
23115 use std::collections::hash_map::DefaultHasher;
23116 let inner = Sexp::symbol("payload");
23117 for (label, sexp, expected_discr) in [
23118 ("quote", Sexp::Quote(Box::new(inner.clone())), 3u8),
23119 ("quasiquote", Sexp::Quasiquote(Box::new(inner.clone())), 4u8),
23120 ("unquote", Sexp::Unquote(Box::new(inner.clone())), 5u8),
23121 (
23122 "unquote-splice",
23123 Sexp::UnquoteSplice(Box::new(inner.clone())),
23124 6u8,
23125 ),
23126 ] {
23127 let mut via_impl = DefaultHasher::new();
23128 sexp.hash(&mut via_impl);
23129
23130 let mut via_legacy = DefaultHasher::new();
23131 expected_discr.hash(&mut via_legacy);
23132 inner.hash(&mut via_legacy);
23133
23134 assert_eq!(
23135 via_impl.finish(),
23136 via_legacy.finish(),
23137 "Hash for Sexp drifted from legacy (discr={expected_discr}, inner) sequence at {label}"
23138 );
23139 }
23140 }
23141
23142 #[test]
23143 fn sexp_hash_discriminator_pins_legacy_outer_cache_key_bytes() {
23144 // CACHE-KEY CONTRACT: pre-lift `Hash for Sexp` used the literal
23145 // byte values 0/1/2 for Nil/Atom/List AND delegated 3/4/5/6 for
23146 // Quote/Quasiquote/Unquote/UnquoteSplice through
23147 // `QuoteForm::hash_discriminator`. The macro-expansion cache
23148 // (`Expander::cache`) keys on Hash; ANY change to a discriminator
23149 // byte silently invalidates every cached expansion across the
23150 // substrate. Pin the seven legacy values explicitly so a
23151 // regression that re-numbers them surfaces immediately — the
23152 // outer-`Sexp` algebra MUST preserve the prior byte mapping bit-
23153 // for-bit. Sibling posture to
23154 // `atom_kind_hash_discriminator_pins_legacy_atom_cache_key_bytes`
23155 // and `quote_form_hash_discriminator_pins_legacy_cache_key_bytes`
23156 // on the two sub-carvings.
23157 assert_eq!(Sexp::Nil.hash_discriminator(), 0);
23158 assert_eq!(Sexp::symbol("x").hash_discriminator(), 1);
23159 assert_eq!(Sexp::keyword("k").hash_discriminator(), 1);
23160 assert_eq!(Sexp::string("s").hash_discriminator(), 1);
23161 assert_eq!(Sexp::int(7).hash_discriminator(), 1);
23162 assert_eq!(Sexp::float(2.5).hash_discriminator(), 1);
23163 assert_eq!(Sexp::boolean(true).hash_discriminator(), 1);
23164 assert_eq!(Sexp::List(vec![]).hash_discriminator(), 2);
23165 assert_eq!(Sexp::Quote(Box::new(Sexp::Nil)).hash_discriminator(), 3);
23166 assert_eq!(
23167 Sexp::Quasiquote(Box::new(Sexp::Nil)).hash_discriminator(),
23168 4
23169 );
23170 assert_eq!(Sexp::Unquote(Box::new(Sexp::Nil)).hash_discriminator(), 5);
23171 assert_eq!(
23172 Sexp::UnquoteSplice(Box::new(Sexp::Nil)).hash_discriminator(),
23173 6
23174 );
23175 }
23176
23177 #[test]
23178 fn sexp_hash_discriminator_bytes_partition_zero_through_six_injectively() {
23179 // Closed-set injectivity across the seven outer-`Sexp` variants:
23180 // the seven discriminator bytes MUST partition `{0, 1, 2, 3, 4,
23181 // 5, 6}` injectively so two distinct outer variants never
23182 // conflate their outer cache-key byte — a violation here means
23183 // the cache could conflate e.g. `Sexp::List(vec![])` and
23184 // `Sexp::Quote(Box::new(Sexp::Nil))` at the outer discriminator
23185 // slot. Uses ONE seed per outer-variant sweep. Sibling pin to
23186 // `atom_kind_hash_discriminator_bytes_are_pairwise_disjoint` (six-
23187 // arm partition of `{0..=5}` nested inside the Atom outer byte
23188 // `1`) and `quote_form_hash_discriminator_bytes_are_pairwise_
23189 // disjoint` (four-arm partition of `{3..=6}` surfaced through
23190 // this outer method's quote-family arms). Together the three
23191 // partitions jointly cover the outer-Sexp discriminator space
23192 // `{0..=6}` — the joint partition contract is pinned by
23193 // `sexp_hash_discriminator_partitions_the_full_outer_discriminator_space_zero_through_six`
23194 // below.
23195 let bytes: Vec<u8> = [
23196 Sexp::Nil,
23197 Sexp::symbol("x"),
23198 Sexp::List(vec![]),
23199 Sexp::Quote(Box::new(Sexp::Nil)),
23200 Sexp::Quasiquote(Box::new(Sexp::Nil)),
23201 Sexp::Unquote(Box::new(Sexp::Nil)),
23202 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
23203 ]
23204 .iter()
23205 .map(Sexp::hash_discriminator)
23206 .collect();
23207 let mut sorted = bytes.clone();
23208 sorted.sort_unstable();
23209 let mut deduped = sorted.clone();
23210 deduped.dedup();
23211 assert_eq!(
23212 sorted, deduped,
23213 "Sexp hash discriminator bytes must be pairwise disjoint across the seven outer variants"
23214 );
23215 assert_eq!(sorted, vec![0, 1, 2, 3, 4, 5, 6]);
23216 }
23217
23218 #[test]
23219 fn sexp_hash_discriminator_partitions_the_full_outer_discriminator_space_zero_through_six() {
23220 // JOINT PARTITION CONTRACT: the outer-`Sexp` discriminator byte
23221 // space `{0..=6}` is jointly covered by the three carvings' typed
23222 // discriminator methods — pinning the joint contract makes the
23223 // prefix-uniqueness invariant a compile-time-verified theorem
23224 // rather than a per-carving isolated pin.
23225 //
23226 // Sexp-outer: `{0, 1, 2, 3, 4, 5, 6}` via `Sexp::hash_discriminator`
23227 // (the outer arm-partition method; the entire outer space).
23228 // AtomKind: `{0, 1, 2, 3, 4, 5}` via `AtomKind::hash_discriminator`
23229 // (nested inside the Atom outer byte `1`; NOT part of the outer
23230 // partition, but pinned here to document the sub-carving space).
23231 // QuoteForm: `{3, 4, 5, 6}` via `QuoteForm::hash_discriminator`
23232 // (surfaced through the four quote-family arms of the outer
23233 // method; MUST equal the outer sweep's `{3..=6}` slice).
23234 //
23235 // A regression that drifts the outer method's quote-family arms
23236 // from the delegated `QuoteForm::hash_discriminator` bytes (e.g.
23237 // routes `Sexp::Quote(_)` to `7u8` inline) fails-loudly here.
23238 // Sibling posture to
23239 // `structural_kind_hash_discriminator_disjoint_from_atom_outer_carve_byte_and_quote_form_hash_discriminator_partition`
23240 // on the sub-carving axis — this pin binds the OUTER joint
23241 // partition; that pin would bind a hypothetical structural sub-
23242 // carving's disjointness.
23243 let outer_seeds: Vec<Sexp> = vec![
23244 Sexp::Nil,
23245 Sexp::symbol("x"),
23246 Sexp::List(vec![]),
23247 Sexp::Quote(Box::new(Sexp::Nil)),
23248 Sexp::Quasiquote(Box::new(Sexp::Nil)),
23249 Sexp::Unquote(Box::new(Sexp::Nil)),
23250 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
23251 ];
23252 let outer_bytes: std::collections::BTreeSet<u8> =
23253 outer_seeds.iter().map(Sexp::hash_discriminator).collect();
23254 let expected: std::collections::BTreeSet<u8> = (0u8..=6u8).collect();
23255 assert_eq!(
23256 outer_bytes, expected,
23257 "Sexp::hash_discriminator must cover exactly the outer discriminator space {{0..=6}}"
23258 );
23259
23260 let quote_bytes: std::collections::BTreeSet<u8> = QuoteForm::ALL
23261 .iter()
23262 .map(|qf| qf.hash_discriminator())
23263 .collect();
23264 let quote_slice: std::collections::BTreeSet<u8> = (3u8..=6u8).collect();
23265 assert_eq!(
23266 quote_bytes, quote_slice,
23267 "QuoteForm::hash_discriminator must cover {{3..=6}} — the quote-family slice of the outer Sexp partition"
23268 );
23269 assert!(
23270 quote_bytes.is_subset(&outer_bytes),
23271 "QuoteForm::hash_discriminator bytes must be a subset of Sexp::hash_discriminator's outer partition"
23272 );
23273 }
23274
23275 #[test]
23276 fn sexp_hash_discriminator_atom_arm_collapses_over_every_atom_kind() {
23277 // OUTER-CARVING CONTRACT (atomic arm): every `AtomKind` variant
23278 // projects through `Sexp::Atom` to the SAME outer discriminator
23279 // byte `1u8` — the atomic outer arm is a single-byte marker on
23280 // the outer partition, with the per-atom-kind inner byte
23281 // (`AtomKind::hash_discriminator`'s `{0..=5}`) nested INSIDE
23282 // `Atom::hash` — NOT surfaced through this method. Pin the six-
23283 // way collapse so a regression that drifts ONE atom kind's outer
23284 // routing (e.g. routes `Sexp::Atom(Atom::Int(_))` to `7u8`
23285 // inline) surfaces here immediately. Sibling posture to
23286 // `sexp_hash_discriminator_quote_arm_delegates_to_quote_form_
23287 // hash_discriminator` on the quote-family arm — that arm
23288 // DELEGATES to `QuoteForm::hash_discriminator` for `{3..=6}`;
23289 // this arm COLLAPSES to a single outer byte `1`.
23290 for (kind, sexp) in [
23291 (AtomKind::Symbol, Sexp::symbol("s")),
23292 (AtomKind::Keyword, Sexp::keyword("k")),
23293 (AtomKind::Str, Sexp::string("t")),
23294 (AtomKind::Int, Sexp::int(7)),
23295 (AtomKind::Float, Sexp::float(2.5)),
23296 (AtomKind::Bool, Sexp::boolean(true)),
23297 ] {
23298 assert_eq!(
23299 sexp.hash_discriminator(),
23300 1,
23301 "Sexp::Atom({kind:?}) must collapse to outer byte 1"
23302 );
23303 }
23304 }
23305
23306 #[test]
23307 fn sexp_hash_discriminator_quote_arm_delegates_to_quote_form_hash_discriminator() {
23308 // OUTER-CARVING CONTRACT (quote-family arm): every `QuoteForm`
23309 // variant projects through `QuoteForm::wrap` to a `Sexp::Quote_*`
23310 // whose `hash_discriminator` equals `qf.hash_discriminator()` —
23311 // the four quote-family arms DELEGATE to the sub-algebra's
23312 // discriminator method rather than inline four literals. Pin
23313 // the delegation-identity across the closed set so a regression
23314 // that inlines a byte at ONE arm (e.g. routes
23315 // `Sexp::UnquoteSplice(_)` to `6u8` inline instead of through
23316 // `QuoteForm::UnquoteSplice.hash_discriminator()`) fails-loudly
23317 // here — it would type-check but silently drift if the sub-
23318 // algebra's byte is renumbered.
23319 for qf in QuoteForm::ALL {
23320 let sexp = qf.wrap(Sexp::Nil);
23321 assert_eq!(
23322 sexp.hash_discriminator(),
23323 qf.hash_discriminator(),
23324 "Sexp {qf:?}-arm must delegate to QuoteForm::hash_discriminator"
23325 );
23326 }
23327 }
23328
23329 #[test]
23330 fn hash_for_sexp_routes_outer_discriminator_through_sexp_hash_discriminator() {
23331 // ROUTING-LAW CONTRACT: pin the outer-`Sexp` routing IDENTITY —
23332 // for every reachable outer-variant shape, `Hash for Sexp`
23333 // produces byte-identical output to a hand-driven
23334 // `<sexp.hash_discriminator()>.hash(h); <inner-payload-hash>`
23335 // sequence. Binds the composition IDENTITY (not just value
23336 // equality) between the outer Hash body and the typed algebra
23337 // method — a regression that re-inlines the three literals
23338 // (`0u8` / `1u8` / `2u8`) at the outer arms still drifts
23339 // detectably if the future `Sexp::hash_discriminator` is
23340 // re-numbered. Sibling posture to
23341 // `hash_for_sexp_preserves_legacy_quote_family_discriminator_bytes`
23342 // — that pin binds the quote-family arms against the pre-lift
23343 // literal bytes; this pin binds ALL SEVEN outer arms against the
23344 // post-lift typed method.
23345 use std::collections::hash_map::DefaultHasher;
23346 let payload = Sexp::symbol("payload");
23347 let seeds: Vec<(&str, Sexp)> = vec![
23348 ("nil", Sexp::Nil),
23349 ("atom-symbol", Sexp::symbol("s")),
23350 ("atom-int", Sexp::int(7)),
23351 ("atom-float", Sexp::float(2.5)),
23352 ("atom-bool", Sexp::boolean(true)),
23353 ("empty list", Sexp::List(vec![])),
23354 (
23355 "non-empty list",
23356 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23357 ),
23358 ("quote", Sexp::Quote(Box::new(payload.clone()))),
23359 ("quasiquote", Sexp::Quasiquote(Box::new(payload.clone()))),
23360 ("unquote", Sexp::Unquote(Box::new(payload.clone()))),
23361 (
23362 "unquote-splice",
23363 Sexp::UnquoteSplice(Box::new(payload.clone())),
23364 ),
23365 ];
23366 for (label, sexp) in seeds {
23367 let mut via_impl = DefaultHasher::new();
23368 sexp.hash(&mut via_impl);
23369
23370 let mut via_lifted = DefaultHasher::new();
23371 sexp.hash_discriminator().hash(&mut via_lifted);
23372 match &sexp {
23373 Sexp::Nil => {}
23374 Sexp::Atom(a) => a.hash(&mut via_lifted),
23375 Sexp::List(items) => {
23376 items.len().hash(&mut via_lifted);
23377 for i in items {
23378 i.hash(&mut via_lifted);
23379 }
23380 }
23381 Sexp::Quote(_)
23382 | Sexp::Quasiquote(_)
23383 | Sexp::Unquote(_)
23384 | Sexp::UnquoteSplice(_) => {
23385 let (_, inner) = sexp.expect_quote_form();
23386 inner.hash(&mut via_lifted);
23387 }
23388 }
23389
23390 assert_eq!(
23391 via_impl.finish(),
23392 via_lifted.finish(),
23393 "Hash for Sexp drifted from routed-through-hash_discriminator sequence at {label}"
23394 );
23395 }
23396 }
23397
23398 #[test]
23399 fn sexp_hash_discriminator_routes_through_shape_hash_discriminator_via_composition() {
23400 // COMPOSITION-IDENTITY CONTRACT (five-layer post-lift): pin the
23401 // outer-`Sexp` cache-key routing IDENTITY through the new shape-
23402 // level algebra layer — for every reachable outer-variant shape,
23403 // `Sexp::hash_discriminator` MUST agree byte-for-byte with
23404 // `self.shape().hash_discriminator()`. Post-lift the outer
23405 // method's body is EXACTLY `self.shape().hash_discriminator()`,
23406 // and this pin binds the routing identity across every reachable
23407 // shape so a regression that re-inlines the seven arm literals
23408 // (e.g. reverts to an inline match returning `0u8`/`1u8`/`2u8`
23409 // and the four quote-family sub-carving delegations) still
23410 // drifts detectably if the future `SexpShape::hash_discriminator`
23411 // is re-numbered — the composition identity is what closes the
23412 // outer-`Sexp` cache-key algebra at five typed layers (outer →
23413 // shape → three sub-carvings). Sibling posture to
23414 // `hash_for_sexp_routes_outer_discriminator_through_sexp_hash_discriminator`
23415 // — that pin binds the `Hash for Sexp` body against the outer
23416 // method; this pin binds the outer method against the shape-
23417 // level method.
23418 let payload = Sexp::symbol("payload");
23419 let seeds: Vec<(&str, Sexp)> = vec![
23420 ("nil", Sexp::Nil),
23421 ("atom-symbol", Sexp::symbol("s")),
23422 ("atom-keyword", Sexp::keyword("k")),
23423 ("atom-string", Sexp::string("t")),
23424 ("atom-int", Sexp::int(7)),
23425 ("atom-float", Sexp::float(2.5)),
23426 ("atom-bool", Sexp::boolean(true)),
23427 ("empty list", Sexp::List(vec![])),
23428 (
23429 "non-empty list",
23430 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23431 ),
23432 ("quote", Sexp::Quote(Box::new(payload.clone()))),
23433 ("quasiquote", Sexp::Quasiquote(Box::new(payload.clone()))),
23434 ("unquote", Sexp::Unquote(Box::new(payload.clone()))),
23435 (
23436 "unquote-splice",
23437 Sexp::UnquoteSplice(Box::new(payload.clone())),
23438 ),
23439 ];
23440 for (label, sexp) in seeds {
23441 let outer = sexp.hash_discriminator();
23442 let via_shape = sexp.shape().hash_discriminator();
23443 assert_eq!(
23444 outer, via_shape,
23445 "Sexp::hash_discriminator at {label} drifted from self.shape().hash_discriminator() — the five-layer typed cache-key composition is broken",
23446 );
23447 }
23448 }
23449
23450 #[test]
23451 fn sexp_iac_forge_tag_routes_through_shape_iac_forge_tag_via_composition() {
23452 // COMPOSITION-IDENTITY CONTRACT (outer-value peer): pin the
23453 // outer-`Sexp` cross-crate canonical-form tag routing IDENTITY
23454 // through the pre-existing shape-level projection — for every
23455 // reachable outer-variant shape, `Sexp::iac_forge_tag` MUST agree
23456 // arm-for-arm with `self.shape().iac_forge_tag()`. Post-lift the
23457 // outer method's body is EXACTLY `self.shape().iac_forge_tag()`,
23458 // and this pin binds the routing identity across every reachable
23459 // shape so a regression that re-inlines a parallel four-arm
23460 // match on the outer `Self::Quote | Self::Quasiquote | ...` set
23461 // returning literal tag strings inline still drifts detectably
23462 // if the shape-level projection's tag composition is re-numbered
23463 // — the composition identity is what closes the outer-`Sexp`
23464 // cross-crate canonical-form tag surface at four typed layers
23465 // (outer → shape → carving → sub-carving-tag). Sibling posture
23466 // to `sexp_hash_discriminator_routes_through_shape_hash_discriminator_via_composition`
23467 // — that pin binds the outer method against the shape-level
23468 // method on the cache-key byte axis; this pin binds the outer
23469 // method against the shape-level method on the cross-crate
23470 // canonical-form tag axis.
23471 let payload = Sexp::symbol("payload");
23472 let seeds: Vec<(&str, Sexp)> = vec![
23473 ("nil", Sexp::Nil),
23474 ("atom-symbol", Sexp::symbol("s")),
23475 ("atom-keyword", Sexp::keyword("k")),
23476 ("atom-string", Sexp::string("t")),
23477 ("atom-int", Sexp::int(7)),
23478 ("atom-float", Sexp::float(2.5)),
23479 ("atom-bool", Sexp::boolean(true)),
23480 ("empty list", Sexp::List(vec![])),
23481 (
23482 "non-empty list",
23483 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23484 ),
23485 ("quote", Sexp::Quote(Box::new(payload.clone()))),
23486 ("quasiquote", Sexp::Quasiquote(Box::new(payload.clone()))),
23487 ("unquote", Sexp::Unquote(Box::new(payload.clone()))),
23488 (
23489 "unquote-splice",
23490 Sexp::UnquoteSplice(Box::new(payload.clone())),
23491 ),
23492 ];
23493 for (label, sexp) in seeds {
23494 let outer = sexp.iac_forge_tag();
23495 let via_shape = sexp.shape().iac_forge_tag();
23496 assert_eq!(
23497 outer, via_shape,
23498 "Sexp::iac_forge_tag at {label} drifted from self.shape().iac_forge_tag() — the four-layer typed cross-crate canonical-form tag composition is broken",
23499 );
23500 }
23501 }
23502
23503 #[test]
23504 fn sexp_iac_forge_tag_pins_canonical_cl_tags_for_every_quote_family_arm() {
23505 // CANONICAL-TAG CONTRACT (outer-value peer): the outer-value
23506 // `Sexp::iac_forge_tag` MUST project each of the four homoiconic
23507 // prefix-wrapper arms to the SAME canonical Common-Lisp tag
23508 // string `crate::error::SexpShape::iac_forge_tag` projects at the
23509 // shape-level (and `crate::ast::QuoteForm::iac_forge_tag` at the
23510 // sub-carving level) — `Sexp::Quote → Some("quote")`,
23511 // `Sexp::Quasiquote → Some("quasiquote")`, `Sexp::Unquote →
23512 // Some("unquote")`, `Sexp::UnquoteSplice → Some("unquote-
23513 // splicing")`. A regression that inlines a byte-drifted spelling
23514 // here (e.g. `Sexp::UnquoteSplice → Some("unquote-splice")`
23515 // conflating the substrate's shorter diagnostic label with the
23516 // CL canonical form) silently breaks every cross-crate iac-forge
23517 // consumer keyed on `(unquote-splicing ...)`. Sibling posture to
23518 // `sexp_shape_iac_forge_tag_pins_canonical_cl_tags_for_every_quote_family_arm`
23519 // one algebra level down — that pin binds the shape-level
23520 // projection's canonical tag surface; this pin binds the outer-
23521 // value projection's canonical tag surface across the closed
23522 // four-arm quote-family sweep on the outer `Sexp` algebra.
23523 let inner = Sexp::symbol("payload");
23524 assert_eq!(
23525 Sexp::Quote(Box::new(inner.clone())).iac_forge_tag(),
23526 Some("quote"),
23527 );
23528 assert_eq!(
23529 Sexp::Quasiquote(Box::new(inner.clone())).iac_forge_tag(),
23530 Some("quasiquote"),
23531 );
23532 assert_eq!(
23533 Sexp::Unquote(Box::new(inner.clone())).iac_forge_tag(),
23534 Some("unquote"),
23535 );
23536 assert_eq!(
23537 Sexp::UnquoteSplice(Box::new(inner)).iac_forge_tag(),
23538 Some("unquote-splicing"),
23539 );
23540 }
23541
23542 #[test]
23543 fn sexp_iac_forge_tag_returns_none_on_every_non_quote_family_variant() {
23544 // PARTIAL-PROJECTION KERNEL CONTRACT (outer-value peer): every
23545 // `Sexp` variant OUTSIDE the four-arm quote-family carving MUST
23546 // project through `Sexp::iac_forge_tag` to `None` — the three-
23547 // arm outer kernel `{Nil, Atom, List}` (which corresponds to the
23548 // eight-shape kernel at the shape-level projection through the
23549 // six-atomic-arms → outer `Atom` collapse of `Self::shape`). Pin
23550 // representative seeds for each kernel arm — `Nil`, one atom per
23551 // `AtomKind`, one empty + one non-empty list — so a regression
23552 // that surfaces a bogus tag for a non-quote-family arm (e.g.
23553 // `Sexp::List → Some("list")` conflating the outer-shape
23554 // diagnostic label with the quote-family canonical form) fails-
23555 // loudly here. Sibling posture to
23556 // `sexp_shape_iac_forge_tag_returns_none_on_every_non_quote_family_shape`
23557 // one algebra level down — that pin binds the shape-level
23558 // projection's kernel; this pin binds the outer-value
23559 // projection's kernel on the three-arm outer partition.
23560 assert_eq!(Sexp::Nil.iac_forge_tag(), None);
23561 assert_eq!(Sexp::symbol("s").iac_forge_tag(), None);
23562 assert_eq!(Sexp::keyword("k").iac_forge_tag(), None);
23563 assert_eq!(Sexp::string("t").iac_forge_tag(), None);
23564 assert_eq!(Sexp::int(7).iac_forge_tag(), None);
23565 assert_eq!(Sexp::float(2.5).iac_forge_tag(), None);
23566 assert_eq!(Sexp::boolean(true).iac_forge_tag(), None);
23567 assert_eq!(Sexp::List(vec![]).iac_forge_tag(), None);
23568 assert_eq!(
23569 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]).iac_forge_tag(),
23570 None,
23571 );
23572 }
23573
23574 #[test]
23575 fn sexp_iac_forge_tag_partitions_quote_family_and_kernel_disjointly() {
23576 // IMAGE-PARTITION CONTRACT (outer-value peer): sweeping a
23577 // representative seed per outer `Sexp` variant through
23578 // `Sexp::iac_forge_tag` MUST partition into EXACTLY the four-arm
23579 // quote-family image (four distinct canonical CL tag strings —
23580 // the pre-image of `Some(_)`) AND the outer three-arm non-quote-
23581 // family kernel (all `None` — `Nil` + one atom per `AtomKind` +
23582 // one list). The image's `is_some()` count MUST be four
23583 // (surjective onto the four-tag closed set), the kernel's
23584 // `is_none()` count MUST cover every non-quote-family seed, and
23585 // the total sweep sums to the thirteen-seed representative sweep
23586 // covering all seven outer variants (six `Atom` payloads +
23587 // `Nil` + one `List` + four quote-family arms). A regression that
23588 // leaks a bogus `Some(_)` from a non-quote-family arm or drops a
23589 // `Some(_)` from a quote-family arm fails-loudly here on the
23590 // partition-cardinality axis before any downstream iac-forge
23591 // consumer would surface the drift. Sibling posture to
23592 // `sexp_shape_iac_forge_tag_partitions_quote_family_and_kernel_disjointly`
23593 // one algebra level down — that pin binds the shape-level
23594 // image-partition on the twelve-shape closed sweep; this pin
23595 // binds the outer-value image-partition on the representative
23596 // outer-variant sweep.
23597 let payload = Sexp::symbol("payload");
23598 let seeds: Vec<Sexp> = vec![
23599 Sexp::Nil,
23600 Sexp::symbol("s"),
23601 Sexp::keyword("k"),
23602 Sexp::string("t"),
23603 Sexp::int(7),
23604 Sexp::float(2.5),
23605 Sexp::boolean(true),
23606 Sexp::List(vec![]),
23607 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23608 Sexp::Quote(Box::new(payload.clone())),
23609 Sexp::Quasiquote(Box::new(payload.clone())),
23610 Sexp::Unquote(Box::new(payload.clone())),
23611 Sexp::UnquoteSplice(Box::new(payload.clone())),
23612 ];
23613 let tag_image: std::collections::BTreeSet<&'static str> =
23614 seeds.iter().filter_map(Sexp::iac_forge_tag).collect();
23615 let expected_tag_image: std::collections::BTreeSet<&'static str> =
23616 ["quote", "quasiquote", "unquote", "unquote-splicing"]
23617 .into_iter()
23618 .collect();
23619 assert_eq!(
23620 tag_image, expected_tag_image,
23621 "Sexp::iac_forge_tag image must exactly cover the four canonical CL quote-family tags",
23622 );
23623 let some_count = seeds
23624 .iter()
23625 .filter(|sexp| sexp.iac_forge_tag().is_some())
23626 .count();
23627 assert_eq!(
23628 some_count, 4,
23629 "Sexp::iac_forge_tag must return `Some(_)` on exactly the four-arm quote-family carving",
23630 );
23631 let none_count = seeds
23632 .iter()
23633 .filter(|sexp| sexp.iac_forge_tag().is_none())
23634 .count();
23635 assert_eq!(
23636 none_count,
23637 seeds.len() - 4,
23638 "Sexp::iac_forge_tag must return `None` on every seed outside the four-arm quote-family carving",
23639 );
23640 assert_eq!(
23641 some_count + none_count,
23642 seeds.len(),
23643 "Sexp::iac_forge_tag's image + kernel must partition the representative outer-variant sweep exactly",
23644 );
23645 }
23646
23647 #[test]
23648 fn sexp_prefix_routes_through_shape_prefix_via_composition() {
23649 // COMPOSITION-IDENTITY CONTRACT (outer-value peer): pin the
23650 // outer-`Sexp` reader-punctuation surface routing IDENTITY
23651 // through the pre-existing shape-level projection — for every
23652 // reachable outer-variant shape, `Sexp::prefix` MUST agree arm-
23653 // for-arm with `self.shape().prefix()`. Post-lift the outer
23654 // method's body is EXACTLY `self.shape().prefix()`, and this
23655 // pin binds the routing identity across every reachable shape so
23656 // a regression that re-inlines a parallel four-arm match on the
23657 // outer `Self::Quote | Self::Quasiquote | ...` set returning
23658 // literal reader-punctuation strings inline still drifts
23659 // detectably if the shape-level projection's prefix composition
23660 // is re-numbered — the composition identity is what closes the
23661 // outer-`Sexp` reader-punctuation surface at four typed layers
23662 // (outer → shape → carving → sub-carving-prefix). Sibling
23663 // posture to
23664 // `sexp_iac_forge_tag_routes_through_shape_iac_forge_tag_via_composition`
23665 // one vocabulary axis over — that pin binds the outer method
23666 // against the shape-level method on the cross-crate canonical-
23667 // form tag axis; this pin binds the outer method against the
23668 // shape-level method on the reader-punctuation axis.
23669 let payload = Sexp::symbol("payload");
23670 let seeds: Vec<(&str, Sexp)> = vec![
23671 ("nil", Sexp::Nil),
23672 ("atom-symbol", Sexp::symbol("s")),
23673 ("atom-keyword", Sexp::keyword("k")),
23674 ("atom-string", Sexp::string("t")),
23675 ("atom-int", Sexp::int(7)),
23676 ("atom-float", Sexp::float(2.5)),
23677 ("atom-bool", Sexp::boolean(true)),
23678 ("empty list", Sexp::List(vec![])),
23679 (
23680 "non-empty list",
23681 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23682 ),
23683 ("quote", Sexp::Quote(Box::new(payload.clone()))),
23684 ("quasiquote", Sexp::Quasiquote(Box::new(payload.clone()))),
23685 ("unquote", Sexp::Unquote(Box::new(payload.clone()))),
23686 (
23687 "unquote-splice",
23688 Sexp::UnquoteSplice(Box::new(payload.clone())),
23689 ),
23690 ];
23691 for (label, sexp) in seeds {
23692 let outer = sexp.prefix();
23693 let via_shape = sexp.shape().prefix();
23694 assert_eq!(
23695 outer, via_shape,
23696 "Sexp::prefix at {label} drifted from self.shape().prefix() — the four-layer typed reader-punctuation composition is broken",
23697 );
23698 }
23699 }
23700
23701 #[test]
23702 fn sexp_prefix_pins_canonical_reader_prefixes_for_every_quote_family_arm() {
23703 // CANONICAL-PREFIX CONTRACT (outer-value peer): the outer-value
23704 // `Sexp::prefix` MUST project each of the four homoiconic
23705 // prefix-wrapper arms to the SAME canonical reader-punctuation
23706 // string `crate::error::SexpShape::prefix` projects at the
23707 // shape-level (and `crate::ast::QuoteForm::prefix` at the sub-
23708 // carving level) — `Sexp::Quote → Some("'")`,
23709 // `Sexp::Quasiquote → Some("`")`, `Sexp::Unquote → Some(",")`,
23710 // `Sexp::UnquoteSplice → Some(",@")`. A regression that inlines
23711 // a byte-drifted spelling here (e.g. `Sexp::UnquoteSplice →
23712 // Some(", @")` inserting a spurious space, or `Sexp::Quote →
23713 // Some("`")` swapping arms between Quote and Quasiquote)
23714 // silently breaks the `Display for Sexp` round-trip against the
23715 // reader's prefix dispatch. Sibling posture to
23716 // `sexp_shape_prefix_pins_canonical_reader_prefixes_for_every_quote_family_arm`
23717 // one algebra level down — that pin binds the shape-level
23718 // projection's canonical reader-punctuation surface; this pin
23719 // binds the outer-value projection's canonical reader-
23720 // punctuation surface across the closed four-arm quote-family
23721 // sweep on the outer `Sexp` algebra.
23722 let inner = Sexp::symbol("payload");
23723 assert_eq!(Sexp::Quote(Box::new(inner.clone())).prefix(), Some("'"),);
23724 assert_eq!(
23725 Sexp::Quasiquote(Box::new(inner.clone())).prefix(),
23726 Some("`"),
23727 );
23728 assert_eq!(Sexp::Unquote(Box::new(inner.clone())).prefix(), Some(","),);
23729 assert_eq!(Sexp::UnquoteSplice(Box::new(inner)).prefix(), Some(",@"),);
23730 }
23731
23732 #[test]
23733 fn sexp_prefix_returns_none_on_every_non_quote_family_variant() {
23734 // PARTIAL-PROJECTION KERNEL CONTRACT (outer-value peer): every
23735 // `Sexp` variant OUTSIDE the four-arm quote-family carving MUST
23736 // project through `Sexp::prefix` to `None` — the three-arm
23737 // outer kernel `{Nil, Atom, List}` (which corresponds to the
23738 // eight-shape kernel at the shape-level projection through the
23739 // six-atomic-arms → outer `Atom` collapse of `Self::shape`).
23740 // Pin representative seeds for each kernel arm — `Nil`, one
23741 // atom per `AtomKind`, one empty + one non-empty list — so a
23742 // regression that surfaces a bogus prefix for a non-quote-
23743 // family arm (e.g. `Sexp::List → Some("(")` conflating the
23744 // outer-shape structural delimiter with the quote-family
23745 // reader-punctuation) fails-loudly here. Sibling posture to
23746 // `sexp_shape_prefix_returns_none_on_every_non_quote_family_shape`
23747 // one algebra level down — that pin binds the shape-level
23748 // projection's kernel; this pin binds the outer-value
23749 // projection's kernel on the three-arm outer partition.
23750 assert_eq!(Sexp::Nil.prefix(), None);
23751 assert_eq!(Sexp::symbol("s").prefix(), None);
23752 assert_eq!(Sexp::keyword("k").prefix(), None);
23753 assert_eq!(Sexp::string("t").prefix(), None);
23754 assert_eq!(Sexp::int(7).prefix(), None);
23755 assert_eq!(Sexp::float(2.5).prefix(), None);
23756 assert_eq!(Sexp::boolean(true).prefix(), None);
23757 assert_eq!(Sexp::List(vec![]).prefix(), None);
23758 assert_eq!(
23759 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]).prefix(),
23760 None,
23761 );
23762 }
23763
23764 #[test]
23765 fn sexp_prefix_partitions_quote_family_and_kernel_disjointly() {
23766 // IMAGE-PARTITION CONTRACT (outer-value peer): sweeping a
23767 // representative seed per outer `Sexp` variant through
23768 // `Sexp::prefix` MUST partition into EXACTLY the four-arm
23769 // quote-family image (four distinct canonical reader-punctuation
23770 // strings — the pre-image of `Some(_)`) AND the outer three-arm
23771 // non-quote-family kernel (all `None` — `Nil` + one atom per
23772 // `AtomKind` + one list). The image's `is_some()` count MUST be
23773 // four (surjective onto the four-prefix closed set), the
23774 // kernel's `is_none()` count MUST cover every non-quote-family
23775 // seed, and the total sweep sums to the thirteen-seed
23776 // representative sweep covering all seven outer variants (six
23777 // `Atom` payloads + `Nil` + one `List` + four quote-family
23778 // arms). A regression that leaks a bogus `Some(_)` from a non-
23779 // quote-family arm or drops a `Some(_)` from a quote-family arm
23780 // fails-loudly here on the partition-cardinality axis before
23781 // any downstream reader-round-trip or Display consumer would
23782 // surface the drift. Sibling posture to
23783 // `sexp_shape_prefix_partitions_quote_family_and_kernel_disjointly`
23784 // one algebra level down — that pin binds the shape-level
23785 // image-partition on the twelve-shape closed sweep; this pin
23786 // binds the outer-value image-partition on the representative
23787 // outer-variant sweep.
23788 let payload = Sexp::symbol("payload");
23789 let seeds: Vec<Sexp> = vec![
23790 Sexp::Nil,
23791 Sexp::symbol("s"),
23792 Sexp::keyword("k"),
23793 Sexp::string("t"),
23794 Sexp::int(7),
23795 Sexp::float(2.5),
23796 Sexp::boolean(true),
23797 Sexp::List(vec![]),
23798 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23799 Sexp::Quote(Box::new(payload.clone())),
23800 Sexp::Quasiquote(Box::new(payload.clone())),
23801 Sexp::Unquote(Box::new(payload.clone())),
23802 Sexp::UnquoteSplice(Box::new(payload.clone())),
23803 ];
23804 let prefix_image: std::collections::BTreeSet<&'static str> =
23805 seeds.iter().filter_map(Sexp::prefix).collect();
23806 let expected_prefix_image: std::collections::BTreeSet<&'static str> =
23807 ["'", "`", ",", ",@"].into_iter().collect();
23808 assert_eq!(
23809 prefix_image, expected_prefix_image,
23810 "Sexp::prefix image must exactly cover the four canonical reader-punctuation quote-family prefixes",
23811 );
23812 let some_count = seeds.iter().filter(|sexp| sexp.prefix().is_some()).count();
23813 assert_eq!(
23814 some_count, 4,
23815 "Sexp::prefix must return `Some(_)` on exactly the four-arm quote-family carving",
23816 );
23817 let none_count = seeds.iter().filter(|sexp| sexp.prefix().is_none()).count();
23818 assert_eq!(
23819 none_count,
23820 seeds.len() - 4,
23821 "Sexp::prefix must return `None` on every seed outside the four-arm quote-family carving",
23822 );
23823 assert_eq!(
23824 some_count + none_count,
23825 seeds.len(),
23826 "Sexp::prefix's image + kernel must partition the representative outer-variant sweep exactly",
23827 );
23828 }
23829
23830 #[test]
23831 fn display_for_sexp_renders_each_quote_family_variant_with_canonical_prefix() {
23832 // Pin the post-lift Display rendering: every wrapper variant
23833 // renders as `<prefix><inner>` with the prefix sourced from
23834 // `QuoteForm::prefix`. A regression that drifts the prefix
23835 // arm-routing (e.g. routes Quote through `` ` `` instead of
23836 // `'`) fails loudly here. The literal `inner` rendering is
23837 // the symbol `foo` so the prefix is the only diff between
23838 // arms — pin path-uniformity across the closed set.
23839 let inner = Sexp::symbol("foo");
23840 assert_eq!(Sexp::Quote(Box::new(inner.clone())).to_string(), "'foo");
23841 assert_eq!(
23842 Sexp::Quasiquote(Box::new(inner.clone())).to_string(),
23843 "`foo"
23844 );
23845 assert_eq!(Sexp::Unquote(Box::new(inner.clone())).to_string(), ",foo");
23846 assert_eq!(Sexp::UnquoteSplice(Box::new(inner)).to_string(), ",@foo");
23847 }
23848
23849 #[test]
23850 fn display_for_sexp_round_trips_each_quote_family_variant_through_reader() {
23851 // ROUND-TRIP CONTRACT: every wrapper variant's Display →
23852 // reader path produces the matching `Sexp::*` variant. The
23853 // reader's prefix-dispatch (in `reader::parse`) consumes the
23854 // canonical `'` / `` ` `` / `,` / `,@` tokens and produces
23855 // the corresponding wrapper; the Display impl emits the same
23856 // tokens via `QuoteForm::prefix`. Pin the round-trip
23857 // end-to-end so a regression that drifts the prefix on
23858 // either side (Display or reader) fails loudly here. Sibling
23859 // posture to `fmt_float_round_trips_integral_float_through
23860 // _reader_as_float` — the Float round-trip pin at the
23861 // Display→read boundary; this test pins the four
23862 // quote-family round-trips at the same boundary.
23863 let inner_body = Sexp::symbol("payload");
23864
23865 let quote = Sexp::Quote(Box::new(inner_body.clone()));
23866 let forms = crate::reader::read("e.to_string()).expect("quote must round-trip");
23867 assert_eq!(forms.len(), 1);
23868 assert_eq!(forms[0], quote);
23869
23870 let quasiquote = Sexp::Quasiquote(Box::new(inner_body.clone()));
23871 let forms =
23872 crate::reader::read(&quasiquote.to_string()).expect("quasiquote must round-trip");
23873 assert_eq!(forms.len(), 1);
23874 assert_eq!(forms[0], quasiquote);
23875
23876 let unquote = Sexp::Unquote(Box::new(inner_body.clone()));
23877 let forms = crate::reader::read(&unquote.to_string()).expect("unquote must round-trip");
23878 assert_eq!(forms.len(), 1);
23879 assert_eq!(forms[0], unquote);
23880
23881 let splice = Sexp::UnquoteSplice(Box::new(inner_body));
23882 let forms =
23883 crate::reader::read(&splice.to_string()).expect("unquote-splice must round-trip");
23884 assert_eq!(forms.len(), 1);
23885 assert_eq!(forms[0], splice);
23886 }
23887
23888 #[test]
23889 fn quote_form_wrap_projects_each_typed_marker_into_matching_sexp_wrapper() {
23890 // CLOSED-SET CONSTRUCTOR CONTRACT: pin that `QuoteForm::wrap` is
23891 // the structural inverse of `Sexp::as_quote_form` at the
23892 // marker→wrapper boundary. Every variant of the closed-set
23893 // `QuoteForm` algebra projects to its matching `Sexp::*` wrapper
23894 // applied to the supplied inner — `Quote → Sexp::Quote`,
23895 // `Quasiquote → Sexp::Quasiquote`, `Unquote → Sexp::Unquote`,
23896 // `UnquoteSplice → Sexp::UnquoteSplice`. A regression that swaps
23897 // two arms (e.g. `Self::Quote → Sexp::Quasiquote`) type-checks
23898 // but silently corrupts every consumer that constructs a quote-
23899 // family Sexp through the projection — fails loudly here.
23900 // Sibling-arm sweep so the (marker, constructor) pair stays
23901 // load-bearing under reordering refactors.
23902 let inner = Sexp::symbol("payload");
23903 assert_eq!(
23904 QuoteForm::Quote.wrap(inner.clone()),
23905 Sexp::Quote(Box::new(inner.clone()))
23906 );
23907 assert_eq!(
23908 QuoteForm::Quasiquote.wrap(inner.clone()),
23909 Sexp::Quasiquote(Box::new(inner.clone()))
23910 );
23911 assert_eq!(
23912 QuoteForm::Unquote.wrap(inner.clone()),
23913 Sexp::Unquote(Box::new(inner.clone()))
23914 );
23915 assert_eq!(
23916 QuoteForm::UnquoteSplice.wrap(inner.clone()),
23917 Sexp::UnquoteSplice(Box::new(inner))
23918 );
23919 }
23920
23921 #[test]
23922 fn quote_form_wrap_round_trips_through_as_quote_form_for_every_variant() {
23923 // ROUND-TRIP CONTRACT: pin the structural identity
23924 // `qf.wrap(inner.clone()).as_quote_form() == Some((qf, &inner))`
23925 // for every variant of the closed-set `QuoteForm` algebra. This
23926 // is the canonical law binding the marker→wrapper projection
23927 // (`wrap`) to its wrapper→marker dual (`as_quote_form`) on the
23928 // substrate's `Sexp` algebra. A regression that drifts the
23929 // (marker, constructor) pair on EITHER side — `wrap` routing
23930 // `Quote` to `Sexp::Quasiquote`, OR `as_quote_form` routing
23931 // `Sexp::Quote(_)` to `QuoteForm::Quasiquote` — surfaces as a
23932 // round-trip mismatch here. Sweep all four variants so the
23933 // round-trip stays load-bearing across the closed set. Same
23934 // posture as the `display_for_sexp_round_trips_each_quote_family
23935 // _variant_through_reader` round-trip pin at the Display→read
23936 // boundary; this test pins the round-trip at the marker→Sexp
23937 // projection boundary.
23938 let inner_body = Sexp::symbol("payload");
23939 for qf in [
23940 QuoteForm::Quote,
23941 QuoteForm::Quasiquote,
23942 QuoteForm::Unquote,
23943 QuoteForm::UnquoteSplice,
23944 ] {
23945 let wrapped = qf.wrap(inner_body.clone());
23946 let projected = wrapped
23947 .as_quote_form()
23948 .expect("wrap output must project back through as_quote_form");
23949 assert_eq!(
23950 projected.0, qf,
23951 "wrap→as_quote_form drifted at marker for variant {qf:?}"
23952 );
23953 assert_eq!(
23954 projected.1, &inner_body,
23955 "wrap→as_quote_form drifted at inner body for variant {qf:?}"
23956 );
23957 }
23958 }
23959
23960 #[test]
23961 fn quote_form_all_is_unique_and_complete() {
23962 // CLOSED-SET TRUTH-TABLE: pin that `QuoteForm::ALL` carries
23963 // exactly the four reachable quote-family wrappers — no duplicates,
23964 // byte-equal coverage of `{Quote, Quasiquote, Unquote, UnquoteSplice}`.
23965 // The `[Self; 4]` array-literal arity already binds the count at
23966 // compile time; this test pins the *identity* of each slot so a
23967 // future re-ordering refactor (e.g. swapping `Unquote` and
23968 // `UnquoteSplice` positions) that leaves the cardinality intact
23969 // still fails loudly. Sibling discipline to
23970 // `unquote_form_all_is_unique_and_complete` (the 2-of-4 subset
23971 // sibling) and `atom_kind_all_is_unique_and_complete` (the peer
23972 // atomic-payload axis).
23973 //
23974 // The `iter+map+collect+sort_unstable` quadruple this test inlined
23975 // pre-lift now binds at `<QuoteForm as ClosedSet>::sorted_labels()`
23976 // — the canonical-ordered candidate-list projection on the trait.
23977 // Distinctness of the sorted result is covered by
23978 // `assert_closed_set_well_formed::<QuoteForm>()` (the workspace-wide
23979 // testkit), so this test reduces to the per-implementor unique
23980 // payload (the four reader-punctuation literals in lexicographic
23981 // order — the load-bearing per-enum ground truth the substrate-wide
23982 // sort lift does NOT subsume).
23983 assert_eq!(QuoteForm::ALL.len(), 4);
23984 assert_eq!(
23985 <QuoteForm as tatara_closed_set::ClosedSet>::sorted_labels(),
23986 vec!["'", ",", ",@", "`"],
23987 "QuoteForm::ALL must cover every reachable homoiconic prefix-wrapper"
23988 );
23989 }
23990
23991 #[test]
23992 fn quote_form_display_matches_prefix_for_every_variant() {
23993 // DISPLAY-EQUALS-PREFIX CONTRACT: pin that
23994 // `<QuoteForm as fmt::Display>::fmt` projects through
23995 // `QuoteForm::prefix` byte-for-byte for every variant in
23996 // `QuoteForm::ALL`. The Display impl is the canonical rendering
23997 // surface a future diagnostic annotation (`#[error("... {prefix}")]`
23998 // shape) threads through; pinning the equality here means a
23999 // regression that drifts EITHER the Display arm OR the `prefix`
24000 // arm independently surfaces at this test rather than silently
24001 // bifurcating the operator-facing rendered marker. Sibling
24002 // discipline to `unquote_form_display_renders_canonical_marker_
24003 // for_each_variant` (the 2-of-4 subset sibling) and
24004 // `atom_kind_display_matches_label_for_every_variant` (the peer
24005 // atomic-payload axis).
24006 for qf in QuoteForm::ALL {
24007 assert_eq!(
24008 qf.to_string(),
24009 qf.prefix(),
24010 "Display rendering for {qf:?} diverged from prefix() projection"
24011 );
24012 }
24013 }
24014
24015 #[test]
24016 fn quote_form_prefix_round_trips_through_from_str() {
24017 // BIDIRECTIONAL ROUND-TRIP: pin the structural identity
24018 // `qf.prefix().parse() == Ok(qf)` for every variant in
24019 // `QuoteForm::ALL`. This is the canonical law binding the
24020 // marker→string projection (`prefix`) to its string→marker dual
24021 // (`FromStr`). A regression that drifts EITHER side — `prefix`
24022 // routing `Quote` to `` "`" ``, OR `FromStr` decoding `"'"` to
24023 // `Quasiquote` — surfaces as a round-trip mismatch here. Sweep
24024 // all four variants so the round-trip stays load-bearing across
24025 // the closed set. Same posture as the
24026 // `unquote_form_marker_round_trips_through_from_str` sibling on
24027 // the 2-of-4 template-substitution subset axis and
24028 // `atom_kind_label_round_trips_through_from_str` on the peer
24029 // atomic-payload axis.
24030 for qf in QuoteForm::ALL {
24031 let prefix = qf.prefix();
24032 let decoded: QuoteForm = prefix
24033 .parse()
24034 .expect("canonical prefix must decode through FromStr");
24035 assert_eq!(
24036 decoded, qf,
24037 "FromStr ↔ prefix round-trip drifted for variant {qf:?} (prefix {prefix:?})"
24038 );
24039 }
24040 }
24041
24042 #[test]
24043 fn unknown_quote_form_carries_offending_input_verbatim() {
24044 // TYPED PARSE-FAILURE CONTRACT: pin the exact rendered shape of
24045 // `UnknownQuoteForm`'s `#[error(...)]` annotation AND the
24046 // verbatim `.0` field projection — no normalization, no case-
24047 // folding, no whitespace trimming. The error is part of the
24048 // substrate-wide `Unknown*` parse-rejection family
24049 // (`UnknownSexpShape`, `UnknownAtomKind`, `UnknownUnquoteForm`,
24050 // `UnknownRequestorKind`, `UnknownReceiptKind`, `UnknownPhase`,
24051 // `UnknownConditionKind`, `UnknownTeardownPolicy`, …) and the
24052 // joint rendered shape (`"unknown <thing>: {0}"`) is the
24053 // operator-facing diagnostic idiom every member preserves. A
24054 // regression that case-folds, trims, or strips the offending
24055 // input would silently rewrite an operator's literal value at
24056 // the diagnostic boundary — fails loudly here.
24057 let offending = "not-a-quote-prefix";
24058 let err: UnknownQuoteForm = offending
24059 .parse::<QuoteForm>()
24060 .expect_err("non-canonical input must reject through FromStr");
24061 assert_eq!(
24062 err.0, offending,
24063 "offending input was not preserved verbatim"
24064 );
24065 assert_eq!(
24066 err.to_string(),
24067 "unknown quote form: not-a-quote-prefix",
24068 "Display rendering diverged from the substrate-wide Unknown* idiom"
24069 );
24070 }
24071
24072 #[test]
24073 fn quote_form_is_well_formed_closed_set() {
24074 // Structural contract: QuoteForm's four variants are pairwise
24075 // distinct, round-trip through the trait's `label` ↔
24076 // `parse_label`, and reject the empty string — the
24077 // workspace-wide `assert_closed_set_well_formed::<T>()` testkit
24078 // pinned across every `tatara-process` closed-set implementor
24079 // (`AllocationPhase`, `RequestorKind`, `ProcessPhase`,
24080 // `ConditionKind`, `WorkloadKind`, …). The substrate-level
24081 // assertion runs on the auto-derived `impl ClosedSet for
24082 // QuoteForm` emitted by `#[derive(tatara_closed_set::DeriveClosedSet)]`
24083 // — a regression that drifts the derive's `make_unknown`
24084 // delegation, the `via = "prefix"` projection
24085 // (`"'" / "`" / "," / ",@"`), or the variant listing forced
24086 // through `Self::ALL` fails-loudly here in isolation from the
24087 // per-variant truth tables above.
24088 tatara_closed_set::assert_closed_set_well_formed::<QuoteForm>();
24089 }
24090
24091 #[test]
24092 fn quote_form_from_str_rejects_sexp_shape_labels_on_homoiconic_prefix_axis() {
24093 // CROSS-AXIS DISJOINTNESS: pin that `QuoteForm::FromStr` decodes
24094 // the homoiconic punctuation markers `'` / `` ` `` / `,` / `,@`
24095 // but rejects the `SexpShape` structural-identity vocabulary
24096 // (`"quote"` / `"quasiquote"` / `"unquote"` / `"unquote-splice"`)
24097 // AND the `iac_forge_tag` cross-crate canonical-form vocabulary
24098 // (`"quote"` / `"quasiquote"` / `"unquote"` / `"unquote-splicing"`).
24099 // The three closed sets project the SAME four `Sexp::*` quote-
24100 // family constructors on DISTINCT axes — a regression that
24101 // conflated them would let `"quote".parse::<QuoteForm>()` succeed
24102 // (silently bifurcating the diagnostic surface) or
24103 // `"'".parse::<SexpShape>()` succeed (silently colliding the
24104 // punctuation and structural-identity vocabularies). Sibling
24105 // discipline to `unquote_form_from_str_rejects_sexp_shape_labels_
24106 // on_template_marker_axis` (the 2-of-4 subset's matching
24107 // cross-axis pin).
24108 use crate::error::SexpShape;
24109 for shape in [
24110 SexpShape::Quote,
24111 SexpShape::Quasiquote,
24112 SexpShape::Unquote,
24113 SexpShape::UnquoteSplice,
24114 ] {
24115 let label = shape.label();
24116 assert!(
24117 label.parse::<QuoteForm>().is_err(),
24118 "SexpShape label {label:?} unexpectedly decoded through QuoteForm::FromStr — cross-axis vocabulary collision"
24119 );
24120 }
24121 for qf in QuoteForm::ALL {
24122 let tag = qf.iac_forge_tag();
24123 assert!(
24124 tag.parse::<QuoteForm>().is_err(),
24125 "iac_forge_tag {tag:?} unexpectedly decoded through QuoteForm::FromStr — cross-axis vocabulary collision"
24126 );
24127 }
24128 }
24129
24130 #[test]
24131 fn quote_form_from_str_extends_unquote_form_from_str_on_the_2_of_4_subset() {
24132 // SUBSET-CONTAINMENT CONTRACT: pin that every successful
24133 // `UnquoteForm::FromStr` input is ALSO a successful
24134 // `QuoteForm::FromStr` input, AND the resulting variants project
24135 // to each other through `QuoteForm::as_unquote_form` (the 2-of-4
24136 // subset gate). This binds the two homoiconic-prefix axes
24137 // (`UnquoteForm`'s 2-of-2 template-substitution subset and
24138 // `QuoteForm`'s full 4-of-4 quote-family) at the FromStr
24139 // boundary: a regression that drifts EITHER FromStr's vocabulary
24140 // from the other (e.g. `UnquoteForm::FromStr` adding a spelling
24141 // `","` rejects in `QuoteForm::FromStr` would surface) fails
24142 // loudly here. Composition law: for every `uf` in
24143 // `UnquoteForm::ALL`, `uf.marker().parse::<QuoteForm>()` is
24144 // `Ok(qf)` where `qf.as_unquote_form() == Some(uf)`.
24145 use crate::error::UnquoteForm;
24146 for uf in UnquoteForm::ALL {
24147 let marker = uf.marker();
24148 let qf: QuoteForm = marker.parse().unwrap_or_else(|_| {
24149 panic!(
24150 "UnquoteForm marker {marker:?} for {uf:?} did not decode through QuoteForm::FromStr — 2-of-4 subset containment violated"
24151 )
24152 });
24153 assert_eq!(
24154 qf.as_unquote_form(),
24155 Some(uf),
24156 "QuoteForm decoded from {marker:?} did not project back to UnquoteForm::{uf:?} via as_unquote_form"
24157 );
24158 }
24159 }
24160
24161 #[test]
24162 fn quote_form_wrap_derives_each_arm_to_its_pre_lift_box_new_form() {
24163 // PATH-UNIFORMITY CONTRACT: pin that `QuoteForm::wrap` is
24164 // observably equivalent to the pre-lift four-arm reader pattern
24165 // `Sexp::<Variant>(Box::new(inner))` across every variant of the
24166 // closed set. The reader's pre-lift parse arms each constructed
24167 // their corresponding wrapper inline; post-lift the parse routes
24168 // through `QuoteForm::wrap`. A regression that drifts the
24169 // projection's allocation posture (e.g. wraps in an extra layer,
24170 // or skips the `Box::new`) fails loudly here. Companion to the
24171 // `wrap` projection test above — that test pins the (marker,
24172 // constructor) pairing; this test pins the structural shape of
24173 // each wrap output bit-for-bit against the pre-lift inline form.
24174 let inner = Sexp::List(vec![Sexp::symbol("inner"), Sexp::int(7)]);
24175 for (qf, expected) in [
24176 (QuoteForm::Quote, Sexp::Quote(Box::new(inner.clone()))),
24177 (
24178 QuoteForm::Quasiquote,
24179 Sexp::Quasiquote(Box::new(inner.clone())),
24180 ),
24181 (QuoteForm::Unquote, Sexp::Unquote(Box::new(inner.clone()))),
24182 (
24183 QuoteForm::UnquoteSplice,
24184 Sexp::UnquoteSplice(Box::new(inner.clone())),
24185 ),
24186 ] {
24187 assert_eq!(
24188 qf.wrap(inner.clone()),
24189 expected,
24190 "wrap drifted from pre-lift Sexp::<Variant>(Box::new(inner)) form for {qf:?}"
24191 );
24192 }
24193 }
24194
24195 #[test]
24196 fn fmt_float_leaves_int_display_unchanged() {
24197 // Path-uniformity sibling: `Atom::Int` Display is unaffected by
24198 // the `fmt_float` introduction — the helper is wired only into
24199 // the `Atom::Float` arm of the Display match. A regression that
24200 // accidentally routes `Atom::Int` through `fmt_float` would
24201 // render `"1.0"` here and break every consumer that authored an
24202 // int kwarg expecting the bare-integer rendering.
24203 assert_eq!(Sexp::int(1).to_string(), "1");
24204 assert_eq!(Sexp::int(0).to_string(), "0");
24205 assert_eq!(Sexp::int(-42).to_string(), "-42");
24206 }
24207
24208 // ── AtomKind + Atom::kind: closed-set atomic-payload projection ─────
24209 //
24210 // `AtomKind` is the closed-set typed discriminator for `Atom`'s six
24211 // payload variants — `Symbol`, `Keyword`, `Str`, `Int`, `Float`,
24212 // `Bool`. It is the atomic-payload peer of `QuoteForm` (the four
24213 // homoiconic prefix wrappers), and the two closed sets together
24214 // carve every non-Nil non-List arm of `SexpShape`'s twelve-variant
24215 // closed set via their typed `sexp_shape` projections. Lifting the
24216 // (Atom variant, byte-discriminator, canonical-label,
24217 // SexpShape variant) quadruple onto ONE typed algebra collapses:
24218 // - `Hash for Atom`'s six byte literals (0/1/2/3/4/5) onto
24219 // `AtomKind::hash_discriminator` via `self.kind()` — ONE arm
24220 // at the discriminator site;
24221 // - `domain::sexp_shape`'s six `Atom::X(_) → SexpShape::X` arms
24222 // onto `a.kind().sexp_shape()` — ONE arm at the projection
24223 // site;
24224 // - any future LSP / REPL / metric-aggregator consumer that
24225 // needs to round-trip a rendered diagnostic label back into
24226 // the typed discriminator onto `AtomKind::FromStr` — ONE
24227 // decode site keyed on `AtomKind::ALL` + `AtomKind::label`.
24228 //
24229 // Tests below pin:
24230 // (a) `Atom::kind` projects every Atom variant to its typed
24231 // discriminator, regardless of inner payload contents;
24232 // (b) `AtomKind::ALL` enumerates every variant EXACTLY ONCE;
24233 // (c) `AtomKind::label` returns the canonical
24234 // lowercase / kebab string for every variant — byte-for-byte
24235 // identical to the corresponding `SexpShape::label`;
24236 // (d) `Display for AtomKind` delegates to `label`;
24237 // (e) `AtomKind::hash_discriminator` returns the same byte
24238 // values the pre-lift `Hash for Atom` arms emitted
24239 // (0/1/2/3/4/5) — pin the cache-key contract so a
24240 // regression that drifts a discriminator silently
24241 // invalidates every cached macro expansion fails loudly
24242 // here;
24243 // (f) `AtomKind::sexp_shape` projects every variant to the
24244 // matching `SexpShape` — the typed pairing the
24245 // `domain::sexp_shape` collapse relies on;
24246 // (g) `AtomKind::FromStr` round-trips every variant through its
24247 // label; rejects non-canonical capitalizations, empty input,
24248 // and the non-atom `SexpShape` labels (`"nil"`, `"list"`,
24249 // `"quote"`, `"quasiquote"`, `"unquote"`, `"unquote-splice"`);
24250 // (h) `UnknownAtomKind` carries the offending input verbatim and
24251 // renders the `#[error(...)]` annotation byte-exactly;
24252 // (i) `Hash for Atom` produces byte-identical hashes for every
24253 // atomic variant as the pre-lift implementation — pin the
24254 // cache-key contract end-to-end so the post-lift routing
24255 // through `AtomKind::hash_discriminator` cannot drift the
24256 // cache;
24257 // (j) the cross-projection composition law
24258 // `crate::domain::sexp_shape(&Sexp::Atom(a)) ==
24259 // a.kind().sexp_shape()` holds for every atomic kind.
24260
24261 #[test]
24262 fn atom_kind_projects_each_atom_variant_to_typed_marker() {
24263 // The structural identity `Atom::kind` establishes:
24264 // `Symbol(_) → AtomKind::Symbol`, `Keyword(_) →
24265 // AtomKind::Keyword`, etc. Pin every arm with a representative
24266 // payload + an empty / boundary payload so a regression that
24267 // matches on the payload rather than the variant identity
24268 // (e.g. a typo that routes `Str("")` to a different marker
24269 // than `Str("nonempty")`) surfaces immediately.
24270 assert_eq!(Atom::Symbol("foo".into()).kind(), AtomKind::Symbol);
24271 assert_eq!(Atom::Symbol(String::new()).kind(), AtomKind::Symbol);
24272 assert_eq!(Atom::Keyword("k".into()).kind(), AtomKind::Keyword);
24273 assert_eq!(Atom::Str("s".into()).kind(), AtomKind::Str);
24274 assert_eq!(Atom::Str(String::new()).kind(), AtomKind::Str);
24275 assert_eq!(Atom::Int(0).kind(), AtomKind::Int);
24276 assert_eq!(Atom::Int(i64::MIN).kind(), AtomKind::Int);
24277 assert_eq!(Atom::Int(i64::MAX).kind(), AtomKind::Int);
24278 assert_eq!(Atom::Float(0.0).kind(), AtomKind::Float);
24279 assert_eq!(Atom::Float(f64::NAN).kind(), AtomKind::Float);
24280 assert_eq!(Atom::Float(f64::INFINITY).kind(), AtomKind::Float);
24281 assert_eq!(Atom::Bool(true).kind(), AtomKind::Bool);
24282 assert_eq!(Atom::Bool(false).kind(), AtomKind::Bool);
24283 }
24284
24285 #[test]
24286 fn atom_kind_all_is_unique_and_complete() {
24287 // Closed-set posture: `ALL` enumerates every reachable variant
24288 // EXACTLY ONCE — no duplicates, no omissions. The `[Self; 6]`
24289 // array literal in the declaration forces the arity at compile
24290 // time; this test catches the orthogonal failure modes — a
24291 // future variant added at the type without being added to ALL
24292 // (silently dropped from every consumer's sweep), or a typo
24293 // that duplicates an entry (silently double-counted). Same
24294 // truth-table pinning every sibling closed-set lift in the
24295 // workspace uses (`SexpShape::ALL`, `RequestorKind::ALL`,
24296 // `ReceiptKind::ALL`, `ConditionKind::ALL`, `ProcessPhase::ALL`,
24297 // `ChannelKind::ALL`, …).
24298 //
24299 // The `iter+map+collect+sort_unstable` quadruple this test inlined
24300 // pre-lift now binds at `<AtomKind as ClosedSet>::sorted_labels()`
24301 // — the canonical-ordered candidate-list projection on the trait.
24302 // Distinctness of the sorted result is covered by
24303 // `assert_closed_set_well_formed::<AtomKind>()`, so this test
24304 // reduces to the per-implementor unique payload (the six diagnostic
24305 // labels in lexicographic order).
24306 assert_eq!(AtomKind::ALL.len(), 6);
24307 assert_eq!(
24308 <AtomKind as tatara_closed_set::ClosedSet>::sorted_labels(),
24309 vec!["bool", "float", "int", "keyword", "string", "symbol"],
24310 "AtomKind::ALL must cover every reachable Atom payload kind"
24311 );
24312 }
24313
24314 #[test]
24315 fn atom_kind_label_renders_canonical_string_for_every_variant() {
24316 // Pin every variant's canonical `&'static str` projection — a
24317 // regression that drifts any label (typo `"sym"` for
24318 // `"symbol"`, swap of `"int"` ↔ `"float"`, capitalization
24319 // drift `"String"` for `"string"`, or the `Str → "string"`
24320 // boundary rename being reversed to a literal `"str"`) fails-
24321 // loudly here. The six labels are byte-for-byte identical to
24322 // the corresponding `SexpShape::label` arms so the typed
24323 // diagnostic vocabulary stays unified across the AtomKind ⊂
24324 // SexpShape containment.
24325 assert_eq!(AtomKind::Symbol.label(), "symbol");
24326 assert_eq!(AtomKind::Keyword.label(), "keyword");
24327 assert_eq!(AtomKind::Str.label(), "string");
24328 assert_eq!(AtomKind::Int.label(), "int");
24329 assert_eq!(AtomKind::Float.label(), "float");
24330 assert_eq!(AtomKind::Bool.label(), "bool");
24331 }
24332
24333 #[test]
24334 fn atom_kind_label_agrees_with_sexp_shape_label_for_every_atom_arm() {
24335 // CROSS-PROJECTION VOCABULARY CONTRACT: each `AtomKind`
24336 // variant's `label()` is byte-for-byte identical to the
24337 // corresponding `SexpShape` variant's `label()` (after the
24338 // `Str → String` typed-variant rename which is intentional
24339 // — the wire vocabulary is `"string"` on both axes). Pin the
24340 // six-way agreement so a future label rename on EITHER side
24341 // (a SexpShape `"string"` → `"str"` drift, or an AtomKind
24342 // `"int"` → `"i64"` drift) fails-loudly here, NOT silently
24343 // at every cross-axis consumer. The pairing is load-bearing
24344 // for the typed-projection composition
24345 // `AtomKind::sexp_shape().label() == AtomKind::label()`.
24346 //
24347 // Post-lift this contract is structurally true by composition
24348 // (`AtomKind::label`'s body IS `self.sexp_shape().label()`),
24349 // so the cross-axis sweep is a tautology — the regression
24350 // surface lives at `SexpShape::label`'s atomic arms now,
24351 // pinned by `atom_kind_label_renders_canonical_string_for_every_variant`
24352 // (which keys the same six literals through the composition).
24353 // The sweep stays in place as a structural invariant pin in
24354 // case a future implementor reverses the lift and re-inlines
24355 // the per-variant arms here — drift between the two sites
24356 // would re-emerge and this test catches it.
24357 for kind in AtomKind::ALL {
24358 assert_eq!(
24359 kind.label(),
24360 kind.sexp_shape().label(),
24361 "label vocabulary drift between AtomKind::{kind:?} \
24362 and its SexpShape projection",
24363 );
24364 }
24365 }
24366
24367 #[test]
24368 fn atom_kind_label_routes_through_sexp_shape_label_via_sexp_shape_projection() {
24369 // ROUTING-PIN CONTRACT: post-lift `AtomKind::label`'s body
24370 // composes `Self::sexp_shape()` with `SexpShape::label()`
24371 // verbatim — no inline per-arm literal table. The composition
24372 // law `AtomKind::label(k) == AtomKind::sexp_shape(k).label()`
24373 // is structurally true for every `k: AtomKind`; pinning the
24374 // routing means a regression that re-inlines the six atomic-
24375 // arm literals here surfaces as a drift between the inline
24376 // copy and the `SexpShape::label` canonical site rather than
24377 // surviving silently.
24378 //
24379 // Six representative cases — one per variant — walked through
24380 // the composition manually and through the direct projection,
24381 // then byte-compared. A drift in EITHER half of the composition
24382 // (a typo in `Self::sexp_shape`'s match arms swapping
24383 // `Self::Int → SexpShape::Float`, OR a typo in `SexpShape::label`
24384 // dropping the `Int → "int"` arm) fails this assertion AND every
24385 // sibling per-arm assertion in
24386 // `atom_kind_label_renders_canonical_string_for_every_variant`
24387 // — but THIS test names the routing axis explicitly so a
24388 // regression to inline-literal-arms shows up as a failure of
24389 // the routing pin alongside the per-arm pin.
24390 //
24391 // Sibling-lift posture to the prior-run routing pins:
24392 // `sexp_to_json_object_arm_routes_through_is_kwargs_list_method`
24393 // (commit 4a11f5b) pins `Sexp::to_json`'s kwargs gate through
24394 // the lifted predicate. This pin extends the same posture to
24395 // `AtomKind::label`'s structural routing through the
24396 // `Self::sexp_shape() ∘ SexpShape::label` composition.
24397 //
24398 // Theory anchor: THEORY.md §V.1 — knowable platform; the
24399 // label-projection routing is a NAMED structural contract
24400 // pinned alongside the per-arm vocabulary contract, so
24401 // operators reading the test surface see BOTH the load-bearing
24402 // identity AND the load-bearing composition. THEORY.md §VI.1
24403 // — generation over composition; the label projection emerges
24404 // from the typed pairing rather than per-arm literal discipline,
24405 // and the routing pin enforces the lift stays in effect.
24406 for kind in AtomKind::ALL {
24407 let via_label = kind.label();
24408 let via_composition = kind.sexp_shape().label();
24409 assert_eq!(
24410 via_label, via_composition,
24411 "AtomKind::{kind:?}::label() must route through \
24412 Self::sexp_shape().label() — drift here means the \
24413 lift was reverted to inline arms",
24414 );
24415 // The pointer-equality check pins the composition produces
24416 // the SAME `&'static str` (not just a byte-equal copy) for
24417 // every variant — proof the routing hits ONE static literal
24418 // site (`SexpShape::label`) rather than a parallel inline
24419 // table.
24420 assert!(
24421 std::ptr::eq(via_label.as_ptr(), via_composition.as_ptr()),
24422 "AtomKind::{kind:?}::label() must return the SAME \
24423 `&'static str` as Self::sexp_shape().label() — \
24424 pointer drift means the lift composes through a \
24425 parallel literal table rather than routing into the \
24426 canonical SexpShape::label site",
24427 );
24428 }
24429 }
24430
24431 #[test]
24432 fn atom_kind_display_matches_label_for_every_variant() {
24433 // Pin Display-equals-label: any future
24434 // `#[error("... got {got}")]` annotation that threads through
24435 // this projection projects through Display, and Display
24436 // delegates to `label()`. A regression that introduces a
24437 // Display impl that deviates from `label()` (e.g. capitalizing
24438 // one variant) would drift any future diagnostic surface;
24439 // this test pins the contract. Sibling posture to
24440 // `sexp_shape_display_matches_label_for_every_variant` in
24441 // `error.rs`.
24442 assert_eq!(format!("{}", AtomKind::Symbol), "symbol");
24443 assert_eq!(format!("{}", AtomKind::Keyword), "keyword");
24444 assert_eq!(format!("{}", AtomKind::Str), "string");
24445 assert_eq!(format!("{}", AtomKind::Int), "int");
24446 assert_eq!(format!("{}", AtomKind::Float), "float");
24447 assert_eq!(format!("{}", AtomKind::Bool), "bool");
24448 }
24449
24450 #[test]
24451 fn atom_kind_hash_discriminator_pins_legacy_atom_cache_key_bytes() {
24452 // CACHE-KEY CONTRACT: pre-lift `Hash for Atom` used the literal
24453 // byte values 0/1/2/3/4/5 for Symbol/Keyword/Str/Int/Float/Bool
24454 // as the per-variant discriminator. The macro-expansion cache
24455 // (`Expander::cache`) keys on Hash; ANY change to a
24456 // discriminator byte silently invalidates every cached
24457 // expansion across the substrate. Pin the six legacy values
24458 // explicitly so a regression that re-numbers them surfaces
24459 // immediately — the `AtomKind` algebra MUST preserve the prior
24460 // byte mapping bit-for-bit. Sibling posture to
24461 // `quote_form_hash_discriminator_pins_legacy_cache_key_bytes`
24462 // on the quote-family axis.
24463 assert_eq!(AtomKind::Symbol.hash_discriminator(), 0);
24464 assert_eq!(AtomKind::Keyword.hash_discriminator(), 1);
24465 assert_eq!(AtomKind::Str.hash_discriminator(), 2);
24466 assert_eq!(AtomKind::Int.hash_discriminator(), 3);
24467 assert_eq!(AtomKind::Float.hash_discriminator(), 4);
24468 assert_eq!(AtomKind::Bool.hash_discriminator(), 5);
24469 }
24470
24471 #[test]
24472 fn atom_kind_hash_discriminator_bytes_are_pairwise_disjoint() {
24473 // Closed-set injectivity: the six discriminator bytes must
24474 // partition `{0, 1, 2, 3, 4, 5}` injectively so two distinct
24475 // `Atom` variants never produce the SAME hash discriminator —
24476 // a violation here means the cache could conflate two atomic
24477 // kinds with identical payloads (`Symbol("x")` and `Str("x")`
24478 // would silently share a cache slot). Sibling pin to
24479 // `atom_kind_all_is_unique_and_complete` on the label axis.
24480 let bytes: Vec<u8> = AtomKind::ALL
24481 .iter()
24482 .map(|k| k.hash_discriminator())
24483 .collect();
24484 let mut sorted = bytes.clone();
24485 sorted.sort_unstable();
24486 let mut deduped = sorted.clone();
24487 deduped.dedup();
24488 assert_eq!(
24489 sorted, deduped,
24490 "AtomKind hash discriminator bytes must be pairwise disjoint"
24491 );
24492 assert_eq!(sorted, vec![0, 1, 2, 3, 4, 5]);
24493 }
24494
24495 // ── `AtomKind::{SYMBOL_HASH_DISCRIMINATOR,
24496 // KEYWORD_HASH_DISCRIMINATOR, STR_HASH_DISCRIMINATOR,
24497 // INT_HASH_DISCRIMINATOR, FLOAT_HASH_DISCRIMINATOR,
24498 // BOOL_HASH_DISCRIMINATOR, HASH_DISCRIMINATORS}` — per-role `u8`
24499 // cache-key byte algebra on the closed-set atomic-payload
24500 // [`AtomKind`]. Second per-role axis on the algebra alongside the
24501 // diagnostic-label (commit fc126b8) `&'static str` axis — direct
24502 // sibling of the prior-run
24503 // [`crate::error::QuoteForm::HASH_DISCRIMINATORS`] lift (commit
24504 // cb9b026) on the quote-family sub-carving of the same outer-Sexp
24505 // Hash body.
24506 //
24507 // The two families partition their respective cache-key spaces
24508 // independently: `AtomKind` at `{0..=5}` NESTED inside
24509 // `Sexp::Atom`'s outer `1u8` byte (`Hash for Atom` runs on the
24510 // `Atom` type, not `Sexp`), `QuoteForm` at `{3..=6}` at the outer
24511 // `Sexp` cache-key space itself. Post-lift ALL FOUR closed-set
24512 // carvings that participate in `Hash for Sexp` / `Hash for Atom`
24513 // (`AtomKind` here, `QuoteForm` prior-run, `StructuralKind` still
24514 // inline `{0, 2}`, outer-`SexpShape` composed through the three
24515 // carvings) name their canonical bytes on the typed algebra rather
24516 // than at scattered inline literals.
24517
24518 #[test]
24519 fn atom_kind_hash_discriminators_pin_legacy_cache_key_bytes() {
24520 // Pin each per-role `pub(crate) const` at its exact canonical
24521 // `u8` byte. Sibling of
24522 // `atom_kind_hash_discriminator_pins_legacy_atom_cache_key_bytes`
24523 // (which pins the method's projection) — this pin asserts the
24524 // `pub(crate) const` value itself, so a regression that drifts
24525 // the constant but leaves the method's arm literal in place
24526 // (unlikely post-lift but structurally distinct) surfaces here.
24527 // The cache-key partition `{0..=5}` is load-bearing for
24528 // `Hash for Atom`'s injectivity contract — a `2u8` drift to
24529 // `0u8` would silently collide `AtomKind::Str` with
24530 // `AtomKind::Symbol`'s cache-key byte and mis-hash every
24531 // `Str(x)` through the `Symbol(x)` arm's cache slot.
24532 assert_eq!(AtomKind::SYMBOL_HASH_DISCRIMINATOR, 0);
24533 assert_eq!(AtomKind::KEYWORD_HASH_DISCRIMINATOR, 1);
24534 assert_eq!(AtomKind::STR_HASH_DISCRIMINATOR, 2);
24535 assert_eq!(AtomKind::INT_HASH_DISCRIMINATOR, 3);
24536 assert_eq!(AtomKind::FLOAT_HASH_DISCRIMINATOR, 4);
24537 assert_eq!(AtomKind::BOOL_HASH_DISCRIMINATOR, 5);
24538 }
24539
24540 #[test]
24541 fn atom_kind_hash_discriminator_routes_through_typed_per_role_constants() {
24542 // PATH-UNIFORMITY: `Self::hash_discriminator(self)` returns the
24543 // per-role `pub(crate) const` byte-for-byte per variant,
24544 // catching a regression that reverts ONE arm to an inline
24545 // `0` / `1` / `2` / `3` / `4` / `5` `u8` literal (or drifts one
24546 // arm's byte silently). Sibling posture to
24547 // `quote_form_hash_discriminator_routes_through_typed_per_role_constants`
24548 // on the quote-family sub-carving of the SAME outer-Sexp Hash
24549 // body.
24550 for (kind, expected) in [
24551 (AtomKind::Symbol, AtomKind::SYMBOL_HASH_DISCRIMINATOR),
24552 (AtomKind::Keyword, AtomKind::KEYWORD_HASH_DISCRIMINATOR),
24553 (AtomKind::Str, AtomKind::STR_HASH_DISCRIMINATOR),
24554 (AtomKind::Int, AtomKind::INT_HASH_DISCRIMINATOR),
24555 (AtomKind::Float, AtomKind::FLOAT_HASH_DISCRIMINATOR),
24556 (AtomKind::Bool, AtomKind::BOOL_HASH_DISCRIMINATOR),
24557 ] {
24558 let actual = kind.hash_discriminator();
24559 assert_eq!(
24560 actual, expected,
24561 "AtomKind::{kind:?}.hash_discriminator() `{actual}` \
24562 drifted from per-role constant `{expected}` — the \
24563 arm must route through the typed `pub(crate) const` \
24564 rather than an inline `u8` literal",
24565 );
24566 }
24567 }
24568
24569 #[test]
24570 fn atom_kind_hash_discriminators_has_expected_cardinality() {
24571 // Cardinality contract: `Self::HASH_DISCRIMINATORS.len() == 6`
24572 // — pinned at the declaration site by rustc's forced-arity
24573 // check on `[u8; 6]`. This test surfaces the arity as a
24574 // fail-loud runtime pin so a future refactor that switches the
24575 // array type to `&[u8]` (dropping the compile-time arity
24576 // forcing) doesn't silently loosen the closed-set discipline
24577 // the family relies on. Sibling posture to
24578 // `atom_kind_labels_has_expected_cardinality` on the diagnostic
24579 // label axis of the SAME closed set, and to
24580 // `quote_form_hash_discriminators_has_expected_cardinality`
24581 // on the quote-family sub-carving's cache-key-byte peer.
24582 assert_eq!(
24583 AtomKind::HASH_DISCRIMINATORS.len(),
24584 6,
24585 "AtomKind::HASH_DISCRIMINATORS cardinality drifted from 6 \
24586 — the closed atomic-payload domain admits exactly six \
24587 kinds by construction; a seventh extension surfaces here"
24588 );
24589 }
24590
24591 #[test]
24592 fn atom_kind_hash_discriminators_align_with_all_by_index() {
24593 // ALIGNMENT CONTRACT: `Self::HASH_DISCRIMINATORS[i] ==
24594 // Self::ALL[i].hash_discriminator()` element-wise. Pins that
24595 // the typed variant ALL and the `u8` HASH_DISCRIMINATORS ALL
24596 // stay in lockstep under any reorder — a regression that
24597 // reorders ONE array without reordering the other silently
24598 // misaligns every `zip(ALL, HASH_DISCRIMINATORS)` consumer.
24599 // Sibling posture to `atom_kind_labels_align_with_all_by_index`
24600 // on the diagnostic label axis of the SAME closed set.
24601 for (i, kind) in AtomKind::ALL.iter().enumerate() {
24602 assert_eq!(
24603 AtomKind::HASH_DISCRIMINATORS[i],
24604 kind.hash_discriminator(),
24605 "AtomKind::HASH_DISCRIMINATORS[{i}] `{disc}` drifted \
24606 from AtomKind::ALL[{i}] ({kind:?}).hash_discriminator() \
24607 `{via_variant}` — the canonical declaration order of \
24608 the ALL array and the hash_discriminator projection \
24609 must match element-wise",
24610 disc = AtomKind::HASH_DISCRIMINATORS[i],
24611 via_variant = kind.hash_discriminator(),
24612 );
24613 }
24614 }
24615
24616 #[test]
24617 fn atom_kind_hash_discriminators_pairwise_distinct() {
24618 // PAIRWISE DISJOINTNESS: every entry of the
24619 // `HASH_DISCRIMINATORS` array must differ so the nested `Atom`
24620 // Hash body cannot route two atomic-payload variants through
24621 // the same cache-key byte — a collision would silently mis-
24622 // hash two structurally-distinct atoms (`Symbol("x")` and
24623 // `Str("x")`) to the same `Expander::cache` slot. Family-wide
24624 // sweep over `HASH_DISCRIMINATORS × HASH_DISCRIMINATORS` —
24625 // supersedes any per-pair pin and picks up new discriminators
24626 // mechanically. Sibling posture to
24627 // `atom_kind_hash_discriminator_bytes_are_pairwise_disjoint`
24628 // (which sweeps via the projection) — this sweep pins the array
24629 // itself so a regression that lifts a fresh entry with a
24630 // colliding byte surfaces at the ALL sweep.
24631 for (i, a) in AtomKind::HASH_DISCRIMINATORS.iter().enumerate() {
24632 for (j, b) in AtomKind::HASH_DISCRIMINATORS.iter().enumerate() {
24633 if i == j {
24634 continue;
24635 }
24636 assert_ne!(
24637 a, b,
24638 "AtomKind::HASH_DISCRIMINATORS[{i}] `{a}` collides \
24639 with AtomKind::HASH_DISCRIMINATORS[{j}] `{b}` — \
24640 the nested Atom Hash body's cache-key partition \
24641 would route two atomic-payload variants through \
24642 the same slot"
24643 );
24644 }
24645 }
24646 }
24647
24648 #[test]
24649 fn atom_kind_outer_hash_discriminator_pins_legacy_atomic_carve_outer_marker_byte() {
24650 // Pin `AtomKind::OUTER_HASH_DISCRIMINATOR` at its exact canonical
24651 // `u8` byte — the outer-Sexp cache-key byte at which ALL SIX
24652 // atomic-payload shapes collapse when hashed at the outer
24653 // `Hash for Sexp` level. Pre-lift the same byte lived at four
24654 // sites: the inline `1u8` literal at
24655 // `SexpShape::hash_discriminator`'s six-arm atomic collapse (in
24656 // error.rs), the inline `1u8` literal at
24657 // `sexp_shape_hash_discriminator_atomic_arms_collapse_to_outer_atom_marker`'s
24658 // assertion body, the inline `1u8` literal at
24659 // `sexp_shape_hash_discriminator_partitions_by_three_way_carving_disjointly`'s
24660 // `expected_atomic` fixture, PLUS a duplicated local `const
24661 // ATOM_OUTER_CARVE_BYTE: u8 = 1` inside
24662 // `structural_kind_hash_discriminator_disjoint_from_atom_outer_carve_byte_and_quote_form_hash_discriminator_partition`.
24663 // Post-lift the byte binds at ONE `pub(crate) const` on the
24664 // closed-set `AtomKind` algebra; every downstream consumer
24665 // (the shape-level projection's atomic collapse arm, the two
24666 // three-way carving pins in error.rs, the joint-partition
24667 // disjointness pin in error.rs, a future `tatara-check`
24668 // predicate on the outer-Sexp cache-key partition) picks up
24669 // the same canonical byte from ONE source of truth. Sibling
24670 // posture to `atom_kind_hash_discriminators_pin_legacy_cache_key_bytes`
24671 // (which pins the six NESTED INNER `{0..=5}` bytes on the
24672 // per-atom-kind inner algebra) — this pin closes the (nested,
24673 // outer) pairing on the same `AtomKind` algebra by naming the
24674 // outer-Sexp marker byte alongside the nested inner carve.
24675 // The cache-key partition is load-bearing for the outer
24676 // `Hash for Sexp` prefix-uniqueness contract — a `1u8` drift
24677 // to `0u8` would silently collide the atomic-carve outer
24678 // marker with `StructuralKind::Nil`'s outer byte (`0`) and
24679 // mis-hash every `Sexp::Atom(_)` through the `Sexp::Nil`
24680 // arm's cache slot; a drift to `2u8` would collide with
24681 // `StructuralKind::List`'s outer byte and mis-hash through
24682 // the `Sexp::List(_)` arm's slot; a drift to `3u8` /
24683 // `4u8` / `5u8` / `6u8` would collide with the quote-family
24684 // carve's four bytes.
24685 assert_eq!(AtomKind::OUTER_HASH_DISCRIMINATOR, 1);
24686 }
24687
24688 #[test]
24689 fn atom_kind_outer_hash_discriminator_disjoint_from_inner_hash_discriminators_at_outer_sexp_level(
24690 ) {
24691 // OUTER-vs-INNER SEPARATION: the outer-Sexp cache-key algebra
24692 // uses TWO distinct byte spaces at TWO hash-sequence positions
24693 // for the atomic-carve: `AtomKind::OUTER_HASH_DISCRIMINATOR`
24694 // (`1u8` at the outer `Hash for Sexp` position) AND the
24695 // NESTED INNER `AtomKind::HASH_DISCRIMINATORS` bytes (`{0..=5}`
24696 // at the inner `Hash for Atom` position). The two byte spaces
24697 // OVERLAP numerically (both contain `1u8`) but do NOT collide
24698 // at the cache because they live at DIFFERENT hash-sequence
24699 // positions in the composed `(outer_discriminator,
24700 // inner_discriminator, inner_payload)` triple. Pin the outer
24701 // scalar's byte at `AtomKind::OUTER_HASH_DISCRIMINATOR` and
24702 // the inner set at `AtomKind::HASH_DISCRIMINATORS` so a future
24703 // refactor that conflates the two axes (e.g. drops the outer
24704 // marker byte at `Hash for Sexp`'s Atom arm and expects the
24705 // inner byte to distinguish outer-Sexp variants directly)
24706 // surfaces at THIS test as a documentation-of-intent failure.
24707 // The check is intentionally structural: it asserts the outer
24708 // scalar is IN the same numeric range as the inner set (both
24709 // are `u8`, both live within `{0..=6}` on the outer cache-key
24710 // partition) BUT that the outer scalar sits at position 1
24711 // where the inner byte space would otherwise have
24712 // `AtomKind::KEYWORD_HASH_DISCRIMINATOR` collide. That
24713 // numeric-collision without cache-collision is the
24714 // load-bearing property this lift documents: the two axes are
24715 // typed distinct because they live at typed distinct
24716 // positions in the composed hash sequence, even though their
24717 // byte spaces overlap.
24718 assert!(
24719 AtomKind::HASH_DISCRIMINATORS.contains(&AtomKind::OUTER_HASH_DISCRIMINATOR),
24720 "the outer-carve marker byte `{outer}` MUST lie within the \
24721 inner cache-key partition `{inner:?}` — the two axes' \
24722 byte spaces overlap by design (the outer distinguishes \
24723 `Sexp::Atom(_)` from every other outer-Sexp variant at \
24724 the outer hash position; the inner distinguishes the six \
24725 atomic-payload variants at the nested inner hash \
24726 position). If this contains check fails, the outer \
24727 scalar has drifted OUTSIDE the inner partition and the \
24728 (outer, inner) numeric-overlap-without-cache-collision \
24729 property this lift documents no longer holds — which \
24730 would in turn mean either the outer byte drifted out of \
24731 `{{0..=5}}` (compile the substrate against the resulting \
24732 outer-Sexp partition to find the drift) or the inner \
24733 partition shrank below `{{0..=5}}` (`atom_kind_hash_discriminators_align_with_all_by_index` \
24734 fails-loudly first).",
24735 outer = AtomKind::OUTER_HASH_DISCRIMINATOR,
24736 inner = AtomKind::HASH_DISCRIMINATORS,
24737 );
24738 }
24739
24740 #[test]
24741 fn atom_kind_sexp_shape_pins_canonical_shape_identity_for_every_variant() {
24742 // CLOSED-SET SHAPE-PROJECTION CONTRACT: each `AtomKind` variant
24743 // projects to its matching `SexpShape` variant — load-bearing
24744 // for the (Atom variant, SexpShape variant) pairing the
24745 // substrate's outer-shape projection `domain::sexp_shape` routes
24746 // through. Sibling-arm sweep so the six pairings stay
24747 // load-bearing under reordering refactors. A regression that
24748 // drifts ONE arm (e.g. routes `AtomKind::Int` to
24749 // `SexpShape::Float`) surfaces here immediately rather than as
24750 // a silent operator-facing diagnostic drift at every
24751 // `LispError::TypeMismatch.got` slot for an atomic witness.
24752 // Sibling posture to
24753 // `quote_form_sexp_shape_pins_canonical_shape_identity_for_every_variant`.
24754 assert_eq!(AtomKind::Symbol.sexp_shape(), SexpShape::Symbol);
24755 assert_eq!(AtomKind::Keyword.sexp_shape(), SexpShape::Keyword);
24756 assert_eq!(AtomKind::Str.sexp_shape(), SexpShape::String);
24757 assert_eq!(AtomKind::Int.sexp_shape(), SexpShape::Int);
24758 assert_eq!(AtomKind::Float.sexp_shape(), SexpShape::Float);
24759 assert_eq!(AtomKind::Bool.sexp_shape(), SexpShape::Bool);
24760 }
24761
24762 #[test]
24763 fn atom_kind_per_role_shapes_pin_canonical_sexp_shape_variants() {
24764 // PER-ROLE ALIAS CONTRACT: each `AtomKind::*_SHAPE` per-role
24765 // `pub const` binds byte-for-byte to its canonical `SexpShape`
24766 // variant on the AtomKind ⊂ SexpShape carving. Pin so a
24767 // regression that swaps ONE alias (e.g. re-aims `STR_SHAPE`
24768 // at `SexpShape::Symbol`) surfaces at rustc / test time
24769 // rather than as a silent operator-facing diagnostic drift at
24770 // every consumer keyed on the typed embed target. Sibling
24771 // posture to `atom_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
24772 // on the diagnostic label axis of the SAME closed set — this
24773 // pin is the peer on the `SexpShape` embed-target axis.
24774 assert_eq!(AtomKind::SYMBOL_SHAPE, SexpShape::Symbol);
24775 assert_eq!(AtomKind::KEYWORD_SHAPE, SexpShape::Keyword);
24776 assert_eq!(AtomKind::STR_SHAPE, SexpShape::String);
24777 assert_eq!(AtomKind::INT_SHAPE, SexpShape::Int);
24778 assert_eq!(AtomKind::FLOAT_SHAPE, SexpShape::Float);
24779 assert_eq!(AtomKind::BOOL_SHAPE, SexpShape::Bool);
24780 }
24781
24782 #[test]
24783 fn atom_kind_shapes_has_expected_cardinality() {
24784 // CLOSED-SET CARDINALITY CONTRACT: `AtomKind::SHAPES` carries
24785 // exactly SIX entries — one per variant in the closed-set
24786 // atomic-payload carving. Runtime companion to the `[SexpShape;
24787 // 6]` type annotation's compile-time forced arity. A regression
24788 // that widens the array without adding a matching per-role
24789 // `*_SHAPE` alias would fail the `[SexpShape; 6]` type check
24790 // at rustc time; this runtime pin catches a silent shrink or
24791 // duplicate-arm coalesce. Sibling posture to
24792 // `atom_kind_labels_has_expected_cardinality` and
24793 // `atom_kind_hash_discriminators_has_expected_cardinality` on
24794 // the other two per-role axes of the SAME closed set.
24795 assert_eq!(AtomKind::SHAPES.len(), 6);
24796 assert_eq!(AtomKind::SHAPES.len(), AtomKind::ALL.len());
24797 }
24798
24799 #[test]
24800 fn atom_kind_shapes_align_with_all_by_index() {
24801 // ALIGNMENT CONTRACT: `Self::SHAPES[i] ==
24802 // Self::ALL[i].sexp_shape()` element-wise. Pins that the typed
24803 // variant ALL and the `SexpShape` SHAPES ALL stay in lockstep
24804 // under any reorder — a regression that reorders ONE array
24805 // without reordering the other silently misaligns every
24806 // `zip(ALL, SHAPES)` consumer that wants to project each
24807 // atomic variant to its canonical outer-shape identity
24808 // (LSP completion, `tatara-check` predicate over the
24809 // atomic ⊂ outer partition, Sekiban audit-trail metric
24810 // jointly labeled by the embed target). Sibling posture to
24811 // `atom_kind_labels_align_with_all_by_index` and
24812 // `atom_kind_hash_discriminators_align_with_all_by_index` on
24813 // the other two per-role axes of the SAME closed set.
24814 for (i, kind) in AtomKind::ALL.iter().enumerate() {
24815 assert_eq!(
24816 AtomKind::SHAPES[i],
24817 kind.sexp_shape(),
24818 "AtomKind::SHAPES[{i}] `{shape:?}` drifted from \
24819 AtomKind::ALL[{i}] ({kind:?}).sexp_shape() \
24820 `{via_variant:?}` — the canonical declaration order \
24821 of the ALL array and the sexp_shape projection must \
24822 match element-wise",
24823 shape = AtomKind::SHAPES[i],
24824 via_variant = kind.sexp_shape(),
24825 );
24826 }
24827 }
24828
24829 #[test]
24830 fn atom_kind_shapes_align_with_all_by_index_through_as_atom_kind() {
24831 // ROUND-TRIP CONTRACT: `Self::SHAPES[i].as_atom_kind() ==
24832 // Some(Self::ALL[i])` element-wise. Pins the embed / project
24833 // section of the (`AtomKind::sexp_shape`,
24834 // `SexpShape::as_atom_kind`) `Iso(AtomKind, AtomShape ⊂
24835 // SexpShape)` as a family-wide array-indexed law rather than
24836 // as a per-variant assertion sweep. A regression that drifts
24837 // EITHER the `SHAPES` array entries OR the peer inverse
24838 // `as_atom_kind` arms silently breaks the (embed, project)
24839 // section — this pin catches both directions at ONCE.
24840 // Sibling posture to the pre-existing per-variant round-trip
24841 // sweep `atom_kind_sexp_shape_round_trips_through_sexp_shape_as_atom_kind`
24842 // (which sweeps through the projection method); this sweep
24843 // pins the round-trip through the SHAPES array directly so a
24844 // regression on ANY of the three surfaces (per-role alias,
24845 // family-wide array, projection method) fails-loudly at the
24846 // array-indexed sweep.
24847 for (i, kind) in AtomKind::ALL.iter().enumerate() {
24848 assert_eq!(
24849 AtomKind::SHAPES[i].as_atom_kind(),
24850 Some(*kind),
24851 "AtomKind::SHAPES[{i}] `{shape:?}`.as_atom_kind() = \
24852 {actual:?} drifted from Some(AtomKind::ALL[{i}]) = \
24853 Some({expected:?}) — the (SHAPES entry, ALL variant) \
24854 round-trip through the peer inverse SexpShape::as_atom_kind \
24855 must hold element-wise",
24856 shape = AtomKind::SHAPES[i],
24857 actual = AtomKind::SHAPES[i].as_atom_kind(),
24858 expected = kind,
24859 );
24860 }
24861 }
24862
24863 #[test]
24864 fn atom_kind_sexp_shape_routes_through_typed_per_role_constants() {
24865 // ROUTING CONTRACT: `AtomKind::sexp_shape()`'s six arms bind
24866 // through the per-role `pub const *_SHAPE` aliases rather
24867 // than through inline `SexpShape::X` literals. Pin so a
24868 // regression that re-inlines the six literals here (and
24869 // gains its own drift surface separate from the canonical
24870 // per-role alias site) surfaces immediately at variant
24871 // equality. Sibling posture to
24872 // `atom_kind_label_arms_route_through_per_role_labels_for_every_variant`
24873 // and `atom_kind_hash_discriminator_routes_through_typed_per_role_constants`
24874 // on the other two per-role axes of the SAME closed set.
24875 assert_eq!(AtomKind::Symbol.sexp_shape(), AtomKind::SYMBOL_SHAPE);
24876 assert_eq!(AtomKind::Keyword.sexp_shape(), AtomKind::KEYWORD_SHAPE);
24877 assert_eq!(AtomKind::Str.sexp_shape(), AtomKind::STR_SHAPE);
24878 assert_eq!(AtomKind::Int.sexp_shape(), AtomKind::INT_SHAPE);
24879 assert_eq!(AtomKind::Float.sexp_shape(), AtomKind::FLOAT_SHAPE);
24880 assert_eq!(AtomKind::Bool.sexp_shape(), AtomKind::BOOL_SHAPE);
24881 }
24882
24883 #[test]
24884 fn atom_kind_shapes_pairwise_distinct() {
24885 // PAIRWISE DISJOINTNESS: every entry of the `SHAPES` array
24886 // must differ so the (AtomKind variant, SexpShape embed
24887 // target) mapping stays injective — a collision would silently
24888 // route two distinct AtomKind variants through the SAME
24889 // outer-shape identity, breaking the AtomKind ⊂ SexpShape
24890 // 6-of-12 carving. Family-wide sweep over `SHAPES × SHAPES` —
24891 // supersedes any per-pair pin and picks up new embed targets
24892 // mechanically. Sibling posture to
24893 // `atom_kind_labels_pairwise_distinct` and
24894 // `atom_kind_hash_discriminators_pairwise_distinct` on the
24895 // other two per-role axes of the SAME closed set.
24896 for (i, a) in AtomKind::SHAPES.iter().enumerate() {
24897 for (j, b) in AtomKind::SHAPES.iter().enumerate() {
24898 if i == j {
24899 continue;
24900 }
24901 assert_ne!(
24902 a, b,
24903 "AtomKind::SHAPES[{i}] `{a:?}` collides with \
24904 AtomKind::SHAPES[{j}] `{b:?}` — the AtomKind ⊂ \
24905 SexpShape 6-of-12 carving would route two atomic \
24906 variants through the same outer-shape identity"
24907 );
24908 }
24909 }
24910 }
24911
24912 #[test]
24913 fn quote_form_per_role_shapes_pin_canonical_sexp_shape_variants() {
24914 // PER-ROLE ALIAS CONTRACT: each `QuoteForm::*_SHAPE` per-role
24915 // `pub const` binds byte-for-byte to its canonical `SexpShape`
24916 // variant on the QuoteForm ⊂ SexpShape 4-of-12 carving. Pin
24917 // so a regression that swaps ONE alias (e.g. re-aims
24918 // `UNQUOTE_SPLICE_SHAPE` at `SexpShape::Unquote`) surfaces at
24919 // rustc / test time rather than as a silent operator-facing
24920 // diagnostic drift at every consumer keyed on the typed embed
24921 // target. Sibling posture to
24922 // `quote_form_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
24923 // on the diagnostic label axis of the SAME closed set — this
24924 // pin is the peer on the `SexpShape` embed-target axis.
24925 assert_eq!(QuoteForm::QUOTE_SHAPE, SexpShape::Quote);
24926 assert_eq!(QuoteForm::QUASIQUOTE_SHAPE, SexpShape::Quasiquote);
24927 assert_eq!(QuoteForm::UNQUOTE_SHAPE, SexpShape::Unquote);
24928 assert_eq!(QuoteForm::UNQUOTE_SPLICE_SHAPE, SexpShape::UnquoteSplice);
24929 }
24930
24931 #[test]
24932 fn quote_form_shapes_has_expected_cardinality() {
24933 // CLOSED-SET CARDINALITY CONTRACT: `QuoteForm::SHAPES` carries
24934 // exactly FOUR entries — one per variant in the closed-set
24935 // quote-family carving. Runtime companion to the `[SexpShape;
24936 // 4]` type annotation's compile-time forced arity. A regression
24937 // that widens the array without adding a matching per-role
24938 // `*_SHAPE` alias would fail the `[SexpShape; 4]` type check
24939 // at rustc time; this runtime pin catches a silent shrink or
24940 // duplicate-arm coalesce. Sibling posture to
24941 // `quote_form_labels_has_expected_cardinality` /
24942 // `quote_form_prefixes_has_expected_cardinality` /
24943 // `quote_form_iac_forge_tags_has_expected_cardinality` /
24944 // `quote_form_hash_discriminators_has_expected_cardinality` on
24945 // the other four per-role axes of the SAME closed set.
24946 assert_eq!(QuoteForm::SHAPES.len(), 4);
24947 assert_eq!(QuoteForm::SHAPES.len(), QuoteForm::ALL.len());
24948 }
24949
24950 #[test]
24951 fn quote_form_shapes_align_with_all_by_index() {
24952 // ALIGNMENT CONTRACT: `Self::SHAPES[i] ==
24953 // Self::ALL[i].sexp_shape()` element-wise. Pins that the typed
24954 // variant ALL and the `SexpShape` SHAPES ALL stay in lockstep
24955 // under any reorder — a regression that reorders ONE array
24956 // without reordering the other silently misaligns every
24957 // `zip(ALL, SHAPES)` consumer that wants to project each
24958 // quote-family variant to its canonical outer-shape identity
24959 // (LSP completion, `tatara-check` predicate over the quote-
24960 // family ⊂ outer partition, Sekiban audit-trail metric jointly
24961 // labeled by the embed target). Sibling posture to
24962 // `quote_form_labels_align_with_all_by_index` /
24963 // `quote_form_prefixes_align_with_all_by_index` on the other
24964 // per-role axes of the SAME closed set.
24965 for (i, form) in QuoteForm::ALL.iter().enumerate() {
24966 assert_eq!(
24967 QuoteForm::SHAPES[i],
24968 form.sexp_shape(),
24969 "QuoteForm::SHAPES[{i}] `{shape:?}` drifted from \
24970 QuoteForm::ALL[{i}] ({form:?}).sexp_shape() \
24971 `{via_variant:?}` — the canonical declaration order \
24972 of the ALL array and the sexp_shape projection must \
24973 match element-wise",
24974 shape = QuoteForm::SHAPES[i],
24975 via_variant = form.sexp_shape(),
24976 );
24977 }
24978 }
24979
24980 #[test]
24981 fn quote_form_shapes_align_with_all_by_index_through_as_quote_form() {
24982 // ROUND-TRIP CONTRACT: `Self::SHAPES[i].as_quote_form() ==
24983 // Some(Self::ALL[i])` element-wise. Pins the embed / project
24984 // section of the (`QuoteForm::sexp_shape`,
24985 // `SexpShape::as_quote_form`) `Iso(QuoteForm, QuoteShape ⊂
24986 // SexpShape)` as a family-wide array-indexed law rather than
24987 // as a per-variant assertion sweep. A regression that drifts
24988 // EITHER the `SHAPES` array entries OR the peer inverse
24989 // `as_quote_form` arms silently breaks the (embed, project)
24990 // section — this pin catches both directions at ONCE. Sibling
24991 // posture to
24992 // `atom_kind_shapes_align_with_all_by_index_through_as_atom_kind`
24993 // on the peer 6-of-12 atomic-payload carving.
24994 for (i, form) in QuoteForm::ALL.iter().enumerate() {
24995 assert_eq!(
24996 QuoteForm::SHAPES[i].as_quote_form(),
24997 Some(*form),
24998 "QuoteForm::SHAPES[{i}] `{shape:?}`.as_quote_form() = \
24999 {actual:?} drifted from Some(QuoteForm::ALL[{i}]) = \
25000 Some({expected:?}) — the (SHAPES entry, ALL variant) \
25001 round-trip through the peer inverse SexpShape::as_quote_form \
25002 must hold element-wise",
25003 shape = QuoteForm::SHAPES[i],
25004 actual = QuoteForm::SHAPES[i].as_quote_form(),
25005 expected = form,
25006 );
25007 }
25008 }
25009
25010 #[test]
25011 fn quote_form_sexp_shape_routes_through_typed_per_role_constants() {
25012 // ROUTING CONTRACT: `QuoteForm::sexp_shape()`'s four arms bind
25013 // through the per-role `pub const *_SHAPE` aliases rather than
25014 // through inline `SexpShape::X` literals. Pin so a regression
25015 // that re-inlines the four literals here (and gains its own
25016 // drift surface separate from the canonical per-role alias
25017 // site) surfaces immediately at variant equality. Sibling
25018 // posture to
25019 // `quote_form_label_composes_through_sexp_shape_label_for_every_variant`
25020 // (which pins label routing through sexp_shape().label()) and
25021 // `atom_kind_sexp_shape_routes_through_typed_per_role_constants`
25022 // on the peer 6-of-12 atomic-payload carving.
25023 assert_eq!(QuoteForm::Quote.sexp_shape(), QuoteForm::QUOTE_SHAPE);
25024 assert_eq!(
25025 QuoteForm::Quasiquote.sexp_shape(),
25026 QuoteForm::QUASIQUOTE_SHAPE
25027 );
25028 assert_eq!(QuoteForm::Unquote.sexp_shape(), QuoteForm::UNQUOTE_SHAPE);
25029 assert_eq!(
25030 QuoteForm::UnquoteSplice.sexp_shape(),
25031 QuoteForm::UNQUOTE_SPLICE_SHAPE
25032 );
25033 }
25034
25035 #[test]
25036 fn quote_form_shapes_pairwise_distinct() {
25037 // PAIRWISE DISJOINTNESS: every entry of the `SHAPES` array
25038 // must differ so the (QuoteForm variant, SexpShape embed
25039 // target) mapping stays injective — a collision would silently
25040 // route two distinct QuoteForm variants through the SAME
25041 // outer-shape identity, breaking the QuoteForm ⊂ SexpShape
25042 // 4-of-12 carving. Family-wide sweep over `SHAPES × SHAPES` —
25043 // supersedes any per-pair pin and picks up new embed targets
25044 // mechanically. Sibling posture to
25045 // `atom_kind_shapes_pairwise_distinct` on the peer 6-of-12
25046 // atomic-payload carving.
25047 for (i, a) in QuoteForm::SHAPES.iter().enumerate() {
25048 for (j, b) in QuoteForm::SHAPES.iter().enumerate() {
25049 if i == j {
25050 continue;
25051 }
25052 assert_ne!(
25053 a, b,
25054 "QuoteForm::SHAPES[{i}] `{a:?}` collides with \
25055 QuoteForm::SHAPES[{j}] `{b:?}` — the QuoteForm ⊂ \
25056 SexpShape 4-of-12 carving would route two quote-\
25057 family variants through the same outer-shape identity"
25058 );
25059 }
25060 }
25061 }
25062
25063 #[test]
25064 fn atom_kind_label_round_trips_through_from_str() {
25065 // Bidirectional `label` ↔ `FromStr` contract: for every variant
25066 // in ALL, `kind.label().parse() == Ok(kind)`. A regression that
25067 // drifts the (variant, literal) pairing at ONE arm of `label`
25068 // (typo, capitalization drift) OR at the `FromStr` decode body
25069 // (off-by-one, missing variant in the sweep) fails-loudly here.
25070 // The canonical-literal site is singular (`label`) so the
25071 // round-trip is the only way the typed surface and the
25072 // rendered diagnostic literal can drift apart — pinning it
25073 // here means they cannot. Sibling posture to
25074 // `sexp_shape_label_round_trips_through_from_str`.
25075 for kind in AtomKind::ALL {
25076 let parsed: AtomKind = kind
25077 .label()
25078 .parse()
25079 .expect("every ALL variant's label must round-trip through FromStr");
25080 assert_eq!(
25081 parsed,
25082 kind,
25083 "FromStr({}) must round-trip to the same variant",
25084 kind.label()
25085 );
25086 }
25087 }
25088
25089 #[test]
25090 fn unknown_atom_kind_carries_offending_input_verbatim() {
25091 // Operator-facing diagnostic contract: the offending input
25092 // lands in the typed error verbatim — no normalization, no
25093 // case-folding, no truncation. Pin the exact `#[error(...)]`
25094 // rendering AND the typed `.0` field projection so a future
25095 // refactor that normalizes (e.g. `.to_lowercase()`) before
25096 // building the error or that drops the input fails-loudly
25097 // here. Symmetric to every sibling `Unknown*` carrier in the
25098 // workspace.
25099 let err: UnknownAtomKind = "Symbol".parse::<AtomKind>().expect_err(
25100 "capitalized `Symbol` must NOT decode — labels are byte-equal case-sensitive",
25101 );
25102 assert_eq!(err.0, "Symbol");
25103 assert_eq!(format!("{err}"), "unknown atom kind: Symbol");
25104
25105 let err: UnknownAtomKind = "str"
25106 .parse::<AtomKind>()
25107 .expect_err("`str` is not a canonical AtomKind label — `string` is");
25108 assert_eq!(err.0, "str");
25109 assert_eq!(format!("{err}"), "unknown atom kind: str");
25110
25111 let err: UnknownAtomKind = ""
25112 .parse::<AtomKind>()
25113 .expect_err("empty input must NOT decode to an AtomKind");
25114 assert_eq!(err.0, "");
25115 assert_eq!(format!("{err}"), "unknown atom kind: ");
25116 }
25117
25118 #[test]
25119 fn atom_kind_from_str_rejects_non_atom_sexp_shape_labels() {
25120 // CROSS-AXIS GUARD: `SexpShape::label()`'s vocabulary is the
25121 // SUPERSET of `AtomKind::label()`'s — every AtomKind label
25122 // decodes successfully through SexpShape's FromStr to the
25123 // matching SexpShape variant (because the typed projections
25124 // agree), but the SIX non-atom SexpShape labels (`"nil"`,
25125 // `"list"`, `"quote"`, `"quasiquote"`, `"unquote"`,
25126 // `"unquote-splice"`) MUST reject through AtomKind's FromStr
25127 // — they have no atomic-kind preimage. A FromStr that
25128 // silently accepted `"list"` as an AtomKind would corrupt
25129 // the typed identity downstream of any future diagnostic
25130 // round-trip. Pin BOTH directions: the six atom labels
25131 // decode successfully (and to the matching `AtomKind`
25132 // variant), the six non-atom labels reject.
25133 assert_eq!("symbol".parse::<AtomKind>().unwrap(), AtomKind::Symbol);
25134 assert_eq!("keyword".parse::<AtomKind>().unwrap(), AtomKind::Keyword);
25135 assert_eq!("string".parse::<AtomKind>().unwrap(), AtomKind::Str);
25136 assert_eq!("int".parse::<AtomKind>().unwrap(), AtomKind::Int);
25137 assert_eq!("float".parse::<AtomKind>().unwrap(), AtomKind::Float);
25138 assert_eq!("bool".parse::<AtomKind>().unwrap(), AtomKind::Bool);
25139
25140 // Non-atom SexpShape labels (the six structural shapes
25141 // OUTSIDE the AtomKind closed set) must reject.
25142 for label in [
25143 "nil",
25144 "list",
25145 "quote",
25146 "quasiquote",
25147 "unquote",
25148 "unquote-splice",
25149 ] {
25150 assert!(
25151 label.parse::<AtomKind>().is_err(),
25152 "non-atom SexpShape label {label:?} must NOT decode to an AtomKind",
25153 );
25154 }
25155
25156 // Sanity: typed peers' labels (`UnquoteForm::marker`'s
25157 // `,` / `,@` punctuation, `ExpectedKwargShape`'s
25158 // `"number"` / `"list of strings"` vocabulary) live on
25159 // different axes and MUST reject too — pin the closed-set
25160 // boundary.
25161 for label in [",", ",@", "number", "list of strings", "atom", "Atom"] {
25162 assert!(
25163 label.parse::<AtomKind>().is_err(),
25164 "cross-axis label {label:?} must NOT decode to an AtomKind",
25165 );
25166 }
25167 }
25168
25169 #[test]
25170 fn atom_kind_is_well_formed_closed_set() {
25171 // Structural contract: AtomKind's six variants are pairwise
25172 // distinct, round-trip through the trait's `label` ↔
25173 // `parse_label`, and reject the empty string — the
25174 // workspace-wide `assert_closed_set_well_formed::<T>()` testkit
25175 // pinned across every `tatara-process` closed-set implementor
25176 // (`AllocationPhase`, `RequestorKind`, `ProcessPhase`,
25177 // `ConditionKind`, `WorkloadKind`, …). The substrate-level
25178 // assertion runs on the auto-derived `impl ClosedSet for
25179 // AtomKind` emitted by `#[derive(tatara_closed_set::DeriveClosedSet)]`
25180 // — a regression that drifts the derive's `make_unknown`
25181 // delegation, the `via = "label"` projection, or the variant
25182 // listing forced through `Self::ALL` fails-loudly here in
25183 // isolation from the per-variant truth tables above.
25184 tatara_closed_set::assert_closed_set_well_formed::<AtomKind>();
25185 }
25186
25187 #[test]
25188 fn atom_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte() {
25189 // ALIAS CONTRACT: pin every one of the six per-role
25190 // `pub const AtomKind::*_LABEL` aliases equals the corresponding
25191 // `pub const SexpShape::*_LABEL` byte-for-byte — so the AtomKind
25192 // ⊂ SexpShape marker-vocabulary containment routes through the
25193 // typed `pub const AtomKind::V_LABEL: &'static str =
25194 // SexpShape::V_LABEL` alias chain rather than through two
25195 // independent literal-discipline sites. A regression that
25196 // renames the SexpShape side without updating the AtomKind
25197 // alias pointing at it fails-loudly here with the exact axis
25198 // identified (SYMBOL / KEYWORD / STRING / INT / FLOAT / BOOL);
25199 // a regression that re-inlines the AtomKind constant to a
25200 // fresh literal still passes this pin but loses the alias-
25201 // chain typing (which is what
25202 // `atom_kind_label_arms_route_through_per_role_labels_for_every_variant`
25203 // + `atom_kind_labels_align_with_all_by_index` catch in
25204 // combination).
25205 //
25206 // Six per-role checks, each spelled out so a regression on ONE
25207 // variant surfaces the exact axis rather than through a
25208 // variant-loop that hides which arm drifted. The `Str →
25209 // String` boundary rename is intentional and load-bearing (the
25210 // wire vocabulary is `"string"` on both axes) — the
25211 // STRING_LABEL alias is the canonical bridge, so a future
25212 // rename that reverses the `Str → "string"` rename to a
25213 // literal `"str"` fails the byte-equality pin at THIS test.
25214 assert_eq!(AtomKind::SYMBOL_LABEL, SexpShape::SYMBOL_LABEL);
25215 assert_eq!(AtomKind::KEYWORD_LABEL, SexpShape::KEYWORD_LABEL);
25216 assert_eq!(AtomKind::STRING_LABEL, SexpShape::STRING_LABEL);
25217 assert_eq!(AtomKind::INT_LABEL, SexpShape::INT_LABEL);
25218 assert_eq!(AtomKind::FLOAT_LABEL, SexpShape::FLOAT_LABEL);
25219 assert_eq!(AtomKind::BOOL_LABEL, SexpShape::BOOL_LABEL);
25220 }
25221
25222 #[test]
25223 fn atom_kind_label_arms_route_through_per_role_labels_for_every_variant() {
25224 // PATH-UNIFORMITY: `AtomKind::V.label()` MUST equal the per-
25225 // role `pub const AtomKind::V_LABEL` for every `v: AtomKind`.
25226 // Pre-lift the six atomic-payload marker bytes were reachable
25227 // through `AtomKind::label` (the composition
25228 // `self.sexp_shape().label()` — routing into
25229 // `SexpShape::*_LABEL`) OR through direct
25230 // `SexpShape::*_LABEL` reach-across; post-lift each variant's
25231 // canonical bytes are reachable through the per-role
25232 // `AtomKind::*_LABEL` alias too. Pin the byte-equality between
25233 // the runtime projection and the compile-time alias so a
25234 // regression that renames the alias without updating the arm
25235 // (or vice versa) fails-loudly at the exact axis.
25236 //
25237 // Sibling-shape pin to
25238 // `sexp_shape_label_routes_through_typed_per_variant_constants`
25239 // one algebra layer up — the parent superset's per-role
25240 // constants are pinned against `SexpShape::label`'s arms
25241 // there; this pin binds the AtomKind subset algebra's per-
25242 // role aliases against `AtomKind::label`'s composition-routed
25243 // arms so the six atomic-payload marker labels project through
25244 // ONE aliased typed source of truth per role rather than
25245 // through per-consumer inline literals.
25246 assert_eq!(AtomKind::Symbol.label(), AtomKind::SYMBOL_LABEL);
25247 assert_eq!(AtomKind::Keyword.label(), AtomKind::KEYWORD_LABEL);
25248 assert_eq!(AtomKind::Str.label(), AtomKind::STRING_LABEL);
25249 assert_eq!(AtomKind::Int.label(), AtomKind::INT_LABEL);
25250 assert_eq!(AtomKind::Float.label(), AtomKind::FLOAT_LABEL);
25251 assert_eq!(AtomKind::Bool.label(), AtomKind::BOOL_LABEL);
25252 }
25253
25254 #[test]
25255 fn atom_kind_labels_has_expected_cardinality() {
25256 // Cardinality pin: `LABELS.len() == 6` matches `ALL.len()` so a
25257 // refactor that loosens the type to `&'static [&'static str]`
25258 // fails HERE (the `[_; 6]` slot cannot be sliced silently), and
25259 // a variant added to `ALL` without a matching `LABELS` row fails
25260 // the pair-arity gate at the array literal itself before this
25261 // test even runs. The pin doubles as an operator-visible mark
25262 // of the family's cardinality across the substrate — six
25263 // atomic-payload markers, matching the six-arm carving of the
25264 // parent `SexpShape::LABELS` (the atomic subset of the twelve
25265 // canonical outer-shape labels).
25266 assert_eq!(AtomKind::LABELS.len(), 6);
25267 assert_eq!(AtomKind::LABELS.len(), AtomKind::ALL.len());
25268 }
25269
25270 #[test]
25271 fn atom_kind_labels_align_with_all_by_index() {
25272 // ALIGNMENT PIN: sweep `LABELS[i] == ALL[i].label()` so any
25273 // `zip(ALL, LABELS)` consumer reads a coherent (variant, label)
25274 // pair off ONE forced-arity array pair. The declaration-order
25275 // pin makes a family-wide consumer that walks the ALL /
25276 // LABELS pair in lockstep (an LSP completion bar keyed on
25277 // `AtomKind::LABELS`, a Sekiban metric emitter labeling
25278 // `tatara_lisp_atom_type_mismatch_total{kind}` by the
25279 // per-index label) read one canonical (variant, bytes) pair per
25280 // slot rather than routing through per-consumer paired-
25281 // iteration. A regression that reorders LABELS without also
25282 // reordering ALL (or vice versa) fails-loudly at the exact
25283 // index that drifted.
25284 assert_eq!(AtomKind::LABELS.len(), AtomKind::ALL.len());
25285 for (i, kind) in AtomKind::ALL.iter().enumerate() {
25286 assert_eq!(
25287 AtomKind::LABELS[i],
25288 kind.label(),
25289 "AtomKind::LABELS[{i}] `{lbl}` drifted from \
25290 AtomKind::ALL[{i}].label() `{via_variant}` — the \
25291 canonical ALL ordering and the LABELS ordering must \
25292 match element-wise",
25293 lbl = AtomKind::LABELS[i],
25294 via_variant = kind.label(),
25295 );
25296 }
25297 }
25298
25299 #[test]
25300 fn atom_kind_labels_pairwise_distinct() {
25301 // 6x6 pairwise sweep so a collision between any two labels
25302 // (which would silently degrade two distinct atomic-payload
25303 // markers to the SAME diagnostic bytes and violate the
25304 // closed-set FromStr round-trip) fails-loudly at the exact
25305 // pair. Distinctness is already enforced structurally by
25306 // `assert_closed_set_well_formed::<AtomKind>()` (clause 3), so
25307 // this pin is a secondary guard focused on the per-role
25308 // `pub const` surface directly rather than the runtime
25309 // projection through the trait's default `labels()`.
25310 for (i, a) in AtomKind::LABELS.iter().enumerate() {
25311 for (j, b) in AtomKind::LABELS.iter().enumerate() {
25312 if i == j {
25313 continue;
25314 }
25315 assert_ne!(
25316 a, b,
25317 "AtomKind::LABELS[{i}] ({a:?}) collides with \
25318 AtomKind::LABELS[{j}] ({b:?}) — two distinct \
25319 atomic-payload markers cannot share diagnostic bytes",
25320 );
25321 }
25322 }
25323 }
25324
25325 #[test]
25326 fn atom_label_projects_each_variant_to_canonical_diagnostic_label() {
25327 // PER-ARM CONTRACT: pin the outer-`Atom` `Self::label`
25328 // projection produces the SIX canonical `&'static str` labels
25329 // byte-for-byte across every reachable atomic-payload variant.
25330 // Pre-lift the outer-`Atom` label projection had no typed
25331 // primitive on the value-carrier algebra — a consumer with an
25332 // `Atom` value in hand wanting the canonical diagnostic label
25333 // had to spell the two-step composition `atom.kind().label()`
25334 // at every callsite, OR go through
25335 // `Sexp::Atom(atom.clone()).type_name()` which wraps and
25336 // unwraps for no runtime purpose. Post-lift the SIX arms bind
25337 // at ONE typed projection on the outer-`Atom` algebra that
25338 // routes through `AtomKind::label` (which itself composes
25339 // through `AtomKind::sexp_shape().label()` into the canonical
25340 // `SexpShape::label` site) — the (Atom variant, diagnostic
25341 // label) pairing binds at ONE typed algebra composition
25342 // spanning FOUR typed layers.
25343 //
25344 // Sibling-shape pin to
25345 // `atom_kind_label_renders_canonical_string_for_every_variant`
25346 // one algebra layer down and
25347 // `sexp_type_name_method_projects_each_outer_arm_to_canonical_label`
25348 // one algebra layer up. A regression that drifts ONE arm's
25349 // label (e.g. Symbol → "sym", swapping Int ↔ Float, dropping
25350 // the `Str → "string"` boundary rename) fails-loudly at THIS
25351 // test AND the sibling `AtomKind::label` per-arm pin.
25352 assert_eq!(Atom::Symbol("foo".to_owned()).label(), "symbol");
25353 assert_eq!(Atom::Keyword("kw".to_owned()).label(), "keyword");
25354 assert_eq!(Atom::Str("hi".to_owned()).label(), "string");
25355 assert_eq!(Atom::Int(42).label(), "int");
25356 assert_eq!(Atom::Float(1.5).label(), "float");
25357 assert_eq!(Atom::Bool(true).label(), "bool");
25358 assert_eq!(Atom::Bool(false).label(), "bool");
25359 }
25360
25361 #[test]
25362 fn atom_label_composes_through_kind_label_for_every_variant() {
25363 // COMPOSITION-LAW CONTRACT: `atom.label() == atom.kind().label()`
25364 // for every reachable atomic payload — the outer-`Atom` label
25365 // projection is structurally derived through `Self::kind` +
25366 // `AtomKind::label` rather than through a parallel six-arm
25367 // inline match on the outer-`Atom` algebra. Pin the composition
25368 // law so a future refactor that re-inlines the six atomic-arm
25369 // literals here (and gains its own drift surface separate from
25370 // the `AtomKind::label` canonical site) surfaces immediately.
25371 // The pointer-equality check pins the composition produces the
25372 // SAME `&'static str` (not just a byte-equal copy) for every
25373 // variant — proof the routing hits ONE static literal site
25374 // (`SexpShape::label` via `AtomKind::sexp_shape().label()` via
25375 // `AtomKind::label`'s composition) rather than a parallel inline
25376 // table on the outer-`Atom` algebra.
25377 //
25378 // Sibling-shape pin to
25379 // `atom_kind_label_routes_through_sexp_shape_label_via_sexp_shape_projection`
25380 // one algebra layer down (which pins `AtomKind::label`'s routing
25381 // through `SexpShape::label`) and
25382 // `sexp_type_name_method_composes_through_shape_label_for_every_outer_shape`
25383 // one algebra layer up (which pins `Sexp::type_name`'s routing
25384 // through `Sexp::shape().label()`). The three routing pins jointly
25385 // enforce the (outer-`Atom` value, canonical label) pairing
25386 // stays a full four-layer typed composition (`Atom` → `AtomKind`
25387 // → `SexpShape` → `&'static str`) rather than degrading to a
25388 // per-layer inline literal table.
25389 let samples: Vec<Atom> = vec![
25390 Atom::Symbol("foo".to_owned()),
25391 Atom::Keyword("kw".to_owned()),
25392 Atom::Str("hi".to_owned()),
25393 Atom::Int(0),
25394 Atom::Int(-7),
25395 Atom::Int(42),
25396 Atom::Float(0.0),
25397 Atom::Float(-1.5),
25398 Atom::Float(f64::INFINITY),
25399 Atom::Bool(true),
25400 Atom::Bool(false),
25401 ];
25402 for atom in &samples {
25403 let via_label = atom.label();
25404 let via_composition = atom.kind().label();
25405 assert_eq!(
25406 via_label, via_composition,
25407 "Atom::label() must route through self.kind().label() \
25408 for {atom:?} — drift here means the lift was reverted \
25409 to inline arms",
25410 );
25411 assert!(
25412 std::ptr::eq(via_label.as_ptr(), via_composition.as_ptr()),
25413 "Atom::label() must return the SAME `&'static str` as \
25414 self.kind().label() for {atom:?} — pointer drift \
25415 means the lift composes through a parallel literal \
25416 table rather than routing into the canonical \
25417 AtomKind::label site",
25418 );
25419 }
25420 }
25421
25422 #[test]
25423 fn atom_label_agrees_with_sexp_type_name_at_every_atom_arm() {
25424 // CROSS-ALGEBRA AGREEMENT CONTRACT: for every atomic payload
25425 // `a`, `a.label() == Sexp::Atom(a.clone()).type_name()`. The
25426 // agreement is a TYPED CONSEQUENCE of the two typed
25427 // compositions — `Sexp::Atom(a).type_name()` routes through
25428 // `Sexp::shape()`'s `Self::Atom(a) => a.kind().sexp_shape()`
25429 // arm which composes with `SexpShape::label` byte-for-byte
25430 // with `a.kind().label()` (which itself composes through
25431 // `AtomKind::sexp_shape().label()`). A regression that drifts
25432 // either side of the cross-algebra bridge (an outer-`Atom`
25433 // label re-inlined onto a different literal, an outer-`Sexp`
25434 // Atom-arm re-routed through a stale shape projection, an
25435 // `AtomKind::sexp_shape` arm that swaps Int ↔ Float) fails-
25436 // loudly here rather than as a silent operator-facing
25437 // diagnostic drift at every consumer that pattern-matches on
25438 // the outer-`Sexp` label vs the outer-`Atom` label
25439 // independently.
25440 //
25441 // Sibling posture to
25442 // `atom_kind_label_agrees_with_sexp_shape_label_for_every_atom_arm`
25443 // one algebra layer down — that pin binds the marker-level
25444 // vocabulary containment (`AtomKind::label ==
25445 // AtomKind::sexp_shape().label()`), this pin binds the
25446 // outer-value-level vocabulary containment (`Atom::label ==
25447 // Sexp::Atom(_).type_name()`) so the FOUR-layer typed
25448 // composition on the outer-`Atom` algebra and the FIVE-layer
25449 // typed composition on the outer-`Sexp` algebra agree at their
25450 // common atomic-payload arms.
25451 for atom in [
25452 Atom::Symbol("foo".to_owned()),
25453 Atom::Keyword("kw".to_owned()),
25454 Atom::Str("hi".to_owned()),
25455 Atom::Int(42),
25456 Atom::Float(2.5),
25457 Atom::Bool(true),
25458 Atom::Bool(false),
25459 ] {
25460 let via_atom = atom.label();
25461 let via_sexp = Sexp::Atom(atom.clone()).type_name();
25462 assert_eq!(
25463 via_atom, via_sexp,
25464 "Atom::label() must agree with Sexp::Atom(_).type_name() \
25465 for {atom:?} — cross-algebra label drift at the \
25466 atomic-payload arms would fracture the typed diagnostic \
25467 vocabulary between the outer-Atom and outer-Sexp \
25468 algebras",
25469 );
25470 assert!(
25471 std::ptr::eq(via_atom.as_ptr(), via_sexp.as_ptr()),
25472 "Atom::label() must return the SAME `&'static str` as \
25473 Sexp::Atom(_).type_name() for {atom:?} — pointer drift \
25474 means one algebra layer re-inlined the literal rather \
25475 than routing into the canonical `SexpShape::label` \
25476 site",
25477 );
25478 }
25479 }
25480
25481 #[test]
25482 fn atom_sexp_shape_projects_each_variant_to_canonical_outer_shape() {
25483 // PER-ARM CONTRACT: pin the outer-`Atom` `Self::sexp_shape`
25484 // projection produces the SIX canonical `SexpShape` variants
25485 // byte-for-byte across every reachable atomic-payload variant.
25486 // Pre-lift the outer-`Atom` outer-shape projection had no typed
25487 // primitive on the value-carrier algebra — a consumer with an
25488 // `Atom` value in hand wanting the canonical outer-shape had to
25489 // spell the two-step composition `atom.kind().sexp_shape()` at
25490 // every callsite, OR go through `Sexp::Atom(atom.clone()).shape()`
25491 // which wraps and unwraps for no runtime purpose. Post-lift the
25492 // SIX arms bind at ONE typed projection on the outer-`Atom`
25493 // algebra that routes through `AtomKind::sexp_shape` — the
25494 // (Atom variant, SexpShape variant) pairing binds at ONE typed
25495 // algebra composition spanning THREE typed layers.
25496 //
25497 // Sibling-shape pin to
25498 // `atom_kind_sexp_shape_projects_each_variant_to_canonical_outer_shape`
25499 // one algebra layer down and `atom_label_projects_each_variant_to_canonical_diagnostic_label`
25500 // one vocabulary axis over. A regression that drifts ONE arm's
25501 // mapping (e.g. swapping Int ↔ Float, dropping the `Str →
25502 // SexpShape::String` boundary rename) fails-loudly at THIS
25503 // test AND the sibling `AtomKind::sexp_shape` per-arm pin.
25504 assert_eq!(
25505 Atom::Symbol("foo".to_owned()).sexp_shape(),
25506 SexpShape::Symbol
25507 );
25508 assert_eq!(
25509 Atom::Keyword("kw".to_owned()).sexp_shape(),
25510 SexpShape::Keyword
25511 );
25512 assert_eq!(Atom::Str("hi".to_owned()).sexp_shape(), SexpShape::String);
25513 assert_eq!(Atom::Int(42).sexp_shape(), SexpShape::Int);
25514 assert_eq!(Atom::Float(1.5).sexp_shape(), SexpShape::Float);
25515 assert_eq!(Atom::Bool(true).sexp_shape(), SexpShape::Bool);
25516 assert_eq!(Atom::Bool(false).sexp_shape(), SexpShape::Bool);
25517 }
25518
25519 #[test]
25520 fn atom_sexp_shape_composes_through_kind_sexp_shape_for_every_variant() {
25521 // COMPOSITION-LAW CONTRACT: `atom.sexp_shape() ==
25522 // atom.kind().sexp_shape()` for every reachable atomic payload
25523 // — the outer-`Atom` outer-shape projection is structurally
25524 // derived through `Self::kind` + `AtomKind::sexp_shape` rather
25525 // than through a parallel six-arm inline match on the outer-
25526 // `Atom` algebra. Pin the composition law so a future refactor
25527 // that re-inlines the six atomic-arm literals here (and gains
25528 // its own drift surface separate from the `AtomKind::sexp_shape`
25529 // canonical site) surfaces immediately.
25530 //
25531 // `SexpShape` carries the `String`-carrying `Unknown` arm so
25532 // it can't be `Copy`; the pointer-equality axis
25533 // `atom_label_composes_through_kind_label_for_every_variant`
25534 // uses on the `&'static str` axis doesn't apply here. Byte-
25535 // equality on the `SexpShape` discriminant IS the routing
25536 // contract this pin binds: a regression that re-inlines the
25537 // mapping produces byte-equal SexpShape values yet gains its
25538 // own drift surface at the outer-`Atom` layer separate from
25539 // the canonical `AtomKind::sexp_shape` site.
25540 //
25541 // Sibling-shape pin to
25542 // `atom_label_composes_through_kind_label_for_every_variant`
25543 // one vocabulary axis over (the diagnostic-label axis) and
25544 // `atom_kind_sexp_shape_partition_matches_sexp_shape_atomic_carving`
25545 // one algebra layer down (which pins `AtomKind::sexp_shape`'s
25546 // partition-membership against `SexpShape::as_atom_kind`).
25547 // The three pins jointly enforce the (outer-`Atom` value,
25548 // outer-shape) pairing stays a full three-layer typed
25549 // composition (`Atom` → `AtomKind` → `SexpShape`) rather than
25550 // degrading to a per-layer inline literal table on the
25551 // outer-`Atom` algebra.
25552 let samples: Vec<Atom> = vec![
25553 Atom::Symbol("foo".to_owned()),
25554 Atom::Symbol(String::new()),
25555 Atom::Keyword("kw".to_owned()),
25556 Atom::Keyword(String::new()),
25557 Atom::Str("hi".to_owned()),
25558 Atom::Str(String::new()),
25559 Atom::Int(0),
25560 Atom::Int(-7),
25561 Atom::Int(i64::MIN),
25562 Atom::Int(i64::MAX),
25563 Atom::Float(0.0),
25564 Atom::Float(-1.5),
25565 Atom::Float(f64::INFINITY),
25566 Atom::Float(f64::from_bits(f64::NAN.to_bits())),
25567 Atom::Bool(true),
25568 Atom::Bool(false),
25569 ];
25570 for atom in &samples {
25571 let via_sexp_shape = atom.sexp_shape();
25572 let via_composition = atom.kind().sexp_shape();
25573 assert_eq!(
25574 via_sexp_shape, via_composition,
25575 "Atom::sexp_shape() must route through self.kind().sexp_shape() \
25576 for {atom:?} — drift here means the lift was reverted \
25577 to inline arms",
25578 );
25579 // Cross-projection agreement: the routed shape's diagnostic
25580 // label is byte-equal to `atom.label()` (the sibling
25581 // vocabulary axis' composition), pinning that the two typed
25582 // projections through `AtomKind` (one via `label`, one via
25583 // `sexp_shape`) agree at the canonical `SexpShape::label`
25584 // site.
25585 assert_eq!(
25586 via_sexp_shape.label(),
25587 atom.label(),
25588 "atom.sexp_shape().label() must agree with atom.label() \
25589 for {atom:?} — cross-axis vocabulary drift at the \
25590 shape-projection site would fracture the FOUR-layer \
25591 diagnostic composition on the outer-Atom algebra",
25592 );
25593 }
25594 }
25595
25596 #[test]
25597 fn atom_sexp_shape_agrees_with_sexp_shape_at_every_atom_arm() {
25598 // CROSS-ALGEBRA AGREEMENT CONTRACT: for every atomic payload
25599 // `a`, `a.sexp_shape() == Sexp::Atom(a.clone()).shape()`. The
25600 // agreement is a TYPED CONSEQUENCE of the two typed
25601 // compositions — `Sexp::Atom(a).shape()` routes through
25602 // `Sexp::shape()`'s `Self::Atom(a) => a.kind().sexp_shape()`
25603 // arm which byte-for-byte matches `a.sexp_shape()`'s composition
25604 // through `Self::kind` + `AtomKind::sexp_shape`. A regression
25605 // that drifts either side of the cross-algebra bridge (an
25606 // outer-`Atom` shape re-inlined onto a different projection,
25607 // an outer-`Sexp` Atom-arm re-routed through a stale kind
25608 // projection, an `AtomKind::sexp_shape` arm that swaps Int ↔
25609 // Float) fails-loudly here rather than as a silent operator-
25610 // facing drift at every consumer that pattern-matches on the
25611 // outer-`Sexp` shape vs the outer-`Atom` shape independently.
25612 //
25613 // Sibling posture to
25614 // `atom_label_agrees_with_sexp_type_name_at_every_atom_arm`
25615 // one vocabulary axis over — that pin binds the (outer-`Atom`,
25616 // outer-`Sexp`) cross-algebra bridge on the diagnostic-label
25617 // axis, this pin binds it on the outer-shape axis.
25618 for atom in [
25619 Atom::Symbol("foo".to_owned()),
25620 Atom::Keyword("kw".to_owned()),
25621 Atom::Str("hi".to_owned()),
25622 Atom::Int(42),
25623 Atom::Float(2.5),
25624 Atom::Bool(true),
25625 Atom::Bool(false),
25626 ] {
25627 let via_atom = atom.sexp_shape();
25628 let via_sexp = Sexp::Atom(atom.clone()).shape();
25629 assert_eq!(
25630 via_atom, via_sexp,
25631 "Atom::sexp_shape() must agree with Sexp::Atom(_).shape() \
25632 for {atom:?} — cross-algebra shape drift at the \
25633 atomic-payload arms would fracture the typed shape \
25634 vocabulary between the outer-Atom and outer-Sexp \
25635 algebras",
25636 );
25637 }
25638 }
25639
25640 #[test]
25641 fn atom_sexp_shape_round_trips_through_sexp_shape_as_atom_kind() {
25642 // ROUND-TRIP CONTRACT: for every atomic payload `a`,
25643 // `a.sexp_shape().as_atom_kind() == Some(a.kind())`. The typed
25644 // embed `Atom → AtomKind → SexpShape` inverts through the
25645 // soft-projection retraction `SexpShape → AtomKind` exactly on
25646 // the 6-of-12 atomic-payload image. A regression that ANY of
25647 // the three embeds (`Self::kind`, `AtomKind::sexp_shape`) OR
25648 // the soft-projection retraction `SexpShape::as_atom_kind`
25649 // drifts on any arm fails-loudly here — the structural
25650 // round-trip is the invariant that holds the closed-set-
25651 // lattice's atomic-payload cell load-bearing across future
25652 // edits.
25653 //
25654 // Peer to `unquote_form_sexp_shape_round_trips_through_sexp_shape_as_quote_form_and_as_unquote_form`
25655 // (error.rs) one carving axis over on the substitution-subset
25656 // side of the outer-shape lattice, and to `atom_kind_sexp_shape_round_trips_through_sexp_shape_as_atom_kind`
25657 // one algebra layer down (which pins the marker-level round-
25658 // trip). This pin extends the round-trip up to the outer-
25659 // `Atom` value carrier.
25660 for atom in [
25661 Atom::Symbol("foo".to_owned()),
25662 Atom::Keyword("kw".to_owned()),
25663 Atom::Str("hi".to_owned()),
25664 Atom::Int(0),
25665 Atom::Int(i64::MIN),
25666 Atom::Float(0.0),
25667 Atom::Float(f64::INFINITY),
25668 Atom::Bool(true),
25669 Atom::Bool(false),
25670 ] {
25671 let shape = atom.sexp_shape();
25672 let round_tripped = shape.as_atom_kind();
25673 assert_eq!(
25674 round_tripped,
25675 Some(atom.kind()),
25676 "Atom::sexp_shape() must round-trip through \
25677 SexpShape::as_atom_kind for {atom:?} — the typed embed \
25678 Atom → AtomKind → SexpShape is no longer a section of \
25679 SexpShape::as_atom_kind's inverse on the atomic \
25680 6-of-12 image",
25681 );
25682 }
25683 }
25684
25685 #[test]
25686 fn hash_for_atom_preserves_legacy_discriminator_bytes() {
25687 // CACHE-KEY CONTRACT (Hash side): pin that the lifted
25688 // `Hash for Atom` impl produces byte-identical hashes for the
25689 // six atomic variants as the pre-lift implementation. We
25690 // compute the expected hash via a SECOND hasher that manually
25691 // drives the pre-lift `<discr>u8.hash(h); <inner>.hash(h)`
25692 // sequence (with `Float`'s `to_bits()` projection preserved
25693 // and `String` payloads hashed via `String::hash`), then
25694 // compare. A regression that drifts the discriminator OR
25695 // re-orders the (discr, inner) sequence surfaces here as a
25696 // hash-value mismatch. Sibling posture to
25697 // `hash_for_sexp_preserves_legacy_quote_family_discriminator_bytes`
25698 // on the quote-family axis.
25699 use std::collections::hash_map::DefaultHasher;
25700
25701 let payload = String::from("payload");
25702
25703 // Helper: hash the legacy `<discr>u8.hash(h); <inner>` shape
25704 // through a fresh DefaultHasher and finish.
25705 let legacy_hash = |atom: &Atom, expected_discr: u8| -> u64 {
25706 let mut h = DefaultHasher::new();
25707 expected_discr.hash(&mut h);
25708 match atom {
25709 Atom::Symbol(s) | Atom::Keyword(s) | Atom::Str(s) => s.hash(&mut h),
25710 Atom::Int(n) => n.hash(&mut h),
25711 Atom::Float(f) => f.to_bits().hash(&mut h),
25712 Atom::Bool(b) => b.hash(&mut h),
25713 }
25714 h.finish()
25715 };
25716
25717 // (label, atom, pre-lift discriminator byte)
25718 let cases: &[(&str, Atom, u8)] = &[
25719 ("symbol", Atom::Symbol(payload.clone()), 0u8),
25720 ("keyword", Atom::Keyword(payload.clone()), 1u8),
25721 ("str", Atom::Str(payload.clone()), 2u8),
25722 ("int", Atom::Int(42), 3u8),
25723 ("float", Atom::Float(1.5), 4u8),
25724 ("bool-true", Atom::Bool(true), 5u8),
25725 ("bool-false", Atom::Bool(false), 5u8),
25726 ];
25727
25728 for (label, atom, expected_discr) in cases {
25729 let mut via_impl = DefaultHasher::new();
25730 atom.hash(&mut via_impl);
25731
25732 let via_legacy = legacy_hash(atom, *expected_discr);
25733
25734 assert_eq!(
25735 via_impl.finish(),
25736 via_legacy,
25737 "Hash for Atom drifted from legacy \
25738 (discr={expected_discr}, inner) sequence at {label}"
25739 );
25740 }
25741 }
25742
25743 #[test]
25744 fn atom_hash_discriminator_composes_through_kind_hash_discriminator_for_every_variant() {
25745 // COMPOSITION-LAW CONTRACT: `atom.hash_discriminator() ==
25746 // atom.kind().hash_discriminator()` for every reachable atomic
25747 // payload — the outer-`Atom` cache-key byte projection is
25748 // structurally derived through `Self::kind` +
25749 // `AtomKind::hash_discriminator` rather than through a parallel
25750 // six-arm inline match on the outer-`Atom` algebra. Pin the
25751 // composition law so a future refactor that re-inlines the six
25752 // atomic-arm literals here (and gains its own drift surface
25753 // separate from the `AtomKind::hash_discriminator` canonical
25754 // site) surfaces immediately.
25755 //
25756 // Sibling-shape pin to
25757 // `atom_label_composes_through_kind_label_for_every_variant`
25758 // (diagnostic-label axis) and
25759 // `atom_sexp_shape_composes_through_kind_sexp_shape_for_every_variant`
25760 // (outer-shape axis) one vocabulary axis over — the three pins
25761 // jointly enforce the outer-`Atom` algebra closes the (label,
25762 // sexp_shape, hash_discriminator) trio through the SAME typed
25763 // marker layer (`Self::kind` into `AtomKind`) rather than
25764 // degrading to a per-layer inline literal table on the
25765 // outer-`Atom` algebra. The sweep includes NaN and ±∞ Float
25766 // payloads (matching `Hash for Atom`'s `f64::to_bits()`
25767 // posture), both empty and non-empty String/Symbol/Keyword
25768 // arms, `i64::{MIN, MAX}` on the Int arm, and both Bool arms —
25769 // exhausting the byte-partition surface at every reachable
25770 // atomic-payload witness.
25771 let samples: Vec<Atom> = vec![
25772 Atom::Symbol("foo".to_owned()),
25773 Atom::Symbol(String::new()),
25774 Atom::Keyword("kw".to_owned()),
25775 Atom::Keyword(String::new()),
25776 Atom::Str("hi".to_owned()),
25777 Atom::Str(String::new()),
25778 Atom::Int(0),
25779 Atom::Int(-7),
25780 Atom::Int(42),
25781 Atom::Int(i64::MIN),
25782 Atom::Int(i64::MAX),
25783 Atom::Float(0.0),
25784 Atom::Float(-1.5),
25785 Atom::Float(f64::INFINITY),
25786 Atom::Float(f64::NEG_INFINITY),
25787 Atom::Float(f64::NAN),
25788 Atom::Bool(true),
25789 Atom::Bool(false),
25790 ];
25791 for atom in &samples {
25792 let via_outer = atom.hash_discriminator();
25793 let via_composition = atom.kind().hash_discriminator();
25794 assert_eq!(
25795 via_outer, via_composition,
25796 "Atom::hash_discriminator() must route through \
25797 self.kind().hash_discriminator() for {atom:?} — drift \
25798 here means the lift was reverted to inline arms and \
25799 the outer-`Atom` cache-key algebra fractured from the \
25800 canonical AtomKind::hash_discriminator site",
25801 );
25802 }
25803 }
25804
25805 #[test]
25806 fn hash_for_atom_routes_atom_discriminator_through_atom_hash_discriminator() {
25807 // ROUTING-LAW CONTRACT: pin the outer-`Atom` routing IDENTITY —
25808 // for every reachable atomic payload, `Hash for Atom` produces
25809 // byte-identical output to a hand-driven
25810 // `atom.hash_discriminator().hash(h); <inner-payload-hash>`
25811 // sequence. Binds the composition IDENTITY (not just value
25812 // equality) between the outer Hash body and the typed algebra
25813 // method — a regression that re-inlines the two-hop
25814 // `self.kind().hash_discriminator()` chain at the outer arm
25815 // still drifts detectably if the future
25816 // `Atom::hash_discriminator` composes through a different site.
25817 // Sibling posture to
25818 // `hash_for_sexp_routes_outer_discriminator_through_sexp_hash_discriminator`
25819 // — that pin binds the `Hash for Sexp` body against the
25820 // outer-`Sexp` cache-key method; this pin binds the
25821 // `Hash for Atom` body against the outer-`Atom` cache-key
25822 // method. Together the two routing pins enforce the outer-value
25823 // Hash bodies at BOTH algebras stay structurally parallel
25824 // (`self.hash_discriminator().hash(h); <inner>`).
25825 use std::collections::hash_map::DefaultHasher;
25826 let seeds: Vec<(&str, Atom)> = vec![
25827 ("symbol", Atom::Symbol("s".to_owned())),
25828 ("symbol-empty", Atom::Symbol(String::new())),
25829 ("keyword", Atom::Keyword("kw".to_owned())),
25830 ("str", Atom::Str("hi".to_owned())),
25831 ("int-zero", Atom::Int(0)),
25832 ("int-min", Atom::Int(i64::MIN)),
25833 ("int-max", Atom::Int(i64::MAX)),
25834 ("float", Atom::Float(2.5)),
25835 ("float-nan", Atom::Float(f64::NAN)),
25836 ("float-inf", Atom::Float(f64::INFINITY)),
25837 ("bool-true", Atom::Bool(true)),
25838 ("bool-false", Atom::Bool(false)),
25839 ];
25840 for (label, atom) in &seeds {
25841 let mut via_impl = DefaultHasher::new();
25842 atom.hash(&mut via_impl);
25843
25844 let mut via_lifted = DefaultHasher::new();
25845 atom.hash_discriminator().hash(&mut via_lifted);
25846 match atom {
25847 Atom::Symbol(s) | Atom::Keyword(s) | Atom::Str(s) => s.hash(&mut via_lifted),
25848 Atom::Int(n) => n.hash(&mut via_lifted),
25849 Atom::Float(f) => f.to_bits().hash(&mut via_lifted),
25850 Atom::Bool(b) => b.hash(&mut via_lifted),
25851 }
25852
25853 assert_eq!(
25854 via_impl.finish(),
25855 via_lifted.finish(),
25856 "Hash for Atom drifted from routed-through-hash_discriminator sequence at {label}"
25857 );
25858 }
25859 }
25860
25861 #[test]
25862 fn atom_kind_composes_with_domain_sexp_shape_for_every_atomic_arm() {
25863 // PATH-UNIFORMITY / COMPOSITION-LAW CONTRACT: the substrate's
25864 // outer-shape projection `domain::sexp_shape` now routes the
25865 // six atomic arms through `Atom::kind` + `AtomKind::sexp_shape`.
25866 // Pin that the composed projection produces the SAME
25867 // `SexpShape` variant that the pre-lift inline six-arm match
25868 // produced for every `Atom` payload. A regression that drifts
25869 // ONE arm of either `Atom::kind` (e.g. routes `Atom::Int(_)`
25870 // through `AtomKind::Float`) or `AtomKind::sexp_shape` (e.g.
25871 // routes `AtomKind::Symbol` through `SexpShape::Keyword`)
25872 // surfaces as an immediate inequality between
25873 // `domain::sexp_shape(&Sexp::Atom(a))` and
25874 // `a.kind().sexp_shape()` — and since both projections are
25875 // load-bearing for the diagnostic surface, the test pins both
25876 // sides of the typed algebra at once. Sibling posture to
25877 // `quote_form_sexp_shape_paired_with_as_quote_form_preserves_
25878 // pre_lift_pairing_for_every_sexp` on the quote-family axis.
25879 let cases: &[(Atom, SexpShape)] = &[
25880 (Atom::Symbol("x".into()), SexpShape::Symbol),
25881 (Atom::Keyword("k".into()), SexpShape::Keyword),
25882 (Atom::Str("s".into()), SexpShape::String),
25883 (Atom::Int(7), SexpShape::Int),
25884 (Atom::Float(2.5), SexpShape::Float),
25885 (Atom::Bool(true), SexpShape::Bool),
25886 ];
25887 for (atom, expected_shape) in cases {
25888 let via_composed = atom.kind().sexp_shape();
25889 assert_eq!(
25890 via_composed, *expected_shape,
25891 "Atom::kind().sexp_shape() drifted for {atom:?}"
25892 );
25893 // Cross-projection identity with the public
25894 // `domain::sexp_shape` projection — pins that the lifted
25895 // arm routes through `AtomKind` exactly as the inline
25896 // arms did pre-lift.
25897 let via_domain = crate::domain::sexp_shape(&Sexp::Atom(atom.clone()));
25898 assert_eq!(
25899 via_domain, via_composed,
25900 "domain::sexp_shape vs Atom::kind().sexp_shape() drift for {atom:?}"
25901 );
25902 }
25903 }
25904
25905 #[test]
25906 fn atom_display_renders_each_variant_to_canonical_form() {
25907 // CANONICAL-RENDERING CONTRACT: pin that the lifted
25908 // `fmt::Display for Atom` impl produces byte-identical
25909 // canonical output for the seven atomic variant cases
25910 // (Bool splits into true/false) as the pre-lift inline
25911 // sub-arms inside `Display for Sexp`'s atom arm. Sibling-arm
25912 // sweep so the seven pairings stay load-bearing under
25913 // reordering refactors. A regression that drifts the Bool
25914 // spelling (`#t`/`#f` vs Rust's `true`/`false`) — the
25915 // CLAUDE.md-pinned reader-round-trip invariant — fails
25916 // loudly here. Direct sibling to `atom_kind_label_renders_
25917 // canonical_string_for_every_variant` on the diagnostic-
25918 // label axis: this pins the rendered SOURCE (`#t`), that pins
25919 // the rendered LABEL (`bool`); the two projections share the
25920 // closed-set `AtomKind` algebra but render to distinct
25921 // surfaces (source vs diagnostic vocabulary).
25922 let cases: &[(Atom, &str)] = &[
25923 (Atom::Symbol("foo".into()), "foo"),
25924 (Atom::Keyword("k".into()), ":k"),
25925 (Atom::Str("hello".into()), "\"hello\""),
25926 (Atom::Int(42), "42"),
25927 (Atom::Int(-7), "-7"),
25928 (Atom::Float(1.5), "1.5"),
25929 (Atom::Bool(true), "#t"),
25930 (Atom::Bool(false), "#f"),
25931 ];
25932 for (atom, expected) in cases {
25933 assert_eq!(
25934 atom.to_string(),
25935 *expected,
25936 "Atom::Display drifted from canonical rendering for {atom:?}"
25937 );
25938 }
25939 }
25940
25941 #[test]
25942 fn atom_display_renders_integral_float_with_dot_zero_suffix() {
25943 // ROUND-TRIP-INVARIANT PIN: `fmt_float`'s `.0`-suffix
25944 // discipline composes through `Atom::Display` — `Float(1.0)`
25945 // renders as `"1.0"`, NOT `"1"` (which the reader would
25946 // re-parse as `Atom::Int(1)`, silently coercing the typed
25947 // `Float` track into the `Int` track at the Display→read
25948 // boundary). Direct sibling pin to the existing Display-for-
25949 // Sexp round-trip tests that exercise the same invariant
25950 // through the `Sexp::Atom` outer wrap. Lifting the rendering
25951 // onto the typed `Atom` algebra surfaces a future regression
25952 // (e.g. an Atom::Display arm that bypasses `fmt_float` and
25953 // formats `f64` directly) at the atom layer without
25954 // requiring a Sexp wrap to reproduce.
25955 assert_eq!(Atom::Float(1.0).to_string(), "1.0");
25956 assert_eq!(Atom::Float(-42.0).to_string(), "-42.0");
25957 assert_eq!(Atom::Float(0.99).to_string(), "0.99");
25958 }
25959
25960 #[test]
25961 fn sexp_atom_display_arm_routes_through_atom_display_for_every_variant() {
25962 // LIFTED-BOUNDARY CONTRACT: pin that `Sexp::Atom(a).to_string()
25963 // == a.to_string()` for every atomic payload variant. Pre-
25964 // lift the per-variant body lived inline at the `Sexp::Atom(a)
25965 // => match a { … }` arm of `Display for Sexp`; post-lift the
25966 // outer arm delegates to `fmt::Display::fmt(a, f)`. A
25967 // regression that drifts the outer arm (e.g. wraps the atom
25968 // rendering in parens, or routes Symbol through a Sexp-
25969 // specific arm before delegating) surfaces as an inequality
25970 // here. The cases sweep all six `Atom` variants (Bool unified
25971 // — both true/false agree under the impl). Sibling posture
25972 // to the quote-family routing test
25973 // `sexp_to_json_routes_quote_family_arms_through_as_quote_form_typed_marker`
25974 // that pins the analogous `Sexp` outer arm routing through
25975 // a typed algebra projection.
25976 let cases: &[Atom] = &[
25977 Atom::Symbol("name".into()),
25978 Atom::Keyword("kw".into()),
25979 Atom::Str("body".into()),
25980 Atom::Int(7),
25981 Atom::Float(2.5),
25982 Atom::Float(1.0),
25983 Atom::Bool(true),
25984 Atom::Bool(false),
25985 ];
25986 for atom in cases {
25987 let via_sexp = Sexp::Atom(atom.clone()).to_string();
25988 let via_atom = atom.to_string();
25989 assert_eq!(
25990 via_sexp, via_atom,
25991 "Sexp::Atom Display arm drifted from Atom::Display for {atom:?}"
25992 );
25993 }
25994 }
25995
25996 #[test]
25997 fn atom_display_round_trips_through_reader_preserving_typed_identity() {
25998 // BIDIRECTIONAL TYPED-IDENTITY CONTRACT: render an atom via
25999 // `Atom::Display`, parse the rendering through
26000 // `crate::reader::read`, and pin that the parsed value's
26001 // outer shape is `Sexp::Atom(_)` carrying the SAME variant
26002 // discriminator as the seed (via `Atom::kind`) AND that the
26003 // payload round-trips bit-for-bit. This is the typed-exit /
26004 // typed-entry mirror at the atomic-payload boundary — the
26005 // load-bearing invariant the `fmt_float` `.0`-suffix
26006 // discipline already exists to preserve. A regression that
26007 // drifts ONE side (Display arm OR reader arm) corrupts the
26008 // round-trip; pin it at the typed boundary directly. Sibling
26009 // posture to the existing Sexp-layer round-trip tests:
26010 // `float_display_round_trips_through_reader_into_typed_float`,
26011 // `quote_prefix_round_trips_through_read_quoted_into_sexp_quote`.
26012 let cases: &[Atom] = &[
26013 Atom::Symbol("foo-bar".into()),
26014 Atom::Keyword("kw".into()),
26015 Atom::Int(42),
26016 Atom::Int(-7),
26017 Atom::Int(0),
26018 Atom::Float(1.0),
26019 Atom::Float(1.5),
26020 Atom::Float(-42.0),
26021 Atom::Bool(true),
26022 Atom::Bool(false),
26023 ];
26024 for seed in cases {
26025 let rendered = seed.to_string();
26026 let mut parsed = crate::reader::read(&rendered)
26027 .unwrap_or_else(|e| panic!("reader rejected {rendered:?} for {seed:?}: {e}"));
26028 assert_eq!(
26029 parsed.len(),
26030 1,
26031 "rendered {rendered:?} for {seed:?} re-read as != 1 form"
26032 );
26033 let Sexp::Atom(round_tripped) = parsed.remove(0) else {
26034 panic!("rendered {rendered:?} for {seed:?} re-read as non-Atom");
26035 };
26036 assert_eq!(
26037 round_tripped.kind(),
26038 seed.kind(),
26039 "Atom::Display→reader drifted variant for {seed:?} via {rendered:?}"
26040 );
26041 assert_eq!(
26042 round_tripped, *seed,
26043 "Atom::Display→reader drifted payload for {seed:?} via {rendered:?}"
26044 );
26045 }
26046 }
26047
26048 #[test]
26049 fn atom_to_json_projects_each_variant_to_canonical_json_value() {
26050 // CANONICAL-MAPPING CONTRACT: pin that `Atom::to_json` produces
26051 // byte-identical `serde_json::Value` outputs for each
26052 // `AtomKind` variant as the pre-lift inline arms inside
26053 // `crate::domain::sexp_to_json` did. Sweeps a representative
26054 // atom of each variant so a regression that drifts ONE arm
26055 // (e.g. swaps `Symbol`'s mapping to a Number, or drops
26056 // `Keyword`'s `:` prefix that `json_to_sexp`'s inverse strips
26057 // — silently breaking every `:values-overlay` payload pinned
26058 // by the CLAUDE.md bool warning) fails loudly. Sibling-arm
26059 // sweep to `atom_display_renders_each_variant_to_canonical_form`
26060 // — both pin the typed-algebra rendering of the atomic
26061 // payload at its canonical projection. The float case uses
26062 // `1.5` (finite) here; NaN / ±∞ get their own pin below.
26063 use serde_json::Value as JValue;
26064 assert_eq!(
26065 Atom::Symbol("name".into()).to_json(),
26066 JValue::String("name".into()),
26067 );
26068 assert_eq!(
26069 Atom::Keyword("parent".into()).to_json(),
26070 JValue::String(":parent".into()),
26071 );
26072 assert_eq!(
26073 Atom::Str("body".into()).to_json(),
26074 JValue::String("body".into()),
26075 );
26076 assert_eq!(Atom::Int(42).to_json(), JValue::Number(42i64.into()));
26077 assert_eq!(Atom::Int(-7).to_json(), JValue::Number((-7i64).into()));
26078 assert_eq!(
26079 Atom::Float(1.5).to_json(),
26080 JValue::Number(serde_json::Number::from_f64(1.5).unwrap()),
26081 );
26082 assert_eq!(Atom::Bool(true).to_json(), JValue::Bool(true));
26083 assert_eq!(Atom::Bool(false).to_json(), JValue::Bool(false));
26084 }
26085
26086 #[test]
26087 fn atom_from_json_number_int_arm_projects_i64_backed_numbers_to_atom_int() {
26088 // TYPED-INVERSE CONTRACT (Int arm): pin that `Atom::from_json_number`
26089 // decodes every `serde_json::Number` whose `.as_i64()` returns
26090 // `Some(i)` to `Atom::Int(i)`. Sweeps every i64-boundary value
26091 // the substrate pinned in the sibling `Atom::to_json` sweep
26092 // (0, ±1, ±42, i64::MAX, i64::MIN) plus a representative
26093 // interior sample; the sweep pins that the `as_i64()` arm
26094 // fires eagerly BEFORE the `as_f64()` arm, so an
26095 // integer-valued `Number` never sinks to `Atom::Float` at the
26096 // atomic-algebra boundary. A regression that drifts the arm
26097 // (e.g. swaps the `as_i64` / `as_f64` order) fails at the
26098 // `i64::MAX` / `i64::MIN` boundary samples because those two
26099 // values exceed `f64`'s 53-bit mantissa and would silently
26100 // round through the `as_f64` sink.
26101 for i in [0i64, 1, -1, 42, -7, i64::MAX, i64::MIN] {
26102 let n: serde_json::Number = i.into();
26103 assert_eq!(
26104 Atom::from_json_number(&n),
26105 Atom::Int(i),
26106 "Atom::from_json_number drifted Int arm for i64 sample {i}",
26107 );
26108 }
26109 }
26110
26111 #[test]
26112 fn atom_from_json_number_float_arm_projects_finite_non_integer_f64_backed_numbers_to_atom_float(
26113 ) {
26114 // TYPED-INVERSE CONTRACT (Float arm): pin that
26115 // `Atom::from_json_number` decodes every `serde_json::Number`
26116 // whose `.as_i64()` returns `None` but `.as_f64()` returns
26117 // `Some(f)` to `Atom::Float(f)`. Sweeps a representative set of
26118 // finite non-integer-valued f64 samples the substrate pinned
26119 // in the sibling `Atom::to_json` sweep (1.5, -2.5, positive
26120 // and negative fractional values, subnormal, `f64::MIN_POSITIVE`).
26121 // Every sample is constructed via `serde_json::Number::from_f64`
26122 // which the standard library documents as f64-backed
26123 // (`.as_i64()` returns `None`, `.as_f64()` returns
26124 // `Some(input)`) so the Float arm fires deterministically.
26125 // A regression that drifts the arm (e.g. drops the `as_f64`
26126 // sink entirely and falls through to the `Int(0)` typed floor)
26127 // fails HERE at the fractional-value assertions with an
26128 // `Int(0)` mismatch.
26129 for f in [
26130 1.5f64,
26131 -2.5,
26132 0.1,
26133 -0.1,
26134 f64::MIN_POSITIVE,
26135 1.234_567_890_123,
26136 ] {
26137 let n = serde_json::Number::from_f64(f)
26138 .unwrap_or_else(|| panic!("Number::from_f64({f}) must accept finite float"));
26139 assert_eq!(
26140 Atom::from_json_number(&n),
26141 Atom::Float(f),
26142 "Atom::from_json_number drifted Float arm for f64 sample {f}",
26143 );
26144 }
26145 }
26146
26147 #[test]
26148 fn atom_from_json_number_round_trips_atom_to_json_int_arm() {
26149 // ROUND-TRIP LAW (Int axis): pin the paired-projection identity
26150 // `Atom::from_json_number(&<Atom::Int(i).to_json() as Number>)
26151 // == Atom::Int(i)` for every i64 boundary sample. The pair
26152 // `Atom::to_json` (forward) + `Atom::from_json_number` (inverse)
26153 // now lives on the SAME closed-set [`Atom`] algebra — this
26154 // pin proves the closure at ONE algebra layer without a
26155 // `Sexp::from_json` intermediary. A regression that drifts
26156 // either side of the pair (e.g. `Atom::to_json` emits `Int(n)`
26157 // as a JSON string, or `Atom::from_json_number` inverts the
26158 // `as_i64` / `as_f64` cascade order) surfaces here at the
26159 // boundary-value mismatch. Sibling-shape pin to
26160 // `atom_display_round_trips_through_reader_preserving_typed_identity`
26161 // — where that pin closes the `Atom → Display → reader → Atom`
26162 // round-trip on the Display axis, THIS pin closes the
26163 // `Atom::Int → to_json → Number → from_json_number → Atom::Int`
26164 // round-trip on the JSON numeric axis, both on the SAME [`Atom`]
26165 // algebra.
26166 for i in [0i64, 1, -1, 42, -7, i64::MAX, i64::MIN] {
26167 let atom_before = Atom::Int(i);
26168 let via_forward = atom_before.to_json();
26169 let n = match via_forward {
26170 serde_json::Value::Number(n) => n,
26171 other => {
26172 panic!("Atom::Int({i}).to_json() must project to JValue::Number, got {other:?}",)
26173 }
26174 };
26175 assert_eq!(
26176 Atom::from_json_number(&n),
26177 atom_before,
26178 "Atom::Int({i}) round-trip through to_json + from_json_number drifted",
26179 );
26180 }
26181 }
26182
26183 #[test]
26184 fn atom_from_json_number_round_trips_atom_to_json_float_arm_for_non_integer_finite_samples() {
26185 // ROUND-TRIP LAW (Float axis, non-integer subset): pin the
26186 // paired-projection identity `Atom::from_json_number(&<Atom::Float(f)
26187 // .to_json() as Number>) == Atom::Float(f)` for every finite
26188 // non-integer f64 sample. The non-integer restriction is
26189 // load-bearing: `serde_json::Number::from_f64` on an
26190 // integer-valued f64 like `1.0` produces a Number whose
26191 // `.as_i64()` may return `Some(1)` (the exact behavior depends
26192 // on `serde_json`'s internal representation of the JSON
26193 // number tower — integer-valued floats can round-trip through
26194 // the `as_i64` arm, sinking to `Atom::Int(1)` instead of
26195 // `Atom::Float(1.0)`). The three-way (`Symbol` / `Keyword` /
26196 // `Str`) collapse on the string side of `Sexp::from_json`'s
26197 // docstring is one axis; THIS pin covers the (`Int` / `Float`)
26198 // collapse on the numeric side for the round-trippable subset
26199 // (non-integer-valued finite floats). Together with the Int
26200 // round-trip pin above the two floors close the numeric-axis
26201 // round-trip closure at the algebra layer. NaN / ±∞ are
26202 // pinned separately at `atom_to_json_float_nan_and_infinity_collapse_to_null`
26203 // — those don't produce a `Number` from `to_json` so the
26204 // round-trip law does NOT apply to them.
26205 for f in [
26206 1.5f64,
26207 -2.5,
26208 0.1,
26209 -0.1,
26210 f64::MIN_POSITIVE,
26211 1.234_567_890_123,
26212 ] {
26213 let atom_before = Atom::Float(f);
26214 let via_forward = atom_before.to_json();
26215 let n = match via_forward {
26216 serde_json::Value::Number(n) => n,
26217 other => panic!(
26218 "Atom::Float({f}).to_json() must project to JValue::Number, got {other:?}",
26219 ),
26220 };
26221 assert_eq!(
26222 Atom::from_json_number(&n),
26223 atom_before,
26224 "Atom::Float({f}) round-trip through to_json + from_json_number drifted",
26225 );
26226 }
26227 }
26228
26229 #[test]
26230 fn atom_to_json_float_nan_and_infinity_collapse_to_null() {
26231 // JSON-INEXPRESSIBILITY PIN: JSON has no canonical form for
26232 // `NaN` / `±∞` — `serde_json::Number::from_f64` returns `None`
26233 // for those values, and the substrate's pre-lift behavior at
26234 // `sexp_to_json` mapped them to `JValue::Null` via
26235 // `unwrap_or(JValue::Null)`. Pin the special-case branch at
26236 // the typed-algebra boundary directly so a future refactor
26237 // that bypasses `serde_json::Number::from_f64` (e.g. emits
26238 // `NaN` as the string `"NaN"`, which the JSON deserializer
26239 // would silently re-read as a String at the round-trip
26240 // boundary) surfaces at this test without requiring a Sexp
26241 // wrap to reproduce. Sibling-shape pin to
26242 // `atom_display_renders_integral_float_with_dot_zero_suffix`
26243 // — both pin a non-default branch of the float projection's
26244 // canonical rendering. The branch IS load-bearing for the
26245 // `sexp_to_json` → `serde_json::from_value::<T>` bridge the
26246 // derive-macro fallthrough uses: a downstream `f64` field
26247 // that the operator wrote `:rate :nan` for collapses to
26248 // `JValue::Null` HERE rather than at the serde boundary,
26249 // emitting a clean structural diagnostic instead of a JSON
26250 // parse error miles downstream.
26251 use serde_json::Value as JValue;
26252 assert_eq!(Atom::Float(f64::NAN).to_json(), JValue::Null);
26253 assert_eq!(Atom::Float(f64::INFINITY).to_json(), JValue::Null);
26254 assert_eq!(Atom::Float(f64::NEG_INFINITY).to_json(), JValue::Null);
26255 }
26256
26257 #[test]
26258 fn atom_from_lexeme_classifies_each_atom_kind_for_canonical_lexeme() {
26259 // CANONICAL-CLASSIFICATION CONTRACT: pin that `Atom::from_lexeme`
26260 // produces byte-identical typed `Atom` outputs for a canonical
26261 // lexeme of each `AtomKind` variant against the pre-lift
26262 // `crate::reader::atom_from_str` cascade. Sweeps a representative
26263 // lexeme of each variant so a regression that drifts ONE arm
26264 // (e.g. swaps `"#t"` to `Atom::Symbol("#t")` silently breaking
26265 // every `:values-overlay` payload pinned by the CLAUDE.md bool
26266 // warning, or strips `":kw"`'s prefix when classifying to
26267 // `Atom::Symbol` rather than `Atom::Keyword`) fails loudly.
26268 // Sibling-arm sweep to
26269 // `atom_display_renders_each_variant_to_canonical_form` and
26270 // `atom_to_json_projects_each_variant_to_canonical_json_value` —
26271 // all three pin the typed-algebra at its canonical per-variant
26272 // projection. This is the typed-ENTRY side of the bidirectional
26273 // sweep; those are the typed-EXIT sides.
26274 //
26275 // `Atom::Str` is intentionally absent — `Atom::from_lexeme`'s
26276 // typed-entry surface processes BARE reader-token lexemes, and
26277 // string literals take the reader's `"`-quoted tokenizer branch
26278 // (a `Token::Str(_)`, NOT a `Token::Atom(_)`). The reader's
26279 // string round-trip is pinned by `string_escapes` in
26280 // `crate::reader::tests`.
26281 assert_eq!(Atom::from_lexeme("foo"), Atom::Symbol("foo".into()));
26282 assert_eq!(
26283 Atom::from_lexeme("defpoint"),
26284 Atom::Symbol("defpoint".into())
26285 );
26286 assert_eq!(Atom::from_lexeme("seph.1"), Atom::Symbol("seph.1".into()));
26287 assert_eq!(Atom::from_lexeme(":parent"), Atom::Keyword("parent".into()));
26288 assert_eq!(Atom::from_lexeme(":kw"), Atom::Keyword("kw".into()));
26289 assert_eq!(Atom::from_lexeme("42"), Atom::Int(42));
26290 assert_eq!(Atom::from_lexeme("-7"), Atom::Int(-7));
26291 assert_eq!(Atom::from_lexeme("0"), Atom::Int(0));
26292 assert_eq!(Atom::from_lexeme("1.5"), Atom::Float(1.5));
26293 assert_eq!(Atom::from_lexeme("-2.5"), Atom::Float(-2.5));
26294 assert_eq!(Atom::from_lexeme("#t"), Atom::Bool(true));
26295 assert_eq!(Atom::from_lexeme("#f"), Atom::Bool(false));
26296 }
26297
26298 #[test]
26299 fn atom_from_lexeme_prefers_int_over_float_for_integer_lexeme() {
26300 // LOAD-BEARING DISPATCH-ORDERING PIN: `Atom::from_lexeme` tries
26301 // `i64::from_str` BEFORE `f64::from_str` so a bare `"1"`
26302 // classifies as `Atom::Int(1)`, NOT `Atom::Float(1.0)`. The
26303 // typed-int-vs-typed-float distinction at the typed-entry
26304 // boundary is the dual of `fmt_float`'s `.0`-suffix discipline
26305 // on the typed-exit side — together the two projections form
26306 // the round-trip identity `from_lexeme(a.to_string()) == a`
26307 // for both `Int(_)` and `Float(_)` payloads pinned by
26308 // `atom_from_lexeme_round_trips_with_atom_display_for_every_non_str_variant`
26309 // below. A regression that reorders the parse-cascade (e.g.
26310 // tries `f64::from_str` first, or unifies both via
26311 // `f64::from_str` alone since `f64` parse accepts integer
26312 // lexemes too) silently demotes every integer authoring slot
26313 // into the float track at the reader, corrupting every
26314 // downstream `i64` field's serde round-trip without a
26315 // structural error to point to.
26316 assert_eq!(Atom::from_lexeme("1"), Atom::Int(1));
26317 assert_eq!(Atom::from_lexeme("0"), Atom::Int(0));
26318 assert_eq!(Atom::from_lexeme("-100"), Atom::Int(-100));
26319 // The bare-int lexeme MUST NOT classify to `Atom::Float`.
26320 assert_ne!(Atom::from_lexeme("1"), Atom::Float(1.0));
26321 // Float lexemes (with explicit `.` or scientific notation)
26322 // route through the f64 arm — pin the cascade's fallthrough
26323 // ordering so the int-shortcut doesn't swallow them.
26324 assert_eq!(Atom::from_lexeme("1.0"), Atom::Float(1.0));
26325 assert_eq!(Atom::from_lexeme("1.5"), Atom::Float(1.5));
26326 assert_eq!(Atom::from_lexeme("1e3"), Atom::Float(1e3));
26327 }
26328
26329 #[test]
26330 fn atom_from_lexeme_routes_unknown_lexeme_to_symbol_default() {
26331 // CLOSED-SET DEFAULT-ARM PIN: every lexeme that didn't match a
26332 // structural prefix (`"#t"`/`"#f"` for Bool, `":"` prefix for
26333 // Keyword) or parse as a number (`i64` then `f64`) classifies
26334 // to `Atom::Symbol(_)` by default — the closed-set fallthrough
26335 // arm the reader has shipped with from inception. Pin the
26336 // default-arm projection so a future refactor that adds a new
26337 // structural prefix (e.g. `"#["` for vector literals, `"#\\x"`
26338 // for char literals) without updating the default-arm wording
26339 // cannot silently drift previously-Symbol lexemes into a new
26340 // bucket — the regression surfaces at this test, which sweeps
26341 // the structural-prefix non-matches every closed-set extension
26342 // must continue to classify as Symbol unless the extension
26343 // explicitly claims them. Sibling-shape pin to
26344 // `atom_from_lexeme_classifies_each_atom_kind_for_canonical_lexeme`
26345 // — that pins the structural-prefix MATCHES, this pins the
26346 // structural-prefix NON-MATCHES.
26347 //
26348 // The CLAUDE.md-pinned `true`/`false` round-trip discipline
26349 // also rides this default arm: bare `true`/`false` re-read as
26350 // `Atom::Symbol("true")` / `Atom::Symbol("false")` because the
26351 // Scheme bool spellings are `"#t"`/`"#f"`. The pin guards the
26352 // `serde_json::Value::Bool` field round-trip every
26353 // `:values-overlay` payload depends on.
26354 assert_eq!(Atom::from_lexeme("foo"), Atom::Symbol("foo".into()));
26355 assert_eq!(
26356 Atom::from_lexeme("defpoint"),
26357 Atom::Symbol("defpoint".into())
26358 );
26359 // The CLAUDE.md `true`/`false` warning — these lexemes MUST
26360 // route through the default Symbol arm, NOT through the Bool
26361 // arm. A regression that adds `"true"`/`"false"` recognition
26362 // silently flips every `:values-overlay` Bool field to the
26363 // wrong serde shape.
26364 assert_eq!(Atom::from_lexeme("true"), Atom::Symbol("true".into()));
26365 assert_eq!(Atom::from_lexeme("false"), Atom::Symbol("false".into()));
26366 // Non-structural-prefix shapes — pin a sampling so the
26367 // default arm continues to absorb every shape the prefix
26368 // arms haven't claimed.
26369 assert_eq!(Atom::from_lexeme("seph.1"), Atom::Symbol("seph.1".into()));
26370 assert_eq!(Atom::from_lexeme("a-b"), Atom::Symbol("a-b".into()));
26371 assert_eq!(Atom::from_lexeme("+"), Atom::Symbol("+".into()));
26372 }
26373
26374 #[test]
26375 fn atom_from_lexeme_round_trips_with_atom_display_for_every_non_str_variant() {
26376 // BIDIRECTIONAL TYPED-IDENTITY CONTRACT: render each `Atom`
26377 // (excluding `Atom::Str` — see below) via `fmt::Display`, parse
26378 // the rendering through `Atom::from_lexeme`, and pin that the
26379 // round-trip preserves the typed identity exactly. This is the
26380 // typed-exit / typed-entry mirror at the atomic-payload
26381 // boundary AT THE ALGEBRA LEVEL — sibling-shape pin to
26382 // `atom_display_round_trips_through_reader_preserving_typed_identity`
26383 // which exercises the same round-trip through the full reader.
26384 // Lifting the typed-entry surface onto `Atom::from_lexeme`
26385 // means the round-trip law now lives at the algebra rather
26386 // than at the reader's free-function boundary — a future
26387 // tool that wants to round-trip an `Atom` through its
26388 // canonical lexeme spelling (LSP token-completion, REPL
26389 // pretty-printer, structural editor) binds to `from_lexeme` +
26390 // `Display` directly without crossing through the reader's
26391 // tokenizer.
26392 //
26393 // `Atom::Str` is intentionally absent — `Display for Atom`
26394 // renders `Str(s)` as `"{s:?}"` (debug-quoted, with quote
26395 // marks around the content). The quoted form is NOT a bare
26396 // reader-token lexeme: it's a `Token::Str(_)` to the
26397 // tokenizer, taking a distinct branch. The Str round-trip
26398 // through the FULL reader is pinned by `string_escapes` in
26399 // `crate::reader::tests`.
26400 let cases: &[Atom] = &[
26401 Atom::Symbol("foo-bar".into()),
26402 Atom::Symbol("defpoint".into()),
26403 Atom::Symbol("seph.1".into()),
26404 Atom::Keyword("parent".into()),
26405 Atom::Keyword("kw".into()),
26406 Atom::Int(0),
26407 Atom::Int(42),
26408 Atom::Int(-7),
26409 Atom::Float(1.0),
26410 Atom::Float(1.5),
26411 Atom::Float(-42.0),
26412 Atom::Bool(true),
26413 Atom::Bool(false),
26414 ];
26415 for seed in cases {
26416 let rendered = seed.to_string();
26417 let round_tripped = Atom::from_lexeme(&rendered);
26418 assert_eq!(
26419 round_tripped.kind(),
26420 seed.kind(),
26421 "Atom::from_lexeme∘Display drifted variant for {seed:?} via {rendered:?}"
26422 );
26423 assert_eq!(
26424 round_tripped, *seed,
26425 "Atom::from_lexeme∘Display drifted payload for {seed:?} via {rendered:?}"
26426 );
26427 }
26428 }
26429
26430 // ── Atom::as_X soft-projection family + Sexp::as_atom structural lift ──
26431 //
26432 // The six per-variant soft-projection methods on the typed `Atom` algebra
26433 // (`as_symbol` / `as_keyword` / `as_string` / `as_int` / `as_float` /
26434 // `as_bool`) lift the inline `Self::Atom(Atom::X(s)) => Some(s)` arms
26435 // that previously lived at the six `Sexp::as_X` consumer sites onto ONE
26436 // method per closed-set arm. The `Sexp::as_atom` structural lift gives
26437 // the consumer family a uniform two-step composition `as_atom().and_then
26438 // (Atom::as_X)`. The tests below pin:
26439 //
26440 // (1) per-variant typed projection — `Atom::as_X` returns `Some(payload)`
26441 // iff the variant matches AND `None` for every other closed-set arm
26442 // (path-uniformity over `AtomKind::ALL`);
26443 // (2) the `Sexp::as_atom` projection — `Some(&Atom)` iff `Sexp::Atom(_)`
26444 // AND `None` for the structural shapes (`Nil` / `List` /
26445 // `Quote` / `Quasiquote` / `Unquote` / `UnquoteSplice`);
26446 // (3) lifted-boundary composition — `Sexp::as_<X>(s) == s.as_atom()
26447 // .and_then(Atom::as_<X>)` for every atomic variant, AND the
26448 // `Sexp::as_float` widening specialization (`Atom::Int(n)` →
26449 // `Some(n as f64)`) lives at the consumer layer, NOT the algebra
26450 // layer (per the typed-identity discipline pinned at
26451 // `Atom::as_int`'s docstring).
26452
26453 #[test]
26454 fn atom_as_symbol_returns_payload_iff_symbol_variant() {
26455 // PER-VARIANT PROJECTION CONTRACT: `Atom::as_symbol` projects
26456 // `Atom::Symbol(s)` to `Some(&s)` and every other `AtomKind`
26457 // variant to `None`. Sweeps `AtomKind::ALL` for the path-
26458 // uniformity guard — catches a regression that mis-routes ONE
26459 // arm (e.g. accepts `Atom::Keyword(s)` thinking it's "also a
26460 // symbol-like identifier", or rejects `Atom::Symbol("foo")` if
26461 // a future closed-set sweep accidentally narrows the projection
26462 // by an `s.is_empty()` filter).
26463 assert_eq!(Atom::Symbol("foo".into()).as_symbol(), Some("foo"));
26464 assert_eq!(Atom::Symbol("seph.1".into()).as_symbol(), Some("seph.1"));
26465 assert_eq!(Atom::Symbol(String::new()).as_symbol(), Some(""));
26466 for kind in AtomKind::ALL {
26467 if kind == AtomKind::Symbol {
26468 continue;
26469 }
26470 let probe: Atom = match kind {
26471 AtomKind::Symbol => unreachable!(),
26472 AtomKind::Keyword => Atom::Keyword("kw".into()),
26473 AtomKind::Str => Atom::Str("body".into()),
26474 AtomKind::Int => Atom::Int(42),
26475 AtomKind::Float => Atom::Float(1.5),
26476 AtomKind::Bool => Atom::Bool(true),
26477 };
26478 assert_eq!(
26479 probe.as_symbol(),
26480 None,
26481 "Atom::as_symbol must reject non-Symbol variant {kind:?}",
26482 );
26483 }
26484 }
26485
26486 #[test]
26487 fn atom_as_keyword_returns_payload_iff_keyword_variant() {
26488 // PER-VARIANT PROJECTION CONTRACT: `Atom::as_keyword` projects
26489 // `Atom::Keyword(s)` to `Some(&s)` and every other `AtomKind`
26490 // variant to `None`. The returned `&str` is the BARE identifier
26491 // (the `:` prefix was already stripped at the typed-ENTRY
26492 // classifier boundary, `Atom::from_lexeme`); this projection
26493 // does not re-add or re-strip the prefix — pinned by the empty
26494 // probe to catch a regression that accidentally trims a leading
26495 // char.
26496 assert_eq!(Atom::Keyword("parent".into()).as_keyword(), Some("parent"));
26497 assert_eq!(Atom::Keyword(String::new()).as_keyword(), Some(""));
26498 for kind in AtomKind::ALL {
26499 if kind == AtomKind::Keyword {
26500 continue;
26501 }
26502 let probe: Atom = match kind {
26503 AtomKind::Symbol => Atom::Symbol("foo".into()),
26504 AtomKind::Keyword => unreachable!(),
26505 AtomKind::Str => Atom::Str("body".into()),
26506 AtomKind::Int => Atom::Int(42),
26507 AtomKind::Float => Atom::Float(1.5),
26508 AtomKind::Bool => Atom::Bool(true),
26509 };
26510 assert_eq!(
26511 probe.as_keyword(),
26512 None,
26513 "Atom::as_keyword must reject non-Keyword variant {kind:?}",
26514 );
26515 }
26516 }
26517
26518 #[test]
26519 fn atom_as_string_returns_payload_iff_str_variant() {
26520 // PER-VARIANT PROJECTION CONTRACT: `Atom::as_string` projects
26521 // `Atom::Str(s)` to `Some(&s)` and every other `AtomKind`
26522 // variant (including `Symbol` and `Keyword`, which also carry
26523 // `String` payloads) to `None`. The closed-set discriminator is
26524 // load-bearing: a `Symbol("foo")` MUST NOT route through this
26525 // projection — a regression that conflates the three string-
26526 // carrying variants would silently re-classify operator-position
26527 // symbols as string-typed kwarg values at every `extract_string`
26528 // boundary.
26529 assert_eq!(Atom::Str("body".into()).as_string(), Some("body"));
26530 assert_eq!(
26531 Atom::Str("with\nnewline".into()).as_string(),
26532 Some("with\nnewline"),
26533 );
26534 assert_eq!(Atom::Str(String::new()).as_string(), Some(""));
26535 assert_eq!(
26536 Atom::Symbol("looks-like-a-string".into()).as_string(),
26537 None,
26538 "Atom::as_string MUST NOT conflate Symbol with Str — load-bearing typed-identity",
26539 );
26540 assert_eq!(
26541 Atom::Keyword("looks-like-a-string".into()).as_string(),
26542 None,
26543 "Atom::as_string MUST NOT conflate Keyword with Str — load-bearing typed-identity",
26544 );
26545 for kind in [AtomKind::Int, AtomKind::Float, AtomKind::Bool] {
26546 let probe: Atom = match kind {
26547 AtomKind::Int => Atom::Int(42),
26548 AtomKind::Float => Atom::Float(1.5),
26549 AtomKind::Bool => Atom::Bool(true),
26550 _ => unreachable!(),
26551 };
26552 assert_eq!(
26553 probe.as_string(),
26554 None,
26555 "Atom::as_string must reject non-Str variant {kind:?}",
26556 );
26557 }
26558 }
26559
26560 #[test]
26561 fn atom_as_int_returns_payload_iff_int_variant_strict_no_float_widening() {
26562 // PER-VARIANT PROJECTION CONTRACT (STRICT): `Atom::as_int`
26563 // projects `Atom::Int(n)` to `Some(n)` and every other variant
26564 // to `None`. STRICT typed identity: `Atom::Float(1.0)` does
26565 // NOT project through (stays `None`) — the typed-identity
26566 // distinction `Int(1)` vs `Float(1.0)` (load-bearing at the
26567 // `Atom::from_lexeme` ⇄ `Atom::Display` round-trip boundary, dual of
26568 // `fmt_float`'s `.0`-suffix discipline) is preserved at the
26569 // algebra layer. The widening face lives at the
26570 // `Sexp::as_float` consumer (which accepts both `Float` AND
26571 // `Int`); the strict typed identity at the `Atom` algebra is
26572 // load-bearing.
26573 assert_eq!(Atom::Int(42).as_int(), Some(42));
26574 assert_eq!(Atom::Int(-7).as_int(), Some(-7));
26575 assert_eq!(Atom::Int(0).as_int(), Some(0));
26576 assert_eq!(
26577 Atom::Float(1.0).as_int(),
26578 None,
26579 "Atom::as_int MUST be strict — Float(1.0) is NOT Int(1) at the algebra layer",
26580 );
26581 for kind in [
26582 AtomKind::Symbol,
26583 AtomKind::Keyword,
26584 AtomKind::Str,
26585 AtomKind::Float,
26586 AtomKind::Bool,
26587 ] {
26588 let probe: Atom = match kind {
26589 AtomKind::Symbol => Atom::Symbol("foo".into()),
26590 AtomKind::Keyword => Atom::Keyword("kw".into()),
26591 AtomKind::Str => Atom::Str("body".into()),
26592 AtomKind::Float => Atom::Float(1.5),
26593 AtomKind::Bool => Atom::Bool(true),
26594 _ => unreachable!(),
26595 };
26596 assert_eq!(
26597 probe.as_int(),
26598 None,
26599 "Atom::as_int must reject non-Int variant {kind:?}",
26600 );
26601 }
26602 }
26603
26604 #[test]
26605 fn atom_as_float_returns_payload_iff_float_variant_strict_no_int_widening() {
26606 // PER-VARIANT PROJECTION CONTRACT (STRICT): `Atom::as_float`
26607 // projects `Atom::Float(n)` to `Some(n)` and every other
26608 // variant to `None`. STRICT typed identity: `Atom::Int(1)`
26609 // does NOT project through (stays `None`) — see
26610 // `atom_as_int_returns_payload_iff_int_variant_strict_no_float_widening`
26611 // for the symmetric discipline. The widening face
26612 // (`Atom::Int(n) → Some(n as f64)`) lives at the `Sexp::as_float`
26613 // consumer layer, NOT the algebra layer.
26614 assert_eq!(Atom::Float(1.5).as_float(), Some(1.5));
26615 assert_eq!(Atom::Float(1.0).as_float(), Some(1.0));
26616 assert_eq!(Atom::Float(-42.0).as_float(), Some(-42.0));
26617 assert_eq!(
26618 Atom::Int(1).as_float(),
26619 None,
26620 "Atom::as_float MUST be strict — Int(1) is NOT Float(1.0) at the algebra layer",
26621 );
26622 for kind in [
26623 AtomKind::Symbol,
26624 AtomKind::Keyword,
26625 AtomKind::Str,
26626 AtomKind::Int,
26627 AtomKind::Bool,
26628 ] {
26629 let probe: Atom = match kind {
26630 AtomKind::Symbol => Atom::Symbol("foo".into()),
26631 AtomKind::Keyword => Atom::Keyword("kw".into()),
26632 AtomKind::Str => Atom::Str("body".into()),
26633 AtomKind::Int => Atom::Int(42),
26634 AtomKind::Bool => Atom::Bool(true),
26635 _ => unreachable!(),
26636 };
26637 assert_eq!(
26638 probe.as_float(),
26639 None,
26640 "Atom::as_float must reject non-Float variant {kind:?}",
26641 );
26642 }
26643 }
26644
26645 #[test]
26646 fn atom_as_bool_returns_payload_iff_bool_variant() {
26647 // PER-VARIANT PROJECTION CONTRACT: `Atom::as_bool` projects
26648 // `Atom::Bool(b)` to `Some(b)` and every other variant to
26649 // `None`. Both spellings (`true` / `false`) project through
26650 // the SAME projection — the variant identity (`Bool`) is what
26651 // routes; the inner payload (`true` / `false`) is the
26652 // projected value. CLAUDE.md "Lisp bools": at the reader
26653 // boundary the typed-entry classifier `Atom::from_lexeme`
26654 // routes `"#t"` / `"#f"` to `Atom::Bool(_)` and bare
26655 // `"true"` / `"false"` to `Atom::Symbol(_)`; this projection
26656 // does NOT re-classify the symbol-spelled bools — they STAY
26657 // symbols. The negative test (`Atom::Symbol("true")` rejects)
26658 // pins the discriminator discipline.
26659 assert_eq!(Atom::Bool(true).as_bool(), Some(true));
26660 assert_eq!(Atom::Bool(false).as_bool(), Some(false));
26661 assert_eq!(
26662 Atom::Symbol("true".into()).as_bool(),
26663 None,
26664 "Atom::as_bool MUST reject Symbol(\"true\") — CLAUDE.md typed-identity discipline",
26665 );
26666 assert_eq!(
26667 Atom::Symbol("false".into()).as_bool(),
26668 None,
26669 "Atom::as_bool MUST reject Symbol(\"false\") — CLAUDE.md typed-identity discipline",
26670 );
26671 for kind in [
26672 AtomKind::Symbol,
26673 AtomKind::Keyword,
26674 AtomKind::Str,
26675 AtomKind::Int,
26676 AtomKind::Float,
26677 ] {
26678 let probe: Atom = match kind {
26679 AtomKind::Symbol => Atom::Symbol("foo".into()),
26680 AtomKind::Keyword => Atom::Keyword("kw".into()),
26681 AtomKind::Str => Atom::Str("body".into()),
26682 AtomKind::Int => Atom::Int(42),
26683 AtomKind::Float => Atom::Float(1.5),
26684 _ => unreachable!(),
26685 };
26686 assert_eq!(
26687 probe.as_bool(),
26688 None,
26689 "Atom::as_bool must reject non-Bool variant {kind:?}",
26690 );
26691 }
26692 }
26693
26694 #[test]
26695 fn atom_as_symbol_or_string_returns_payload_iff_symbol_or_str_variant() {
26696 // UNION-PROJECTION CONTRACT: `Atom::as_symbol_or_string` projects
26697 // BOTH `Atom::Symbol(s)` AND `Atom::Str(s)` to `Some(s)` and every
26698 // other atomic kind (`Keyword`, `Int`, `Float`, `Bool`) to `None`.
26699 // The disjunctive composition `as_symbol().or_else(||
26700 // as_string())` lives at ONE typed-algebra projection on the
26701 // closed-set `Atom` algebra; pre-lift the composition lived at
26702 // `Sexp::as_symbol_or_string`'s consumer body and traversed
26703 // `Sexp::as_atom` TWICE (once per per-variant projection),
26704 // post-lift it traverses `Sexp::as_atom` ONCE through the
26705 // algebra-level union projection. Pin the algebra-level contract
26706 // sweep so a regression that drifts ONE union arm (e.g. drops the
26707 // `Str` arm, accidentally widens to accept `Keyword`) surfaces
26708 // structurally.
26709 assert_eq!(
26710 Atom::Symbol("my-name".into()).as_symbol_or_string(),
26711 Some("my-name"),
26712 "Atom::as_symbol_or_string must accept Atom::Symbol",
26713 );
26714 assert_eq!(
26715 Atom::Str("my-name".into()).as_symbol_or_string(),
26716 Some("my-name"),
26717 "Atom::as_symbol_or_string must accept Atom::Str",
26718 );
26719 // Empty payloads project through too — the union projection
26720 // is keyed on variant identity, not payload contents.
26721 assert_eq!(
26722 Atom::Symbol(String::new()).as_symbol_or_string(),
26723 Some(""),
26724 "Atom::as_symbol_or_string must accept empty Symbol payload",
26725 );
26726 assert_eq!(
26727 Atom::Str(String::new()).as_symbol_or_string(),
26728 Some(""),
26729 "Atom::as_symbol_or_string must accept empty Str payload",
26730 );
26731 // Negative sweep: the four non-Symbol-non-Str variants reject.
26732 for kind in [
26733 AtomKind::Keyword,
26734 AtomKind::Int,
26735 AtomKind::Float,
26736 AtomKind::Bool,
26737 ] {
26738 let probe: Atom = match kind {
26739 AtomKind::Keyword => Atom::Keyword("kw".into()),
26740 AtomKind::Int => Atom::Int(42),
26741 AtomKind::Float => Atom::Float(1.5),
26742 AtomKind::Bool => Atom::Bool(true),
26743 _ => unreachable!(),
26744 };
26745 assert_eq!(
26746 probe.as_symbol_or_string(),
26747 None,
26748 "Atom::as_symbol_or_string must reject non-Symbol-non-Str variant {kind:?}",
26749 );
26750 }
26751 }
26752
26753 #[test]
26754 fn atom_as_symbol_or_string_borrow_ptr_eq_payload() {
26755 // BORROW-LIFETIME CONTRACT: the yielded `&str` borrows the inner
26756 // `String` payload's `&str` view verbatim — no copy, no
26757 // allocation, no `to_string()` round-trip. Pin via `ptr::eq` on
26758 // both projection sides (Symbol arm AND Str arm) so a regression
26759 // that re-inlines the union as `match self { Symbol(s) =>
26760 // Some(s.clone().as_str()), … }` (a `String::clone` reborrow that
26761 // changes the byte-identity) surfaces structurally. Same posture
26762 // as `as_call_to_args_borrow_is_same_pointer_as_as_call_tail` on
26763 // the call-form algebra.
26764 let sym = Atom::Symbol("my-name".into());
26765 let projected = sym.as_symbol_or_string().expect("Symbol arm projects");
26766 match &sym {
26767 Atom::Symbol(s) => assert!(
26768 std::ptr::eq(projected.as_ptr(), s.as_ptr()),
26769 "Atom::as_symbol_or_string must borrow Atom::Symbol payload verbatim",
26770 ),
26771 _ => unreachable!(),
26772 }
26773 let str_atom = Atom::Str("my-name".into());
26774 let projected_str = str_atom.as_symbol_or_string().expect("Str arm projects");
26775 match &str_atom {
26776 Atom::Str(s) => assert!(
26777 std::ptr::eq(projected_str.as_ptr(), s.as_ptr()),
26778 "Atom::as_symbol_or_string must borrow Atom::Str payload verbatim",
26779 ),
26780 _ => unreachable!(),
26781 }
26782 }
26783
26784 #[test]
26785 fn atom_as_symbol_or_string_is_the_disjunction_of_as_symbol_and_as_string() {
26786 // COMPOSITION LAW: pin that the union projection's value AGREES
26787 // byte-for-byte with the explicit disjunctive composition
26788 // `as_symbol().or_else(|| as_string())` across every atom kind.
26789 // A regression that drifts the union from its disjunctive
26790 // composition (e.g. swaps the `or_else` order so an
26791 // `Atom::Symbol` somehow routes through the `Str` arm first, or
26792 // adds a phantom arm that accepts `Keyword` payloads) surfaces
26793 // here. Same posture as `is_kwargs_list` composing through
26794 // `as_list ∘ atom_as_keyword`.
26795 for atom in [
26796 Atom::Symbol("foo".into()),
26797 Atom::Keyword("kw".into()),
26798 Atom::Str("body".into()),
26799 Atom::Int(42),
26800 Atom::Float(1.5),
26801 Atom::Bool(true),
26802 Atom::Bool(false),
26803 Atom::Symbol(String::new()),
26804 Atom::Str(String::new()),
26805 ] {
26806 let by_hand = atom.as_symbol().or_else(|| atom.as_string());
26807 assert_eq!(
26808 atom.as_symbol_or_string(),
26809 by_hand,
26810 "Atom::as_symbol_or_string drifted from as_symbol().or_else(|| as_string()) for {atom:?}",
26811 );
26812 }
26813 }
26814
26815 #[test]
26816 fn sexp_as_symbol_or_string_routes_through_atom_as_symbol_or_string_via_as_atom_composition() {
26817 // CONSUMER-LAYER COMPOSITION LAW: pin that `Sexp::as_symbol_or_string`
26818 // routes through the structural lift `Sexp::as_atom` + the
26819 // algebra-level `Atom::as_symbol_or_string` union projection —
26820 // a regression that re-inlines the pre-lift body
26821 // `self.as_symbol().or_else(|| self.as_string())` (TWO
26822 // `Sexp::as_atom` traversals) at the `Sexp` consumer layer
26823 // becomes detectable here. Sweeps every reachable outer shape so
26824 // the closed-form composition is pinned across Nil + every Atom
26825 // variant + every quote-family wrapper + List + the Sexp::Atom
26826 // arms a regression could route to.
26827 let cases = [
26828 Sexp::Nil,
26829 Sexp::symbol("foo"),
26830 Sexp::symbol(""),
26831 Sexp::string("body"),
26832 Sexp::string(""),
26833 Sexp::keyword("kw"),
26834 Sexp::int(7),
26835 Sexp::float(2.5),
26836 Sexp::boolean(true),
26837 Sexp::Quote(Box::new(Sexp::symbol("x"))),
26838 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
26839 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
26840 Sexp::UnquoteSplice(Box::new(Sexp::symbol("x"))),
26841 Sexp::List(vec![Sexp::symbol("a")]),
26842 Sexp::List(vec![]),
26843 ];
26844 for s in &cases {
26845 let by_composition = s.as_atom().and_then(Atom::as_symbol_or_string);
26846 assert_eq!(
26847 s.as_symbol_or_string(),
26848 by_composition,
26849 "Sexp::as_symbol_or_string drifted from as_atom().and_then(Atom::as_symbol_or_string) for {s}",
26850 );
26851 }
26852 }
26853
26854 #[test]
26855 fn sexp_as_symbol_or_string_yields_none_for_non_atom_outer_shapes() {
26856 // OUTER-SHAPE NEGATIVE SWEEP: pin that every non-Atom outer
26857 // shape (`Nil`, `List`, every quote-family wrapper) projects to
26858 // `None` — the structural-lift `Sexp::as_atom` rejects them at
26859 // the outer match before the union projection even runs. Pins
26860 // the soft-projection face: the named-form NAME gate
26861 // (`crate::compile::split_name_slot`'s `as_symbol_or_string`
26862 // consumer at compile.rs:671) sees `None` for these shapes and
26863 // emits `NamedFormNonSymbolName` with the projected `SexpShape`
26864 // — the lift preserves the same rejection arm boundary.
26865 for outer in [
26866 Sexp::Nil,
26867 Sexp::List(vec![Sexp::symbol("a")]),
26868 Sexp::List(vec![]),
26869 Sexp::Quote(Box::new(Sexp::symbol("x"))),
26870 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
26871 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
26872 Sexp::UnquoteSplice(Box::new(Sexp::symbol("x"))),
26873 ] {
26874 assert_eq!(
26875 outer.as_symbol_or_string(),
26876 None,
26877 "Sexp::as_symbol_or_string must reject non-Atom outer shape {outer:?}",
26878 );
26879 }
26880 }
26881
26882 #[test]
26883 fn sexp_as_symbol_or_string_borrow_ptr_eq_atom_payload() {
26884 // BORROW-LIFETIME CONTRACT: the yielded `&str` borrows the inner
26885 // `Atom::Symbol` / `Atom::Str` payload verbatim — no copy, no
26886 // allocation, same lifetime as the outer `&Sexp`. Pin via
26887 // `ptr::eq` on both projection sides so a regression that
26888 // re-inlines the union as a `String`-allocating reborrow (e.g.
26889 // `.map(|s| s.to_owned())` somewhere along the chain) surfaces
26890 // structurally. Sibling pin to
26891 // `atom_as_symbol_or_string_borrow_ptr_eq_payload` at the outer
26892 // (`&Sexp`) layer rather than the inner (`&Atom`) layer.
26893 let sym_sexp = Sexp::symbol("my-name");
26894 let projected = sym_sexp.as_symbol_or_string().expect("Symbol arm projects");
26895 match &sym_sexp {
26896 Sexp::Atom(Atom::Symbol(s)) => assert!(
26897 std::ptr::eq(projected.as_ptr(), s.as_ptr()),
26898 "Sexp::as_symbol_or_string must borrow Atom::Symbol payload verbatim",
26899 ),
26900 _ => unreachable!(),
26901 }
26902 let str_sexp = Sexp::string("my-name");
26903 let projected_str = str_sexp.as_symbol_or_string().expect("Str arm projects");
26904 match &str_sexp {
26905 Sexp::Atom(Atom::Str(s)) => assert!(
26906 std::ptr::eq(projected_str.as_ptr(), s.as_ptr()),
26907 "Sexp::as_symbol_or_string must borrow Atom::Str payload verbatim",
26908 ),
26909 _ => unreachable!(),
26910 }
26911 }
26912
26913 #[test]
26914 fn sexp_as_atom_projects_inner_atom_iff_outer_is_atom_variant() {
26915 // STRUCTURAL-LIFT CONTRACT: `Sexp::as_atom` projects
26916 // `Sexp::Atom(a)` to `Some(&a)` and every other outer shape
26917 // (`Nil` / `List` / `Quote` / `Quasiquote` / `Unquote` /
26918 // `UnquoteSplice`) to `None`. Sweeps each outer shape so a
26919 // regression that mis-routes ONE arm (e.g. accepts the
26920 // singleton list `(a)` thinking the inner counts as the
26921 // "wrapped atom", or rejects an `Atom` whose payload is empty)
26922 // fails loudly. The `&Atom` borrow is rooted at the outer
26923 // `&Sexp` — the projection does not clone, allocate, or take
26924 // ownership.
26925 let atom = Atom::Symbol("foo".into());
26926 let sexp = Sexp::Atom(atom.clone());
26927 assert_eq!(sexp.as_atom(), Some(&atom));
26928
26929 for outer in [
26930 Sexp::Nil,
26931 Sexp::List(vec![Sexp::symbol("a")]),
26932 Sexp::List(vec![]),
26933 Sexp::Quote(Box::new(Sexp::symbol("x"))),
26934 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
26935 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
26936 Sexp::UnquoteSplice(Box::new(Sexp::symbol("x"))),
26937 ] {
26938 assert_eq!(
26939 outer.as_atom(),
26940 None,
26941 "Sexp::as_atom must reject non-Atom outer shape {outer:?}",
26942 );
26943 }
26944 }
26945
26946 #[test]
26947 fn sexp_shape_method_projects_each_outer_arm_to_canonical_sexp_shape() {
26948 // CANONICAL-MAPPING CONTRACT: pin that `Sexp::shape()` produces
26949 // byte-identical `SexpShape` markers for each outer-arm of the
26950 // closed `Sexp` algebra. Sweeps every reachable outer shape
26951 // (`Nil`, every `AtomKind` payload, `List`, every `QuoteForm`
26952 // wrapper) so a regression that drifts ONE arm (e.g. routes the
26953 // `Atom::Keyword` arm through `Atom::kind().sexp_shape()` to the
26954 // wrong `SexpShape` variant, or drops the `expect_quote_form`
26955 // projection's marker for a quote-family wrapper) fails loudly.
26956 // Sibling-arm sweep to
26957 // `quote_form_sexp_shape_pins_canonical_shape_identity_for_every_variant`
26958 // (the four quote-family arms in isolation) AND
26959 // `atom_kind_sexp_shape_pins_canonical_atom_payload_shape_for_every_variant`
26960 // (the six atomic-payload arms in isolation) — this test pins
26961 // the OUTER projection that COMPOSES both peer algebras + the
26962 // `Nil` / `List` arms into ONE typed method on the `Sexp`
26963 // algebra.
26964 use crate::error::SexpShape;
26965 assert_eq!(Sexp::Nil.shape(), SexpShape::Nil);
26966 assert_eq!(Sexp::symbol("foo").shape(), SexpShape::Symbol);
26967 assert_eq!(Sexp::keyword("k").shape(), SexpShape::Keyword);
26968 assert_eq!(Sexp::string("s").shape(), SexpShape::String);
26969 assert_eq!(Sexp::int(7).shape(), SexpShape::Int);
26970 assert_eq!(Sexp::float(7.5).shape(), SexpShape::Float);
26971 assert_eq!(Sexp::boolean(true).shape(), SexpShape::Bool);
26972 assert_eq!(Sexp::List(vec![]).shape(), SexpShape::List);
26973 assert_eq!(
26974 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]).shape(),
26975 SexpShape::List,
26976 "non-empty list must project to SexpShape::List — payload count is irrelevant",
26977 );
26978 assert_eq!(Sexp::Quote(Box::new(Sexp::Nil)).shape(), SexpShape::Quote);
26979 assert_eq!(
26980 Sexp::Quasiquote(Box::new(Sexp::Nil)).shape(),
26981 SexpShape::Quasiquote
26982 );
26983 assert_eq!(
26984 Sexp::Unquote(Box::new(Sexp::Nil)).shape(),
26985 SexpShape::Unquote
26986 );
26987 assert_eq!(
26988 Sexp::UnquoteSplice(Box::new(Sexp::Nil)).shape(),
26989 SexpShape::UnquoteSplice
26990 );
26991 }
26992
26993 #[test]
26994 fn sexp_shape_method_agrees_with_domain_sexp_shape_for_every_outer_shape() {
26995 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
26996 // `Sexp::shape()` method agrees with the free-function
26997 // delegate `crate::domain::sexp_shape` for every reachable
26998 // outer shape. Pre-lift the dispatcher lived as a free
26999 // function in `domain.rs`; post-lift the canonical site is
27000 // the inherent method on the `Sexp` algebra and the free
27001 // function is a one-line delegate. Pin that the delegation
27002 // stays byte-for-byte equivalent across every outer arm so a
27003 // regression where the free function drifts from the inherent
27004 // method (or vice versa) surfaces here immediately. Catches
27005 // a future "consolidation" that removes the free function
27006 // without updating the method, or vice versa.
27007 let samples = [
27008 Sexp::Nil,
27009 Sexp::symbol("foo"),
27010 Sexp::keyword("k"),
27011 Sexp::string("s"),
27012 Sexp::int(7),
27013 Sexp::int(-1),
27014 Sexp::float(7.5),
27015 Sexp::float(0.0),
27016 Sexp::boolean(true),
27017 Sexp::boolean(false),
27018 Sexp::List(vec![]),
27019 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
27020 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
27021 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
27022 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
27023 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
27024 ];
27025 for s in &samples {
27026 let via_method = s.shape();
27027 let via_delegate = crate::domain::sexp_shape(s);
27028 assert_eq!(
27029 via_method, via_delegate,
27030 "Sexp::shape and domain::sexp_shape drifted at {s:?}",
27031 );
27032 }
27033 }
27034
27035 #[test]
27036 fn sexp_shape_method_routes_atom_arm_through_atom_kind_sexp_shape_projection() {
27037 // PATH-UNIFORMITY CONTRACT (atomic axis): the lifted
27038 // `Sexp::shape()` routes its Atom arm through
27039 // `Atom::kind().sexp_shape()` — the typed closed-set projection
27040 // on the `AtomKind` algebra. Pin that the composition agrees
27041 // bit-for-bit with the direct `Sexp::shape()` projection across
27042 // every atomic kind variant. A regression in EITHER projection
27043 // direction (an `Atom::kind` arm that swaps markers, or an
27044 // `AtomKind::sexp_shape` arm that drifts its `SexpShape` mapping)
27045 // surfaces here immediately. Sibling shape to
27046 // `sexp_shape_method_routes_quote_family_arms_through_quote_form_sexp_shape_projection`
27047 // for the quote-family axis.
27048 for kind in AtomKind::ALL {
27049 let atom = match kind {
27050 AtomKind::Symbol => Atom::Symbol("name".into()),
27051 AtomKind::Keyword => Atom::Keyword("parent".into()),
27052 AtomKind::Str => Atom::Str("body".into()),
27053 AtomKind::Int => Atom::Int(42),
27054 AtomKind::Float => Atom::Float(1.5),
27055 AtomKind::Bool => Atom::Bool(true),
27056 };
27057 let via_outer = Sexp::Atom(atom.clone()).shape();
27058 let via_composed = atom.kind().sexp_shape();
27059 assert_eq!(
27060 via_outer, via_composed,
27061 "Sexp::shape's Atom arm drifted from Atom::kind().sexp_shape() at {kind:?}",
27062 );
27063 }
27064 }
27065
27066 #[test]
27067 fn sexp_shape_method_routes_quote_family_arms_through_quote_form_sexp_shape_projection() {
27068 // PATH-UNIFORMITY CONTRACT (quote-family axis): the lifted
27069 // `Sexp::shape()` routes its four quote-family arms through
27070 // `as_quote_form() + QuoteForm::sexp_shape()`. Pin that the
27071 // composition agrees bit-for-bit with the direct `Sexp::shape()`
27072 // projection across every quote-family wrapper variant. A
27073 // regression in EITHER projection direction (an `as_quote_form`
27074 // arm that swaps markers, or a `QuoteForm::sexp_shape` arm that
27075 // drifts its `SexpShape` mapping) surfaces here immediately.
27076 // Mirrors the atomic-axis test
27077 // `sexp_shape_method_routes_atom_arm_through_atom_kind_sexp_shape_projection`.
27078 let samples = [
27079 (
27080 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
27081 QuoteForm::Quote,
27082 ),
27083 (
27084 Sexp::Quasiquote(Box::new(Sexp::symbol("payload"))),
27085 QuoteForm::Quasiquote,
27086 ),
27087 (
27088 Sexp::Unquote(Box::new(Sexp::symbol("payload"))),
27089 QuoteForm::Unquote,
27090 ),
27091 (
27092 Sexp::UnquoteSplice(Box::new(Sexp::symbol("payload"))),
27093 QuoteForm::UnquoteSplice,
27094 ),
27095 ];
27096 for (sexp, expected_qf) in &samples {
27097 let via_outer = sexp.shape();
27098 let (qf, _) = sexp
27099 .as_quote_form()
27100 .expect("quote-family sample must project through as_quote_form");
27101 assert_eq!(
27102 qf, *expected_qf,
27103 "as_quote_form drifted typed marker at {sexp:?}"
27104 );
27105 let via_composed = qf.sexp_shape();
27106 assert_eq!(
27107 via_outer, via_composed,
27108 "Sexp::shape drifted from as_quote_form + QuoteForm::sexp_shape at {sexp:?}"
27109 );
27110 }
27111 }
27112
27113 #[test]
27114 fn sexp_shape_method_routes_structural_arms_through_structural_kind_sexp_shape_projection() {
27115 // PATH-UNIFORMITY CONTRACT (structural-residual axis): the
27116 // lifted `Sexp::shape()` routes its two structural-residual
27117 // arms (Nil, List) through `StructuralKind::sexp_shape()`. Pin
27118 // that the composition agrees bit-for-bit with the direct
27119 // `Sexp::shape()` projection across the two structural-residual
27120 // variants. A regression that drifts EITHER projection direction
27121 // (a `Sexp::shape` arm that inlines `SexpShape::Nil` /
27122 // `SexpShape::List` back as a raw literal, or a
27123 // `StructuralKind::sexp_shape` arm that drifts its `SexpShape`
27124 // mapping) surfaces here immediately. Sibling-shape pin to the
27125 // atomic-axis routing test
27126 // `sexp_shape_method_routes_atom_arm_through_atom_kind_sexp_shape_projection`
27127 // and the quote-family-axis routing test
27128 // `sexp_shape_method_routes_quote_family_arms_through_quote_form_sexp_shape_projection`
27129 // — together the three tests pin ALL THREE closed-set
27130 // carving-marker `sexp_shape` compositions the lifted
27131 // `Sexp::shape()` body owns.
27132 let samples = [
27133 (Sexp::Nil, StructuralKind::Nil),
27134 (Sexp::List(vec![]), StructuralKind::List),
27135 (Sexp::List(vec![Sexp::symbol("a")]), StructuralKind::List),
27136 ];
27137 for (sexp, expected_sk) in &samples {
27138 let via_outer = sexp.shape();
27139 let sk = sexp
27140 .as_structural_kind()
27141 .expect("structural-residual sample must project through as_structural_kind");
27142 assert_eq!(
27143 sk, *expected_sk,
27144 "as_structural_kind drifted typed marker at {sexp:?}"
27145 );
27146 let via_composed = sk.sexp_shape();
27147 assert_eq!(
27148 via_outer, via_composed,
27149 "Sexp::shape drifted from as_structural_kind + StructuralKind::sexp_shape at {sexp:?}"
27150 );
27151 }
27152 }
27153
27154 #[test]
27155 fn sexp_as_structural_kind_projects_nil_and_list_to_canonical_structural_kind() {
27156 // PER-ARM CONTRACT: pin that `Sexp::as_structural_kind()`
27157 // projects `Sexp::Nil` to `Some(StructuralKind::Nil)` and
27158 // `Sexp::List(_)` to `Some(StructuralKind::List)` — the two
27159 // structural-residual arms of the `Sexp` algebra. A regression
27160 // that swaps the two arms (routes `Nil` to `Some(List)` or
27161 // vice versa), returns `None` for either, or projects to a
27162 // wrong `StructuralKind` variant surfaces here immediately.
27163 // The List arm is exercised with an empty AND a non-empty
27164 // items slice so a body that gates on `items.is_empty()`
27165 // (rather than the outer arm) fails loudly.
27166 assert_eq!(Sexp::Nil.as_structural_kind(), Some(StructuralKind::Nil));
27167 assert_eq!(
27168 Sexp::List(vec![]).as_structural_kind(),
27169 Some(StructuralKind::List)
27170 );
27171 assert_eq!(
27172 Sexp::List(vec![Sexp::symbol("a")]).as_structural_kind(),
27173 Some(StructuralKind::List)
27174 );
27175 assert_eq!(
27176 Sexp::List(vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)]).as_structural_kind(),
27177 Some(StructuralKind::List)
27178 );
27179 }
27180
27181 #[test]
27182 fn sexp_as_structural_kind_rejects_non_structural_outer_shapes() {
27183 // KERNEL CONTRACT: pin that `Sexp::as_structural_kind()`
27184 // returns `None` for every non-structural outer shape — every
27185 // `Sexp::Atom` variant (the atomic-payload carving) AND every
27186 // quote-family wrapper (the quote-family carving). Sweeps
27187 // every non-residual arm so a regression that accepts an atom
27188 // (e.g. routes `Sexp::Atom(_)` to `Some(List)` because the
27189 // outer arm is misread as a "container" of an atomic payload)
27190 // or a quote-family wrapper (e.g. routes `Sexp::Quote(_)`
27191 // through `_ => Some(_)` because the residual match falls
27192 // through) fails loudly. Sibling-cohort sweep to
27193 // `sexp_as_atom_projects_inner_atom_iff_outer_is_atom_variant`
27194 // — that test pins the atomic-projection kernel, this one
27195 // pins the structural-residual kernel.
27196 for outer in [
27197 Sexp::symbol("foo"),
27198 Sexp::keyword("k"),
27199 Sexp::string("s"),
27200 Sexp::int(7),
27201 Sexp::float(7.5),
27202 Sexp::boolean(true),
27203 Sexp::Quote(Box::new(Sexp::symbol("x"))),
27204 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
27205 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
27206 Sexp::UnquoteSplice(Box::new(Sexp::symbol("x"))),
27207 ] {
27208 assert_eq!(
27209 outer.as_structural_kind(),
27210 None,
27211 "Sexp::as_structural_kind must reject non-structural outer shape {outer:?}",
27212 );
27213 }
27214 }
27215
27216 #[test]
27217 fn sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant() {
27218 // COMPOSITION-LAW CONTRACT: `s.as_structural_kind() ==
27219 // s.shape().as_structural_kind()` for every reachable Sexp
27220 // outer shape. The value-level projection and the shape-level
27221 // projection MUST agree bit-for-bit — the substrate's
27222 // (Sexp value, StructuralKind marker) pairing binds at TWO
27223 // typed methods (one on `Sexp`, one on `SexpShape`) that must
27224 // stay in lockstep. Sweeps every outer shape (residual + atom
27225 // + quote-family) so a drift on ANY arm surfaces immediately.
27226 // Sibling-shape pin to the (Sexp → SexpShape → label) path-
27227 // uniformity test
27228 // `sexp_shape_method_label_composes_with_sexp_type_name_for_every_outer_shape`
27229 // — where that test pins the label-projection composition,
27230 // this one pins the structural-carving-marker projection
27231 // composition.
27232 let samples = [
27233 Sexp::Nil,
27234 Sexp::List(vec![]),
27235 Sexp::List(vec![Sexp::symbol("a")]),
27236 Sexp::symbol("foo"),
27237 Sexp::keyword("k"),
27238 Sexp::string("s"),
27239 Sexp::int(7),
27240 Sexp::float(7.5),
27241 Sexp::boolean(true),
27242 Sexp::Quote(Box::new(Sexp::Nil)),
27243 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27244 Sexp::Unquote(Box::new(Sexp::Nil)),
27245 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27246 ];
27247 for s in &samples {
27248 assert_eq!(
27249 s.as_structural_kind(),
27250 s.shape().as_structural_kind(),
27251 "Sexp::as_structural_kind and Sexp::shape().as_structural_kind must agree at {s:?}",
27252 );
27253 }
27254 }
27255
27256 #[test]
27257 fn sexp_as_structural_kind_partitions_outer_shapes_jointly_with_as_atom_and_as_quote_form() {
27258 // PARTITION-TOTAL CONTRACT (value-level): pin that for every
27259 // reachable Sexp outer shape, EXACTLY ONE of `as_atom`,
27260 // `as_quote_form`, `as_structural_kind` returns `Some(_)`.
27261 // Post-lift the three carving-marker projections at the value
27262 // level form a partition of the `Sexp` variant algebra —
27263 // symmetric with the partition-total invariant pinned at the
27264 // shape level by
27265 // `sexp_shape_partition_is_total_across_atom_quote_structural_carvings`
27266 // (in `error.rs`). A regression that drifts any carving's
27267 // membership (an `as_atom` arm that accepts a non-atom, an
27268 // `as_quote_form` arm that misses a quote-family wrapper, an
27269 // `as_structural_kind` arm that swaps its Nil/List
27270 // membership) surfaces here immediately, so the value-level
27271 // partition invariant is a TYPED THEOREM (rustc-enforced
27272 // exhaustiveness through the joint sweep) rather than a
27273 // runtime `matches!` assertion.
27274 let samples = [
27275 Sexp::Nil,
27276 Sexp::List(vec![]),
27277 Sexp::List(vec![Sexp::symbol("a")]),
27278 Sexp::symbol("foo"),
27279 Sexp::keyword("k"),
27280 Sexp::string("s"),
27281 Sexp::int(7),
27282 Sexp::float(7.5),
27283 Sexp::boolean(true),
27284 Sexp::Quote(Box::new(Sexp::Nil)),
27285 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27286 Sexp::Unquote(Box::new(Sexp::Nil)),
27287 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27288 ];
27289 for s in &samples {
27290 let hits = [
27291 s.as_atom().is_some(),
27292 s.as_quote_form().is_some(),
27293 s.as_structural_kind().is_some(),
27294 ];
27295 let hit_count: usize = hits.iter().filter(|b| **b).count();
27296 assert_eq!(
27297 hit_count, 1,
27298 "value-level carvings must partition Sexp variants — {s:?} matched {hit_count} carvings (as_atom/as_quote_form/as_structural_kind = {hits:?})",
27299 );
27300 }
27301 }
27302
27303 #[test]
27304 fn sexp_as_structural_kind_composes_with_label_via_structural_kind_label() {
27305 // CROSS-PROJECTION COHERENCE: pin that
27306 // `s.as_structural_kind().map(StructuralKind::label)` agrees
27307 // with `s.shape().label()` for every residual-carving Sexp
27308 // (and returns `None` for every non-residual Sexp). Composes
27309 // the new value-level projection with the closed-set
27310 // `StructuralKind::label` projection (which itself composes
27311 // through `sexp_shape().label()`) so the label vocabulary
27312 // stays load-bearing at ONE canonical site
27313 // (`SexpShape::label`) rather than a parallel per-projection
27314 // literal table.
27315 let residual = [
27316 (Sexp::Nil, "nil"),
27317 (Sexp::List(vec![]), "list"),
27318 (Sexp::List(vec![Sexp::symbol("a")]), "list"),
27319 ];
27320 for (sexp, expected_label) in &residual {
27321 let via_carving = sexp.as_structural_kind().map(StructuralKind::label);
27322 assert_eq!(
27323 via_carving,
27324 Some(*expected_label),
27325 "structural-carving-marker label drifted at {sexp:?}"
27326 );
27327 assert_eq!(
27328 via_carving,
27329 Some(sexp.shape().label()),
27330 "as_structural_kind.map(label) must equal shape().label() for residual sample {sexp:?}"
27331 );
27332 }
27333 for non_residual in [
27334 Sexp::symbol("foo"),
27335 Sexp::keyword("k"),
27336 Sexp::string("s"),
27337 Sexp::int(7),
27338 Sexp::float(7.5),
27339 Sexp::boolean(true),
27340 Sexp::Quote(Box::new(Sexp::Nil)),
27341 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27342 Sexp::Unquote(Box::new(Sexp::Nil)),
27343 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27344 ] {
27345 assert_eq!(
27346 non_residual.as_structural_kind().map(StructuralKind::label),
27347 None,
27348 "non-residual Sexp must project to None on as_structural_kind.map(label) — {non_residual:?}"
27349 );
27350 }
27351 }
27352
27353 #[test]
27354 fn sexp_as_atom_kind_projects_each_atom_variant_to_canonical_atom_kind() {
27355 // PER-VARIANT TRUTH-TABLE (atomic axis): pin byte-for-byte per-
27356 // Sexp-atom-arm mapping — Symbol payload → Some(AtomKind::Symbol),
27357 // Keyword payload → Some(AtomKind::Keyword), Str payload →
27358 // Some(AtomKind::Str), Int payload → Some(AtomKind::Int), Float
27359 // payload → Some(AtomKind::Float), Bool payload →
27360 // Some(AtomKind::Bool). Value-level peer of the shape-level
27361 // sweep `as_atom_kind_projects_each_atom_shape_to_canonical_atom_kind_and_rejects_non_atom_shapes`
27362 // in error.rs — each atomic Sexp value's carving-marker
27363 // projection must land on the matching AtomKind arm the shape-
27364 // level projection lands on. A future thirteenth Atom variant
27365 // extends both this sweep + the composition body via the
27366 // as_atom + Atom::kind primitives, with rustc enforcing the
27367 // match arms in lockstep.
27368 assert_eq!(Sexp::symbol("foo").as_atom_kind(), Some(AtomKind::Symbol));
27369 assert_eq!(Sexp::keyword("k").as_atom_kind(), Some(AtomKind::Keyword));
27370 assert_eq!(Sexp::string("s").as_atom_kind(), Some(AtomKind::Str));
27371 assert_eq!(Sexp::int(7).as_atom_kind(), Some(AtomKind::Int));
27372 assert_eq!(Sexp::float(7.5).as_atom_kind(), Some(AtomKind::Float));
27373 assert_eq!(Sexp::boolean(true).as_atom_kind(), Some(AtomKind::Bool));
27374 // Empty-payload edge cases (empty-string vs Symbol vs Keyword)
27375 // — pin the projection ignores payload content entirely (it
27376 // reads only the outer variant discriminant), so a body that
27377 // gates on payload emptiness fails loudly.
27378 assert_eq!(Sexp::symbol("").as_atom_kind(), Some(AtomKind::Symbol));
27379 assert_eq!(Sexp::string("").as_atom_kind(), Some(AtomKind::Str));
27380 }
27381
27382 #[test]
27383 fn sexp_as_atom_kind_rejects_non_atom_outer_shapes() {
27384 // KERNEL: every non-atom outer shape (Nil, List, every quote-
27385 // family wrapper) projects to `None`. Sibling kernel-pin to
27386 // `sexp_as_structural_kind_rejects_non_structural_outer_shapes`
27387 // on the residual axis. Together the two kernel pins bracket
27388 // the atomic-carving membership from BOTH sides of the
27389 // partition — the atomic-arm membership from
27390 // `sexp_as_atom_kind_projects_each_atom_variant_to_canonical_atom_kind`
27391 // and the non-atomic-arm kernel from THIS test.
27392 for non_atom in [
27393 Sexp::Nil,
27394 Sexp::List(vec![]),
27395 Sexp::List(vec![Sexp::symbol("a")]),
27396 Sexp::Quote(Box::new(Sexp::Nil)),
27397 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27398 Sexp::Unquote(Box::new(Sexp::Nil)),
27399 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27400 ] {
27401 assert_eq!(
27402 non_atom.as_atom_kind(),
27403 None,
27404 "non-atom Sexp must project to None on as_atom_kind — {non_atom:?}"
27405 );
27406 }
27407 }
27408
27409 #[test]
27410 fn sexp_as_atom_kind_agrees_with_as_atom_map_kind_for_every_variant() {
27411 // COMPOSITION-LAW CONTRACT (atomic-axis peer of the shape-
27412 // agreement law): `s.as_atom_kind() == s.as_atom().map(Atom::kind)`
27413 // for every reachable Sexp outer shape. Pre-lift the atomic
27414 // carving marker at the value level was reachable via this
27415 // two-step composition through the Atom algebra; post-lift the
27416 // new projection MUST agree bit-for-bit — the substrate's
27417 // (Sexp value, AtomKind marker) pairing binds at TWO
27418 // compositions (this Atom-axis composition AND the shape-axis
27419 // composition pinned by the sibling test below) that must stay
27420 // in lockstep. Sweeps every outer shape (atom + residual +
27421 // quote-family) so a drift on ANY arm surfaces immediately.
27422 let samples = [
27423 Sexp::Nil,
27424 Sexp::List(vec![]),
27425 Sexp::List(vec![Sexp::symbol("a")]),
27426 Sexp::symbol("foo"),
27427 Sexp::keyword("k"),
27428 Sexp::string("s"),
27429 Sexp::int(7),
27430 Sexp::float(7.5),
27431 Sexp::boolean(true),
27432 Sexp::Quote(Box::new(Sexp::Nil)),
27433 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27434 Sexp::Unquote(Box::new(Sexp::Nil)),
27435 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27436 ];
27437 for s in &samples {
27438 assert_eq!(
27439 s.as_atom_kind(),
27440 s.as_atom().map(Atom::kind),
27441 "Sexp::as_atom_kind and Sexp::as_atom().map(Atom::kind) must agree at {s:?}",
27442 );
27443 }
27444 }
27445
27446 #[test]
27447 fn sexp_as_atom_kind_agrees_with_shape_as_atom_kind_for_every_variant() {
27448 // COMPOSITION-LAW CONTRACT (shape-axis peer): `s.as_atom_kind()
27449 // == s.shape().as_atom_kind()` for every reachable Sexp outer
27450 // shape. Sibling to
27451 // `sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant`
27452 // on the atomic axis. Pre-lift the atomic carving marker at
27453 // the value level was reachable via this two-step composition
27454 // through the shape algebra; post-lift the new projection MUST
27455 // agree bit-for-bit — the substrate's (Sexp value, AtomKind
27456 // marker) pairing binds at THREE typed methods (Sexp::as_atom_kind,
27457 // Sexp::as_atom + Atom::kind composition, Sexp::shape +
27458 // SexpShape::as_atom_kind composition) that must ALL stay in
27459 // lockstep.
27460 let samples = [
27461 Sexp::Nil,
27462 Sexp::List(vec![]),
27463 Sexp::List(vec![Sexp::symbol("a")]),
27464 Sexp::symbol("foo"),
27465 Sexp::keyword("k"),
27466 Sexp::string("s"),
27467 Sexp::int(7),
27468 Sexp::float(7.5),
27469 Sexp::boolean(true),
27470 Sexp::Quote(Box::new(Sexp::Nil)),
27471 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27472 Sexp::Unquote(Box::new(Sexp::Nil)),
27473 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27474 ];
27475 for s in &samples {
27476 assert_eq!(
27477 s.as_atom_kind(),
27478 s.shape().as_atom_kind(),
27479 "Sexp::as_atom_kind and Sexp::shape().as_atom_kind must agree at {s:?}",
27480 );
27481 }
27482 }
27483
27484 #[test]
27485 fn sexp_as_atom_kind_partitions_outer_shapes_jointly_with_as_quote_form_and_as_structural_kind()
27486 {
27487 // PARTITION-TOTAL CONTRACT (value-level, marker-only axis):
27488 // pin that for every reachable Sexp outer shape, EXACTLY ONE
27489 // of `as_atom_kind`, `as_quote_form`, `as_structural_kind`
27490 // returns `Some(_)`. Post-lift ALL THREE carving-marker
27491 // projections at the value level form a partition of the
27492 // `Sexp` variant algebra using ONLY the marker-only siblings —
27493 // symmetric with the shape-level partition-total invariant
27494 // pinned by
27495 // `sexp_shape_partition_is_total_across_atom_quote_structural_carvings`
27496 // (in error.rs). The pre-existing value-level partition pin
27497 // `sexp_as_structural_kind_partitions_outer_shapes_jointly_with_as_atom_and_as_quote_form`
27498 // uses `as_atom().is_some()` on the atomic axis (the
27499 // structural-lift projection); THIS pin uses `as_atom_kind()
27500 // .is_some()` (the marker-only projection). Both partition
27501 // invariants must hold — they pin the atomic axis's TWO
27502 // value-level projections (structural + marker) as jointly
27503 // partition-consistent with the residual and quote-family
27504 // siblings. A regression that drifts any carving's
27505 // marker-only membership (an `as_atom_kind` arm that accepts
27506 // a non-atom, an `as_quote_form` arm that misses a quote-
27507 // family wrapper, an `as_structural_kind` arm that swaps its
27508 // Nil/List membership) surfaces here immediately.
27509 let samples = [
27510 Sexp::Nil,
27511 Sexp::List(vec![]),
27512 Sexp::List(vec![Sexp::symbol("a")]),
27513 Sexp::symbol("foo"),
27514 Sexp::keyword("k"),
27515 Sexp::string("s"),
27516 Sexp::int(7),
27517 Sexp::float(7.5),
27518 Sexp::boolean(true),
27519 Sexp::Quote(Box::new(Sexp::Nil)),
27520 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27521 Sexp::Unquote(Box::new(Sexp::Nil)),
27522 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27523 ];
27524 for s in &samples {
27525 let hits = [
27526 s.as_atom_kind().is_some(),
27527 s.as_quote_form().is_some(),
27528 s.as_structural_kind().is_some(),
27529 ];
27530 let hit_count: usize = hits.iter().filter(|b| **b).count();
27531 assert_eq!(
27532 hit_count, 1,
27533 "value-level marker-only carvings must partition Sexp variants — {s:?} matched {hit_count} carvings (as_atom_kind/as_quote_form/as_structural_kind = {hits:?})",
27534 );
27535 }
27536 }
27537
27538 #[test]
27539 fn sexp_as_atom_kind_composes_with_label_via_atom_kind_label() {
27540 // CROSS-PROJECTION COHERENCE: pin that
27541 // `s.as_atom_kind().map(AtomKind::label)` agrees with
27542 // `s.shape().label()` for every atomic Sexp (and returns
27543 // `None` for every non-atomic Sexp). Sibling to
27544 // `sexp_as_structural_kind_composes_with_label_via_structural_kind_label`
27545 // on the atomic axis. Composes the new value-level marker
27546 // projection with the closed-set `AtomKind::label` projection
27547 // (which itself composes through `sexp_shape().label()`) so
27548 // the label vocabulary stays load-bearing at ONE canonical
27549 // site (`SexpShape::label`) rather than a parallel per-
27550 // projection literal table.
27551 let atomic = [
27552 (Sexp::symbol("foo"), "symbol"),
27553 (Sexp::keyword("k"), "keyword"),
27554 (Sexp::string("s"), "string"),
27555 (Sexp::int(7), "int"),
27556 (Sexp::float(7.5), "float"),
27557 (Sexp::boolean(true), "bool"),
27558 ];
27559 for (sexp, expected_label) in &atomic {
27560 let via_carving = sexp.as_atom_kind().map(AtomKind::label);
27561 assert_eq!(
27562 via_carving,
27563 Some(*expected_label),
27564 "atomic-carving-marker label drifted at {sexp:?}"
27565 );
27566 assert_eq!(
27567 via_carving,
27568 Some(sexp.shape().label()),
27569 "as_atom_kind.map(label) must equal shape().label() for atomic sample {sexp:?}"
27570 );
27571 }
27572 for non_atomic in [
27573 Sexp::Nil,
27574 Sexp::List(vec![]),
27575 Sexp::List(vec![Sexp::symbol("a")]),
27576 Sexp::Quote(Box::new(Sexp::Nil)),
27577 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27578 Sexp::Unquote(Box::new(Sexp::Nil)),
27579 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27580 ] {
27581 assert_eq!(
27582 non_atomic.as_atom_kind().map(AtomKind::label),
27583 None,
27584 "non-atomic Sexp must project to None on as_atom_kind.map(label) — {non_atomic:?}"
27585 );
27586 }
27587 }
27588
27589 #[test]
27590 fn sexp_as_unquote_form_projects_each_variant_to_canonical_unquote_form() {
27591 // PER-VARIANT TRUTH-TABLE (unquote-subset axis): pin byte-for-
27592 // byte per-Sexp-substitution-arm mapping — `Sexp::Unquote(inner)`
27593 // → `Some(UnquoteForm::Unquote)`, `Sexp::UnquoteSplice(inner)`
27594 // → `Some(UnquoteForm::Splice)`. Value-level peer of the shape-
27595 // level sweep
27596 // `as_unquote_form_projects_each_unquote_shape_to_canonical_unquote_form_and_rejects_non_unquote_shapes`
27597 // in error.rs — each substitution-wrapper Sexp value's carving-
27598 // marker projection must land on the matching UnquoteForm arm
27599 // the shape-level projection lands on. A future third UnquoteForm
27600 // variant (e.g. `,~` reverse-unquote) extends both this sweep +
27601 // the composition body via the as_unquote + QuoteForm::as_unquote_form
27602 // primitives, with rustc enforcing the match arms in lockstep.
27603 assert_eq!(
27604 Sexp::Unquote(Box::new(Sexp::symbol("x"))).as_unquote_form(),
27605 Some(UnquoteForm::Unquote)
27606 );
27607 assert_eq!(
27608 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))).as_unquote_form(),
27609 Some(UnquoteForm::Splice)
27610 );
27611 // Inner-payload invariance edge cases — pin the projection
27612 // ignores inner payload content entirely (it reads only the
27613 // outer wrapper variant discriminant), so a body that gates on
27614 // inner payload shape fails loudly.
27615 assert_eq!(
27616 Sexp::Unquote(Box::new(Sexp::Nil)).as_unquote_form(),
27617 Some(UnquoteForm::Unquote)
27618 );
27619 assert_eq!(
27620 Sexp::UnquoteSplice(Box::new(Sexp::List(vec![]))).as_unquote_form(),
27621 Some(UnquoteForm::Splice)
27622 );
27623 assert_eq!(
27624 Sexp::Unquote(Box::new(Sexp::List(vec![
27625 Sexp::symbol("nested"),
27626 Sexp::int(42),
27627 ])))
27628 .as_unquote_form(),
27629 Some(UnquoteForm::Unquote)
27630 );
27631 }
27632
27633 #[test]
27634 fn sexp_as_unquote_form_rejects_non_unquote_subset_outer_shapes() {
27635 // KERNEL: every non-unquote-subset outer shape (Nil, every Atom
27636 // variant, List, AND the two non-substitution quote-family
27637 // wrappers `Sexp::Quote` and `Sexp::Quasiquote`) projects to
27638 // `None`. Sibling kernel-pin to
27639 // `sexp_as_atom_kind_rejects_non_atom_outer_shapes` and
27640 // `sexp_as_structural_kind_rejects_non_structural_outer_shapes`
27641 // on the substitution axis. The two non-substitution quote-
27642 // family wrappers ARE quote-family (`as_quote_form` accepts
27643 // them) but NOT substitution-subset (`as_unquote_form` must
27644 // reject them) — pin the 2-of-4 subset gate operates at the
27645 // value level exactly as the shape-level
27646 // `QuoteForm::as_unquote_form` gate operates on the closed-set
27647 // marker enum.
27648 for non_unquote in [
27649 Sexp::Nil,
27650 Sexp::symbol("foo"),
27651 Sexp::keyword("k"),
27652 Sexp::string("s"),
27653 Sexp::int(7),
27654 Sexp::float(7.5),
27655 Sexp::boolean(true),
27656 Sexp::List(vec![]),
27657 Sexp::List(vec![Sexp::symbol("a")]),
27658 Sexp::Quote(Box::new(Sexp::Nil)),
27659 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27660 ] {
27661 assert_eq!(
27662 non_unquote.as_unquote_form(),
27663 None,
27664 "non-substitution-subset Sexp must project to None on as_unquote_form — {non_unquote:?}"
27665 );
27666 }
27667 }
27668
27669 #[test]
27670 fn sexp_as_unquote_form_agrees_with_as_unquote_map_marker_for_every_variant() {
27671 // COMPOSITION-LAW CONTRACT (parent-projection peer): pin
27672 // `s.as_unquote_form() == s.as_unquote().map(|(uf, _)| uf)` for
27673 // every reachable Sexp outer shape. Pre-lift the substitution
27674 // carving marker at the value level was reachable via this
27675 // two-step composition through the parent [`Sexp::as_unquote`]
27676 // projection (discarding the wrapped inner); post-lift the new
27677 // marker-only projection MUST agree bit-for-bit — the
27678 // substrate's (Sexp value, UnquoteForm marker) pairing binds at
27679 // FOUR compositions (this parent-projection composition AND the
27680 // shape-axis composition AND the quote-family + subset-gate
27681 // composition, all pinned by the sibling tests below) that must
27682 // stay in lockstep. Sweeps every outer shape (atom + residual +
27683 // quote-family) so a drift on ANY arm surfaces immediately.
27684 let samples = [
27685 Sexp::Nil,
27686 Sexp::List(vec![]),
27687 Sexp::List(vec![Sexp::symbol("a")]),
27688 Sexp::symbol("foo"),
27689 Sexp::keyword("k"),
27690 Sexp::string("s"),
27691 Sexp::int(7),
27692 Sexp::float(7.5),
27693 Sexp::boolean(true),
27694 Sexp::Quote(Box::new(Sexp::Nil)),
27695 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27696 Sexp::Unquote(Box::new(Sexp::Nil)),
27697 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27698 ];
27699 for s in &samples {
27700 assert_eq!(
27701 s.as_unquote_form(),
27702 s.as_unquote().map(|(uf, _)| uf),
27703 "Sexp::as_unquote_form and Sexp::as_unquote().map(|(uf, _)| uf) must agree at {s:?}",
27704 );
27705 }
27706 }
27707
27708 #[test]
27709 fn sexp_as_unquote_form_agrees_with_shape_as_unquote_form_for_every_variant() {
27710 // COMPOSITION-LAW CONTRACT (shape-axis peer): `s.as_unquote_form()
27711 // == s.shape().as_unquote_form()` for every reachable Sexp outer
27712 // shape. Sibling to
27713 // `sexp_as_atom_kind_agrees_with_shape_as_atom_kind_for_every_variant`
27714 // and
27715 // `sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant`
27716 // on the substitution axis. Pre-lift the substitution carving
27717 // marker at the value level was reachable via this two-step
27718 // composition through the shape algebra; post-lift the new
27719 // projection MUST agree bit-for-bit — the substrate's (Sexp
27720 // value, UnquoteForm marker) pairing binds at FOUR typed methods
27721 // (Sexp::as_unquote_form, Sexp::as_unquote + `|(uf, _)| uf`
27722 // composition, Sexp::shape + SexpShape::as_unquote_form
27723 // composition, Sexp::as_quote_form +
27724 // QuoteForm::as_unquote_form composition) that must ALL stay
27725 // in lockstep.
27726 let samples = [
27727 Sexp::Nil,
27728 Sexp::List(vec![]),
27729 Sexp::List(vec![Sexp::symbol("a")]),
27730 Sexp::symbol("foo"),
27731 Sexp::keyword("k"),
27732 Sexp::string("s"),
27733 Sexp::int(7),
27734 Sexp::float(7.5),
27735 Sexp::boolean(true),
27736 Sexp::Quote(Box::new(Sexp::Nil)),
27737 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27738 Sexp::Unquote(Box::new(Sexp::Nil)),
27739 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27740 ];
27741 for s in &samples {
27742 assert_eq!(
27743 s.as_unquote_form(),
27744 s.shape().as_unquote_form(),
27745 "Sexp::as_unquote_form and Sexp::shape().as_unquote_form must agree at {s:?}",
27746 );
27747 }
27748 }
27749
27750 #[test]
27751 fn sexp_as_unquote_form_agrees_with_as_quote_form_and_quote_form_as_unquote_form_for_every_variant(
27752 ) {
27753 // COMPOSITION-LAW CONTRACT (parent-family + subset-gate peer):
27754 // pin `s.as_unquote_form() ==
27755 // s.as_quote_form().and_then(|(qf, _)| qf.as_unquote_form())`
27756 // for every reachable Sexp outer shape. Value-level peer of the
27757 // shape-level route
27758 // `as_unquote_form_routes_through_as_quote_form_and_quote_form_as_unquote_form_via_composition`
27759 // in error.rs — where that test pins the shape-level
27760 // `SexpShape::as_unquote_form` routes through the shape-level
27761 // `SexpShape::as_quote_form` + `QuoteForm::as_unquote_form`
27762 // subset gate, THIS test pins the value-level
27763 // `Sexp::as_unquote_form` routes through the value-level
27764 // `Sexp::as_quote_form` + the SAME subset gate. Pre-lift the
27765 // substitution carving marker at the value level was reachable
27766 // via this three-step composition through the parent quote-
27767 // family projection [`Sexp::as_quote_form`] composed with the
27768 // 2-of-4 subset gate [`QuoteForm::as_unquote_form`]; post-lift
27769 // the new marker-only projection MUST agree bit-for-bit — the
27770 // subset-gate composition (which the pre-existing
27771 // [`Sexp::as_unquote`] projection ALSO routes through, per its
27772 // body `let (qf, inner) = self.as_quote_form()?;
27773 // qf.as_unquote_form().map(|uf| (uf, inner))`) must land the
27774 // same marker as the new value-level projection.
27775 let samples = [
27776 Sexp::Nil,
27777 Sexp::List(vec![]),
27778 Sexp::List(vec![Sexp::symbol("a")]),
27779 Sexp::symbol("foo"),
27780 Sexp::keyword("k"),
27781 Sexp::string("s"),
27782 Sexp::int(7),
27783 Sexp::float(7.5),
27784 Sexp::boolean(true),
27785 Sexp::Quote(Box::new(Sexp::Nil)),
27786 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27787 Sexp::Unquote(Box::new(Sexp::Nil)),
27788 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27789 ];
27790 for s in &samples {
27791 assert_eq!(
27792 s.as_unquote_form(),
27793 s.as_quote_form().and_then(|(qf, _)| qf.as_unquote_form()),
27794 "Sexp::as_unquote_form and Sexp::as_quote_form().and_then(|(qf, _)| qf.as_unquote_form()) must agree at {s:?}",
27795 );
27796 }
27797 }
27798
27799 #[test]
27800 fn sexp_as_unquote_form_composes_with_marker_via_unquote_form_marker() {
27801 // CROSS-PROJECTION COHERENCE: pin that
27802 // `s.as_unquote_form().map(UnquoteForm::marker)` agrees with
27803 // `s.shape().label()` for every substitution-subset Sexp (and
27804 // returns `None` for every non-substitution-subset Sexp).
27805 // Sibling to `sexp_as_atom_kind_composes_with_label_via_atom_kind_label`
27806 // on the substitution axis. Composes the new value-level
27807 // marker projection with the closed-set `UnquoteForm::marker`
27808 // projection (which itself composes through
27809 // `to_quote_form().prefix()` — see `UnquoteForm::marker`'s
27810 // docstring for the composition route) so the marker vocabulary
27811 // (`","` / `",@"`) stays load-bearing at ONE canonical site
27812 // (`QuoteForm::prefix`'s Unquote/UnquoteSplice arms) rather
27813 // than a parallel per-projection literal table.
27814 //
27815 // Note: `UnquoteForm::marker` returns the READER prefix (`,` or
27816 // `,@`) which is ALSO the canonical `SexpShape::label` for the
27817 // Unquote / UnquoteSplice arms — the shape-label vocabulary
27818 // was pinned to the reader-prefix vocabulary in the
27819 // `SexpShape::label` truth-table (Unquote → "unquote",
27820 // UnquoteSplice → "unquote-splice"). This test uses
27821 // `UnquoteForm::marker` = reader prefix directly (`,` /
27822 // `,@`), NOT the shape label — the two are distinct
27823 // vocabularies, both derived from the closed-set carving
27824 // marker, both stable across the lift.
27825 let substitution = [
27826 (
27827 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
27828 UnquoteForm::Unquote,
27829 ),
27830 (
27831 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
27832 UnquoteForm::Splice,
27833 ),
27834 ];
27835 for (sexp, expected_uf) in &substitution {
27836 let via_carving = sexp.as_unquote_form().map(UnquoteForm::marker);
27837 assert_eq!(
27838 via_carving,
27839 Some(expected_uf.marker()),
27840 "substitution-carving-marker string drifted at {sexp:?}"
27841 );
27842 }
27843 for non_substitution in [
27844 Sexp::Nil,
27845 Sexp::symbol("foo"),
27846 Sexp::keyword("k"),
27847 Sexp::string("s"),
27848 Sexp::int(7),
27849 Sexp::float(7.5),
27850 Sexp::boolean(true),
27851 Sexp::List(vec![]),
27852 Sexp::List(vec![Sexp::symbol("a")]),
27853 Sexp::Quote(Box::new(Sexp::Nil)),
27854 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27855 ] {
27856 assert_eq!(
27857 non_substitution.as_unquote_form().map(UnquoteForm::marker),
27858 None,
27859 "non-substitution-subset Sexp must project to None on as_unquote_form.map(marker) — {non_substitution:?}"
27860 );
27861 }
27862 }
27863
27864 #[test]
27865 fn sexp_as_unquote_form_narrows_as_quote_form_to_substitution_subset() {
27866 // SUBSET-GATE CONTRACT (value-level): pin that at every
27867 // reachable Sexp outer shape, `as_unquote_form().is_some()`
27868 // implies `as_quote_form().is_some()` (subset containment) AND
27869 // `as_quote_form().is_some() && !as_unquote_form().is_some()`
27870 // holds exactly for the two non-substitution quote-family
27871 // wrappers (`Sexp::Quote` and `Sexp::Quasiquote`) — the 2-of-4
27872 // subset gate at the VALUE level, symmetric with the shape-
27873 // level subset gate pinned by the sibling
27874 // `QuoteForm::as_unquote_form` truth-table in error.rs. Pins the
27875 // (substitution-subset ⊂ quote-family) inclusion as an invariant
27876 // on the value algebra so a regression that widens
27877 // `as_unquote_form` beyond its 2-of-4 subset (e.g. an emitter
27878 // that starts accepting `Sexp::Quote` as substitution) surfaces
27879 // immediately as a subset-inclusion drift.
27880 let samples = [
27881 (Sexp::Nil, false, false),
27882 (Sexp::List(vec![]), false, false),
27883 (Sexp::List(vec![Sexp::symbol("a")]), false, false),
27884 (Sexp::symbol("foo"), false, false),
27885 (Sexp::keyword("k"), false, false),
27886 (Sexp::string("s"), false, false),
27887 (Sexp::int(7), false, false),
27888 (Sexp::float(7.5), false, false),
27889 (Sexp::boolean(true), false, false),
27890 // Quote-family, NOT substitution-subset
27891 (Sexp::Quote(Box::new(Sexp::Nil)), true, false),
27892 (Sexp::Quasiquote(Box::new(Sexp::Nil)), true, false),
27893 // Quote-family AND substitution-subset
27894 (Sexp::Unquote(Box::new(Sexp::Nil)), true, true),
27895 (Sexp::UnquoteSplice(Box::new(Sexp::Nil)), true, true),
27896 ];
27897 for (s, quote_expected, unquote_expected) in &samples {
27898 let quote_hit = s.as_quote_form().is_some();
27899 let unquote_hit = s.as_unquote_form().is_some();
27900 assert_eq!(
27901 quote_hit, *quote_expected,
27902 "as_quote_form membership drifted at {s:?}"
27903 );
27904 assert_eq!(
27905 unquote_hit, *unquote_expected,
27906 "as_unquote_form membership drifted at {s:?}"
27907 );
27908 // Subset containment: substitution ⊂ quote-family.
27909 assert!(
27910 !unquote_hit || quote_hit,
27911 "subset containment violated at {s:?}: as_unquote_form Some but as_quote_form None",
27912 );
27913 }
27914 }
27915
27916 #[test]
27917 fn sexp_as_quote_form_marker_projects_each_variant_to_canonical_quote_form() {
27918 // PER-VARIANT TRUTH-TABLE (quote-family axis): pin byte-for-byte
27919 // per-Sexp-quote-family-arm mapping — `Sexp::Quote(inner)`
27920 // → `Some(QuoteForm::Quote)`, `Sexp::Quasiquote(inner)`
27921 // → `Some(QuoteForm::Quasiquote)`, `Sexp::Unquote(inner)`
27922 // → `Some(QuoteForm::Unquote)`, `Sexp::UnquoteSplice(inner)`
27923 // → `Some(QuoteForm::UnquoteSplice)`. Value-level marker-only
27924 // peer of the pre-existing tuple projection
27925 // `Sexp::as_quote_form` — each quote-family-wrapper Sexp value's
27926 // carving-marker projection must land on the matching QuoteForm
27927 // arm the parent projection's tuple carries. A future fifth
27928 // QuoteForm variant (e.g. `,~` reverse-unquote) extends both
27929 // this sweep + the composition body via the as_quote_form
27930 // primitive, with rustc enforcing the match arms in lockstep.
27931 assert_eq!(
27932 Sexp::Quote(Box::new(Sexp::symbol("x"))).as_quote_form_marker(),
27933 Some(QuoteForm::Quote)
27934 );
27935 assert_eq!(
27936 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))).as_quote_form_marker(),
27937 Some(QuoteForm::Quasiquote)
27938 );
27939 assert_eq!(
27940 Sexp::Unquote(Box::new(Sexp::symbol("x"))).as_quote_form_marker(),
27941 Some(QuoteForm::Unquote)
27942 );
27943 assert_eq!(
27944 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))).as_quote_form_marker(),
27945 Some(QuoteForm::UnquoteSplice)
27946 );
27947 // Inner-payload invariance edge cases — pin the projection
27948 // ignores inner payload content entirely (it reads only the
27949 // outer wrapper variant discriminant), so a body that gates on
27950 // inner payload shape fails loudly.
27951 assert_eq!(
27952 Sexp::Quote(Box::new(Sexp::Nil)).as_quote_form_marker(),
27953 Some(QuoteForm::Quote)
27954 );
27955 assert_eq!(
27956 Sexp::Quasiquote(Box::new(Sexp::List(vec![]))).as_quote_form_marker(),
27957 Some(QuoteForm::Quasiquote)
27958 );
27959 assert_eq!(
27960 Sexp::Unquote(Box::new(Sexp::List(vec![
27961 Sexp::symbol("nested"),
27962 Sexp::int(42),
27963 ])))
27964 .as_quote_form_marker(),
27965 Some(QuoteForm::Unquote)
27966 );
27967 assert_eq!(
27968 Sexp::UnquoteSplice(Box::new(Sexp::Quote(Box::new(Sexp::symbol("y")))))
27969 .as_quote_form_marker(),
27970 Some(QuoteForm::UnquoteSplice)
27971 );
27972 }
27973
27974 #[test]
27975 fn sexp_as_quote_form_marker_rejects_non_quote_family_outer_shapes() {
27976 // KERNEL: every non-quote-family outer shape (Nil, every Atom
27977 // variant, List — empty and non-empty) projects to `None`.
27978 // Sibling kernel-pin to
27979 // `sexp_as_atom_kind_rejects_non_atom_outer_shapes`,
27980 // `sexp_as_structural_kind_rejects_non_structural_outer_shapes`,
27981 // and `sexp_as_unquote_form_rejects_non_unquote_subset_outer_shapes`
27982 // on the quote-family axis. A body that widens the projection to
27983 // any non-quote-family arm (e.g. `Sexp::List` starts returning
27984 // `Some(QuoteForm::Quote)`) surfaces as a `None` expectation
27985 // failure at the specific offending variant.
27986 for non_quote in [
27987 Sexp::Nil,
27988 Sexp::symbol("foo"),
27989 Sexp::keyword("k"),
27990 Sexp::string("s"),
27991 Sexp::int(7),
27992 Sexp::float(7.5),
27993 Sexp::boolean(true),
27994 Sexp::List(vec![]),
27995 Sexp::List(vec![Sexp::symbol("a")]),
27996 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
27997 ] {
27998 assert_eq!(
27999 non_quote.as_quote_form_marker(),
28000 None,
28001 "non-quote-family Sexp must project to None on as_quote_form_marker — {non_quote:?}"
28002 );
28003 }
28004 }
28005
28006 #[test]
28007 fn sexp_as_quote_form_marker_agrees_with_as_quote_form_map_marker_for_every_variant() {
28008 // COMPOSITION-LAW CONTRACT (parent-projection peer): pin
28009 // `s.as_quote_form_marker() == s.as_quote_form().map(|(qf, _)| qf)`
28010 // for every reachable Sexp outer shape. Pre-lift the quote-
28011 // family carving marker at the value level was reachable via
28012 // this two-step composition through the parent
28013 // [`Sexp::as_quote_form`] projection (discarding the wrapped
28014 // inner via `.map(|(qf, _)| qf)`); post-lift the new marker-
28015 // only projection MUST agree bit-for-bit — the substrate's
28016 // (Sexp value, QuoteForm marker) pairing binds at THREE
28017 // compositions (this parent-projection composition AND the
28018 // shape-axis composition, both pinned in this module, AND the
28019 // direct match in the new method's body) that must stay in
28020 // lockstep. Sweeps every outer shape (atom + residual +
28021 // quote-family) so a drift on ANY arm surfaces immediately.
28022 let samples = [
28023 Sexp::Nil,
28024 Sexp::List(vec![]),
28025 Sexp::List(vec![Sexp::symbol("a")]),
28026 Sexp::symbol("foo"),
28027 Sexp::keyword("k"),
28028 Sexp::string("s"),
28029 Sexp::int(7),
28030 Sexp::float(7.5),
28031 Sexp::boolean(true),
28032 Sexp::Quote(Box::new(Sexp::Nil)),
28033 Sexp::Quasiquote(Box::new(Sexp::Nil)),
28034 Sexp::Unquote(Box::new(Sexp::Nil)),
28035 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
28036 ];
28037 for s in &samples {
28038 assert_eq!(
28039 s.as_quote_form_marker(),
28040 s.as_quote_form().map(|(qf, _)| qf),
28041 "Sexp::as_quote_form_marker and Sexp::as_quote_form().map(|(qf, _)| qf) must agree at {s:?}",
28042 );
28043 }
28044 }
28045
28046 #[test]
28047 fn sexp_as_quote_form_marker_agrees_with_shape_as_quote_form_for_every_variant() {
28048 // COMPOSITION-LAW CONTRACT (shape-axis peer):
28049 // `s.as_quote_form_marker() == s.shape().as_quote_form()` for
28050 // every reachable Sexp outer shape. Sibling to
28051 // `sexp_as_atom_kind_agrees_with_shape_as_atom_kind_for_every_variant`,
28052 // `sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant`,
28053 // and `sexp_as_unquote_form_agrees_with_shape_as_unquote_form_for_every_variant`
28054 // on the quote-family axis. Pre-lift the quote-family carving
28055 // marker at the value level was reachable via this two-step
28056 // composition through the shape algebra (`shape().as_quote_form()`,
28057 // walking the full 12-variant [`SexpShape`](crate::error::SexpShape)
28058 // closed set to arrive at the 4-of-12 carving marker); post-
28059 // lift the new projection MUST agree bit-for-bit — the
28060 // substrate's (Sexp value, QuoteForm marker) pairing now binds
28061 // at ONE typed method on the value algebra, with both
28062 // compositions (this shape-axis peer and the parent-projection
28063 // peer pinned above) staying in lockstep.
28064 use crate::error::SexpShape;
28065 let samples = [
28066 Sexp::Nil,
28067 Sexp::List(vec![]),
28068 Sexp::List(vec![Sexp::symbol("a")]),
28069 Sexp::symbol("foo"),
28070 Sexp::keyword("k"),
28071 Sexp::string("s"),
28072 Sexp::int(7),
28073 Sexp::float(7.5),
28074 Sexp::boolean(true),
28075 Sexp::Quote(Box::new(Sexp::Nil)),
28076 Sexp::Quasiquote(Box::new(Sexp::Nil)),
28077 Sexp::Unquote(Box::new(Sexp::Nil)),
28078 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
28079 ];
28080 for s in &samples {
28081 let via_value: Option<QuoteForm> = s.as_quote_form_marker();
28082 let via_shape: Option<QuoteForm> = SexpShape::as_quote_form(s.shape());
28083 assert_eq!(
28084 via_value, via_shape,
28085 "Sexp::as_quote_form_marker and Sexp::shape().as_quote_form must agree at {s:?}",
28086 );
28087 }
28088 }
28089
28090 #[test]
28091 fn sexp_as_quote_form_marker_composes_with_prefix_via_quote_form_prefix() {
28092 // CROSS-PROJECTION COHERENCE: pin that
28093 // `s.as_quote_form_marker().map(QuoteForm::prefix)` agrees with
28094 // the reader-prefix vocabulary carried on [`QuoteForm::prefix`]
28095 // for every quote-family Sexp (and returns `None` for every
28096 // non-quote-family Sexp). Sibling to
28097 // `sexp_as_unquote_form_composes_with_marker_via_unquote_form_marker`
28098 // on the quote-family axis. Composes the new value-level marker
28099 // projection with the closed-set [`QuoteForm::prefix`]
28100 // projection so the reader/writer prefix vocabulary (`'` / `` ` ``
28101 // / `,` / `,@`) stays load-bearing at ONE canonical site
28102 // ([`QuoteForm::prefix`]'s four arms) rather than a parallel
28103 // per-projection literal table on the value algebra.
28104 let quote_family = [
28105 (Sexp::Quote(Box::new(Sexp::symbol("x"))), QuoteForm::Quote),
28106 (
28107 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
28108 QuoteForm::Quasiquote,
28109 ),
28110 (
28111 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28112 QuoteForm::Unquote,
28113 ),
28114 (
28115 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28116 QuoteForm::UnquoteSplice,
28117 ),
28118 ];
28119 for (sexp, expected_qf) in "e_family {
28120 let via_carving = sexp.as_quote_form_marker().map(QuoteForm::prefix);
28121 assert_eq!(
28122 via_carving,
28123 Some(expected_qf.prefix()),
28124 "quote-family carving-marker prefix drifted at {sexp:?}"
28125 );
28126 }
28127 for non_quote in [
28128 Sexp::Nil,
28129 Sexp::symbol("foo"),
28130 Sexp::keyword("k"),
28131 Sexp::string("s"),
28132 Sexp::int(7),
28133 Sexp::float(7.5),
28134 Sexp::boolean(true),
28135 Sexp::List(vec![]),
28136 Sexp::List(vec![Sexp::symbol("a")]),
28137 ] {
28138 assert_eq!(
28139 non_quote.as_quote_form_marker().map(QuoteForm::prefix),
28140 None,
28141 "non-quote-family Sexp must project to None on as_quote_form_marker.map(prefix) — {non_quote:?}"
28142 );
28143 }
28144 }
28145
28146 #[test]
28147 fn sexp_as_quote_form_marker_extends_as_unquote_form_to_full_quote_family() {
28148 // SUPERSET-GATE CONTRACT (value-level): pin that at every
28149 // reachable Sexp outer shape,
28150 // `as_unquote_form().is_some()` implies
28151 // `as_quote_form_marker().is_some()` (the 2-of-12 substitution
28152 // subset is a proper subset of the 4-of-12 quote family) AND
28153 // `as_quote_form_marker().is_some() && !as_unquote_form().is_some()`
28154 // holds exactly for the two non-substitution quote-family
28155 // wrappers (`Sexp::Quote` and `Sexp::Quasiquote`) — the value-
28156 // level image of the 2-of-4 subset gate
28157 // [`QuoteForm::as_unquote_form`], mirroring
28158 // `sexp_as_unquote_form_narrows_as_quote_form_to_substitution_subset`
28159 // from the substitution-axis side. Pins the (substitution-
28160 // subset ⊂ quote-family) inclusion as an invariant on the
28161 // value algebra where the SUPERSET side is now a NAMED typed
28162 // method — so a regression that widens either projection
28163 // beyond its cell (e.g. `as_quote_form_marker` starts accepting
28164 // `Sexp::List`, or `as_unquote_form` starts accepting
28165 // `Sexp::Quote`) surfaces immediately as a subset-inclusion
28166 // drift. Also pin that
28167 // `as_unquote_form() == as_quote_form_marker().and_then(
28168 // QuoteForm::as_unquote_form)` — the value-level projection
28169 // composes with the 2-of-4 subset gate at the marker algebra
28170 // level, so the substrate's (Sexp value, UnquoteForm marker)
28171 // pairing derives from the (Sexp value, QuoteForm marker)
28172 // pairing at ONE composition rather than two parallel value-
28173 // level projections.
28174 let samples = [
28175 (Sexp::Nil, false, false),
28176 (Sexp::List(vec![]), false, false),
28177 (Sexp::List(vec![Sexp::symbol("a")]), false, false),
28178 (Sexp::symbol("foo"), false, false),
28179 (Sexp::keyword("k"), false, false),
28180 (Sexp::string("s"), false, false),
28181 (Sexp::int(7), false, false),
28182 (Sexp::float(7.5), false, false),
28183 (Sexp::boolean(true), false, false),
28184 // Quote-family, NOT substitution-subset
28185 (Sexp::Quote(Box::new(Sexp::Nil)), true, false),
28186 (Sexp::Quasiquote(Box::new(Sexp::Nil)), true, false),
28187 // Quote-family AND substitution-subset
28188 (Sexp::Unquote(Box::new(Sexp::Nil)), true, true),
28189 (Sexp::UnquoteSplice(Box::new(Sexp::Nil)), true, true),
28190 ];
28191 for (s, quote_expected, unquote_expected) in &samples {
28192 let quote_hit = s.as_quote_form_marker().is_some();
28193 let unquote_hit = s.as_unquote_form().is_some();
28194 assert_eq!(
28195 quote_hit, *quote_expected,
28196 "as_quote_form_marker membership drifted at {s:?}"
28197 );
28198 assert_eq!(
28199 unquote_hit, *unquote_expected,
28200 "as_unquote_form membership drifted at {s:?}"
28201 );
28202 // Superset containment: substitution ⊂ quote-family.
28203 assert!(
28204 !unquote_hit || quote_hit,
28205 "subset containment violated at {s:?}: as_unquote_form Some but as_quote_form_marker None",
28206 );
28207 // Composition through the 2-of-4 subset gate:
28208 // `s.as_unquote_form() == s.as_quote_form_marker().and_then(QuoteForm::as_unquote_form)`.
28209 assert_eq!(
28210 s.as_unquote_form(),
28211 s.as_quote_form_marker()
28212 .and_then(QuoteForm::as_unquote_form),
28213 "as_unquote_form and as_quote_form_marker + QuoteForm::as_unquote_form composition disagree at {s:?}",
28214 );
28215 }
28216 }
28217
28218 #[test]
28219 fn sexp_shape_method_label_composes_with_sexp_type_name_for_every_outer_shape() {
28220 // COMPOSITION-LAW CONTRACT: `s.shape().label() ==
28221 // crate::domain::sexp_type_name(&s)` for every reachable Sexp
28222 // outer shape. Post-lift `sexp_type_name` routes through
28223 // `s.shape().label()` directly (no longer through the free-
28224 // function `sexp_shape`). Pin the composition law so a future
28225 // refactor that drifts either projection (e.g. a label typo
28226 // in `SexpShape::label`, a change in `sexp_type_name`'s
28227 // delegation) surfaces here immediately.
28228 let samples = [
28229 Sexp::Nil,
28230 Sexp::symbol("foo"),
28231 Sexp::keyword("k"),
28232 Sexp::string("s"),
28233 Sexp::int(7),
28234 Sexp::float(7.5),
28235 Sexp::boolean(true),
28236 Sexp::List(vec![]),
28237 Sexp::Quote(Box::new(Sexp::Nil)),
28238 Sexp::Quasiquote(Box::new(Sexp::Nil)),
28239 Sexp::Unquote(Box::new(Sexp::Nil)),
28240 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
28241 ];
28242 for s in &samples {
28243 assert_eq!(
28244 s.shape().label(),
28245 crate::domain::sexp_type_name(s),
28246 "Sexp::shape().label() must equal domain::sexp_type_name for {s:?}",
28247 );
28248 }
28249 }
28250
28251 #[test]
28252 fn sexp_type_name_method_projects_each_outer_arm_to_canonical_label() {
28253 // PER-ARM CONTRACT: pin that the inherent `Sexp::type_name()`
28254 // method projects each reachable outer Sexp shape to its
28255 // canonical `&'static str` label. Pre-lift the projection
28256 // lived as a free function `domain::sexp_type_name`; post-
28257 // lift the canonical site is the inherent method on the
28258 // `Sexp` algebra and the free function delegates. A
28259 // regression that drifts a per-arm label (e.g. a typo in
28260 // `SexpShape::label`, a stale arm in `Sexp::shape`'s match,
28261 // a change in the body away from `self.shape().label()`)
28262 // surfaces here immediately. Sweeps every outer shape and
28263 // every atomic payload kind so all 8 `SexpShape` variants
28264 // are covered.
28265 assert_eq!(Sexp::Nil.type_name(), "nil");
28266 assert_eq!(Sexp::symbol("foo").type_name(), "symbol");
28267 assert_eq!(Sexp::keyword("k").type_name(), "keyword");
28268 assert_eq!(Sexp::string("s").type_name(), "string");
28269 assert_eq!(Sexp::int(7).type_name(), "int");
28270 assert_eq!(Sexp::float(7.5).type_name(), "float");
28271 assert_eq!(Sexp::boolean(true).type_name(), "bool");
28272 assert_eq!(Sexp::List(vec![]).type_name(), "list");
28273 assert_eq!(Sexp::Quote(Box::new(Sexp::Nil)).type_name(), "quote");
28274 assert_eq!(
28275 Sexp::Quasiquote(Box::new(Sexp::Nil)).type_name(),
28276 "quasiquote",
28277 );
28278 assert_eq!(Sexp::Unquote(Box::new(Sexp::Nil)).type_name(), "unquote");
28279 assert_eq!(
28280 Sexp::UnquoteSplice(Box::new(Sexp::Nil)).type_name(),
28281 "unquote-splice",
28282 );
28283 }
28284
28285 #[test]
28286 fn sexp_type_name_method_composes_through_shape_label_for_every_outer_shape() {
28287 // COMPOSITION-LAW CONTRACT: `s.type_name() == s.shape().label()`
28288 // for every reachable Sexp outer shape — the method body is
28289 // structurally derived through `Self::shape` + `SexpShape::label`
28290 // rather than re-matching `Sexp` arms directly. Pin the
28291 // composition law so a future refactor that re-inlines the
28292 // match (and gains its own drift surface) surfaces here
28293 // immediately. Sibling-shape pin to the existing
28294 // `sexp_shape_method_label_composes_with_sexp_type_name_for_every_outer_shape`
28295 // pin (which pins the inverse direction: the free function
28296 // routes through the inherent method).
28297 let samples = [
28298 Sexp::Nil,
28299 Sexp::symbol("foo"),
28300 Sexp::keyword("k"),
28301 Sexp::string("s"),
28302 Sexp::int(7),
28303 Sexp::float(7.5),
28304 Sexp::boolean(true),
28305 Sexp::List(vec![]),
28306 Sexp::Quote(Box::new(Sexp::Nil)),
28307 Sexp::Quasiquote(Box::new(Sexp::Nil)),
28308 Sexp::Unquote(Box::new(Sexp::Nil)),
28309 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
28310 ];
28311 for s in &samples {
28312 assert_eq!(
28313 s.type_name(),
28314 s.shape().label(),
28315 "Sexp::type_name() must compose through Sexp::shape().label() for {s:?}",
28316 );
28317 }
28318 }
28319
28320 #[test]
28321 fn sexp_type_name_method_agrees_with_domain_sexp_type_name_for_every_outer_shape() {
28322 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28323 // `Sexp::type_name()` method agrees with the free-function
28324 // delegate `crate::domain::sexp_type_name` for every
28325 // reachable outer shape. Pre-lift the dispatcher lived as a
28326 // free function in `domain.rs`; post-lift the canonical site
28327 // is the inherent method on the `Sexp` algebra and the free
28328 // function is a one-line delegate. Pin that the delegation
28329 // stays byte-for-byte equivalent across every outer arm so
28330 // a regression where the free function drifts from the
28331 // inherent method (or vice versa) surfaces here immediately.
28332 // Mirrors `sexp_witness_method_agrees_with_domain_sexp_witness_for_every_outer_shape`
28333 // for the canonical-label-only peer projection.
28334 let samples = [
28335 Sexp::Nil,
28336 Sexp::symbol("foo"),
28337 Sexp::keyword("k"),
28338 Sexp::string("s"),
28339 Sexp::int(7),
28340 Sexp::int(-1),
28341 Sexp::float(7.5),
28342 Sexp::boolean(true),
28343 Sexp::boolean(false),
28344 Sexp::List(vec![]),
28345 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
28346 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
28347 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
28348 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28349 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28350 ];
28351 for s in &samples {
28352 assert_eq!(
28353 s.type_name(),
28354 crate::domain::sexp_type_name(s),
28355 "Sexp::type_name() must equal domain::sexp_type_name for {s:?}",
28356 );
28357 }
28358 }
28359
28360 #[test]
28361 fn sexp_witness_method_pairs_shape_with_display_for_every_outer_shape() {
28362 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28363 // `Sexp::witness()` method projects each reachable outer Sexp
28364 // shape to a `SexpWitness` whose `shape` field equals
28365 // `s.shape()` AND whose `display` field equals
28366 // `s.to_string()` for every variant + payload combination.
28367 // Pre-lift the projection lived as a free function in
28368 // `domain.rs`; post-lift the canonical site is the inherent
28369 // method on the `Sexp` algebra. A regression where the method
28370 // drifts EITHER half of the joint identity (a stale `shape`
28371 // projection that re-inlines without composing through
28372 // `Sexp::shape`, a `display` projection that diverges from
28373 // `Sexp::Display`) surfaces here immediately. Sweeps every
28374 // outer shape, every atomic payload kind, and every
28375 // quote-family wrapper.
28376 let samples = [
28377 Sexp::Nil,
28378 Sexp::symbol("foo"),
28379 Sexp::keyword("k"),
28380 Sexp::string("s"),
28381 Sexp::int(7),
28382 Sexp::int(-1),
28383 Sexp::float(7.5),
28384 Sexp::boolean(true),
28385 Sexp::boolean(false),
28386 Sexp::List(vec![]),
28387 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
28388 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
28389 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
28390 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28391 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28392 ];
28393 for s in &samples {
28394 let w = s.witness();
28395 assert_eq!(
28396 w.shape,
28397 s.shape(),
28398 "Sexp::witness().shape drifted from Sexp::shape() for {s:?}",
28399 );
28400 assert_eq!(
28401 w.display,
28402 s.to_string(),
28403 "Sexp::witness().display drifted from Sexp::Display for {s:?}",
28404 );
28405 }
28406 }
28407
28408 #[test]
28409 fn sexp_witness_method_agrees_with_domain_sexp_witness_for_every_outer_shape() {
28410 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28411 // `Sexp::witness()` method agrees with the free-function
28412 // delegate `crate::domain::sexp_witness` for every reachable
28413 // outer shape. Pre-lift the dispatcher lived as a free
28414 // function in `domain.rs`; post-lift the canonical site is
28415 // the inherent method on the `Sexp` algebra and the free
28416 // function is a one-line delegate. Pin that the delegation
28417 // stays byte-for-byte equivalent across every outer arm so
28418 // a regression where the free function drifts from the
28419 // inherent method (or vice versa) surfaces here immediately.
28420 // Mirrors `sexp_shape_method_agrees_with_domain_sexp_shape_for_every_outer_shape`
28421 // for the joint-identity peer projection. Catches a future
28422 // "consolidation" that removes the free function without
28423 // updating the method, or vice versa.
28424 let samples = [
28425 Sexp::Nil,
28426 Sexp::symbol("foo"),
28427 Sexp::keyword("k"),
28428 Sexp::string("s"),
28429 Sexp::int(7),
28430 Sexp::int(-1),
28431 Sexp::float(7.5),
28432 Sexp::float(0.0),
28433 Sexp::boolean(true),
28434 Sexp::boolean(false),
28435 Sexp::List(vec![]),
28436 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
28437 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
28438 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
28439 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28440 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28441 ];
28442 for s in &samples {
28443 let via_method = s.witness();
28444 let via_delegate = crate::domain::sexp_witness(s);
28445 assert_eq!(
28446 via_method.shape, via_delegate.shape,
28447 "Sexp::witness().shape drifted from domain::sexp_witness().shape at {s:?}",
28448 );
28449 assert_eq!(
28450 via_method.display, via_delegate.display,
28451 "Sexp::witness().display drifted from domain::sexp_witness().display at {s:?}",
28452 );
28453 }
28454 }
28455
28456 #[test]
28457 fn sexp_witness_method_routes_through_shape_and_display_projections() {
28458 // PATH-UNIFORMITY CONTRACT: the lifted `Sexp::witness()` body
28459 // composes the two algebra-level projections `Sexp::shape()`
28460 // (structural identity) + `Sexp::Display` (renderable
28461 // identity) into ONE `SexpWitness::new(shape, display)`
28462 // value. Pin that the composition agrees bit-for-bit with
28463 // the direct `SexpWitness::new(s.shape(), s.to_string())`
28464 // construction across a sweep covering every outer shape.
28465 // A regression in EITHER projection direction (a
28466 // `Sexp::witness` arm that bypasses `Sexp::shape` and
28467 // re-inlines the dispatch, a `Sexp::witness` arm that
28468 // bypasses `Sexp::Display` and re-formats the literal) is
28469 // structurally impossible — the typed joint primitive
28470 // composes through the typed primitive halves once.
28471 // Sibling shape to `sexp_shape_method_routes_atom_arm_through_atom_kind_sexp_shape_projection`
28472 // for the joint-identity axis.
28473 let samples = [
28474 Sexp::Nil,
28475 Sexp::symbol("x"),
28476 Sexp::keyword("kw"),
28477 Sexp::string("text"),
28478 Sexp::int(0),
28479 Sexp::float(2.5),
28480 Sexp::boolean(true),
28481 Sexp::List(vec![Sexp::symbol("f"), Sexp::int(1)]),
28482 Sexp::Quote(Box::new(Sexp::symbol("q"))),
28483 Sexp::Quasiquote(Box::new(Sexp::symbol("qq"))),
28484 Sexp::Unquote(Box::new(Sexp::symbol("uq"))),
28485 Sexp::UnquoteSplice(Box::new(Sexp::symbol("uqs"))),
28486 ];
28487 for s in &samples {
28488 let via_method = s.witness();
28489 let via_composed = crate::error::SexpWitness::new(s.shape(), s.to_string());
28490 assert_eq!(
28491 via_method.shape, via_composed.shape,
28492 "Sexp::witness drifted shape from SexpWitness::new(s.shape(), s.to_string()) at {s:?}",
28493 );
28494 assert_eq!(
28495 via_method.display, via_composed.display,
28496 "Sexp::witness drifted display from SexpWitness::new(s.shape(), s.to_string()) at {s:?}",
28497 );
28498 }
28499 }
28500
28501 #[test]
28502 fn sexp_witness_distinguishes_int_atom_from_symbol_with_identical_display() {
28503 // STRUCTURAL-IDENTITY CONTRACT: `Sexp::int(5)` and
28504 // `Sexp::symbol("5")` Display-render identically (`"5"`) but
28505 // are STRUCTURALLY DISTINCT — one is `SexpShape::Int`, the
28506 // other is `SexpShape::Symbol`. Pin that `Sexp::witness()`
28507 // carries the structural identity through the `shape` slot
28508 // so the rejection diagnostic distinguishes the two even
28509 // when the rendered literal collides. Mirrors the
28510 // free-function-delegate sibling test
28511 // `sexp_witness_distinguishes_int_atom_from_symbol_with_same_display`
28512 // in `domain.rs::tests` — that test pins the delegate; this
28513 // one pins the inherent method on the algebra. Both stay
28514 // load-bearing across the lifted boundary.
28515 let w_int = Sexp::int(5).witness();
28516 let w_sym = Sexp::symbol("5").witness();
28517 assert_eq!(
28518 w_int.display, w_sym.display,
28519 "display collision precondition"
28520 );
28521 assert_ne!(
28522 w_int.shape, w_sym.shape,
28523 "Sexp::witness must distinguish Int from Symbol via shape even when display collides",
28524 );
28525 assert_eq!(w_int.shape, crate::error::SexpShape::Int);
28526 assert_eq!(w_sym.shape, crate::error::SexpShape::Symbol);
28527 }
28528
28529 #[test]
28530 fn sexp_as_x_family_routes_through_atom_as_x_for_every_atomic_variant() {
28531 // LIFTED-BOUNDARY CONTRACT: pin that the six `Sexp::as_X`
28532 // consumer-side projections equal the two-step composition
28533 // `s.as_atom().and_then(Atom::as_X)` for every atomic payload
28534 // variant. Pre-lift the six methods opened the same `Self::Atom
28535 // (Atom::X(s)) => Some(s)` inline arm; post-lift they delegate
28536 // through the typed projection family on the closed-set `Atom`
28537 // algebra. A regression that drifts the outer arm (e.g. re-
28538 // inlines one variant's match without updating the typed
28539 // projection) surfaces as an inequality here. Sweeps every
28540 // atomic variant + every consumer projection, AND pins the
28541 // `Sexp::as_float` widening specialization (`Atom::Int(n)` →
28542 // `Some(n as f64)`) lives at the consumer layer.
28543 let cases: &[Atom] = &[
28544 Atom::Symbol("name".into()),
28545 Atom::Keyword("kw".into()),
28546 Atom::Str("body".into()),
28547 Atom::Int(42),
28548 Atom::Int(-7),
28549 Atom::Float(1.5),
28550 Atom::Float(1.0),
28551 Atom::Bool(true),
28552 Atom::Bool(false),
28553 ];
28554 for atom in cases {
28555 let sexp = Sexp::Atom(atom.clone());
28556
28557 assert_eq!(
28558 sexp.as_symbol(),
28559 sexp.as_atom().and_then(Atom::as_symbol),
28560 "Sexp::as_symbol drifted from as_atom().and_then(Atom::as_symbol) for {atom:?}",
28561 );
28562 assert_eq!(
28563 sexp.as_keyword(),
28564 sexp.as_atom().and_then(Atom::as_keyword),
28565 "Sexp::as_keyword drifted from as_atom().and_then(Atom::as_keyword) for {atom:?}",
28566 );
28567 assert_eq!(
28568 sexp.as_string(),
28569 sexp.as_atom().and_then(Atom::as_string),
28570 "Sexp::as_string drifted from as_atom().and_then(Atom::as_string) for {atom:?}",
28571 );
28572 assert_eq!(
28573 sexp.as_int(),
28574 sexp.as_atom().and_then(Atom::as_int),
28575 "Sexp::as_int drifted from as_atom().and_then(Atom::as_int) for {atom:?}",
28576 );
28577 assert_eq!(
28578 sexp.as_bool(),
28579 sexp.as_atom().and_then(Atom::as_bool),
28580 "Sexp::as_bool drifted from as_atom().and_then(Atom::as_bool) for {atom:?}",
28581 );
28582
28583 // `Sexp::as_float` specializes through the widening composition
28584 // `s.as_atom().and_then(|a| a.as_float().or_else(|| a.as_int()
28585 // .map(|n| n as f64)))` so the algebra-level `Atom::as_float`
28586 // stays strict and the typed-identity distinction `Int(1)` vs
28587 // `Float(1.0)` is preserved at the algebra layer.
28588 let expected_float = sexp
28589 .as_atom()
28590 .and_then(|a| a.as_float().or_else(|| a.as_int().map(|n| n as f64)));
28591 assert_eq!(
28592 sexp.as_float(),
28593 expected_float,
28594 "Sexp::as_float drifted from widening composition for {atom:?}",
28595 );
28596 }
28597 }
28598
28599 #[test]
28600 fn sexp_as_float_widens_int_to_float_at_consumer_layer_only() {
28601 // CONSUMER-LAYER WIDENING CONTRACT: pin that the `Sexp::as_float`
28602 // consumer DOES widen `Atom::Int(n)` to `Some(n as f64)` (the
28603 // load-bearing widening at the numeric-kwarg boundary the
28604 // `extract_float` extractor depends on) AND that the algebra-
28605 // level `Atom::as_float` does NOT (the strict typed-identity
28606 // discipline pinned at `atom_as_float_returns_payload_iff_float_variant_strict_no_int_widening`).
28607 // The widening lives at the CONSUMER layer ONLY; a regression
28608 // that drifts the widening into the algebra layer (e.g. re-
28609 // adds an `Atom::Int(n) => Some(n as f64)` arm at
28610 // `Atom::as_float`) would silently coerce `Int(1)` slots into
28611 // the `Float` track at every `Atom` consumer that bypasses
28612 // `Sexp`, breaking the typed-identity discipline at the
28613 // canonical-form rendering surfaces (Display, JSON,
28614 // iac-forge).
28615 let int_sexp = Sexp::int(7);
28616 assert_eq!(
28617 int_sexp.as_float(),
28618 Some(7.0),
28619 "Sexp::as_float must widen Atom::Int to f64 at the consumer layer",
28620 );
28621 assert_eq!(
28622 Atom::Int(7).as_float(),
28623 None,
28624 "Atom::as_float must stay strict at the algebra layer",
28625 );
28626
28627 // The widening sweeps the int domain — pin a few canonical
28628 // values so a regression that loses the `as f64` cast (e.g. an
28629 // accidental `usize` round-trip) surfaces directly.
28630 for n in [-42i64, -1, 0, 1, 42] {
28631 assert_eq!(
28632 Sexp::int(n).as_float(),
28633 Some(n as f64),
28634 "Sexp::as_float widening drifted for Int({n})",
28635 );
28636 }
28637 }
28638
28639 #[test]
28640 fn sexp_to_json_method_projects_each_outer_arm_to_canonical_json() {
28641 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28642 // `Sexp::to_json()` method projects each reachable outer Sexp
28643 // shape to a `serde_json::Value` byte-identical to the
28644 // pre-lift inline rule at `crate::domain::sexp_to_json`'s
28645 // outer match — Nil → Null, Atom → `Atom::to_json` (composed
28646 // through the typed-algebra projection), List(kwargs) →
28647 // Object keyed by kebab→camel, List(other) → Array, and
28648 // each quote-family wrapper → recurse on inner (the wrapper
28649 // is structurally erased into JSON). A regression that
28650 // drifts ANY outer arm (e.g. emits Nil as `"nil"` instead of
28651 // Null, swaps List(kwargs) for Array unconditionally, drops
28652 // a quote-family arm's recursion) surfaces here. Pre-lift
28653 // the dispatcher lived as a free function in `domain.rs`;
28654 // post-lift the canonical site is the inherent method on
28655 // the `Sexp` algebra (same posture as the prior
28656 // `Sexp::shape` (121bb60) and `Sexp::witness` (a427e3b)
28657 // lifts).
28658 assert_eq!(
28659 Sexp::Nil.to_json().expect("nil to_json"),
28660 serde_json::Value::Null,
28661 );
28662 assert_eq!(
28663 Sexp::symbol("foo").to_json().expect("symbol to_json"),
28664 serde_json::Value::String("foo".into()),
28665 );
28666 assert_eq!(
28667 Sexp::keyword("k").to_json().expect("keyword to_json"),
28668 serde_json::Value::String(":k".into()),
28669 );
28670 assert_eq!(
28671 Sexp::string("body").to_json().expect("string to_json"),
28672 serde_json::Value::String("body".into()),
28673 );
28674 assert_eq!(
28675 Sexp::int(7).to_json().expect("int to_json"),
28676 serde_json::json!(7),
28677 );
28678 assert_eq!(
28679 Sexp::float(1.5).to_json().expect("float to_json"),
28680 serde_json::json!(1.5),
28681 );
28682 assert_eq!(
28683 Sexp::boolean(true).to_json().expect("true to_json"),
28684 serde_json::Value::Bool(true),
28685 );
28686
28687 // List(kwargs) → Object with kebab→camel keys.
28688 let kwargs = Sexp::List(vec![
28689 Sexp::keyword("point-type"),
28690 Sexp::symbol("Gate"),
28691 Sexp::keyword("must-reach"),
28692 Sexp::boolean(true),
28693 ]);
28694 assert_eq!(
28695 kwargs.to_json().expect("kwargs list to_json"),
28696 serde_json::json!({"pointType": "Gate", "mustReach": true}),
28697 );
28698
28699 // List(non-kwargs) → Array.
28700 let arr = Sexp::List(vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)]);
28701 assert_eq!(
28702 arr.to_json().expect("non-kwargs list to_json"),
28703 serde_json::json!([1, 2, 3]),
28704 );
28705
28706 // Empty list → Array (kwargs guard rejects empty lists).
28707 let empty = Sexp::List(vec![]);
28708 assert_eq!(
28709 empty.to_json().expect("empty list to_json"),
28710 serde_json::json!([]),
28711 );
28712
28713 // Quote-family wrappers strip and recurse.
28714 let payload = Sexp::List(vec![Sexp::keyword("k"), Sexp::int(42)]);
28715 let expected = serde_json::json!({"k": 42});
28716 for wrapped in [
28717 Sexp::Quote(Box::new(payload.clone())),
28718 Sexp::Quasiquote(Box::new(payload.clone())),
28719 Sexp::Unquote(Box::new(payload.clone())),
28720 Sexp::UnquoteSplice(Box::new(payload.clone())),
28721 ] {
28722 assert_eq!(
28723 wrapped.to_json().expect("quote-family to_json"),
28724 expected,
28725 "quote-family wrapper {wrapped:?} drifted from inner-recursion shape",
28726 );
28727 }
28728 }
28729
28730 #[test]
28731 fn sexp_to_json_method_agrees_with_domain_sexp_to_json_for_every_outer_shape() {
28732 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28733 // `Sexp::to_json()` method agrees with the free-function
28734 // delegate `crate::domain::sexp_to_json` for every reachable
28735 // outer shape. Pre-lift the dispatcher lived as a free
28736 // function in `domain.rs`; post-lift the canonical site is
28737 // the inherent method and the free function is a one-line
28738 // delegate. Pin that the delegation stays byte-for-byte
28739 // equivalent across every outer arm so a regression where
28740 // the free function drifts from the inherent method (or
28741 // vice versa) surfaces here immediately. Mirrors
28742 // `sexp_shape_method_agrees_with_domain_sexp_shape_for_every_outer_shape`
28743 // and
28744 // `sexp_witness_method_agrees_with_domain_sexp_witness_for_every_outer_shape`
28745 // for the JSON canonical-form projection peer.
28746 let samples = [
28747 Sexp::Nil,
28748 Sexp::symbol("foo"),
28749 Sexp::keyword("k"),
28750 Sexp::string("s"),
28751 Sexp::int(7),
28752 Sexp::int(-1),
28753 Sexp::float(7.5),
28754 Sexp::float(0.0),
28755 Sexp::boolean(true),
28756 Sexp::boolean(false),
28757 Sexp::List(vec![]),
28758 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
28759 Sexp::List(vec![
28760 Sexp::keyword("point-type"),
28761 Sexp::symbol("Gate"),
28762 Sexp::keyword("must-reach"),
28763 Sexp::boolean(true),
28764 ]),
28765 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
28766 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
28767 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28768 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28769 ];
28770 for s in &samples {
28771 let via_method = s.to_json().expect("method projection must succeed");
28772 let via_delegate =
28773 crate::domain::sexp_to_json(s).expect("delegate projection must succeed");
28774 assert_eq!(
28775 via_method, via_delegate,
28776 "Sexp::to_json drifted from domain::sexp_to_json at {s:?}",
28777 );
28778 }
28779 }
28780
28781 #[test]
28782 fn sexp_to_json_method_routes_atom_arm_through_atom_to_json() {
28783 // PATH-UNIFORMITY CONTRACT: the lifted `Sexp::to_json()`
28784 // body composes through the typed-algebra primitive
28785 // [`Atom::to_json`] at the Atom arm — `Sexp::Atom(a).to_json()
28786 // == Ok(a.to_json())` for every atomic payload variant. A
28787 // regression in EITHER direction (a `Sexp::to_json` arm
28788 // that bypasses `Atom::to_json` and re-inlines a per-variant
28789 // mapping, or an `Atom::to_json` projection that diverges
28790 // from the rendering the outer arm depends on) is
28791 // structurally impossible — the typed JSON primitive composes
28792 // through the typed primitive halves once. Sibling-shape pin
28793 // to `sexp_to_json_atom_arms_route_through_atom_to_json` in
28794 // `domain.rs` (the free-function-delegate peer that pinned
28795 // the same identity at the pre-lift site).
28796 let cases: &[Atom] = &[
28797 Atom::Symbol("name".into()),
28798 Atom::Keyword("kw".into()),
28799 Atom::Str("body".into()),
28800 Atom::Int(7),
28801 Atom::Int(-3),
28802 Atom::Float(2.5),
28803 Atom::Float(1.0),
28804 Atom::Bool(true),
28805 Atom::Bool(false),
28806 ];
28807 for atom in cases {
28808 let via_method = Sexp::Atom(atom.clone())
28809 .to_json()
28810 .expect("atom must serialize through Sexp::to_json");
28811 let via_atom = atom.to_json();
28812 assert_eq!(
28813 via_method, via_atom,
28814 "Sexp::to_json Atom arm drifted from Atom::to_json for {atom:?}",
28815 );
28816 }
28817 }
28818
28819 #[test]
28820 fn sexp_to_json_method_routes_quote_family_arms_through_inner_recursion() {
28821 // PATH-UNIFORMITY CONTRACT: the four quote-family arms each
28822 // strip the wrapper and recurse on the projected `inner`
28823 // (via `Self::expect_quote_form`), NOT on the outer `self`.
28824 // Pin that this binding semantic is observable across all
28825 // four wrappers: `wrap_qf(inner).to_json() == inner.to_json()`
28826 // for every `QuoteForm` variant. A regression that lifted
28827 // the recursion onto `self` (the outer wrapper) instead of
28828 // the projected inner would infinite-loop or surface as a
28829 // structural mismatch here. Sibling shape to
28830 // `sexp_to_json_routes_quote_family_arms_through_as_quote_form_typed_marker`
28831 // in `domain.rs::tests` (the free-function-delegate peer)
28832 // — both pin the same invariant at the lifted boundary.
28833 let inner = Sexp::List(vec![Sexp::keyword("k"), Sexp::int(42)]);
28834 let expected = inner.to_json().expect("inner serializes");
28835 for wrap in [
28836 Sexp::Quote(Box::new(inner.clone())),
28837 Sexp::Quasiquote(Box::new(inner.clone())),
28838 Sexp::Unquote(Box::new(inner.clone())),
28839 Sexp::UnquoteSplice(Box::new(inner.clone())),
28840 ] {
28841 let via_method = wrap
28842 .to_json()
28843 .expect("quote-family wrapper must serialize via Sexp::to_json");
28844 assert_eq!(
28845 via_method, expected,
28846 "Sexp::to_json drifted from inner-recursion shape at {wrap:?}",
28847 );
28848 }
28849 }
28850
28851 #[test]
28852 fn sexp_to_json_method_rejects_duplicate_kwargs_at_lifted_boundary() {
28853 // TYPED-ENTRY CONTRACT: the duplicate-keyword rejection at
28854 // the kwargs-list arm fires at the inherent method directly,
28855 // not at the delegate — the canonical typed-entry gate lives
28856 // on the algebra. Pin that two `:k` entries in the same
28857 // kwargs list collapse to `LispError::DuplicateKwarg { key }`
28858 // with `key == "notify-ref"` (the kebab spelling, before
28859 // kebab→camel conversion — the diagnostic surface matches
28860 // the spelling the operator typed). The error type
28861 // discriminator is checked via debug-format substring so a
28862 // future LispError variant rename doesn't silently break
28863 // this pin. Mirrors `sexp_to_json_nested_duplicate_emits_structural_variant`
28864 // in `domain.rs::tests` (the free-function delegate peer at
28865 // the pre-lift site) at the lifted boundary.
28866 let dup = Sexp::List(vec![
28867 Sexp::keyword("notify-ref"),
28868 Sexp::string("a"),
28869 Sexp::keyword("notify-ref"),
28870 Sexp::string("b"),
28871 ]);
28872 let err = dup.to_json().expect_err("duplicate kwarg must reject");
28873 let rendered = format!("{err:?}");
28874 assert!(
28875 rendered.contains("DuplicateKwarg"),
28876 "expected DuplicateKwarg variant, got {rendered}",
28877 );
28878 assert!(
28879 rendered.contains("notify-ref"),
28880 "expected diagnostic to name the kebab-spelled duplicate key, got {rendered}",
28881 );
28882 }
28883
28884 // ── Sexp::from_json: the inverse JSON-projection on the algebra ─────
28885 //
28886 // `Sexp::from_json` lifts the `domain::json_to_sexp` free-function
28887 // dispatcher onto the inherent-method canonical site on the [`Sexp`]
28888 // algebra — sibling-lift posture to the prior `sexp_to_json` →
28889 // `Sexp::to_json` (875ee3b), `sexp_witness` → `Sexp::witness`
28890 // (a427e3b), and `sexp_shape` → `Sexp::shape` (121bb60). The tests
28891 // below pin the per-arm contract on the new canonical site directly;
28892 // the free function delegates so the existing path-uniformity tests
28893 // at `domain::json_to_sexp_*` continue to pass post-lift unchanged.
28894
28895 #[test]
28896 fn sexp_from_json_projects_each_outer_arm_to_canonical_sexp() {
28897 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28898 // `Sexp::from_json` associated function projects each reachable
28899 // outer `serde_json::Value` shape to a `Sexp` byte-identical to
28900 // the pre-lift inline rule at `crate::domain::json_to_sexp`'s
28901 // outer match — Null → Nil, Bool → boolean, Number(i64) → int,
28902 // Number(f64-only) → float, String → string, Array → List(map),
28903 // Object → List of alternating `:k v` pairs in iteration order
28904 // via `camel_to_kebab` on each key. A regression that drifts ANY
28905 // outer arm (e.g. emits Null as Sexp::string(""), swaps Array
28906 // for a kwargs-shaped List, drops the camel→kebab projection on
28907 // Object keys) surfaces here. Pre-lift the dispatcher lived as a
28908 // free function in `domain.rs`; post-lift the canonical site is
28909 // the inherent associated function on the `Sexp` algebra.
28910 assert_eq!(Sexp::from_json(&serde_json::Value::Null), Sexp::Nil);
28911 assert_eq!(
28912 Sexp::from_json(&serde_json::Value::Bool(true)),
28913 Sexp::boolean(true),
28914 );
28915 assert_eq!(
28916 Sexp::from_json(&serde_json::Value::Bool(false)),
28917 Sexp::boolean(false),
28918 );
28919 assert_eq!(Sexp::from_json(&serde_json::json!(42)), Sexp::int(42));
28920 assert_eq!(Sexp::from_json(&serde_json::json!(-1)), Sexp::int(-1));
28921 assert_eq!(Sexp::from_json(&serde_json::json!(0)), Sexp::int(0));
28922 // Float that does NOT fit i64 falls through to the float arm.
28923 assert_eq!(Sexp::from_json(&serde_json::json!(1.5)), Sexp::float(1.5));
28924 assert_eq!(
28925 Sexp::from_json(&serde_json::Value::String("body".into())),
28926 Sexp::string("body"),
28927 );
28928
28929 // Array → List with each element projected recursively.
28930 let arr = serde_json::json!([1, "x", true, null]);
28931 assert_eq!(
28932 Sexp::from_json(&arr),
28933 Sexp::List(vec![
28934 Sexp::int(1),
28935 Sexp::string("x"),
28936 Sexp::boolean(true),
28937 Sexp::Nil,
28938 ]),
28939 );
28940
28941 // Object → List of alternating `:k v` pairs, JSON key projected
28942 // through camel→kebab so the kwarg authoring shape is recovered.
28943 // The iteration order of the JSON object is implementation-
28944 // defined here (no `preserve_order` feature on `serde_json`), so
28945 // pin the SET of (kebab-key, value) pairs rather than the
28946 // sequence — order-uniformity vs. the delegate is pinned in the
28947 // path-uniformity test below.
28948 let obj = serde_json::json!({"pointType": "Gate", "mustReach": true});
28949 let result = Sexp::from_json(&obj);
28950 let items = match &result {
28951 Sexp::List(items) => items.clone(),
28952 other => panic!("expected List, got {other:?}"),
28953 };
28954 assert_eq!(items.len(), 4);
28955 let mut pairs: Vec<(String, Sexp)> = items
28956 .chunks_exact(2)
28957 .map(|c| (c[0].as_keyword().expect("kw").to_string(), c[1].clone()))
28958 .collect();
28959 pairs.sort_by(|a, b| a.0.cmp(&b.0));
28960 assert_eq!(
28961 pairs,
28962 vec![
28963 ("must-reach".to_string(), Sexp::boolean(true)),
28964 ("point-type".to_string(), Sexp::string("Gate")),
28965 ],
28966 );
28967 }
28968
28969 #[test]
28970 fn sexp_from_json_agrees_with_domain_json_to_sexp_for_every_outer_shape() {
28971 // PATH-UNIFORMITY GUARD: pin that the free-function delegate
28972 // `crate::domain::json_to_sexp(v) == Sexp::from_json(v)` for
28973 // every reachable `serde_json::Value` outer shape. Post-lift the
28974 // free function delegates to the inherent associated function;
28975 // this test pins the delegation byte-for-byte so a future
28976 // regression that drifts the delegate (e.g. inlines a stale
28977 // pre-lift body, swaps the iteration order at one site) fires
28978 // here, parallel to `sexp_to_json_method_agrees_with_domain_
28979 // sexp_to_json_for_every_outer_shape`'s posture for the forward
28980 // direction.
28981 let shapes = [
28982 serde_json::Value::Null,
28983 serde_json::Value::Bool(true),
28984 serde_json::Value::Bool(false),
28985 serde_json::json!(7),
28986 serde_json::json!(-3),
28987 serde_json::json!(2.5),
28988 serde_json::Value::String("body".into()),
28989 serde_json::json!([1, 2, 3]),
28990 serde_json::json!({"camelCase": "v", "another-key": 5}),
28991 serde_json::json!({"nested": {"inner": [1, 2]}}),
28992 serde_json::json!([]),
28993 serde_json::json!({}),
28994 ];
28995 for v in &shapes {
28996 assert_eq!(
28997 Sexp::from_json(v),
28998 crate::domain::json_to_sexp(v),
28999 "delegate drifted from inherent associated function for {v}",
29000 );
29001 }
29002 }
29003
29004 #[test]
29005 fn sexp_from_json_object_keys_route_through_camel_to_kebab() {
29006 // KEY-PROJECTION CONTRACT: pin that JSON object keys land in
29007 // the resulting `Sexp::List` as `Sexp::keyword(camel_to_kebab(k))`
29008 // — the inverse of `Sexp::to_json`'s kebab→camel projection.
29009 // A regression that drops the projection (writes the JSON key
29010 // verbatim, breaking the kwarg round-trip), substitutes a
29011 // different camel→kebab implementation at this site, or routes
29012 // through `kebab_to_camel` (the wrong direction) surfaces here.
29013 let obj = serde_json::json!({
29014 "pointType": 1,
29015 "mustReach": 2,
29016 "already-kebab": 3,
29017 "withABC": 4,
29018 });
29019 let result = Sexp::from_json(&obj);
29020 let items = match &result {
29021 Sexp::List(items) => items,
29022 other => panic!("expected List, got {other:?}"),
29023 };
29024 // Even-position elements are keywords; odd-position elements are
29025 // values. Pin the keyword spellings against the camel→kebab
29026 // projection (camel boundaries become `-`; consecutive uppercase
29027 // each get a leading `-` per the implementation in
29028 // `domain::camel_to_kebab`).
29029 let kws: Vec<&str> = items
29030 .iter()
29031 .step_by(2)
29032 .map(|s| s.as_keyword().expect("even position must be keyword"))
29033 .collect();
29034 // Match the order JSON preserve_order gives us — sortable for
29035 // stability; the contract is just that each key landed through
29036 // camel→kebab, not the insertion order itself.
29037 let mut sorted = kws.clone();
29038 sorted.sort();
29039 assert_eq!(
29040 sorted,
29041 vec!["already-kebab", "must-reach", "point-type", "with-a-b-c"],
29042 );
29043 }
29044
29045 #[test]
29046 fn sexp_from_json_number_arm_routes_through_atom_from_json_number() {
29047 // LIFTED-BOUNDARY CONTRACT: pin that the outer `Sexp::from_json`'s
29048 // `serde_json::Value::Number(_)` arm delegates through the
29049 // typed-algebra method `Atom::from_json_number` for every
29050 // reachable `serde_json::Number` shape (i64-backed, finite
29051 // f64-backed, and the empty structural-impossibility residual —
29052 // although `serde_json::Number`'s closed-set discriminator
29053 // excludes the last case in practice, so it's exercised only
29054 // via the direct algebra method). Pre-lift the outer arm
29055 // carried its own inline three-branch cascade (`n.as_i64` then
29056 // `n.as_f64` then `Self::int(0)` typed floor); post-lift the
29057 // arm collapses to `Self::Atom(Atom::from_json_number(n))` and
29058 // the per-variant numeric-axis body binds at ONE typed
29059 // projection on the [`Atom`] algebra. A regression that drifts
29060 // the outer arm (e.g. re-inlines ONE variant's rendering
29061 // without updating `Atom::from_json_number`, or introduces a
29062 // spurious f64→JValue::Null re-wrap) surfaces as an inequality
29063 // here. The sweep covers every Number shape the substrate
29064 // encounters in practice.
29065 //
29066 // Sibling-shape pin to `sexp_to_json_atom_arms_route_through_atom_to_json`
29067 // (in `crate::domain::tests`) — where that pin closes the
29068 // FORWARD `Sexp::Atom → JValue` routing through
29069 // `Atom::to_json`, THIS pin closes the INVERSE
29070 // `JValue::Number → Sexp::Atom` routing through
29071 // `Atom::from_json_number`. Together the two pins pin the
29072 // round-trip closure `Sexp::from_json ∘ Sexp::to_json` for
29073 // the numeric-axis subset AT the algebra layer rather than
29074 // per consumer.
29075 let number_shapes: Vec<serde_json::Number> = vec![
29076 0i64.into(),
29077 1i64.into(),
29078 (-1i64).into(),
29079 42i64.into(),
29080 i64::MAX.into(),
29081 i64::MIN.into(),
29082 serde_json::Number::from_f64(1.5).unwrap(),
29083 serde_json::Number::from_f64(-2.5).unwrap(),
29084 serde_json::Number::from_f64(1.234_567_890_123).unwrap(),
29085 ];
29086 for n in &number_shapes {
29087 let via_outer = Sexp::from_json(&serde_json::Value::Number(n.clone()));
29088 let via_algebra = Sexp::Atom(Atom::from_json_number(n));
29089 assert_eq!(
29090 via_outer, via_algebra,
29091 "Sexp::from_json Number arm drifted from \
29092 Sexp::Atom(Atom::from_json_number(n)) for {n:?}",
29093 );
29094 }
29095 }
29096
29097 // ── Sexp::is_kwargs_list: the kwargs-shape predicate on the algebra ─
29098 //
29099 // `Sexp::is_kwargs_list` lifts the `pub(crate) domain::is_kwargs_list`
29100 // free function onto the inherent-method canonical site on the
29101 // [`Sexp`] algebra — sibling-shape predicate peer of [`Sexp::is_list`]
29102 // narrowing the structural witness to the kwargs-shaped sub-cohort.
29103 // The tests below pin the per-arm contract on the new canonical site
29104 // directly; the `pub(crate)` free function has zero remaining callers
29105 // post-lift and is removed in the same patch so the substrate's
29106 // "kwargs-shape predicate" lives at exactly one canonical site on the
29107 // algebra rather than splitting across a `domain.rs` helper and the
29108 // `Sexp::to_json` call site.
29109
29110 #[test]
29111 fn sexp_is_kwargs_list_method_returns_true_for_canonical_kwargs_shape() {
29112 // PER-ARM CONTRACT (true cell): pin that a `Sexp::List` whose
29113 // even-indexed items are all keywords and whose length is non-zero
29114 // even returns `true` — the canonical kwargs shape `(:k v :k v …)`.
29115 // Covers the two-arity and four-arity baseline cases plus a mixed
29116 // payload (keyword odd index — even-index check is keyword-only,
29117 // odd-index payload is unconstrained per the kwargs convention).
29118 // A regression that drifts the predicate (incorrect parity check,
29119 // wrong keyword-position check, off-by-one in the step) surfaces
29120 // here immediately.
29121 let two = Sexp::List(vec![Sexp::keyword("k"), Sexp::int(1)]);
29122 assert!(two.is_kwargs_list());
29123 let four = Sexp::List(vec![
29124 Sexp::keyword("k1"),
29125 Sexp::int(1),
29126 Sexp::keyword("k2"),
29127 Sexp::string("v2"),
29128 ]);
29129 assert!(four.is_kwargs_list());
29130 // Odd-position values can themselves be keywords; the convention
29131 // only constrains the EVEN positions.
29132 let mixed = Sexp::List(vec![
29133 Sexp::keyword("k1"),
29134 Sexp::keyword("v-is-keyword-too"),
29135 Sexp::keyword("k2"),
29136 Sexp::Nil,
29137 ]);
29138 assert!(mixed.is_kwargs_list());
29139 }
29140
29141 #[test]
29142 fn sexp_is_kwargs_list_method_returns_false_for_non_list_outer_shapes_and_violating_lists() {
29143 // PER-ARM CONTRACT (false cell): pin that every non-`Self::List`
29144 // outer shape (Nil, every Atom payload variant, every quote-family
29145 // wrapper) returns `false`, and that every `Self::List` violating
29146 // the kwargs convention (empty, odd length, non-keyword at any
29147 // even index) also returns `false`. A regression that returns
29148 // `true` for a wrong shape (e.g. claiming a Nil or a non-kwargs
29149 // list satisfies the predicate, opening the door to a
29150 // `Sexp::to_json` arm misrouting) surfaces here immediately.
29151 // Non-list outer shapes:
29152 assert!(!Sexp::Nil.is_kwargs_list());
29153 assert!(!Sexp::symbol("s").is_kwargs_list());
29154 assert!(!Sexp::keyword("k").is_kwargs_list());
29155 assert!(!Sexp::string("body").is_kwargs_list());
29156 assert!(!Sexp::int(0).is_kwargs_list());
29157 assert!(!Sexp::float(0.0).is_kwargs_list());
29158 assert!(!Sexp::boolean(true).is_kwargs_list());
29159 assert!(!Sexp::Quote(Box::new(Sexp::keyword("k"))).is_kwargs_list());
29160 assert!(!Sexp::Quasiquote(Box::new(Sexp::keyword("k"))).is_kwargs_list());
29161 assert!(!Sexp::Unquote(Box::new(Sexp::keyword("k"))).is_kwargs_list());
29162 assert!(!Sexp::UnquoteSplice(Box::new(Sexp::keyword("k"))).is_kwargs_list());
29163 // List arm violations:
29164 assert!(!Sexp::List(vec![]).is_kwargs_list()); // empty
29165 assert!(!Sexp::List(vec![Sexp::keyword("k")]).is_kwargs_list()); // odd length 1
29166 assert!(
29167 !Sexp::List(vec![Sexp::keyword("k1"), Sexp::int(1), Sexp::keyword("k2")])
29168 .is_kwargs_list()
29169 ); // odd length 3
29170 assert!(!Sexp::List(vec![Sexp::int(1), Sexp::int(2)]).is_kwargs_list()); // non-keyword at even 0
29171 assert!(!Sexp::List(vec![
29172 Sexp::keyword("k1"),
29173 Sexp::int(1),
29174 Sexp::symbol("not-kw"),
29175 Sexp::int(2)
29176 ])
29177 .is_kwargs_list()); // non-keyword at even 2
29178 }
29179
29180 #[test]
29181 fn sexp_is_kwargs_list_method_composes_through_as_list_and_atom_as_keyword() {
29182 // COMPOSITION LAW: pin that the lifted predicate composes through
29183 // the already-lifted `Self::as_list` (structural projection onto
29184 // `&[Sexp]`) and `Atom::as_keyword` (typed projection onto the
29185 // keyword payload) primitives — a regression that re-inlines the
29186 // body without routing through the algebra-level soft-projection
29187 // family becomes detectable here. Sweeps every reachable outer
29188 // shape (Nil, every Atom variant, every quote-family wrapper, a
29189 // selection of List shapes covering the true + false cells) and
29190 // asserts the predicate's value agrees with the by-hand
29191 // `as_list().is_some_and(...)` recomposition.
29192 fn by_hand(s: &Sexp) -> bool {
29193 s.as_list().is_some_and(|items| {
29194 !items.is_empty()
29195 && items.len().is_multiple_of(2)
29196 && items.iter().step_by(2).all(|e| e.as_keyword().is_some())
29197 })
29198 }
29199 let cases = [
29200 Sexp::Nil,
29201 Sexp::symbol("s"),
29202 Sexp::keyword("k"),
29203 Sexp::string("body"),
29204 Sexp::int(7),
29205 Sexp::float(2.5),
29206 Sexp::boolean(false),
29207 Sexp::Quote(Box::new(Sexp::keyword("k"))),
29208 Sexp::Quasiquote(Box::new(Sexp::keyword("k"))),
29209 Sexp::Unquote(Box::new(Sexp::keyword("k"))),
29210 Sexp::UnquoteSplice(Box::new(Sexp::keyword("k"))),
29211 Sexp::List(vec![]),
29212 Sexp::List(vec![Sexp::int(1)]),
29213 Sexp::List(vec![Sexp::keyword("k"), Sexp::int(1)]),
29214 Sexp::List(vec![
29215 Sexp::keyword("k1"),
29216 Sexp::int(1),
29217 Sexp::keyword("k2"),
29218 Sexp::int(2),
29219 ]),
29220 Sexp::List(vec![Sexp::int(1), Sexp::int(2)]),
29221 Sexp::List(vec![Sexp::keyword("k1"), Sexp::int(1), Sexp::symbol("x")]),
29222 ];
29223 for s in &cases {
29224 assert_eq!(
29225 s.is_kwargs_list(),
29226 by_hand(s),
29227 "predicate drifted from as_list ∘ atom_as_keyword composition for {s}",
29228 );
29229 }
29230 }
29231
29232 #[test]
29233 fn sexp_to_json_object_arm_routes_through_is_kwargs_list_method() {
29234 // CALLSITE-CONTRACT: pin that `Sexp::to_json`'s kwargs-vs-array
29235 // bifurcation routes through the lifted `Sexp::is_kwargs_list`
29236 // method — the kwargs-shape witness that gates the
29237 // `serde_json::Value::Object` arm vs the `serde_json::Value::Array`
29238 // arm at the `Sexp::List` outer shape. The pin walks the gate
29239 // both directions: a kwargs-shaped list must project as `Object`
29240 // (and the inherent predicate must agree, `true`); a non-kwargs
29241 // list (empty, odd-length, or even-index non-keyword) must
29242 // project as `Array` (and the predicate must agree, `false`). A
29243 // regression that decouples the two paths (e.g. `to_json` routes
29244 // through a re-inlined check while `is_kwargs_list` continues to
29245 // delegate, or vice versa) surfaces here.
29246 // Kwargs-shaped: Object projection, predicate true.
29247 let kw = Sexp::List(vec![Sexp::keyword("foo-bar"), Sexp::int(1)]);
29248 assert!(kw.is_kwargs_list());
29249 assert!(matches!(
29250 kw.to_json().expect("kwargs list projects"),
29251 serde_json::Value::Object(_)
29252 ));
29253 // Non-kwargs (empty list): Array projection, predicate false.
29254 let empty = Sexp::List(vec![]);
29255 assert!(!empty.is_kwargs_list());
29256 assert!(matches!(
29257 empty.to_json().expect("empty list projects"),
29258 serde_json::Value::Array(arr) if arr.is_empty(),
29259 ));
29260 // Non-kwargs (positional): Array projection, predicate false.
29261 let positional = Sexp::List(vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)]);
29262 assert!(!positional.is_kwargs_list());
29263 assert!(matches!(
29264 positional.to_json().expect("positional list projects"),
29265 serde_json::Value::Array(arr) if arr.len() == 3,
29266 ));
29267 // Non-kwargs (even-index non-keyword): Array projection.
29268 let mixed = Sexp::List(vec![
29269 Sexp::keyword("k"),
29270 Sexp::int(1),
29271 Sexp::symbol("x"),
29272 Sexp::int(2),
29273 ]);
29274 assert!(!mixed.is_kwargs_list());
29275 assert!(matches!(
29276 mixed.to_json().expect("mixed list projects"),
29277 serde_json::Value::Array(_)
29278 ));
29279 }
29280
29281 #[test]
29282 fn sexp_from_json_round_trips_to_json_for_canonical_subset() {
29283 // ROUND-TRIP LAW: pin `Sexp::to_json(s)?.from_json() == s` for
29284 // the round-trippable subset of Sexp shapes — Nil, Atom::Str
29285 // (the lossless atomic floor that absorbs Symbol/Keyword on
29286 // re-projection, so this test stays inside the lossless cell),
29287 // Atom::Int, Atom::Float, Atom::Bool, and recursively
29288 // Sexp::List of round-trippable elements. Pin that the inverse
29289 // composes byte-for-byte against the forward projection inside
29290 // the lossless cell — the round-trip law's structural anchor
29291 // documented at `Sexp::from_json`'s docstring.
29292 let cases = [
29293 Sexp::Nil,
29294 Sexp::string("body"),
29295 Sexp::int(42),
29296 Sexp::float(1.5),
29297 Sexp::boolean(true),
29298 Sexp::List(vec![Sexp::int(1), Sexp::string("x"), Sexp::Nil]),
29299 // Empty list → empty array → empty list. Round-trips cleanly.
29300 Sexp::List(vec![]),
29301 ];
29302 for s in &cases {
29303 let projected = s
29304 .to_json()
29305 .expect("round-trippable Sexp must project to JSON");
29306 let recovered = Sexp::from_json(&projected);
29307 assert_eq!(recovered, *s, "round-trip drifted at {s}");
29308 }
29309 }
29310
29311 // ── Atom typed-construct family + Sexp outer-constructor routing ─────
29312 //
29313 // The six `Atom::{symbol, keyword, string, int, float, boolean}`
29314 // typed-construct methods are the section sibling of the existing
29315 // six `Atom::as_{symbol, keyword, string, int, float, bool}` soft-
29316 // projection family — closing the (construct, project) algebra dual
29317 // on the closed-set `Atom` algebra. The six `Sexp::{symbol, ...,
29318 // boolean}` outer constructors now route through
29319 // `Self::Atom(Atom::X(_))` so the `impl Into<String>` ergonomy +
29320 // tuple-variant constructor pair lives at ONE site per kind on the
29321 // `Atom` algebra. Pin the four structural laws:
29322 // (a) each `Atom::X` constructor produces the canonical tuple
29323 // variant payload byte-for-byte (`Atom::symbol("foo") ==
29324 // Atom::Symbol("foo".into())`, etc.) — pre-lift behavior
29325 // under the new construction face;
29326 // (b) the (construct, kind-project) round-trip
29327 // `Atom::X(_).kind() == AtomKind::X` for every (kind, payload)
29328 // pair — the typed-construct family pairs section-for-
29329 // retraction with the `Atom::kind` projection;
29330 // (c) the (construct, soft-project) round-trip
29331 // `Atom::X(payload).as_X() == Some(payload)` for every kind —
29332 // the typed-construct family pairs section-for-retraction
29333 // with the `Atom::as_X` family it now siblings;
29334 // (d) the outer-constructor composition law `Sexp::X(p) ==
29335 // Sexp::Atom(Atom::X(p))` for every kind — the `Sexp` outer
29336 // constructors route through the typed `Atom` constructors
29337 // rather than re-deriving the `Self::Atom(Atom::X(_))` pair
29338 // inline.
29339
29340 #[test]
29341 fn atom_typed_constructors_emit_canonical_tuple_variant_for_every_kind() {
29342 // STRUCTURAL CONSTRUCT CONTRACT: each `Atom::X` constructor
29343 // emits the matching `Atom::Variant(payload)` tuple-variant
29344 // value byte-for-byte. A regression that drifts ONE arm (e.g.
29345 // a typo routing `Atom::keyword(s)` to `Self::Symbol(s.into())`
29346 // — type-checks but silently mis-classifies every kwarg key
29347 // authored through the algebra-level constructor) surfaces
29348 // here. The `impl Into<String>` arms also accept `String`
29349 // payloads — pinned alongside `&str` so the `.into()` ergonomy
29350 // is exercised across both source types.
29351 assert_eq!(Atom::symbol("foo"), Atom::Symbol("foo".into()));
29352 assert_eq!(
29353 Atom::symbol(String::from("seph.1")),
29354 Atom::Symbol("seph.1".into()),
29355 );
29356 assert_eq!(Atom::symbol(""), Atom::Symbol(String::new()));
29357 assert_eq!(Atom::keyword("parent"), Atom::Keyword("parent".into()));
29358 assert_eq!(
29359 Atom::keyword(String::from("attr")),
29360 Atom::Keyword("attr".into()),
29361 );
29362 assert_eq!(Atom::keyword(""), Atom::Keyword(String::new()));
29363 assert_eq!(Atom::string("body"), Atom::Str("body".into()));
29364 assert_eq!(
29365 Atom::string(String::from("with\nnewline")),
29366 Atom::Str("with\nnewline".into()),
29367 );
29368 assert_eq!(Atom::string(""), Atom::Str(String::new()));
29369 assert_eq!(Atom::int(0), Atom::Int(0));
29370 assert_eq!(Atom::int(42), Atom::Int(42));
29371 assert_eq!(Atom::int(-7), Atom::Int(-7));
29372 assert_eq!(Atom::int(i64::MIN), Atom::Int(i64::MIN));
29373 assert_eq!(Atom::int(i64::MAX), Atom::Int(i64::MAX));
29374 assert_eq!(Atom::float(0.0), Atom::Float(0.0));
29375 assert_eq!(Atom::float(1.5), Atom::Float(1.5));
29376 assert_eq!(Atom::float(-2.5), Atom::Float(-2.5));
29377 // NaN compares unequal to itself; pin via `to_bits` round-trip,
29378 // matching the `Hash for Atom` Float-arm posture
29379 // (`f.to_bits().hash(...)`).
29380 assert_eq!(Atom::float(f64::NAN).kind(), AtomKind::Float);
29381 match Atom::float(f64::NAN) {
29382 Atom::Float(n) => assert!(n.is_nan()),
29383 _ => panic!("Atom::float must emit Atom::Float"),
29384 }
29385 assert_eq!(Atom::float(f64::INFINITY), Atom::Float(f64::INFINITY));
29386 assert_eq!(Atom::boolean(true), Atom::Bool(true));
29387 assert_eq!(Atom::boolean(false), Atom::Bool(false));
29388 }
29389
29390 #[test]
29391 fn atom_typed_constructors_round_trip_through_kind_projection() {
29392 // SECTION LAW (construct → kind): every typed constructor's
29393 // output projects through `Atom::kind` to its matching
29394 // `AtomKind` variant. The `(construct, kind-project)` pair
29395 // forms a deterministic surjection from the construct face
29396 // onto the closed-set `AtomKind` algebra — six (kind,
29397 // representative payload) probes sweep `AtomKind::ALL` so a
29398 // future seventh atomic kind landing on the algebra extends
29399 // BOTH the construct face AND this sweep in lockstep (rustc-
29400 // enforced through the closed-set match below).
29401 for kind in AtomKind::ALL {
29402 let constructed = match kind {
29403 AtomKind::Symbol => Atom::symbol("foo"),
29404 AtomKind::Keyword => Atom::keyword("parent"),
29405 AtomKind::Str => Atom::string("body"),
29406 AtomKind::Int => Atom::int(42),
29407 AtomKind::Float => Atom::float(1.5),
29408 AtomKind::Bool => Atom::boolean(true),
29409 };
29410 assert_eq!(
29411 constructed.kind(),
29412 kind,
29413 "Atom typed constructor for {kind:?} drifted from its closed-set kind projection",
29414 );
29415 }
29416 }
29417
29418 #[test]
29419 fn atom_typed_constructors_round_trip_through_per_variant_soft_projection() {
29420 // RETRACTION LAW (construct → soft-project): every typed
29421 // constructor's output projects through its matching `Atom::as_X`
29422 // soft projection to `Some(payload)` — the (construct, soft-
29423 // project) pair forms an `Iso(payload, Atom::Variant(payload))`
29424 // on the typed-payload axis. Sibling-axis to the
29425 // `(construct, kind-project)` pair above and to the
29426 // `Sexp::as_quote_form / QuoteForm::wrap` round-trip on the
29427 // outer-shape axis (`QuoteForm::wrap(inner).as_quote_form()
29428 // == Some((qf, &inner))`). The retraction's load-bearing
29429 // contract is what the substrate's named-form NAME gate
29430 // (`split_name_slot` → `as_symbol_or_string`) depends on at
29431 // every typed-domain dispatcher.
29432 assert_eq!(Atom::symbol("foo").as_symbol(), Some("foo"));
29433 assert_eq!(Atom::symbol("").as_symbol(), Some(""));
29434 assert_eq!(Atom::keyword("parent").as_keyword(), Some("parent"));
29435 assert_eq!(Atom::keyword("").as_keyword(), Some(""));
29436 assert_eq!(Atom::string("body").as_string(), Some("body"));
29437 assert_eq!(Atom::string("").as_string(), Some(""));
29438 assert_eq!(Atom::int(42).as_int(), Some(42));
29439 assert_eq!(Atom::int(0).as_int(), Some(0));
29440 assert_eq!(Atom::int(i64::MIN).as_int(), Some(i64::MIN));
29441 assert_eq!(Atom::float(1.5).as_float(), Some(1.5));
29442 assert_eq!(Atom::float(0.0).as_float(), Some(0.0));
29443 assert_eq!(Atom::boolean(true).as_bool(), Some(true));
29444 assert_eq!(Atom::boolean(false).as_bool(), Some(false));
29445 }
29446
29447 #[test]
29448 fn sexp_outer_constructors_route_through_atom_typed_construct_family() {
29449 // OUTER-CONSTRUCTOR COMPOSITION LAW: pin that each `Sexp::X`
29450 // outer constructor emits `Sexp::Atom(Atom::X(_))` byte-for-byte
29451 // — a regression that re-inlines the pre-lift body
29452 // `Self::Atom(Atom::Variant(s.into()))` and drifts ONE arm
29453 // (e.g. a future copy-edit that swaps `Sexp::symbol` to route
29454 // through `Atom::Keyword` after a refactor) becomes detectable
29455 // at this site. Sibling-shape pin to the `Sexp::as_X` family's
29456 // structural-lift composition through `Sexp::as_atom +
29457 // Atom::as_X` on the projection axis (sweep posture in
29458 // `sexp_as_symbol_or_string_routes_through_atom_as_symbol_or_string_via_as_atom_composition`).
29459 assert_eq!(Sexp::symbol("foo"), Sexp::Atom(Atom::symbol("foo")));
29460 assert_eq!(Sexp::symbol(""), Sexp::Atom(Atom::symbol("")));
29461 assert_eq!(
29462 Sexp::symbol(String::from("seph.1")),
29463 Sexp::Atom(Atom::symbol("seph.1")),
29464 );
29465 assert_eq!(Sexp::keyword("parent"), Sexp::Atom(Atom::keyword("parent")),);
29466 assert_eq!(Sexp::string("body"), Sexp::Atom(Atom::string("body")));
29467 assert_eq!(Sexp::int(42), Sexp::Atom(Atom::int(42)));
29468 assert_eq!(Sexp::int(i64::MIN), Sexp::Atom(Atom::int(i64::MIN)));
29469 assert_eq!(Sexp::float(1.5), Sexp::Atom(Atom::float(1.5)));
29470 assert_eq!(Sexp::boolean(true), Sexp::Atom(Atom::boolean(true)));
29471 assert_eq!(Sexp::boolean(false), Sexp::Atom(Atom::boolean(false)));
29472 }
29473
29474 #[test]
29475 fn atom_typed_constructors_partition_atom_kind_across_constructed_payloads() {
29476 // PARTITION LAW: every typed constructor's output projects to
29477 // `Some(_)` on its matching soft projection AND to `None` on
29478 // every other soft projection. The (construct, soft-project)
29479 // matrix is the diagonal of `AtomKind::ALL × AtomKind::ALL`:
29480 // on-diagonal cells return `Some`, off-diagonal cells return
29481 // `None`. Pin the full matrix so a regression that conflates
29482 // two construct arms (e.g. a future `Atom::keyword(s)` typo
29483 // routing to `Self::Symbol(s.into())` — type-checks, passes
29484 // the kind-projection sweep above iff the typo also drifts
29485 // `Atom::kind`, but fails THIS sweep because the off-diagonal
29486 // `Atom::keyword(s).as_symbol() == None` cell flips to `Some`)
29487 // surfaces structurally. The matrix's diagonal-restriction
29488 // form rebuilds the closed-set partition law every soft-
29489 // projection sweep above pins per-axis into ONE joint pin
29490 // across the (construct, project) algebra dual.
29491 let constructed = [
29492 (AtomKind::Symbol, Atom::symbol("foo")),
29493 (AtomKind::Keyword, Atom::keyword("parent")),
29494 (AtomKind::Str, Atom::string("body")),
29495 (AtomKind::Int, Atom::int(42)),
29496 (AtomKind::Float, Atom::float(1.5)),
29497 (AtomKind::Bool, Atom::boolean(true)),
29498 ];
29499 for (built_kind, a) in &constructed {
29500 assert_eq!(
29501 a.as_symbol().is_some(),
29502 *built_kind == AtomKind::Symbol,
29503 "as_symbol partition row drifted for {built_kind:?}",
29504 );
29505 assert_eq!(
29506 a.as_keyword().is_some(),
29507 *built_kind == AtomKind::Keyword,
29508 "as_keyword partition row drifted for {built_kind:?}",
29509 );
29510 assert_eq!(
29511 a.as_string().is_some(),
29512 *built_kind == AtomKind::Str,
29513 "as_string partition row drifted for {built_kind:?}",
29514 );
29515 assert_eq!(
29516 a.as_int().is_some(),
29517 *built_kind == AtomKind::Int,
29518 "as_int partition row drifted for {built_kind:?}",
29519 );
29520 assert_eq!(
29521 a.as_float().is_some(),
29522 *built_kind == AtomKind::Float,
29523 "as_float partition row drifted for {built_kind:?}",
29524 );
29525 assert_eq!(
29526 a.as_bool().is_some(),
29527 *built_kind == AtomKind::Bool,
29528 "as_bool partition row drifted for {built_kind:?}",
29529 );
29530 }
29531 }
29532
29533 // ── Sexp quote-family typed-construct algebra ────────────────────────
29534 //
29535 // `Sexp::quote` / `Sexp::quasiquote` / `Sexp::unquote` /
29536 // `Sexp::unquote_splice` are the outer-Sexp typed-construct family for
29537 // the four homoiconic prefix wrappers, section-for-retraction with the
29538 // `Sexp::as_quote_form` soft-projection sibling. Each routes through
29539 // `QuoteForm::X.wrap(inner)` so the (marker, `Sexp::* tuple-variant
29540 // constructor + `Box::new`) welded triple lives at ONE site on the
29541 // closed-set `QuoteForm` algebra. Pin FOUR structural laws:
29542 // (a) the canonical-tuple emission
29543 // `Sexp::quote(inner) == Sexp::Quote(Box::new(inner))` for
29544 // every wrapper marker — the typed constructor pairs section-
29545 // for-retraction with the tuple-variant constructor;
29546 // (b) the composition law
29547 // `Sexp::X_variant(inner) == QuoteForm::X.wrap(inner)` for
29548 // every marker — the outer typed constructor routes through
29549 // the inner-algebra `QuoteForm::wrap` typed dispatch;
29550 // (c) the round-trip law
29551 // `Sexp::X_variant(inner).as_quote_form() == Some((QuoteForm::X,
29552 // &inner))` for every marker — the (construct, soft-project)
29553 // algebra dual closes on the outer [`Sexp`] algebra with
29554 // marker + inner-body cross-projection preserved;
29555 // (d) the outer-shape pairing
29556 // `Sexp::X_variant(inner).shape() == QuoteForm::X.sexp_shape()`
29557 // for every marker — the construct family composes coherently
29558 // through the outer-shape projection on the typed-shape
29559 // lattice, so a regression that drifts ONE marker's outer-
29560 // shape pairing from `QuoteForm::sexp_shape` surfaces here.
29561
29562 #[test]
29563 fn sexp_quote_family_constructors_emit_canonical_tuple_variant_for_every_marker() {
29564 // STRUCTURAL CONSTRUCT CONTRACT: each `Sexp::X_variant`
29565 // constructor emits the matching `Sexp::X(Box::new(inner))`
29566 // tuple-variant value byte-for-byte. A regression that drifts
29567 // ONE arm (e.g. a typo routing `Sexp::unquote(inner)` to
29568 // `Sexp::UnquoteSplice(Box::new(inner))` — type-checks but
29569 // silently mis-classifies every macro-template substitution
29570 // authored through the algebra-level constructor) surfaces
29571 // here. Sibling-shape pin to the `Atom` typed-construct
29572 // family's canonical-tuple-variant test posture
29573 // (`atom_typed_constructors_emit_canonical_tuple_variant_for_every_kind`).
29574 let payloads = [
29575 Sexp::Nil,
29576 Sexp::symbol("x"),
29577 Sexp::keyword("k"),
29578 Sexp::string("body"),
29579 Sexp::int(42),
29580 Sexp::boolean(true),
29581 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
29582 ];
29583 for inner in &payloads {
29584 assert_eq!(
29585 Sexp::quote(inner.clone()),
29586 Sexp::Quote(Box::new(inner.clone())),
29587 "Sexp::quote drifted from canonical tuple variant for {inner:?}",
29588 );
29589 assert_eq!(
29590 Sexp::quasiquote(inner.clone()),
29591 Sexp::Quasiquote(Box::new(inner.clone())),
29592 "Sexp::quasiquote drifted from canonical tuple variant for {inner:?}",
29593 );
29594 assert_eq!(
29595 Sexp::unquote(inner.clone()),
29596 Sexp::Unquote(Box::new(inner.clone())),
29597 "Sexp::unquote drifted from canonical tuple variant for {inner:?}",
29598 );
29599 assert_eq!(
29600 Sexp::unquote_splice(inner.clone()),
29601 Sexp::UnquoteSplice(Box::new(inner.clone())),
29602 "Sexp::unquote_splice drifted from canonical tuple variant for {inner:?}",
29603 );
29604 }
29605 }
29606
29607 #[test]
29608 fn sexp_quote_family_constructors_route_through_quote_form_wrap() {
29609 // COMPOSITION LAW: pin that each `Sexp::X_variant` outer
29610 // constructor emits `QuoteForm::X.wrap(inner)` byte-for-byte —
29611 // a regression that re-inlines the pre-lift body
29612 // `Self::X(Box::new(inner))` and drifts ONE arm (e.g. a future
29613 // copy-edit that swaps `Sexp::quote` to route through
29614 // `QuoteForm::Quasiquote` after a refactor) becomes detectable
29615 // at this site. Sibling-shape pin to the `Sexp::X_atom` family's
29616 // composition-through-`Atom::X` posture
29617 // (`sexp_outer_constructors_route_through_atom_typed_construct_family`).
29618 let inner = Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]);
29619 assert_eq!(
29620 Sexp::quote(inner.clone()),
29621 QuoteForm::Quote.wrap(inner.clone())
29622 );
29623 assert_eq!(
29624 Sexp::quasiquote(inner.clone()),
29625 QuoteForm::Quasiquote.wrap(inner.clone()),
29626 );
29627 assert_eq!(
29628 Sexp::unquote(inner.clone()),
29629 QuoteForm::Unquote.wrap(inner.clone())
29630 );
29631 assert_eq!(
29632 Sexp::unquote_splice(inner.clone()),
29633 QuoteForm::UnquoteSplice.wrap(inner.clone()),
29634 );
29635 }
29636
29637 #[test]
29638 fn sexp_quote_family_constructors_round_trip_through_as_quote_form() {
29639 // ROUND-TRIP LAW (construct → soft-project): every quote-family
29640 // typed constructor's output projects through `Sexp::as_quote_form`
29641 // to `Some((matching QuoteForm, &inner))`. Sweeps `QuoteForm::ALL`
29642 // paired with a representative inner payload — the four
29643 // (construct, project) pairs form an `Iso(inner, Sexp::X(inner))`
29644 // on the typed-marker axis at the outer [`Sexp`] algebra. A
29645 // regression that drifts ONE marker's construct arm (marker/
29646 // constructor swap) fails BOTH the marker-projection AND the
29647 // inner-borrow round-trip. Sibling-shape pin to the `Atom` typed-
29648 // construct family's per-variant soft-projection round-trip test
29649 // posture
29650 // (`atom_typed_constructors_round_trip_through_per_variant_soft_projection`).
29651 let inner = Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]);
29652 let constructed: [(QuoteForm, Sexp); 4] = [
29653 (QuoteForm::Quote, Sexp::quote(inner.clone())),
29654 (QuoteForm::Quasiquote, Sexp::quasiquote(inner.clone())),
29655 (QuoteForm::Unquote, Sexp::unquote(inner.clone())),
29656 (
29657 QuoteForm::UnquoteSplice,
29658 Sexp::unquote_splice(inner.clone()),
29659 ),
29660 ];
29661 for qf in QuoteForm::ALL {
29662 let (built_qf, sexp) = constructed
29663 .iter()
29664 .find(|(m, _)| *m == qf)
29665 .expect("QuoteForm::ALL sweep must reach every marker");
29666 assert_eq!(*built_qf, qf);
29667 let (proj_qf, proj_inner) = sexp
29668 .as_quote_form()
29669 .unwrap_or_else(|| panic!("construct→as_quote_form drifted at {qf:?}"));
29670 assert_eq!(
29671 proj_qf, qf,
29672 "typed-marker round-trip drifted at {qf:?} — construct+project pair broken",
29673 );
29674 assert_eq!(
29675 proj_inner, &inner,
29676 "inner-body round-trip drifted at {qf:?} — construct+project pair broken",
29677 );
29678 }
29679 }
29680
29681 #[test]
29682 fn sexp_quote_family_constructors_compose_with_shape_via_quote_form_sexp_shape() {
29683 // OUTER-SHAPE COMPOSITION LAW: every quote-family typed
29684 // constructor's output projects through `Sexp::shape` to the
29685 // matching `QuoteForm::X.sexp_shape()` — the (construct,
29686 // outer-shape) composition binds through the closed-set
29687 // `QuoteForm::sexp_shape` embed already lifted onto the
29688 // typed-shape lattice. A regression that drifts ONE construct
29689 // arm's outer-shape from `QuoteForm::sexp_shape` (e.g. a future
29690 // marker/wrapper swap that surfaces through the typed-shape
29691 // lattice but not through the tuple-variant emission itself)
29692 // surfaces here alongside the round-trip pin. Sibling-shape pin
29693 // to `quote_form_sexp_shape_paired_with_as_quote_form_preserves_pre_lift_pairing_for_every_sexp`
29694 // on the projection axis — this pin closes the same axis on the
29695 // outer construct family.
29696 let inner = Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]);
29697 let constructed: [(QuoteForm, Sexp); 4] = [
29698 (QuoteForm::Quote, Sexp::quote(inner.clone())),
29699 (QuoteForm::Quasiquote, Sexp::quasiquote(inner.clone())),
29700 (QuoteForm::Unquote, Sexp::unquote(inner.clone())),
29701 (
29702 QuoteForm::UnquoteSplice,
29703 Sexp::unquote_splice(inner.clone()),
29704 ),
29705 ];
29706 for (qf, sexp) in &constructed {
29707 assert_eq!(
29708 sexp.shape(),
29709 qf.sexp_shape(),
29710 "Sexp::X_variant→shape drifted from QuoteForm::sexp_shape at {qf:?}",
29711 );
29712 }
29713 }
29714
29715 // ── Sexp::list residual-axis typed-construct algebra ─────────────────
29716 //
29717 // `Sexp::list(items)` is the residual-axis section-for-retraction
29718 // sibling of the pre-existing `Sexp::as_list` soft-projection — the
29719 // (construct, project) algebra dual on the 2-of-12 residual carving of
29720 // the [`SexpShape`] closed set now closes at ONE constructor + ONE
29721 // projection on the outer [`Sexp`] algebra, symmetric with the atomic-
29722 // payload carving's (six `Sexp::X_atom(payload)` constructors +
29723 // `Sexp::as_atom` / `Sexp::as_atom_kind` projections) and the quote-
29724 // family carving's (four `Sexp::X_variant(inner)` constructors +
29725 // `Sexp::as_quote_form` / `Sexp::as_quote_form_marker` projections).
29726 // [`Sexp::Nil`] is a unit variant with no payload — the residual-axis
29727 // construct family closes at ONE constructor (the sole payload-bearing
29728 // residual arm). Pin FIVE structural laws:
29729 // (a) the canonical-tuple emission
29730 // `Sexp::list(items) == Sexp::List(items.into_iter().collect())`
29731 // across representative empty / single-element / multi-element /
29732 // heterogeneous-inner samples — the typed constructor pairs
29733 // section-for-retraction with the tuple-variant constructor;
29734 // (b) the round-trip law
29735 // `Sexp::list(items.clone()).as_list() == Some(items.as_slice())`
29736 // — the (construct, soft-project) algebra dual closes on the
29737 // outer [`Sexp`] algebra with the borrowed-slice cross-
29738 // projection preserving identity;
29739 // (c) the outer-shape law
29740 // `Sexp::list(items).shape() == SexpShape::List` — the residual-
29741 // arm outer-shape identity binds through the typed-shape
29742 // lattice at ONE arm, symmetric with the quote-family
29743 // construct family's `Sexp::X_variant(inner).shape() ==
29744 // QuoteForm::X.sexp_shape()`;
29745 // (d) the structural-kind law
29746 // `Sexp::list(items).as_structural_kind() == Some(
29747 // StructuralKind::List)` — the residual carving marker binds
29748 // through the closed-set [`StructuralKind`] algebra at ONE
29749 // arm, symmetric with the atomic-axis's
29750 // `Sexp::X_atom(payload).as_atom_kind() == Some(AtomKind::X)`;
29751 // (e) the input-shape flexibility
29752 // `Sexp::list(&Vec<Sexp>)` / `Sexp::list([Sexp; N])` /
29753 // `Sexp::list(iter::map(...))` all agree with the canonical
29754 // tuple-variant emission — the `impl IntoIterator<Item = Sexp>`
29755 // bound accepts every reasonable owned-sequence shape without a
29756 // per-consumer `.collect::<Vec<Sexp>>()` coercion.
29757
29758 #[test]
29759 fn sexp_list_constructor_emits_canonical_tuple_variant_across_representative_inputs() {
29760 // STRUCTURAL CONSTRUCT CONTRACT: `Sexp::list(items)` emits
29761 // `Sexp::List(items.into_iter().collect::<Vec<Sexp>>())` byte-
29762 // for-byte across representative empty, single-element, multi-
29763 // element, and heterogeneous-inner samples. A regression that
29764 // drifts the body (e.g. wrapping items in an extra `Sexp::Nil`
29765 // sentinel, deduplicating, filtering) surfaces here. Sibling-
29766 // shape pin to the quote-family construct family's canonical-
29767 // tuple-variant test posture
29768 // (`sexp_quote_family_constructors_emit_canonical_tuple_variant_for_every_marker`).
29769 let samples: [Vec<Sexp>; 5] = [
29770 vec![],
29771 vec![Sexp::symbol("only")],
29772 vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)],
29773 vec![
29774 Sexp::Nil,
29775 Sexp::keyword("k"),
29776 Sexp::string("body"),
29777 Sexp::boolean(true),
29778 Sexp::List(vec![Sexp::symbol("nested")]),
29779 ],
29780 vec![
29781 Sexp::Quote(Box::new(Sexp::symbol("x"))),
29782 Sexp::Quasiquote(Box::new(Sexp::List(vec![
29783 Sexp::symbol("template"),
29784 Sexp::Unquote(Box::new(Sexp::symbol("var"))),
29785 ]))),
29786 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
29787 ],
29788 ];
29789 for items in &samples {
29790 assert_eq!(
29791 Sexp::list(items.clone()),
29792 Sexp::List(items.clone()),
29793 "Sexp::list drifted from canonical Sexp::List(_) tuple variant for {items:?}",
29794 );
29795 }
29796 }
29797
29798 #[test]
29799 fn sexp_list_constructor_round_trips_through_as_list() {
29800 // ROUND-TRIP LAW (section-for-retraction on the residual axis):
29801 // `Sexp::list(items.clone()).as_list() == Some(items.as_slice())`
29802 // sweeps the same representative input matrix as the canonical-
29803 // tuple pin — proves the (construct, soft-project) pair forms an
29804 // `Iso(Vec<Sexp>, Sexp::List(Vec<Sexp>))` on the residual axis,
29805 // symmetric with the quote-family axis's `Sexp::X_variant(inner)
29806 // .as_quote_form() == Some((QuoteForm::X, &inner))` round-trip
29807 // (pinned by `sexp_quote_family_constructors_round_trip_through_as_quote_form`).
29808 // A regression that mis-implements `Sexp::list` (e.g. dropping
29809 // items, cloning off-by-one) fails here on top of the canonical-
29810 // tuple pin.
29811 let samples: [Vec<Sexp>; 4] = [
29812 vec![],
29813 vec![Sexp::symbol("solo")],
29814 vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)],
29815 vec![
29816 Sexp::Nil,
29817 Sexp::List(vec![Sexp::symbol("nested"), Sexp::int(7)]),
29818 Sexp::Quote(Box::new(Sexp::symbol("q"))),
29819 ],
29820 ];
29821 for items in &samples {
29822 let built = Sexp::list(items.clone());
29823 assert_eq!(
29824 built.as_list(),
29825 Some(items.as_slice()),
29826 "Sexp::list→as_list round-trip drifted for {items:?}",
29827 );
29828 }
29829 }
29830
29831 #[test]
29832 fn sexp_list_constructor_composes_with_shape_via_sexp_shape_list() {
29833 // OUTER-SHAPE COMPOSITION LAW: every `Sexp::list(items)` output
29834 // projects through `Sexp::shape` to `SexpShape::List` regardless
29835 // of inner-item content — the (construct, outer-shape)
29836 // composition binds through the typed-shape lattice's residual-
29837 // arm at ONE arm. Sibling-shape pin to the quote-family construct
29838 // family's outer-shape composition
29839 // (`sexp_quote_family_constructors_compose_with_shape_via_quote_form_sexp_shape`).
29840 // A regression that reroutes `Sexp::list` through another shape
29841 // arm (e.g. wrapping in `Sexp::Quote` after a copy-edit that
29842 // type-checks) surfaces here alongside the canonical-tuple pin.
29843 let samples: [Vec<Sexp>; 4] = [
29844 vec![],
29845 vec![Sexp::symbol("only")],
29846 vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)],
29847 vec![
29848 Sexp::Nil,
29849 Sexp::Quote(Box::new(Sexp::symbol("x"))),
29850 Sexp::List(vec![Sexp::symbol("nested")]),
29851 ],
29852 ];
29853 for items in &samples {
29854 assert_eq!(
29855 Sexp::list(items.clone()).shape(),
29856 SexpShape::List,
29857 "Sexp::list→shape drifted from SexpShape::List for {items:?}",
29858 );
29859 }
29860 }
29861
29862 #[test]
29863 fn sexp_list_constructor_composes_with_as_structural_kind() {
29864 // STRUCTURAL-KIND COMPOSITION LAW: every `Sexp::list(items)`
29865 // output projects through `Sexp::as_structural_kind` to
29866 // `Some(StructuralKind::List)` regardless of inner-item content
29867 // — the residual carving marker binds through the closed-set
29868 // `StructuralKind` algebra at ONE arm. Sibling-shape pin to the
29869 // atomic-axis's `Sexp::X_atom(payload).as_atom_kind() ==
29870 // Some(AtomKind::X)` marker composition. A regression that
29871 // reroutes `Sexp::list` through a non-residual arm (e.g. a copy-
29872 // edit that wraps items in `Sexp::Quote`) surfaces here through
29873 // the returned marker no longer being `StructuralKind::List`.
29874 let samples: [Vec<Sexp>; 4] = [
29875 vec![],
29876 vec![Sexp::symbol("only")],
29877 vec![Sexp::keyword("k"), Sexp::string("v")],
29878 vec![
29879 Sexp::Nil,
29880 Sexp::List(vec![Sexp::symbol("nested")]),
29881 Sexp::Unquote(Box::new(Sexp::symbol("var"))),
29882 ],
29883 ];
29884 for items in &samples {
29885 assert_eq!(
29886 Sexp::list(items.clone()).as_structural_kind(),
29887 Some(StructuralKind::List),
29888 "Sexp::list→as_structural_kind drifted from Some(StructuralKind::List) for {items:?}",
29889 );
29890 }
29891 }
29892
29893 #[test]
29894 fn sexp_list_constructor_accepts_diverse_intoiterator_input_shapes() {
29895 // INPUT-SHAPE FLEXIBILITY: the `impl IntoIterator<Item = Sexp>`
29896 // bound accepts every reasonable owned-sequence shape without a
29897 // per-consumer `.collect::<Vec<Sexp>>()` coercion at the call
29898 // site — pin that `Vec<Sexp>`, `[Sexp; N]` array, `iter::empty
29899 // ::<Sexp>()`, and `.map(...)` iterator chains all reach the
29900 // same canonical tuple-variant output. A regression that
29901 // narrows the bound (e.g. taking `&[Sexp]` or `Vec<Sexp>` only)
29902 // fails this pin. The IntoIterator bound is load-bearing for the
29903 // ergonomy claim in the docstring — consumers threading a `.map`
29904 // chain through the outer algebra must not need an intermediate
29905 // `.collect()` before handing the result to `Sexp::list`.
29906 let expected = Sexp::List(vec![
29907 Sexp::symbol("a"),
29908 Sexp::symbol("b"),
29909 Sexp::symbol("c"),
29910 ]);
29911 // Vec<Sexp> — the canonical owned-sequence shape.
29912 assert_eq!(
29913 Sexp::list(vec![
29914 Sexp::symbol("a"),
29915 Sexp::symbol("b"),
29916 Sexp::symbol("c"),
29917 ]),
29918 expected,
29919 "Sexp::list drifted for Vec<Sexp> input",
29920 );
29921 // [Sexp; N] — array-literal shape (elements moved out of the
29922 // fixed-size array via the `IntoIterator` impl on `[T; N]`).
29923 assert_eq!(
29924 Sexp::list([Sexp::symbol("a"), Sexp::symbol("b"), Sexp::symbol("c"),]),
29925 expected,
29926 "Sexp::list drifted for [Sexp; N] input",
29927 );
29928 // `iter::empty::<Sexp>()` — the zero-item iterator shape.
29929 assert_eq!(
29930 Sexp::list(std::iter::empty::<Sexp>()),
29931 Sexp::List(vec![]),
29932 "Sexp::list drifted for iter::empty input",
29933 );
29934 // `.map(...)` iterator chain — the composition shape the
29935 // docstring's ergonomy claim rests on.
29936 assert_eq!(
29937 Sexp::list(["a", "b", "c"].iter().map(|s| Sexp::symbol(*s))),
29938 expected,
29939 "Sexp::list drifted for iterator-map chain input",
29940 );
29941 // `once(head).chain(tail)` — the head-then-rest shape a builder
29942 // consuming `head_symbol` + the tail slice threads through.
29943 assert_eq!(
29944 Sexp::list(
29945 std::iter::once(Sexp::symbol("a")).chain([Sexp::symbol("b"), Sexp::symbol("c")]),
29946 ),
29947 expected,
29948 "Sexp::list drifted for once+chain input",
29949 );
29950 }
29951
29952 // ── Sexp::call — call-form (symbol-headed list) construct ──────────
29953 //
29954 // `Sexp::call(head, args)` is the section-for-retraction dual of the
29955 // soft-projection `Sexp::as_call() -> Option<(&str, &[Sexp])>` — it
29956 // embeds a fresh `(head string, item sequence)` pair into a symbol-
29957 // headed `Sexp::List` value at ONE site on the outer `Sexp` algebra,
29958 // composing the atomic-payload construct family's `Sexp::symbol` (for
29959 // the head position) with the residual-axis construct family's
29960 // `Sexp::list` (for the list wrapper) via `std::iter::once(head_sexp)
29961 // .chain(args)`. Pre-lift the composition lived inline at every
29962 // consumer that built a `(defX …)` typed-domain call form, a
29963 // macroexpander template head, or a synthetic dispatch form —
29964 // `Sexp::List(vec![Sexp::symbol(head), args...])` or `Sexp::List(
29965 // std::iter::once(Sexp::symbol(head)).chain(args).collect())` was the
29966 // welded three-method open coding. Post-lift the closure binds at
29967 // ONE typed-algebra method.
29968 //
29969 // These pins cover:
29970 // (a) the composition law
29971 // `Sexp::call(head, args) == Sexp::list(std::iter::once(
29972 // Sexp::symbol(head)).chain(args))` — the constructor body is
29973 // BY DEFINITION the two-method composition;
29974 // (b) the round-trip law
29975 // `Sexp::call(head, args.clone()).as_call() == Some((head,
29976 // args.as_slice()))` — the (construct, project) call-form
29977 // algebra dual closes at this pair, symmetric with the
29978 // residual-axis's `Sexp::list(items.clone()).as_list() ==
29979 // Some(items.as_slice())` round-trip;
29980 // (c) the keyword-matched round-trip law
29981 // `Sexp::call(head, args.clone()).as_call_to(head) == Some(
29982 // args.as_slice())` — the keyword-typed projection recovers
29983 // the args tail iff its argument matches the constructor's
29984 // head;
29985 // (d) the head-symbol composition law
29986 // `Sexp::call(head, args).head_symbol() == Some(head)` — the
29987 // head-position projection recovers the constructor's head
29988 // byte-for-byte;
29989 // (e) the outer-shape composition law
29990 // `Sexp::call(head, args).shape() == SexpShape::List` — a
29991 // call form is a list-shaped `Sexp`;
29992 // (f) the structural-kind composition law
29993 // `Sexp::call(head, args).as_structural_kind() == Some(
29994 // StructuralKind::List)` — the residual carving marker binds
29995 // through the closed-set `StructuralKind` algebra at ONE
29996 // arm, symmetric with the residual-axis's `Sexp::list(items)
29997 // .as_structural_kind() == Some(StructuralKind::List)` marker
29998 // composition;
29999 // (g) the input-shape flexibility
30000 // `Sexp::call("h", Vec<Sexp>)` / `Sexp::call(String, [Sexp;
30001 // N])` / `Sexp::call(&String, iter::map(...))` all agree with
30002 // the canonical composition emission — the `impl Into<String>`
30003 // head bound + `impl IntoIterator<Item = Sexp>` args bound
30004 // accept every reasonable input shape without a per-consumer
30005 // `.to_string()` / `.collect::<Vec<Sexp>>()` coercion.
30006
30007 #[test]
30008 fn sexp_call_constructor_body_matches_canonical_two_method_composition_across_representative_inputs(
30009 ) {
30010 // COMPOSITION LAW: `Sexp::call(head, args) == Sexp::list(
30011 // std::iter::once(Sexp::symbol(head)).chain(args))` for every
30012 // representative (empty-args, single-arg, multi-arg,
30013 // heterogeneous-inner, quote-family-wrapping-inner) sample. A
30014 // regression that drifts the body (e.g. a copy-edit that
30015 // switches to `Sexp::keyword(head)` for the head position, or
30016 // that reorders `head` and `args` in the chain) surfaces here
30017 // BEFORE the projection pins fail. Sibling-shape pin to the
30018 // residual-axis's canonical-composition test posture
30019 // (`sexp_list_constructor_emits_canonical_tuple_variant_across_representative_inputs`).
30020 let samples: [(&str, Vec<Sexp>); 5] = [
30021 ("defcompiler", vec![]),
30022 ("defpoint", vec![Sexp::symbol("obs")]),
30023 (
30024 "defpoint",
30025 vec![
30026 Sexp::symbol("obs"),
30027 Sexp::keyword("class"),
30028 Sexp::symbol("Gate"),
30029 ],
30030 ),
30031 (
30032 "defcheck",
30033 vec![
30034 Sexp::List(vec![Sexp::symbol("crd-in-sync")]),
30035 Sexp::keyword("params"),
30036 Sexp::int(42),
30037 Sexp::string("body"),
30038 Sexp::boolean(true),
30039 ],
30040 ),
30041 (
30042 "defalert-policy",
30043 vec![
30044 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30045 Sexp::Quasiquote(Box::new(Sexp::List(vec![
30046 Sexp::symbol("template"),
30047 Sexp::Unquote(Box::new(Sexp::symbol("var"))),
30048 ]))),
30049 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
30050 ],
30051 ),
30052 ];
30053 for (head, args) in &samples {
30054 let expected =
30055 Sexp::list(std::iter::once(Sexp::symbol(*head)).chain(args.iter().cloned()));
30056 assert_eq!(
30057 Sexp::call(*head, args.clone()),
30058 expected,
30059 "Sexp::call drifted from Sexp::list(once(symbol(head)).chain(args)) for head={head:?} args={args:?}",
30060 );
30061 }
30062 }
30063
30064 #[test]
30065 fn sexp_call_constructor_round_trips_through_as_call() {
30066 // ROUND-TRIP LAW (section-for-retraction with the call-form
30067 // soft-projection): `Sexp::call(head, args.clone()).as_call()
30068 // == Some((head, args.as_slice()))` sweeps the same
30069 // representative input matrix as the composition-law pin —
30070 // proves the (construct, soft-project) pair forms an
30071 // `Iso((&str, Vec<Sexp>), symbol-headed Sexp::List)` on the
30072 // call-form typed decomposition. Sibling-shape pin to the
30073 // residual-axis's `Sexp::list(items.clone()).as_list() ==
30074 // Some(items.as_slice())` round-trip
30075 // (`sexp_list_constructor_round_trips_through_as_list`).
30076 let samples: [(&str, Vec<Sexp>); 4] = [
30077 ("defcompiler", vec![]),
30078 ("defpoint", vec![Sexp::symbol("solo")]),
30079 (
30080 "defmonitor",
30081 vec![Sexp::symbol("m"), Sexp::int(1), Sexp::int(2)],
30082 ),
30083 (
30084 "defnotify",
30085 vec![
30086 Sexp::Nil,
30087 Sexp::List(vec![Sexp::symbol("nested"), Sexp::int(7)]),
30088 Sexp::Quote(Box::new(Sexp::symbol("q"))),
30089 ],
30090 ),
30091 ];
30092 for (head, args) in &samples {
30093 let built = Sexp::call(*head, args.clone());
30094 assert_eq!(
30095 built.as_call(),
30096 Some((*head, args.as_slice())),
30097 "Sexp::call→as_call round-trip drifted for head={head:?} args={args:?}",
30098 );
30099 }
30100 }
30101
30102 #[test]
30103 fn sexp_call_constructor_round_trips_through_as_call_to_matching_keyword() {
30104 // KEYWORD-MATCHED ROUND-TRIP LAW: `Sexp::call(head, args
30105 // .clone()).as_call_to(head) == Some(args.as_slice())` for the
30106 // head-matched keyword, and `.as_call_to(other)` returns `None`
30107 // for every other keyword. Pins the (construct, keyword-typed-
30108 // project) pair on the outer algebra — the same dispatch
30109 // shape `compile_typed` / `compile_named_from_forms` route
30110 // through post-macroexpansion.
30111 let samples: [(&str, Vec<Sexp>); 4] = [
30112 ("defcompiler", vec![]),
30113 ("defpoint", vec![Sexp::symbol("obs")]),
30114 ("defmonitor", vec![Sexp::keyword("k"), Sexp::string("v")]),
30115 (
30116 "defalert-policy",
30117 vec![Sexp::Nil, Sexp::List(vec![Sexp::symbol("body")])],
30118 ),
30119 ];
30120 for (head, args) in &samples {
30121 let built = Sexp::call(*head, args.clone());
30122 assert_eq!(
30123 built.as_call_to(head),
30124 Some(args.as_slice()),
30125 "Sexp::call→as_call_to(head) round-trip drifted for head={head:?} args={args:?}",
30126 );
30127 // Cross-keyword rejection: every DIFFERENT keyword misses.
30128 let mismatched = format!("{head}-mismatch");
30129 assert_eq!(
30130 built.as_call_to(&mismatched),
30131 None,
30132 "Sexp::call→as_call_to(mismatch) leaked args for head={head:?}",
30133 );
30134 }
30135 }
30136
30137 #[test]
30138 fn sexp_call_constructor_composes_with_head_symbol_and_shape_and_structural_kind() {
30139 // OUTER-ALGEBRA PROJECTION COMPOSITIONS: every `Sexp::call(head,
30140 // args)` output projects through `head_symbol` /
30141 // `shape` / `as_structural_kind` to the shape-invariants that
30142 // pin the constructor's structural identity:
30143 // * `head_symbol() == Some(head)` — the head-position
30144 // projection recovers the constructor's head byte-for-byte;
30145 // * `shape() == SexpShape::List` — a call form is a list-
30146 // shaped `Sexp` on the residual carving;
30147 // * `as_structural_kind() == Some(StructuralKind::List)` — the
30148 // residual carving marker binds through the closed-set
30149 // `StructuralKind` algebra at ONE arm.
30150 // A regression that reroutes `Sexp::call` through a non-list
30151 // arm (e.g. wrapping in `Sexp::Quote` after a copy-edit that
30152 // type-checks) fails ALL THREE pins simultaneously. Sibling to
30153 // the residual-axis's `Sexp::list` shape-composition pins
30154 // (`sexp_list_constructor_composes_with_shape_via_sexp_shape_list`
30155 // + `sexp_list_constructor_composes_with_as_structural_kind`).
30156 let samples: [(&str, Vec<Sexp>); 4] = [
30157 ("head", vec![]),
30158 ("head", vec![Sexp::symbol("only")]),
30159 (
30160 "head",
30161 vec![Sexp::keyword("k"), Sexp::string("v"), Sexp::boolean(false)],
30162 ),
30163 (
30164 "head",
30165 vec![
30166 Sexp::Nil,
30167 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30168 Sexp::List(vec![Sexp::symbol("nested")]),
30169 ],
30170 ),
30171 ];
30172 for (head, args) in &samples {
30173 let built = Sexp::call(*head, args.clone());
30174 assert_eq!(
30175 built.head_symbol(),
30176 Some(*head),
30177 "Sexp::call→head_symbol drifted from Some({head:?}) for args={args:?}",
30178 );
30179 assert_eq!(
30180 built.shape(),
30181 SexpShape::List,
30182 "Sexp::call→shape drifted from SexpShape::List for head={head:?} args={args:?}",
30183 );
30184 assert_eq!(
30185 built.as_structural_kind(),
30186 Some(StructuralKind::List),
30187 "Sexp::call→as_structural_kind drifted from Some(StructuralKind::List) for head={head:?} args={args:?}",
30188 );
30189 }
30190 }
30191
30192 #[test]
30193 fn sexp_call_constructor_accepts_diverse_head_and_arg_input_shapes() {
30194 // INPUT-SHAPE FLEXIBILITY: the `impl Into<String>` head bound
30195 // absorbs `&str` / `String` / `&String`, and the `impl
30196 // IntoIterator<Item = Sexp>` args bound absorbs `Vec<Sexp>` /
30197 // `[Sexp; N]` / `iter::empty()` / `.map(...)` chains — pin that
30198 // all six representative input shapes reach the same canonical
30199 // composition output. A regression that narrows either bound
30200 // (e.g. requiring `String` on the head or `Vec<Sexp>` on the
30201 // args) fails this pin. The two bounds are load-bearing for the
30202 // ergonomy claim in the docstring — consumers threading a
30203 // borrowed head + a `.map` chain must not need `.to_string()` /
30204 // `.collect()` coercions before handing the pair to
30205 // `Sexp::call`. Sibling to `Sexp::list`'s input-shape pin
30206 // (`sexp_list_constructor_accepts_diverse_intoiterator_input_shapes`)
30207 // and `Sexp::symbol`'s head-string absorption posture.
30208 let expected = Sexp::List(vec![
30209 Sexp::symbol("head"),
30210 Sexp::symbol("a"),
30211 Sexp::symbol("b"),
30212 ]);
30213 // (&str, Vec<Sexp>) — the canonical borrowed-head + owned-args
30214 // shape.
30215 assert_eq!(
30216 Sexp::call("head", vec![Sexp::symbol("a"), Sexp::symbol("b")]),
30217 expected,
30218 "Sexp::call drifted for (&str, Vec<Sexp>) input",
30219 );
30220 // (String, [Sexp; N]) — the owned-head + array-literal shape.
30221 assert_eq!(
30222 Sexp::call(String::from("head"), [Sexp::symbol("a"), Sexp::symbol("b")],),
30223 expected,
30224 "Sexp::call drifted for (String, [Sexp; N]) input",
30225 );
30226 // (&String, .map(...)) — the borrowed-owned-head + iterator-map
30227 // chain shape.
30228 let owned_head = String::from("head");
30229 assert_eq!(
30230 Sexp::call(&owned_head, ["a", "b"].iter().map(|s| Sexp::symbol(*s))),
30231 expected,
30232 "Sexp::call drifted for (&String, iter::map) input",
30233 );
30234 // (&str, iter::empty::<Sexp>()) — the zero-arg iterator shape,
30235 // pinning the singleton-list emission (`(head)`) via the
30236 // composition path.
30237 assert_eq!(
30238 Sexp::call("head", std::iter::empty::<Sexp>()),
30239 Sexp::List(vec![Sexp::symbol("head")]),
30240 "Sexp::call drifted for zero-arg iter::empty input",
30241 );
30242 // (&str, once(head_of_args).chain(tail_of_args)) — the head-
30243 // then-rest args shape a builder decomposing an existing call
30244 // form via `as_call` and re-emitting through this constructor
30245 // threads through.
30246 assert_eq!(
30247 Sexp::call(
30248 "head",
30249 std::iter::once(Sexp::symbol("a")).chain([Sexp::symbol("b")]),
30250 ),
30251 expected,
30252 "Sexp::call drifted for (&str, once+chain) args input",
30253 );
30254 }
30255
30256 #[test]
30257 fn sexp_call_constructor_body_matches_typed_composition_through_list_and_symbol() {
30258 // EXPLICIT COMPOSITION-LAW PIN: `Sexp::call(head, args) ==
30259 // Sexp::list(std::iter::once(Sexp::symbol(head)).chain(args))`
30260 // BY DEFINITION — the constructor body IS this composition, and
30261 // the pin exists so a regression that in-lines a hand-authored
30262 // `Sexp::List(vec![Sexp::symbol(head), args...])` body (which
30263 // would type-check and pass the projection round-trips) still
30264 // surfaces here through the composition-path drift. This closes
30265 // the "the constructor routes through the outer-algebra's
30266 // atomic + residual construct families" invariant as a typed
30267 // pin rather than a docstring claim.
30268 let head = "defpoint";
30269 let args = vec![
30270 Sexp::symbol("obs"),
30271 Sexp::keyword("class"),
30272 Sexp::List(vec![Sexp::symbol("Gate"), Sexp::symbol("Observability")]),
30273 ];
30274 assert_eq!(
30275 Sexp::call(head, args.clone()),
30276 Sexp::list(std::iter::once(Sexp::symbol(head)).chain(args.iter().cloned())),
30277 "Sexp::call body drifted from the Sexp::list ∘ once(Sexp::symbol) ∘ chain composition for head={head:?}",
30278 );
30279 }
30280
30281 // ── Sexp::named_call — named-call-form (symbol-headed + NAME slot)
30282 // construct ───────────────────────────────────────────────────────
30283 //
30284 // `Sexp::named_call(head, name, spec_args)` is the section-for-
30285 // retraction dual of the soft-projection `Sexp::as_named_call_to(
30286 // keyword) -> Option<Result<(&str, &[Sexp])>>` — it embeds a fresh
30287 // `(head string, NAME string, spec args sequence)` triple into a
30288 // symbol-headed `(head NAME spec_args…)` `Sexp::List` value at ONE
30289 // site on the outer `Sexp` algebra, composing the call-form
30290 // typed constructor `Sexp::call` (which itself composes the atomic
30291 // `Sexp::symbol` head with the residual `Sexp::list` wrapper) with
30292 // a NAME-slot `Sexp::symbol` embedding via `std::iter::once(
30293 // Sexp::symbol(name)).chain(spec_args)`. Pre-lift the composition
30294 // lived inline at every consumer that built a `(defX NAME …)`
30295 // typed-domain named authoring form or a synthetic named-dispatch
30296 // form — `Sexp::List(vec![Sexp::symbol(head), Sexp::symbol(name),
30297 // spec_args...])` or `Sexp::call(head, std::iter::once(
30298 // Sexp::symbol(name)).chain(spec_args))` was the welded quadruple
30299 // open coding. Post-lift the closure binds at ONE typed-algebra
30300 // method.
30301 //
30302 // These pins cover:
30303 // (a) the composition law
30304 // `Sexp::named_call(head, name, spec_args) == Sexp::call(
30305 // head, std::iter::once(Sexp::symbol(name)).chain(spec_args))`
30306 // — the constructor body is BY DEFINITION the two-method
30307 // composition;
30308 // (b) the round-trip law
30309 // `Sexp::named_call(head, name, spec_args.clone())
30310 // .as_named_call_to(head) == Some(Ok((name, spec_args
30311 // .as_slice())))` — the (construct, named-project) named-
30312 // call-form algebra dual closes at this pair, symmetric with
30313 // the call-form's `Sexp::call(head, args.clone()).as_call()
30314 // == Some((head, args.as_slice()))` round-trip;
30315 // (c) the call-form projection composition
30316 // `Sexp::named_call(head, name, spec_args)
30317 // .as_call() == Some((head, [Sexp::symbol(name),
30318 // spec_args…].as_slice()))` — the call-form soft-projection
30319 // recovers `(head, [name, spec_args…])` with the NAME symbol
30320 // as the first arg, threading the constructor's output
30321 // through the encompassing call-form projection;
30322 // (d) the keyword-matched round-trip law
30323 // `Sexp::named_call(head, name, spec_args)
30324 // .as_call_to(head) == Some([Sexp::symbol(name),
30325 // spec_args…].as_slice())` — the keyword-typed projection
30326 // recovers the NAME-headed args tail iff its argument
30327 // matches the constructor's head;
30328 // (e) the head-symbol composition law
30329 // `Sexp::named_call(head, name, spec_args).head_symbol()
30330 // == Some(head)` — the head-position projection recovers
30331 // the constructor's head byte-for-byte;
30332 // (f) the named-form gate composition law
30333 // `crate::compile::split_name_slot(&Sexp::named_call(head,
30334 // name, spec_args).as_call_to(head).unwrap(), head) == Ok((
30335 // name, spec_args.as_slice()))` — the substrate's named-
30336 // form arity + NAME-shape gate accepts every output of this
30337 // constructor byte-for-byte, closing the section-for-
30338 // retraction pair at the gate level as well as the
30339 // projection level;
30340 // (g) the outer-shape composition law
30341 // `Sexp::named_call(head, name, spec_args).shape() ==
30342 // SexpShape::List` and `.as_structural_kind() == Some(
30343 // StructuralKind::List)` — the residual carving marker binds
30344 // through the closed-set `StructuralKind` algebra at ONE
30345 // arm, symmetric with `Sexp::call`'s residual-arm marker
30346 // composition;
30347 // (h) the input-shape flexibility
30348 // `Sexp::named_call("h", "n", Vec<Sexp>)` / `Sexp::
30349 // named_call(String, String, [Sexp; N])` / `Sexp::
30350 // named_call(&str, &String, iter::map(...))` all agree with
30351 // the canonical composition emission — the two `impl
30352 // Into<String>` bounds + `impl IntoIterator<Item = Sexp>`
30353 // args bound accept every reasonable input shape without a
30354 // per-consumer `.to_string()` / `.collect::<Vec<Sexp>>()`
30355 // coercion.
30356
30357 #[test]
30358 fn sexp_named_call_constructor_body_matches_canonical_two_method_composition_across_representative_inputs(
30359 ) {
30360 // COMPOSITION LAW: `Sexp::named_call(head, name, spec_args) ==
30361 // Sexp::call(head, std::iter::once(Sexp::symbol(name)).chain(
30362 // spec_args))` for every representative (empty-spec-args,
30363 // single-spec-arg, multi-spec-arg, heterogeneous-inner,
30364 // quote-family-wrapping-inner) sample. A regression that
30365 // drifts the body (e.g. a copy-edit that switches to
30366 // `Sexp::keyword(name)` for the NAME position, or that
30367 // reorders `name` and `spec_args` in the chain) surfaces here
30368 // BEFORE the projection pins fail. Sibling-shape pin to
30369 // `Sexp::call`'s canonical-composition test posture.
30370 let samples: [(&'static str, &'static str, Vec<Sexp>); 5] = [
30371 ("defcompiler", "solo", vec![]),
30372 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30373 (
30374 "defmonitor",
30375 "m",
30376 vec![
30377 Sexp::keyword("severity"),
30378 Sexp::symbol("Warning"),
30379 Sexp::keyword("threshold"),
30380 Sexp::int(42),
30381 ],
30382 ),
30383 (
30384 "defcheck",
30385 "coherent",
30386 vec![
30387 Sexp::List(vec![Sexp::symbol("crd-in-sync")]),
30388 Sexp::string("body"),
30389 Sexp::boolean(true),
30390 ],
30391 ),
30392 (
30393 "defalert-policy",
30394 "outage",
30395 vec![
30396 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30397 Sexp::Quasiquote(Box::new(Sexp::List(vec![
30398 Sexp::symbol("template"),
30399 Sexp::Unquote(Box::new(Sexp::symbol("var"))),
30400 ]))),
30401 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
30402 ],
30403 ),
30404 ];
30405 for (head, name, spec_args) in &samples {
30406 let expected = Sexp::call(
30407 *head,
30408 std::iter::once(Sexp::symbol(*name)).chain(spec_args.iter().cloned()),
30409 );
30410 assert_eq!(
30411 Sexp::named_call(*head, *name, spec_args.clone()),
30412 expected,
30413 "Sexp::named_call drifted from Sexp::call(head, once(symbol(name)).chain(spec_args)) for head={head:?} name={name:?} spec_args={spec_args:?}",
30414 );
30415 }
30416 }
30417
30418 #[test]
30419 fn sexp_named_call_constructor_round_trips_through_as_named_call_to() {
30420 // ROUND-TRIP LAW (section-for-retraction with the named-form
30421 // soft-projection): `Sexp::named_call(head, name, spec_args
30422 // .clone()).as_named_call_to(head) == Some(Ok((name,
30423 // spec_args.as_slice())))` sweeps the same representative
30424 // input matrix — proves the (construct, named-project) pair
30425 // forms an `Iso((&'static str, &str, Vec<Sexp>),
30426 // (head-symbol-headed + NAME-symbol-second Sexp::List))` on
30427 // the named-call-form typed decomposition. Sibling-shape pin
30428 // to `Sexp::call`'s round-trip through `as_call` posture.
30429 let samples: [(&'static str, &'static str, Vec<Sexp>); 4] = [
30430 ("defcompiler", "solo", vec![]),
30431 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30432 (
30433 "defmonitor",
30434 "m",
30435 vec![Sexp::keyword("k"), Sexp::string("v")],
30436 ),
30437 (
30438 "defalert-policy",
30439 "outage",
30440 vec![Sexp::Nil, Sexp::List(vec![Sexp::symbol("body")])],
30441 ),
30442 ];
30443 for (head, name, spec_args) in &samples {
30444 let built = Sexp::named_call(*head, *name, spec_args.clone());
30445 assert_eq!(
30446 built.as_named_call_to(head).and_then(|res| res.ok()),
30447 Some((*name, spec_args.as_slice())),
30448 "Sexp::named_call→as_named_call_to round-trip drifted for head={head:?} name={name:?} spec_args={spec_args:?}",
30449 );
30450 }
30451 }
30452
30453 #[test]
30454 fn sexp_named_call_constructor_projects_through_as_call_with_name_first_arg() {
30455 // CALL-FORM PROJECTION COMPOSITION: `Sexp::named_call(head,
30456 // name, spec_args).as_call() == Some((head, [Sexp::symbol(
30457 // name), spec_args…].as_slice()))` — the call-form soft-
30458 // projection recovers `(head, [name, spec_args…])` with the
30459 // NAME symbol as the first arg. Sibling-shape pin to the
30460 // call-form encompassing algebra: the named-call constructor
30461 // routes cleanly through the call-form projection AS A
30462 // COMPOSITION.
30463 let samples: [(&'static str, &'static str, Vec<Sexp>); 3] = [
30464 ("defcompiler", "solo", vec![]),
30465 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30466 (
30467 "defmonitor",
30468 "m",
30469 vec![Sexp::keyword("threshold"), Sexp::int(42)],
30470 ),
30471 ];
30472 for (head, name, spec_args) in &samples {
30473 let built = Sexp::named_call(*head, *name, spec_args.clone());
30474 let expected_args: Vec<Sexp> = std::iter::once(Sexp::symbol(*name))
30475 .chain(spec_args.iter().cloned())
30476 .collect();
30477 assert_eq!(
30478 built.as_call(),
30479 Some((*head, expected_args.as_slice())),
30480 "Sexp::named_call→as_call drifted for head={head:?} name={name:?} spec_args={spec_args:?}",
30481 );
30482 }
30483 }
30484
30485 #[test]
30486 fn sexp_named_call_constructor_round_trips_through_as_call_to_matching_keyword() {
30487 // KEYWORD-MATCHED ROUND-TRIP LAW: `Sexp::named_call(head,
30488 // name, spec_args.clone()).as_call_to(head) == Some([
30489 // Sexp::symbol(name), spec_args…].as_slice())` for the head-
30490 // matched keyword, and `.as_call_to(other) == None` for every
30491 // other keyword. Pins the (construct, keyword-typed-project)
30492 // pair on the outer algebra threading through the NAMED axis
30493 // — the same dispatch shape `compile_named_from_forms` routes
30494 // through post-macroexpansion.
30495 let samples: [(&'static str, &'static str, Vec<Sexp>); 3] = [
30496 ("defcompiler", "solo", vec![]),
30497 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30498 (
30499 "defmonitor",
30500 "m",
30501 vec![Sexp::keyword("k"), Sexp::string("v")],
30502 ),
30503 ];
30504 for (head, name, spec_args) in &samples {
30505 let built = Sexp::named_call(*head, *name, spec_args.clone());
30506 let expected_args: Vec<Sexp> = std::iter::once(Sexp::symbol(*name))
30507 .chain(spec_args.iter().cloned())
30508 .collect();
30509 assert_eq!(
30510 built.as_call_to(head),
30511 Some(expected_args.as_slice()),
30512 "Sexp::named_call→as_call_to(head) round-trip drifted for head={head:?} name={name:?} spec_args={spec_args:?}",
30513 );
30514 // Cross-keyword rejection: every DIFFERENT keyword misses.
30515 let mismatched = format!("{head}-mismatch");
30516 assert_eq!(
30517 built.as_call_to(&mismatched),
30518 None,
30519 "Sexp::named_call→as_call_to(mismatch) leaked args for head={head:?} name={name:?}",
30520 );
30521 }
30522 }
30523
30524 #[test]
30525 fn sexp_named_call_constructor_composes_with_head_symbol_and_shape_and_structural_kind() {
30526 // OUTER-ALGEBRA PROJECTION COMPOSITIONS: every `Sexp::
30527 // named_call(head, name, spec_args)` output projects through
30528 // `head_symbol` / `shape` / `as_structural_kind` to the shape-
30529 // invariants that pin the constructor's structural identity:
30530 // * `head_symbol() == Some(head)` — the head-position
30531 // projection recovers the constructor's head byte-for-byte;
30532 // * `shape() == SexpShape::List` — a named call form is a
30533 // list-shaped `Sexp` on the residual carving;
30534 // * `as_structural_kind() == Some(StructuralKind::List)` —
30535 // the residual carving marker binds through the closed-
30536 // set `StructuralKind` algebra at ONE arm.
30537 let samples: [(&'static str, &'static str, Vec<Sexp>); 3] = [
30538 ("head", "n", vec![]),
30539 ("head", "n", vec![Sexp::keyword("k"), Sexp::string("v")]),
30540 (
30541 "head",
30542 "n",
30543 vec![
30544 Sexp::Nil,
30545 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30546 Sexp::List(vec![Sexp::symbol("nested")]),
30547 ],
30548 ),
30549 ];
30550 for (head, name, spec_args) in &samples {
30551 let built = Sexp::named_call(*head, *name, spec_args.clone());
30552 assert_eq!(
30553 built.head_symbol(),
30554 Some(*head),
30555 "Sexp::named_call→head_symbol drifted from Some({head:?}) for name={name:?} spec_args={spec_args:?}",
30556 );
30557 assert_eq!(
30558 built.shape(),
30559 SexpShape::List,
30560 "Sexp::named_call→shape drifted from SexpShape::List for head={head:?} name={name:?} spec_args={spec_args:?}",
30561 );
30562 assert_eq!(
30563 built.as_structural_kind(),
30564 Some(StructuralKind::List),
30565 "Sexp::named_call→as_structural_kind drifted from Some(StructuralKind::List) for head={head:?} name={name:?} spec_args={spec_args:?}",
30566 );
30567 }
30568 }
30569
30570 #[test]
30571 fn sexp_named_call_constructor_output_passes_the_split_name_slot_gate() {
30572 // NAMED-FORM GATE COMPOSITION LAW: `crate::compile::
30573 // split_name_slot(&Sexp::named_call(head, name, spec_args)
30574 // .as_call_to(head).unwrap(), head) == Ok((name, spec_args
30575 // .as_slice()))` — the substrate's named-form arity + NAME-
30576 // shape gate accepts every output of this constructor byte-
30577 // for-byte, closing the section-for-retraction pair at the
30578 // GATE level as well as the projection level. A regression
30579 // that emits a value the gate rejects (e.g. a
30580 // `NamedFormNonSymbolName` from a `Sexp::keyword(name)` NAME
30581 // slot copy-edit) surfaces here even when the projection
30582 // pins pass.
30583 let samples: [(&'static str, &'static str, Vec<Sexp>); 4] = [
30584 ("defcompiler", "solo", vec![]),
30585 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30586 (
30587 "defmonitor",
30588 "m",
30589 vec![Sexp::keyword("severity"), Sexp::symbol("Warning")],
30590 ),
30591 (
30592 "defalert-policy",
30593 "outage",
30594 vec![
30595 Sexp::List(vec![Sexp::symbol("body")]),
30596 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30597 ],
30598 ),
30599 ];
30600 for (head, name, spec_args) in &samples {
30601 let built = Sexp::named_call(*head, *name, spec_args.clone());
30602 let args_tail = built
30603 .as_call_to(head)
30604 .expect("Sexp::named_call output must pass Sexp::as_call_to(head)");
30605 let gated = crate::compile::split_name_slot(args_tail, head)
30606 .expect("Sexp::named_call output must pass split_name_slot");
30607 assert_eq!(
30608 gated,
30609 (*name, spec_args.as_slice()),
30610 "Sexp::named_call→split_name_slot round-trip drifted for head={head:?} name={name:?} spec_args={spec_args:?}",
30611 );
30612 }
30613 }
30614
30615 #[test]
30616 fn sexp_named_call_constructor_accepts_diverse_head_name_and_arg_input_shapes() {
30617 // INPUT-SHAPE FLEXIBILITY: the two `impl Into<String>` bounds
30618 // absorb `&str` / `String` / `&String` on both head + NAME
30619 // positions, and the `impl IntoIterator<Item = Sexp>` spec-
30620 // args bound absorbs `Vec<Sexp>` / `[Sexp; N]` / `iter::
30621 // empty()` / `.map(...)` chains — pin that all five
30622 // representative input shapes reach the same canonical
30623 // composition output. A regression that narrows any bound
30624 // fails this pin. Sibling to `Sexp::call`'s input-shape pin.
30625 let expected = Sexp::List(vec![
30626 Sexp::symbol("head"),
30627 Sexp::symbol("name"),
30628 Sexp::symbol("a"),
30629 Sexp::symbol("b"),
30630 ]);
30631 // (&str, &str, Vec<Sexp>) — the canonical borrowed shape.
30632 assert_eq!(
30633 Sexp::named_call("head", "name", vec![Sexp::symbol("a"), Sexp::symbol("b")]),
30634 expected,
30635 "Sexp::named_call drifted for (&str, &str, Vec<Sexp>) input",
30636 );
30637 // (String, String, [Sexp; N]) — the owned + array-literal
30638 // shape.
30639 assert_eq!(
30640 Sexp::named_call(
30641 String::from("head"),
30642 String::from("name"),
30643 [Sexp::symbol("a"), Sexp::symbol("b")],
30644 ),
30645 expected,
30646 "Sexp::named_call drifted for (String, String, [Sexp; N]) input",
30647 );
30648 // (&str, &String, .map(...)) — the borrowed-owned-name +
30649 // iterator-map chain shape.
30650 let owned_name = String::from("name");
30651 assert_eq!(
30652 Sexp::named_call(
30653 "head",
30654 &owned_name,
30655 ["a", "b"].iter().map(|s| Sexp::symbol(*s))
30656 ),
30657 expected,
30658 "Sexp::named_call drifted for (&str, &String, iter::map) input",
30659 );
30660 // (&str, &str, iter::empty::<Sexp>()) — the zero-spec-args
30661 // iterator shape, pinning the two-element list emission
30662 // (`(head name)`) via the composition path.
30663 assert_eq!(
30664 Sexp::named_call("head", "name", std::iter::empty::<Sexp>()),
30665 Sexp::List(vec![Sexp::symbol("head"), Sexp::symbol("name")]),
30666 "Sexp::named_call drifted for zero-spec-args iter::empty input",
30667 );
30668 // (&str, &str, once+chain) — the head-then-rest spec-args
30669 // shape a builder decomposing an existing named call form
30670 // via `as_named_call_to` and re-emitting through this
30671 // constructor threads through.
30672 assert_eq!(
30673 Sexp::named_call(
30674 "head",
30675 "name",
30676 std::iter::once(Sexp::symbol("a")).chain([Sexp::symbol("b")]),
30677 ),
30678 expected,
30679 "Sexp::named_call drifted for (&str, &str, once+chain) spec-args input",
30680 );
30681 }
30682
30683 #[test]
30684 fn sexp_named_call_constructor_body_matches_typed_composition_through_call_and_symbol() {
30685 // EXPLICIT COMPOSITION-LAW PIN: `Sexp::named_call(head, name,
30686 // spec_args) == Sexp::call(head, std::iter::once(Sexp::symbol(
30687 // name)).chain(spec_args))` BY DEFINITION — the constructor
30688 // body IS this composition, and the pin exists so a
30689 // regression that in-lines a hand-authored `Sexp::List(vec![
30690 // Sexp::symbol(head), Sexp::symbol(name), spec_args...])`
30691 // body (which would type-check and pass the projection round-
30692 // trips) still surfaces here through the composition-path
30693 // drift. Closes the "the constructor routes through
30694 // `Sexp::call` + `Sexp::symbol`" invariant as a typed pin
30695 // rather than a docstring claim.
30696 let head = "defpoint";
30697 let name = "observability-stack";
30698 let spec_args = vec![
30699 Sexp::keyword("class"),
30700 Sexp::List(vec![Sexp::symbol("Gate"), Sexp::symbol("Observability")]),
30701 ];
30702 assert_eq!(
30703 Sexp::named_call(head, name, spec_args.clone()),
30704 Sexp::call(
30705 head,
30706 std::iter::once(Sexp::symbol(name)).chain(spec_args.iter().cloned()),
30707 ),
30708 "Sexp::named_call body drifted from the Sexp::call ∘ once(Sexp::symbol) ∘ chain composition for head={head:?} name={name:?}",
30709 );
30710 }
30711
30712 #[test]
30713 fn sexp_quote_form_constructor_body_matches_quote_form_wrap_across_every_marker() {
30714 // COMPOSITION-LAW PIN: `Sexp::quote_form(marker, inner) ==
30715 // marker.wrap(inner)` for every `marker: QuoteForm` and every
30716 // representative `inner: Sexp`. Sweeps `QuoteForm::ALL` × a
30717 // representative gallery of inner bodies (atomic-payload,
30718 // residual-Nil, residual-List, quote-family-nested,
30719 // named-call-shaped) so a regression in the constructor body
30720 // that inlined a per-variant match arm — e.g. `match marker {
30721 // QuoteForm::Quote => Sexp::Quote(Box::new(inner)), … }` —
30722 // that drifts one arm's tuple-variant target from the closed-
30723 // set `QuoteForm::wrap` marker-to-wrapper mapping fails
30724 // loudly at the first drifted variant. Pointer-inequality
30725 // safe: `assert_eq!` compares by value, so the pin binds the
30726 // structural composition path rather than any borrowed
30727 // pointer identity.
30728 let inners: Vec<Sexp> = vec![
30729 Sexp::symbol("x"),
30730 Sexp::keyword("k"),
30731 Sexp::string("hello"),
30732 Sexp::int(42),
30733 Sexp::float(2.5),
30734 Sexp::boolean(true),
30735 Sexp::Nil,
30736 Sexp::list(vec![Sexp::symbol("a"), Sexp::int(1)]),
30737 Sexp::quote(Sexp::symbol("nested")),
30738 Sexp::named_call(
30739 "defpoint",
30740 "observability-stack",
30741 std::iter::empty::<Sexp>(),
30742 ),
30743 ];
30744 for marker in QuoteForm::ALL {
30745 for inner in &inners {
30746 assert_eq!(
30747 Sexp::quote_form(marker, inner.clone()),
30748 marker.wrap(inner.clone()),
30749 "Sexp::quote_form body drifted from QuoteForm::wrap composition at marker={marker:?} inner={inner:?}",
30750 );
30751 }
30752 }
30753 }
30754
30755 #[test]
30756 fn sexp_quote_form_constructor_round_trips_through_as_quote_form_for_every_marker() {
30757 // ROUND-TRIP LAW PIN (section-for-retraction with the outer-
30758 // algebra soft-projection): for every `marker: QuoteForm` +
30759 // representative `inner: Sexp`, `Sexp::quote_form(marker,
30760 // inner.clone()).as_quote_form() == Some((marker, &inner))`.
30761 // Proves the (construct, project) pair forms an isomorphism
30762 // between (QuoteForm × Sexp) and the closed-set 4-of-12 quote-
30763 // family carving of the outer `Sexp` algebra — a regression
30764 // that emits a value the projection rejects (unreachable by
30765 // the closed-set structure, since `QuoteForm::wrap` targets a
30766 // quote-family arm exactly) or that drifts the marker
30767 // recovered from the projection (e.g. swapping `Quote` ↔
30768 // `Quasiquote` in the constructor's dispatch) fails loudly
30769 // at the first drifted variant.
30770 for marker in QuoteForm::ALL {
30771 let inner = Sexp::List(vec![Sexp::keyword("body"), Sexp::string("data")]);
30772 let wrapped = Sexp::quote_form(marker, inner.clone());
30773 assert_eq!(
30774 wrapped.as_quote_form(),
30775 Some((marker, &inner)),
30776 "Sexp::quote_form({marker:?}, _).as_quote_form() failed to round-trip — the (construct, project) pair on the outer algebra is not a section-for-retraction of Sexp::as_quote_form",
30777 );
30778 }
30779 }
30780
30781 #[test]
30782 fn sexp_quote_form_constructor_composes_with_as_quote_form_marker_and_shape() {
30783 // MARKER-RECOVERING + OUTER-SHAPE COMPOSITION PIN: for every
30784 // `marker: QuoteForm` + representative `inner: Sexp`, the
30785 // constructor's output projects through the marker-only sibling
30786 // `Sexp::as_quote_form_marker` back to the constructor's marker
30787 // AND through the outer-shape projection `Sexp::shape` to the
30788 // canonical `marker.sexp_shape()`. Pins the two independent
30789 // projection compositions simultaneously so a regression that
30790 // reroutes through a non-quote-family arm (which the outer-
30791 // shape lattice would surface as `SexpShape::List` or
30792 // `SexpShape::Nil`) fails BOTH pins at once.
30793 for marker in QuoteForm::ALL {
30794 let inner = Sexp::symbol("body");
30795 let wrapped = Sexp::quote_form(marker, inner.clone());
30796 assert_eq!(
30797 wrapped.as_quote_form_marker(),
30798 Some(marker),
30799 "Sexp::quote_form({marker:?}, _).as_quote_form_marker() drifted from Some({marker:?})",
30800 );
30801 assert_eq!(
30802 wrapped.shape(),
30803 marker.sexp_shape(),
30804 "Sexp::quote_form({marker:?}, _).shape() drifted from {marker:?}.sexp_shape()",
30805 );
30806 }
30807 }
30808
30809 #[test]
30810 fn sexp_quote_form_constructor_specializes_to_each_per_variant_sibling() {
30811 // PER-VARIANT RESTRICTION LAW PIN: the four per-variant
30812 // siblings ARE the marker-driven parent specialized on a
30813 // compile-time-known marker — `Sexp::quote_form(QuoteForm::X,
30814 // inner) == Sexp::x_variant(inner)` for every X ∈
30815 // {Quote, Quasiquote, Unquote, UnquoteSplice}. Any regression
30816 // that drifts the marker-driven parent from its per-variant
30817 // siblings' single canonical composition site
30818 // (`QuoteForm::X.wrap(inner)`) fails at the first drifted
30819 // variant.
30820 let inner = Sexp::symbol("body");
30821 assert_eq!(
30822 Sexp::quote_form(QuoteForm::Quote, inner.clone()),
30823 Sexp::quote(inner.clone()),
30824 "Sexp::quote_form(QuoteForm::Quote, _) drifted from Sexp::quote(_)",
30825 );
30826 assert_eq!(
30827 Sexp::quote_form(QuoteForm::Quasiquote, inner.clone()),
30828 Sexp::quasiquote(inner.clone()),
30829 "Sexp::quote_form(QuoteForm::Quasiquote, _) drifted from Sexp::quasiquote(_)",
30830 );
30831 assert_eq!(
30832 Sexp::quote_form(QuoteForm::Unquote, inner.clone()),
30833 Sexp::unquote(inner.clone()),
30834 "Sexp::quote_form(QuoteForm::Unquote, _) drifted from Sexp::unquote(_)",
30835 );
30836 assert_eq!(
30837 Sexp::quote_form(QuoteForm::UnquoteSplice, inner.clone()),
30838 Sexp::unquote_splice(inner),
30839 "Sexp::quote_form(QuoteForm::UnquoteSplice, _) drifted from Sexp::unquote_splice(_)",
30840 );
30841 }
30842
30843 #[test]
30844 fn sexp_quote_form_constructor_targets_matching_tuple_variant_for_every_marker() {
30845 // TUPLE-VARIANT-TARGET PIN: `Sexp::quote_form(marker, inner)`
30846 // must be structurally equal to `Sexp::X(Box::new(inner))` for
30847 // the X matching the marker — pinned per variant against the
30848 // hand-authored tuple-variant literal so a regression that
30849 // reroutes the wrap through an off-by-one closed-set match
30850 // (e.g. `QuoteForm::Quote → Sexp::Quasiquote`) surfaces at
30851 // this shape pin even when the round-trip law happens to
30852 // still project through the projection sibling (it wouldn't —
30853 // but the pin gives a distinct, tuple-variant-anchored
30854 // failure signature). Sibling-shape lift to the same-anchor
30855 // pin the `QuoteForm::wrap` inner algebra already carries.
30856 let inner = Sexp::string("payload");
30857 assert_eq!(
30858 Sexp::quote_form(QuoteForm::Quote, inner.clone()),
30859 Sexp::Quote(Box::new(inner.clone())),
30860 "Sexp::quote_form(QuoteForm::Quote, _) drifted from Sexp::Quote(Box::new(_)) canonical tuple-variant shape",
30861 );
30862 assert_eq!(
30863 Sexp::quote_form(QuoteForm::Quasiquote, inner.clone()),
30864 Sexp::Quasiquote(Box::new(inner.clone())),
30865 "Sexp::quote_form(QuoteForm::Quasiquote, _) drifted from Sexp::Quasiquote(Box::new(_)) canonical tuple-variant shape",
30866 );
30867 assert_eq!(
30868 Sexp::quote_form(QuoteForm::Unquote, inner.clone()),
30869 Sexp::Unquote(Box::new(inner.clone())),
30870 "Sexp::quote_form(QuoteForm::Unquote, _) drifted from Sexp::Unquote(Box::new(_)) canonical tuple-variant shape",
30871 );
30872 assert_eq!(
30873 Sexp::quote_form(QuoteForm::UnquoteSplice, inner.clone()),
30874 Sexp::UnquoteSplice(Box::new(inner)),
30875 "Sexp::quote_form(QuoteForm::UnquoteSplice, _) drifted from Sexp::UnquoteSplice(Box::new(_)) canonical tuple-variant shape",
30876 );
30877 }
30878
30879 #[test]
30880 fn sexp_unquote_form_constructor_body_matches_unquote_form_wrap_across_every_marker() {
30881 // COMPOSITION-LAW PIN: `Sexp::unquote_form(marker, inner) ==
30882 // marker.wrap(inner)` for every `marker: UnquoteForm` and every
30883 // representative `inner: Sexp`. Sweeps `UnquoteForm::ALL` × a
30884 // representative gallery of inner bodies (atomic-payload,
30885 // residual-Nil, residual-List, quote-family-nested, unquote-
30886 // subset-nested, named-call-shaped) so a regression that
30887 // inlined a per-variant match arm — e.g. `match marker {
30888 // UnquoteForm::Unquote => Sexp::Unquote(Box::new(inner)),
30889 // UnquoteForm::Splice => Sexp::UnquoteSplice(Box::new(inner)) }`
30890 // — that drifts one arm's tuple-variant target from the closed-
30891 // set `UnquoteForm::wrap` marker-to-wrapper mapping fails
30892 // loudly at the first drifted variant.
30893 let inners: Vec<Sexp> = vec![
30894 Sexp::symbol("x"),
30895 Sexp::keyword("k"),
30896 Sexp::string("hello"),
30897 Sexp::int(42),
30898 Sexp::float(2.5),
30899 Sexp::boolean(true),
30900 Sexp::Nil,
30901 Sexp::list(vec![Sexp::symbol("a"), Sexp::int(1)]),
30902 Sexp::quote(Sexp::symbol("nested")),
30903 Sexp::unquote(Sexp::symbol("subnested")),
30904 Sexp::named_call(
30905 "defpoint",
30906 "observability-stack",
30907 std::iter::empty::<Sexp>(),
30908 ),
30909 ];
30910 for marker in UnquoteForm::ALL {
30911 for inner in &inners {
30912 assert_eq!(
30913 Sexp::unquote_form(marker, inner.clone()),
30914 marker.wrap(inner.clone()),
30915 "Sexp::unquote_form body drifted from UnquoteForm::wrap composition at marker={marker:?} inner={inner:?}",
30916 );
30917 }
30918 }
30919 }
30920
30921 #[test]
30922 fn sexp_unquote_form_constructor_round_trips_through_as_unquote_for_every_marker() {
30923 // ROUND-TRIP LAW PIN (section-for-retraction with the outer-
30924 // algebra soft-projection): for every `marker: UnquoteForm` +
30925 // representative `inner: Sexp`, `Sexp::unquote_form(marker,
30926 // inner.clone()).as_unquote() == Some((marker, &inner))`.
30927 // Proves the (construct, project) pair forms an isomorphism
30928 // between (UnquoteForm × Sexp) and the closed-set 2-of-12
30929 // template-substitution subset carving of the outer `Sexp`
30930 // algebra — a regression that emits a value the projection
30931 // rejects (e.g. drifting to a non-substitution quote-family
30932 // arm like `Sexp::Quote`, which `as_unquote` filters out via
30933 // `QuoteForm::as_unquote_form`) or that drifts the marker
30934 // recovered from the projection (e.g. swapping `Unquote` ↔
30935 // `Splice`) fails loudly at the first drifted variant.
30936 for marker in UnquoteForm::ALL {
30937 let inner = Sexp::List(vec![Sexp::keyword("body"), Sexp::string("data")]);
30938 let wrapped = Sexp::unquote_form(marker, inner.clone());
30939 assert_eq!(
30940 wrapped.as_unquote(),
30941 Some((marker, &inner)),
30942 "Sexp::unquote_form({marker:?}, _).as_unquote() failed to round-trip — the (construct, project) pair on the outer algebra is not a section-for-retraction of Sexp::as_unquote",
30943 );
30944 }
30945 }
30946
30947 #[test]
30948 fn sexp_unquote_form_constructor_composes_with_as_unquote_form_and_shape() {
30949 // MARKER-RECOVERING + OUTER-SHAPE COMPOSITION PIN: for every
30950 // `marker: UnquoteForm` + representative `inner: Sexp`, the
30951 // constructor's output projects through the marker-only sibling
30952 // `Sexp::as_unquote_form` back to the constructor's marker AND
30953 // through the outer-shape projection `Sexp::shape` to the
30954 // canonical `marker.sexp_shape()`. Pins the two independent
30955 // projection compositions simultaneously so a regression that
30956 // reroutes through a non-substitution quote-family arm
30957 // (`SexpShape::Quote` / `SexpShape::Quasiquote`) or through a
30958 // non-quote-family arm (`SexpShape::List` / `SexpShape::Nil`)
30959 // fails BOTH pins at once.
30960 for marker in UnquoteForm::ALL {
30961 let inner = Sexp::symbol("body");
30962 let wrapped = Sexp::unquote_form(marker, inner.clone());
30963 assert_eq!(
30964 wrapped.as_unquote_form(),
30965 Some(marker),
30966 "Sexp::unquote_form({marker:?}, _).as_unquote_form() drifted from Some({marker:?})",
30967 );
30968 assert_eq!(
30969 wrapped.shape(),
30970 marker.sexp_shape(),
30971 "Sexp::unquote_form({marker:?}, _).shape() drifted from {marker:?}.sexp_shape()",
30972 );
30973 }
30974 }
30975
30976 #[test]
30977 fn sexp_unquote_form_constructor_routes_through_superset_quote_form_via_to_quote_form() {
30978 // SUPERSET-ROUTING COMPOSITION-LAW PIN: for every `marker:
30979 // UnquoteForm` + representative `inner: Sexp`, `Sexp::unquote_form(
30980 // marker, inner) == Sexp::quote_form(marker.to_quote_form(),
30981 // inner)`. The subset-algebra construct routes through the
30982 // SAME closed-set `QuoteForm::wrap` composition site the
30983 // superset construct routes through — threaded via the typed
30984 // 2-of-4 subset → superset projection `UnquoteForm::to_quote_form`.
30985 // A regression that re-implements the subset construct on a
30986 // parallel dispatch table (rather than composing through the
30987 // superset construct's composition site) can still project
30988 // through `as_unquote` correctly on the round-trip pin above,
30989 // but will fail this pin because the constructed values compare
30990 // equal only when both routes bind at the same closed-set
30991 // `QuoteForm::wrap` arm. Structural sibling of the composition
30992 // law `UnquoteForm::wrap` itself carries at ast.rs:2469 —
30993 // `self.to_quote_form().wrap(inner)`.
30994 for marker in UnquoteForm::ALL {
30995 let inner = Sexp::List(vec![Sexp::symbol("outer"), Sexp::int(7)]);
30996 assert_eq!(
30997 Sexp::unquote_form(marker, inner.clone()),
30998 Sexp::quote_form(marker.to_quote_form(), inner.clone()),
30999 "Sexp::unquote_form({marker:?}, _) drifted from Sexp::quote_form({:?}, _) — subset-construct did not route through superset-construct via UnquoteForm::to_quote_form",
31000 marker.to_quote_form(),
31001 );
31002 }
31003 }
31004
31005 #[test]
31006 fn sexp_unquote_form_constructor_specializes_to_each_per_variant_sibling() {
31007 // PER-VARIANT RESTRICTION LAW PIN: the two per-variant siblings
31008 // ARE the marker-driven parent specialized on a compile-time-
31009 // known subset marker — `Sexp::unquote_form(UnquoteForm::X,
31010 // inner) == Sexp::x_variant(inner)` for every X ∈ {Unquote,
31011 // Splice}. Any regression that drifts the marker-driven parent
31012 // from its per-variant siblings' single canonical composition
31013 // site (`UnquoteForm::X.wrap(inner)` → `QuoteForm::X.wrap(inner)`)
31014 // fails at the first drifted variant.
31015 let inner = Sexp::symbol("body");
31016 assert_eq!(
31017 Sexp::unquote_form(UnquoteForm::Unquote, inner.clone()),
31018 Sexp::unquote(inner.clone()),
31019 "Sexp::unquote_form(UnquoteForm::Unquote, _) drifted from Sexp::unquote(_)",
31020 );
31021 assert_eq!(
31022 Sexp::unquote_form(UnquoteForm::Splice, inner.clone()),
31023 Sexp::unquote_splice(inner),
31024 "Sexp::unquote_form(UnquoteForm::Splice, _) drifted from Sexp::unquote_splice(_)",
31025 );
31026 }
31027
31028 #[test]
31029 fn sexp_unquote_form_constructor_targets_matching_tuple_variant_for_every_marker() {
31030 // TUPLE-VARIANT-TARGET PIN: `Sexp::unquote_form(marker, inner)`
31031 // must be structurally equal to `Sexp::X(Box::new(inner))` for
31032 // the X matching the subset marker (`Unquote → Sexp::Unquote`,
31033 // `Splice → Sexp::UnquoteSplice`) — pinned per variant against
31034 // the hand-authored tuple-variant literal so a regression that
31035 // reroutes the wrap through an off-by-one closed-set match
31036 // (e.g. `UnquoteForm::Unquote → Sexp::UnquoteSplice`, or the
31037 // subset→superset projection drifting `UnquoteForm::Unquote →
31038 // QuoteForm::Quote` inside `to_quote_form`) surfaces at this
31039 // shape pin with a distinct, tuple-variant-anchored failure
31040 // signature. Sibling-shape lift to the same-anchor pin the
31041 // `QuoteForm::wrap` inner algebra already carries on the
31042 // superset 4-of-4 arms.
31043 let inner = Sexp::string("payload");
31044 assert_eq!(
31045 Sexp::unquote_form(UnquoteForm::Unquote, inner.clone()),
31046 Sexp::Unquote(Box::new(inner.clone())),
31047 "Sexp::unquote_form(UnquoteForm::Unquote, _) drifted from Sexp::Unquote(Box::new(_)) canonical tuple-variant shape",
31048 );
31049 assert_eq!(
31050 Sexp::unquote_form(UnquoteForm::Splice, inner.clone()),
31051 Sexp::UnquoteSplice(Box::new(inner)),
31052 "Sexp::unquote_form(UnquoteForm::Splice, _) drifted from Sexp::UnquoteSplice(Box::new(_)) canonical tuple-variant shape",
31053 );
31054 }
31055
31056 // ── `Atom::KEYWORD_MARKER` — the canonical `:` prefix routed through
31057 // the four Keyword-round-trip sites (reader-entry classifier, Lisp
31058 // canonical Display, JSON canonical projection, iac-forge canonical
31059 // projection). Pins the constant value AND the four sites' composition
31060 // through it so a regression that re-inlines any single site's byte
31061 // literal drifts against these pins even when the rendered bytes still
31062 // agree at that site.
31063
31064 #[test]
31065 fn atom_keyword_marker_projects_canonical_colon_byte() {
31066 assert_eq!(
31067 Atom::KEYWORD_MARKER,
31068 ":",
31069 "KEYWORD_MARKER byte drifted from the substrate-canonical `:` \
31070 prefix — the reader-round-trip contract at Self::from_lexeme \
31071 + fmt::Display for Atom + Self::to_json + \
31072 Self::to_iac_forge_sexpr all bind to this one constant.",
31073 );
31074 }
31075
31076 #[test]
31077 fn atom_display_keyword_arm_routes_through_keyword_marker_constant() {
31078 for name in ["parent", "class", "intent", "x", ""] {
31079 let rendered = Atom::keyword(name).to_string();
31080 let expected = format!("{}{name}", Atom::KEYWORD_MARKER);
31081 assert_eq!(
31082 rendered, expected,
31083 "fmt::Display for Atom's Keyword arm drifted from the \
31084 KEYWORD_MARKER composition at name={name:?}",
31085 );
31086 }
31087 }
31088
31089 #[test]
31090 fn atom_to_json_keyword_arm_routes_through_keyword_marker_constant() {
31091 for name in ["parent", "class", "intent", "x", ""] {
31092 let projected = Atom::keyword(name).to_json();
31093 let expected = serde_json::Value::String(format!("{}{name}", Atom::KEYWORD_MARKER));
31094 assert_eq!(
31095 projected, expected,
31096 "Atom::to_json's Keyword arm drifted from the \
31097 KEYWORD_MARKER composition at name={name:?}",
31098 );
31099 }
31100 }
31101
31102 #[test]
31103 fn atom_from_lexeme_keyword_classifier_routes_through_keyword_marker_constant() {
31104 for name in ["parent", "class", "intent", "x", ""] {
31105 let lexeme = format!("{}{name}", Atom::KEYWORD_MARKER);
31106 let classified = Atom::from_lexeme(&lexeme);
31107 assert_eq!(
31108 classified,
31109 Atom::keyword(name),
31110 "Atom::from_lexeme's Keyword classifier drifted from the \
31111 KEYWORD_MARKER strip at lexeme={lexeme:?}",
31112 );
31113 }
31114 }
31115
31116 #[test]
31117 fn atom_keyword_marker_closes_reader_display_round_trip_for_every_name() {
31118 // The load-bearing round-trip contract:
31119 // Atom::from_lexeme(&Atom::keyword(name).to_string())
31120 // == Atom::keyword(name)
31121 // Both sides bind to Atom::KEYWORD_MARKER — the reader-entry
31122 // classifier strips it via strip_prefix, the canonical-form
31123 // Display re-emits it via write!. A future refactor that
31124 // silently drifts ONE site's byte (e.g. by re-inlining `":"` at
31125 // Display while migrating `strip_prefix` to a different byte)
31126 // breaks THIS round-trip even when both bytes happen to agree on
31127 // the surface — because the round-trip binds to the composition
31128 // through the constant at BOTH endpoints.
31129 for name in ["parent", "class", "intent", "x", "kebab-cased-name"] {
31130 let a = Atom::keyword(name);
31131 let round_tripped = Atom::from_lexeme(&a.to_string());
31132 assert_eq!(
31133 round_tripped, a,
31134 "keyword round-trip through KEYWORD_MARKER drifted at \
31135 name={name:?}",
31136 );
31137 }
31138 }
31139
31140 // ── `Atom::KEYWORD_MARKER_LEAD` — the canonical `:` LEAD `char`
31141 // of the `Atom::KEYWORD_MARKER` `&'static str` prefix, routed
31142 // through the SEVEN test-surface sites that pre-lift each extracted
31143 // the byte via `Atom::KEYWORD_MARKER.chars().next().expect(_)`
31144 // (or `.unwrap()`) — the Keyword arm of the
31145 // `Sexp::is_bare_atom_boundary` negative sweep AND the six cross-
31146 // axis disjointness pins on the sibling marker-byte algebras
31147 // (`SPLICE_DISCRIMINATOR`, `BOOL_LITERAL_LEAD`, `STR_DELIMITER`,
31148 // `STR_ESCAPE_LEAD`, `LIST_OPEN` / `LIST_CLOSE`, `COMMENT_LEAD`,
31149 // `COMMENT_TERM`). Sibling-shape tests to the `atom_bool_literal_lead_*`
31150 // block below (Bool-family shared LEAD-byte axis) and the
31151 // `atom_str_delimiter_*` / `atom_str_escape_lead_*` blocks below
31152 // (Str-payload delimiter + escape-lead axes) — pins the SAME shape
31153 // on the Keyword-prefix LEAD-byte axis of the closed-set outer
31154 // [`Atom`] algebra.
31155
31156 #[test]
31157 fn atom_keyword_marker_lead_projects_canonical_colon_char() {
31158 // Pins the constant's exact `char` value so a typo (`';'`,
31159 // `'.'`, `'!'`) or an accidental redefinition surfaces
31160 // immediately. Sibling-shape pin to
31161 // `atom_bool_literal_lead_projects_canonical_hash_char`
31162 // (the Bool-family shared LEAD-byte axis) and
31163 // `atom_str_delimiter_projects_canonical_double_quote_char`
31164 // (the Str-payload delimiter axis) — pins the SAME shape on
31165 // the Keyword-prefix LEAD-byte axis of the closed-set outer
31166 // [`Atom`] algebra.
31167 assert_eq!(
31168 Atom::KEYWORD_MARKER_LEAD,
31169 ':',
31170 "KEYWORD_MARKER_LEAD char drifted from the substrate- \
31171 canonical `:` LEAD byte — the seven test-surface sites \
31172 that pre-lift each extracted this byte from \
31173 Atom::KEYWORD_MARKER via `.chars().next().expect(_)` all \
31174 bind to this ONE constant.",
31175 );
31176 }
31177
31178 #[test]
31179 fn atom_keyword_marker_lead_prefixes_keyword_marker() {
31180 // STRUCTURAL ROUND-TRIP CONTRACT: the `&'static str` prefix
31181 // `Atom::KEYWORD_MARKER` starts with the `char` LEAD byte
31182 // `Atom::KEYWORD_MARKER_LEAD` — the projection law that binds
31183 // the two typed constants on the outer [`Atom`] algebra. A
31184 // regression that renames the `&'static str` (e.g. `"#:"` for
31185 // a Racket-compat `#:name` keyword-arg port) OR the `char`
31186 // (e.g. to `'.'` for a dotted-path prefix) without updating the
31187 // other fails HERE — the structural invariant that
31188 // `KEYWORD_MARKER_LEAD` IS the lead byte of `KEYWORD_MARKER`
31189 // is what makes the seven consumer sites safe to route
31190 // through the `char` constant instead of the `&'static str`
31191 // projection. Sibling-shape pin to
31192 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`
31193 // on the Bool-family axis: where that pin sweeps every `b:
31194 // bool` spelling to prove BOTH spellings share the lead byte,
31195 // this pin binds the one-char `KEYWORD_MARKER` prefix to its
31196 // projected lead byte via a single `starts_with` call.
31197 //
31198 // Load-bearing: this pin IS the "the two typed constants
31199 // project onto each other" invariant that lets the seven
31200 // consumer sites bind to the `char` constant instead of the
31201 // `.chars().next().expect(_)` chain on the `&'static str`.
31202 assert!(
31203 Atom::KEYWORD_MARKER.starts_with(Atom::KEYWORD_MARKER_LEAD),
31204 "Atom::KEYWORD_MARKER `{}` does NOT start with \
31205 Atom::KEYWORD_MARKER_LEAD `{:?}` — the two typed constants \
31206 have drifted apart on the closed-set outer [`Atom`] \
31207 algebra; the seven consumer sites that route through the \
31208 `char` constant instead of the `&'static str` projection \
31209 would silently disagree with the reader's actual `:foo` \
31210 classification.",
31211 Atom::KEYWORD_MARKER,
31212 Atom::KEYWORD_MARKER_LEAD,
31213 );
31214 }
31215
31216 #[test]
31217 fn atom_keyword_marker_lead_distinct_from_every_other_algebra_marker() {
31218 // CROSS-AXIS DISJOINTNESS PIN: `Atom::KEYWORD_MARKER_LEAD`
31219 // MUST NOT alias any sibling outer-marker `char` on the
31220 // substrate's other closed-set algebras — the Str-payload
31221 // delimiter (`Atom::STR_DELIMITER`), Str-payload escape lead
31222 // (`Atom::STR_ESCAPE_LEAD`), Bool-family shared lead
31223 // (`Atom::BOOL_LITERAL_LEAD`), the paired list delimiters
31224 // (`Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`), the paired line-
31225 // comment delimiters (`Sexp::COMMENT_LEAD` /
31226 // `Sexp::COMMENT_TERM`), every `QuoteForm::lead_char`
31227 // projection, AND `QuoteForm::SPLICE_DISCRIMINATOR`. A
31228 // collision would silently break the reader's outer dispatch:
31229 // a `:`-prefixed bare atom `:foo` would collide with whichever
31230 // marker it aliased. Sibling-shape pin to
31231 // `atom_bool_literal_lead_distinct_from_every_other_algebra_marker`
31232 // (the Bool-family LEAD-byte axis) — pins the SAME shape at
31233 // the Keyword-prefix LEAD-byte axis. A future outer-marker
31234 // extension that collided with `':'` fails HERE at the cross-
31235 // axis enumeration.
31236 assert_ne!(
31237 Atom::KEYWORD_MARKER_LEAD,
31238 Atom::STR_DELIMITER,
31239 "KEYWORD_MARKER_LEAD collides with STR_DELIMITER — a bare \
31240 `:foo` would ambiguously begin a keyword AND open a \
31241 string.",
31242 );
31243 assert_ne!(
31244 Atom::KEYWORD_MARKER_LEAD,
31245 Atom::STR_ESCAPE_LEAD,
31246 "KEYWORD_MARKER_LEAD collides with STR_ESCAPE_LEAD — the \
31247 reader's Str-escape lead byte would alias the Keyword- \
31248 prefix lead byte.",
31249 );
31250 assert_ne!(
31251 Atom::KEYWORD_MARKER_LEAD,
31252 Atom::BOOL_LITERAL_LEAD,
31253 "KEYWORD_MARKER_LEAD collides with BOOL_LITERAL_LEAD — a \
31254 bare `:foo` would ambiguously begin a keyword AND \
31255 classify as a Bool.",
31256 );
31257 assert_ne!(
31258 Atom::KEYWORD_MARKER_LEAD,
31259 Sexp::LIST_OPEN,
31260 "KEYWORD_MARKER_LEAD collides with LIST_OPEN — a bare \
31261 `:foo` would ambiguously begin a keyword AND open a list.",
31262 );
31263 assert_ne!(
31264 Atom::KEYWORD_MARKER_LEAD,
31265 Sexp::LIST_CLOSE,
31266 "KEYWORD_MARKER_LEAD collides with LIST_CLOSE — a bare \
31267 `:foo` would ambiguously begin a keyword AND close a list.",
31268 );
31269 assert_ne!(
31270 Atom::KEYWORD_MARKER_LEAD,
31271 Sexp::COMMENT_LEAD,
31272 "KEYWORD_MARKER_LEAD collides with COMMENT_LEAD — a bare \
31273 `:foo` would ambiguously begin a keyword AND begin a \
31274 comment.",
31275 );
31276 assert_ne!(
31277 Atom::KEYWORD_MARKER_LEAD,
31278 Sexp::COMMENT_TERM,
31279 "KEYWORD_MARKER_LEAD collides with COMMENT_TERM — the \
31280 reader's line-comment discard loop would terminate on \
31281 the SAME byte the from_lexeme keyword-prefix arm binds to.",
31282 );
31283 for qf in QuoteForm::ALL {
31284 assert_ne!(
31285 Atom::KEYWORD_MARKER_LEAD,
31286 qf.lead_char(),
31287 "KEYWORD_MARKER_LEAD collides with QuoteForm::{qf:?}'s \
31288 lead_char — a bare `:foo` would ambiguously begin a \
31289 keyword AND begin a quote-family prefix.",
31290 );
31291 }
31292 assert_ne!(
31293 Atom::KEYWORD_MARKER_LEAD,
31294 QuoteForm::SPLICE_DISCRIMINATOR,
31295 "KEYWORD_MARKER_LEAD collides with SPLICE_DISCRIMINATOR — \
31296 the reader's `,@` splice-promotion peek byte would alias \
31297 the Keyword-prefix lead byte.",
31298 );
31299 }
31300
31301 // ── `Atom::keyword_qualified` — the ONE projection composing
31302 // `Atom::KEYWORD_MARKER` with a bare keyword name across the three
31303 // canonical-rendering Keyword-arm sites (JSON, iac-forge, Lisp
31304 // Display). Pins the composition + the round-trip law with
31305 // `Atom::from_lexeme` + the path-uniformity at every routed site so
31306 // a regression that re-inlines any single site's `format!("{}{s}",
31307 // KEYWORD_MARKER)` composition drifts against these pins even when
31308 // the rendered bytes still agree at that site.
31309
31310 #[test]
31311 fn atom_keyword_qualified_composes_keyword_marker_with_bare_name() {
31312 // BYTE-COMPOSITION CONTRACT: `Atom::keyword_qualified(name)`
31313 // renders exactly `Atom::KEYWORD_MARKER ++ name` for every
31314 // bare-name input — the ONE typed composition of the Keyword-
31315 // prefix constant with a bare-name payload on the [`Atom`]
31316 // algebra. Sibling-shape pin to
31317 // `atom_bool_literal_projects_canonical_scheme_spellings`
31318 // (the Bool-family canonical-rendering axis): where that pin
31319 // sweeps the CLOSED `bool` domain to its canonical spellings,
31320 // this pin sweeps a representative set of bare-name inputs
31321 // (empty, single-char, dashed, dotted, unicode) through the
31322 // Keyword-family projection to prove the composition holds
31323 // uniformly over the open-set bare-name domain.
31324 for name in [
31325 "",
31326 "x",
31327 "class",
31328 "parent",
31329 "point-type",
31330 "kebab-case-name",
31331 "dotted.path",
31332 "with_underscore",
31333 "α",
31334 ] {
31335 let expected = format!("{}{name}", Atom::KEYWORD_MARKER);
31336 assert_eq!(
31337 Atom::keyword_qualified(name),
31338 expected,
31339 "Atom::keyword_qualified({name:?}) drifted from the \
31340 substrate-canonical composition \
31341 `Atom::KEYWORD_MARKER ++ name` — the three canonical- \
31342 rendering Keyword-arm sites (to_json, to_iac_forge_sexpr, \
31343 Display) all bind to this ONE projection.",
31344 );
31345 }
31346 }
31347
31348 #[test]
31349 fn atom_keyword_qualified_starts_with_keyword_marker() {
31350 // STRUCTURAL PREFIX CONTRACT: for every bare-name input,
31351 // `Atom::keyword_qualified(name).starts_with(Atom::KEYWORD_MARKER)`.
31352 // The projection MUST begin with the canonical
31353 // [`Atom::KEYWORD_MARKER`] prefix so
31354 // [`Atom::from_lexeme`]'s `s.strip_prefix(Self::KEYWORD_MARKER)`
31355 // classifier gate matches every rendered qualified keyword.
31356 // Sibling-shape pin to
31357 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`
31358 // (the Bool-family shared-lead-byte axis): where that pin
31359 // sweeps every `b: bool` to prove BOTH spellings share the
31360 // lead byte, this pin sweeps every representative bare-name
31361 // to prove EVERY qualified rendering starts with the shared
31362 // Keyword-family prefix.
31363 for name in ["", "x", "class", "parent", "point-type", "α"] {
31364 let qualified = Atom::keyword_qualified(name);
31365 assert!(
31366 qualified.starts_with(Atom::KEYWORD_MARKER),
31367 "Atom::keyword_qualified({name:?}) = {qualified:?} does \
31368 NOT start with Atom::KEYWORD_MARKER `{}` — the \
31369 projection has drifted from its structural prefix \
31370 contract; Atom::from_lexeme's `strip_prefix` classifier \
31371 gate would silently disagree with the rendered \
31372 qualified keyword.",
31373 Atom::KEYWORD_MARKER,
31374 );
31375 }
31376 }
31377
31378 #[test]
31379 fn atom_from_lexeme_inverts_keyword_qualified_on_bare_name() {
31380 // ROUND-TRIP CONTRACT (typed-EXIT rendering ↔ typed-ENTRY
31381 // classification): [`Atom::from_lexeme`] is the LEFT-inverse
31382 // of [`Atom::keyword_qualified`] on the Keyword-payload
31383 // subset — i.e. `Atom::from_lexeme(&Atom::keyword_qualified(n))
31384 // == Atom::Keyword(n.to_owned())` for every bare `name` that
31385 // does NOT itself parse as a Bool spelling, integer, or float
31386 // (the four typed-entry classification arms preceding the
31387 // KEYWORD_MARKER-prefix arm at `from_lexeme`).
31388 //
31389 // A regression that drifts EITHER the composition (this
31390 // projection) OR the classification (from_lexeme's
31391 // strip_prefix arm) surfaces at THIS pin rather than as a
31392 // silent Keyword-round-trip drift at a downstream consumer.
31393 // Sibling-shape pin to
31394 // `atom_from_lexeme_round_trips_through_bool_literal_for_every_bool`
31395 // (the Bool-family round-trip axis) — pins the SAME shape at
31396 // the Keyword-family round-trip axis of the closed-set outer
31397 // [`Atom`] algebra.
31398 for name in [
31399 "x",
31400 "class",
31401 "parent",
31402 "point-type",
31403 "kebab-case-name",
31404 "dotted.path",
31405 "with_underscore",
31406 "α",
31407 ] {
31408 let qualified = Atom::keyword_qualified(name);
31409 let classified = Atom::from_lexeme(&qualified);
31410 assert_eq!(
31411 classified,
31412 Atom::Keyword(name.to_owned()),
31413 "Atom::from_lexeme({qualified:?}) drifted from \
31414 Atom::Keyword({name:?}) — the round-trip law between \
31415 Atom::keyword_qualified (typed-EXIT canonical \
31416 rendering) and Atom::from_lexeme's strip_prefix arm \
31417 (typed-ENTRY classification) has broken on the \
31418 Keyword-family axis of the closed-set outer [`Atom`] \
31419 algebra.",
31420 );
31421 }
31422 }
31423
31424 #[test]
31425 fn atom_display_keyword_arm_agrees_with_keyword_qualified_bytes() {
31426 // DISPLAY BYTE-IDENTITY PIN: [`fmt::Display for Atom`]'s
31427 // [`Atom::Keyword`] arm keeps its allocation-free
31428 // `write!(f, "{}{s}", Self::KEYWORD_MARKER)` composition
31429 // (Display is called at every canonical-rendering surface
31430 // that composes via `format!("{sexp}")` — avoiding the
31431 // allocation is load-bearing on tokenizer-adjacent hot paths
31432 // like `checks.lisp`'s exhaustive `defcheck` rendering) but
31433 // MUST produce byte-identical output to
31434 // [`Atom::keyword_qualified`]. Otherwise the three canonical-
31435 // rendering surfaces (Display, to_json, to_iac_forge_sexpr)
31436 // would silently disagree on the qualified-keyword bytes even
31437 // though two of the three route through the typed projection.
31438 //
31439 // Load-bearing: this pin IS the "Display's inline write! and
31440 // the typed projection agree byte-for-byte" invariant that
31441 // lets Display keep the zero-alloc path while to_json /
31442 // to_iac_forge_sexpr collapse onto the ONE algebra site.
31443 for name in [
31444 "",
31445 "x",
31446 "class",
31447 "parent",
31448 "point-type",
31449 "kebab-case-name",
31450 "α",
31451 ] {
31452 let atom = Atom::Keyword(name.to_owned());
31453 let via_display = atom.to_string();
31454 let via_projection = Atom::keyword_qualified(name);
31455 assert_eq!(
31456 via_display, via_projection,
31457 "fmt::Display for Atom's Keyword arm drifted from \
31458 Atom::keyword_qualified on name {name:?} — the \
31459 write! path and the typed projection have disagreed \
31460 on the qualified-keyword bytes.",
31461 );
31462 }
31463 }
31464
31465 #[test]
31466 fn atom_to_json_keyword_arm_routes_through_keyword_qualified() {
31467 // PATH-UNIFORMITY GUARD: [`Atom::to_json`]'s [`Atom::Keyword`]
31468 // arm's rendered String value MUST equal
31469 // [`Atom::keyword_qualified`] on the same bare name — the
31470 // FIRST of the two routed sites is bound to the typed
31471 // projection. A regression that re-inlines the `format!("{}{s}",
31472 // Self::KEYWORD_MARKER)` composition at this arm without
31473 // updating the projection (or vice versa) fails HERE.
31474 // Sibling-shape pin to
31475 // `atom_to_iac_forge_sexpr_keyword_arm_routes_through_keyword_qualified`
31476 // on the iac-forge canonical-attestation-form axis: where that
31477 // pin binds the iac-forge arm's Symbol payload, this pin binds
31478 // the JSON arm's String payload.
31479 for name in ["", "x", "class", "parent", "α"] {
31480 let atom = Atom::Keyword(name.to_owned());
31481 let via_json = atom.to_json();
31482 let expected = serde_json::Value::String(Atom::keyword_qualified(name));
31483 assert_eq!(
31484 via_json, expected,
31485 "Atom::to_json's Keyword arm drifted from \
31486 Atom::keyword_qualified on name {name:?} — the JSON \
31487 canonical-rendering site has diverged from the typed \
31488 algebra projection.",
31489 );
31490 }
31491 }
31492
31493 // ── `Atom::bool_literal` — the ONE projection routing the closed-set
31494 // `bool` domain through its canonical Scheme spelling across the two
31495 // Bool-round-trip sites (reader-entry classifier, Lisp canonical
31496 // Display). Pins the projection's spellings for BOTH bool values AND
31497 // both sites' composition through it so a regression that re-inlines
31498 // any single site's byte literal drifts against these pins even when
31499 // the rendered bytes still agree at that site.
31500
31501 #[test]
31502 fn atom_bool_literal_projects_canonical_scheme_spellings() {
31503 assert_eq!(
31504 Atom::bool_literal(true),
31505 "#t",
31506 "Atom::bool_literal(true) drifted from the substrate-canonical \
31507 Scheme spelling `#t` — the reader-round-trip contract at \
31508 Self::from_lexeme + fmt::Display for Atom both bind to this \
31509 projection.",
31510 );
31511 assert_eq!(
31512 Atom::bool_literal(false),
31513 "#f",
31514 "Atom::bool_literal(false) drifted from the substrate-canonical \
31515 Scheme spelling `#f` — the reader-round-trip contract at \
31516 Self::from_lexeme + fmt::Display for Atom both bind to this \
31517 projection.",
31518 );
31519 }
31520
31521 #[test]
31522 fn atom_bool_literal_partitions_the_closed_bool_domain_injectively() {
31523 // Sanity pin on the projection's shape: the two spellings partition
31524 // the closed-set `bool` domain injectively (`true` and `false` do
31525 // NOT alias to the same byte) — otherwise `from_lexeme` on either
31526 // spelling would classify to a single Bool variant, silently
31527 // collapsing the typed distinction at the reader-entry boundary.
31528 assert_ne!(
31529 Atom::bool_literal(true),
31530 Atom::bool_literal(false),
31531 "Atom::bool_literal collapsed the closed-set `bool` domain — \
31532 both bools projected to the same Scheme spelling, breaking \
31533 the reader-entry classifier's injection property.",
31534 );
31535 }
31536
31537 #[test]
31538 fn atom_display_bool_arm_routes_through_bool_literal_projection() {
31539 for b in [true, false] {
31540 let rendered = Atom::boolean(b).to_string();
31541 let expected = Atom::bool_literal(b);
31542 assert_eq!(
31543 rendered, expected,
31544 "fmt::Display for Atom's Bool arm drifted from the \
31545 bool_literal composition at b={b:?}",
31546 );
31547 }
31548 }
31549
31550 #[test]
31551 fn atom_from_lexeme_bool_classifier_routes_through_bool_literal_projection() {
31552 for b in [true, false] {
31553 let lexeme = Atom::bool_literal(b);
31554 let classified = Atom::from_lexeme(lexeme);
31555 assert_eq!(
31556 classified,
31557 Atom::boolean(b),
31558 "Atom::from_lexeme's Bool classifier drifted from the \
31559 bool_literal composition at lexeme={lexeme:?}",
31560 );
31561 }
31562 }
31563
31564 #[test]
31565 fn atom_bool_literal_closes_reader_display_round_trip_for_both_variants() {
31566 // The load-bearing round-trip contract:
31567 // Atom::from_lexeme(&Atom::boolean(b).to_string())
31568 // == Atom::boolean(b)
31569 // Both sides bind to Atom::bool_literal — the reader-entry
31570 // classifier gates on `s == Self::bool_literal(true|false)`, the
31571 // canonical-form Display re-emits it via
31572 // `f.write_str(Self::bool_literal(*b))`. A future refactor that
31573 // silently drifts ONE site's byte (e.g. by re-inlining `"#t"` at
31574 // Display while migrating the classifier gate to a different
31575 // spelling) breaks THIS round-trip even when both bytes happen to
31576 // agree on the surface — because the round-trip binds to the
31577 // composition through the projection at BOTH endpoints.
31578 for b in [true, false] {
31579 let a = Atom::boolean(b);
31580 let round_tripped = Atom::from_lexeme(&a.to_string());
31581 assert_eq!(
31582 round_tripped, a,
31583 "bool round-trip through bool_literal drifted at b={b:?}",
31584 );
31585 }
31586 }
31587
31588 #[test]
31589 fn atom_true_literal_projects_canonical_pound_t_bytes() {
31590 // Pins the exact `"#t"` bytes at the typed constant. A
31591 // regression that drifts the constant (e.g. Common-Lisp-compat
31592 // typo `"T"`, JSON-compat typo `"true"`, Racket-compat typo
31593 // `"#true"`, or an accidental case swap `"#T"`) fails-loudly
31594 // here. This is the single site the substrate's canonical-
31595 // Scheme `true` spelling resolves to; every downstream consumer
31596 // (`Atom::bool_literal`'s `true`-arm, `Atom::from_lexeme`'s
31597 // reader-entry gate, `fmt::Display for Atom`'s `Bool(true)`
31598 // arm, `Atom::BOOL_LITERALS[0]`) routes through this constant.
31599 // Sibling posture to
31600 // `macro_def_head_defmacro_keyword_projects_canonical_defmacro_bytes`
31601 // on the head-keyword algebra.
31602 assert_eq!(
31603 Atom::TRUE_LITERAL,
31604 "#t",
31605 "Atom::TRUE_LITERAL drifted from the substrate-canonical \
31606 Scheme spelling `#t` — the reader-entry classifier at \
31607 Atom::from_lexeme + fmt::Display for Atom + \
31608 Atom::bool_literal's true-arm ALL bind to this constant."
31609 );
31610 }
31611
31612 #[test]
31613 fn atom_false_literal_projects_canonical_pound_f_bytes() {
31614 // Pins the exact `"#f"` bytes at the typed constant. Peer of
31615 // `atom_true_literal_projects_canonical_pound_t_bytes` on the
31616 // `false` element of the closed `bool` domain.
31617 assert_eq!(
31618 Atom::FALSE_LITERAL,
31619 "#f",
31620 "Atom::FALSE_LITERAL drifted from the substrate-canonical \
31621 Scheme spelling `#f` — the reader-entry classifier at \
31622 Atom::from_lexeme + fmt::Display for Atom + \
31623 Atom::bool_literal's false-arm ALL bind to this constant."
31624 );
31625 }
31626
31627 #[test]
31628 fn atom_bool_literal_routes_through_typed_per_variant_constants() {
31629 // PATH-UNIFORMITY: the inherent `Atom::bool_literal(b)` method
31630 // MUST return the per-variant `pub const` byte-for-byte for
31631 // each `b: bool`. A regression that reverts ONE arm to an
31632 // inline `"#t"` / `"#f"` string literal (e.g. a merge-conflict
31633 // resolution that picked the pre-lift form) silently
31634 // reintroduces the ≥2 PRIME-DIRECTIVE trigger the lift
31635 // resolved — this test catches that by pinning each arm's
31636 // return value to the constant, so the two paths (inline vs.
31637 // typed constant) cannot both hold. Sibling posture to
31638 // `macro_def_head_keyword_method_routes_through_typed_constants`
31639 // on the head-keyword algebra.
31640 assert_eq!(
31641 Atom::bool_literal(true),
31642 Atom::TRUE_LITERAL,
31643 "Atom::bool_literal(true) drifted from Atom::TRUE_LITERAL — \
31644 the `true`-arm reverted to an inline literal"
31645 );
31646 assert_eq!(
31647 Atom::bool_literal(false),
31648 Atom::FALSE_LITERAL,
31649 "Atom::bool_literal(false) drifted from Atom::FALSE_LITERAL \
31650 — the `false`-arm reverted to an inline literal"
31651 );
31652 }
31653
31654 #[test]
31655 fn atom_bool_literals_has_expected_cardinality() {
31656 // Cardinality contract: `Self::BOOL_LITERALS.len() == 2` —
31657 // pinned at the declaration site by rustc's forced-arity check
31658 // on `[&'static str; 2]`. This test surfaces the arity as a
31659 // fail-loud runtime pin so a future refactor that switches the
31660 // array type to `&[&'static str]` (dropping the compile-time
31661 // arity forcing) doesn't silently loosen the closed-set
31662 // discipline the family relies on. The `N == 2` is pinned by
31663 // the mathematics of the closed `bool` domain; a future tri-
31664 // valued-logic extension surfaces at THIS pin. Sibling posture
31665 // to `macro_def_head_keywords_has_expected_cardinality` on the
31666 // head-keyword algebra AND to
31667 // `macro_params_lambda_list_keywords_has_expected_cardinality`
31668 // on the CL lambda-list-keyword family.
31669 assert_eq!(
31670 Atom::BOOL_LITERALS.len(),
31671 2,
31672 "Atom::BOOL_LITERALS cardinality drifted from 2 — the \
31673 closed `bool` domain admits exactly two spellings by \
31674 construction; a tri-valued extension surfaces here"
31675 );
31676 }
31677
31678 #[test]
31679 fn atom_bool_literals_align_with_bool_literal_by_index() {
31680 // ALIGNMENT CONTRACT: `Self::BOOL_LITERALS[i] ==
31681 // Self::bool_literal([true, false][i])` element-wise. The
31682 // `[true, false]` sweep order is the canonical declaration
31683 // order every existing sibling test in the crate uses; a
31684 // regression that reorders ONE array without reordering the
31685 // other silently misaligns every `zip([true, false],
31686 // Self::BOOL_LITERALS)` consumer (LSP completion providers,
31687 // metric-label emitters, coverage reporters). Sibling posture
31688 // to `macro_def_head_keywords_align_with_all_by_index` on the
31689 // head-keyword algebra.
31690 for (i, b) in [true, false].iter().enumerate() {
31691 assert_eq!(
31692 Atom::BOOL_LITERALS[i],
31693 Atom::bool_literal(*b),
31694 "Atom::BOOL_LITERALS[{i}] `{kw}` drifted from \
31695 Atom::bool_literal({b:?}) `{via_variant}` — the \
31696 canonical declaration order of the ALL array and the \
31697 bool_literal projection must match element-wise",
31698 kw = Atom::BOOL_LITERALS[i],
31699 via_variant = Atom::bool_literal(*b),
31700 );
31701 }
31702 }
31703
31704 #[test]
31705 fn atom_bool_literals_pairwise_distinct() {
31706 // PAIRWISE DISJOINTNESS: the two `&'static str` spellings on
31707 // the bool-spelling algebra MUST differ so the reader-entry
31708 // classifier's `s == Self::bool_literal(true|false)` cascade
31709 // cannot route both bools through the same arm — otherwise
31710 // `from_lexeme` on either spelling would classify to a single
31711 // Bool variant, silently collapsing the typed distinction at
31712 // the reader-entry boundary. Family-wide sweep over
31713 // `BOOL_LITERALS × BOOL_LITERALS` — supersedes any single
31714 // per-pair assertion and picks up new spellings mechanically
31715 // (should the closed `bool` domain ever extend). Sibling
31716 // posture to `macro_def_head_keywords_pairwise_distinct` on the
31717 // head-keyword algebra AND to
31718 // `atom_bool_literal_partitions_the_closed_bool_domain_injectively`
31719 // on the direct-projection axis.
31720 for (i, a) in Atom::BOOL_LITERALS.iter().enumerate() {
31721 for (j, b) in Atom::BOOL_LITERALS.iter().enumerate() {
31722 if i == j {
31723 continue;
31724 }
31725 assert_ne!(
31726 a, b,
31727 "Atom::BOOL_LITERALS[{i}] `{a}` collides with \
31728 Atom::BOOL_LITERALS[{j}] `{b}` — the reader-entry \
31729 classifier's cascade would route two bools through \
31730 the same arm"
31731 );
31732 }
31733 }
31734 }
31735
31736 #[test]
31737 fn atom_bool_literals_all_route_through_bool_literal_leading_byte() {
31738 // Structural round-trip pin composed with the algebra's
31739 // `Atom::BOOL_LITERAL_LEAD` axis peer: every entry of
31740 // `Self::BOOL_LITERALS` MUST start with `Self::BOOL_LITERAL_LEAD`.
31741 // The lead-byte-prefixes-spelling law from
31742 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`
31743 // sweeps [true, false] through `bool_literal`; THIS test
31744 // sweeps the same invariant over the `BOOL_LITERALS` array
31745 // directly, catching a regression where a per-role constant
31746 // drifts from the shared lead byte (e.g. `FALSE_LITERAL` set
31747 // to `"~f"` while `bool_literal(false)` still returns
31748 // `FALSE_LITERAL`, which would preserve the projection
31749 // contract but break the lead-byte contract). Sibling posture
31750 // to `macro_def_head_keywords_all_round_trip_through_from_str`
31751 // on the head-keyword algebra.
31752 for kw in Atom::BOOL_LITERALS {
31753 assert!(
31754 kw.starts_with(Atom::BOOL_LITERAL_LEAD),
31755 "Atom::BOOL_LITERALS entry `{kw}` does NOT start with \
31756 Atom::BOOL_LITERAL_LEAD ({lead:?}) — the per-role \
31757 constant drifted from the shared lead byte",
31758 lead = Atom::BOOL_LITERAL_LEAD,
31759 );
31760 }
31761 }
31762
31763 // ── `Atom::BOOL_LITERAL_LEAD` — the canonical `'#'` char shared
31764 // across BOTH `Atom::bool_literal` spellings (`"#t"` for `true`,
31765 // `"#f"` for `false`). Sibling-shape tests to the
31766 // `atom_bool_literal_*` block above (the two-char spelling axis
31767 // of the Bool payload) — where those pins bind the two-char
31768 // Scheme spelling of each `bool` payload, THESE tests bind the
31769 // ONE shared lead byte of the two spellings onto the closed-set
31770 // outer [`Atom`] algebra so a hash-prefix reader-family
31771 // extension (`#\char`, `#(vector)`, `#|block-comment|#`, `#;
31772 // datum-comment`) lands at ONE constant on the algebra.
31773
31774 #[test]
31775 fn atom_bool_literal_lead_projects_canonical_hash_char() {
31776 // Pins the constant's exact `char` value so a typo (`'#'`
31777 // vs. `'$'`, `'@'`, or `'!'`) or an accidental redefinition
31778 // surfaces immediately. Sibling-shape pin to
31779 // `atom_str_delimiter_projects_canonical_double_quote_char`
31780 // (Str-delimiter axis) and
31781 // `atom_str_escape_lead_projects_canonical_backslash_char`
31782 // (Str-escape-lead axis) — pins the SAME shape on the
31783 // Bool-family lead-byte axis of the closed-set [`Atom`]
31784 // algebra.
31785 assert_eq!(
31786 Atom::BOOL_LITERAL_LEAD,
31787 '#',
31788 "BOOL_LITERAL_LEAD char drifted from the substrate-\
31789 canonical `#` Bool-family lead byte — the disjointness \
31790 contract at is_bare_atom_boundary's negative sweep AND \
31791 the QuoteForm::SPLICE_DISCRIMINATOR non-collision pin \
31792 both bind to this ONE constant.",
31793 );
31794 }
31795
31796 #[test]
31797 fn atom_bool_literal_lead_prefixes_every_bool_literal_spelling() {
31798 // Structural round-trip pin — the (BOOL_LITERAL_LEAD,
31799 // bool_literal spelling) pairing binds at ONE algebra layer:
31800 // for every `b: bool`, `Atom::bool_literal(b)` MUST start
31801 // with `Atom::BOOL_LITERAL_LEAD`. A regression that drifts
31802 // EITHER the constant OR the two `bool_literal` arms
31803 // surfaces here rather than at a silent bool-family reader
31804 // drift where `#t` / `#f` classify as `Atom::Symbol` instead
31805 // of `Atom::Bool`.
31806 //
31807 // Load-bearing across both `b: bool` values because the
31808 // structural invariant IS "both spellings share the lead
31809 // byte" — sweeps the closed `bool` domain so a hypothetical
31810 // regression that renamed ONE spelling only (e.g. moved
31811 // `"#f"` to `"~f"`) fails at THIS pin even though the
31812 // constant AND the sibling spelling both agreed.
31813 //
31814 // Sibling-shape peer of
31815 // `atom_bool_literal_closes_reader_display_round_trip_for_both_variants`
31816 // one axis over: that test binds the (spelling, typed Bool
31817 // variant) round-trip through the reader/Display; THIS test
31818 // binds the (lead byte, spelling) prefix invariant at the
31819 // atomic-algebra layer directly.
31820 for b in [true, false] {
31821 let spelling = Atom::bool_literal(b);
31822 assert!(
31823 spelling.starts_with(Atom::BOOL_LITERAL_LEAD),
31824 "Atom::bool_literal({b:?}) = {spelling:?} does NOT \
31825 start with BOOL_LITERAL_LEAD ({:?}) — the structural \
31826 invariant \"both bool_literal spellings share the \
31827 lead byte\" broke at b={b:?}",
31828 Atom::BOOL_LITERAL_LEAD,
31829 );
31830 }
31831 }
31832
31833 #[test]
31834 fn atom_bool_literal_lead_distinct_from_every_other_algebra_marker() {
31835 // Cross-axis disjointness pin: BOOL_LITERAL_LEAD's byte MUST
31836 // NOT alias any other closed-set outer-marker byte the
31837 // reader's tokenizer specialises on — otherwise a bare `#t`
31838 // / `#f` lexeme would ambiguously route through the
31839 // colliding arm AND the bool classifier. Sibling-shape pin
31840 // to `sexp_comment_term_distinct_from_every_non_whitespace_algebra_marker`
31841 // (COMMENT_TERM axis) — enumerates every closed-set outer-
31842 // marker char AND asserts non-collision on the Bool-family
31843 // lead-byte axis.
31844 //
31845 // The enumerated set spans THREE type namespaces and every
31846 // canonical reader byte the substrate exposes:
31847 // * `Sexp::LIST_OPEN` / `LIST_CLOSE` / `COMMENT_LEAD` /
31848 // `COMMENT_TERM` — outer-structural + reader-discard
31849 // * `Atom::STR_DELIMITER` / `STR_ESCAPE_LEAD` — atomic-
31850 // payload delimiter + escape lead
31851 // * `Atom::KEYWORD_MARKER`'s lead byte — atomic-payload
31852 // prefix marker
31853 // * every `QuoteForm::lead_char` projection AND
31854 // `QuoteForm::SPLICE_DISCRIMINATOR` — homoiconic
31855 // prefix + splice-discriminator
31856 assert_ne!(
31857 Atom::BOOL_LITERAL_LEAD,
31858 Sexp::LIST_OPEN,
31859 "BOOL_LITERAL_LEAD collides with LIST_OPEN — a bare `#t` \
31860 would ambiguously open a list AND classify as a Bool.",
31861 );
31862 assert_ne!(
31863 Atom::BOOL_LITERAL_LEAD,
31864 Sexp::LIST_CLOSE,
31865 "BOOL_LITERAL_LEAD collides with LIST_CLOSE — a bare `#t` \
31866 would ambiguously close a list AND classify as a Bool.",
31867 );
31868 assert_ne!(
31869 Atom::BOOL_LITERAL_LEAD,
31870 Sexp::COMMENT_LEAD,
31871 "BOOL_LITERAL_LEAD collides with COMMENT_LEAD — a bare \
31872 `#t` would ambiguously open a line comment AND classify \
31873 as a Bool.",
31874 );
31875 assert_ne!(
31876 Atom::BOOL_LITERAL_LEAD,
31877 Sexp::COMMENT_TERM,
31878 "BOOL_LITERAL_LEAD collides with COMMENT_TERM — the \
31879 reader's line-comment discard-loop terminator would \
31880 alias the Bool-family lead byte.",
31881 );
31882 assert_ne!(
31883 Atom::BOOL_LITERAL_LEAD,
31884 Atom::STR_DELIMITER,
31885 "BOOL_LITERAL_LEAD collides with STR_DELIMITER — a bare \
31886 `#t` would ambiguously open a string AND classify as a \
31887 Bool.",
31888 );
31889 assert_ne!(
31890 Atom::BOOL_LITERAL_LEAD,
31891 Atom::STR_ESCAPE_LEAD,
31892 "BOOL_LITERAL_LEAD collides with STR_ESCAPE_LEAD — the \
31893 reader's Str-escape lead byte would alias the Bool-\
31894 family lead byte.",
31895 );
31896 assert_ne!(
31897 Atom::BOOL_LITERAL_LEAD,
31898 Atom::KEYWORD_MARKER_LEAD,
31899 "BOOL_LITERAL_LEAD collides with KEYWORD_MARKER_LEAD — a \
31900 bare `#t` would ambiguously begin a keyword AND classify \
31901 as a Bool.",
31902 );
31903 for qf in QuoteForm::ALL {
31904 assert_ne!(
31905 Atom::BOOL_LITERAL_LEAD,
31906 qf.lead_char(),
31907 "BOOL_LITERAL_LEAD collides with QuoteForm::{qf:?}'s \
31908 lead_char — a bare `#t` would ambiguously begin a \
31909 quote-family prefix AND classify as a Bool.",
31910 );
31911 }
31912 assert_ne!(
31913 Atom::BOOL_LITERAL_LEAD,
31914 QuoteForm::SPLICE_DISCRIMINATOR,
31915 "BOOL_LITERAL_LEAD collides with SPLICE_DISCRIMINATOR — \
31916 the reader's `,@` splice-promotion peek byte would alias \
31917 the Bool-family lead byte.",
31918 );
31919 }
31920
31921 // ── `Atom::STR_DELIMITER` — the canonical `"` char routed through
31922 // the four Str-round-trip sites inside `crate::reader::tokenize`
31923 // (string-opening arm, escape-handler self-escape mapping, string-
31924 // closing arm, bare-atom terminator disjunct). Pins the constant
31925 // value AND the composition against a byte-identical drift at any
31926 // one of the four sites. Sibling-shape tests to the
31927 // `atom_keyword_marker_*` block above (Keyword prefix axis) and
31928 // the `atom_bool_literal_*` block above (Bool spelling axis).
31929
31930 #[test]
31931 fn atom_str_delimiter_projects_canonical_double_quote_char() {
31932 // Pins the constant's exact `char` value so a typo (`'\''`,
31933 // `'\`'`, `'#'`) or an accidental redefinition surfaces
31934 // immediately. Sibling-shape pin to
31935 // `atom_keyword_marker_projects_canonical_colon_byte` (the
31936 // Keyword prefix axis) and
31937 // `atom_bool_literal_projects_canonical_scheme_spellings`
31938 // (the Bool spelling axis) — pins the SAME shape on the
31939 // Str-delimiter axis of the closed-set [`Atom`] algebra.
31940 assert_eq!(
31941 Atom::STR_DELIMITER,
31942 '"',
31943 "STR_DELIMITER char drifted from the substrate-canonical `\"` \
31944 delimiter — the reader-round-trip contract at \
31945 crate::reader::tokenize (string-opening arm, escape-handler \
31946 self-escape, string-closing arm, bare-atom terminator \
31947 disjunct) all bind to this ONE constant.",
31948 );
31949 }
31950
31951 #[test]
31952 fn atom_str_delimiter_distinct_from_every_other_atom_marker() {
31953 // Cross-axis disjointness pin: the Str-delimiter byte
31954 // (`Atom::STR_DELIMITER`) must NOT alias the Keyword-marker
31955 // prefix byte (`Atom::KEYWORD_MARKER`) or the two Bool-
31956 // literal spellings (`Atom::bool_literal(true|false)`) —
31957 // otherwise a bare `:foo` or `#t` lexeme starting with the
31958 // Str-delimiter would ambiguously route through the reader's
31959 // `Token::Str` branch AND the `Token::Atom` classifier's
31960 // Keyword / Bool arms in `Atom::from_lexeme`. Guards the
31961 // structural disjointness of the atomic-payload marker
31962 // family on the closed-set [`Atom`] algebra so a future
31963 // marker-swap that accidentally collides two axes surfaces
31964 // at this pin rather than as a silent reader misclassification.
31965 //
31966 // `KEYWORD_MARKER` is `":"` (single-char) — its LEAD `char`
31967 // lives at `Atom::KEYWORD_MARKER_LEAD` on the closed-set outer
31968 // [`Atom`] algebra AND MUST differ from `STR_DELIMITER`'s
31969 // (`'"'`) so `:foo` never opens a string. The two
31970 // `bool_literal` spellings (`"#t"`, `"#f"`) begin with `'#'` —
31971 // a two-char prefix distinct from the single `'"'`
31972 // STR_DELIMITER — so a bare `#t` never opens a string either.
31973 // Pin the byte-level disjointness directly here so any future
31974 // refactor that swaps a marker to collide with STR_DELIMITER
31975 // fails loudly.
31976 assert_ne!(
31977 Atom::STR_DELIMITER,
31978 Atom::KEYWORD_MARKER_LEAD,
31979 "STR_DELIMITER and KEYWORD_MARKER_LEAD share a byte — a \
31980 bare `{}foo` lexeme would ambiguously open a string AND \
31981 begin a keyword classification.",
31982 Atom::KEYWORD_MARKER,
31983 );
31984 for b in [true, false] {
31985 assert!(
31986 !Atom::bool_literal(b).starts_with(Atom::STR_DELIMITER),
31987 "bool_literal({b:?}) begins with STR_DELIMITER — a bare \
31988 `{}` lexeme would ambiguously open a string AND classify \
31989 as a Bool.",
31990 Atom::bool_literal(b),
31991 );
31992 }
31993 }
31994
31995 #[test]
31996 fn atom_str_delimiter_closes_reader_display_round_trip_for_escape_free_str_payloads() {
31997 // Load-bearing round-trip contract for the reader's four
31998 // Str-round-trip sites — the reader's string-opening AND
31999 // string-closing arms both bind to `Atom::STR_DELIMITER`, so
32000 // wrapping an escape-free payload in the constant's byte on
32001 // both sides recovers the SAME `Atom::string(s)` value the
32002 // typed constructor produces. A regression that drifts ONE
32003 // of the two arms (e.g. re-inlines `'"'` at the opener while
32004 // migrating the closer to a different delimiter) breaks the
32005 // opener-must-match-closer contract even when the byte-value
32006 // at the drifted site still agrees at the surface, because
32007 // this round-trip binds to the constant at BOTH endpoints.
32008 //
32009 // Escape-free payload sweep — the reader's escape-handler
32010 // arm's self-escape (`\"` → `"`) is pinned separately at
32011 // `reader_str_escape_self_escape_arm_routes_through_atom_str_delimiter`
32012 // in `crate::reader::tests`; here we sweep the payloads that
32013 // do NOT hit the escape-handler branch so the opener/closer
32014 // pairing is isolated as the load-bearing composition.
32015 for payload in ["hello", "", "foo bar", "kw", "seph.1", "42"] {
32016 let source = format!("{}{payload}{}", Atom::STR_DELIMITER, Atom::STR_DELIMITER,);
32017 let forms = crate::reader::read(&source).unwrap_or_else(|e| {
32018 panic!(
32019 "reader rejected `{source}` composed from \
32020 Atom::STR_DELIMITER at payload={payload:?}: {e}"
32021 )
32022 });
32023 assert_eq!(
32024 forms.len(),
32025 1,
32026 "STR_DELIMITER-wrapped payload {payload:?} must read as \
32027 exactly one form, got {forms:?}",
32028 );
32029 assert_eq!(
32030 forms[0],
32031 Sexp::Atom(Atom::string(payload)),
32032 "STR_DELIMITER-wrapped payload {payload:?} drifted from \
32033 the Sexp::Atom(Atom::string(_)) typed-constructor shape",
32034 );
32035 }
32036 }
32037
32038 // ── `Atom::STR_ESCAPE_LEAD` — the canonical `\` char routed
32039 // through the TWO Str-escape-lead round-trip sites inside
32040 // `crate::reader::tokenize` (escape-handler outer arm's escape-lead
32041 // pattern, escape-handler's self-escape arm's pattern + value pair).
32042 // Pins the constant value AND the composition against a byte-
32043 // identical drift at either site. Sibling-shape peer of the
32044 // `atom_str_delimiter_*` block above on the Str-payload delimiter
32045 // axis — where those pin the OPENER/CLOSER byte's four round-trip
32046 // sites, these pin the ESCAPE-LEAD byte's two round-trip sites.
32047 // The two constants together span the reader's `Token::Str`
32048 // tokenization boundary.
32049
32050 #[test]
32051 fn atom_str_escape_lead_projects_canonical_backslash_char() {
32052 // Pins the constant's exact `char` value so a typo (`'/'`,
32053 // `'|'`, `'^'`) or an accidental redefinition surfaces
32054 // immediately. Sibling-shape pin to
32055 // `atom_str_delimiter_projects_canonical_double_quote_char`
32056 // — pins the SAME shape on the Str-escape-lead axis of the
32057 // closed-set [`Atom`] algebra.
32058 assert_eq!(
32059 Atom::STR_ESCAPE_LEAD,
32060 '\\',
32061 "STR_ESCAPE_LEAD char drifted from the substrate-canonical \
32062 `\\` escape lead — the reader-round-trip contract at \
32063 crate::reader::tokenize (escape-handler outer arm, escape-\
32064 handler self-escape arm's pattern + value pair) all bind \
32065 to this ONE constant.",
32066 );
32067 }
32068
32069 #[test]
32070 fn atom_str_escape_lead_distinct_from_every_other_atom_marker() {
32071 // Cross-axis disjointness pin: the Str-escape-lead byte
32072 // (`Atom::STR_ESCAPE_LEAD`) must NOT alias the Str-delimiter
32073 // (`Atom::STR_DELIMITER`), Keyword-marker prefix
32074 // (`Atom::KEYWORD_MARKER`), or Bool-literal spellings
32075 // (`Atom::bool_literal(true|false)`) — otherwise the reader's
32076 // escape-lead outer arm would ambiguously route the alias
32077 // byte through the escape-handler branch AND the alias's
32078 // corresponding classifier arm. Guards the structural
32079 // disjointness of the atomic-payload marker family on the
32080 // closed-set [`Atom`] algebra so a future marker-swap that
32081 // accidentally collides two axes surfaces at this pin rather
32082 // than as a silent reader misclassification.
32083 //
32084 // In particular: `STR_ESCAPE_LEAD` (`'\\'`) MUST differ from
32085 // `STR_DELIMITER` (`'"'`) so that the reader's `Token::Str`
32086 // accumulation loop's inner branch dispatch (escape-lead
32087 // outer arm vs string-closing arm vs passthrough) remains
32088 // structurally disjoint — collapsing the two would make the
32089 // opener/closer AND the escape-lead the SAME byte, breaking
32090 // both dispatch axes at once.
32091 assert_ne!(
32092 Atom::STR_ESCAPE_LEAD,
32093 Atom::STR_DELIMITER,
32094 "STR_ESCAPE_LEAD and STR_DELIMITER share a byte — the \
32095 reader's Token::Str inner loop's escape-lead outer arm \
32096 would ambiguously route through the string-closing arm.",
32097 );
32098 assert_ne!(
32099 Atom::STR_ESCAPE_LEAD,
32100 Atom::KEYWORD_MARKER_LEAD,
32101 "STR_ESCAPE_LEAD and KEYWORD_MARKER_LEAD share a byte — a \
32102 bare `{}foo` lexeme would ambiguously match the escape- \
32103 lead AND begin a keyword classification.",
32104 Atom::KEYWORD_MARKER,
32105 );
32106 for b in [true, false] {
32107 assert!(
32108 !Atom::bool_literal(b).starts_with(Atom::STR_ESCAPE_LEAD),
32109 "bool_literal({b:?}) begins with STR_ESCAPE_LEAD — a bare \
32110 `{}` lexeme would ambiguously match the escape-lead AND \
32111 classify as a Bool.",
32112 Atom::bool_literal(b),
32113 );
32114 }
32115 }
32116
32117 #[test]
32118 fn atom_str_escape_lead_closes_reader_self_escape_round_trip_for_backslash_payload() {
32119 // Load-bearing round-trip contract for the reader's two
32120 // Str-escape-lead round-trip sites — the reader's escape-lead
32121 // outer arm AND the escape-handler's self-escape arm both bind
32122 // to `Atom::STR_ESCAPE_LEAD`, so wrapping the constant's byte
32123 // TWICE (i.e. the two-byte `\\` sequence) between two
32124 // `STR_DELIMITER` bytes recovers a Str payload holding ONE
32125 // `\` byte through the reader. A regression that drifts EITHER
32126 // of the two arms (outer arm's pattern OR self-escape arm's
32127 // pattern + value) fails HERE — even when the byte-value at
32128 // the drifted site still agrees at the surface — because the
32129 // round-trip binds to the constant at BOTH sites.
32130 //
32131 // Sibling-shape pin to
32132 // `atom_str_delimiter_closes_reader_display_round_trip_for_escape_free_str_payloads`
32133 // (the Str-payload delimiter axis's opener/closer round-trip)
32134 // — where that pin sweeps escape-FREE payloads through the
32135 // opener/closer pair, this pin exercises the SINGLE escape-lead
32136 // self-escape composition end-to-end.
32137 let source = format!(
32138 "{}{}{}{}",
32139 Atom::STR_DELIMITER,
32140 Atom::STR_ESCAPE_LEAD,
32141 Atom::STR_ESCAPE_LEAD,
32142 Atom::STR_DELIMITER,
32143 );
32144 let forms = crate::reader::read(&source).unwrap_or_else(|e| {
32145 panic!(
32146 "reader rejected `{source}` composed from \
32147 STR_DELIMITER + STR_ESCAPE_LEAD self-escape: {e}"
32148 )
32149 });
32150 assert_eq!(
32151 forms.len(),
32152 1,
32153 "STR_ESCAPE_LEAD-self-escape source must read as exactly one \
32154 form, got {forms:?}",
32155 );
32156 assert_eq!(
32157 forms[0],
32158 Sexp::Atom(Atom::string(Atom::STR_ESCAPE_LEAD.to_string())),
32159 "STR_ESCAPE_LEAD self-escape drifted from the \
32160 Sexp::Atom(Atom::string(str_escape_lead)) typed-constructor \
32161 shape — the reader's escape-lead outer arm OR the escape-\
32162 handler's self-escape arm's pattern + value pair drifted \
32163 from the Atom::STR_ESCAPE_LEAD constant",
32164 );
32165 }
32166
32167 // ── `Atom::decode_str_escape` — the ONE typed Str-escape decode
32168 // projection on the closed-set [`Atom`] algebra. Pins the six-arm
32169 // decode table (three named-escape arms `'n' / 't' / 'r'`, two
32170 // pattern-equals-value self-escape arms on
32171 // [`Atom::STR_DELIMITER`] + [`Atom::STR_ESCAPE_LEAD`], one
32172 // passthrough `other`) AND the reader-level composition through
32173 // [`crate::reader::tokenize`]'s escape-handler branch. Sibling-shape
32174 // peer of the `atom_str_escape_lead_*` block above on the same
32175 // Str-payload tokenization boundary: where those pin the
32176 // ESCAPE-LEAD byte's two round-trip sites, these pin the escape
32177 // TABLE's decode arms end-to-end.
32178
32179 #[test]
32180 fn atom_decode_str_escape_named_escape_arms_project_canonical_c0_control_bytes() {
32181 // NAMED-ESCAPE CONTRACT: the three canonical whitespace-
32182 // shorthand arms map each ASCII letter to its corresponding
32183 // C0 control byte. Pins the ONE typed projection's arms so a
32184 // regression that swaps ONE arm's decoded byte (e.g. drifts
32185 // `'n' → '\r'`, breaking the substrate's canonical newline
32186 // shorthand) surfaces at this pin rather than at some
32187 // downstream Str-payload round-trip.
32188 assert_eq!(
32189 Atom::decode_str_escape(Atom::NEWLINE_ESCAPE_SOURCE),
32190 Atom::NEWLINE_ESCAPE_DECODED,
32191 "decode_str_escape(NEWLINE_ESCAPE_SOURCE) drifted from the \
32192 substrate-canonical newline (`\\n`) shorthand — the reader's \
32193 escape-handler branch's `'n' → '\\n'` arm binds to THIS \
32194 projection.",
32195 );
32196 assert_eq!(
32197 Atom::decode_str_escape(Atom::TAB_ESCAPE_SOURCE),
32198 Atom::TAB_ESCAPE_DECODED,
32199 "decode_str_escape(TAB_ESCAPE_SOURCE) drifted from the \
32200 substrate-canonical tab (`\\t`) shorthand.",
32201 );
32202 assert_eq!(
32203 Atom::decode_str_escape(Atom::CARRIAGE_RETURN_ESCAPE_SOURCE),
32204 Atom::CARRIAGE_RETURN_ESCAPE_DECODED,
32205 "decode_str_escape(CARRIAGE_RETURN_ESCAPE_SOURCE) drifted from \
32206 the substrate-canonical carriage-return (`\\r`) shorthand.",
32207 );
32208 }
32209
32210 #[test]
32211 fn atom_decode_str_escape_self_escape_arms_route_through_atom_algebra_constants() {
32212 // SELF-ESCAPE CONTRACT: the two pattern-equals-value arms in
32213 // the escape table bind through the closed-set [`Atom`]
32214 // algebra constants at BOTH pattern AND value. Pin that
32215 // decoding the delimiter byte OR the escape-lead byte from
32216 // its escaped form recovers the SAME algebra constant — a
32217 // delimiter-swap on the algebra propagates through pattern
32218 // AND value at ONE site (the `decode_str_escape` match arm)
32219 // rather than as scattered inline byte literals. Sibling-
32220 // shape pin to
32221 // `reader_str_escape_self_escape_arm_routes_through_atom_str_delimiter`
32222 // AND
32223 // `reader_str_escape_lead_outer_arm_and_self_escape_arm_route_through_atom_str_escape_lead`
32224 // — where those anchor the reader's inner-loop dispatch to
32225 // the constants, this anchors the algebra-level projection's
32226 // pattern-equals-value arms to the SAME constants so the
32227 // reader-round-trip through `decode_str_escape` cannot drift
32228 // if the constants ever change.
32229 assert_eq!(
32230 Atom::decode_str_escape(Atom::STR_DELIMITER),
32231 Atom::STR_DELIMITER,
32232 "decode_str_escape(STR_DELIMITER) drifted from the self-\
32233 escape identity on the Str-payload delimiter axis — the \
32234 `\\\"` sequence must decode to the STR_DELIMITER byte.",
32235 );
32236 assert_eq!(
32237 Atom::decode_str_escape(Atom::STR_ESCAPE_LEAD),
32238 Atom::STR_ESCAPE_LEAD,
32239 "decode_str_escape(STR_ESCAPE_LEAD) drifted from the self-\
32240 escape identity on the Str-payload escape-lead axis — \
32241 the `\\\\` sequence must decode to the STR_ESCAPE_LEAD \
32242 byte.",
32243 );
32244 }
32245
32246 #[test]
32247 fn atom_decode_str_escape_passthrough_arm_returns_source_byte_unchanged_for_non_named_chars() {
32248 // PASSTHROUGH CONTRACT: every `esc` NOT bound by the five
32249 // typed arms (three named + two self-escape) decodes to
32250 // itself. Pins the total-function property of the projection
32251 // — every `char` maps to exactly one decoded `char`, and the
32252 // decode is identity outside the closed-set table. A
32253 // regression that stripped the `other => other` fallthrough
32254 // (e.g. swapped it for a diagnostic path or a `Result` return
32255 // shape) surfaces at this pin. Sweep a representative
32256 // cross-section of ASCII printable + non-ASCII payload bytes
32257 // that MUST NOT alias any of the five typed arms — the
32258 // reader's pre-lift six-arm table pushed each of these
32259 // through unchanged, and the typed projection must preserve
32260 // that identity end-to-end.
32261 for esc in ['a', 'z', '0', '9', '!', '/', '<', '≠', 'π', '🌱'] {
32262 // Cross-arm disjointness precondition — none of the swept
32263 // chars may alias the five typed arms; otherwise the sweep
32264 // conflates passthrough with a typed decode and no longer
32265 // pins the fallthrough identity.
32266 assert!(
32267 !Atom::NAMED_ESCAPE_TABLE.iter().any(|&(src, _)| src == esc)
32268 && !Atom::SELF_ESCAPE_TABLE.contains(&esc),
32269 "passthrough sweep char `{esc}` aliases a typed \
32270 escape-table arm — the sweep no longer pins the \
32271 `other => other` fallthrough",
32272 );
32273 assert_eq!(
32274 Atom::decode_str_escape(esc),
32275 esc,
32276 "decode_str_escape({esc:?}) drifted from the passthrough \
32277 identity — every non-named-escape byte must decode to \
32278 itself so the reader's `\\{esc}` sequence yields the \
32279 payload byte `{esc}`.",
32280 );
32281 }
32282 }
32283
32284 #[test]
32285 fn atom_named_escape_per_role_constants_pin_canonical_bytes() {
32286 // PER-ROLE CANONICAL-BYTE PIN: each of the six per-role
32287 // `pub const`s carries its exact substrate-canonical byte.
32288 // A regression that swaps a SOURCE letter (e.g. drifts
32289 // `NEWLINE_ESCAPE_SOURCE` from `'n'` to `'N'`) or a DECODED
32290 // C0 control (e.g. drifts `TAB_ESCAPE_DECODED` from `'\t'`
32291 // to `'\0'`) surfaces at this pin before the round-trip
32292 // sweep below has a chance to conflate the two. Sibling
32293 // shape to `atom_str_delimiter_projects_canonical_double_quote_char`
32294 // on the same closed-set [`Atom`] algebra.
32295 assert_eq!(
32296 Atom::NEWLINE_ESCAPE_SOURCE,
32297 'n',
32298 "NEWLINE_ESCAPE_SOURCE drifted from the substrate-canonical `n` letter.",
32299 );
32300 assert_eq!(
32301 Atom::NEWLINE_ESCAPE_DECODED,
32302 '\n',
32303 "NEWLINE_ESCAPE_DECODED drifted from the substrate-canonical `\\n` C0 control byte.",
32304 );
32305 assert_eq!(
32306 Atom::TAB_ESCAPE_SOURCE,
32307 't',
32308 "TAB_ESCAPE_SOURCE drifted from the substrate-canonical `t` letter.",
32309 );
32310 assert_eq!(
32311 Atom::TAB_ESCAPE_DECODED,
32312 '\t',
32313 "TAB_ESCAPE_DECODED drifted from the substrate-canonical `\\t` C0 control byte.",
32314 );
32315 assert_eq!(
32316 Atom::CARRIAGE_RETURN_ESCAPE_SOURCE,
32317 'r',
32318 "CARRIAGE_RETURN_ESCAPE_SOURCE drifted from the substrate-canonical `r` letter.",
32319 );
32320 assert_eq!(
32321 Atom::CARRIAGE_RETURN_ESCAPE_DECODED,
32322 '\r',
32323 "CARRIAGE_RETURN_ESCAPE_DECODED drifted from the substrate-canonical `\\r` C0 control byte.",
32324 );
32325 }
32326
32327 #[test]
32328 fn atom_named_escape_table_composes_from_per_role_constants_in_declaration_order() {
32329 // COMPOSITION LAW: the ALL array's rows are exactly the
32330 // per-role (SOURCE, DECODED) pairings in canonical
32331 // declaration order. Pins the composition at ONE site so a
32332 // reorder of ONE row without reordering the per-role
32333 // `pub const`s silently misaligns every consumer that sweeps
32334 // the array by index. Sibling posture to
32335 // `quote_form_iac_forge_tags_align_with_all_by_index` on the
32336 // outer-tokenizer quote-family axis.
32337 assert_eq!(
32338 Atom::NAMED_ESCAPE_TABLE,
32339 [
32340 (Atom::NEWLINE_ESCAPE_SOURCE, Atom::NEWLINE_ESCAPE_DECODED),
32341 (Atom::TAB_ESCAPE_SOURCE, Atom::TAB_ESCAPE_DECODED),
32342 (
32343 Atom::CARRIAGE_RETURN_ESCAPE_SOURCE,
32344 Atom::CARRIAGE_RETURN_ESCAPE_DECODED,
32345 ),
32346 ],
32347 "NAMED_ESCAPE_TABLE drifted from its per-role (SOURCE, \
32348 DECODED) `pub const` composition in canonical declaration \
32349 order (newline / tab / carriage-return).",
32350 );
32351 }
32352
32353 #[test]
32354 fn atom_named_escape_table_has_expected_cardinality() {
32355 // ARITY PIN: the forced-arity `[(char, char); 3]` shape
32356 // survives at runtime. Pins the closed-set size against a
32357 // refactor that loosens the array's type to
32358 // `&'static [(char, char)]` or `Vec<(char, char)>` (which
32359 // would drop the compile-time arity forcing rustc bakes
32360 // into `[T; N]` declarations). Sibling posture to
32361 // `quote_form_iac_forge_tags_has_expected_cardinality` on
32362 // the outer-tokenizer quote-family axis.
32363 assert_eq!(
32364 Atom::NAMED_ESCAPE_TABLE.len(),
32365 3,
32366 "NAMED_ESCAPE_TABLE cardinality drifted from the \
32367 substrate-canonical THREE named-escape arms (newline / \
32368 tab / carriage-return).",
32369 );
32370 }
32371
32372 #[test]
32373 fn atom_named_escape_table_sources_pairwise_distinct() {
32374 // SOURCE DISJOINTNESS: every SOURCE char in the array is
32375 // distinct from every other SOURCE. A regression that
32376 // aliased two source letters (e.g. drifts
32377 // `TAB_ESCAPE_SOURCE` to `'n'`, colliding with
32378 // `NEWLINE_ESCAPE_SOURCE`) would silently route two escape
32379 // sequences through the first-matching decoded byte in
32380 // `decode_str_escape`'s match. Pin the closed-set injective
32381 // shape on the SOURCE axis. Sibling posture to
32382 // `quote_form_iac_forge_tags_pairwise_distinct` on the outer
32383 // quote-family axis.
32384 let sources: Vec<char> = Atom::NAMED_ESCAPE_TABLE
32385 .iter()
32386 .map(|&(src, _)| src)
32387 .collect();
32388 for i in 0..sources.len() {
32389 for j in (i + 1)..sources.len() {
32390 assert_ne!(
32391 sources[i], sources[j],
32392 "NAMED_ESCAPE_TABLE SOURCE chars at indices {i} and {j} \
32393 alias — every named-escape arm must specialize on a \
32394 distinct SOURCE letter.",
32395 );
32396 }
32397 }
32398 }
32399
32400 #[test]
32401 fn atom_named_escape_table_decoded_pairwise_distinct() {
32402 // DECODED DISJOINTNESS: every DECODED byte in the array is
32403 // distinct from every other DECODED byte. A regression that
32404 // aliased two decoded bytes (e.g. drifts
32405 // `TAB_ESCAPE_DECODED` to `'\n'`, colliding with
32406 // `NEWLINE_ESCAPE_DECODED`) would silently collapse two
32407 // canonical whitespace shorthands to the same emitted byte
32408 // — a `\t` in a Str payload would decode to a newline.
32409 // Sibling posture to the SOURCE-axis disjointness pin above.
32410 let decoded: Vec<char> = Atom::NAMED_ESCAPE_TABLE
32411 .iter()
32412 .map(|&(_, dec)| dec)
32413 .collect();
32414 for i in 0..decoded.len() {
32415 for j in (i + 1)..decoded.len() {
32416 assert_ne!(
32417 decoded[i], decoded[j],
32418 "NAMED_ESCAPE_TABLE DECODED bytes at indices {i} and {j} \
32419 alias — every named-escape arm must emit a distinct \
32420 DECODED byte.",
32421 );
32422 }
32423 }
32424 }
32425
32426 #[test]
32427 fn atom_named_escape_table_pattern_distinct_from_value_per_row() {
32428 // PATTERN-DISTINCT-FROM-VALUE INVARIANT: every named-escape
32429 // row's SOURCE differs from its DECODED byte. This is the
32430 // structural axis that distinguishes the three named-escape
32431 // rows from the two pattern-EQUALS-value self-escape arms
32432 // (`STR_DELIMITER → STR_DELIMITER`,
32433 // `STR_ESCAPE_LEAD → STR_ESCAPE_LEAD`) EXCLUDED from this
32434 // array by design. A regression that drifted a named row
32435 // into pattern-equals-value (e.g. `TAB_ESCAPE_DECODED = 't'`)
32436 // silently promoted it to a passthrough and lost the
32437 // canonical whitespace shorthand — pinned HERE at the
32438 // typed-algebra level.
32439 for (i, &(src, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32440 assert_ne!(
32441 src, dec,
32442 "NAMED_ESCAPE_TABLE row {i} pattern-EQUALS-value — the \
32443 named-escape rows are structurally pattern-DISTINCT; \
32444 the two pattern-equals-value arms live at \
32445 STR_DELIMITER / STR_ESCAPE_LEAD one axis over.",
32446 );
32447 }
32448 }
32449
32450 #[test]
32451 fn atom_named_escape_table_disjoint_from_self_escape_algebra_constants() {
32452 // CROSS-AXIS DISJOINTNESS: no SOURCE or DECODED byte in the
32453 // named-escape table aliases either of the two self-escape
32454 // algebra constants (`Self::STR_DELIMITER`,
32455 // `Self::STR_ESCAPE_LEAD`). A regression that drifted ONE
32456 // named-escape SOURCE to `'\\'` (colliding with
32457 // `STR_ESCAPE_LEAD`) or ONE DECODED to `'"'` (colliding
32458 // with `STR_DELIMITER`) would silently reshuffle the
32459 // decode-arm dispatch order in `decode_str_escape` (the
32460 // named arm would fire before the self-escape arm, or vice
32461 // versa). Pins the two escape-family axes as structurally
32462 // disjoint sub-vocabularies of the Str-payload tokenization
32463 // boundary.
32464 for (i, &(src, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32465 assert_ne!(
32466 src,
32467 Atom::STR_DELIMITER,
32468 "NAMED_ESCAPE_TABLE row {i} SOURCE aliases STR_DELIMITER",
32469 );
32470 assert_ne!(
32471 src,
32472 Atom::STR_ESCAPE_LEAD,
32473 "NAMED_ESCAPE_TABLE row {i} SOURCE aliases STR_ESCAPE_LEAD",
32474 );
32475 assert_ne!(
32476 dec,
32477 Atom::STR_DELIMITER,
32478 "NAMED_ESCAPE_TABLE row {i} DECODED aliases STR_DELIMITER",
32479 );
32480 assert_ne!(
32481 dec,
32482 Atom::STR_ESCAPE_LEAD,
32483 "NAMED_ESCAPE_TABLE row {i} DECODED aliases STR_ESCAPE_LEAD",
32484 );
32485 }
32486 }
32487
32488 #[test]
32489 fn atom_decode_str_escape_routes_through_named_escape_table_for_every_row() {
32490 // PATH-UNIFORMITY PIN: `decode_str_escape` returns the
32491 // DECODED byte of every `NAMED_ESCAPE_TABLE` row when called
32492 // with that row's SOURCE. Path-uniformity contract between
32493 // the projection method and the closed-set algebra — a
32494 // regression that reverted `decode_str_escape`'s named arms
32495 // to inline `'n' => '\n'` literals AND drifted the SOURCE
32496 // per-role `pub const` (or vice versa) fails HERE at the
32497 // first mismatched row rather than at a distant Str-payload
32498 // round-trip. Sibling posture to
32499 // `quote_form_iac_forge_tag_routes_through_typed_per_role_constants`
32500 // on the outer-tokenizer quote-family axis.
32501 for (i, &(src, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32502 assert_eq!(
32503 Atom::decode_str_escape(src),
32504 dec,
32505 "decode_str_escape drifted from NAMED_ESCAPE_TABLE row {i} \
32506 — the projection's named-escape arm must route through \
32507 the per-role (SOURCE, DECODED) `pub const` pairing.",
32508 );
32509 }
32510 }
32511
32512 #[test]
32513 fn atom_self_escape_table_composes_from_algebra_constants_in_declaration_order() {
32514 // COMPOSITION LAW: the ALL array's rows are the two closed-set
32515 // [`Atom`] algebra constants in canonical declaration order
32516 // ([`Atom::STR_DELIMITER`], [`Atom::STR_ESCAPE_LEAD`]) matching
32517 // `decode_str_escape`'s match-arm order. Pins the composition
32518 // at ONE site so a reorder of ONE row without reordering the
32519 // constants silently misaligns every consumer that sweeps the
32520 // array by index. Sibling posture to
32521 // `atom_named_escape_table_composes_from_per_role_constants_in_declaration_order`
32522 // one axis over.
32523 assert_eq!(
32524 Atom::SELF_ESCAPE_TABLE,
32525 [Atom::STR_DELIMITER, Atom::STR_ESCAPE_LEAD],
32526 "SELF_ESCAPE_TABLE drifted from its algebra-constant \
32527 composition in canonical declaration order \
32528 (STR_DELIMITER / STR_ESCAPE_LEAD).",
32529 );
32530 }
32531
32532 #[test]
32533 fn atom_self_escape_table_has_expected_cardinality() {
32534 // ARITY PIN: the forced-arity `[char; 2]` shape survives at
32535 // runtime. Pins the closed-set size against a refactor that
32536 // loosens the array's type to `&'static [char]` or
32537 // `Vec<char>` (which would drop the compile-time arity
32538 // forcing rustc bakes into `[T; N]` declarations). Sibling
32539 // posture to `atom_named_escape_table_has_expected_cardinality`
32540 // on the peer pattern-DISTINCT-from-value sub-vocabulary.
32541 assert_eq!(
32542 Atom::SELF_ESCAPE_TABLE.len(),
32543 2,
32544 "SELF_ESCAPE_TABLE cardinality drifted from the substrate-\
32545 canonical TWO self-escape arms (STR_DELIMITER / \
32546 STR_ESCAPE_LEAD).",
32547 );
32548 }
32549
32550 #[test]
32551 fn atom_self_escape_table_pairwise_distinct() {
32552 // DISJOINTNESS: every byte in the array is distinct from every
32553 // other byte. A regression that aliased the two self-escape
32554 // bytes (e.g. adopting `'"'` as ALSO the escape lead in a
32555 // hypothetical smart-quote reader mode) would silently collapse
32556 // the two pattern-EQUALS-value arms of `decode_str_escape` to
32557 // one and lose the ability to escape one delimiter without
32558 // shadowing the other. Sibling posture to
32559 // `atom_named_escape_table_sources_pairwise_distinct` on the
32560 // peer sub-vocabulary.
32561 for i in 0..Atom::SELF_ESCAPE_TABLE.len() {
32562 for j in (i + 1)..Atom::SELF_ESCAPE_TABLE.len() {
32563 assert_ne!(
32564 Atom::SELF_ESCAPE_TABLE[i],
32565 Atom::SELF_ESCAPE_TABLE[j],
32566 "SELF_ESCAPE_TABLE bytes at indices {i} and {j} \
32567 alias — every self-escape arm must specialize on \
32568 a distinct byte.",
32569 );
32570 }
32571 }
32572 }
32573
32574 #[test]
32575 fn atom_self_escape_table_disjoint_from_named_escape_table() {
32576 // CROSS-AXIS DISJOINTNESS: no byte in the self-escape table
32577 // aliases any SOURCE or DECODED byte in the named-escape
32578 // table. A regression that drifted a self-escape byte into
32579 // the named vocabulary (e.g. adopted `'n'` as an additional
32580 // self-escaping delimiter) OR a named byte into the self
32581 // vocabulary would reshuffle the decode-arm dispatch order in
32582 // `decode_str_escape` — the two sub-vocabularies must remain
32583 // structurally disjoint sub-tables of the FIVE non-passthrough
32584 // arms. Sibling posture (inverse direction) to
32585 // `atom_named_escape_table_disjoint_from_self_escape_algebra_constants`
32586 // — where that pin sweeps from the named side outward, this
32587 // sweeps from the self side outward; both pin the same
32588 // cross-axis disjointness invariant.
32589 for (i, &self_byte) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
32590 for (j, &(src, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32591 assert_ne!(
32592 self_byte, src,
32593 "SELF_ESCAPE_TABLE row {i} aliases NAMED_ESCAPE_TABLE \
32594 row {j} SOURCE — the pattern-EQUALS-value and \
32595 pattern-DISTINCT-from-value sub-vocabularies must \
32596 remain disjoint.",
32597 );
32598 assert_ne!(
32599 self_byte, dec,
32600 "SELF_ESCAPE_TABLE row {i} aliases NAMED_ESCAPE_TABLE \
32601 row {j} DECODED — the pattern-EQUALS-value and \
32602 pattern-DISTINCT-from-value sub-vocabularies must \
32603 remain disjoint.",
32604 );
32605 }
32606 }
32607 }
32608
32609 #[test]
32610 fn atom_decode_str_escape_routes_through_self_escape_table_for_every_row() {
32611 // PATH-UNIFORMITY PIN: `decode_str_escape` returns the SAME
32612 // byte for every `SELF_ESCAPE_TABLE` row — the pattern-EQUALS-
32613 // value invariant is exercised on every row. A regression
32614 // that reverted `decode_str_escape`'s two self-escape arms to
32615 // inline `'"' => '"'` / `'\\' => '\\'` literals AND drifted
32616 // one of the two constants (or vice versa) fails HERE at the
32617 // first mismatched row rather than at a downstream Str-payload
32618 // round-trip. Sibling posture to
32619 // `atom_decode_str_escape_routes_through_named_escape_table_for_every_row`
32620 // — where that pin exercises the pattern-DISTINCT-from-value
32621 // path-uniformity contract on the peer sub-vocabulary, this
32622 // pins the pattern-EQUALS-value axis on the self-escape
32623 // sub-vocabulary.
32624 for (i, &esc) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
32625 assert_eq!(
32626 Atom::decode_str_escape(esc),
32627 esc,
32628 "decode_str_escape drifted from SELF_ESCAPE_TABLE row \
32629 {i} — the projection's self-escape arm must map \
32630 pattern to the SAME byte (definitional identity).",
32631 );
32632 }
32633 }
32634
32635 #[test]
32636 fn atom_named_and_self_escape_tables_span_the_five_non_passthrough_arms() {
32637 // TOTAL-DECODE PIN: the two sub-vocabulary ALL arrays
32638 // together account for exactly the FIVE non-passthrough arms
32639 // of [`Atom::decode_str_escape`]. Pins the closed-set
32640 // decomposition against a refactor that adds a sixth typed
32641 // arm to `decode_str_escape` without extending one of the
32642 // ALL arrays (which would leak a stale byte through
32643 // `other => other` at the sweep sites). The three named arms
32644 // + two self arms = five typed arms; the sixth branch is the
32645 // `other => other` passthrough at the algebra's projection.
32646 assert_eq!(
32647 Atom::NAMED_ESCAPE_TABLE.len() + Atom::SELF_ESCAPE_TABLE.len(),
32648 5,
32649 "NAMED_ESCAPE_TABLE + SELF_ESCAPE_TABLE cardinality drifted \
32650 from the substrate-canonical FIVE non-passthrough arms of \
32651 Atom::decode_str_escape.",
32652 );
32653 }
32654
32655 // ── `Atom::ESCAPE_SOURCES` — the closed-set forced-arity ALL array
32656 // over every escape-SOURCE byte `Atom::decode_str_escape` has a
32657 // non-passthrough arm for. Cross-sub-vocabulary SPAN peer of
32658 // `NAMED_ESCAPE_TABLE` (three pattern-DISTINCT-from-value rows'
32659 // SOURCE column) + `SELF_ESCAPE_TABLE` (two pattern-EQUALS-value
32660 // rows) at ONE typed `[char; 5]` on the SAME closed-set [`Atom`]
32661 // algebra. The pins below anchor (a) the SPAN composition against
32662 // the two peer sub-vocabulary arrays' SOURCE columns in canonical
32663 // declaration order, (b) the forced-arity cardinality against a
32664 // refactor that loosens the array to `&[char]`, (c) pairwise
32665 // disjointness inherited from the two peer sub-vocabularies' own
32666 // pairwise disjointness + cross-sub-vocabulary disjointness, (d)
32667 // the "every row hits a non-passthrough arm" closure identity
32668 // against a refactor that added a stale ESCAPE_SOURCES row without
32669 // extending `decode_str_escape`, and (e) the load-bearing partition
32670 // NAMED-source-column prefix / SELF-source-column suffix that the
32671 // declaration order encodes for consumers that walk the array by
32672 // index. Sibling-shape to the composition-pin block above for
32673 // `SELF_ESCAPE_TABLE` — those pins bind the peer sub-vocabulary at
32674 // the same closed-set algebra; this block binds the SPAN of the
32675 // two sub-vocabularies at ONE array up.
32676
32677 #[test]
32678 fn atom_escape_sources_composes_from_named_and_self_escape_sub_vocabularies_in_declaration_order(
32679 ) {
32680 // COMPOSITION LAW: the ALL array's rows are (NAMED_ESCAPE_TABLE
32681 // SOURCE column, then SELF_ESCAPE_TABLE rows) in canonical
32682 // declaration order matching `decode_str_escape`'s match-arm
32683 // order. Pins the SPAN composition at ONE site so a reorder
32684 // that broke the (named prefix, self suffix) partition (e.g.
32685 // interleaving the two sub-vocabularies' rows, sorting
32686 // alphabetically) silently misaligns every consumer that
32687 // sweeps the array by index. Sibling-shape pin to
32688 // `atom_self_escape_table_composes_from_algebra_constants_in_declaration_order`
32689 // one axis over on the peer sub-vocabulary at
32690 // `Atom::SELF_ESCAPE_TABLE`, and to
32691 // `atom_named_escape_table_composes_from_per_role_constants_in_declaration_order`
32692 // on the SOURCE column of the other peer sub-vocabulary at
32693 // `Atom::NAMED_ESCAPE_TABLE`.
32694 assert_eq!(
32695 Atom::ESCAPE_SOURCES,
32696 [
32697 Atom::NAMED_ESCAPE_TABLE[0].0,
32698 Atom::NAMED_ESCAPE_TABLE[1].0,
32699 Atom::NAMED_ESCAPE_TABLE[2].0,
32700 Atom::SELF_ESCAPE_TABLE[0],
32701 Atom::SELF_ESCAPE_TABLE[1],
32702 ],
32703 "ESCAPE_SOURCES composition drifted from the canonical \
32704 (NAMED SOURCE column, then SELF rows) SPAN in declaration \
32705 order — the SPAN lift must route through the two peer \
32706 sub-vocabulary arrays' rows in the exact order \
32707 decode_str_escape's match arms fire on them.",
32708 );
32709 }
32710
32711 #[test]
32712 fn atom_escape_sources_has_expected_cardinality() {
32713 // ARITY PIN: the forced-arity `[char; 5]` shape survives at
32714 // runtime AND matches the two peer sub-vocabularies' summed
32715 // cardinality. Pins the closed-set size against a refactor
32716 // that loosens the array's type to `&'static [char]` or
32717 // `Vec<char>` (which would drop the compile-time arity
32718 // forcing rustc bakes into `[T; N]` declarations) AND against
32719 // a refactor that drifted this array's arity without extending
32720 // one of the two peer sub-vocabularies (which would silently
32721 // desynchronize the SPAN identity from its two source arrays).
32722 // Sibling posture to
32723 // `atom_named_and_self_escape_tables_span_the_five_non_passthrough_arms`
32724 // — where that pin binds the summed cardinality at ONE
32725 // assert_eq!, this pin binds the SPAN's OWN cardinality at
32726 // the ALL array level so a future refactor that added a
32727 // NAMED_ESCAPE_TABLE row without extending ESCAPE_SOURCES
32728 // fails HERE at the paired-length equality rather than at a
32729 // distant sweep site.
32730 assert_eq!(
32731 Atom::ESCAPE_SOURCES.len(),
32732 5,
32733 "ESCAPE_SOURCES cardinality drifted from the substrate-\
32734 canonical FIVE non-passthrough arms of \
32735 Atom::decode_str_escape.",
32736 );
32737 assert_eq!(
32738 Atom::ESCAPE_SOURCES.len(),
32739 Atom::NAMED_ESCAPE_TABLE.len() + Atom::SELF_ESCAPE_TABLE.len(),
32740 "ESCAPE_SOURCES cardinality drifted from the SUM of the two \
32741 peer sub-vocabulary arrays' cardinalities — the SPAN \
32742 identity is broken.",
32743 );
32744 }
32745
32746 #[test]
32747 fn atom_escape_sources_pairwise_distinct() {
32748 // PAIRWISE DISJOINTNESS: every row is distinct from every
32749 // other row. The closed-set SPAN inherits pairwise
32750 // disjointness from the two peer sub-vocabularies (each
32751 // pinned pairwise distinct at their own sibling tests) PLUS
32752 // the cross-sub-vocabulary disjointness pinned by
32753 // `atom_self_escape_table_disjoint_from_named_escape_table` —
32754 // this test closes the disjointness contract at the SPAN
32755 // level so a future refactor that added a sixth arm whose
32756 // SOURCE aliased an existing row surfaces HERE rather than
32757 // at a distant reader round-trip. Sibling posture to
32758 // `atom_self_escape_table_pairwise_distinct` +
32759 // `atom_named_escape_table_sources_pairwise_distinct` on the
32760 // two peer sub-vocabularies.
32761 for i in 0..Atom::ESCAPE_SOURCES.len() {
32762 for j in (i + 1)..Atom::ESCAPE_SOURCES.len() {
32763 assert_ne!(
32764 Atom::ESCAPE_SOURCES[i],
32765 Atom::ESCAPE_SOURCES[j],
32766 "ESCAPE_SOURCES bytes at indices {i} and {j} alias \
32767 — every non-passthrough arm must specialize on a \
32768 distinct SOURCE byte.",
32769 );
32770 }
32771 }
32772 }
32773
32774 #[test]
32775 fn atom_escape_sources_partitions_into_named_source_prefix_and_self_source_suffix() {
32776 // PARTITION PIN: the SPAN's canonical declaration order
32777 // encodes a (named prefix, self suffix) partition —
32778 // `ESCAPE_SOURCES[0..3]` IS `NAMED_ESCAPE_TABLE`'s SOURCE
32779 // column, `ESCAPE_SOURCES[3..5]` IS `SELF_ESCAPE_TABLE`. Pin
32780 // the partition at the index level so a reorder that broke
32781 // the sub-vocabulary boundary (e.g. moved `STR_DELIMITER` to
32782 // index 2 to sort alphabetically, or interleaved a self row
32783 // between two named rows) fails HERE rather than as silent
32784 // drift where consumers that walk the array by index would
32785 // route the wrong sub-vocabulary's row through the wrong
32786 // downstream handler.
32787 for (i, &(src, _)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32788 assert_eq!(
32789 Atom::ESCAPE_SOURCES[i],
32790 src,
32791 "ESCAPE_SOURCES[{i}] ({}) drifted from \
32792 NAMED_ESCAPE_TABLE[{i}].0 ({src}) — the named-prefix \
32793 partition is broken.",
32794 Atom::ESCAPE_SOURCES[i],
32795 );
32796 }
32797 for (i, &self_byte) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
32798 let span_index = Atom::NAMED_ESCAPE_TABLE.len() + i;
32799 assert_eq!(
32800 Atom::ESCAPE_SOURCES[span_index],
32801 self_byte,
32802 "ESCAPE_SOURCES[{span_index}] ({}) drifted from \
32803 SELF_ESCAPE_TABLE[{i}] ({self_byte}) — the self-suffix \
32804 partition is broken.",
32805 Atom::ESCAPE_SOURCES[span_index],
32806 );
32807 }
32808 }
32809
32810 #[test]
32811 fn atom_escape_sources_every_row_projects_through_a_non_passthrough_arm_of_decode_str_escape() {
32812 // NON-PASSTHROUGH-CLOSURE CONTRACT: every row of
32813 // `ESCAPE_SOURCES` MUST project through a NON-passthrough arm
32814 // of `decode_str_escape` — the SPAN is definitionally the set
32815 // of SOURCE bytes for which `decode_str_escape` has a typed
32816 // arm. Pin the closure structurally so a refactor that added
32817 // an `ESCAPE_SOURCES` row without extending
32818 // `decode_str_escape`'s match (or vice versa) surfaces HERE at
32819 // the first drifted row rather than at a distant sweep-site
32820 // regression. The row projects through a non-passthrough arm
32821 // iff EITHER (a) it's a `NAMED_ESCAPE_TABLE` SOURCE and
32822 // `decode_str_escape` returns the paired DECODED byte
32823 // (`decode(src) != src`), OR (b) it's a `SELF_ESCAPE_TABLE`
32824 // row and `decode_str_escape` returns the SAME byte
32825 // (`decode(esc) == esc` by definitional identity — the arm
32826 // exists AND fires, but the projection is pattern-EQUALS-
32827 // value). Both arm-kinds are non-passthrough because they
32828 // have a TYPED arm in the match; the `other => other`
32829 // passthrough only fires for chars NOT in the SPAN. Pin the
32830 // dispatch identity by checking each row lives in exactly ONE
32831 // of the two peer sub-vocabularies.
32832 for (i, &esc) in Atom::ESCAPE_SOURCES.iter().enumerate() {
32833 let in_named = Atom::NAMED_ESCAPE_TABLE.iter().any(|&(src, _)| src == esc);
32834 let in_self = Atom::SELF_ESCAPE_TABLE.contains(&esc);
32835 assert!(
32836 in_named ^ in_self,
32837 "ESCAPE_SOURCES[{i}] ({esc:?}) must belong to EXACTLY \
32838 ONE peer sub-vocabulary (in_named={in_named}, \
32839 in_self={in_self}) — the SPAN identity requires each \
32840 row to fire a typed non-passthrough arm on either \
32841 the pattern-DISTINCT-from-value axis (NAMED) OR the \
32842 pattern-EQUALS-value axis (SELF), never both, never \
32843 neither.",
32844 );
32845 // Consistency check: the arm's projection must agree with
32846 // the row's sub-vocabulary identity. NAMED rows project to
32847 // the paired DECODED byte (which the pairwise-distinct pin
32848 // guarantees differs from SOURCE); SELF rows project to
32849 // the row byte itself.
32850 let decoded = Atom::decode_str_escape(esc);
32851 if in_named {
32852 assert_ne!(
32853 decoded, esc,
32854 "ESCAPE_SOURCES[{i}] ({esc:?}) is a NAMED SOURCE — \
32855 decode_str_escape MUST project to the paired \
32856 DECODED byte (distinct from SOURCE) but returned \
32857 {decoded:?}.",
32858 );
32859 } else {
32860 assert_eq!(
32861 decoded, esc,
32862 "ESCAPE_SOURCES[{i}] ({esc:?}) is a SELF row — \
32863 decode_str_escape MUST project to the same byte \
32864 (pattern-EQUALS-value by definitional identity) \
32865 but returned {decoded:?}.",
32866 );
32867 }
32868 }
32869 }
32870
32871 #[test]
32872 fn atom_decode_str_escape_composes_end_to_end_through_reader_for_every_named_arm() {
32873 // END-TO-END COMPOSITION CONTRACT: pin that every typed
32874 // escape-table arm — the three named-escape arms + the two
32875 // pattern-equals-value self-escape arms + a representative
32876 // passthrough — decodes through the reader's full
32877 // escape-handler pipeline via ONE
32878 // `Atom::decode_str_escape(esc)` call. Wrap each `esc` in a
32879 // STR_DELIMITER-wrapped, STR_ESCAPE_LEAD-led source, run it
32880 // through [`crate::reader::read`], and assert the resulting
32881 // Str payload equals `decode_str_escape(esc).to_string()`.
32882 // A regression that re-inlined the reader's table (e.g.
32883 // reverted the escape-handler branch to per-arm literals AND
32884 // added a sixth named-escape arm to
32885 // [`Atom::decode_str_escape`] without updating the reader)
32886 // fails HERE at the first arm whose decode diverges.
32887 //
32888 // Sibling-shape pin to
32889 // `atom_str_escape_lead_closes_reader_self_escape_round_trip_for_backslash_payload`
32890 // — where that pin exercises the SINGLE self-escape arm on
32891 // the escape-lead axis end-to-end, this sweep exercises the
32892 // FULL closed-set table on the same axis.
32893 // Pre-lift the (NAMED_ESCAPE_TABLE.iter().map(|&(src, _)| src)
32894 // .chain(SELF_ESCAPE_TABLE.iter().copied())) runtime iterator
32895 // chain reassembled the FIVE non-passthrough source bytes at
32896 // this callsite; post-lift the SPAN binds at ONE forced-arity
32897 // `Atom::ESCAPE_SOURCES: [char; 5]` on the closed-set [`Atom`]
32898 // algebra so the sweep iterates the typed ALL array directly.
32899 // The trailing `'x'` is a representative PASSTHROUGH byte
32900 // (deliberately NOT in `ESCAPE_SOURCES`) — the sweep exercises
32901 // BOTH the non-passthrough closed set AND the passthrough
32902 // default-arm through the same reader pipeline in ONE loop.
32903 let escs: Vec<char> = Atom::ESCAPE_SOURCES
32904 .iter()
32905 .copied()
32906 .chain(std::iter::once('x'))
32907 .collect();
32908 for esc in escs {
32909 let source = format!(
32910 "{}{}{}{}",
32911 Atom::STR_DELIMITER,
32912 Atom::STR_ESCAPE_LEAD,
32913 esc,
32914 Atom::STR_DELIMITER,
32915 );
32916 let forms = crate::reader::read(&source).unwrap_or_else(|e| {
32917 panic!(
32918 "reader rejected `{source}` composed from \
32919 STR_DELIMITER + STR_ESCAPE_LEAD + `{esc}`: {e}"
32920 )
32921 });
32922 assert_eq!(
32923 forms.len(),
32924 1,
32925 "escape sweep for `{esc}` must read as exactly one form, \
32926 got {forms:?}",
32927 );
32928 let decoded = Atom::decode_str_escape(esc);
32929 assert_eq!(
32930 forms[0],
32931 Sexp::Atom(Atom::string(decoded.to_string())),
32932 "escape sweep for `{esc}` drifted from \
32933 Sexp::Atom(Atom::string(decode_str_escape({esc:?}) = \
32934 {decoded:?}).to_string()) — the reader's escape-handler \
32935 branch OR Atom::decode_str_escape drifted from the ONE \
32936 shared closed-set escape-table projection",
32937 );
32938 }
32939 }
32940
32941 // ── `Atom::ESCAPE_DECODED` — the closed-set forced-arity ALL array
32942 // over every DECODED byte `Atom::decode_str_escape` can emit from a
32943 // non-passthrough arm. Column-dual peer of `Atom::ESCAPE_SOURCES` at
32944 // the SAME `[char; 5]` shape on the SAME closed-set [`Atom`] algebra:
32945 // together the two arrays close the (SOURCE, DECODED) cross-product
32946 // of `decode_str_escape`'s non-passthrough arm-set at two byte-
32947 // identical `[char; 5]` shapes. Cross-sub-vocabulary SPAN peer of
32948 // `NAMED_ESCAPE_TABLE` (three pattern-DISTINCT-from-value rows'
32949 // DECODED column) + `SELF_ESCAPE_TABLE` (two pattern-EQUALS-value
32950 // rows whose DECODED column is definitionally the row byte itself)
32951 // at ONE typed `[char; 5]` on the SAME closed-set [`Atom`] algebra.
32952 // The pins below anchor (a) the SPAN composition against the two
32953 // peer sub-vocabulary arrays' DECODED columns in canonical
32954 // declaration order, (b) the forced-arity cardinality against a
32955 // refactor that loosens the array to `&[char]`, (c) pairwise
32956 // disjointness on the DECODED column, (d) the load-bearing
32957 // partition NAMED-decoded-column prefix / SELF-decoded-column suffix
32958 // that the declaration order encodes for consumers that walk the
32959 // array by index, and (e) the column-dual POINTWISE projection
32960 // identity that pins `ESCAPE_DECODED[i] ==
32961 // decode_str_escape(ESCAPE_SOURCES[i])` for every index — the
32962 // NEW load-bearing invariant this SPAN adds on top of `ESCAPE_SOURCES`.
32963
32964 #[test]
32965 fn atom_escape_decoded_composes_from_named_and_self_escape_sub_vocabularies_in_declaration_order(
32966 ) {
32967 // COMPOSITION LAW: the ALL array's rows are (NAMED_ESCAPE_TABLE
32968 // DECODED column, then SELF_ESCAPE_TABLE rows) in canonical
32969 // declaration order matching `decode_str_escape`'s match-arm
32970 // order. Pins the SPAN composition at ONE site so a reorder
32971 // that broke the (named prefix, self suffix) partition (e.g.
32972 // interleaving the two sub-vocabularies' rows, sorting by C0
32973 // byte value) silently misaligns every consumer that sweeps the
32974 // array by index. Sibling-shape pin to
32975 // `atom_escape_sources_composes_from_named_and_self_escape_sub_vocabularies_in_declaration_order`
32976 // one column over on the peer SOURCE-column SPAN at
32977 // `Atom::ESCAPE_SOURCES`.
32978 assert_eq!(
32979 Atom::ESCAPE_DECODED,
32980 [
32981 Atom::NAMED_ESCAPE_TABLE[0].1,
32982 Atom::NAMED_ESCAPE_TABLE[1].1,
32983 Atom::NAMED_ESCAPE_TABLE[2].1,
32984 Atom::SELF_ESCAPE_TABLE[0],
32985 Atom::SELF_ESCAPE_TABLE[1],
32986 ],
32987 "ESCAPE_DECODED composition drifted from the canonical \
32988 (NAMED DECODED column, then SELF rows) SPAN in declaration \
32989 order — the SPAN lift must route through the two peer \
32990 sub-vocabulary arrays' rows in the exact order \
32991 decode_str_escape's match arms fire on them.",
32992 );
32993 }
32994
32995 #[test]
32996 fn atom_escape_decoded_has_expected_cardinality() {
32997 // ARITY PIN: the forced-arity `[char; 5]` shape survives at
32998 // runtime AND matches the two peer sub-vocabularies' summed
32999 // cardinality AND matches the column-dual peer
33000 // `ESCAPE_SOURCES`'s arity. Pins the closed-set size against a
33001 // refactor that loosens the array's type to `&'static [char]`
33002 // or `Vec<char>` (which would drop the compile-time arity
33003 // forcing rustc bakes into `[T; N]` declarations) AND against a
33004 // refactor that drifted this array's arity without extending
33005 // one of the two peer sub-vocabularies (which would silently
33006 // desynchronize the SPAN identity from its two source arrays)
33007 // AND against a refactor that broke the column-dual shape
33008 // symmetry with `ESCAPE_SOURCES`. Sibling posture to
33009 // `atom_escape_sources_has_expected_cardinality` on the peer
33010 // SOURCE-column SPAN.
33011 assert_eq!(
33012 Atom::ESCAPE_DECODED.len(),
33013 5,
33014 "ESCAPE_DECODED cardinality drifted from the substrate-\
33015 canonical FIVE non-passthrough arms of \
33016 Atom::decode_str_escape.",
33017 );
33018 assert_eq!(
33019 Atom::ESCAPE_DECODED.len(),
33020 Atom::NAMED_ESCAPE_TABLE.len() + Atom::SELF_ESCAPE_TABLE.len(),
33021 "ESCAPE_DECODED cardinality drifted from the SUM of the two \
33022 peer sub-vocabulary arrays' cardinalities — the SPAN \
33023 identity is broken.",
33024 );
33025 assert_eq!(
33026 Atom::ESCAPE_DECODED.len(),
33027 Atom::ESCAPE_SOURCES.len(),
33028 "ESCAPE_DECODED cardinality drifted from the column-dual peer \
33029 ESCAPE_SOURCES's cardinality — the column-dual shape \
33030 symmetry `[char; 5]` × `[char; 5]` on decode_str_escape's \
33031 non-passthrough arm-set is broken.",
33032 );
33033 }
33034
33035 #[test]
33036 fn atom_escape_decoded_pairwise_distinct() {
33037 // PAIRWISE DISJOINTNESS: every row is distinct from every
33038 // other row. On the DECODED column this is TIGHTER than on
33039 // the SOURCE column: the NAMED sub-vocabulary's DECODED rows
33040 // are C0 control bytes (`'\n'`, `'\t'`, `'\r'` — bytes 0x0A,
33041 // 0x09, 0x0D) and the SELF sub-vocabulary's rows are printable
33042 // ASCII bytes (`'"'`, `'\\'` — bytes 0x22, 0x5C), so no
33043 // NAMED-DECODED byte can alias a SELF byte by byte-class
33044 // disjointness. This test closes the disjointness contract at
33045 // the SPAN level so a future refactor that added a sixth arm
33046 // whose DECODED aliased an existing row (e.g. drifted
33047 // TAB_ESCAPE_DECODED from `'\t'` to `'\n'`, collapsing two
33048 // arms onto the same DECODED byte) surfaces HERE rather than
33049 // at a distant reader round-trip. Sibling posture to
33050 // `atom_escape_sources_pairwise_distinct` on the peer
33051 // SOURCE-column SPAN.
33052 for i in 0..Atom::ESCAPE_DECODED.len() {
33053 for j in (i + 1)..Atom::ESCAPE_DECODED.len() {
33054 assert_ne!(
33055 Atom::ESCAPE_DECODED[i],
33056 Atom::ESCAPE_DECODED[j],
33057 "ESCAPE_DECODED bytes at indices {i} and {j} alias \
33058 — every non-passthrough arm must emit a distinct \
33059 DECODED byte.",
33060 );
33061 }
33062 }
33063 }
33064
33065 #[test]
33066 fn atom_escape_decoded_partitions_into_named_decoded_prefix_and_self_decoded_suffix() {
33067 // PARTITION PIN: the SPAN's canonical declaration order
33068 // encodes a (named prefix, self suffix) partition —
33069 // `ESCAPE_DECODED[0..3]` IS `NAMED_ESCAPE_TABLE`'s DECODED
33070 // column, `ESCAPE_DECODED[3..5]` IS `SELF_ESCAPE_TABLE` (the
33071 // SELF sub-vocabulary's DECODED column IS its row column by
33072 // pattern-EQUALS-value identity). Pin the partition at the
33073 // index level so a reorder that broke the sub-vocabulary
33074 // boundary (e.g. moved a SELF row to index 2 to sort by C0
33075 // byte value, or interleaved a self row between two named
33076 // rows) fails HERE rather than as silent drift where consumers
33077 // that walk the array by index would route the wrong
33078 // sub-vocabulary's row through the wrong downstream handler.
33079 // Sibling-shape pin to
33080 // `atom_escape_sources_partitions_into_named_source_prefix_and_self_source_suffix`
33081 // one column over on the peer SOURCE-column SPAN.
33082 for (i, &(_, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
33083 assert_eq!(
33084 Atom::ESCAPE_DECODED[i],
33085 dec,
33086 "ESCAPE_DECODED[{i}] ({}) drifted from \
33087 NAMED_ESCAPE_TABLE[{i}].1 ({dec}) — the named-decoded-\
33088 prefix partition is broken.",
33089 Atom::ESCAPE_DECODED[i],
33090 );
33091 }
33092 for (i, &self_byte) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
33093 let span_index = Atom::NAMED_ESCAPE_TABLE.len() + i;
33094 assert_eq!(
33095 Atom::ESCAPE_DECODED[span_index],
33096 self_byte,
33097 "ESCAPE_DECODED[{span_index}] ({}) drifted from \
33098 SELF_ESCAPE_TABLE[{i}] ({self_byte}) — the self-decoded-\
33099 suffix partition is broken (SELF rows are pattern-\
33100 EQUALS-value so DECODED == SOURCE by definitional \
33101 identity).",
33102 Atom::ESCAPE_DECODED[span_index],
33103 );
33104 }
33105 }
33106
33107 #[test]
33108 fn atom_escape_decoded_projects_pointwise_from_escape_sources_through_decode_str_escape() {
33109 // COLUMN-DUAL POINTWISE PROJECTION LAW: the two forced-arity
33110 // `[char; 5]` peer arrays [`Atom::ESCAPE_SOURCES`] +
33111 // [`Atom::ESCAPE_DECODED`] are the SOURCE column and DECODED
33112 // column of `decode_str_escape`'s non-passthrough arm-set — for
33113 // every index `i` in `0..5`,
33114 // `ESCAPE_DECODED[i] == Atom::decode_str_escape(
33115 // ESCAPE_SOURCES[i])`. This is the NEW load-bearing invariant
33116 // this SPAN adds on top of `ESCAPE_SOURCES`: the column-dual
33117 // pointwise projection identity that pins the two `[char; 5]`
33118 // arrays as the two columns of the SAME arm-set at the SAME
33119 // row-order at rustc time. Pin the projection identity
33120 // structurally so a refactor that drifted either array's
33121 // declaration order OR drifted `decode_str_escape`'s match-arm
33122 // ordering surfaces HERE at the first drifted index rather
33123 // than at a distant sweep site. A regression that swapped
33124 // (e.g.) rows 3 and 4 in `ESCAPE_DECODED` without swapping
33125 // them in `ESCAPE_SOURCES` (or vice versa) would silently
33126 // collapse the column-dual identity; this pin catches the
33127 // divergence at the first drifted index.
33128 for (i, &src) in Atom::ESCAPE_SOURCES.iter().enumerate() {
33129 let expected = Atom::decode_str_escape(src);
33130 assert_eq!(
33131 Atom::ESCAPE_DECODED[i],
33132 expected,
33133 "ESCAPE_DECODED[{i}] ({}) drifted from \
33134 decode_str_escape(ESCAPE_SOURCES[{i}] = {src:?}) = \
33135 {expected:?} — the column-dual pointwise projection \
33136 law from ESCAPE_SOURCES onto ESCAPE_DECODED through \
33137 decode_str_escape is broken at index {i}.",
33138 Atom::ESCAPE_DECODED[i],
33139 );
33140 }
33141 }
33142
33143 // ── `Atom::ESCAPE_TABLE` — the paired-column SPAN closing BOTH
33144 // columns of `decode_str_escape`'s FIVE non-passthrough arms at
33145 // ONE typed forced-arity `[(char, char); 5]` on the closed-set
33146 // [`Atom`] algebra. Peer-collapse of the two column-dual
33147 // `[char; 5]` peer arrays `Atom::ESCAPE_SOURCES` and
33148 // `Atom::ESCAPE_DECODED` at the paired-shape level. Tests pin
33149 // (a) the pointwise composition law `ESCAPE_TABLE[i] ==
33150 // (ESCAPE_SOURCES[i], ESCAPE_DECODED[i])`, (b) the forced-arity
33151 // `[(char, char); 5]` shape, (c) the sub-vocabulary partition into
33152 // NAMED-paired prefix + SELF-reshape suffix (the SELF sub-
33153 // vocabulary's rows re-shape from `char` to `(row, row)` via the
33154 // definitional-identity collapse), (d) the pattern-classification
33155 // partition where NAMED rows carry `row.0 != row.1` and SELF rows
33156 // carry `row.0 == row.1`, and (e) the pointwise projection law
33157 // `decode_str_escape(row.0) == row.1` for every row.
33158
33159 #[test]
33160 fn atom_escape_table_composes_pointwise_from_escape_sources_and_escape_decoded_column_duals() {
33161 // POINTWISE COMPOSITION LAW: the paired-column SPAN's rows are
33162 // the two column-dual peer arrays zipped pointwise — for every
33163 // index `i` in `0..5`, `ESCAPE_TABLE[i] ==
33164 // (ESCAPE_SOURCES[i], ESCAPE_DECODED[i])`. This is the load-
33165 // bearing pointwise identity carrying the two-column
33166 // composition relation between the paired SPAN and its two
33167 // column-dual peer SPANs. A refactor that drifted any of the
33168 // three arrays' declaration orders OR drifted
33169 // `decode_str_escape`'s match-arm ordering surfaces HERE at
33170 // the first drifted index rather than at a distant sweep site.
33171 // Sibling-shape pin to
33172 // `atom_escape_decoded_projects_pointwise_from_escape_sources_through_decode_str_escape`
33173 // one composition layer up: where that pin binds the DECODED-
33174 // column SPAN to the SOURCE-column SPAN through
33175 // `decode_str_escape`, this pin binds the paired-column SPAN
33176 // to the two column-dual peer SPANs through pointwise
33177 // composition.
33178 assert_eq!(Atom::ESCAPE_TABLE.len(), Atom::ESCAPE_SOURCES.len());
33179 assert_eq!(Atom::ESCAPE_TABLE.len(), Atom::ESCAPE_DECODED.len());
33180 for (i, &(src, decoded)) in Atom::ESCAPE_TABLE.iter().enumerate() {
33181 assert_eq!(
33182 src,
33183 Atom::ESCAPE_SOURCES[i],
33184 "ESCAPE_TABLE[{i}].0 ({src:?}) drifted from \
33185 ESCAPE_SOURCES[{i}] ({:?}) — the paired-column SPAN's \
33186 SOURCE-column projection is broken at index {i}.",
33187 Atom::ESCAPE_SOURCES[i],
33188 );
33189 assert_eq!(
33190 decoded,
33191 Atom::ESCAPE_DECODED[i],
33192 "ESCAPE_TABLE[{i}].1 ({decoded:?}) drifted from \
33193 ESCAPE_DECODED[{i}] ({:?}) — the paired-column SPAN's \
33194 DECODED-column projection is broken at index {i}.",
33195 Atom::ESCAPE_DECODED[i],
33196 );
33197 }
33198 }
33199
33200 #[test]
33201 fn atom_escape_table_has_expected_cardinality() {
33202 // ARITY PIN: the forced-arity `[(char, char); 5]` shape
33203 // survives at runtime AND matches the two column-dual peer
33204 // arrays' arities AND matches the summed cardinality of the
33205 // two sub-vocabulary arrays. Pins the closed-set size against
33206 // a refactor that loosens the array's type to
33207 // `&'static [(char, char)]` or `Vec<(char, char)>` (which
33208 // would drop the compile-time arity forcing rustc bakes into
33209 // `[T; N]` declarations) AND against a refactor that drifted
33210 // this array's arity without extending one of the two peer
33211 // column-dual arrays or one of the two peer sub-vocabulary
33212 // arrays. Sibling posture to
33213 // `atom_escape_sources_has_expected_cardinality` +
33214 // `atom_escape_decoded_has_expected_cardinality` on the two
33215 // column-dual peer SPANs.
33216 assert_eq!(
33217 Atom::ESCAPE_TABLE.len(),
33218 5,
33219 "ESCAPE_TABLE cardinality drifted from the substrate-\
33220 canonical FIVE non-passthrough arms of \
33221 Atom::decode_str_escape.",
33222 );
33223 assert_eq!(
33224 Atom::ESCAPE_TABLE.len(),
33225 Atom::ESCAPE_SOURCES.len(),
33226 "ESCAPE_TABLE cardinality drifted from the column-dual \
33227 peer ESCAPE_SOURCES's cardinality — the paired-column \
33228 shape symmetry `[(char, char); 5]` × `[char; 5]` on \
33229 decode_str_escape's non-passthrough arm-set is broken on \
33230 the SOURCE column.",
33231 );
33232 assert_eq!(
33233 Atom::ESCAPE_TABLE.len(),
33234 Atom::ESCAPE_DECODED.len(),
33235 "ESCAPE_TABLE cardinality drifted from the column-dual \
33236 peer ESCAPE_DECODED's cardinality — the paired-column \
33237 shape symmetry `[(char, char); 5]` × `[char; 5]` on \
33238 decode_str_escape's non-passthrough arm-set is broken on \
33239 the DECODED column.",
33240 );
33241 assert_eq!(
33242 Atom::ESCAPE_TABLE.len(),
33243 Atom::NAMED_ESCAPE_TABLE.len() + Atom::SELF_ESCAPE_TABLE.len(),
33244 "ESCAPE_TABLE cardinality drifted from the SUM of the two \
33245 peer sub-vocabulary arrays' cardinalities — the paired-\
33246 column SPAN identity is broken.",
33247 );
33248 }
33249
33250 #[test]
33251 fn atom_escape_table_partitions_into_named_paired_prefix_and_self_reshape_suffix() {
33252 // SUB-VOCABULARY PARTITION LAW: `ESCAPE_TABLE[0..3]` IS the
33253 // NAMED_ESCAPE_TABLE (pattern-DISTINCT-from-value sub-
33254 // vocabulary at its native paired shape passes through
33255 // identically); `ESCAPE_TABLE[3..5]` re-shapes the SELF_ESCAPE_TABLE
33256 // rows from `char` to `(row, row)` via the definitional-
33257 // identity collapse (pattern-EQUALS-value sub-vocabulary). The
33258 // index-level partition pin binds every consumer walking the
33259 // paired-column SPAN by index to the (named-paired prefix,
33260 // self-reshape suffix) partition at rustc time. Sibling
33261 // posture to
33262 // `atom_escape_sources_partitions_into_named_source_prefix_and_self_source_suffix`
33263 // + `atom_escape_decoded_partitions_into_named_decoded_prefix_and_self_decoded_suffix`
33264 // on the two column-dual peer SPANs.
33265 assert_eq!(
33266 &Atom::ESCAPE_TABLE[0..3],
33267 &Atom::NAMED_ESCAPE_TABLE[..],
33268 "ESCAPE_TABLE named-paired prefix drifted from \
33269 NAMED_ESCAPE_TABLE — the sub-vocabulary partition is \
33270 broken.",
33271 );
33272 for (self_index, &row) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
33273 let span_index = Atom::NAMED_ESCAPE_TABLE.len() + self_index;
33274 assert_eq!(
33275 Atom::ESCAPE_TABLE[span_index],
33276 (row, row),
33277 "ESCAPE_TABLE[{span_index}] ({:?}) drifted from the \
33278 SELF-reshape suffix pair (SELF_ESCAPE_TABLE[{self_index}], \
33279 SELF_ESCAPE_TABLE[{self_index}]) = ({row:?}, \
33280 {row:?}) — the pattern-EQUALS-value definitional-\
33281 identity reshape is broken.",
33282 Atom::ESCAPE_TABLE[span_index],
33283 );
33284 }
33285 }
33286
33287 #[test]
33288 fn atom_escape_table_named_prefix_is_pattern_distinct_and_self_suffix_is_pattern_equals() {
33289 // PATTERN-CLASSIFICATION PARTITION LAW: the paired-column
33290 // SPAN's rows encode their sub-vocabulary identity in the
33291 // per-row per-column identity relation — NAMED rows (indices
33292 // 0..3) carry `row.0 != row.1` (pattern-DISTINCT-from-value),
33293 // SELF rows (indices 3..5) carry `row.0 == row.1` (pattern-
33294 // EQUALS-value). A consumer classifying an escape arm reads
33295 // the sub-vocabulary off `row.0 == row.1` rather than off an
33296 // index range or a separate tag — the classification IS the
33297 // pair's per-column identity relation. Pins the load-bearing
33298 // structural invariant that the paired-column SPAN encodes
33299 // both sub-vocabularies AT the paired shape.
33300 for i in 0..Atom::NAMED_ESCAPE_TABLE.len() {
33301 let (src, decoded) = Atom::ESCAPE_TABLE[i];
33302 assert_ne!(
33303 src, decoded,
33304 "ESCAPE_TABLE[{i}] ({src:?}, {decoded:?}) is in the \
33305 NAMED prefix but its per-column identity relation \
33306 collapsed to `src == decoded` — the pattern-DISTINCT-\
33307 from-value classification is broken.",
33308 );
33309 }
33310 for self_index in 0..Atom::SELF_ESCAPE_TABLE.len() {
33311 let span_index = Atom::NAMED_ESCAPE_TABLE.len() + self_index;
33312 let (src, decoded) = Atom::ESCAPE_TABLE[span_index];
33313 assert_eq!(
33314 src, decoded,
33315 "ESCAPE_TABLE[{span_index}] ({src:?}, {decoded:?}) is \
33316 in the SELF suffix but its per-column identity \
33317 relation split to `src != decoded` — the pattern-\
33318 EQUALS-value definitional-identity classification is \
33319 broken.",
33320 );
33321 }
33322 }
33323
33324 #[test]
33325 fn atom_escape_table_every_row_projects_through_decode_str_escape_pointwise() {
33326 // POINTWISE PROJECTION LAW: for every `(src, decoded)` row in
33327 // ESCAPE_TABLE, `Atom::decode_str_escape(src) == decoded`. The
33328 // paired-column SPAN closes the (input, output) cross-product
33329 // of `decode_str_escape`'s non-passthrough arm-set at ONE typed
33330 // array so a refactor that drifted the arm-set (swapped rows
33331 // in ESCAPE_TABLE without swapping them in `decode_str_escape`
33332 // or vice versa) surfaces HERE at the first drifted pair
33333 // rather than at a distant sweep site. Sibling-shape pin to
33334 // `atom_escape_decoded_projects_pointwise_from_escape_sources_through_decode_str_escape`
33335 // one composition layer over: where that pin binds the two
33336 // column-dual peer SPANs through `decode_str_escape`, this pin
33337 // binds the paired-column SPAN's per-row `(src, decoded)`
33338 // shape to `decode_str_escape`'s projection at the row level.
33339 for (i, &(src, decoded)) in Atom::ESCAPE_TABLE.iter().enumerate() {
33340 let projected = Atom::decode_str_escape(src);
33341 assert_eq!(
33342 projected, decoded,
33343 "ESCAPE_TABLE[{i}] = ({src:?}, {decoded:?}) drifted \
33344 from Atom::decode_str_escape({src:?}) = {projected:?} \
33345 — the paired-column SPAN's per-row projection law is \
33346 broken at index {i}.",
33347 );
33348 }
33349 }
33350
33351 #[test]
33352 fn atom_escape_table_sources_and_decoded_columns_pairwise_distinct() {
33353 // COLUMN-WISE PAIRWISE DISJOINTNESS: the paired-column SPAN's
33354 // SOURCE column AND DECODED column are each pairwise distinct.
33355 // Inherited disjointness from the two column-dual peer
33356 // `[char; 5]` SPANs already pinned at
33357 // `atom_escape_sources_pairwise_distinct` +
33358 // `atom_escape_decoded_pairwise_distinct`, but pinned HERE at
33359 // the paired-array level so a refactor that drifted the paired
33360 // SPAN's declaration order (collapsing two arms onto the same
33361 // SOURCE or DECODED byte) surfaces at this test rather than
33362 // at a distant sweep site. Sibling posture to the two column-
33363 // dual peer SPANs' pairwise-distinctness tests.
33364 for i in 0..Atom::ESCAPE_TABLE.len() {
33365 for j in (i + 1)..Atom::ESCAPE_TABLE.len() {
33366 assert_ne!(
33367 Atom::ESCAPE_TABLE[i].0,
33368 Atom::ESCAPE_TABLE[j].0,
33369 "ESCAPE_TABLE SOURCE column collision: \
33370 ESCAPE_TABLE[{i}].0 and ESCAPE_TABLE[{j}].0 are \
33371 both {:?} — the paired-column SPAN's SOURCE \
33372 column pairwise disjointness is broken.",
33373 Atom::ESCAPE_TABLE[i].0,
33374 );
33375 assert_ne!(
33376 Atom::ESCAPE_TABLE[i].1,
33377 Atom::ESCAPE_TABLE[j].1,
33378 "ESCAPE_TABLE DECODED column collision: \
33379 ESCAPE_TABLE[{i}].1 and ESCAPE_TABLE[{j}].1 are \
33380 both {:?} — the paired-column SPAN's DECODED \
33381 column pairwise disjointness is broken.",
33382 Atom::ESCAPE_TABLE[i].1,
33383 );
33384 }
33385 }
33386 }
33387
33388 // ── `Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE` — the paired canonical
33389 // `(` / `)` chars routed through the FOUR outer-structural round-
33390 // trip sites: two `crate::reader::tokenize` outer-dispatch arms
33391 // (`Token::LParen`, `Token::RParen`), two bare-atom terminator
33392 // disjuncts (`|| ch == LIST_OPEN` / `|| ch == LIST_CLOSE`), and
33393 // two `fmt::Display for Sexp` arms (`Self::List(_)` opener +
33394 // closer AND `Self::Nil` two-char `()`). Sibling-shape tests to
33395 // the `atom_str_delimiter_*` block above (Str-payload delimiter
33396 // axis), lifted onto the outer-structural [`Sexp`] algebra.
33397
33398 #[test]
33399 fn sexp_list_open_projects_canonical_open_paren_char() {
33400 // Pins the constant's exact `char` value so a typo (`'['`,
33401 // `'{'`, `';'`) or an accidental redefinition surfaces
33402 // immediately. Sibling-shape pin to
33403 // `atom_str_delimiter_projects_canonical_double_quote_char`
33404 // (the Str-payload delimiter axis) — pins the SAME shape on
33405 // the outer list-opener axis of the closed-set outer [`Sexp`]
33406 // algebra.
33407 assert_eq!(
33408 Sexp::LIST_OPEN,
33409 '(',
33410 "LIST_OPEN char drifted from the substrate-canonical `(` \
33411 opener — the reader-round-trip contract at \
33412 crate::reader::tokenize (Token::LParen outer arm, bare-\
33413 atom terminator disjunct) AND fmt::Display for Sexp \
33414 (Self::List(_) opener, Self::Nil two-char left) all bind \
33415 to this ONE constant.",
33416 );
33417 }
33418
33419 #[test]
33420 fn sexp_list_close_projects_canonical_close_paren_char() {
33421 // Pins the constant's exact `char` value so a typo (`']'`,
33422 // `'}'`, `';'`) or an accidental redefinition surfaces
33423 // immediately. Section-for-retraction sibling pin of the
33424 // opener above; the paired-delimiter round-trip contract
33425 // holds iff both constants pin their canonical bytes here.
33426 assert_eq!(
33427 Sexp::LIST_CLOSE,
33428 ')',
33429 "LIST_CLOSE char drifted from the substrate-canonical `)` \
33430 closer — the reader-round-trip contract at \
33431 crate::reader::tokenize (Token::RParen outer arm, bare-\
33432 atom terminator disjunct) AND fmt::Display for Sexp \
33433 (Self::List(_) closer, Self::Nil two-char right) all \
33434 bind to this ONE constant.",
33435 );
33436 }
33437
33438 #[test]
33439 fn sexp_list_delimiters_distinct_from_every_other_algebra_marker() {
33440 // Cross-axis disjointness pin: neither `Sexp::LIST_OPEN` nor
33441 // `Sexp::LIST_CLOSE` may alias any sibling outer-marker
33442 // char on the substrate's other closed-set algebras — the
33443 // Str-payload delimiter (`Atom::STR_DELIMITER`), the
33444 // Keyword-marker prefix (`Atom::KEYWORD_MARKER`), the two
33445 // Bool-literal spellings (`Atom::bool_literal(true|false)`),
33446 // AND every quote-family lead char
33447 // (`QuoteForm::lead_char(qf)` for each `qf` in
33448 // `QuoteForm::ALL`). Otherwise a bare `(`/`)`-starting lexeme
33449 // would ambiguously route through TWO outer-dispatch arms in
33450 // `crate::reader::tokenize`. Guards the paired disjointness
33451 // across the substrate's outer-marker axes so a future
33452 // refactor that swaps a marker to collide with either list
33453 // delimiter surfaces at this pin rather than as a silent
33454 // reader misclassification.
33455 //
33456 // First: opener/closer disjoint from each other — pairs
33457 // MUST be structurally distinct.
33458 assert_ne!(
33459 Sexp::LIST_OPEN,
33460 Sexp::LIST_CLOSE,
33461 "LIST_OPEN and LIST_CLOSE share a byte — the paired-\
33462 delimiter contract would collapse.",
33463 );
33464
33465 // Second: opener/closer disjoint from Str-delimiter.
33466 assert_ne!(
33467 Sexp::LIST_OPEN,
33468 Atom::STR_DELIMITER,
33469 "LIST_OPEN and STR_DELIMITER share a byte — a bare `{}foo` \
33470 lexeme would ambiguously begin a list AND open a string.",
33471 Atom::STR_DELIMITER,
33472 );
33473 assert_ne!(
33474 Sexp::LIST_CLOSE,
33475 Atom::STR_DELIMITER,
33476 "LIST_CLOSE and STR_DELIMITER share a byte — a bare `{}foo` \
33477 lexeme would ambiguously close a list AND open a string.",
33478 Atom::STR_DELIMITER,
33479 );
33480
33481 // Third: opener/closer disjoint from KEYWORD_MARKER's LEAD
33482 // `char` (single-char `":"` on the outer axis) — the
33483 // `Atom::KEYWORD_MARKER` `&'static str`'s projected lead byte
33484 // lives at `Atom::KEYWORD_MARKER_LEAD` on the closed-set
33485 // outer [`Atom`] algebra.
33486 assert_ne!(
33487 Sexp::LIST_OPEN,
33488 Atom::KEYWORD_MARKER_LEAD,
33489 "LIST_OPEN and KEYWORD_MARKER_LEAD share a byte — a bare \
33490 `{}foo` lexeme would ambiguously begin a list AND begin \
33491 a keyword.",
33492 Atom::KEYWORD_MARKER_LEAD,
33493 );
33494 assert_ne!(
33495 Sexp::LIST_CLOSE,
33496 Atom::KEYWORD_MARKER_LEAD,
33497 "LIST_CLOSE and KEYWORD_MARKER_LEAD share a byte — a bare \
33498 `{}foo` lexeme would ambiguously close a list AND begin \
33499 a keyword.",
33500 Atom::KEYWORD_MARKER_LEAD,
33501 );
33502
33503 // Fourth: opener/closer disjoint from every Bool-literal
33504 // spelling's first char (`'#'` for both `"#t"` / `"#f"`).
33505 for b in [true, false] {
33506 let lead = Atom::bool_literal(b)
33507 .chars()
33508 .next()
33509 .expect("bool_literal must be non-empty");
33510 assert_ne!(
33511 Sexp::LIST_OPEN,
33512 lead,
33513 "LIST_OPEN and bool_literal({b:?}) share a lead byte — a \
33514 bare `{lead}...` lexeme would ambiguously begin a list \
33515 AND classify as a Bool.",
33516 );
33517 assert_ne!(
33518 Sexp::LIST_CLOSE,
33519 lead,
33520 "LIST_CLOSE and bool_literal({b:?}) share a lead byte — a \
33521 bare `{lead}...` lexeme would ambiguously close a list \
33522 AND classify as a Bool.",
33523 );
33524 }
33525
33526 // Fifth: opener/closer disjoint from every quote-family
33527 // lead char (`'\''`, `` '`' ``, `','` — three distinct lead
33528 // chars across the four `QuoteForm` variants). The reader's
33529 // outer-dispatch orders the quote-family arm BEFORE the
33530 // list-delimiter arms, so a collision here would silently
33531 // route `(`/`)` through the quote-family branch.
33532 for qf in QuoteForm::ALL {
33533 assert_ne!(
33534 Sexp::LIST_OPEN,
33535 qf.lead_char(),
33536 "LIST_OPEN and QuoteForm::{qf:?}::lead_char share a byte — \
33537 a bare `(...)` list would silently route through the \
33538 quote-family outer-dispatch arm.",
33539 );
33540 assert_ne!(
33541 Sexp::LIST_CLOSE,
33542 qf.lead_char(),
33543 "LIST_CLOSE and QuoteForm::{qf:?}::lead_char share a byte — \
33544 a bare `(...)` list-closer would silently route through \
33545 the quote-family outer-dispatch arm.",
33546 );
33547 }
33548 }
33549
33550 #[test]
33551 fn sexp_display_nil_arm_binds_to_both_list_delimiter_constants() {
33552 // NIL DISPLAY CONTRACT: pin that `format!("{}", Sexp::Nil)`
33553 // produces the two-char rendering composed of BOTH typed
33554 // delimiters — `LIST_OPEN` followed by `LIST_CLOSE`. Pre-lift
33555 // this arm carried the two bytes inline as ONE `"()"` string
33556 // literal; post-lift each byte binds to its typed constant,
33557 // so a delimiter swap flips both the opener AND the closer in
33558 // lockstep at the typed algebra rather than at this arm's
33559 // inline literal. A regression that re-inlines the two bytes
33560 // OR drifts ONE of the two typed-constant bindings fails
33561 // loudly at this composition pin.
33562 let rendered = format!("{}", Sexp::Nil);
33563 let expected: String = Sexp::LIST_DELIMITERS.iter().collect();
33564 assert_eq!(
33565 rendered, expected,
33566 "Sexp::Nil Display drifted from the [LIST_OPEN, LIST_CLOSE] \
33567 composition — the two-char `()` rendering must be the \
33568 string composed of the two typed constants in order.",
33569 );
33570 // Cross-check against the bare byte-string to catch a
33571 // regression that silently swaps the two constants' values.
33572 assert_eq!(
33573 rendered, "()",
33574 "Sexp::Nil Display drifted from the canonical `()` two-char \
33575 rendering — a typed-constant swap would produce e.g. `)(` \
33576 which passes the `[LIST_OPEN, LIST_CLOSE]` compose test \
33577 but fails this hard-coded reference check.",
33578 );
33579 }
33580
33581 #[test]
33582 fn sexp_display_list_arms_bind_to_sexp_list_delimiter_constants() {
33583 // LIST DISPLAY CONTRACT: pin that `format!("{}", Sexp::List(_))`
33584 // opens with `LIST_OPEN`, closes with `LIST_CLOSE`, and
33585 // interleaves the children through the arm's ` `-separator
33586 // loop. Sweeps a representative small list plus the singleton
33587 // list plus the empty list — the empty list is IMPORTANT
33588 // because `Sexp::List(vec![])` is a distinct `Sexp` shape from
33589 // `Sexp::Nil`, and both must render as `()` through the outer
33590 // algebra (the reader's `()` source produces `Sexp::List(vec![])`
33591 // — see the `read` pipeline in `crate::reader`). A regression
33592 // that drifts either arm's binding surfaces here.
33593 for list in [
33594 Sexp::List(vec![]),
33595 Sexp::List(vec![Sexp::symbol("x")]),
33596 Sexp::List(vec![Sexp::symbol("a"), Sexp::symbol("b")]),
33597 Sexp::List(vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)]),
33598 ] {
33599 let rendered = format!("{list}");
33600 let first = rendered
33601 .chars()
33602 .next()
33603 .unwrap_or_else(|| panic!("empty rendering for {list:?}"));
33604 let last = rendered
33605 .chars()
33606 .last()
33607 .unwrap_or_else(|| panic!("empty rendering for {list:?}"));
33608 assert_eq!(
33609 first,
33610 Sexp::LIST_OPEN,
33611 "List Display opener drifted from LIST_OPEN for {list:?}: \
33612 got {rendered:?}",
33613 );
33614 assert_eq!(
33615 last,
33616 Sexp::LIST_CLOSE,
33617 "List Display closer drifted from LIST_CLOSE for {list:?}: \
33618 got {rendered:?}",
33619 );
33620 }
33621 }
33622
33623 #[test]
33624 fn sexp_list_delimiters_close_reader_display_round_trip_for_lists_of_atoms() {
33625 // Load-bearing round-trip contract for the four outer-
33626 // structural sites — the reader's `Token::LParen` /
33627 // `Token::RParen` outer-dispatch arms both bind to
33628 // `Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`, AND the Display
33629 // impl's `Self::List(_)` arms bind to the SAME two constants,
33630 // so wrapping a sequence of atom lexemes in the constants on
33631 // both sides recovers a `Sexp::List(_)` shape that
33632 // round-trips through Display and the reader without drift.
33633 // A regression that swaps ONE of the four arms (e.g.
33634 // re-inlines `'('` at the reader opener while migrating the
33635 // Display opener to a different delimiter) breaks the
33636 // opener-must-match-closer contract across two files.
33637 for children in [
33638 vec![],
33639 vec![Sexp::symbol("foo")],
33640 vec![Sexp::symbol("a"), Sexp::symbol("b"), Sexp::int(42)],
33641 ] {
33642 let original = Sexp::List(children);
33643 let rendered = format!("{original}");
33644 let reread = crate::reader::read(&rendered).unwrap_or_else(|e| {
33645 panic!(
33646 "reader rejected Display-rendered list `{rendered}` \
33647 for `{original:?}`: {e}"
33648 )
33649 });
33650 assert_eq!(
33651 reread.len(),
33652 1,
33653 "Display-rendered list `{rendered}` must read as exactly \
33654 one form, got {reread:?}",
33655 );
33656 assert_eq!(
33657 reread[0], original,
33658 "read(display(list)) drifted from list for {original:?} \
33659 — rendered={rendered:?} reread={reread:?}",
33660 );
33661 }
33662 }
33663
33664 #[test]
33665 fn sexp_list_delimiters_composes_from_algebra_constants_in_declaration_order() {
33666 // FAMILY COMPOSITION LAW: pin that the ALL array's rows are the
33667 // two paired-delimiter algebra constants (`Self::LIST_OPEN`,
33668 // `Self::LIST_CLOSE`) in canonical declaration order matching
33669 // the substrate-canonical (opener, closer) pair shape every
33670 // consumer expects. A reorder of ONE row without reordering
33671 // the underlying algebra constants silently misaligns every
33672 // index-sweep consumer (`Nil` Display's `.iter().collect()`
33673 // routing, a hypothetical `Sexp::LIST_DELIMITERS[0]` opener
33674 // lookup, the sub-vocabulary sweep at
33675 // `is_bare_atom_boundary`). Sibling-shape pin to
33676 // `atom_self_escape_table_composes_from_algebra_constants_in_declaration_order`
33677 // on the peer `[char; 2]` sub-vocabulary at
33678 // [`Atom::SELF_ESCAPE_TABLE`].
33679 assert_eq!(
33680 Sexp::LIST_DELIMITERS,
33681 [Sexp::LIST_OPEN, Sexp::LIST_CLOSE],
33682 "LIST_DELIMITERS composition drifted from the canonical \
33683 (LIST_OPEN, LIST_CLOSE) pair — the paired-delimiter \
33684 sub-vocabulary lift must route through the two typed \
33685 algebra constants in that order.",
33686 );
33687 }
33688
33689 #[test]
33690 fn sexp_list_delimiters_has_expected_cardinality() {
33691 // CARDINALITY PIN: `[char; 2]` at rustc — this assert pins the
33692 // runtime observable so a refactor that loosens the array's
33693 // type to `&[char]` (dropping the compile-time arity forcing)
33694 // fails HERE at the runtime cardinality assertion rather than
33695 // silently allowing a third or absent row. Sibling-shape pin
33696 // to `atom_self_escape_table_has_expected_cardinality`.
33697 assert_eq!(
33698 Sexp::LIST_DELIMITERS.len(),
33699 2,
33700 "LIST_DELIMITERS cardinality drifted from 2 — the paired \
33701 (opener, closer) sub-vocabulary MUST be exactly two rows.",
33702 );
33703 }
33704
33705 #[test]
33706 fn sexp_list_delimiters_pairwise_distinct() {
33707 // PAIRWISE DISJOINTNESS: the two paired-delimiter rows MUST NOT
33708 // alias (a hypothetical `[` opener + `[` closer degenerate
33709 // list-mode would silently collapse the paired-delimiter
33710 // contract and lose the ability to bracket a well-formed list
33711 // — the reader's `Token::LParen` and `Token::RParen` arms
33712 // would collide at the same byte). Sibling-shape pin to
33713 // `atom_self_escape_table_pairwise_distinct` on the peer
33714 // `[char; 2]` sub-vocabulary at [`Atom::SELF_ESCAPE_TABLE`].
33715 for (i, a) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33716 for (j, b) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33717 if i == j {
33718 continue;
33719 }
33720 assert_ne!(
33721 a, b,
33722 "LIST_DELIMITERS rows [{i}] and [{j}] share a byte \
33723 ({a:?} == {b:?}) — the paired-delimiter contract \
33724 would collapse.",
33725 );
33726 }
33727 }
33728 }
33729
33730 #[test]
33731 fn sexp_list_delimiters_disjoint_from_str_delimiter() {
33732 // CROSS-AXIS DISJOINTNESS (Str-delimiter): no row of
33733 // `LIST_DELIMITERS` may alias `Atom::STR_DELIMITER` — otherwise
33734 // the reader's `Token::LParen` / `Token::RParen` outer-dispatch
33735 // arms would collide with `Token::Str`'s opener/closer arm at
33736 // the same byte. Sibling-shape pin to
33737 // `atom_named_escape_table_disjoint_from_self_escape_algebra_constants`:
33738 // both close the cross-sub-vocabulary disjointness contract at
33739 // the ALL-array level rather than as an inline disjunction per
33740 // consumer.
33741 for (i, ch) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33742 assert_ne!(
33743 *ch,
33744 Atom::STR_DELIMITER,
33745 "LIST_DELIMITERS[{i}] ({ch:?}) aliases Atom::STR_DELIMITER \
33746 ({:?}) — the reader's list-delimiter arm would collide \
33747 with the Str-payload delimiter arm at the same byte.",
33748 Atom::STR_DELIMITER,
33749 );
33750 }
33751 }
33752
33753 #[test]
33754 fn sexp_list_delimiters_disjoint_from_comment_lead() {
33755 // CROSS-AXIS DISJOINTNESS (comment lead): no row of
33756 // `LIST_DELIMITERS` may alias `Sexp::COMMENT_LEAD` — otherwise
33757 // the reader's list-delimiter arm would collide with the
33758 // line-comment discard arm at the same byte. Closes the
33759 // structural coherence contract between the outer-structural
33760 // list-delimiter sub-vocabulary and the reader-discard
33761 // sub-vocabulary on the SAME closed-set outer [`Sexp`]
33762 // algebra.
33763 for (i, ch) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33764 assert_ne!(
33765 *ch,
33766 Sexp::COMMENT_LEAD,
33767 "LIST_DELIMITERS[{i}] ({ch:?}) aliases Sexp::COMMENT_LEAD \
33768 ({:?}) — the reader's list-delimiter arm would collide \
33769 with the line-comment lead arm at the same byte.",
33770 Sexp::COMMENT_LEAD,
33771 );
33772 }
33773 }
33774
33775 #[test]
33776 fn sexp_is_bare_atom_boundary_routes_through_list_delimiters_for_every_row() {
33777 // PATH-UNIFORMITY PIN: every row of `LIST_DELIMITERS` MUST
33778 // classify as a bare-atom boundary via
33779 // `Sexp::is_bare_atom_boundary`. A regression that reverted the
33780 // projection's list-delimiter disjunct to two inline
33781 // `|| ch == Self::LIST_OPEN || ch == Self::LIST_CLOSE`
33782 // enumerations AND drifted one of the two constants (or vice
33783 // versa) fails HERE at the first mismatched row rather than at
33784 // a distant tokenize-round-trip. Sibling-shape pin to
33785 // `atom_decode_str_escape_routes_through_self_escape_table_for_every_row`
33786 // on the peer `[char; 2]` sub-vocabulary at
33787 // [`Atom::SELF_ESCAPE_TABLE`].
33788 for (i, ch) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33789 assert!(
33790 Sexp::is_bare_atom_boundary(*ch),
33791 "LIST_DELIMITERS[{i}] ({ch:?}) does NOT classify as a \
33792 bare-atom boundary via Sexp::is_bare_atom_boundary — \
33793 the paired-delimiter sub-vocabulary sweep drifted from \
33794 the reader's outer-dispatch arm-set.",
33795 );
33796 }
33797 }
33798
33799 // ── `Sexp::COMMENT_LEAD` — the canonical `;` char routed through
33800 // the reader's TWO comment-boundary sites: the outer-dispatch arm
33801 // that begins a line-comment run AND the bare-atom terminator
33802 // disjunct that breaks a `Token::Atom` accumulator on this byte.
33803 // Sibling-shape tests to the `sexp_list_open_close` block above
33804 // (outer-structural paired-delimiter axis), lifted onto the reader-
33805 // discard axis of the closed-set outer [`Sexp`] algebra.
33806
33807 #[test]
33808 fn sexp_comment_lead_projects_canonical_semicolon_char() {
33809 // Pins the constant's exact `char` value so a typo (`'#'`,
33810 // `'!'`, `':'`) or an accidental redefinition surfaces
33811 // immediately. Sibling-shape pin to
33812 // `sexp_list_open_projects_canonical_open_paren_char` on the
33813 // outer-structural axis — pins the SAME shape on the reader-
33814 // discard axis of the closed-set outer [`Sexp`] algebra.
33815 assert_eq!(
33816 Sexp::COMMENT_LEAD,
33817 ';',
33818 "COMMENT_LEAD char drifted from the substrate-canonical `;` \
33819 line-comment lead — the reader-discard contract at \
33820 crate::reader::tokenize (line-comment outer arm, bare-atom \
33821 terminator disjunct) binds to this ONE constant.",
33822 );
33823 }
33824
33825 #[test]
33826 fn sexp_comment_lead_distinct_from_every_other_algebra_marker() {
33827 // Cross-axis disjointness pin: `Sexp::COMMENT_LEAD` may NOT
33828 // alias any sibling outer-marker char on the substrate's other
33829 // closed-set algebras — the paired list delimiters
33830 // (`Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`), the Str-payload
33831 // delimiter (`Atom::STR_DELIMITER`), the Keyword-marker prefix
33832 // (`Atom::KEYWORD_MARKER`'s lead byte), the two Bool-literal
33833 // spellings' lead byte (`Atom::bool_literal(true|false)`'s first
33834 // char), AND every quote-family lead char
33835 // (`QuoteForm::lead_char(qf)` for each `qf` in `QuoteForm::ALL`).
33836 // Otherwise a bare `;`-starting lexeme would ambiguously route
33837 // through the line-comment arm AND a sibling algebra's arm in
33838 // `crate::reader::tokenize`. Guards the paired disjointness
33839 // across the substrate's outer-marker axes so a future refactor
33840 // that swaps a marker to collide with the comment lead surfaces
33841 // at this pin rather than as a silent reader misclassification.
33842 assert_ne!(
33843 Sexp::COMMENT_LEAD,
33844 Sexp::LIST_OPEN,
33845 "COMMENT_LEAD and LIST_OPEN share a byte — a bare `{}foo` \
33846 lexeme would ambiguously begin a list AND begin a comment.",
33847 Sexp::LIST_OPEN,
33848 );
33849 assert_ne!(
33850 Sexp::COMMENT_LEAD,
33851 Sexp::LIST_CLOSE,
33852 "COMMENT_LEAD and LIST_CLOSE share a byte — a bare `{}foo` \
33853 lexeme would ambiguously close a list AND begin a comment.",
33854 Sexp::LIST_CLOSE,
33855 );
33856 assert_ne!(
33857 Sexp::COMMENT_LEAD,
33858 Atom::STR_DELIMITER,
33859 "COMMENT_LEAD and STR_DELIMITER share a byte — a bare `{}foo` \
33860 lexeme would ambiguously begin a comment AND open a string.",
33861 Atom::STR_DELIMITER,
33862 );
33863 assert_ne!(
33864 Sexp::COMMENT_LEAD,
33865 Atom::KEYWORD_MARKER_LEAD,
33866 "COMMENT_LEAD and KEYWORD_MARKER_LEAD share a byte — a bare \
33867 `{lead}foo` lexeme would ambiguously begin a comment AND \
33868 begin a keyword.",
33869 lead = Atom::KEYWORD_MARKER_LEAD,
33870 );
33871 for b in [true, false] {
33872 let lead = Atom::bool_literal(b)
33873 .chars()
33874 .next()
33875 .expect("bool_literal must be non-empty");
33876 assert_ne!(
33877 Sexp::COMMENT_LEAD,
33878 lead,
33879 "COMMENT_LEAD and bool_literal({b:?}) share a lead byte — a \
33880 bare `{lead}...` lexeme would ambiguously begin a comment \
33881 AND classify as a Bool.",
33882 );
33883 }
33884 for qf in QuoteForm::ALL {
33885 assert_ne!(
33886 Sexp::COMMENT_LEAD,
33887 qf.lead_char(),
33888 "COMMENT_LEAD and QuoteForm::{qf:?}::lead_char share a byte — \
33889 a bare `;`-starting source would silently route through the \
33890 quote-family outer-dispatch arm.",
33891 );
33892 }
33893 }
33894
33895 // ── `Sexp::COMMENT_TERM` — the canonical `\n` char that terminates
33896 // a line-comment run in the reader's tokenizer. Section-for-retraction
33897 // sibling of `Sexp::COMMENT_LEAD` on the reader-discard axis; paired
33898 // opener/terminator peer of `Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`
33899 // on the outer-structural axis. The pins below anchor the constant's
33900 // exact byte AND its cross-axis disjointness against every
33901 // non-whitespace closed-set outer-marker char.
33902
33903 #[test]
33904 fn sexp_comment_term_projects_canonical_line_feed_char() {
33905 // Pins the constant's exact `char` value so a typo (`'\r'`,
33906 // `'\0'`, a display-glyph substitution) or an accidental
33907 // redefinition surfaces immediately. Sibling-shape pin to
33908 // `sexp_comment_lead_projects_canonical_semicolon_char` on the
33909 // reader-discard axis — pins the SAME shape at the terminator
33910 // side of the (COMMENT_LEAD, COMMENT_TERM) paired-delimiter
33911 // algebra on the closed-set outer [`Sexp`] algebra.
33912 assert_eq!(
33913 Sexp::COMMENT_TERM,
33914 '\n',
33915 "COMMENT_TERM char drifted from the substrate-canonical `\\n` \
33916 line-feed byte — the reader-discard contract at \
33917 crate::reader::tokenize's line-comment discard loop binds \
33918 to this ONE constant.",
33919 );
33920 }
33921
33922 #[test]
33923 fn sexp_comment_term_is_whitespace_family_char() {
33924 // WHITESPACE-FAMILY PIN: the line-comment terminator MUST be a
33925 // whitespace char. The reader's outer-match's `ws if
33926 // ws.is_whitespace()` arm absorbs any lingering COMMENT_TERM
33927 // byte after the discard loop terminates on it — a regression
33928 // that repointed COMMENT_TERM at a NON-whitespace byte would
33929 // BOTH break the discard loop's semantics AND leak the byte
33930 // into the token stream as an outer-dispatch dispatch (either a
33931 // specific arm firing on it, or the bare-atom accumulator
33932 // consuming it). Pin the property structurally so a byte swap
33933 // that lost whitespace-family membership surfaces here rather
33934 // than as a downstream tokenizer misclassification.
33935 assert!(
33936 Sexp::COMMENT_TERM.is_whitespace(),
33937 "COMMENT_TERM `{:?}` is NOT a whitespace char — the reader's \
33938 outer-match whitespace arm would fail to absorb it AND the \
33939 line-comment discard loop's terminator would leak the byte \
33940 into the token stream.",
33941 Sexp::COMMENT_TERM,
33942 );
33943 }
33944
33945 #[test]
33946 fn sexp_comment_term_distinct_from_every_non_whitespace_algebra_marker() {
33947 // Cross-axis disjointness pin: `Sexp::COMMENT_TERM` may NOT
33948 // alias any NON-whitespace sibling outer-marker char on the
33949 // substrate's other closed-set algebras — the paired list
33950 // delimiters (`Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`), the
33951 // line-comment lead (`Sexp::COMMENT_LEAD`), the Str-payload
33952 // delimiter + escape-lead (`Atom::STR_DELIMITER` /
33953 // `Atom::STR_ESCAPE_LEAD`), the Keyword-marker prefix
33954 // (`Atom::KEYWORD_MARKER`'s lead byte), the two Bool-literal
33955 // spellings' lead byte (`Atom::bool_literal(true|false)`'s
33956 // first char), AND every quote-family lead char
33957 // (`QuoteForm::lead_char(qf)` for each `qf` in `QuoteForm::ALL`).
33958 // The disjointness contract EXCLUDES the whitespace-family
33959 // axis — the terminator's role IS to be whitespace-family, so
33960 // the outer-match's `ws if ws.is_whitespace()` arm absorbs it
33961 // by design (see `sexp_comment_term_is_whitespace_family_char`
33962 // above). Sibling-shape pin to
33963 // `sexp_comment_lead_distinct_from_every_other_algebra_marker`
33964 // on the lead side of the paired-delimiter algebra — pins the
33965 // SAME shape at the terminator side.
33966 assert_ne!(
33967 Sexp::COMMENT_TERM,
33968 Sexp::LIST_OPEN,
33969 "COMMENT_TERM and LIST_OPEN share a byte — the reader's \
33970 line-comment discard loop would terminate on the SAME byte \
33971 the outer-dispatch's list-opening arm binds to.",
33972 );
33973 assert_ne!(
33974 Sexp::COMMENT_TERM,
33975 Sexp::LIST_CLOSE,
33976 "COMMENT_TERM and LIST_CLOSE share a byte — the reader's \
33977 line-comment discard loop would terminate on the SAME byte \
33978 the outer-dispatch's list-closing arm binds to.",
33979 );
33980 assert_ne!(
33981 Sexp::COMMENT_TERM,
33982 Sexp::COMMENT_LEAD,
33983 "COMMENT_TERM and COMMENT_LEAD share a byte — a bare \
33984 `{lead}{lead}` two-char source would AMBIGUOUSLY begin AND \
33985 terminate the SAME comment run at the SAME byte, collapsing \
33986 the paired-delimiter algebra onto ONE char.",
33987 lead = Sexp::COMMENT_LEAD,
33988 );
33989 assert_ne!(
33990 Sexp::COMMENT_TERM,
33991 Atom::STR_DELIMITER,
33992 "COMMENT_TERM and STR_DELIMITER share a byte — the reader's \
33993 line-comment discard loop would terminate on the SAME byte \
33994 the outer-dispatch's string-opening arm binds to.",
33995 );
33996 assert_ne!(
33997 Sexp::COMMENT_TERM,
33998 Atom::STR_ESCAPE_LEAD,
33999 "COMMENT_TERM and STR_ESCAPE_LEAD share a byte — the reader's \
34000 line-comment discard loop would terminate on the SAME byte \
34001 the Str-payload's escape-handler outer arm binds to.",
34002 );
34003 assert_ne!(
34004 Sexp::COMMENT_TERM,
34005 Atom::KEYWORD_MARKER_LEAD,
34006 "COMMENT_TERM and KEYWORD_MARKER_LEAD share a byte — the \
34007 reader's line-comment discard loop would terminate on the \
34008 SAME byte the from_lexeme keyword-prefix arm binds to.",
34009 );
34010 for b in [true, false] {
34011 let lead = Atom::bool_literal(b)
34012 .chars()
34013 .next()
34014 .expect("bool_literal must be non-empty");
34015 assert_ne!(
34016 Sexp::COMMENT_TERM,
34017 lead,
34018 "COMMENT_TERM and bool_literal({b:?}) share a lead byte — \
34019 the reader's line-comment discard loop would terminate \
34020 on the SAME byte the from_lexeme bool-literal arm binds \
34021 to.",
34022 );
34023 }
34024 for qf in QuoteForm::ALL {
34025 assert_ne!(
34026 Sexp::COMMENT_TERM,
34027 qf.lead_char(),
34028 "COMMENT_TERM and QuoteForm::{qf:?}::lead_char share a \
34029 byte — the reader's line-comment discard loop would \
34030 terminate on the SAME byte the quote-family outer- \
34031 dispatch arm binds to.",
34032 );
34033 }
34034 }
34035
34036 // ── `Sexp::COMMENT_DELIMITERS` — the paired (opener, terminator)
34037 // reader-discard sub-vocabulary on the closed-set outer [`Sexp`]
34038 // algebra. Sibling-shape tests to the `sexp_list_delimiters_*` block
34039 // above (outer-structural paired-delimiter axis), lifted onto the
34040 // reader-discard axis of the SAME algebra at the SAME `[char; 2]`
34041 // shape.
34042
34043 #[test]
34044 fn sexp_comment_delimiters_composes_from_algebra_constants_in_declaration_order() {
34045 // FAMILY COMPOSITION LAW: pin that the ALL array's rows are the
34046 // two paired reader-discard algebra constants
34047 // (`Self::COMMENT_LEAD`, `Self::COMMENT_TERM`) in canonical
34048 // (opener, terminator) declaration order matching the
34049 // substrate-canonical paired-role shape every consumer expects.
34050 // A reorder of ONE row without reordering the underlying
34051 // algebra constants silently misaligns every index-sweep
34052 // consumer (a hypothetical `Sexp::COMMENT_DELIMITERS[0]` opener
34053 // lookup, an LSP span-highlighter that renders the (opener,
34054 // terminator) span as-is, the metric label-set generator that
34055 // walks the array). Sibling-shape pin to
34056 // `sexp_list_delimiters_composes_from_algebra_constants_in_declaration_order`
34057 // on the outer-structural axis; both close the (opener,
34058 // closer_or_terminator) pair contract at ONE typed ALL array.
34059 assert_eq!(
34060 Sexp::COMMENT_DELIMITERS,
34061 [Sexp::COMMENT_LEAD, Sexp::COMMENT_TERM],
34062 "COMMENT_DELIMITERS composition drifted from the canonical \
34063 (COMMENT_LEAD, COMMENT_TERM) pair — the paired-discard \
34064 sub-vocabulary lift must route through the two typed \
34065 algebra constants in that order.",
34066 );
34067 }
34068
34069 #[test]
34070 fn sexp_comment_delimiters_has_expected_cardinality() {
34071 // CARDINALITY PIN: `[char; 2]` at rustc — this assert pins the
34072 // runtime observable so a refactor that loosens the array's
34073 // type to `&[char]` (dropping the compile-time arity forcing)
34074 // fails HERE at the runtime cardinality assertion rather than
34075 // silently allowing a third or absent row. Sibling-shape pin
34076 // to `sexp_list_delimiters_has_expected_cardinality` on the
34077 // outer-structural axis.
34078 assert_eq!(
34079 Sexp::COMMENT_DELIMITERS.len(),
34080 2,
34081 "COMMENT_DELIMITERS cardinality drifted from 2 — the paired \
34082 (opener, terminator) reader-discard sub-vocabulary MUST be \
34083 exactly two rows.",
34084 );
34085 }
34086
34087 #[test]
34088 fn sexp_comment_delimiters_pairwise_distinct() {
34089 // PAIRWISE DISJOINTNESS: the two paired reader-discard rows
34090 // MUST NOT alias (a hypothetical degenerate `;` opener + `;`
34091 // terminator convention would collapse the paired-delimiter
34092 // contract — the discard loop's terminator check `ch ==
34093 // Sexp::COMMENT_TERM` inside a run led by `Sexp::COMMENT_LEAD`
34094 // would fire on the very byte that opened the run, breaking
34095 // the loop after zero characters and leaving the actual comment
34096 // body in the token stream). Sibling-shape pin to
34097 // `sexp_list_delimiters_pairwise_distinct` on the outer-
34098 // structural axis; both close the pairwise-distinctness
34099 // contract at the ALL-array level rather than at an inline
34100 // `assert_ne!(LEAD, TERM)` per consumer.
34101 for (i, a) in Sexp::COMMENT_DELIMITERS.iter().enumerate() {
34102 for (j, b) in Sexp::COMMENT_DELIMITERS.iter().enumerate() {
34103 if i == j {
34104 continue;
34105 }
34106 assert_ne!(
34107 a, b,
34108 "COMMENT_DELIMITERS rows [{i}] and [{j}] share a byte \
34109 ({a:?} == {b:?}) — the paired-discard contract \
34110 would collapse.",
34111 );
34112 }
34113 }
34114 }
34115
34116 #[test]
34117 fn sexp_comment_delimiters_lead_row_is_bare_atom_boundary() {
34118 // PATH-UNIFORMITY PIN (LEAD row, non-whitespace): the [0] row
34119 // (`Sexp::COMMENT_LEAD`, `;`) MUST classify as a bare-atom
34120 // boundary via `Sexp::is_bare_atom_boundary` — the reader's
34121 // outer-dispatch cascade has a DEDICATED line-comment arm
34122 // keyed on this byte, so the projection's disjunction
34123 // enumerates it. A regression that dropped the LEAD from
34124 // `is_bare_atom_boundary`'s disjunction OR that pointed
34125 // `COMMENT_DELIMITERS[0]` at a byte the projection doesn't
34126 // recognize as a boundary fails HERE rather than at a distant
34127 // tokenize-round-trip. Sibling-shape pin to
34128 // `sexp_is_bare_atom_boundary_routes_through_list_delimiters_for_every_row`
34129 // — the LIST_DELIMITERS peer sweeps BOTH rows through the
34130 // predicate; this pin sweeps ONLY the LEAD row (index 0),
34131 // because the TERM row (index 1) classifies through the
34132 // whitespace-family axis, NOT the bare-atom-boundary predicate
34133 // directly.
34134 assert!(
34135 Sexp::is_bare_atom_boundary(Sexp::COMMENT_DELIMITERS[0]),
34136 "COMMENT_DELIMITERS[0] ({:?}) does NOT classify as a \
34137 bare-atom boundary via Sexp::is_bare_atom_boundary — the \
34138 reader-discard opener row drifted from the reader's \
34139 outer-dispatch arm-set.",
34140 Sexp::COMMENT_DELIMITERS[0],
34141 );
34142 }
34143
34144 #[test]
34145 fn sexp_comment_delimiters_term_row_is_whitespace_family_char() {
34146 // PATH-UNIFORMITY PIN (TERM row, whitespace): the [1] row
34147 // (`Sexp::COMMENT_TERM`, `'\n'`) MUST classify as a whitespace
34148 // char via `char::is_whitespace` — the reader's line-comment
34149 // discard loop consumes bytes up to and including the FIRST
34150 // COMMENT_TERM, then hands control back to the outer-dispatch's
34151 // `ws if ws.is_whitespace()` arm which absorbs any lingering
34152 // COMMENT_TERM byte cleanly. A regression that repointed
34153 // `COMMENT_DELIMITERS[1]` at a NON-whitespace byte (e.g. a `#`
34154 // reader-macro-lead) would break the post-discard hand-off:
34155 // the reader would either loop indefinitely on the byte or
34156 // silently tokenize it into the next Token::Atom. Pinning the
34157 // whitespace-family membership at the array's TERM row keeps
34158 // the outer-dispatch's cascading arm-set structurally coherent
34159 // through the ALL array. Sibling-shape pin to
34160 // `sexp_comment_term_is_whitespace_family_char` above; that
34161 // pin binds the whitespace-family membership to the
34162 // `Self::COMMENT_TERM` constant directly, this pin binds it to
34163 // the ALL array's [1] row so any override of the TERM row via
34164 // a refactored `pub const` misalignment surfaces HERE too.
34165 assert!(
34166 Sexp::COMMENT_DELIMITERS[1].is_whitespace(),
34167 "COMMENT_DELIMITERS[1] ({:?}) does NOT classify as a \
34168 whitespace char via char::is_whitespace — the reader's \
34169 line-comment discard loop's post-loop hand-off to the \
34170 outer-dispatch's whitespace arm would break at the \
34171 non-whitespace terminator.",
34172 Sexp::COMMENT_DELIMITERS[1],
34173 );
34174 }
34175
34176 #[test]
34177 fn sexp_comment_delimiters_disjoint_from_list_delimiters() {
34178 // CROSS-AXIS DISJOINTNESS (same-algebra): no row of
34179 // `COMMENT_DELIMITERS` may alias any row of
34180 // `LIST_DELIMITERS` — the reader-discard axis and the
34181 // outer-structural list-delimiter axis partition their
34182 // respective bytes disjointly on the SAME closed-set outer
34183 // [`Sexp`] algebra. Otherwise the reader's dedicated line-
34184 // comment arm would collide with the `Token::LParen` /
34185 // `Token::RParen` arms at the same byte, silently reclassifying
34186 // a bare `(` or `)` as a comment lead or vice versa. Sibling-
34187 // shape pin to `sexp_list_delimiters_disjoint_from_comment_lead`
34188 // above (the outer-structural axis's mirror pin against the
34189 // LEAD row); this pin closes the FULL 2×2 cross-axis
34190 // disjointness contract rather than only the LEAD row against
34191 // both LIST rows.
34192 for (i, ch) in Sexp::COMMENT_DELIMITERS.iter().enumerate() {
34193 for (j, other) in Sexp::LIST_DELIMITERS.iter().enumerate() {
34194 assert_ne!(
34195 *ch, *other,
34196 "COMMENT_DELIMITERS[{i}] ({ch:?}) aliases \
34197 LIST_DELIMITERS[{j}] ({other:?}) — the reader- \
34198 discard axis and the outer-structural list- \
34199 delimiter axis share a byte, silently reclassifying \
34200 the shared byte at the reader's outer dispatch.",
34201 );
34202 }
34203 }
34204 }
34205
34206 #[test]
34207 fn sexp_comment_delimiters_disjoint_from_str_delimiter() {
34208 // CROSS-ALGEBRA DISJOINTNESS (Str-delimiter): no row of
34209 // `COMMENT_DELIMITERS` may alias `Atom::STR_DELIMITER` —
34210 // otherwise the reader's line-comment discard arm would
34211 // collide with `Token::Str`'s opener/closer arm at the same
34212 // byte. The disjointness contract binds ACROSS the two closed-
34213 // set algebras (outer [`Sexp`] discard vocabulary vs. inner
34214 // [`Atom`] Str-payload vocabulary), matching the sibling-shape
34215 // pin `sexp_list_delimiters_disjoint_from_str_delimiter` on
34216 // the outer-structural axis of the SAME [`Sexp`] algebra.
34217 for (i, ch) in Sexp::COMMENT_DELIMITERS.iter().enumerate() {
34218 assert_ne!(
34219 *ch,
34220 Atom::STR_DELIMITER,
34221 "COMMENT_DELIMITERS[{i}] ({ch:?}) aliases \
34222 Atom::STR_DELIMITER ({:?}) — the reader's line- \
34223 comment discard arm would collide with the Str-payload \
34224 delimiter arm at the same byte across the two closed- \
34225 set algebras.",
34226 Atom::STR_DELIMITER,
34227 );
34228 }
34229 }
34230
34231 // ── `Sexp::is_bare_atom_boundary` — the ONE typed projection on the
34232 // outer [`Sexp`] algebra that names the SIX-fold outer-dispatch
34233 // category-leading char disjunction. The exhaustive pin below
34234 // anchors each of the six categories AS a positive arm AND the
34235 // load-bearing substrate marker LEAD bytes (`:` / `#` / `@`) AS
34236 // negative arms — the three lead bytes are first-char classifiers
34237 // for bare-atom payload families (Keyword / Bool / splice second-
34238 // char), NOT outer-dispatch category-leading chars, and a
34239 // regression that promoted any to a boundary would break their
34240 // tokenization. The composition end-to-end pin (in reader tests)
34241 // sweeps every boundary char through the bare-atom accumulator and
34242 // asserts the atom terminates at exactly the boundary byte.
34243
34244 #[test]
34245 fn sexp_is_bare_atom_boundary_matches_outer_dispatch_arm_set_exhaustively() {
34246 // OUTER-DISPATCH COHERENCE PIN: the SIX outer-dispatch category-
34247 // leading char families the reader's tokenizer specialises on
34248 // MUST ALL classify as boundaries; no other char (bare-atom
34249 // chars) may classify as one. This test enumerates the FULL
34250 // outer-dispatch arm-set as a typed table AND sweeps a
34251 // representative negative set, asserting the projection's
34252 // predicate matches the outer-dispatch's specific-arm firing
34253 // exactly. A refactor that added a SEVENTH outer-dispatch arm
34254 // (e.g. `#|…|#` block-comment) to the reader without extending
34255 // the projection's disjunction would fail HERE at the coherence
34256 // sweep: the new lead byte would EITHER be a boundary the
34257 // projection missed (test asserts positive, projection returns
34258 // false → panic) OR a bare-atom char the reader stole from the
34259 // default arm (silent tokenizer drift the projection doesn't
34260 // catch). Either way the coherence pin catches the drift and
34261 // forces the projection + outer-dispatch to stay in lockstep.
34262 let positive_arms: Vec<(char, String)> = {
34263 let mut arms: Vec<(char, String)> = vec![
34264 (' ', "whitespace".to_string()),
34265 ('\t', "whitespace-tab".to_string()),
34266 ('\n', "whitespace-newline".to_string()),
34267 (Sexp::LIST_OPEN, "LIST_OPEN".to_string()),
34268 (Sexp::LIST_CLOSE, "LIST_CLOSE".to_string()),
34269 (Atom::STR_DELIMITER, "STR_DELIMITER".to_string()),
34270 (Sexp::COMMENT_LEAD, "COMMENT_LEAD".to_string()),
34271 ];
34272 for qf in QuoteForm::ALL {
34273 arms.push((qf.lead_char(), format!("QuoteForm::{qf:?}")));
34274 }
34275 arms
34276 };
34277 for (ch, name) in &positive_arms {
34278 assert!(
34279 Sexp::is_bare_atom_boundary(*ch),
34280 "outer-dispatch arm `{name}` (`{ch:?}`) must classify as \
34281 a boundary — the projection missed a category the \
34282 reader's outer-dispatch specialises on",
34283 );
34284 }
34285 // Negative sweep: every char below MUST fall through to the
34286 // default bare-atom arm. Includes typical alpha, digit, and
34287 // sign chars AND the three load-bearing substrate marker LEAD
34288 // bytes (`:` / `#` / `@`) that are first-char classifiers for
34289 // bare-atom payload families rather than outer-dispatch
34290 // categories.
34291 let negative_arms: &[char] = &[
34292 'a',
34293 'z',
34294 'A',
34295 'Z',
34296 '0',
34297 '9',
34298 '-',
34299 '+',
34300 '_',
34301 '.',
34302 '=',
34303 '<',
34304 '>',
34305 '!',
34306 '?',
34307 '/',
34308 '*',
34309 '%',
34310 '&',
34311 '|',
34312 '^',
34313 '~',
34314 // The `KEYWORD_MARKER` prefix's lead byte lives at the
34315 // typed `Atom::KEYWORD_MARKER_LEAD` constant on the closed-
34316 // set outer [`Atom`] algebra. Pre-lift this slot held an
34317 // inline `Atom::KEYWORD_MARKER.chars().next().unwrap()`
34318 // chain extracting the byte from the `&'static str`
34319 // projection; post-lift the byte lives at ONE named
34320 // constant the `&'static str` projects to (pinned by
34321 // `atom_keyword_marker_lead_prefixes_keyword_marker`).
34322 Atom::KEYWORD_MARKER_LEAD,
34323 // The bool_literal spellings' shared lead byte lives at
34324 // the typed `Atom::BOOL_LITERAL_LEAD` constant on the
34325 // closed-set outer [`Atom`] algebra. Pre-lift this slot
34326 // held an inline `Atom::bool_literal(true).chars().next()
34327 // .unwrap()` chain extracting the byte from ONE spelling;
34328 // post-lift the byte lives at ONE named constant that
34329 // BOTH spellings project through (pinned by
34330 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`).
34331 Atom::BOOL_LITERAL_LEAD,
34332 QuoteForm::SPLICE_DISCRIMINATOR,
34333 ];
34334 for ch in negative_arms {
34335 assert!(
34336 !Sexp::is_bare_atom_boundary(*ch),
34337 "bare-atom char {ch:?} must NOT classify as a boundary — \
34338 the reader's outer-dispatch has NO specific arm on this \
34339 byte; the default bare-atom arm must accept it",
34340 );
34341 }
34342 }
34343
34344 #[test]
34345 fn sexp_non_whitespace_bare_atom_terminators_has_expected_cardinality() {
34346 // Cardinality contract: `Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.len()
34347 // == 7` — pinned at the declaration site by rustc's forced-arity
34348 // check on `[char; 7]`. This test surfaces the arity as a fail-
34349 // loud runtime pin so a future refactor that switches the array
34350 // type to `&[char]` (dropping the compile-time arity forcing)
34351 // doesn't silently loosen the closed-set discipline the family
34352 // relies on. The seven arms are the FULL non-whitespace category-
34353 // leading char set the reader's outer-dispatch specialises on
34354 // (two structural `Sexp::LIST_{OPEN,CLOSE}`, three
34355 // `QuoteForm::{QUOTE,QUASIQUOTE,UNQUOTE}_LEAD`, one
34356 // `Atom::STR_DELIMITER`, one `Sexp::COMMENT_LEAD`). Sibling
34357 // posture to `sexp_list_delimiters_has_expected_cardinality`
34358 // and `sexp_comment_delimiters_has_expected_cardinality` on the
34359 // paired-role sub-arrays of the SAME closed set.
34360 assert_eq!(
34361 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS.len(),
34362 7,
34363 "Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS cardinality \
34364 drifted from 7 — the reader's outer-dispatch specialises \
34365 on exactly seven non-whitespace category-leading chars by \
34366 construction; an extension surfaces here"
34367 );
34368 }
34369
34370 #[test]
34371 fn sexp_non_whitespace_bare_atom_terminators_route_through_typed_sub_algebra_constants() {
34372 // PATH-UNIFORMITY (family-wide ARRAY-side): the seven entries
34373 // of `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS` MUST bind
34374 // byte-for-byte to the seven typed sub-algebra `pub const`
34375 // primitives — no inline `char` literals. Catches a regression
34376 // that inlines `['(', ')', '\'', '`', ',', '"', ';']`: both
34377 // the alignment property AND the numeric bytes would still
34378 // hold, but the algebra-provenance would disappear, so a
34379 // future rename (e.g. `Sexp::LIST_OPEN → Sexp::PAREN_OPEN`,
34380 // `Sexp::COMMENT_LEAD → Sexp::LINE_COMMENT_OPEN`) at any of
34381 // the three sibling algebras would silently drift the array's
34382 // provenance from the algebra's canonical spelling. Sibling
34383 // posture to the `LIST_DELIMITERS` / `COMMENT_DELIMITERS`
34384 // ARRAY-side provenance pins on the paired-role sub-arrays,
34385 // AND to the shape-level HASH_DISCRIMINATORS ARRAY-side
34386 // provenance pin
34387 // `sexp_shape_hash_discriminators_align_with_typed_per_role_constants_by_index`
34388 // on the outer-`Sexp` cache-key axis.
34389 assert_eq!(
34390 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[0],
34391 Sexp::LIST_OPEN
34392 );
34393 assert_eq!(
34394 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[1],
34395 Sexp::LIST_CLOSE
34396 );
34397 assert_eq!(
34398 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[2],
34399 QuoteForm::QUOTE_LEAD,
34400 );
34401 assert_eq!(
34402 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[3],
34403 QuoteForm::QUASIQUOTE_LEAD,
34404 );
34405 assert_eq!(
34406 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[4],
34407 QuoteForm::UNQUOTE_LEAD,
34408 );
34409 assert_eq!(
34410 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[5],
34411 Atom::STR_DELIMITER,
34412 );
34413 assert_eq!(
34414 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[6],
34415 Sexp::COMMENT_LEAD,
34416 );
34417 }
34418
34419 #[test]
34420 fn sexp_non_whitespace_bare_atom_terminators_are_pairwise_distinct() {
34421 // INJECTIVITY CONTRACT: the seven entries of
34422 // `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS` MUST be pairwise
34423 // distinct — no char appears twice across the (structural,
34424 // quote-family, atomic-delimiter, comment-lead) sub-vocabularies.
34425 // A silent collision (e.g. a hypothetical Racket-compat port
34426 // moving `QuoteForm::QUOTE_LEAD` from `'\''` to `';'` conflating
34427 // it with `Sexp::COMMENT_LEAD`, or a future block-comment
34428 // extension re-using `Atom::STR_DELIMITER`) would break the
34429 // reader's outer-dispatch's implicit "each specific arm fires
34430 // on a DISTINCT lead char" contract — the `match c { … }`
34431 // outer-dispatch in `crate::reader::tokenize` cannot admit
34432 // duplicate patterns without breaking exhaustiveness. This pin
34433 // surfaces the distinctness as a substrate-level structural
34434 // theorem so a cross-algebra rename that flips the injectivity
34435 // is a compile-time-verified regression at the sub-carving
34436 // level (rustc-forbidden duplicate `match` patterns) AND a
34437 // runtime fail-loud regression here.
34438 let mut seen: std::collections::HashSet<char> = std::collections::HashSet::new();
34439 for ch in Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS {
34440 assert!(
34441 seen.insert(ch),
34442 "Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS carries a \
34443 duplicate char {ch:?} — the reader's outer-dispatch \
34444 arms MUST be pairwise disjoint by construction",
34445 );
34446 }
34447 assert_eq!(seen.len(), Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS.len(),);
34448 }
34449
34450 #[test]
34451 fn sexp_is_bare_atom_boundary_agrees_with_terminators_array_on_non_whitespace_partition() {
34452 // BOUNDARY-PREDICATE COMPOSITION LAW: for every char `ch`,
34453 // `Sexp::is_bare_atom_boundary(ch) == (ch.is_whitespace() ||
34454 // Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch))`.
34455 // Pins the ARRAY-driven refactor of the boundary predicate
34456 // against a regression that reverts the disjunction to the
34457 // pre-lift three-part sub-expression composition (or drifts
34458 // ONE sub-expression's arm silently). Sibling posture to
34459 // `sexp_is_bare_atom_boundary_matches_outer_dispatch_arm_set_exhaustively`
34460 // — that pin binds the boundary predicate's TRUTH-TABLE against
34461 // the reader's outer-dispatch categories; this pin binds the
34462 // boundary predicate's DEFINITION against the family-wide
34463 // terminators ARRAY as the sole non-whitespace clause. The
34464 // (positive, negative) sweep covers a whitespace char + the
34465 // seven terminator entries as positives, and a representative
34466 // negative set (alpha, digit, sign, and the three substrate
34467 // marker LEADs `:` / `#` / `@`) as negatives.
34468 for &ch in &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS {
34469 assert!(
34470 Sexp::is_bare_atom_boundary(ch),
34471 "Sexp::is_bare_atom_boundary({ch:?}) must return true \
34472 for every char in NON_WHITESPACE_BARE_ATOM_TERMINATORS",
34473 );
34474 }
34475 assert!(Sexp::is_bare_atom_boundary(' '));
34476 assert!(Sexp::is_bare_atom_boundary('\t'));
34477 assert!(Sexp::is_bare_atom_boundary('\n'));
34478 for ch in ['a', 'Z', '0', '9', '-', '_', ':', '#', '@'] {
34479 let expected =
34480 ch.is_whitespace() || Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch);
34481 assert_eq!(
34482 Sexp::is_bare_atom_boundary(ch),
34483 expected,
34484 "Sexp::is_bare_atom_boundary({ch:?}) drifted from the \
34485 ARRAY-driven composition law",
34486 );
34487 }
34488 }
34489
34490 // ── `assert_char_array_pairwise_distinct` — the const-fn compile-
34491 // time pairwise-distinctness contract lifter. Sibling posture to
34492 // the runtime `_pairwise_distinct` tests: those pin the property
34493 // at test-run time on each individual array; these pins bind the
34494 // lift's OWN contract (accept-distinct, reject-collision, runtime-
34495 // callability, cross-array coverage) so a regression that silently
34496 // weakens the helper (e.g. flipping `!=` to `==`, dropping the
34497 // inner `j` loop, or returning early on collision) is caught by
34498 // the helper's OWN test surface rather than only surfacing as a
34499 // false-positive on some future array's distinctness pin.
34500
34501 #[test]
34502 fn assert_char_array_pairwise_distinct_accepts_the_empty_array() {
34503 // Empty array — vacuously pairwise distinct (no pair to
34504 // collide). The compile-time `const _: () = assert_char_array_
34505 // pairwise_distinct(&EMPTY);` would land on this arm, so the
34506 // runtime call MUST return normally.
34507 assert_char_array_pairwise_distinct::<0>(&[]);
34508 }
34509
34510 #[test]
34511 fn assert_char_array_pairwise_distinct_accepts_singleton_arrays() {
34512 // Singleton array — vacuously pairwise distinct (only one
34513 // element, no pair). Cross-arity coverage on the `[char; 1]`
34514 // corner of the const-N generic.
34515 assert_char_array_pairwise_distinct(&['a']);
34516 assert_char_array_pairwise_distinct(&[Sexp::LIST_OPEN]);
34517 }
34518
34519 #[test]
34520 fn assert_char_array_pairwise_distinct_accepts_every_family_wide_substrate_array() {
34521 // Runtime cross-check that the SAME seven arrays the module-
34522 // level `const _: () = ...` witnesses cover at COMPILE time
34523 // are pairwise distinct. A regression that removes ONE of the
34524 // `const _` witnesses would still leave THIS runtime pin as a
34525 // safety net; the const witness fires FIRST at `cargo check`,
34526 // this runtime pin catches the collision at `cargo test`. The
34527 // pair enforces the theorem at TWO stages of the toolchain.
34528 assert_char_array_pairwise_distinct(&Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS);
34529 assert_char_array_pairwise_distinct(&Sexp::LIST_DELIMITERS);
34530 assert_char_array_pairwise_distinct(&Sexp::COMMENT_DELIMITERS);
34531 assert_char_array_pairwise_distinct(&QuoteForm::LEADS);
34532 assert_char_array_pairwise_distinct(&Atom::SELF_ESCAPE_TABLE);
34533 assert_char_array_pairwise_distinct(&Atom::ESCAPE_SOURCES);
34534 assert_char_array_pairwise_distinct(&Atom::ESCAPE_DECODED);
34535 }
34536
34537 #[test]
34538 #[should_panic(expected = "assert_char_array_pairwise_distinct")]
34539 fn assert_char_array_pairwise_distinct_panics_at_runtime_on_binary_collision() {
34540 // NEGATIVE PIN — binary corner: a two-element array carrying
34541 // the same char twice MUST panic at runtime (the const-eval
34542 // panic surfaces normally when the function is invoked from
34543 // a runtime context, not just a `const _` context). Pins the
34544 // helper's OWN reject-collision arm — a regression that
34545 // silently returns without panicking on a duplicate would
34546 // slip through the compile-time witnesses' failure mode too.
34547 assert_char_array_pairwise_distinct(&['x', 'x']);
34548 }
34549
34550 #[test]
34551 #[should_panic(expected = "assert_char_array_pairwise_distinct")]
34552 fn assert_char_array_pairwise_distinct_panics_at_runtime_on_non_adjacent_collision() {
34553 // NEGATIVE PIN — non-adjacent corner: the collision fires on
34554 // ANY (i, j) pair with i < j, not just the adjacent (0, 1)
34555 // corner. Pins the nested-loop shape of the helper — a
34556 // regression that walked ONLY the adjacent pairs (i.e., swept
34557 // `while i + 1 < N { if arr[i] == arr[i+1] { panic } … }`)
34558 // would silently accept `['a', 'b', 'a']` (non-adjacent
34559 // collision at positions 0 and 2), missing the contract.
34560 assert_char_array_pairwise_distinct(&['a', 'b', 'a']);
34561 }
34562
34563 #[test]
34564 #[should_panic(expected = "assert_char_array_pairwise_distinct")]
34565 fn assert_char_array_pairwise_distinct_panics_at_runtime_on_terminal_collision() {
34566 // NEGATIVE PIN — terminal corner: the collision at the LAST
34567 // pair (positions N-2 and N-1) MUST also fire. Pins the outer
34568 // `while i < N` bound — a regression that walked `while i <
34569 // N - 1` (dropping the last row) would silently accept a
34570 // collision at the tail.
34571 assert_char_array_pairwise_distinct(&['a', 'b', 'c', 'd', 'd']);
34572 }
34573
34574 #[test]
34575 fn assert_char_array_pairwise_distinct_panic_message_names_the_helper() {
34576 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST begin
34577 // with the helper's own name so downstream diagnostics
34578 // (`cargo check` const-eval error output, test-suite failure
34579 // reports) route the drift back to the helper by string
34580 // search — the family-wide contract's failure mode surfaces
34581 // as an identifiable panic-message prefix rather than as an
34582 // opaque const-eval error. Sibling posture to the runtime
34583 // pairwise-distinctness tests that name the ARRAY in their
34584 // failure message; this pin names the HELPER.
34585 let outcome = std::panic::catch_unwind(|| {
34586 assert_char_array_pairwise_distinct(&['x', 'x']);
34587 });
34588 let payload = outcome.expect_err(
34589 "assert_char_array_pairwise_distinct must panic on a \
34590 duplicate — the reject-collision arm is the point of the \
34591 helper",
34592 );
34593 let msg = payload
34594 .downcast_ref::<&'static str>()
34595 .map(|s| (*s).to_owned())
34596 .or_else(|| payload.downcast_ref::<String>().cloned())
34597 .expect(
34598 "assert_char_array_pairwise_distinct panic payload \
34599 must be a static &str or String",
34600 );
34601 assert!(
34602 msg.contains("assert_char_array_pairwise_distinct"),
34603 "assert_char_array_pairwise_distinct panic message \
34604 {msg:?} must name the helper for provenance-preserving \
34605 failure diagnostics",
34606 );
34607 }
34608
34609 // ── assert_char_array_all_ascii — the per-entry ASCII-SCALAR-RANGE
34610 // gate row-dual peer of `assert_str_array_all_ascii` on the
34611 // (element-type ∈ {char, `&'static str`}) axis of the (per-entry ×
34612 // contract-shape) matrix at the (per-entry, ASCII) column. Contract-
34613 // orthogonal sibling of `assert_char_array_pairwise_distinct` on
34614 // the (INJECTIVITY, ASCII) axis of the SAME (`char`) row. ──
34615
34616 #[test]
34617 fn assert_char_array_all_ascii_accepts_the_empty_array() {
34618 // Empty array — vacuously all-ASCII (no entry to fail the
34619 // scalar-range gate). The compile-time `const _: () =
34620 // assert_char_array_all_ascii(&EMPTY);` would land on this
34621 // arm, so the runtime call MUST return normally. Sibling
34622 // posture to `assert_str_array_all_ascii_accepts_the_empty_
34623 // array` on the (`&'static str`) row-dual ASCII helper — the
34624 // two share the trivial-arity arm across the element-type
34625 // column.
34626 assert_char_array_all_ascii::<0>(&[]);
34627 }
34628
34629 #[test]
34630 fn assert_char_array_all_ascii_accepts_ascii_singleton_arrays() {
34631 // Singleton array carrying an all-ASCII entry — the sole
34632 // entry clears the scalar-range gate. Cross-arity coverage
34633 // on the `[char; 1]` corner of the const-N generic. Includes
34634 // a substrate-scalar entry (`Sexp::LIST_OPEN`) to cover the
34635 // named-constant projection alongside the char literal
34636 // projection.
34637 assert_char_array_all_ascii(&['a']);
34638 assert_char_array_all_ascii(&[Sexp::LIST_OPEN]);
34639 }
34640
34641 #[test]
34642 fn assert_char_array_all_ascii_accepts_every_family_wide_substrate_array() {
34643 // Runtime cross-check that the SAME seven arrays the module-
34644 // level `const _: () = ...` witnesses cover at COMPILE time
34645 // are all-ASCII. Sibling posture to the runtime
34646 // `_pairwise_distinct_accepts_every_family_wide_substrate_
34647 // array` cross-check — the two together pin BOTH the SET-
34648 // LEVEL INJECTIVITY axis AND the PER-ENTRY ASCII-SCALAR-RANGE
34649 // axis on the SAME seven arrays, at TWO stages of the
34650 // toolchain (compile-time `const _` line + this runtime
34651 // safety-net). Row-dual posture to
34652 // `assert_str_array_all_ascii_accepts_every_family_wide_
34653 // substrate_array` on the (`&'static str`) row — the two
34654 // together sweep the ASCII contract across every reader-
34655 // boundary AND every outer-algebra vocabulary the substrate
34656 // ships.
34657 assert_char_array_all_ascii(&Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS);
34658 assert_char_array_all_ascii(&Sexp::LIST_DELIMITERS);
34659 assert_char_array_all_ascii(&Sexp::COMMENT_DELIMITERS);
34660 assert_char_array_all_ascii(&QuoteForm::LEADS);
34661 assert_char_array_all_ascii(&Atom::SELF_ESCAPE_TABLE);
34662 assert_char_array_all_ascii(&Atom::ESCAPE_SOURCES);
34663 assert_char_array_all_ascii(&Atom::ESCAPE_DECODED);
34664 }
34665
34666 #[test]
34667 fn assert_char_array_all_ascii_accepts_the_ascii_boundary_scalar() {
34668 // Boundary-inclusive pin on the ASCII scalar range's upper
34669 // edge — U+007F (DEL, the last ASCII scalar) MUST be
34670 // accepted. Guards against an off-by-one regression that
34671 // walked `arr[i] as u32 >= 0x7F` and spuriously rejected the
34672 // sole `'\u{7F}'` character. Together with the negative pins
34673 // below (which fire on U+0080 — the first non-ASCII scalar),
34674 // the two pin the scalar-range boundary at both edges.
34675 assert_char_array_all_ascii(&['\u{7F}']);
34676 }
34677
34678 #[test]
34679 #[should_panic(expected = "assert_char_array_all_ascii")]
34680 fn assert_char_array_all_ascii_panics_at_runtime_on_singleton_non_ascii() {
34681 // NEGATIVE PIN — singleton corner: a one-element array
34682 // carrying a non-ASCII scalar MUST panic. Pins the helper's
34683 // own reject-non-ascii arm on the smallest possible array
34684 // shape. The scalar `'é'` (U+00E9) has `as u32 == 0xE9 >
34685 // 0x7F` and fires the range gate.
34686 assert_char_array_all_ascii(&['é']);
34687 }
34688
34689 #[test]
34690 #[should_panic(expected = "assert_char_array_all_ascii")]
34691 fn assert_char_array_all_ascii_panics_at_runtime_on_head_non_ascii() {
34692 // NEGATIVE PIN — head corner: the non-ASCII scalar at
34693 // position 0 MUST fire even when subsequent entries are
34694 // ASCII. Pins the sweep's inclusive-start behavior — a
34695 // regression that walked `while i < N { … i += 1 }` from
34696 // an off-by-one start (`i = 1`) would silently accept a
34697 // leading non-ASCII entry.
34698 assert_char_array_all_ascii(&['é', 'a', 'b']);
34699 }
34700
34701 #[test]
34702 #[should_panic(expected = "assert_char_array_all_ascii")]
34703 fn assert_char_array_all_ascii_panics_at_runtime_on_interior_non_ascii() {
34704 // NEGATIVE PIN — interior corner: the non-ASCII scalar at a
34705 // strictly-interior position MUST fire. Pins the sweep's
34706 // non-early-exit behavior at the head-arm — a regression
34707 // that returned `Ok` on the first ASCII entry (bailing out
34708 // of the sweep prematurely) would silently accept an
34709 // interior non-ASCII entry.
34710 assert_char_array_all_ascii(&['a', 'é', 'b']);
34711 }
34712
34713 #[test]
34714 #[should_panic(expected = "assert_char_array_all_ascii")]
34715 fn assert_char_array_all_ascii_panics_at_runtime_on_tail_non_ascii() {
34716 // NEGATIVE PIN — tail corner: the non-ASCII scalar at
34717 // position `N - 1` MUST fire. Pins the outer `while i < N`
34718 // upper bound — a regression that walked `while i < N - 1`
34719 // (dropping the last slot) would silently accept a trailing
34720 // non-ASCII entry.
34721 assert_char_array_all_ascii(&['a', 'b', 'c', 'é']);
34722 }
34723
34724 #[test]
34725 #[should_panic(expected = "assert_char_array_all_ascii")]
34726 fn assert_char_array_all_ascii_panics_on_the_first_non_ascii_scalar() {
34727 // NEGATIVE PIN — first-non-ASCII-scalar boundary: U+0080
34728 // (the smallest non-ASCII scalar) MUST fire. Guards against
34729 // an off-by-one regression that walked `arr[i] as u32 >
34730 // 0x80` (dropping U+0080 from the reject set). Together
34731 // with `_accepts_the_ascii_boundary_scalar` (which pins
34732 // U+007F acceptance), the two pin the scalar-range boundary
34733 // at both edges. Row-dual posture to `assert_str_array_all_
34734 // ascii_panics_on_the_first_non_ascii_byte` — the two
34735 // together pin the boundary at both element-type projections.
34736 assert_char_array_all_ascii(&['\u{80}']);
34737 }
34738
34739 #[test]
34740 fn assert_char_array_all_ascii_rejects_the_all_non_ascii_array() {
34741 // POSITIVE-ORTHOGONAL PIN — the ALL-non-ASCII corner: an
34742 // array whose every entry is a non-ASCII scalar fires the
34743 // helper on the FIRST entry (the head-arm). Confirms the
34744 // helper does not silently accept an array whose every
34745 // entry ships non-ASCII scalars. Row-dual posture to
34746 // `assert_str_array_all_ascii_rejects_the_all_non_ascii_
34747 // array` on the (`&'static str`) row — the two together pin
34748 // the all-reject corner across the element-type column.
34749 let outcome = std::panic::catch_unwind(|| {
34750 assert_char_array_all_ascii(&['é', 'ü']);
34751 });
34752 outcome.expect_err(
34753 "assert_char_array_all_ascii must panic on an array \
34754 whose every entry is a non-ASCII scalar — the head-arm \
34755 fires on the first non-ASCII scalar at position 0",
34756 );
34757 }
34758
34759 #[test]
34760 fn assert_char_array_all_ascii_panic_message_names_the_helper_and_axis() {
34761 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST
34762 // begin with the helper's own name AND name the failed axis
34763 // as `"CHAR-NON-ASCII-SCALAR"` (chosen DISTINCT from every
34764 // sibling helper's axis vocabulary: `"duplicate"` on the
34765 // pairwise-distinct sibling; `"CHAR-SUBSET-VIOLATION"` on
34766 // the within-finite-set sibling; `"CHAR-DISJOINTNESS-
34767 // VIOLATION"` on the arrays-disjoint sibling; `"STR-NON-
34768 // ASCII-ENTRY"` on the (`&'static str`) row-dual ASCII
34769 // sibling) so a diagnostic that names the failed axis routes
34770 // UNAMBIGUOUSLY to THIS specific (`char`)-row ASCII helper.
34771 // The `"CHAR-"` prefix disambiguates from the (`&'static
34772 // str`) row-dual ASCII sibling; the shared `"-NON-ASCII-"`
34773 // infix lets callers grep any row's ASCII sibling by `"NON-
34774 // ASCII"` alone.
34775 let outcome = std::panic::catch_unwind(|| {
34776 assert_char_array_all_ascii(&['é']);
34777 });
34778 let payload = outcome.expect_err(
34779 "assert_char_array_all_ascii must panic on a non-ASCII \
34780 scalar — the reject-non-ascii arm is the point of the \
34781 helper",
34782 );
34783 let msg = payload
34784 .downcast_ref::<&'static str>()
34785 .map(|s| (*s).to_owned())
34786 .or_else(|| payload.downcast_ref::<String>().cloned())
34787 .expect(
34788 "assert_char_array_all_ascii panic payload must be \
34789 a static &str or String",
34790 );
34791 assert!(
34792 msg.contains("assert_char_array_all_ascii"),
34793 "assert_char_array_all_ascii panic message {msg:?} must \
34794 name the helper for provenance-preserving failure \
34795 diagnostics",
34796 );
34797 assert!(
34798 msg.contains("CHAR-NON-ASCII-SCALAR"),
34799 "assert_char_array_all_ascii panic message {msg:?} must \
34800 name the failed axis as `CHAR-NON-ASCII-SCALAR` \
34801 DISTINCT from every sibling helper's axis vocabulary",
34802 );
34803 assert!(
34804 !msg.contains("STR-NON-ASCII-ENTRY"),
34805 "assert_char_array_all_ascii panic message {msg:?} must \
34806 NOT name the (`&'static str`) row-dual sibling's axis \
34807 — the two row-dual ASCII helpers must keep their axis-\
34808 provenance strings lexically distinct on the element-\
34809 type column",
34810 );
34811 assert!(
34812 !msg.contains("CHAR-SUBSET-VIOLATION"),
34813 "assert_char_array_all_ascii panic message {msg:?} must \
34814 NOT name the SUBSET-embedding sibling's axis — the \
34815 per-entry ASCII helper and the SUBSET-embedding helper \
34816 must keep their axis-provenance strings lexically \
34817 distinct on the contract-shape column",
34818 );
34819 }
34820
34821 // ── `assert_char_array_within_char_finite_set` — the char-element
34822 // SUBSET-EMBEDDING verifier that binds `arr ⊆ set` at compile time
34823 // on the reader-boundary `char` vocabulary, peer to the (u8) row's
34824 // `assert_u8_array_within_u8_finite_set` on the (element-type ×
34825 // contract-shape) matrix. The runtime test surface pins each of
34826 // the helper's arms (accept-empty, accept-singleton-in-set,
34827 // accept-arr-equals-set, accept-each-family-wide-substrate-subset,
34828 // accept-array-duplicates-in-set, reject-single-out-of-set-entry,
34829 // reject-terminal-out-of-set-entry, panic-message-provenance on
34830 // the CHAR-SUBSET-VIOLATION axis, negative pin on the DELEGATED
34831 // SET-side well-formedness arm) so a regression that silently
34832 // weakened the helper on ANY arm is caught by the helper's OWN
34833 // test surface rather than only surfacing as a false-positive on
34834 // some future subset-embedded `[char; N]` array's compound pin.
34835
34836 #[test]
34837 fn assert_char_array_within_char_finite_set_accepts_the_empty_array_within_any_set() {
34838 // Empty array `arr = []` at the `[char; 0]` corner — vacuously
34839 // a subset of every set (no `i` position exists to test).
34840 // Cross-arity coverage on the trivial ARRAY corner of the
34841 // const-N generic across three witness-set widths (empty,
34842 // singleton, multi-element) to pin the helper's OUTER-sweep
34843 // arm across the whole (`N == 0` × `M`) axis. Turbofish
34844 // binding required because there's no other cue for the const
34845 // parameters on the empty array literal. Sibling posture to
34846 // `assert_u8_array_within_u8_finite_set_accepts_the_empty_array_within_any_set`
34847 // on the (u8) row's SUBSET-EMBEDDING helper — the two share
34848 // the trivial-arity arm across the element-type column.
34849 assert_char_array_within_char_finite_set::<0, 0>(&[], &[]);
34850 assert_char_array_within_char_finite_set::<0, 1>(&[], &['x']);
34851 assert_char_array_within_char_finite_set::<0, 3>(&[], &['a', 'b', 'c']);
34852 }
34853
34854 #[test]
34855 fn assert_char_array_within_char_finite_set_accepts_singleton_array_when_char_in_set() {
34856 // Singleton array `arr = [K]` at the `[char; 1]` corner MUST
34857 // pass when `K ∈ set`. Cross-position coverage: the char can
34858 // sit at the FIRST, MIDDLE, or LAST position of the `set` —
34859 // pins the INNER `while j < M` sweep terminates at the first-
34860 // match position rather than always at position `0` OR always
34861 // at position `M - 1`. A regression that narrowed the inner
34862 // sweep to `j == 0` would silently reject singleton arrays
34863 // hitting non-first set positions.
34864 assert_char_array_within_char_finite_set::<1, 3>(&['a'], &['a', 'b', 'c']);
34865 assert_char_array_within_char_finite_set::<1, 3>(&['b'], &['a', 'b', 'c']);
34866 assert_char_array_within_char_finite_set::<1, 3>(&['c'], &['a', 'b', 'c']);
34867 }
34868
34869 #[test]
34870 fn assert_char_array_within_char_finite_set_accepts_arr_equals_set() {
34871 // Boundary corner where `arr` and `set` cover byte-for-byte
34872 // identical distinct-value sets — the SUBSET relation
34873 // degenerates to EQUALITY. Pins that the helper does NOT
34874 // gratuitously require the SUBSET to be PROPER (strict):
34875 // equal-multisets pass the SUBSET check. Sibling posture to
34876 // `assert_u8_array_within_u8_finite_set_accepts_arr_equals_set`
34877 // on the (u8) row's SUBSET-EMBEDDING helper — the two share
34878 // the EQUAL-SETS corner across the element-type column.
34879 assert_char_array_within_char_finite_set(&['a', 'b'], &['a', 'b']);
34880 assert_char_array_within_char_finite_set(&[Sexp::LIST_OPEN], &[Sexp::LIST_OPEN]);
34881 }
34882
34883 #[test]
34884 fn assert_char_array_within_char_finite_set_accepts_each_family_wide_substrate_subset() {
34885 // Runtime cross-check that the THREE (subset, superset) pairs
34886 // the substrate's module-level `const _` witnesses pin at
34887 // COMPILE time are PROPER SUBSET embeddings at runtime too.
34888 // The pairs enforce the theorem at TWO stages of the
34889 // toolchain: the const witnesses fire FIRST at `cargo check`
34890 // (through the three module-level `const _: () =
34891 // assert_char_array_within_char_finite_set::<N, M>(...)`
34892 // lines), this runtime pin catches the drift at `cargo test`
34893 // as a safety net. Sibling posture to
34894 // `assert_char_array_pairwise_distinct_accepts_every_family_wide_substrate_array`
34895 // which sweeps the seven family-wide `[char; N]` arrays at
34896 // the INJECTIVITY axis; this pin sweeps the three (subset,
34897 // superset) PAIRS at the SUBSET-EMBEDDING axis.
34898 assert_char_array_within_char_finite_set::<2, 7>(
34899 &Sexp::LIST_DELIMITERS,
34900 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
34901 );
34902 assert_char_array_within_char_finite_set::<3, 7>(
34903 &QuoteForm::LEADS,
34904 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
34905 );
34906 assert_char_array_within_char_finite_set::<2, 5>(
34907 &Atom::SELF_ESCAPE_TABLE,
34908 &Atom::ESCAPE_SOURCES,
34909 );
34910 }
34911
34912 #[test]
34913 fn assert_char_array_within_char_finite_set_accepts_repeated_array_entries_in_set() {
34914 // Peer corner to a (future-lift) `_covers_char_finite_set`:
34915 // this helper permits duplicates in `arr` because SUBSET-
34916 // membership is a DISTINCT-value predicate — `['a', 'a', 'b']`
34917 // is a subset of `{'a', 'b', 'c'}` even though the array is
34918 // not pairwise-distinct. Pins that the helper does NOT
34919 // gratuitously require INJECTIVITY on `arr` (the injectivity
34920 // axis is a DIFFERENT compile-time contract bound by
34921 // `assert_char_array_pairwise_distinct`; combining both binds
34922 // BOTH axes). Sibling posture to
34923 // `assert_u8_array_within_u8_finite_set_accepts_repeated_array_entries_in_set`
34924 // on the (u8) row's SUBSET-EMBEDDING peer.
34925 assert_char_array_within_char_finite_set(&['a', 'a', 'b'], &['a', 'b', 'c']);
34926 }
34927
34928 #[test]
34929 #[should_panic(expected = "CHAR-SUBSET-VIOLATION")]
34930 fn assert_char_array_within_char_finite_set_panics_at_runtime_on_out_of_set_entry() {
34931 // NEGATIVE PIN — CHAR-SUBSET-VIOLATION corner: an array
34932 // carrying a single entry NOT in the target set MUST panic at
34933 // runtime with the CHAR-SUBSET-VIOLATION-named message. Pins
34934 // the helper's OWN reject arm — a regression that silently
34935 // returned without panicking on an out-of-set entry would
34936 // slip through the compile-time witnesses' failure mode too.
34937 // The offending char `'z'` is intentionally chosen OUTSIDE
34938 // the target set to pin the OUT-OF-SET drift mode.
34939 assert_char_array_within_char_finite_set(&['a', 'z'], &['a', 'b', 'c']);
34940 }
34941
34942 #[test]
34943 #[should_panic(expected = "CHAR-SUBSET-VIOLATION")]
34944 fn assert_char_array_within_char_finite_set_panics_at_runtime_on_terminal_out_of_set_entry() {
34945 // NEGATIVE PIN — terminal-position drift: an out-of-set entry
34946 // at the LAST array position MUST panic — pins that the outer
34947 // `while i < N` loop reaches `i = N - 1` (else the terminal
34948 // drift would slip through). A regression that narrowed the
34949 // outer sweep to `while i < N - 1` (off-by-one on the OUTER
34950 // bound) would silently accept this array. Sibling posture to
34951 // `assert_u8_array_within_u8_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
34952 // on the (u8) row's terminal-position pin — both bind the
34953 // outer-sweep terminal bound at the ONE array-side outer loop
34954 // the helper carries.
34955 assert_char_array_within_char_finite_set(&['a', 'b', 'c', 'z'], &['a', 'b', 'c']);
34956 }
34957
34958 #[test]
34959 fn assert_char_array_within_char_finite_set_panic_message_names_the_helper_and_char_subset_violation_axis(
34960 ) {
34961 // PANIC-MESSAGE PROVENANCE PIN — CHAR-SUBSET-VIOLATION arm:
34962 // the panic message MUST begin with the helper's own name AND
34963 // identify the failed AXIS as "CHAR-SUBSET-VIOLATION" so
34964 // downstream diagnostics route the drift back to (a) the
34965 // helper by string search on
34966 // `"assert_char_array_within_char_finite_set"` and (b) the
34967 // axis by string search on `"CHAR-SUBSET-VIOLATION"`. Sibling
34968 // posture to
34969 // `assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis`
34970 // on the (u8) row's provenance pin — the two pins together
34971 // bind the (helper, failed-axis) provenance pair at ONE test
34972 // per SUBSET helper on the (element-type) 2×1 face. The axis-
34973 // provenance string `"CHAR-SUBSET-VIOLATION"` is chosen
34974 // DISTINCT from EVERY sibling helper's axis vocabulary
34975 // (`"duplicate"` on the ARRAY-side pairwise-distinct sibling;
34976 // `"SUBSET-VIOLATION"` on the (u8) finite-set SUBSET-only
34977 // sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8) range
34978 // SUBSET-only sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"`
34979 // on the (u8) covers-finite-set sibling; `"OUT-OF-RANGE"` /
34980 // `"MISSING"` on the (u8) covers-inclusive-range sibling;
34981 // `"ARITY-MISMATCH"` on both (u8) `_permutes_*` compound
34982 // helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on the (u8) SET-side
34983 // well-formedness sibling) so a diagnostic that names the
34984 // failed axis routes UNAMBIGUOUSLY to (a) this specific char
34985 // SUBSET-embedding helper, (b) the `arr` argument as the
34986 // drift site rather than the `set` argument specifying the
34987 // target superset.
34988 let outcome = std::panic::catch_unwind(|| {
34989 assert_char_array_within_char_finite_set(&['a', 'z'], &['a', 'b', 'c']);
34990 });
34991 let payload = outcome.expect_err(
34992 "assert_char_array_within_char_finite_set must panic on \
34993 an out-of-set entry — the reject-out-of-set arm is the \
34994 sole CHAR-SUBSET-VIOLATION failure mode of the helper",
34995 );
34996 let msg = payload
34997 .downcast_ref::<&'static str>()
34998 .map(|s| (*s).to_owned())
34999 .or_else(|| payload.downcast_ref::<String>().cloned())
35000 .expect(
35001 "assert_char_array_within_char_finite_set panic \
35002 payload must be a static &str or String",
35003 );
35004 assert!(
35005 msg.contains("assert_char_array_within_char_finite_set"),
35006 "assert_char_array_within_char_finite_set panic message \
35007 {msg:?} must name the helper for provenance-preserving \
35008 failure diagnostics",
35009 );
35010 assert!(
35011 msg.contains("CHAR-SUBSET-VIOLATION"),
35012 "assert_char_array_within_char_finite_set panic message \
35013 {msg:?} must name the failed AXIS (\"CHAR-SUBSET-\
35014 VIOLATION\") for axis-provenance-preserving failure \
35015 diagnostics",
35016 );
35017 }
35018
35019 #[test]
35020 #[should_panic(expected = "assert_char_array_pairwise_distinct")]
35021 fn assert_char_array_within_char_finite_set_panics_on_malformed_target_set_spec() {
35022 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
35023 // malformed target-set spec `['a', 'a', 'b']` fed into the
35024 // ARRAY-side within helper MUST panic on the DELEGATED
35025 // pairwise-distinct arm BEFORE the CHAR-SUBSET-VIOLATION arm
35026 // fires. Pins the delegation chain: a regression that dropped
35027 // the `assert_char_array_pairwise_distinct(set)` call at the
35028 // top of `assert_char_array_within_char_finite_set` would
35029 // silently accept a malformed set and produce a false-positive
35030 // verdict on any `arr` embedded in the DISTINCT-value subset.
35031 // The panic message here surfaces from the SIBLING helper
35032 // (containing the ARRAY-side helper's `"assert_char_array_
35033 // pairwise_distinct"` panic-name prefix rather than a bespoke
35034 // `"SET-NOT-PAIRWISE-DISTINCT"` axis string) because the char
35035 // row does NOT yet carry a separate `assert_char_finite_set_
35036 // pairwise_distinct` alias — the delegation reuses the
35037 // ARRAY-side helper directly per the design choice documented
35038 // on the SET-side-well-formedness section of the helper's
35039 // docstring. Sibling posture to
35040 // `assert_u8_array_within_u8_finite_set_panics_on_malformed_target_set_spec`
35041 // on the (u8) row's delegated-SET-well-formedness pin — the
35042 // two pins together bind the delegation chain at ONE test per
35043 // element-type row.
35044 assert_char_array_within_char_finite_set::<2, 3>(&['a', 'b'], &['a', 'b', 'b']);
35045 }
35046
35047 // ── `assert_char_arrays_disjoint` — the CHAR-DISJOINTNESS-VIOLATION
35048 // verifier that binds `a ∩ b = ∅` at compile time on the reader-
35049 // boundary `char` vocabulary, peer to
35050 // `assert_char_array_within_char_finite_set` on the (char) row of
35051 // the (subset, disjointness) 2-corner face of the (contract-shape)
35052 // axis. The runtime test surface pins each of the helper's arms
35053 // (accept-both-empty, accept-either-empty, accept-disjoint-
35054 // singletons, accept-each-family-wide-substrate-pair, accept-arg-
35055 // order-symmetry, reject-single-collision, reject-terminal-a-
35056 // collision, reject-terminal-b-collision, panic-message-provenance
35057 // on the CHAR-DISJOINTNESS-VIOLATION axis) so a regression that
35058 // silently weakened the helper on ANY arm is caught by the
35059 // helper's OWN test surface rather than only surfacing as a false-
35060 // positive on some future disjoint `[char; N] × [char; M]` pair's
35061 // compound pin.
35062
35063 #[test]
35064 fn assert_char_arrays_disjoint_accepts_both_empty_arrays() {
35065 // Both arrays empty at the `[char; 0] × [char; 0]` corner —
35066 // vacuously disjoint (no `(i, j)` pair exists to test). The
35067 // compile-time `const _: () = assert_char_arrays_disjoint(&[],
35068 // &[]);` would land on this arm, so the runtime call MUST
35069 // return normally. Turbofish binding required because there's
35070 // no other cue for the const parameters on the empty array
35071 // literals. Sibling posture to
35072 // `assert_char_array_within_char_finite_set_accepts_the_empty_array_within_any_set`
35073 // on the peer SUBSET-embedding helper — the two share the
35074 // trivial-arity arm across the (subset, disjointness) 2-corner
35075 // face on the (char) row.
35076 assert_char_arrays_disjoint::<0, 0>(&[], &[]);
35077 }
35078
35079 #[test]
35080 fn assert_char_arrays_disjoint_accepts_either_side_empty() {
35081 // Either side empty at the `[char; 0] × [char; M]` OR
35082 // `[char; N] × [char; 0]` corners — vacuously disjoint (the
35083 // OUTER `while i < N` OR the INNER `while j < M` sweep is a
35084 // no-op). Pins BOTH SIDES of the (a-empty, b-empty) 2-corner
35085 // sub-face on the trivial-arity axis so a regression that
35086 // narrowed the sweep to only-a-non-empty OR only-b-non-empty
35087 // fails HERE at the empty-side corner rather than at a distant
35088 // false-positive.
35089 assert_char_arrays_disjoint::<0, 3>(&[], &['a', 'b', 'c']);
35090 assert_char_arrays_disjoint::<3, 0>(&['a', 'b', 'c'], &[]);
35091 }
35092
35093 #[test]
35094 fn assert_char_arrays_disjoint_accepts_disjoint_singletons() {
35095 // Singleton `a = [K]` and singleton `b = [L]` with `K != L` at
35096 // the `[char; 1] × [char; 1]` corner — the minimal non-empty
35097 // disjointness relation. Pins the INNER `if a[i] != b[j]` gate
35098 // returns without panicking on a single distinct-pair check.
35099 // A regression that flipped the equality direction (`==` vs
35100 // `!=`) would silently reject every disjoint singleton pair.
35101 assert_char_arrays_disjoint(&['a'], &['b']);
35102 }
35103
35104 #[test]
35105 fn assert_char_arrays_disjoint_accepts_each_family_wide_substrate_pair() {
35106 // Runtime cross-check that the FIVE (a, b) `[char; N] ×
35107 // [char; M]` pairs the substrate's module-level `const _`
35108 // witnesses pin at COMPILE time are proper DISJOINTNESS
35109 // embeddings at runtime too. The pairs enforce the theorem at
35110 // TWO stages of the toolchain: the const witnesses fire FIRST
35111 // at `cargo check` (through the five module-level `const _:
35112 // () = assert_char_arrays_disjoint::<N, M>(...)` lines), this
35113 // runtime pin catches the drift at `cargo test` as a safety
35114 // net. Sibling posture to
35115 // `assert_char_array_within_char_finite_set_accepts_each_family_wide_substrate_subset`
35116 // which sweeps the three (subset, superset) PAIRS at the
35117 // SUBSET-EMBEDDING axis; this pin sweeps the five (a, b)
35118 // PAIRS at the DISJOINTNESS axis on the SAME (char) row.
35119 assert_char_arrays_disjoint::<2, 2>(&Sexp::LIST_DELIMITERS, &Sexp::COMMENT_DELIMITERS);
35120 assert_char_arrays_disjoint::<2, 3>(&Sexp::LIST_DELIMITERS, &QuoteForm::LEADS);
35121 assert_char_arrays_disjoint::<2, 3>(&Sexp::COMMENT_DELIMITERS, &QuoteForm::LEADS);
35122 assert_char_arrays_disjoint::<2, 2>(&Sexp::LIST_DELIMITERS, &Atom::SELF_ESCAPE_TABLE);
35123 assert_char_arrays_disjoint::<3, 2>(&QuoteForm::LEADS, &Atom::SELF_ESCAPE_TABLE);
35124 }
35125
35126 #[test]
35127 fn assert_char_arrays_disjoint_is_symmetric_in_argument_order() {
35128 // SYMMETRY PIN: swapping the two arguments produces the SAME
35129 // verdict. Pins that the disjointness relation is truly
35130 // symmetric across the two array arguments (the helper's
35131 // nested-sweep implementation does NOT gratuitously depend on
35132 // argument order). A regression that narrowed the sweep to
35133 // `for i in a { if !b.contains(a[i]) }` (subset-shape, not
35134 // disjointness-shape) would fail the swap on one direction
35135 // only. Runs each substrate pair BOTH ways.
35136 assert_char_arrays_disjoint(&Sexp::COMMENT_DELIMITERS, &Sexp::LIST_DELIMITERS);
35137 assert_char_arrays_disjoint(&QuoteForm::LEADS, &Sexp::LIST_DELIMITERS);
35138 assert_char_arrays_disjoint(&Atom::SELF_ESCAPE_TABLE, &QuoteForm::LEADS);
35139 }
35140
35141 #[test]
35142 #[should_panic(expected = "CHAR-DISJOINTNESS-VIOLATION")]
35143 fn assert_char_arrays_disjoint_panics_at_runtime_on_collision() {
35144 // NEGATIVE PIN — CHAR-DISJOINTNESS-VIOLATION corner: two arrays
35145 // sharing a single entry MUST panic at runtime with the
35146 // CHAR-DISJOINTNESS-VIOLATION-named message. Pins the helper's
35147 // OWN reject arm — a regression that silently returned
35148 // without panicking on a cross-array collision would slip
35149 // through the compile-time witnesses' failure mode too. The
35150 // shared entry `'a'` is intentionally placed at the FIRST
35151 // position of BOTH arrays to pin the initial-position drift
35152 // mode.
35153 assert_char_arrays_disjoint(&['a', 'b'], &['a', 'c']);
35154 }
35155
35156 #[test]
35157 #[should_panic(expected = "CHAR-DISJOINTNESS-VIOLATION")]
35158 fn assert_char_arrays_disjoint_panics_at_runtime_on_terminal_a_collision() {
35159 // NEGATIVE PIN — terminal-position drift on the OUTER `a` side:
35160 // a shared entry at the LAST position of `a` MUST panic — pins
35161 // that the outer `while i < N` loop reaches `i = N - 1` (else
35162 // the terminal drift on `a` would slip through). A regression
35163 // that narrowed the outer sweep to `while i < N - 1` (off-by-
35164 // one on the OUTER bound) would silently accept this pair.
35165 assert_char_arrays_disjoint(&['x', 'y', 'z'], &['z']);
35166 }
35167
35168 #[test]
35169 #[should_panic(expected = "CHAR-DISJOINTNESS-VIOLATION")]
35170 fn assert_char_arrays_disjoint_panics_at_runtime_on_terminal_b_collision() {
35171 // NEGATIVE PIN — terminal-position drift on the INNER `b` side:
35172 // a shared entry at the LAST position of `b` MUST panic — pins
35173 // that the inner `while j < M` loop reaches `j = M - 1` (else
35174 // the terminal drift on `b` would slip through). A regression
35175 // that narrowed the inner sweep to `while j < M - 1` (off-by-
35176 // one on the INNER bound) would silently accept this pair.
35177 // Sibling posture to the outer-terminal pin above — the two
35178 // pins together bind the terminal bounds on BOTH loops.
35179 assert_char_arrays_disjoint(&['x'], &['a', 'b', 'x']);
35180 }
35181
35182 #[test]
35183 fn assert_char_arrays_disjoint_panic_message_names_the_helper_and_char_disjointness_violation_axis(
35184 ) {
35185 // PANIC-MESSAGE PROVENANCE PIN — CHAR-DISJOINTNESS-VIOLATION
35186 // arm: the panic message MUST begin with the helper's own name
35187 // AND identify the failed AXIS as "CHAR-DISJOINTNESS-VIOLATION"
35188 // so downstream diagnostics route the drift back to (a) the
35189 // helper by string search on `"assert_char_arrays_disjoint"`
35190 // and (b) the axis by string search on `"CHAR-DISJOINTNESS-
35191 // VIOLATION"`. Sibling posture to
35192 // `assert_char_array_within_char_finite_set_panic_message_names_the_helper_and_char_subset_violation_axis`
35193 // on the peer SUBSET-embedding helper's provenance pin — the
35194 // two pins together bind the (helper, failed-axis) provenance
35195 // pair at ONE test per corner of the (subset, disjointness)
35196 // 2-corner face on the (char) row. The axis-provenance string
35197 // `"CHAR-DISJOINTNESS-VIOLATION"` is chosen DISTINCT from EVERY
35198 // sibling helper's axis vocabulary (`"duplicate"` on the
35199 // ARRAY-side pairwise-distinct sibling; `"CHAR-SUBSET-
35200 // VIOLATION"` on the (char) SUBSET-embedding sibling;
35201 // `"SUBSET-VIOLATION"` on the (u8) finite-set SUBSET-only
35202 // sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8) range
35203 // SUBSET-only sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"`
35204 // on the (u8) covers-finite-set sibling; `"OUT-OF-RANGE"` /
35205 // `"MISSING"` on the (u8) covers-inclusive-range sibling;
35206 // `"ARITY-MISMATCH"` on both (u8) `_permutes_*` compound
35207 // helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on the (u8) SET-side
35208 // well-formedness sibling) so a diagnostic that names the
35209 // failed axis routes UNAMBIGUOUSLY to (a) this specific char
35210 // DISJOINTNESS helper.
35211 let outcome = std::panic::catch_unwind(|| {
35212 assert_char_arrays_disjoint(&['a', 'b'], &['a', 'c']);
35213 });
35214 let payload = outcome.expect_err(
35215 "assert_char_arrays_disjoint must panic on a cross-array \
35216 collision — the reject-collision arm is the sole CHAR-\
35217 DISJOINTNESS-VIOLATION failure mode of the helper",
35218 );
35219 let msg = payload
35220 .downcast_ref::<&'static str>()
35221 .map(|s| (*s).to_owned())
35222 .or_else(|| payload.downcast_ref::<String>().cloned())
35223 .expect(
35224 "assert_char_arrays_disjoint panic payload must be a \
35225 static &str or String",
35226 );
35227 assert!(
35228 msg.contains("assert_char_arrays_disjoint"),
35229 "assert_char_arrays_disjoint panic message {msg:?} must \
35230 name the helper for provenance-preserving failure \
35231 diagnostics",
35232 );
35233 assert!(
35234 msg.contains("CHAR-DISJOINTNESS-VIOLATION"),
35235 "assert_char_arrays_disjoint panic message {msg:?} must \
35236 name the failed AXIS (\"CHAR-DISJOINTNESS-VIOLATION\") \
35237 for axis-provenance-preserving failure diagnostics",
35238 );
35239 }
35240
35241 // ── `assert_char_array_slice_equals_char_array` — the CHAR-SLICE-
35242 // EQUALS-ARRAY-VIOLATION verifier that binds the sub-slice
35243 // `full[START..START + M) == sub[..]` positionwise-composition
35244 // contract at compile time on the substrate's reader-boundary
35245 // `[char; N]` scalar-composed vocabulary, row-dual peer to
35246 // `assert_u8_array_slice_equals_u8_array` on the (u8) row of the
35247 // (element-type) axis of the SAME (SUB-SLICE ARRAY-image) column
35248 // of the (element-type × contract-shape) matrix. The runtime test
35249 // surface pins each of the helper's arms (accept-canonical-
35250 // middle-slice, accept-empty-sub-array-at-three-start-positions,
35251 // accept-full-array-degenerate-at-three-arities, accept-each-of-
35252 // the-eight-family-wide-substrate-arrays, reject-positionwise-
35253 // drift, reject-start-out-of-bounds, reject-slice-length-out-of-
35254 // bounds, panic-message-provenance on the CHAR-SLICE-EQUALS-
35255 // ARRAY-VIOLATION axis) so a regression that silently weakened
35256 // the helper on ANY arm (e.g. flipping `!=` to `==` on the char
35257 // comparison, dropping the `START` offset from the `full[START +
35258 // i]` read, returning early past ANY bounds gate, or dropping the
35259 // `as u32` char-to-scalar bridge that lets the const-eval sweep
35260 // proceed byte-for-byte) is caught by the helper's OWN test
35261 // surface rather than only surfacing as a false-positive on some
35262 // future `[char; N]`-typed reader-boundary array's per-position
35263 // ORDER pin.
35264
35265 #[test]
35266 fn assert_char_array_slice_equals_char_array_accepts_a_canonical_middle_slice() {
35267 // Canonical sub-slice `full[START..START + M) == sub[..]`
35268 // inside a longer array `full` whose ENDPOINTS carry
35269 // DIFFERENT chars than the peer sub-array. Pins the outer
35270 // `while i < M` sweep reads `full[START + i]` at the OFFSET
35271 // position (not `full[i]`) — a regression that dropped the
35272 // `START` offset would compare `full[0..M)` against `sub[..]`
35273 // and pass on `full[0]='z' != sub[0]='b'` silently or panic
35274 // on the wrong axis. `START = 1` pins the sweep skips
35275 // position `[0..START)` and reads only `[1..1+3) = [1..4)`.
35276 assert_char_array_slice_equals_char_array::<7, 3, 1>(
35277 &['z', 'b', 'c', 'd', 'z', 'z', 'z'],
35278 &['b', 'c', 'd'],
35279 );
35280 }
35281
35282 #[test]
35283 fn assert_char_array_slice_equals_char_array_accepts_the_empty_sub_array() {
35284 // LEGAL degenerate: `M == 0` collapses the sub-array into an
35285 // empty listing `[]`. The sweep never enters the loop body
35286 // and the helper accepts. Cross-position coverage pins the
35287 // empty-sub-array acceptance at THREE distinct `START`
35288 // positions (`START == 0` at the left endpoint, `START == 3`
35289 // in the interior, `START == N` at the right endpoint — the
35290 // latter is the corner `START == N` combined with `M == 0`
35291 // that the START-OUT-OF-BOUNDS gate's inclusive upper bound
35292 // must accept). A regression that hard-coded `START < N` OR
35293 // panicked on the `M == 0` corner is caught on ALL THREE
35294 // arms.
35295 assert_char_array_slice_equals_char_array::<5, 0, 0>(&['x', 'x', 'x', 'x', 'x'], &[]);
35296 assert_char_array_slice_equals_char_array::<5, 0, 3>(&['x', 'x', 'x', 'x', 'x'], &[]);
35297 assert_char_array_slice_equals_char_array::<5, 0, 5>(&['x', 'x', 'x', 'x', 'x'], &[]);
35298 }
35299
35300 #[test]
35301 fn assert_char_array_slice_equals_char_array_accepts_the_full_array_degenerate() {
35302 // Full-array-covering slice `M == N, START == 0` collapses
35303 // to the ALL-positions-equal-peer-array shape `full == sub`
35304 // pointwise. Pins that the sweep proceeds through EVERY
35305 // position of the outer array when `START = 0` and `M = N`.
35306 // Cross-arity coverage on `N ∈ {1, 3, 7}` pins the sweep's
35307 // terminal-position visit across the range of char arities
35308 // the substrate's reader-boundary arrays span (`N = 1` for
35309 // `UnquoteForm::LEADS`, `N = 2` for the delimiter/escape
35310 // pairs, `N = 3` for `QuoteForm::LEADS`, `N = 5` for
35311 // `ESCAPE_SOURCES` / `ESCAPE_DECODED`, `N = 7` for
35312 // `NON_WHITESPACE_BARE_ATOM_TERMINATORS`).
35313 assert_char_array_slice_equals_char_array::<1, 1, 0>(&[','], &[',']);
35314 assert_char_array_slice_equals_char_array::<3, 3, 0>(&['\'', '`', ','], &['\'', '`', ',']);
35315 assert_char_array_slice_equals_char_array::<7, 7, 0>(
35316 &['(', ')', '\'', '`', ',', '"', ';'],
35317 &['(', ')', '\'', '`', ',', '"', ';'],
35318 );
35319 }
35320
35321 #[test]
35322 fn assert_char_array_slice_equals_char_array_accepts_each_family_wide_substrate_array() {
35323 // Runtime cross-check that the EIGHT reader-boundary
35324 // `[char; N]` scalar-composed substrate arrays each byte-
35325 // equal their canonical literal-char listing pointwise at the
35326 // FULL-ARRAY corner (`M == N`, `START == 0`). Runs the SAME
35327 // helper the eight `const _` witnesses at line ~616 in this
35328 // file run at rustc time — a runtime safety net enforcing
35329 // the theorem at BOTH stages of the toolchain (const at
35330 // `cargo check`, runtime at `cargo test`). A regression that
35331 // renamed one of the per-role `*_LEAD` / `*_DELIMITER` /
35332 // `*_ESCAPE_LEAD` / `*_ESCAPE_SOURCE` / `*_ESCAPE_DECODED`
35333 // aliases (or drifted its literal char value at the
35334 // declaration site, or reordered a slot in the outer array's
35335 // initializer) fails HERE at the substrate callsite AND at
35336 // the const witness above. Peer of
35337 // `assert_u8_array_slice_equals_u8_array_accepts_sub_carving_hash_discriminators_per_position_order`
35338 // on the (u8) row — that witness carries the FULL-ARRAY per-
35339 // position ORDER theorem for the FOUR sub-carving
35340 // `HASH_DISCRIMINATORS` arrays; this witness carries the same
35341 // theorem for the EIGHT reader-boundary `char` arrays.
35342 //
35343 // The eight reader-boundary scalar-composed arrays appear
35344 // here in canonical (owning-algebra, per-role-alias-count-
35345 // ascending) order:
35346 // * `Sexp::LIST_DELIMITERS == ['(', ')']` — the outer-`Sexp`
35347 // list-delimiter pair.
35348 // * `Sexp::COMMENT_DELIMITERS == [';', '\n']` — the outer-
35349 // `Sexp` comment-boundary pair.
35350 // * `Atom::SELF_ESCAPE_TABLE == ['"', '\\']` — the `Atom`
35351 // escape-self pair.
35352 // * `Atom::ESCAPE_SOURCES == ['n', 't', 'r', '"', '\\']` —
35353 // the `Atom` escape-source column.
35354 // * `Atom::ESCAPE_DECODED == ['\n', '\t', '\r', '"', '\\']`
35355 // — the `Atom` escape-decoded column.
35356 // * `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS == ['(',
35357 // ')', '\'', '`', ',', '"', ';']` — the outer-`Sexp`
35358 // reader-boundary category-leading seven-char SPAN.
35359 // * `QuoteForm::LEADS == ['\'', '`', ',']` — the quote-
35360 // family reader-lead-char triple.
35361 // * `UnquoteForm::LEADS == [',']` — the substitution-subset
35362 // shared-lead singleton.
35363 assert_char_array_slice_equals_char_array::<2, 2, 0>(&Sexp::LIST_DELIMITERS, &['(', ')']);
35364 assert_char_array_slice_equals_char_array::<2, 2, 0>(
35365 &Sexp::COMMENT_DELIMITERS,
35366 &[';', '\n'],
35367 );
35368 assert_char_array_slice_equals_char_array::<2, 2, 0>(
35369 &Atom::SELF_ESCAPE_TABLE,
35370 &['"', '\\'],
35371 );
35372 assert_char_array_slice_equals_char_array::<5, 5, 0>(
35373 &Atom::ESCAPE_SOURCES,
35374 &['n', 't', 'r', '"', '\\'],
35375 );
35376 assert_char_array_slice_equals_char_array::<5, 5, 0>(
35377 &Atom::ESCAPE_DECODED,
35378 &['\n', '\t', '\r', '"', '\\'],
35379 );
35380 assert_char_array_slice_equals_char_array::<7, 7, 0>(
35381 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35382 &['(', ')', '\'', '`', ',', '"', ';'],
35383 );
35384 assert_char_array_slice_equals_char_array::<3, 3, 0>(&QuoteForm::LEADS, &['\'', '`', ',']);
35385 assert_char_array_slice_equals_char_array::<1, 1, 0>(
35386 &crate::error::UnquoteForm::LEADS,
35387 &[','],
35388 );
35389 }
35390
35391 #[test]
35392 fn assert_char_array_slice_equals_char_array_accepts_terminator_span_sub_carving_positional_composition(
35393 ) {
35394 // Runtime cross-check that the outer-`Sexp` reader-boundary
35395 // terminator SPAN `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS`
35396 // (`[char; 7]`) positionally composes as the segmented
35397 // concatenation of its FOUR sub-carvings:
35398 //
35399 // NON_WHITESPACE_BARE_ATOM_TERMINATORS
35400 // == Sexp::LIST_DELIMITERS // slots [0..2)
35401 // ++ QuoteForm::LEADS // slots [2..5)
35402 // ++ [Atom::STR_DELIMITER] // slot [5..6)
35403 // ++ [Sexp::COMMENT_LEAD] // slot [6..7)
35404 //
35405 // Runs the SAME helper the FOUR `const _` witnesses at line
35406 // ~915 in this file run at rustc time — a runtime safety net
35407 // enforcing the ARRAY-LEVEL POSITIONAL-COMPOSITION theorem at
35408 // BOTH stages of the toolchain (const at `cargo check`,
35409 // runtime at `cargo test`). Strictly STRONGER on the
35410 // (contract-strength) axis than the sibling
35411 // `sexp_list_delimiters_positionally_align_with_terminator_head`
35412 // /
35413 // `quote_form_leads_positionally_align_with_terminator_mid`
35414 // -shape pre-lift runtime pins that lived only in prose in the
35415 // sub-carvings' composition-rule docstrings: those pin the
35416 // SUBSET containment `LIST_DELIMITERS ⊆ NON_WHITESPACE_BARE_
35417 // ATOM_TERMINATORS` (and `QuoteForm::LEADS ⊆
35418 // NON_WHITESPACE_BARE_ATOM_TERMINATORS`) as a SET-level
35419 // theorem, order-invariant on the sub-carving side; this pin
35420 // binds the ARRAY-LEVEL POSITIONAL identity at the CANONICAL
35421 // slot segments. A regression that reorders any sub-carving's
35422 // declaration (e.g. `Sexp::LIST_DELIMITERS = [LIST_CLOSE,
35423 // LIST_OPEN]` swapping the pair, or `QuoteForm::LEADS =
35424 // [QUASIQUOTE_LEAD, QUOTE_LEAD, UNQUOTE_LEAD]` permuting the
35425 // triple) preserves the SET-level SUBSET theorem AND the FULL-
35426 // ARRAY LITERAL witness on `NON_WHITESPACE_BARE_ATOM_
35427 // TERMINATORS` (which peer-compares against a HARDCODED
35428 // `[char; 7]` literal via its inline listing rather than
35429 // against the SUB-CARVING arrays) but silently misaligns every
35430 // consumer that treats the composite's slot segment as
35431 // positionally-interchangeable with its sub-carving. Peer
35432 // posture to `assert_u8_array_slice_equals_u8_array_accepts_
35433 // sexp_shape_hash_discriminators_per_position_order`-shape
35434 // sibling on the (u8) row — that pin carries the SUB-CARVING
35435 // per-position POSITIONAL-COMPOSITION theorem for the twelve-
35436 // slot outer `SexpShape::HASH_DISCRIMINATORS` container; this
35437 // pin carries the same theorem for the seven-slot outer
35438 // `NON_WHITESPACE_BARE_ATOM_TERMINATORS` SPAN.
35439 assert_char_array_slice_equals_char_array::<7, 2, 0>(
35440 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35441 &Sexp::LIST_DELIMITERS,
35442 );
35443 assert_char_array_slice_equals_char_array::<7, 3, 2>(
35444 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35445 &QuoteForm::LEADS,
35446 );
35447 assert_char_array_slice_equals_char_array::<7, 1, 5>(
35448 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35449 &[Atom::STR_DELIMITER],
35450 );
35451 assert_char_array_slice_equals_char_array::<7, 1, 6>(
35452 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35453 &[Sexp::COMMENT_LEAD],
35454 );
35455 }
35456
35457 #[test]
35458 #[should_panic(expected = "CHAR-SLICE-EQUALS-ARRAY-VIOLATION")]
35459 fn assert_char_array_slice_equals_char_array_panics_at_runtime_on_positionwise_drift() {
35460 // NEGATIVE PIN — CHAR-SLICE-EQUALS-ARRAY-VIOLATION corner: a
35461 // char at some position in `full[START..START + M)` that
35462 // does NOT byte-equal the peer sub-array `sub` at the
35463 // offset-matched position MUST panic at runtime with the
35464 // axis-named message. Pins the helper's positionwise-drift
35465 // reject arm — a regression that silently short-circuited on
35466 // the first slice position without checking the middle or
35467 // terminal slice positions would slip through the compile-
35468 // time witness's failure mode too. The offending char `'!'`
35469 // at outer position `3` (interior of the sub-slice `[1..4)`,
35470 // offset `2` inside `sub`) pins the middle-of-slice drift
35471 // mode.
35472 assert_char_array_slice_equals_char_array::<5, 3, 1>(
35473 &['z', 'b', 'c', '!', 'z'],
35474 &['b', 'c', 'd'],
35475 );
35476 }
35477
35478 #[test]
35479 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
35480 fn assert_char_array_slice_equals_char_array_panics_at_runtime_on_start_out_of_bounds() {
35481 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
35482 // turbofish arity slip on the `START` const-generic where
35483 // `START > N` MUST panic at runtime with the START-OUT-OF-
35484 // BOUNDS-named message BEFORE the peer SLICE-LENGTH-OUT-OF-
35485 // BOUNDS gate reads `N - START` (which would `usize`-
35486 // underflow had this gate not caught the slip first). Pins
35487 // the gate's placement at the TOP of the helper — a
35488 // regression that dropped the gate would either underflow
35489 // subtraction at the peer gate OR panic deeper in
35490 // `full[START + i]` bounds-checking with a helper-name-less
35491 // panic message. The offending `START = 7` against `N = 5`
35492 // pins the strict `START > N` reject arm; the LEGAL
35493 // `START == N` empty-slice-at-right-endpoint corner is
35494 // covered by the peer acceptance test above.
35495 assert_char_array_slice_equals_char_array::<5, 0, 7>(&['x', 'x', 'x', 'x', 'x'], &[]);
35496 }
35497
35498 #[test]
35499 #[should_panic(expected = "SLICE-LENGTH-OUT-OF-BOUNDS")]
35500 fn assert_char_array_slice_equals_char_array_panics_at_runtime_on_slice_length_out_of_bounds() {
35501 // NEGATIVE PIN — SLICE-LENGTH-OUT-OF-BOUNDS gate: a peer
35502 // sub-array arity `M` that exceeds the outer array's tail
35503 // cardinality `N - START` MUST panic at runtime with the
35504 // slice-length-out-of-bounds-named message. Peer gate to the
35505 // START-OUT-OF-BOUNDS arm above — the two gates jointly
35506 // enforce `START ≤ N` and `M ≤ N - START` before any content
35507 // sweep. The offending `M = 5` against `N - START = 5 - 3 =
35508 // 2` pins the strict `M > N - START` reject arm; the LEGAL
35509 // exact-fit corner `M == N - START` is covered by the
35510 // middle-slice acceptance test above.
35511 assert_char_array_slice_equals_char_array::<5, 5, 3>(
35512 &['x', 'x', 'x', 'x', 'x'],
35513 &['x', 'x', 'x', 'x', 'x'],
35514 );
35515 }
35516
35517 #[test]
35518 fn assert_char_array_slice_equals_char_array_panic_message_names_the_helper_and_char_slice_equals_array_violation_axis(
35519 ) {
35520 // PANIC-MESSAGE PROVENANCE PIN — CHAR-SLICE-EQUALS-ARRAY-
35521 // VIOLATION arm: the panic message MUST begin with the
35522 // helper's own name AND identify the failed AXIS as "CHAR-
35523 // SLICE-EQUALS-ARRAY-VIOLATION" so downstream diagnostics
35524 // route the drift back to (a) the helper by string search on
35525 // `"assert_char_array_slice_equals_char_array"` and (b) the
35526 // failed axis by string search on `"CHAR-SLICE-EQUALS-ARRAY-
35527 // VIOLATION"`. Sibling posture to the u8-row peer's
35528 // provenance pin
35529 // `assert_u8_array_slice_equals_u8_array_panic_message_names_the_helper_and_slice_equals_array_violation_axis`
35530 // — the two pins together bind the (helper, failed-axis)
35531 // provenance pair at ONE test per corner of the (SUB-SLICE
35532 // ARRAY-image) column on both the (u8) row AND the (char)
35533 // row of the (element-type × contract-shape) matrix. The
35534 // `CHAR-` prefix on this axis disambiguates it from the u8-
35535 // row sibling's plain `SLICE-EQUALS-ARRAY-VIOLATION` axis
35536 // vocabulary; the shared `-SLICE-EQUALS-ARRAY-VIOLATION`
35537 // infix lets callers grep either element-type variant by
35538 // the shared axis substring.
35539 let outcome = std::panic::catch_unwind(|| {
35540 assert_char_array_slice_equals_char_array::<5, 3, 1>(
35541 &['z', 'b', 'c', '!', 'z'],
35542 &['b', 'c', 'd'],
35543 );
35544 });
35545 let payload = outcome.expect_err(
35546 "assert_char_array_slice_equals_char_array must panic on \
35547 a positionwise drift — the reject-positionwise-drift arm \
35548 is the CONTENT failure mode of the helper",
35549 );
35550 let msg = payload
35551 .downcast_ref::<&'static str>()
35552 .map(|s| (*s).to_owned())
35553 .or_else(|| payload.downcast_ref::<String>().cloned())
35554 .expect(
35555 "assert_char_array_slice_equals_char_array panic \
35556 payload must be a static &str or String",
35557 );
35558 assert!(
35559 msg.contains("assert_char_array_slice_equals_char_array"),
35560 "assert_char_array_slice_equals_char_array panic message \
35561 {msg:?} must name the helper for provenance-preserving \
35562 failure diagnostics",
35563 );
35564 assert!(
35565 msg.contains("CHAR-SLICE-EQUALS-ARRAY-VIOLATION"),
35566 "assert_char_array_slice_equals_char_array panic message \
35567 {msg:?} must name the failed AXIS (\"CHAR-SLICE-EQUALS-\
35568 ARRAY-VIOLATION\") for axis-provenance-preserving \
35569 failure diagnostics",
35570 );
35571 }
35572
35573 // ── `assert_str_array_slice_equals_str_array` — the (str)-row
35574 // peer to `assert_u8_array_slice_equals_u8_array` +
35575 // `assert_char_array_slice_equals_char_array` on the (element-
35576 // type) axis of the SUB-SLICE ARRAY-image column of the
35577 // (element-type × contract-shape) matrix. The runtime test
35578 // surface pins each of the helper's arms (accept-middle-slice,
35579 // accept-empty-sub-array, accept-full-array-degenerate, accept-
35580 // sexp-shape-labels-positional-decomposition, reject-positionwise-
35581 // drift, reject-start-out-of-bounds, reject-slice-length-out-of-
35582 // bounds, panic-message-provenance on the STR-SLICE-EQUALS-ARRAY-
35583 // VIOLATION axis) so a regression that silently weakened the
35584 // helper on ANY arm is caught by the helper's OWN test surface
35585 // rather than only surfacing as a false-positive on some future
35586 // sub-slice `[&'static str; N]` pair's compound pin.
35587
35588 #[test]
35589 fn assert_str_array_slice_equals_str_array_accepts_a_canonical_middle_slice() {
35590 // Canonical sub-slice `full[START..START + M) == sub[..]`
35591 // inside a longer array `full` whose ENDPOINTS carry DIFFERENT
35592 // strings than the peer sub-array. Pins the outer `while i <
35593 // M` sweep reads `full[START + i]` at the OFFSET position
35594 // (not `full[i]`) — a regression that dropped the `START`
35595 // offset would compare `full[0..M)` against `sub[..]` and
35596 // pass on `full[0]="z" != sub[0]="b"` silently or panic on
35597 // the wrong axis. `START = 1` pins the sweep skips position
35598 // `[0..START)` and reads only `[1..1+3) = [1..4)`. Sibling
35599 // posture to
35600 // `assert_char_array_slice_equals_char_array_accepts_a_canonical_middle_slice`
35601 // and
35602 // `assert_u8_array_slice_equals_u8_array_accepts_a_canonical_middle_slice`
35603 // — the three share the middle-slice acceptance arm across
35604 // the (element-type × contract-shape) 3-row × 1-column face
35605 // at the (SUB-SLICE ARRAY-image) column.
35606 assert_str_array_slice_equals_str_array::<7, 3, 1>(
35607 &["z", "b", "c", "d", "z", "z", "z"],
35608 &["b", "c", "d"],
35609 );
35610 }
35611
35612 #[test]
35613 fn assert_str_array_slice_equals_str_array_accepts_the_empty_sub_array() {
35614 // LEGAL degenerate: `M == 0` collapses the sub-array into an
35615 // empty listing `[]`. The sweep never enters the loop body
35616 // and the helper accepts. Cross-position coverage pins the
35617 // empty-sub-array acceptance at THREE distinct `START`
35618 // positions (`START == 0` at the left endpoint, `START == 3`
35619 // in the interior, `START == N` at the right endpoint — the
35620 // latter is the corner `START == N` combined with `M == 0`
35621 // that the START-OUT-OF-BOUNDS gate's inclusive upper bound
35622 // must accept). A regression that hard-coded `START < N` OR
35623 // panicked on the `M == 0` corner is caught on ALL THREE
35624 // arms.
35625 assert_str_array_slice_equals_str_array::<5, 0, 0>(&["x", "x", "x", "x", "x"], &[]);
35626 assert_str_array_slice_equals_str_array::<5, 0, 3>(&["x", "x", "x", "x", "x"], &[]);
35627 assert_str_array_slice_equals_str_array::<5, 0, 5>(&["x", "x", "x", "x", "x"], &[]);
35628 }
35629
35630 #[test]
35631 fn assert_str_array_slice_equals_str_array_accepts_the_full_array_degenerate() {
35632 // Full-array-covering slice `M == N, START == 0` collapses
35633 // to the ALL-positions-equal-peer-array shape `full == sub`
35634 // pointwise. Pins that the sweep proceeds through EVERY
35635 // position of the outer array when `START = 0` and `M = N`.
35636 // Cross-arity coverage on `N ∈ {1, 3, 6}` pins the sweep's
35637 // terminal-position visit across the range of str arities
35638 // the substrate's LABELS arrays span (`N = 1` for the
35639 // singleton sub-carvings, `N = 4` for `QuoteForm::LABELS`,
35640 // `N = 6` for `AtomKind::LABELS`).
35641 assert_str_array_slice_equals_str_array::<1, 1, 0>(&["nil"], &["nil"]);
35642 assert_str_array_slice_equals_str_array::<3, 3, 0>(
35643 &["quote", "quasiquote", "unquote"],
35644 &["quote", "quasiquote", "unquote"],
35645 );
35646 assert_str_array_slice_equals_str_array::<6, 6, 0>(
35647 &["symbol", "keyword", "string", "int", "float", "bool"],
35648 &["symbol", "keyword", "string", "int", "float", "bool"],
35649 );
35650 }
35651
35652 #[test]
35653 fn assert_str_array_slice_equals_str_array_accepts_sexp_shape_labels_positional_decomposition()
35654 {
35655 // Runtime cross-check that the FOUR canonical sub-slices of
35656 // `crate::error::SexpShape::LABELS` (`[&'static str; 12]`)
35657 // each byte-equal their sub-carving's canonical
35658 // `[&'static str; M]` listing pointwise. Runs the SAME
35659 // helper the FOUR `const _` witnesses in `error.rs` (in the
35660 // block titled "Compile-time SLICE-EQUALS-ARRAY witnesses
35661 // closing the twelve-arm `SexpShape::LABELS` POSITIONAL
35662 // decomposition") run at rustc time — a runtime safety net
35663 // enforcing the theorem at BOTH stages of the toolchain
35664 // (const at `cargo check`, runtime at `cargo test`). A
35665 // regression that reordered any of the twelve slots in the
35666 // outer array's initializer, or drifted any sub-carving's
35667 // per-role LABEL alias, fails HERE at the substrate callsite
35668 // AND at the const witness in `error.rs`. Sibling posture to
35669 // `assert_u8_array_slice_equals_u8_array_accepts_sub_carving_hash_discriminators_per_position_order`
35670 // on the (u8) row — that witness carries the positional
35671 // decomposition theorem for the FOUR sub-carving
35672 // `HASH_DISCRIMINATORS` arrays; this witness carries the
35673 // SAME theorem for the FOUR sub-carving LABELS arrays
35674 // (strictly STRONGER at the `[1..7)` slice: the (u8) row's
35675 // sibling collapses to a SCALAR replica witness on the six-
35676 // slot atomic-payload middle slice, but the (str) row's
35677 // per-slot label listing distinguishes ALL SIX slots
35678 // individually).
35679 //
35680 // The four canonical sub-slices are (all under the shared
35681 // `&'static str` element-type and shared parent
35682 // `[&'static str; 12]` outer container `SexpShape::LABELS`):
35683 // 1. `SexpShape::LABELS[0..1) ==
35684 // [StructuralKind::NIL_LABEL]`
35685 // 2. `SexpShape::LABELS[1..7) == AtomKind::LABELS`
35686 // 3. `SexpShape::LABELS[7..8) ==
35687 // [StructuralKind::LIST_LABEL]`
35688 // 4. `SexpShape::LABELS[8..12) == QuoteForm::LABELS`
35689 assert_str_array_slice_equals_str_array::<12, 1, 0>(
35690 &crate::error::SexpShape::LABELS,
35691 &[crate::error::StructuralKind::NIL_LABEL],
35692 );
35693 assert_str_array_slice_equals_str_array::<12, 6, 1>(
35694 &crate::error::SexpShape::LABELS,
35695 &AtomKind::LABELS,
35696 );
35697 assert_str_array_slice_equals_str_array::<12, 1, 7>(
35698 &crate::error::SexpShape::LABELS,
35699 &[crate::error::StructuralKind::LIST_LABEL],
35700 );
35701 assert_str_array_slice_equals_str_array::<12, 4, 8>(
35702 &crate::error::SexpShape::LABELS,
35703 &QuoteForm::LABELS,
35704 );
35705 }
35706
35707 #[test]
35708 fn assert_str_array_slice_equals_str_array_accepts_every_family_wide_substrate_array_full_array_literal_listing(
35709 ) {
35710 // Runtime cross-check that the SAME five (str)-row FULL-ARRAY
35711 // LITERAL witnesses covered at COMPILE time by the module-
35712 // level `const _: () = ...` cluster immediately below the
35713 // (str)-row `assert_str_array_pairwise_distinct` witnesses
35714 // (`Atom::BOOL_LITERALS` / `AtomKind::LABELS` /
35715 // `QuoteForm::PREFIXES` / `QuoteForm::LABELS` /
35716 // `QuoteForm::IAC_FORGE_TAGS`) also hold when exercised at
35717 // runtime. A regression that removes ONE of the const
35718 // witnesses would still leave THIS runtime pin as a safety
35719 // net; the const witness fires FIRST at `cargo check`, this
35720 // runtime pin catches the positionwise drift at `cargo
35721 // test`. The pair enforces the theorem at TWO stages of the
35722 // toolchain (const at `cargo check`, runtime at `cargo
35723 // test`). Sibling posture to
35724 // `assert_char_array_slice_equals_char_array_accepts_every_family_wide_substrate_array_full_array_literal_listing`
35725 // (if / when that (char)-row runtime-safety-net peer is
35726 // added) — the two would jointly enforce the FULL-ARRAY per-
35727 // position ORDER column at runtime across the (char, str)
35728 // 2-row face of the (element-type × contract-shape) matrix.
35729 //
35730 // A regression that (a) reorders one of the outer arrays'
35731 // slots away from canonical declaration order, or (b) drifts
35732 // one of the per-role scalar `pub const *_LABEL` / `*_PREFIX`
35733 // / `*_TAG` / `TRUE_LITERAL` / `FALSE_LITERAL` aliases the
35734 // outer array's slots re-export, fails HERE with the
35735 // `STR-SLICE-EQUALS-ARRAY-VIOLATION` axis panic naming the
35736 // drifted position.
35737 assert_str_array_slice_equals_str_array::<2, 2, 0>(&Atom::BOOL_LITERALS, &["#t", "#f"]);
35738 assert_str_array_slice_equals_str_array::<6, 6, 0>(
35739 &AtomKind::LABELS,
35740 &["symbol", "keyword", "string", "int", "float", "bool"],
35741 );
35742 assert_str_array_slice_equals_str_array::<4, 4, 0>(
35743 &QuoteForm::PREFIXES,
35744 &["'", "`", ",", ",@"],
35745 );
35746 assert_str_array_slice_equals_str_array::<4, 4, 0>(
35747 &QuoteForm::LABELS,
35748 &["quote", "quasiquote", "unquote", "unquote-splice"],
35749 );
35750 assert_str_array_slice_equals_str_array::<4, 4, 0>(
35751 &QuoteForm::IAC_FORGE_TAGS,
35752 &["quote", "quasiquote", "unquote", "unquote-splicing"],
35753 );
35754 }
35755
35756 #[test]
35757 #[should_panic(expected = "STR-SLICE-EQUALS-ARRAY-VIOLATION")]
35758 fn assert_str_array_slice_equals_str_array_panics_at_runtime_on_positionwise_drift() {
35759 // NEGATIVE PIN — STR-SLICE-EQUALS-ARRAY-VIOLATION corner: a
35760 // str at some position in `full[START..START + M)` that does
35761 // NOT byte-equal the peer sub-array `sub` at the offset-
35762 // matched position MUST panic at runtime with the axis-named
35763 // message. Pins the helper's positionwise-drift reject arm —
35764 // a regression that silently short-circuited on the first
35765 // slice position without checking the middle or terminal
35766 // slice positions would slip through the compile-time
35767 // witness's failure mode too. The offending str `"!"` at
35768 // outer position `3` (interior of the sub-slice `[1..4)`,
35769 // offset `2` inside `sub`) pins the middle-of-slice drift
35770 // mode.
35771 assert_str_array_slice_equals_str_array::<5, 3, 1>(
35772 &["z", "b", "c", "!", "z"],
35773 &["b", "c", "d"],
35774 );
35775 }
35776
35777 #[test]
35778 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
35779 fn assert_str_array_slice_equals_str_array_panics_at_runtime_on_start_out_of_bounds() {
35780 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
35781 // turbofish arity slip on the `START` const-generic where
35782 // `START > N` MUST panic at runtime with the START-OUT-OF-
35783 // BOUNDS-named message BEFORE the peer SLICE-LENGTH-OUT-OF-
35784 // BOUNDS gate reads `N - START` (which would `usize`-
35785 // underflow had this gate not caught the slip first). Pins
35786 // the gate's placement at the TOP of the helper — a
35787 // regression that dropped the gate would either underflow
35788 // subtraction at the peer gate OR panic deeper in
35789 // `full[START + i]` bounds-checking with a helper-name-less
35790 // panic message. The offending `START = 7` against `N = 5`
35791 // pins the strict `START > N` reject arm; the LEGAL
35792 // `START == N` empty-slice-at-right-endpoint corner is
35793 // covered by the peer acceptance test above.
35794 assert_str_array_slice_equals_str_array::<5, 0, 7>(&["x", "x", "x", "x", "x"], &[]);
35795 }
35796
35797 #[test]
35798 #[should_panic(expected = "SLICE-LENGTH-OUT-OF-BOUNDS")]
35799 fn assert_str_array_slice_equals_str_array_panics_at_runtime_on_slice_length_out_of_bounds() {
35800 // NEGATIVE PIN — SLICE-LENGTH-OUT-OF-BOUNDS gate: a peer
35801 // sub-array arity `M` that exceeds the outer array's tail
35802 // cardinality `N - START` MUST panic at runtime with the
35803 // slice-length-out-of-bounds-named message. Peer gate to the
35804 // START-OUT-OF-BOUNDS arm above — the two gates jointly
35805 // enforce `START ≤ N` and `M ≤ N - START` before any content
35806 // sweep. The offending `M = 5` against `N - START = 5 - 3 =
35807 // 2` pins the strict `M > N - START` reject arm; the LEGAL
35808 // exact-fit corner `M == N - START` is covered by the middle-
35809 // slice acceptance test above.
35810 assert_str_array_slice_equals_str_array::<5, 5, 3>(
35811 &["x", "x", "x", "x", "x"],
35812 &["x", "x", "x", "x", "x"],
35813 );
35814 }
35815
35816 #[test]
35817 fn assert_str_array_slice_equals_str_array_panic_message_names_the_helper_and_str_slice_equals_array_violation_axis(
35818 ) {
35819 // PANIC-MESSAGE PROVENANCE PIN — STR-SLICE-EQUALS-ARRAY-
35820 // VIOLATION arm: the panic message MUST begin with the
35821 // helper's own name AND identify the failed AXIS as "STR-
35822 // SLICE-EQUALS-ARRAY-VIOLATION" so downstream diagnostics
35823 // route the drift back to (a) the helper by string search on
35824 // `"assert_str_array_slice_equals_str_array"` and (b) the
35825 // failed axis by string search on `"STR-SLICE-EQUALS-ARRAY-
35826 // VIOLATION"`. Sibling posture to the u8-row peer's
35827 // provenance pin
35828 // `assert_u8_array_slice_equals_u8_array_panic_message_names_the_helper_and_slice_equals_array_violation_axis`
35829 // AND the char-row peer's provenance pin
35830 // `assert_char_array_slice_equals_char_array_panic_message_names_the_helper_and_char_slice_equals_array_violation_axis`
35831 // — the three pins together bind the (helper, failed-axis)
35832 // provenance triple at ONE test per corner of the (SUB-SLICE
35833 // ARRAY-image) column on the (u8) + (char) + (str) rows of
35834 // the (element-type × contract-shape) matrix. The `STR-`
35835 // prefix on this axis disambiguates it from the u8-row
35836 // sibling's plain `SLICE-EQUALS-ARRAY-VIOLATION` axis
35837 // vocabulary AND the char-row sibling's `CHAR-SLICE-EQUALS-
35838 // ARRAY-VIOLATION` axis vocabulary; the shared `-SLICE-
35839 // EQUALS-ARRAY-VIOLATION` infix lets callers grep any of
35840 // the three element-type variants by the shared axis
35841 // substring.
35842 let outcome = std::panic::catch_unwind(|| {
35843 assert_str_array_slice_equals_str_array::<5, 3, 1>(
35844 &["z", "b", "c", "!", "z"],
35845 &["b", "c", "d"],
35846 );
35847 });
35848 let payload = outcome.expect_err(
35849 "assert_str_array_slice_equals_str_array must panic on a \
35850 positionwise drift — the reject-positionwise-drift arm \
35851 is the CONTENT failure mode of the helper",
35852 );
35853 let msg = payload
35854 .downcast_ref::<&'static str>()
35855 .map(|s| (*s).to_owned())
35856 .or_else(|| payload.downcast_ref::<String>().cloned())
35857 .expect(
35858 "assert_str_array_slice_equals_str_array panic \
35859 payload must be a static &str or String",
35860 );
35861 assert!(
35862 msg.contains("assert_str_array_slice_equals_str_array"),
35863 "assert_str_array_slice_equals_str_array panic message \
35864 {msg:?} must name the helper for provenance-preserving \
35865 failure diagnostics",
35866 );
35867 assert!(
35868 msg.contains("STR-SLICE-EQUALS-ARRAY-VIOLATION"),
35869 "assert_str_array_slice_equals_str_array panic message \
35870 {msg:?} must name the failed AXIS (\"STR-SLICE-EQUALS-\
35871 ARRAY-VIOLATION\") for axis-provenance-preserving \
35872 failure diagnostics",
35873 );
35874 }
35875
35876 // ── `assert_u8_arrays_disjoint` — the U8-DISJOINTNESS-VIOLATION
35877 // verifier that binds `a ∩ b = ∅` at compile time on the outer-
35878 // `Sexp` cache-key `u8` vocabulary, peer to
35879 // `assert_u8_array_within_u8_finite_set` on the (u8) row of the
35880 // (subset, disjointness) 2-corner face of the (contract-shape)
35881 // axis AND row-dual peer to `assert_char_arrays_disjoint` on the
35882 // (element-type) axis of the SAME (contract-shape) column. The
35883 // runtime test surface pins each of the helper's arms (accept-
35884 // both-empty, accept-either-empty, accept-disjoint-singletons,
35885 // accept-each-family-wide-substrate-pair, accept-arg-order-
35886 // symmetry, reject-single-collision, reject-terminal-a-collision,
35887 // reject-terminal-b-collision, panic-message-provenance on the
35888 // U8-DISJOINTNESS-VIOLATION axis) so a regression that silently
35889 // weakened the helper on ANY arm is caught by the helper's OWN
35890 // test surface rather than only surfacing as a false-positive on
35891 // some future disjoint `[u8; N] × [u8; M]` pair's compound pin.
35892
35893 #[test]
35894 fn assert_u8_arrays_disjoint_accepts_both_empty_arrays() {
35895 // Both arrays empty at the `[u8; 0] × [u8; 0]` corner —
35896 // vacuously disjoint (no `(i, j)` pair exists to test). The
35897 // compile-time `const _: () = assert_u8_arrays_disjoint(&[],
35898 // &[]);` would land on this arm, so the runtime call MUST
35899 // return normally. Turbofish binding required because there's
35900 // no other cue for the const parameters on the empty array
35901 // literals. Sibling posture to
35902 // `assert_char_arrays_disjoint_accepts_both_empty_arrays` on
35903 // the (char) row-dual peer — the two share the trivial-arity
35904 // arm across the (element-type × contract-shape) 4-corner
35905 // face at the (disjointness) column.
35906 assert_u8_arrays_disjoint::<0, 0>(&[], &[]);
35907 }
35908
35909 #[test]
35910 fn assert_u8_arrays_disjoint_accepts_either_side_empty() {
35911 // Either side empty at the `[u8; 0] × [u8; M]` OR
35912 // `[u8; N] × [u8; 0]` corners — vacuously disjoint (the
35913 // OUTER `while i < N` OR the INNER `while j < M` sweep is a
35914 // no-op). Pins BOTH SIDES of the (a-empty, b-empty) 2-corner
35915 // sub-face on the trivial-arity axis so a regression that
35916 // narrowed the sweep to only-a-non-empty OR only-b-non-empty
35917 // fails HERE at the empty-side corner rather than at a
35918 // distant false-positive.
35919 assert_u8_arrays_disjoint::<0, 3>(&[], &[0, 1, 2]);
35920 assert_u8_arrays_disjoint::<3, 0>(&[0, 1, 2], &[]);
35921 }
35922
35923 #[test]
35924 fn assert_u8_arrays_disjoint_accepts_disjoint_singletons() {
35925 // Singleton `a = [K]` and singleton `b = [L]` with `K != L`
35926 // at the `[u8; 1] × [u8; 1]` corner — the minimal non-empty
35927 // disjointness relation. Pins the INNER `if a[i] != b[j]`
35928 // gate returns without panicking on a single distinct-pair
35929 // check. A regression that flipped the equality direction
35930 // (`==` vs `!=`) would silently reject every disjoint
35931 // singleton pair.
35932 assert_u8_arrays_disjoint(&[0u8], &[1u8]);
35933 }
35934
35935 #[test]
35936 fn assert_u8_arrays_disjoint_accepts_each_family_wide_substrate_pair() {
35937 // Runtime cross-check that the TWO (a, b) `[u8; N] × [u8; M]`
35938 // pairs the substrate's module-level `const _` witnesses pin
35939 // at COMPILE time are proper DISJOINTNESS embeddings at
35940 // runtime too. The pairs enforce the theorem at TWO stages
35941 // of the toolchain: the const witnesses fire FIRST at `cargo
35942 // check` (through the two module-level `const _: () =
35943 // assert_u8_arrays_disjoint::<N, M>(...)` lines), this
35944 // runtime pin catches the drift at `cargo test` as a safety
35945 // net. Sibling posture to
35946 // `assert_char_arrays_disjoint_accepts_each_family_wide_substrate_pair`
35947 // which sweeps the FIVE (a, b) PAIRS at the (char) row's
35948 // DISJOINTNESS axis; this pin sweeps the two (a, b) PAIRS
35949 // at the (u8) row's DISJOINTNESS axis on the SAME contract-
35950 // shape column.
35951 assert_u8_arrays_disjoint::<2, 4>(
35952 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
35953 &QuoteForm::HASH_DISCRIMINATORS,
35954 );
35955 assert_u8_arrays_disjoint::<2, 2>(
35956 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
35957 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
35958 );
35959 }
35960
35961 #[test]
35962 fn assert_u8_arrays_disjoint_is_symmetric_in_argument_order() {
35963 // SYMMETRY PIN: swapping the two arguments produces the SAME
35964 // verdict. Pins that the disjointness relation is truly
35965 // symmetric across the two array arguments (the helper's
35966 // nested-sweep implementation does NOT gratuitously depend on
35967 // argument order). A regression that narrowed the sweep to
35968 // `for i in a { if !b.contains(a[i]) }` (subset-shape, not
35969 // disjointness-shape) would fail the swap on one direction
35970 // only. Runs each substrate pair BOTH ways. Sibling posture
35971 // to `assert_char_arrays_disjoint_is_symmetric_in_argument_order`
35972 // on the (char) row-dual peer.
35973 assert_u8_arrays_disjoint(
35974 &QuoteForm::HASH_DISCRIMINATORS,
35975 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
35976 );
35977 assert_u8_arrays_disjoint(
35978 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
35979 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
35980 );
35981 }
35982
35983 #[test]
35984 #[should_panic(expected = "U8-DISJOINTNESS-VIOLATION")]
35985 fn assert_u8_arrays_disjoint_panics_at_runtime_on_collision() {
35986 // NEGATIVE PIN — U8-DISJOINTNESS-VIOLATION corner: two arrays
35987 // sharing a single byte MUST panic at runtime with the
35988 // U8-DISJOINTNESS-VIOLATION-named message. Pins the helper's
35989 // OWN reject arm — a regression that silently returned
35990 // without panicking on a cross-array collision would slip
35991 // through the compile-time witnesses' failure mode too. The
35992 // shared byte `0u8` is intentionally placed at the FIRST
35993 // position of BOTH arrays to pin the initial-position drift
35994 // mode.
35995 assert_u8_arrays_disjoint(&[0u8, 1u8], &[0u8, 2u8]);
35996 }
35997
35998 #[test]
35999 #[should_panic(expected = "U8-DISJOINTNESS-VIOLATION")]
36000 fn assert_u8_arrays_disjoint_panics_at_runtime_on_terminal_a_collision() {
36001 // NEGATIVE PIN — terminal-position drift on the OUTER `a`
36002 // side: a shared byte at the LAST position of `a` MUST panic
36003 // — pins that the outer `while i < N` loop reaches `i = N -
36004 // 1` (else the terminal drift on `a` would slip through). A
36005 // regression that narrowed the outer sweep to `while i < N
36006 // - 1` (off-by-one on the OUTER bound) would silently accept
36007 // this pair.
36008 assert_u8_arrays_disjoint(&[10u8, 20u8, 30u8], &[30u8]);
36009 }
36010
36011 #[test]
36012 #[should_panic(expected = "U8-DISJOINTNESS-VIOLATION")]
36013 fn assert_u8_arrays_disjoint_panics_at_runtime_on_terminal_b_collision() {
36014 // NEGATIVE PIN — terminal-position drift on the INNER `b`
36015 // side: a shared byte at the LAST position of `b` MUST panic
36016 // — pins that the inner `while j < M` loop reaches `j = M -
36017 // 1` (else the terminal drift on `b` would slip through). A
36018 // regression that narrowed the inner sweep to `while j < M
36019 // - 1` (off-by-one on the INNER bound) would silently accept
36020 // this pair. Sibling posture to the outer-terminal pin above
36021 // — the two pins together bind the terminal bounds on BOTH
36022 // loops.
36023 assert_u8_arrays_disjoint(&[30u8], &[10u8, 20u8, 30u8]);
36024 }
36025
36026 #[test]
36027 fn assert_u8_arrays_disjoint_panic_message_names_the_helper_and_u8_disjointness_violation_axis()
36028 {
36029 // PANIC-MESSAGE PROVENANCE PIN — U8-DISJOINTNESS-VIOLATION
36030 // arm: the panic message MUST begin with the helper's own
36031 // name AND identify the failed AXIS as "U8-DISJOINTNESS-
36032 // VIOLATION" so downstream diagnostics route the drift back
36033 // to (a) the helper by string search on
36034 // `"assert_u8_arrays_disjoint"` and (b) the axis by string
36035 // search on `"U8-DISJOINTNESS-VIOLATION"`. Sibling posture to
36036 // `assert_char_arrays_disjoint_panic_message_names_the_helper_and_char_disjointness_violation_axis`
36037 // on the (char) row-dual peer's provenance pin AND to
36038 // `assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis`
36039 // on the (u8, subset) contract-orthogonal peer's provenance
36040 // pin — the three pins together bind the (helper, failed-
36041 // axis) provenance triple across the (element-type × contract-
36042 // shape) 2×2 = 4-corner face's three currently-populated
36043 // corners with matching provenance-preservation discipline.
36044 // The axis-provenance string `"U8-DISJOINTNESS-VIOLATION"` is
36045 // chosen DISTINCT from EVERY sibling helper's axis vocabulary
36046 // (`"duplicate"` on the ARRAY-side pairwise-distinct sibling;
36047 // `"CHAR-DISJOINTNESS-VIOLATION"` on the (char) row-dual
36048 // DISJOINTNESS sibling; `"CHAR-SUBSET-VIOLATION"` on the
36049 // (char) SUBSET-embedding sibling; `"SUBSET-VIOLATION"` on
36050 // the (u8) finite-set SUBSET-only sibling; `"RANGE-SUBSET-
36051 // VIOLATION"` on the (u8) range SUBSET-only sibling;
36052 // `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the (u8) covers-
36053 // finite-set sibling; `"OUT-OF-RANGE"` / `"MISSING"` on the
36054 // (u8) covers-inclusive-range sibling; `"ARITY-MISMATCH"` on
36055 // both (u8) `_permutes_*` compound helpers; `"SET-NOT-
36056 // PAIRWISE-DISTINCT"` on the (u8) SET-side well-formedness
36057 // sibling) so a diagnostic that names the failed axis routes
36058 // UNAMBIGUOUSLY to (a) this specific u8 DISJOINTNESS helper.
36059 let outcome = std::panic::catch_unwind(|| {
36060 assert_u8_arrays_disjoint(&[0u8, 1u8], &[0u8, 2u8]);
36061 });
36062 let payload = outcome.expect_err(
36063 "assert_u8_arrays_disjoint must panic on a cross-array \
36064 collision — the reject-collision arm is the sole U8-\
36065 DISJOINTNESS-VIOLATION failure mode of the helper",
36066 );
36067 let msg = payload
36068 .downcast_ref::<&'static str>()
36069 .map(|s| (*s).to_owned())
36070 .or_else(|| payload.downcast_ref::<String>().cloned())
36071 .expect(
36072 "assert_u8_arrays_disjoint panic payload must be a \
36073 static &str or String",
36074 );
36075 assert!(
36076 msg.contains("assert_u8_arrays_disjoint"),
36077 "assert_u8_arrays_disjoint panic message {msg:?} must \
36078 name the helper for provenance-preserving failure \
36079 diagnostics",
36080 );
36081 assert!(
36082 msg.contains("U8-DISJOINTNESS-VIOLATION"),
36083 "assert_u8_arrays_disjoint panic message {msg:?} must \
36084 name the failed AXIS (\"U8-DISJOINTNESS-VIOLATION\") \
36085 for axis-provenance-preserving failure diagnostics",
36086 );
36087 }
36088
36089 // ── `assert_str_array_pairwise_distinct` — the `&'static str`
36090 // element-type sibling of `assert_char_array_pairwise_distinct`.
36091 // Sibling posture: same runtime-test surface (accept-empty,
36092 // accept-singleton, accept-every-family-wide-substrate-array,
36093 // reject-binary, reject-non-adjacent, reject-terminal, panic-
36094 // message-provenance) restricted to the `&'static str` element
36095 // type. A regression that silently weakens the helper (e.g.
36096 // dropping the byte-length gate, flipping `!=` to `==`, or
36097 // returning early on collision) is caught by the helper's OWN
36098 // test surface rather than only surfacing as a false-positive
36099 // on some future string-typed array's distinctness pin.
36100
36101 #[test]
36102 fn assert_str_array_pairwise_distinct_accepts_the_empty_array() {
36103 // Empty array — vacuously pairwise distinct (no pair to
36104 // collide). The compile-time `const _: () =
36105 // assert_str_array_pairwise_distinct(&EMPTY);` would land on
36106 // this arm, so the runtime call MUST return normally.
36107 assert_str_array_pairwise_distinct::<0>(&[]);
36108 }
36109
36110 #[test]
36111 fn assert_str_array_pairwise_distinct_accepts_singleton_arrays() {
36112 // Singleton array — vacuously pairwise distinct (only one
36113 // element, no pair). Cross-arity coverage on the
36114 // `[&'static str; 1]` corner of the const-N generic.
36115 assert_str_array_pairwise_distinct(&["a"]);
36116 assert_str_array_pairwise_distinct(&[Atom::TRUE_LITERAL]);
36117 }
36118
36119 #[test]
36120 fn assert_str_array_pairwise_distinct_accepts_every_family_wide_substrate_array() {
36121 // Runtime cross-check that the SAME five arrays the module-
36122 // level `const _: () = ...` witnesses cover at COMPILE time
36123 // are pairwise distinct. A regression that removes ONE of
36124 // the `const _` witnesses would still leave THIS runtime pin
36125 // as a safety net; the const witness fires FIRST at `cargo
36126 // check`, this runtime pin catches the collision at `cargo
36127 // test`. The pair enforces the theorem at TWO stages of the
36128 // toolchain.
36129 assert_str_array_pairwise_distinct(&Atom::BOOL_LITERALS);
36130 assert_str_array_pairwise_distinct(&AtomKind::LABELS);
36131 assert_str_array_pairwise_distinct(&QuoteForm::PREFIXES);
36132 assert_str_array_pairwise_distinct(&QuoteForm::IAC_FORGE_TAGS);
36133 assert_str_array_pairwise_distinct(&QuoteForm::LABELS);
36134 }
36135
36136 #[test]
36137 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
36138 fn assert_str_array_pairwise_distinct_panics_at_runtime_on_binary_collision() {
36139 // NEGATIVE PIN — binary corner: a two-element array carrying
36140 // the same string twice MUST panic at runtime (the const-eval
36141 // panic surfaces normally when the function is invoked from a
36142 // runtime context, not just a `const _` context). Pins the
36143 // helper's OWN reject-collision arm — a regression that
36144 // silently returned without panicking on a duplicate would
36145 // slip through the compile-time witnesses' failure mode too.
36146 assert_str_array_pairwise_distinct(&["dup", "dup"]);
36147 }
36148
36149 #[test]
36150 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
36151 fn assert_str_array_pairwise_distinct_panics_at_runtime_on_non_adjacent_collision() {
36152 // NEGATIVE PIN — non-adjacent corner: the collision fires on
36153 // ANY (i, j) pair with i < j, not just the adjacent (0, 1)
36154 // corner. Pins the nested-loop shape of the helper — a
36155 // regression that walked ONLY the adjacent pairs (i.e., swept
36156 // `while i + 1 < N { if arr[i] == arr[i+1] { panic } … }`)
36157 // would silently accept `["a", "b", "a"]` (non-adjacent
36158 // collision at positions 0 and 2), missing the contract.
36159 assert_str_array_pairwise_distinct(&["a", "b", "a"]);
36160 }
36161
36162 #[test]
36163 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
36164 fn assert_str_array_pairwise_distinct_panics_at_runtime_on_terminal_collision() {
36165 // NEGATIVE PIN — terminal corner: the collision at the LAST
36166 // pair (positions N-2 and N-1) MUST also fire. Pins the outer
36167 // `while i < N` bound — a regression that walked `while i <
36168 // N - 1` (dropping the last row) would silently accept a
36169 // collision at the tail.
36170 assert_str_array_pairwise_distinct(&["a", "b", "c", "d", "d"]);
36171 }
36172
36173 #[test]
36174 fn assert_str_array_pairwise_distinct_rejects_length_zero_collision() {
36175 // POSITIVE-ORTHOGONAL PIN — the byte-length gate at
36176 // `str_bytes_equal`: two empty strings must be flagged as
36177 // EQUAL (both are the zero-length byte sequence). A
36178 // regression that mishandled `a.len() == 0 && b.len() == 0`
36179 // (e.g. by returning `false` when both lengths are 0, or by
36180 // panicking on the zero-length index) would silently accept
36181 // a `["", ""]` duplicate. Pins the vacuous-length corner of
36182 // the byte-equality helper via the outer helper's runtime
36183 // panic surface.
36184 let outcome = std::panic::catch_unwind(|| {
36185 assert_str_array_pairwise_distinct(&["", ""]);
36186 });
36187 outcome.expect_err(
36188 "assert_str_array_pairwise_distinct must panic on a \
36189 duplicate at the zero-length string corner — the \
36190 `str_bytes_equal` helper's `a.len() == b.len()` gate \
36191 holds vacuously at length 0, so the byte-walk falls \
36192 through to a positive equality verdict",
36193 );
36194 }
36195
36196 #[test]
36197 fn assert_str_array_pairwise_distinct_accepts_prefix_pair_without_collision() {
36198 // POSITIVE-ORTHOGONAL PIN — the byte-length gate at
36199 // `str_bytes_equal`: two strings where one is a strict
36200 // prefix of the other must be flagged as DISTINCT. A
36201 // regression that dropped the `a.len() != b.len()` gate
36202 // (e.g. by comparing bytes only up to the shorter length)
36203 // would silently accept `["ab", "abc"]` as equal. This pin
36204 // fires the outer helper on a prefix pair; the runtime call
36205 // MUST return normally.
36206 assert_str_array_pairwise_distinct(&["ab", "abc"]);
36207 assert_str_array_pairwise_distinct(&["", "a"]);
36208 }
36209
36210 #[test]
36211 fn assert_str_array_pairwise_distinct_panic_message_names_the_helper() {
36212 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST begin
36213 // with the helper's own name so downstream diagnostics
36214 // (`cargo check` const-eval error output, test-suite failure
36215 // reports) route the drift back to the helper by string
36216 // search — the family-wide contract's failure mode surfaces
36217 // as an identifiable panic-message prefix rather than as an
36218 // opaque const-eval error. Sibling posture to the runtime
36219 // pairwise-distinctness tests that name the ARRAY in their
36220 // failure message; this pin names the HELPER.
36221 let outcome = std::panic::catch_unwind(|| {
36222 assert_str_array_pairwise_distinct(&["x", "x"]);
36223 });
36224 let payload = outcome.expect_err(
36225 "assert_str_array_pairwise_distinct must panic on a \
36226 duplicate — the reject-collision arm is the point of \
36227 the helper",
36228 );
36229 let msg = payload
36230 .downcast_ref::<&'static str>()
36231 .map(|s| (*s).to_owned())
36232 .or_else(|| payload.downcast_ref::<String>().cloned())
36233 .expect(
36234 "assert_str_array_pairwise_distinct panic payload \
36235 must be a static &str or String",
36236 );
36237 assert!(
36238 msg.contains("assert_str_array_pairwise_distinct"),
36239 "assert_str_array_pairwise_distinct panic message \
36240 {msg:?} must name the helper for provenance-preserving \
36241 failure diagnostics",
36242 );
36243 }
36244
36245 #[test]
36246 fn assert_str_array_all_nonempty_accepts_the_empty_array() {
36247 // Empty array — vacuously all-nonempty (no entry to be empty).
36248 // The compile-time `const _: () = assert_str_array_all_nonempty
36249 // (&EMPTY);` would land on this arm, so the runtime call MUST
36250 // return normally.
36251 assert_str_array_all_nonempty::<0>(&[]);
36252 }
36253
36254 #[test]
36255 fn assert_str_array_all_nonempty_accepts_nonempty_singleton_arrays() {
36256 // Singleton array carrying a nonempty entry — the sole entry
36257 // clears the length-gate. Cross-arity coverage on the
36258 // `[&'static str; 1]` corner of the const-N generic.
36259 assert_str_array_all_nonempty(&["a"]);
36260 assert_str_array_all_nonempty(&[Atom::TRUE_LITERAL]);
36261 }
36262
36263 #[test]
36264 fn assert_str_array_all_nonempty_accepts_every_family_wide_substrate_array() {
36265 // Runtime cross-check that the SAME five arrays the module-
36266 // level `const _: () = ...` witnesses cover at COMPILE time
36267 // are all-nonempty. Sibling posture to the runtime
36268 // `_pairwise_distinct` cross-check above — the two together
36269 // pin BOTH the INJECTIVITY axis AND the NONEMPTY-CARDINALITY-
36270 // LOWER-BOUND axis on the SAME five arrays, at TWO stages of
36271 // the toolchain (compile-time `const _` line + this runtime
36272 // safety-net).
36273 assert_str_array_all_nonempty(&Atom::BOOL_LITERALS);
36274 assert_str_array_all_nonempty(&AtomKind::LABELS);
36275 assert_str_array_all_nonempty(&QuoteForm::PREFIXES);
36276 assert_str_array_all_nonempty(&QuoteForm::IAC_FORGE_TAGS);
36277 assert_str_array_all_nonempty(&QuoteForm::LABELS);
36278 }
36279
36280 #[test]
36281 #[should_panic(expected = "assert_str_array_all_nonempty")]
36282 fn assert_str_array_all_nonempty_panics_at_runtime_on_singleton_empty() {
36283 // NEGATIVE PIN — singleton corner: a one-element array
36284 // carrying the empty string MUST panic. Pins the helper's own
36285 // reject-empty arm on the smallest possible array shape.
36286 assert_str_array_all_nonempty(&[""]);
36287 }
36288
36289 #[test]
36290 #[should_panic(expected = "assert_str_array_all_nonempty")]
36291 fn assert_str_array_all_nonempty_panics_at_runtime_on_head_empty() {
36292 // NEGATIVE PIN — head corner: the empty entry at position 0
36293 // MUST fire even when subsequent entries are nonempty. Pins
36294 // the outer sweep's inclusive-start behavior — a regression
36295 // that walked `while i < N { … i += 1 }` from an off-by-one
36296 // start (`i = 1`) would silently accept a leading `""` entry.
36297 assert_str_array_all_nonempty(&["", "a", "b"]);
36298 }
36299
36300 #[test]
36301 #[should_panic(expected = "assert_str_array_all_nonempty")]
36302 fn assert_str_array_all_nonempty_panics_at_runtime_on_interior_empty() {
36303 // NEGATIVE PIN — interior corner: the empty entry at a
36304 // strictly-interior position MUST fire. Pins the outer
36305 // sweep's non-early-exit behavior at the head-arm — a
36306 // regression that returned `Ok` on the first nonempty entry
36307 // (bailing out of the sweep prematurely) would silently
36308 // accept an interior `""` entry.
36309 assert_str_array_all_nonempty(&["a", "", "b"]);
36310 }
36311
36312 #[test]
36313 #[should_panic(expected = "assert_str_array_all_nonempty")]
36314 fn assert_str_array_all_nonempty_panics_at_runtime_on_tail_empty() {
36315 // NEGATIVE PIN — tail corner: the empty entry at position
36316 // `N - 1` MUST fire. Pins the outer `while i < N` upper bound
36317 // — a regression that walked `while i < N - 1` (dropping the
36318 // last slot) would silently accept a trailing `""` entry.
36319 assert_str_array_all_nonempty(&["a", "b", "c", ""]);
36320 }
36321
36322 #[test]
36323 fn assert_str_array_all_nonempty_rejects_the_all_empty_array() {
36324 // POSITIVE-ORTHOGONAL PIN — the ALL-empty corner: an array
36325 // whose every entry is `""` fires the helper on the FIRST
36326 // entry (the head-arm). Confirms the helper does not silently
36327 // accept an array whose every entry alias-collapses onto the
36328 // zero-length byte sequence — a corner that composes with
36329 // `assert_str_array_pairwise_distinct_rejects_length_zero_
36330 // collision` (that pin rejects `["", ""]` on the pairwise-
36331 // distinctness axis; this pin rejects `["", ""]` on the
36332 // NONEMPTY axis — the two contracts pin the `""`-repeat
36333 // failure mode at BOTH the INJECTIVITY axis AND the
36334 // NONEMPTY axis).
36335 let outcome = std::panic::catch_unwind(|| {
36336 assert_str_array_all_nonempty(&["", ""]);
36337 });
36338 outcome.expect_err(
36339 "assert_str_array_all_nonempty must panic on an array \
36340 whose every entry is `\"\"` — the head-arm fires on the \
36341 zero-length entry at position 0",
36342 );
36343 }
36344
36345 #[test]
36346 fn assert_str_array_all_nonempty_panic_message_names_the_helper_and_axis() {
36347 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST begin
36348 // with the helper's own name AND name the failed axis as
36349 // `"STR-EMPTY-ENTRY"` (chosen DISTINCT from every sibling
36350 // helper's axis vocabulary: `"duplicate"` on the pairwise-
36351 // distinct sibling; `"STR-DISJOINTNESS-VIOLATION"` on the
36352 // arrays-disjoint sibling; `"STR-SUBSET-VIOLATION"` on the
36353 // within-finite-set sibling) so a diagnostic that names the
36354 // failed axis routes UNAMBIGUOUSLY to THIS specific helper.
36355 let outcome = std::panic::catch_unwind(|| {
36356 assert_str_array_all_nonempty(&[""]);
36357 });
36358 let payload = outcome.expect_err(
36359 "assert_str_array_all_nonempty must panic on an empty \
36360 entry — the reject-empty arm is the point of the helper",
36361 );
36362 let msg = payload
36363 .downcast_ref::<&'static str>()
36364 .map(|s| (*s).to_owned())
36365 .or_else(|| payload.downcast_ref::<String>().cloned())
36366 .expect(
36367 "assert_str_array_all_nonempty panic payload must be \
36368 a static &str or String",
36369 );
36370 assert!(
36371 msg.contains("assert_str_array_all_nonempty"),
36372 "assert_str_array_all_nonempty panic message {msg:?} must \
36373 name the helper for provenance-preserving failure \
36374 diagnostics",
36375 );
36376 assert!(
36377 msg.contains("STR-EMPTY-ENTRY"),
36378 "assert_str_array_all_nonempty panic message {msg:?} must \
36379 name the failed axis as `STR-EMPTY-ENTRY` DISTINCT from \
36380 every sibling helper's axis vocabulary",
36381 );
36382 // Trailing gate — the sibling `_all_ascii` axis vocabulary
36383 // (`STR-NON-ASCII-ENTRY`) must NOT appear in the NONEMPTY
36384 // helper's panic message. Guards against a copy-paste
36385 // regression that drifted the two helpers' axis-provenance
36386 // strings onto the same slot.
36387 assert!(
36388 !msg.contains("STR-NON-ASCII-ENTRY"),
36389 "assert_str_array_all_nonempty panic message {msg:?} \
36390 must NOT name the ASCII-sibling axis — the two per-\
36391 entry helpers must keep their axis-provenance strings \
36392 lexically distinct",
36393 );
36394 }
36395
36396 // ── assert_str_array_all_ascii — the per-entry ASCII-BYTE-RANGE
36397 // gate sibling of `assert_str_array_all_nonempty` on the
36398 // (`&'static str`) row's (per-entry × contract-shape) axis. ──
36399
36400 #[test]
36401 fn assert_str_array_all_ascii_accepts_the_empty_array() {
36402 // Empty array — vacuously all-ASCII (no entry to fail the
36403 // byte-range gate). The compile-time `const _: () =
36404 // assert_str_array_all_ascii(&EMPTY);` would land on this
36405 // arm, so the runtime call MUST return normally.
36406 assert_str_array_all_ascii::<0>(&[]);
36407 }
36408
36409 #[test]
36410 fn assert_str_array_all_ascii_accepts_ascii_singleton_arrays() {
36411 // Singleton array carrying an all-ASCII entry — the sole
36412 // entry clears the byte-range gate. Cross-arity coverage on
36413 // the `[&'static str; 1]` corner of the const-N generic.
36414 assert_str_array_all_ascii(&["a"]);
36415 assert_str_array_all_ascii(&[Atom::TRUE_LITERAL]);
36416 }
36417
36418 #[test]
36419 fn assert_str_array_all_ascii_accepts_the_empty_entry() {
36420 // Vacuous-inner-loop corner — a zero-length entry has no
36421 // byte to fail the range gate, so the helper MUST accept.
36422 // Positive-orthogonal pin against the NONEMPTY sibling — a
36423 // regression that fused the two contracts into one (rejecting
36424 // `""` on the ASCII sweep) would fail this pin. The two
36425 // per-entry contracts stay independent axes on the SAME row.
36426 assert_str_array_all_ascii(&[""]);
36427 assert_str_array_all_ascii(&["", "a", ""]);
36428 }
36429
36430 #[test]
36431 fn assert_str_array_all_ascii_accepts_every_family_wide_substrate_array() {
36432 // Runtime cross-check that the SAME five arrays the module-
36433 // level `const _: () = ...` witnesses cover at COMPILE time
36434 // are all-ASCII. Sibling posture to the runtime
36435 // `_pairwise_distinct` + `_all_nonempty` cross-checks — the
36436 // three together pin BOTH the INJECTIVITY axis AND the
36437 // NONEMPTY-CARDINALITY-LOWER-BOUND axis AND the ASCII-BYTE-
36438 // RANGE axis on the SAME five arrays, at TWO stages of the
36439 // toolchain (compile-time `const _` line + this runtime
36440 // safety-net).
36441 assert_str_array_all_ascii(&Atom::BOOL_LITERALS);
36442 assert_str_array_all_ascii(&AtomKind::LABELS);
36443 assert_str_array_all_ascii(&QuoteForm::PREFIXES);
36444 assert_str_array_all_ascii(&QuoteForm::IAC_FORGE_TAGS);
36445 assert_str_array_all_ascii(&QuoteForm::LABELS);
36446 }
36447
36448 #[test]
36449 fn assert_str_array_all_ascii_accepts_the_ascii_boundary_byte() {
36450 // Boundary-inclusive pin on the ASCII byte range's upper
36451 // edge — byte `0x7F` (DEL, the last ASCII byte) MUST be
36452 // accepted. Guards against an off-by-one regression that
36453 // walked `bytes[j] >= 0x7F` and spuriously rejected the
36454 // sole `0x7F` character. Together with the negative pins
36455 // below (which fire on `0x80` — the first non-ASCII byte),
36456 // the two pin the byte-range boundary at both edges.
36457 assert_str_array_all_ascii(&["\u{7F}"]);
36458 }
36459
36460 #[test]
36461 #[should_panic(expected = "assert_str_array_all_ascii")]
36462 fn assert_str_array_all_ascii_panics_at_runtime_on_singleton_non_ascii() {
36463 // NEGATIVE PIN — singleton corner: a one-element array
36464 // carrying a non-ASCII entry MUST panic. Pins the helper's
36465 // own reject-non-ascii arm on the smallest possible array
36466 // shape. The two-byte UTF-8 sequence `"é"` (0xC3 0xA9) fires
36467 // on the leading high-byte `0xC3`.
36468 assert_str_array_all_ascii(&["é"]);
36469 }
36470
36471 #[test]
36472 #[should_panic(expected = "assert_str_array_all_ascii")]
36473 fn assert_str_array_all_ascii_panics_at_runtime_on_head_non_ascii() {
36474 // NEGATIVE PIN — head corner: the non-ASCII entry at
36475 // position 0 MUST fire even when subsequent entries are
36476 // ASCII. Pins the outer sweep's inclusive-start behavior
36477 // — a regression that walked `while i < N { … i += 1 }`
36478 // from an off-by-one start (`i = 1`) would silently accept
36479 // a leading non-ASCII entry.
36480 assert_str_array_all_ascii(&["café", "a", "b"]);
36481 }
36482
36483 #[test]
36484 #[should_panic(expected = "assert_str_array_all_ascii")]
36485 fn assert_str_array_all_ascii_panics_at_runtime_on_interior_non_ascii() {
36486 // NEGATIVE PIN — interior corner: the non-ASCII entry at a
36487 // strictly-interior position MUST fire. Pins the outer
36488 // sweep's non-early-exit behavior at the head-arm — a
36489 // regression that returned `Ok` on the first ASCII entry
36490 // (bailing out of the sweep prematurely) would silently
36491 // accept an interior non-ASCII entry.
36492 assert_str_array_all_ascii(&["a", "café", "b"]);
36493 }
36494
36495 #[test]
36496 #[should_panic(expected = "assert_str_array_all_ascii")]
36497 fn assert_str_array_all_ascii_panics_at_runtime_on_tail_non_ascii() {
36498 // NEGATIVE PIN — tail corner: the non-ASCII entry at
36499 // position `N - 1` MUST fire. Pins the outer `while i < N`
36500 // upper bound — a regression that walked `while i < N - 1`
36501 // (dropping the last slot) would silently accept a trailing
36502 // non-ASCII entry.
36503 assert_str_array_all_ascii(&["a", "b", "c", "café"]);
36504 }
36505
36506 #[test]
36507 #[should_panic(expected = "assert_str_array_all_ascii")]
36508 fn assert_str_array_all_ascii_panics_at_runtime_on_interior_byte_non_ascii() {
36509 // NEGATIVE PIN — interior-byte corner: the non-ASCII byte at
36510 // a strictly-interior position WITHIN a single entry MUST
36511 // fire. Pins the inner `while j < bytes.len()` sweep's
36512 // non-early-exit behavior — a regression that returned on
36513 // the first ASCII byte within an entry (bailing out of the
36514 // inner sweep prematurely at `bytes[0] <= 0x7F`) would
36515 // silently accept a non-ASCII byte in a strictly-interior
36516 // slot of the entry.
36517 assert_str_array_all_ascii(&["asdéf"]);
36518 }
36519
36520 #[test]
36521 #[should_panic(expected = "assert_str_array_all_ascii")]
36522 fn assert_str_array_all_ascii_panics_on_the_first_non_ascii_byte() {
36523 // NEGATIVE PIN — first-non-ASCII-byte boundary: byte `0x80`
36524 // (the smallest non-ASCII byte) MUST fire. Guards against
36525 // an off-by-one regression that walked `bytes[j] > 0x80`
36526 // (dropping `0x80` from the reject set). Together with
36527 // `_accepts_the_ascii_boundary_byte` (which pins `0x7F`
36528 // acceptance), the two pin the byte-range boundary at both
36529 // edges. UTF-8 lowest continuation byte in isolation is not
36530 // well-formed, so we use `"\u{80}"` — which is the two-byte
36531 // sequence `0xC2 0x80` — either byte is `> 0x7F` so the
36532 // sweep fires on the leading byte `0xC2` regardless.
36533 assert_str_array_all_ascii(&["\u{80}"]);
36534 }
36535
36536 #[test]
36537 fn assert_str_array_all_ascii_rejects_the_all_non_ascii_array() {
36538 // POSITIVE-ORTHOGONAL PIN — the ALL-non-ASCII corner: an
36539 // array whose every entry contains a non-ASCII byte fires
36540 // the helper on the FIRST entry (the head-arm). Confirms
36541 // the helper does not silently accept an array whose every
36542 // entry ships non-ASCII bytes.
36543 let outcome = std::panic::catch_unwind(|| {
36544 assert_str_array_all_ascii(&["café", "über"]);
36545 });
36546 outcome.expect_err(
36547 "assert_str_array_all_ascii must panic on an array whose \
36548 every entry carries a non-ASCII byte — the head-arm \
36549 fires on the first non-ASCII byte at position 0",
36550 );
36551 }
36552
36553 #[test]
36554 fn assert_str_array_all_ascii_panic_message_names_the_helper_and_axis() {
36555 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST
36556 // begin with the helper's own name AND name the failed axis
36557 // as `"STR-NON-ASCII-ENTRY"` (chosen DISTINCT from every
36558 // sibling helper's axis vocabulary: `"duplicate"` on the
36559 // pairwise-distinct sibling; `"STR-EMPTY-ENTRY"` on the
36560 // per-entry NONEMPTY sibling; `"STR-DISJOINTNESS-VIOLATION"`
36561 // on the arrays-disjoint sibling; `"STR-SUBSET-VIOLATION"`
36562 // on the within-finite-set sibling) so a diagnostic that
36563 // names the failed axis routes UNAMBIGUOUSLY to THIS
36564 // specific ASCII helper.
36565 let outcome = std::panic::catch_unwind(|| {
36566 assert_str_array_all_ascii(&["é"]);
36567 });
36568 let payload = outcome.expect_err(
36569 "assert_str_array_all_ascii must panic on a non-ASCII \
36570 entry — the reject-non-ascii arm is the point of the \
36571 helper",
36572 );
36573 let msg = payload
36574 .downcast_ref::<&'static str>()
36575 .map(|s| (*s).to_owned())
36576 .or_else(|| payload.downcast_ref::<String>().cloned())
36577 .expect(
36578 "assert_str_array_all_ascii panic payload must be a \
36579 static &str or String",
36580 );
36581 assert!(
36582 msg.contains("assert_str_array_all_ascii"),
36583 "assert_str_array_all_ascii panic message {msg:?} must \
36584 name the helper for provenance-preserving failure \
36585 diagnostics",
36586 );
36587 assert!(
36588 msg.contains("STR-NON-ASCII-ENTRY"),
36589 "assert_str_array_all_ascii panic message {msg:?} must \
36590 name the failed axis as `STR-NON-ASCII-ENTRY` DISTINCT \
36591 from every sibling helper's axis vocabulary",
36592 );
36593 assert!(
36594 !msg.contains("STR-EMPTY-ENTRY"),
36595 "assert_str_array_all_ascii panic message {msg:?} must \
36596 NOT name the NONEMPTY-sibling axis — the two per-entry \
36597 helpers must keep their axis-provenance strings \
36598 lexically distinct",
36599 );
36600 }
36601
36602 #[test]
36603 fn str_bytes_equal_accepts_the_empty_pair() {
36604 // Direct pin on `str_bytes_equal`'s zero-length corner —
36605 // both inputs are the empty byte sequence, so the helper
36606 // MUST return `true`. Verifies the `a.len() == b.len()`
36607 // gate does not spuriously reject at `len == 0`.
36608 assert!(str_bytes_equal("", ""));
36609 }
36610
36611 #[test]
36612 fn str_bytes_equal_rejects_the_prefix_pair() {
36613 // Direct pin on `str_bytes_equal`'s length-gate: a strict
36614 // prefix pair MUST be rejected. Guards against a regression
36615 // that dropped the length gate and compared bytes only up
36616 // to the shorter length.
36617 assert!(!str_bytes_equal("ab", "abc"));
36618 assert!(!str_bytes_equal("abc", "ab"));
36619 }
36620
36621 #[test]
36622 fn str_bytes_equal_accepts_the_identical_pair() {
36623 // Direct pin on `str_bytes_equal`'s positive-match arm —
36624 // two identical strings MUST be flagged as equal at every
36625 // canonical shape the substrate carries (bool literal,
36626 // atomic-kind label, quote-family prefix). Sibling posture
36627 // to the `_accepts_every_family_wide_substrate_array` pin
36628 // on the outer helper: this pin fires the inner helper on
36629 // the SAME string values that back the outer witnesses'
36630 // per-array entries.
36631 assert!(str_bytes_equal(Atom::TRUE_LITERAL, Atom::TRUE_LITERAL));
36632 assert!(str_bytes_equal(
36633 AtomKind::SYMBOL_LABEL,
36634 AtomKind::SYMBOL_LABEL,
36635 ));
36636 assert!(str_bytes_equal(
36637 QuoteForm::QUOTE_PREFIX,
36638 QuoteForm::QUOTE_PREFIX,
36639 ));
36640 }
36641
36642 #[test]
36643 fn str_bytes_equal_rejects_the_distinct_pair() {
36644 // Direct pin on `str_bytes_equal`'s reject-arm — two
36645 // distinct family-wide substrate strings MUST be flagged
36646 // as unequal. Guards against a regression that returned
36647 // `true` unconditionally (which would silently pass every
36648 // caller through the collision arm).
36649 assert!(!str_bytes_equal(Atom::TRUE_LITERAL, Atom::FALSE_LITERAL,));
36650 assert!(!str_bytes_equal(
36651 AtomKind::SYMBOL_LABEL,
36652 AtomKind::KEYWORD_LABEL,
36653 ));
36654 assert!(!str_bytes_equal(
36655 QuoteForm::QUOTE_PREFIX,
36656 QuoteForm::QUASIQUOTE_PREFIX,
36657 ));
36658 }
36659
36660 // ── `assert_str_arrays_disjoint` — the `&'static str` element-
36661 // type sibling of `assert_char_arrays_disjoint` and
36662 // `assert_u8_arrays_disjoint`. Sibling posture: same runtime-test
36663 // surface (accept-both-empty, accept-either-side-empty, accept-
36664 // disjoint-singletons, accept-every-family-wide-substrate-pair,
36665 // symmetric-in-argument-order, reject-cross-collision, reject-
36666 // terminal-a-collision, reject-terminal-b-collision, panic-message-
36667 // provenance) restricted to the `&'static str` element type. A
36668 // regression that silently weakens the helper (e.g. flipping
36669 // `str_bytes_equal` polarity, dropping the outer OR inner sweep,
36670 // or returning early on collision) is caught by the helper's OWN
36671 // test surface rather than only surfacing as a false-positive on
36672 // some future disjoint `[&'static str; N] × [&'static str; M]`
36673 // pair's compound pin.
36674
36675 #[test]
36676 fn assert_str_arrays_disjoint_accepts_both_empty_arrays() {
36677 // Both arrays empty at the `[&'static str; 0] × [&'static str; 0]`
36678 // corner — vacuously disjoint (no `(i, j)` pair exists to
36679 // test). The compile-time `const _: () =
36680 // assert_str_arrays_disjoint(&[], &[]);` would land on this
36681 // arm, so the runtime call MUST return normally. Turbofish
36682 // binding required because there's no other cue for the const
36683 // parameters on the empty array literals. Sibling posture to
36684 // `assert_char_arrays_disjoint_accepts_both_empty_arrays` and
36685 // `assert_u8_arrays_disjoint_accepts_both_empty_arrays` on
36686 // the (char) and (u8) row-dual peers — the three share the
36687 // trivial-arity arm across the (element-type × contract-shape)
36688 // 3-row × (disjointness)-column face.
36689 assert_str_arrays_disjoint::<0, 0>(&[], &[]);
36690 }
36691
36692 #[test]
36693 fn assert_str_arrays_disjoint_accepts_either_side_empty() {
36694 // Either side empty at the `[&'static str; 0] × [&'static str; M]`
36695 // OR `[&'static str; N] × [&'static str; 0]` corners —
36696 // vacuously disjoint (the OUTER `while i < N` OR the INNER
36697 // `while j < M` sweep is a no-op). Pins BOTH SIDES of the
36698 // (a-empty, b-empty) 2-corner sub-face on the trivial-arity
36699 // axis so a regression that narrowed the sweep to only-a-non-
36700 // empty OR only-b-non-empty fails HERE at the empty-side
36701 // corner rather than at a distant false-positive.
36702 assert_str_arrays_disjoint::<0, 3>(&[], &["a", "b", "c"]);
36703 assert_str_arrays_disjoint::<3, 0>(&["a", "b", "c"], &[]);
36704 }
36705
36706 #[test]
36707 fn assert_str_arrays_disjoint_accepts_disjoint_singletons() {
36708 // Singleton `a = [K]` and singleton `b = [L]` with `K != L`
36709 // at the `[&'static str; 1] × [&'static str; 1]` corner — the
36710 // minimal non-empty disjointness relation. Pins the INNER
36711 // `if str_bytes_equal(a[i], b[j])` gate returns without
36712 // panicking on a single distinct-pair check. A regression
36713 // that flipped the equality direction (`!=` vs `==` on the
36714 // byte-equality delegate) would silently reject every
36715 // disjoint singleton pair.
36716 assert_str_arrays_disjoint(&["a"], &["b"]);
36717 }
36718
36719 #[test]
36720 fn assert_str_arrays_disjoint_accepts_each_family_wide_substrate_pair() {
36721 // Runtime cross-check that the FOUR (a, b) `[&'static str; N]
36722 // × [&'static str; M]` pairs the substrate's module-level
36723 // `const _` witnesses pin at COMPILE time are proper
36724 // DISJOINTNESS embeddings at runtime too. The pairs enforce
36725 // the theorem at TWO stages of the toolchain: the const
36726 // witnesses fire FIRST at `cargo check` (through the four
36727 // module-level `const _: () = assert_str_arrays_disjoint::
36728 // <N, M>(...)` lines), this runtime pin catches the drift at
36729 // `cargo test` as a safety net. Sibling posture to
36730 // `assert_char_arrays_disjoint_accepts_each_family_wide_substrate_pair`
36731 // which sweeps the FIVE (a, b) PAIRS at the (char) row and
36732 // `assert_u8_arrays_disjoint_accepts_each_family_wide_substrate_pair`
36733 // which sweeps the TWO (a, b) PAIRS at the (u8) row; this
36734 // pin sweeps the four (a, b) PAIRS at the (`&'static str`)
36735 // row on the SAME contract-shape column.
36736 assert_str_arrays_disjoint::<4, 4>(&QuoteForm::PREFIXES, &QuoteForm::LABELS);
36737 assert_str_arrays_disjoint::<4, 4>(&QuoteForm::PREFIXES, &QuoteForm::IAC_FORGE_TAGS);
36738 assert_str_arrays_disjoint::<4, 6>(&QuoteForm::PREFIXES, &AtomKind::LABELS);
36739 assert_str_arrays_disjoint::<6, 4>(&AtomKind::LABELS, &QuoteForm::LABELS);
36740 }
36741
36742 #[test]
36743 fn assert_str_arrays_disjoint_is_symmetric_in_argument_order() {
36744 // SYMMETRY PIN: swapping the two arguments produces the SAME
36745 // verdict. Pins that the disjointness relation is truly
36746 // symmetric across the two array arguments (the helper's
36747 // nested-sweep implementation does NOT gratuitously depend on
36748 // argument order). A regression that narrowed the sweep to
36749 // `for i in a { if !b.contains(a[i]) }` (subset-shape, not
36750 // disjointness-shape) would fail the swap on one direction
36751 // only. Runs each substrate pair BOTH ways. Sibling posture
36752 // to `assert_char_arrays_disjoint_is_symmetric_in_argument_order`
36753 // on the (char) row-dual peer and
36754 // `assert_u8_arrays_disjoint_is_symmetric_in_argument_order`
36755 // on the (u8) row-dual peer.
36756 assert_str_arrays_disjoint(&QuoteForm::LABELS, &QuoteForm::PREFIXES);
36757 assert_str_arrays_disjoint(&QuoteForm::IAC_FORGE_TAGS, &QuoteForm::PREFIXES);
36758 assert_str_arrays_disjoint(&AtomKind::LABELS, &QuoteForm::PREFIXES);
36759 assert_str_arrays_disjoint(&QuoteForm::LABELS, &AtomKind::LABELS);
36760 }
36761
36762 #[test]
36763 #[should_panic(expected = "STR-DISJOINTNESS-VIOLATION")]
36764 fn assert_str_arrays_disjoint_panics_at_runtime_on_collision() {
36765 // NEGATIVE PIN — STR-DISJOINTNESS-VIOLATION corner: two arrays
36766 // sharing a single string MUST panic at runtime with the
36767 // STR-DISJOINTNESS-VIOLATION-named message. Pins the helper's
36768 // OWN reject arm — a regression that silently returned
36769 // without panicking on a cross-array collision would slip
36770 // through the compile-time witnesses' failure mode too. The
36771 // shared string `"dup"` is intentionally placed at the FIRST
36772 // position of BOTH arrays to pin the initial-position drift
36773 // mode.
36774 assert_str_arrays_disjoint(&["dup", "x"], &["dup", "y"]);
36775 }
36776
36777 #[test]
36778 #[should_panic(expected = "STR-DISJOINTNESS-VIOLATION")]
36779 fn assert_str_arrays_disjoint_panics_at_runtime_on_terminal_a_collision() {
36780 // NEGATIVE PIN — terminal-position drift on the OUTER `a`
36781 // side: a shared string at the LAST position of `a` MUST
36782 // panic — pins that the outer `while i < N` loop reaches
36783 // `i = N - 1` (else the terminal drift on `a` would slip
36784 // through). A regression that narrowed the outer sweep to
36785 // `while i < N - 1` (off-by-one on the OUTER bound) would
36786 // silently accept this pair.
36787 assert_str_arrays_disjoint(&["a", "b", "shared"], &["shared"]);
36788 }
36789
36790 #[test]
36791 #[should_panic(expected = "STR-DISJOINTNESS-VIOLATION")]
36792 fn assert_str_arrays_disjoint_panics_at_runtime_on_terminal_b_collision() {
36793 // NEGATIVE PIN — terminal-position drift on the INNER `b`
36794 // side: a shared string at the LAST position of `b` MUST
36795 // panic — pins that the inner `while j < M` loop reaches
36796 // `j = M - 1` (else the terminal drift on `b` would slip
36797 // through). A regression that narrowed the inner sweep to
36798 // `while j < M - 1` (off-by-one on the INNER bound) would
36799 // silently accept this pair. Sibling posture to the outer-
36800 // terminal pin above — the two pins together bind the
36801 // terminal bounds on BOTH loops.
36802 assert_str_arrays_disjoint(&["shared"], &["a", "b", "shared"]);
36803 }
36804
36805 #[test]
36806 #[should_panic(expected = "STR-DISJOINTNESS-VIOLATION")]
36807 fn assert_str_arrays_disjoint_panics_at_runtime_on_length_zero_collision() {
36808 // NEGATIVE PIN — the byte-length gate at `str_bytes_equal`
36809 // must classify TWO empty strings across the (a, b) boundary
36810 // as equal too (both are the zero-length byte sequence). A
36811 // regression that treated `""` as never-colliding across the
36812 // arrays would silently accept the empty-string collision.
36813 // Delegated to the SAME `str_bytes_equal` helper the
36814 // pairwise-distinct sibling uses — this pin exercises the
36815 // cross-array direction of the same zero-length arm.
36816 assert_str_arrays_disjoint(&[""], &[""]);
36817 }
36818
36819 #[test]
36820 fn assert_str_arrays_disjoint_accepts_prefix_pair_without_collision() {
36821 // POSITIVE-ORTHOGONAL PIN — the byte-length gate at
36822 // `str_bytes_equal` across the (a, b) boundary: two arrays
36823 // where entries of one are strict prefixes of entries of the
36824 // other but no full-length equal pair exists must be flagged
36825 // as DISJOINT. A regression that dropped the
36826 // `a.len() != b.len()` gate (e.g. by comparing bytes only up
36827 // to the shorter length) would silently reject
36828 // `(["ab"], ["abc"])` as a collision. Sibling to the (str,
36829 // pairwise-distinct) sibling's `_accepts_prefix_pair_without_
36830 // collision` pin — the two share the byte-length-gate
36831 // theorem across (intra-array, inter-array) row-dual peers.
36832 assert_str_arrays_disjoint(&["ab"], &["abc"]);
36833 assert_str_arrays_disjoint(&["abc"], &["ab"]);
36834 assert_str_arrays_disjoint(&[""], &["a"]);
36835 }
36836
36837 #[test]
36838 fn assert_str_arrays_disjoint_panic_message_names_the_helper_and_str_disjointness_violation_axis(
36839 ) {
36840 // PANIC-MESSAGE PROVENANCE PIN — STR-DISJOINTNESS-VIOLATION
36841 // arm: the panic message MUST begin with the helper's own
36842 // name AND identify the failed AXIS as "STR-DISJOINTNESS-
36843 // VIOLATION" so downstream diagnostics route the drift back
36844 // to (a) the helper by string search on
36845 // `"assert_str_arrays_disjoint"` and (b) the axis by string
36846 // search on `"STR-DISJOINTNESS-VIOLATION"`. Sibling posture
36847 // to `assert_char_arrays_disjoint_panic_message_names_the_helper_and_char_disjointness_violation_axis`
36848 // on the (char) row-dual peer's provenance pin AND to
36849 // `assert_u8_arrays_disjoint_panic_message_names_the_helper_and_u8_disjointness_violation_axis`
36850 // on the (u8) row-dual peer's provenance pin — the three
36851 // pins together bind the (helper, failed-axis) provenance
36852 // triple across the (element-type ∈ {char, u8, `&'static
36853 // str`}) × (disjointness)-column 3-row face with matching
36854 // provenance-preservation discipline. The axis-provenance
36855 // string `"STR-DISJOINTNESS-VIOLATION"` is chosen DISTINCT
36856 // from EVERY sibling helper's axis vocabulary (`"duplicate"`
36857 // on the (str) ARRAY-side pairwise-distinct sibling;
36858 // `"CHAR-DISJOINTNESS-VIOLATION"` on the (char) row-dual
36859 // DISJOINTNESS sibling; `"U8-DISJOINTNESS-VIOLATION"` on
36860 // the (u8) row-dual DISJOINTNESS sibling; `"CHAR-SUBSET-
36861 // VIOLATION"` on the (char) SUBSET-embedding sibling;
36862 // `"SUBSET-VIOLATION"` on the (u8) finite-set SUBSET-only
36863 // sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8) range
36864 // SUBSET-only sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"`
36865 // on the (u8) covers-finite-set sibling; `"OUT-OF-RANGE"` /
36866 // `"MISSING"` on the (u8) covers-inclusive-range sibling;
36867 // `"ARITY-MISMATCH"` on both (u8) `_permutes_*` compound
36868 // helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on the (u8) SET-side
36869 // well-formedness sibling) so a diagnostic that names the
36870 // failed axis routes UNAMBIGUOUSLY to (a) this specific
36871 // `&'static str` DISJOINTNESS helper.
36872 let outcome = std::panic::catch_unwind(|| {
36873 assert_str_arrays_disjoint(&["dup", "x"], &["dup", "y"]);
36874 });
36875 let payload = outcome.expect_err(
36876 "assert_str_arrays_disjoint must panic on a cross-array \
36877 collision — the reject-collision arm is the sole STR-\
36878 DISJOINTNESS-VIOLATION failure mode of the helper",
36879 );
36880 let msg = payload
36881 .downcast_ref::<&'static str>()
36882 .map(|s| (*s).to_owned())
36883 .or_else(|| payload.downcast_ref::<String>().cloned())
36884 .expect(
36885 "assert_str_arrays_disjoint panic payload must be a \
36886 static &str or String",
36887 );
36888 assert!(
36889 msg.contains("assert_str_arrays_disjoint"),
36890 "assert_str_arrays_disjoint panic message {msg:?} must \
36891 name the helper for provenance-preserving failure \
36892 diagnostics",
36893 );
36894 assert!(
36895 msg.contains("STR-DISJOINTNESS-VIOLATION"),
36896 "assert_str_arrays_disjoint panic message {msg:?} must \
36897 name the failed AXIS (\"STR-DISJOINTNESS-VIOLATION\") \
36898 for axis-provenance-preserving failure diagnostics",
36899 );
36900 }
36901
36902 // ── `assert_str_array_within_str_finite_set` — the `&'static str`
36903 // element-type sibling of `assert_char_array_within_char_finite_
36904 // set` and `assert_u8_array_within_u8_finite_set` on the (element-
36905 // type) axis of the (element-type × contract-shape) matrix at the
36906 // (subset-embedding) column. Sibling posture: same runtime-test
36907 // surface (accept-empty, accept-singleton, accept-arr-equals-set,
36908 // accept-each-family-wide-substrate-subset, accept-repeated-array-
36909 // entries, reject-out-of-set, reject-terminal-out-of-set, panic-
36910 // message-provenance, delegated-set-well-formedness) restricted to
36911 // the `&'static str` element type. A regression that silently
36912 // weakens the helper (e.g. flipping the found flag, dropping the
36913 // outer `i` loop, or short-circuiting on the first-position match)
36914 // is caught by the helper's OWN test surface rather than only
36915 // surfacing as a false-positive on some future `[&'static str; N]`
36916 // sub-vocabulary array's subset-embedding pin.
36917
36918 #[test]
36919 fn assert_str_array_within_str_finite_set_accepts_the_empty_array_within_any_set() {
36920 // Empty array `arr = []` at the `[&'static str; 0]` corner —
36921 // vacuously a subset of every set (no `i` position exists to
36922 // test). Cross-arity coverage on the trivial ARRAY corner of
36923 // the const-N generic across three witness-set widths (empty,
36924 // singleton, multi-element) to pin the helper's OUTER-sweep
36925 // arm across the whole (`N == 0` × `M`) axis. Turbofish
36926 // binding required because there's no other cue for the const
36927 // parameters on the empty array literal. Sibling posture to
36928 // `assert_char_array_within_char_finite_set_accepts_the_empty_array_within_any_set`
36929 // and `assert_u8_array_within_u8_finite_set_accepts_the_empty_array_within_any_set`
36930 // on the (char) + (u8) row-dual peers of the SUBSET-EMBEDDING
36931 // helper — the three share the trivial-arity arm across the
36932 // element-type column.
36933 assert_str_array_within_str_finite_set::<0, 0>(&[], &[]);
36934 assert_str_array_within_str_finite_set::<0, 1>(&[], &["x"]);
36935 assert_str_array_within_str_finite_set::<0, 3>(&[], &["a", "b", "c"]);
36936 }
36937
36938 #[test]
36939 fn assert_str_array_within_str_finite_set_accepts_singleton_array_when_str_in_set() {
36940 // Singleton array `arr = [K]` at the `[&'static str; 1]`
36941 // corner MUST pass when `K ∈ set`. Cross-position coverage:
36942 // the str can sit at the FIRST, MIDDLE, or LAST position of
36943 // the `set` — pins the INNER `while j < M` sweep terminates
36944 // at the first-match position rather than always at position
36945 // `0` OR always at position `M - 1`. A regression that
36946 // narrowed the inner sweep to `j == 0` would silently reject
36947 // singleton arrays hitting non-first set positions.
36948 assert_str_array_within_str_finite_set::<1, 3>(&["a"], &["a", "b", "c"]);
36949 assert_str_array_within_str_finite_set::<1, 3>(&["b"], &["a", "b", "c"]);
36950 assert_str_array_within_str_finite_set::<1, 3>(&["c"], &["a", "b", "c"]);
36951 }
36952
36953 #[test]
36954 fn assert_str_array_within_str_finite_set_accepts_arr_equals_set() {
36955 // Boundary corner where `arr` and `set` cover byte-for-byte
36956 // identical distinct-value sets — the SUBSET relation
36957 // degenerates to EQUALITY. Pins that the helper does NOT
36958 // gratuitously require the SUBSET to be PROPER (strict):
36959 // equal-multisets pass the SUBSET check. Sibling posture to
36960 // `assert_char_array_within_char_finite_set_accepts_arr_equals_set`
36961 // and `assert_u8_array_within_u8_finite_set_accepts_arr_equals_set`
36962 // on the (char) + (u8) row-dual peers of the SUBSET-EMBEDDING
36963 // helper — the three share the EQUAL-SETS corner across the
36964 // element-type column.
36965 assert_str_array_within_str_finite_set(&["a", "b"], &["a", "b"]);
36966 assert_str_array_within_str_finite_set(&[Atom::TRUE_LITERAL], &[Atom::TRUE_LITERAL]);
36967 }
36968
36969 #[test]
36970 fn assert_str_array_within_str_finite_set_accepts_each_family_wide_substrate_subset() {
36971 // Runtime cross-check that the THREE (subset, superset) pairs
36972 // the substrate's module-level `const _` witnesses at
36973 // `error.rs` pin at COMPILE time are PROPER SUBSET embeddings
36974 // at runtime too. The pairs enforce the theorem at TWO stages
36975 // of the toolchain: the const witnesses fire FIRST at `cargo
36976 // check` (through the three module-level `const _: () =
36977 // crate::ast::assert_str_array_within_str_finite_set::<N, M>
36978 // (...)` lines in `error.rs`), this runtime pin catches the
36979 // drift at `cargo test` as a safety net. Sibling posture to
36980 // `assert_char_array_within_char_finite_set_accepts_each_family_wide_substrate_subset`
36981 // and `assert_str_array_pairwise_distinct_accepts_every_family_wide_substrate_array`
36982 // — the first sweeps the (char) subset pairs at the SUBSET-
36983 // EMBEDDING axis; the second sweeps the (str) five arrays at
36984 // the INJECTIVITY axis; this pin sweeps the (str) three
36985 // (subset, superset) PAIRS at the SUBSET-EMBEDDING axis.
36986 // Cardinality composition: 6 + 4 + 2 = 12 =
36987 // `SexpShape::LABELS.len()` — the three subsets partition the
36988 // outer twelve-arm vocabulary (composed with the pre-existing
36989 // pairwise-disjointness witness of `AtomKind::LABELS ∩
36990 // QuoteForm::LABELS`).
36991 assert_str_array_within_str_finite_set::<6, 12>(&AtomKind::LABELS, &SexpShape::LABELS);
36992 assert_str_array_within_str_finite_set::<4, 12>(&QuoteForm::LABELS, &SexpShape::LABELS);
36993 assert_str_array_within_str_finite_set::<2, 12>(
36994 &StructuralKind::LABELS,
36995 &SexpShape::LABELS,
36996 );
36997 }
36998
36999 #[test]
37000 fn assert_str_array_within_str_finite_set_accepts_repeated_array_entries_in_set() {
37001 // Peer corner to a (future-lift) `_covers_str_finite_set`:
37002 // this helper permits duplicates in `arr` because SUBSET-
37003 // membership is a DISTINCT-value predicate — `["a", "a", "b"]`
37004 // is a subset of `{"a", "b", "c"}` even though the array is
37005 // not pairwise-distinct. Pins that the helper does NOT
37006 // gratuitously require INJECTIVITY on `arr` (the injectivity
37007 // axis is a DIFFERENT compile-time contract bound by
37008 // `assert_str_array_pairwise_distinct`; combining both binds
37009 // BOTH axes). Sibling posture to
37010 // `assert_char_array_within_char_finite_set_accepts_repeated_array_entries_in_set`
37011 // and `assert_u8_array_within_u8_finite_set_accepts_repeated_array_entries_in_set`
37012 // on the (char) + (u8) row-dual peers of the SUBSET-EMBEDDING
37013 // helper.
37014 assert_str_array_within_str_finite_set(&["a", "a", "b"], &["a", "b", "c"]);
37015 }
37016
37017 #[test]
37018 #[should_panic(expected = "STR-SUBSET-VIOLATION")]
37019 fn assert_str_array_within_str_finite_set_panics_at_runtime_on_out_of_set_entry() {
37020 // NEGATIVE PIN — STR-SUBSET-VIOLATION corner: an array
37021 // carrying a single entry NOT in the target set MUST panic
37022 // at runtime with the STR-SUBSET-VIOLATION-named message.
37023 // Pins the helper's OWN reject arm — a regression that
37024 // silently returned without panicking on an out-of-set entry
37025 // would slip through the compile-time witnesses' failure mode
37026 // too. The offending str `"z"` is intentionally chosen
37027 // OUTSIDE the target set to pin the OUT-OF-SET drift mode.
37028 assert_str_array_within_str_finite_set(&["a", "z"], &["a", "b", "c"]);
37029 }
37030
37031 #[test]
37032 #[should_panic(expected = "STR-SUBSET-VIOLATION")]
37033 fn assert_str_array_within_str_finite_set_panics_at_runtime_on_terminal_out_of_set_entry() {
37034 // NEGATIVE PIN — terminal-position drift: an out-of-set entry
37035 // at the LAST array position MUST panic — pins that the
37036 // outer `while i < N` loop reaches `i = N - 1` (else the
37037 // terminal drift would slip through). A regression that
37038 // narrowed the outer sweep to `while i < N - 1` (off-by-one
37039 // on the OUTER bound) would silently accept this array.
37040 // Sibling posture to
37041 // `assert_char_array_within_char_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
37042 // and `assert_u8_array_within_u8_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
37043 // on the (char) + (u8) row-dual peers' terminal-position
37044 // pins — all three bind the outer-sweep terminal bound at
37045 // the ONE array-side outer loop the helper carries.
37046 assert_str_array_within_str_finite_set(&["a", "b", "c", "z"], &["a", "b", "c"]);
37047 }
37048
37049 #[test]
37050 fn assert_str_array_within_str_finite_set_rejects_length_prefix_shorter_than_set_entry() {
37051 // Byte-length gate corner: an `arr` entry that shares a
37052 // strict-prefix with a `set` entry but is BYTE-SHORTER than
37053 // that `set` entry MUST fail the subset check (prefix ≠
37054 // equal-bytes at the `str_bytes_equal` byte-length gate).
37055 // Pins the cross-array direction of the SAME byte-length
37056 // gate the pairwise-distinct sibling's test surface
37057 // (`assert_str_array_pairwise_distinct_rejects_length_zero_collision`,
37058 // `assert_str_array_pairwise_distinct_accepts_prefix_pair_without_collision`)
37059 // pins on the intra-array direction. Cross-array prefix
37060 // mismatch → out-of-set → STR-SUBSET-VIOLATION panic.
37061 let outcome = std::panic::catch_unwind(|| {
37062 assert_str_array_within_str_finite_set(&["quo"], &["quote", "quasiquote"]);
37063 });
37064 outcome.expect_err(
37065 "assert_str_array_within_str_finite_set must panic on a \
37066 STRICT-PREFIX-only entry outside the target set — the \
37067 byte-length gate at str_bytes_equal separates \
37068 `\"quo\"` from `\"quote\"` at the length-check arm before \
37069 any byte comparison, routing to the STR-SUBSET-VIOLATION \
37070 out-of-set panic",
37071 );
37072 }
37073
37074 #[test]
37075 fn assert_str_array_within_str_finite_set_accepts_length_zero_entry_in_length_zero_set() {
37076 // BYTE-LENGTH-ZERO corner: the empty-string entry `""` in
37077 // `arr` MUST match the empty-string entry `""` in `set` at
37078 // the byte-length gate (both length 0). Pins the LOWER
37079 // boundary of the byte-length axis on the cross-array
37080 // direction of the SAME byte-length gate. A regression that
37081 // gated `str_bytes_equal` on `a.len() > 0` (rather than
37082 // `a.len() == b.len()`) would silently reject this pair.
37083 assert_str_array_within_str_finite_set(&[""], &[""]);
37084 }
37085
37086 #[test]
37087 fn assert_str_array_within_str_finite_set_panic_message_names_the_helper_and_str_subset_violation_axis(
37088 ) {
37089 // PANIC-MESSAGE PROVENANCE PIN — STR-SUBSET-VIOLATION arm:
37090 // the panic message MUST begin with the helper's own name AND
37091 // identify the failed AXIS as "STR-SUBSET-VIOLATION" so
37092 // downstream diagnostics route the drift back to (a) the
37093 // helper by string search on
37094 // `"assert_str_array_within_str_finite_set"` and (b) the axis
37095 // by string search on `"STR-SUBSET-VIOLATION"`. Sibling
37096 // posture to
37097 // `assert_char_array_within_char_finite_set_panic_message_names_the_helper_and_char_subset_violation_axis`
37098 // on the (char) row-dual peer's provenance pin AND to
37099 // `assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis`
37100 // on the (u8) row-dual peer's provenance pin — the three
37101 // pins together bind the (helper, failed-axis) provenance
37102 // triple across the (element-type ∈ {char, u8, `&'static
37103 // str`}) × (subset-embedding)-column 3-row face with matching
37104 // provenance-preservation discipline. The axis-provenance
37105 // string `"STR-SUBSET-VIOLATION"` is chosen DISTINCT from
37106 // EVERY sibling helper's axis vocabulary (`"duplicate"` on
37107 // the (str) ARRAY-side pairwise-distinct sibling; `"STR-
37108 // DISJOINTNESS-VIOLATION"` on the (str) row DISJOINTNESS
37109 // sibling; `"CHAR-SUBSET-VIOLATION"` on the (char) row-dual
37110 // SUBSET sibling; `"SUBSET-VIOLATION"` on the (u8) finite-
37111 // set SUBSET-only sibling; `"RANGE-SUBSET-VIOLATION"` on the
37112 // (u8) range SUBSET-only sibling; `"CHAR-DISJOINTNESS-
37113 // VIOLATION"` / `"U8-DISJOINTNESS-VIOLATION"` on the (char)
37114 // / (u8) row-dual DISJOINTNESS siblings; `"OUT-OF-SET"` /
37115 // `"SET-BYTE-MISSING"` on the (u8) covers-finite-set
37116 // sibling; `"OUT-OF-RANGE"` / `"MISSING"` on the (u8)
37117 // covers-inclusive-range sibling; `"ARITY-MISMATCH"` on both
37118 // (u8) `_permutes_*` compound helpers; `"SET-NOT-PAIRWISE-
37119 // DISTINCT"` on the (u8) SET-side well-formedness sibling)
37120 // so a diagnostic that names the failed axis routes
37121 // UNAMBIGUOUSLY to (a) this specific `&'static str` SUBSET-
37122 // embedding helper, (b) the `arr` argument as the drift site
37123 // rather than the `set` argument specifying the target
37124 // superset.
37125 let outcome = std::panic::catch_unwind(|| {
37126 assert_str_array_within_str_finite_set(&["a", "z"], &["a", "b", "c"]);
37127 });
37128 let payload = outcome.expect_err(
37129 "assert_str_array_within_str_finite_set must panic on an \
37130 out-of-set entry — the reject-out-of-set arm is the \
37131 sole STR-SUBSET-VIOLATION failure mode of the helper",
37132 );
37133 let msg = payload
37134 .downcast_ref::<&'static str>()
37135 .map(|s| (*s).to_owned())
37136 .or_else(|| payload.downcast_ref::<String>().cloned())
37137 .expect(
37138 "assert_str_array_within_str_finite_set panic payload \
37139 must be a static &str or String",
37140 );
37141 assert!(
37142 msg.contains("assert_str_array_within_str_finite_set"),
37143 "assert_str_array_within_str_finite_set panic message \
37144 {msg:?} must name the helper for provenance-preserving \
37145 failure diagnostics",
37146 );
37147 assert!(
37148 msg.contains("STR-SUBSET-VIOLATION"),
37149 "assert_str_array_within_str_finite_set panic message \
37150 {msg:?} must name the failed AXIS (\"STR-SUBSET-\
37151 VIOLATION\") for axis-provenance-preserving failure \
37152 diagnostics",
37153 );
37154 }
37155
37156 #[test]
37157 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
37158 fn assert_str_array_within_str_finite_set_panics_on_malformed_target_set_spec() {
37159 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
37160 // malformed target-set spec `["a", "a", "b"]` fed into the
37161 // ARRAY-side within helper MUST panic on the DELEGATED
37162 // pairwise-distinct arm BEFORE the STR-SUBSET-VIOLATION arm
37163 // fires. Pins the delegation chain: a regression that
37164 // dropped the `assert_str_array_pairwise_distinct(set)` call
37165 // at the top of `assert_str_array_within_str_finite_set`
37166 // would silently accept a malformed set and produce a false-
37167 // positive verdict on any `arr` embedded in the DISTINCT-
37168 // value subset. The panic message here surfaces from the
37169 // SIBLING helper (containing the ARRAY-side helper's
37170 // `"assert_str_array_pairwise_distinct"` panic-name prefix
37171 // rather than a bespoke `"SET-NOT-PAIRWISE-DISTINCT"` axis
37172 // string) because the (str) row does NOT yet carry a
37173 // separate `assert_str_finite_set_pairwise_distinct` alias —
37174 // the delegation reuses the ARRAY-side helper directly per
37175 // the design choice documented on the SET-side-well-
37176 // formedness section of the helper's docstring. Sibling
37177 // posture to
37178 // `assert_char_array_within_char_finite_set_panics_on_malformed_target_set_spec`
37179 // and `assert_u8_array_within_u8_finite_set_panics_on_malformed_target_set_spec`
37180 // on the (char) + (u8) row-dual peers' delegated-SET-well-
37181 // formedness pins — the three pins together bind the
37182 // delegation chain at ONE test per element-type row.
37183 assert_str_array_within_str_finite_set::<2, 3>(&["a", "b"], &["a", "b", "b"]);
37184 }
37185
37186 // ── `assert_str_finite_set_covered_by_three_str_arrays` — the
37187 // (⊆) SET-COVERAGE dual of the SUBSET-embedding sibling on the
37188 // (`&'static str`) row of the (element-type × contract-shape)
37189 // matrix, extended to the 3-array-union carrier shape. Sibling
37190 // posture: same runtime-test surface (accept-empty-parent,
37191 // accept-parent-covered-by-first-array, accept-parent-covered-
37192 // by-second-array, accept-parent-covered-by-third-array, accept-
37193 // sexp-shape-labels-partition, reject-uncovered-parent-entry,
37194 // reject-terminal-uncovered-parent-entry, panic-message-
37195 // provenance, delegated-set-well-formedness) specialised to the
37196 // 3-array coverage shape. A regression that silently weakens the
37197 // helper (e.g. dropping the second-array probe, dropping the
37198 // third-array probe, or short-circuiting on the first parent
37199 // position) is caught by the helper's OWN test surface rather
37200 // than only surfacing as a false-positive on the
37201 // `SexpShape::LABELS` disjoint-union witness.
37202
37203 #[test]
37204 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_the_empty_parent() {
37205 // Empty parent `set = []` at the `[&'static str; 0]` corner
37206 // — vacuously covered by any triple of sub-vocabulary arrays
37207 // (no `w` position exists to test). Cross-arity coverage on
37208 // the trivial PARENT corner of the const-W generic across
37209 // three sub-vocabulary-arity combinations. Turbofish binding
37210 // required because there's no other cue for the const
37211 // parameters on the empty array literal.
37212 assert_str_finite_set_covered_by_three_str_arrays::<0, 0, 0, 0>(&[], &[], &[], &[]);
37213 assert_str_finite_set_covered_by_three_str_arrays::<1, 0, 0, 0>(&["x"], &[], &[], &[]);
37214 assert_str_finite_set_covered_by_three_str_arrays::<1, 2, 3, 0>(
37215 &["a"],
37216 &["b", "c"],
37217 &["d", "e", "f"],
37218 &[],
37219 );
37220 }
37221
37222 #[test]
37223 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_parent_covered_by_first_array() {
37224 // Singleton parent `set = [K]` at the `[&'static str; 1]`
37225 // corner MUST pass when `K` is in the FIRST sub-vocabulary
37226 // array `a`. Pins the FIRST-ARRAY probe arm's short-circuit
37227 // discovery — the parent entry is reached without exhausting
37228 // the second- or third-array probes. Cross-position coverage
37229 // inside `a`: parent hit at FIRST, MIDDLE, LAST position of
37230 // `a` pins the INNER `while i < N` sweep terminates at the
37231 // first-match position rather than always at position `0` OR
37232 // always at position `N - 1`.
37233 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 1, 1>(
37234 &["k", "b", "c"],
37235 &["d", "e"],
37236 &["f"],
37237 &["k"],
37238 );
37239 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 1, 1>(
37240 &["a", "k", "c"],
37241 &["d", "e"],
37242 &["f"],
37243 &["k"],
37244 );
37245 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 1, 1>(
37246 &["a", "b", "k"],
37247 &["d", "e"],
37248 &["f"],
37249 &["k"],
37250 );
37251 }
37252
37253 #[test]
37254 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_parent_covered_by_second_array() {
37255 // Singleton parent MUST pass when `K` is in the SECOND sub-
37256 // vocabulary array `b` but NOT in `a`. Pins the SECOND-ARRAY
37257 // probe arm — the coverage sweep proceeds from `a` to `b`
37258 // when `a` returns no hit. A regression that dropped the
37259 // `if !found { … b sweep … }` block would silently reject
37260 // this case as SET-STR-MISSING even though `b` carries the
37261 // covering entry. Cross-position coverage inside `b`: FIRST,
37262 // MIDDLE, LAST positions of `b` pin the INNER `while j < M`
37263 // sweep terminates at the first-match position.
37264 assert_str_finite_set_covered_by_three_str_arrays::<3, 3, 1, 1>(
37265 &["a", "b", "c"],
37266 &["k", "e", "f"],
37267 &["g"],
37268 &["k"],
37269 );
37270 assert_str_finite_set_covered_by_three_str_arrays::<3, 3, 1, 1>(
37271 &["a", "b", "c"],
37272 &["d", "k", "f"],
37273 &["g"],
37274 &["k"],
37275 );
37276 assert_str_finite_set_covered_by_three_str_arrays::<3, 3, 1, 1>(
37277 &["a", "b", "c"],
37278 &["d", "e", "k"],
37279 &["g"],
37280 &["k"],
37281 );
37282 }
37283
37284 #[test]
37285 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_parent_covered_by_third_array() {
37286 // Singleton parent MUST pass when `K` is in the THIRD sub-
37287 // vocabulary array `c` but NOT in `a` or `b`. Pins the
37288 // THIRD-ARRAY probe arm — the coverage sweep proceeds all
37289 // the way to `c` when `a` and `b` both return no hit. A
37290 // regression that dropped the `if !found { … c sweep … }`
37291 // block would silently reject this case as SET-STR-MISSING
37292 // even though `c` carries the covering entry. Cross-position
37293 // coverage inside `c`: FIRST, MIDDLE, LAST positions of `c`
37294 // pin the INNER `while k < K` sweep terminates at the first-
37295 // match position.
37296 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 3, 1>(
37297 &["a", "b", "c"],
37298 &["d", "e"],
37299 &["k", "g", "h"],
37300 &["k"],
37301 );
37302 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 3, 1>(
37303 &["a", "b", "c"],
37304 &["d", "e"],
37305 &["f", "k", "h"],
37306 &["k"],
37307 );
37308 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 3, 1>(
37309 &["a", "b", "c"],
37310 &["d", "e"],
37311 &["f", "g", "k"],
37312 &["k"],
37313 );
37314 }
37315
37316 #[test]
37317 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_sexp_shape_labels_partition() {
37318 // Runtime cross-check that the twelve-arm
37319 // (`AtomKind::LABELS`, `QuoteForm::LABELS`,
37320 // `StructuralKind::LABELS`, `SexpShape::LABELS`) partition
37321 // quadruple the substrate's module-level `const _` witness at
37322 // `error.rs` pins at COMPILE time is a well-formed SET-
37323 // COVERAGE relation at runtime too. The quadruple enforces the
37324 // (⊆) direction of the disjoint-union theorem at TWO stages
37325 // of the toolchain: the const witness fires FIRST at `cargo
37326 // check`, this runtime pin catches the drift at `cargo test`
37327 // as a safety net. Sibling posture to
37328 // `assert_str_array_within_str_finite_set_accepts_each_family_wide_substrate_subset`
37329 // — the sibling sweeps the (⊇) SUBSET direction for the three
37330 // sub-vocabularies; this pin sweeps the (⊆) COVERAGE direction
37331 // for the parent. Cardinality composition: 6 + 4 + 2 = 12 =
37332 // `SexpShape::LABELS.len()`.
37333 assert_str_finite_set_covered_by_three_str_arrays::<6, 4, 2, 12>(
37334 &AtomKind::LABELS,
37335 &QuoteForm::LABELS,
37336 &crate::error::StructuralKind::LABELS,
37337 &crate::error::SexpShape::LABELS,
37338 );
37339 }
37340
37341 #[test]
37342 #[should_panic(expected = "SET-STR-MISSING")]
37343 fn assert_str_finite_set_covered_by_three_str_arrays_panics_at_runtime_on_uncovered_parent_entry(
37344 ) {
37345 // NEGATIVE PIN — SET-STR-MISSING corner: a parent set carrying
37346 // an entry NOT in any of the three sub-vocabulary arrays MUST
37347 // panic at runtime with the SET-STR-MISSING-named message.
37348 // Pins the helper's OWN reject arm — a regression that
37349 // silently returned without panicking on an uncovered entry
37350 // would slip through the compile-time witness's failure mode
37351 // too. The offending str `"z"` is intentionally chosen ABSENT
37352 // from `a ∪ b ∪ c` to pin the SET-STR-MISSING drift mode.
37353 assert_str_finite_set_covered_by_three_str_arrays::<1, 1, 1, 2>(
37354 &["a"],
37355 &["b"],
37356 &["c"],
37357 &["a", "z"],
37358 );
37359 }
37360
37361 #[test]
37362 #[should_panic(expected = "SET-STR-MISSING")]
37363 fn assert_str_finite_set_covered_by_three_str_arrays_panics_at_runtime_on_terminal_uncovered_parent_entry(
37364 ) {
37365 // NEGATIVE PIN — terminal-position drift: an uncovered entry
37366 // at the LAST parent position MUST panic — pins that the
37367 // outer `while w < W` loop reaches `w = W - 1` (else the
37368 // terminal drift would slip through). A regression that
37369 // narrowed the outer sweep to `while w < W - 1` (off-by-one
37370 // on the OUTER bound) would silently accept this parent set
37371 // even though the trailing `"z"` is uncovered.
37372 assert_str_finite_set_covered_by_three_str_arrays::<1, 1, 1, 4>(
37373 &["a"],
37374 &["b"],
37375 &["c"],
37376 &["a", "b", "c", "z"],
37377 );
37378 }
37379
37380 #[test]
37381 fn assert_str_finite_set_covered_by_three_str_arrays_panic_message_names_the_helper_and_set_str_missing_axis(
37382 ) {
37383 // PANIC-MESSAGE PROVENANCE PIN — SET-STR-MISSING arm: the
37384 // panic message MUST begin with the helper's own name AND
37385 // identify the failed AXIS as "SET-STR-MISSING" so downstream
37386 // diagnostics route the drift back to (a) the helper by
37387 // string search on
37388 // `"assert_str_finite_set_covered_by_three_str_arrays"` and
37389 // (b) the axis by string search on `"SET-STR-MISSING"`.
37390 // Sibling posture to
37391 // `assert_str_array_within_str_finite_set_panic_message_names_the_helper_and_str_subset_violation_axis`
37392 // on the sibling (str)-row SUBSET-VIOLATION provenance pin
37393 // AND to the (u8) row-dual peer's `"SET-BYTE-MISSING"` axis
37394 // vocabulary on `assert_u8_array_covers_finite_set` — the
37395 // element-type infix `"STR"` vs `"BYTE"` disambiguates the
37396 // element-type peer while the shared `"SET-…-MISSING"` suffix
37397 // lets callers grep any row's COVERAGE-side SET-MISSING
37398 // sibling by the shared suffix pattern alone.
37399 let outcome = std::panic::catch_unwind(|| {
37400 assert_str_finite_set_covered_by_three_str_arrays::<1, 1, 1, 2>(
37401 &["a"],
37402 &["b"],
37403 &["c"],
37404 &["a", "z"],
37405 );
37406 });
37407 let payload = outcome.expect_err(
37408 "assert_str_finite_set_covered_by_three_str_arrays must \
37409 panic on an uncovered parent entry — the reject-uncovered \
37410 arm is the sole SET-STR-MISSING failure mode of the \
37411 helper",
37412 );
37413 let msg = payload
37414 .downcast_ref::<&'static str>()
37415 .map(|s| (*s).to_owned())
37416 .or_else(|| payload.downcast_ref::<String>().cloned())
37417 .expect(
37418 "assert_str_finite_set_covered_by_three_str_arrays \
37419 panic payload must be a static &str or String",
37420 );
37421 assert!(
37422 msg.contains("assert_str_finite_set_covered_by_three_str_arrays"),
37423 "assert_str_finite_set_covered_by_three_str_arrays panic \
37424 message {msg:?} must name the helper for provenance-\
37425 preserving failure diagnostics",
37426 );
37427 assert!(
37428 msg.contains("SET-STR-MISSING"),
37429 "assert_str_finite_set_covered_by_three_str_arrays panic \
37430 message {msg:?} must name the failed AXIS (\"SET-STR-\
37431 MISSING\") for axis-provenance-preserving failure \
37432 diagnostics",
37433 );
37434 }
37435
37436 #[test]
37437 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
37438 fn assert_str_finite_set_covered_by_three_str_arrays_panics_on_malformed_target_set_spec() {
37439 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
37440 // malformed parent-set spec `["a", "a", "b"]` fed into the
37441 // COVERAGE helper MUST panic on the DELEGATED pairwise-
37442 // distinct arm BEFORE the SET-STR-MISSING arm fires. Pins the
37443 // delegation chain: a regression that dropped the
37444 // `assert_str_array_pairwise_distinct(set)` call at the top
37445 // of `assert_str_finite_set_covered_by_three_str_arrays`
37446 // would silently accept a malformed parent set with duplicate
37447 // entries (the duplicated byte counts as covered on the first
37448 // hit even when the second copy is absent from `a ∪ b ∪ c`).
37449 // The panic message surfaces from the sibling ARRAY-side
37450 // pairwise-distinct helper directly (containing its
37451 // `"assert_str_array_pairwise_distinct"` panic-name prefix)
37452 // because the (str) row does NOT carry a separate
37453 // `assert_str_finite_set_pairwise_distinct` alias — the
37454 // delegation reuses the ARRAY-side helper per the design
37455 // choice documented on the SET-side well-formedness section
37456 // of the helper's docstring.
37457 assert_str_finite_set_covered_by_three_str_arrays::<1, 1, 1, 3>(
37458 &["a"],
37459 &["b"],
37460 &["c"],
37461 &["a", "a", "b"],
37462 );
37463 }
37464
37465 // ── `assert_str_array_is_concatenation_of_two_scalar_replicas` —
37466 // the MANY-TO-ONE-BLOCK-CONSTANCY sibling of the INJECTIVITY
37467 // sibling on the (`&'static str`) row of the (element-type ×
37468 // contract-shape) matrix. Sibling posture: symmetric runtime-test
37469 // surface (accept-two-block-partition, accept-K-zero-degenerate,
37470 // accept-K-equals-N-degenerate, accept-empty-array, accept-
37471 // compiler-spec-io-stage-operations-partition, reject-head-drift,
37472 // reject-tail-drift, reject-arity-slip, panic-message-provenance)
37473 // specialised to the MANY-TO-ONE block-constant projection shape.
37474 // A regression that silently weakens the helper (e.g. flipping
37475 // `str_bytes_equal` to `!str_bytes_equal`, narrowing the outer
37476 // sweep to `while i < K - 1` on the HEAD segment, silently
37477 // skipping the TAIL segment, or returning early past the
37478 // CARDINALITY-MISMATCH gate) is caught by the helper's OWN test
37479 // surface rather than only surfacing as a false-positive on the
37480 // `CompilerSpecIoStage::OPERATIONS` witness.
37481
37482 #[test]
37483 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_the_two_block_partition() {
37484 // Canonical two-block partition `[head; K] ++ [tail; N - K]`
37485 // at the substrate's ONE non-injective per-index projection
37486 // array `CompilerSpecIoStage::OPERATIONS` — the `(N, K) =
37487 // (4, 2)` corner with `(head, tail) = (REALIZE_TO_DISK,
37488 // LOAD_FROM_DISK)` MUST pass. Cross-partition coverage on
37489 // arities (3, 1), (4, 2), (5, 3) pins the INNER `while i < K`
37490 // and `while j < N` sweeps proceed through EACH position of
37491 // BOTH segments rather than short-circuiting on ANY early
37492 // position.
37493 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 1>(
37494 &["a", "b", "b"],
37495 "a",
37496 "b",
37497 );
37498 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 2>(
37499 &["a", "a", "b", "b"],
37500 "a",
37501 "b",
37502 );
37503 assert_str_array_is_concatenation_of_two_scalar_replicas::<5, 3>(
37504 &["a", "a", "a", "b", "b"],
37505 "a",
37506 "b",
37507 );
37508 }
37509
37510 #[test]
37511 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_the_k_zero_all_tail_degenerate(
37512 ) {
37513 // Degenerate `K = 0` corner collapses the array into the ALL-
37514 // TAIL replica `arr == [tail; N]`. The HEAD segment is empty
37515 // (`while i < 0` never enters); the TAIL segment covers
37516 // positions `[0, N)`. Cross-arity coverage on the `K = 0`
37517 // axis pins the HEAD-segment sweep's outer `while i < K`
37518 // terminates at `i = 0` when `K = 0`, and the TAIL-segment
37519 // sweep's outer `while j < N` starts at `j = K = 0` and
37520 // proceeds through EVERY position. A regression that hard-
37521 // coded `K > 0` or short-circuited on the empty HEAD-segment
37522 // arm would silently reject this legal degenerate shape.
37523 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 0>(
37524 &["x", "x", "x"],
37525 "unused",
37526 "x",
37527 );
37528 }
37529
37530 #[test]
37531 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_the_k_equals_n_all_head_degenerate(
37532 ) {
37533 // Degenerate `K = N` corner collapses the array into the ALL-
37534 // HEAD replica `arr == [head; N]`. The HEAD segment covers
37535 // positions `[0, N)`; the TAIL segment is empty (`while j <
37536 // N` starts at `j = K = N` and never enters). Peer to the
37537 // `K = 0` degenerate corner — together the two corners pin
37538 // the ONE-BLOCK degenerate shapes on BOTH endpoints of the
37539 // `K ∈ [0, N]` const-generic range. A regression that hard-
37540 // coded `K < N` or short-circuited on the empty TAIL-segment
37541 // arm would silently reject this legal degenerate shape.
37542 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 3>(
37543 &["x", "x", "x"],
37544 "x",
37545 "unused",
37546 );
37547 }
37548
37549 #[test]
37550 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_the_empty_array() {
37551 // Trivial `N = 0, K = 0` corner: the empty array trivially
37552 // satisfies BOTH the empty HEAD segment `[head; 0]` and the
37553 // empty TAIL segment `[tail; 0]`. Pins that the vacuous case
37554 // on the `[&'static str; 0]` corner of the const-N generic
37555 // passes without either segment sweep entering. Turbofish
37556 // binding required because there's no cue for the const
37557 // parameters on the empty array literal.
37558 assert_str_array_is_concatenation_of_two_scalar_replicas::<0, 0>(
37559 &[],
37560 "unused-head",
37561 "unused-tail",
37562 );
37563 }
37564
37565 #[test]
37566 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_head_equals_tail_single_block(
37567 ) {
37568 // Corner where `head == tail` collapses the two-block
37569 // partition into a SINGLE-BLOCK replica: `arr == [head; N] ==
37570 // [tail; N]`. The partition boundary `K` is irrelevant to the
37571 // observable byte-shape when `head` and `tail` are byte-
37572 // equal, so ANY `K ∈ [0, N]` accepts. Pins the docstring
37573 // note "the two scalars `head` and `tail` MAY be byte-equal
37574 // (in which case the helper degenerates to a SINGLE-BLOCK
37575 // replica-check)". Cross-K coverage inside `N = 4`: K = 0
37576 // (empty HEAD), K = 2 (interior split), K = 4 (empty TAIL).
37577 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 0>(
37578 &["z", "z", "z", "z"],
37579 "z",
37580 "z",
37581 );
37582 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 2>(
37583 &["z", "z", "z", "z"],
37584 "z",
37585 "z",
37586 );
37587 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 4>(
37588 &["z", "z", "z", "z"],
37589 "z",
37590 "z",
37591 );
37592 }
37593
37594 #[test]
37595 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_compiler_spec_io_stage_operations_partition(
37596 ) {
37597 // Runtime cross-check that the substrate's ONE non-injective
37598 // per-index projection array `CompilerSpecIoStage::OPERATIONS`
37599 // — the `(N, K) = (4, 2)` corner with `(head, tail) =
37600 // (REALIZE_TO_DISK_OPERATION, LOAD_FROM_DISK_OPERATION)` —
37601 // the substrate's module-level `const _` witness at `error.rs`
37602 // pins at COMPILE time is a well-formed BLOCK-CONSTANCY
37603 // relation at runtime too. The pair enforces the theorem at
37604 // TWO stages of the toolchain: the const witness fires FIRST
37605 // at `cargo check`, this runtime pin catches the drift at
37606 // `cargo test` as a safety net. Sibling posture to
37607 // `compiler_spec_io_stage_operations_align_with_all_by_index`
37608 // (which pins per-position equality via `stage.operation()`
37609 // at runtime) and to
37610 // `compiler_spec_io_stage_operations_partition_all_two_ways`
37611 // (which pins the two operation-label multiplicities at
37612 // runtime) — this witness carries the SAME theorem at the
37613 // ARRAY-LEVEL block-constant shape at COMPILE time.
37614 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 2>(
37615 &crate::error::CompilerSpecIoStage::OPERATIONS,
37616 crate::error::CompilerSpecIoStage::REALIZE_TO_DISK_OPERATION,
37617 crate::error::CompilerSpecIoStage::LOAD_FROM_DISK_OPERATION,
37618 );
37619 }
37620
37621 #[test]
37622 #[should_panic(expected = "HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION")]
37623 fn assert_str_array_is_concatenation_of_two_scalar_replicas_panics_at_runtime_on_head_segment_drift(
37624 ) {
37625 // NEGATIVE PIN — HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION
37626 // corner: an entry in the HEAD segment `arr[0..K)` that does
37627 // NOT byte-equal `head` MUST panic at runtime with the HEAD-
37628 // segment-named message. Pins the helper's HEAD-segment
37629 // reject arm — a regression that silently short-circuited on
37630 // the first HEAD position without checking the middle or
37631 // terminal HEAD positions would slip through the compile-
37632 // time witness's failure mode too. The offending str "x" at
37633 // position 1 (interior HEAD) pins the middle-of-HEAD drift
37634 // mode. `K = 3, N = 5` so the HEAD segment spans positions
37635 // `[0, 3)` — the interior drift at position 1 is inside the
37636 // HEAD, not the TAIL boundary.
37637 assert_str_array_is_concatenation_of_two_scalar_replicas::<5, 3>(
37638 &["a", "x", "a", "b", "b"],
37639 "a",
37640 "b",
37641 );
37642 }
37643
37644 #[test]
37645 #[should_panic(expected = "TAIL-SEGMENT-BLOCK-CONSTANCY-VIOLATION")]
37646 fn assert_str_array_is_concatenation_of_two_scalar_replicas_panics_at_runtime_on_tail_segment_drift(
37647 ) {
37648 // NEGATIVE PIN — TAIL-SEGMENT-BLOCK-CONSTANCY-VIOLATION
37649 // corner: an entry in the TAIL segment `arr[K..N)` that does
37650 // NOT byte-equal `tail` MUST panic at runtime with the TAIL-
37651 // segment-named message. Peer to the HEAD-segment drift arm
37652 // above — the SAME helper, DIFFERENT segment arm. Pins that
37653 // the outer `while j < N` sweep on the TAIL segment reaches
37654 // `j = N - 1` (else the terminal drift would slip through).
37655 // The offending str "x" at position 4 (terminal TAIL) with
37656 // `K = 3, N = 5` pins the terminal-TAIL drift mode.
37657 assert_str_array_is_concatenation_of_two_scalar_replicas::<5, 3>(
37658 &["a", "a", "a", "b", "x"],
37659 "a",
37660 "b",
37661 );
37662 }
37663
37664 #[test]
37665 #[should_panic(expected = "CARDINALITY-MISMATCH")]
37666 fn assert_str_array_is_concatenation_of_two_scalar_replicas_panics_at_runtime_on_arity_slip() {
37667 // NEGATIVE PIN — CARDINALITY-MISMATCH gate: a caller-side
37668 // turbofish arity slip on the `K` const-generic where `K > N`
37669 // MUST panic at runtime with the CARDINALITY-MISMATCH-named
37670 // message BEFORE any per-position sweep begins. Pins the
37671 // gate's placement at the TOP of the helper — a regression
37672 // that dropped the gate would silently degenerate into a
37673 // truncated HEAD-only sweep at the caller's `K` cap without
37674 // ever entering the TAIL sweep, silently accepting an array
37675 // whose TAIL positions carry arbitrary drift. The offending
37676 // `K = 5` against `N = 3` pins the strict `K > N` reject
37677 // arm; the LEGAL `K == N` degenerate is covered by a peer
37678 // acceptance test above.
37679 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 5>(
37680 &["a", "a", "a"],
37681 "a",
37682 "b",
37683 );
37684 }
37685
37686 #[test]
37687 fn assert_str_array_is_concatenation_of_two_scalar_replicas_panic_message_names_the_helper_and_block_constancy_violation_axis(
37688 ) {
37689 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-BLOCK-
37690 // CONSTANCY-VIOLATION arm: the panic message MUST begin with
37691 // the helper's own name AND identify the failed AXIS as
37692 // "HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION" so downstream
37693 // diagnostics route the drift back to (a) the helper by
37694 // string search on
37695 // `"assert_str_array_is_concatenation_of_two_scalar_replicas"`
37696 // and (b) the segment + axis by string search on
37697 // `"HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION"`. Sibling posture
37698 // to `assert_str_array_pairwise_distinct_panic_message_...`
37699 // on the sibling (str)-row INJECTIVITY provenance pin AND to
37700 // the sibling ROW-DUAL `_covered_by_three_str_arrays`'s
37701 // `"SET-STR-MISSING"` axis vocabulary — the shared
37702 // `"BLOCK-CONSTANCY-VIOLATION"` suffix lets callers grep
37703 // either the HEAD or TAIL segment arm by the shared suffix
37704 // pattern alone, while the `HEAD-` / `TAIL-` prefix
37705 // disambiguates the SEGMENT.
37706 let outcome = std::panic::catch_unwind(|| {
37707 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 2>(
37708 &["a", "x", "b"],
37709 "a",
37710 "b",
37711 );
37712 });
37713 let payload = outcome.expect_err(
37714 "assert_str_array_is_concatenation_of_two_scalar_replicas \
37715 must panic on a HEAD-segment drift — the reject-HEAD-drift \
37716 arm is one of the two BLOCK-CONSTANCY-VIOLATION failure \
37717 modes of the helper",
37718 );
37719 let msg = payload
37720 .downcast_ref::<&'static str>()
37721 .map(|s| (*s).to_owned())
37722 .or_else(|| payload.downcast_ref::<String>().cloned())
37723 .expect(
37724 "assert_str_array_is_concatenation_of_two_scalar_replicas \
37725 panic payload must be a static &str or String",
37726 );
37727 assert!(
37728 msg.contains("assert_str_array_is_concatenation_of_two_scalar_replicas"),
37729 "assert_str_array_is_concatenation_of_two_scalar_replicas \
37730 panic message {msg:?} must name the helper for \
37731 provenance-preserving failure diagnostics",
37732 );
37733 assert!(
37734 msg.contains("HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION"),
37735 "assert_str_array_is_concatenation_of_two_scalar_replicas \
37736 panic message {msg:?} must name the failed AXIS (\"HEAD-\
37737 SEGMENT-BLOCK-CONSTANCY-VIOLATION\") for axis-provenance-\
37738 preserving failure diagnostics",
37739 );
37740 }
37741
37742 // ── `assert_u8_array_slice_is_scalar_replica` — the (`u8`)-row
37743 // SLICE-BLOCK-CONSTANCY sibling of the (`&'static str`)-row FULL-
37744 // ARRAY-two-block-partition sibling
37745 // `assert_str_array_is_concatenation_of_two_scalar_replicas`.
37746 // Sibling posture: symmetric runtime-test surface (accept-canonical-
37747 // slice, accept-empty-slice-corners, accept-full-array-degenerate,
37748 // accept-sexp-shape-atomic-collapse, reject-slice-content-drift,
37749 // reject-start-out-of-bounds, reject-end-out-of-bounds, reject-
37750 // inverted-range, panic-message-provenance) specialised to the (u8)
37751 // sub-slice SINGLE-scalar block-constant shape. A regression that
37752 // silently weakens the helper (e.g. flipping `arr[i] != scalar`
37753 // to `==`, moving the START gate BELOW the sweep, narrowing the
37754 // sweep to `while i < END - 1` and missing the terminal position,
37755 // or returning early past ANY bounds gate) is caught by the
37756 // helper's OWN test surface rather than only surfacing as a false-
37757 // positive on the `SexpShape::HASH_DISCRIMINATORS[1..7)` witness.
37758
37759 #[test]
37760 fn assert_u8_array_slice_is_scalar_replica_accepts_a_canonical_middle_slice() {
37761 // Canonical sub-slice `arr[START..END) == [scalar; END - START]`
37762 // inside a longer array `arr` whose ENDPOINTS carry DIFFERENT
37763 // bytes than the slice. Pins the outer `while i < END` sweep
37764 // enters at `i = START` (skipping positions `[0..START)`) AND
37765 // terminates at `i = END` (skipping positions `[END..N)`) —
37766 // BOTH endpoint bytes DIFFER from `scalar` so a regression
37767 // that widened the sweep beyond the slice would fail here.
37768 assert_u8_array_slice_is_scalar_replica::<7, 1, 6>(&[9, 1, 1, 1, 1, 1, 9], 1);
37769 }
37770
37771 #[test]
37772 fn assert_u8_array_slice_is_scalar_replica_accepts_the_empty_slice_at_start_equals_end() {
37773 // LEGAL degenerate: `START == END` collapses the slice into an
37774 // empty range `[START..START)`. The sweep never enters the
37775 // loop body and the helper accepts. Cross-position coverage
37776 // pins the empty-slice acceptance at THREE distinct positions
37777 // (`START == 0` at the left endpoint, `START == 3` in the
37778 // interior, `START == N` at the right endpoint) so a
37779 // regression that hard-coded `START < END` OR panicked on the
37780 // `START == END` corner is caught on ALL THREE arms. Peer to
37781 // the `K == N` / `K == 0` degenerate corners of the sibling
37782 // `assert_str_array_is_concatenation_of_two_scalar_replicas`.
37783 assert_u8_array_slice_is_scalar_replica::<5, 0, 0>(&[7, 7, 7, 7, 7], 42);
37784 assert_u8_array_slice_is_scalar_replica::<5, 3, 3>(&[7, 7, 7, 7, 7], 42);
37785 assert_u8_array_slice_is_scalar_replica::<5, 5, 5>(&[7, 7, 7, 7, 7], 42);
37786 }
37787
37788 #[test]
37789 fn assert_u8_array_slice_is_scalar_replica_accepts_the_full_array_slice() {
37790 // Full-array-covering slice `[0..N)` collapses to the ALL-
37791 // scalar-replica shape `arr == [scalar; N]` (peer to the
37792 // `K == N` degenerate of the sibling str-row helper). Pins
37793 // that the sweep proceeds through EVERY position of the array
37794 // when `START = 0` and `END = N`. Cross-arity coverage on
37795 // `N ∈ {3, 6, 8}` pins the sweep's terminal-position visit
37796 // across a range of array cardinalities.
37797 assert_u8_array_slice_is_scalar_replica::<3, 0, 3>(&[5, 5, 5], 5);
37798 assert_u8_array_slice_is_scalar_replica::<6, 0, 6>(&[0, 0, 0, 0, 0, 0], 0);
37799 assert_u8_array_slice_is_scalar_replica::<8, 0, 8>(
37800 &[255, 255, 255, 255, 255, 255, 255, 255],
37801 255,
37802 );
37803 }
37804
37805 #[test]
37806 fn assert_u8_array_slice_is_scalar_replica_accepts_sexp_shape_atomic_collapse_slice() {
37807 // Runtime cross-check that the substrate's ONE MANY-TO-ONE-
37808 // COLLAPSE `[u8; N]` sub-slice `SexpShape::HASH_DISCRIMINATORS
37809 // [1..7)` — the six-slot atomic-outer-shape collapse onto
37810 // `AtomKind::OUTER_HASH_DISCRIMINATOR` — the substrate's
37811 // module-level `const _` witness at `ast.rs` pins at COMPILE
37812 // time is a well-formed SLICE-BLOCK-CONSTANCY relation at
37813 // runtime too. The pair enforces the theorem at TWO stages of
37814 // the toolchain: the const witness fires FIRST at `cargo
37815 // check`, this runtime pin catches the drift at `cargo test`
37816 // as a safety net. Sibling posture to the peer runtime cross-
37817 // check on the sibling
37818 // `assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_compiler_spec_io_stage_operations_partition`
37819 // — this witness carries the SAME class of MANY-TO-ONE-
37820 // COLLAPSE theorem at the ARRAY-slice level on the (u8) row
37821 // rather than the FULL-ARRAY-two-block level on the (str) row.
37822 assert_u8_array_slice_is_scalar_replica::<12, 1, 7>(
37823 &crate::error::SexpShape::HASH_DISCRIMINATORS,
37824 AtomKind::OUTER_HASH_DISCRIMINATOR,
37825 );
37826 }
37827
37828 #[test]
37829 #[should_panic(expected = "SLICE-BLOCK-CONSTANCY-VIOLATION")]
37830 fn assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_slice_content_drift() {
37831 // NEGATIVE PIN — SLICE-BLOCK-CONSTANCY-VIOLATION corner: an
37832 // entry in the slice `arr[START..END)` that does NOT byte-
37833 // equal `scalar` MUST panic at runtime with the slice-named
37834 // message. Pins the helper's slice-content reject arm — a
37835 // regression that silently short-circuited on the first slice
37836 // position without checking the middle or terminal slice
37837 // positions would slip through the compile-time witness's
37838 // failure mode too. The offending byte `9` at position `3`
37839 // (interior of the slice) with `START = 1, END = 6` pins the
37840 // middle-of-slice drift mode.
37841 assert_u8_array_slice_is_scalar_replica::<7, 1, 6>(&[0, 1, 1, 9, 1, 1, 0], 1);
37842 }
37843
37844 #[test]
37845 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
37846 fn assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_start_out_of_bounds() {
37847 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
37848 // turbofish arity slip on the `START` const-generic where
37849 // `START > N` MUST panic at runtime with the START-OUT-OF-
37850 // BOUNDS-named message BEFORE any per-position sweep begins.
37851 // Pins the gate's placement at the TOP of the helper — a
37852 // regression that dropped the gate would either silently
37853 // degenerate into a vacuous sweep OR panic deeper in `arr[i]`
37854 // bounds-checking with a helper-name-less panic message. The
37855 // offending `START = 7` against `N = 5` pins the strict
37856 // `START > N` reject arm; the LEGAL `START == N` empty
37857 // corner is covered by the peer acceptance test above.
37858 assert_u8_array_slice_is_scalar_replica::<5, 7, 7>(&[1, 1, 1, 1, 1], 1);
37859 }
37860
37861 #[test]
37862 #[should_panic(expected = "END-OUT-OF-BOUNDS")]
37863 fn assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_end_out_of_bounds() {
37864 // NEGATIVE PIN — END-OUT-OF-BOUNDS gate: a caller-side
37865 // turbofish arity slip on the `END` const-generic where
37866 // `END > N` MUST panic at runtime with the END-OUT-OF-BOUNDS-
37867 // named message. Peer to the START-OUT-OF-BOUNDS arm above —
37868 // the two gates jointly enforce `START, END ∈ [0..N]` before
37869 // any content sweep. The offending `END = 9` against `N = 5`
37870 // pins the strict `END > N` reject arm; the LEGAL `END == N`
37871 // slice-to-end corner is covered by the full-array acceptance
37872 // test above. `START = 0` sits in-bounds so the START gate
37873 // does not fire first.
37874 assert_u8_array_slice_is_scalar_replica::<5, 0, 9>(&[1, 1, 1, 1, 1], 1);
37875 }
37876
37877 #[test]
37878 #[should_panic(expected = "INVERTED-RANGE")]
37879 fn assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_inverted_range() {
37880 // NEGATIVE PIN — INVERTED-RANGE gate: a caller-side turbofish
37881 // typo that swaps `START` and `END` (both individually in-
37882 // bounds, but `START > END`) MUST panic at runtime with the
37883 // INVERTED-RANGE-named message. Pins that the strict `START
37884 // > END` slip fails-loud on a DISTINCT axis rather than
37885 // silently accepting the empty sweep — a regression that
37886 // dropped this gate would silently accept ANY array on the
37887 // swapped-turbofish call site. The offending `START = 5,
37888 // END = 2` against `N = 7` pins the strict `START > END`
37889 // reject arm; the LEGAL `START == END` empty corner is
37890 // covered by the peer acceptance test above.
37891 assert_u8_array_slice_is_scalar_replica::<7, 5, 2>(&[1, 1, 1, 1, 1, 1, 1], 1);
37892 }
37893
37894 #[test]
37895 fn assert_u8_array_slice_is_scalar_replica_panic_message_names_the_helper_and_slice_block_constancy_violation_axis(
37896 ) {
37897 // PANIC-MESSAGE PROVENANCE PIN — SLICE-BLOCK-CONSTANCY-
37898 // VIOLATION arm: the panic message MUST begin with the
37899 // helper's own name AND identify the failed AXIS as "SLICE-
37900 // BLOCK-CONSTANCY-VIOLATION" so downstream diagnostics route
37901 // the drift back to (a) the helper by string search on
37902 // `"assert_u8_array_slice_is_scalar_replica"` and (b) the
37903 // failed axis by string search on `"SLICE-BLOCK-CONSTANCY-
37904 // VIOLATION"`. Sibling posture to
37905 // `assert_str_array_is_concatenation_of_two_scalar_replicas_panic_message_...`
37906 // on the sibling (str)-row FULL-ARRAY BLOCK-CONSTANCY
37907 // provenance pin — the shared `"BLOCK-CONSTANCY-VIOLATION"`
37908 // suffix lets callers grep either the SUB-SLICE or FULL-
37909 // ARRAY variant by the shared suffix pattern alone, while
37910 // the `SLICE-` / `HEAD-SEGMENT-` / `TAIL-SEGMENT-` prefix
37911 // disambiguates the CONTRACT SHAPE.
37912 let outcome = std::panic::catch_unwind(|| {
37913 assert_u8_array_slice_is_scalar_replica::<5, 1, 4>(&[0, 1, 9, 1, 0], 1);
37914 });
37915 let payload = outcome.expect_err(
37916 "assert_u8_array_slice_is_scalar_replica must panic on a \
37917 slice-content drift — the reject-slice-drift arm is the \
37918 CONTENT failure mode of the helper",
37919 );
37920 let msg = payload
37921 .downcast_ref::<&'static str>()
37922 .map(|s| (*s).to_owned())
37923 .or_else(|| payload.downcast_ref::<String>().cloned())
37924 .expect(
37925 "assert_u8_array_slice_is_scalar_replica panic payload \
37926 must be a static &str or String",
37927 );
37928 assert!(
37929 msg.contains("assert_u8_array_slice_is_scalar_replica"),
37930 "assert_u8_array_slice_is_scalar_replica panic message \
37931 {msg:?} must name the helper for provenance-preserving \
37932 failure diagnostics",
37933 );
37934 assert!(
37935 msg.contains("SLICE-BLOCK-CONSTANCY-VIOLATION"),
37936 "assert_u8_array_slice_is_scalar_replica panic message \
37937 {msg:?} must name the failed AXIS (\"SLICE-BLOCK-\
37938 CONSTANCY-VIOLATION\") for axis-provenance-preserving \
37939 failure diagnostics",
37940 );
37941 }
37942
37943 // ── `assert_u8_array_slice_equals_u8_array` — the SLICE-EQUALS-
37944 // ARRAY sibling of `assert_u8_array_slice_is_scalar_replica` on
37945 // the SAME (u8) row. Sibling posture: same runtime-test surface
37946 // (accept-canonical-middle-slice, accept-empty-sub-array, accept-
37947 // full-array-degenerate, accept-sexp-shape-quote-tail-composition,
37948 // reject-positionwise-drift, reject-start-out-of-bounds, reject-
37949 // slice-length-out-of-bounds, panic-message-provenance)
37950 // specialised to the (u8) SUB-SLICE ARRAY-image composition shape.
37951 // A regression that silently weakens the helper (e.g. flipping
37952 // `full[START + i] != sub[i]` to `==`, dropping the `START`
37953 // offset from the outer read, moving the START gate BELOW the
37954 // sweep, narrowing the sweep to `while i < M - 1` and missing
37955 // the terminal position, or returning early past ANY bounds gate)
37956 // is caught by the helper's OWN test surface rather than only
37957 // surfacing as a false-positive on the
37958 // `SexpShape::HASH_DISCRIMINATORS[8..12] ==
37959 // QuoteForm::HASH_DISCRIMINATORS` witness.
37960
37961 #[test]
37962 fn assert_u8_array_slice_equals_u8_array_accepts_a_canonical_middle_slice() {
37963 // Canonical sub-slice `full[START..START + M) == sub[..]`
37964 // inside a longer array `full` whose ENDPOINTS carry
37965 // DIFFERENT bytes than the peer sub-array. Pins the outer
37966 // `while i < M` sweep reads `full[START + i]` at the
37967 // OFFSET position (not `full[i]`) — a regression that
37968 // dropped the `START` offset would compare `full[0..M)`
37969 // against `sub[..]` and pass on `full[0]=9 != sub[0]=1`
37970 // silently or panic on the wrong axis. `START = 1` pins
37971 // the sweep skips position `[0..START)` and reads only
37972 // `[1..1+3) = [1..4)`.
37973 assert_u8_array_slice_equals_u8_array::<7, 3, 1>(&[9, 3, 4, 5, 9, 9, 9], &[3, 4, 5]);
37974 }
37975
37976 #[test]
37977 fn assert_u8_array_slice_equals_u8_array_accepts_the_empty_sub_array() {
37978 // LEGAL degenerate: `M == 0` collapses the sub-array into
37979 // an empty listing `[]`. The sweep never enters the loop
37980 // body and the helper accepts. Cross-position coverage
37981 // pins the empty-sub-array acceptance at THREE distinct
37982 // `START` positions (`START == 0` at the left endpoint,
37983 // `START == 3` in the interior, `START == N` at the right
37984 // endpoint — the latter is the corner `START == N` combined
37985 // with `M == 0` that the START-OUT-OF-BOUNDS gate's
37986 // inclusive upper bound must accept). A regression that
37987 // hard-coded `START < N` OR panicked on the `M == 0` corner
37988 // is caught on ALL THREE arms.
37989 assert_u8_array_slice_equals_u8_array::<5, 0, 0>(&[7, 7, 7, 7, 7], &[]);
37990 assert_u8_array_slice_equals_u8_array::<5, 0, 3>(&[7, 7, 7, 7, 7], &[]);
37991 assert_u8_array_slice_equals_u8_array::<5, 0, 5>(&[7, 7, 7, 7, 7], &[]);
37992 }
37993
37994 #[test]
37995 fn assert_u8_array_slice_equals_u8_array_accepts_the_full_array_degenerate() {
37996 // Full-array-covering slice `M == N, START == 0` collapses
37997 // to the ALL-positions-equal-peer-array shape `full == sub`
37998 // pointwise. Pins that the sweep proceeds through EVERY
37999 // position of the outer array when `START = 0` and `M = N`.
38000 // Cross-arity coverage on `N ∈ {3, 4, 6}` pins the sweep's
38001 // terminal-position visit across a range of array
38002 // cardinalities.
38003 assert_u8_array_slice_equals_u8_array::<3, 3, 0>(&[10, 20, 30], &[10, 20, 30]);
38004 assert_u8_array_slice_equals_u8_array::<4, 4, 0>(&[3, 4, 5, 6], &[3, 4, 5, 6]);
38005 assert_u8_array_slice_equals_u8_array::<6, 6, 0>(&[0, 1, 2, 3, 4, 5], &[0, 1, 2, 3, 4, 5]);
38006 }
38007
38008 #[test]
38009 fn assert_u8_array_slice_equals_u8_array_accepts_sexp_shape_quote_tail_composition() {
38010 // Runtime cross-check that the substrate's ONE
38011 // POSITIONWISE-COMPOSITION `[u8; N]` sub-slice
38012 // `SexpShape::HASH_DISCRIMINATORS[8..12]` byte-for-byte
38013 // equal to the peer `QuoteForm::HASH_DISCRIMINATORS` is a
38014 // well-formed SLICE-EQUALS-ARRAY relation at runtime too.
38015 // The pair enforces the theorem at TWO stages of the
38016 // toolchain: the const witness fires FIRST at `cargo
38017 // check`, this runtime pin catches the drift at `cargo
38018 // test` as a safety net. Sibling posture to the peer
38019 // runtime cross-check
38020 // `assert_u8_array_slice_is_scalar_replica_accepts_sexp_shape_atomic_collapse_slice`
38021 // — this witness carries the SLICE-EQUALS-ARRAY theorem
38022 // (positionwise composition with a peer array of arity
38023 // `M`) on the SAME (u8) row's quote-family tail slice
38024 // rather than the MANY-TO-ONE-COLLAPSE theorem
38025 // (positionwise composition with a SCALAR) on the atomic-
38026 // collapse mid slice.
38027 assert_u8_array_slice_equals_u8_array::<12, 4, 8>(
38028 &crate::error::SexpShape::HASH_DISCRIMINATORS,
38029 &QuoteForm::HASH_DISCRIMINATORS,
38030 );
38031 }
38032
38033 #[test]
38034 fn assert_u8_array_slice_equals_u8_array_accepts_sub_carving_hash_discriminators_per_position_order(
38035 ) {
38036 // Runtime cross-check that the FOUR sub-carving
38037 // `HASH_DISCRIMINATORS` arrays each byte-equal their
38038 // canonical literal-byte listing pointwise at the FULL-ARRAY
38039 // corner (`M == N`, `START == 0`). Runs the SAME helper the
38040 // four `const _` witnesses above line 5842 in this file run
38041 // at rustc time — a runtime safety net enforcing the
38042 // theorem at BOTH stages of the toolchain (const at `cargo
38043 // check`, runtime at `cargo test`). A regression that
38044 // renamed one of the per-role `*_HASH_DISCRIMINATOR` aliases
38045 // (or drifted its literal byte value at the declaration
38046 // site, or reordered a slot in the outer array's
38047 // initializer) fails HERE at the substrate callsite AND at
38048 // the const witness above. Peer of
38049 // `assert_u8_array_slice_equals_u8_array_accepts_sexp_shape_quote_tail_composition`
38050 // above — that witness carries the SLICE-EQUALS-ARRAY
38051 // theorem for the OUTER container against a SUB-CARVING
38052 // array; this witness carries the theorem for each of the
38053 // FOUR sub-carving arrays against a literal-byte listing.
38054 //
38055 // The four sub-carvings appear here in canonical order
38056 // (`{0..=6}` outer-`Sexp` cache-key partition, top-to-
38057 // bottom):
38058 // * `StructuralKind::HASH_DISCRIMINATORS == [0u8, 2]` — the
38059 // structural-residual carving. Non-contiguous (gap at
38060 // `1u8` reserved for the atomic-carve outer marker).
38061 // * `AtomKind::HASH_DISCRIMINATORS == [0u8, 1, 2, 3, 4, 5]`
38062 // — the nested-inner atomic-payload carving specialising
38063 // the outer `1u8` atomic marker inside `Hash for Atom`.
38064 // * `QuoteForm::HASH_DISCRIMINATORS == [3u8, 4, 5, 6]` —
38065 // the quote-family carving covering the four homoiconic
38066 // prefixes.
38067 // * `UnquoteForm::HASH_DISCRIMINATORS == [5u8, 6]` — the
38068 // two-of-four substitution subset of `QuoteForm`
38069 // projecting through `to_quote_form()`.
38070 assert_u8_array_slice_equals_u8_array::<2, 2, 0>(
38071 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
38072 &[0u8, 2],
38073 );
38074 assert_u8_array_slice_equals_u8_array::<6, 6, 0>(
38075 &AtomKind::HASH_DISCRIMINATORS,
38076 &[0u8, 1, 2, 3, 4, 5],
38077 );
38078 assert_u8_array_slice_equals_u8_array::<4, 4, 0>(
38079 &QuoteForm::HASH_DISCRIMINATORS,
38080 &[3u8, 4, 5, 6],
38081 );
38082 assert_u8_array_slice_equals_u8_array::<2, 2, 0>(
38083 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
38084 &[5u8, 6],
38085 );
38086 }
38087
38088 /// Runtime SLICE-EQUALS-ARRAY safety net for the (UnquoteForm ⊂
38089 /// QuoteForm) 2-of-4 sub-carve on the (u8) HASH_DISCRIMINATORS
38090 /// vocabulary axis — mirrors the module-level `const _: () =
38091 /// assert_u8_array_slice_equals_u8_array::<4, 2, 2>(&QuoteForm::
38092 /// HASH_DISCRIMINATORS, &UnquoteForm::HASH_DISCRIMINATORS)` witness
38093 /// added below line 5617 in this file. The `const _` witness fires
38094 /// FIRST at `cargo check`; this runtime pin catches the ARRAY-LEVEL
38095 /// positionwise drift at `cargo test` as a safety net enforcing the
38096 /// theorem at BOTH stages of the toolchain.
38097 ///
38098 /// (U8)-row peer to `assert_str_array_slice_equals_str_array_
38099 /// accepts_unquote_form_sub_carve_of_quote_form` at `error.rs`'s
38100 /// tests submodule — that test sweeps the SAME 2-of-4 sub-carve at
38101 /// the (str) row across three vocabulary axes (`LABELS`,
38102 /// `MARKERS↔PREFIXES`, `IAC_FORGE_TAGS`); this test sweeps the same
38103 /// carve at the (u8) row on the ONE `HASH_DISCRIMINATORS` axis.
38104 /// Together the four runtime witnesses close the (element-type ×
38105 /// vocabulary-axis) matrix of the substitution-subset carve at the
38106 /// SLICE-EQUALS positionwise-composition contract, on the runtime-
38107 /// pin sibling face of the four-witness compile-time cluster.
38108 ///
38109 /// A regression that (a) swaps `UnquoteForm::HASH_DISCRIMINATORS`
38110 /// from `[UNQUOTE_HASH_DISCRIMINATOR, SPLICE_HASH_DISCRIMINATOR]`
38111 /// to `[SPLICE_HASH_DISCRIMINATOR, UNQUOTE_HASH_DISCRIMINATOR]`, or
38112 /// (b) reorders `QuoteForm::HASH_DISCRIMINATORS` such that the two
38113 /// `UnquoteForm` bytes no longer sit contiguously at slots
38114 /// `[2..4)`, fails HERE with the `SLICE-EQUALS-ARRAY-VIOLATION`
38115 /// axis panic naming the drifted position — where the sibling
38116 /// SET-level SUBSET safety-net stays silent (both bytes still
38117 /// appear in the superset, just at different positions).
38118 #[test]
38119 fn assert_u8_array_slice_equals_u8_array_accepts_unquote_form_sub_carve_of_quote_form() {
38120 assert_u8_array_slice_equals_u8_array::<4, 2, 2>(
38121 &QuoteForm::HASH_DISCRIMINATORS,
38122 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
38123 );
38124 }
38125
38126 #[test]
38127 #[should_panic(expected = "SLICE-EQUALS-ARRAY-VIOLATION")]
38128 fn assert_u8_array_slice_equals_u8_array_panics_at_runtime_on_positionwise_drift() {
38129 // NEGATIVE PIN — SLICE-EQUALS-ARRAY-VIOLATION corner: a
38130 // byte at some position in `full[START..START + M)` that
38131 // does NOT byte-equal the peer sub-array `sub` at the
38132 // offset-matched position MUST panic at runtime with the
38133 // slice-named message. Pins the helper's positionwise-
38134 // drift reject arm — a regression that silently short-
38135 // circuited on the first slice position without checking
38136 // the middle or terminal slice positions would slip
38137 // through the compile-time witness's failure mode too.
38138 // The offending byte `9` at outer position `3` (interior
38139 // of the sub-slice `[1..4)`, offset `2` inside `sub`)
38140 // pins the middle-of-slice drift mode.
38141 assert_u8_array_slice_equals_u8_array::<5, 3, 1>(&[0, 3, 4, 9, 0], &[3, 4, 5]);
38142 }
38143
38144 #[test]
38145 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
38146 fn assert_u8_array_slice_equals_u8_array_panics_at_runtime_on_start_out_of_bounds() {
38147 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
38148 // turbofish arity slip on the `START` const-generic where
38149 // `START > N` MUST panic at runtime with the START-OUT-OF-
38150 // BOUNDS-named message BEFORE the peer SLICE-LENGTH-OUT-OF-
38151 // BOUNDS gate reads `N - START` (which would `usize`-
38152 // underflow had this gate not caught the slip first). Pins
38153 // the gate's placement at the TOP of the helper — a
38154 // regression that dropped the gate would either underflow
38155 // subtraction at the peer gate OR panic deeper in
38156 // `full[START + i]` bounds-checking with a helper-name-less
38157 // panic message. The offending `START = 7` against `N = 5`
38158 // pins the strict `START > N` reject arm; the LEGAL
38159 // `START == N` empty-slice-at-right-endpoint corner is
38160 // covered by the peer acceptance test above.
38161 assert_u8_array_slice_equals_u8_array::<5, 0, 7>(&[1, 1, 1, 1, 1], &[]);
38162 }
38163
38164 #[test]
38165 #[should_panic(expected = "SLICE-LENGTH-OUT-OF-BOUNDS")]
38166 fn assert_u8_array_slice_equals_u8_array_panics_at_runtime_on_slice_length_out_of_bounds() {
38167 // NEGATIVE PIN — SLICE-LENGTH-OUT-OF-BOUNDS gate: a peer
38168 // sub-array arity `M` that exceeds the outer array's tail
38169 // cardinality `N - START` MUST panic at runtime with the
38170 // slice-length-out-of-bounds-named message. Peer gate to
38171 // the START-OUT-OF-BOUNDS arm above — the two gates
38172 // jointly enforce `START ≤ N` and `M ≤ N - START` before
38173 // any content sweep. The offending `M = 5` against
38174 // `N - START = 5 - 3 = 2` pins the strict `M > N - START`
38175 // reject arm; the LEGAL exact-fit corner `M == N - START`
38176 // is covered by the middle-slice acceptance test above.
38177 assert_u8_array_slice_equals_u8_array::<5, 5, 3>(&[1, 1, 1, 1, 1], &[1, 1, 1, 1, 1]);
38178 }
38179
38180 #[test]
38181 fn assert_u8_array_slice_equals_u8_array_panic_message_names_the_helper_and_slice_equals_array_violation_axis(
38182 ) {
38183 // PANIC-MESSAGE PROVENANCE PIN — SLICE-EQUALS-ARRAY-
38184 // VIOLATION arm: the panic message MUST begin with the
38185 // helper's own name AND identify the failed AXIS as
38186 // "SLICE-EQUALS-ARRAY-VIOLATION" so downstream diagnostics
38187 // route the drift back to (a) the helper by string search
38188 // on `"assert_u8_array_slice_equals_u8_array"` and (b) the
38189 // failed axis by string search on `"SLICE-EQUALS-ARRAY-
38190 // VIOLATION"`. Sibling posture to
38191 // `assert_u8_array_slice_is_scalar_replica_panic_message_names_the_helper_and_slice_block_constancy_violation_axis`
38192 // on the sibling SLICE-BLOCK-CONSTANCY corner — the shared
38193 // `"SLICE-"` prefix lets callers grep either the SINGLE-
38194 // scalar-image or ARRAY-of-length-`M`-image variant by
38195 // the shared prefix, while the `-BLOCK-CONSTANCY-` /
38196 // `-EQUALS-ARRAY-` infix disambiguates the CONTRACT
38197 // SHAPE.
38198 let outcome = std::panic::catch_unwind(|| {
38199 assert_u8_array_slice_equals_u8_array::<5, 3, 1>(&[0, 3, 4, 9, 0], &[3, 4, 5]);
38200 });
38201 let payload = outcome.expect_err(
38202 "assert_u8_array_slice_equals_u8_array must panic on a \
38203 positionwise drift — the reject-positionwise-drift arm \
38204 is the CONTENT failure mode of the helper",
38205 );
38206 let msg = payload
38207 .downcast_ref::<&'static str>()
38208 .map(|s| (*s).to_owned())
38209 .or_else(|| payload.downcast_ref::<String>().cloned())
38210 .expect(
38211 "assert_u8_array_slice_equals_u8_array panic payload \
38212 must be a static &str or String",
38213 );
38214 assert!(
38215 msg.contains("assert_u8_array_slice_equals_u8_array"),
38216 "assert_u8_array_slice_equals_u8_array panic message \
38217 {msg:?} must name the helper for provenance-preserving \
38218 failure diagnostics",
38219 );
38220 assert!(
38221 msg.contains("SLICE-EQUALS-ARRAY-VIOLATION"),
38222 "assert_u8_array_slice_equals_u8_array panic message \
38223 {msg:?} must name the failed AXIS (\"SLICE-EQUALS-\
38224 ARRAY-VIOLATION\") for axis-provenance-preserving \
38225 failure diagnostics",
38226 );
38227 }
38228
38229 // ── `assert_u8_array_pairwise_distinct` — the `u8` element-type
38230 // sibling of `assert_char_array_pairwise_distinct` and
38231 // `assert_str_array_pairwise_distinct`. Sibling posture: same
38232 // runtime-test surface (accept-empty, accept-singleton, accept-
38233 // every-family-wide-substrate-array, reject-binary, reject-non-
38234 // adjacent, reject-terminal, panic-message-provenance) restricted
38235 // to the `u8` element type. A regression that silently weakens the
38236 // helper (e.g. flipping `==` to `!=`, dropping the inner `j` loop,
38237 // or returning early on collision) is caught by the helper's OWN
38238 // test surface rather than only surfacing as a false-positive on
38239 // some future `[u8; N]`-typed discriminator array's distinctness pin.
38240
38241 #[test]
38242 fn assert_u8_array_pairwise_distinct_accepts_the_empty_array() {
38243 // Empty array — vacuously pairwise distinct (no pair to
38244 // collide). The compile-time `const _: () =
38245 // assert_u8_array_pairwise_distinct(&EMPTY);` would land on
38246 // this arm, so the runtime call MUST return normally.
38247 assert_u8_array_pairwise_distinct::<0>(&[]);
38248 }
38249
38250 #[test]
38251 fn assert_u8_array_pairwise_distinct_accepts_singleton_arrays() {
38252 // Singleton array — vacuously pairwise distinct (only one
38253 // element, no pair). Cross-arity coverage on the `[u8; 1]`
38254 // corner of the const-N generic.
38255 assert_u8_array_pairwise_distinct(&[0u8]);
38256 assert_u8_array_pairwise_distinct(&[AtomKind::SYMBOL_HASH_DISCRIMINATOR]);
38257 }
38258
38259 #[test]
38260 fn assert_u8_array_pairwise_distinct_accepts_every_family_wide_substrate_array() {
38261 // Runtime cross-check that the SAME four arrays the module-
38262 // level `const _: () = ...` witnesses cover at COMPILE time
38263 // are pairwise distinct. A regression that removes ONE of
38264 // the `const _` witnesses would still leave THIS runtime pin
38265 // as a safety net; the const witness fires FIRST at `cargo
38266 // check`, this runtime pin catches the collision at `cargo
38267 // test`. The pair enforces the theorem at TWO stages of the
38268 // toolchain. `SexpShape::HASH_DISCRIMINATORS` is excluded
38269 // per the intentionally-non-injective twelve-shape → seven-
38270 // byte collapse rule documented on the helper.
38271 assert_u8_array_pairwise_distinct(&AtomKind::HASH_DISCRIMINATORS);
38272 assert_u8_array_pairwise_distinct(&QuoteForm::HASH_DISCRIMINATORS);
38273 assert_u8_array_pairwise_distinct(&crate::error::StructuralKind::HASH_DISCRIMINATORS);
38274 assert_u8_array_pairwise_distinct(&crate::error::UnquoteForm::HASH_DISCRIMINATORS);
38275 }
38276
38277 #[test]
38278 #[should_panic(expected = "assert_u8_array_pairwise_distinct")]
38279 fn assert_u8_array_pairwise_distinct_panics_at_runtime_on_binary_collision() {
38280 // NEGATIVE PIN — binary corner: a two-element array carrying
38281 // the same byte twice MUST panic at runtime (the const-eval
38282 // panic surfaces normally when the function is invoked from a
38283 // runtime context, not just a `const _` context). Pins the
38284 // helper's OWN reject-collision arm — a regression that
38285 // silently returned without panicking on a duplicate would
38286 // slip through the compile-time witnesses' failure mode too.
38287 assert_u8_array_pairwise_distinct(&[0u8, 0u8]);
38288 }
38289
38290 #[test]
38291 #[should_panic(expected = "assert_u8_array_pairwise_distinct")]
38292 fn assert_u8_array_pairwise_distinct_panics_at_runtime_on_non_adjacent_collision() {
38293 // NEGATIVE PIN — non-adjacent corner: the collision fires on
38294 // ANY (i, j) pair with i < j, not just the adjacent (0, 1)
38295 // corner. Pins the nested-loop shape of the helper — a
38296 // regression that walked ONLY the adjacent pairs (i.e., swept
38297 // `while i + 1 < N { if arr[i] == arr[i+1] { panic } … }`)
38298 // would silently accept `[0, 1, 0]` (non-adjacent collision
38299 // at positions 0 and 2), missing the contract.
38300 assert_u8_array_pairwise_distinct(&[0u8, 1u8, 0u8]);
38301 }
38302
38303 #[test]
38304 #[should_panic(expected = "assert_u8_array_pairwise_distinct")]
38305 fn assert_u8_array_pairwise_distinct_panics_at_runtime_on_terminal_collision() {
38306 // NEGATIVE PIN — terminal corner: the collision at the LAST
38307 // pair (positions N-2 and N-1) MUST also fire. Pins the outer
38308 // `while i < N` bound — a regression that walked `while i <
38309 // N - 1` (dropping the last row) would silently accept a
38310 // collision at the tail.
38311 assert_u8_array_pairwise_distinct(&[0u8, 1u8, 2u8, 3u8, 3u8]);
38312 }
38313
38314 #[test]
38315 #[should_panic(expected = "assert_u8_array_pairwise_distinct")]
38316 fn assert_u8_array_pairwise_distinct_panics_at_runtime_on_boundary_byte_collision() {
38317 // NEGATIVE PIN — u8-domain boundary corner: the reject-arm
38318 // fires equivalently at the two u8-domain endpoints (`0x00`,
38319 // `0xff`). Pins the element-type-native `==` on `u8` — a
38320 // regression that widened the comparison via `as u32` (from
38321 // an over-mechanical copy of the char sibling's shape) or
38322 // via a signed-cast (`as i8` collapsing `0xff` to `-1`)
38323 // would still fire on the `0x00` corner AND on the
38324 // `0xff` corner because both survive any widening; a truly
38325 // load-bearing regression here would silently drop the
38326 // comparison altogether.
38327 assert_u8_array_pairwise_distinct(&[0xffu8, 0xffu8]);
38328 }
38329
38330 #[test]
38331 fn assert_u8_array_pairwise_distinct_panic_message_names_the_helper() {
38332 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST begin
38333 // with the helper's own name so downstream diagnostics
38334 // (`cargo check` const-eval error output, test-suite failure
38335 // reports) route the drift back to the helper by string
38336 // search — the family-wide contract's failure mode surfaces
38337 // as an identifiable panic-message prefix rather than as an
38338 // opaque const-eval error. Sibling posture to the runtime
38339 // pairwise-distinctness tests that name the ARRAY in their
38340 // failure message; this pin names the HELPER.
38341 let outcome = std::panic::catch_unwind(|| {
38342 assert_u8_array_pairwise_distinct(&[7u8, 7u8]);
38343 });
38344 let payload = outcome.expect_err(
38345 "assert_u8_array_pairwise_distinct must panic on a \
38346 duplicate — the reject-collision arm is the point of \
38347 the helper",
38348 );
38349 let msg = payload
38350 .downcast_ref::<&'static str>()
38351 .map(|s| (*s).to_owned())
38352 .or_else(|| payload.downcast_ref::<String>().cloned())
38353 .expect(
38354 "assert_u8_array_pairwise_distinct panic payload \
38355 must be a static &str or String",
38356 );
38357 assert!(
38358 msg.contains("assert_u8_array_pairwise_distinct"),
38359 "assert_u8_array_pairwise_distinct panic message \
38360 {msg:?} must name the helper for provenance-preserving \
38361 failure diagnostics",
38362 );
38363 }
38364
38365 // ── `assert_char_pair_array_bijective` — the product-element
38366 // sibling of the three scalar-element `assert_{char,str,u8}_array_
38367 // pairwise_distinct` compile-time contract verifiers, restricted
38368 // to the `(char, char)` product element type at the paired
38369 // escape-table substrate vocabulary (`Atom::NAMED_ESCAPE_TABLE`,
38370 // `Atom::ESCAPE_TABLE`). The runtime test surface here matches
38371 // the scalar-sibling shape (accept-empty, accept-singleton,
38372 // accept-every-family-wide-substrate-array, reject-left-column-
38373 // collision, reject-right-column-collision, reject-non-adjacent,
38374 // reject-terminal, panic-message-provenance-left, panic-message-
38375 // provenance-right) split across BOTH columns so a regression that
38376 // silently weakens the helper on EITHER column (e.g. dropping ONE
38377 // of the two `if arr[i].{0,1} == arr[j].{0,1}` checks, or
38378 // conflating the two `panic!` calls into one column-anonymous
38379 // message) is caught by the helper's OWN test surface rather than
38380 // only surfacing as a false-positive on some future
38381 // `[(char, char); N]`-typed paired array's bijection pin.
38382
38383 #[test]
38384 fn assert_char_pair_array_bijective_accepts_the_empty_array() {
38385 // Empty array — vacuously bijective (no pair to collide on
38386 // EITHER column). The compile-time `const _: () =
38387 // assert_char_pair_array_bijective(&EMPTY);` would land on
38388 // this arm, so the runtime call MUST return normally.
38389 assert_char_pair_array_bijective::<0>(&[]);
38390 }
38391
38392 #[test]
38393 fn assert_char_pair_array_bijective_accepts_singleton_arrays() {
38394 // Singleton array — vacuously bijective (only one pair, so
38395 // no cross-pair collision is possible on EITHER column even
38396 // if the two components alias each other WITHIN the pair —
38397 // that's not a bijection failure, it's the pattern-equals-
38398 // value SELF-escape shape at `SELF_ESCAPE_TABLE`'s two rows).
38399 // Cross-arity coverage on the `[(char, char); 1]` corner of
38400 // the const-N generic.
38401 assert_char_pair_array_bijective(&[('a', 'b')]);
38402 assert_char_pair_array_bijective(&[('x', 'x')]);
38403 }
38404
38405 #[test]
38406 fn assert_char_pair_array_bijective_accepts_every_family_wide_substrate_array() {
38407 // Runtime cross-check that the SAME two paired arrays the
38408 // module-level `const _: () = ...` witnesses cover at COMPILE
38409 // time are bijective. A regression that removes ONE of the
38410 // `const _` witnesses would still leave THIS runtime pin as a
38411 // safety net; the const witness fires FIRST at `cargo check`,
38412 // this runtime pin catches the collision at `cargo test`. The
38413 // pair enforces the theorem at TWO stages of the toolchain.
38414 assert_char_pair_array_bijective(&Atom::NAMED_ESCAPE_TABLE);
38415 assert_char_pair_array_bijective(&Atom::ESCAPE_TABLE);
38416 }
38417
38418 #[test]
38419 #[should_panic(expected = "assert_char_pair_array_bijective: LEFT column")]
38420 fn assert_char_pair_array_bijective_panics_at_runtime_on_left_column_collision() {
38421 // NEGATIVE PIN — LEFT-column corner: a two-element array
38422 // whose two SOURCE chars alias (regardless of whether the two
38423 // DECODED chars alias) MUST panic at runtime with the LEFT-
38424 // column-named message. Pins the helper's OWN LEFT-column
38425 // reject-arm — a regression that silently returned without
38426 // panicking on a source-column duplicate would slip through
38427 // the compile-time witnesses' failure mode too. The two
38428 // distinct DECODED chars witness that the RIGHT column is
38429 // INTACT — the panic MUST fire specifically on the LEFT-
38430 // column disjointness failure.
38431 assert_char_pair_array_bijective(&[('a', 'x'), ('a', 'y')]);
38432 }
38433
38434 #[test]
38435 #[should_panic(expected = "assert_char_pair_array_bijective: RIGHT column")]
38436 fn assert_char_pair_array_bijective_panics_at_runtime_on_right_column_collision() {
38437 // NEGATIVE PIN — RIGHT-column corner: a two-element array
38438 // whose two DECODED chars alias (with distinct SOURCE chars
38439 // so the LEFT column is intact) MUST panic at runtime with
38440 // the RIGHT-column-named message. Column-provenance in the
38441 // panic message is load-bearing: downstream diagnostics
38442 // route the drift back to the failed COLUMN (LEFT vs.
38443 // RIGHT) by string search — the two panic sites MUST NOT
38444 // collapse into one column-anonymous message.
38445 assert_char_pair_array_bijective(&[('a', 'x'), ('b', 'x')]);
38446 }
38447
38448 #[test]
38449 #[should_panic(expected = "assert_char_pair_array_bijective")]
38450 fn assert_char_pair_array_bijective_panics_at_runtime_on_non_adjacent_collision() {
38451 // NEGATIVE PIN — non-adjacent corner: the collision fires on
38452 // ANY (i, j) pair with i < j, not just the adjacent (0, 1)
38453 // corner. Pins the nested-loop shape of the helper — a
38454 // regression that walked ONLY the adjacent pairs (i.e., swept
38455 // `while i + 1 < N { if arr[i].0 == arr[i+1].0 { panic } … }`)
38456 // would silently accept the non-adjacent collision at
38457 // positions 0 and 2 tested here (a LEFT-column collision
38458 // `'a'` at [0].0 and [2].0, with `'b'` at [1].0 breaking
38459 // adjacency).
38460 assert_char_pair_array_bijective(&[('a', 'x'), ('b', 'y'), ('a', 'z')]);
38461 }
38462
38463 #[test]
38464 #[should_panic(expected = "assert_char_pair_array_bijective")]
38465 fn assert_char_pair_array_bijective_panics_at_runtime_on_terminal_collision() {
38466 // NEGATIVE PIN — terminal corner: the collision at the LAST
38467 // pair (positions N-2 and N-1) MUST also fire. Pins the outer
38468 // `while i < N` bound — a regression that walked `while i <
38469 // N - 1` (dropping the last row) would silently accept a
38470 // collision at the tail. Uses a RIGHT-column collision at
38471 // the terminal pair to also exercise the second (RIGHT)
38472 // panic arm's terminal-index reachability, symmetric to the
38473 // LEFT-column non-adjacent pin above.
38474 assert_char_pair_array_bijective(&[
38475 ('a', 'w'),
38476 ('b', 'x'),
38477 ('c', 'y'),
38478 ('d', 'z'),
38479 ('e', 'z'),
38480 ]);
38481 }
38482
38483 #[test]
38484 fn assert_char_pair_array_bijective_panic_message_names_the_helper_and_left_column() {
38485 // PANIC-MESSAGE PROVENANCE PIN — LEFT-column arm: the panic
38486 // message MUST begin with the helper's own name AND identify
38487 // the failed COLUMN as "LEFT column" so downstream diagnostics
38488 // route the drift back to (a) the helper by string search on
38489 // `"assert_char_pair_array_bijective"` and (b) the column by
38490 // string search on `"LEFT column"`. Sibling posture to the
38491 // scalar-sibling `_panic_message_names_the_helper` tests
38492 // (which name only the helper); the column-provenance
38493 // extension is load-bearing on the paired-array vocabulary
38494 // where a bijection failure has TWO distinguishable failure
38495 // modes (source-column non-injective vs. decoded-column
38496 // non-injective).
38497 let outcome = std::panic::catch_unwind(|| {
38498 assert_char_pair_array_bijective(&[('a', 'x'), ('a', 'y')]);
38499 });
38500 let payload = outcome.expect_err(
38501 "assert_char_pair_array_bijective must panic on a LEFT-\
38502 column duplicate — the reject-collision arm is the \
38503 point of the helper",
38504 );
38505 let msg = payload
38506 .downcast_ref::<&'static str>()
38507 .map(|s| (*s).to_owned())
38508 .or_else(|| payload.downcast_ref::<String>().cloned())
38509 .expect(
38510 "assert_char_pair_array_bijective panic payload \
38511 must be a static &str or String",
38512 );
38513 assert!(
38514 msg.contains("assert_char_pair_array_bijective"),
38515 "assert_char_pair_array_bijective LEFT-column panic \
38516 message {msg:?} must name the helper for provenance-\
38517 preserving failure diagnostics",
38518 );
38519 assert!(
38520 msg.contains("LEFT column"),
38521 "assert_char_pair_array_bijective LEFT-column panic \
38522 message {msg:?} must name the failed COLUMN (\"LEFT \
38523 column\") for column-provenance-preserving failure \
38524 diagnostics",
38525 );
38526 }
38527
38528 #[test]
38529 fn assert_char_pair_array_bijective_panic_message_names_the_helper_and_right_column() {
38530 // PANIC-MESSAGE PROVENANCE PIN — RIGHT-column arm: the panic
38531 // message MUST begin with the helper's own name AND identify
38532 // the failed COLUMN as "RIGHT column". Column-symmetric
38533 // sibling of the LEFT-column pin above — a regression that
38534 // silently unified the two panic sites into ONE column-
38535 // anonymous message would collapse THIS pin's RIGHT-column
38536 // substring assertion.
38537 let outcome = std::panic::catch_unwind(|| {
38538 assert_char_pair_array_bijective(&[('a', 'x'), ('b', 'x')]);
38539 });
38540 let payload = outcome.expect_err(
38541 "assert_char_pair_array_bijective must panic on a RIGHT-\
38542 column duplicate — the reject-collision arm is the \
38543 point of the helper",
38544 );
38545 let msg = payload
38546 .downcast_ref::<&'static str>()
38547 .map(|s| (*s).to_owned())
38548 .or_else(|| payload.downcast_ref::<String>().cloned())
38549 .expect(
38550 "assert_char_pair_array_bijective panic payload \
38551 must be a static &str or String",
38552 );
38553 assert!(
38554 msg.contains("assert_char_pair_array_bijective"),
38555 "assert_char_pair_array_bijective RIGHT-column panic \
38556 message {msg:?} must name the helper for provenance-\
38557 preserving failure diagnostics",
38558 );
38559 assert!(
38560 msg.contains("RIGHT column"),
38561 "assert_char_pair_array_bijective RIGHT-column panic \
38562 message {msg:?} must name the failed COLUMN (\"RIGHT \
38563 column\") for column-provenance-preserving failure \
38564 diagnostics",
38565 );
38566 }
38567
38568 // ── `assert_char_pair_array_pairwise_distinct` — the (char, char)
38569 // product-element TUPLE-level pairwise-distinctness verifier that
38570 // binds `∀ i < j. arr[i] ≠ arr[j]` at compile time on the paired-
38571 // array vocabulary via CONJOINED-tuple equality, WEAKER peer of
38572 // `assert_char_pair_array_bijective`'s column-INDEPENDENT
38573 // INJECTIVITY axis on the SAME (char, char) row of the
38574 // (element-type × contract-shape) matrix. Both bijective ⇒
38575 // pairwise-distinct-as-tuples witnesses hold on the substrate's
38576 // two family-wide `[(char, char); N]` arrays (NAMED_ESCAPE_TABLE
38577 // + ESCAPE_TABLE), so the runtime test surface pins ONLY the
38578 // helper's OWN reject-arm (tuple-collision at three (adjacent,
38579 // non-adjacent, terminal) positions) + accept-arms (empty,
38580 // singletons, per-column-alias-with-distinct-tuples, family-wide
38581 // substrate arrays) + panic-message-provenance on the CHAR-PAIR-
38582 // TUPLE-COLLISION axis so a regression that silently weakened
38583 // the helper on ANY arm is caught by the helper's OWN test
38584 // surface rather than only surfacing as a false-positive on
38585 // some future tuple-level pairwise-distinct-but-NOT-bijective
38586 // `[(char, char); N]` array's compound pin.
38587
38588 #[test]
38589 fn assert_char_pair_array_pairwise_distinct_accepts_the_empty_array() {
38590 // Empty array `arr = []` at the `[(char, char); 0]` corner —
38591 // vacuously pairwise-distinct at the tuple level (no (i, j)
38592 // pair with i < j exists to test). Pins the outer `while i <
38593 // N` guard's short-circuit on `N == 0` — a regression that
38594 // panicked on the empty array would collapse this pin.
38595 assert_char_pair_array_pairwise_distinct::<0>(&[]);
38596 }
38597
38598 #[test]
38599 fn assert_char_pair_array_pairwise_distinct_accepts_a_singleton_array() {
38600 // Singleton array `arr = [(a, b)]` — vacuously pairwise-
38601 // distinct at the tuple level (no `j > i == 0` position
38602 // exists to compare against). Cross-corner coverage on the
38603 // trivial-array face of the const-N generic past the empty
38604 // corner, closing the two edge cases (N == 0, N == 1) at
38605 // which the inner sweep is vacuous.
38606 assert_char_pair_array_pairwise_distinct(&[('a', 'x')]);
38607 }
38608
38609 #[test]
38610 fn assert_char_pair_array_pairwise_distinct_accepts_two_distinct_tuples() {
38611 // Two-element array with two BYTE-FOR-BYTE-DISTINCT tuples —
38612 // the smallest non-trivial well-formed case. Both column
38613 // projections happen to be distinct too (`'a' ≠ 'b'` on the
38614 // LEFT column, `'x' ≠ 'y'` on the RIGHT column) but the
38615 // helper reads ONLY the CONJOINED-tuple gate, not the per-
38616 // column gates — pinned in isolation from the sibling
38617 // bijective helper's stricter column-independent axis.
38618 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('b', 'y')]);
38619 }
38620
38621 #[test]
38622 fn assert_char_pair_array_pairwise_distinct_accepts_per_column_alias_when_conjoined_tuple_differs(
38623 ) {
38624 // Accept-corner load-bearing to the helper's DISTINCTION from
38625 // the sibling `assert_char_pair_array_bijective`: a pair-
38626 // array with LEFT-column collision (`'a'` at [0].0 and
38627 // [1].0) but with DISTINCT RIGHT columns (`'x' ≠ 'y'`) has
38628 // TWO CONJOINED-tuples `('a', 'x') ≠ ('a', 'y')` that DIFFER
38629 // — this helper MUST accept it. The bijective sibling would
38630 // REJECT this pair on its LEFT-column arm because
38631 // bijectivity is per-column-INDEPENDENT. The two helpers'
38632 // divergence here is THE point of opening the tuple-level
38633 // INJECTIVITY axis as a distinct sub-shape past the column-
38634 // INDEPENDENT INJECTIVITY axis.
38635 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('a', 'y')]);
38636 // Symmetric RIGHT-column-alias corner with distinct LEFT
38637 // columns — same accept posture on the OTHER column-alias
38638 // sibling.
38639 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('b', 'x')]);
38640 }
38641
38642 #[test]
38643 fn assert_char_pair_array_pairwise_distinct_accepts_the_family_wide_substrate_arrays() {
38644 // Positive pin on the TWO family-wide `[(char, char); N]`
38645 // paired arrays this helper is applied to at compile time
38646 // via the module-level `const _:` witnesses: `NAMED_ESCAPE_
38647 // TABLE` (`[(char, char); 3]`) and `ESCAPE_TABLE` (`[(char,
38648 // char); 5]`). Both bijective ⇒ pairwise-distinct-as-tuples
38649 // WITNESSES the compile-time `const _:` pass at runtime for
38650 // a second-stage safety net if the const-eval sweep is ever
38651 // silently dropped. Sibling posture to
38652 // `assert_char_pair_array_bijective`'s
38653 // `_accepts_the_family_wide_substrate_bijections` on the
38654 // SAME two arrays — this pin binds the WEAKER tuple-level
38655 // axis; that pin binds the STRONGER column-independent axis.
38656 assert_char_pair_array_pairwise_distinct(&Atom::NAMED_ESCAPE_TABLE);
38657 assert_char_pair_array_pairwise_distinct(&Atom::ESCAPE_TABLE);
38658 }
38659
38660 #[test]
38661 #[should_panic(expected = "assert_char_pair_array_pairwise_distinct")]
38662 fn assert_char_pair_array_pairwise_distinct_panics_at_runtime_on_adjacent_tuple_collision() {
38663 // NEGATIVE PIN — adjacent (0, 1) corner: two adjacent
38664 // CONJOINED-tuples byte-for-byte equal (BOTH columns match
38665 // in lockstep) MUST panic at runtime. Pins the helper's OWN
38666 // reject-arm — a regression that silently returned without
38667 // panicking on a tuple duplicate would slip through the
38668 // compile-time witnesses' failure mode too.
38669 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('a', 'x')]);
38670 }
38671
38672 #[test]
38673 #[should_panic(expected = "assert_char_pair_array_pairwise_distinct")]
38674 fn assert_char_pair_array_pairwise_distinct_panics_at_runtime_on_non_adjacent_tuple_collision()
38675 {
38676 // NEGATIVE PIN — non-adjacent (0, 2) corner: the collision
38677 // fires on ANY (i, j) pair with i < j, not just the
38678 // adjacent (0, 1) corner. Pins the nested-loop shape of the
38679 // helper — a regression that walked ONLY the adjacent pairs
38680 // (i.e., swept `while i + 1 < N { if arr[i] == arr[i+1] {
38681 // panic } … }`) would silently accept the non-adjacent
38682 // collision at positions 0 and 2 tested here (a tuple
38683 // collision at ('a', 'x') across [0] and [2], with the
38684 // distinct ('b', 'y') at [1] breaking adjacency). Sibling
38685 // posture to `assert_char_pair_array_bijective_panics_at_
38686 // runtime_on_non_adjacent_collision` on the column-
38687 // independent axis — the loop-shape pin is row-parallel.
38688 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('b', 'y'), ('a', 'x')]);
38689 }
38690
38691 #[test]
38692 #[should_panic(expected = "assert_char_pair_array_pairwise_distinct")]
38693 fn assert_char_pair_array_pairwise_distinct_panics_at_runtime_on_terminal_tuple_collision() {
38694 // NEGATIVE PIN — terminal corner: the collision at the LAST
38695 // pair (positions N-2 and N-1) MUST also fire. Pins the
38696 // outer `while i < N` bound — a regression that walked
38697 // `while i < N - 1` (dropping the last row) would silently
38698 // accept a collision at the tail. Uses a tuple collision at
38699 // the terminal pair (`('e', 'z')` at [3] and [4]).
38700 assert_char_pair_array_pairwise_distinct(&[
38701 ('a', 'w'),
38702 ('b', 'x'),
38703 ('c', 'y'),
38704 ('e', 'z'),
38705 ('e', 'z'),
38706 ]);
38707 }
38708
38709 #[test]
38710 fn assert_char_pair_array_pairwise_distinct_panic_message_names_the_helper_and_char_pair_tuple_collision_axis(
38711 ) {
38712 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-TUPLE-COLLISION
38713 // arm: the panic message MUST begin with the helper's own
38714 // name AND identify the failed AXIS as "CHAR-PAIR-TUPLE-
38715 // COLLISION" so downstream diagnostics route the drift back
38716 // to (a) the helper by string search on
38717 // `"assert_char_pair_array_pairwise_distinct"` and (b) the
38718 // axis by string search on `"CHAR-PAIR-TUPLE-COLLISION"`.
38719 // The axis-provenance string `"CHAR-PAIR-TUPLE-COLLISION"`
38720 // is chosen DISTINCT from EVERY sibling helper's axis
38721 // vocabulary (`"LEFT column"` / `"RIGHT column"` on the
38722 // paired-array BIJECTIVITY sibling; `"CHAR-PAIR-SUBSET-
38723 // VIOLATION"` on the paired-array SUBSET-embedding sibling;
38724 // `"CHAR-PAIR-DISJOINTNESS-VIOLATION"` on the paired-array
38725 // DISJOINTNESS sibling) so a diagnostic that names the
38726 // failed axis routes UNAMBIGUOUSLY to (a) this specific
38727 // paired-array TUPLE-LEVEL PAIRWISE-DISTINCTNESS helper,
38728 // (b) the failed axis-shape by the `"TUPLE-COLLISION"`
38729 // suffix stem distinguishing the CONJOINED-tuple axis from
38730 // the column-INDEPENDENT axis names used by the bijective
38731 // sibling.
38732 let outcome = std::panic::catch_unwind(|| {
38733 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('a', 'x')]);
38734 });
38735 let payload = outcome.expect_err(
38736 "assert_char_pair_array_pairwise_distinct must panic on \
38737 a CONJOINED-tuple duplicate — the reject-collision arm \
38738 is the sole CHAR-PAIR-TUPLE-COLLISION failure mode of \
38739 the helper",
38740 );
38741 let msg = payload
38742 .downcast_ref::<&'static str>()
38743 .map(|s| (*s).to_owned())
38744 .or_else(|| payload.downcast_ref::<String>().cloned())
38745 .expect(
38746 "assert_char_pair_array_pairwise_distinct panic \
38747 payload must be a static &str or String",
38748 );
38749 assert!(
38750 msg.contains("assert_char_pair_array_pairwise_distinct"),
38751 "assert_char_pair_array_pairwise_distinct panic message \
38752 {msg:?} must name the helper for provenance-preserving \
38753 failure diagnostics",
38754 );
38755 assert!(
38756 msg.contains("CHAR-PAIR-TUPLE-COLLISION"),
38757 "assert_char_pair_array_pairwise_distinct panic message \
38758 {msg:?} must name the failed AXIS (\"CHAR-PAIR-TUPLE-\
38759 COLLISION\") for axis-provenance-preserving failure \
38760 diagnostics",
38761 );
38762 }
38763
38764 // ── `assert_char_pair_array_columns_equal_char_arrays` — the
38765 // `(char, char)` product-element COLUMN-PROJECTION-EQUALITY
38766 // verifier that binds a JOINT (LEFT_col == left) ∧ (RIGHT_col ==
38767 // right) POSITIONWISE-EQUALITY contract at compile time on the
38768 // (paired-array, peer-scalar-LEFT, peer-scalar-RIGHT) three-way
38769 // column bond. Opens the (column-projection-equality) column on
38770 // the (char, char) row of the (element-type × contract-shape)
38771 // matrix past the pre-existing (INJECTIVITY, SUBSET-EMBEDDING,
38772 // DISJOINTNESS) triple. Runtime test surface pins each of the
38773 // helper's arms (accept-empty, accept-singleton-equal, accept-
38774 // multi-element-equal, accept-family-wide-substrate-triple on
38775 // the pinned (`ESCAPE_TABLE`, `ESCAPE_SOURCES`, `ESCAPE_DECODED`)
38776 // triple, reject-LEFT-divergence at head / middle / tail, reject-
38777 // RIGHT-divergence at head / middle / tail, panic-message-
38778 // provenance on BOTH the LEFT-COLUMN-DIVERGENCE and RIGHT-
38779 // COLUMN-DIVERGENCE axes) so a regression that silently weakened
38780 // the helper on ANY arm is caught by the helper's OWN test
38781 // surface rather than only surfacing as a false-positive on some
38782 // future column-bonded `[(char, char); N]` array's compound pin.
38783
38784 #[test]
38785 fn assert_char_pair_array_columns_equal_char_arrays_accepts_the_empty_triple() {
38786 // Empty arrays `pairs = []`, `left = []`, `right = []` at
38787 // the `[(char, char); 0]` + two `[char; 0]` corner —
38788 // vacuously column-equal (no `i` position exists to test).
38789 // Pins the outer `while i < N` guard's short-circuit on
38790 // `N == 0`. Turbofish binding required because there's no
38791 // other cue for the const parameter on the three empty
38792 // array literals.
38793 assert_char_pair_array_columns_equal_char_arrays::<0>(&[], &[], &[]);
38794 }
38795
38796 #[test]
38797 fn assert_char_pair_array_columns_equal_char_arrays_accepts_a_singleton_triple() {
38798 // Singleton triple `pairs = [('a', 'x')]`, `left = ['a']`,
38799 // `right = ['x']` — the smallest non-trivial well-formed
38800 // case. Both column-projections match position-for-position.
38801 assert_char_pair_array_columns_equal_char_arrays(&[('a', 'x')], &['a'], &['x']);
38802 }
38803
38804 #[test]
38805 fn assert_char_pair_array_columns_equal_char_arrays_accepts_a_multi_element_triple() {
38806 // Three-element triple with three BYTE-FOR-BYTE-EQUAL
38807 // column projections — pins the inner loop's `i += 1`
38808 // advancement and the sweep's coverage of BOTH clauses at
38809 // EACH position (a regression that returned early after
38810 // position 0 would silently accept a mismatch at position
38811 // 1 or 2).
38812 assert_char_pair_array_columns_equal_char_arrays(
38813 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38814 &['a', 'b', 'c'],
38815 &['x', 'y', 'z'],
38816 );
38817 }
38818
38819 #[test]
38820 fn assert_char_pair_array_columns_equal_char_arrays_accepts_the_family_wide_substrate_triple() {
38821 // Positive pin on the family-wide (`ESCAPE_TABLE`,
38822 // `ESCAPE_SOURCES`, `ESCAPE_DECODED`) triple this helper is
38823 // applied to at compile time via the module-level `const _:`
38824 // witness. Runtime pin as a second-stage safety net if the
38825 // const-eval sweep is ever silently dropped. Sibling posture
38826 // to the `_bijective` + `_pairwise_distinct` family-wide pins
38827 // on the same paired array — the three pins bind
38828 // complementary axes of the same substrate table.
38829 assert_char_pair_array_columns_equal_char_arrays(
38830 &Atom::ESCAPE_TABLE,
38831 &Atom::ESCAPE_SOURCES,
38832 &Atom::ESCAPE_DECODED,
38833 );
38834 }
38835
38836 #[test]
38837 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38838 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_left_head_divergence()
38839 {
38840 // NEGATIVE PIN — LEFT-column head-position (i == 0) corner:
38841 // `pairs[0].0 = 'a'` diverges from `left[0] = 'z'` on the
38842 // FIRST position. Pins the LEFT arm firing at the head — a
38843 // regression that started the sweep at `i = 1` would
38844 // silently accept a head-position divergence.
38845 assert_char_pair_array_columns_equal_char_arrays(
38846 &[('a', 'x'), ('b', 'y')],
38847 &['z', 'b'],
38848 &['x', 'y'],
38849 );
38850 }
38851
38852 #[test]
38853 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38854 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_left_middle_divergence(
38855 ) {
38856 // NEGATIVE PIN — LEFT-column middle-position (i == 1)
38857 // corner: divergence fires on ANY interior position, not
38858 // just the head. Pins the loop-shape of the LEFT arm — a
38859 // regression that walked ONLY the head would silently
38860 // accept the middle-position divergence at position 1.
38861 assert_char_pair_array_columns_equal_char_arrays(
38862 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38863 &['a', 'q', 'c'],
38864 &['x', 'y', 'z'],
38865 );
38866 }
38867
38868 #[test]
38869 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38870 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_left_tail_divergence()
38871 {
38872 // NEGATIVE PIN — LEFT-column tail-position (i == N-1)
38873 // corner: divergence at the LAST position MUST also fire.
38874 // Pins the outer `while i < N` bound — a regression that
38875 // walked `while i < N - 1` (dropping the last row) would
38876 // silently accept a tail LEFT-column divergence.
38877 assert_char_pair_array_columns_equal_char_arrays(
38878 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38879 &['a', 'b', 'q'],
38880 &['x', 'y', 'z'],
38881 );
38882 }
38883
38884 #[test]
38885 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38886 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_right_head_divergence()
38887 {
38888 // NEGATIVE PIN — RIGHT-column head-position (i == 0) corner:
38889 // symmetric to the LEFT arm at the head — pins the RIGHT
38890 // arm firing at position 0 with the LEFT arm satisfied.
38891 assert_char_pair_array_columns_equal_char_arrays(
38892 &[('a', 'x'), ('b', 'y')],
38893 &['a', 'b'],
38894 &['q', 'y'],
38895 );
38896 }
38897
38898 #[test]
38899 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38900 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_right_middle_divergence(
38901 ) {
38902 // NEGATIVE PIN — RIGHT-column middle-position (i == 1)
38903 // corner: symmetric to the LEFT-middle arm — pins the
38904 // RIGHT arm firing at an interior position with the LEFT
38905 // arm satisfied at every position.
38906 assert_char_pair_array_columns_equal_char_arrays(
38907 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38908 &['a', 'b', 'c'],
38909 &['x', 'q', 'z'],
38910 );
38911 }
38912
38913 #[test]
38914 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38915 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_right_tail_divergence()
38916 {
38917 // NEGATIVE PIN — RIGHT-column tail-position (i == N-1)
38918 // corner: symmetric to the LEFT-tail arm — pins the outer
38919 // sweep bound on the RIGHT arm with the LEFT arm satisfied.
38920 assert_char_pair_array_columns_equal_char_arrays(
38921 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38922 &['a', 'b', 'c'],
38923 &['x', 'y', 'q'],
38924 );
38925 }
38926
38927 #[test]
38928 fn assert_char_pair_array_columns_equal_char_arrays_panic_message_names_the_helper_and_left_column_divergence_axis(
38929 ) {
38930 // PANIC-MESSAGE PROVENANCE PIN — LEFT-COLUMN-DIVERGENCE
38931 // arm: the panic message MUST begin with the helper's own
38932 // name AND identify the failed AXIS as "LEFT-COLUMN-
38933 // DIVERGENCE" so downstream diagnostics route the drift
38934 // back to (a) the helper by string search on
38935 // `"assert_char_pair_array_columns_equal_char_arrays"` and
38936 // (b) the axis by string search on
38937 // `"LEFT-COLUMN-DIVERGENCE"`. The axis-provenance string
38938 // is chosen DISTINCT from every sibling helper's axis
38939 // vocabulary — a diagnostic that names the failed axis
38940 // routes UNAMBIGUOUSLY to this specific paired-array
38941 // COLUMN-PROJECTION-EQUALITY helper's LEFT arm.
38942 let outcome = std::panic::catch_unwind(|| {
38943 assert_char_pair_array_columns_equal_char_arrays(
38944 &[('a', 'x'), ('b', 'y')],
38945 &['z', 'b'],
38946 &['x', 'y'],
38947 );
38948 });
38949 let payload = outcome.expect_err(
38950 "assert_char_pair_array_columns_equal_char_arrays must \
38951 panic on a LEFT-column POSITIONWISE-EQUALITY divergence",
38952 );
38953 let msg = payload
38954 .downcast_ref::<&'static str>()
38955 .map(|s| (*s).to_owned())
38956 .or_else(|| payload.downcast_ref::<String>().cloned())
38957 .expect(
38958 "assert_char_pair_array_columns_equal_char_arrays \
38959 panic payload must be a static &str or String",
38960 );
38961 assert!(
38962 msg.contains("assert_char_pair_array_columns_equal_char_arrays"),
38963 "assert_char_pair_array_columns_equal_char_arrays panic \
38964 message {msg:?} must name the helper for provenance-\
38965 preserving failure diagnostics",
38966 );
38967 assert!(
38968 msg.contains("LEFT-COLUMN-DIVERGENCE"),
38969 "assert_char_pair_array_columns_equal_char_arrays panic \
38970 message {msg:?} must name the failed AXIS (\"LEFT-\
38971 COLUMN-DIVERGENCE\") for axis-provenance-preserving \
38972 failure diagnostics",
38973 );
38974 }
38975
38976 #[test]
38977 fn assert_char_pair_array_columns_equal_char_arrays_panic_message_names_the_helper_and_right_column_divergence_axis(
38978 ) {
38979 // PANIC-MESSAGE PROVENANCE PIN — RIGHT-COLUMN-DIVERGENCE
38980 // arm: symmetric to the LEFT-column-divergence provenance
38981 // pin. The panic message MUST begin with the helper's own
38982 // name AND identify the failed AXIS as "RIGHT-COLUMN-
38983 // DIVERGENCE" — the two axis-provenance strings
38984 // (`"LEFT-COLUMN-DIVERGENCE"` + `"RIGHT-COLUMN-DIVERGENCE"`)
38985 // partition the helper's failure modes into TWO disjoint
38986 // arms so a diagnostic that names the axis routes
38987 // UNAMBIGUOUSLY to the specific COLUMN that diverged.
38988 let outcome = std::panic::catch_unwind(|| {
38989 assert_char_pair_array_columns_equal_char_arrays(
38990 &[('a', 'x'), ('b', 'y')],
38991 &['a', 'b'],
38992 &['q', 'y'],
38993 );
38994 });
38995 let payload = outcome.expect_err(
38996 "assert_char_pair_array_columns_equal_char_arrays must \
38997 panic on a RIGHT-column POSITIONWISE-EQUALITY \
38998 divergence",
38999 );
39000 let msg = payload
39001 .downcast_ref::<&'static str>()
39002 .map(|s| (*s).to_owned())
39003 .or_else(|| payload.downcast_ref::<String>().cloned())
39004 .expect(
39005 "assert_char_pair_array_columns_equal_char_arrays \
39006 panic payload must be a static &str or String",
39007 );
39008 assert!(
39009 msg.contains("assert_char_pair_array_columns_equal_char_arrays"),
39010 "assert_char_pair_array_columns_equal_char_arrays panic \
39011 message {msg:?} must name the helper for provenance-\
39012 preserving failure diagnostics",
39013 );
39014 assert!(
39015 msg.contains("RIGHT-COLUMN-DIVERGENCE"),
39016 "assert_char_pair_array_columns_equal_char_arrays panic \
39017 message {msg:?} must name the failed AXIS (\"RIGHT-\
39018 COLUMN-DIVERGENCE\") for axis-provenance-preserving \
39019 failure diagnostics",
39020 );
39021 }
39022
39023 // ── `assert_char_pair_array_columns_cross_disjoint` — the
39024 // `(char, char)` product-element WITHIN-ARRAY CROSS-COLUMN
39025 // disjointness verifier that binds `LEFT column ∩ RIGHT column
39026 // = ∅` at compile time on a pattern-DISTINCT-from-value paired-
39027 // array vocabulary. Peer to the four prior paired-array verifiers
39028 // on the SAME row (`_pairwise_distinct` — CONJOINED-tuple
39029 // INJECTIVITY; `_bijective` — per-column INJECTIVITY;
39030 // `_within_char_pair_finite_set` — SUBSET-EMBEDDING;
39031 // `_arrays_disjoint` — BETWEEN-ARRAY CONJOINED-tuple
39032 // DISJOINTNESS): where the four siblings close within-tuple /
39033 // within-column / between-array axes, this helper closes the
39034 // WITHIN-ARRAY CROSS-COLUMN-CHARACTER axis. The runtime test
39035 // surface pins each of the helper's arms (accept-empty-array,
39036 // accept-singleton-when-left-disjoint-from-right, accept-multi-
39037 // element-when-cross-disjoint, accept-substrate-family-wide-
39038 // NAMED_ESCAPE_TABLE, accept-when-left-column-duplicates-with-
39039 // cross-disjoint-preserved, reject-diagonal-collision, reject-
39040 // head-left-matches-head-right-off-diagonal, reject-head-left-
39041 // matches-tail-right, reject-tail-left-matches-head-right,
39042 // reject-tail-left-matches-tail-right, panic-message-provenance
39043 // on the CROSS-COLUMN-COLLISION axis) so a regression that
39044 // silently weakened the helper on ANY arm is caught by the
39045 // helper's OWN test surface rather than only surfacing as a
39046 // false-positive on some future pattern-DISTINCT-from-value
39047 // `[(char, char); N]` array's compound pin.
39048
39049 #[test]
39050 fn assert_char_pair_array_columns_cross_disjoint_accepts_the_empty_array() {
39051 // Empty array `arr = []` at the `[(char, char); 0]` corner —
39052 // vacuously cross-column-disjoint (no `(i, j)` position pair
39053 // exists to test). Pins the outer `while i < N` guard's
39054 // short-circuit on `N == 0`. Turbofish binding required
39055 // because there's no other cue for the const parameter on
39056 // the empty array literal.
39057 assert_char_pair_array_columns_cross_disjoint::<0>(&[]);
39058 }
39059
39060 #[test]
39061 fn assert_char_pair_array_columns_cross_disjoint_accepts_a_singleton_when_left_disjoint_from_right(
39062 ) {
39063 // Singleton `arr = [('a', 'x')]` — LEFT column = {'a'},
39064 // RIGHT column = {'x'}, disjoint. Pins the singleton corner
39065 // where the ONLY `(i, j)` position pair is `(0, 0)` on the
39066 // diagonal and `arr[0].0 != arr[0].1` so the diagonal arm
39067 // does NOT fire.
39068 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x')]);
39069 }
39070
39071 #[test]
39072 fn assert_char_pair_array_columns_cross_disjoint_accepts_a_multi_element_when_cross_disjoint() {
39073 // Three-element `arr = [('a', 'x'), ('b', 'y'), ('c', 'z')]`
39074 // with LEFT = {'a', 'b', 'c'}, RIGHT = {'x', 'y', 'z'},
39075 // disjoint. Pins the inner sweep's coverage of BOTH
39076 // diagonal AND off-diagonal `(i, j)` position pairs at
39077 // EACH `i` — a regression that returned early after
39078 // position 0 would silently accept a cross-column collision
39079 // at position 1 or 2 or an off-diagonal cross-column
39080 // collision at `(0, 1)`, `(1, 0)`, `(0, 2)`, `(2, 0)`,
39081 // `(1, 2)`, `(2, 1)`.
39082 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'y'), ('c', 'z')]);
39083 }
39084
39085 #[test]
39086 fn assert_char_pair_array_columns_cross_disjoint_accepts_the_family_wide_substrate_named_escape_table(
39087 ) {
39088 // Positive pin on the family-wide `Atom::NAMED_ESCAPE_TABLE`
39089 // paired array this helper is applied to at compile time via
39090 // the module-level `const _:` witness. Runtime pin as a
39091 // second-stage safety net if the const-eval sweep is ever
39092 // silently dropped. Sibling posture to the `_bijective` +
39093 // `_pairwise_distinct` family-wide pins on the same paired
39094 // array — the three pins bind complementary INJECTIVITY axes
39095 // (per-column, per-tuple, per-CROSS-column) of the same
39096 // substrate table. LEFT column = {'n', 't', 'r'} (printable
39097 // ASCII source chars), RIGHT column = {'\n', '\t', '\r'}
39098 // (control character decoded values), disjoint by algebra
39099 // design.
39100 assert_char_pair_array_columns_cross_disjoint(&Atom::NAMED_ESCAPE_TABLE);
39101 }
39102
39103 #[test]
39104 fn assert_char_pair_array_columns_cross_disjoint_accepts_when_left_column_duplicates_but_cross_disjoint_preserved(
39105 ) {
39106 // Orthogonality with `_bijective`: a table like
39107 // `[('a', 'x'), ('a', 'y')]` VIOLATES per-column INJECTIVITY
39108 // (LEFT column repeats 'a') but PASSES cross-column
39109 // disjointness (LEFT = {'a'}, RIGHT = {'x', 'y'} are
39110 // disjoint). Pins this helper's axis as INDEPENDENT of
39111 // `_bijective` — a diagnostic that names CROSS-COLUMN-
39112 // COLLISION routes UNAMBIGUOUSLY to a cross-column drift,
39113 // not to a per-column duplicate.
39114 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('a', 'y')]);
39115 // Symmetric orthogonality on the RIGHT column: a table like
39116 // `[('a', 'x'), ('b', 'x')]` VIOLATES per-column INJECTIVITY
39117 // (RIGHT column repeats 'x') but PASSES cross-column
39118 // disjointness (LEFT = {'a', 'b'}, RIGHT = {'x'} are
39119 // disjoint).
39120 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'x')]);
39121 }
39122
39123 #[test]
39124 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39125 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_diagonal_collision() {
39126 // NEGATIVE PIN — diagonal `(i == j == 0)` corner: `arr[0] =
39127 // ('a', 'a')` collides with itself across columns. Pins the
39128 // inner sweep INCLUDING the `j == i` case — a regression that
39129 // set `j = i + 1` (skipping the diagonal) would silently
39130 // accept a `('a', 'a')` self-mapping pair, which is EXACTLY
39131 // the SELF-arm identity-relation shape the pattern-DISTINCT-
39132 // from-value sub-vocabulary MUST NOT carry.
39133 assert_char_pair_array_columns_cross_disjoint(&[('a', 'a')]);
39134 }
39135
39136 #[test]
39137 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39138 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_diagonal_collision_at_interior_position(
39139 ) {
39140 // NEGATIVE PIN — interior diagonal `(i == j == 1)` corner:
39141 // pins the diagonal arm firing at ANY interior position,
39142 // not just the head. A regression that started the sweep
39143 // at `i = 1` for `j` (skipping the diagonal at each `i`)
39144 // would silently accept a `('b', 'b')` self-mapping pair at
39145 // position 1.
39146 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'b'), ('c', 'z')]);
39147 }
39148
39149 #[test]
39150 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39151 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_head_left_matches_tail_right(
39152 ) {
39153 // NEGATIVE PIN — off-diagonal `(i, j) = (0, 1)` corner:
39154 // `arr[0].0 == 'a'` collides with `arr[1].1 == 'a'` at
39155 // an off-diagonal position pair. Pins the inner sweep
39156 // covering `j > i` — a regression that only checked the
39157 // diagonal `i == j` would silently accept the head LEFT
39158 // aliasing the tail RIGHT.
39159 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'a')]);
39160 }
39161
39162 #[test]
39163 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39164 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_tail_left_matches_head_right(
39165 ) {
39166 // NEGATIVE PIN — off-diagonal `(i, j) = (1, 0)` corner:
39167 // `arr[1].0 == 'a'` collides with `arr[0].1 == 'a'` at the
39168 // OTHER off-diagonal orientation. Pins the inner sweep
39169 // covering `j < i` — a regression that only checked `j >=
39170 // i` would silently accept the tail LEFT aliasing the head
39171 // RIGHT.
39172 assert_char_pair_array_columns_cross_disjoint(&[('x', 'a'), ('a', 'y')]);
39173 }
39174
39175 #[test]
39176 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39177 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_tail_position_diagonal_collision(
39178 ) {
39179 // NEGATIVE PIN — tail-position diagonal `(i == j == N-1)`
39180 // corner: pins the outer `while i < N` bound INCLUDING the
39181 // last row — a regression that walked `while i < N - 1`
39182 // (dropping the last row) would silently accept a tail-
39183 // position `('c', 'c')` diagonal collision.
39184 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'y'), ('c', 'c')]);
39185 }
39186
39187 #[test]
39188 fn assert_char_pair_array_columns_cross_disjoint_panic_message_names_the_helper_and_cross_column_collision_axis(
39189 ) {
39190 // PANIC-MESSAGE PROVENANCE PIN — CROSS-COLUMN-COLLISION arm:
39191 // the panic message MUST begin with the helper's own name
39192 // AND identify the failed AXIS as "CROSS-COLUMN-COLLISION"
39193 // so downstream diagnostics route the drift back to (a) the
39194 // helper by string search on
39195 // `"assert_char_pair_array_columns_cross_disjoint"` and (b)
39196 // the axis by string search on `"CROSS-COLUMN-COLLISION"`.
39197 // The axis-provenance string is chosen DISTINCT from every
39198 // sibling helper's axis vocabulary (`"CHAR-PAIR-TUPLE-
39199 // COLLISION"` on `_pairwise_distinct`, `"LEFT column"` /
39200 // `"RIGHT column"` on `_bijective`, `"CHAR-PAIR-SUBSET-
39201 // VIOLATION"` on `_within_char_pair_finite_set`, `"CHAR-
39202 // PAIR-DISJOINTNESS-VIOLATION"` on `_arrays_disjoint`,
39203 // `"LEFT-COLUMN-DIVERGENCE"` / `"RIGHT-COLUMN-DIVERGENCE"`
39204 // on `_columns_equal_char_arrays`) — a diagnostic that
39205 // names the failed axis routes UNAMBIGUOUSLY to this
39206 // specific cross-column-disjointness helper's arm.
39207 let outcome = std::panic::catch_unwind(|| {
39208 assert_char_pair_array_columns_cross_disjoint(&[('a', 'a')]);
39209 });
39210 let payload = outcome.expect_err(
39211 "assert_char_pair_array_columns_cross_disjoint must \
39212 panic on a diagonal `('a', 'a')` self-mapping pair",
39213 );
39214 let msg = payload
39215 .downcast_ref::<&'static str>()
39216 .map(|s| (*s).to_owned())
39217 .or_else(|| payload.downcast_ref::<String>().cloned())
39218 .expect(
39219 "assert_char_pair_array_columns_cross_disjoint \
39220 panic payload must be a static &str or String",
39221 );
39222 assert!(
39223 msg.contains("assert_char_pair_array_columns_cross_disjoint"),
39224 "assert_char_pair_array_columns_cross_disjoint panic \
39225 message {msg:?} must name the helper for provenance-\
39226 preserving failure diagnostics",
39227 );
39228 assert!(
39229 msg.contains("CROSS-COLUMN-COLLISION"),
39230 "assert_char_pair_array_columns_cross_disjoint panic \
39231 message {msg:?} must name the failed AXIS (\"CROSS-\
39232 COLUMN-COLLISION\") for axis-provenance-preserving \
39233 failure diagnostics",
39234 );
39235 }
39236
39237 // ── `assert_char_pair_array_is_concatenation_of_char_pair_array_
39238 // and_char_array_diagonal` — the `(char, char)` product-element
39239 // SEGMENTED-CONCATENATION-with-DIAGONAL-TAIL verifier that binds
39240 // the composite-construction identity `arr == head ++
39241 // diagonal(tail_diag)` at compile time on a paired-array
39242 // vocabulary composed from a peer paired HEAD + a peer scalar
39243 // DIAGONAL TAIL. Peer to the `_columns_equal_char_arrays` cross-
39244 // row-bond sibling — where the sibling binds the FULL column-
39245 // projection bond across TWO peer scalar arrays at EVERY position,
39246 // this helper binds the SEGMENTED composite-construction bond
39247 // across a peer paired vocabulary (head) and a peer scalar
39248 // vocabulary (diagonally embedded tail). The runtime test surface
39249 // pins each of the helper's arms (accept-empty-triple, accept-
39250 // head-only, accept-diagonal-tail-only, accept-mixed-small-triple,
39251 // accept-family-wide-substrate-triple, reject-head-left-head-
39252 // position, reject-head-left-tail-position, reject-head-right-
39253 // head-position, reject-head-right-tail-position, reject-
39254 // diagonal-tail-left-head-position, reject-diagonal-tail-left-
39255 // tail-position, reject-diagonal-tail-right-head-position, reject-
39256 // diagonal-tail-right-tail-position, reject-cardinality-mismatch
39257 // on `K + M != N`, panic-message-provenance on FIVE distinct
39258 // AXES) so a regression that silently weakened the helper on ANY
39259 // arm is caught by the helper's OWN test surface rather than only
39260 // surfacing as a false-positive on some future composite-
39261 // constructed `[(char, char); N]` array's compound pin.
39262
39263 #[test]
39264 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_the_empty_triple(
39265 ) {
39266 // Empty arrays `arr = []`, `head = []`, `tail_diag = []` at
39267 // the `[(char, char); 0]` + `[(char, char); 0]` + `[char; 0]`
39268 // corner — vacuously composite (no position exists to test).
39269 // Pins the outer sweep bounds' short-circuit on `N == K ==
39270 // M == 0`. Turbofish binding required because there's no
39271 // other cue for the const parameters on the three empty
39272 // array literals.
39273 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<0, 0, 0>(
39274 &[],
39275 &[],
39276 &[],
39277 );
39278 }
39279
39280 #[test]
39281 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_head_only_triple(
39282 ) {
39283 // Head-only triple `arr = [('a','x')]`, `head = [('a','x')]`,
39284 // `tail_diag = []` at the `M == 0` corner — the composite
39285 // reduces to the peer paired HEAD verbatim, no diagonal-
39286 // embedding. Pins the outer `while j < M` guard's short-
39287 // circuit on empty tail. Turbofish binding required for the
39288 // three const parameters.
39289 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<1, 1, 0>(
39290 &[('a', 'x')],
39291 &[('a', 'x')],
39292 &[],
39293 );
39294 }
39295
39296 #[test]
39297 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_diagonal_tail_only_triple(
39298 ) {
39299 // Diagonal-tail-only triple `arr = [('q','q')]`, `head = []`,
39300 // `tail_diag = ['q']` at the `K == 0` corner — the composite
39301 // reduces to the DIAGONAL-EMBEDDING of the peer scalar
39302 // `tail_diag` verbatim, no head. Pins the outer `while i < K`
39303 // guard's short-circuit on empty head. Turbofish binding
39304 // required for the three const parameters.
39305 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<1, 0, 1>(
39306 &[('q', 'q')],
39307 &[],
39308 &['q'],
39309 );
39310 }
39311
39312 #[test]
39313 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_mixed_small_triple(
39314 ) {
39315 // Mixed small triple `arr = [('a','x'), ('q','q')]`, `head =
39316 // [('a','x')]`, `tail_diag = ['q']` at the `K == 1, M == 1`
39317 // corner — the smallest non-trivial case that exercises BOTH
39318 // segments. Pins that BOTH sweeps advance and that BOTH arms
39319 // cover their columns at their positions. A regression that
39320 // silently walked ONLY the head OR ONLY the tail would
39321 // silently accept a divergence on the un-swept segment.
39322 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<2, 1, 1>(
39323 &[('a', 'x'), ('q', 'q')],
39324 &[('a', 'x')],
39325 &['q'],
39326 );
39327 }
39328
39329 #[test]
39330 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_the_family_wide_substrate_triple(
39331 ) {
39332 // Positive pin on the family-wide (`Atom::ESCAPE_TABLE`,
39333 // `Atom::NAMED_ESCAPE_TABLE`, `Atom::SELF_ESCAPE_TABLE`)
39334 // triple this helper is applied to at compile time via the
39335 // module-level `const _:` witness. Runtime pin as a second-
39336 // stage safety net if the const-eval sweep is ever silently
39337 // dropped. SIXTH witness posture on the same paired array in
39338 // complementary posture to the FIVE prior sibling helper
39339 // family-wide pins (`_pairwise_distinct`, `_bijective`,
39340 // `_within_char_pair_finite_set`, `_arrays_disjoint`,
39341 // `_columns_equal_char_arrays`) — the six pins bind six
39342 // complementary axes of the same substrate table.
39343 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal(
39344 &Atom::ESCAPE_TABLE,
39345 &Atom::NAMED_ESCAPE_TABLE,
39346 &Atom::SELF_ESCAPE_TABLE,
39347 );
39348 }
39349
39350 #[test]
39351 #[should_panic(
39352 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39353 )]
39354 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_head_left_head_position_divergence(
39355 ) {
39356 // NEGATIVE PIN — HEAD-SEGMENT LEFT-column head-position
39357 // (i == 0) corner: `arr[0].0 = 'z'` diverges from `head[0].0
39358 // = 'a'` on the FIRST head-segment position. Pins the HEAD-
39359 // LEFT arm firing at the head — a regression that started
39360 // the head sweep at `i = 1` would silently accept a head-
39361 // position divergence.
39362 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 2, 1>(
39363 &[('z', 'x'), ('b', 'y'), ('q', 'q')],
39364 &[('a', 'x'), ('b', 'y')],
39365 &['q'],
39366 );
39367 }
39368
39369 #[test]
39370 #[should_panic(
39371 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39372 )]
39373 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_head_left_tail_position_divergence(
39374 ) {
39375 // NEGATIVE PIN — HEAD-SEGMENT LEFT-column tail-position
39376 // (i == K-1) corner: divergence at the LAST head-segment
39377 // position MUST also fire. Pins the outer head-sweep bound
39378 // `while i < K` — a regression that walked `while i < K - 1`
39379 // (dropping the last head row) would silently accept a tail-
39380 // of-head LEFT-column divergence.
39381 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 2, 1>(
39382 &[('a', 'x'), ('z', 'y'), ('q', 'q')],
39383 &[('a', 'x'), ('b', 'y')],
39384 &['q'],
39385 );
39386 }
39387
39388 #[test]
39389 #[should_panic(
39390 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39391 )]
39392 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_head_right_head_position_divergence(
39393 ) {
39394 // NEGATIVE PIN — HEAD-SEGMENT RIGHT-column head-position
39395 // (i == 0) corner: symmetric to the HEAD-LEFT arm at the
39396 // head — pins the HEAD-RIGHT arm firing at position 0 with
39397 // the HEAD-LEFT arm satisfied.
39398 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 2, 1>(
39399 &[('a', 'z'), ('b', 'y'), ('q', 'q')],
39400 &[('a', 'x'), ('b', 'y')],
39401 &['q'],
39402 );
39403 }
39404
39405 #[test]
39406 #[should_panic(
39407 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39408 )]
39409 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_head_right_tail_position_divergence(
39410 ) {
39411 // NEGATIVE PIN — HEAD-SEGMENT RIGHT-column tail-position
39412 // (i == K-1) corner: symmetric to the HEAD-LEFT-tail arm —
39413 // pins the outer head-sweep bound on the RIGHT arm with the
39414 // LEFT arm satisfied at every position.
39415 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 2, 1>(
39416 &[('a', 'x'), ('b', 'z'), ('q', 'q')],
39417 &[('a', 'x'), ('b', 'y')],
39418 &['q'],
39419 );
39420 }
39421
39422 #[test]
39423 #[should_panic(
39424 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39425 )]
39426 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_diagonal_tail_left_head_position_divergence(
39427 ) {
39428 // NEGATIVE PIN — DIAGONAL-TAIL LEFT-column head-position
39429 // (j == 0, arr[K + 0].0 diverges from `tail_diag[0]`) corner:
39430 // pins the DIAGONAL-TAIL-LEFT arm firing at the tail segment's
39431 // FIRST position — a regression that started the tail sweep
39432 // at `j = 1` would silently accept a head-of-tail LEFT-column
39433 // divergence.
39434 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 1, 2>(
39435 &[('a', 'x'), ('z', 'q'), ('r', 'r')],
39436 &[('a', 'x')],
39437 &['q', 'r'],
39438 );
39439 }
39440
39441 #[test]
39442 #[should_panic(
39443 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39444 )]
39445 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_diagonal_tail_left_tail_position_divergence(
39446 ) {
39447 // NEGATIVE PIN — DIAGONAL-TAIL LEFT-column tail-position
39448 // (j == M-1, arr[N-1].0 diverges from `tail_diag[M-1]`)
39449 // corner: pins the outer tail-sweep bound `while j < M` — a
39450 // regression that walked `while j < M - 1` (dropping the last
39451 // tail row) would silently accept a tail-of-tail LEFT-column
39452 // divergence.
39453 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 1, 2>(
39454 &[('a', 'x'), ('q', 'q'), ('z', 'r')],
39455 &[('a', 'x')],
39456 &['q', 'r'],
39457 );
39458 }
39459
39460 #[test]
39461 #[should_panic(
39462 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39463 )]
39464 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_diagonal_tail_right_head_position_divergence(
39465 ) {
39466 // NEGATIVE PIN — DIAGONAL-TAIL RIGHT-column head-position
39467 // (j == 0, arr[K + 0].1 diverges from `tail_diag[0]`) corner:
39468 // symmetric to the DIAGONAL-TAIL-LEFT arm at the tail-head —
39469 // pins the DIAGONAL-TAIL-RIGHT arm firing at the tail
39470 // segment's FIRST position with the LEFT arm satisfied.
39471 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 1, 2>(
39472 &[('a', 'x'), ('q', 'z'), ('r', 'r')],
39473 &[('a', 'x')],
39474 &['q', 'r'],
39475 );
39476 }
39477
39478 #[test]
39479 #[should_panic(
39480 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39481 )]
39482 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_diagonal_tail_right_tail_position_divergence(
39483 ) {
39484 // NEGATIVE PIN — DIAGONAL-TAIL RIGHT-column tail-position
39485 // (j == M-1, arr[N-1].1 diverges from `tail_diag[M-1]`)
39486 // corner: symmetric to the DIAGONAL-TAIL-LEFT-tail arm —
39487 // pins the outer tail-sweep bound on the RIGHT arm with the
39488 // LEFT arm satisfied at every position.
39489 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 1, 2>(
39490 &[('a', 'x'), ('q', 'q'), ('r', 'z')],
39491 &[('a', 'x')],
39492 &['q', 'r'],
39493 );
39494 }
39495
39496 #[test]
39497 #[should_panic(
39498 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39499 )]
39500 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_cardinality_mismatch(
39501 ) {
39502 // NEGATIVE PIN — CARDINALITY-MISMATCH corner: `K + M != N`
39503 // (here `K == 3, M == 3, N == 5` so `K + M == 6 != 5`) fires
39504 // the CARDINALITY-MISMATCH panic BEFORE any per-position
39505 // sweep begins. Pins the FIRST guard arm — a regression that
39506 // silently omitted the arity check would OOB-panic instead
39507 // OR silently truncate the tail sweep, both of which would
39508 // corrupt the AXIS-provenance signal downstream diagnostics
39509 // depend on.
39510 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<5, 3, 3>(
39511 &[('a', 'x'), ('b', 'y'), ('c', 'z'), ('q', 'q'), ('r', 'r')],
39512 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39513 &['q', 'r', 's'],
39514 );
39515 }
39516
39517 #[test]
39518 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panic_message_names_the_helper_and_head_left_divergence_axis(
39519 ) {
39520 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-LEFT-DIVERGENCE
39521 // arm: the panic message MUST begin with the helper's own
39522 // name AND identify the failed AXIS as "HEAD-SEGMENT-LEFT-
39523 // DIVERGENCE" so downstream diagnostics route the drift back
39524 // to (a) the helper by string search AND (b) the axis by
39525 // string search on `"HEAD-SEGMENT-LEFT-DIVERGENCE"`.
39526 let outcome = std::panic::catch_unwind(|| {
39527 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39528 3,
39529 2,
39530 1,
39531 >(
39532 &[('z', 'x'), ('b', 'y'), ('q', 'q')],
39533 &[('a', 'x'), ('b', 'y')],
39534 &['q'],
39535 );
39536 });
39537 let payload = outcome.expect_err(
39538 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39539 must panic on a HEAD-SEGMENT LEFT-column divergence",
39540 );
39541 let msg = payload
39542 .downcast_ref::<&'static str>()
39543 .map(|s| (*s).to_owned())
39544 .or_else(|| payload.downcast_ref::<String>().cloned())
39545 .expect(
39546 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39547 panic payload must be a static &str or String",
39548 );
39549 assert!(
39550 msg.contains(
39551 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39552 ),
39553 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39554 panic message {msg:?} must name the helper for \
39555 provenance-preserving failure diagnostics",
39556 );
39557 assert!(
39558 msg.contains("HEAD-SEGMENT-LEFT-DIVERGENCE"),
39559 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39560 panic message {msg:?} must name the failed AXIS \
39561 (\"HEAD-SEGMENT-LEFT-DIVERGENCE\") for axis-provenance-\
39562 preserving failure diagnostics",
39563 );
39564 }
39565
39566 #[test]
39567 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panic_message_names_the_helper_and_head_right_divergence_axis(
39568 ) {
39569 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-RIGHT-
39570 // DIVERGENCE arm: symmetric to the HEAD-LEFT arm's
39571 // provenance pin.
39572 let outcome = std::panic::catch_unwind(|| {
39573 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39574 3,
39575 2,
39576 1,
39577 >(
39578 &[('a', 'z'), ('b', 'y'), ('q', 'q')],
39579 &[('a', 'x'), ('b', 'y')],
39580 &['q'],
39581 );
39582 });
39583 let payload = outcome.expect_err(
39584 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39585 must panic on a HEAD-SEGMENT RIGHT-column divergence",
39586 );
39587 let msg = payload
39588 .downcast_ref::<&'static str>()
39589 .map(|s| (*s).to_owned())
39590 .or_else(|| payload.downcast_ref::<String>().cloned())
39591 .expect(
39592 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39593 panic payload must be a static &str or String",
39594 );
39595 assert!(
39596 msg.contains("HEAD-SEGMENT-RIGHT-DIVERGENCE"),
39597 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39598 panic message {msg:?} must name the failed AXIS \
39599 (\"HEAD-SEGMENT-RIGHT-DIVERGENCE\") for axis-provenance-\
39600 preserving failure diagnostics",
39601 );
39602 }
39603
39604 #[test]
39605 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panic_message_names_the_helper_and_diagonal_tail_left_divergence_axis(
39606 ) {
39607 // PANIC-MESSAGE PROVENANCE PIN — DIAGONAL-TAIL-SEGMENT-LEFT-
39608 // DIVERGENCE arm: partitions the failure vocabulary DISTINCT
39609 // from the HEAD-SEGMENT-LEFT-DIVERGENCE arm — a diagnostic
39610 // that reads the AXIS routes UNAMBIGUOUSLY to the TAIL
39611 // segment's LEFT column rather than the HEAD segment's.
39612 let outcome = std::panic::catch_unwind(|| {
39613 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39614 3,
39615 1,
39616 2,
39617 >(
39618 &[('a', 'x'), ('z', 'q'), ('r', 'r')],
39619 &[('a', 'x')],
39620 &['q', 'r'],
39621 );
39622 });
39623 let payload = outcome.expect_err(
39624 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39625 must panic on a DIAGONAL-TAIL LEFT-column divergence",
39626 );
39627 let msg = payload
39628 .downcast_ref::<&'static str>()
39629 .map(|s| (*s).to_owned())
39630 .or_else(|| payload.downcast_ref::<String>().cloned())
39631 .expect(
39632 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39633 panic payload must be a static &str or String",
39634 );
39635 assert!(
39636 msg.contains("DIAGONAL-TAIL-SEGMENT-LEFT-DIVERGENCE"),
39637 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39638 panic message {msg:?} must name the failed AXIS \
39639 (\"DIAGONAL-TAIL-SEGMENT-LEFT-DIVERGENCE\") for axis-\
39640 provenance-preserving failure diagnostics",
39641 );
39642 }
39643
39644 #[test]
39645 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panic_message_names_the_helper_and_diagonal_tail_right_divergence_axis(
39646 ) {
39647 // PANIC-MESSAGE PROVENANCE PIN — DIAGONAL-TAIL-SEGMENT-RIGHT-
39648 // DIVERGENCE arm: symmetric to the DIAGONAL-TAIL-LEFT arm's
39649 // provenance pin. The FOUR positionwise axes together
39650 // (HEAD-LEFT, HEAD-RIGHT, DIAGONAL-TAIL-LEFT, DIAGONAL-TAIL-
39651 // RIGHT) partition the helper's positionwise failure surface
39652 // into FOUR disjoint arms so a diagnostic reading the AXIS
39653 // routes UNAMBIGUOUSLY to the specific (segment, column)
39654 // corner that diverged.
39655 let outcome = std::panic::catch_unwind(|| {
39656 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39657 3,
39658 1,
39659 2,
39660 >(
39661 &[('a', 'x'), ('q', 'z'), ('r', 'r')],
39662 &[('a', 'x')],
39663 &['q', 'r'],
39664 );
39665 });
39666 let payload = outcome.expect_err(
39667 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39668 must panic on a DIAGONAL-TAIL RIGHT-column divergence",
39669 );
39670 let msg = payload
39671 .downcast_ref::<&'static str>()
39672 .map(|s| (*s).to_owned())
39673 .or_else(|| payload.downcast_ref::<String>().cloned())
39674 .expect(
39675 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39676 panic payload must be a static &str or String",
39677 );
39678 assert!(
39679 msg.contains("DIAGONAL-TAIL-SEGMENT-RIGHT-DIVERGENCE"),
39680 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39681 panic message {msg:?} must name the failed AXIS \
39682 (\"DIAGONAL-TAIL-SEGMENT-RIGHT-DIVERGENCE\") for axis-\
39683 provenance-preserving failure diagnostics",
39684 );
39685 }
39686
39687 #[test]
39688 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panic_message_names_the_helper_and_cardinality_mismatch_axis(
39689 ) {
39690 // PANIC-MESSAGE PROVENANCE PIN — CARDINALITY-MISMATCH arm:
39691 // fires BEFORE any per-position sweep, at a distinct axis
39692 // vocabulary. Pins the FIRST guard's provenance so a
39693 // diagnostic reading the axis distinguishes ARITY drift
39694 // (mistyped turbofish) from CONTENT drift (segment / column
39695 // divergence).
39696 let outcome = std::panic::catch_unwind(|| {
39697 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39698 5,
39699 3,
39700 3,
39701 >(
39702 &[('a', 'x'), ('b', 'y'), ('c', 'z'), ('q', 'q'), ('r', 'r')],
39703 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39704 &['q', 'r', 's'],
39705 );
39706 });
39707 let payload = outcome.expect_err(
39708 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39709 must panic on a CARDINALITY-MISMATCH `K + M != N`",
39710 );
39711 let msg = payload
39712 .downcast_ref::<&'static str>()
39713 .map(|s| (*s).to_owned())
39714 .or_else(|| payload.downcast_ref::<String>().cloned())
39715 .expect(
39716 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39717 panic payload must be a static &str or String",
39718 );
39719 assert!(
39720 msg.contains("CARDINALITY-MISMATCH"),
39721 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39722 panic message {msg:?} must name the failed AXIS \
39723 (\"CARDINALITY-MISMATCH\") for axis-provenance-preserving \
39724 failure diagnostics",
39725 );
39726 }
39727
39728 // ── `assert_char_pair_array_slice_equals_char_pair_array` — the
39729 // `(char, char)` product-element row's SLICE-EQUALS-ARRAY verifier
39730 // that binds the sub-slice `full[START..START + M) == sub[..]`
39731 // paired-positionwise-composition contract at compile time. Row-
39732 // dual peer to `assert_u8_array_slice_equals_u8_array`,
39733 // `assert_char_array_slice_equals_char_array`, and
39734 // `assert_str_array_slice_equals_str_array` on the (element-type)
39735 // axis of the SAME (SUB-SLICE ARRAY-image) column of the (element-
39736 // type × contract-shape) matrix. The runtime test surface pins
39737 // each of the helper's arms (accept-canonical-middle-slice,
39738 // accept-empty-sub-array-at-three-start-positions, accept-full-
39739 // array-degenerate-at-two-arities, accept-substrate-escape-table-
39740 // head-segment-decomposition, reject-left-column-drift, reject-
39741 // right-column-drift, reject-start-out-of-bounds, reject-slice-
39742 // length-out-of-bounds, panic-message-provenance on the LEFT-
39743 // COLUMN + RIGHT-COLUMN axes) so a regression that silently
39744 // weakened the helper on ANY arm (e.g. dropping the LEFT-column
39745 // check, dropping the RIGHT-column check, dropping the `START`
39746 // offset from the `full[START + i]` read, returning early past
39747 // ANY bounds gate, or dropping the `as u32` char-to-scalar bridge
39748 // that lets the const-eval sweep proceed byte-for-byte on each
39749 // column) is caught by the helper's OWN test surface rather than
39750 // only surfacing as a false-positive on some future paired
39751 // container-array sub-slice equality.
39752
39753 #[test]
39754 fn assert_char_pair_array_slice_equals_char_pair_array_accepts_a_canonical_middle_slice() {
39755 // Canonical paired sub-slice `full[START..START + M) == sub[..]`
39756 // inside a longer paired array `full` whose ENDPOINTS carry
39757 // DIFFERENT pairs than the peer sub-array. Pins the outer
39758 // `while i < M` sweep reads `full[START + i]` at the OFFSET
39759 // position (not `full[i]`) on BOTH columns — a regression that
39760 // dropped the `START` offset would compare `full[0..M)`
39761 // against `sub[..]` and panic on the wrong axis. `START = 1`
39762 // pins the sweep skips position `[0..START)` and reads only
39763 // `[1..1+3) = [1..4)` on both columns of the paired sub-array.
39764 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
39765 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('c', 'z'), ('z', 'Z')],
39766 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39767 );
39768 }
39769
39770 #[test]
39771 fn assert_char_pair_array_slice_equals_char_pair_array_accepts_the_empty_sub_array() {
39772 // LEGAL degenerate: `M == 0` collapses the paired sub-array
39773 // into an empty listing `[]`. The sweep never enters the loop
39774 // body and the helper accepts. Cross-position coverage pins
39775 // the empty-sub-array acceptance at THREE distinct `START`
39776 // positions (`START == 0` at the left endpoint, `START == 2`
39777 // in the interior, `START == N` at the right endpoint — the
39778 // latter is the corner `START == N` combined with `M == 0`
39779 // that the START-OUT-OF-BOUNDS gate's inclusive upper bound
39780 // must accept). A regression that hard-coded `START < N` OR
39781 // panicked on the `M == 0` corner is caught on ALL THREE
39782 // arms.
39783 assert_char_pair_array_slice_equals_char_pair_array::<5, 0, 0>(
39784 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39785 &[],
39786 );
39787 assert_char_pair_array_slice_equals_char_pair_array::<5, 0, 2>(
39788 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39789 &[],
39790 );
39791 assert_char_pair_array_slice_equals_char_pair_array::<5, 0, 5>(
39792 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39793 &[],
39794 );
39795 }
39796
39797 #[test]
39798 fn assert_char_pair_array_slice_equals_char_pair_array_accepts_the_full_array_degenerate() {
39799 // Full-array-covering slice `M == N, START == 0` collapses
39800 // to the ALL-positions-equal-peer-array shape `full == sub`
39801 // pointwise on BOTH columns. Pins that the sweep proceeds
39802 // through EVERY position of the outer paired array when
39803 // `START = 0` and `M = N`. Cross-arity coverage on `N ∈ {2,
39804 // 3}` pins the sweep's terminal-position visit across the
39805 // small arities the substrate's paired escape-table
39806 // vocabulary spans (`N = 2` for a hypothetical two-slot
39807 // paired table, `N = 3` for `NAMED_ESCAPE_TABLE`).
39808 assert_char_pair_array_slice_equals_char_pair_array::<2, 2, 0>(
39809 &[('a', 'x'), ('b', 'y')],
39810 &[('a', 'x'), ('b', 'y')],
39811 );
39812 assert_char_pair_array_slice_equals_char_pair_array::<3, 3, 0>(
39813 &[('n', '\n'), ('t', '\t'), ('r', '\r')],
39814 &[('n', '\n'), ('t', '\t'), ('r', '\r')],
39815 );
39816 }
39817
39818 #[test]
39819 fn assert_char_pair_array_slice_equals_char_pair_array_accepts_the_substrate_escape_table_head_segment(
39820 ) {
39821 // Runtime cross-check that the substrate's (ESCAPE_TABLE,
39822 // NAMED_ESCAPE_TABLE) HEAD-segment positionwise-composition
39823 // identity `ESCAPE_TABLE[0..3] == NAMED_ESCAPE_TABLE` holds
39824 // pointwise on BOTH columns. Runs the SAME helper the module-
39825 // level `const _` witness runs at rustc time — a runtime
39826 // safety net enforcing the theorem at BOTH stages of the
39827 // toolchain (const at `cargo check`, runtime at `cargo
39828 // test`). A regression that renamed one of the three
39829 // NAMED-escape SOURCE or DECODED entries (or drifted the
39830 // ordering across ESCAPE_TABLE's first three initializer
39831 // slots) fails HERE at the substrate callsite AND at the
39832 // const witness above. Peer of
39833 // `assert_char_array_slice_equals_char_array_accepts_each_family_wide_substrate_array`
39834 // on the (char) row — that witness carries the FULL-ARRAY
39835 // per-position ORDER theorem for the EIGHT reader-boundary
39836 // `[char; N]` arrays; this witness carries the HEAD-segment
39837 // per-position ORDER theorem for the substrate's SINGLE
39838 // paired-composite pair on the `(char, char)` product-
39839 // element row.
39840 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 0>(
39841 &Atom::ESCAPE_TABLE,
39842 &Atom::NAMED_ESCAPE_TABLE,
39843 );
39844 }
39845
39846 #[test]
39847 #[should_panic(expected = "CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-VIOLATION")]
39848 fn assert_char_pair_array_slice_equals_char_pair_array_panics_at_runtime_on_left_column_drift()
39849 {
39850 // NEGATIVE PIN — CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-
39851 // VIOLATION corner: a LEFT-column entry at some position in
39852 // `full[START..START + M)` that does NOT byte-equal the peer
39853 // sub-array `sub` at the offset-matched position MUST panic
39854 // at runtime with the axis-named message. Pins the helper's
39855 // LEFT-column reject arm — a regression that silently
39856 // dropped the LEFT-column check while retaining the RIGHT-
39857 // column check would slip through the compile-time witness's
39858 // LEFT-drift failure mode too. The offending LEFT-column
39859 // char `'!'` at outer position `3` (interior of the sub-
39860 // slice `[1..4)`, offset `2` inside `sub`) pins the middle-
39861 // of-slice LEFT-column drift mode.
39862 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
39863 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('!', 'z'), ('z', 'Z')],
39864 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39865 );
39866 }
39867
39868 #[test]
39869 #[should_panic(expected = "CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-VIOLATION")]
39870 fn assert_char_pair_array_slice_equals_char_pair_array_panics_at_runtime_on_right_column_drift()
39871 {
39872 // NEGATIVE PIN — CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-
39873 // VIOLATION corner: a RIGHT-column entry at some position in
39874 // `full[START..START + M)` that does NOT byte-equal the peer
39875 // sub-array `sub` at the offset-matched position MUST panic
39876 // at runtime with the axis-named message. Pins the helper's
39877 // RIGHT-column reject arm — a regression that silently
39878 // dropped the RIGHT-column check while retaining the LEFT-
39879 // column check would slip through the compile-time witness's
39880 // RIGHT-drift failure mode too. The LEFT column matches
39881 // exactly on every position so ONLY the RIGHT column drift
39882 // (`'!'` at outer position `3`, offset `2` inside `sub`)
39883 // fires the reject arm — routing the diagnostic to the
39884 // RIGHT-COLUMN-* axis rather than the sibling LEFT-COLUMN-*
39885 // axis.
39886 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
39887 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('c', '!'), ('z', 'Z')],
39888 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39889 );
39890 }
39891
39892 #[test]
39893 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
39894 fn assert_char_pair_array_slice_equals_char_pair_array_panics_at_runtime_on_start_out_of_bounds(
39895 ) {
39896 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
39897 // turbofish arity slip on the `START` const-generic where
39898 // `START > N` MUST panic at runtime with the START-OUT-OF-
39899 // BOUNDS-named message BEFORE the peer SLICE-LENGTH-OUT-OF-
39900 // BOUNDS gate reads `N - START` (which would `usize`-
39901 // underflow had this gate not caught the slip first). Pins
39902 // the gate's placement at the TOP of the helper — a
39903 // regression that dropped the gate would either underflow
39904 // subtraction at the peer gate OR panic deeper in
39905 // `full[START + i]` bounds-checking with a helper-name-less
39906 // panic message. The offending `START = 7` against `N = 5`
39907 // pins the strict `START > N` reject arm; the LEGAL
39908 // `START == N` empty-slice-at-right-endpoint corner is
39909 // covered by the peer acceptance test above.
39910 assert_char_pair_array_slice_equals_char_pair_array::<5, 0, 7>(
39911 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39912 &[],
39913 );
39914 }
39915
39916 #[test]
39917 #[should_panic(expected = "SLICE-LENGTH-OUT-OF-BOUNDS")]
39918 fn assert_char_pair_array_slice_equals_char_pair_array_panics_at_runtime_on_slice_length_out_of_bounds(
39919 ) {
39920 // NEGATIVE PIN — SLICE-LENGTH-OUT-OF-BOUNDS gate: a peer
39921 // sub-array arity `M` that exceeds the outer array's tail
39922 // cardinality `N - START` MUST panic at runtime with the
39923 // slice-length-out-of-bounds-named message. Peer gate to the
39924 // START-OUT-OF-BOUNDS arm above — the two gates jointly
39925 // enforce `START ≤ N` and `M ≤ N - START` before any content
39926 // sweep. The offending `M = 5` against `N - START = 5 - 3 =
39927 // 2` pins the strict `M > N - START` reject arm; the LEGAL
39928 // exact-fit corner `M == N - START` is covered by the
39929 // middle-slice acceptance test above.
39930 assert_char_pair_array_slice_equals_char_pair_array::<5, 5, 3>(
39931 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39932 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39933 );
39934 }
39935
39936 #[test]
39937 fn assert_char_pair_array_slice_equals_char_pair_array_panic_message_names_the_helper_and_left_column_violation_axis(
39938 ) {
39939 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-SLICE-EQUALS-
39940 // ARRAY-LEFT-COLUMN-VIOLATION arm: the panic message MUST
39941 // begin with the helper's own name AND identify the failed
39942 // AXIS as "CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-
39943 // VIOLATION" so downstream diagnostics route the drift back
39944 // to (a) the helper by string search on
39945 // `"assert_char_pair_array_slice_equals_char_pair_array"`
39946 // and (b) the failed COLUMN of the paired sweep by string
39947 // search on `"CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-
39948 // VIOLATION"`. Sibling posture to the (u8) / (char) / (str)
39949 // row peers' provenance pins — the FOUR pins together bind
39950 // the (helper, failed-axis) provenance pair at ONE test per
39951 // corner of the (SUB-SLICE ARRAY-image) column across the
39952 // FOUR element-type rows of the matrix.
39953 let outcome = std::panic::catch_unwind(|| {
39954 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
39955 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('!', 'z'), ('z', 'Z')],
39956 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39957 );
39958 });
39959 let payload = outcome.expect_err(
39960 "assert_char_pair_array_slice_equals_char_pair_array must \
39961 panic on a LEFT-column drift — the reject-left-column-\
39962 drift arm is a CONTENT failure mode of the helper",
39963 );
39964 let msg = payload
39965 .downcast_ref::<&'static str>()
39966 .map(|s| (*s).to_owned())
39967 .or_else(|| payload.downcast_ref::<String>().cloned())
39968 .expect(
39969 "assert_char_pair_array_slice_equals_char_pair_array \
39970 panic payload must be a static &str or String",
39971 );
39972 assert!(
39973 msg.contains("assert_char_pair_array_slice_equals_char_pair_array"),
39974 "assert_char_pair_array_slice_equals_char_pair_array panic \
39975 message {msg:?} must name the helper for provenance-\
39976 preserving failure diagnostics",
39977 );
39978 assert!(
39979 msg.contains("CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-VIOLATION"),
39980 "assert_char_pair_array_slice_equals_char_pair_array panic \
39981 message {msg:?} must name the failed AXIS (\"CHAR-PAIR-\
39982 SLICE-EQUALS-ARRAY-LEFT-COLUMN-VIOLATION\") for axis-\
39983 provenance-preserving failure diagnostics",
39984 );
39985 }
39986
39987 #[test]
39988 fn assert_char_pair_array_slice_equals_char_pair_array_panic_message_names_the_helper_and_right_column_violation_axis(
39989 ) {
39990 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-SLICE-EQUALS-
39991 // ARRAY-RIGHT-COLUMN-VIOLATION arm: the panic message MUST
39992 // begin with the helper's own name AND identify the failed
39993 // AXIS as "CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-
39994 // VIOLATION" so downstream diagnostics route the drift back
39995 // to (a) the helper by string search AND (b) the failed
39996 // COLUMN of the paired sweep by string search on the
39997 // RIGHT-COLUMN-* axis. Peer pin to the LEFT-COLUMN
39998 // provenance pin above — the two pins bind the (LEFT,
39999 // RIGHT) column-axis provenance pair at ONE test per column.
40000 let outcome = std::panic::catch_unwind(|| {
40001 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
40002 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('c', '!'), ('z', 'Z')],
40003 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40004 );
40005 });
40006 let payload = outcome.expect_err(
40007 "assert_char_pair_array_slice_equals_char_pair_array must \
40008 panic on a RIGHT-column drift — the reject-right-column-\
40009 drift arm is a CONTENT failure mode of the helper",
40010 );
40011 let msg = payload
40012 .downcast_ref::<&'static str>()
40013 .map(|s| (*s).to_owned())
40014 .or_else(|| payload.downcast_ref::<String>().cloned())
40015 .expect(
40016 "assert_char_pair_array_slice_equals_char_pair_array \
40017 panic payload must be a static &str or String",
40018 );
40019 assert!(
40020 msg.contains("assert_char_pair_array_slice_equals_char_pair_array"),
40021 "assert_char_pair_array_slice_equals_char_pair_array panic \
40022 message {msg:?} must name the helper for provenance-\
40023 preserving failure diagnostics",
40024 );
40025 assert!(
40026 msg.contains("CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-VIOLATION"),
40027 "assert_char_pair_array_slice_equals_char_pair_array panic \
40028 message {msg:?} must name the failed AXIS (\"CHAR-PAIR-\
40029 SLICE-EQUALS-ARRAY-RIGHT-COLUMN-VIOLATION\") for axis-\
40030 provenance-preserving failure diagnostics",
40031 );
40032 }
40033
40034 // ── `assert_char_array_is_concatenation_of_char_pair_array_column_
40035 // and_char_array` — the (char) scalar-row SEGMENTED-CONCATENATION-
40036 // through-PAIRED-COLUMN-PROJECTION verifier that binds `arr ==
40037 // col(head_table) ++ tail` at compile time on the substrate's Str-
40038 // payload escape-table (`ESCAPE_SOURCES`, `NAMED_ESCAPE_TABLE`,
40039 // `SELF_ESCAPE_TABLE`) LEFT-column-projection triple AND the
40040 // (`ESCAPE_DECODED`, `NAMED_ESCAPE_TABLE`, `SELF_ESCAPE_TABLE`)
40041 // RIGHT-column-projection triple, CROSS-ROW peer to the (char, char)
40042 // paired-row (`_is_concatenation_of_char_pair_array_and_char_array_
40043 // diagonal`) sibling on the (segmented-concatenation) column of the
40044 // (element-type × contract-shape) matrix. The runtime test surface
40045 // pins each of the helper's arms — accept the empty triple with
40046 // BOTH `take_right_column` values, accept a head-only + tail-only +
40047 // mixed small triple, accept the two family-wide substrate triples
40048 // for each `take_right_column` value, reject at every failure-arm
40049 // corner (CARDINALITY-MISMATCH, HEAD-SEGMENT-LEFT/RIGHT-COLUMN-
40050 // DIVERGENCE at head/tail positions, TAIL-SEGMENT-DIVERGENCE at
40051 // head/tail positions), pin panic-message provenance on the four
40052 // axes — so a regression that silently weakened the helper on ANY
40053 // arm is caught by the helper's OWN test surface rather than only
40054 // surfacing as a false-positive on some future scalar-composite
40055 // pin.
40056
40057 #[test]
40058 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_the_empty_triple_left_column(
40059 ) {
40060 // Empty arrays at `N == K == M == 0` with `take_right_column ==
40061 // false` — vacuously composite (no position exists to test).
40062 // Pins the outer sweep bounds' short-circuit and the LEFT-column
40063 // branch's dispatch on the empty head sweep.
40064 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<0, 0, 0>(
40065 &[],
40066 &[],
40067 &[],
40068 false,
40069 );
40070 }
40071
40072 #[test]
40073 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_the_empty_triple_right_column(
40074 ) {
40075 // Empty arrays at `N == K == M == 0` with `take_right_column ==
40076 // true` — vacuously composite. Pins the RIGHT-column branch's
40077 // dispatch on the empty head sweep.
40078 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<0, 0, 0>(
40079 &[],
40080 &[],
40081 &[],
40082 true,
40083 );
40084 }
40085
40086 #[test]
40087 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_head_only_triple_left_column(
40088 ) {
40089 // Head-only triple `arr = ['a']`, `head_table = [('a','x')]`,
40090 // `tail = []` with `take_right_column == false` — LEFT column
40091 // projection at position 0. Pins the `while j < M` guard's
40092 // short-circuit on empty tail and the LEFT-column arm firing.
40093 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<1, 1, 0>(
40094 &['a'],
40095 &[('a', 'x')],
40096 &[],
40097 false,
40098 );
40099 }
40100
40101 #[test]
40102 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_head_only_triple_right_column(
40103 ) {
40104 // Head-only triple `arr = ['x']`, `head_table = [('a','x')]`,
40105 // `tail = []` with `take_right_column == true` — RIGHT column
40106 // projection at position 0. Pins the RIGHT-column arm firing on
40107 // the empty-tail short-circuit.
40108 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<1, 1, 0>(
40109 &['x'],
40110 &[('a', 'x')],
40111 &[],
40112 true,
40113 );
40114 }
40115
40116 #[test]
40117 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_tail_only_triple(
40118 ) {
40119 // Tail-only triple `arr = ['q']`, `head_table = []`, `tail =
40120 // ['q']` at the `K == 0` corner — the composite reduces to
40121 // `tail` verbatim, no head-column projection to perform. Pins
40122 // the outer `while i < K` guard's short-circuit on empty head
40123 // (identical behavior on both `take_right_column` values —
40124 // exercised here at `false`; the peer at `true` at the empty-
40125 // triple test above already covers the RIGHT branch's empty-K
40126 // dispatch).
40127 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<1, 0, 1>(
40128 &['q'],
40129 &[],
40130 &['q'],
40131 false,
40132 );
40133 }
40134
40135 #[test]
40136 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_mixed_small_triple_left_column(
40137 ) {
40138 // Mixed small triple `arr = ['a', 'q']`, `head_table =
40139 // [('a','x')]`, `tail = ['q']` at `K == 1, M == 1` with
40140 // `take_right_column == false` — the smallest non-trivial case
40141 // exercising BOTH the LEFT-column head-projection arm and the
40142 // tail-verbatim arm. Pins that BOTH sweeps advance.
40143 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<2, 1, 1>(
40144 &['a', 'q'],
40145 &[('a', 'x')],
40146 &['q'],
40147 false,
40148 );
40149 }
40150
40151 #[test]
40152 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_mixed_small_triple_right_column(
40153 ) {
40154 // Mixed small triple `arr = ['x', 'q']`, `head_table =
40155 // [('a','x')]`, `tail = ['q']` at `K == 1, M == 1` with
40156 // `take_right_column == true` — the smallest non-trivial case
40157 // exercising BOTH the RIGHT-column head-projection arm and the
40158 // tail-verbatim arm.
40159 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<2, 1, 1>(
40160 &['x', 'q'],
40161 &[('a', 'x')],
40162 &['q'],
40163 true,
40164 );
40165 }
40166
40167 #[test]
40168 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_the_family_wide_substrate_left_column_triple(
40169 ) {
40170 // Positive pin on the family-wide (`Atom::ESCAPE_SOURCES`,
40171 // `Atom::NAMED_ESCAPE_TABLE`, `Atom::SELF_ESCAPE_TABLE`,
40172 // `take_right_column: false`) LEFT-column triple this helper
40173 // is applied to at compile time via the module-level `const
40174 // _:` witness. Runtime pin as a second-stage safety net if the
40175 // const-eval sweep is ever silently dropped. Complementary to
40176 // the pre-existing (`_pairwise_distinct`, `_within_char_
40177 // finite_set`) witnesses on the SAME scalar array — those
40178 // pins bind SET-level axes (INJECTIVITY, SUBSET-EMBEDDING);
40179 // this pin binds the SEGMENTED-CONCATENATION structural
40180 // identity axis at the (char) scalar-row cross-row bond.
40181 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array(
40182 &Atom::ESCAPE_SOURCES,
40183 &Atom::NAMED_ESCAPE_TABLE,
40184 &Atom::SELF_ESCAPE_TABLE,
40185 false,
40186 );
40187 }
40188
40189 #[test]
40190 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_the_family_wide_substrate_right_column_triple(
40191 ) {
40192 // Positive pin on the family-wide (`Atom::ESCAPE_DECODED`,
40193 // `Atom::NAMED_ESCAPE_TABLE`, `Atom::SELF_ESCAPE_TABLE`,
40194 // `take_right_column: true`) RIGHT-column triple this helper is
40195 // applied to at compile time via the SECOND module-level
40196 // `const _:` witness. Runtime pin as a second-stage safety net.
40197 // The two family-wide RUNTIME pins (LEFT above, RIGHT here)
40198 // mirror the two module-level `const _:` witnesses at ONE
40199 // runtime posture per column.
40200 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array(
40201 &Atom::ESCAPE_DECODED,
40202 &Atom::NAMED_ESCAPE_TABLE,
40203 &Atom::SELF_ESCAPE_TABLE,
40204 true,
40205 );
40206 }
40207
40208 #[test]
40209 #[should_panic(
40210 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40211 )]
40212 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_head_left_column_head_position_divergence(
40213 ) {
40214 // NEGATIVE PIN — HEAD-SEGMENT LEFT-COLUMN head-position
40215 // (i == 0) corner: `arr[0] = 'z'` diverges from `head_table[0].0
40216 // = 'a'` on the FIRST head-segment position with
40217 // `take_right_column == false`. Pins the HEAD-LEFT-COLUMN arm
40218 // firing at the head.
40219 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40220 &['z', 'b', 'q'],
40221 &[('a', 'x'), ('b', 'y')],
40222 &['q'],
40223 false,
40224 );
40225 }
40226
40227 #[test]
40228 #[should_panic(
40229 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40230 )]
40231 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_head_left_column_tail_position_divergence(
40232 ) {
40233 // NEGATIVE PIN — HEAD-SEGMENT LEFT-COLUMN tail-position
40234 // (i == K-1) corner: divergence at the LAST head-segment
40235 // position MUST also fire. Pins the outer head-sweep bound
40236 // `while i < K` — a regression that walked `while i < K - 1`
40237 // would silently accept a tail-of-head divergence.
40238 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40239 &['a', 'z', 'q'],
40240 &[('a', 'x'), ('b', 'y')],
40241 &['q'],
40242 false,
40243 );
40244 }
40245
40246 #[test]
40247 #[should_panic(
40248 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40249 )]
40250 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_head_right_column_head_position_divergence(
40251 ) {
40252 // NEGATIVE PIN — HEAD-SEGMENT RIGHT-COLUMN head-position
40253 // (i == 0) corner: symmetric to the HEAD-LEFT-COLUMN arm at
40254 // the head with `take_right_column == true`. Pins the HEAD-
40255 // RIGHT-COLUMN arm firing at position 0.
40256 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40257 &['z', 'y', 'q'],
40258 &[('a', 'x'), ('b', 'y')],
40259 &['q'],
40260 true,
40261 );
40262 }
40263
40264 #[test]
40265 #[should_panic(
40266 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40267 )]
40268 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_head_right_column_tail_position_divergence(
40269 ) {
40270 // NEGATIVE PIN — HEAD-SEGMENT RIGHT-COLUMN tail-position
40271 // (i == K-1) corner: divergence at the LAST head-segment
40272 // RIGHT-column position MUST also fire. Symmetric to the
40273 // LEFT-column tail-position arm.
40274 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40275 &['x', 'z', 'q'],
40276 &[('a', 'x'), ('b', 'y')],
40277 &['q'],
40278 true,
40279 );
40280 }
40281
40282 #[test]
40283 #[should_panic(
40284 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40285 )]
40286 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_tail_head_position_divergence(
40287 ) {
40288 // NEGATIVE PIN — TAIL-SEGMENT head-position (j == 0) corner:
40289 // divergence at `arr[K + 0] = 'z'` vs `tail[0] = 'q'` fires
40290 // after the head-sweep succeeds. Pins the outer tail-sweep's
40291 // `while j < M` guard firing at j == 0 and correctly using
40292 // `K + j` for `arr` indexing. INDEPENDENT of
40293 // `take_right_column` — the tail is scalar-verbatim, not
40294 // column-projected.
40295 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 1, 2>(
40296 &['a', 'z', 'r'],
40297 &[('a', 'x')],
40298 &['q', 'r'],
40299 false,
40300 );
40301 }
40302
40303 #[test]
40304 #[should_panic(
40305 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40306 )]
40307 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_tail_tail_position_divergence(
40308 ) {
40309 // NEGATIVE PIN — TAIL-SEGMENT tail-position (j == M-1) corner:
40310 // divergence at the LAST tail-segment position MUST also fire.
40311 // Pins the outer tail-sweep bound `while j < M` — a regression
40312 // that walked `while j < M - 1` (dropping the last tail entry)
40313 // would silently accept a tail-of-tail divergence.
40314 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 1, 2>(
40315 &['a', 'q', 'z'],
40316 &[('a', 'x')],
40317 &['q', 'r'],
40318 false,
40319 );
40320 }
40321
40322 #[test]
40323 #[should_panic(
40324 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40325 )]
40326 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_cardinality_mismatch(
40327 ) {
40328 // NEGATIVE PIN — CARDINALITY-MISMATCH corner: fires BEFORE any
40329 // per-position sweep at the FIRST guard. `K + M == 3 + 3 == 6`
40330 // vs `N == 5` — the caller's turbofish is inconsistent. A
40331 // mistyped ARITY doesn't degenerate into a silent truncation
40332 // of the segment sweep.
40333 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<5, 3, 3>(
40334 &['a', 'b', 'c', 'q', 'r'],
40335 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40336 &['q', 'r', 's'],
40337 false,
40338 );
40339 }
40340
40341 #[test]
40342 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panic_message_names_the_helper_and_head_left_column_divergence_axis(
40343 ) {
40344 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-LEFT-COLUMN-
40345 // DIVERGENCE arm: distinct axis vocabulary distinguishing this
40346 // helper's LEFT-column head-divergence from the paired-row
40347 // diagonal-tail sibling's `HEAD-SEGMENT-LEFT-DIVERGENCE` axis
40348 // (this helper's `-COLUMN-` infix distinguishes them on any
40349 // downstream substring search) AND from the sibling
40350 // `_columns_equal_char_arrays`'s `LEFT-COLUMN-DIVERGENCE`
40351 // axis (this helper's `HEAD-SEGMENT-` prefix distinguishes
40352 // them).
40353 let outcome = std::panic::catch_unwind(|| {
40354 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40355 &['z', 'b', 'q'],
40356 &[('a', 'x'), ('b', 'y')],
40357 &['q'],
40358 false,
40359 );
40360 });
40361 let payload = outcome.expect_err(
40362 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40363 must panic on a HEAD-SEGMENT LEFT-COLUMN head-position \
40364 divergence",
40365 );
40366 let msg = payload
40367 .downcast_ref::<&'static str>()
40368 .map(|s| (*s).to_owned())
40369 .or_else(|| payload.downcast_ref::<String>().cloned())
40370 .expect(
40371 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40372 panic payload must be a static &str or String",
40373 );
40374 assert!(
40375 msg.contains("HEAD-SEGMENT-LEFT-COLUMN-DIVERGENCE"),
40376 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40377 panic message {msg:?} must name the failed AXIS \
40378 (\"HEAD-SEGMENT-LEFT-COLUMN-DIVERGENCE\") for axis-\
40379 provenance-preserving failure diagnostics",
40380 );
40381 }
40382
40383 #[test]
40384 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panic_message_names_the_helper_and_head_right_column_divergence_axis(
40385 ) {
40386 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-RIGHT-COLUMN-
40387 // DIVERGENCE arm: distinct axis vocabulary at the RIGHT-column
40388 // dual of the LEFT-column head-divergence provenance arm.
40389 let outcome = std::panic::catch_unwind(|| {
40390 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40391 &['z', 'y', 'q'],
40392 &[('a', 'x'), ('b', 'y')],
40393 &['q'],
40394 true,
40395 );
40396 });
40397 let payload = outcome.expect_err(
40398 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40399 must panic on a HEAD-SEGMENT RIGHT-COLUMN head-position \
40400 divergence",
40401 );
40402 let msg = payload
40403 .downcast_ref::<&'static str>()
40404 .map(|s| (*s).to_owned())
40405 .or_else(|| payload.downcast_ref::<String>().cloned())
40406 .expect(
40407 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40408 panic payload must be a static &str or String",
40409 );
40410 assert!(
40411 msg.contains("HEAD-SEGMENT-RIGHT-COLUMN-DIVERGENCE"),
40412 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40413 panic message {msg:?} must name the failed AXIS \
40414 (\"HEAD-SEGMENT-RIGHT-COLUMN-DIVERGENCE\") for axis-\
40415 provenance-preserving failure diagnostics",
40416 );
40417 }
40418
40419 #[test]
40420 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panic_message_names_the_helper_and_tail_segment_divergence_axis(
40421 ) {
40422 // PANIC-MESSAGE PROVENANCE PIN — TAIL-SEGMENT-DIVERGENCE arm:
40423 // distinct axis vocabulary distinguishing this helper's tail-
40424 // arm from the paired-row diagonal-tail sibling's `DIAGONAL-
40425 // TAIL-SEGMENT-{LEFT,RIGHT}-DIVERGENCE` axes (this helper's
40426 // tail arm omits `DIAGONAL-` and `-{LEFT,RIGHT}` — the scalar
40427 // tail is column-invariant).
40428 let outcome = std::panic::catch_unwind(|| {
40429 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 1, 2>(
40430 &['a', 'z', 'r'],
40431 &[('a', 'x')],
40432 &['q', 'r'],
40433 false,
40434 );
40435 });
40436 let payload = outcome.expect_err(
40437 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40438 must panic on a TAIL-SEGMENT head-position divergence",
40439 );
40440 let msg = payload
40441 .downcast_ref::<&'static str>()
40442 .map(|s| (*s).to_owned())
40443 .or_else(|| payload.downcast_ref::<String>().cloned())
40444 .expect(
40445 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40446 panic payload must be a static &str or String",
40447 );
40448 assert!(
40449 msg.contains("TAIL-SEGMENT-DIVERGENCE"),
40450 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40451 panic message {msg:?} must name the failed AXIS \
40452 (\"TAIL-SEGMENT-DIVERGENCE\") for axis-provenance-\
40453 preserving failure diagnostics",
40454 );
40455 }
40456
40457 #[test]
40458 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panic_message_names_the_helper_and_cardinality_mismatch_axis(
40459 ) {
40460 // PANIC-MESSAGE PROVENANCE PIN — CARDINALITY-MISMATCH arm:
40461 // fires BEFORE any per-position sweep at a distinct axis
40462 // vocabulary. Pins the FIRST guard's provenance so a
40463 // diagnostic reading the axis distinguishes ARITY drift from
40464 // CONTENT drift.
40465 let outcome = std::panic::catch_unwind(|| {
40466 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<5, 3, 3>(
40467 &['a', 'b', 'c', 'q', 'r'],
40468 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40469 &['q', 'r', 's'],
40470 false,
40471 );
40472 });
40473 let payload = outcome.expect_err(
40474 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40475 must panic on a CARDINALITY-MISMATCH `K + M != N`",
40476 );
40477 let msg = payload
40478 .downcast_ref::<&'static str>()
40479 .map(|s| (*s).to_owned())
40480 .or_else(|| payload.downcast_ref::<String>().cloned())
40481 .expect(
40482 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40483 panic payload must be a static &str or String",
40484 );
40485 assert!(
40486 msg.contains("CARDINALITY-MISMATCH"),
40487 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40488 panic message {msg:?} must name the failed AXIS \
40489 (\"CARDINALITY-MISMATCH\") for axis-provenance-preserving \
40490 failure diagnostics",
40491 );
40492 }
40493
40494 // ── `assert_char_pair_array_within_char_pair_finite_set` — the
40495 // `(char, char)` product-element SUBSET-EMBEDDING verifier that
40496 // binds `arr ⊆ set` at compile time on the paired-array vocabulary
40497 // via CONJOINED-pair equality, peer to the (char) + (u8) +
40498 // (`&'static str`) scalar rows' SUBSET-EMBEDDING helpers on the
40499 // (element-type × contract-shape) matrix at the (subset-embedding)
40500 // column. The runtime test surface pins each of the helper's arms
40501 // (accept-empty, accept-singleton-in-set at three positions,
40502 // accept-arr-equals-set, accept-family-wide-substrate-subset on
40503 // the pinned `NAMED_ESCAPE_TABLE ⊆ ESCAPE_TABLE` pair, accept-
40504 // repeated-array-entries-in-set, reject-out-of-set-pair, reject-
40505 // terminal-out-of-set-pair, reject-cross-row-alias, panic-message-
40506 // provenance on the CHAR-PAIR-SUBSET-VIOLATION axis, negative pin
40507 // on the DELEGATED SET-side BIJECTIVITY well-formedness arm at
40508 // BOTH the LEFT-column and RIGHT-column collision corners) so a
40509 // regression that silently weakened the helper on ANY arm is
40510 // caught by the helper's OWN test surface rather than only
40511 // surfacing as a false-positive on some future subset-embedded
40512 // `[(char, char); N]` array's compound pin.
40513
40514 #[test]
40515 fn assert_char_pair_array_within_char_pair_finite_set_accepts_the_empty_array_within_any_set() {
40516 // Empty array `arr = []` at the `[(char, char); 0]` corner —
40517 // vacuously a subset of every well-formed BIJECTIVE set (no
40518 // `i` position exists to test). Cross-arity coverage on the
40519 // trivial ARRAY corner of the const-N generic across three
40520 // witness-set widths (empty, singleton, multi-entry) to pin
40521 // the helper's OUTER-sweep arm across the whole (`N == 0` ×
40522 // `M`) axis. Turbofish binding required because there's no
40523 // other cue for the const parameters on the empty array
40524 // literal. Sibling posture to
40525 // `assert_char_array_within_char_finite_set_accepts_the_empty_array_within_any_set`
40526 // on the (char) scalar row-dual peer — the two share the
40527 // trivial-arity arm across the (scalar, paired) element-type
40528 // partition on the (subset-embedding) column.
40529 assert_char_pair_array_within_char_pair_finite_set::<0, 0>(&[], &[]);
40530 assert_char_pair_array_within_char_pair_finite_set::<0, 1>(&[], &[('a', 'x')]);
40531 assert_char_pair_array_within_char_pair_finite_set::<0, 3>(
40532 &[],
40533 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40534 );
40535 }
40536
40537 #[test]
40538 fn assert_char_pair_array_within_char_pair_finite_set_accepts_singleton_array_when_pair_in_set()
40539 {
40540 // Singleton array `arr = [P]` at the `[(char, char); 1]`
40541 // corner MUST pass when `P ∈ set` by CONJOINED-pair equality.
40542 // Cross-position coverage: the pair can sit at the FIRST,
40543 // MIDDLE, or LAST position of the `set` — pins the INNER
40544 // `while j < M` sweep terminates at the first-match position
40545 // rather than always at position `0` OR always at position
40546 // `M - 1`. A regression that narrowed the inner sweep to
40547 // `j == 0` would silently reject singleton arrays hitting
40548 // non-first set positions. Sibling posture to
40549 // `assert_char_array_within_char_finite_set_accepts_singleton_array_when_char_in_set`
40550 // on the (char) scalar row-dual peer — the two share the
40551 // FIRST/MIDDLE/LAST sweep-position arm across the (scalar,
40552 // paired) element-type partition.
40553 assert_char_pair_array_within_char_pair_finite_set::<1, 3>(
40554 &[('a', 'x')],
40555 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40556 );
40557 assert_char_pair_array_within_char_pair_finite_set::<1, 3>(
40558 &[('b', 'y')],
40559 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40560 );
40561 assert_char_pair_array_within_char_pair_finite_set::<1, 3>(
40562 &[('c', 'z')],
40563 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40564 );
40565 }
40566
40567 #[test]
40568 fn assert_char_pair_array_within_char_pair_finite_set_accepts_arr_equals_set() {
40569 // Boundary corner where `arr` and `set` cover pair-for-pair
40570 // identical distinct-value sets — the SUBSET relation
40571 // degenerates to EQUALITY. Pins that the helper does NOT
40572 // gratuitously require the SUBSET to be PROPER (strict):
40573 // equal-multisets pass the SUBSET check. Sibling posture to
40574 // `assert_char_array_within_char_finite_set_accepts_arr_equals_set`
40575 // on the (char) scalar row-dual peer.
40576 assert_char_pair_array_within_char_pair_finite_set(&[('a', 'x')], &[('a', 'x')]);
40577 assert_char_pair_array_within_char_pair_finite_set(
40578 &[('a', 'x'), ('b', 'y')],
40579 &[('a', 'x'), ('b', 'y')],
40580 );
40581 }
40582
40583 #[test]
40584 fn assert_char_pair_array_within_char_pair_finite_set_accepts_the_family_wide_substrate_subset()
40585 {
40586 // Runtime cross-check that the ONE (subset, superset)
40587 // paired-array pair the substrate's module-level `const _`
40588 // witness pins at COMPILE time is a proper SUBSET embedding
40589 // at runtime too. The pair enforces the theorem at TWO stages
40590 // of the toolchain: the const witness fires FIRST at `cargo
40591 // check` (through the module-level `const _: () =
40592 // assert_char_pair_array_within_char_pair_finite_set::<3, 5>(...)`
40593 // line), this runtime pin catches the drift at `cargo test`
40594 // as a safety net. Sibling posture to
40595 // `assert_char_pair_array_bijective_accepts_every_family_wide_substrate_array`
40596 // which sweeps the two family-wide `[(char, char); N]` arrays
40597 // at the paired-array INJECTIVITY axis; this pin sweeps the
40598 // ONE (subset, superset) PAIR at the paired-array SUBSET-
40599 // EMBEDDING axis. Together the two pins bind the (paired
40600 // array, paired subset-embedding) 2-corner face on the
40601 // `(char, char)` row of the (element-type × contract-shape)
40602 // matrix.
40603 assert_char_pair_array_within_char_pair_finite_set::<3, 5>(
40604 &Atom::NAMED_ESCAPE_TABLE,
40605 &Atom::ESCAPE_TABLE,
40606 );
40607 }
40608
40609 #[test]
40610 fn assert_char_pair_array_within_char_pair_finite_set_accepts_repeated_array_entries_in_set() {
40611 // Peer corner to a (future-lift) `_covers_char_pair_finite_
40612 // set`: this helper permits duplicates in `arr` because
40613 // SUBSET-membership is a DISTINCT-value predicate — the
40614 // pair-multiset `[('a', 'x'), ('a', 'x'), ('b', 'y')]` is a
40615 // subset of the bijective set `{('a', 'x'), ('b', 'y'),
40616 // ('c', 'z')}` even though the array is not pairwise-
40617 // distinct. Pins that the helper does NOT gratuitously
40618 // require BIJECTIVITY on `arr` (the BIJECTIVITY axis is a
40619 // DIFFERENT compile-time contract bound by
40620 // `assert_char_pair_array_bijective`; combining both binds
40621 // BOTH axes). Sibling posture to
40622 // `assert_char_array_within_char_finite_set_accepts_repeated_array_entries_in_set`
40623 // on the (char) scalar row-dual peer.
40624 assert_char_pair_array_within_char_pair_finite_set(
40625 &[('a', 'x'), ('a', 'x'), ('b', 'y')],
40626 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40627 );
40628 }
40629
40630 #[test]
40631 #[should_panic(expected = "CHAR-PAIR-SUBSET-VIOLATION")]
40632 fn assert_char_pair_array_within_char_pair_finite_set_panics_at_runtime_on_out_of_set_pair() {
40633 // NEGATIVE PIN — CHAR-PAIR-SUBSET-VIOLATION corner: an array
40634 // carrying a single pair NOT in the target set MUST panic at
40635 // runtime with the CHAR-PAIR-SUBSET-VIOLATION-named message.
40636 // Pins the helper's OWN reject arm — a regression that
40637 // silently returned without panicking on an out-of-set pair
40638 // would slip through the compile-time witness's failure mode
40639 // too. The offending pair `('z', 'w')` is intentionally
40640 // chosen OUTSIDE the target set to pin the OUT-OF-SET drift
40641 // mode.
40642 assert_char_pair_array_within_char_pair_finite_set(
40643 &[('a', 'x'), ('z', 'w')],
40644 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40645 );
40646 }
40647
40648 #[test]
40649 #[should_panic(expected = "CHAR-PAIR-SUBSET-VIOLATION")]
40650 fn assert_char_pair_array_within_char_pair_finite_set_panics_at_runtime_on_terminal_out_of_set_pair(
40651 ) {
40652 // NEGATIVE PIN — terminal-position drift: an out-of-set pair
40653 // at the LAST array position MUST panic — pins that the
40654 // outer `while i < N` loop reaches `i = N - 1` (else the
40655 // terminal drift would slip through). A regression that
40656 // narrowed the outer sweep to `while i < N - 1` (off-by-one
40657 // on the OUTER bound) would silently accept this array.
40658 // Sibling posture to
40659 // `assert_char_array_within_char_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
40660 // on the (char) scalar row-dual peer — both bind the outer-
40661 // sweep terminal bound at the ONE array-side outer loop the
40662 // helper carries.
40663 assert_char_pair_array_within_char_pair_finite_set(
40664 &[('a', 'x'), ('b', 'y'), ('c', 'z'), ('z', 'w')],
40665 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40666 );
40667 }
40668
40669 #[test]
40670 #[should_panic(expected = "CHAR-PAIR-SUBSET-VIOLATION")]
40671 fn assert_char_pair_array_within_char_pair_finite_set_panics_at_runtime_on_cross_row_alias_pair(
40672 ) {
40673 // KEY LOAD-BEARING NEGATIVE PIN — CONJOINED-pair equality
40674 // gate corner: a pair whose LEFT column matches ONE set
40675 // entry's LEFT column AND whose RIGHT column matches a
40676 // DIFFERENT set entry's RIGHT column but the CONJOINED pair
40677 // itself is NOT in the set MUST panic. This is the exact
40678 // property that distinguishes the CONJOINED-pair SUBSET
40679 // helper from a per-column-membership check: a regression
40680 // that silently split the CONJOINED equality gate into TWO
40681 // separate per-column membership sweeps (one over the LEFT
40682 // column, one over the RIGHT column) would accept the pair
40683 // `('a', 'y')` here (`'a'` appears in the set's LEFT column
40684 // via `('a', 'x')`, `'y'` appears in the set's RIGHT column
40685 // via `('b', 'y')`), but the CONJOINED pair `('a', 'y')` is
40686 // NOT in the set. A drift from `&&` to `||` on the CONJOINED
40687 // gate would silently accept this cross-row aliasing pair.
40688 assert_char_pair_array_within_char_pair_finite_set(
40689 &[('a', 'y')],
40690 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40691 );
40692 }
40693
40694 #[test]
40695 fn assert_char_pair_array_within_char_pair_finite_set_panic_message_names_the_helper_and_char_pair_subset_violation_axis(
40696 ) {
40697 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-SUBSET-VIOLATION
40698 // arm: the panic message MUST begin with the helper's own
40699 // name AND identify the failed AXIS as "CHAR-PAIR-SUBSET-
40700 // VIOLATION" so downstream diagnostics route the drift back
40701 // to (a) the helper by string search on
40702 // `"assert_char_pair_array_within_char_pair_finite_set"` and
40703 // (b) the axis by string search on `"CHAR-PAIR-SUBSET-
40704 // VIOLATION"`. Sibling posture to
40705 // `assert_char_array_within_char_finite_set_panic_message_names_the_helper_and_char_subset_violation_axis`
40706 // on the (char) scalar row-dual peer's provenance pin — the
40707 // two pins together bind the (helper, failed-axis)
40708 // provenance pair at ONE test per SUBSET helper on the
40709 // (element-type × contract-shape) matrix's SUBSET column.
40710 // The axis-provenance string `"CHAR-PAIR-SUBSET-VIOLATION"`
40711 // is chosen DISTINCT from EVERY sibling helper's axis
40712 // vocabulary (`"CHAR-SUBSET-VIOLATION"` on the (char) scalar
40713 // row-dual SUBSET peer; `"SUBSET-VIOLATION"` on the (u8)
40714 // scalar row-dual finite-set SUBSET peer; `"STR-SUBSET-
40715 // VIOLATION"` on the (`&'static str`) scalar row-dual
40716 // SUBSET peer; `"RANGE-SUBSET-VIOLATION"` on the (u8) range
40717 // SUBSET peer; `"CHAR-DISJOINTNESS-VIOLATION"` / `"U8-
40718 // DISJOINTNESS-VIOLATION"` / `"STR-DISJOINTNESS-VIOLATION"`
40719 // on the DISJOINTNESS-column row peers; `"LEFT column"` /
40720 // `"RIGHT column"` on the paired-array BIJECTIVITY sibling)
40721 // so a diagnostic that names the failed axis routes
40722 // UNAMBIGUOUSLY to (a) this specific paired-array SUBSET-
40723 // embedding helper, (b) the `arr` argument as the drift
40724 // site rather than the `set` argument specifying the target
40725 // paired superset.
40726 let outcome = std::panic::catch_unwind(|| {
40727 assert_char_pair_array_within_char_pair_finite_set(
40728 &[('a', 'x'), ('z', 'w')],
40729 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40730 );
40731 });
40732 let payload = outcome.expect_err(
40733 "assert_char_pair_array_within_char_pair_finite_set must \
40734 panic on an out-of-set pair — the reject-out-of-set arm \
40735 is the sole CHAR-PAIR-SUBSET-VIOLATION failure mode of \
40736 the helper",
40737 );
40738 let msg = payload
40739 .downcast_ref::<&'static str>()
40740 .map(|s| (*s).to_owned())
40741 .or_else(|| payload.downcast_ref::<String>().cloned())
40742 .expect(
40743 "assert_char_pair_array_within_char_pair_finite_set \
40744 panic payload must be a static &str or String",
40745 );
40746 assert!(
40747 msg.contains("assert_char_pair_array_within_char_pair_finite_set"),
40748 "assert_char_pair_array_within_char_pair_finite_set \
40749 panic message {msg:?} must name the helper for \
40750 provenance-preserving failure diagnostics",
40751 );
40752 assert!(
40753 msg.contains("CHAR-PAIR-SUBSET-VIOLATION"),
40754 "assert_char_pair_array_within_char_pair_finite_set \
40755 panic message {msg:?} must name the failed AXIS \
40756 (\"CHAR-PAIR-SUBSET-VIOLATION\") for axis-provenance-\
40757 preserving failure diagnostics",
40758 );
40759 }
40760
40761 #[test]
40762 #[should_panic(expected = "assert_char_pair_array_bijective")]
40763 fn assert_char_pair_array_within_char_pair_finite_set_panics_on_malformed_target_set_left_column_collision(
40764 ) {
40765 // NEGATIVE PIN — DELEGATED SET-side BIJECTIVITY well-
40766 // formedness at the LEFT-column collision corner: a
40767 // malformed target-set spec `[('a', 'x'), ('a', 'y')]`
40768 // (LEFT-column duplicate `'a'`) fed into the ARRAY-side
40769 // within helper MUST panic on the DELEGATED bijectivity arm
40770 // BEFORE the CHAR-PAIR-SUBSET-VIOLATION arm fires. Pins the
40771 // delegation chain: a regression that dropped the
40772 // `assert_char_pair_array_bijective(set)` call at the top of
40773 // `assert_char_pair_array_within_char_pair_finite_set` would
40774 // silently accept a malformed set and produce a false-
40775 // positive verdict on any `arr` embedded in the distinct-
40776 // value bijective subset. The panic message here surfaces
40777 // from the SIBLING helper (containing the paired-array
40778 // BIJECTIVITY helper's `"assert_char_pair_array_bijective"`
40779 // panic-name prefix rather than a bespoke `"SET-NOT-
40780 // BIJECTIVE"` axis string) because the paired row's
40781 // delegation reuses the ARRAY-side BIJECTIVITY helper
40782 // directly per the design choice documented on the SET-side-
40783 // well-formedness section of the helper's docstring. Sibling
40784 // posture to
40785 // `assert_char_array_within_char_finite_set_panics_on_malformed_target_set_spec`
40786 // on the (char) scalar row-dual peer's delegated-SET-well-
40787 // formedness pin. Peer corner to the RIGHT-column companion
40788 // pin below.
40789 assert_char_pair_array_within_char_pair_finite_set::<1, 2>(
40790 &[('a', 'x')],
40791 &[('a', 'x'), ('a', 'y')],
40792 );
40793 }
40794
40795 #[test]
40796 #[should_panic(expected = "assert_char_pair_array_bijective")]
40797 fn assert_char_pair_array_within_char_pair_finite_set_panics_on_malformed_target_set_right_column_collision(
40798 ) {
40799 // NEGATIVE PIN — DELEGATED SET-side BIJECTIVITY well-
40800 // formedness at the RIGHT-column collision corner: a
40801 // malformed target-set spec `[('a', 'x'), ('b', 'x')]`
40802 // (RIGHT-column duplicate `'x'`) fed into the ARRAY-side
40803 // within helper MUST panic on the DELEGATED bijectivity arm
40804 // BEFORE the CHAR-PAIR-SUBSET-VIOLATION arm fires. Column-
40805 // symmetric sibling to the LEFT-column pin above — a
40806 // regression that silently narrowed the delegated
40807 // bijectivity sweep to only the LEFT column (dropping the
40808 // RIGHT-column injectivity half) would slip a RIGHT-column-
40809 // colliding set past the delegation while still binding a
40810 // LEFT-column-colliding one. The two pins together bind the
40811 // FULL bijectivity delegation across BOTH columns of the
40812 // paired-array well-formedness contract.
40813 assert_char_pair_array_within_char_pair_finite_set::<1, 2>(
40814 &[('a', 'x')],
40815 &[('a', 'x'), ('b', 'x')],
40816 );
40817 }
40818
40819 // ── `assert_char_pair_arrays_disjoint` — the CHAR-PAIR-DISJOINTNESS-
40820 // VIOLATION verifier that binds `a ∩ b = ∅` at compile time on the
40821 // paired-array vocabulary via CONJOINED-pair equality, peer to the
40822 // (char) + (u8) + (`&'static str`) SCALAR rows' DISJOINTNESS helpers
40823 // on the (element-type × contract-shape) matrix at the (disjointness)
40824 // column. Contract-orthogonal peer to
40825 // `assert_char_pair_array_within_char_pair_finite_set` on the
40826 // (SUBSET-EMBEDDING, DISJOINTNESS) axis of the (contract-shape)
40827 // column on the SAME paired-array row. The runtime test surface
40828 // pins each of the helper's arms (accept-empty × 3, accept-disjoint
40829 // singletons, accept-family-wide-substrate-pair on the pinned
40830 // `NAMED_ESCAPE_TABLE` disjoint from SELF-as-pairs partition,
40831 // accept-per-column-alias-only corner (the CONJOINED-gate load-
40832 // bearing NON-INSTANCE of the stricter per-column disjointness
40833 // contract), reject-full-pair-collision, reject-terminal-position
40834 // collision, argument-order symmetry, panic-message provenance on
40835 // the CHAR-PAIR-DISJOINTNESS-VIOLATION axis) so a regression that
40836 // silently weakened the helper on ANY arm is caught by the helper's
40837 // OWN test surface rather than only surfacing as a false-positive on
40838 // some future disjoint `[(char, char); N]` paired-array pair's
40839 // compound pin.
40840
40841 #[test]
40842 fn assert_char_pair_arrays_disjoint_accepts_both_empty_arrays() {
40843 // Empty × empty at the `[(char, char); 0] × [(char, char); 0]`
40844 // corner — vacuously disjoint (no `(i, j)` position pair exists
40845 // to test). The module-level `const _: () =
40846 // assert_char_pair_arrays_disjoint(&[], &[])` witness would bind
40847 // this at compile time; this runtime pin catches the drift a
40848 // second time at test-run stage as a safety net. Turbofish
40849 // binding required because there's no other cue for the const
40850 // parameters on the two empty array literals. Sibling posture
40851 // to `assert_char_arrays_disjoint_accepts_both_empty_arrays` on
40852 // the (char) SCALAR row-dual peer.
40853 assert_char_pair_arrays_disjoint::<0, 0>(&[], &[]);
40854 }
40855
40856 #[test]
40857 fn assert_char_pair_arrays_disjoint_accepts_either_side_empty() {
40858 // Empty × non-empty (and non-empty × empty) at the outer-loop-
40859 // vacuous corners — the outer `while i < N` sweep terminates
40860 // immediately when `N == 0`, and the outer sweep executing with
40861 // a non-empty `a` against an empty `b` finds no `j` to test
40862 // (inner sweep vacuous). Cross-position coverage on the two
40863 // vacuous outer arms. Sibling posture to
40864 // `assert_char_arrays_disjoint_accepts_either_side_empty` on the
40865 // (char) SCALAR row-dual peer — the two share the vacuous-arm
40866 // arm across the (scalar, paired) element-type partition on the
40867 // (disjointness) column.
40868 assert_char_pair_arrays_disjoint::<0, 3>(&[], &[('a', 'x'), ('b', 'y'), ('c', 'z')]);
40869 assert_char_pair_arrays_disjoint::<3, 0>(&[('a', 'x'), ('b', 'y'), ('c', 'z')], &[]);
40870 }
40871
40872 #[test]
40873 fn assert_char_pair_arrays_disjoint_accepts_disjoint_singletons() {
40874 // Singleton × singleton at the `[(char, char); 1] × [(char,
40875 // char); 1]` corner with the two pairs distinct across BOTH
40876 // columns — the minimal non-vacuous accept arm. Pins the
40877 // CONJOINED-pair equality gate does NOT gratuitously match when
40878 // BOTH columns differ. Sibling posture to
40879 // `assert_char_arrays_disjoint_accepts_disjoint_singletons` on
40880 // the (char) SCALAR row-dual peer.
40881 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('b', 'y')]);
40882 }
40883
40884 #[test]
40885 fn assert_char_pair_arrays_disjoint_accepts_the_family_wide_substrate_pair() {
40886 // Runtime cross-check that the ONE (a, b) paired-array pair the
40887 // substrate's module-level `const _` witness pins at COMPILE
40888 // time is a proper DISJOINT relation at runtime too. The pair
40889 // enforces the theorem at TWO stages of the toolchain: the
40890 // const witness fires FIRST at `cargo check` (through the
40891 // module-level `const _: () = assert_char_pair_arrays_disjoint::
40892 // <3, 2>(...)` line), this runtime pin catches the drift at
40893 // `cargo test` as a safety net. Sibling posture to
40894 // `assert_char_pair_array_within_char_pair_finite_set_accepts_
40895 // the_family_wide_substrate_subset` which sweeps the ONE (arr,
40896 // set) pair at the paired-array SUBSET-EMBEDDING axis; this pin
40897 // sweeps the ONE (a, b) pair at the paired-array DISJOINTNESS
40898 // axis. Together the two pins bind the (paired subset-
40899 // embedding, paired disjointness) 2-corner face on the
40900 // `(char, char)` row of the (element-type × contract-shape)
40901 // matrix. The `NAMED_ESCAPE_TABLE` disjoint from SELF-as-pairs
40902 // partition of `ESCAPE_TABLE` is the substrate-load-bearing
40903 // instance: a NAMED escape source `('n' / 't' / 'r')` cannot
40904 // simultaneously ALSO be a SELF-escape source `STR_DELIMITER /
40905 // STR_ESCAPE_LEAD` — the two sub-vocabularies of
40906 // `Atom::decode_str_escape` MUST partition disjointly to route
40907 // each escape byte through EXACTLY ONE arm.
40908 assert_char_pair_arrays_disjoint::<3, 2>(
40909 &Atom::NAMED_ESCAPE_TABLE,
40910 &[
40911 (Atom::SELF_ESCAPE_TABLE[0], Atom::SELF_ESCAPE_TABLE[0]),
40912 (Atom::SELF_ESCAPE_TABLE[1], Atom::SELF_ESCAPE_TABLE[1]),
40913 ],
40914 );
40915 }
40916
40917 #[test]
40918 fn assert_char_pair_arrays_disjoint_accepts_per_column_alias_when_conjoined_pair_differs() {
40919 // KEY LOAD-BEARING ACCEPT PIN — CONJOINED-pair equality gate
40920 // NON-INSTANCE corner: two paired arrays whose LEFT columns
40921 // share an entry OR whose RIGHT columns share an entry but the
40922 // CONJOINED pairs themselves differ MUST be accepted as
40923 // disjoint. This is the exact property that distinguishes the
40924 // CONJOINED-pair DISJOINTNESS helper from a stricter per-column
40925 // disjointness check: a regression that split the CONJOINED
40926 // equality gate into TWO separate per-column membership sweeps
40927 // (one over the LEFT column, one over the RIGHT column) would
40928 // REJECT the pair `(&[('a', 'x')], &[('a', 'y')])` here as a
40929 // false-positive collision (`'a'` appears in BOTH arrays' LEFT
40930 // columns via `('a', 'x')` and `('a', 'y')` respectively), but
40931 // the CONJOINED pairs `('a', 'x')` and `('a', 'y')` themselves
40932 // differ so the paired arrays ARE disjoint. A drift from `&&`
40933 // to `||` on the CONJOINED gate would silently REJECT this
40934 // per-column-alias corner as a false-positive collision.
40935 // Symmetric pin on the RIGHT-column-alias-only sibling corner
40936 // — the two together bind the CONJOINED-gate semantics across
40937 // BOTH column-alias arms. Peer to
40938 // `assert_char_pair_array_within_char_pair_finite_set_panics_at_
40939 // runtime_on_cross_row_alias_pair` on the SUBSET-EMBEDDING
40940 // sibling's dual corner — where the SUBSET helper REJECTS the
40941 // cross-row alias pair as NOT-in-set, this DISJOINTNESS helper
40942 // ACCEPTS the analogous per-column alias as disjoint (the two
40943 // contract shapes flip the accept/reject arm across the
40944 // CONJOINED-gate corner).
40945 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('a', 'y')]);
40946 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('b', 'x')]);
40947 }
40948
40949 #[test]
40950 #[should_panic(expected = "CHAR-PAIR-DISJOINTNESS-VIOLATION")]
40951 fn assert_char_pair_arrays_disjoint_panics_at_runtime_on_collision() {
40952 // NEGATIVE PIN — CHAR-PAIR-DISJOINTNESS-VIOLATION corner: two
40953 // paired arrays sharing a CONJOINED pair MUST panic at runtime
40954 // with the CHAR-PAIR-DISJOINTNESS-VIOLATION-named message.
40955 // Pins the helper's OWN reject arm — a regression that
40956 // silently returned without panicking on a shared pair would
40957 // slip through the compile-time witness's failure mode too.
40958 // The offending pair `('b', 'y')` is intentionally chosen to
40959 // appear in BOTH arrays at their MIDDLE positions to pin that
40960 // the reject arm fires from an interior-position collision (not
40961 // only a first-position or last-position degenerate corner).
40962 assert_char_pair_arrays_disjoint(
40963 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40964 &[('p', 'q'), ('b', 'y'), ('r', 's')],
40965 );
40966 }
40967
40968 #[test]
40969 #[should_panic(expected = "CHAR-PAIR-DISJOINTNESS-VIOLATION")]
40970 fn assert_char_pair_arrays_disjoint_panics_at_runtime_on_terminal_position_collision() {
40971 // NEGATIVE PIN — terminal-position drift: a shared CONJOINED
40972 // pair at the LAST position of BOTH arrays MUST panic — pins
40973 // that BOTH the outer `while i < N` and inner `while j < M`
40974 // loops reach their terminal indices (else the terminal drift
40975 // would slip through). A regression that narrowed EITHER
40976 // bound to `< N - 1` / `< M - 1` (off-by-one on the OUTER or
40977 // INNER bound) would silently accept this array pair. Peer
40978 // sibling posture to
40979 // `assert_char_pair_array_within_char_pair_finite_set_panics_
40980 // at_runtime_on_terminal_out_of_set_pair` on the SUBSET-
40981 // EMBEDDING helper — both bind the terminal-position sweep
40982 // arm on the paired-array row.
40983 assert_char_pair_arrays_disjoint(&[('a', 'x'), ('z', 'w')], &[('p', 'q'), ('z', 'w')]);
40984 }
40985
40986 #[test]
40987 fn assert_char_pair_arrays_disjoint_is_symmetric_in_argument_order() {
40988 // SYMMETRY PIN — the disjointness relation is symmetric in
40989 // `(a, b)` so swapping arguments at the call site MUST produce
40990 // the SAME verdict (accept remains accept, reject remains
40991 // reject). Pins the two-loop sweep visits every `(i, j) ∈
40992 // [0, N) × [0, M)` pair without gratuitous argument-order
40993 // preference. Sibling posture to
40994 // `assert_char_arrays_disjoint_is_symmetric_in_argument_order`
40995 // on the (char) SCALAR row-dual peer — the two share the
40996 // symmetric-relation arm across the (scalar, paired) element-
40997 // type partition on the (disjointness) column.
40998 assert_char_pair_arrays_disjoint(&[('a', 'x'), ('b', 'y')], &[('c', 'z'), ('d', 'w')]);
40999 assert_char_pair_arrays_disjoint(&[('c', 'z'), ('d', 'w')], &[('a', 'x'), ('b', 'y')]);
41000 let swap_left_rejects = std::panic::catch_unwind(|| {
41001 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('a', 'x')]);
41002 })
41003 .is_err();
41004 let swap_right_rejects = std::panic::catch_unwind(|| {
41005 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('a', 'x')]);
41006 })
41007 .is_err();
41008 assert!(
41009 swap_left_rejects && swap_right_rejects,
41010 "assert_char_pair_arrays_disjoint must reject a shared \
41011 CONJOINED pair regardless of which argument carries the \
41012 `a` role vs. the `b` role — the disjointness relation \
41013 is symmetric in argument order",
41014 );
41015 }
41016
41017 #[test]
41018 fn assert_char_pair_arrays_disjoint_panic_message_names_the_helper_and_char_pair_disjointness_violation_axis(
41019 ) {
41020 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-DISJOINTNESS-
41021 // VIOLATION arm: the panic message MUST begin with the
41022 // helper's own name AND identify the failed AXIS as "CHAR-
41023 // PAIR-DISJOINTNESS-VIOLATION" so downstream diagnostics
41024 // route the drift back to (a) the helper by string search on
41025 // `"assert_char_pair_arrays_disjoint"` and (b) the axis by
41026 // string search on `"CHAR-PAIR-DISJOINTNESS-VIOLATION"`.
41027 // Sibling posture to
41028 // `assert_char_arrays_disjoint_panic_message_names_the_helper_
41029 // and_char_disjointness_violation_axis` +
41030 // `assert_str_arrays_disjoint_panic_message_names_the_helper_
41031 // and_str_disjointness_violation_axis` +
41032 // `assert_u8_arrays_disjoint_panic_message_names_the_helper_
41033 // and_u8_disjointness_violation_axis` on the SCALAR row-dual
41034 // provenance pins. The axis-provenance string `"CHAR-PAIR-
41035 // DISJOINTNESS-VIOLATION"` is chosen DISTINCT from EVERY
41036 // sibling helper's axis vocabulary (`"CHAR-DISJOINTNESS-
41037 // VIOLATION"` on the (char) SCALAR row-dual DISJOINTNESS
41038 // peer; `"U8-DISJOINTNESS-VIOLATION"` on the (u8) SCALAR row-
41039 // dual peer; `"STR-DISJOINTNESS-VIOLATION"` on the
41040 // (`&'static str`) SCALAR row-dual peer; `"CHAR-PAIR-SUBSET-
41041 // VIOLATION"` on the paired-array SUBSET-embedding sibling;
41042 // `"LEFT column"` / `"RIGHT column"` on the paired-array
41043 // BIJECTIVITY sibling) so a diagnostic that names the failed
41044 // axis routes UNAMBIGUOUSLY to (a) this specific paired-array
41045 // DISJOINTNESS helper, (b) the failed axis-shape by the
41046 // `"DISJOINTNESS-VIOLATION"` suffix stem shared with the
41047 // three SCALAR row-dual siblings.
41048 let outcome = std::panic::catch_unwind(|| {
41049 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('a', 'x')]);
41050 });
41051 let payload = outcome.expect_err(
41052 "assert_char_pair_arrays_disjoint must panic on a shared \
41053 CONJOINED pair — the reject-on-collision arm is the sole \
41054 CHAR-PAIR-DISJOINTNESS-VIOLATION failure mode of the \
41055 helper",
41056 );
41057 let msg = payload
41058 .downcast_ref::<&'static str>()
41059 .map(|s| (*s).to_owned())
41060 .or_else(|| payload.downcast_ref::<String>().cloned())
41061 .expect(
41062 "assert_char_pair_arrays_disjoint panic payload must \
41063 be a static &str or String",
41064 );
41065 assert!(
41066 msg.contains("assert_char_pair_arrays_disjoint"),
41067 "assert_char_pair_arrays_disjoint panic message {msg:?} \
41068 must name the helper for provenance-preserving failure \
41069 diagnostics",
41070 );
41071 assert!(
41072 msg.contains("CHAR-PAIR-DISJOINTNESS-VIOLATION"),
41073 "assert_char_pair_arrays_disjoint panic message {msg:?} \
41074 must name the failed AXIS (\"CHAR-PAIR-DISJOINTNESS-\
41075 VIOLATION\") for axis-provenance-preserving failure \
41076 diagnostics",
41077 );
41078 }
41079
41080 // ── `assert_u8_array_covers_inclusive_range` — the SURJECTIVITY-
41081 // onto-a-range peer of `assert_u8_array_pairwise_distinct`'s
41082 // INJECTIVITY axis on the SAME `u8` cache-key element type. Where
41083 // the four sibling distinctness/bijectivity helpers close the
41084 // pairwise-DISTINCTNESS axis of the substrate's typed-array
41085 // vocabulary, this range-coverage helper opens the SURJECTIVITY-
41086 // onto-a-range axis at four family-wide `[u8; N]` arrays whose
41087 // distinct-value sets are intentionally-closed inclusive ranges
41088 // (`AtomKind::HASH_DISCRIMINATORS` covers `{0..=5}`, `QuoteForm::
41089 // HASH_DISCRIMINATORS` covers `{3..=6}`, `UnquoteForm::HASH_
41090 // DISCRIMINATORS` covers `{5..=6}`, `SexpShape::HASH_DISCRIMINATORS`
41091 // covers `{0..=6}` with a load-bearing six-fold collapse at `1u8`).
41092 // The runtime test surface matches the sibling-helpers' shape
41093 // (accept-singleton, accept-with-duplicates, accept-every-family-
41094 // wide-substrate-array, reject-above-HI, reject-below-LO, reject-
41095 // missing-range-byte, panic-message-provenance on the RANGE-BOUND
41096 // axis, panic-message-provenance on the FULL-COVERAGE axis) split
41097 // across BOTH axes so a regression that silently weakens the
41098 // helper on EITHER axis (e.g. dropping the `arr[i] < LO || arr[i]
41099 // > HI` guard, or dropping the `while cur <= HI` full-coverage
41100 // sweep) is caught by the helper's OWN test surface rather than
41101 // only surfacing as a false-positive on some future range-
41102 // covering `[u8; N]` array's coverage pin.
41103
41104 #[test]
41105 fn assert_u8_array_covers_inclusive_range_accepts_the_singleton_range() {
41106 // Singleton range `{K..=K}` at the `[u8; 1]` corner —
41107 // vacuously covering (the one entry MUST equal the one range
41108 // byte). Cross-arity coverage on the trivial-range corner of
41109 // the const-N generic; simultaneously pins BOTH axes (RANGE-
41110 // BOUND: `K in [K, K]`; FULL-COVERAGE: `K` appears in the
41111 // singleton array) at the smallest witness.
41112 assert_u8_array_covers_inclusive_range::<1, 7, 7>(&[7u8]);
41113 assert_u8_array_covers_inclusive_range::<1, 0, 0>(&[0u8]);
41114 assert_u8_array_covers_inclusive_range::<1, 255, 255>(&[255u8]);
41115 }
41116
41117 #[test]
41118 fn assert_u8_array_covers_inclusive_range_accepts_arrays_with_duplicates() {
41119 // KEY LOAD-BEARING PIN: duplicates in the entries are ACCEPTED
41120 // — the range-coverage contract is non-injective on the
41121 // ENTRIES axis, only requires each RANGE byte to appear. This
41122 // is the exact property that distinguishes this helper from
41123 // its `assert_u8_array_pairwise_distinct` sibling and lets
41124 // `SexpShape::HASH_DISCRIMINATORS`'s six-fold collapse at
41125 // `1u8` bind a compile-time coverage witness despite failing
41126 // distinctness. A regression that added a pairwise-distinct
41127 // guard on the entries axis (accidentally merging the two
41128 // sibling helpers' contracts) would fail-loudly here.
41129 assert_u8_array_covers_inclusive_range::<4, 0, 2>(&[0u8, 1u8, 1u8, 2u8]);
41130 assert_u8_array_covers_inclusive_range::<7, 0, 2>(&[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 2u8]);
41131 }
41132
41133 #[test]
41134 fn assert_u8_array_covers_inclusive_range_accepts_every_family_wide_substrate_array() {
41135 // Runtime cross-check that the SAME four range-covering arrays
41136 // the module-level `const _: () = ...` witnesses cover at
41137 // COMPILE time are range-covering. A regression that removes
41138 // ONE of the `const _` witnesses would still leave THIS
41139 // runtime pin as a safety net; the const witness fires FIRST
41140 // at `cargo check`, this runtime pin catches the drift at
41141 // `cargo test`. The pair enforces the theorem at TWO stages of
41142 // the toolchain. Sibling posture to
41143 // `assert_char_pair_array_bijective_accepts_every_family_wide_
41144 // substrate_array` at the paired-array vocabulary.
41145 assert_u8_array_covers_inclusive_range::<12, 0, 6>(
41146 &crate::error::SexpShape::HASH_DISCRIMINATORS,
41147 );
41148 assert_u8_array_covers_inclusive_range::<6, 0, 5>(&AtomKind::HASH_DISCRIMINATORS);
41149 assert_u8_array_covers_inclusive_range::<4, 3, 6>(&QuoteForm::HASH_DISCRIMINATORS);
41150 assert_u8_array_covers_inclusive_range::<2, 5, 6>(
41151 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
41152 );
41153 }
41154
41155 #[test]
41156 #[should_panic(expected = "OUT-OF-RANGE")]
41157 fn assert_u8_array_covers_inclusive_range_panics_at_runtime_on_entry_above_hi() {
41158 // NEGATIVE PIN — RANGE-BOUND above-HI corner: an entry
41159 // `HI + 1` MUST panic at runtime with the RANGE-BOUND-named
41160 // message. Pins the helper's OWN reject-above-HI arm — a
41161 // regression that silently returned without panicking on an
41162 // out-of-range entry above the upper bound would slip through
41163 // the compile-time witnesses' failure mode too.
41164 assert_u8_array_covers_inclusive_range::<3, 0, 2>(&[0u8, 1u8, 3u8]);
41165 }
41166
41167 #[test]
41168 #[should_panic(expected = "OUT-OF-RANGE")]
41169 fn assert_u8_array_covers_inclusive_range_panics_at_runtime_on_entry_below_lo() {
41170 // NEGATIVE PIN — RANGE-BOUND below-LO corner: an entry
41171 // `LO - 1` MUST panic at runtime with the RANGE-BOUND-named
41172 // message. Symmetric sibling to the above-HI pin — a
41173 // regression that dropped the `arr[i] < LO` half of the
41174 // OR-disjunction guard (leaving only the `arr[i] > HI` half)
41175 // would silently accept below-LO entries.
41176 assert_u8_array_covers_inclusive_range::<3, 1, 3>(&[0u8, 1u8, 2u8]);
41177 }
41178
41179 #[test]
41180 #[should_panic(expected = "MISSING")]
41181 fn assert_u8_array_covers_inclusive_range_panics_at_runtime_on_missing_range_byte() {
41182 // NEGATIVE PIN — FULL-COVERAGE corner: a range byte NOT
41183 // reached by any entry MUST panic at runtime with the FULL-
41184 // COVERAGE-named message. Pins the helper's OWN reject-
41185 // incomplete-coverage arm — a regression that silently
41186 // returned without panicking on an incomplete-coverage array
41187 // (e.g. dropping the `while cur <= HI` sweep entirely, or
41188 // narrowing it to `while cur < HI`) would slip through the
41189 // compile-time witnesses' failure mode too. The three entries
41190 // stay within `[0, 3]` (satisfying RANGE-BOUND) but skip the
41191 // middle byte `2u8` — the panic MUST fire specifically on
41192 // the FULL-COVERAGE axis, not on the RANGE-BOUND axis.
41193 assert_u8_array_covers_inclusive_range::<3, 0, 3>(&[0u8, 1u8, 3u8]);
41194 }
41195
41196 #[test]
41197 fn assert_u8_array_covers_inclusive_range_panic_message_names_the_helper_and_range_bound_axis()
41198 {
41199 // PANIC-MESSAGE PROVENANCE PIN — RANGE-BOUND arm: the panic
41200 // message MUST begin with the helper's own name AND identify
41201 // the failed AXIS as "OUT-OF-RANGE" so downstream diagnostics
41202 // route the drift back to (a) the helper by string search on
41203 // `"assert_u8_array_covers_inclusive_range"` and (b) the axis
41204 // by string search on `"OUT-OF-RANGE"`. Sibling posture to
41205 // `assert_char_pair_array_bijective_panic_message_names_the_
41206 // helper_and_left_column` on the paired-array bijectivity
41207 // helper's LEFT-column arm — both bind the (helper, failed-
41208 // axis) provenance pair at ONE test per axis.
41209 let outcome = std::panic::catch_unwind(|| {
41210 assert_u8_array_covers_inclusive_range::<3, 0, 2>(&[0u8, 1u8, 3u8]);
41211 });
41212 let payload = outcome.expect_err(
41213 "assert_u8_array_covers_inclusive_range must panic on an \
41214 out-of-range entry — the reject-out-of-range arm is one \
41215 of the two failure modes of the helper",
41216 );
41217 let msg = payload
41218 .downcast_ref::<&'static str>()
41219 .map(|s| (*s).to_owned())
41220 .or_else(|| payload.downcast_ref::<String>().cloned())
41221 .expect(
41222 "assert_u8_array_covers_inclusive_range panic payload \
41223 must be a static &str or String",
41224 );
41225 assert!(
41226 msg.contains("assert_u8_array_covers_inclusive_range"),
41227 "assert_u8_array_covers_inclusive_range RANGE-BOUND panic \
41228 message {msg:?} must name the helper for provenance-\
41229 preserving failure diagnostics",
41230 );
41231 assert!(
41232 msg.contains("OUT-OF-RANGE"),
41233 "assert_u8_array_covers_inclusive_range RANGE-BOUND panic \
41234 message {msg:?} must name the failed AXIS (\"OUT-OF-\
41235 RANGE\") for axis-provenance-preserving failure \
41236 diagnostics",
41237 );
41238 }
41239
41240 #[test]
41241 fn assert_u8_array_covers_inclusive_range_panic_message_names_the_helper_and_full_coverage_axis(
41242 ) {
41243 // PANIC-MESSAGE PROVENANCE PIN — FULL-COVERAGE arm: the panic
41244 // message MUST begin with the helper's own name AND identify
41245 // the failed AXIS as "MISSING" so downstream diagnostics route
41246 // the drift back to (a) the helper by string search and (b)
41247 // the axis by string search on `"MISSING"`. Axis-symmetric
41248 // sibling of the RANGE-BOUND panic-message provenance pin
41249 // above — a regression that silently unified the two panic
41250 // sites into ONE axis-anonymous message would collapse EITHER
41251 // this pin's `"MISSING"` substring assertion OR the sibling
41252 // pin's `"OUT-OF-RANGE"` substring assertion.
41253 let outcome = std::panic::catch_unwind(|| {
41254 assert_u8_array_covers_inclusive_range::<3, 0, 3>(&[0u8, 1u8, 3u8]);
41255 });
41256 let payload = outcome.expect_err(
41257 "assert_u8_array_covers_inclusive_range must panic on a \
41258 missing range byte — the reject-incomplete-coverage arm \
41259 is one of the two failure modes of the helper",
41260 );
41261 let msg = payload
41262 .downcast_ref::<&'static str>()
41263 .map(|s| (*s).to_owned())
41264 .or_else(|| payload.downcast_ref::<String>().cloned())
41265 .expect(
41266 "assert_u8_array_covers_inclusive_range panic payload \
41267 must be a static &str or String",
41268 );
41269 assert!(
41270 msg.contains("assert_u8_array_covers_inclusive_range"),
41271 "assert_u8_array_covers_inclusive_range FULL-COVERAGE panic \
41272 message {msg:?} must name the helper for provenance-\
41273 preserving failure diagnostics",
41274 );
41275 assert!(
41276 msg.contains("MISSING"),
41277 "assert_u8_array_covers_inclusive_range FULL-COVERAGE panic \
41278 message {msg:?} must name the failed AXIS (\"MISSING\") \
41279 for axis-provenance-preserving failure diagnostics",
41280 );
41281 }
41282
41283 // ── `assert_u8_array_covers_finite_set` — the non-contiguous-
41284 // finite-set peer of `assert_u8_array_covers_inclusive_range` on
41285 // the (contiguity) axis of the substrate's SURJECTIVITY-axis
41286 // coverage helpers. Where the sibling range-coverage helper closes
41287 // the contiguous corner of the target-partition axis at four
41288 // range-covering `[u8; N]` HASH_DISCRIMINATORS arrays, this helper
41289 // closes the non-contiguous corner at
41290 // `StructuralKind::HASH_DISCRIMINATORS` (`[u8; 2]` covering
41291 // `{0, 2}` with a load-bearing gap at `1u8` where the atomic-
41292 // carve outer marker lives). The runtime test surface matches
41293 // the sibling-helpers' shape (accept-singleton, accept-with-
41294 // duplicates, accept-every-family-wide-substrate-array, reject-
41295 // out-of-set, reject-set-byte-missing, panic-message-provenance
41296 // on the OUT-OF-SET axis, panic-message-provenance on the SET-
41297 // BYTE-MISSING axis) split across BOTH axes so a regression that
41298 // silently weakens the helper on EITHER axis (e.g. dropping the
41299 // `found` guard on the set-membership sweep, or dropping the
41300 // outer `while j < M` set-coverage sweep entirely) is caught by
41301 // the helper's OWN test surface rather than only surfacing as a
41302 // false-positive on some future finite-set-covering `[u8; N]`
41303 // array's coverage pin.
41304
41305 #[test]
41306 fn assert_u8_array_covers_finite_set_accepts_the_singleton_set() {
41307 // Singleton set `{K}` at the `[u8; 1]` × `[u8; 1]` corner —
41308 // vacuously covering (the one entry MUST equal the one set
41309 // byte). Cross-arity coverage on the trivial-set corner of
41310 // the const-M generic; simultaneously pins BOTH axes (OUT-OF-
41311 // SET: `K in {K}`; SET-BYTE-MISSING: `K` appears in the
41312 // singleton array) at the smallest witness.
41313 assert_u8_array_covers_finite_set::<1, 1>(&[7u8], &[7u8]);
41314 assert_u8_array_covers_finite_set::<1, 1>(&[0u8], &[0u8]);
41315 assert_u8_array_covers_finite_set::<1, 1>(&[255u8], &[255u8]);
41316 }
41317
41318 #[test]
41319 fn assert_u8_array_covers_finite_set_accepts_arrays_with_duplicates() {
41320 // KEY LOAD-BEARING PIN: duplicates in the entries are ACCEPTED
41321 // — the finite-set-coverage contract is non-injective on the
41322 // ENTRIES axis, only requires each SET byte to appear. This
41323 // is the exact property that distinguishes this helper from
41324 // its `assert_u8_array_pairwise_distinct` sibling and lets
41325 // any future array with a load-bearing collapse bind a
41326 // compile-time coverage witness despite failing distinctness.
41327 // A regression that added a pairwise-distinct guard on the
41328 // entries axis (accidentally merging the two sibling
41329 // helpers' contracts) would fail-loudly here.
41330 assert_u8_array_covers_finite_set::<4, 3>(&[0u8, 1u8, 1u8, 2u8], &[0u8, 1u8, 2u8]);
41331 assert_u8_array_covers_finite_set::<7, 3>(
41332 &[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 2u8],
41333 &[0u8, 1u8, 2u8],
41334 );
41335 }
41336
41337 #[test]
41338 fn assert_u8_array_covers_finite_set_accepts_the_non_contiguous_partition() {
41339 // KEY LOAD-BEARING PIN: a non-contiguous target set `{0, 2}`
41340 // (with a gap at `1u8`) is ACCEPTED — a two-element array
41341 // hitting exactly those two bytes binds the finite-set-
41342 // coverage contract even though NO contiguous `[LO..=HI]`
41343 // range describes the target partition. This is the exact
41344 // property that distinguishes this helper from its
41345 // `assert_u8_array_covers_inclusive_range` sibling on the
41346 // (contiguity) axis and lets
41347 // `StructuralKind::HASH_DISCRIMINATORS`'s `{0, 2}` gap-
41348 // partition bind a compile-time coverage witness where the
41349 // range sibling cannot. A regression that narrowed the
41350 // helper's target-set semantics to contiguous ranges only
41351 // (e.g. re-derived the `while cur <= HI` sweep instead of
41352 // the per-set-byte membership sweep) would fail-loudly here.
41353 assert_u8_array_covers_finite_set::<2, 2>(&[0u8, 2u8], &[0u8, 2u8]);
41354 assert_u8_array_covers_finite_set::<3, 3>(&[0u8, 2u8, 5u8], &[0u8, 2u8, 5u8]);
41355 }
41356
41357 #[test]
41358 fn assert_u8_array_covers_finite_set_accepts_every_family_wide_substrate_array() {
41359 // Runtime cross-check that the SAME array the module-level
41360 // `const _: () = ...` witness covers at COMPILE time is
41361 // finite-set-covering. A regression that removes the
41362 // `const _` witness would still leave THIS runtime pin as a
41363 // safety net; the const witness fires FIRST at `cargo check`,
41364 // this runtime pin catches the drift at `cargo test`. The
41365 // pair enforces the theorem at TWO stages of the toolchain.
41366 // Sibling posture to
41367 // `assert_u8_array_covers_inclusive_range_accepts_every_
41368 // family_wide_substrate_array` at the contiguous-range peer.
41369 assert_u8_array_covers_finite_set::<2, 2>(
41370 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
41371 &[0u8, 2u8],
41372 );
41373 }
41374
41375 #[test]
41376 #[should_panic(expected = "OUT-OF-SET")]
41377 fn assert_u8_array_covers_finite_set_panics_at_runtime_on_out_of_set_entry() {
41378 // NEGATIVE PIN — SET-MEMBERSHIP corner: an entry NOT in the
41379 // target set MUST panic at runtime with the OUT-OF-SET-named
41380 // message. Pins the helper's OWN reject-out-of-set arm — a
41381 // regression that silently returned without panicking on an
41382 // entry outside the target set would slip through the
41383 // compile-time witnesses' failure mode too. The two set
41384 // bytes `{0, 2}` witness that the FULL-COVERAGE axis is
41385 // INTACT — the panic MUST fire specifically on the SET-
41386 // MEMBERSHIP disjointness failure at the drift entry `3u8`.
41387 assert_u8_array_covers_finite_set::<3, 2>(&[0u8, 2u8, 3u8], &[0u8, 2u8]);
41388 }
41389
41390 #[test]
41391 #[should_panic(expected = "OUT-OF-SET")]
41392 fn assert_u8_array_covers_finite_set_panics_on_gap_byte_drift() {
41393 // NEGATIVE PIN — GAP-BYTE corner: an entry that drifts INTO
41394 // the intentional gap of a non-contiguous target set MUST
41395 // panic — this is the exact regression the archetype
41396 // `StructuralKind::HASH_DISCRIMINATORS` witness catches. A
41397 // regression that lifted a fresh `1u8` entry into the
41398 // `{0, 2}`-partitioned array would silently collide with
41399 // `AtomKind::OUTER_HASH_DISCRIMINATOR = 1u8` on the outer-
41400 // `Sexp` cache-key partition; this pin binds that failure
41401 // mode as an OUT-OF-SET rejection at the const-eval site.
41402 assert_u8_array_covers_finite_set::<3, 2>(&[0u8, 1u8, 2u8], &[0u8, 2u8]);
41403 }
41404
41405 #[test]
41406 #[should_panic(expected = "SET-BYTE-MISSING")]
41407 fn assert_u8_array_covers_finite_set_panics_at_runtime_on_missing_set_byte() {
41408 // NEGATIVE PIN — FULL-COVERAGE corner: a set byte NOT
41409 // reached by any entry MUST panic at runtime with the SET-
41410 // BYTE-MISSING-named message. Pins the helper's OWN reject-
41411 // incomplete-coverage arm — a regression that silently
41412 // returned without panicking on an incomplete-coverage array
41413 // (e.g. dropping the outer `while j < M` sweep entirely, or
41414 // narrowing it to `while j < M - 1`) would slip through the
41415 // compile-time witnesses' failure mode too. The two entries
41416 // stay within `{0, 2, 5}` (satisfying OUT-OF-SET) but skip
41417 // the middle byte `2u8` — the panic MUST fire specifically
41418 // on the FULL-COVERAGE axis, not on the SET-MEMBERSHIP axis.
41419 assert_u8_array_covers_finite_set::<2, 3>(&[0u8, 5u8], &[0u8, 2u8, 5u8]);
41420 }
41421
41422 #[test]
41423 fn assert_u8_array_covers_finite_set_panic_message_names_the_helper_and_out_of_set_axis() {
41424 // PANIC-MESSAGE PROVENANCE PIN — OUT-OF-SET arm: the panic
41425 // message MUST begin with the helper's own name AND identify
41426 // the failed AXIS as "OUT-OF-SET" so downstream diagnostics
41427 // route the drift back to (a) the helper by string search on
41428 // `"assert_u8_array_covers_finite_set"` and (b) the axis by
41429 // string search on `"OUT-OF-SET"`. Sibling posture to
41430 // `assert_u8_array_covers_inclusive_range_panic_message_
41431 // names_the_helper_and_range_bound_axis` on the contiguous-
41432 // range peer's RANGE-BOUND arm — both bind the (helper,
41433 // failed-axis) provenance pair at ONE test per axis. The
41434 // axis-provenance strings ("OUT-OF-SET" here vs. "OUT-OF-
41435 // RANGE" on the range sibling) are chosen DISTINCT so a
41436 // diagnostic that names the failed axis routes UNAMBIGUOUSLY
41437 // to the failed HELPER too — a regression that unified the
41438 // two helpers' axis-provenance strings would collapse either
41439 // this substring assertion or the sibling helper's.
41440 let outcome = std::panic::catch_unwind(|| {
41441 assert_u8_array_covers_finite_set::<3, 2>(&[0u8, 2u8, 3u8], &[0u8, 2u8]);
41442 });
41443 let payload = outcome.expect_err(
41444 "assert_u8_array_covers_finite_set must panic on an out-\
41445 of-set entry — the reject-out-of-set arm is one of the \
41446 two failure modes of the helper",
41447 );
41448 let msg = payload
41449 .downcast_ref::<&'static str>()
41450 .map(|s| (*s).to_owned())
41451 .or_else(|| payload.downcast_ref::<String>().cloned())
41452 .expect(
41453 "assert_u8_array_covers_finite_set panic payload must \
41454 be a static &str or String",
41455 );
41456 assert!(
41457 msg.contains("assert_u8_array_covers_finite_set"),
41458 "assert_u8_array_covers_finite_set OUT-OF-SET panic \
41459 message {msg:?} must name the helper for provenance-\
41460 preserving failure diagnostics",
41461 );
41462 assert!(
41463 msg.contains("OUT-OF-SET"),
41464 "assert_u8_array_covers_finite_set OUT-OF-SET panic \
41465 message {msg:?} must name the failed AXIS (\"OUT-OF-\
41466 SET\") for axis-provenance-preserving failure \
41467 diagnostics",
41468 );
41469 }
41470
41471 #[test]
41472 fn assert_u8_array_covers_finite_set_panic_message_names_the_helper_and_missing_set_byte_axis()
41473 {
41474 // PANIC-MESSAGE PROVENANCE PIN — SET-BYTE-MISSING arm: the
41475 // panic message MUST begin with the helper's own name AND
41476 // identify the failed AXIS as "SET-BYTE-MISSING" so
41477 // downstream diagnostics route the drift back to (a) the
41478 // helper by string search and (b) the axis by string search
41479 // on `"SET-BYTE-MISSING"`. Axis-symmetric sibling of the
41480 // OUT-OF-SET panic-message provenance pin above — a
41481 // regression that silently unified the two panic sites into
41482 // ONE axis-anonymous message would collapse EITHER this
41483 // pin's `"SET-BYTE-MISSING"` substring assertion OR the
41484 // sibling pin's `"OUT-OF-SET"` substring assertion. The
41485 // axis-provenance string ("SET-BYTE-MISSING" here vs.
41486 // "MISSING" on the range sibling) is chosen DISTINCT so a
41487 // diagnostic that names the failed axis routes UNAMBIGUOUSLY
41488 // to the failed HELPER too.
41489 let outcome = std::panic::catch_unwind(|| {
41490 assert_u8_array_covers_finite_set::<2, 3>(&[0u8, 5u8], &[0u8, 2u8, 5u8]);
41491 });
41492 let payload = outcome.expect_err(
41493 "assert_u8_array_covers_finite_set must panic on a \
41494 missing set byte — the reject-incomplete-coverage arm \
41495 is one of the two failure modes of the helper",
41496 );
41497 let msg = payload
41498 .downcast_ref::<&'static str>()
41499 .map(|s| (*s).to_owned())
41500 .or_else(|| payload.downcast_ref::<String>().cloned())
41501 .expect(
41502 "assert_u8_array_covers_finite_set panic payload must \
41503 be a static &str or String",
41504 );
41505 assert!(
41506 msg.contains("assert_u8_array_covers_finite_set"),
41507 "assert_u8_array_covers_finite_set SET-BYTE-MISSING panic \
41508 message {msg:?} must name the helper for provenance-\
41509 preserving failure diagnostics",
41510 );
41511 assert!(
41512 msg.contains("SET-BYTE-MISSING"),
41513 "assert_u8_array_covers_finite_set SET-BYTE-MISSING panic \
41514 message {msg:?} must name the failed AXIS (\"SET-BYTE-\
41515 MISSING\") for axis-provenance-preserving failure \
41516 diagnostics",
41517 );
41518 }
41519
41520 // ── `assert_u8_finite_set_pairwise_distinct` — the caller-side
41521 // SET-WELL-FORMEDNESS verifier that closes the pre-lift docstring-
41522 // level "assuming `set` is itself pairwise-distinct" caveat on
41523 // both `assert_u8_array_covers_finite_set` and
41524 // `assert_u8_array_permutes_finite_set` into a compile-time
41525 // theorem the substrate carries per call site. The runtime test
41526 // surface pins each of the helper's arms (accept-empty, accept-
41527 // singleton, accept-every-family-wide-substrate-target-set,
41528 // reject-binary-collision, reject-non-adjacent-collision, reject-
41529 // terminal-collision, panic-message-provenance on the SET-NOT-
41530 // PAIRWISE-DISTINCT axis, negative pins on BOTH downstream
41531 // covers/permutes helpers exercising the SET-side delegation
41532 // chain) so a regression that silently weakened the helper on
41533 // ANY arm is caught by the helper's OWN test surface rather than
41534 // only surfacing as a false-positive on some future finite-set-
41535 // covering `[u8; N]` array's compound pin.
41536
41537 #[test]
41538 fn assert_u8_finite_set_pairwise_distinct_accepts_the_empty_set() {
41539 // Empty set `{}` at the `[u8; 0]` corner — vacuously pairwise-
41540 // distinct (no `(i, j)` pair with `i < j` exists to test).
41541 // Cross-arity coverage on the trivial corner of the const-M
41542 // generic; a regression that fired on an empty set (e.g. by
41543 // computing `M - 1` and wrapping to `usize::MAX`) would
41544 // fail-loudly here. Turbofish binding required because there's
41545 // no other cue for the `M` const parameter on the empty array
41546 // literal.
41547 assert_u8_finite_set_pairwise_distinct::<0>(&[]);
41548 }
41549
41550 #[test]
41551 fn assert_u8_finite_set_pairwise_distinct_accepts_singleton_sets() {
41552 // Singleton set `{K}` at the `[u8; 1]` corner — vacuously
41553 // pairwise-distinct (no `(i, j)` pair with `i < j` exists).
41554 // Cross-value coverage on the singleton corner at THREE byte
41555 // widths (`0u8`, `7u8`, `255u8`) to pin the helper's SINGLETON
41556 // arm across the whole `u8` domain.
41557 assert_u8_finite_set_pairwise_distinct(&[0u8]);
41558 assert_u8_finite_set_pairwise_distinct(&[7u8]);
41559 assert_u8_finite_set_pairwise_distinct(&[255u8]);
41560 }
41561
41562 #[test]
41563 fn assert_u8_finite_set_pairwise_distinct_accepts_every_family_wide_target_set() {
41564 // Runtime cross-check that the ONE target-SET spec the
41565 // substrate's `const _` witness (at
41566 // `assert_u8_array_permutes_finite_set::<2, 2>(
41567 // &StructuralKind::HASH_DISCRIMINATORS, &[0u8, 2u8])`)
41568 // passes through this helper's SET-side delegation at
41569 // compile time is ALSO well-formed at runtime. The pair
41570 // enforces the theorem at TWO stages of the toolchain: the
41571 // const witness fires FIRST at `cargo check` (through the
41572 // delegated call chain), this runtime pin catches the drift
41573 // at `cargo test` as a safety net. Sibling posture to
41574 // `assert_u8_array_covers_finite_set_accepts_every_family_
41575 // wide_substrate_array` — the two pins together verify the
41576 // (ARRAY, SET) pair at BOTH sides of the finite-set-coverage
41577 // compound contract.
41578 assert_u8_finite_set_pairwise_distinct(&[0u8, 2u8]);
41579 }
41580
41581 #[test]
41582 fn assert_u8_finite_set_pairwise_distinct_accepts_the_structural_kind_hash_discriminators() {
41583 // Runtime cross-check that
41584 // `StructuralKind::HASH_DISCRIMINATORS` itself is pairwise-
41585 // distinct WHEN VIEWED AS A TARGET-SET SPEC (i.e. this
41586 // helper accepts arrays that would themselves be valid
41587 // target-set specs). While the SUBSTRATE'S primary call
41588 // site passes the caller-provided `&[0u8, 2u8]` literal as
41589 // the target-set spec (with `StructuralKind::HASH_
41590 // DISCRIMINATORS` as the ARRAY under verification), the two
41591 // arrays HAPPEN to have byte-equal contents by design (the
41592 // ARRAY is a permutation of the target SET); a regression
41593 // that silently unified two `StructuralKind` arms' cache-
41594 // key bytes would fail-loudly here on the ARRAY-as-SET
41595 // interpretation TOO, providing a redundant safety net past
41596 // the primary compound witness at the module level.
41597 assert_u8_finite_set_pairwise_distinct(&crate::error::StructuralKind::HASH_DISCRIMINATORS);
41598 }
41599
41600 #[test]
41601 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41602 fn assert_u8_finite_set_pairwise_distinct_panics_at_runtime_on_binary_collision() {
41603 // NEGATIVE PIN — binary-collision corner: the smallest
41604 // duplicate-carrying set `[K, K]` MUST panic at runtime with
41605 // the SET-NOT-PAIRWISE-DISTINCT-named message. Pins the
41606 // helper's OWN reject arm — a regression that silently
41607 // returned without panicking on an adjacent duplicate would
41608 // slip through the compile-time witnesses' failure mode too.
41609 assert_u8_finite_set_pairwise_distinct(&[42u8, 42u8]);
41610 }
41611
41612 #[test]
41613 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41614 fn assert_u8_finite_set_pairwise_distinct_panics_at_runtime_on_non_adjacent_collision() {
41615 // NEGATIVE PIN — non-adjacent-collision corner: a duplicate
41616 // separated by ONE intervening element MUST panic — pins
41617 // that the `(i, j)` pair-walk sweeps the FULL upper-triangle
41618 // rather than only adjacent pairs. A regression that
41619 // narrowed the inner `while j < M` loop to `j == i + 1`
41620 // would silently accept this set. Positions `(0, 2)` witness
41621 // a non-adjacent collision through the middle `2u8`
41622 // separator.
41623 assert_u8_finite_set_pairwise_distinct(&[1u8, 2u8, 1u8]);
41624 }
41625
41626 #[test]
41627 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41628 fn assert_u8_finite_set_pairwise_distinct_panics_at_runtime_on_terminal_collision() {
41629 // NEGATIVE PIN — terminal-collision corner: a duplicate at
41630 // the LAST two positions MUST panic — pins that the outer
41631 // `while i < M` loop reaches `i = M - 2` (else the terminal
41632 // duplicate at positions `(M - 2, M - 1)` would slip
41633 // through). A regression that narrowed the outer sweep to
41634 // `while i < M - 1` (off-by-one on the OUTER bound) would
41635 // silently accept this set.
41636 assert_u8_finite_set_pairwise_distinct(&[0u8, 1u8, 2u8, 3u8, 3u8]);
41637 }
41638
41639 #[test]
41640 fn assert_u8_finite_set_pairwise_distinct_panic_message_names_the_helper_and_set_axis() {
41641 // PANIC-MESSAGE PROVENANCE PIN — SET-NOT-PAIRWISE-DISTINCT
41642 // arm: the panic message MUST begin with the helper's own
41643 // name AND identify the failed AXIS as "SET-NOT-PAIRWISE-
41644 // DISTINCT" so downstream diagnostics route the drift back
41645 // to (a) the helper by string search on `"assert_u8_finite_
41646 // set_pairwise_distinct"` and (b) the axis by string search
41647 // on `"SET-NOT-PAIRWISE-DISTINCT"`. Sibling posture to
41648 // `assert_u8_array_covers_finite_set_panic_message_names_
41649 // the_helper_and_out_of_set_axis` on the ARRAY-side covers
41650 // helper's OUT-OF-SET arm — both bind the (helper, failed-
41651 // axis) provenance pair at ONE test per axis. The axis-
41652 // provenance string "SET-NOT-PAIRWISE-DISTINCT" is chosen
41653 // DISTINCT from EVERY sibling helper's axis vocabulary
41654 // (`"duplicate"` on the ARRAY-side pairwise-distinct
41655 // sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the
41656 // covers-finite-set sibling; `"OUT-OF-RANGE"` / `"MISSING"`
41657 // on the covers-inclusive-range sibling; `"ARITY-MISMATCH"`
41658 // on both `_permutes_*` compound helpers) so a diagnostic
41659 // that names the failed axis routes UNAMBIGUOUSLY to (a)
41660 // this specific SET-side helper, (b) the CALLER'S TARGET-
41661 // SET SPEC as the drift site rather than the downstream
41662 // `arr` under verification.
41663 let outcome = std::panic::catch_unwind(|| {
41664 assert_u8_finite_set_pairwise_distinct(&[42u8, 42u8]);
41665 });
41666 let payload = outcome.expect_err(
41667 "assert_u8_finite_set_pairwise_distinct must panic on a \
41668 malformed target-set spec — the reject-duplicate arm \
41669 is the sole failure mode of the helper",
41670 );
41671 let msg = payload
41672 .downcast_ref::<&'static str>()
41673 .map(|s| (*s).to_owned())
41674 .or_else(|| payload.downcast_ref::<String>().cloned())
41675 .expect(
41676 "assert_u8_finite_set_pairwise_distinct panic payload \
41677 must be a static &str or String",
41678 );
41679 assert!(
41680 msg.contains("assert_u8_finite_set_pairwise_distinct"),
41681 "assert_u8_finite_set_pairwise_distinct panic message \
41682 {msg:?} must name the helper for provenance-preserving \
41683 failure diagnostics",
41684 );
41685 assert!(
41686 msg.contains("SET-NOT-PAIRWISE-DISTINCT"),
41687 "assert_u8_finite_set_pairwise_distinct panic message \
41688 {msg:?} must name the failed AXIS (\"SET-NOT-PAIRWISE-\
41689 DISTINCT\") for axis-provenance-preserving failure \
41690 diagnostics",
41691 );
41692 }
41693
41694 #[test]
41695 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41696 fn assert_u8_array_covers_finite_set_panics_on_malformed_target_set_spec() {
41697 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
41698 // malformed target-set spec `[0, 2, 2]` fed into the
41699 // ARRAY-side covers-finite-set helper MUST panic on the
41700 // DELEGATED SET-NOT-PAIRWISE-DISTINCT arm BEFORE the OUT-
41701 // OF-SET / SET-BYTE-MISSING arms fire. Pins the
41702 // delegation chain: a regression that dropped the
41703 // `assert_u8_finite_set_pairwise_distinct(set)` call at
41704 // the top of `assert_u8_array_covers_finite_set` would
41705 // silently accept a malformed set and produce a false-
41706 // positive verdict on any `arr` covering the DISTINCT-
41707 // value subset (`arr = [0, 2, 2]` here matches BOTH the
41708 // OUT-OF-SET and SET-BYTE-MISSING sweeps because every
41709 // entry appears in `set` and every set byte appears in
41710 // `arr`, so without the SET-well-formedness delegation
41711 // the intended cardinality-3 contract would silently
41712 // verify against a really-cardinality-2 set). The
41713 // `SET-NOT-PAIRWISE-DISTINCT` axis-provenance string
41714 // routes the operator to the CALLER'S SPEC.
41715 assert_u8_array_covers_finite_set::<3, 3>(&[0u8, 2u8, 2u8], &[0u8, 2u8, 2u8]);
41716 }
41717
41718 #[test]
41719 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41720 fn assert_u8_array_permutes_finite_set_panics_on_malformed_target_set_spec() {
41721 // NEGATIVE PIN — DELEGATED SET-side well-formedness on
41722 // the COMPOUND helper: a malformed target-set spec `[0, 2,
41723 // 2]` fed into `assert_u8_array_permutes_finite_set` MUST
41724 // panic on the DELEGATED SET-NOT-PAIRWISE-DISTINCT arm
41725 // BEFORE the ARITY-MISMATCH / pairwise-distinct-on-arr /
41726 // covers-finite-set arms fire. Pins the delegation chain at
41727 // the compound helper's ENTRY point: a regression that
41728 // dropped the `assert_u8_finite_set_pairwise_distinct(set)`
41729 // call at the top of the compound helper would silently
41730 // route a malformed set through the ARITY check (`N == M`
41731 // holds on the substrate's cardinality-3 pair, since BOTH
41732 // the array and the phantom-3 set have cardinality `3` at
41733 // the type level even though the SET really has DISTINCT-
41734 // cardinality `2`) and produce a false-positive permutation
41735 // verdict. The `SET-NOT-PAIRWISE-DISTINCT` panic here MUST
41736 // fire BEFORE the sibling `ARITY-MISMATCH` panic on the
41737 // same input (both arms would fire independently but
41738 // ordering routes the operator to the CALLER'S SPEC first,
41739 // not to a downstream arithmetic symptom).
41740 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 2u8, 5u8], &[0u8, 2u8, 2u8]);
41741 }
41742
41743 // ── `assert_u8_array_within_u8_finite_set` — the SET-MEMBERSHIP-
41744 // ONLY subset-embedding verifier that binds `arr ⊆ set` at compile
41745 // time (WITHOUT the additional `set ⊆ arr's entries` full-coverage
41746 // clause the sibling `_covers_finite_set` binds). The runtime test
41747 // surface pins each of the helper's arms (accept-empty, accept-
41748 // singleton-in-set, accept-arr-equals-set, accept-the-family-wide
41749 // substitution-subset substrate embedding, reject-single-out-of-
41750 // set-entry, reject-terminal-out-of-set-entry, panic-message-
41751 // provenance on the SUBSET-VIOLATION axis, negative pin on the
41752 // DELEGATED SET-side well-formedness arm) so a regression that
41753 // silently weakened the helper on ANY arm is caught by the
41754 // helper's OWN test surface rather than only surfacing as a
41755 // false-positive on some future subset-embedded `[u8; N]` array's
41756 // compound pin.
41757
41758 #[test]
41759 fn assert_u8_array_within_u8_finite_set_accepts_the_empty_array_within_any_set() {
41760 // Empty array `arr = []` at the `[u8; 0]` corner — vacuously
41761 // a subset of every set (no `i` position exists to test). Cross-
41762 // arity coverage on the trivial ARRAY corner of the const-N
41763 // generic across three witness-set widths (empty, singleton,
41764 // multi-element) to pin the helper's OUTER-sweep arm across
41765 // the whole (`N == 0` × `M`) axis. Turbofish binding required
41766 // because there's no other cue for the const parameters on
41767 // the empty array literal.
41768 assert_u8_array_within_u8_finite_set::<0, 0>(&[], &[]);
41769 assert_u8_array_within_u8_finite_set::<0, 1>(&[], &[42u8]);
41770 assert_u8_array_within_u8_finite_set::<0, 4>(&[], &[3u8, 4u8, 5u8, 6u8]);
41771 }
41772
41773 #[test]
41774 fn assert_u8_array_within_u8_finite_set_accepts_singleton_array_when_byte_in_set() {
41775 // Singleton array `arr = [K]` at the `[u8; 1]` corner MUST
41776 // pass when `K ∈ set`. Cross-position coverage: the byte can
41777 // sit at the FIRST, MIDDLE, or LAST position of the `set` —
41778 // pins the INNER `while j < M` sweep terminates at the
41779 // first-match position rather than always at position `0`
41780 // OR always at position `M - 1`. A regression that narrowed
41781 // the inner sweep to `j == 0` would silently reject singleton
41782 // arrays hitting non-first set positions.
41783 assert_u8_array_within_u8_finite_set::<1, 4>(&[3u8], &[3u8, 4u8, 5u8, 6u8]);
41784 assert_u8_array_within_u8_finite_set::<1, 4>(&[5u8], &[3u8, 4u8, 5u8, 6u8]);
41785 assert_u8_array_within_u8_finite_set::<1, 4>(&[6u8], &[3u8, 4u8, 5u8, 6u8]);
41786 }
41787
41788 #[test]
41789 fn assert_u8_array_within_u8_finite_set_accepts_arr_equals_set() {
41790 // Boundary corner where `arr` and `set` cover byte-for-byte
41791 // identical distinct-value sets — the SUBSET relation degenerates
41792 // to EQUALITY. Pins that the helper does NOT gratuitously
41793 // require the SUBSET to be PROPER (strict): equal-multisets
41794 // pass the SUBSET check. Sibling posture to the covers-finite-
41795 // set peer whose SET-BYTE-MISSING arm would ALSO accept this
41796 // input — the two helpers agree on the EQUAL-SETS corner
41797 // while disagreeing on the PROPER-SUBSET corner (only this
41798 // helper accepts proper subsets; the covers helper rejects
41799 // them at the SET-BYTE-MISSING arm).
41800 assert_u8_array_within_u8_finite_set(&[3u8, 4u8, 5u8, 6u8], &[3u8, 4u8, 5u8, 6u8]);
41801 assert_u8_array_within_u8_finite_set(&[0u8, 2u8], &[0u8, 2u8]);
41802 }
41803
41804 #[test]
41805 fn assert_u8_array_within_u8_finite_set_accepts_the_substitution_subset_embedding() {
41806 // Runtime cross-check that the ONE (subset, superset) pair
41807 // the substrate's module-level `const _` witness pins at
41808 // COMPILE time is a PROPER SUBSET embedding at runtime too.
41809 // The pair enforces the theorem at TWO stages of the
41810 // toolchain: the const witness fires FIRST at `cargo check`
41811 // (through the module-level `const _: () = assert_u8_array_
41812 // within_u8_finite_set::<2, 4>(...)` line), this runtime pin
41813 // catches the drift at `cargo test` as a safety net. Sibling
41814 // posture to
41815 // `assert_u8_finite_set_pairwise_distinct_accepts_every_family_wide_target_set`
41816 // (which runtime-checks the SAME `QuoteForm::HASH_DISCRIMINATORS`
41817 // superset viewed as a SET-side well-formedness input) — the
41818 // two pins together verify the (UnquoteForm subset, QuoteForm
41819 // superset) pair at BOTH sides of the subset-embedding
41820 // contract.
41821 assert_u8_array_within_u8_finite_set::<2, 4>(
41822 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
41823 &QuoteForm::HASH_DISCRIMINATORS,
41824 );
41825 }
41826
41827 #[test]
41828 fn assert_u8_array_within_u8_finite_set_accepts_repeated_array_entries_in_set() {
41829 // Peer corner to `_covers_finite_set`: this helper permits
41830 // duplicates in `arr` because SUBSET-membership is a
41831 // DISTINCT-value predicate — `[3, 3, 5]` is a subset of
41832 // `{3, 4, 5, 6}` even though the array is not pairwise-
41833 // distinct. Pins that the helper does NOT gratuitously
41834 // require INJECTIVITY on `arr` (the injectivity axis is a
41835 // DIFFERENT compile-time contract bound by
41836 // `assert_u8_array_pairwise_distinct`; combining both binds
41837 // BOTH axes). Sibling posture to
41838 // `assert_u8_array_covers_inclusive_range` which ALSO
41839 // permits array duplicates on its RANGE-BOUND arm.
41840 assert_u8_array_within_u8_finite_set(&[3u8, 3u8, 5u8], &[3u8, 4u8, 5u8, 6u8]);
41841 }
41842
41843 #[test]
41844 #[should_panic(expected = "SUBSET-VIOLATION")]
41845 fn assert_u8_array_within_u8_finite_set_panics_at_runtime_on_out_of_set_entry() {
41846 // NEGATIVE PIN — SUBSET-VIOLATION corner: an array carrying
41847 // a single entry NOT in the target set MUST panic at runtime
41848 // with the SUBSET-VIOLATION-named message. Pins the helper's
41849 // OWN reject arm — a regression that silently returned
41850 // without panicking on an out-of-set entry would slip through
41851 // the compile-time witness's failure mode too. The offending
41852 // byte `7u8` is intentionally chosen ONE PAST the superset's
41853 // maximum (`QuoteForm::HASH_DISCRIMINATORS`'s upper endpoint
41854 // is `6u8`) to pin the OVERSHOOT drift mode.
41855 assert_u8_array_within_u8_finite_set(&[5u8, 7u8], &[3u8, 4u8, 5u8, 6u8]);
41856 }
41857
41858 #[test]
41859 #[should_panic(expected = "SUBSET-VIOLATION")]
41860 fn assert_u8_array_within_u8_finite_set_panics_at_runtime_on_terminal_out_of_set_entry() {
41861 // NEGATIVE PIN — terminal-position drift: an out-of-set entry
41862 // at the LAST array position MUST panic — pins that the outer
41863 // `while i < N` loop reaches `i = N - 1` (else the terminal
41864 // drift would slip through). A regression that narrowed the
41865 // outer sweep to `while i < N - 1` (off-by-one on the OUTER
41866 // bound) would silently accept this array.
41867 assert_u8_array_within_u8_finite_set(&[3u8, 4u8, 5u8, 6u8, 7u8], &[3u8, 4u8, 5u8, 6u8]);
41868 }
41869
41870 #[test]
41871 #[should_panic(expected = "SUBSET-VIOLATION")]
41872 fn assert_u8_array_within_u8_finite_set_panics_at_runtime_on_undershoot_out_of_set_entry() {
41873 // NEGATIVE PIN — undershoot drift mode complementary to the
41874 // OVERSHOOT drift mode `_panics_at_runtime_on_out_of_set_entry`
41875 // above: an offending byte BELOW the superset's minimum
41876 // (`QuoteForm::HASH_DISCRIMINATORS`'s lower endpoint is `3u8`;
41877 // this array carries a `0u8` entry NOT in the set) MUST panic.
41878 // Pins that the helper's SET-MEMBERSHIP sweep does NOT
41879 // silently accept bytes UNDER the target set's minimum on
41880 // some misapplied range-min assumption — the helper binds a
41881 // FINITE-SET subset relation, not a RANGE subset relation.
41882 assert_u8_array_within_u8_finite_set(&[0u8, 5u8], &[3u8, 4u8, 5u8, 6u8]);
41883 }
41884
41885 #[test]
41886 fn assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis(
41887 ) {
41888 // PANIC-MESSAGE PROVENANCE PIN — SUBSET-VIOLATION arm: the
41889 // panic message MUST begin with the helper's own name AND
41890 // identify the failed AXIS as "SUBSET-VIOLATION" so
41891 // downstream diagnostics route the drift back to (a) the
41892 // helper by string search on
41893 // `"assert_u8_array_within_u8_finite_set"` and (b) the axis
41894 // by string search on `"SUBSET-VIOLATION"`. Sibling posture
41895 // to
41896 // `assert_u8_finite_set_pairwise_distinct_panic_message_names_the_helper_and_set_axis`
41897 // on the SET-side well-formedness sibling and
41898 // `assert_u8_array_covers_finite_set_panic_message_names_the_helper_and_out_of_set_axis`
41899 // on the ARRAY-side covers helper — all three bind the
41900 // (helper, failed-axis) provenance pair at ONE test per
41901 // helper. The axis-provenance string "SUBSET-VIOLATION" is
41902 // chosen DISTINCT from EVERY sibling helper's axis
41903 // vocabulary (`"duplicate"` on the ARRAY-side pairwise-
41904 // distinct sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"`
41905 // on the covers-finite-set sibling; `"OUT-OF-RANGE"` /
41906 // `"MISSING"` on the covers-inclusive-range sibling;
41907 // `"ARITY-MISMATCH"` on both `_permutes_*` compound helpers;
41908 // `"SET-NOT-PAIRWISE-DISTINCT"` on the SET-side well-
41909 // formedness sibling) so a diagnostic that names the failed
41910 // axis routes UNAMBIGUOUSLY to (a) this specific SUBSET-
41911 // embedding helper, (b) the `arr` argument as the drift
41912 // site rather than the `set` argument specifying the target
41913 // superset.
41914 let outcome = std::panic::catch_unwind(|| {
41915 assert_u8_array_within_u8_finite_set(&[5u8, 7u8], &[3u8, 4u8, 5u8, 6u8]);
41916 });
41917 let payload = outcome.expect_err(
41918 "assert_u8_array_within_u8_finite_set must panic on an \
41919 out-of-set entry — the reject-out-of-set arm is the \
41920 sole SUBSET-VIOLATION failure mode of the helper",
41921 );
41922 let msg = payload
41923 .downcast_ref::<&'static str>()
41924 .map(|s| (*s).to_owned())
41925 .or_else(|| payload.downcast_ref::<String>().cloned())
41926 .expect(
41927 "assert_u8_array_within_u8_finite_set panic payload \
41928 must be a static &str or String",
41929 );
41930 assert!(
41931 msg.contains("assert_u8_array_within_u8_finite_set"),
41932 "assert_u8_array_within_u8_finite_set panic message \
41933 {msg:?} must name the helper for provenance-preserving \
41934 failure diagnostics",
41935 );
41936 assert!(
41937 msg.contains("SUBSET-VIOLATION"),
41938 "assert_u8_array_within_u8_finite_set panic message \
41939 {msg:?} must name the failed AXIS (\"SUBSET-VIOLATION\") \
41940 for axis-provenance-preserving failure diagnostics",
41941 );
41942 }
41943
41944 #[test]
41945 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41946 fn assert_u8_array_within_u8_finite_set_panics_on_malformed_target_set_spec() {
41947 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
41948 // malformed target-set spec `[3, 4, 4]` fed into the ARRAY-
41949 // side within helper MUST panic on the DELEGATED SET-NOT-
41950 // PAIRWISE-DISTINCT arm BEFORE the SUBSET-VIOLATION arm
41951 // fires. Pins the delegation chain: a regression that
41952 // dropped the `assert_u8_finite_set_pairwise_distinct(set)`
41953 // call at the top of `assert_u8_array_within_u8_finite_set`
41954 // would silently accept a malformed set and produce a
41955 // false-positive verdict on any `arr` embedded in the
41956 // DISTINCT-value subset. Sibling posture to
41957 // `assert_u8_array_covers_finite_set_panics_on_malformed_target_set_spec`
41958 // and
41959 // `assert_u8_array_permutes_finite_set_panics_on_malformed_target_set_spec`
41960 // — the three DELEGATED tests pin the SET-side well-
41961 // formedness arm at the top of ALL THREE finite-set-family
41962 // ARRAY-side helpers.
41963 assert_u8_array_within_u8_finite_set::<2, 3>(&[3u8, 4u8], &[3u8, 4u8, 4u8]);
41964 }
41965
41966 // ── `assert_u8_array_within_inclusive_range` — the RANGE-MEMBERSHIP-
41967 // ONLY subset-embedding verifier that binds `arr ⊆ [LO..=HI]` at
41968 // compile time (WITHOUT the additional `[LO..=HI] ⊆ arr's entries`
41969 // full-coverage clause the sibling `_covers_inclusive_range` binds).
41970 // Contiguity-axis peer to `_within_u8_finite_set` on the (contiguity)
41971 // axis of the substrate's SUBSET-only verifiers. The runtime test
41972 // surface pins each of the helper's arms (accept-empty, accept-
41973 // singleton-in-range, accept-arr-equals-range-endpoints, accept-the-
41974 // family-wide structural-residual substrate embedding, accept-with-
41975 // duplicates, reject-above-HI-entry, reject-below-LO-entry, reject-
41976 // terminal-out-of-range-entry, panic-message-provenance on the
41977 // RANGE-SUBSET-VIOLATION axis) so a regression that silently weakens
41978 // the helper on ANY arm is caught by the helper's OWN test surface
41979 // rather than only surfacing as a false-positive on some future
41980 // range-subset-embedded `[u8; N]` array's compound pin.
41981
41982 #[test]
41983 fn assert_u8_array_within_inclusive_range_accepts_the_empty_array_within_any_range() {
41984 // Empty array `arr = []` at the `[u8; 0]` corner — vacuously a
41985 // subset of every inclusive range (no `i` position exists to
41986 // test). Cross-range coverage on the trivial ARRAY corner of
41987 // the const-N generic across three witness-range widths
41988 // (singleton, small, whole-`u8`-space) to pin the helper's
41989 // OUTER-sweep arm across the whole (`N == 0` × `[LO..=HI]`)
41990 // axis. Turbofish binding required because there's no other
41991 // cue for the const parameters on the empty array literal.
41992 assert_u8_array_within_inclusive_range::<0, 0, 0>(&[]);
41993 assert_u8_array_within_inclusive_range::<0, 0, 6>(&[]);
41994 assert_u8_array_within_inclusive_range::<0, 0, 255>(&[]);
41995 }
41996
41997 #[test]
41998 fn assert_u8_array_within_inclusive_range_accepts_singleton_array_when_byte_in_range() {
41999 // Singleton array `arr = [K]` at the `[u8; 1]` corner MUST pass
42000 // when `K in [LO..=HI]`. Cross-position coverage: the byte can
42001 // sit at the LO endpoint, an INTERIOR position, or the HI
42002 // endpoint — pins the OUTER-sweep's OR-disjunction guard
42003 // handles all three cases without gratuitously narrowing to
42004 // strict-inequality on either endpoint. A regression that
42005 // narrowed the guard to `arr[i] <= LO || arr[i] >= HI` (strict
42006 // endpoint exclusion) would silently reject singleton arrays
42007 // hitting the endpoints — this test catches BOTH endpoint
42008 // regressions in ONE forward sweep.
42009 assert_u8_array_within_inclusive_range::<1, 0, 6>(&[0u8]);
42010 assert_u8_array_within_inclusive_range::<1, 0, 6>(&[3u8]);
42011 assert_u8_array_within_inclusive_range::<1, 0, 6>(&[6u8]);
42012 }
42013
42014 #[test]
42015 fn assert_u8_array_within_inclusive_range_accepts_singleton_range_when_byte_equals_endpoint() {
42016 // Degenerate singleton-range corner `[K..=K]` where LO == HI —
42017 // the ONLY accepting arrays are those whose every entry equals
42018 // `K`. Pins the helper does NOT gratuitously reject the LO ==
42019 // HI degenerate case (a regression that narrowed `LO <= HI` to
42020 // `LO < HI` at some downstream bounds check would surface here
42021 // if this helper ever grew such a check; today it does not,
42022 // and this test locks it out).
42023 assert_u8_array_within_inclusive_range::<1, 42, 42>(&[42u8]);
42024 assert_u8_array_within_inclusive_range::<3, 42, 42>(&[42u8, 42u8, 42u8]);
42025 assert_u8_array_within_inclusive_range::<1, 0, 0>(&[0u8]);
42026 assert_u8_array_within_inclusive_range::<1, 255, 255>(&[255u8]);
42027 }
42028
42029 #[test]
42030 fn assert_u8_array_within_inclusive_range_accepts_arrays_with_duplicates() {
42031 // Peer corner to `_covers_inclusive_range`: this helper permits
42032 // duplicates in `arr` because RANGE-membership is a DISTINCT-
42033 // value predicate — `[3, 3, 5]` embeds in `[0..=6]` even
42034 // though the array is not pairwise-distinct. Pins that the
42035 // helper does NOT gratuitously require INJECTIVITY on `arr`
42036 // (the injectivity axis is a DIFFERENT compile-time contract
42037 // bound by `assert_u8_array_pairwise_distinct`; combining both
42038 // binds BOTH axes). Sibling posture to
42039 // `assert_u8_array_within_u8_finite_set_accepts_repeated_array_entries_in_set`
42040 // on the finite-set peer.
42041 assert_u8_array_within_inclusive_range::<3, 0, 6>(&[3u8, 3u8, 5u8]);
42042 assert_u8_array_within_inclusive_range::<4, 0, 6>(&[0u8, 0u8, 6u8, 6u8]);
42043 }
42044
42045 #[test]
42046 fn assert_u8_array_within_inclusive_range_accepts_the_structural_residual_subset_embedding() {
42047 // Runtime cross-check that the ONE (array, range) pair the
42048 // substrate's module-level `const _` witness pins at COMPILE
42049 // time is a PROPER SUBSET embedding at runtime too. The pair
42050 // enforces the theorem at TWO stages of the toolchain: the
42051 // const witness fires FIRST at `cargo check` (through the
42052 // module-level `const _: () = assert_u8_array_within_inclusive_
42053 // range::<2, 0, 6>(&StructuralKind::HASH_DISCRIMINATORS)`
42054 // line), this runtime pin catches the drift at `cargo test`
42055 // as a safety net. Sibling posture to
42056 // `assert_u8_array_within_u8_finite_set_accepts_the_substitution_subset_embedding`
42057 // on the finite-set-subset peer's substrate cross-check —
42058 // both pin the substrate's ONE-witness embedding at BOTH
42059 // stages of the toolchain.
42060 assert_u8_array_within_inclusive_range::<2, 0, 6>(
42061 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
42062 );
42063 }
42064
42065 #[test]
42066 #[should_panic(expected = "RANGE-SUBSET-VIOLATION")]
42067 fn assert_u8_array_within_inclusive_range_panics_at_runtime_on_entry_above_hi() {
42068 // NEGATIVE PIN — above-HI overshoot corner: an array carrying
42069 // a single entry `HI + 1` MUST panic at runtime with the
42070 // RANGE-SUBSET-VIOLATION-named message. Pins the helper's OWN
42071 // reject-above-HI arm — a regression that silently returned
42072 // without panicking on an out-of-range entry above the upper
42073 // bound would slip through the compile-time witness's failure
42074 // mode too. The offending byte `7u8` is intentionally chosen
42075 // ONE PAST the outer-`Sexp` partition's upper endpoint (`6u8`)
42076 // to pin the OVERSHOOT drift mode against the substrate's
42077 // load-bearing partition.
42078 assert_u8_array_within_inclusive_range::<2, 0, 6>(&[0u8, 7u8]);
42079 }
42080
42081 #[test]
42082 #[should_panic(expected = "RANGE-SUBSET-VIOLATION")]
42083 fn assert_u8_array_within_inclusive_range_panics_at_runtime_on_entry_below_lo() {
42084 // NEGATIVE PIN — below-LO undershoot corner symmetric to the
42085 // OVERSHOOT drift mode above: an offending byte BELOW the
42086 // range's minimum (`LO = 3u8`; this array carries a `0u8`
42087 // entry NOT in the range) MUST panic. Pins that the helper's
42088 // OR-disjunction guard does NOT drop the `arr[i] < LO` half
42089 // (leaving only the `arr[i] > HI` half) — a regression that
42090 // silently accepted below-LO entries would surface here.
42091 assert_u8_array_within_inclusive_range::<2, 3, 6>(&[0u8, 5u8]);
42092 }
42093
42094 #[test]
42095 #[should_panic(expected = "RANGE-SUBSET-VIOLATION")]
42096 fn assert_u8_array_within_inclusive_range_panics_at_runtime_on_terminal_out_of_range_entry() {
42097 // NEGATIVE PIN — terminal-position drift: an out-of-range
42098 // entry at the LAST array position MUST panic — pins that the
42099 // outer `while i < N` loop reaches `i = N - 1` (else the
42100 // terminal drift would slip through). A regression that
42101 // narrowed the outer sweep to `while i < N - 1` (off-by-one on
42102 // the OUTER bound) would silently accept this array. Sibling
42103 // posture to
42104 // `assert_u8_array_within_u8_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
42105 // on the finite-set-subset peer's terminal-position pin —
42106 // both bind the outer-sweep terminal bound at the ONE array-
42107 // side outer loop the helper carries.
42108 assert_u8_array_within_inclusive_range::<4, 0, 6>(&[0u8, 3u8, 6u8, 7u8]);
42109 }
42110
42111 #[test]
42112 fn assert_u8_array_within_inclusive_range_panic_message_names_the_helper_and_range_subset_violation_axis(
42113 ) {
42114 // PANIC-MESSAGE PROVENANCE PIN — RANGE-SUBSET-VIOLATION arm:
42115 // the panic message MUST begin with the helper's own name AND
42116 // identify the failed AXIS as "RANGE-SUBSET-VIOLATION" so
42117 // downstream diagnostics route the drift back to (a) the
42118 // helper by string search on
42119 // `"assert_u8_array_within_inclusive_range"` and (b) the axis
42120 // by string search on `"RANGE-SUBSET-VIOLATION"`. Sibling
42121 // posture to
42122 // `assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis`
42123 // on the finite-set-subset peer's provenance pin — the two
42124 // pins together bind the (helper, failed-axis) provenance
42125 // pair at ONE test per SUBSET helper on the (contiguity)
42126 // 2×2 face. The axis-provenance string
42127 // `"RANGE-SUBSET-VIOLATION"` is chosen DISTINCT from EVERY
42128 // sibling helper's axis vocabulary (`"duplicate"` on the
42129 // ARRAY-side pairwise-distinct sibling; `"OUT-OF-SET"` /
42130 // `"SET-BYTE-MISSING"` on the covers-finite-set sibling;
42131 // `"OUT-OF-RANGE"` / `"MISSING"` on the covers-inclusive-
42132 // range sibling; `"SUBSET-VIOLATION"` on the finite-set
42133 // SUBSET-only sibling; `"ARITY-MISMATCH"` on both
42134 // `_permutes_*` compound helpers; `"SET-NOT-PAIRWISE-
42135 // DISTINCT"` on the SET-side well-formedness sibling) so a
42136 // diagnostic that names the failed axis routes UNAMBIGUOUSLY
42137 // to (a) this specific range SUBSET-embedding helper, (b)
42138 // the `arr` argument as the drift site rather than the
42139 // `LO`/`HI` const parameters specifying the target range.
42140 let outcome = std::panic::catch_unwind(|| {
42141 assert_u8_array_within_inclusive_range::<2, 0, 6>(&[0u8, 7u8]);
42142 });
42143 let payload = outcome.expect_err(
42144 "assert_u8_array_within_inclusive_range must panic on an \
42145 out-of-range entry — the reject-out-of-range arm is the \
42146 sole RANGE-SUBSET-VIOLATION failure mode of the helper",
42147 );
42148 let msg = payload
42149 .downcast_ref::<&'static str>()
42150 .map(|s| (*s).to_owned())
42151 .or_else(|| payload.downcast_ref::<String>().cloned())
42152 .expect(
42153 "assert_u8_array_within_inclusive_range panic payload \
42154 must be a static &str or String",
42155 );
42156 assert!(
42157 msg.contains("assert_u8_array_within_inclusive_range"),
42158 "assert_u8_array_within_inclusive_range panic message \
42159 {msg:?} must name the helper for provenance-preserving \
42160 failure diagnostics",
42161 );
42162 assert!(
42163 msg.contains("RANGE-SUBSET-VIOLATION"),
42164 "assert_u8_array_within_inclusive_range panic message \
42165 {msg:?} must name the failed AXIS (\"RANGE-SUBSET-\
42166 VIOLATION\") for axis-provenance-preserving failure \
42167 diagnostics",
42168 );
42169 }
42170
42171 // ── `assert_u8_array_permutes_inclusive_range` — the compound
42172 // (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) permutation-of-range
42173 // verifier that collapses the pre-existing weak-witness pair
42174 // (`assert_u8_array_pairwise_distinct` +
42175 // `assert_u8_array_covers_inclusive_range`) into ONE `const _`
42176 // line per array on the three permutation-shaped
42177 // HASH_DISCRIMINATORS arrays (`AtomKind`, `QuoteForm`,
42178 // `UnquoteForm`). The runtime test surface matches the sibling-
42179 // helpers' shape (accept-singleton, accept-every-family-wide-
42180 // substrate-array, reject-arity-below-cardinality, reject-
42181 // arity-above-cardinality, reject-duplicate, reject-out-of-range,
42182 // panic-message-provenance on the ARITY-MISMATCH axis, panic-
42183 // message-provenance on the DELEGATED range-coverage axis,
42184 // panic-message-provenance on the DELEGATED pairwise-distinct
42185 // axis) split across the THREE failure arms so a regression that
42186 // silently weakens the helper on ANY arm (e.g. dropping the arity
42187 // check, dropping the range-coverage delegation, or dropping the
42188 // pairwise-distinct delegation) is caught by the helper's OWN
42189 // test surface rather than only surfacing as a false-positive on
42190 // some future permutation-shaped `[u8; N]` array's compound pin.
42191
42192 #[test]
42193 fn assert_u8_array_permutes_inclusive_range_accepts_the_singleton_range() {
42194 // Singleton range `{K..=K}` at the `[u8; 1]` corner — a
42195 // singleton array `[K]` is vacuously a permutation of `{K}`.
42196 // Cross-arity coverage on the trivial-range corner of the
42197 // const-N generic; simultaneously pins ALL THREE arms
42198 // (ARITY: `1 == 1`; RANGE-MEMBERSHIP: `K in [K, K]`;
42199 // PAIRWISE-DISTINCT: vacuously true on a singleton) at the
42200 // smallest witness. Sibling posture to
42201 // `assert_u8_array_covers_inclusive_range_accepts_the_
42202 // singleton_range` on the delegated range-coverage helper.
42203 assert_u8_array_permutes_inclusive_range::<1, 7, 7>(&[7u8]);
42204 assert_u8_array_permutes_inclusive_range::<1, 0, 0>(&[0u8]);
42205 assert_u8_array_permutes_inclusive_range::<1, 255, 255>(&[255u8]);
42206 }
42207
42208 #[test]
42209 fn assert_u8_array_permutes_inclusive_range_accepts_every_family_wide_permutation_array() {
42210 // Runtime cross-check that the SAME three permutation-shaped
42211 // HASH_DISCRIMINATORS arrays the module-level `const _: () =
42212 // ...` witnesses cover at COMPILE time are permutations of
42213 // their target ranges. A regression that removes ONE of the
42214 // `const _` witnesses would still leave THIS runtime pin as
42215 // a safety net; the const witness fires FIRST at `cargo
42216 // check`, this runtime pin catches the drift at `cargo test`.
42217 // The pair enforces the theorem at TWO stages of the
42218 // toolchain. Sibling posture to
42219 // `assert_u8_array_covers_inclusive_range_accepts_every_
42220 // family_wide_substrate_array` on the delegated range-
42221 // coverage helper; where that pin sweeps FOUR arrays
42222 // (`SexpShape` + `AtomKind` + `QuoteForm` + `UnquoteForm`)
42223 // on the single-axis SURJECTIVITY corner, this pin sweeps
42224 // the THREE permutation-shaped arrays on the compound
42225 // (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) corner —
42226 // `SexpShape::HASH_DISCRIMINATORS` is intentionally OMITTED
42227 // per the twelve-shape → seven-byte collapse rule
42228 // (DISTINCTNESS does not hold; it binds SURJECTIVITY-only
42229 // on the sibling helper).
42230 assert_u8_array_permutes_inclusive_range::<6, 0, 5>(&AtomKind::HASH_DISCRIMINATORS);
42231 assert_u8_array_permutes_inclusive_range::<4, 3, 6>(&QuoteForm::HASH_DISCRIMINATORS);
42232 assert_u8_array_permutes_inclusive_range::<2, 5, 6>(
42233 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
42234 );
42235 }
42236
42237 #[test]
42238 #[should_panic(expected = "ARITY-MISMATCH")]
42239 fn assert_u8_array_permutes_inclusive_range_panics_at_runtime_on_arity_below_cardinality() {
42240 // NEGATIVE PIN — ARITY-MISMATCH below-cardinality corner: an
42241 // array of arity `N < HI - LO + 1` MUST panic at runtime with
42242 // the ARITY-MISMATCH-named message. Pins the compound
42243 // helper's OWN reject-below-cardinality arm — a regression
42244 // that silently dropped the arity-check gate would let this
42245 // array reach the delegated `assert_u8_array_covers_
42246 // inclusive_range` call, which would then panic with the
42247 // sibling helper's `"MISSING"` message on the range byte
42248 // absent from the too-short array — masking the ARITY-
42249 // provenance behind the coverage-provenance downstream.
42250 assert_u8_array_permutes_inclusive_range::<2, 0, 2>(&[0u8, 2u8]);
42251 }
42252
42253 #[test]
42254 #[should_panic(expected = "ARITY-MISMATCH")]
42255 fn assert_u8_array_permutes_inclusive_range_panics_at_runtime_on_arity_above_cardinality() {
42256 // NEGATIVE PIN — ARITY-MISMATCH above-cardinality corner: an
42257 // array of arity `N > HI - LO + 1` MUST panic at runtime with
42258 // the ARITY-MISMATCH-named message. Symmetric sibling to the
42259 // below-cardinality pin — a regression that dropped the
42260 // arity-check gate would let this array reach the delegated
42261 // `assert_u8_array_pairwise_distinct` call, which would then
42262 // panic with the sibling helper's generic-duplicate message
42263 // on the pigeonhole-forced collision, masking the ARITY-
42264 // provenance behind the pairwise-distinct-provenance
42265 // downstream. The three entries stay within `[0, 1]` and are
42266 // pairwise-distinct on the first two positions, so the arity
42267 // check is the ONLY axis that can distinguish this from a
42268 // valid permutation.
42269 assert_u8_array_permutes_inclusive_range::<3, 0, 1>(&[0u8, 1u8, 0u8]);
42270 }
42271
42272 #[test]
42273 #[should_panic(expected = "duplicate")]
42274 fn assert_u8_array_permutes_inclusive_range_panics_at_runtime_on_duplicate() {
42275 // NEGATIVE PIN — PAIRWISE-DISTINCT arm: an array whose arity
42276 // matches the range cardinality but which contains a
42277 // duplicate entry (necessarily missing a range byte by
42278 // pigeonhole) MUST panic at runtime. Pins the delegated
42279 // `assert_u8_array_pairwise_distinct` arm — a regression
42280 // that silently dropped the delegation would leave the
42281 // duplicate uncaught. Note: the panic message here surfaces
42282 // from the SIBLING helper (containing `"duplicate"`) rather
42283 // than a compound-helper-namespaced string, per the
42284 // delegation-based body design; the compound helper's OWN
42285 // name still appears in the const-eval panic trace for a
42286 // caller debugging a `cargo check` failure.
42287 assert_u8_array_permutes_inclusive_range::<3, 0, 2>(&[0u8, 1u8, 1u8]);
42288 }
42289
42290 #[test]
42291 #[should_panic(expected = "OUT-OF-RANGE")]
42292 fn assert_u8_array_permutes_inclusive_range_panics_at_runtime_on_out_of_range() {
42293 // NEGATIVE PIN — RANGE-MEMBERSHIP arm: an array whose arity
42294 // matches the range cardinality but which contains an
42295 // out-of-range entry MUST panic at runtime with the
42296 // delegated `assert_u8_array_covers_inclusive_range`'s
42297 // `"OUT-OF-RANGE"` axis-provenance message. Pins the
42298 // delegated range-membership arm — a regression that
42299 // silently dropped the delegation would leave the
42300 // out-of-range entry uncaught. The three entries `[0, 1, 3]`
42301 // exhaust the arity of `[0..=2]` (3 entries for a 3-byte
42302 // range) but include `3u8` outside the target range;
42303 // pairwise-distinct is satisfied so this array can ONLY be
42304 // rejected on the range-membership axis.
42305 assert_u8_array_permutes_inclusive_range::<3, 0, 2>(&[0u8, 1u8, 3u8]);
42306 }
42307
42308 #[test]
42309 fn assert_u8_array_permutes_inclusive_range_panic_message_names_the_helper_and_arity_mismatch_axis(
42310 ) {
42311 // PANIC-MESSAGE PROVENANCE PIN — ARITY-MISMATCH arm: the
42312 // panic message MUST begin with the compound helper's own
42313 // name AND identify the failed AXIS as "ARITY-MISMATCH" so
42314 // downstream diagnostics route the drift back to (a) THIS
42315 // compound helper by string search on
42316 // `"assert_u8_array_permutes_inclusive_range"` and (b) the
42317 // ARITY axis by string search on `"ARITY-MISMATCH"`. This
42318 // string is chosen DISTINCT from every sibling helper's axis
42319 // strings ("OUT-OF-RANGE" / "MISSING" on the range-coverage
42320 // helper; "OUT-OF-SET" / "SET-BYTE-MISSING" on the finite-
42321 // set-coverage helper) so a drift's axis-provenance routes
42322 // UNAMBIGUOUSLY to (helper, axis) even in the presence of
42323 // the multiple sibling helpers. Sibling posture to
42324 // `assert_u8_array_covers_inclusive_range_panic_message_
42325 // names_the_helper_and_range_bound_axis` on the range-
42326 // coverage helper — both bind the (helper, failed-axis)
42327 // provenance pair at ONE test per axis.
42328 let outcome = std::panic::catch_unwind(|| {
42329 assert_u8_array_permutes_inclusive_range::<2, 0, 2>(&[0u8, 2u8]);
42330 });
42331 let payload = outcome.expect_err(
42332 "assert_u8_array_permutes_inclusive_range must panic on \
42333 an arity-cardinality mismatch — the reject-arity-\
42334 mismatch arm is one of the three failure modes of the \
42335 compound helper",
42336 );
42337 let msg = payload
42338 .downcast_ref::<&'static str>()
42339 .map(|s| (*s).to_owned())
42340 .or_else(|| payload.downcast_ref::<String>().cloned())
42341 .expect(
42342 "assert_u8_array_permutes_inclusive_range panic \
42343 payload must be a static &str or String",
42344 );
42345 assert!(
42346 msg.contains("assert_u8_array_permutes_inclusive_range"),
42347 "assert_u8_array_permutes_inclusive_range ARITY-MISMATCH \
42348 panic message {msg:?} must name the helper for \
42349 provenance-preserving failure diagnostics",
42350 );
42351 assert!(
42352 msg.contains("ARITY-MISMATCH"),
42353 "assert_u8_array_permutes_inclusive_range ARITY-MISMATCH \
42354 panic message {msg:?} must name the failed AXIS \
42355 (\"ARITY-MISMATCH\") for axis-provenance-preserving \
42356 failure diagnostics — the string is chosen DISTINCT \
42357 from every sibling helper's axis strings so downstream \
42358 diagnostics route UNAMBIGUOUSLY to this compound \
42359 helper's arity arm",
42360 );
42361 }
42362
42363 // ── `assert_u8_array_permutes_finite_set` — the compound
42364 // (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) permutation-of-finite-set
42365 // verifier on the NON-CONTIGUOUS-FINITE-SET corner of the
42366 // (contiguity) axis peer to the pre-existing
42367 // `assert_u8_array_permutes_inclusive_range` (contiguous-
42368 // inclusive-range corner) sibling. Ships `StructuralKind::HASH_
42369 // DISCRIMINATORS` from the pre-lift weak-witness pair
42370 // (`assert_u8_array_pairwise_distinct` + `assert_u8_array_covers_
42371 // finite_set`) into the compound tier at ONE `const _` line while
42372 // adding the arity-cardinality-equality contract that neither
42373 // weak sibling carries alone. The runtime test surface mirrors
42374 // the sibling `assert_u8_array_permutes_inclusive_range` compound
42375 // helper's shape (accept-singleton-set, accept-every-family-wide-
42376 // substrate-array, reject-arity-below-cardinality, reject-arity-
42377 // above-cardinality, reject-duplicate, reject-out-of-set, panic-
42378 // message-provenance on the ARITY-MISMATCH axis) split across the
42379 // three failure arms so a regression that silently weakens the
42380 // helper on ANY arm (e.g. dropping the arity check, dropping the
42381 // covers-finite-set delegation, or dropping the pairwise-distinct
42382 // delegation) is caught by the helper's OWN test surface rather
42383 // than only surfacing as a false-positive on some future
42384 // permutation-of-finite-set-shaped `[u8; N]` array's compound pin.
42385
42386 #[test]
42387 fn assert_u8_array_permutes_finite_set_accepts_the_singleton_set() {
42388 // Singleton set `{K}` at the `[u8; 1]` corner — a singleton
42389 // array `[K]` is vacuously a permutation of `{K}`. Cross-arity
42390 // coverage on the trivial-set corner of the const-N generic;
42391 // simultaneously pins ALL THREE arms (ARITY: `1 == 1`; SET-
42392 // MEMBERSHIP: `K in {K}`; PAIRWISE-DISTINCT: vacuously true on
42393 // a singleton) at the smallest witness. Sibling posture to
42394 // `assert_u8_array_permutes_inclusive_range_accepts_the_
42395 // singleton_range` on the contiguous-range corner peer.
42396 assert_u8_array_permutes_finite_set::<1, 1>(&[7u8], &[7u8]);
42397 assert_u8_array_permutes_finite_set::<1, 1>(&[0u8], &[0u8]);
42398 assert_u8_array_permutes_finite_set::<1, 1>(&[255u8], &[255u8]);
42399 }
42400
42401 #[test]
42402 fn assert_u8_array_permutes_finite_set_accepts_every_family_wide_permutation_of_finite_set_array(
42403 ) {
42404 // Runtime cross-check that the SAME
42405 // `StructuralKind::HASH_DISCRIMINATORS` array the module-level
42406 // `const _: () = ...` witness covers at COMPILE time is a
42407 // permutation of the non-contiguous target set `{0, 2}`. A
42408 // regression that removes the `const _` witness would still
42409 // leave THIS runtime pin as a safety net; the const witness
42410 // fires FIRST at `cargo check`, this runtime pin catches the
42411 // drift at `cargo test`. The pair enforces the theorem at TWO
42412 // stages of the toolchain. Sibling posture to
42413 // `assert_u8_array_permutes_inclusive_range_accepts_every_
42414 // family_wide_permutation_array` on the contiguous-range
42415 // corner: where that pin sweeps THREE range-covering arrays
42416 // (`AtomKind` + `QuoteForm` + `UnquoteForm`), this pin binds
42417 // the ONE non-contiguous-covering permutation-shaped array
42418 // (`StructuralKind`). Together the two runtime pins close the
42419 // whole compound tier of the substrate's `[u8; N]`
42420 // HASH_DISCRIMINATORS vocabulary at runtime symmetrically to
42421 // the compile-time compound witnesses.
42422 assert_u8_array_permutes_finite_set::<2, 2>(
42423 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
42424 &[0u8, 2u8],
42425 );
42426 }
42427
42428 #[test]
42429 fn assert_u8_array_permutes_finite_set_accepts_a_synthetic_non_contiguous_partition() {
42430 // POSITIVE — a synthetic `[u8; 3]` permutation of the non-
42431 // contiguous partition `{0, 2, 5}` (two gaps: at `{1}` and at
42432 // `{3, 4}`). Isolates the helper's INJECTIVITY-∧-SURJECTIVITY-
42433 // ∧-ARITY verdict from the substrate constants: a green here
42434 // plus a red on any of the negative pins below constrains the
42435 // helper's behavior structurally on the non-contiguous corner,
42436 // independent of the substrate's specific byte layout. Sibling
42437 // posture to `assert_u8_array_covers_finite_set_accepts_the_
42438 // non_contiguous_partition` on the single-axis SURJECTIVITY
42439 // sibling — where that pin binds the (SET-MEMBERSHIP + FULL-
42440 // COVERAGE) axes on a non-contiguous set, THIS pin binds the
42441 // compound (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) contract on
42442 // the same shape.
42443 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 2u8, 5u8], &[0u8, 2u8, 5u8]);
42444 assert_u8_array_permutes_finite_set::<3, 3>(&[5u8, 0u8, 2u8], &[0u8, 2u8, 5u8]);
42445 }
42446
42447 #[test]
42448 #[should_panic(expected = "ARITY-MISMATCH")]
42449 fn assert_u8_array_permutes_finite_set_panics_at_runtime_on_arity_below_cardinality() {
42450 // NEGATIVE PIN — ARITY-MISMATCH below-cardinality corner: an
42451 // array of arity `N < M` MUST panic at runtime with the ARITY-
42452 // MISMATCH-named message. Pins the compound helper's OWN
42453 // reject-below-cardinality arm — a regression that silently
42454 // dropped the arity-check gate would let this array reach the
42455 // delegated `assert_u8_array_covers_finite_set` call, which
42456 // would then panic with the sibling helper's `"SET-BYTE-
42457 // MISSING"` message on the set byte absent from the too-short
42458 // array — masking the ARITY-provenance behind the coverage-
42459 // provenance downstream. The array `[0]` covers set byte `0`
42460 // but is missing set byte `2`. Sibling posture to
42461 // `assert_u8_array_permutes_inclusive_range_panics_at_runtime_
42462 // on_arity_below_cardinality` on the contiguous-range corner.
42463 assert_u8_array_permutes_finite_set::<1, 2>(&[0u8], &[0u8, 2u8]);
42464 }
42465
42466 #[test]
42467 #[should_panic(expected = "ARITY-MISMATCH")]
42468 fn assert_u8_array_permutes_finite_set_panics_at_runtime_on_arity_above_cardinality() {
42469 // NEGATIVE PIN — ARITY-MISMATCH above-cardinality corner: an
42470 // array of arity `N > M` MUST panic at runtime with the
42471 // ARITY-MISMATCH-named message. Symmetric sibling to the
42472 // below-cardinality pin — a regression that dropped the arity-
42473 // check gate would let this array reach the delegated
42474 // `assert_u8_array_pairwise_distinct` call, which would then
42475 // panic with the sibling helper's generic-duplicate message on
42476 // the pigeonhole-forced collision, masking the ARITY-
42477 // provenance behind the pairwise-distinct-provenance
42478 // downstream. The three entries `[0, 2, 0]` stay within
42479 // `{0, 2}` (satisfying SET-MEMBERSHIP) but WOULD fail
42480 // pairwise-distinct downstream on the duplicate `0u8` —
42481 // however, the ARITY-check FIRES FIRST so the panic message
42482 // routes to the ARITY axis with the compound helper's own
42483 // name.
42484 assert_u8_array_permutes_finite_set::<3, 2>(&[0u8, 2u8, 0u8], &[0u8, 2u8]);
42485 }
42486
42487 #[test]
42488 #[should_panic(expected = "duplicate")]
42489 fn assert_u8_array_permutes_finite_set_panics_at_runtime_on_duplicate() {
42490 // NEGATIVE PIN — PAIRWISE-DISTINCT arm: an array whose arity
42491 // matches the set cardinality but which contains a duplicate
42492 // entry (necessarily missing a set byte by pigeonhole) MUST
42493 // panic at runtime. Pins the delegated
42494 // `assert_u8_array_pairwise_distinct` arm — a regression that
42495 // silently dropped the delegation would leave the duplicate
42496 // uncaught. Note: the panic message here surfaces from the
42497 // SIBLING helper (containing `"duplicate"`) rather than a
42498 // compound-helper-namespaced string, per the delegation-based
42499 // body design; the compound helper's OWN name still appears
42500 // in the const-eval panic trace for a caller debugging a
42501 // `cargo check` failure. Sibling posture to
42502 // `assert_u8_array_permutes_inclusive_range_panics_at_runtime_
42503 // on_duplicate` on the contiguous-range corner.
42504 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 2u8, 2u8], &[0u8, 2u8, 5u8]);
42505 }
42506
42507 #[test]
42508 #[should_panic(expected = "OUT-OF-SET")]
42509 fn assert_u8_array_permutes_finite_set_panics_at_runtime_on_out_of_set() {
42510 // NEGATIVE PIN — SET-MEMBERSHIP arm: an array whose arity
42511 // matches the set cardinality but which contains an entry
42512 // outside the target set MUST panic at runtime with the
42513 // delegated `assert_u8_array_covers_finite_set`'s `"OUT-OF-
42514 // SET"` axis-provenance message. Pins the delegated set-
42515 // membership arm — a regression that silently dropped the
42516 // delegation would leave the out-of-set entry uncaught. The
42517 // three entries `[0, 2, 3]` exhaust the arity of `{0, 2, 5}`
42518 // (3 entries for a 3-byte set) but include `3u8` outside the
42519 // target set; pairwise-distinct is satisfied so this array
42520 // can ONLY be rejected on the set-membership axis. Sibling
42521 // posture to `assert_u8_array_permutes_inclusive_range_panics_
42522 // at_runtime_on_out_of_range` on the contiguous-range corner
42523 // (`"OUT-OF-SET"` here vs. `"OUT-OF-RANGE"` there — chosen
42524 // DISTINCT so downstream diagnostics route UNAMBIGUOUSLY to
42525 // the failed contiguity corner).
42526 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 2u8, 3u8], &[0u8, 2u8, 5u8]);
42527 }
42528
42529 #[test]
42530 #[should_panic(expected = "OUT-OF-SET")]
42531 fn assert_u8_array_permutes_finite_set_panics_on_gap_byte_drift() {
42532 // NEGATIVE PIN — ARCHETYPE GAP-BYTE corner: an entry that
42533 // drifts INTO the intentional gap of a non-contiguous target
42534 // set MUST panic — this is the exact regression the archetype
42535 // `StructuralKind::HASH_DISCRIMINATORS` witness catches. A
42536 // regression that lifted a fresh `1u8` entry into the
42537 // `{0, 2}`-partitioned array would silently collide with
42538 // `AtomKind::OUTER_HASH_DISCRIMINATOR = 1u8` on the outer-
42539 // `Sexp` cache-key partition; this pin binds that failure mode
42540 // as an OUT-OF-SET rejection at the delegated covers-finite-
42541 // set arm's panic site. Sibling posture to `assert_u8_array_
42542 // covers_finite_set_panics_on_gap_byte_drift` at the single-
42543 // axis SURJECTIVITY sibling — where that pin binds the gap-
42544 // byte-drift failure at the (SET-MEMBERSHIP) axis alone, THIS
42545 // pin binds the SAME drift on the compound (INJECTIVITY ∧
42546 // SURJECTIVITY ∧ ARITY) contract. The four-entry witness
42547 // `[0, 1, 2, 5]` stays pairwise-distinct and matches the set
42548 // cardinality of `{0, 2, 1, 5}` but the middle `1u8` — if the
42549 // set were `{0, 2, 5}` and `[0, 1, 2]` were the array — would
42550 // fire the OUT-OF-SET arm; here the arity is aligned to test
42551 // the middle-gap drift with `1u8` in `arr` but NOT in `set`.
42552 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 1u8, 2u8], &[0u8, 2u8, 5u8]);
42553 }
42554
42555 #[test]
42556 fn assert_u8_array_permutes_finite_set_panic_message_names_the_helper_and_arity_mismatch_axis()
42557 {
42558 // PANIC-MESSAGE PROVENANCE PIN — ARITY-MISMATCH arm: the panic
42559 // message MUST begin with the compound helper's own name AND
42560 // identify the failed AXIS as "ARITY-MISMATCH" so downstream
42561 // diagnostics route the drift back to (a) THIS compound helper
42562 // by string search on
42563 // `"assert_u8_array_permutes_finite_set"` and (b) the ARITY
42564 // axis by string search on `"ARITY-MISMATCH"`. The
42565 // "ARITY-MISMATCH" axis-name is SHARED with the contiguous-
42566 // range sibling `assert_u8_array_permutes_inclusive_range`
42567 // (both compound permutation helpers share the arity axis),
42568 // but the HELPER-name distinguishes the two: string search on
42569 // (`"assert_u8_array_permutes_finite_set"`, `"ARITY-MISMATCH"`)
42570 // routes UNAMBIGUOUSLY to the (non-contiguous corner, arity
42571 // arm) pair. Sibling posture to
42572 // `assert_u8_array_permutes_inclusive_range_panic_message_
42573 // names_the_helper_and_arity_mismatch_axis` on the contiguous-
42574 // range corner — both bind the (helper, failed-axis)
42575 // provenance pair at ONE test per axis.
42576 let outcome = std::panic::catch_unwind(|| {
42577 assert_u8_array_permutes_finite_set::<1, 2>(&[0u8], &[0u8, 2u8]);
42578 });
42579 let payload = outcome.expect_err(
42580 "assert_u8_array_permutes_finite_set must panic on an \
42581 arity-cardinality mismatch — the reject-arity-mismatch \
42582 arm is one of the three failure modes of the compound \
42583 helper",
42584 );
42585 let msg = payload
42586 .downcast_ref::<&'static str>()
42587 .map(|s| (*s).to_owned())
42588 .or_else(|| payload.downcast_ref::<String>().cloned())
42589 .expect(
42590 "assert_u8_array_permutes_finite_set panic payload \
42591 must be a static &str or String",
42592 );
42593 assert!(
42594 msg.contains("assert_u8_array_permutes_finite_set"),
42595 "assert_u8_array_permutes_finite_set ARITY-MISMATCH panic \
42596 message {msg:?} must name the helper for provenance-\
42597 preserving failure diagnostics — the HELPER-name is the \
42598 axis-name-tie-breaker between this helper and the \
42599 contiguous-range sibling which SHARES the \
42600 \"ARITY-MISMATCH\" axis-provenance string",
42601 );
42602 assert!(
42603 msg.contains("ARITY-MISMATCH"),
42604 "assert_u8_array_permutes_finite_set ARITY-MISMATCH panic \
42605 message {msg:?} must name the failed AXIS \
42606 (\"ARITY-MISMATCH\") for axis-provenance-preserving \
42607 failure diagnostics",
42608 );
42609 }
42610
42611 // ── `assert_scalar_plus_two_u8_arrays_permute_inclusive_range` —
42612 // the compound JOINT (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY)
42613 // permutation-of-range verifier on the (scalar-plus-two-arrays)
42614 // corner of the (carving-shape) axis peer to the pre-existing
42615 // (single-array) corner. The runtime test surface matches the
42616 // sibling `assert_u8_array_permutes_inclusive_range` compound
42617 // helper's shape (accept-canonical-outer-Sexp-partition, accept-
42618 // synthetic-valid-partition, reject-arity-below-cardinality,
42619 // reject-arity-above-cardinality, reject-scalar-out-of-range,
42620 // reject-first-array-out-of-range, reject-second-array-out-of-
42621 // range, reject-cross-carving-duplicate, reject-intra-carving-
42622 // duplicate, panic-message-provenance on the JOINT ARITY-
42623 // MISMATCH axis, panic-message-provenance on the JOINT SCALAR-
42624 // OUT-OF-RANGE axis) split across the six failure arms so a
42625 // regression that silently weakens the helper on ANY arm is
42626 // caught by the helper's OWN test surface rather than only
42627 // surfacing as a false-positive on some future permutation-
42628 // shaped joint carving's compound pin.
42629
42630 #[test]
42631 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_accepts_canonical_outer_sexp_partition(
42632 ) {
42633 // Runtime cross-check that the SAME joint carving the
42634 // module-level `const _: () = ...` witness covers at COMPILE
42635 // time is a permutation of the outer-`Sexp` cache-key
42636 // discriminator range `{0..=6}`. A regression that removes
42637 // the `const _` witness would still leave THIS runtime pin as
42638 // a safety net; the const witness fires FIRST at `cargo
42639 // check`, this runtime pin catches the drift at `cargo test`.
42640 // The pair enforces the joint theorem at TWO stages of the
42641 // toolchain. Sibling posture to
42642 // `assert_u8_array_permutes_inclusive_range_accepts_every_
42643 // family_wide_permutation_array` on the (single-array)
42644 // corner — where that pin sweeps THREE arrays on the
42645 // single-array corner, THIS pin binds the ONE joint carving
42646 // on the (scalar-plus-two-arrays) corner.
42647 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42648 AtomKind::OUTER_HASH_DISCRIMINATOR,
42649 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
42650 &QuoteForm::HASH_DISCRIMINATORS,
42651 );
42652 }
42653
42654 #[test]
42655 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_accepts_synthetic_valid_partition()
42656 {
42657 // POSITIVE — a valid `(scalar, [u8; 2], [u8; 4])` permutation
42658 // of `{0..=6}` byte-identical in shape to the outer-`Sexp`
42659 // partition but with the scalar at `1u8` remapped through
42660 // literals rather than the substrate constant. Isolates the
42661 // helper's INJECTIVITY-∧-SURJECTIVITY-∧-ARITY verdict from
42662 // the substrate constants: a green here plus a red on any
42663 // of the negative pins below constrains the helper's
42664 // behavior structurally, independent of the substrate's
42665 // specific byte layout.
42666 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42667 1u8,
42668 &[0u8, 2u8],
42669 &[3u8, 4u8, 5u8, 6u8],
42670 );
42671 }
42672
42673 #[test]
42674 #[should_panic(expected = "ARITY-MISMATCH")]
42675 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_arity_below_cardinality(
42676 ) {
42677 // NEGATIVE PIN — ARITY-MISMATCH below-cardinality corner: a
42678 // joint carving of arity `1 + M + N < HI - LO + 1` MUST
42679 // panic at runtime with the ARITY-MISMATCH-named message.
42680 // Pins the helper's OWN reject-below-cardinality arm — a
42681 // regression that silently dropped the arity-check gate
42682 // would let this triple reach the sweep-and-count loop,
42683 // which would then panic with `"MISSING"` on the range byte
42684 // absent from the too-short joint carving — masking the
42685 // JOINT ARITY-provenance behind the JOINT SURJECTIVITY-
42686 // provenance downstream. Sibling posture to
42687 // `assert_u8_array_permutes_inclusive_range_panics_at_
42688 // runtime_on_arity_below_cardinality` on the (single-array)
42689 // corner.
42690 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 3, 0, 6>(
42691 1u8,
42692 &[0u8, 2u8],
42693 &[3u8, 4u8, 5u8],
42694 );
42695 }
42696
42697 #[test]
42698 #[should_panic(expected = "ARITY-MISMATCH")]
42699 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_arity_above_cardinality(
42700 ) {
42701 // NEGATIVE PIN — ARITY-MISMATCH above-cardinality corner:
42702 // symmetric sibling to the below-cardinality pin. `1 + 2 + 3
42703 // = 6` slots for a `{0..=2}` range of cardinality 3 — the
42704 // arity check fires FIRST before the sweep-and-count would
42705 // catch the joint duplicates on `0`/`1`/`2`. Pins that the
42706 // ARITY-provenance surfaces even when downstream axes would
42707 // ALSO reject.
42708 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 3, 0, 2>(
42709 0u8,
42710 &[1u8, 2u8],
42711 &[0u8, 1u8, 2u8],
42712 );
42713 }
42714
42715 #[test]
42716 #[should_panic(expected = "OUT-OF-RANGE")]
42717 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_scalar_out_of_range(
42718 ) {
42719 // NEGATIVE PIN — SCALAR OUT-OF-RANGE arm: a joint carving
42720 // whose arity matches the range cardinality but whose SCALAR
42721 // lies outside `[LO, HI]` MUST panic at runtime. Pins the
42722 // helper's scalar-carving arm distinct from the array
42723 // carvings' out-of-range arms — a regression that skipped
42724 // the scalar check but kept the array checks would leave
42725 // this drift uncaught.
42726 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42727 7u8,
42728 &[0u8, 2u8],
42729 &[3u8, 4u8, 5u8, 6u8],
42730 );
42731 }
42732
42733 #[test]
42734 #[should_panic(expected = "OUT-OF-RANGE")]
42735 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_first_array_out_of_range(
42736 ) {
42737 // NEGATIVE PIN — FIRST-ARRAY OUT-OF-RANGE arm: a joint
42738 // carving whose arity + scalar are valid but whose FIRST
42739 // ARRAY carries an out-of-range entry MUST panic at runtime.
42740 // The `1u8` slot the scalar occupies is left absent from
42741 // the first array; the entry `7u8` lies outside `[0, 6]`.
42742 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42743 1u8,
42744 &[0u8, 7u8],
42745 &[3u8, 4u8, 5u8, 6u8],
42746 );
42747 }
42748
42749 #[test]
42750 #[should_panic(expected = "OUT-OF-RANGE")]
42751 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_second_array_out_of_range(
42752 ) {
42753 // NEGATIVE PIN — SECOND-ARRAY OUT-OF-RANGE arm: symmetric
42754 // sibling to the first-array pin. The entry `9u8` in the
42755 // second array lies outside `[0, 6]`; all other arms are
42756 // green.
42757 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42758 1u8,
42759 &[0u8, 2u8],
42760 &[3u8, 4u8, 5u8, 9u8],
42761 );
42762 }
42763
42764 #[test]
42765 #[should_panic(expected = "duplicate")]
42766 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_cross_carving_duplicate(
42767 ) {
42768 // NEGATIVE PIN — JOINT-duplicate arm (cross-carving): the
42769 // byte `1u8` appears in BOTH the scalar carving AND the
42770 // first array. Arity matches, all bytes are in-range, but
42771 // JOINT INJECTIVITY fails — the duplicate byte occurs at
42772 // `count == 2` in the sweep-and-count loop, masking the
42773 // pigeonhole-forced JOINT-MISSING at byte `2u8` behind the
42774 // duplicate arm's message. Pins the cross-carving collision
42775 // detection between the scalar arm and a downstream array
42776 // arm.
42777 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42778 1u8,
42779 &[0u8, 1u8],
42780 &[3u8, 4u8, 5u8, 6u8],
42781 );
42782 }
42783
42784 #[test]
42785 #[should_panic(expected = "duplicate")]
42786 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_intra_carving_duplicate(
42787 ) {
42788 // NEGATIVE PIN — JOINT-duplicate arm (intra-carving): the
42789 // byte `0u8` appears TWICE inside the first array. Arity
42790 // matches, all bytes are in-range, but JOINT INJECTIVITY
42791 // fails — the sweep-and-count discipline treats intra- and
42792 // cross-carving collisions with the SAME mechanism, so this
42793 // failure mode surfaces on the same `"duplicate"` axis-
42794 // provenance message as the cross-carving pin.
42795 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42796 1u8,
42797 &[0u8, 0u8],
42798 &[3u8, 4u8, 5u8, 6u8],
42799 );
42800 }
42801
42802 #[test]
42803 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panic_message_names_the_helper_and_arity_mismatch_axis(
42804 ) {
42805 // PANIC-MESSAGE PROVENANCE PIN — ARITY-MISMATCH arm: the
42806 // panic message MUST begin with the compound helper's own
42807 // name AND identify the failed AXIS as "ARITY-MISMATCH".
42808 // Sibling posture to `assert_u8_array_permutes_inclusive_
42809 // range_panic_message_names_the_helper_and_arity_mismatch_
42810 // axis` on the (single-array) corner — both bind the
42811 // (helper, failed-axis) provenance pair.
42812 let outcome = std::panic::catch_unwind(|| {
42813 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 3, 0, 6>(
42814 1u8,
42815 &[0u8, 2u8],
42816 &[3u8, 4u8, 5u8],
42817 );
42818 });
42819 let payload = outcome.expect_err(
42820 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range \
42821 must panic on an arity-cardinality mismatch",
42822 );
42823 let msg = payload
42824 .downcast_ref::<&'static str>()
42825 .map(|s| (*s).to_owned())
42826 .or_else(|| payload.downcast_ref::<String>().cloned())
42827 .expect(
42828 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range \
42829 panic payload must be a static &str or String",
42830 );
42831 assert!(
42832 msg.contains("assert_scalar_plus_two_u8_arrays_permute_inclusive_range"),
42833 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range \
42834 ARITY-MISMATCH panic message {msg:?} must name the helper \
42835 for provenance-preserving failure diagnostics",
42836 );
42837 assert!(
42838 msg.contains("ARITY-MISMATCH"),
42839 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range \
42840 ARITY-MISMATCH panic message {msg:?} must name the failed \
42841 AXIS (\"ARITY-MISMATCH\") for axis-provenance-preserving \
42842 failure diagnostics",
42843 );
42844 }
42845
42846 // ── node_count: structural size on the outer Sexp algebra ──────────
42847
42848 #[test]
42849 fn node_count_nil_is_one() {
42850 // The residual-axis unit-payload arm contributes exactly one
42851 // node — the outer arm itself. Base case of the recursion.
42852 assert_eq!(Sexp::Nil.node_count(), 1);
42853 }
42854
42855 #[test]
42856 fn node_count_atom_is_one_for_every_atom_kind() {
42857 // Every atomic arm — Symbol, Keyword, Str, Int, Float, Bool —
42858 // is a leaf on the AST algebra. The payload's own length
42859 // (a string with 100 chars, an integer with a large value)
42860 // does not contribute; the projection counts NODES on the
42861 // typed algebra, not bytes on the payload.
42862 assert_eq!(Sexp::symbol("foo").node_count(), 1);
42863 assert_eq!(Sexp::keyword("k").node_count(), 1);
42864 assert_eq!(Sexp::string("hello world").node_count(), 1);
42865 assert_eq!(Sexp::int(42).node_count(), 1);
42866 assert_eq!(Sexp::float(1.5).node_count(), 1);
42867 assert_eq!(Sexp::boolean(true).node_count(), 1);
42868 assert_eq!(
42869 Sexp::symbol("a-symbol-with-a-very-long-name").node_count(),
42870 1
42871 );
42872 }
42873
42874 #[test]
42875 fn node_count_empty_list_is_one() {
42876 // The residual-axis payload-bearing arm with an empty payload
42877 // contributes exactly one node — the outer arm itself, no
42878 // children to sum over. Sits at the boundary between the
42879 // `Sexp::Nil` unit arm (also one node) and every non-empty
42880 // list.
42881 assert_eq!(Sexp::List(vec![]).node_count(), 1);
42882 }
42883
42884 #[test]
42885 fn node_count_flat_list_is_one_plus_child_count() {
42886 // `(a b c)` — one outer List arm plus three Atom children,
42887 // each contributing one node. Load-bearing arithmetic
42888 // identity: `list(items).node_count() == 1 +
42889 // sum(item.node_count())` with atom children.
42890 let form = Sexp::list([Sexp::symbol("a"), Sexp::symbol("b"), Sexp::symbol("c")]);
42891 assert_eq!(form.node_count(), 4);
42892 }
42893
42894 #[test]
42895 fn node_count_nested_list_sums_recursively() {
42896 // `(a (b c) d)` — one outer, plus a=1, plus inner (b c)=3,
42897 // plus d=1. Total 6. Pins the recursive sum over the tree.
42898 let form = Sexp::list([
42899 Sexp::symbol("a"),
42900 Sexp::list([Sexp::symbol("b"), Sexp::symbol("c")]),
42901 Sexp::symbol("d"),
42902 ]);
42903 assert_eq!(form.node_count(), 6);
42904 }
42905
42906 #[test]
42907 fn node_count_each_quote_family_wrapper_adds_one_plus_inner() {
42908 // The four homoiconic wrappers each contribute one node for
42909 // the wrapper arm plus the node count of the wrapped inner.
42910 // `'x` = 2 (Quote wrapper + Atom x).
42911 // `` `x `` = 2. `,x` = 2. `,@x` = 2.
42912 let x = Sexp::symbol("x");
42913 assert_eq!(Sexp::Quote(Box::new(x.clone())).node_count(), 2);
42914 assert_eq!(Sexp::Quasiquote(Box::new(x.clone())).node_count(), 2);
42915 assert_eq!(Sexp::Unquote(Box::new(x.clone())).node_count(), 2);
42916 assert_eq!(Sexp::UnquoteSplice(Box::new(x.clone())).node_count(), 2);
42917 // Nested inner: `'(a b)` = Quote wrapper (1) + inner (a b)
42918 // list (3) = 4. Pins the recursion through wrappers.
42919 let inner = Sexp::list([Sexp::symbol("a"), Sexp::symbol("b")]);
42920 assert_eq!(Sexp::Quote(Box::new(inner)).node_count(), 4);
42921 }
42922
42923 #[test]
42924 fn node_count_is_monotone_under_list_growth() {
42925 // Adding a strictly-larger subtree to a list strictly grows
42926 // the node count. LOAD-BEARING monotonicity pin — a resource
42927 // ceiling keyed on `node_count` relies on this identity so
42928 // that a strictly-larger expansion is bounded by a strictly-
42929 // larger count. A regression that ever ignored a child's
42930 // contribution (a bug that hardcoded arm-only counting)
42931 // fails this pin because the added subtree's own children
42932 // would silently drop out.
42933 let small = Sexp::list([Sexp::symbol("a")]);
42934 let large = Sexp::list([
42935 Sexp::symbol("a"),
42936 Sexp::list([Sexp::symbol("b"), Sexp::symbol("c")]),
42937 ]);
42938 assert!(large.node_count() > small.node_count());
42939 assert_eq!(small.node_count(), 2);
42940 assert_eq!(large.node_count(), 5);
42941 }
42942}