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 /// A string payload as source text, quoted and escaped so that
9623 /// [`Self::decode_str_escape`] (plus the reader's `\u{…}` arm) reads back
9624 /// exactly these bytes.
9625 ///
9626 /// The inverse of the reader, deliberately written as one: anything the
9627 /// reader cannot decode is emitted as `\u{…}` rather than as a shorter
9628 /// escape that would mean something different.
9629 #[must_use]
9630 pub fn escape_str_payload(s: &str) -> String {
9631 let mut out = String::with_capacity(s.len() + 2);
9632 out.push(Self::STR_DELIMITER);
9633 for c in s.chars() {
9634 match c {
9635 Self::STR_DELIMITER | Self::STR_ESCAPE_LEAD => {
9636 out.push(Self::STR_ESCAPE_LEAD);
9637 out.push(c);
9638 }
9639 Self::NEWLINE_ESCAPE_DECODED => {
9640 out.push(Self::STR_ESCAPE_LEAD);
9641 out.push(Self::NEWLINE_ESCAPE_SOURCE);
9642 }
9643 Self::TAB_ESCAPE_DECODED => {
9644 out.push(Self::STR_ESCAPE_LEAD);
9645 out.push(Self::TAB_ESCAPE_SOURCE);
9646 }
9647 Self::CARRIAGE_RETURN_ESCAPE_DECODED => {
9648 out.push(Self::STR_ESCAPE_LEAD);
9649 out.push(Self::CARRIAGE_RETURN_ESCAPE_SOURCE);
9650 }
9651 // Everything else the reader cannot round-trip literally goes
9652 // out as `\u{…}`, which it CAN. Printable characters — including
9653 // every non-ASCII one — are emitted raw, so ordinary text stays
9654 // readable.
9655 c if c.is_control() => {
9656 out.push_str("\\u{");
9657 out.push_str(&alloc_hex(c as u32));
9658 out.push('}');
9659 }
9660 c => out.push(c),
9661 }
9662 }
9663 out.push(Self::STR_DELIMITER);
9664 out
9665 }
9666
9667 pub const fn decode_str_escape(esc: char) -> char {
9668 match esc {
9669 Self::NEWLINE_ESCAPE_SOURCE => Self::NEWLINE_ESCAPE_DECODED,
9670 Self::TAB_ESCAPE_SOURCE => Self::TAB_ESCAPE_DECODED,
9671 Self::CARRIAGE_RETURN_ESCAPE_SOURCE => Self::CARRIAGE_RETURN_ESCAPE_DECODED,
9672 Self::STR_DELIMITER => Self::STR_DELIMITER,
9673 Self::STR_ESCAPE_LEAD => Self::STR_ESCAPE_LEAD,
9674 other => other,
9675 }
9676 }
9677
9678 /// Canonical [`Self::Symbol`] constructor — first of the six per-
9679 /// variant typed-construct methods on the closed-set [`Atom`]
9680 /// algebra. Takes `impl Into<String>` so the consumer composes any
9681 /// `&str` / `String` / `Cow<'_, str>` into the typed payload without
9682 /// pre-coercing at its site — the `.into()` boundary lives at this
9683 /// method on the algebra, parallel to how the [`Sexp`] outer
9684 /// constructors ([`Sexp::symbol`], [`Sexp::keyword`],
9685 /// [`Sexp::string`]) accept the same `impl Into<String>` shape at
9686 /// the outer algebra layer.
9687 ///
9688 /// Sibling typed-construct family on the closed-set [`Atom`]
9689 /// algebra — paired section-for-retraction with the soft-projection
9690 /// family ([`Self::as_symbol`], [`Self::as_keyword`],
9691 /// [`Self::as_string`], [`Self::as_int`], [`Self::as_float`],
9692 /// [`Self::as_bool`]). Pre-lift the typed-construct family was
9693 /// missing from the algebra: consumers reached for the bare
9694 /// `Self::Symbol(s.into())` tuple-variant constructor + `.into()`
9695 /// coercion at every site (with no `impl Into` ergonomy on the
9696 /// algebra), AND the soft-projection family had no constructor
9697 /// peer — section-for-retraction was uneven. Post-lift every
9698 /// consumer that builds an [`Atom`] from a typed payload at one
9699 /// site AND projects an [`Atom`] back to its typed payload at
9700 /// another binds to ONE method per direction on the algebra. The
9701 /// six [`Sexp`] outer constructors ([`Sexp::symbol`] through
9702 /// [`Sexp::boolean`]) route through `Self::Atom(Atom::X(_))` —
9703 /// `.into()` ergonomy on the inner algebra is reused at the outer
9704 /// algebra without re-derivation.
9705 ///
9706 /// Round-trip law binding it to the soft-projection sibling: for
9707 /// every `s: &str`, `Atom::symbol(s).as_symbol() == Some(s)` —
9708 /// every other arm projects to `None`. Same posture across the
9709 /// five sibling pairs (`Atom::keyword(s).as_keyword() == Some(s)`,
9710 /// …). The `kind()` projection ([`Self::kind`]) similarly
9711 /// round-trips through the construct face: `Atom::symbol(_).kind()
9712 /// == AtomKind::Symbol`.
9713 ///
9714 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle;
9715 /// every consumer that constructs an [`Atom`] of a typed kind binds
9716 /// to ONE typed method on the algebra rather than to the bare
9717 /// tuple-variant constructor + per-site `.into()` coercion.
9718 /// THEORY.md §V.1 — knowable platform; the `(AtomKind variant,
9719 /// typed construct method)` pair becomes a TYPE projection on the
9720 /// substrate's [`Atom`] algebra. THEORY.md §VI.1 — generation over
9721 /// composition; the `[Sexp; 6]` outer constructors at
9722 /// [`Sexp::symbol`]–[`Sexp::boolean`] regenerate identically
9723 /// through `Self::Atom(Atom::X(_))` composition rather than
9724 /// re-deriving the `.into()` + tuple-variant pair per outer
9725 /// constructor.
9726 ///
9727 /// Frontier inspiration: Racket's `(symbol 'x)` / `(string s)` —
9728 /// the typed-construct face the consumer reaches for a typed
9729 /// atomic value paired one-for-one with `(symbol? v)` /
9730 /// `(symbol->string v)` predicate/projection siblings; the
9731 /// substrate's [`Self::symbol`] / [`Self::as_symbol`] pair is the
9732 /// Rust-typed peer on the closed-set [`Atom`] algebra, with
9733 /// `impl Into<String>` standing in for Racket's typed-pair coerce
9734 /// face. MLIR's `mlir::SymbolAttr::get(ctx, name)` — typed-IR
9735 /// attribute construction routes through ONE typed factory paired
9736 /// with `mlir::dyn_cast<SymbolAttr>(attr)` on the projection face;
9737 /// `Atom::symbol` is the substrate's unstructured-Rust peer.
9738 #[must_use]
9739 pub fn symbol(s: impl Into<String>) -> Self {
9740 Self::Symbol(s.into())
9741 }
9742
9743 /// Canonical [`Self::Keyword`] constructor — second of the six
9744 /// per-variant typed-construct methods on the closed-set [`Atom`]
9745 /// algebra. See [`Self::symbol`] for the algebra-level docstring.
9746 #[must_use]
9747 pub fn keyword(s: impl Into<String>) -> Self {
9748 Self::Keyword(s.into())
9749 }
9750
9751 /// Canonical [`Self::Str`] constructor — third of the six per-variant
9752 /// typed-construct methods. The method name is `string` for
9753 /// consumer-vocabulary continuity with [`Self::as_string`] /
9754 /// [`Sexp::string`] / [`crate::error::SexpShape::String`] (the typed
9755 /// payload variant is `Str` for `String` shortening; the consumer-
9756 /// facing method keeps `string` for symmetry).
9757 #[must_use]
9758 pub fn string(s: impl Into<String>) -> Self {
9759 Self::Str(s.into())
9760 }
9761
9762 /// Canonical [`Self::Int`] constructor — fourth of the six per-variant
9763 /// typed-construct methods. The `i64` is taken by value (no
9764 /// `impl Into<…>` widening) — strict typed identity at the algebra
9765 /// boundary, the same posture [`Self::as_int`] preserves on the
9766 /// soft-projection face (`Atom::Int(n)` projects to `Some(n)` only;
9767 /// the `Sexp::as_float` consumer is where Int→Float widening lives).
9768 #[must_use]
9769 pub fn int(n: i64) -> Self {
9770 Self::Int(n)
9771 }
9772
9773 /// Canonical [`Self::Float`] constructor — fifth of the six
9774 /// per-variant typed-construct methods. The `f64` is taken by value
9775 /// (no `impl Into<…>` widening), matching [`Self::int`]'s strict
9776 /// typed-identity posture at the algebra boundary.
9777 #[must_use]
9778 pub fn float(n: f64) -> Self {
9779 Self::Float(n)
9780 }
9781
9782 /// Canonical [`Self::Bool`] constructor — sixth and last of the six
9783 /// per-variant typed-construct methods on the closed-set [`Atom`]
9784 /// algebra. Together with the five siblings ([`Self::symbol`],
9785 /// [`Self::keyword`], [`Self::string`], [`Self::int`],
9786 /// [`Self::float`]) the per-`Atom`-variant typed-construct family is
9787 /// complete across all six closed-set arms, and pairs section-for-
9788 /// retraction with the soft-projection family ([`Self::as_symbol`],
9789 /// [`Self::as_keyword`], [`Self::as_string`], [`Self::as_int`],
9790 /// [`Self::as_float`], [`Self::as_bool`]) — every consumer that
9791 /// constructs an [`Atom`] from a typed payload at one site AND
9792 /// projects an [`Atom`] back to its typed payload at another binds
9793 /// to ONE method per direction on the algebra rather than to the
9794 /// bare tuple-variant constructor + the soft-projection method
9795 /// asymmetrically.
9796 ///
9797 /// The closed-set `bool` payload's Scheme-canonical `#t` / `#f`
9798 /// reader lexemes are dispatched at [`Self::from_lexeme`] (the
9799 /// typed-ENTRY classifier) — this method is the construction face
9800 /// the consumer composes the typed `bool` value into when building
9801 /// an [`Atom`] from already-typed Rust, parallel to how
9802 /// [`Self::int`] and [`Self::float`] take their typed payload by
9803 /// value.
9804 #[must_use]
9805 pub fn boolean(b: bool) -> Self {
9806 Self::Bool(b)
9807 }
9808
9809 /// Project the atomic value into its closed-set [`AtomKind`] marker —
9810 /// `Symbol(_) → AtomKind::Symbol`, `Keyword(_) → AtomKind::Keyword`,
9811 /// `Str(_) → AtomKind::Str`, `Int(_) → AtomKind::Int`,
9812 /// `Float(_) → AtomKind::Float`, `Bool(_) → AtomKind::Bool`. The
9813 /// projection discards the payload and surfaces the typed
9814 /// discriminator that every per-atom-kind dispatch site (Hash cache-
9815 /// key bytes via [`AtomKind::hash_discriminator`], outer-shape
9816 /// projection via [`AtomKind::sexp_shape`], diagnostic label via
9817 /// [`AtomKind::label`]) keys on.
9818 ///
9819 /// Soft-projection peer of [`Sexp::as_quote_form`]: where
9820 /// `as_quote_form` decomposes the four homoiconic prefix wrappers
9821 /// into `(QuoteForm, &Sexp)`, `kind` decomposes the six atomic
9822 /// payloads into `AtomKind` alone — there is no inner-sexp body to
9823 /// surface, so the projection's return type is just the marker.
9824 /// Sibling-arm sweep with the quote-family `as_quote_form` /
9825 /// `QuoteForm` algebra lifts the (Atom variant, byte-discriminator,
9826 /// canonical-label, SexpShape variant) quadruple from per-callsite
9827 /// discipline (`Hash for Atom`'s six byte literals AND
9828 /// `domain::sexp_shape`'s six SexpShape literals) onto ONE typed
9829 /// algebra the substrate's diagnostic + cache-key surfaces both
9830 /// route through.
9831 ///
9832 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
9833 /// (Atom variant, downstream-consumer-payload) pairing now binds at
9834 /// ONE typed projection site (this method composed with
9835 /// [`AtomKind`]'s arms) regardless of which consumer surface
9836 /// (cache-key Hash, diagnostic SexpShape, future LSP completion
9837 /// label) needs it. A regression that drifts ONE consumer's pairing
9838 /// from the others cannot reach the substrate's runtime.
9839 #[must_use]
9840 pub fn kind(&self) -> AtomKind {
9841 match self {
9842 Self::Symbol(_) => AtomKind::Symbol,
9843 Self::Keyword(_) => AtomKind::Keyword,
9844 Self::Str(_) => AtomKind::Str,
9845 Self::Int(_) => AtomKind::Int,
9846 Self::Float(_) => AtomKind::Float,
9847 Self::Bool(_) => AtomKind::Bool,
9848 }
9849 }
9850
9851 /// Project the atomic payload to its canonical `&'static str`
9852 /// diagnostic label — `"symbol"` for [`Self::Symbol`], `"keyword"`
9853 /// for [`Self::Keyword`], `"string"` for [`Self::Str`], `"int"` for
9854 /// [`Self::Int`], `"float"` for [`Self::Float`], `"bool"` for
9855 /// [`Self::Bool`]. The outer-`Atom` peer on the [`Atom`] algebra of
9856 /// [`AtomKind::label`] (the marker-level label projection on the
9857 /// closed-set atomic-kind algebra) and [`crate::ast::Sexp::type_name`]
9858 /// (the outer-value label projection on the [`crate::ast::Sexp`]
9859 /// algebra composed through [`crate::ast::Sexp::shape`] +
9860 /// [`crate::error::SexpShape::label`]). Every label is byte-for-byte
9861 /// identical to the corresponding [`crate::error::SexpShape`] variant's
9862 /// label — the AtomKind ⊂ SexpShape label-vocabulary containment
9863 /// established by [`AtomKind::label`]'s composition through
9864 /// [`AtomKind::sexp_shape`] surfaces at the outer-`Atom` layer through
9865 /// this projection.
9866 ///
9867 /// Composition law: `atom.label() == atom.kind().label() ==
9868 /// atom.kind().sexp_shape().label()` for every `atom: &Atom`. The
9869 /// body composes [`Self::kind`] (the typed projection lifting each
9870 /// [`Atom`] variant into its peer [`AtomKind`] marker) with
9871 /// [`AtomKind::label`] (the canonical `&'static str` projection on the
9872 /// closed-set atomic-payload algebra), so the six atomic-arm labels
9873 /// live at ONE canonical site ([`crate::error::SexpShape::label`]'s
9874 /// atomic arms, via [`AtomKind::label`]'s composition through
9875 /// [`AtomKind::sexp_shape`]) rather than at TWO
9876 /// ([`crate::error::SexpShape::label`] AND a parallel six-arm match
9877 /// on the outer [`Atom`] algebra, pre-lift). Cross-algebra agreement
9878 /// law: `Sexp::Atom(atom.clone()).type_name() == atom.label()` for
9879 /// every `atom: Atom` — the outer-[`crate::ast::Sexp`] label
9880 /// projection at the atomic-payload arms routes through
9881 /// [`crate::ast::Sexp::shape`]'s
9882 /// `Self::Atom(a) => a.kind().sexp_shape()` arm which composes with
9883 /// [`crate::error::SexpShape::label`] byte-for-byte with this
9884 /// projection's `self.kind().label()` composition, so the (outer
9885 /// `Sexp` label, outer `Atom` label) agreement is a TYPED CONSEQUENCE
9886 /// of the two typed compositions rather than literal discipline at
9887 /// two sites.
9888 ///
9889 /// Sibling-shape lift to [`Self::kind`] (the closed-set atomic-kind
9890 /// projection): where `kind()` carries the typed [`AtomKind`] marker
9891 /// on the [`Atom`] algebra, `label()` carries the `&'static str`
9892 /// literal the rendered diagnostic surface wants (still derived from
9893 /// the typed marker, but flattened through [`AtomKind::label`] for
9894 /// substring-grep callers, future
9895 /// [`crate::error::LispError::TypeMismatch`] `got` slots keyed on an
9896 /// atomic witness before the outer [`crate::ast::Sexp`] wrap, and
9897 /// future LSP hover / REPL completion / audit-trail metric surfaces
9898 /// that hold an [`Atom`] value directly rather than a wrapped
9899 /// [`crate::ast::Sexp::Atom`]). The `&'static str` lifetime is
9900 /// load-bearing: the composition allocates nothing at runtime
9901 /// ([`Self::kind`] returns a `Copy` value and [`AtomKind::label`]
9902 /// yields `&'static str`).
9903 ///
9904 /// Pre-lift the (Atom variant, `&'static str` diagnostic label)
9905 /// pairing had no typed projection on the outer-[`Atom`] algebra —
9906 /// a consumer with a typed [`Atom`] in hand (a hand-authored
9907 /// [`Atom`] value at a test-harness diagnostic, a future
9908 /// [`crate::domain`] typed-kwarg gate that rejects on an atomic
9909 /// witness before the outer [`crate::ast::Sexp`] wrap, a future LSP
9910 /// hover surface that emits an atomic-payload identity without an
9911 /// enclosing [`crate::ast::Sexp::Atom`] wrap, a future audit-trail
9912 /// metric keyed on the observed atomic kind) wanting the canonical
9913 /// diagnostic label had to spell the two-step composition
9914 /// `atom.kind().label()` at every callsite, OR go through
9915 /// [`crate::ast::Sexp::Atom(atom.clone()).type_name()`] which wraps
9916 /// and unwraps for no runtime purpose. Post-lift the composition
9917 /// binds at ONE typed-algebra method on the outer [`Atom`] value-
9918 /// carrier — the SIXTH consumer of the outer-[`Atom`] projection
9919 /// surface (sibling of [`Self::kind`], [`Self::to_json`],
9920 /// `Atom::to_iac_forge_sexpr` (removed), [`Self::from_lexeme`], and the six
9921 /// per-variant soft-projection methods [`Self::as_symbol`] /
9922 /// [`Self::as_keyword`] / [`Self::as_string`] / [`Self::as_int`] /
9923 /// [`Self::as_float`] / [`Self::as_bool`] + the composite
9924 /// [`Self::as_symbol_or_string`]).
9925 ///
9926 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (Atom
9927 /// variant, `&'static str` diagnostic label) pairing becomes a TYPE
9928 /// projection on the outer-[`Atom`] algebra rather than a per-
9929 /// callsite `.kind().label()` two-step OR a wrap-through-Sexp
9930 /// [`crate::ast::Sexp::Atom(atom.clone()).type_name()`] round-trip.
9931 /// A typo or swap at the outer-`Atom` label site is no longer a
9932 /// runtime label drift but a compile error against the typed
9933 /// composition — the [`Atom`] ↔ [`AtomKind`] ↔ label chain is
9934 /// rustc-enforced end-to-end. THEORY.md §II.1 invariant 2 — free
9935 /// middle; the outer-[`Atom`] diagnostic-label projection now binds
9936 /// at ONE site on the outer-`Atom` algebra, composing through the
9937 /// pre-existing marker-level label projection ([`AtomKind::label`])
9938 /// rather than duplicating the six-arm match. THEORY.md §VI.1 —
9939 /// generation over composition; the outer-`Atom` label projection is
9940 /// the missing algebra layer between the outer [`Atom`] value-carrier
9941 /// and the pre-existing [`AtomKind`] marker-level label projection —
9942 /// the three pre-existing typed layers ([`Atom`] → [`AtomKind`] →
9943 /// [`crate::error::SexpShape`] → `&'static str`) become a full
9944 /// four-layer typed composition through ONE new named projection on
9945 /// the outer value-carrier.
9946 ///
9947 /// Frontier inspiration: MLIR's `mlir::Attribute::getAbstractAttribute()
9948 /// .getName()` typed projection composed with the attribute-kind's
9949 /// typed string identity — narrowing an attribute-carrier value
9950 /// through its typed kind identity yields the canonical diagnostic
9951 /// string identity in ONE typed composition on the outer attribute
9952 /// algebra. Translated through the substrate's outer-[`Atom`]
9953 /// value-carrier algebra, `atom.kind().label()` closes the (outer
9954 /// value, canonical diagnostic label) pairing at ONE typed projection
9955 /// on the value-carrier algebra composed through the marker-level
9956 /// diagnostic-label face. Racket's `(syntax-kind stx)` composed with
9957 /// `(kind-label kind)` on the datum-kind taxonomy — the typed
9958 /// diagnostic label emerges from a two-hop composition on the outer
9959 /// datum-carrier through the typed kind identity. `Atom::label` is
9960 /// the Rust-typed peer on the closed-set outer-[`Atom`] algebra with
9961 /// [`AtomKind`] standing in for Racket's datum-kind taxonomy.
9962 #[must_use]
9963 pub fn label(&self) -> &'static str {
9964 self.kind().label()
9965 }
9966
9967 /// Project the atomic value into its outer-shape [`SexpShape`]
9968 /// variant — `Symbol(_) → SexpShape::Symbol`,
9969 /// `Keyword(_) → SexpShape::Keyword`, `Str(_) → SexpShape::String`,
9970 /// `Int(_) → SexpShape::Int`, `Float(_) → SexpShape::Float`,
9971 /// `Bool(_) → SexpShape::Bool`. The outer-value peer of
9972 /// [`AtomKind::sexp_shape`] one algebra layer down and of
9973 /// [`Sexp::shape`] one algebra layer up. Body composes through
9974 /// `self.kind().sexp_shape()` — routing through [`Self::kind`]
9975 /// (the typed 6-arm outer-value → marker projection) then
9976 /// [`AtomKind::sexp_shape`] (the canonical 6-of-12 atomic-payload
9977 /// carving of [`SexpShape`]) so the (Atom variant, SexpShape
9978 /// variant) pairing lives at ONE canonical site
9979 /// ([`AtomKind::sexp_shape`]'s six match arms in `ast.rs`) rather
9980 /// than at six byte-identical inline arms across consumers.
9981 ///
9982 /// Same composition-through-carving-marker posture as [`Self::label`]
9983 /// (`self.kind().label()`) one vocabulary axis over on the
9984 /// outer-`Atom` algebra: [`Self::label`] closes the diagnostic-label
9985 /// axis, this method closes the outer-shape-projection axis, and
9986 /// both compose through the SAME typed marker layer
9987 /// ([`Self::kind`] into [`AtomKind`]) into the outer-shape's
9988 /// per-axis canonical site. The two methods now paint the
9989 /// outer-`Atom` value with typed projections onto BOTH the
9990 /// diagnostic-label vocabulary AND the outer-shape closed-set —
9991 /// the pair mirrors how [`Sexp::type_name`] and [`Sexp::shape`]
9992 /// paint the outer-`Sexp` value one algebra layer up.
9993 ///
9994 /// Composition law: `atom.sexp_shape() == atom.kind().sexp_shape()`
9995 /// for every `atom: &Atom`. Pinned by
9996 /// `atom_sexp_shape_composes_through_kind_sexp_shape_for_every_variant`
9997 /// across a representative payload sweep (including NaN via
9998 /// `f64::to_bits` round-trip on the Float arm, matching
9999 /// [`Hash for Atom`]'s posture; both empty and non-empty
10000 /// String/Symbol/Keyword arms; `i64::{MIN, MAX}` on the Int arm;
10001 /// both Bool arms). Sibling of
10002 /// `atom_label_composes_through_kind_label_for_every_variant` one
10003 /// vocabulary axis over.
10004 ///
10005 /// Cross-algebra agreement law: for every `atom: &Atom`,
10006 /// `atom.sexp_shape() == Sexp::Atom(atom.clone()).shape()`. The
10007 /// outer-`Atom` shape projection routes into the SAME canonical
10008 /// site the outer-`Sexp` [`Sexp::shape`] projection lands on for
10009 /// every atomic-payload arm — pinned by
10010 /// `atom_sexp_shape_agrees_with_sexp_shape_at_every_atom_arm` via
10011 /// byte-equality on the `SexpShape` variant across all six atomic
10012 /// arms. Sibling of
10013 /// `atom_label_agrees_with_sexp_type_name_at_every_atom_arm` one
10014 /// vocabulary axis over.
10015 ///
10016 /// Round-trip through the outer-shape's soft-projection sibling:
10017 /// `atom.sexp_shape().as_atom_kind() == Some(atom.kind())` for
10018 /// every `atom: &Atom` — the typed embed `Atom → AtomKind →
10019 /// SexpShape` inverts through the soft-projection retraction
10020 /// `SexpShape → AtomKind` exactly on the 6-of-12 atomic-payload
10021 /// image. Pinned by
10022 /// `atom_sexp_shape_round_trips_through_sexp_shape_as_atom_kind`.
10023 ///
10024 /// The `SexpShape` return type (owned; [`SexpShape`] is not `Copy`
10025 /// because its `Unknown(String)` arm carries a `String`) is the
10026 /// outer-shape closed set; consumers that want the diagnostic
10027 /// label render string compose `atom.sexp_shape().label()`, and
10028 /// that composition IS `atom.label()` byte-for-byte by the
10029 /// composition-through-carving-marker posture the two methods
10030 /// share.
10031 ///
10032 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (outer
10033 /// `Atom` variant, `SexpShape` variant) pairing becomes a TYPE
10034 /// projection on the outermost atomic value-carrier algebra
10035 /// composed through the pre-existing marker-level projection,
10036 /// rather than at parallel inline match arms each future consumer
10037 /// of the outer-shape from an outer-`Atom` value has to re-derive.
10038 /// THEORY.md §II.1 invariant 2 — free middle; the outer-`Atom`
10039 /// outer-shape algebra now closes over THREE typed layers (outer
10040 /// `Atom` → [`AtomKind`] → [`SexpShape`]) with rustc-enforced
10041 /// consistency across each — a regression that drifts ONE layer's
10042 /// shape mapping from the others cannot reach the substrate's
10043 /// runtime typed-witness surface, `LispError::TypeMismatch.got`
10044 /// slot, or [`SexpWitness::shape`] projection. THEORY.md §VI.1 —
10045 /// generation over composition; the outer-value projection is the
10046 /// missing algebra layer between the outer `Atom` and the
10047 /// pre-existing marker-level shape projection — the two
10048 /// pre-existing typed layers become a full THREE-layer typed
10049 /// composition through ONE new named projection.
10050 ///
10051 /// Frontier inspiration: MLIR's `mlir::Attribute::getType()`
10052 /// typed projection composed with the attribute-kind's typed
10053 /// outer-type identity — narrowing an attribute-carrier value
10054 /// through its typed kind identity yields the outer-type identity
10055 /// in ONE typed composition on the outer attribute algebra.
10056 /// Translated through the substrate's outer-`Atom` value-carrier
10057 /// algebra, `atom.kind().sexp_shape()` closes the (outer value,
10058 /// outer-shape) pairing at ONE typed projection on the value-
10059 /// carrier algebra composed through the marker-level
10060 /// outer-shape face.
10061 #[must_use]
10062 pub fn sexp_shape(&self) -> SexpShape {
10063 self.kind().sexp_shape()
10064 }
10065
10066 /// Stable, per-variant byte discriminator that paired with the
10067 /// recursive payload hash builds the substrate's [`Hash for Atom`]
10068 /// projection — `0u8` for [`Self::Symbol`], `1u8` for
10069 /// [`Self::Keyword`], `2u8` for [`Self::Str`], `3u8` for
10070 /// [`Self::Int`], `4u8` for [`Self::Float`], `5u8` for
10071 /// [`Self::Bool`]. The outer-value peer on the [`Atom`] algebra of
10072 /// [`AtomKind::hash_discriminator`] (the marker-level cache-key byte
10073 /// projection on the closed-set atomic-kind algebra), sibling of
10074 /// [`Self::label`] and [`Self::sexp_shape`] one vocabulary axis over
10075 /// on the outer-`Atom` algebra. Body composes through
10076 /// `self.kind().hash_discriminator()` — routing through [`Self::kind`]
10077 /// (the typed 6-arm outer-value → marker projection) then
10078 /// [`AtomKind::hash_discriminator`] (the canonical 6-arm cache-key
10079 /// byte projection) so the (Atom variant, byte) pairing lives at
10080 /// ONE canonical site ([`AtomKind::hash_discriminator`]'s six match
10081 /// arms) rather than at six inline `<N>u8.hash(h)` arms at
10082 /// [`Hash for Atom`]'s callsite.
10083 ///
10084 /// Composition law: `atom.hash_discriminator() ==
10085 /// atom.kind().hash_discriminator()` for every `atom: &Atom`. Pinned
10086 /// by `atom_hash_discriminator_composes_through_kind_hash_discriminator_for_every_variant`
10087 /// across a representative payload sweep (including NaN via
10088 /// `f64::to_bits` round-trip on the Float arm, matching
10089 /// [`Hash for Atom`]'s posture; both empty and non-empty
10090 /// String/Symbol/Keyword arms; `i64::{MIN, MAX}` on the Int arm;
10091 /// both Bool arms). Sibling of
10092 /// `atom_label_composes_through_kind_label_for_every_variant` and
10093 /// `atom_sexp_shape_composes_through_kind_sexp_shape_for_every_variant`
10094 /// one vocabulary axis over.
10095 ///
10096 /// Routing-identity law binding it to [`Hash for Atom`]'s post-lift
10097 /// body: for every `atom: &Atom`, hashing via the impl produces
10098 /// byte-identical output to a hand-driven
10099 /// `atom.hash_discriminator().hash(h); <inner-payload-hash>`
10100 /// sequence. Pinned by
10101 /// `hash_for_atom_routes_atom_discriminator_through_atom_hash_discriminator`.
10102 /// Sibling posture to
10103 /// `hash_for_sexp_routes_outer_discriminator_through_sexp_hash_discriminator`
10104 /// one algebra layer up — the two routing pins jointly enforce the
10105 /// outer-value Hash bodies at BOTH algebras stay structurally
10106 /// parallel (`self.hash_discriminator().hash(h); <inner>`).
10107 ///
10108 /// `pub(crate)` because the byte-discriminator surface is an
10109 /// implementation detail of the substrate's [`Hash for Atom`]
10110 /// cache-key contract; exposing it publicly would leak the cache-key
10111 /// shape through the API without enabling any external consumer the
10112 /// public projections ([`Self::kind`], [`Self::label`],
10113 /// [`Self::sexp_shape`]) don't already serve. Same posture as
10114 /// [`AtomKind::hash_discriminator`] one algebra layer down and
10115 /// [`crate::ast::Sexp::hash_discriminator`] one algebra layer up.
10116 ///
10117 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (outer
10118 /// `Atom` variant, cache-key byte) pairing becomes a TYPE projection
10119 /// on the outermost atomic value-carrier algebra composed through the
10120 /// pre-existing marker-level projection, rather than a two-hop
10121 /// composition at the [`Hash for Atom`] callsite. THEORY.md §II.1
10122 /// invariant 2 — free middle; the outer-`Atom` cache-key algebra now
10123 /// closes over THREE typed layers (outer `Atom` → [`AtomKind`] → byte)
10124 /// with rustc-enforced consistency across each — a regression that
10125 /// drifts the [`Hash for Atom`] callsite's byte routing from the
10126 /// canonical [`AtomKind::hash_discriminator`] site cannot reach the
10127 /// substrate's expansion-cache runtime. THEORY.md §VI.1 — generation
10128 /// over composition; the outer-value byte projection is the missing
10129 /// algebra layer between the outer `Atom` and the pre-existing
10130 /// marker-level byte projection — the two pre-existing typed layers
10131 /// become a full THREE-layer typed composition through ONE new named
10132 /// projection on the outer value-carrier, closing the (label,
10133 /// sexp_shape, hash_discriminator) trio on the outer-`Atom` algebra.
10134 ///
10135 /// Frontier inspiration: MLIR's
10136 /// `mlir::Attribute::getAsOpaquePointer()` typed projection composed
10137 /// with the attribute-kind's stable identifier — narrowing an
10138 /// attribute-carrier value through its typed kind identity yields the
10139 /// canonical cache-key identity in ONE typed composition on the outer
10140 /// attribute algebra. Translated through the substrate's
10141 /// outer-[`Atom`] value-carrier algebra,
10142 /// `atom.kind().hash_discriminator()` closes the (outer value, byte
10143 /// cache-key discriminator) pairing at ONE typed projection on the
10144 /// value-carrier algebra composed through the marker-level cache-key
10145 /// face. Racket's `(datum-hash-key datum)` composed with
10146 /// `(kind-hash-tag kind)` on the datum-kind taxonomy — the byte
10147 /// cache-key identity emerges from a two-hop composition on the
10148 /// outer datum-carrier through the typed kind identity;
10149 /// `Atom::hash_discriminator` is the Rust-typed peer on the
10150 /// closed-set outer-[`Atom`] algebra with [`AtomKind`] standing in
10151 /// for Racket's datum-kind taxonomy.
10152 #[must_use]
10153 pub(crate) fn hash_discriminator(&self) -> u8 {
10154 self.kind().hash_discriminator()
10155 }
10156
10157 /// Project the atomic payload to its canonical [`serde_json::Value`]
10158 /// rendering — the typed-algebra peer of [`fmt::Display for Atom`] at
10159 /// the JSON-projection boundary. Lifts six inline atom arms inside
10160 /// [`crate::domain::sexp_to_json`]'s outer match (one
10161 /// `Sexp::Atom(Atom::<variant>(payload)) => JValue::<…>(…)` arm
10162 /// per [`AtomKind`] variant) onto ONE typed-algebra method that
10163 /// every consumer routes through. Sibling-shape lift to the prior
10164 /// `Display for Atom` (the canonical-string rendering surface),
10165 /// `Hash for Atom` (the cache-key bytes surface via
10166 /// [`AtomKind::hash_discriminator`]), and the upcoming
10167 /// `Atom::to_iac_forge_sexpr` (the canonical-SExpr rendering
10168 /// surface, feature-gated `iac-forge`) — every per-`Atom`-variant
10169 /// projection now binds at ONE method on the closed-set algebra
10170 /// rather than at six inline arms inside its consumer.
10171 ///
10172 /// Mapping (preserves the byte-identical pre-lift behavior at the
10173 /// `sexp_to_json` callsite):
10174 /// * [`Self::Symbol`] payload `s` → [`serde_json::Value::String`] of
10175 /// `s` cloned (Symbols are enum discriminants — the JSON
10176 /// deserializer reads them as the string-form variant tag).
10177 /// * [`Self::Keyword`] payload `s` → [`serde_json::Value::String`]
10178 /// of `":{s}"` (Keywords prefix with `:` in their canonical
10179 /// wire-form; `json_to_sexp`'s inverse strips the prefix).
10180 /// * [`Self::Str`] payload `s` → [`serde_json::Value::String`] of
10181 /// `s` cloned.
10182 /// * [`Self::Int`] payload `n` → [`serde_json::Value::Number`] of
10183 /// `n` (lossless via `serde_json::Number::from(i64)`).
10184 /// * [`Self::Float`] payload `n` → [`serde_json::Value::Number`] of
10185 /// `n` IFF `n` is finite (NaN / ±∞ collapse to
10186 /// [`serde_json::Value::Null`]; this is JSON's structural
10187 /// inexpressibility of those f64 values, NOT a substrate
10188 /// choice). The NaN/∞→Null branch is pinned at one test below
10189 /// (`atom_to_json_float_nan_and_infinity_collapse_to_null`).
10190 /// * [`Self::Bool`] payload `b` → [`serde_json::Value::Bool`] of
10191 /// `b`.
10192 ///
10193 /// Bidirectional contract anchored by tests in this module:
10194 /// * `atom_to_json_projects_each_variant_to_canonical_json_value`
10195 /// — sweeps a representative atom of each [`AtomKind`] variant
10196 /// and pins each variant's canonical JValue mapping
10197 /// byte-for-byte against the pre-lift inline rule, so a future
10198 /// regression that drifts ONE arm (e.g. swaps `Symbol`'s
10199 /// mapping to a Number, or drops `Keyword`'s `:` prefix) fails
10200 /// loudly.
10201 /// * `atom_to_json_float_nan_and_infinity_collapse_to_null`
10202 /// — pins the JSON-structural inexpressibility branch at the
10203 /// atom layer directly, so a future Atom-Display-style refactor
10204 /// that bypasses [`serde_json::Number::from_f64`] (e.g. tries
10205 /// to emit `NaN` as the string `"NaN"`) surfaces at the
10206 /// typed-algebra boundary without requiring a Sexp wrap.
10207 /// * `sexp_to_json_atom_arms_route_through_atom_to_json` (in
10208 /// [`crate::domain::tests`]) — pins the lifted boundary:
10209 /// `sexp_to_json(&Sexp::Atom(a.clone())) == Ok(a.to_json())`
10210 /// for every atomic payload variant. Catches a future drift
10211 /// where one surface's per-variant body changes without the
10212 /// other.
10213 ///
10214 /// Theory anchor: THEORY.md §VI.1 — generation over composition;
10215 /// the (Atom variant, canonical JValue rendering) pair lived inline
10216 /// at the `sexp_to_json` site as six byte-identical arms. The lift
10217 /// retires the per-site fan-out onto ONE method on the `Atom`
10218 /// algebra. THEORY.md §II.1 invariant 2 — free middle; the typed-
10219 /// exit JSON projection, the Display-surface rendering, the
10220 /// diagnostic surface, and any future canonical-form surface
10221 /// (e.g. `Atom::to_iac_forge_sexpr`) all route through ONE
10222 /// per-variant projection family rather than per-callsite
10223 /// re-derivation. THEORY.md §V.1 — knowable platform; a future
10224 /// seventh atomic kind (e.g. `Char` for `#\x` reader syntax) lands
10225 /// at one [`AtomKind::ALL`] entry plus one arm here plus one arm
10226 /// per sibling projection — exhaustively checked by the compiler,
10227 /// not by per-consumer convention.
10228 ///
10229 /// Frontier inspiration: MLIR's `mlir::AsmPrinter::printAttribute`
10230 /// — the typed-IR attribute printer dispatches on the closed-set
10231 /// `AttributeKind` so every printer body for a kind lives at ONE
10232 /// implementation site; `Atom::to_json` is the unstructured-Rust
10233 /// peer on the `Atom` algebra for the JSON canonical-form surface
10234 /// (where `Display for Atom` is the Lisp-canonical-form peer and
10235 /// `From<&Sexp> for iac_forge::SExpr` is the canonical-attestation-
10236 /// form peer). Racket's `(syntax->datum stx)` then a serializer
10237 /// over the datum prim — `to_json` is the substrate's serializer
10238 /// at the atomic-payload layer, with the closed-set `AtomKind`
10239 /// standing in for Racket's datum-prim taxonomy.
10240 #[must_use]
10241 pub fn to_json(&self) -> serde_json::Value {
10242 match self {
10243 Self::Symbol(s) => serde_json::Value::String(s.clone()),
10244 // Keyword arm routes through the typed
10245 // [`Self::keyword_qualified`] projection on the atomic-
10246 // payload canonical-rendering axis — the ONE composition
10247 // of [`Self::KEYWORD_MARKER`] with a bare keyword name on
10248 // the [`Atom`] algebra, shared with
10249 // `Atom::to_iac_forge_sexpr` (removed)'s Keyword arm and pinned
10250 // byte-identical to [`fmt::Display for Atom`]'s Keyword
10251 // arm. Pre-lift each of the three sites carried its own
10252 // inline `format!("{}{s}", Self::KEYWORD_MARKER)`
10253 // composition; post-lift the composition lives at ONE
10254 // typed algebra projection.
10255 Self::Keyword(s) => serde_json::Value::String(Self::keyword_qualified(s)),
10256 Self::Str(s) => serde_json::Value::String(s.clone()),
10257 Self::Int(n) => serde_json::Value::Number((*n).into()),
10258 Self::Float(n) => serde_json::Number::from_f64(*n)
10259 .map(serde_json::Value::Number)
10260 .unwrap_or(serde_json::Value::Null),
10261 Self::Bool(b) => serde_json::Value::Bool(*b),
10262 }
10263 }
10264
10265 /// Inverse of [`Self::to_json`] restricted to the JSON `Number`
10266 /// discriminator — the ONE typed inverse projection on the closed-set
10267 /// [`Atom`] algebra that names the (`serde_json::Number` →
10268 /// [`Self::Int`] / [`Self::Float`]) bifurcation. Lifts the pre-lift
10269 /// inline three-arm cascade inside [`crate::ast::Sexp::from_json`]'s
10270 /// `serde_json::Value::Number(n)` outer-match arm — first
10271 /// `n.as_i64()?` sink to [`Self::Int`] then `n.as_f64()?` sink to
10272 /// [`Self::Float`] then a `Self::Int(0)` typed floor for the
10273 /// structural-impossibility residual — onto ONE typed projection on
10274 /// the [`Atom`] algebra so the paired FORWARD ([`Self::to_json`]'s
10275 /// [`Self::Int`] / [`Self::Float`] arms) and INVERSE (this method)
10276 /// numeric-axis projections live at ONE algebra layer.
10277 ///
10278 /// Mapping (byte-identical to the pre-lift cascade in
10279 /// [`crate::ast::Sexp::from_json`]):
10280 ///
10281 /// | `serde_json::Number` shape | result |
10282 /// | ---------------------------------- | ------------------ |
10283 /// | `n.as_i64() == Some(i)` | [`Self::Int`]`(i)` |
10284 /// | `n.as_i64() == None`, `.as_f64() == Some(f)` | [`Self::Float`]`(f)` |
10285 /// | `n.as_i64() == None`, `.as_f64() == None` | [`Self::Int`]`(0)` — typed floor |
10286 ///
10287 /// Every `serde_json::Number` today is either i64-fitting,
10288 /// u64-fitting (projected through f64), or f64-fitting — the
10289 /// `Int(0)` residual arm is a static-invariant statement that
10290 /// `serde_json::Number`'s closed-set discriminator excludes the
10291 /// "neither i64 nor f64" case in practice; the typed floor stays
10292 /// explicit so a future `serde_json` extension does NOT silently
10293 /// misroute through an unreachable-panic. The `Self::int(0)`
10294 /// composition in the pre-lift code equalled `Self::Atom(Atom::Int(0))`
10295 /// via the `Sexp::int` sugar; post-lift the [`Atom`] algebra owns
10296 /// the typed floor at the atomic layer directly.
10297 ///
10298 /// Round-trip laws (paired with [`Self::to_json`]'s numeric arms):
10299 ///
10300 /// * For every `i: i64`, `Atom::from_json_number(&i.into()) ==
10301 /// Atom::Int(i)` — the [`Self::Int`] → `JValue::Number` →
10302 /// [`Self::Int`] round-trip is byte-identical.
10303 /// * For every finite non-integer-valued `f: f64`,
10304 /// `Atom::from_json_number(&serde_json::Number::from_f64(f)
10305 /// .unwrap()) == Atom::Float(f)` — the [`Self::Float`] →
10306 /// `JValue::Number` → [`Self::Float`] round-trip is byte-identical
10307 /// for `f64` values that don't overlap the i64-fitting subset of
10308 /// [`serde_json::Number`]'s discriminator (i.e. non-integer-valued
10309 /// finite floats; integer-valued floats round-trip through the
10310 /// [`Self::Int`] arm by `as_i64`'s eager check).
10311 /// * Non-finite floats ([`f64::NAN`], [`f64::INFINITY`],
10312 /// [`f64::NEG_INFINITY`]) collapse to [`serde_json::Value::Null`]
10313 /// in [`Self::to_json`] — they NEVER produce a [`serde_json::Number`]
10314 /// value, so the round-trip law does not apply to them. This
10315 /// asymmetry is JSON's structural inexpressibility of non-finite
10316 /// floats (pinned at
10317 /// `atom_to_json_float_nan_and_infinity_collapse_to_null`), not a
10318 /// substrate choice.
10319 ///
10320 /// ONE consumer entrypoint the substrate binds against: the outer
10321 /// [`crate::ast::Sexp::from_json`]'s `serde_json::Value::Number(n)`
10322 /// arm was pre-lift a hand-rolled three-branch cascade
10323 /// (`if let Some(i) = n.as_i64() { Self::int(i) } else if let
10324 /// Some(f) = n.as_f64() { Self::float(f) } else { Self::int(0) }`);
10325 /// post-lift the outer arm collapses to
10326 /// `Self::Atom(Atom::from_json_number(n))` — the ONE typed inverse
10327 /// on the [`Atom`] algebra owns the numeric-axis bifurcation, the
10328 /// outer arm delegates. A regression that drifts the outer arm
10329 /// (e.g. re-inlines the bifurcation and swaps the `as_i64`/`as_f64`
10330 /// order so `42.0` sinks to [`Self::Float`] instead of
10331 /// [`Self::Int`]) becomes structurally unreachable — there is
10332 /// exactly ONE numeric decode both directions of the round-trip
10333 /// consume.
10334 ///
10335 /// Sibling-lift posture: this method mirrors [`Self::from_lexeme`]
10336 /// on the typed-entry classification axis — that method decodes a
10337 /// bare-atom lexeme (`&str`) into the six-way [`Atom`] taxonomy;
10338 /// THIS method decodes a JSON `Number` into the two-way (
10339 /// [`Self::Int`] / [`Self::Float`]) numeric subtaxonomy on the SAME
10340 /// algebra. Together with the seven typed-EXIT projections on
10341 /// [`Atom`] ([`fmt::Display for Atom`], [`Self::to_json`],
10342 /// `Atom::to_iac_forge_sexpr` (removed), [`Self::label`],
10343 /// [`Self::sexp_shape`], [`Self::hash_discriminator`],
10344 /// [`Self::bool_literal`]) and the two typed-ENTRY projections on
10345 /// [`Atom`] ([`Self::from_lexeme`], THIS method) the algebra's
10346 /// canonical-form bidirectional sweep is complete across every
10347 /// production-site rendering + parsing surface — every consumer's
10348 /// (`Atom` variant, canonical rendering) OR (canonical source,
10349 /// `Atom` variant) pairing binds at ONE method per direction per
10350 /// surface on the closed-set algebra rather than at inline arms
10351 /// scattered across per-consumer sites.
10352 ///
10353 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
10354 /// (JSON `Number` → typed [`Atom`] numeric variant) projection IS
10355 /// the typed-entry gate on the JSON numeric axis. Placing the
10356 /// paired forward (`Atom::to_json`'s [`Self::Int`] / [`Self::Float`]
10357 /// arms) AND inverse (THIS method) on the [`Atom`] algebra closes
10358 /// the round-trip closure at ONE algebra layer — future numeric
10359 /// taxonomy extensions (e.g. `u64`-fitting arm for the
10360 /// `serde-preserve-order` feature's `arbitrary_precision` mode, a
10361 /// [`Self::Bigint`] variant for arbitrary-precision integers, a
10362 /// [`Self::Rational`] variant for [`num_rational::Rational64`])
10363 /// extend the algebra ONCE at [`Self::to_json`]'s match AND ONCE at
10364 /// this method's cascade — both edits land on the SAME algebra
10365 /// rather than across the `Atom` module AND the `Sexp` module
10366 /// boundary. THEORY.md §V.1 — knowable platform; the numeric
10367 /// inverse projection becomes a NAMED primitive on the substrate's
10368 /// [`Atom`] algebra rather than an inline three-arm cascade at the
10369 /// `Sexp::from_json` consumer. THEORY.md §II.1 invariant 5 —
10370 /// composition preserves proofs; the round-trip law
10371 /// `Atom::from_json_number(&Atom::Int(n).to_json_as_number()) ==
10372 /// Atom::Int(n)` (pinned at
10373 /// `atom_from_json_number_round_trips_atom_to_json_int_arm`) is a
10374 /// coherence proof BETWEEN the paired projections on ONE algebra —
10375 /// a regression that drifts either side surfaces at the pin
10376 /// rather than as a silent Sexp ↔ JSON round-trip drift.
10377 ///
10378 /// Frontier inspiration: MLIR's `mlir::parseAttribute(str, ctx)` —
10379 /// the typed-IR parser inverse of `printAttribute` lives on the
10380 /// SAME `Attribute` algebra as its printer dual; the substrate's
10381 /// [`Self::from_json_number`] is the unstructured-Rust peer on the
10382 /// [`Atom`] algebra for the JSON-numeric canonical-form inverse,
10383 /// paired with [`Self::to_json`]'s numeric arms as the closed
10384 /// numeric-axis round-trip. Racket's
10385 /// `(json->racket (racket->json v))` numeric identity — the
10386 /// round-trip law that a JSON-projected numeric datum recovers to
10387 /// its source Racket numeric primitive; THIS method's round-trip
10388 /// pins are the Rust-typed peer on the [`Atom`] algebra with the
10389 /// closed-set numeric taxonomy ([`Self::Int`] / [`Self::Float`])
10390 /// standing in for Racket's numeric tower.
10391 #[must_use]
10392 pub fn from_json_number(n: &serde_json::Number) -> Self {
10393 if let Some(i) = n.as_i64() {
10394 Self::Int(i)
10395 } else if let Some(f) = n.as_f64() {
10396 Self::Float(f)
10397 } else {
10398 Self::Int(0)
10399 }
10400 }
10401
10402 // REMOVED (consolidation phase 2 step 8): `Atom::to_iac_forge_sexpr`
10403 // and the `crate::interop` module it mirrored. Both were gated on a
10404 // `iac-forge` Cargo feature this crate never declared, against an
10405 // `iac_forge` dependency it never had — unconditionally dead, and
10406 // uncompilable had the gate ever opened. Re-introducing the bridge
10407 // needs a crates.io-published `iac-forge` first; see
10408 // TATARA-LISP-CONSOLIDATION.md open question 7.
10409 //
10410 // NOT part of this deletion: the `iac_forge_tag` / `IAC_FORGE_TAGS`
10411 // / `from_iac_forge_tag` family on `QuoteForm`, `SexpShape`,
10412 // `UnquoteForm` and `Sexp`. Those are pure `&'static str` closed-set
10413 // projections with no dependency on the `iac-forge` crate; they stay
10414 // live and tested.
10415
10416 /// Classify a bare reader-token lexeme into its typed [`Atom`]
10417 /// variant — the typed-ENTRY mirror of the three typed-EXIT
10418 /// projections on the [`Atom`] algebra ([`fmt::Display for Atom`],
10419 /// [`Self::to_json`], `Atom::to_iac_forge_sexpr` (removed)). Lifts the
10420 /// five-statement classification cascade that lived inline at the
10421 /// reader's private `atom_from_str` helper onto ONE typed-algebra
10422 /// method on the closed-set [`Atom`] algebra; the reader's
10423 /// `Token::Atom(s)` arm collapses to `Sexp::Atom(Atom::from_lexeme(&s))`.
10424 /// Completes the bidirectional sweep across the four production-site
10425 /// per-`Atom`-variant projection shapes (typed-exit Display, JSON,
10426 /// iac-forge canonical attestation, AND now typed-entry
10427 /// classification) onto the algebra.
10428 ///
10429 /// Classification rule (byte-identical to the pre-lift reader
10430 /// `atom_from_str` cascade):
10431 /// 1. `"#t"`/`"#f"` → [`Self::Bool`] — the Scheme bool spellings;
10432 /// bare `true`/`false` re-read as [`Self::Symbol`] (the
10433 /// CLAUDE.md "Lisp bools" warning — every `:values-overlay`
10434 /// payload depends on this for `Value::Bool` round-trip).
10435 /// 2. `:foo` (leading `:`) → [`Self::Keyword`] — strips the `:`
10436 /// so the inverse [`fmt::Display`] rule (`Keyword(s) →
10437 /// ":{s}"`) round-trips.
10438 /// 3. `i64::from_str` succeeds → [`Self::Int`] — load-bearing
10439 /// ORDERING: tried BEFORE `f64` so `"1"` classifies as
10440 /// [`Self::Int`]`(1)`, NOT [`Self::Float`]`(1.0)`. Typed-int-
10441 /// vs-typed-float distinction at the Display→read boundary
10442 /// is the dual of `fmt_float`'s `.0`-suffix discipline.
10443 /// 4. `f64::from_str` succeeds → [`Self::Float`].
10444 /// 5. Default → [`Self::Symbol`].
10445 ///
10446 /// Composition laws (pinned by tests below):
10447 /// * `Atom::from_lexeme(&a.to_string()) == a` for every variant
10448 /// EXCEPT [`Self::Str`] (Display renders Str with quote marks
10449 /// — strings take the reader's `"`-quoted tokenizer branch,
10450 /// NOT the bare-atom branch).
10451 /// * `read(s)` for every canonical bare-atom source lexeme
10452 /// equals `vec![Sexp::Atom(Atom::from_lexeme(s))]` (pinned by
10453 /// `reader_atom_token_arm_routes_through_atom_from_lexeme_for_
10454 /// every_kind` in [`crate::reader::tests`]).
10455 ///
10456 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry;
10457 /// `atom_from_str` was the typed-entry gate as a free function in
10458 /// `reader.rs`, outside the typed `Atom` algebra. Naming it on the
10459 /// algebra brings the typed-entry side INTO the same closed-set
10460 /// match family the typed-exit projections live on, so a future
10461 /// seventh atomic kind (e.g. `Char` for `#\x` reader syntax) lands
10462 /// at ONE [`AtomKind::ALL`] entry plus ONE arm here plus ONE arm
10463 /// per typed-exit projection — exhaustively checked by rustc
10464 /// across all FOUR per-variant projection families. THEORY.md
10465 /// §II.1 invariant 2 — free middle; FOUR consumers (typed-entry
10466 /// classification, Display rendering, JSON projection, canonical-
10467 /// attestation-form projection) now route through ONE
10468 /// per-`Atom`-variant projection family on the closed-set algebra.
10469 /// THEORY.md §VI.1 — generation over composition; this lift
10470 /// completes the bidirectional sweep across the four production
10471 /// surfaces the prior runs in this series named.
10472 ///
10473 /// Frontier inspiration: Racket's `(read-syntax …)` dispatches a
10474 /// bare-atom lexeme through a closed-set classifier keyed on
10475 /// prefix + parse-as-numeric cascade; `Atom::from_lexeme` is the
10476 /// substrate's typed-Rust peer, with [`AtomKind`] standing in for
10477 /// Racket's datum-prim taxonomy. MLIR's
10478 /// `mlir::AsmParser::parseAttribute` dispatches on the closed-set
10479 /// `AttributeKind` so every parser body for a kind lives at ONE
10480 /// implementation site; `Atom::from_lexeme` is the
10481 /// unstructured-Rust peer on the [`Atom`] algebra for the
10482 /// typed-entry classification surface.
10483 #[must_use]
10484 pub fn from_lexeme(s: &str) -> Self {
10485 if s == Self::bool_literal(true) {
10486 return Self::Bool(true);
10487 }
10488 if s == Self::bool_literal(false) {
10489 return Self::Bool(false);
10490 }
10491 if let Some(rest) = s.strip_prefix(Self::KEYWORD_MARKER) {
10492 return Self::Keyword(rest.to_owned());
10493 }
10494 if let Ok(n) = s.parse::<i64>() {
10495 return Self::Int(n);
10496 }
10497 if let Ok(n) = s.parse::<f64>() {
10498 return Self::Float(n);
10499 }
10500 Self::Symbol(s.to_owned())
10501 }
10502
10503 /// Soft projection onto the [`Self::Symbol`] payload — `Some(&str)`
10504 /// iff this is a [`Self::Symbol`] variant, `None` for every other
10505 /// atomic kind (`Keyword`, `Str`, `Int`, `Float`, `Bool`).
10506 ///
10507 /// FIRST of the six per-variant soft-projection methods on the typed
10508 /// [`Atom`] algebra — the typed-EXIT *soft*-projection peer of the
10509 /// typed-EXIT canonical-form projections ([`fmt::Display for Atom`],
10510 /// [`Self::to_json`], `Atom::to_iac_forge_sexpr` (removed)) and the typed-ENTRY
10511 /// classifier ([`Self::from_lexeme`]). Where the canonical-form trio
10512 /// projects the atomic payload to a *rendered* canonical surface
10513 /// (string / JSON / iac-forge SExpr) and the classifier projects a
10514 /// lexeme to the typed `Atom`, this method projects the typed `Atom`
10515 /// to its inner payload — the soft-decomposition face of the closed
10516 /// set, completing the algebra surface across BOTH bidirectional axes
10517 /// (canonical-form rendering + classification on the typed-ENTRY/
10518 /// typed-EXIT axis; soft decomposition on the typed-EXIT side at the
10519 /// payload axis).
10520 ///
10521 /// Sibling soft-projection peer of [`Sexp::as_quote_form`]: where
10522 /// `as_quote_form` soft-decomposes the four homoiconic prefix
10523 /// wrappers into `Option<(QuoteForm, &Sexp)>`, this method (and its
10524 /// five `as_*` siblings on [`Atom`]) soft-decompose the six atomic
10525 /// payloads into `Option<&str>` / `Option<i64>` / `Option<f64>` /
10526 /// `Option<bool>` — there is no inner-sexp body to surface, so the
10527 /// projection's return type is just the payload. The
10528 /// `Sexp::as_symbol` consumer at the `Sexp` algebra layer composes
10529 /// this projection with [`Sexp::as_atom`] (the structural lift to
10530 /// the inner [`Atom`]) — `Sexp::as_symbol(self) ==
10531 /// self.as_atom().and_then(Atom::as_symbol)` — so the per-`Atom`-
10532 /// variant soft-projection binds at ONE method on the typed algebra
10533 /// rather than at six inline `Self::Atom(Atom::X(s)) => Some(s)` arms
10534 /// inside the `Sexp` consumer.
10535 ///
10536 /// Lifts the inline `Self::Atom(Atom::Symbol(s)) => Some(s)` arm at
10537 /// [`Sexp::as_symbol`]'s match body onto ONE typed-algebra projection
10538 /// the `Sexp` consumer routes through via the structural lift
10539 /// [`Sexp::as_atom`]. Sibling-shape lift to the typed-EXIT
10540 /// canonical-form projections (`Display for Atom`, `Atom::to_json`,
10541 /// `Atom::to_iac_forge_sexpr`) and the typed-ENTRY classifier
10542 /// (`Atom::from_lexeme`) — every per-`Atom`-variant projection
10543 /// across both the rendering surfaces AND the soft-decomposition
10544 /// surface now binds at ONE method on the closed-set algebra rather
10545 /// than at inline arms inside its consumer.
10546 ///
10547 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
10548 /// (Atom variant, downstream-consumer-payload) pairing now binds at
10549 /// ONE typed projection per consumer surface (six canonical-form
10550 /// surfaces — `Display`, JSON, iac-forge, plus the soft-projection
10551 /// FAMILY this method opens), regardless of which consumer reaches
10552 /// in. THEORY.md §VI.1 — generation over composition; the six inline
10553 /// `Self::Atom(Atom::X(s)) => Some(_)` arms at `Sexp::as_X` sites
10554 /// (well past the ≥2 PRIME-DIRECTIVE trigger once the structural
10555 /// shape is named) collapse onto the closed-set `Atom` algebra so a
10556 /// future seventh atomic kind (e.g. `Char` for `#\x` reader syntax,
10557 /// `Bigint` for arbitrary-precision integers) extends `Atom::ALL` +
10558 /// the per-variant soft-projection method ONCE and rustc enforces
10559 /// matching across every consumer through the closed-set match.
10560 /// THEORY.md §V.1 — knowable platform; the (Atom variant, payload)
10561 /// pairing becomes a TYPE projection on the substrate algebra
10562 /// rather than six inline arms at the `Sexp` consumer. A typo or
10563 /// swap at the soft-projection site is no longer a runtime drift
10564 /// but a compile error against the typed projection.
10565 ///
10566 /// Frontier inspiration: Racket's `(symbol? v)` / `(symbol->string
10567 /// v)` pair — the typed-predicate + typed-projection pair at the
10568 /// atomic-payload layer; this method (and its five `as_*` siblings)
10569 /// is the substrate's typed soft-projection peer on the closed-set
10570 /// `Atom` algebra, with `Option<&str>` standing in for the
10571 /// predicate-AND-projection pair Racket carries as two functions.
10572 /// MLIR's `mlir::dyn_cast<SymbolAttribute>(attr)` — the typed-IR
10573 /// soft-downcast onto a closed-set attribute family; `Atom::as_symbol`
10574 /// is the unstructured-Rust peer on the `Atom` algebra for the
10575 /// soft-projection face, with the closed-set `AtomKind` standing in
10576 /// for MLIR's `AttributeKind` taxonomy.
10577 #[must_use]
10578 pub fn as_symbol(&self) -> Option<&str> {
10579 match self {
10580 Self::Symbol(s) => Some(s),
10581 _ => None,
10582 }
10583 }
10584
10585 /// Soft projection onto the [`Self::Keyword`] payload — `Some(&str)`
10586 /// iff this is a [`Self::Keyword`] variant, `None` for every other
10587 /// atomic kind. The returned `&str` is the payload AFTER the `:`
10588 /// prefix has been stripped at the typed-ENTRY classifier
10589 /// boundary ([`Self::from_lexeme`] strips `:` when constructing a
10590 /// `Keyword`; this projection surfaces the bare identifier).
10591 /// SECOND of the six per-variant soft-projection methods on the
10592 /// typed [`Atom`] algebra — see [`Self::as_symbol`] for the
10593 /// algebra-level docstring.
10594 #[must_use]
10595 pub fn as_keyword(&self) -> Option<&str> {
10596 match self {
10597 Self::Keyword(s) => Some(s),
10598 _ => None,
10599 }
10600 }
10601
10602 /// Soft projection onto the [`Self::Str`] payload — `Some(&str)` iff
10603 /// this is a [`Self::Str`] variant (the typed `"…"`-quoted string
10604 /// literal payload at the reader's [`crate::reader::Token::Str`]
10605 /// branch), `None` for every other atomic kind. THIRD of the six
10606 /// per-variant soft-projection methods — named `as_string` at the
10607 /// `Sexp` consumer for consumer-vocabulary continuity with the
10608 /// pre-lift `Sexp::as_string` projection (the typed payload variant
10609 /// is `Str` for `String` shortening; the consumer-facing method
10610 /// keeps `string` for symmetry with the `ExpectedKwargShape::String`
10611 /// label and the [`SexpShape::String`] outer-shape marker).
10612 #[must_use]
10613 pub fn as_string(&self) -> Option<&str> {
10614 match self {
10615 Self::Str(s) => Some(s),
10616 _ => None,
10617 }
10618 }
10619
10620 /// Soft projection onto the [`Self::Int`] payload — `Some(i64)` iff
10621 /// this is a [`Self::Int`] variant, `None` for every other atomic
10622 /// kind. FOURTH of the six per-variant soft-projection methods.
10623 /// The `i64` is returned by value (the payload is `Copy`); contrast
10624 /// with [`Self::as_symbol`] / [`Self::as_keyword`] / [`Self::as_string`]
10625 /// which borrow the underlying `String` payload as `&str` because
10626 /// `String` is not `Copy`.
10627 ///
10628 /// Strict typed identity: this method projects `Atom::Int(n)` to
10629 /// `Some(n)` only. The `Sexp::as_float` consumer at the `Sexp`
10630 /// algebra layer widens `Int` to `Float` (`Atom::Int(n) → Some(n as
10631 /// f64)`) for caller convenience at the numeric-kwarg boundary; the
10632 /// `Atom`-level projection here stays strict so the typed-identity
10633 /// distinction `Int(1)` vs `Float(1.0)` (the load-bearing typed
10634 /// identity at the [`Self::from_lexeme`] ⇄ Display round-trip
10635 /// boundary, dual of [`fmt_float`]'s `.0`-suffix discipline) is
10636 /// preserved at the algebra layer. The widening lives at the
10637 /// `Sexp::as_float` consumer (`a.as_float().or_else(|| a.as_int()
10638 /// .map(|n| n as f64))`) where the convenience is wanted, not at
10639 /// the algebra-level projection where the typed identity is
10640 /// load-bearing.
10641 #[must_use]
10642 pub fn as_int(&self) -> Option<i64> {
10643 match self {
10644 Self::Int(n) => Some(*n),
10645 _ => None,
10646 }
10647 }
10648
10649 /// Soft projection onto the [`Self::Float`] payload — `Some(f64)`
10650 /// iff this is a [`Self::Float`] variant, `None` for every other
10651 /// atomic kind. FIFTH of the six per-variant soft-projection
10652 /// methods.
10653 ///
10654 /// Strict typed identity: `Atom::Int(n)` does NOT project through
10655 /// this method (it stays `None`). The [`Sexp::as_float`] consumer
10656 /// widens `Int` to `Float` at the `Sexp` algebra layer for caller
10657 /// convenience; this algebra-level projection stays strict. See
10658 /// [`Self::as_int`]'s docstring for the typed-identity contract.
10659 #[must_use]
10660 pub fn as_float(&self) -> Option<f64> {
10661 match self {
10662 Self::Float(n) => Some(*n),
10663 _ => None,
10664 }
10665 }
10666
10667 /// Soft projection onto the [`Self::Bool`] payload — `Some(bool)`
10668 /// iff this is a [`Self::Bool`] variant, `None` for every other
10669 /// atomic kind. SIXTH and LAST of the six per-variant soft-projection
10670 /// methods on the typed [`Atom`] algebra; together with the five
10671 /// siblings ([`Self::as_symbol`], [`Self::as_keyword`],
10672 /// [`Self::as_string`], [`Self::as_int`], [`Self::as_float`]) the
10673 /// per-`Atom`-variant soft-projection family is complete across all
10674 /// six closed-set arms. The CLAUDE.md-pinned `"#t"` / `"#f"` Scheme
10675 /// bool spellings the reader's typed-ENTRY classifier
10676 /// [`Self::from_lexeme`] dispatches on bind the lexeme → typed
10677 /// [`Self::Bool`] direction; this method binds the typed
10678 /// [`Self::Bool`] → payload direction at the soft-decomposition
10679 /// face.
10680 #[must_use]
10681 pub fn as_bool(&self) -> Option<bool> {
10682 match self {
10683 Self::Bool(b) => Some(*b),
10684 _ => None,
10685 }
10686 }
10687
10688 /// Soft projection onto the *symbol-or-string* union — `Some(&str)` iff
10689 /// this is a [`Self::Symbol`] variant OR a [`Self::Str`] variant, `None`
10690 /// for every other atomic kind (`Keyword`, `Int`, `Float`, `Bool`).
10691 /// The atomic-payload peer of [`Sexp::as_symbol_or_string`] —
10692 /// disjunctive composition of [`Self::as_symbol`] + [`Self::as_string`]
10693 /// at the typed [`Atom`] algebra rather than at the [`Sexp`] consumer
10694 /// layer where the union previously composed two distinct
10695 /// [`Sexp::as_atom`] traversals.
10696 ///
10697 /// Sibling soft-projection peer of the six per-variant projections
10698 /// ([`Self::as_symbol`], [`Self::as_keyword`], [`Self::as_string`],
10699 /// [`Self::as_int`], [`Self::as_float`], [`Self::as_bool`]) — this
10700 /// union projection completes the soft-decomposition family on the
10701 /// closed-set [`Atom`] algebra by naming the (Symbol ⊎ Str) union
10702 /// the substrate's named-form NAME gate ([`crate::compile::split_name_slot`]
10703 /// via [`Sexp::as_symbol_or_string`]) keys on. Both NAME-author
10704 /// surfaces (`(defcompiler my-name …)` — bare symbol; `(defcompiler
10705 /// "my-name" …)` — quoted string) project to `Some("my-name")`
10706 /// through one method on the algebra.
10707 ///
10708 /// Composition law binding it to [`Sexp::as_symbol_or_string`]: for
10709 /// every [`Sexp`] `s`,
10710 /// `s.as_symbol_or_string() == s.as_atom().and_then(Atom::as_symbol_or_string)`
10711 /// — the same structural-lift composition pattern [`Sexp::as_symbol`]
10712 /// / [`Sexp::as_keyword`] / [`Sexp::as_string`] / [`Sexp::as_int`] /
10713 /// [`Sexp::as_bool`] route through on the six per-variant axis.
10714 /// Lifts the `self.as_symbol().or_else(|| self.as_string())`
10715 /// disjunctive composition at [`Sexp::as_symbol_or_string`]'s body
10716 /// (TWO `Sexp::as_atom` traversals pre-lift) onto ONE typed-algebra
10717 /// projection the `Sexp` consumer routes through via the structural
10718 /// lift [`Sexp::as_atom`] (ONE `Sexp::as_atom` traversal post-lift).
10719 ///
10720 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
10721 /// (Symbol ⊎ Str) union projection now binds at ONE method on the
10722 /// closed-set [`Atom`] algebra regardless of which consumer reaches
10723 /// in. THEORY.md §VI.1 — generation over composition; the
10724 /// disjunctive `as_symbol().or_else(|| as_string())` composition at
10725 /// [`Sexp::as_symbol_or_string`]'s body collapses onto a SINGLE
10726 /// structural lift through [`Sexp::as_atom`] + the algebra-level
10727 /// union projection, eliminating the double-traversal redundancy
10728 /// the pre-lift consumer-layer composition carried. THEORY.md §V.1
10729 /// — knowable platform; the (Symbol-or-Str) NAME-slot union becomes
10730 /// a TYPE projection on the substrate algebra rather than a
10731 /// disjunctive composition at every NAME-gate consumer.
10732 ///
10733 /// Frontier inspiration: Racket's `(or/c symbol? string?)`
10734 /// contract — a typed disjunctive predicate the consumer binds to
10735 /// in one place rather than re-deriving the disjunction at every
10736 /// callsite; [`Self::as_symbol_or_string`] is the substrate's
10737 /// unstructured-Rust peer with the typed projection (`Option<&str>`)
10738 /// surfacing the underlying payload alongside the predicate face.
10739 /// MLIR's `mlir::dyn_cast<StringLike>(attr)` — typed soft-downcast
10740 /// onto a closed-set attribute union; [`Self::as_symbol_or_string`]
10741 /// is the substrate's [`Atom`]-algebra peer for the
10742 /// (Symbol ⊎ Str) union, with `Option<&str>` standing in for MLIR's
10743 /// typed downcast result.
10744 #[must_use]
10745 pub fn as_symbol_or_string(&self) -> Option<&str> {
10746 self.as_symbol().or_else(|| self.as_string())
10747 }
10748}
10749
10750/// Closed-set typed discriminator for the six [`Atom`] payload variants —
10751/// `Symbol(String)`, `Keyword(String)`, `Str(String)`, `Int(i64)`,
10752/// `Float(f64)`, `Bool(bool)` — paired with the projections every
10753/// per-atom-kind consumer keys on ([`Self::hash_discriminator`] for
10754/// [`Hash for Atom`]'s cache-key bytes, [`Self::sexp_shape`] for
10755/// [`crate::domain::sexp_shape`]'s atom-arm collapse, [`Self::label`]
10756/// for the operator-facing diagnostic vocabulary, [`Self::FromStr`]
10757/// for the typed-inverse decode that lets LSP / REPL / metric-aggregator
10758/// consumers round-trip a rendered diagnostic label back into the typed
10759/// discriminator).
10760///
10761/// Atomic-payload peer of [`QuoteForm`] (the four homoiconic prefix
10762/// wrappers — `Sexp::{Quote, Quasiquote, Unquote, UnquoteSplice}`):
10763/// where `QuoteForm` carves the closed set on `Sexp`'s wrapper-variant
10764/// axis, `AtomKind` carves the closed set on `Sexp`'s atomic-payload
10765/// axis. Together the two closed-set discriminators cover every reachable
10766/// `Sexp` outermost shape except `Nil` and `List` (the structural
10767/// constructors `()` and `(…)`) — every other shape is either an
10768/// `Atom(_)` projecting through this enum's [`Self::sexp_shape`] arm or a
10769/// quote-family wrapper projecting through [`QuoteForm::sexp_shape`].
10770/// After this lift the two enums' [`Self::sexp_shape`] arms own ALL TEN
10771/// of [`SexpShape`]'s twelve canonical labels through ONE typed
10772/// composition each rather than through per-callsite arm-pairing in
10773/// [`crate::domain::sexp_shape`].
10774///
10775/// Mirror at the atomic-payload boundary of the prior-run [`QuoteForm`]
10776/// (homoiconic-prefix-wrapper closed set, 4 variants), the cross-crate
10777/// `tatara-process` closed-set family
10778/// (`ConditionKind::ALL`, `ProcessPhase::ALL`, `ProcessSignal::ALL`,
10779/// `ChannelKind::ALL`, `IntentKind::ALL`, `LifetimeKind::ALL`,
10780/// `RequestorKind::ALL`, `ReceiptKind::ALL`, …) and this crate's own
10781/// [`SexpShape`] (the twelve reachable Sexp outermost shapes — the
10782/// SUPERSET this enum projects into via [`Self::sexp_shape`]) and
10783/// [`UnquoteForm`] (the two template-substitution markers) closed-set
10784/// lifts: those enums key their respective rejection or projection
10785/// variants on a typed identity carried inside the variant's data shape;
10786/// this enum keys the SIX [`Atom`] payload variants on a typed
10787/// discriminator identity threaded through ALL THREE per-atom-kind
10788/// dispatch sites ([`Hash for Atom`]'s six byte literals,
10789/// [`crate::domain::sexp_shape`]'s six atom arms, AND the
10790/// diagnostic-label vocabulary [`SexpShape::label`] publishes for the
10791/// atom subset). Adding a hypothetical seventh atomic kind (e.g. a
10792/// `Char` literal for `#\x` reader syntax, a `Bigint` for arbitrary-
10793/// precision integers, a `Symbol2` for namespaced symbols) requires
10794/// extending this enum, which rustc-enforces matching at every
10795/// projection site ([`Self::label`], [`Self::hash_discriminator`],
10796/// [`Self::sexp_shape`], [`Atom::kind`], the [`Hash for Atom`] inner
10797/// match, and the [`Self::FromStr`] sweep keyed on [`Self::ALL`]) — the
10798/// closed set becomes a TYPE rather than six `&'static str` / `u8`
10799/// / `SexpShape` literals that could drift independently across the
10800/// substrate's three per-atom-kind consumer surfaces.
10801///
10802/// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
10803/// atomic-payload discriminator at a typed-entry rejection IS part of
10804/// the proof of WHAT the gate observed, and naming its closed-set
10805/// identity lifts the discriminator from per-site literal-pair
10806/// discipline (a byte at the Hash site, a SexpShape variant at the
10807/// `sexp_shape` site, a `&'static str` at any future LSP completion
10808/// site) to ONE typed enum the substrate's diagnostic + cache-key
10809/// surfaces both bind against. THEORY.md §II.1 invariant 2 — free
10810/// middle; THREE consumers ([`Hash for Atom`],
10811/// [`crate::domain::sexp_shape`], and the future diagnostic /
10812/// completion surface) route through ONE typed closed-set match
10813/// family, so a regression that drifts ONE consumer's pairing from the
10814/// others cannot reach the substrate's runtime. THEORY.md §V.1 —
10815/// knowable platform; the closed set of atomic payload kinds becomes a
10816/// TYPE rather than six byte literals (Hash) + six SexpShape literals
10817/// (`sexp_shape`) scattered across distinct files — a typo in any one
10818/// site is no longer a runtime drift but a compile error against the
10819/// typed projection. THEORY.md §VI.1 — generation over composition;
10820/// the (Atom variant, label, discriminator-byte, SexpShape variant)
10821/// quadruple appeared inline at THREE sites (`Hash for Atom`'s six
10822/// byte arms, `domain::sexp_shape`'s six atom arms, plus implicit
10823/// pairing across `SexpShape::label`'s six atom-subset arms) — well
10824/// past the ≥2 PRIME-DIRECTIVE trigger once the structural shape is
10825/// named.
10826#[derive(Debug, Clone, Copy, PartialEq, Eq, tatara_closed_set::DeriveClosedSet)]
10827#[closed_set(via = "label", display, generate_unknown = "atom kind")]
10828pub enum AtomKind {
10829 /// `Atom::Symbol(_)` — `"symbol"` diagnostic label, byte `0u8`
10830 /// hash discriminator, projects to [`SexpShape::Symbol`].
10831 Symbol,
10832 /// `Atom::Keyword(_)` — `"keyword"` diagnostic label, byte `1u8`
10833 /// hash discriminator, projects to [`SexpShape::Keyword`].
10834 Keyword,
10835 /// `Atom::Str(_)` — `"string"` diagnostic label, byte `2u8` hash
10836 /// discriminator, projects to [`SexpShape::String`].
10837 Str,
10838 /// `Atom::Int(_)` — `"int"` diagnostic label, byte `3u8` hash
10839 /// discriminator, projects to [`SexpShape::Int`].
10840 Int,
10841 /// `Atom::Float(_)` — `"float"` diagnostic label, byte `4u8` hash
10842 /// discriminator, projects to [`SexpShape::Float`].
10843 Float,
10844 /// `Atom::Bool(_)` — `"bool"` diagnostic label, byte `5u8` hash
10845 /// discriminator, projects to [`SexpShape::Bool`].
10846 Bool,
10847}
10848
10849impl AtomKind {
10850 /// The closed set of six atomic [`Atom`] payload kinds — single
10851 /// source of truth that drives every per-kind projection
10852 /// ([`Self::label`] / [`fmt::Display`], [`Self::hash_discriminator`],
10853 /// [`Self::sexp_shape`], and the [`Self::FromStr`] decode sweep
10854 /// keyed on [`Self::label`]).
10855 ///
10856 /// Adding a hypothetical seventh atomic kind (e.g. `Char` for
10857 /// `#\x` reader syntax, `Bigint` for arbitrary-precision
10858 /// integers) lands at one [`Self::ALL`] entry plus one arm per
10859 /// projection — exhaustively checked by the compiler (the
10860 /// `[Self; 6]` array literal forces the arity) AND by the
10861 /// per-variant truth-table tests below.
10862 ///
10863 /// Sibling closed-set lift to every other typed-shape enum the
10864 /// substrate carries: this crate's own [`SexpShape::ALL`] (the
10865 /// twelve reachable outer shapes — superset of this kind's six),
10866 /// [`QuoteForm`] (the four homoiconic prefix wrappers — peer
10867 /// projection on the SAME `Sexp` algebra), [`UnquoteForm`] (the
10868 /// two template-substitution markers — proper subset of
10869 /// `QuoteForm`), and the cross-crate `tatara-process` family
10870 /// (`ConditionKind::ALL`, `ProcessPhase::ALL`,
10871 /// `ProcessSignal::ALL`, `ChannelKind::ALL`, `IntentKind::ALL`,
10872 /// …) every one of which paired its typed projection with `ALL`
10873 /// before this lift.
10874 ///
10875 /// Future consumers that compose against `ALL`: LSP / REPL
10876 /// completion for the operator-facing rendered atom-kind label
10877 /// (every `expected X, got Y` substring in `LispError`'s rendered
10878 /// diagnostics for an atomic witness keys on this set's projection
10879 /// through [`Self::label`]); `tatara-check` coverage assertions
10880 /// over which atomic kinds reach a `TypeMismatch.got` arm at all
10881 /// — the typed sweep replaces a per-callsite vocabulary of six
10882 /// `&'static str` literals; any future audit-trail metric jointly
10883 /// labeled by [`Self::label`] (e.g.
10884 /// `tatara_lisp_atom_type_mismatch_total{got="symbol"}`) — the
10885 /// metric label set IS [`Self::ALL`] mapped through
10886 /// [`Self::label`]; any future structural rewriter (typed
10887 /// analogue of MLIR's `op.walk<AtomKind::Symbol>()`) that wants
10888 /// to sweep over every atomic kind in a typed sequence.
10889 pub const ALL: [Self; 6] = [
10890 Self::Symbol,
10891 Self::Keyword,
10892 Self::Str,
10893 Self::Int,
10894 Self::Float,
10895 Self::Bool,
10896 ];
10897
10898 /// Canonical `&'static str` bytes for the [`Self::Symbol`] atomic-
10899 /// payload marker — aliases [`SexpShape::SYMBOL_LABEL`] on the
10900 /// AtomKind ⊂ SexpShape carving so the marker-level per-role bytes
10901 /// bind at ONE `pub const` on the parent superset's atomic arm
10902 /// rather than at TWO sites (the per-role `pub const` AND a
10903 /// parallel inline literal). Per-role peer of `Self::Symbol` on the
10904 /// closed-set atomic algebra; consumers reach for
10905 /// `AtomKind::SYMBOL_LABEL` when the caller has a variant in hand
10906 /// at compile time and wants the canonical diagnostic bytes without
10907 /// runtime dispatch through [`Self::label`].
10908 pub const SYMBOL_LABEL: &'static str = SexpShape::SYMBOL_LABEL;
10909
10910 /// Canonical `&'static str` bytes for the [`Self::Keyword`] atomic-
10911 /// payload marker — aliases [`SexpShape::KEYWORD_LABEL`] on the
10912 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Keyword`.
10913 pub const KEYWORD_LABEL: &'static str = SexpShape::KEYWORD_LABEL;
10914
10915 /// Canonical `&'static str` bytes for the [`Self::Str`] atomic-
10916 /// payload marker — aliases [`SexpShape::STRING_LABEL`] on the
10917 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Str`; the
10918 /// `Str → "string"` wire-shape rename matches
10919 /// [`SexpShape::String`]'s label projection so the AtomKind marker
10920 /// and its SexpShape peer emit byte-identical diagnostic bytes.
10921 pub const STRING_LABEL: &'static str = SexpShape::STRING_LABEL;
10922
10923 /// Canonical `&'static str` bytes for the [`Self::Int`] atomic-
10924 /// payload marker — aliases [`SexpShape::INT_LABEL`] on the
10925 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Int`.
10926 pub const INT_LABEL: &'static str = SexpShape::INT_LABEL;
10927
10928 /// Canonical `&'static str` bytes for the [`Self::Float`] atomic-
10929 /// payload marker — aliases [`SexpShape::FLOAT_LABEL`] on the
10930 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Float`.
10931 pub const FLOAT_LABEL: &'static str = SexpShape::FLOAT_LABEL;
10932
10933 /// Canonical `&'static str` bytes for the [`Self::Bool`] atomic-
10934 /// payload marker — aliases [`SexpShape::BOOL_LABEL`] on the
10935 /// AtomKind ⊂ SexpShape carving. Per-role peer of `Self::Bool`.
10936 pub const BOOL_LABEL: &'static str = SexpShape::BOOL_LABEL;
10937
10938 /// Closed-set forced-arity ALL array over the canonical atomic-
10939 /// payload marker `&'static str` bytes, in declaration order
10940 /// matching [`Self::ALL`] element-wise (pinned by
10941 /// `atom_kind_labels_align_with_all_by_index`). Sibling posture to
10942 /// [`SexpShape::LABELS`] (`[&'static str; 12]` — the superset
10943 /// carving this AtomKind subset embeds into),
10944 /// [`crate::error::ExpectedKwargShape::LABELS`] (`[&'static str; 7]`),
10945 /// [`crate::error::KwargPathKind::LABELS`] (`[&'static str; 3]`),
10946 /// [`crate::error::MacroDefHead::KEYWORDS`] (`[&'static str; 3]`),
10947 /// [`Atom::BOOL_LITERALS`] (`[&'static str; 2]`), and
10948 /// [`QuoteForm::PREFIXES`] (`[&'static str; 4]`) — every closed-set
10949 /// outer projection on the substrate that carries an `&'static str`-
10950 /// per-variant label now pins its per-role canonical bytes at ONE
10951 /// `pub const` per role PLUS an ALL array for family-wide consumers.
10952 ///
10953 /// Pre-lift the six atomic-payload marker bytes had NO per-role
10954 /// primitive on this closed-set algebra — a consumer with an
10955 /// `AtomKind` variant in hand at compile time reaching for the
10956 /// canonical diagnostic bytes had to spell
10957 /// `AtomKind::Symbol.label()` (runtime dispatch through the
10958 /// composition [`Self::sexp_shape`] + [`SexpShape::label`]) OR
10959 /// reach across the algebra boundary into
10960 /// [`SexpShape::SYMBOL_LABEL`] and re-derive the AtomKind ⊂
10961 /// SexpShape variant pairing at the call site. Post-lift the SIX
10962 /// canonical bytes bind at ONE `pub const` per role on the typed
10963 /// [`AtomKind`] algebra AND at [`Self::LABELS`] as a family-wide
10964 /// forced-arity array — a future LSP / REPL completion bar keyed on
10965 /// `AtomKind::LABELS`, a `tatara-check` coverage sweep over the
10966 /// atomic-payload arms of a `TypeMismatch.got` corpus, or a Sekiban
10967 /// audit-trail metric jointly labeled by the atomic marker
10968 /// (`tatara_lisp_atom_type_mismatch_total{kind="symbol"}`) reads
10969 /// through the typed constants on this subset algebra without
10970 /// re-deriving the 6-of-12 carving inline.
10971 ///
10972 /// Each entry is byte-for-byte identical to the corresponding
10973 /// [`SexpShape`] atomic arm — an intentional cross-axis overlap
10974 /// pinned by
10975 /// `atom_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
10976 /// so a future label rename on EITHER side (a SexpShape `"string"`
10977 /// → `"str"` drift, or an AtomKind rename that skips the alias)
10978 /// fails-loudly at the alias test rather than as a silent
10979 /// operator-facing vocabulary fracture. Adding a hypothetical
10980 /// seventh atomic kind (e.g. `Char` for `#\x` reader syntax,
10981 /// `Bigint` for arbitrary-precision integers) extends [`Self::ALL`]
10982 /// AND [`Self::LABELS`] AND adds ONE per-role `pub const` alias in
10983 /// lockstep — rustc's forced-arity check on the two `[_; N]` arrays
10984 /// fails compilation if EITHER ALL array grows without the other.
10985 ///
10986 /// Theory anchor: THEORY.md §III — the typescape; the six canonical
10987 /// atomic-payload marker bytes bind at ONE typed
10988 /// `[&'static str; 6]` array on the closed-set AtomKind algebra
10989 /// rather than at zero-primitive-on-this-subset-plus-six-inline-
10990 /// lookups scattered across the substrate. THEORY.md §V.1 —
10991 /// knowable platform; the family's cardinality becomes a TYPE-level
10992 /// constant on the substrate algebra rather than a per-consumer
10993 /// runtime dispatch through the composition. THEORY.md §VI.1 —
10994 /// generation over composition; the family-wide contract sweeps
10995 /// (alignment with `ALL`, pairwise disjointness, membership through
10996 /// [`Self::label`]) emerge from the composition of TWO substrate
10997 /// primitives (this `pub const` array + the six per-role
10998 /// `pub const *_LABEL` aliases) rather than as per-variant inline
10999 /// assertions duplicated at each call site.
11000 pub const LABELS: [&'static str; 6] = [
11001 Self::SYMBOL_LABEL,
11002 Self::KEYWORD_LABEL,
11003 Self::STRING_LABEL,
11004 Self::INT_LABEL,
11005 Self::FLOAT_LABEL,
11006 Self::BOOL_LABEL,
11007 ];
11008
11009 /// Canonical `u8` cache-key byte for [`Self::Symbol`]'s
11010 /// [`Self::hash_discriminator`] arm — `0`. Per-role peer of
11011 /// [`Self::Symbol`] on the closed-set atomic-payload cache-key-byte
11012 /// axis; consumers reach for `AtomKind::SYMBOL_HASH_DISCRIMINATOR`
11013 /// when the caller has a variant in hand at compile time and wants
11014 /// the canonical byte without runtime dispatch through
11015 /// [`Self::hash_discriminator`]. The byte is load-bearing because
11016 /// the macro-expansion cache ([`crate::macro_expand::Expander`]'s
11017 /// cache) keys on [`Hash for Atom`], and any renumbering silently
11018 /// invalidates every cached expansion — post-lift the six canonical
11019 /// bytes bind at ONE `pub(crate) const` per role rather than at
11020 /// six inline `u8` literals scattered across
11021 /// [`Self::hash_discriminator`]'s match arms.
11022 ///
11023 /// Sibling posture to [`crate::error::QuoteForm::QUOTE_HASH_DISCRIMINATOR`]
11024 /// on the quote-family sub-carving — both close their respective
11025 /// closed-set cache-key algebras at ONE per-role constant per
11026 /// variant PLUS a family-wide [`Self::HASH_DISCRIMINATORS`] array.
11027 /// The two families partition their respective cache-key spaces
11028 /// independently: `AtomKind` at `{0..=5}` NESTED inside
11029 /// [`crate::ast::Sexp::Atom`]'s outer `1u8` byte (`Hash for Atom`
11030 /// runs on the [`Atom`] type, not [`Sexp`]), `QuoteForm` at
11031 /// `{3..=6}` at the outer [`Sexp`] cache-key space itself.
11032 ///
11033 /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
11034 /// preserves proofs; the alias-chain composition law
11035 /// `AtomKind::HASH_DISCRIMINATORS[i] ==
11036 /// AtomKind::ALL[i].hash_discriminator()` binds the family-wide
11037 /// array to the projection method at rustc time, pinned by byte
11038 /// equality. THEORY.md §III — the typescape; the six canonical
11039 /// cache-key bytes bind at ONE `pub(crate) const` per role on the
11040 /// typed algebra rather than as inline `u8` literals in the
11041 /// `hash_discriminator` match arms.
11042 pub(crate) const SYMBOL_HASH_DISCRIMINATOR: u8 = 0;
11043
11044 /// Canonical `u8` cache-key byte for [`Self::Keyword`]'s
11045 /// [`Self::hash_discriminator`] arm — `1`. Sibling of
11046 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11047 /// atomic-payload cache-key-byte axis; see
11048 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] for the algebra-level
11049 /// round-trip + disjointness contracts every sibling shares.
11050 pub(crate) const KEYWORD_HASH_DISCRIMINATOR: u8 = 1;
11051
11052 /// Canonical `u8` cache-key byte for [`Self::Str`]'s
11053 /// [`Self::hash_discriminator`] arm — `2`. Sibling of
11054 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11055 /// atomic-payload cache-key-byte axis.
11056 pub(crate) const STR_HASH_DISCRIMINATOR: u8 = 2;
11057
11058 /// Canonical `u8` cache-key byte for [`Self::Int`]'s
11059 /// [`Self::hash_discriminator`] arm — `3`. Sibling of
11060 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11061 /// atomic-payload cache-key-byte axis.
11062 pub(crate) const INT_HASH_DISCRIMINATOR: u8 = 3;
11063
11064 /// Canonical `u8` cache-key byte for [`Self::Float`]'s
11065 /// [`Self::hash_discriminator`] arm — `4`. Sibling of
11066 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11067 /// atomic-payload cache-key-byte axis.
11068 pub(crate) const FLOAT_HASH_DISCRIMINATOR: u8 = 4;
11069
11070 /// Canonical `u8` cache-key byte for [`Self::Bool`]'s
11071 /// [`Self::hash_discriminator`] arm — `5`. Sibling of
11072 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] on the closed-set per-role
11073 /// atomic-payload cache-key-byte axis. The HIGHEST byte on the
11074 /// closed set — a future seventh atomic kind (e.g. `Char` for
11075 /// `#\x` reader syntax, `Bigint` for arbitrary-precision integers)
11076 /// would extend the partition to `{0..=6}` and land the new
11077 /// discriminator at `6u8`.
11078 pub(crate) const BOOL_HASH_DISCRIMINATOR: u8 = 5;
11079
11080 /// Closed-set forced-arity ALL array over the canonical atomic-
11081 /// payload cache-key `u8` bytes, in declaration order matching
11082 /// [`Self::ALL`] element-wise (pinned by
11083 /// `atom_kind_hash_discriminators_align_with_all_by_index`).
11084 /// Sibling posture to [`Self::LABELS`] (`[&'static str; 6]` — the
11085 /// diagnostic-label `&'static str` axis on the SAME closed set) and
11086 /// to [`crate::error::QuoteForm::HASH_DISCRIMINATORS`] (`[u8; 4]` —
11087 /// the quote-family sub-carving's cache-key-byte peer). Every
11088 /// closed-set outer projection on the substrate's [`AtomKind`]
11089 /// algebra that carries a `u8` per-variant discriminator now pins
11090 /// its per-role canonical bytes at ONE `pub(crate) const` per role
11091 /// PLUS an ALL array for family-wide consumers.
11092 ///
11093 /// Pre-lift the six cache-key bytes had NO per-role primitive on
11094 /// this closed-set algebra — a consumer with an [`AtomKind`]
11095 /// variant in hand at compile time reaching for the canonical byte
11096 /// had to spell `AtomKind::Str.hash_discriminator()` (runtime
11097 /// dispatch through the match arm) OR reach across into the inline
11098 /// `2u8` at the pre-lift match arm's [`Self::Str`] branch and
11099 /// re-derive the (variant, byte) pairing at the call site.
11100 /// Post-lift the SIX canonical bytes bind at ONE `pub(crate) const`
11101 /// per role on the typed [`AtomKind`] algebra AND at
11102 /// [`Self::HASH_DISCRIMINATORS`] as a family-wide forced-arity
11103 /// array — a future substrate-facing cache-key introspection tool
11104 /// (a `tatara-check` predicate that asserts every atomic arm's
11105 /// discriminator injective on the nested [`Atom`] axis, a Sekiban
11106 /// audit-trail metric jointly labeled by the atomic cache-key
11107 /// partition, a future `TypedRewriter<AtomKindOp>` sweep zipping
11108 /// ALL / LABELS / HASH_DISCRIMINATORS in lockstep for a family-wide
11109 /// (variant, label, byte) triple render) reads through the typed
11110 /// constants without re-deriving the six-arm carving inline.
11111 ///
11112 /// Each entry is byte-for-byte identical to the pre-lift inline
11113 /// `u8` literal at the corresponding [`Self::hash_discriminator`]
11114 /// arm — pinned by
11115 /// `atom_kind_hash_discriminators_pin_legacy_cache_key_bytes` so
11116 /// a regression that drifts ONE `pub(crate) const` from its pre-
11117 /// lift byte silently invalidates every cached expansion of an
11118 /// [`Atom`] participating in [`crate::macro_expand::Expander::cache`],
11119 /// fails-loudly at the alias test rather than at a silent cache
11120 /// mis-hash. Adding a hypothetical seventh atomic kind (e.g.
11121 /// `Char` for `#\x` reader syntax, `Bigint` for arbitrary-
11122 /// precision integers) extends [`Self::ALL`] AND
11123 /// [`Self::HASH_DISCRIMINATORS`] AND adds ONE per-role
11124 /// `pub(crate) const` in lockstep — rustc's forced-arity check on
11125 /// the two `[_; N]` arrays fails compilation if EITHER array grows
11126 /// without the other, closing the extensibility gap that pre-lift
11127 /// silently allowed a discriminator collision on `6u8` (the next
11128 /// free byte on the nested [`Atom`] cache-key space).
11129 ///
11130 /// Theory anchor: THEORY.md §III — the typescape; the six
11131 /// canonical cache-key bytes bind at ONE typed `[u8; 6]` array on
11132 /// the closed-set [`AtomKind`] algebra rather than at zero-
11133 /// primitive-plus-six-inline-`u8`-literals scattered across the
11134 /// [`Self::hash_discriminator`] match arms. THEORY.md §V.1 —
11135 /// knowable platform; the family's cardinality becomes a TYPE-
11136 /// level constant on the substrate algebra rather than a per-
11137 /// consumer runtime dispatch through the match table. THEORY.md
11138 /// §V.3 — three-pillar attestation; the cache-key partition is
11139 /// the substrate's nested [`Atom`] `intent_hash` composition axis
11140 /// for every atomic arm — binding the six bytes on the typed
11141 /// algebra makes attestation-key drift a compile error rather
11142 /// than a silent BLAKE3 mis-hash. THEORY.md §VI.1 — generation
11143 /// over composition; the family-wide contract sweeps (alignment
11144 /// with `ALL`, pairwise disjointness, membership through
11145 /// [`Self::hash_discriminator`]) emerge from the composition of
11146 /// TWO substrate primitives (this `pub(crate) const` array + the
11147 /// six per-role `pub(crate) const *_HASH_DISCRIMINATOR` aliases)
11148 /// rather than as per-variant inline assertions duplicated at each
11149 /// call site.
11150 ///
11151 /// The `#[allow(dead_code)]` posture matches
11152 /// [`crate::error::QuoteForm::HASH_DISCRIMINATORS`]: the substrate's
11153 /// current [`Hash for Atom`] body composes through the per-variant
11154 /// [`Self::hash_discriminator`] projection arm-by-arm rather than
11155 /// sweeping the family-wide array, so no non-test caller currently
11156 /// reaches this ALL array directly. The lift lands the substrate
11157 /// primitive so future consumers keyed on the whole family (a
11158 /// future [`crate::macro_expand::Expander`] cache-warmup pass that
11159 /// hashes the atomic byte-set upfront, a future `tatara-check`
11160 /// predicate `(check-atom-cache-key-partition-injective …)` that
11161 /// verifies the `{0..=5}` partition structurally, a future
11162 /// `TypedRewriter<AtomKindOp>` sweep zipping ALL / LABELS /
11163 /// HASH_DISCRIMINATORS in lockstep for a family-wide (variant,
11164 /// label, byte) triple render) bind to ONE `[u8; 6]` primitive
11165 /// rather than re-deriving the array inline per callsite.
11166 #[allow(dead_code)]
11167 pub(crate) const HASH_DISCRIMINATORS: [u8; 6] = [
11168 Self::SYMBOL_HASH_DISCRIMINATOR,
11169 Self::KEYWORD_HASH_DISCRIMINATOR,
11170 Self::STR_HASH_DISCRIMINATOR,
11171 Self::INT_HASH_DISCRIMINATOR,
11172 Self::FLOAT_HASH_DISCRIMINATOR,
11173 Self::BOOL_HASH_DISCRIMINATOR,
11174 ];
11175
11176 /// Canonical `u8` OUTER-`Sexp` cache-key byte at which ALL SIX
11177 /// atomic-payload shapes collapse when hashed at the outer
11178 /// [`Hash for Sexp`](crate::ast::Sexp) level — `1`. The
11179 /// outer-carve peer of [`Self::HASH_DISCRIMINATORS`] (the six
11180 /// nested INNER cache-key bytes `{0..=5}` that specialise INSIDE
11181 /// [`Hash for Atom`] after the outer marker byte is emitted).
11182 /// The lift moves the byte the outer-Sexp cache-key algebra uses
11183 /// to distinguish [`crate::ast::Sexp::Atom(_)`] from every other
11184 /// outer-Sexp variant off inline `1u8` literals scattered across
11185 /// [`crate::error::SexpShape::hash_discriminator`]'s six-arm
11186 /// atomic collapse + the two structural-carve joint-partition
11187 /// disjointness pins and onto ONE `pub(crate) const` on the
11188 /// [`AtomKind`] algebra it names.
11189 ///
11190 /// Where the byte appears at the outer-Sexp cache-key algebra:
11191 /// [`crate::error::SexpShape::hash_discriminator`]'s atomic-arm
11192 /// collapse `Self::Symbol | Self::Keyword | Self::String |
11193 /// Self::Int | Self::Float | Self::Bool => 1` binds directly to
11194 /// this constant; every one of the six atomic shapes routes
11195 /// through the shape-level projection into the outer-Sexp cache
11196 /// key at THIS byte. The nested inner
11197 /// [`Self::HASH_DISCRIMINATORS`] `{0..=5}` bytes then specialise
11198 /// the atomic payload INSIDE [`Hash for Atom`] via a second
11199 /// discriminator emission (`self.hash_discriminator().hash(h)`
11200 /// on the [`Atom`] value carrier), so the two byte spaces live
11201 /// at different hash-sequence positions and do not collide.
11202 ///
11203 /// Sibling posture to [`crate::error::StructuralKind::HASH_DISCRIMINATORS`]
11204 /// (`[u8; 2]` at `{0, 2}` on the outer-Sexp cache-key space) and
11205 /// to [`crate::error::QuoteForm::HASH_DISCRIMINATORS`] (`[u8; 4]`
11206 /// at `{3, 4, 5, 6}` on the same space) — together with THIS
11207 /// scalar the three sibling carvings' byte spaces jointly
11208 /// partition the outer-Sexp discriminator space `{0..=6}`
11209 /// injectively. Post-lift the outer-Sexp cache-key algebra
11210 /// closes over FOUR typed byte primitives:
11211 /// * [`Self::OUTER_HASH_DISCRIMINATOR`] (this constant) —
11212 /// scalar `1u8` for the atomic-payload outer-carve;
11213 /// * [`crate::error::StructuralKind::HASH_DISCRIMINATORS`] —
11214 /// `{0, 2}` for the structural-residual carve;
11215 /// * [`crate::error::QuoteForm::HASH_DISCRIMINATORS`] —
11216 /// `{3..=6}` for the quote-family carve;
11217 /// * [`Self::HASH_DISCRIMINATORS`] — the nested inner
11218 /// `{0..=5}` byte-set INSIDE [`Hash for Atom`], NOT on the
11219 /// outer-Sexp space.
11220 ///
11221 /// The scalar shape (single `u8`, NOT an array) is intrinsic to
11222 /// the carve: all six atomic-payload arms of
11223 /// [`crate::error::SexpShape`] collapse to the SAME outer byte
11224 /// (the outer-Sexp distinguisher is variant-level: `Sexp::Atom(_)`
11225 /// vs the six sibling `Sexp` variants); per-atom-kind
11226 /// specialisation lives at the nested inner
11227 /// [`Self::HASH_DISCRIMINATORS`] carve inside [`Hash for Atom`].
11228 /// The other two carvings' `HASH_DISCRIMINATORS` are arrays
11229 /// because their shape-level arms each carry a DISTINCT outer
11230 /// byte; the atomic carve is a scalar because its arms carry the
11231 /// SAME outer byte.
11232 ///
11233 /// `pub(crate)` because the byte is an implementation detail of
11234 /// the substrate's `Hash for Sexp` cache-key contract; exposing
11235 /// it publicly would leak the cache-key shape through the API
11236 /// without enabling any external consumer the public projections
11237 /// ([`Self::label`], [`Self::sexp_shape`]) don't already serve.
11238 /// Same posture as [`Self::HASH_DISCRIMINATORS`] +
11239 /// [`Self::SYMBOL_HASH_DISCRIMINATOR`] and the sibling carvings'
11240 /// per-role `pub(crate) const` peers.
11241 ///
11242 /// Pre-lift the outer-Atom marker byte lived at THREE sites: the
11243 /// inline `1u8` literal at
11244 /// [`crate::error::SexpShape::hash_discriminator`]'s six-arm
11245 /// atomic collapse; the inline `1u8` literal at
11246 /// `sexp_shape_hash_discriminator_atomic_arms_collapse_to_outer_atom_marker`'s
11247 /// assertion body; the inline `1u8` literal at
11248 /// `sexp_shape_hash_discriminator_partitions_by_three_way_carving_disjointly`'s
11249 /// `expected_atomic` fixture; PLUS a duplicated local `const
11250 /// ATOM_OUTER_CARVE_BYTE: u8 = 1` inside
11251 /// `structural_kind_hash_discriminator_disjoint_from_atom_outer_carve_byte_and_quote_form_hash_discriminator_partition`.
11252 /// The (byte, algebra) pairing had no typed home — a consumer
11253 /// with a typed [`AtomKind`] identity in hand reaching for the
11254 /// outer-Sexp cache-key byte the atomic arm collapses to had to
11255 /// re-derive the byte from the shape-level projection method's
11256 /// atomic collapse arm inline, OR re-derive the pre-lift local
11257 /// `ATOM_OUTER_CARVE_BYTE` fixture at every joint-partition-check
11258 /// site. Post-lift the byte binds at ONE `pub(crate) const` on
11259 /// the closed-set [`AtomKind`] algebra it names; every downstream
11260 /// consumer (the shape-level projection, the joint-partition
11261 /// disjointness pins, the three-way carving image pin, a future
11262 /// `tatara-check` predicate that verifies the outer-Sexp cache-
11263 /// key partition structurally, a future
11264 /// [`crate::macro_expand::Expander`] cache-warmup pass that
11265 /// hashes the outer-Sexp byte-set upfront) picks up the same
11266 /// canonical byte from ONE source of truth.
11267 ///
11268 /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
11269 /// preserves proofs; the (AtomKind ⊂ SexpShape carve, outer-Sexp
11270 /// cache-key byte) pairing binds at rustc time by byte equality
11271 /// against the shape-level projection's atomic collapse arm.
11272 /// THEORY.md §III — the typescape; the outer-Atom cache-key byte
11273 /// binds at ONE `pub(crate) const` on the typed algebra rather
11274 /// than as inline `1u8` literals at every joint-partition + shape-
11275 /// level-collapse site. THEORY.md §V.1 — knowable platform; the
11276 /// outer-Sexp cache-key space's four-way partition (this scalar
11277 /// PLUS the three sibling carvings' arrays) becomes a TYPE-level
11278 /// constant on the substrate algebra rather than a per-callsite
11279 /// hand-rolled `{0, 1, 2, 3, 4, 5, 6}` re-enumeration. THEORY.md
11280 /// §V.3 — three-pillar attestation; the outer-Sexp cache-key
11281 /// partition is the substrate's outer [`Sexp`] `intent_hash`
11282 /// composition axis — binding the four-way partition's atomic-
11283 /// carve byte on the typed algebra makes attestation-key drift a
11284 /// compile error rather than a silent BLAKE3 mis-hash. A future
11285 /// eighth [`Sexp`] variant (e.g. `Vector` for `#(...)` reader
11286 /// syntax, `Map` for `{...}`, `Char` for `#\x`) picks a fresh
11287 /// cache-key byte outside `{0..=6}` (e.g. `7u8`), extends the
11288 /// closed-set [`crate::error::SexpShape`] enum + its shape-level
11289 /// `hash_discriminator` and either an existing carving OR a fresh
11290 /// sub-algebra — the outer-Atom scalar itself stays untouched
11291 /// unless the new variant is also an atomic-payload arm.
11292 pub(crate) const OUTER_HASH_DISCRIMINATOR: u8 = 1;
11293
11294 /// Project the typed marker to the canonical `&'static str`
11295 /// diagnostic label — `"symbol"` for [`Self::Symbol`],
11296 /// `"keyword"` for [`Self::Keyword`], `"string"` for [`Self::Str`]
11297 /// (the wire-shape rename `Str → "string"` matches the
11298 /// [`SexpShape::String`] label projection), `"int"` for
11299 /// [`Self::Int`], `"float"` for [`Self::Float`], `"bool"` for
11300 /// [`Self::Bool`]. Each label is byte-for-byte identical to the
11301 /// corresponding [`SexpShape`] variant's label — and post-lift this
11302 /// agreement is STRUCTURAL rather than two literal-discipline sites
11303 /// pinned by a cross-projection test.
11304 ///
11305 /// Composition law: `AtomKind::label(k) ==
11306 /// AtomKind::sexp_shape(k).label()` for every `k: AtomKind`. The
11307 /// body composes [`Self::sexp_shape`] (the typed projection lifting
11308 /// each AtomKind variant into its peer [`SexpShape`] variant) with
11309 /// [`SexpShape::label`] (the canonical `&'static str` projection on
11310 /// the supeset's twelve-variant closed set), so the six atomic-arm
11311 /// labels live at ONE canonical site ([`SexpShape::label`]) rather
11312 /// than at TWO ([`SexpShape::label`] AND a parallel six-arm match
11313 /// here, pre-lift). Pre-lift the substrate-wide AtomKind ⊂ SexpShape
11314 /// label-vocabulary agreement was enforced by literal discipline at
11315 /// the two sites + a cross-projection test
11316 /// (`atom_kind_label_agrees_with_sexp_shape_label_for_every_atom_arm`);
11317 /// post-lift the agreement is a TYPED CONSEQUENCE of the composition
11318 /// — a typo in `SexpShape::label`'s atomic arms is a typo in BOTH
11319 /// projections, and the cross-projection test is true by
11320 /// construction. Same lift posture as the prior-run
11321 /// `Atom::as_X → Atom::as_X` algebra-lift commit (6935416), the
11322 /// `from_lexeme` reader-atom lift commit (9b95e64), and the
11323 /// `to_iac_forge_sexpr` Atom-arm lift commit (418be51): the typed
11324 /// projection sits on the value, and the consumer composes through
11325 /// the existing structural pairing rather than re-deriving the
11326 /// per-variant literal.
11327 ///
11328 /// The `&'static str` lifetime is load-bearing: it lets the
11329 /// variant project through this method without an allocation,
11330 /// parallel to how [`SexpShape::label`], [`QuoteForm::prefix`],
11331 /// [`QuoteForm::iac_forge_tag`], [`UnquoteForm::marker`], and
11332 /// [`crate::error::ExpectedKwargShape::label`] project their
11333 /// respective closed-set surfaces. The composition preserves the
11334 /// no-allocation contract: [`Self::sexp_shape`] returns a `Copy`
11335 /// value and [`SexpShape::label`] yields `&'static str`, so the
11336 /// `&'static str` projection through the composition allocates
11337 /// nothing at runtime.
11338 ///
11339 /// The bidirectional contract is anchored by tests:
11340 /// `atom_kind_label_renders_canonical_string_for_every_variant`
11341 /// pins each variant's canonical literal so a typo in
11342 /// [`SexpShape::label`]'s atomic arms fails-loudly through this
11343 /// projection too, `atom_kind_display_matches_label_for_every_variant`
11344 /// pins Display-equals-label so any future
11345 /// `#[error("... got {got}")]` annotation that threads through
11346 /// this projection projects byte-for-byte, and
11347 /// `atom_kind_label_round_trips_through_from_str` pins the
11348 /// `label` ↔ [`Self::FromStr`] round-trip for every variant in
11349 /// [`Self::ALL`] so the typed surface and the rendered diagnostic
11350 /// literal cannot drift. The post-lift composition contract is
11351 /// pinned by
11352 /// `atom_kind_label_routes_through_sexp_shape_label_via_sexp_shape_projection`
11353 /// — a regression that re-inlines the six atomic-arm literals here
11354 /// and silently drifts ONE arm from the [`SexpShape::label`] axis
11355 /// fails the routing pin loudly without needing a per-variant
11356 /// cross-axis literal sweep.
11357 ///
11358 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
11359 /// AtomKind ⊂ SexpShape label-vocabulary containment becomes a
11360 /// TYPED CONSEQUENCE of the [`Self::sexp_shape`] + [`SexpShape::label`]
11361 /// composition rather than literal discipline at two sites. THEORY.md
11362 /// §VI.1 — generation over composition; the six atomic-arm labels
11363 /// live at ONE canonical site ([`SexpShape::label`]) and this method
11364 /// generates its identity through the typed-projection composition.
11365 /// THEORY.md §II.1 invariant 2 — free middle; FOUR consumers of the
11366 /// [`AtomKind`] algebra ([`Hash for Atom`] via
11367 /// [`Self::hash_discriminator`], [`crate::domain::sexp_shape`] via
11368 /// [`Self::sexp_shape`], the diagnostic-rendering surface via this
11369 /// method, and the `ClosedSet`-trait FromStr/Display surface via
11370 /// `#[closed_set(via = "label")]`) now route through ONE typed
11371 /// closed-set projection family with no per-consumer literal
11372 /// duplication.
11373 #[must_use]
11374 pub fn label(self) -> &'static str {
11375 self.sexp_shape().label()
11376 }
11377
11378 /// Stable, per-variant byte discriminator that paired with the
11379 /// recursive payload hash builds the substrate's [`Hash for Atom`]
11380 /// projection — `0u8` for [`Self::Symbol`], `1u8` for
11381 /// [`Self::Keyword`], `2u8` for [`Self::Str`], `3u8` for
11382 /// [`Self::Int`], `4u8` for [`Self::Float`], `5u8` for
11383 /// [`Self::Bool`]. The byte values are load-bearing because the
11384 /// macro-expansion cache ([`crate::macro_expand::Expander`]'s
11385 /// cache) keys on the hash of `(macro_name, args)`, and any
11386 /// `Atom` participates in that hash — changing a discriminator
11387 /// silently invalidates every cached expansion across the
11388 /// substrate.
11389 ///
11390 /// The closed set ensures the six arms partition `{0, 1, 2, 3,
11391 /// 4, 5}` injectively. Disjointness from [`QuoteForm`]'s
11392 /// `{3, 4, 5, 6}` is structural rather than overlap-induced
11393 /// hash collision: [`Hash for Atom`] and the quote-family arms of
11394 /// [`Hash for Sexp`] hash DISTINCT types (`Atom` vs `Sexp`), and
11395 /// `Atom`'s discriminator lives nested INSIDE `Sexp::Atom`'s outer
11396 /// `1u8` discriminator — the prefix-uniqueness contract that the
11397 /// `Hash for Sexp` outer match maintains independently. A future
11398 /// quote-family or atomic-kind extension must extend BOTH bodies'
11399 /// arms in lockstep, with rustc binding the consistency through
11400 /// exhaustiveness over BOTH closed enums.
11401 ///
11402 /// `pub(crate)` because the byte-discriminator surface is an
11403 /// implementation detail of the substrate's [`Hash for Atom`]
11404 /// cache-key contract; exposing it publicly would leak the
11405 /// cache-key shape through the API without enabling any external
11406 /// consumer the public projections ([`Atom::kind`], [`Self::label`],
11407 /// [`Self::sexp_shape`]) don't already serve. Same posture as
11408 /// [`QuoteForm::hash_discriminator`] and its outer-value peer
11409 /// [`Atom::hash_discriminator`] (the outer-`Atom` projection that
11410 /// composes through this method via `self.kind().hash_discriminator()`
11411 /// so the [`Hash for Atom`] callsite binds at ONE site on the
11412 /// outer-`Atom` algebra rather than at the two-hop
11413 /// `.kind().hash_discriminator()` chain).
11414 #[must_use]
11415 pub(crate) fn hash_discriminator(self) -> u8 {
11416 match self {
11417 Self::Symbol => Self::SYMBOL_HASH_DISCRIMINATOR,
11418 Self::Keyword => Self::KEYWORD_HASH_DISCRIMINATOR,
11419 Self::Str => Self::STR_HASH_DISCRIMINATOR,
11420 Self::Int => Self::INT_HASH_DISCRIMINATOR,
11421 Self::Float => Self::FLOAT_HASH_DISCRIMINATOR,
11422 Self::Bool => Self::BOOL_HASH_DISCRIMINATOR,
11423 }
11424 }
11425
11426 /// Canonical [`SexpShape`] embed target for the [`Self::Symbol`]
11427 /// atomic-payload arm on the AtomKind ⊂ SexpShape 6-of-12 carving —
11428 /// [`SexpShape::Symbol`]. Per-role peer of `Self::Symbol` on the
11429 /// closed-set atomic-payload → outer-shape embed axis; consumers
11430 /// reach for `AtomKind::SYMBOL_SHAPE` when the caller has a variant
11431 /// in hand at compile time and wants the canonical outer-shape
11432 /// identity without runtime dispatch through [`Self::sexp_shape`].
11433 ///
11434 /// Sibling posture to the six pre-existing per-role LABEL /
11435 /// HASH_DISCRIMINATOR aliases on this same closed-set algebra
11436 /// ([`Self::SYMBOL_LABEL`], [`Self::SYMBOL_HASH_DISCRIMINATOR`]) —
11437 /// each closes a distinct per-role sub-vocabulary axis on the
11438 /// AtomKind carving. This constant closes the THIRD per-role
11439 /// axis on [`AtomKind`] (the `SexpShape`-embed axis, paired with
11440 /// the pre-existing `&'static str` diagnostic-label axis + the
11441 /// `u8` cache-key axis) at ONE typed alias through the peer
11442 /// superset variant on the [`SexpShape`] closed set.
11443 pub const SYMBOL_SHAPE: SexpShape = SexpShape::Symbol;
11444
11445 /// Canonical [`SexpShape`] embed target for the [`Self::Keyword`]
11446 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11447 /// [`SexpShape::Keyword`]. Per-role peer of `Self::Keyword`.
11448 pub const KEYWORD_SHAPE: SexpShape = SexpShape::Keyword;
11449
11450 /// Canonical [`SexpShape`] embed target for the [`Self::Str`]
11451 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11452 /// [`SexpShape::String`]. Per-role peer of `Self::Str`; the
11453 /// `Str → String` wire-shape rename matches the peer
11454 /// [`Self::STRING_LABEL`] alias (both bind the AtomKind subset's
11455 /// `Str` variant to the SexpShape superset's `String` variant on
11456 /// their respective per-role sub-vocabulary axes).
11457 pub const STR_SHAPE: SexpShape = SexpShape::String;
11458
11459 /// Canonical [`SexpShape`] embed target for the [`Self::Int`]
11460 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11461 /// [`SexpShape::Int`]. Per-role peer of `Self::Int`.
11462 pub const INT_SHAPE: SexpShape = SexpShape::Int;
11463
11464 /// Canonical [`SexpShape`] embed target for the [`Self::Float`]
11465 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11466 /// [`SexpShape::Float`]. Per-role peer of `Self::Float`.
11467 pub const FLOAT_SHAPE: SexpShape = SexpShape::Float;
11468
11469 /// Canonical [`SexpShape`] embed target for the [`Self::Bool`]
11470 /// atomic-payload arm on the AtomKind ⊂ SexpShape carving —
11471 /// [`SexpShape::Bool`]. Per-role peer of `Self::Bool`.
11472 pub const BOOL_SHAPE: SexpShape = SexpShape::Bool;
11473
11474 /// Closed-set forced-arity ALL array over the canonical
11475 /// [`SexpShape`] embed targets on the AtomKind ⊂ SexpShape
11476 /// 6-of-12 carving, in declaration order matching [`Self::ALL`]
11477 /// element-wise (pinned by
11478 /// `atom_kind_shapes_align_with_all_by_index`). Sibling posture
11479 /// to [`Self::LABELS`] (`[&'static str; 6]` — per-role diagnostic
11480 /// bytes) and [`Self::HASH_DISCRIMINATORS`] (`[u8; 6]` — per-role
11481 /// nested-Atom cache-key bytes) on the SAME closed-set AtomKind
11482 /// algebra; where those two arrays lift the per-role
11483 /// `&'static str` and `u8` sub-vocabularies onto the substrate,
11484 /// this array lifts the per-role [`SexpShape`] embed-target
11485 /// sub-vocabulary at the same `[_; 6]` forced arity.
11486 ///
11487 /// Pre-lift the six [`SexpShape`] embed targets had NO per-role
11488 /// primitive on this closed-set algebra — a consumer with an
11489 /// `AtomKind` variant in hand at compile time reaching for the
11490 /// canonical embed target had to spell
11491 /// `AtomKind::Symbol.sexp_shape()` (runtime dispatch through the
11492 /// six-arm match body) OR re-derive the AtomKind ⊂ SexpShape
11493 /// variant pairing at the call site by importing both enums and
11494 /// spelling `SexpShape::Symbol` inline. Post-lift the SIX
11495 /// canonical embed targets bind at ONE `pub const` per role on
11496 /// the typed [`AtomKind`] algebra AND at [`Self::SHAPES`] as a
11497 /// family-wide forced-arity array — a future LSP / REPL
11498 /// completion bar keyed on `AtomKind::SHAPES` for the "which
11499 /// SexpShape does this AtomKind embed into?" outer-shape column,
11500 /// a `tatara-check` coverage sweep zipping `AtomKind::ALL` /
11501 /// `LABELS` / `HASH_DISCRIMINATORS` / `SHAPES` in lockstep for a
11502 /// family-wide (variant, label, byte, embed-target) quadruple
11503 /// render, or a Sekiban audit-trail metric jointly labeled by
11504 /// the embed-target's SexpShape identity reads through the typed
11505 /// constants on this subset algebra without re-deriving the
11506 /// 6-of-12 carving inline.
11507 ///
11508 /// Round-trip identity with the inverse projection
11509 /// [`crate::error::SexpShape::as_atom_kind`]: for every index `i`,
11510 /// `Self::SHAPES[i].as_atom_kind() == Some(Self::ALL[i])`
11511 /// (pinned by
11512 /// `atom_kind_shapes_align_with_all_by_index_through_as_atom_kind`) —
11513 /// the embed / project section closes as a family-wide array-
11514 /// indexed law rather than as a per-variant assertion sweep.
11515 /// Adding a hypothetical seventh atomic kind (e.g. `Char` for
11516 /// `#\x` reader syntax, `Bigint` for arbitrary-precision
11517 /// integers) extends [`Self::ALL`] AND [`Self::SHAPES`] AND
11518 /// [`SexpShape::ALL`] AND adds ONE per-role `pub const *_SHAPE`
11519 /// in lockstep — rustc's forced-arity check on the two `[_; N]`
11520 /// arrays fails compilation if EITHER ALL array grows without
11521 /// the other, AND the peer [`SexpShape::as_atom_kind`] arm must
11522 /// grow in lockstep to preserve the round-trip identity.
11523 ///
11524 /// Theory anchor: THEORY.md §III — the typescape; the six
11525 /// canonical [`SexpShape`] embed targets bind at ONE typed
11526 /// `[SexpShape; 6]` array on the closed-set AtomKind algebra
11527 /// rather than at zero-primitive-on-this-subset-plus-six-inline-
11528 /// lookups scattered across the substrate. THEORY.md §V.1 —
11529 /// knowable platform; the family's cardinality becomes a TYPE-
11530 /// level constant on the substrate algebra rather than a per-
11531 /// consumer runtime dispatch through the composition. THEORY.md
11532 /// §II.1 invariant 2 — free middle; the (embed, project) pair
11533 /// binds at THREE typed sites now — the projection method
11534 /// [`Self::sexp_shape`], this family-wide array, AND the peer
11535 /// inverse [`crate::error::SexpShape::as_atom_kind`] — with
11536 /// rustc-enforced consistency across all three. THEORY.md §VI.1
11537 /// — generation over composition; the family-wide contract
11538 /// sweeps (alignment with `ALL`, round-trip through
11539 /// `as_atom_kind`, membership through `sexp_shape`) emerge from
11540 /// the composition of TWO substrate primitives (this `pub const`
11541 /// array + the six per-role `pub const *_SHAPE` aliases) rather
11542 /// than as per-variant inline assertions duplicated at each call
11543 /// site.
11544 pub const SHAPES: [SexpShape; 6] = [
11545 Self::SYMBOL_SHAPE,
11546 Self::KEYWORD_SHAPE,
11547 Self::STR_SHAPE,
11548 Self::INT_SHAPE,
11549 Self::FLOAT_SHAPE,
11550 Self::BOOL_SHAPE,
11551 ];
11552
11553 /// Project the typed marker into its matching [`SexpShape`]
11554 /// variant — `Symbol → SexpShape::Symbol`, `Keyword →
11555 /// SexpShape::Keyword`, `Str → SexpShape::String`, `Int →
11556 /// SexpShape::Int`, `Float → SexpShape::Float`, `Bool →
11557 /// SexpShape::Bool`. ONE projection on the closed-set atomic-
11558 /// payload algebra that [`crate::domain::sexp_shape`]'s outer-shape
11559 /// projection routes through for the six atom arms — so the
11560 /// (Atom variant, SexpShape variant) pairing binds at ONE site on
11561 /// the typed algebra rather than at six byte-identical inline arms
11562 /// in [`crate::domain::sexp_shape`]. Direct sibling to
11563 /// [`QuoteForm::sexp_shape`] — that closed enum carves the
11564 /// quote-family arms of [`SexpShape`]'s twelve-variant closed set,
11565 /// while this enum carves the atomic-payload arms.
11566 ///
11567 /// Each arm routes through the per-role `pub const` on `impl Self`
11568 /// ([`Self::SYMBOL_SHAPE`], [`Self::KEYWORD_SHAPE`],
11569 /// [`Self::STR_SHAPE`], [`Self::INT_SHAPE`], [`Self::FLOAT_SHAPE`],
11570 /// [`Self::BOOL_SHAPE`]) so the six canonical embed targets bind
11571 /// at ONE typed source of truth per role rather than as inline
11572 /// `SexpShape::X` literals scattered across the `match` body.
11573 /// Sibling posture to [`Self::label`]'s composition through
11574 /// [`Self::sexp_shape().label()`] and [`Self::hash_discriminator`]'s
11575 /// per-role routing through [`Self::SYMBOL_HASH_DISCRIMINATOR`] …
11576 /// [`Self::BOOL_HASH_DISCRIMINATOR`] — the three per-role axes on
11577 /// the AtomKind algebra (embed target, diagnostic label, cache-key
11578 /// byte) each surface their per-role bytes through the SAME
11579 /// per-role `pub const` shape.
11580 ///
11581 /// Composition law: for every [`Atom`] `a`,
11582 /// `crate::domain::sexp_shape(&Sexp::Atom(a.clone())) ==
11583 /// a.kind().sexp_shape()`. Pinned by the cross-projection round-trip
11584 /// test in this module, so a regression that drifts either side
11585 /// of the typed algebra (an [`Atom::kind`] arm or this
11586 /// [`Self::sexp_shape`] arm) surfaces immediately rather than as a
11587 /// silent operator-facing diagnostic drift at every
11588 /// `LispError::TypeMismatch.got` slot for an atomic witness.
11589 ///
11590 /// Post-lift routing pin
11591 /// `atom_kind_sexp_shape_routes_through_typed_per_role_constants`
11592 /// catches a regression that re-inlines the six `SexpShape::X`
11593 /// arm literals here and silently drifts ONE arm from the per-role
11594 /// `pub const` alias — the routing agreement is a TYPED CONSEQUENCE
11595 /// of the composition rather than literal discipline at two sites.
11596 ///
11597 /// Bidirectional dual: the inverse projection
11598 /// [`crate::error::SexpShape::as_atom_kind`] (12→6, partial)
11599 /// covers the 6-of-12 carving of [`SexpShape`] this embed
11600 /// reaches. The pair `(AtomKind::sexp_shape,
11601 /// SexpShape::as_atom_kind)` forms an `Iso(AtomKind, AtomShape ⊂
11602 /// SexpShape)`: every typed marker round-trips through the embed
11603 /// (`AtomKind::sexp_shape(k).as_atom_kind() == Some(k)` for every
11604 /// `k: AtomKind`), every atom-shape pre-image recovers the typed
11605 /// marker. The non-atom shapes (`Nil`, `List`, every quote-family
11606 /// wrapper) form the kernel of the inverse — `as_atom_kind`
11607 /// returns `None` for them. See [`crate::error::SexpShape::as_atom_kind`]'s
11608 /// docstring for the composition law's other direction +
11609 /// disjointness with the quote-family sibling
11610 /// `SexpShape::as_quote_form`.
11611 ///
11612 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (Atom
11613 /// variant, SexpShape variant) pairing becomes a TYPE projection
11614 /// on the substrate algebra rather than six inline arms in
11615 /// [`crate::domain::sexp_shape`]. A typo or swap at the shape-
11616 /// projection site is no longer a runtime drift but a compile
11617 /// error against the typed projection. THEORY.md §II.1 invariant
11618 /// 2 — free middle; THREE consumers ([`Hash for Atom`] via
11619 /// [`Self::hash_discriminator`], [`crate::domain::sexp_shape`]
11620 /// via this method, and the future diagnostic / completion surface
11621 /// via [`Self::label`]) now route through ONE typed closed-set
11622 /// match family, so a regression that drifts ONE consumer's
11623 /// pairing from the others cannot reach the substrate's runtime.
11624 #[must_use]
11625 pub fn sexp_shape(self) -> SexpShape {
11626 match self {
11627 Self::Symbol => Self::SYMBOL_SHAPE,
11628 Self::Keyword => Self::KEYWORD_SHAPE,
11629 Self::Str => Self::STR_SHAPE,
11630 Self::Int => Self::INT_SHAPE,
11631 Self::Float => Self::FLOAT_SHAPE,
11632 Self::Bool => Self::BOOL_SHAPE,
11633 }
11634 }
11635}
11636
11637// `impl fmt::Display for AtomKind` + `impl std::str::FromStr for AtomKind`
11638// + `impl tatara_closed_set::ClosedSet for AtomKind` + `pub struct UnknownAtomKind(pub
11639// String)` are generated by `#[derive(tatara_closed_set::DeriveClosedSet)]` on
11640// the enum declaration above. `label` delegates to the inherent
11641// `AtomKind::label` via `#[closed_set(via = "label")]` so the
11642// domain-canonical lowercase-vocabulary projection stays load-bearing (the
11643// six labels `"symbol" / "keyword" / "string" / "int" / "float" / "bool"`
11644// match the `SexpShape` atomic-subset labels byte-for-byte AND the
11645// diagnostic-rendering shape `LispError::TypeMismatch.got` keys on
11646// verbatim). The `display` flag emits the substrate-wide
11647// `f.write_str(Self::label(*self))` block. `#[closed_set(generate_unknown =
11648// "atom kind")]` emits the typed parse-rejection carrier with the
11649// substrate-wide `Debug + Clone + PartialEq + Eq + thiserror::Error`
11650// derives and the `#[error("unknown atom kind: {0}")]` annotation
11651// byte-for-byte; the explicit label pins the pre-lift wording even though
11652// the auto-derived `pascal_to_spaced_lowercase("AtomKind")` projects to
11653// the same `"atom kind"` literal.
11654
11655impl Sexp {
11656 /// Canonical `(` char that opens a [`Self::List`] rendering AND
11657 /// (paired with [`Self::LIST_CLOSE`]) the empty [`Self::Nil`]
11658 /// rendering `()`. Outer-structural peer of [`Atom::STR_DELIMITER`]
11659 /// on the atomic-payload delimiter axis: where `STR_DELIMITER` is
11660 /// the ONE `"` byte the reader's tokenizer's FOUR
11661 /// `Token::Str`-round-trip sites bind to on the closed-set [`Atom`]
11662 /// algebra, `LIST_OPEN` is the ONE `(` byte the reader's tokenizer's
11663 /// `Token::LParen` outer-dispatch arm AND the bare-atom terminator
11664 /// disjunct AND [`fmt::Display for Sexp`]'s list-opening emission
11665 /// AND [`Self::Nil`]'s two-char `()` rendering all bind to on the
11666 /// closed-set outer [`Sexp`] algebra.
11667 ///
11668 /// Pre-lift the same `'('` byte lived inline at FOUR sites: two
11669 /// outer-match arms in `crate::reader::tokenize` (the
11670 /// `Token::LParen` construction arm AND the bare-atom terminator's
11671 /// `|| ch == '('` disjunct), and two Display arms in [`fmt::Display
11672 /// for Sexp`] (the `Self::List(_)` opener AND the `Self::Nil`
11673 /// two-char `()` rendering's left char). Post-lift the (typed
11674 /// structural role, canonical byte) pairing binds at ONE constant
11675 /// on the [`Sexp`] algebra that every consumer routes through; a
11676 /// refactor that swaps the byte (e.g. a Racket-style port to `[`
11677 /// for square-bracket list literals, an S-expression-DSL port to
11678 /// `{` for brace-list syntax) touches ONE constant rather than
11679 /// four inline bytes that would silently drift out of round-trip
11680 /// agreement if one was updated without the others.
11681 ///
11682 /// Load-bearing paired-delimiter contract:
11683 /// `Sexp::LIST_OPEN` MUST pair section-for-retraction with
11684 /// [`Self::LIST_CLOSE`] at every round-trip site — the reader's
11685 /// `Token::LParen` (from `LIST_OPEN`) MUST be closed by a
11686 /// `Token::RParen` (from `LIST_CLOSE`) for a well-formed list,
11687 /// and the Display impl's `Self::List(_)` arm MUST emit
11688 /// `LIST_OPEN` at the opener AND `LIST_CLOSE` at the closer for
11689 /// the reader-then-Display round trip
11690 /// `parse(display(list)) == list` to hold. Guards the paired
11691 /// disjointness across the closed-set outer [`Sexp`] algebra so a
11692 /// future refactor that renames one constant without updating the
11693 /// other fails at rustc / test time rather than as a silent list-
11694 /// rendering asymmetry.
11695 ///
11696 /// Cross-axis disjointness with the sibling delimiters (pinned
11697 /// structurally at
11698 /// `sexp_list_delimiters_distinct_from_every_other_algebra_marker`):
11699 /// `LIST_OPEN`'s byte MUST differ from [`Atom::STR_DELIMITER`]
11700 /// (`'"'`), [`Atom::KEYWORD_MARKER`] (`":"`), the two
11701 /// [`Atom::bool_literal`] spellings (`"#t"` / `"#f"`) AND every
11702 /// [`QuoteForm::lead_char`] projection (`'\''`, `` '`' ``, `','`)
11703 /// on the substrate's outer-marker axes. Otherwise the reader's
11704 /// `Token::LParen` outer-dispatch arm would ambiguously route
11705 /// through a sibling algebra's arm.
11706 ///
11707 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
11708 /// (`Self::List` outer structure, canonical `(` opener) pairing
11709 /// now binds at ONE constant on the closed-set outer [`Sexp`]
11710 /// algebra regardless of which of the four consumer surfaces
11711 /// reaches in. THEORY.md §VI.1 — generation over composition;
11712 /// four byte-identical inline `'('` char literals across two
11713 /// substrate files collapse onto ONE named constant, matching
11714 /// the substrate's three-times rule. THEORY.md §V.1 — knowable
11715 /// platform; the canonical list-opener byte becomes a
11716 /// TYPE-level constant on the outer substrate algebra rather
11717 /// than four inline bytes at four consumer surfaces across two
11718 /// substrate files (`crate::reader` and `crate::ast`).
11719 pub const LIST_OPEN: char = '(';
11720
11721 /// Canonical `)` char that closes a [`Self::List`] rendering AND
11722 /// (paired with [`Self::LIST_OPEN`]) the empty [`Self::Nil`]
11723 /// rendering `()`. See [`Self::LIST_OPEN`] for the substrate-wide
11724 /// paired-delimiter contract, cross-axis disjointness, and theory
11725 /// anchors — this constant is its section-for-retraction sibling
11726 /// on the closer axis. Four consumer sites (the reader's
11727 /// `Token::RParen` outer-dispatch arm, the bare-atom terminator's
11728 /// `|| ch == ')'` disjunct, [`fmt::Display for Sexp`]'s
11729 /// `Self::List(_)` closer, [`Self::Nil`]'s two-char `()`
11730 /// rendering's right char) all bind here.
11731 pub const LIST_CLOSE: char = ')';
11732
11733 /// Canonical paired list-delimiter closed-set ALL array — composes
11734 /// [`Self::LIST_OPEN`] followed by [`Self::LIST_CLOSE`] in canonical
11735 /// declaration order, forced-arity `[char; 2]` so a hypothetical
11736 /// third list-delimiter row would extend this array + one algebra
11737 /// constant in lockstep. Peer to [`Atom::SELF_ESCAPE_TABLE`]
11738 /// (`[char; 2]` on the Str-payload self-escape sub-vocabulary axis
11739 /// of the closed-set [`Atom`] algebra): where `SELF_ESCAPE_TABLE`
11740 /// closes the two pattern-EQUALS-value inner-tokenizer arms of
11741 /// `Atom::decode_str_escape` as ONE typed forced-arity array,
11742 /// `LIST_DELIMITERS` closes the two outer-structural list-delimiter
11743 /// arms of the reader's outer-dispatch as the analogous typed
11744 /// forced-arity array one axis over on the closed-set outer
11745 /// [`Sexp`] algebra.
11746 ///
11747 /// Pre-lift the two-element `[Self::LIST_OPEN, Self::LIST_CLOSE]`
11748 /// composition lived inline at TWO sites: the two `|| ch ==
11749 /// Self::LIST_{OPEN,CLOSE}` disjuncts inside
11750 /// [`Self::is_bare_atom_boundary`]'s reader-boundary projection
11751 /// (spanning TWO of the SIX categories the projection carries), AND
11752 /// the `[Sexp::LIST_OPEN, Sexp::LIST_CLOSE].iter().collect()` array
11753 /// literal at the `Nil` Display composition-pin test that binds the
11754 /// two-char `()` rendering to the two typed constants. Post-lift
11755 /// the sub-vocabulary sweep binds at ONE typed forced-arity array
11756 /// on the closed-set outer [`Sexp`] algebra rather than at two
11757 /// inline algebra-constant enumerations per consumer. Adding a
11758 /// hypothetical Racket-compat square-bracket list mode
11759 /// (`[Self::LIST_OPEN, Self::LIST_CLOSE, Self::VEC_OPEN,
11760 /// Self::VEC_CLOSE]`) would extend `LIST_DELIMITERS` ONCE +
11761 /// `Self::is_bare_atom_boundary`'s sub-vocabulary sweep ONCE + two
11762 /// new algebra constants (opener + closer) in lockstep; rustc's
11763 /// forced-arity check on `[char; N]` binds the extension through
11764 /// the array declaration site.
11765 ///
11766 /// Structural invariant carried at the SHAPE level: `[char; 2]`
11767 /// pairs section-for-retraction one-to-one with
11768 /// [`Atom::SELF_ESCAPE_TABLE`]'s `[char; 2]` — the two arrays
11769 /// sit on distinct closed-set algebras (outer-structural
11770 /// list-delimiter vocabulary on [`Sexp`]; inner-Str-payload
11771 /// self-escape vocabulary on [`Atom`]) but share the same
11772 /// forced-arity shape at their respective sub-vocabulary
11773 /// axes. A consumer that reaches for one of the two arrays
11774 /// encodes its vocabulary's paired-role identity in the SHAPE
11775 /// it iterates rather than in a per-site convention.
11776 ///
11777 /// Composition law (round-trip): `LIST_DELIMITERS[0] ==
11778 /// Self::LIST_OPEN` AND `LIST_DELIMITERS[1] == Self::LIST_CLOSE`
11779 /// AND `LIST_DELIMITERS.len() == 2`. The forced-arity + canonical
11780 /// declaration order together pin every downstream index-sweep
11781 /// consumer to the (opener, closer) pairing at rustc time; a
11782 /// reorder without reordering the underlying algebra constants
11783 /// fails at the composition pin below.
11784 ///
11785 /// Cross-axis disjointness pinned structurally at
11786 /// [`sexp_list_delimiters_distinct_from_every_other_algebra_marker`]:
11787 /// neither element aliases any sibling outer-marker char on the
11788 /// substrate's other closed-set algebras — the Str-payload
11789 /// delimiter (`Atom::STR_DELIMITER`), the Keyword-marker prefix
11790 /// (`Atom::KEYWORD_MARKER_LEAD`), the Comment-lead byte
11791 /// (`Self::COMMENT_LEAD`), every quote-family lead char
11792 /// (`QuoteForm::lead_char`), and every Bool-literal spelling's
11793 /// first char.
11794 ///
11795 /// Theory anchor: THEORY.md §III — the typescape; the paired
11796 /// (opener, closer) list-delimiter sub-vocabulary becomes a typed
11797 /// forced-arity ALL array on the closed-set outer [`Sexp`]
11798 /// algebra rather than as two inline algebra-constant
11799 /// enumerations at every consumer that iterates the paired
11800 /// delimiter axis. THEORY.md §V.1 — knowable platform; the
11801 /// paired-delimiter sub-vocabulary now binds as load-bearing
11802 /// typed data at the algebra level rather than as two per-site
11803 /// disjuncts. THEORY.md §VI.1 — generation over composition; the
11804 /// paired-delimiter (opener + closer) composition regenerates
11805 /// identically through this ONE typed forced-arity array rather
11806 /// than through two inline algebra-constant enumerations per
11807 /// consumer.
11808 pub const LIST_DELIMITERS: [char; 2] = [Self::LIST_OPEN, Self::LIST_CLOSE];
11809
11810 /// Canonical `;` char that begins a line-comment run in the reader's
11811 /// tokenizer AND (as a bare-atom terminator disjunct) breaks a
11812 /// `Token::Atom` accumulator when the byte is encountered mid-lexeme.
11813 /// Outer-structural peer of [`Self::LIST_OPEN`] / [`Self::LIST_CLOSE`]
11814 /// on the reader-discard axis: where `LIST_OPEN` / `LIST_CLOSE` are
11815 /// the paired-delimiter constants that shape a `Sexp::List` payload
11816 /// on the closed-set outer [`Sexp`] algebra, `COMMENT_LEAD` is the
11817 /// ONE `;` byte the reader's tokenizer's TWO comment-boundary sites
11818 /// bind to on the same outer algebra — the outer-dispatch arm that
11819 /// begins a line-comment run (consuming through the trailing `\n`
11820 /// which is itself absorbed by the whitespace disjunct in the outer
11821 /// match) AND the bare-atom terminator disjunct that ends a
11822 /// `Token::Atom` accumulator when it encounters this byte mid-lexeme
11823 /// so a bare `foo;bar` source tokenizes as `Token::Atom("foo") @ 0`
11824 /// followed by a discarded line-comment run rather than as ONE
11825 /// `Token::Atom("foo;bar")` payload.
11826 ///
11827 /// Pre-lift the same `';'` byte lived inline at TWO sites in
11828 /// `crate::reader::tokenize`: the outer-match `';' => { … }`
11829 /// line-comment arm AND the bare-atom terminator's `|| ch == ';'`
11830 /// disjunct. Post-lift the (reader-discard role, canonical byte)
11831 /// pairing binds at ONE constant on the [`Sexp`] algebra that both
11832 /// consumer sites route through; a refactor that swaps the byte
11833 /// (e.g. a Scheme R7RS-style port to `#;` datum-comment syntax, an
11834 /// Emacs-style port to `#!` shebang-comment syntax) touches ONE
11835 /// constant rather than two inline bytes that would silently drift
11836 /// out of tokenizer agreement if one was updated without the other.
11837 ///
11838 /// Reader-discard contract: `Sexp::COMMENT_LEAD` MUST NOT surface
11839 /// as an atomic payload in any parsed [`Sexp`] — the outer-dispatch
11840 /// arm consumes the byte AND every char up to (but not past) the
11841 /// trailing `\n`, emitting NO token. The bare-atom terminator
11842 /// disjunct breaks the `Token::Atom` accumulator EXACTLY on this
11843 /// byte so the subsequent line-comment run reaches the outer arm
11844 /// with its byte-offset intact. Both sites bind to ONE constant so
11845 /// a regression that drifts ONE of the two disjuncts (e.g.
11846 /// re-inlines `';'` at the outer arm while migrating the terminator
11847 /// to a different byte) fails at rustc / test time rather than as
11848 /// a silent tokenizer misclassification.
11849 ///
11850 /// Cross-axis disjointness with the sibling markers (pinned
11851 /// structurally at
11852 /// `sexp_comment_lead_distinct_from_every_other_algebra_marker`):
11853 /// `COMMENT_LEAD`'s byte MUST differ from [`Self::LIST_OPEN`]
11854 /// (`'('`), [`Self::LIST_CLOSE`] (`')'`), [`Atom::STR_DELIMITER`]
11855 /// (`'"'`), [`Atom::KEYWORD_MARKER`]'s lead byte (`':'`), the two
11856 /// [`Atom::bool_literal`] spellings' lead byte (`'#'`) AND every
11857 /// [`QuoteForm::lead_char`] projection (`'\''`, `` '`' ``, `','`)
11858 /// on the substrate's outer-marker axes. Otherwise a bare `;foo`
11859 /// lexeme would ambiguously route through the line-comment arm AND
11860 /// a sibling algebra's arm at the reader's outer dispatch.
11861 ///
11862 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
11863 /// (reader-discard role, canonical `;` byte) pairing binds at ONE
11864 /// constant on the closed-set outer [`Sexp`] algebra regardless of
11865 /// which of the two consumer sites reaches in. THEORY.md §VI.1 —
11866 /// generation over composition; two byte-identical inline `';'`
11867 /// char literals across ONE substrate file collapse onto ONE named
11868 /// constant, matching the substrate's three-times rule
11869 /// (`\geq 2` PRIME-DIRECTIVE trigger). THEORY.md §V.1 — knowable
11870 /// platform; the canonical comment-lead byte becomes a TYPE-level
11871 /// constant on the outer substrate algebra rather than two inline
11872 /// bytes at two consumer surfaces in `crate::reader`.
11873 pub const COMMENT_LEAD: char = ';';
11874
11875 /// Canonical `\n` char that terminates a line-comment run in the
11876 /// reader's tokenizer — the section-for-retraction sibling of
11877 /// [`Self::COMMENT_LEAD`] on the reader-discard axis. Paired
11878 /// opener/terminator peer of [`Self::LIST_OPEN`] /
11879 /// [`Self::LIST_CLOSE`] on the outer-structural axis: where
11880 /// [`Self::LIST_OPEN`] / [`Self::LIST_CLOSE`] are the two typed
11881 /// constants that shape a `Sexp::List` payload, [`Self::COMMENT_LEAD`]
11882 /// / [`Self::COMMENT_TERM`] are the two typed constants that shape
11883 /// the reader's line-comment discard run. Both pairs live on the
11884 /// closed-set outer [`Sexp`] algebra so the reader-discard axis
11885 /// carries the same opener/closer discipline the outer-structural
11886 /// axis has carried since the initial [`Self::LIST_OPEN`] /
11887 /// [`Self::LIST_CLOSE`] lift.
11888 ///
11889 /// Pre-lift the same `'\n'` byte lived inline at ONE site in
11890 /// `crate::reader::tokenize` — the line-comment discard loop's
11891 /// terminator check `if ch == '\n' { break; }`. Post-lift the
11892 /// (reader-discard terminator role, canonical byte) pairing binds
11893 /// at ONE constant on the [`Sexp`] algebra that the consumer site
11894 /// routes through; a refactor that ports the reader to a different
11895 /// line-break convention (e.g. Scheme R7RS `#;` datum-comment
11896 /// terminated at the next well-formed datum, an Emacs-style port
11897 /// with `\r\n` CRLF sequences, a Common-Lisp-style `#|…|#` block
11898 /// comment closed by `|#`) touches ONE constant (or extends the
11899 /// algebra by ONE peer method) rather than an inline byte.
11900 ///
11901 /// Reader-discard contract: `Sexp::COMMENT_TERM` MUST NOT surface
11902 /// as an atomic payload in any parsed [`Sexp`] — the reader's
11903 /// [`Self::COMMENT_LEAD`] outer-dispatch arm consumes every byte
11904 /// (INCLUDING this terminator) up to and including the FIRST
11905 /// occurrence of [`Self::COMMENT_TERM`], emitting NO token. The
11906 /// (lead, term) pair carries the SAME reader-discard invariant as
11907 /// (LIST_OPEN, LIST_CLOSE) does on the outer-structural axis: both
11908 /// bytes are structural markers that never appear in a token
11909 /// payload.
11910 ///
11911 /// Cross-axis disjointness with the sibling closed-set markers
11912 /// (pinned structurally at
11913 /// `sexp_comment_term_distinct_from_every_non_whitespace_algebra_marker`):
11914 /// `COMMENT_TERM`'s byte MUST differ from every NON-whitespace
11915 /// outer-marker char — [`Self::LIST_OPEN`], [`Self::LIST_CLOSE`],
11916 /// [`Self::COMMENT_LEAD`], [`Atom::STR_DELIMITER`],
11917 /// [`Atom::STR_ESCAPE_LEAD`], [`Atom::KEYWORD_MARKER`]'s lead byte,
11918 /// the two [`Atom::bool_literal`] spellings' lead byte, AND every
11919 /// [`QuoteForm::lead_char`] projection. The terminator IS a
11920 /// whitespace char (it satisfies `char::is_whitespace`) so the
11921 /// disjointness test explicitly excludes the whitespace-family
11922 /// axis: the terminator's role IS to be whitespace-family, so the
11923 /// disjointness contract binds only against the non-whitespace
11924 /// outer-marker axes.
11925 ///
11926 /// Interaction with the escape-decode codomain axis: the terminator
11927 /// byte `'\n'` COINCIDES with the C0 control byte
11928 /// [`Atom::decode_str_escape`]`('n')` produces — the two roles are
11929 /// distinct algebraic axes (reader-discard structural marker on
11930 /// the outer [`Sexp`] algebra vs. Str-escape shorthand codomain
11931 /// value on the inner [`Atom`] algebra) so the collision at the
11932 /// byte level is by design, NOT a disjointness violation. A `\n`
11933 /// byte APPEARING inside a `Token::Str` payload's decoded output
11934 /// (via `\n` shorthand) is orthogonal to a `\n` byte APPEARING as
11935 /// the line-comment terminator at the reader's outer-dispatch
11936 /// discard loop.
11937 ///
11938 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
11939 /// (reader-discard terminator role, canonical `\n` byte) pairing
11940 /// binds at ONE constant on the closed-set outer [`Sexp`] algebra
11941 /// regardless of which consumer reaches in. THEORY.md §II.1
11942 /// invariant 5 — composition preserves proofs; the paired
11943 /// (COMMENT_LEAD, COMMENT_TERM) shape now lives at the algebra
11944 /// alongside (LIST_OPEN, LIST_CLOSE), so the reader-discard axis
11945 /// carries the SAME opener/closer discipline as the outer-
11946 /// structural axis. THEORY.md §VI.1 — generation over composition;
11947 /// the reader's inline `'\n'` char literal at the line-comment
11948 /// discard loop's terminator check collapses onto ONE named
11949 /// constant on the substrate algebra. THEORY.md §V.1 — knowable
11950 /// platform; the canonical line-comment terminator byte becomes
11951 /// a TYPE-level constant on the outer substrate algebra rather
11952 /// than an inline `'\n'` at one consumer surface in
11953 /// `crate::reader::tokenize`.
11954 pub const COMMENT_TERM: char = '\n';
11955
11956 /// Canonical paired line-comment delimiter closed-set ALL array —
11957 /// composes [`Self::COMMENT_LEAD`] followed by [`Self::COMMENT_TERM`]
11958 /// in canonical (opener, terminator) declaration order, forced-arity
11959 /// `[char; 2]` so a hypothetical alternative comment convention (a
11960 /// Scheme R7RS `#;` datum-comment lead paired with a next-datum
11961 /// terminator, an Emacs-style CRLF paired terminator, a Common-Lisp
11962 /// `#|…|#` block comment closed by `|#`) would extend this array +
11963 /// one algebra constant per new row in lockstep — rustc's forced-
11964 /// arity check on `[char; N]` binds the extension through the array
11965 /// declaration site.
11966 ///
11967 /// Cross-axis peer to [`Self::LIST_DELIMITERS`] (`[char; 2]` on the
11968 /// outer-structural paired-delimiter axis of the SAME closed-set
11969 /// outer [`Sexp`] algebra) — both close a paired-role byte
11970 /// sub-vocabulary at the SAME shape (`[char; 2]`, `(opener,
11971 /// closer_or_terminator)` canonical declaration order) but on
11972 /// DISTINCT roles: [`Self::LIST_DELIMITERS`] closes the two typed
11973 /// constants that shape a [`Self::List`] payload
11974 /// ([`Self::LIST_OPEN`] / [`Self::LIST_CLOSE`], BOTH of which
11975 /// classify as bare-atom boundaries and both non-whitespace);
11976 /// [`Self::COMMENT_DELIMITERS`] closes the two typed constants that
11977 /// shape the reader's line-comment discard run
11978 /// ([`Self::COMMENT_LEAD`] / [`Self::COMMENT_TERM`], where the LEAD
11979 /// row is a bare-atom boundary AND non-whitespace, and the TERM row
11980 /// is a whitespace-family char absorbed by the reader's
11981 /// `ch.is_whitespace()` outer-dispatch arm rather than a distinct
11982 /// bare-atom boundary). The two arrays partition the SIX-category
11983 /// outer-dispatch arm-set into a per-axis paired shape (2 rows for
11984 /// list delimiters + 2 rows for comment delimiters + 1 row for
11985 /// [`Atom::STR_DELIMITER`] + 3-of-4 rows for [`QuoteForm::LEADS`] +
11986 /// the residual whitespace family), each axis carrying its own
11987 /// forced-arity ALL array.
11988 ///
11989 /// Also sibling-shape to [`Atom::SELF_ESCAPE_TABLE`] (`[char; 2]` on
11990 /// the inner-Str-payload self-escape sub-vocabulary axis of the
11991 /// closed-set [`Atom`] algebra), [`Atom::BOOL_LITERALS`]
11992 /// (`[&'static str; 2]` on the Scheme-bool spelling axis), and
11993 /// [`crate::error::UnquoteForm::MARKERS`] / [`crate::error::UnquoteForm::IAC_FORGE_TAGS`]
11994 /// (`[&'static str; 2]` on the template-substitution subset algebra
11995 /// — the 2-of-4 subset carving of [`QuoteForm`]) — every closed-set
11996 /// outer projection on the substrate that carries a paired-role
11997 /// two-row axis now pins its canonical bytes at ONE `pub const` per
11998 /// role plus a forced-arity ALL array for family-wide consumers.
11999 ///
12000 /// Pre-lift the two-element `[Self::COMMENT_LEAD, Self::COMMENT_TERM]`
12001 /// composition had NO typed source of truth on the substrate — the
12002 /// two constants each existed independently on the algebra
12003 /// ([`Self::COMMENT_LEAD`] shipped in the initial reader-discard
12004 /// lift; [`Self::COMMENT_TERM`] shipped in the follow-on paired-
12005 /// terminator lift `bb1bd5e`) and consumers that wanted the
12006 /// (opener, terminator) shape had to reach across the algebra
12007 /// through TWO `pub const` sites. Post-lift the paired-role
12008 /// sub-vocabulary binds at ONE forced-arity ALL array on the closed-
12009 /// set outer [`Sexp`] algebra alongside the peer
12010 /// [`Self::LIST_DELIMITERS`] array on the outer-structural axis; a
12011 /// consumer that walks EITHER axis of the outer-structural /
12012 /// reader-discard cross-product reads the paired-role identity off
12013 /// the shared `[char; 2]` shape.
12014 ///
12015 /// Structural invariant carried at the SHAPE level: [`char; 2`]
12016 /// pairs section-for-retraction one-to-one with
12017 /// [`Self::LIST_DELIMITERS`]'s `[char; 2]` — the two arrays sit at
12018 /// distinct roles on the SAME closed-set outer [`Sexp`] algebra
12019 /// (outer-structural payload-delimiter role for `LIST_DELIMITERS`;
12020 /// reader-discard opener/terminator role for `COMMENT_DELIMITERS`)
12021 /// but share the same forced-arity shape at their respective axes.
12022 /// A consumer that reaches for one of the two arrays encodes its
12023 /// axis's paired-role identity in the SHAPE it iterates rather than
12024 /// in a per-site convention.
12025 ///
12026 /// Composition law (round-trip): `COMMENT_DELIMITERS[0] ==
12027 /// Self::COMMENT_LEAD` AND `COMMENT_DELIMITERS[1] ==
12028 /// Self::COMMENT_TERM` AND `COMMENT_DELIMITERS.len() == 2`. The
12029 /// forced-arity + canonical declaration order together pin every
12030 /// downstream index-sweep consumer to the (opener, terminator)
12031 /// pairing at rustc time; a reorder without reordering the
12032 /// underlying algebra constants fails at the composition pin below.
12033 ///
12034 /// Path-uniformity contract pinned per-row: `COMMENT_DELIMITERS[0]`
12035 /// (the LEAD row) MUST classify as a bare-atom boundary via
12036 /// [`Self::is_bare_atom_boundary`] (the reader's outer-dispatch's
12037 /// dedicated line-comment arm is one of the SIX categories that
12038 /// projection covers), AND `COMMENT_DELIMITERS[1]` (the TERM row)
12039 /// MUST classify as a whitespace-family char via
12040 /// [`char::is_whitespace`] (the reader's `ch.is_whitespace()` arm
12041 /// absorbs the terminator so the discard loop's post-loop hand-off
12042 /// to the outer-dispatch fires the whitespace arm rather than a
12043 /// distinct comment-terminator arm). The per-row asymmetry is
12044 /// LOAD-BEARING and structurally distinct from
12045 /// [`Self::LIST_DELIMITERS`]'s BOTH-rows-are-bare-atom-boundaries
12046 /// contract (both `(` and `)` are non-whitespace outer-dispatch
12047 /// arms). Pinned by
12048 /// `sexp_comment_delimiters_lead_row_is_bare_atom_boundary` +
12049 /// `sexp_comment_delimiters_term_row_is_whitespace_family_char`.
12050 ///
12051 /// Cross-axis disjointness pinned structurally at
12052 /// `sexp_comment_delimiters_disjoint_from_list_delimiters`: no row
12053 /// of `COMMENT_DELIMITERS` aliases any row of
12054 /// [`Self::LIST_DELIMITERS`] — the reader-discard sub-vocabulary
12055 /// and the outer-structural list-delimiter sub-vocabulary partition
12056 /// their respective bytes disjointly on the SAME closed-set outer
12057 /// [`Sexp`] algebra. Cross-algebra disjointness pinned at
12058 /// `sexp_comment_delimiters_disjoint_from_str_delimiter`: no row
12059 /// aliases [`Atom::STR_DELIMITER`] — the reader-discard arm and the
12060 /// Str-payload arm partition their bytes across the two closed-set
12061 /// algebras disjointly.
12062 ///
12063 /// Future consumers that compose against [`Self::COMMENT_DELIMITERS`]:
12064 /// a hypothetical `tatara_lisp_comment_delimiter_total{delimiter=";"|"\n"}`
12065 /// Sekiban metric surface at Prometheus recording time — the
12066 /// label-set generator sweeps this array verbatim rather than
12067 /// re-typing the two paired bytes inline at each recorder, and
12068 /// rustc-binds the metric-label set to the closed set through the
12069 /// forced-arity ALL array; an LSP / structural-editor that
12070 /// highlights line-comment runs — the (opener, terminator) pair the
12071 /// editor spans over IS this array's two rows; a hypothetical
12072 /// `Sexp::BLOCK_COMMENT_DELIMITERS` peer array for a future
12073 /// `#|…|#` block-comment mode would follow the same shape
12074 /// mechanically, extending the reader-discard axis by ONE peer
12075 /// array without touching this one's shape.
12076 ///
12077 /// Theory anchor: THEORY.md §III — the typescape; the paired
12078 /// (opener, terminator) reader-discard sub-vocabulary of the
12079 /// reader's outer-dispatch arm-set now binds at ONE typed `[char;
12080 /// 2]` array on the closed-set outer [`Sexp`] algebra rather than
12081 /// as two independent algebra constants (`Self::COMMENT_LEAD`,
12082 /// `Self::COMMENT_TERM`) accessed independently at every consumer
12083 /// that wants the paired-role shape. The shared `[char; 2]` shape
12084 /// with [`Self::LIST_DELIMITERS`] encodes the paired-role identity
12085 /// relation across the two axes of the SAME closed-set algebra at
12086 /// the type system level. THEORY.md §V.1 — knowable platform; the
12087 /// paired-discard-delimiter sub-vocabulary becomes load-bearing
12088 /// typed data on the closed-set outer [`Sexp`] algebra. THEORY.md
12089 /// §VI.1 — generation over composition; the paired-delimiter
12090 /// (opener + terminator) composition regenerates identically
12091 /// through this ONE typed forced-arity array rather than through
12092 /// two independent algebra constants at every consumer. THEORY.md
12093 /// §II.1 invariant 5 — composition preserves proofs; the two-axis
12094 /// (outer-structural, reader-discard) cross-product on the closed-
12095 /// set outer [`Sexp`] algebra now carries the SAME opener/closer
12096 /// discipline on BOTH axes through two forced-arity ALL arrays
12097 /// with byte-identical shape.
12098 pub const COMMENT_DELIMITERS: [char; 2] = [Self::COMMENT_LEAD, Self::COMMENT_TERM];
12099
12100 /// Closed-set forced-arity ALL array over the SEVEN non-whitespace
12101 /// category-leading chars the reader's outer-dispatch cascade
12102 /// specialises on — the reader-level boundary sub-vocabulary that
12103 /// paired with `char::is_whitespace()` closes the six-clause
12104 /// [`Self::is_bare_atom_boundary`] disjunction. Composes through
12105 /// seven typed `pub const` primitives spanning THREE type namespaces
12106 /// on the SAME reader-outer-dispatch axis of the substrate:
12107 /// * [`Self::LIST_OPEN`] (`'('`) — the outer-structural list-opening
12108 /// delimiter on the outer [`Sexp`] algebra;
12109 /// * [`Self::LIST_CLOSE`] (`')'`) — the outer-structural list-closing
12110 /// delimiter on the outer [`Sexp`] algebra;
12111 /// * [`QuoteForm::QUOTE_LEAD`] (`'\''`) — the [`QuoteForm::Quote`]
12112 /// reader-punctuation lead byte on the quote-family sub-algebra;
12113 /// * [`QuoteForm::QUASIQUOTE_LEAD`] (`` '`' ``) — the
12114 /// [`QuoteForm::Quasiquote`] reader-punctuation lead byte;
12115 /// * [`QuoteForm::UNQUOTE_LEAD`] (`','`) — the shared
12116 /// [`QuoteForm::Unquote`] / [`QuoteForm::UnquoteSplice`] reader-
12117 /// punctuation lead byte (disambiguated at the second-char peek
12118 /// via [`QuoteForm::promote_via_next_char`]);
12119 /// * [`Atom::STR_DELIMITER`] (`'"'`) — the Str-payload opening /
12120 /// closing delimiter on the closed-set [`Atom`] algebra;
12121 /// * [`Self::COMMENT_LEAD`] (`';'`) — the line-comment opener on
12122 /// the outer [`Sexp`] algebra (paired with [`Self::COMMENT_TERM`]
12123 /// inside the discard loop — but the TERM is a run-boundary
12124 /// marker inside a comment run, NOT a reader-outer-dispatch
12125 /// category-leading char, so it is intentionally omitted from
12126 /// this ALL array).
12127 ///
12128 /// Cross-axis peer to [`Self::LIST_DELIMITERS`] (`[char; 2]` on the
12129 /// outer-structural payload-delimiter axis) and [`Self::COMMENT_DELIMITERS`]
12130 /// (`[char; 2]` on the reader-discard opener/terminator axis) at
12131 /// ONE algebra level up: those two arrays close their respective
12132 /// paired-role sub-vocabularies (opener + closer, opener + terminator)
12133 /// on the reader-INNER axis of the outer [`Sexp`] algebra; this
12134 /// array closes the reader-OUTER-dispatch category-leading char
12135 /// sub-vocabulary across the SAME closed-set outer [`Sexp`] algebra
12136 /// PLUS its two sibling sub-algebras ([`QuoteForm`], [`Atom`]) at
12137 /// ONE family-wide `[char; 7]` primitive. Sibling-shape peer of the
12138 /// intra-algebra sub-vocabulary array [`QuoteForm::LEADS`]
12139 /// (`[char; 3]` — the three DISTINCT quote-family reader-lead
12140 /// bytes) which this array embeds as its middle three positions:
12141 /// where [`QuoteForm::LEADS`] closes the quote-family sub-carving's
12142 /// reader-lead sub-vocabulary at ONE forced-arity array on ONE
12143 /// closed-set algebra, this array closes the FULL reader-outer-
12144 /// dispatch non-whitespace category-leading char sub-vocabulary at
12145 /// ONE forced-arity array on the outer [`Sexp`] algebra by
12146 /// composing through the three-arm quote-family sub-carving + the
12147 /// two-arm structural-delimiter sub-carving + the two-arm atomic-
12148 /// carve delimiter + comment-lead singletons.
12149 ///
12150 /// Pre-lift the seven category-leading chars had NO family-wide
12151 /// array on the outer [`Sexp`] algebra — [`Self::is_bare_atom_boundary`]
12152 /// carried them as three sub-expressions (`Self::LIST_DELIMITERS.contains(&ch)`
12153 /// on the structural-delimiter axis, `QuoteForm::from_lead_char(ch).is_some()`
12154 /// on the quote-family axis, `ch == Atom::STR_DELIMITER || ch ==
12155 /// Self::COMMENT_LEAD` on the singleton axes) unified through boolean
12156 /// disjunction. Post-lift the WHOLE non-whitespace terminator sub-
12157 /// vocabulary binds at ONE `pub const [char; 7]` array on the outer
12158 /// [`Sexp`] algebra so [`Self::is_bare_atom_boundary`] collapses to
12159 /// `ch.is_whitespace() || Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch)`
12160 /// — TWO clauses (one whitespace-partial predicate + one
12161 /// contains-check on the family-wide ARRAY) rather than five
12162 /// sub-clauses spanning three type namespaces. Consumers keyed on
12163 /// the whole family (a `tatara-check` predicate `(check-reader-
12164 /// outer-dispatch-terminator-partition-injective …)` that verifies
12165 /// the seven-arm partition structurally, a future REPL / LSP
12166 /// tokenizer-boundary hint that scans the source for the reader-
12167 /// outer-dispatch category-leading chars upfront, a future
12168 /// completion generator that suggests the seven bytes at every
12169 /// bare-atom-lexeme insertion site) read through
12170 /// [`Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS`] without re-deriving
12171 /// the three-part sub-expression composition inline.
12172 ///
12173 /// Composition law (forward): for every `ch: char`,
12174 /// `Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch) ==
12175 /// (Self::LIST_DELIMITERS.contains(&ch) ||
12176 /// QuoteForm::from_lead_char(ch).is_some() ||
12177 /// ch == Atom::STR_DELIMITER || ch == Self::COMMENT_LEAD)` — pinned
12178 /// by `sexp_non_whitespace_bare_atom_terminators_agree_with_pre_lift_sub_expression_disjunction`.
12179 /// Boundary-predicate composition law:
12180 /// `Self::is_bare_atom_boundary(ch) == (ch.is_whitespace() ||
12181 /// Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch))` —
12182 /// pinned by `sexp_is_bare_atom_boundary_agrees_with_terminators_array_on_non_whitespace_partition`.
12183 ///
12184 /// Adding a hypothetical seventh reader-outer-dispatch category
12185 /// (e.g. `#|…|#` block-comment lead byte, `#\` char-literal prefix,
12186 /// `#[` vector-literal prefix — each pinning a new lead byte on
12187 /// [`Self`] or a fresh sub-algebra) extends this array AND
12188 /// [`Self::is_bare_atom_boundary`]'s indirect coverage in
12189 /// LOCKSTEP — rustc's forced-arity check on `[char; 7]` fails
12190 /// compilation if the algebra grows without the array (or the
12191 /// array without the algebra).
12192 ///
12193 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
12194 /// (reader-outer-dispatch category, canonical char) family-wide
12195 /// pairing binds at ONE typed `[char; 7]` array on the outer
12196 /// [`Sexp`] algebra regardless of which of the three sub-algebras
12197 /// the individual chars name their per-role `pub const` primitive
12198 /// on. THEORY.md §III — the typescape; the seven canonical
12199 /// reader-outer-dispatch category-leading bytes bind at ONE typed
12200 /// `[char; 7]` array on the outer [`Sexp`] algebra rather than at
12201 /// three-part sub-expression composition inline at
12202 /// [`Self::is_bare_atom_boundary`]. THEORY.md §V.1 — knowable
12203 /// platform; the family's cardinality becomes a TYPE-level constant
12204 /// on the substrate algebra rather than a per-consumer hand-rolled
12205 /// enumeration of the seven chars. THEORY.md §VI.1 — generation over
12206 /// composition; the family-wide contract sweeps (routing through
12207 /// typed sub-algebra `pub const` primitives, pairwise distinctness,
12208 /// agreement with the pre-lift sub-expression disjunction) emerge
12209 /// from the composition of EIGHT substrate primitives (this
12210 /// `pub const [char; 7]` array + the seven sub-algebra `pub const`
12211 /// primitives) rather than as inline disjunctions at each call site.
12212 pub const NON_WHITESPACE_BARE_ATOM_TERMINATORS: [char; 7] = [
12213 Self::LIST_OPEN,
12214 Self::LIST_CLOSE,
12215 QuoteForm::QUOTE_LEAD,
12216 QuoteForm::QUASIQUOTE_LEAD,
12217 QuoteForm::UNQUOTE_LEAD,
12218 Atom::STR_DELIMITER,
12219 Self::COMMENT_LEAD,
12220 ];
12221
12222 /// Reader-level boundary predicate — returns `true` iff `ch` is one
12223 /// of the SIX outer-dispatch category-leading chars the reader's
12224 /// tokenizer specialises on: whitespace, [`Self::LIST_OPEN`],
12225 /// [`Self::LIST_CLOSE`], any [`QuoteForm::lead_char`] (via the
12226 /// closed-set [`QuoteForm::from_lead_char`] decode),
12227 /// [`Atom::STR_DELIMITER`], AND [`Self::COMMENT_LEAD`]. The ONE
12228 /// typed projection on the outer [`Sexp`] algebra that names the
12229 /// disjunction of "the char would start a NEW reader-level token
12230 /// (or a discarded run) rather than feed the current bare-atom
12231 /// accumulator."
12232 ///
12233 /// Structural dual of the reader's outer-dispatch cascade in
12234 /// `crate::reader::tokenize`: the outer-dispatch has FIVE specific
12235 /// arms (`ws if ws.is_whitespace()`, `Self::COMMENT_LEAD`,
12236 /// `Self::LIST_OPEN`, `Self::LIST_CLOSE`, `Atom::STR_DELIMITER`)
12237 /// plus ONE pre-match `QuoteForm::from_lead_char(c).is_some()`
12238 /// gate — SIX categories in total. The default `_ => { …
12239 /// bare-atom accumulator … }` arm fires EXACTLY when every specific
12240 /// arm rejects. This method is the typed projection of that
12241 /// implicit disjunction: `Sexp::is_bare_atom_boundary(ch) == true`
12242 /// iff `ch` would trigger one of the SIX specific arms, and
12243 /// `false` iff `ch` would fall through to the bare-atom
12244 /// accumulator's default arm. The two consumer sites in
12245 /// `crate::reader::tokenize` — the outer-dispatch's implicit "no
12246 /// specific arm fires" residual predicate AND the bare-atom
12247 /// accumulator's terminator disjunct — now share ONE typed source
12248 /// of truth on the closed-set outer [`Sexp`] algebra.
12249 ///
12250 /// Pre-lift the SIX-clause boolean chain
12251 /// (`ch.is_whitespace() || ch == Sexp::LIST_OPEN || ch ==
12252 /// Sexp::LIST_CLOSE || QuoteForm::from_lead_char(ch).is_some() ||
12253 /// ch == Atom::STR_DELIMITER || ch == Sexp::COMMENT_LEAD`) lived
12254 /// inline at the bare-atom accumulator's terminator gate in
12255 /// `crate::reader::tokenize`, spanning THREE type namespaces
12256 /// ([`Sexp`], [`Atom`], [`QuoteForm`]) at ONE consumer site.
12257 /// Post-lift the WHOLE disjunction binds at ONE typed projection
12258 /// on the outer [`Sexp`] algebra so a refactor that adds a
12259 /// SEVENTH outer-dispatch category (e.g. `#|…|#` block-comment
12260 /// lead byte, `#\` char-literal prefix, `#[` vector-literal
12261 /// prefix) extends the algebra ONCE (via a new arm on THIS method
12262 /// AND a matching outer-dispatch arm in the reader) rather than
12263 /// mutating an inline six-clause boolean chain that would silently
12264 /// drift out of tokenizer agreement if one clause was added
12265 /// without the other. Sibling-shape peer of the outer-dispatch's
12266 /// closed-set per-category projections
12267 /// ([`QuoteForm::from_lead_char`] on the quote-family axis;
12268 /// [`Atom::decode_str_escape`] on the Str-escape axis): where those
12269 /// two methods each lift ONE outer-dispatch category's decode onto
12270 /// its typed algebra, THIS method lifts the DISJUNCTION of ALL SIX
12271 /// outer-dispatch categories onto the outer [`Sexp`] algebra as
12272 /// a bool predicate.
12273 ///
12274 /// Composition law (forward): for every char `ch` and every
12275 /// substrate-marker enumeration
12276 /// `Self::{LIST_OPEN, LIST_CLOSE, COMMENT_LEAD}`,
12277 /// `Atom::STR_DELIMITER`, `QuoteForm::from_lead_char(ch).is_some()`,
12278 /// `is_bare_atom_boundary` returns `true`; for every char that
12279 /// isn't whitespace AND isn't listed on any marker axis, returns
12280 /// `false`. Reader-level composition law:
12281 /// `read(format!("foo{ch}"))` tokenizes as `[Token::Atom("foo"),
12282 /// …trailing token(s) from `ch`]` when
12283 /// `Self::is_bare_atom_boundary(ch)` is `true`, and as
12284 /// `[Token::Atom(format!("foo{ch}"))]` (ONE token) when it is
12285 /// `false`.
12286 ///
12287 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
12288 /// (reader-level boundary role, canonical char) pairing binds at
12289 /// ONE typed projection on the outer [`Sexp`] algebra regardless
12290 /// of which of the SIX outer-dispatch category-leading chars is
12291 /// under test. THEORY.md §VI.1 — generation over composition; a
12292 /// SIX-clause inline boolean disjunction spanning THREE type
12293 /// namespaces collapses onto ONE named method — the substrate's
12294 /// three-times rule saturated at the outer-dispatch's disjunction.
12295 /// THEORY.md §V.1 — knowable platform; the canonical reader-level
12296 /// boundary predicate becomes a TYPE-level method on the outer
12297 /// substrate algebra rather than an inline six-clause boolean
12298 /// chain at ONE consumer site inside `crate::reader::tokenize`.
12299 #[must_use]
12300 pub fn is_bare_atom_boundary(ch: char) -> bool {
12301 ch.is_whitespace() || Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch)
12302 }
12303
12304 /// Canonical [`Self::Atom`]-[`Atom::Symbol`] outer constructor —
12305 /// composes [`Atom::symbol`] (the typed-construct method on the
12306 /// closed-set [`Atom`] algebra) under the [`Self::Atom`] outer
12307 /// wrapper. The first of six `Self::Atom(Atom::X(_))` outer
12308 /// constructors all routing through the typed [`Atom`] construct
12309 /// family at the inner algebra so the `.into()` coercion + tuple-
12310 /// variant constructor pair lives at ONE site per kind on the
12311 /// [`Atom`] algebra rather than at this outer constructor's body.
12312 /// Sibling-shape lift to the [`Atom::as_X`] /
12313 /// [`Self::as_X`] composition through [`Self::as_atom`] on the
12314 /// projection axis: where projections route OUTER `Self::as_X`
12315 /// through `self.as_atom().and_then(Atom::as_X)`, constructions
12316 /// route OUTER `Self::X` through `Self::Atom(Atom::X(payload))`.
12317 ///
12318 /// Composition law (forward): `Sexp::symbol(s) ==
12319 /// Sexp::Atom(Atom::symbol(s))` for every `s: impl Into<String>`.
12320 /// Round-trip law (with the soft-projection sibling): for every
12321 /// `s: &str`, `Sexp::symbol(s).as_symbol() == Some(s)` — the inner
12322 /// algebra's section-for-retraction surfaces through the outer
12323 /// algebra without re-derivation. Same posture across the six
12324 /// sibling pairs.
12325 #[must_use]
12326 pub fn symbol(s: impl Into<String>) -> Self {
12327 Self::Atom(Atom::symbol(s))
12328 }
12329 /// Canonical [`Self::Atom`]-[`Atom::Keyword`] outer constructor —
12330 /// composes [`Atom::keyword`] under [`Self::Atom`]. See
12331 /// [`Self::symbol`] for the outer-algebra docstring.
12332 #[must_use]
12333 pub fn keyword(s: impl Into<String>) -> Self {
12334 Self::Atom(Atom::keyword(s))
12335 }
12336 /// Canonical [`Self::Atom`]-[`Atom::Str`] outer constructor —
12337 /// composes [`Atom::string`] under [`Self::Atom`].
12338 #[must_use]
12339 pub fn string(s: impl Into<String>) -> Self {
12340 Self::Atom(Atom::string(s))
12341 }
12342 /// Canonical [`Self::Atom`]-[`Atom::Int`] outer constructor —
12343 /// composes [`Atom::int`] under [`Self::Atom`].
12344 #[must_use]
12345 pub fn int(n: i64) -> Self {
12346 Self::Atom(Atom::int(n))
12347 }
12348 /// Canonical [`Self::Atom`]-[`Atom::Float`] outer constructor —
12349 /// composes [`Atom::float`] under [`Self::Atom`].
12350 #[must_use]
12351 pub fn float(n: f64) -> Self {
12352 Self::Atom(Atom::float(n))
12353 }
12354 /// Canonical [`Self::Atom`]-[`Atom::Bool`] outer constructor —
12355 /// composes [`Atom::boolean`] under [`Self::Atom`].
12356 #[must_use]
12357 pub fn boolean(b: bool) -> Self {
12358 Self::Atom(Atom::boolean(b))
12359 }
12360
12361 /// Canonical [`Self::Quote`] outer constructor — composes
12362 /// [`QuoteForm::wrap`] on the [`QuoteForm::Quote`] marker so the
12363 /// `Box::new(inner)` allocation + tuple-variant pair lives at ONE
12364 /// site on the closed-set [`QuoteForm`] algebra rather than at
12365 /// this outer-constructor body. The first of four `Self::Quote*`
12366 /// outer constructors all routing through the typed
12367 /// [`QuoteForm::wrap`] family at the inner algebra — the
12368 /// quote-family-axis section peer of the six `Self::Atom(Atom::X(_))`
12369 /// outer constructors ([`Self::symbol`], [`Self::keyword`],
12370 /// [`Self::string`], [`Self::int`], [`Self::float`],
12371 /// [`Self::boolean`]) all routing through the typed [`Atom`]
12372 /// construct family on the atomic-payload axis. Sibling-shape lift
12373 /// to the [`Self::as_quote_form`] soft-projection sibling on the
12374 /// projection axis: where the projection soft-decomposes a
12375 /// quote-family wrapper into `Option<(QuoteForm, &Sexp)>` (surfacing
12376 /// the typed marker alongside the borrowed inner body), each of
12377 /// these four typed constructors embeds a fresh inner body under
12378 /// the typed marker into the matching tuple-variant wrapper.
12379 ///
12380 /// Composition law (forward): `Sexp::quote(inner) ==
12381 /// QuoteForm::Quote.wrap(inner) == Sexp::Quote(Box::new(inner))`
12382 /// for every `inner: Sexp`. Round-trip law (section-for-retraction
12383 /// with the soft-projection sibling): `Sexp::quote(inner)
12384 /// .as_quote_form() == Some((QuoteForm::Quote, &inner))` for every
12385 /// `inner: Sexp` — the inner algebra's typed constructor pairs
12386 /// section-for-retraction with the outer algebra's soft
12387 /// projection, and the marker + inner body cross-projection
12388 /// preserves identity. Same posture across the four sibling
12389 /// pairs (`Sexp::quote` / `Sexp::quasiquote` / `Sexp::unquote` /
12390 /// `Sexp::unquote_splice`).
12391 ///
12392 /// Pre-lift the `Self::Quote(Box::new(inner))` welded triple
12393 /// (`Self::Quote`, `Box::new`, `inner`) appeared inline at every
12394 /// consumer that builds a quote-family wrapper — well past the ≥2
12395 /// PRIME-DIRECTIVE trigger once the structural shape is named. The
12396 /// welded triple already lives at ONE site on the closed-set
12397 /// [`QuoteForm::wrap`] algebra for the marker-driven consumer path;
12398 /// this outer constructor binds the per-variant `Sexp::X(Box::new(
12399 /// inner))` welded triple to ONE typed-algebra method per marker on
12400 /// the outer [`Sexp`] algebra, so consumers that know the marker at
12401 /// compile time bind to the typed method directly rather than
12402 /// re-deriving the `Self::X(Box::new(_))` pair inline. A future
12403 /// allocation-policy change (e.g. arena-allocated wrappers for
12404 /// span-aware [`Sexp`]) lands as ONE edit at [`QuoteForm::wrap`]
12405 /// (the single site the allocation composition lives) and
12406 /// propagates through these four typed constructors byte-for-byte.
12407 ///
12408 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
12409 /// (QuoteForm variant, [`Sexp`] tuple-variant constructor) pairing
12410 /// binds at ONE typed-algebra method per marker on the outer
12411 /// [`Sexp`] algebra regardless of which consumer reaches in.
12412 /// THEORY.md §VI.1 — generation over composition; the welded
12413 /// `Self::X(Box::new(_))` triple at every quote-family construct
12414 /// site regenerates through `QuoteForm::X.wrap(_)` composition over
12415 /// the typed algebra rather than per-site re-derivation. THEORY.md
12416 /// §V.1 — knowable platform; the typed-construct family becomes a
12417 /// TYPE projection on the substrate's outer [`Sexp`] algebra sitting
12418 /// next to the typed-project family [`Self::as_quote_form`] rather
12419 /// than as bare tuple-variant constructor + per-site `Box::new`
12420 /// discipline. A future fifth homoiconic prefix syntax (e.g. syntax
12421 /// quotation `#'x` for hygienic macros) extends [`QuoteForm::ALL`] +
12422 /// [`QuoteForm::wrap`]'s arm + this construct family in lockstep,
12423 /// rustc-enforced through the closed-set exhaustiveness.
12424 ///
12425 /// Frontier inspiration: Racket's `(quote x)` /
12426 /// `(quasiquote x)` / `(unquote x)` / `(unquote-splicing x)` typed
12427 /// syntactic-form construct face paired one-for-one with the
12428 /// [`Self::as_quote_form`] closed-set soft-projection sibling on
12429 /// the outer syntax algebra — the typed-construct + typed-project
12430 /// algebra dual is closed at one method per direction per marker
12431 /// on Racket's surface, and the [`Self::quote`] /
12432 /// [`Self::quasiquote`] / [`Self::unquote`] / [`Self::unquote_splice`]
12433 /// family is the Rust-typed peer on the closed-set outer [`Sexp`]
12434 /// algebra with [`QuoteForm::wrap`] standing in for Racket's typed
12435 /// dispatch face. MLIR's `mlir::OpBuilder::create<QuoteOp>(loc,
12436 /// inner)` typed-IR wrapper construction paired with
12437 /// `mlir::dyn_cast<QuoteOp>(op)` on the projection face — the typed
12438 /// factory + typed downcast pair the IR algebra closes over on
12439 /// every wrapper op; [`Self::quote`] / [`Self::as_quote_form`] is
12440 /// the Rust-typed peer on the outer [`Sexp`] algebra with the
12441 /// closed-set [`QuoteForm`] standing in for MLIR's `OperationName`
12442 /// taxonomy over the wrapper-op family.
12443 #[must_use]
12444 pub fn quote(inner: Sexp) -> Self {
12445 QuoteForm::Quote.wrap(inner)
12446 }
12447 /// Canonical [`Self::Quasiquote`] outer constructor — composes
12448 /// [`QuoteForm::wrap`] on the [`QuoteForm::Quasiquote`] marker.
12449 /// See [`Self::quote`] for the outer-algebra docstring.
12450 #[must_use]
12451 pub fn quasiquote(inner: Sexp) -> Self {
12452 QuoteForm::Quasiquote.wrap(inner)
12453 }
12454 /// Canonical [`Self::Unquote`] outer constructor — composes
12455 /// [`QuoteForm::wrap`] on the [`QuoteForm::Unquote`] marker.
12456 /// See [`Self::quote`] for the outer-algebra docstring.
12457 #[must_use]
12458 pub fn unquote(inner: Sexp) -> Self {
12459 QuoteForm::Unquote.wrap(inner)
12460 }
12461 /// Canonical [`Self::UnquoteSplice`] outer constructor — composes
12462 /// [`QuoteForm::wrap`] on the [`QuoteForm::UnquoteSplice`] marker.
12463 /// See [`Self::quote`] for the outer-algebra docstring.
12464 #[must_use]
12465 pub fn unquote_splice(inner: Sexp) -> Self {
12466 QuoteForm::UnquoteSplice.wrap(inner)
12467 }
12468
12469 /// Canonical marker-driven quote-family outer constructor — routes
12470 /// through [`QuoteForm::wrap`] on the caller-supplied [`QuoteForm`]
12471 /// marker at ONE site on the closed-set [`Sexp`] algebra. The outer-
12472 /// algebra section-for-retraction sibling of the existing
12473 /// [`Self::as_quote_form`] soft-projection ([`Option<(QuoteForm,
12474 /// &Sexp)>`]): where the projection soft-decomposes a quote-family
12475 /// wrapper into its typed [`QuoteForm`] marker + borrowed inner body
12476 /// on the (marker, borrowed-inner) product, this constructor embeds
12477 /// a typed [`QuoteForm`] marker + owned inner body pair into the
12478 /// matching tuple-variant wrapper on the (marker, owned-inner)
12479 /// product. Marker-driven parent of the four per-variant siblings
12480 /// [`Self::quote`] / [`Self::quasiquote`] / [`Self::unquote`] /
12481 /// [`Self::unquote_splice`] — each of the four is `Self::quote_form(
12482 /// QuoteForm::X, inner)` restricted to a compile-time-known marker;
12483 /// this constructor is the marker-abstracted parent every consumer
12484 /// that binds the marker as a runtime value routes through.
12485 ///
12486 /// Sibling posture across the outer-algebra construct-family layer:
12487 /// where [`Self::call`](Self::call) and [`Self::named_call`](Self::named_call)
12488 /// close the (construct, project) dual on the call-form + named-
12489 /// call-form typed decompositions of the residual-axis List arm,
12490 /// and [`Self::list`](Self::list) closes it on the residual-axis
12491 /// List arm itself, this constructor closes it on the quote-family-
12492 /// axis wrapper decomposition — the outer [`Sexp`] algebra now
12493 /// carries a (marker, project) construct-family dual pair for every
12494 /// axis of the [`SexpShape`] closed set at ONE typed method per
12495 /// corner, with `Sexp::quote_form(qf, inner)` as the marker-driven
12496 /// quote-family construct entry and [`Self::as_quote_form`] as its
12497 /// marker-recovering projection sibling.
12498 ///
12499 /// Composition law (forward): `Sexp::quote_form(marker, inner) ==
12500 /// marker.wrap(inner)` for every `marker: QuoteForm` and every
12501 /// `inner: Sexp`. The body routes through the SAME closed-set
12502 /// `QuoteForm::wrap` method the four per-variant siblings
12503 /// ([`Self::quote`] / [`Self::quasiquote`] / [`Self::unquote`] /
12504 /// [`Self::unquote_splice`]) already reach for, so the (marker,
12505 /// [`Sexp`] tuple-variant constructor) pairing binds at ONE closed-
12506 /// set match on the substrate algebra — a regression that drifts
12507 /// one consumer's marker→wrapper mapping from the others (e.g. a
12508 /// copy-edit that pairs [`QuoteForm::Quote`] with the
12509 /// [`Sexp::Quasiquote`] tuple variant, or that drops a
12510 /// [`QuoteForm::UnquoteSplice`] value through the
12511 /// [`Sexp::Unquote`] tuple variant) cannot reach the substrate's
12512 /// runtime.
12513 ///
12514 /// Round-trip law (section-for-retraction with the outer-algebra
12515 /// soft-projection): for every `marker: QuoteForm` and every
12516 /// `inner: Sexp`, `Sexp::quote_form(marker, inner.clone())
12517 /// .as_quote_form() == Some((marker, &inner))` — the outer
12518 /// algebra's marker-driven quote-family constructor pairs section-
12519 /// for-retraction with the outer algebra's soft quote-family
12520 /// projection, and the (marker, inner body) cross-projection
12521 /// preserves identity for every `QuoteForm` variant.
12522 ///
12523 /// Marker-recovering projection composition: `Sexp::quote_form(
12524 /// marker, inner).as_quote_form_marker() == Some(marker)` for every
12525 /// input — the marker-only projection sibling
12526 /// ([`Self::as_quote_form_marker`]) recovers the constructor's
12527 /// marker byte-for-byte. Outer-shape composition law:
12528 /// `Sexp::quote_form(marker, inner).shape() == marker.sexp_shape()`
12529 /// — the outer-shape identity binds through the typed-shape lattice
12530 /// at ONE arm per [`QuoteForm`] variant, symmetric with the atomic
12531 /// construct family's `Sexp::X_atom(payload).shape() ==
12532 /// AtomKind::X.sexp_shape()` composition and the residual construct
12533 /// family's `Sexp::list(items).shape() == SexpShape::List`
12534 /// composition.
12535 ///
12536 /// Per-variant restriction laws (structural identity between the
12537 /// marker-driven parent + the four per-variant siblings):
12538 /// * `Sexp::quote_form(QuoteForm::Quote, inner) == Sexp::quote(inner)`
12539 /// * `Sexp::quote_form(QuoteForm::Quasiquote, inner) == Sexp::quasiquote(inner)`
12540 /// * `Sexp::quote_form(QuoteForm::Unquote, inner) == Sexp::unquote(inner)`
12541 /// * `Sexp::quote_form(QuoteForm::UnquoteSplice, inner) == Sexp::unquote_splice(inner)`
12542 ///
12543 /// The four per-variant constructors ARE the marker-driven parent
12544 /// specialized on a compile-time-known marker; the marker-abstracted
12545 /// parent binds every consumer that routes a runtime `QuoteForm`
12546 /// value through a quote-family construct to ONE typed method on
12547 /// the outer [`Sexp`] algebra rather than a four-arm inline
12548 /// `match qf { QuoteForm::X => Sexp::x(inner), … }` dispatch.
12549 ///
12550 /// Pre-lift consumers with a runtime `QuoteForm` marker routed
12551 /// through `marker.wrap(inner)` directly (the reader's
12552 /// `read_quoted` production consumer at `reader.rs`, the domain
12553 /// module's quote-family round-trip test site) — well past the ≥2
12554 /// PRIME-DIRECTIVE trigger once the marker-driven pattern is
12555 /// named. Post-lift consumers bind to ONE typed-algebra method on
12556 /// the outer [`Sexp`] algebra sitting next to the typed-project
12557 /// family ([`Self::as_quote_form`] / [`Self::as_quote_form_marker`])
12558 /// rather than reaching into the closed-set [`QuoteForm::wrap`]
12559 /// method directly. A future allocation-policy change (e.g. arena-
12560 /// allocated wrappers for span-aware [`Sexp`]) lands as ONE edit at
12561 /// the single [`QuoteForm::wrap`] composition site and propagates
12562 /// through this constructor byte-for-byte.
12563 ///
12564 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
12565 /// (typed [`QuoteForm`] marker, owned inner body, [`QuoteForm::wrap`]
12566 /// composition) triple binds at ONE typed-algebra method on the
12567 /// outer [`Sexp`] algebra, closing the marker-driven quote-family
12568 /// (construct, project) algebra dual pair with
12569 /// [`Self::as_quote_form`] on the projection side. THEORY.md §II.1
12570 /// invariant 2 — free middle; every consumer that has a runtime
12571 /// [`QuoteForm`] marker + an owned inner body and wants to build a
12572 /// quote-family wrapper routes through the SAME typed method, so a
12573 /// regression that drifts one consumer's marker→wrapper mapping
12574 /// cannot reach the substrate's runtime. THEORY.md §V.1 — knowable
12575 /// platform; the marker-driven quote-family typed-construct becomes
12576 /// a TYPE projection on the substrate's outer [`Sexp`] algebra
12577 /// sitting next to the typed-project family
12578 /// [`Self::as_quote_form`] / [`Self::as_quote_form_marker`] rather
12579 /// than the closed-set [`QuoteForm::wrap`] method threaded as a
12580 /// method call on a bound-marker value. THEORY.md §VI.1 —
12581 /// generation over composition; the marker-driven quote-family
12582 /// pair emerges from ONE typed-algebra composition through
12583 /// [`QuoteForm::wrap`] rather than from per-consumer marker→wrapper
12584 /// dispatch literals; a future fifth homoiconic prefix syntax
12585 /// (e.g. `#'x` for hygienic macros) extends [`QuoteForm::ALL`] +
12586 /// [`QuoteForm::wrap`]'s match arm + [`Self::as_quote_form`]'s
12587 /// match arm in lockstep — rustc-enforced through the closed-set
12588 /// exhaustiveness — with THIS constructor inheriting the extension
12589 /// through the [`QuoteForm::wrap`] composition site without a per-
12590 /// site edit.
12591 ///
12592 /// Frontier inspiration: Racket's `(datum->syntax stx (list #'qf
12593 /// inner))` marker-abstracted quote-family construct paired one-
12594 /// for-one with `syntax-e` on the projection face — the typed-
12595 /// construct + typed-project algebra dual is closed on Racket's
12596 /// syntax algebra at one method per direction, and
12597 /// `Sexp::quote_form` / `Sexp::as_quote_form` is the Rust-typed peer
12598 /// on the closed-set outer [`Sexp`] algebra with [`QuoteForm`]
12599 /// standing in for Racket's syntactic-form taxonomy over the four
12600 /// homoiconic prefix wrappers. MLIR's typed-IR
12601 /// `mlir::OpBuilder::create(loc, OperationName, operands)` marker-
12602 /// driven op construction paired with `mlir::Operation::getName()`
12603 /// on the projection face — the typed factory + typed downcast pair
12604 /// the IR algebra closes over on every op kind at one method per
12605 /// direction; `Sexp::quote_form` / [`Self::as_quote_form_marker`]
12606 /// is the Rust-typed peer on the outer [`Sexp`] algebra with the
12607 /// closed-set [`QuoteForm`] standing in for MLIR's `OperationName`
12608 /// taxonomy over the four homoiconic prefix-wrapper op kinds.
12609 #[must_use]
12610 pub fn quote_form(marker: QuoteForm, inner: Sexp) -> Self {
12611 marker.wrap(inner)
12612 }
12613
12614 /// Canonical marker-driven template-substitution outer constructor —
12615 /// routes through [`UnquoteForm::wrap`] on the caller-supplied
12616 /// [`UnquoteForm`] marker at ONE site on the closed-set [`Sexp`]
12617 /// algebra. Subset-algebra peer of the marker-driven quote-family
12618 /// parent [`Self::quote_form`]: where [`Self::quote_form`] embeds a
12619 /// caller-supplied [`QuoteForm`] marker + owned inner body on the
12620 /// 4-of-12 quote-family carving through the closed-set
12621 /// [`QuoteForm::wrap`] composition site, THIS constructor embeds a
12622 /// caller-supplied [`UnquoteForm`] marker + owned inner body on the
12623 /// 2-of-12 template-substitution subset carving through the
12624 /// [`UnquoteForm::wrap`] composition site (which itself composes
12625 /// [`UnquoteForm::to_quote_form`] then [`QuoteForm::wrap`], so the
12626 /// welded `Sexp::X(Box::new(_))` triple ultimately still binds at
12627 /// the ONE canonical [`QuoteForm::wrap`] site the four per-variant
12628 /// siblings [`Self::quote`] / [`Self::quasiquote`] / [`Self::unquote`]
12629 /// / [`Self::unquote_splice`] and the marker-driven parent
12630 /// [`Self::quote_form`] all route through). Closes the (construct,
12631 /// project) algebra dual on the (`UnquoteForm`, `Sexp`) product
12632 /// against the pre-existing projection sibling [`Self::as_unquote`]
12633 /// (soft-decomposition into `Option<(UnquoteForm, &Sexp)>`) and its
12634 /// marker-only peer [`Self::as_unquote_form`] (soft-decomposition
12635 /// into `Option<UnquoteForm>`) — post-lift the outer [`Sexp`]
12636 /// algebra carries a marker-driven (construct, project) dual pair
12637 /// `Sexp::unquote_form` / `Sexp::as_unquote` at ONE typed method per
12638 /// direction on the template-substitution subset alongside the
12639 /// marker-only projection sibling [`Self::as_unquote_form`],
12640 /// symmetric with the pair `Sexp::quote_form` / `Sexp::as_quote_form`
12641 /// / `Sexp::as_quote_form_marker` the superset carries.
12642 ///
12643 /// Sibling posture across the outer-algebra construct-family layer:
12644 /// where [`Self::call`](Self::call) and [`Self::named_call`](Self::named_call)
12645 /// close the (construct, project) dual on the call-form + named-call-
12646 /// form typed decompositions of the residual-axis List arm,
12647 /// [`Self::list`](Self::list) closes it on the residual-axis List
12648 /// arm itself, and [`Self::quote_form`](Self::quote_form) closes it
12649 /// on the quote-family-axis marker-driven decomposition (the parent
12650 /// 4-of-12 quote-family carving), THIS constructor closes it on the
12651 /// template-substitution-subset marker-driven decomposition (the
12652 /// 2-of-4 subset of the quote-family carving, equivalently the
12653 /// 2-of-12 substitution carving of the outer [`SexpShape`] closed
12654 /// set) — the outer [`Sexp`] algebra now carries a marker-driven
12655 /// construct-family dual pair for every closed-set carving on the
12656 /// quote-family axis at ONE typed method per corner.
12657 ///
12658 /// Composition law (forward): `Sexp::unquote_form(marker, inner) ==
12659 /// marker.wrap(inner)` for every `marker: UnquoteForm` and every
12660 /// `inner: Sexp`. The body routes through the SAME
12661 /// [`UnquoteForm::wrap`] method the subset-algebra consumer path
12662 /// already reaches for, so the (subset marker, [`Sexp`] tuple-variant
12663 /// wrapper) pairing binds at ONE closed-set composition site on the
12664 /// substrate — a regression that drifts one consumer's subset
12665 /// marker → wrapper mapping from the others (e.g. a copy-edit that
12666 /// pairs [`UnquoteForm::Unquote`] with the [`Sexp::UnquoteSplice`]
12667 /// tuple variant, or that drops a [`UnquoteForm::Splice`] value
12668 /// through the [`Sexp::Unquote`] tuple variant) cannot reach the
12669 /// substrate's runtime.
12670 ///
12671 /// Round-trip law (section-for-retraction with the outer-algebra
12672 /// soft-projection): for every `marker: UnquoteForm` and every
12673 /// `inner: Sexp`, `Sexp::unquote_form(marker, inner.clone())
12674 /// .as_unquote() == Some((marker, &inner))` — the outer algebra's
12675 /// marker-driven template-substitution constructor pairs section-
12676 /// for-retraction with the outer algebra's soft template-substitution
12677 /// projection, and the (subset marker, inner body) cross-projection
12678 /// preserves identity for every [`UnquoteForm`] variant.
12679 ///
12680 /// Marker-recovering projection composition: `Sexp::unquote_form(
12681 /// marker, inner).as_unquote_form() == Some(marker)` for every input
12682 /// — the marker-only projection sibling [`Self::as_unquote_form`]
12683 /// recovers the constructor's subset marker byte-for-byte. Outer-
12684 /// shape composition law: `Sexp::unquote_form(marker, inner).shape()
12685 /// == marker.sexp_shape()` — the outer-shape identity binds through
12686 /// the typed-shape lattice at ONE arm per [`UnquoteForm`] variant,
12687 /// symmetric with the quote-family construct family's
12688 /// `Sexp::quote_form(marker, inner).shape() == marker.sexp_shape()`
12689 /// composition and the atomic construct family's
12690 /// `Sexp::X_atom(payload).shape() == AtomKind::X.sexp_shape()`
12691 /// composition. Superset-routing composition law:
12692 /// `Sexp::unquote_form(marker, inner) == Sexp::quote_form(
12693 /// marker.to_quote_form(), inner)` for every input — the subset-
12694 /// algebra construct routes through the same closed-set
12695 /// [`QuoteForm::wrap`] composition site the superset construct
12696 /// routes through, threaded via the typed 2-of-4 subset → superset
12697 /// projection [`UnquoteForm::to_quote_form`]. A regression that
12698 /// drifts either direction of this composition fails at the
12699 /// superset-routing pin.
12700 ///
12701 /// Per-variant restriction laws (structural identity between the
12702 /// marker-driven parent + the two per-variant siblings):
12703 /// * `Sexp::unquote_form(UnquoteForm::Unquote, inner) == Sexp::unquote(inner)`
12704 /// * `Sexp::unquote_form(UnquoteForm::Splice, inner) == Sexp::unquote_splice(inner)`
12705 ///
12706 /// The two per-variant constructors ARE the marker-driven parent
12707 /// specialized on a compile-time-known subset marker; the marker-
12708 /// abstracted parent binds every consumer that routes a runtime
12709 /// [`UnquoteForm`] value through a template-substitution construct
12710 /// to ONE typed method on the outer [`Sexp`] algebra rather than a
12711 /// two-arm inline `match uf { UnquoteForm::Unquote => Sexp::unquote(
12712 /// inner), UnquoteForm::Splice => Sexp::unquote_splice(inner) }`
12713 /// dispatch.
12714 ///
12715 /// Pre-lift consumers with a runtime `UnquoteForm` marker routed
12716 /// through `marker.wrap(inner)` directly (reaching into the
12717 /// [`UnquoteForm::wrap`] subset-algebra method) OR through the two-
12718 /// step `Sexp::quote_form(marker.to_quote_form(), inner)`
12719 /// composition (routing via the superset marker-driven parent).
12720 /// Post-lift consumers bind to ONE typed-algebra method on the outer
12721 /// [`Sexp`] algebra sitting next to the typed-project family
12722 /// ([`Self::as_unquote`] / [`Self::as_unquote_form`]) rather than
12723 /// reaching into the closed-set [`UnquoteForm::wrap`] method
12724 /// directly or composing the superset parent with the subset →
12725 /// superset projection. A future allocation-policy change (e.g.
12726 /// arena-allocated wrappers for span-aware [`Sexp`]) lands as ONE
12727 /// edit at the single [`QuoteForm::wrap`] composition site and
12728 /// propagates through this constructor byte-for-byte (via the
12729 /// [`UnquoteForm::wrap`] composition).
12730 ///
12731 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
12732 /// (typed [`UnquoteForm`] marker, owned inner body,
12733 /// [`UnquoteForm::wrap`] composition) triple binds at ONE typed-
12734 /// algebra method on the outer [`Sexp`] algebra, closing the marker-
12735 /// driven template-substitution (construct, project) algebra dual
12736 /// pair with [`Self::as_unquote`] on the projection side. THEORY.md
12737 /// §II.1 invariant 2 — free middle; every consumer that has a
12738 /// runtime [`UnquoteForm`] marker + an owned inner body and wants to
12739 /// build a template-substitution wrapper routes through the SAME
12740 /// typed method, so a regression that drifts one consumer's subset
12741 /// marker → wrapper mapping cannot reach the substrate's runtime.
12742 /// THEORY.md §V.1 — knowable platform; the marker-driven template-
12743 /// substitution typed-construct becomes a TYPE projection on the
12744 /// substrate's outer [`Sexp`] algebra sitting next to the typed-
12745 /// project family [`Self::as_unquote`] / [`Self::as_unquote_form`]
12746 /// rather than the closed-set [`UnquoteForm::wrap`] method threaded
12747 /// as a method call on a bound-marker value or the two-step subset
12748 /// → superset then [`Self::quote_form`] composition. THEORY.md
12749 /// §VI.1 — generation over composition; the marker-driven template-
12750 /// substitution pair emerges from ONE typed-algebra composition
12751 /// through [`UnquoteForm::wrap`] rather than from per-consumer
12752 /// subset-marker → wrapper dispatch literals; a future third
12753 /// template-substitution marker (e.g. a `,~` reverse-unquote)
12754 /// extends [`UnquoteForm::ALL`] + [`UnquoteForm::to_quote_form`]'s
12755 /// dispatch table in lockstep — rustc-enforced through the closed-
12756 /// set exhaustiveness — with THIS constructor inheriting the
12757 /// extension through the [`UnquoteForm::wrap`] composition site
12758 /// without a per-site edit.
12759 ///
12760 /// Frontier inspiration: Racket's `(datum->syntax stx (list #'uf
12761 /// inner))` marker-abstracted template-substitution construct
12762 /// restricted to the substitution-subset of syntactic-form kinds,
12763 /// paired one-for-one with `syntax-e` on the projection face — the
12764 /// typed-construct + typed-project algebra dual is closed on
12765 /// Racket's syntax algebra at one method per direction per subset,
12766 /// and `Sexp::unquote_form` / `Sexp::as_unquote` is the Rust-typed
12767 /// peer on the closed-set outer [`Sexp`] algebra with
12768 /// [`UnquoteForm`] standing in for Racket's substitution-subset
12769 /// syntactic-form taxonomy. MLIR's typed factory
12770 /// `mlir::OpBuilder::create<UnquoteFamilyOp>(loc, marker, operands)`
12771 /// paired with the projection sibling
12772 /// `mlir::dyn_cast<UnquoteFamilyOp>(op)` — the typed factory + typed
12773 /// downcast pair the IR algebra closes over on every op-family
12774 /// subset at one method per direction; `Sexp::unquote_form` /
12775 /// [`Self::as_unquote_form`] is the Rust-typed peer on the outer
12776 /// [`Sexp`] algebra with the closed-set [`UnquoteForm`] standing in
12777 /// for MLIR's `OperationName` subset taxonomy over the template-
12778 /// substitution op family.
12779 #[must_use]
12780 pub fn unquote_form(marker: UnquoteForm, inner: Sexp) -> Self {
12781 marker.wrap(inner)
12782 }
12783
12784 pub fn is_list(&self) -> bool {
12785 matches!(self, Self::List(_))
12786 }
12787 pub fn as_list(&self) -> Option<&[Sexp]> {
12788 match self {
12789 Self::List(xs) => Some(xs),
12790 _ => None,
12791 }
12792 }
12793
12794 /// Canonical [`Self::List`] outer constructor — collects an
12795 /// `impl IntoIterator<Item = Sexp>` into the tuple-variant payload
12796 /// `Vec<Sexp>` at ONE site on the closed-set [`Sexp`] algebra. The
12797 /// residual-axis section-for-retraction sibling of the existing
12798 /// [`Self::as_list`] soft-projection ([`Option<&[Sexp]>`]): where
12799 /// the projection soft-decomposes a [`Self::List`] arm into its
12800 /// borrowed inner slice, this constructor embeds a fresh owned
12801 /// item sequence into the matching tuple-variant wrapper. Sibling
12802 /// of the atomic-payload construct family ([`Self::symbol`],
12803 /// [`Self::keyword`], [`Self::string`], [`Self::int`],
12804 /// [`Self::float`], [`Self::boolean`] — all routing through the
12805 /// typed [`Atom`] construct family on the 6-of-12 atomic-payload
12806 /// carving) and the quote-family construct family ([`Self::quote`],
12807 /// [`Self::quasiquote`], [`Self::unquote`], [`Self::unquote_splice`]
12808 /// — all routing through the typed [`QuoteForm::wrap`] family on
12809 /// the 4-of-12 quote-family carving); closes the (construct,
12810 /// project) algebra dual on the third and final structural carving
12811 /// of the outer [`Sexp`] closed set — the 2-of-12 residual axis
12812 /// covering [`Self::Nil`] and [`Self::List`]. [`Self::Nil`] is a
12813 /// unit variant carrying no payload — the residual-axis
12814 /// construct family closes at ONE constructor (this method) for
12815 /// the sole payload-bearing residual arm.
12816 ///
12817 /// Composition law (forward): `Sexp::list(items) ==
12818 /// Sexp::List(items.into_iter().collect::<Vec<Sexp>>())` for every
12819 /// `items: impl IntoIterator<Item = Sexp>`. Round-trip law
12820 /// (section-for-retraction with the soft-projection sibling): for
12821 /// every `items: Vec<Sexp>`, `Sexp::list(items.clone()).as_list()
12822 /// == Some(items.as_slice())` — the outer algebra's typed
12823 /// constructor pairs section-for-retraction with the outer
12824 /// algebra's soft projection, and the borrowed-slice cross-
12825 /// projection preserves identity. Sibling posture across the
12826 /// three axis-construct families on the outer [`Sexp`] algebra
12827 /// (atomic + quote-family + residual).
12828 ///
12829 /// Outer-shape composition law: `Sexp::list(items).shape() ==
12830 /// SexpShape::List` for every `items: impl IntoIterator<Item =
12831 /// Sexp>` — the residual-arm outer-shape identity binds through
12832 /// the typed-shape lattice at ONE arm, symmetric with the
12833 /// quote-family construct family's outer-shape composition
12834 /// `Sexp::X_variant(inner).shape() == QuoteForm::X.sexp_shape()`
12835 /// and the atomic construct family's `Sexp::X_atom(payload).shape()
12836 /// == AtomKind::X.sexp_shape()`. Structural-carving-marker
12837 /// composition law: `Sexp::list(items).as_structural_kind() ==
12838 /// Some(StructuralKind::List)` for every `items: impl
12839 /// IntoIterator<Item = Sexp>` — the residual-axis carving marker
12840 /// binds through the closed-set [`StructuralKind`] algebra at ONE
12841 /// arm, symmetric with the atomic-axis's `Sexp::X_atom(payload)
12842 /// .as_atom_kind() == Some(AtomKind::X)` marker composition.
12843 ///
12844 /// Pre-lift the [`Self::List(Vec<Sexp>)`] welded pair
12845 /// ([`Self::List`] tuple-variant constructor + `Vec<Sexp>`
12846 /// payload) appeared inline at every consumer that builds a
12847 /// list-shaped [`Sexp`] value — well past the ≥2 PRIME-DIRECTIVE
12848 /// trigger once the structural shape is named. Post-lift the
12849 /// welded pair binds at ONE typed-algebra method on the outer
12850 /// [`Sexp`] algebra with an `impl IntoIterator<Item = Sexp>`
12851 /// bound so consumers that have a `Vec<Sexp>`, a `[Sexp; N]`
12852 /// array, an `iter().cloned()` sequence, a
12853 /// `.map(...).collect()`-worthy chain, or a
12854 /// `once(head).chain(tail)` composition can hand the sequence
12855 /// directly to the algebra without a per-site `.collect::<Vec<
12856 /// Sexp>>()` coercion. A future allocation-policy change (e.g.
12857 /// arena-allocated lists for span-aware [`Sexp`]) lands as ONE
12858 /// edit at this method site and propagates through consumers
12859 /// byte-for-byte.
12860 ///
12861 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
12862 /// (list-shaped inner sequence, [`Self::List`] tuple-variant
12863 /// constructor) pairing binds at ONE typed-algebra method on the
12864 /// outer [`Sexp`] algebra, closing the outer-algebra construct
12865 /// family across ALL THREE structural carvings of the [`SexpShape`]
12866 /// closed set (atomic-payload + quote-family + residual). THEORY.md
12867 /// §II.1 invariant 2 — free middle; every consumer that has an
12868 /// owned or iterable sequence of [`Sexp`] and wants to build a
12869 /// list-shaped wrapper routes through the SAME typed method, so a
12870 /// regression that drifts one consumer's construction from the
12871 /// others cannot reach the substrate's runtime. THEORY.md §V.1 —
12872 /// knowable platform; the typed-construct family becomes a TYPE
12873 /// projection on the substrate's outer [`Sexp`] algebra sitting
12874 /// next to the typed-project family [`Self::as_list`] rather than
12875 /// bare tuple-variant constructor + per-site `Vec<Sexp>` discipline.
12876 /// THEORY.md §VI.1 — generation over composition; the residual-
12877 /// arm outer-shape + carving-marker pairings emerge from ONE
12878 /// typed-algebra composition on the outer [`Sexp`] algebra rather
12879 /// than from per-consumer per-variant literals.
12880 ///
12881 /// Frontier inspiration: Racket's `(list x y z)` typed list-
12882 /// construct primitive paired one-for-one with `(list? v)` /
12883 /// `(car v)` / `(cdr v)` predicate/projection siblings on the
12884 /// same closed-set list shape — the typed-construct + typed-
12885 /// project algebra dual is closed at one method per direction on
12886 /// Racket's surface, and [`Self::list`] / [`Self::as_list`] is
12887 /// the Rust-typed peer on the closed-set outer [`Sexp`] algebra
12888 /// with `impl IntoIterator<Item = Sexp>` standing in for Racket's
12889 /// variadic collect face. MLIR's `mlir::OpBuilder::create<
12890 /// ListOp>(loc, elements)` typed-IR list-op construction paired
12891 /// with `mlir::dyn_cast<ListOp>(op)` on the projection face —
12892 /// the typed factory + typed downcast pair the IR algebra closes
12893 /// over on every list-shaped op; [`Self::list`] / [`Self::as_list`]
12894 /// is the Rust-typed peer on the outer [`Sexp`] algebra with
12895 /// [`StructuralKind::List`] standing in for MLIR's `OperationName`
12896 /// taxonomy over the list-shaped op family.
12897 #[must_use]
12898 pub fn list<I: IntoIterator<Item = Sexp>>(items: I) -> Self {
12899 Self::List(items.into_iter().collect())
12900 }
12901
12902 /// Soft projection onto the closed-set [`StructuralKind`] residual
12903 /// carving marker — the 2-of-12 carving of the [`SexpShape`] algebra
12904 /// covering [`Self::Nil`] and [`Self::List`] (the outer shapes that
12905 /// lie OUTSIDE both the atomic-payload carving
12906 /// [`AtomKind`](crate::error::SexpShape::as_atom_kind) and the
12907 /// quote-family carving
12908 /// [`QuoteForm`](crate::error::SexpShape::as_quote_form)). Returns
12909 /// `Some(StructuralKind::Nil)` for [`Self::Nil`],
12910 /// `Some(StructuralKind::List)` for [`Self::List`], `None` for
12911 /// every other outer shape (every [`Self::Atom`] variant, every
12912 /// quote-family wrapper: [`Self::Quote`], [`Self::Quasiquote`],
12913 /// [`Self::Unquote`], [`Self::UnquoteSplice`]).
12914 ///
12915 /// Sibling soft-projection peer of [`Self::as_quote_form`] (the
12916 /// soft-decomposition of the four homoiconic prefix wrappers into
12917 /// `(QuoteForm, &Sexp)`) and [`Self::as_unquote`] (the
12918 /// soft-decomposition of the two template-substitution wrappers
12919 /// into `(UnquoteForm, &Sexp)`). Direct value-level peer of the
12920 /// shape-level projection
12921 /// [`SexpShape::as_structural_kind`](crate::error::SexpShape::as_structural_kind)
12922 /// — the pair `(Sexp::as_structural_kind, SexpShape::as_structural_kind)`
12923 /// binds the (Sexp value, StructuralKind carving marker) pairing at
12924 /// ONE typed method on each algebra, symmetric with the existing
12925 /// (Sexp value → AtomKind via
12926 /// `Sexp::as_atom().map(Atom::kind)`) atomic-axis composition and
12927 /// the direct (Sexp value → QuoteForm) marker projection
12928 /// [`Self::as_quote_form`] returns.
12929 ///
12930 /// Composition law: `s.as_structural_kind() ==
12931 /// s.shape().as_structural_kind()` for every `s: &Sexp`. Pre-lift
12932 /// the residual-carving marker at the value level was reachable
12933 /// only via the two-step composition
12934 /// `s.shape().as_structural_kind()` (walking through the full
12935 /// 12-variant [`SexpShape`] closed set to arrive at the 2-of-12
12936 /// carving marker); post-lift the composition lands at ONE typed
12937 /// method on the value algebra — the Nil arm returns `Some(Nil)`
12938 /// directly and the List arm returns `Some(List)` directly,
12939 /// matching the residual-carving membership at the value level.
12940 /// The composition law is pinned by
12941 /// `sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant`
12942 /// in this module, so a regression that drifts either projection
12943 /// from the other surfaces immediately.
12944 ///
12945 /// Sibling-shape lift to [`Self::is_list`] (the bare List-arm
12946 /// predicate) and [`Self::is_kwargs_list`] (the narrower
12947 /// kwargs-shaped List cohort predicate): where `is_list` returns
12948 /// `true` iff the value inhabits the List arm of the residual
12949 /// carving, `as_structural_kind` returns the typed carving marker
12950 /// that binds BOTH residual arms (Nil and List) at ONE typed
12951 /// projection — the operator answering "which residual arm?"
12952 /// rather than the bare "is this the List arm?" predicate.
12953 ///
12954 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
12955 /// (Sexp variant, StructuralKind carving marker) pairing becomes a
12956 /// TYPE projection on the substrate `Sexp` algebra rather than a
12957 /// two-step composition through the shape-level projection. A typo
12958 /// or swap at the value-projection site is no longer a runtime
12959 /// drift but a compile error against the typed projection.
12960 /// THEORY.md §VI.1 — generation over composition; the
12961 /// residual-carving marker projection now lives on the typed
12962 /// `Sexp` algebra alongside [`Self::as_atom`], [`Self::as_list`],
12963 /// [`Self::as_quote_form`], [`Self::as_unquote`], completing the
12964 /// (Sexp value → closed-set carving marker) family at the residual
12965 /// axis. THEORY.md §II.1 invariant 2 — free middle; every consumer
12966 /// that needs the residual-carving marker at the value level (a
12967 /// future `tatara-check` predicate keyed on the Nil/List cohort, a
12968 /// future LSP structural-navigation filter that keys on the
12969 /// residual carving, a future typed-rewriter walk over the
12970 /// residual arm) binds to ONE typed method on the value algebra
12971 /// rather than a two-step composition through the shape-level
12972 /// projection.
12973 ///
12974 /// Frontier inspiration: MLIR's `mlir::dyn_cast<StructuralOp>(val)`
12975 /// typed soft-downcast on the residual carving of a closed-set
12976 /// value algebra — the (value, typed carving marker) pairing lives
12977 /// at ONE typed projection on the outer value-algebra sibling. The
12978 /// Rust-typed peer here uses the substrate's outer `Sexp` algebra
12979 /// with `Sexp::as_structural_kind` closing the residual-carving
12980 /// cell of the value-level soft-projection surface, symmetric with
12981 /// the atomic-axis composition through [`Self::as_atom`] and the
12982 /// quote-family projection [`Self::as_quote_form`].
12983 #[must_use]
12984 pub fn as_structural_kind(&self) -> Option<StructuralKind> {
12985 match self {
12986 Self::Nil => Some(StructuralKind::Nil),
12987 Self::List(_) => Some(StructuralKind::List),
12988 _ => None,
12989 }
12990 }
12991
12992 /// Structural-shape predicate — `true` iff this is a [`Self::List`]
12993 /// whose items form a non-empty, even-length `(:k v :k v …)` kwargs
12994 /// sequence with every even-indexed item being an [`Atom::Keyword`].
12995 /// `false` for every other outer shape ([`Self::Nil`], every
12996 /// [`Self::Atom`] variant, every quote-family wrapper) and for every
12997 /// [`Self::List`] that fails the kwargs convention (empty list, odd
12998 /// length, or any even-indexed non-keyword).
12999 ///
13000 /// The structural witness that [`Self::to_json`] will project this
13001 /// value as [`serde_json::Value::Object`] rather than
13002 /// [`serde_json::Value::Array`] at the [`Self::List`] arm — the
13003 /// `(Sexp variant + kwargs shape, JSON canonical-form)` pairing
13004 /// binds at ONE inherent method on the algebra rather than at a
13005 /// free function consumers must reach into the `domain` module
13006 /// path to invoke. Inverse round-trip law: every
13007 /// [`Self::from_json`] projection of a [`serde_json::Value::Object`]
13008 /// satisfies this predicate (the [`Self::List`] arm
13009 /// [`Self::from_json`] builds for an `Object` is non-empty by the
13010 /// `Object`'s non-empty-keys invariant when present, even-length by
13011 /// the alternating `:k v` build, and keyword-headed at every even
13012 /// index by the `Self::keyword(camel_to_kebab(k))` build — except
13013 /// for the structurally degenerate empty `Object` which projects to
13014 /// `Sexp::List(vec![])` and returns `false` here, matching
13015 /// [`Self::to_json`]'s "empty-list ↛ kwargs" gate).
13016 ///
13017 /// Composes through [`Self::as_list`] (the structural soft-projection
13018 /// onto `&[Sexp]`) and [`Atom::as_keyword`] (the typed soft-projection
13019 /// onto the keyword payload from the [`Atom`] algebra) — the predicate
13020 /// is rebuilt from already-lifted algebra primitives rather than
13021 /// inline-matching the [`Self::List`] arm. Sibling-shape predicate
13022 /// peer of [`Self::is_list`] (the unconditional [`Self::List`]-arm
13023 /// predicate), with this method narrowing the structural witness to
13024 /// the kwargs-shaped sub-cohort. The two predicates partition the
13025 /// list-typed cell of the algebra: every [`Self::List`] either
13026 /// satisfies `is_kwargs_list` (projects as [`serde_json::Value::Object`]
13027 /// through [`Self::to_json`]) or does not (projects as
13028 /// [`serde_json::Value::Array`]).
13029 ///
13030 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
13031 /// kwargs-shape predicate, previously a `pub(crate)` free function in
13032 /// `domain.rs` reached across the module boundary by [`Self::to_json`],
13033 /// is lifted ONE algebra level higher onto the inherent method on
13034 /// the [`Sexp`] algebra — completing the structural-predicate family
13035 /// alongside [`Self::is_list`] and the soft-projection family
13036 /// ([`Self::as_atom`], [`Self::as_list`], [`Self::as_quote_form`]).
13037 /// THEORY.md §II.1 invariant 2 — free middle; every consumer that
13038 /// queries "would [`Self::to_json`] project this as `Object`?" (the
13039 /// `Self::to_json` arm itself, future authoring-tool diagnostics, a
13040 /// future LSP completion fallback, a future REPL pretty-printer that
13041 /// chooses between `(…)` and `{…}` rendering, a future `tatara-check`
13042 /// typed-pattern matcher) routes through ONE inherent algebra method
13043 /// rather than reaching into the `domain` module path for a free
13044 /// function. THEORY.md §V.1 — knowable platform; the JSON-format
13045 /// witness becomes a TYPE projection on the substrate `Sexp` algebra
13046 /// next to its sibling `Sexp::is_list` / `Sexp::as_list` pair rather
13047 /// than living in a `domain.rs` `pub(crate)` helper consumers must
13048 /// import via module path.
13049 ///
13050 /// Frontier inspiration: MLIR's `mlir::Operation::hasTrait<T>()` —
13051 /// typed-IR operations carry their structural traits as inherent
13052 /// methods on the operation algebra rather than as free functions
13053 /// in a sibling module; `Sexp::is_kwargs_list` is the
13054 /// unstructured-Rust peer on the `Sexp` algebra for the
13055 /// "would-this-project-as-Object" structural trait. Racket's
13056 /// `(keyword-apply-procedure? stx)` — the syntax-class predicate
13057 /// that gates a kwargs-style application form's printer / expander
13058 /// path on the syntax algebra; `Sexp::is_kwargs_list` is the
13059 /// substrate's peer at the [`Sexp`] layer, with the `as_list().
13060 /// is_some_and(…)` composition standing in for Racket's
13061 /// `syntax-parse` pattern matcher.
13062 #[must_use]
13063 pub fn is_kwargs_list(&self) -> bool {
13064 self.as_list().is_some_and(|items| {
13065 !items.is_empty()
13066 && items.len().is_multiple_of(2)
13067 && items.iter().step_by(2).all(|s| s.as_keyword().is_some())
13068 })
13069 }
13070
13071 /// Soft projection onto the inner [`Atom`] payload — `Some(&Atom)`
13072 /// iff this is a [`Self::Atom`] variant, `None` for every other
13073 /// outer shape (`Nil`, `List`, `Quote`, `Quasiquote`, `Unquote`,
13074 /// `UnquoteSplice`). The structural-lift face of the per-atomic-
13075 /// payload soft-projection family — composes with the typed
13076 /// [`Atom::as_symbol`] / [`Atom::as_keyword`] / [`Atom::as_string`]
13077 /// / [`Atom::as_int`] / [`Atom::as_float`] / [`Atom::as_bool`]
13078 /// projections to give the six `Sexp::as_X` consumers ONE typed
13079 /// boundary instead of six inline `Self::Atom(Atom::X(s)) => Some(s)`
13080 /// arms.
13081 ///
13082 /// Sibling soft-projection peer of [`Self::as_quote_form`] (the
13083 /// soft-decomposition of the four homoiconic prefix wrappers into
13084 /// `(QuoteForm, &Sexp)`) and [`Self::as_list`] (the soft-decomposition
13085 /// of the structural list constructor into `&[Sexp]`). Together the
13086 /// three projections (`as_atom`, `as_list`, `as_quote_form`) and
13087 /// their nullary peer ([`Self::Nil`] via `matches!(self, Self::Nil)`)
13088 /// cover every outer-shape arm of the `Sexp` algebra: Nil + Atom +
13089 /// List + 4 quote-family arms = 7 outer shapes, with the typed-
13090 /// projection set partitioning them by structural axis.
13091 ///
13092 /// Composition law binding `Sexp::as_X` to the typed `Atom` algebra:
13093 /// for every [`Sexp`] `s`,
13094 /// `s.as_symbol()` (and each `as_keyword` / `as_string` / `as_int` /
13095 /// `as_bool` sibling) `== s.as_atom().and_then(Atom::as_<variant>)`.
13096 /// The `Sexp::as_float` consumer specializes through the widening
13097 /// inline composition `s.as_atom().and_then(|a| a.as_float()
13098 /// .or_else(|| a.as_int().map(|n| n as f64)))` so the algebra-level
13099 /// `Atom::as_float` stays strict and the typed-identity
13100 /// distinction `Int(1)` vs `Float(1.0)` is preserved at the algebra
13101 /// layer (see [`Atom::as_int`]'s docstring for the discipline).
13102 ///
13103 /// Theory anchor: THEORY.md §VI.1 — generation over composition;
13104 /// the six inline `Self::Atom(Atom::X(s)) => Some(_)` arms across
13105 /// the `Sexp::as_X` family is past the three-times rule. THEORY.md
13106 /// §II.1 invariant 2 — free middle; SIX consumers (`as_symbol`,
13107 /// `as_keyword`, `as_string`, `as_int`, `as_float`, `as_bool`) now
13108 /// route through ONE typed structural lift (this method) AND ONE
13109 /// per-variant projection family on the closed-set `Atom` algebra
13110 /// rather than six byte-identical outer-arm matches each.
13111 /// THEORY.md §V.1 — knowable platform; the (Sexp variant, inner
13112 /// payload kind) pairing becomes a TYPE projection on the substrate
13113 /// algebra rather than six inline arms scattered across the six
13114 /// `Sexp::as_X` consumers.
13115 #[must_use]
13116 pub fn as_atom(&self) -> Option<&Atom> {
13117 match self {
13118 Self::Atom(a) => Some(a),
13119 _ => None,
13120 }
13121 }
13122
13123 /// Soft projection onto the closed-set [`AtomKind`] atomic-payload
13124 /// carving marker — the 6-of-12 carving of the [`SexpShape`] algebra
13125 /// covering [`Self::Atom`]'s six per-payload variants ([`Atom::Symbol`],
13126 /// [`Atom::Keyword`], [`Atom::Str`], [`Atom::Int`], [`Atom::Float`],
13127 /// [`Atom::Bool`]). Returns `Some(a.kind())` iff this is a
13128 /// [`Self::Atom`] variant, `None` for every other outer shape
13129 /// ([`Self::Nil`], [`Self::List`], every quote-family wrapper:
13130 /// [`Self::Quote`], [`Self::Quasiquote`], [`Self::Unquote`],
13131 /// [`Self::UnquoteSplice`]).
13132 ///
13133 /// Direct value-level peer of the shape-level projection
13134 /// [`SexpShape::as_atom_kind`](crate::error::SexpShape::as_atom_kind)
13135 /// — the pair `(Sexp::as_atom_kind, SexpShape::as_atom_kind)` binds
13136 /// the (Sexp value, AtomKind carving marker) pairing at ONE typed
13137 /// method on each algebra, closing the atomic-axis cell of the
13138 /// (Sexp value → carving marker) matrix. Sibling soft-projection
13139 /// peer of [`Self::as_structural_kind`] (the 2-of-12 residual
13140 /// carving returning `Option<StructuralKind>`) and
13141 /// [`Self::as_quote_form`] (the 4-of-12 quote-family carving
13142 /// returning `Option<(QuoteForm, &Sexp)>`) — post-lift ALL THREE
13143 /// carvings that partition the twelve outer shapes of the
13144 /// [`SexpShape`] algebra have a marker-only value-level projection
13145 /// on `Sexp`: `as_atom_kind` (atomic axis), `as_quote_form`
13146 /// (quote-family axis, marker + inner), `as_structural_kind`
13147 /// (residual axis). The `Sexp::as_atom` projection stays available
13148 /// for consumers that need the inner [`Atom`] payload for further
13149 /// per-variant typed projection ([`Atom::as_symbol`] et al.); this
13150 /// projection is the shortcut for consumers that only need the
13151 /// carving-marker identity.
13152 ///
13153 /// Composition laws (dual bindings): `s.as_atom_kind() ==
13154 /// s.as_atom().map(Atom::kind) == s.shape().as_atom_kind()` for
13155 /// every `s: &Sexp`. Pre-lift the atomic carving marker at the
13156 /// value level was reachable only via one of these two-step
13157 /// compositions — either through the [`Atom`] algebra
13158 /// (`as_atom().map(Atom::kind)`) or through the shape algebra
13159 /// (`shape().as_atom_kind()`). Post-lift the projection lands at
13160 /// ONE typed method on the value algebra, and both compositions
13161 /// are pinned as agreement laws (see
13162 /// `sexp_as_atom_kind_agrees_with_as_atom_map_kind_for_every_variant`
13163 /// and
13164 /// `sexp_as_atom_kind_agrees_with_shape_as_atom_kind_for_every_variant`
13165 /// in this module). A regression that drifts any of the three
13166 /// projections from the others surfaces immediately.
13167 ///
13168 /// Symmetric with [`Self::as_structural_kind`]'s shape (returns
13169 /// just the marker, no inner-payload borrow) — where
13170 /// [`Self::as_quote_form`] and [`Self::as_unquote`] surface both
13171 /// the marker AND the wrapped inner `&Sexp` (because the four
13172 /// quote-family arms and the two substitution arms structurally
13173 /// carry a boxed inner value), `as_atom_kind` and
13174 /// `as_structural_kind` return marker-only projections (the atomic
13175 /// arm's inner payload is heterogeneous across the six variants —
13176 /// `String` / `i64` / `f64` / `bool` — and the residual arms
13177 /// carry no or list-heterogeneous payload). Consumers that need
13178 /// the payload compose through [`Self::as_atom`] +
13179 /// [`Atom::as_symbol`] et al. (atomic axis) or [`Self::as_list`]
13180 /// (residual axis); this projection is the payload-agnostic
13181 /// carving-marker cell.
13182 ///
13183 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (Sexp
13184 /// variant, AtomKind carving marker) pairing becomes a TYPE
13185 /// projection on the substrate `Sexp` algebra rather than a
13186 /// two-step composition through either the [`Atom`] algebra or the
13187 /// shape algebra. A typo or swap at the value-projection site is
13188 /// no longer a runtime drift but a compile error against the
13189 /// typed projection. THEORY.md §VI.1 — generation over composition;
13190 /// the atomic-carving marker projection now lives on the typed
13191 /// `Sexp` algebra alongside [`Self::as_atom`], [`Self::as_list`],
13192 /// [`Self::as_quote_form`], [`Self::as_unquote`],
13193 /// [`Self::as_structural_kind`], completing the (Sexp value →
13194 /// closed-set carving marker) family across ALL THREE axes
13195 /// (atomic + quote-family + structural-residual). THEORY.md §II.1
13196 /// invariant 2 — free middle; every consumer that needs the
13197 /// atomic-carving marker at the value level (a future
13198 /// `tatara-check` predicate keyed on the atomic cohort, a future
13199 /// LSP structural-navigation filter that keys on the atomic
13200 /// carving, a future typed-rewriter walk over the atomic arm)
13201 /// binds to ONE typed method on the value algebra rather than a
13202 /// two-step composition.
13203 ///
13204 /// Sibling posture across the value-level marker family — the
13205 /// three projections (`as_atom_kind`, `as_quote_form`,
13206 /// `as_structural_kind`) form a partition of the seven outer-shape
13207 /// variants of the `Sexp` algebra: for every `s: &Sexp`, EXACTLY
13208 /// ONE returns `Some(_)` (pinned by the joint sweep
13209 /// `sexp_as_atom_kind_partitions_outer_shapes_jointly_with_as_quote_form_and_as_structural_kind`
13210 /// in this module, sibling to the pre-existing partition sweep
13211 /// keyed on `as_atom` rather than `as_atom_kind`). The value-level
13212 /// partition-total invariant across the three carvings is the
13213 /// value-level peer of the shape-level partition-total invariant
13214 /// (`sexp_shape_partition_is_total_across_atom_quote_structural_carvings`
13215 /// in error.rs); each axis has BOTH invariants pinned.
13216 ///
13217 /// Frontier inspiration: MLIR's `mlir::dyn_cast<AtomOp>(val)` typed
13218 /// soft-downcast onto the atomic carving of a closed-set value
13219 /// algebra — the (value, typed carving marker) pairing lives at
13220 /// ONE typed projection on the outer value-algebra sibling. The
13221 /// Rust-typed peer here uses the substrate's outer `Sexp` algebra
13222 /// with `Sexp::as_atom_kind` closing the atomic-carving cell of
13223 /// the value-level soft-projection surface, symmetric with the
13224 /// residual-carving projection [`Self::as_structural_kind`] and
13225 /// the quote-family projection [`Self::as_quote_form`]. Racket's
13226 /// `(atom? stx)` predicate paired with `(syntax->datum stx)` on
13227 /// the atomic branch — the substrate's `as_atom_kind` surfaces the
13228 /// typed witness (`AtomKind`) alongside the predicate verdict in
13229 /// ONE `Option<AtomKind>` projection.
13230 #[must_use]
13231 pub fn as_atom_kind(&self) -> Option<AtomKind> {
13232 self.as_atom().map(Atom::kind)
13233 }
13234
13235 /// Project this [`Sexp`] to its closed-set [`SexpShape`] outer-shape
13236 /// marker — `Nil → SexpShape::Nil`, `Atom(a) → a.kind().sexp_shape()`,
13237 /// `List(_) → SexpShape::List`, and each quote-family wrapper routes
13238 /// through `as_quote_form().map(|(qf, _)| qf.sexp_shape())`. The
13239 /// outer-shape peer on the [`Sexp`] algebra of [`Atom::kind`] (the
13240 /// atomic-payload axis) and [`QuoteForm::sexp_shape`] (the
13241 /// quote-family axis) — completes the substrate's Sexp-shape
13242 /// projection family by lifting the free-function dispatcher
13243 /// [`crate::domain::sexp_shape`] onto the typed `Sexp` algebra
13244 /// alongside its [`Atom`] / [`QuoteForm`] peers.
13245 ///
13246 /// Composition law: `s.shape() == crate::domain::sexp_shape(s)` for
13247 /// every `s: &Sexp`. The free function continues to exist as a thin
13248 /// delegate (its callers in `domain.rs`'s diagnostic-builder paths,
13249 /// `compile.rs`'s `TypeMismatch.got` builder, and downstream tests
13250 /// route through `s.shape()` after this lift), so the (Sexp variant,
13251 /// SexpShape variant) pairing now binds at ONE inherent method on
13252 /// the algebra rather than at a free function `domain` consumers
13253 /// must reach into the module path to invoke.
13254 ///
13255 /// Sibling-shape lift to the typed-EXIT projection trio on [`Atom`]
13256 /// ([`fmt::Display for Atom`], [`Atom::to_json`],
13257 /// `Atom::to_iac_forge_sexpr` (removed)) and the typed-ENTRY classifier
13258 /// ([`Atom::from_lexeme`]): where the atomic-payload algebra carries
13259 /// its own per-variant projection family at the atomic-payload
13260 /// level, the `Sexp` algebra carries this single outer-shape
13261 /// projection that composes through [`Self::as_atom`] +
13262 /// [`Atom::kind`] (atomic axis) and [`Self::as_quote_form`] (quote-
13263 /// family axis) — every other arm (`Nil`, `List`) projects to its
13264 /// own [`SexpShape`] variant directly.
13265 ///
13266 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
13267 /// (Sexp variant, SexpShape variant) pairing becomes an inherent
13268 /// algebra projection rather than a free function in `domain.rs`,
13269 /// so the projection sits next to the rest of the typed `Sexp`
13270 /// algebra ([`Self::as_atom`], [`Self::as_list`],
13271 /// [`Self::as_quote_form`], [`Self::head_symbol`],
13272 /// [`Self::as_call`]) the substrate carries. THEORY.md §II.1
13273 /// invariant 2 — free middle; every consumer that needs the
13274 /// outer shape (diagnostic builders at
13275 /// [`crate::domain::sexp_witness`] / [`crate::domain::missing_head_err`],
13276 /// [`crate::compile`]'s `TypeMismatch.got` projection, future LSP /
13277 /// REPL / `tatara-check` typed-pattern matchers) now reaches a
13278 /// method on the value rather than a free function imported from
13279 /// `domain`. THEORY.md §VI.1 — generation over composition; the
13280 /// inline dispatch lifted to [`crate::domain::sexp_shape`] is now
13281 /// lifted ONE algebra level higher — from the free function to
13282 /// the inherent method — so a future `Sexp` variant lands at the
13283 /// algebra's match site without a module-path indirection. A
13284 /// future extension (e.g. `Sexp::Vector` for `#(...)` reader
13285 /// syntax, `Sexp::Map` for `{...}`) extends THIS method + the
13286 /// `SexpShape` algebra + the free function's delegation in
13287 /// lockstep — exhaustively checked by rustc across the `Sexp`
13288 /// match.
13289 ///
13290 /// Frontier inspiration: MLIR's `mlir::Operation::getName()` —
13291 /// the typed-IR operation projects through an inherent method
13292 /// to its closed-set name on the operation algebra; `Sexp::shape`
13293 /// is the unstructured-Rust peer on the [`Sexp`] algebra for the
13294 /// outer-shape projection surface, with [`SexpShape`] standing in
13295 /// for MLIR's `OperationName` taxonomy. Racket's `(syntax-e stx)`
13296 /// composed with a datum-prim classifier on the closed-set
13297 /// syntax-taxonomy projects a syntax object to its outer shape via
13298 /// a single primitive on the syntax algebra; `Sexp::shape` is the
13299 /// substrate's typed-Rust peer.
13300 #[must_use]
13301 pub fn shape(&self) -> SexpShape {
13302 // Each variant routes through its closed-set carving-marker's
13303 // `sexp_shape` projection — the atomic-payload carving via
13304 // `AtomKind::sexp_shape`, the structural-residual carving via
13305 // `StructuralKind::sexp_shape`, the quote-family carving via
13306 // `QuoteForm::sexp_shape`. Post-lift the twelve outer-shape
13307 // arms of the SexpShape closed set are reached through THREE
13308 // carving-marker `sexp_shape` projections (6 + 2 + 4 = 12),
13309 // symmetric across the partition — no arm hits a raw
13310 // `SexpShape::*` literal here. A future thirteenth variant
13311 // (e.g. `Sexp::Vector` for `#(...)` reader syntax) extends the
13312 // carving-marker family the same way and lands at one arm
13313 // here + one carving-marker `sexp_shape` arm in lockstep.
13314 match self {
13315 Self::Nil => StructuralKind::Nil.sexp_shape(),
13316 Self::Atom(a) => a.kind().sexp_shape(),
13317 Self::List(_) => StructuralKind::List.sexp_shape(),
13318 Self::Quote(_) | Self::Quasiquote(_) | Self::Unquote(_) | Self::UnquoteSplice(_) => {
13319 let (qf, _) = self.expect_quote_form();
13320 qf.sexp_shape()
13321 }
13322 }
13323 }
13324
13325 /// Project this `Sexp` to its [`SexpWitness`] — the typed joint
13326 /// identity pairing the structural [`SexpShape`] with the
13327 /// renderable [`Sexp::Display`] projection in ONE owned value.
13328 /// The joint-identity peer on the [`Sexp`] algebra of
13329 /// [`Self::shape`] (the structural-shape-only projection) and
13330 /// [`fmt::Display for Sexp`] (the rendered-literal-only
13331 /// projection) — completes the substrate's Sexp-projection
13332 /// family by lifting the free-function dispatcher
13333 /// [`crate::domain::sexp_witness`] onto the typed `Sexp` algebra
13334 /// alongside its [`Self::shape`] peer.
13335 ///
13336 /// Composition law: `s.witness() ==
13337 /// crate::domain::sexp_witness(s)` for every `s: &Sexp`. The
13338 /// free function continues to exist as a thin delegate (its
13339 /// callers in `macro_expand.rs`'s 8 typed-entry rejection
13340 /// builders, `domain.rs`'s `missing_head_err` caller +
13341 /// `rewriter_non_list_err` typed-exit builder, and downstream
13342 /// tests route through `s.witness()` after this lift), so the
13343 /// (Sexp variant, SexpWitness identity) pairing now binds at
13344 /// ONE inherent method on the algebra rather than at a free
13345 /// function `domain` consumers must reach into the module path
13346 /// to invoke. Body composes the two algebra-level projections
13347 /// — `self.shape()` for the structural identity, `self.to_string()`
13348 /// for the renderable identity — into ONE
13349 /// [`SexpWitness::new`] call. Pre-lift the dispatcher lived as
13350 /// a free function in `domain.rs`; post-lift the canonical site
13351 /// is the inherent method and the free function delegates
13352 /// (mirrors the [`Self::shape`] lift in 121bb60 exactly).
13353 ///
13354 /// Sibling-shape lift to [`Self::shape`] (the structural-shape
13355 /// projection): where `shape()` carries the typed-shape axis on
13356 /// the `Sexp` algebra, `witness()` carries the JOINT typed-shape
13357 /// and renderable-literal axis — the typed identity an authoring
13358 /// tool diagnostic owes the operator AT the typed-entry or
13359 /// typed-exit rejection boundary. Every rejection-builder
13360 /// helper in `macro_expand.rs` that previously projected `&Sexp`
13361 /// through `crate::domain::sexp_witness(_)` at the variant
13362 /// boundary now reaches a method on the value rather than a
13363 /// free function imported from `domain`.
13364 ///
13365 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
13366 /// (Sexp variant, SexpWitness identity) pairing becomes an
13367 /// inherent algebra projection rather than a free function in
13368 /// `domain.rs`, so the projection sits next to the rest of the
13369 /// typed `Sexp` algebra ([`Self::shape`], [`Self::as_atom`],
13370 /// [`Self::as_list`], [`Self::as_quote_form`],
13371 /// [`Self::head_symbol`], [`Self::as_call`]) the substrate
13372 /// carries. THEORY.md §II.1 invariant 2 — free middle; every
13373 /// consumer that needs the typed joint identity at a
13374 /// rejection-boundary slot (`NonSymbolUnquoteTarget.got`,
13375 /// `SpliceOutsideList.got`, `NonSymbolParam.got`,
13376 /// `RestParamMissingName.got`, `RestParamTrailingTokens.first`,
13377 /// `OptionalParamMalformed.got`, `DefmacroNonSymbolName.got`,
13378 /// `DefmacroNonListParams.got`, `MissingHeadSymbol.got`,
13379 /// `RewriterNonList.got`, future LSP / REPL / `tatara-check`
13380 /// typed-pattern matchers) now reaches a method on the value
13381 /// rather than a free function imported from `domain`.
13382 /// THEORY.md §VI.1 — generation over composition; the inline
13383 /// dispatch lifted to [`crate::domain::sexp_witness`] is now
13384 /// lifted ONE algebra level higher — from the free function
13385 /// to the inherent method — completing the Sexp-projection
13386 /// family alongside [`Self::shape`]. A future `Sexp` variant
13387 /// extension (e.g. `Sexp::Vector` for `#(...)` reader syntax,
13388 /// `Sexp::Map` for `{...}`) reaches this method through the
13389 /// already-lifted [`Self::shape`] + [`fmt::Display for Sexp`]
13390 /// pair — no new arm needed here.
13391 ///
13392 /// Frontier inspiration: MLIR's diagnostic builder pattern —
13393 /// `op.emitOpError() << op` projects the offending operation
13394 /// through inherent methods (`getName()`, `print()`) into ONE
13395 /// diagnostic value; `Sexp::witness` is the unstructured-Rust
13396 /// peer on the [`Sexp`] algebra for the joint typed-shape +
13397 /// renderable-literal projection surface, with [`SexpWitness`]
13398 /// standing in for MLIR's `InFlightDiagnostic` typed payload.
13399 #[must_use]
13400 pub fn witness(&self) -> SexpWitness {
13401 SexpWitness::new(self.shape(), self.to_string())
13402 }
13403
13404 /// Project this `Sexp` to its stable, human-readable outer-shape
13405 /// label — the `&'static str` axis on the [`Sexp`] algebra. Lifts
13406 /// the free-function dispatcher [`crate::domain::sexp_type_name`]
13407 /// onto the typed `Sexp` algebra alongside its [`Self::shape`] /
13408 /// [`Self::witness`] / [`Self::to_json`] / [`Self::from_json`]
13409 /// sibling projections, completing the substrate's
13410 /// Sexp-projection family at the canonical-label axis the way
13411 /// [`Self::shape`] completes the typed-shape axis and
13412 /// [`fmt::Display for Sexp`] completes the canonical-string axis.
13413 ///
13414 /// Composition law: `s.type_name() == s.shape().label() ==
13415 /// crate::domain::sexp_type_name(s)` for every `s: &Sexp`.
13416 /// Pre-lift the projection lived as a free function in
13417 /// `domain.rs` consumers (in particular the `LispError::TypeMismatch`
13418 /// `got` slot in `compile.rs` and the legacy substring-grep
13419 /// rejection-message tests) reached across module boundaries to
13420 /// invoke; post-lift the canonical site is the inherent method on
13421 /// the [`Sexp`] algebra and the free function delegates so existing
13422 /// callers continue to compile. Body composes through
13423 /// [`Self::shape`] + [`SexpShape::label`] so a future `Sexp`
13424 /// variant (e.g. `Sexp::Vector` for `#(...)` reader syntax,
13425 /// `Sexp::Map` for `{...}`) lands at one extension site
13426 /// ([`Self::shape`]'s exhaustive arm) rather than a parallel
13427 /// `&'static str` match — the projection is structurally derived,
13428 /// not duplicated.
13429 ///
13430 /// Sibling-shape lift to [`Self::shape`] (the typed-shape
13431 /// projection): where `shape()` carries the typed
13432 /// [`SexpShape`] identity (matchable, exhaustive across `Sexp`
13433 /// variants), `type_name()` carries the `&'static str` literal
13434 /// the rendered diagnostic surface wants (still derived from
13435 /// the typed identity, but flattened through
13436 /// [`SexpShape::label`] for substring-grep callers and the
13437 /// `TypeMismatch.got` slot). The `&'static str` lifetime makes
13438 /// the projection cheap to embed in any error variant without
13439 /// allocation.
13440 ///
13441 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
13442 /// (Sexp variant, `&'static str` label) pairing becomes an
13443 /// inherent algebra projection rather than a free function in
13444 /// `domain.rs`, so the projection sits next to the rest of the
13445 /// typed `Sexp` algebra ([`Self::shape`], [`Self::witness`],
13446 /// [`Self::to_json`], [`Self::from_json`], [`Self::as_atom`],
13447 /// [`Self::as_list`], [`Self::as_quote_form`],
13448 /// [`Self::head_symbol`], [`Self::as_call`]) the substrate
13449 /// carries. THEORY.md §II.1 invariant 2 — free middle; every
13450 /// consumer that needs the outer-shape label
13451 /// (`LispError::TypeMismatch.got` projection in `compile.rs`,
13452 /// legacy substring-grep rejection-message tests, future LSP /
13453 /// REPL diagnostic surfaces) now reaches a method on the value
13454 /// rather than a free function imported from `domain`.
13455 /// THEORY.md §VI.1 — generation over composition; the inline
13456 /// `s.shape().label()` recipe lifted to
13457 /// [`crate::domain::sexp_type_name`] is now lifted ONE algebra
13458 /// level higher — from the free function to the inherent
13459 /// method — completing the Sexp-projection family alongside
13460 /// [`Self::shape`] / [`Self::witness`] / [`Self::to_json`] /
13461 /// [`Self::from_json`]. The `domain.rs` `sexp_*` free-function
13462 /// namespace is now structurally reserved for free functions
13463 /// that genuinely need a `domain`-module reach (registry
13464 /// dispatch, kwargs gates, registry suggestions), not
13465 /// algebra-layer projections.
13466 ///
13467 /// Frontier inspiration: MLIR's `mlir::Operation::getName()`
13468 /// composed with `OperationName::getStringRef()` — the typed-IR
13469 /// operation projects through inherent methods to its closed-set
13470 /// label on the operation algebra; `Sexp::type_name` is the
13471 /// unstructured-Rust peer on the [`Sexp`] algebra for the
13472 /// canonical-label projection surface, with [`SexpShape::label`]
13473 /// standing in for MLIR's `OperationName::getStringRef` second
13474 /// hop. Racket's `(syntax-name stx)` — the typed inverse of
13475 /// `(syntax-e stx)` on the syntax algebra; `Sexp::type_name`
13476 /// composes the typed-shape projection with its closed-set
13477 /// label projection at the inherent-method site rather than
13478 /// the typeclass-method site, matching pleme-io's
13479 /// "rust-typed, not trait-typed" idiom for closed-set algebras.
13480 #[must_use]
13481 pub fn type_name(&self) -> &'static str {
13482 self.shape().label()
13483 }
13484
13485 /// Project this `Sexp` to its canonical [`serde_json::Value`]
13486 /// rendering — the typed-algebra peer of [`Atom::to_json`] at the
13487 /// `Sexp` layer. Lifts the free-function dispatcher
13488 /// [`crate::domain::sexp_to_json`] onto the typed `Sexp` algebra
13489 /// alongside its [`Self::shape`] / [`Self::witness`] sibling
13490 /// projections, completing the JSON-projection axis at the
13491 /// algebra layer the way [`fmt::Display for Sexp`] completes the
13492 /// canonical-string axis. The free function continues to exist
13493 /// as a thin delegate (its callers in `tatara-lisp-derive`'s
13494 /// derive output route through it via the
13495 /// `crate::domain::sexp_to_json` import); the
13496 /// `from_value_with_path` private helper in `domain.rs` and the
13497 /// recursive sub-calls inside this method route through the
13498 /// inherent method directly so the canonical-site indirection
13499 /// disappears at every internal callsite.
13500 ///
13501 /// Rules (preserve byte-identical pre-lift behavior at the
13502 /// `sexp_to_json` callsite):
13503 /// - [`Self::Nil`] → [`serde_json::Value::Null`].
13504 /// - [`Self::Atom`] → [`Atom::to_json`] (the typed-algebra
13505 /// peer at the atomic-payload layer; pinned by
13506 /// `sexp_to_json_atom_arms_route_through_atom_to_json` in
13507 /// `domain.rs`).
13508 /// - [`Self::List`] with kwargs shape `(:k v :k v …)` →
13509 /// [`serde_json::Value::Object`] keyed by
13510 /// [`crate::domain::kebab_to_camel`] of each `:k`'s name.
13511 /// A duplicate kebab→camel key inside any nested kwargs-list
13512 /// fails with [`crate::domain::duplicate_kwarg`] — same
13513 /// typed-entry posture
13514 /// [`crate::domain::parse_kwargs`] takes at the top level.
13515 /// - [`Self::List`] otherwise → [`serde_json::Value::Array`]
13516 /// mapping each element through this method recursively.
13517 /// - [`Self::Quote`] / [`Self::Quasiquote`] / [`Self::Unquote`]
13518 /// / [`Self::UnquoteSplice`] → recurse on the inner via
13519 /// [`Self::expect_quote_form`] (strips the wrapper; the
13520 /// round-trip via [`crate::domain::json_to_sexp`] re-emits
13521 /// the inner without an enclosing wrapper). All four arms
13522 /// route through ONE [`Self::as_quote_form`]-derived
13523 /// projection so the per-variant pairing binds at ONE site
13524 /// on the [`QuoteForm`] algebra rather than four
13525 /// byte-identical inline arms.
13526 ///
13527 /// Composition law: `s.to_json() == crate::domain::sexp_to_json(s)`
13528 /// for every `s: &Sexp`. Pre-lift the dispatcher lived as a free
13529 /// function in `domain.rs`; post-lift the canonical site is the
13530 /// inherent method and the free function delegates (same lift
13531 /// posture as [`Self::shape`] in 121bb60 and [`Self::witness`]
13532 /// in a427e3b).
13533 ///
13534 /// Sibling-shape lift to [`Self::shape`] (the structural-shape
13535 /// projection), [`Self::witness`] (the joint structural-shape +
13536 /// renderable-literal projection), and [`fmt::Display for Sexp`]
13537 /// (the renderable-literal projection): where those three carry
13538 /// the Lisp-canonical-form / structural-identity axes on the
13539 /// algebra, `to_json` carries the JSON canonical-form axis. The
13540 /// substrate's `Sexp` algebra now binds ALL THREE canonical-form
13541 /// projection surfaces (Lisp Display, JSON, and the feature-gated
13542 /// iac-forge `From<&Sexp> for SExpr`) at the algebra layer, with
13543 /// per-variant atomic rendering composed through the corresponding
13544 /// [`Atom`] projection family (`Atom::Display`, [`Atom::to_json`],
13545 /// `Atom::to_iac_forge_sexpr`).
13546 ///
13547 /// Theory anchor: THEORY.md §VI.1 — generation over composition;
13548 /// the inline dispatch the prior runs lifted onto
13549 /// [`crate::domain::sexp_to_json`] (the free function) is now
13550 /// lifted ONE algebra level higher — from the free function to
13551 /// the inherent method — completing the Sexp-projection family
13552 /// alongside [`Self::shape`] and [`Self::witness`]. THEORY.md
13553 /// §II.1 invariant 2 — free middle; the typed-exit JSON
13554 /// projection (every consumer that round-trips a Sexp through
13555 /// `serde_json::from_value::<T>` for typed-domain
13556 /// deserialization, the typed-rewriter at
13557 /// [`crate::domain::TypedRewriter`], the derive macro's
13558 /// `compile_from_args` fallthrough, and any future canonical-
13559 /// form surface) all route through ONE inherent algebra method
13560 /// rather than reach into the `domain` module path for a free
13561 /// function. THEORY.md §V.1 — knowable platform; a future
13562 /// `Sexp` variant extension (e.g. `Sexp::Vector` for `#(...)`
13563 /// reader syntax, `Sexp::Map` for `{...}`) reaches this method
13564 /// through the already-lifted [`Self::as_quote_form`] +
13565 /// [`Atom::to_json`] pair — one arm added here for the new
13566 /// outer-shape variant; rustc enforces the per-variant body is
13567 /// named.
13568 ///
13569 /// Frontier inspiration: MLIR's `mlir::AsmPrinter::printOp` —
13570 /// the typed-IR printer dispatches on the closed-set `Op` so
13571 /// every printer body for an op lives at ONE implementation site;
13572 /// `Sexp::to_json` is the unstructured-Rust peer on the `Sexp`
13573 /// algebra for the JSON canonical-form surface (where
13574 /// [`fmt::Display for Sexp`] is the Lisp-canonical-form peer
13575 /// and `From<&Sexp> for iac_forge::SExpr` is the
13576 /// canonical-attestation-form peer). Racket's `(syntax->datum
13577 /// stx)` then a serializer over the datum prim — `to_json` is
13578 /// the substrate's serializer at the Sexp layer composed
13579 /// through [`Atom::to_json`] at the atomic-payload layer, with
13580 /// the closed-set [`AtomKind`] standing in for Racket's
13581 /// datum-prim taxonomy.
13582 pub fn to_json(&self) -> crate::error::Result<serde_json::Value> {
13583 Ok(match self {
13584 Self::Nil => serde_json::Value::Null,
13585 Self::Atom(a) => a.to_json(),
13586 Self::List(items) => {
13587 if self.is_kwargs_list() {
13588 let mut map = serde_json::Map::with_capacity(items.len() / 2);
13589 let mut i = 0;
13590 while i + 1 < items.len() {
13591 if let Some(k) = items[i].as_keyword() {
13592 let value = items[i + 1].to_json()?;
13593 if map
13594 .insert(crate::domain::kebab_to_camel(k), value)
13595 .is_some()
13596 {
13597 return Err(crate::domain::duplicate_kwarg(k));
13598 }
13599 i += 2;
13600 } else {
13601 break;
13602 }
13603 }
13604 serde_json::Value::Object(map)
13605 } else {
13606 serde_json::Value::Array(
13607 items
13608 .iter()
13609 .map(Self::to_json)
13610 .collect::<crate::error::Result<Vec<_>>>()?,
13611 )
13612 }
13613 }
13614 Self::Quote(_) | Self::Quasiquote(_) | Self::Unquote(_) | Self::UnquoteSplice(_) => {
13615 let (_, inner) = self.expect_quote_form();
13616 inner.to_json()?
13617 }
13618 })
13619 }
13620
13621 /// Inverse of [`Self::to_json`] — project a [`serde_json::Value`] back
13622 /// onto a [`Sexp`]. The closed-set [`serde_json::Value`] discriminator
13623 /// maps directly onto the corresponding [`Sexp`] constructor:
13624 ///
13625 /// - [`serde_json::Value::Null`] → [`Self::Nil`].
13626 /// - [`serde_json::Value::Bool`] → [`Self::boolean`].
13627 /// - [`serde_json::Value::Number`] → [`Self::int`] when the value
13628 /// fits an [`i64`], otherwise [`Self::float`] when it fits an
13629 /// [`f64`]; the structural impossibility "neither i64 nor f64"
13630 /// collapses to [`Self::int(0)`](Self::int) as a typed floor —
13631 /// [`serde_json::Number`]'s closed-set discriminator excludes
13632 /// this case in practice (every [`serde_json::Number`] is either
13633 /// i64-fitting, u64-fitting projected through f64, or f64-fitting
13634 /// directly), but the typed floor stays explicit so a future
13635 /// `serde_json` extension does not silently misroute. Mirror of
13636 /// [`Atom::to_json`]'s [`Self::int`] / [`Self::float`] bifurcation.
13637 /// - [`serde_json::Value::String`] → [`Self::string`]. The
13638 /// `serde_json::Value::String` discriminator is type-erased — a
13639 /// serde-projected symbol AND a serde-projected keyword AND a
13640 /// genuine string literal ALL inhabit it on the JSON side — so
13641 /// the back-projection chooses [`Self::string`] as the lossless
13642 /// floor for the `Atom::Symbol` / `Atom::Keyword` / `Atom::Str`
13643 /// three-way collapse. Consumers that need the symbol-vs-string
13644 /// distinction must preserve it BEFORE the JSON round-trip
13645 /// (e.g. through a typed enum's serde projection rather than a
13646 /// raw `Sexp`-to-`JValue` round-trip).
13647 /// - [`serde_json::Value::Array`] → [`Self::List`] mapping each
13648 /// element through this method recursively.
13649 /// - [`serde_json::Value::Object`] → [`Self::List`] of alternating
13650 /// `:key value` pairs in [`serde_json::Map`]'s iteration order
13651 /// (sorted by key under `serde_json`'s default `BTreeMap`
13652 /// backing; insertion order under the optional `preserve_order`
13653 /// feature, which the substrate does NOT enable today), with
13654 /// each JSON key projected through
13655 /// [`crate::domain::camel_to_kebab`] to recover the `:k`'s
13656 /// kebab-case authoring shape and each JSON value recursed
13657 /// through this method. Inverse of [`Self::to_json`]'s
13658 /// [`Self::List`] kwargs-shape arm: that arm projects
13659 /// `:k v :k v …` into a JSON object via
13660 /// [`crate::domain::kebab_to_camel`]; this arm projects the
13661 /// object back into a `Self::List` of alternating keyword /
13662 /// value via the inverse [`crate::domain::camel_to_kebab`].
13663 ///
13664 /// Composition law: `Self::from_json(&s.to_json()?)` projects back
13665 /// to a `Sexp` whose [`Self::to_json`] re-projection produces the
13666 /// SAME `JValue` (modulo the lossy `Symbol` / `Keyword` / `Str`
13667 /// three-way collapse documented above; for the round-trippable
13668 /// subset, `Sexp::Nil`, the six [`Atom`] kinds within their
13669 /// discriminator class, and recursively `Sexp::List` of round-
13670 /// trippable elements, the law holds byte-for-byte).
13671 ///
13672 /// Sibling-lift posture: this method mirrors the prior
13673 /// [`crate::domain::sexp_to_json`] → [`Self::to_json`] (commit
13674 /// 875ee3b) / [`crate::domain::sexp_shape`] → [`Self::shape`]
13675 /// (commit 121bb60) / [`crate::domain::sexp_witness`] →
13676 /// [`Self::witness`] (commit a427e3b) family of lifts, all of which
13677 /// promoted a free function in `domain.rs` to the inherent-method
13678 /// canonical site on the [`Sexp`] algebra. Pre-lift the
13679 /// `json_to_sexp` dispatcher lived in `domain.rs` as the canonical
13680 /// site; post-lift this inherent method is the canonical site and
13681 /// the free function delegates so every existing caller continues
13682 /// to compile.
13683 ///
13684 /// Sibling-shape lift on the round-trip closure: the substrate's
13685 /// `Sexp` ↔ `serde_json::Value` round-trip now lives entirely as
13686 /// two inherent methods on the [`Sexp`] algebra — [`Self::to_json`]
13687 /// (forward) and [`Self::from_json`] (inverse). Consumers that
13688 /// previously round-tripped a typed value through Lisp forms via
13689 /// `domain::sexp_to_json` + `domain::json_to_sexp` now bind to ONE
13690 /// algebra (the inherent-method family) rather than reaching across
13691 /// the `domain` module path for two free functions. A future
13692 /// canonical-form surface (e.g., a YAML round-trip via
13693 /// [`serde_yaml`], a Nix-expression round-trip via the typed Nix
13694 /// surface in `tatara-nix`) hangs off the SAME `Sexp` algebra at
13695 /// `Self::to_yaml` / `Self::from_yaml` / `Self::to_nix` /
13696 /// `Self::from_nix` — the naming pattern is now structurally
13697 /// established by this pair.
13698 ///
13699 /// Theory anchor: THEORY.md §VI.1 — generation over composition;
13700 /// the inline `json_to_sexp` dispatcher in `domain.rs` is lifted
13701 /// ONE algebra level higher (from free function to inherent
13702 /// method), completing the Sexp ↔ JValue round-trip closure
13703 /// alongside [`Self::to_json`]. THEORY.md §V.1 — knowable
13704 /// platform; the inverse projection becomes a NAMED primitive on
13705 /// the substrate's `Sexp` algebra rather than a `domain`-module
13706 /// free function consumers reach across module boundaries to call.
13707 /// THEORY.md §II.1 invariant 2 — free middle; every consumer that
13708 /// round-trips through JSON (the typed-rewriter at
13709 /// [`crate::domain::TypedRewriter`], the derive macro's
13710 /// `compile_from_args` JSON fallthrough, the test round-trip
13711 /// fixtures) routes through ONE inherent algebra method — the
13712 /// typed round-trip closure is structurally complete on the
13713 /// `Sexp` algebra.
13714 ///
13715 /// Frontier inspiration: MLIR's `mlir::parseAttribute(str, ctx)` —
13716 /// the typed-IR parser inverse of `printAttribute` lives on the
13717 /// same `Attribute` algebra as its printer dual; the substrate's
13718 /// [`Self::from_json`] is the unstructured-Rust peer on the
13719 /// `Sexp` algebra for the JSON canonical-form inverse, paired
13720 /// with [`Self::to_json`] as the closed round-trip. Racket's
13721 /// `(datum->syntax stx datum)` — the round-trip inverse of
13722 /// `(syntax->datum stx)`, projected at the `datum` algebra layer;
13723 /// `Self::from_json` is the substrate's peer at the `Sexp` layer
13724 /// (one algebra level lower than Racket's `syntax` wrapper).
13725 #[must_use]
13726 pub fn from_json(v: &serde_json::Value) -> Self {
13727 match v {
13728 serde_json::Value::Null => Self::Nil,
13729 serde_json::Value::Bool(b) => Self::boolean(*b),
13730 // Numeric arm — the (JSON `Number` → typed [`Atom`]
13731 // numeric variant) bifurcation lifts onto the closed-set
13732 // [`Atom`] algebra via [`Atom::from_json_number`]. Pre-lift
13733 // this arm carried its own inline three-branch cascade
13734 // (`n.as_i64()` sink to [`Self::int`] then `n.as_f64()`
13735 // sink to [`Self::float`] then a `Self::int(0)` typed
13736 // floor for the structural-impossibility residual);
13737 // post-lift the WHOLE cascade binds at ONE typed projection
13738 // on the algebra so a delimiter swap of the numeric axis
13739 // (e.g. adding a `u64`-fitting arm for the
13740 // `serde-preserve-order` feature's `arbitrary_precision`
13741 // mode, extending [`Atom`] with a `Bigint` variant) extends
13742 // [`Atom::from_json_number`] ONCE — the outer [`Self::from_json`]
13743 // arm here delegates through the algebra with zero edits.
13744 // Structural dual of [`Atom::to_json`]'s [`Atom::Int`] /
13745 // [`Atom::Float`] arms one algebra layer over: the paired
13746 // FORWARD (typed variant → `JValue::Number`) AND INVERSE
13747 // (`JValue::Number` → typed variant) numeric-axis
13748 // projections both live on the closed-set [`Atom`] algebra,
13749 // and this outer [`Self::from_json`] arm binds to the
13750 // inverse peer at ONE typed method. Sibling-shape pin to
13751 // [`Self::Atom`]'s general delegation posture — the outer
13752 // `Sexp` layer wraps the atomic algebra's typed projection
13753 // via [`Self::Atom`] rather than re-deriving the
13754 // per-variant construction at this consumer.
13755 serde_json::Value::Number(n) => Self::Atom(Atom::from_json_number(n)),
13756 serde_json::Value::String(s) => Self::string(s.clone()),
13757 serde_json::Value::Array(items) => {
13758 Self::List(items.iter().map(Self::from_json).collect())
13759 }
13760 serde_json::Value::Object(map) => {
13761 let mut out = Vec::with_capacity(map.len() * 2);
13762 for (k, v) in map {
13763 out.push(Self::keyword(crate::domain::camel_to_kebab(k)));
13764 out.push(Self::from_json(v));
13765 }
13766 Self::List(out)
13767 }
13768 }
13769 }
13770
13771 pub fn as_symbol(&self) -> Option<&str> {
13772 self.as_atom().and_then(Atom::as_symbol)
13773 }
13774 pub fn as_keyword(&self) -> Option<&str> {
13775 self.as_atom().and_then(Atom::as_keyword)
13776 }
13777 pub fn as_string(&self) -> Option<&str> {
13778 self.as_atom().and_then(Atom::as_string)
13779 }
13780 pub fn as_int(&self) -> Option<i64> {
13781 self.as_atom().and_then(Atom::as_int)
13782 }
13783 /// `Some(f)` for `Atom::Float(f)`, AND `Some(n as f64)` for
13784 /// `Atom::Int(n)` — caller convenience at the numeric-kwarg
13785 /// boundary. The Int-widening face lives at this consumer layer
13786 /// rather than at [`Atom::as_float`] (strict per the typed-identity
13787 /// discipline pinned at [`Atom::as_int`]'s docstring); the typed
13788 /// soft-projection algebra on `Atom` stays strict, and the
13789 /// `Sexp::as_float` consumer composes the strict typed projection
13790 /// with a fallback widening branch on `Atom::as_int`.
13791 pub fn as_float(&self) -> Option<f64> {
13792 let a = self.as_atom()?;
13793 a.as_float().or_else(|| a.as_int().map(|n| n as f64))
13794 }
13795 pub fn as_bool(&self) -> Option<bool> {
13796 self.as_atom().and_then(Atom::as_bool)
13797 }
13798 /// `foo` or `"foo"` — useful for names that may be authored either way.
13799 ///
13800 /// Structural-lift composition: routes through [`Sexp::as_atom`] + the
13801 /// algebra-level [`Atom::as_symbol_or_string`] union projection — the
13802 /// same `as_atom().and_then(Atom::as_X)` composition pattern
13803 /// [`Sexp::as_symbol`] / [`Sexp::as_keyword`] / [`Sexp::as_string`] /
13804 /// [`Sexp::as_int`] / [`Sexp::as_bool`] route through on the
13805 /// per-variant axis. Lifts the disjunctive
13806 /// `self.as_symbol().or_else(|| self.as_string())` composition at this
13807 /// site's pre-lift body (TWO `Sexp::as_atom` traversals — one per
13808 /// per-variant projection) onto ONE typed-algebra union projection
13809 /// reached via ONE `Sexp::as_atom` traversal.
13810 ///
13811 /// Composition law: `s.as_symbol_or_string() == s.as_atom().and_then(Atom::as_symbol_or_string)`
13812 /// for every [`Sexp`] `s`. See [`Atom::as_symbol_or_string`] for the
13813 /// algebra-level peer's docstring (per-variant family completion +
13814 /// theory grounding).
13815 pub fn as_symbol_or_string(&self) -> Option<&str> {
13816 self.as_atom().and_then(Atom::as_symbol_or_string)
13817 }
13818
13819 /// The symbol in operator position — `Some(s)` iff this is a non-empty
13820 /// list whose first element is a symbol (`(defpoint …)` → `Some("defpoint")`).
13821 /// `None` for every other shape: a non-list (`foo`, `5`, `:kw`), the
13822 /// empty list `()`, and a list whose head is not a symbol (`(5 …)`,
13823 /// `(:kw …)`, `((nested) …)`).
13824 ///
13825 /// This is the *operator-position projection* — the structural query
13826 /// every form-dispatch site in the substrate keys on: "what operator
13827 /// does this form invoke?" Macroexpansion (`Expander::expand` looks up
13828 /// the head against the macro table; `macro_def_from` reads it to
13829 /// recognize a `defmacro` head) and the typed compilers
13830 /// (`compile_typed` / `compile_named_from_forms` match it against
13831 /// `T::KEYWORD`) all asked the same `self.as_list()?.first()?.as_symbol()`
13832 /// question inline. Naming it once makes "operator position" a primitive
13833 /// of the `Sexp` algebra rather than four byte-identical inline chains.
13834 ///
13835 /// This is the SOFT face of operator-position dispatch — it answers
13836 /// "is this form an invocation of some operator?" and yields `None`
13837 /// (skip / fall through) for everything that isn't, with no diagnostic.
13838 /// Its STRICT sibling is `TataraDomain::compile_from_sexp`, which on a
13839 /// matched-arity form distinguishes the empty-list and
13840 /// present-but-not-a-symbol head sub-modes to emit a rich
13841 /// `MissingHeadSymbol` rejection. The two are the dispatch (`head_symbol`)
13842 /// and the gate (`compile_from_sexp`) faces of the same projection;
13843 /// keeping both lets a site choose "skip silently" or "reject loudly"
13844 /// without re-deriving the head.
13845 ///
13846 /// `head_symbol` is the operator projection of [`Sexp::as_call`]: it
13847 /// keeps the head and discards the argument tail. The
13848 /// `as_list()?.first()?.as_symbol()` chain lives in ONE place
13849 /// (`as_call`); this is its first component.
13850 pub fn head_symbol(&self) -> Option<&str> {
13851 self.as_call().map(|(head, _)| head)
13852 }
13853
13854 /// Decompose a call form into its operator and argument tail —
13855 /// `Some((op, args))` iff this is a non-empty list whose first element
13856 /// is a symbol, where `op` is that head symbol and `args` is the
13857 /// remaining elements (`&self[1..]`, possibly empty). `None` for every
13858 /// shape `head_symbol` rejects: a non-list, the empty list, and a list
13859 /// whose head is present but not a symbol.
13860 ///
13861 /// This is the *call-form decomposition* — the structural shape of a
13862 /// Lisp invocation: an operator applied to an argument tail. It pairs
13863 /// the operator-position projection (`head_symbol`) with the argument
13864 /// tail every dispatch site reads immediately after matching the
13865 /// operator. Macroexpansion (`Expander::expand`) applies the matched
13866 /// macro to `&list[1..]`; the typed compilers (`compile_typed`,
13867 /// `compile_named_from_forms`) feed `&list[1..]` into
13868 /// `T::compile_from_args`. Before this query each site bound
13869 /// `as_list()` for the tail AND independently called `head_symbol()`
13870 /// (which itself re-derives `as_list().first()`) for the operator —
13871 /// two traversals of the same list, two projections. `as_call` yields
13872 /// both from one match, so the operator and its arguments can never
13873 /// drift out of agreement at a dispatch site.
13874 ///
13875 /// Soft face, like `head_symbol`: it answers "is this an invocation of
13876 /// some operator, and what are its arguments?" and yields `None` (skip
13877 /// / fall through) for everything that isn't, with no diagnostic. The
13878 /// strict gate sibling is `TataraDomain::compile_from_sexp`, which
13879 /// distinguishes the empty-list and non-symbol-head sub-modes to reject
13880 /// loudly.
13881 pub fn as_call(&self) -> Option<(&str, &[Sexp])> {
13882 let list = self.as_list()?;
13883 let head = list.first()?.as_symbol()?;
13884 Some((head, &list[1..]))
13885 }
13886
13887 /// Canonical call-form outer constructor — composes the atomic-
13888 /// payload construct family's [`Self::symbol`] (the head-position
13889 /// construct on the 6-of-12 atomic carving of [`SexpShape`]) with
13890 /// the residual-axis construct family's [`Self::list`] (via
13891 /// `std::iter::once(head_sexp).chain(args)`) to build a symbol-
13892 /// headed list-shaped [`Sexp`] value at ONE site on the closed-set
13893 /// [`Sexp`] algebra. The call-form section-for-retraction sibling
13894 /// of the existing [`Self::as_call`] soft-projection ([`Option<(&
13895 /// str, &[Sexp])>`]): where the projection soft-decomposes a
13896 /// symbol-headed list into its head symbol and argument tail, this
13897 /// constructor embeds a fresh (head string, item sequence) pair
13898 /// into the matching call-shaped wrapper.
13899 ///
13900 /// Composition sibling of the atomic-payload construct family
13901 /// ([`Self::symbol`], [`Self::keyword`], [`Self::string`],
13902 /// [`Self::int`], [`Self::float`], [`Self::boolean`] — routing
13903 /// through the typed [`Atom`] family on the 6-of-12 atomic carving),
13904 /// the quote-family construct family ([`Self::quote`],
13905 /// [`Self::quasiquote`], [`Self::unquote`], [`Self::unquote_splice`]
13906 /// — routing through the typed [`QuoteForm::wrap`] family on the
13907 /// 4-of-12 quote-family carving), and the residual-axis construct
13908 /// [`Self::list`] (routing owned or iterable item sequences into
13909 /// the tuple-variant on the 2-of-12 residual carving): those close
13910 /// the (construct, project) algebra dual on their respective
13911 /// STRUCTURAL carvings; this closes the (construct, project)
13912 /// algebra dual on the SYMBOL-HEADED-LIST TYPED DECOMPOSITION — the
13913 /// load-bearing shape every Lisp invocation, every `(defX …)`
13914 /// typed-domain call form, and every macroexpander template head
13915 /// takes on the outer [`Sexp`] algebra.
13916 ///
13917 /// Composition law (forward, through the outer algebra's atomic +
13918 /// residual construct families): `Sexp::call(head, args) ==
13919 /// Sexp::list(std::iter::once(Sexp::symbol(head)).chain(args))` for
13920 /// every `head: impl Into<String>` + `args: impl IntoIterator<Item
13921 /// = Sexp>`. The body binds through the SAME two construct methods
13922 /// consumers already reach for when threading a head-then-rest
13923 /// sequence into a call form — the composition law lifts that
13924 /// two-method inline pattern to ONE named query on the outer
13925 /// [`Sexp`] algebra.
13926 ///
13927 /// Round-trip law (section-for-retraction with the soft-projection
13928 /// sibling): for every `head: &str` + `args: Vec<Sexp>`,
13929 /// `Sexp::call(head, args.clone()).as_call() == Some((head,
13930 /// args.as_slice()))` — the outer algebra's call-form typed
13931 /// constructor pairs section-for-retraction with the outer
13932 /// algebra's soft call-form projection, and the (head symbol,
13933 /// args slice) cross-projection preserves identity. Keyword-
13934 /// matched round-trip law: for every `head: &str` + `args:
13935 /// Vec<Sexp>`, `Sexp::call(head, args.clone()).as_call_to(head) ==
13936 /// Some(args.as_slice())` — the keyword-typed projection recovers
13937 /// the args tail iff its argument keyword matches the constructor's
13938 /// head. Head-symbol composition law: `Sexp::call(head,
13939 /// args).head_symbol() == Some(head.as_str())` for every `head:
13940 /// impl Into<String>` + `args: impl IntoIterator<Item = Sexp>` —
13941 /// the head-position projection recovers the constructor's head
13942 /// byte-for-byte.
13943 ///
13944 /// Outer-shape composition law: `Sexp::call(head, args).shape() ==
13945 /// SexpShape::List` for every input — a call form is a list-shaped
13946 /// [`Sexp`], and the outer-shape identity binds through the typed-
13947 /// shape lattice at the residual arm. Structural-carving-marker
13948 /// composition law: `Sexp::call(head, args).as_structural_kind()
13949 /// == Some(StructuralKind::List)` — the residual-axis carving
13950 /// marker binds through the closed-set [`StructuralKind`] algebra
13951 /// at ONE arm, symmetric with the atomic-axis's `Sexp::X_atom(
13952 /// payload).as_atom_kind() == Some(AtomKind::X)` marker
13953 /// composition.
13954 ///
13955 /// Pre-lift the `Sexp::List(std::iter::once(Sexp::symbol(head))
13956 /// .chain(args).collect())` composition (or equivalently the
13957 /// `Sexp::List(vec![Sexp::symbol(head), args...])` welded triple)
13958 /// appeared inline at every consumer that builds a call-shaped
13959 /// [`Sexp`] value — well past the ≥2 PRIME-DIRECTIVE trigger once
13960 /// the call-form shape is named. Post-lift consumers that have a
13961 /// head string + an owned or iterable sequence of args bind to ONE
13962 /// typed-algebra method on the outer [`Sexp`] algebra with the
13963 /// `impl Into<String>` bound on the head absorbing `&str` /
13964 /// `String` / `&String` and the `impl IntoIterator<Item = Sexp>`
13965 /// bound on the args absorbing `Vec<Sexp>` / `[Sexp; N]` /
13966 /// `.map(...)` chains without a per-site `.collect::<Vec<Sexp>>()`
13967 /// coercion.
13968 ///
13969 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
13970 /// (head string, args sequence, [`Self::List`] tuple-variant
13971 /// constructor) triple binds at ONE typed-algebra method on the
13972 /// outer [`Sexp`] algebra, closing the call-form (construct,
13973 /// project) algebra dual pair with [`Self::as_call`] /
13974 /// [`Self::as_call_to`] / [`Self::head_symbol`]. THEORY.md §II.1
13975 /// invariant 2 — free middle; every consumer that has a head
13976 /// string + an owned or iterable sequence of args and wants to
13977 /// build a call-shaped [`Sexp`] routes through the SAME typed
13978 /// method, so a regression that drifts one consumer's construction
13979 /// from the others (e.g. a copy-edit that emits `Sexp::keyword(
13980 /// head)` for the head position, or that swaps in a `Sexp::string`
13981 /// head that [`Self::as_call`] then rejects at the projection
13982 /// site) cannot reach the substrate's runtime. THEORY.md §V.1 —
13983 /// knowable platform; the call-form typed-construct becomes a TYPE
13984 /// projection on the substrate's outer [`Sexp`] algebra sitting
13985 /// next to the typed-project family [`Self::as_call`] /
13986 /// [`Self::as_call_to`] rather than bare tuple-variant constructor
13987 /// paired with per-site `Sexp::List(vec![Sexp::symbol(...), ...])`
13988 /// discipline. THEORY.md §VI.1 — generation over composition; the
13989 /// call-form pair emerges from ONE typed-algebra composition
13990 /// through [`Self::list`] composed with [`Self::symbol`] rather
13991 /// than from per-consumer per-callsite literals; a future call-
13992 /// form shape extension (e.g. a keyword-headed call form for a
13993 /// Kernel-style applicative-vs-operative split) lands as ONE peer
13994 /// constructor on this algebra alongside the residual, quote-
13995 /// family, and atomic-payload construct families.
13996 #[must_use]
13997 pub fn call<H, I>(head: H, args: I) -> Self
13998 where
13999 H: Into<String>,
14000 I: IntoIterator<Item = Sexp>,
14001 {
14002 Self::list(std::iter::once(Self::symbol(head)).chain(args))
14003 }
14004
14005 /// Canonical named-call-form outer constructor — composes the call-
14006 /// form typed constructor [`Self::call`] with the atomic-payload
14007 /// construct family's [`Self::symbol`] (for the NAME slot) via
14008 /// `std::iter::once(Self::symbol(name)).chain(spec_args)` to build a
14009 /// `(head NAME spec_args…)` symbol-headed named list-shaped [`Sexp`]
14010 /// value at ONE site on the closed-set [`Sexp`] algebra. The named-
14011 /// call-form section-for-retraction sibling of the existing
14012 /// [`Self::as_named_call_to`] soft-projection ([`Option<crate::error::
14013 /// Result<(&str, &[Sexp])>>`]): where the projection soft-decomposes
14014 /// a `(<keyword> NAME spec_args…)` symbol-headed list into its NAME
14015 /// symbol and spec args tail through the named-form gate
14016 /// ([`crate::compile::split_name_slot`]), this constructor embeds a
14017 /// fresh `(head string, name string, spec_args sequence)` triple
14018 /// into the matching named-call-shaped wrapper. Composition sibling
14019 /// of the call-form construct [`Self::call`] on the outer algebra:
14020 /// where [`Self::call`] closes the (construct, project) dual on the
14021 /// CALL-FORM TYPED DECOMPOSITION (`(head args…)`), this closes the
14022 /// dual on the NAMED-CALL-FORM TYPED DECOMPOSITION (`(head NAME
14023 /// spec_args…)`) — the load-bearing shape every `(defX NAME …)`
14024 /// typed-domain named authoring form takes on the outer [`Sexp`]
14025 /// algebra, and the section-for-retraction dual of the
14026 /// [`crate::compile::split_name_slot`] gate at the value level.
14027 ///
14028 /// Composition law (forward, through the call-form + atomic-payload
14029 /// construct families): `Sexp::named_call(head, name, spec_args) ==
14030 /// Sexp::call(head, std::iter::once(Sexp::symbol(name)).chain(
14031 /// spec_args))` for every `head: impl Into<String>` + `name: impl
14032 /// Into<String>` + `spec_args: impl IntoIterator<Item = Sexp>`. The
14033 /// body binds through the SAME two construct methods consumers
14034 /// already reach for when threading a head-then-name-then-rest
14035 /// sequence into a named call form — the composition law lifts that
14036 /// two-method inline pattern to ONE named query on the outer
14037 /// [`Sexp`] algebra.
14038 ///
14039 /// Round-trip law (section-for-retraction with the named-form soft-
14040 /// projection): for every `head: &'static str` + `name: &str` +
14041 /// `spec_args: Vec<Sexp>`, `Sexp::named_call(head, name, spec_args
14042 /// .clone()).as_named_call_to(head) == Some(Ok((name, spec_args
14043 /// .as_slice())))` — the outer algebra's named-call-form typed
14044 /// constructor pairs section-for-retraction with the outer
14045 /// algebra's soft named-call-form projection, and the (head symbol,
14046 /// NAME symbol, spec args slice) cross-projection preserves
14047 /// identity. Call-form projection composition: `Sexp::named_call(
14048 /// head, name, spec_args).as_call() == Some((head,
14049 /// once(Sexp::symbol(name)).chain(spec_args).collect().as_slice()
14050 /// ))` — the call-form soft-projection recovers `(head, [name,
14051 /// spec_args…])` with the NAME symbol as the first arg, mirroring
14052 /// the [`Self::call`] round-trip on the encompassing call algebra.
14053 /// Keyword-matched round-trip law: for every `head: &'static str` +
14054 /// `name: &str` + `spec_args: Vec<Sexp>`, `Sexp::named_call(head,
14055 /// name, spec_args.clone()).as_call_to(head) == Some(
14056 /// [Sexp::symbol(name), spec_args…].as_slice())` — the keyword-
14057 /// typed projection recovers the NAME-headed args tail iff its
14058 /// argument keyword matches the constructor's head. Head-symbol
14059 /// composition law: `Sexp::named_call(head, name, spec_args)
14060 /// .head_symbol() == Some(head.as_str())` — the head-position
14061 /// projection recovers the constructor's head byte-for-byte.
14062 ///
14063 /// Outer-shape composition law: `Sexp::named_call(head, name,
14064 /// spec_args).shape() == SexpShape::List` for every input — a
14065 /// named call form is a list-shaped [`Sexp`], the outer-shape
14066 /// identity binds through the typed-shape lattice at the residual
14067 /// arm. Structural-carving-marker composition law: `Sexp::
14068 /// named_call(head, name, spec_args).as_structural_kind() ==
14069 /// Some(StructuralKind::List)` — the residual-axis carving marker
14070 /// binds through the closed-set [`StructuralKind`] algebra at ONE
14071 /// arm, symmetric with [`Self::call`]'s residual-arm marker
14072 /// composition.
14073 ///
14074 /// Named-form gate composition law: `crate::compile::split_name_slot(
14075 /// &Sexp::named_call(head, name, spec_args).as_call_to(head)
14076 /// .unwrap(), head) == Ok((name, spec_args.as_slice()))` — the
14077 /// substrate's named-form arity + NAME-shape gate accepts every
14078 /// output of this constructor byte-for-byte, closing the section-
14079 /// for-retraction pair at the gate level as well as at the
14080 /// projection level. A constructor emission that drifts into a
14081 /// missing-NAME shape (empty spec_args yields `(head)`, which the
14082 /// call-form projection recovers but the named-form gate rejects
14083 /// with `NamedFormMissingName`) or a non-symbol-NAME shape
14084 /// (`Sexp::keyword(name)` for the NAME position, which the gate
14085 /// rejects with `NamedFormNonSymbolName`) becomes structurally
14086 /// impossible — the `impl Into<String>` NAME bound admits string
14087 /// payloads only, and the [`Self::symbol`] wrap routes to the
14088 /// symbol atom variant `as_symbol_or_string` accepts.
14089 ///
14090 /// Pre-lift the `Sexp::call(head, std::iter::once(Sexp::symbol(
14091 /// name)).chain(spec_args))` composition (or equivalently the
14092 /// `Sexp::List(vec![Sexp::symbol(head), Sexp::symbol(name),
14093 /// spec_args...])` welded quadruple) appeared inline at every
14094 /// consumer that builds a `(defX NAME …)`-shaped [`Sexp`] value
14095 /// — well past the ≥2 PRIME-DIRECTIVE trigger once the named
14096 /// call-form shape is named. Post-lift consumers that have a head
14097 /// string + a NAME string + an owned or iterable sequence of spec
14098 /// args bind to ONE typed-algebra method on the outer [`Sexp`]
14099 /// algebra with the two `impl Into<String>` bounds absorbing `&str`
14100 /// / `String` / `&String` on both string positions and the
14101 /// `impl IntoIterator<Item = Sexp>` bound on the spec args
14102 /// absorbing `Vec<Sexp>` / `[Sexp; N]` / `.map(...)` chains without
14103 /// a per-site `.collect::<Vec<Sexp>>()` coercion.
14104 ///
14105 /// Frontier inspiration: Racket's `syntax-parse`
14106 /// `(~datum keyword) name:id spec ...` pattern binds the NAME slot
14107 /// through the `name:id` capture binder and consumers reference it
14108 /// downstream; the constructor peer on the same surface is
14109 /// `syntax-e` composed with `datum->syntax` wrapping a
14110 /// `(list #'keyword name-id spec-list ...)` triple. `Sexp::
14111 /// named_call` is the unstructured-Rust peer — a section-for-
14112 /// retraction constructor on the outer algebra that mirrors the
14113 /// `~datum keyword name:id spec ...` pattern's NAME capture on the
14114 /// construct side. Tree-sitter's `query`-matched named captures
14115 /// have the same shape on the tree side: the query pattern
14116 /// binds a NAME capture, the constructor peer (`ts_node_new`
14117 /// composed with `ts_node_field_set`) embeds a fresh NAME child at
14118 /// the corresponding field slot. The typed structural rejection
14119 /// chain the substrate's named-form gate emits
14120 /// ([`crate::error::LispError::NamedFormMissingName`],
14121 /// [`crate::error::LispError::NamedFormNonSymbolName`]) is
14122 /// preserved by construction — the constructor cannot emit a
14123 /// value the gate rejects, symmetric with the `~datum` /
14124 /// `name:id` reader-side rejection that fires BEFORE any
14125 /// downstream binding sees the drifted shape.
14126 ///
14127 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
14128 /// (head string, NAME string, spec args sequence, [`Self::call`]
14129 /// call-form constructor) quadruple binds at ONE typed-algebra
14130 /// method on the outer [`Sexp`] algebra, closing the named-call-
14131 /// form (construct, project) algebra dual pair with
14132 /// [`Self::as_named_call_to`] / [`Self::as_named_call_to_any`] on
14133 /// the projection side and [`crate::compile::split_name_slot`] on
14134 /// the gate side. THEORY.md §II.1 invariant 2 — free middle;
14135 /// every consumer that has a head + NAME + spec args and wants to
14136 /// build a named-call-shaped [`Sexp`] routes through the SAME
14137 /// typed method, so a regression that drifts one consumer's
14138 /// construction from the others (e.g. a copy-edit that emits
14139 /// `Sexp::keyword(name)` for the NAME position, which the
14140 /// named-form gate would reject with `NamedFormNonSymbolName`)
14141 /// cannot reach the substrate's runtime. THEORY.md §V.1 —
14142 /// knowable platform; the named-call-form typed-construct becomes
14143 /// a TYPE projection on the substrate's outer [`Sexp`] algebra
14144 /// sitting next to the typed-project family [`Self::
14145 /// as_named_call_to`] / [`Self::as_named_call_to_any`] +
14146 /// [`crate::ast::iter_named_calls_to`] /
14147 /// [`crate::ast::iter_named_calls_to_any`] rather than a per-site
14148 /// inline composition. THEORY.md §VI.1 — generation over
14149 /// composition; the named-call-form pair emerges from ONE typed-
14150 /// algebra composition through [`Self::call`] composed with
14151 /// [`Self::symbol`] rather than from per-consumer per-callsite
14152 /// literals; a future named-form shape extension (e.g. a
14153 /// dotted-NAME form, or a typed-NAME form where the NAME slot
14154 /// carries a compile-time-decoded typed witness) lands as ONE
14155 /// peer constructor on this algebra alongside the call-form,
14156 /// residual, quote-family, and atomic-payload construct
14157 /// families.
14158 #[must_use]
14159 pub fn named_call<H, N, I>(head: H, name: N, spec_args: I) -> Self
14160 where
14161 H: Into<String>,
14162 N: Into<String>,
14163 I: IntoIterator<Item = Sexp>,
14164 {
14165 Self::call(head, std::iter::once(Self::symbol(name)).chain(spec_args))
14166 }
14167
14168 /// Decompose a call form into its argument tail IFF the head matches the
14169 /// supplied `keyword` — `Some(args)` iff this is a non-empty list whose
14170 /// first element is a symbol equal to `keyword`, where `args` is the
14171 /// remaining elements (`&self[1..]`, possibly empty). `None` for every
14172 /// shape `as_call` rejects AND for every call whose head is present but
14173 /// differs from `keyword`.
14174 ///
14175 /// This is the *keyword-typed call decomposition* — the natural
14176 /// extension of [`Sexp::as_call`] for the "is this a call to ONE
14177 /// specific operator?" question every typed-domain dispatch site asks
14178 /// after macroexpansion. [`compile_typed`](crate::compile::compile_typed)
14179 /// and [`compile_named_from_forms`](crate::compile::compile_named_from_forms)
14180 /// both opened the same two-step chain inline —
14181 /// `if let Some((head, args)) = form.as_call() { if head == T::KEYWORD { … } }`
14182 /// — at every form they walked; the chain IS this projection. Naming
14183 /// it lifts "is this form a call to T?" from a two-step inline pattern
14184 /// to ONE structural query on the `Sexp` algebra. A regression that
14185 /// drifts one consumer's comparison from `==` to `!= `, or that
14186 /// compares against a different label than `T::KEYWORD` (e.g.
14187 /// substring-grepping the rendered head), becomes structurally
14188 /// impossible: there is exactly one implementation both dispatchers
14189 /// route through.
14190 ///
14191 /// Soft face, like the rest of the `as_*` family: it answers "is this
14192 /// a call to `keyword`, and what are its arguments?" and yields `None`
14193 /// for everything that isn't (skip / fall through), with no
14194 /// diagnostic. The strict gate sibling is
14195 /// `TataraDomain::compile_from_sexp`, which distinguishes the
14196 /// not-a-list / empty-list / non-symbol-head / wrong-keyword
14197 /// sub-modes to reject loudly. The two are the dispatch
14198 /// (`as_call_to`) and the gate (`compile_from_sexp`) faces of the
14199 /// same projection; keeping both lets a site choose "skip silently"
14200 /// or "reject loudly" without re-deriving the head.
14201 ///
14202 /// Structural identity binding it to its siblings:
14203 /// * `as_call_to(keyword) == as_call().and_then(|(h, args)| (h == keyword).then_some(args))`
14204 /// * `as_call_to(keyword).is_some() == (head_symbol() == Some(keyword))`
14205 ///
14206 /// The returned `&[Sexp]` borrows from the list's tail verbatim — no
14207 /// copy, no allocation, same lifetime as [`Sexp::as_call`]'s tail.
14208 ///
14209 /// Slice-side sibling: [`iter_calls_to`] lifts this per-form projection
14210 /// onto a `&[Sexp]`, yielding the args slices of every matching form in
14211 /// source order — the substrate's typed-keyword filter over a batch of
14212 /// forms, structurally bound to this per-form projection via the
14213 /// closed-form composition
14214 /// `iter_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_call_to(k))`.
14215 pub fn as_call_to(&self, keyword: &str) -> Option<&[Sexp]> {
14216 let (head, args) = self.as_call()?;
14217 (head == keyword).then_some(args)
14218 }
14219
14220 /// Decompose a call form whose head decodes through a caller-supplied
14221 /// classifier — `Some((decoded, args))` iff this is a non-empty list
14222 /// whose first element is a symbol AND `decode(head)` returns
14223 /// `Some(decoded)`, where `args` is the remaining elements
14224 /// (`&self[1..]`, possibly empty). `None` for every shape
14225 /// [`Sexp::as_call`] rejects AND for every call whose head is present
14226 /// but `decode` rejects.
14227 ///
14228 /// This is the *typed-decoded call decomposition* — the closure-typed
14229 /// extension of [`Sexp::as_call_to`] for the "is this a call whose head
14230 /// belongs to a CLOSED SET (or a LIVE REGISTRY) that decodes to a typed
14231 /// witness?" question. Where [`Sexp::as_call_to`] filters by ONE
14232 /// constant keyword, `as_call_to_any` filters AND TYPES by a caller-
14233 /// supplied projection — every dispatch site that asks "is this form
14234 /// an invocation of any of N operators, decoded as a typed enum or
14235 /// resolved against a runtime table?" binds to ONE structural query
14236 /// on the `Sexp` algebra. Two consumers route through it:
14237 ///
14238 /// * The macro-expander's `macro_def_from` — closed-set classifier:
14239 /// `as_call_to_any(MacroDefHead::from_keyword)` decides which of
14240 /// `{defmacro, defpoint-template, defcheck}` a top-level form
14241 /// invokes, decoded to the typed `MacroDefHead` enum. Pre-lift the
14242 /// site opened the same three-step chain inline — `let Some(list)
14243 /// = form.as_list()…; let Some(head) = form.head_symbol()…; let
14244 /// Some(decoded) = MacroDefHead::from_keyword(head)…`.
14245 /// * The macro-expander's `Expander::expand` — live-registry
14246 /// classifier: `as_call_to_any(|h| self.macros.get(h))` decides
14247 /// which of the registered macros (a `HashMap<String, MacroDef>`
14248 /// populated by `expand_program`'s `defmacro` recognition) a form
14249 /// invokes, decoded to `&MacroDef`. Pre-lift the site opened the
14250 /// same `as_list() + as_call() + self.macros.get(head)` chain
14251 /// inline — `as_list()` for the children-walk fallthrough,
14252 /// `as_call()` for the (head, args) pair (which itself re-derives
14253 /// `as_list()` internally), and `self.macros.get(head)` for the
14254 /// registry lookup.
14255 ///
14256 /// Naming the projection lifts "is this form a call to any of N
14257 /// operators, decoded to T?" from the three-step inline pattern to
14258 /// ONE structural query — closed-set enum classifier OR live-registry
14259 /// HashMap classifier, the family primitive is uniform under both.
14260 ///
14261 /// Soft face, like the rest of the `as_*` family: it answers "is this
14262 /// a call whose head decodes through `F`, and what are its arguments?"
14263 /// and yields `None` for everything that isn't (skip / fall through),
14264 /// with no diagnostic. The strict gate sibling stays
14265 /// `TataraDomain::compile_from_sexp` — that distinguishes the
14266 /// not-a-list / empty-list / non-symbol-head / wrong-keyword sub-modes
14267 /// to reject loudly for a single-keyword consumer. The two are the
14268 /// closed-set-decoded dispatch (`as_call_to_any`) and the
14269 /// single-keyword gate (`compile_from_sexp`) faces of the typed-domain
14270 /// recognition problem; keeping both lets a site choose "skip
14271 /// silently if the head isn't ours" or "reject loudly if the head
14272 /// isn't the exact keyword" without re-deriving the head.
14273 ///
14274 /// Structural identity binding it to its siblings:
14275 /// * `as_call_to_any(decode) == as_call().and_then(|(h, args)| decode(h).map(|d| (d, args)))`
14276 /// * `as_call_to(k) == as_call_to_any(|h| (h == k).then_some(())).map(|(_, a)| a)` (modulo the discarded `()`)
14277 /// * `as_call_to_any(decode).is_some() == as_call().map_or(false, |(h, _)| decode(h).is_some())`
14278 ///
14279 /// The returned `&[Sexp]` borrows from the list's tail verbatim — no
14280 /// copy, no allocation, same lifetime as [`Sexp::as_call`]'s tail.
14281 /// `T` is owned because `decode` is `FnOnce(&str) -> Option<T>` and a
14282 /// `&'_ str` borrow into the head symbol would not outlive the helper
14283 /// boundary; consumers projecting to a typed `Copy` enum (e.g.
14284 /// `MacroDefHead`) get the value directly, consumers projecting to a
14285 /// borrowed `&'static str` (a closed-set head) project to
14286 /// `&'static str` and inherit the static lifetime through the
14287 /// classifier.
14288 ///
14289 /// Slice-side sibling: [`iter_calls_to_any`] lifts this per-form
14290 /// projection onto a `&[Sexp]`, yielding the `(decoded, &[Sexp])`
14291 /// pair of every matching form in source order — the substrate's
14292 /// typed-decoded filter over a batch of forms, structurally bound
14293 /// to this per-form projection via the closed-form composition
14294 /// `iter_calls_to_any(forms, decode) == forms.iter().filter_map(|f|
14295 /// f.as_call_to_any(&mut decode))`. The slice-side primitive
14296 /// promotes the closure constraint from [`FnOnce`] (per-form, one
14297 /// call per invocation) to [`FnMut`] (slice-side, one call per
14298 /// element) so a decoder that captures mutable state (a counter, a
14299 /// registry cache) maintains state across the batch walk.
14300 pub fn as_call_to_any<F, T>(&self, decode: F) -> Option<(T, &[Sexp])>
14301 where
14302 F: FnOnce(&str) -> Option<T>,
14303 {
14304 let (head, args) = self.as_call()?;
14305 decode(head).map(|d| (d, args))
14306 }
14307
14308 /// Decompose a named call form (a `(<keyword> NAME :k v …)` shape) whose
14309 /// head decodes through a caller-supplied classifier — `Some(Ok((decoded,
14310 /// name, spec_args)))` iff this is a non-empty list whose first element
14311 /// is a symbol AND `decode(head)` returns `Some((decoded, kw))` AND the
14312 /// remaining elements split cleanly into a NAME slot (symbol or string
14313 /// at position 1) and a spec args tail (position 2..), `Some(Err(…))` iff
14314 /// the head decodes but the NAME slot is missing
14315 /// ([`LispError::NamedFormMissingName`]) or non-symbol-or-string
14316 /// ([`LispError::NamedFormNonSymbolName`]), `None` for every shape
14317 /// [`Sexp::as_call_to_any`] rejects AND for every call whose head is
14318 /// present but `decode` returns `None` for.
14319 ///
14320 /// This is the *per-form named-classifier projection* — the per-form
14321 /// peer of [`iter_named_calls_to_any`] on the slice algebra and of
14322 /// [`crate::macro_expand::Expander::expand_and_collect_named_calls_to_any`]
14323 /// on the expander surface. Closes the (per-form × classifier × named)
14324 /// corner of the soft-dispatch cube the substrate's per-form algebra
14325 /// (`as_call_to{,_any}`) and slice algebra (`iter_calls_to{,_any}`,
14326 /// `iter_named_calls_to{,_any}`) collectively shape — pre-lift the cube
14327 /// at the per-form × named corner was "(composed inline at each named
14328 /// consumer)" (the documented gap the cube table inside
14329 /// [`iter_named_calls_to_any`] called out), post-lift the per-form ×
14330 /// named row binds to ONE primitive every per-form named consumer
14331 /// composes through:
14332 ///
14333 /// | | bare-kwargs | named NAME-then-kwargs |
14334 /// |----------------|------------------------------|--------------------------------------|
14335 /// | per-form | [`Sexp::as_call_to_any`] | `as_named_call_to_any` (this) |
14336 /// | slice | [`iter_calls_to_any`] | [`iter_named_calls_to_any`] |
14337 /// | expander | `expand_and_collect_calls_to_any` | `expand_and_collect_named_calls_to_any` |
14338 ///
14339 /// The slice-side [`iter_named_calls_to_any`] now routes through THIS
14340 /// per-form primitive via the SAME `forms.iter().filter_map(_)`
14341 /// skeleton [`iter_calls_to_any`] uses to route through
14342 /// [`Sexp::as_call_to_any`], so a regression that drifts ONE row's
14343 /// instrumentation, span-aware borrow walker, or fused-iterator
14344 /// invariant from the bare row to the named row (or vice versa) is
14345 /// structurally impossible.
14346 ///
14347 /// Composes [`Sexp::as_call_to_any`] with
14348 /// [`crate::compile::split_name_slot`]: the classifier filter precedes
14349 /// the named gate, mirroring how `split_name_slot` is composed AFTER
14350 /// the classifier-decoded args tail is already in hand inside
14351 /// [`iter_named_calls_to_any`]. Decoder signature `FnOnce(&str) ->
14352 /// Option<(T, &'static str)>` pairs the typed witness `T` with the
14353 /// canonical static keyword threaded through the
14354 /// `NamedFormMissingName.keyword` / `NamedFormNonSymbolName.keyword`
14355 /// slots of the named-form gate — the `&'static` constraint pins the
14356 /// same compile-time discipline [`crate::compile::split_name_slot`]'s
14357 /// `keyword: &'static str` parameter pins at the slice-side boundary,
14358 /// AND that the slice-side decoder signature pins on the slice
14359 /// algebra.
14360 ///
14361 /// Three-arm result shape — `Option<Result<…>>` — preserves both the
14362 /// classifier filter face (`None` for "not our head, skip silently",
14363 /// matching the per-form soft-projection posture of every other `as_*`
14364 /// method on `Sexp`) AND the named gate face (`Err` for "matched head
14365 /// but malformed NAME", surfacing the typed structural-rejection
14366 /// variants `LispError::NamedFormMissingName` /
14367 /// `LispError::NamedFormNonSymbolName` the slice-side and expander-
14368 /// surface consumers already short-circuit on). A consumer that wants
14369 /// "fold over every per-form result, short-circuiting on the first
14370 /// malformed NAME" composes `.transpose()` (yielding
14371 /// `Result<Option<…>>`) and `?`-routes the outer `Result`; a consumer
14372 /// that wants "skip every non-matching form AND every malformed
14373 /// matched form" composes `.and_then(|res| res.ok())` (yielding
14374 /// `Option<(T, &str, &[Sexp])>`); a consumer that wants the raw
14375 /// three-arm shape pattern-matches directly.
14376 ///
14377 /// Two plausible future consumer shapes the per-form named-classifier
14378 /// projection admits with no boilerplate:
14379 /// * **LSP hover tooltip** — an authoring tool that surfaces a
14380 /// tooltip on the symbol under the cursor wants to ask "is THIS
14381 /// form (the one I just resolved to under the cursor) a named
14382 /// call to any registered domain, decoded to a typed kind, with
14383 /// the borrowed NAME slot extracted for the tooltip body?". Pre-
14384 /// lift the tool would re-derive `form.as_call_to_any(decode)
14385 /// .and_then(|((kind, kw), args)| split_name_slot(args,
14386 /// kw).ok().map(|(name, rest)| (kind, name, rest)))` inline;
14387 /// post-lift the tool binds to ONE primitive.
14388 /// * **REPL single-form dispatcher** — a `:dispatch <classifier>
14389 /// <form>` command that walks a single form through the
14390 /// registry classifier, reporting the typed kind AND the NAME
14391 /// slot (for "you said `(defmonitor my-monitor …)`, I see
14392 /// `Monitor` named `my-monitor` with 3 spec args"). Pre-lift
14393 /// the REPL would re-derive the same inline composition; post-
14394 /// lift the REPL binds to ONE primitive, sibling shape to how
14395 /// [`Sexp::as_call_to_any`] backs the slice-side dispatcher
14396 /// [`iter_calls_to_any`].
14397 ///
14398 /// Structural identity binding it to its siblings:
14399 /// * `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)))`
14400 /// * `as_named_call_to(k) == as_named_call_to_any(|h| (h == k).then_some(((), k))).map(|res| res.map(|(_, name, rest)| (name, rest)))`
14401 /// * `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)
14402 ///
14403 /// The returned `&str` NAME slot and `&[Sexp]` spec args tail borrow
14404 /// from `&self` verbatim — no copy, no allocation, same lifetime as
14405 /// [`Sexp::as_call_to_any`]'s tail AND [`crate::compile::split_name_slot`]'s
14406 /// pair. `T` is owned because the underlying [`Sexp::as_call_to_any`]
14407 /// classifier is `FnOnce(&str) -> Option<(T, &'static str)>` and `T`
14408 /// must outlive the helper boundary; consumers projecting to a typed
14409 /// `Copy` enum (e.g. a closed-set `Kind`) get the value directly,
14410 /// consumers projecting to a borrowed `&'static str` (a closed-set
14411 /// head sourced from `ClosedSet::ALL.label()`) project to `&'static
14412 /// str` and inherit the static lifetime through the classifier.
14413 ///
14414 /// Soft face on the classifier filter, strict face on the named gate:
14415 /// "is this a named call whose head decodes through `F`, and what
14416 /// are its NAME and spec args?" yielding `None` for "not our head"
14417 /// (skip / fall through, no diagnostic) AND `Some(Err(…))` for "our
14418 /// head but malformed NAME" (reject loudly, structural variant). The
14419 /// soft-classifier-then-strict-named composition matches the
14420 /// slice-side `iter_named_calls_to_any` yielded `Result` shape (with
14421 /// non-matching forms skipped by the iterator filter) and the
14422 /// expander-surface `expand_and_collect_named_calls_to_any` collect
14423 /// shape (with `Result::collect` short-circuiting on the first
14424 /// malformed NAME) — every layer of the cube preserves both faces.
14425 ///
14426 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
14427 /// per-form × classifier × named cell of the soft-dispatch cube is a
14428 /// CONSEQUENCE of [`Sexp::as_call_to_any`] + [`crate::compile::split_name_slot`],
14429 /// named on the substrate's `Sexp` algebra rather than re-derived
14430 /// inline at every per-form named consumer site. THEORY.md §V.1 —
14431 /// knowable platform; the per-form named-classifier projection
14432 /// becomes a NAMED primitive on the `Sexp` algebra, discoverable by
14433 /// any future authoring tool (LSP, REPL, `tatara-check`) that holds
14434 /// a single form in isolation. THEORY.md §II.1 invariant 2 — free
14435 /// middle; the slice-side sibling [`iter_named_calls_to_any`] now
14436 /// routes through this per-form primitive via the same
14437 /// `forms.iter().filter_map(_)` skeleton the bare-kwargs row uses
14438 /// to route through [`Sexp::as_call_to_any`], so the bare and named
14439 /// rows share ONE filter-and-fuse implementation skeleton on the
14440 /// `Sexp`/`&[Sexp]` algebras.
14441 ///
14442 /// Frontier inspiration: MLIR's `mlir::dyn_cast<NamedOpInterface>(op)`
14443 /// — the typed downcast from a polymorphic IR node onto a NAMED-op
14444 /// interface that exposes both the typed witness AND the
14445 /// symbol-name accessor is the MLIR idiom; `as_named_call_to_any` is
14446 /// the unstructured-Rust peer on the substrate's `Sexp` algebra,
14447 /// with `Option<Result<(T, &str, &[Sexp])>>` standing in for MLIR's
14448 /// typed-downcast-then-name-accessor pair, and the `Result` face
14449 /// carrying the typed structural rejection MLIR encodes via verifier
14450 /// diagnostics. Racket's `syntax-parse` `~or* ((~datum defX) name:id
14451 /// arg ...) ((~datum defY) name:id arg ...)` on a single syntax
14452 /// object — typed named-form decomposition with `name:id` capture
14453 /// binding is the Racket idiom; this method is the per-form
14454 /// Rust-typed peer with the typed structural rejection
14455 /// (`NamedFormMissingName` / `NamedFormNonSymbolName`) preserved
14456 /// across the boundary.
14457 pub fn as_named_call_to_any<F, T>(
14458 &self,
14459 decode: F,
14460 ) -> Option<crate::error::Result<(T, &str, &[Sexp])>>
14461 where
14462 F: FnOnce(&str) -> Option<(T, &'static str)>,
14463 {
14464 self.as_call_to_any(decode).map(|((decoded, kw), args)| {
14465 let (name, spec_args) = crate::compile::split_name_slot(args, kw)?;
14466 Ok((decoded, name, spec_args))
14467 })
14468 }
14469
14470 /// Decompose a named call form whose head matches a constant
14471 /// `keyword` — `Some(Ok((name, spec_args)))` iff this is a non-empty
14472 /// list whose first element is the symbol `keyword` AND the remaining
14473 /// elements split cleanly into a NAME slot and a spec args tail,
14474 /// `Some(Err(…))` iff the head matches but the NAME slot is missing
14475 /// or non-symbol-or-string, `None` for every shape
14476 /// [`Sexp::as_call_to`] rejects.
14477 ///
14478 /// Constant-keyword sibling of [`Sexp::as_named_call_to_any`] and
14479 /// per-form sibling of [`iter_named_calls_to`] on the slice algebra.
14480 /// Routes through the typed-decoded sibling with a constant-classifier
14481 /// decoder (`|h| (h == keyword).then_some(((), keyword))`) — the same
14482 /// constant-classifier composition [`Sexp::as_call_to`] uses to route
14483 /// through [`Sexp::as_call_to_any`] on the bare-kwargs axis, and that
14484 /// [`iter_named_calls_to`] uses to route through
14485 /// [`iter_named_calls_to_any`] on the slice algebra. The discarded
14486 /// `()` typed witness (`then_some(((), keyword))`) is consumed by the
14487 /// wrapper projection so the consumer's per-form mapper sees only the
14488 /// `(name, spec_args)` borrowed pair, matching the bare projection
14489 /// signature on the named axis.
14490 ///
14491 /// `keyword: &'static str` threads verbatim through the
14492 /// `NamedFormMissingName.keyword` / `NamedFormNonSymbolName.keyword`
14493 /// slots of the named-form gate — same `&'static` discipline
14494 /// [`crate::compile::split_name_slot`] pins at its boundary, AND that
14495 /// [`iter_named_calls_to`] pins on the slice algebra. Consumers that
14496 /// want a runtime keyword whose lifetime is shorter use
14497 /// [`Sexp::as_named_call_to_any`] directly with a constant-classifier
14498 /// decoder that converts post-resolution.
14499 ///
14500 /// Structural identity binding it to its siblings:
14501 /// * `as_named_call_to(k) == as_named_call_to_any(|h| (h == k).then_some(((), k))).map(|res| res.map(|(_, name, rest)| (name, rest)))`
14502 /// * `as_named_call_to(k).is_none() == as_call_to(k).is_none()`
14503 /// * `iter_named_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_named_call_to(k))`
14504 ///
14505 /// Theory anchor: see [`Sexp::as_named_call_to_any`] — the constant-
14506 /// keyword sibling shares the same lift posture, threading the
14507 /// `&'static str` keyword constraint through the named-form gate's
14508 /// canonical-keyword slot rather than admitting an arbitrary runtime
14509 /// keyword.
14510 pub fn as_named_call_to(
14511 &self,
14512 keyword: &'static str,
14513 ) -> Option<crate::error::Result<(&str, &[Sexp])>> {
14514 self.as_named_call_to_any(move |h| (h == keyword).then_some(((), keyword)))
14515 .map(|res| res.map(|(_, name, rest)| (name, rest)))
14516 }
14517
14518 /// Decompose an unquote-family form into its typed marker and inner
14519 /// expression — `Some((UnquoteForm::Unquote, inner))` iff this is `,x`
14520 /// (a [`Sexp::Unquote`] wrapper), `Some((UnquoteForm::Splice, inner))`
14521 /// iff this is `,@x` (a [`Sexp::UnquoteSplice`] wrapper), `None` for
14522 /// every other shape (Quote, Quasiquote, Nil, Atom, List).
14523 ///
14524 /// This is the *unquote-family projection* — the typed-marker peer of
14525 /// [`Sexp::as_call`] for the macro-template substitution surface. Where
14526 /// [`Sexp::as_call`] decomposes `(op args …)` into a `(head, args)`
14527 /// pair, `as_unquote` decomposes `,x` / `,@x` into a `(form, inner)`
14528 /// pair where `form: UnquoteForm` is the closed-set typed marker
14529 /// (`Unquote` for `,`, `Splice` for `,@`) and `inner: &Sexp` is the
14530 /// borrowed body. The pairing of `Sexp::Unquote ↔ UnquoteForm::Unquote`
14531 /// and `Sexp::UnquoteSplice ↔ UnquoteForm::Splice` is the structural
14532 /// invariant the macro-expander's substitution path keys every
14533 /// rejection on — naming the projection lifts the pair from
14534 /// per-callsite discipline (two `Sexp::Unquote(inner)` arms paired
14535 /// with two `UnquoteForm::Unquote` literals at distinct sites, two
14536 /// `Sexp::UnquoteSplice(inner)` arms paired with two
14537 /// `UnquoteForm::Splice` literals at distinct sites) into ONE typed
14538 /// projection both expansion strategies route through.
14539 ///
14540 /// Three consumers in [`macro_expand`](crate::macro_expand) route
14541 /// through this primitive:
14542 /// * `compile_node` (bytecode-template compile path) — `,x` becomes
14543 /// `TemplateOp::Subst(idx)`, `,@x` becomes `TemplateOp::Splice(idx)`;
14544 /// both arms share the gate-1+gate-2 composition
14545 /// `resolve_unquote_in_params(inner, params, form)?` keyed on the
14546 /// typed `form` projection.
14547 /// * `substitute` top-level (substitute fallback path) — `,x` resolves
14548 /// to its bound value, `,@x` rejects with
14549 /// `LispError::SpliceOutsideList` (a splice form with no containing
14550 /// list to flatten into).
14551 /// * `substitute` list-inner (substitute fallback path's per-item
14552 /// walk) — `,@x` items splice their bound list/nil/scalar value
14553 /// into the assembled list builder via
14554 /// [`crate::macro_expand::splice_value_into`]; non-splice items
14555 /// recurse into `substitute`.
14556 ///
14557 /// Pre-lift each site opened the same per-variant match arms —
14558 /// `Sexp::Unquote(inner) => … UnquoteForm::Unquote …` and
14559 /// `Sexp::UnquoteSplice(inner) => … UnquoteForm::Splice …` —
14560 /// independently. The (Sexp variant, UnquoteForm variant) pairing was
14561 /// load-bearing across distinct sites yet only enforced by callsite
14562 /// discipline. Post-lift the pair binds at ONE projection function the
14563 /// type system threads through `(UnquoteForm, &Sexp)`: a regression
14564 /// that drifts ONE site's pairing (e.g. a future emitter that matches
14565 /// `Sexp::Unquote(_)` but threads `UnquoteForm::Splice` into
14566 /// `unquote_target_symbol` — type-checks but renders a misleading
14567 /// diagnostic) becomes structurally impossible.
14568 ///
14569 /// Soft face, like the rest of the `as_*` family on `Sexp`: it answers
14570 /// "is this form an unquote-family marker, and what does it wrap?" and
14571 /// yields `None` for everything that isn't (skip / fall through), with
14572 /// no diagnostic. The strict siblings —
14573 /// [`crate::macro_expand::splice_value_into`] for the bound-list
14574 /// coercion, `non_symbol_unquote_target` /
14575 /// `splice_outside_list` for the per-failure-mode rejections — keep
14576 /// their loud-reject posture; this projection is the dispatch face the
14577 /// soft pre-rejection walk binds to.
14578 ///
14579 /// Structural identity binding it to the unquote-family variants:
14580 /// * `as_unquote() == Some((UnquoteForm::Unquote, inner))` iff `self == Sexp::Unquote(inner)`
14581 /// * `as_unquote() == Some((UnquoteForm::Splice, inner))` iff `self == Sexp::UnquoteSplice(inner)`
14582 /// * `as_unquote().is_some() == matches!(self, Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
14583 ///
14584 /// The returned `&Sexp` borrows the inner box's body verbatim — no
14585 /// clone, no allocation — same lifetime as `&self`. The closed-set
14586 /// guarantee on [`UnquoteForm`] (exactly `Unquote ⊎ Splice`) is
14587 /// threaded through this projection's return tuple, so consumers that
14588 /// pattern-match on `form: UnquoteForm` get rustc-enforced
14589 /// exhaustiveness — a future `Sexp` variant must extend `UnquoteForm`
14590 /// AND this match arm together (or stay outside the unquote family
14591 /// and project to `None`), eliminating the silent two-site
14592 /// extension-drift this lift was already designed to forbid.
14593 ///
14594 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
14595 /// `(Sexp::Unquote, UnquoteForm::Unquote)` and
14596 /// `(Sexp::UnquoteSplice, UnquoteForm::Splice)` pairings appear ≥3
14597 /// times across `compile_node` (2 arms) + `substitute` (top-level +
14598 /// list-inner) — past the PRIME-DIRECTIVE trigger once the structural
14599 /// shape is named. THEORY.md §V.1 — knowable platform; the
14600 /// unquote-family projection becomes a NAMED primitive on the
14601 /// substrate's `Sexp` algebra rather than per-site `Sexp::Unquote(_)
14602 /// | Sexp::UnquoteSplice(_)` inline matches paired with per-site
14603 /// `UnquoteForm::Unquote` / `UnquoteForm::Splice` literals.
14604 /// THEORY.md §II.1 invariant 1 — typed entry; the macro-template
14605 /// substitution surface's typed-marker projection IS the rust-level
14606 /// typed-entry gate's structural component, lifted from per-site
14607 /// duplication onto ONE rust method the substrate's diagnostic
14608 /// promotions hang off of. THEORY.md §II.1 invariant 2 — free middle;
14609 /// both expansion strategies (bytecode `compile_node` and substitute
14610 /// fallback `substitute`) route through the SAME projection, so a
14611 /// regression that drifts ONE strategy's (Sexp variant, UnquoteForm
14612 /// variant) pairing from the other cannot reach the substrate's
14613 /// runtime — the type system binds both strategies to the
14614 /// projection's single emission shape.
14615 ///
14616 /// Frontier inspiration: Racket's `syntax-parse` `~or* (~unquote stx)
14617 /// (~unquote-splice stx)` pattern — every macro-template pattern over
14618 /// `,id` / `,@id` binds to ONE typed decomposition that surfaces the
14619 /// marker identity alongside the inner expression; the substrate's
14620 /// `as_unquote` is the Rust-typed peer of that pattern, lifted onto
14621 /// the `Sexp` algebra with [`UnquoteForm`] standing in for Racket's
14622 /// pattern-class identity. MLIR's typed-IR projection
14623 /// `mlir::dyn_cast<UnquoteFamilyOp>(op)` — the typed downcast from a
14624 /// polymorphic IR node onto a closed-set op family is the MLIR idiom;
14625 /// `as_unquote` is the unstructured-projection peer on the substrate's
14626 /// `Sexp` algebra, with `Option<(UnquoteForm, &Sexp)>` standing in for
14627 /// MLIR's typed downcast result.
14628 pub fn as_unquote(&self) -> Option<(UnquoteForm, &Sexp)> {
14629 let (qf, inner) = self.as_quote_form()?;
14630 qf.as_unquote_form().map(|uf| (uf, inner))
14631 }
14632
14633 /// Soft projection onto the closed-set [`UnquoteForm`] template-
14634 /// substitution carving marker — the 2-of-12 carving of the
14635 /// [`SexpShape`](crate::error::SexpShape) algebra covering the two
14636 /// homoiconic template-substitution wrappers ([`Self::Unquote`] and
14637 /// [`Self::UnquoteSplice`]), which is itself a 2-of-4 subset of the
14638 /// quote-family carving ([`QuoteForm`]). Returns
14639 /// `Some(UnquoteForm::Unquote)` iff this is `,x` (a [`Self::Unquote`]
14640 /// wrapper), `Some(UnquoteForm::Splice)` iff this is `,@x` (a
14641 /// [`Self::UnquoteSplice`] wrapper), `None` for every other outer
14642 /// shape ([`Self::Nil`], every [`Self::Atom`] variant, [`Self::List`],
14643 /// and the two non-substitution quote-family wrappers [`Self::Quote`]
14644 /// and [`Self::Quasiquote`]).
14645 ///
14646 /// Direct value-level peer of the shape-level projection
14647 /// [`SexpShape::as_unquote_form`](crate::error::SexpShape::as_unquote_form)
14648 /// — the pair `(Sexp::as_unquote_form, SexpShape::as_unquote_form)`
14649 /// binds the (Sexp value, UnquoteForm carving marker) pairing at ONE
14650 /// typed method on each algebra, closing the unquote-subset cell of
14651 /// the (Sexp value → carving marker) matrix. Marker-only sibling of
14652 /// [`Self::as_unquote`] (which returns
14653 /// `Option<(UnquoteForm, &Sexp)>` — marker + wrapped inner) and
14654 /// direct 2-of-4 subset peer of [`Self::as_quote_form`] (which
14655 /// covers the 4-of-12 quote-family carving with `Option<(QuoteForm,
14656 /// &Sexp)>`). Post-lift the substrate's value-level marker-only
14657 /// carving-marker matrix closes ONE more cell: the atomic axis via
14658 /// [`Self::as_atom_kind`] (6-of-12), the residual axis via
14659 /// [`Self::as_structural_kind`] (2-of-12), the quote-family axis via
14660 /// `Self::as_quote_form().map(|(qf, _)| qf)` (4-of-12, marker + inner
14661 /// available via the pre-existing method), and now the unquote-
14662 /// subset axis via `Self::as_unquote_form` (2-of-12, marker only) —
14663 /// symmetric with the shape-level marker-only projection family on
14664 /// [`SexpShape`](crate::error::SexpShape).
14665 ///
14666 /// Composition laws (three-way agreement — bindings): for every
14667 /// `s: &Sexp`,
14668 /// `s.as_unquote_form() == s.as_unquote().map(|(uf, _)| uf) ==
14669 /// s.shape().as_unquote_form() ==
14670 /// s.as_quote_form().and_then(|(qf, _)| qf.as_unquote_form())`.
14671 /// Pre-lift the unquote-subset carving marker at the value level
14672 /// was reachable only via one of these three-step compositions —
14673 /// either through the parent [`Self::as_unquote`] projection
14674 /// (discarding the inner), through the shape algebra
14675 /// (`shape().as_unquote_form()`), or through the parent quote-family
14676 /// projection composed with the 2-of-4 subset gate
14677 /// [`QuoteForm::as_unquote_form`]. Post-lift the projection lands at
14678 /// ONE typed method on the value algebra, and all three compositions
14679 /// are pinned as agreement laws (see
14680 /// `sexp_as_unquote_form_agrees_with_as_unquote_map_marker_for_every_variant`,
14681 /// `sexp_as_unquote_form_agrees_with_shape_as_unquote_form_for_every_variant`,
14682 /// and
14683 /// `sexp_as_unquote_form_agrees_with_as_quote_form_and_quote_form_as_unquote_form_for_every_variant`
14684 /// in this module). A regression that drifts any of the four
14685 /// projections from the others surfaces immediately.
14686 ///
14687 /// Symmetric with [`Self::as_atom_kind`] and [`Self::as_structural_kind`]
14688 /// on the marker-only shape (returns just the closed-set marker, no
14689 /// inner-payload borrow) — where [`Self::as_quote_form`] and
14690 /// [`Self::as_unquote`] surface both the marker AND the wrapped
14691 /// inner `&Sexp` (because the four quote-family arms and the two
14692 /// substitution arms structurally carry a boxed inner value),
14693 /// `as_unquote_form` returns a marker-only projection: consumers that
14694 /// need the wrapped inner reach the marker-plus-inner sibling
14695 /// [`Self::as_unquote`], while consumers that only need the closed-
14696 /// set carving-marker identity (typed-pattern matchers, diagnostic
14697 /// filters, coverage sweeps, LSP/REPL structural-navigation gates)
14698 /// reach this projection and never allocate the tuple.
14699 ///
14700 /// Composes cleanly with [`UnquoteForm::marker`] to project the value-
14701 /// level substitution carving membership onto its canonical marker
14702 /// string (`,` / `,@`):
14703 /// `s.as_unquote_form().map(UnquoteForm::marker)` — the marker-string
14704 /// witness for the substitution subset, sibling to
14705 /// `s.as_atom_kind().map(AtomKind::label)` on the atomic axis, both
14706 /// routing through the closed-set marker enum's canonical-vocabulary
14707 /// projection at ONE canonical site (`UnquoteForm::marker` —
14708 /// itself composed through `QuoteForm::prefix`).
14709 ///
14710 /// Structural identity (pinned as a truth-table by
14711 /// `sexp_as_unquote_form_projects_each_variant_to_canonical_unquote_form`
14712 /// and `sexp_as_unquote_form_rejects_non_unquote_subset_outer_shapes`):
14713 /// * `as_unquote_form() == Some(UnquoteForm::Unquote)` iff `matches!(self, Sexp::Unquote(_))`
14714 /// * `as_unquote_form() == Some(UnquoteForm::Splice)` iff `matches!(self, Sexp::UnquoteSplice(_))`
14715 /// * `as_unquote_form() == None` iff `!matches!(self, Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
14716 ///
14717 /// Theory anchor: THEORY.md §V.1 — knowable platform; the substitution-
14718 /// subset carving marker at the value level becomes a NAMED
14719 /// primitive on the substrate's `Sexp` algebra rather than a per-
14720 /// site composition through either [`Self::as_unquote`] (discarding
14721 /// its `&Sexp` inner) or [`Self::shape`] (walking through the full
14722 /// 12-variant `SexpShape` closed set to arrive at the 2-of-12
14723 /// carving marker) or the parent [`Self::as_quote_form`] combined
14724 /// with [`QuoteForm::as_unquote_form`] (the 2-of-4 subset gate).
14725 /// THEORY.md §II.1 invariant 2 — free middle; every consumer that
14726 /// wants the substitution-subset carving identity without needing
14727 /// the wrapped inner (a future `tatara-check` predicate
14728 /// `(check-value-projects-to-unquote-subset …)` that filters
14729 /// diagnostics keyed on the substitution-subset cohort; a future LSP
14730 /// structural-navigation filter that keys on the substitution-subset
14731 /// carving membership at the value level; a future
14732 /// `TypedRewriter<TemplateOp>` sweep that walks `Sexp` values whose
14733 /// substitution-arm identity is `Some(UnquoteForm::_)` regardless of
14734 /// inner payload identity; a future REPL pretty-printer that chooses
14735 /// rendering paths keyed on the value-level substitution carving
14736 /// marker without needing the inner payload) binds to ONE typed
14737 /// method on the value algebra. THEORY.md §VI.1 — generation over
14738 /// composition; the (Sexp variant, UnquoteForm variant) pairing
14739 /// binds at ONE inherent method on the algebra rather than at three
14740 /// parallel compositions (`as_unquote().map(…)`, `shape()
14741 /// .as_unquote_form()`, `as_quote_form().and_then(|(qf, _)|
14742 /// qf.as_unquote_form())`), so a regression that drifts ONE
14743 /// composition's pairing from the others cannot reach the substrate's
14744 /// runtime — the type system binds all three compositions to the
14745 /// projection's single emission shape.
14746 ///
14747 /// Frontier inspiration: MLIR's `mlir::dyn_cast<UnquoteFamilyOp>(op)
14748 /// .map(|op| op.marker())` — every typed rewriter that only needs
14749 /// the op-family identity (without the op's operands) binds to the
14750 /// typed-downcast projection composed with an operand-discarding
14751 /// marker extract; `Sexp::as_unquote_form` is the marker-only peer
14752 /// on the substrate's `Sexp` algebra, with `Option<UnquoteForm>`
14753 /// standing in for MLIR's `Optional<OperationName>` marker-only
14754 /// downcast result. Racket's `syntax-parse` `~or* (~unquote _)
14755 /// (~unquote-splice _)` — every syntax-class pattern that keys on
14756 /// the substitution-subset marker identity without binding the
14757 /// inner form; `Sexp::as_unquote_form` is the Rust-typed peer that
14758 /// surfaces the marker identity through a single primitive on the
14759 /// syntax algebra.
14760 #[must_use]
14761 pub fn as_unquote_form(&self) -> Option<UnquoteForm> {
14762 self.as_unquote().map(|(uf, _)| uf)
14763 }
14764
14765 /// Decompose a quote-family form into its typed marker and inner
14766 /// expression — `Some((QuoteForm::Quote, inner))` iff this is `'x`
14767 /// (a [`Sexp::Quote`] wrapper), `Some((QuoteForm::Quasiquote, inner))`
14768 /// iff this is `` `x `` (a [`Sexp::Quasiquote`] wrapper),
14769 /// `Some((QuoteForm::Unquote, inner))` iff this is `,x` (a
14770 /// [`Sexp::Unquote`] wrapper), `Some((QuoteForm::UnquoteSplice, inner))`
14771 /// iff this is `,@x` (a [`Sexp::UnquoteSplice`] wrapper), `None` for
14772 /// every other shape (Nil, Atom, List).
14773 ///
14774 /// This is the *quote-family projection* — the typed-marker peer of
14775 /// [`Sexp::as_unquote`] generalized across all four homoiconic
14776 /// prefix-wrappers. Where [`Sexp::as_unquote`] keys the macro-template
14777 /// SUBSTITUTION surface on the closed pair `{Unquote, Splice}` (the
14778 /// two prefixes whose template-time semantic is substitution),
14779 /// `as_quote_form` keys the WIRE-SHAPE surfaces (Display rendering,
14780 /// Hash discrimination, canonical-form interop) on the closed superset
14781 /// `{Quote, Quasiquote, Unquote, UnquoteSplice}` — all four prefixes
14782 /// the reader can tokenize and the writer must round-trip. The
14783 /// `Sexp::as_unquote` projection now derives structurally from
14784 /// `as_quote_form`'s output via [`QuoteForm::as_unquote_form`] — the
14785 /// 2-of-4 subset gate — so the two projections share a SINGLE
14786 /// implementation site on the `Sexp` algebra and the
14787 /// (Sexp variant, QuoteForm variant) pairing binds at ONE rust
14788 /// function regardless of whether the consumer wants the substitution
14789 /// subset or the wire-shape superset.
14790 ///
14791 /// Three consumers in this file route through this primitive:
14792 /// * `Hash for Sexp` — the four `Quote`/`Quasiquote`/`Unquote`/
14793 /// `UnquoteSplice` arms (pre-lift each carrying its own
14794 /// `<discr>.hash(h); inner.hash(h)` body) collapse to ONE arm
14795 /// that routes through `as_quote_form` and reads the
14796 /// discriminator via [`QuoteForm::hash_discriminator`].
14797 /// * `Display for Sexp` — the four `write!(f, "<prefix>{inner}")`
14798 /// arms (pre-lift each carrying its own literal prefix string)
14799 /// collapse to ONE arm that routes through `as_quote_form` and
14800 /// reads the prefix via [`QuoteForm::prefix`].
14801 /// * [`Sexp::as_unquote`] — derives `Option<(UnquoteForm, &Sexp)>`
14802 /// by composing `as_quote_form` with [`QuoteForm::as_unquote_form`]
14803 /// (the 2-of-4 subset projection), so the macro-template
14804 /// substitution surface inherits the (Sexp variant, marker)
14805 /// pairing through this projection's typed dispatch rather than
14806 /// re-deriving its own arm-based match.
14807 ///
14808 /// The closed-set guarantee on [`QuoteForm`] (exactly
14809 /// `Quote ⊎ Quasiquote ⊎ Unquote ⊎ UnquoteSplice`) is threaded through
14810 /// this projection's return tuple, so consumers that pattern-match on
14811 /// `form: QuoteForm` get rustc-enforced exhaustiveness — a future
14812 /// `Sexp` wrapper variant must extend `QuoteForm` AND this match arm
14813 /// together (or stay outside the quote family and project to `None`),
14814 /// eliminating the silent multi-site extension-drift this lift was
14815 /// designed to forbid.
14816 ///
14817 /// Soft face, like the rest of the `as_*` family on `Sexp`: it
14818 /// answers "is this form a quote-family marker, and what does it
14819 /// wrap?" and yields `None` for everything that isn't (skip / fall
14820 /// through), with no diagnostic.
14821 ///
14822 /// Structural identity binding it to the quote-family variants and
14823 /// its `as_unquote` subset sibling:
14824 /// * `as_quote_form() == Some((QuoteForm::Quote, inner))` iff `self == Sexp::Quote(inner)`
14825 /// * `as_quote_form() == Some((QuoteForm::Quasiquote, inner))` iff `self == Sexp::Quasiquote(inner)`
14826 /// * `as_quote_form() == Some((QuoteForm::Unquote, inner))` iff `self == Sexp::Unquote(inner)`
14827 /// * `as_quote_form() == Some((QuoteForm::UnquoteSplice, inner))` iff `self == Sexp::UnquoteSplice(inner)`
14828 /// * `as_quote_form().is_some() == matches!(self, Sexp::Quote(_) | Sexp::Quasiquote(_) | Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
14829 /// * `as_unquote() == as_quote_form().and_then(|(qf, inner)| qf.as_unquote_form().map(|uf| (uf, inner)))`
14830 ///
14831 /// The returned `&Sexp` borrows the inner box's body verbatim — no
14832 /// clone, no allocation — same lifetime as `&self` and same posture
14833 /// as [`Sexp::as_unquote`]'s tail.
14834 ///
14835 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
14836 /// quote-family (Sexp variant, prefix string, hash discriminator)
14837 /// triple appeared inline at three sites (`Hash for Sexp`,
14838 /// `Display for Sexp`, `as_unquote`) — well past the ≥2 PRIME-DIRECTIVE
14839 /// trigger once the structural shape is named. THEORY.md §V.1 —
14840 /// knowable platform; the quote-family typed-marker projection becomes
14841 /// a NAMED primitive on the substrate's `Sexp` algebra rather than
14842 /// per-site inline matches paired with per-site discriminator literals
14843 /// and prefix literals. THEORY.md §II.1 invariant 1 — typed entry; the
14844 /// reader's prefix-to-variant dispatch ([`crate::reader::read_quoted`])
14845 /// AND the Display impl's variant-to-prefix dispatch are dual
14846 /// typed-entry / typed-exit gates over the same closed set; the
14847 /// `QuoteForm` algebra threads BOTH gates through ONE typed enum so a
14848 /// regression that drifts one side's prefix from the other (e.g. the
14849 /// reader gains a fifth prefix but the Display impl doesn't) is no
14850 /// longer a silent two-site divergence — rustc binds both sides to
14851 /// the same closed-set enum. THEORY.md §II.1 invariant 2 — free
14852 /// middle; the three consumers (Hash, Display, `as_unquote`) route
14853 /// through the SAME projection, so a regression that drifts ONE
14854 /// consumer's (Sexp variant, marker) pairing from the others cannot
14855 /// reach the substrate's runtime.
14856 ///
14857 /// Frontier inspiration: Racket's `syntax-parse` `~or* (~quote stx)
14858 /// (~quasiquote stx) (~unquote stx) (~unquote-splice stx)` pattern —
14859 /// every macro-template pattern over `'`/`` ` ``/`,`/`,@` binds to
14860 /// ONE typed decomposition that surfaces the marker identity
14861 /// alongside the inner expression; the substrate's `as_quote_form` is
14862 /// the Rust-typed peer of that pattern, lifted onto the `Sexp`
14863 /// algebra with `QuoteForm` standing in for Racket's pattern-class
14864 /// identity at the homoiconic prefix surface. MLIR's typed-IR
14865 /// projection `mlir::dyn_cast<QuoteFamilyOp>(op)` — the typed downcast
14866 /// from a polymorphic IR node onto a closed-set op family is the MLIR
14867 /// idiom; `as_quote_form` is the unstructured-projection peer on the
14868 /// substrate's `Sexp` algebra, with `Option<(QuoteForm, &Sexp)>`
14869 /// standing in for MLIR's typed downcast result.
14870 pub fn as_quote_form(&self) -> Option<(QuoteForm, &Sexp)> {
14871 match self {
14872 Self::Quote(inner) => Some((QuoteForm::Quote, inner)),
14873 Self::Quasiquote(inner) => Some((QuoteForm::Quasiquote, inner)),
14874 Self::Unquote(inner) => Some((QuoteForm::Unquote, inner)),
14875 Self::UnquoteSplice(inner) => Some((QuoteForm::UnquoteSplice, inner)),
14876 _ => None,
14877 }
14878 }
14879
14880 /// Soft projection onto the closed-set [`QuoteForm`] quote-family
14881 /// carving marker — the 4-of-12 carving of the [`SexpShape`] algebra
14882 /// covering the four homoiconic prefix-wrappers ([`Self::Quote`],
14883 /// [`Self::Quasiquote`], [`Self::Unquote`], [`Self::UnquoteSplice`]).
14884 /// Returns `Some(QuoteForm::Quote)` iff this is `'x` (a
14885 /// [`Self::Quote`] wrapper), `Some(QuoteForm::Quasiquote)` iff this
14886 /// is `` `x `` (a [`Self::Quasiquote`] wrapper),
14887 /// `Some(QuoteForm::Unquote)` iff this is `,x` (a [`Self::Unquote`]
14888 /// wrapper), `Some(QuoteForm::UnquoteSplice)` iff this is `,@x` (a
14889 /// [`Self::UnquoteSplice`] wrapper), `None` for every other outer
14890 /// shape ([`Self::Nil`], every [`Self::Atom`] variant, [`Self::List`]).
14891 ///
14892 /// Direct value-level peer of the shape-level projection
14893 /// [`SexpShape::as_quote_form`](crate::error::SexpShape::as_quote_form)
14894 /// — the pair `(Sexp::as_quote_form_marker, SexpShape::as_quote_form)`
14895 /// binds the (Sexp value, QuoteForm carving marker) pairing at ONE
14896 /// typed method on each algebra, closing the quote-family cell of
14897 /// the (Sexp value → carving marker) matrix at the marker-only
14898 /// value-level projection surface. Marker-only sibling of
14899 /// [`Self::as_quote_form`] (which returns `Option<(QuoteForm, &Sexp)>`
14900 /// — marker + wrapped inner). Post-lift the substrate's value-level
14901 /// marker-only carving-marker matrix closes its FINAL cell: the
14902 /// atomic axis via [`Self::as_atom_kind`] (6-of-12), the residual
14903 /// axis via [`Self::as_structural_kind`] (2-of-12), the unquote-
14904 /// subset axis via [`Self::as_unquote_form`] (2-of-12), and NOW the
14905 /// quote-family axis via `Self::as_quote_form_marker` (4-of-12) —
14906 /// symmetric with the shape-level marker-only projection family on
14907 /// [`SexpShape`](crate::error::SexpShape).
14908 ///
14909 /// Composition laws (two-way agreement — bindings): for every
14910 /// `s: &Sexp`,
14911 /// `s.as_quote_form_marker() == s.as_quote_form().map(|(qf, _)| qf)
14912 /// == s.shape().as_quote_form()`. Pre-lift the quote-family carving
14913 /// marker at the value level was reachable only via one of these
14914 /// two-step compositions — either through the parent
14915 /// [`Self::as_quote_form`] projection (discarding the wrapped inner
14916 /// via `.map(|(qf, _)| qf)`) or through the shape algebra
14917 /// (`s.shape().as_quote_form()`, walking the full 12-variant
14918 /// [`SexpShape`](crate::error::SexpShape) closed set to arrive at
14919 /// the 4-of-12 carving marker). Post-lift the projection lands at
14920 /// ONE typed method on the value algebra, and both compositions
14921 /// are pinned as agreement laws (see
14922 /// `sexp_as_quote_form_marker_agrees_with_as_quote_form_map_marker_for_every_variant`
14923 /// and
14924 /// `sexp_as_quote_form_marker_agrees_with_shape_as_quote_form_for_every_variant`
14925 /// in this module).
14926 ///
14927 /// Superset-gate contract with [`Self::as_unquote_form`]: for every
14928 /// `s: &Sexp`, `s.as_unquote_form().is_some()` implies
14929 /// `s.as_quote_form_marker().is_some()` (the 2-of-12 substitution
14930 /// subset is a proper subset of the 4-of-12 quote family). The two
14931 /// non-substitution quote-family wrappers ([`Self::Quote`] and
14932 /// [`Self::Quasiquote`]) satisfy `as_quote_form_marker().is_some()`
14933 /// AND `as_unquote_form().is_none()` — the value-level image of the
14934 /// 2-of-4 subset gate [`QuoteForm::as_unquote_form`]. Pinned by
14935 /// `sexp_as_quote_form_marker_extends_as_unquote_form_to_full_quote_family`.
14936 ///
14937 /// Structural identity binding it to the quote-family variants:
14938 /// * `as_quote_form_marker() == Some(QuoteForm::Quote)` iff `matches!(self, Sexp::Quote(_))`
14939 /// * `as_quote_form_marker() == Some(QuoteForm::Quasiquote)` iff `matches!(self, Sexp::Quasiquote(_))`
14940 /// * `as_quote_form_marker() == Some(QuoteForm::Unquote)` iff `matches!(self, Sexp::Unquote(_))`
14941 /// * `as_quote_form_marker() == Some(QuoteForm::UnquoteSplice)` iff `matches!(self, Sexp::UnquoteSplice(_))`
14942 /// * `as_quote_form_marker() == None` iff `!matches!(self, Sexp::Quote(_) | Sexp::Quasiquote(_) | Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
14943 ///
14944 /// Theory anchor: THEORY.md §V.1 — knowable platform; the quote-
14945 /// family carving marker at the value level becomes a NAMED
14946 /// primitive on the substrate's `Sexp` algebra rather than a per-
14947 /// site two-step composition through either [`Self::as_quote_form`]
14948 /// (discarding its `&Sexp` inner) or [`Self::shape`] (walking through
14949 /// the full 12-variant [`SexpShape`](crate::error::SexpShape) closed
14950 /// set to arrive at the 4-of-12 carving marker). THEORY.md §II.1
14951 /// invariant 2 — free middle; every consumer that wants the quote-
14952 /// family carving identity without needing the wrapped inner (a
14953 /// future `tatara-check` predicate `(check-value-projects-to-quote-
14954 /// family …)` that filters diagnostics keyed on the quote-family
14955 /// cohort; a future LSP structural-navigation filter that keys on
14956 /// the quote-family carving membership at the value level; a
14957 /// future `TypedRewriter<QuoteFamilyOp>` sweep that walks `Sexp`
14958 /// values whose quote-family arm identity is `Some(QuoteForm::_)`
14959 /// regardless of inner payload identity; a future REPL pretty-
14960 /// printer that chooses rendering paths keyed on the value-level
14961 /// quote-family carving marker without needing the inner payload)
14962 /// routes through ONE typed method rather than reaching into one of
14963 /// the two composition sites, and both compositions are pinned as
14964 /// agreement laws so a regression that drifts ONE composition's
14965 /// pairing from the other cannot reach the substrate's runtime.
14966 /// THEORY.md §VI.1 — generation over composition; the (Sexp variant,
14967 /// QuoteForm variant) pairing binds at ONE inherent method on the
14968 /// algebra rather than at two parallel compositions, so a future
14969 /// extension (e.g. a fifth `Sexp` quote-family wrapper) lands at
14970 /// ONE match arm in the parent `as_quote_form` projection and
14971 /// inherits through this method's structural composition.
14972 ///
14973 /// Frontier inspiration: MLIR's `mlir::dyn_cast<QuoteFamilyOp>(op)
14974 /// .map(|op| op.marker())` — every typed rewriter that only needs
14975 /// the op-family identity (without the op's operands) binds to the
14976 /// typed-downcast projection composed with an operand-discarding
14977 /// marker extract; `Sexp::as_quote_form_marker` is the marker-only
14978 /// peer on the substrate's `Sexp` algebra, with
14979 /// `Option<QuoteForm>` standing in for MLIR's
14980 /// `Optional<OperationName>` marker-only downcast result. Racket's
14981 /// `syntax-parse` `~or* (~quote _) (~quasiquote _) (~unquote _)
14982 /// (~unquote-splice _)` — every syntax-class pattern that keys on
14983 /// the quote-family marker identity without binding the inner form;
14984 /// `Sexp::as_quote_form_marker` is the Rust-typed peer that
14985 /// surfaces the marker identity through a single primitive on the
14986 /// syntax algebra.
14987 #[must_use]
14988 pub fn as_quote_form_marker(&self) -> Option<QuoteForm> {
14989 self.as_quote_form().map(|(qf, _)| qf)
14990 }
14991
14992 /// Quote-family projection, asserted-total face of [`Sexp::as_quote_form`].
14993 /// Returns `(QuoteForm, &Sexp)` verbatim — same borrowed-inner posture,
14994 /// same closed-set marker — but panics with [`QUOTE_FAMILY_PROJECTION_INVARIANT`]
14995 /// instead of yielding `None` for non-quote-family variants. Use AFTER
14996 /// an outer pattern match has narrowed the discriminant union to the
14997 /// quote family (`Sexp::Quote(_) | Sexp::Quasiquote(_) | Sexp::Unquote(_) |
14998 /// Sexp::UnquoteSplice(_)`); the panic message states the invariant the
14999 /// caller's outer pattern already proves.
15000 ///
15001 /// Pre-lift the five production-site quote-family-arm consumers —
15002 /// `Hash for Sexp::hash_discriminator`, `Display for Sexp::prefix`,
15003 /// `domain::sexp_shape`, `domain::sexp_to_json`, `interop::iac_forge_tag` —
15004 /// each carried a verbatim copy of the 4-arm wildcard pattern AND a
15005 /// verbatim copy of the inline
15006 /// `.as_quote_form().expect("matched quote-family variant must project
15007 /// to Some via as_quote_form")` re-projection. The `(pattern, expect
15008 /// message)` pair appeared bit-for-bit at FIVE sites. Post-lift the
15009 /// expect message lives at ONE named const and the projection-with-
15010 /// assertion lives at ONE primitive on the `Sexp` algebra; the five
15011 /// callsites collapse to ONE typed query each. A future quote-family
15012 /// extension that drifts ONE site's panic text from the others becomes
15013 /// structurally impossible (one const, one method); a future site that
15014 /// needs the same "outer-narrowed, total projection" shape lands on
15015 /// this primitive directly without re-deriving the expect literal.
15016 ///
15017 /// `#[track_caller]` ensures a panic surfaces the consumer's source
15018 /// position, not this projection's — so the diagnostic stays
15019 /// load-bearing under the lift.
15020 ///
15021 /// Sibling posture to the `expect_*` family of typed-projection
15022 /// asserted-total faces across the substrate's closed-set algebras
15023 /// (`Option::expect`, `Result::expect`) — the assertion is the same
15024 /// shape, the message is named on the algebra it asserts about.
15025 ///
15026 /// # Panics
15027 ///
15028 /// Panics with [`QUOTE_FAMILY_PROJECTION_INVARIANT`] if `self` is not
15029 /// a quote-family variant. The outer pattern match at every caller
15030 /// site is the proof of the invariant; the panic is the static
15031 /// fall-through for a regression that drifts that proof.
15032 ///
15033 /// Theory anchor: THEORY.md §VI.1 — generation over composition; the
15034 /// (4-arm wildcard pattern, expect re-projection) pair appeared bit-
15035 /// for-bit at five production sites — well past the ≥2 PRIME-DIRECTIVE
15036 /// trigger. THEORY.md §V.1 — knowable platform; the panic message and
15037 /// the projection-with-assertion are now ONE named primitive on the
15038 /// substrate's `Sexp` algebra, structurally binding the invariant
15039 /// across every consumer that asserts an outer narrowing.
15040 #[must_use]
15041 #[track_caller]
15042 pub fn expect_quote_form(&self) -> (QuoteForm, &Sexp) {
15043 self.as_quote_form()
15044 .expect(QUOTE_FAMILY_PROJECTION_INVARIANT)
15045 }
15046
15047 /// Stable, per-outer-variant byte discriminator for the substrate's
15048 /// [`Hash for Sexp`] cache-key projection — `0` for [`Self::Nil`],
15049 /// `1` for [`Self::Atom`], `2` for [`Self::List`], `3` for
15050 /// [`Self::Quote`], `4` for [`Self::Quasiquote`], `5` for
15051 /// [`Self::Unquote`], `6` for [`Self::UnquoteSplice`]. Composes
15052 /// through [`Self::shape`] into
15053 /// [`crate::error::SexpShape::hash_discriminator`], which in turn
15054 /// composes through the three closed-set sub-carvings' discriminator
15055 /// methods: [`crate::error::StructuralKind::hash_discriminator`] for
15056 /// the two structural-residual arms `{0, 2}`,
15057 /// [`crate::error::QuoteForm::hash_discriminator`] for the four
15058 /// quote-family arms `{3..=6}`, and the outer atomic marker byte
15059 /// `1u8` for the six atomic-payload arms (whose inner
15060 /// [`crate::ast::AtomKind::hash_discriminator`] `{0..=5}` partition
15061 /// nests inside [`Hash for Atom`] rather than surfacing here). The
15062 /// outer-`Sexp` cache-key algebra now closes at FIVE typed layers
15063 /// (outer `Sexp` → [`crate::error::SexpShape`] → three sub-carvings)
15064 /// with rustc-enforced consistency across each.
15065 ///
15066 /// The byte values are load-bearing because the macro-expansion cache
15067 /// ([`crate::macro_expand::Expander`]'s cache) keys on the hash of
15068 /// `(macro_name, args)` — changing a discriminator silently
15069 /// invalidates every cached expansion across the substrate.
15070 ///
15071 /// The seven outer-variant arms partition `{0, 1, 2, 3, 4, 5, 6}`
15072 /// injectively — closed-set-typed intra-Sexp injectivity that
15073 /// composes through the intermediate
15074 /// [`crate::error::SexpShape::hash_discriminator`] shape-level
15075 /// projection (12 arms → 7 bytes; the six atomic-shape arms
15076 /// collapse to the outer Atom marker byte `1u8`; the two
15077 /// structural-residual arms surface `{0, 2}`; the four quote-family
15078 /// arms surface `{3..=6}`). Together the four sub-algebras (this
15079 /// outer method + shape + three sub-carvings) jointly cover the
15080 /// entire outer-Sexp discriminator space through ONE typed method
15081 /// per algebra layer. A future eighth `Sexp` variant (e.g. a
15082 /// hypothetical `Vector` for `#(...)` reader syntax, `Map` for
15083 /// `{...}`, or `Char` for `#\x`) picks a fresh cache-key byte
15084 /// outside `{0..=6}` (e.g. `7u8`), extends the closed-set
15085 /// [`crate::error::SexpShape`] enum + its
15086 /// `hash_discriminator` (plus either [`crate::error::StructuralKind`]
15087 /// or a fresh sub-algebra) in lockstep — rustc binds the
15088 /// consistency through exhaustiveness over each closed enum.
15089 ///
15090 /// Pre-lift this outer method dispatched over the seven `Sexp`
15091 /// variants directly and routed the three structural + four
15092 /// quote-family arms into the two sub-carvings' discriminator
15093 /// methods with the Atom arm inline at `1u8` — the intermediate
15094 /// [`crate::error::SexpShape`] shape-level projection did not
15095 /// exist, so a consumer with a typed [`crate::error::SexpShape`]
15096 /// identity in hand had to re-embed into a `Sexp` value to reach
15097 /// the outer cache-key byte. Post-lift the outer method routes
15098 /// through [`Self::shape`] into
15099 /// [`crate::error::SexpShape::hash_discriminator`]; the shape-level
15100 /// projection is the missing algebra layer between the outer `Sexp`
15101 /// and the three sub-carvings, and consumers with a typed shape
15102 /// identity now reach the outer cache-key byte at ONE typed method
15103 /// per algebra layer without a re-embed.
15104 ///
15105 /// `pub(crate)` because the byte-discriminator surface is an
15106 /// implementation detail of the substrate's `Hash for Sexp` cache-
15107 /// key contract; exposing it publicly would leak the cache-key shape
15108 /// through the API without enabling any external consumer the public
15109 /// projections ([`Self::as_atom`], [`Self::as_list`],
15110 /// [`Self::as_quote_form`]) don't already serve. Same posture as
15111 /// [`crate::error::SexpShape::hash_discriminator`],
15112 /// [`crate::ast::AtomKind::hash_discriminator`],
15113 /// [`crate::error::QuoteForm::hash_discriminator`], and
15114 /// [`crate::error::StructuralKind::hash_discriminator`].
15115 #[must_use]
15116 pub(crate) fn hash_discriminator(&self) -> u8 {
15117 self.shape().hash_discriminator()
15118 }
15119
15120 /// Cross-crate canonical iac-forge tag for the outer [`Sexp`] value —
15121 /// the OUTER-VALUE peer of the shape-level [`crate::error::SexpShape
15122 /// ::iac_forge_tag`] one algebra layer down. `Some(&'static str)` for
15123 /// the four homoiconic prefix-wrapper arms — `Self::Quote →
15124 /// Some("quote")`, `Self::Quasiquote → Some("quasiquote")`,
15125 /// `Self::Unquote → Some("unquote")`, `Self::UnquoteSplice →
15126 /// Some("unquote-splicing")` — and `None` for the outer atomic-payload
15127 /// arm ([`Self::Atom`]) AND the two structural-residual arms
15128 /// ([`Self::Nil`], [`Self::List`]). The 4-of-7 partial projection on
15129 /// the outer-`Sexp` algebra surfaces
15130 /// [`crate::ast::QuoteForm::iac_forge_tag`]'s cross-crate canonical-
15131 /// form tag surface at the outermost value-carrier algebra level,
15132 /// composed through the pre-existing [`Self::shape`] projection and
15133 /// [`crate::error::SexpShape::iac_forge_tag`]'s shape-level partial
15134 /// projection.
15135 ///
15136 /// Composition law: `sexp.iac_forge_tag() ==
15137 /// sexp.shape().iac_forge_tag()` for every `sexp: &Sexp` — the outer-
15138 /// `Sexp` cross-crate canonical-form tag surface routes through
15139 /// [`Self::shape`] into the shape-level partial projection, which in
15140 /// turn composes through [`crate::error::SexpShape::as_quote_form`]
15141 /// with [`crate::ast::QuoteForm::iac_forge_tag`]'s canonical 4-of-4
15142 /// closed-set tag projection. Post-lift the outer-`Sexp` cross-crate
15143 /// canonical-form tag surface closes at FOUR typed layers: outer
15144 /// [`Self::iac_forge_tag`] (7-arm outer dispatch on the outer
15145 /// [`Sexp`] algebra, this method) → shape-level
15146 /// [`crate::error::SexpShape::iac_forge_tag`] (12-arm shape-level
15147 /// dispatch on the [`crate::error::SexpShape`] algebra) →
15148 /// quote-family carving [`crate::error::SexpShape::as_quote_form`]
15149 /// (4-of-12 quote-family sub-carving) → sub-carving tag
15150 /// [`crate::ast::QuoteForm::iac_forge_tag`] (4-arm quote-family
15151 /// sub-carving's canonical-form tag projection).
15152 ///
15153 /// Pre-lift a consumer with a typed [`Sexp`] value in hand (a
15154 /// generation-side canonical-form emitter, a downstream iac-forge
15155 /// attestation site, an LSP / REPL / audit-trail metric keyed on the
15156 /// observed outer value) wanting the cross-crate iac-forge canonical
15157 /// tag string had to spell the two-step composition
15158 /// `sexp.shape().iac_forge_tag()` at every callsite, or route through
15159 /// [`Self::as_quote_form_marker`] composed with
15160 /// [`crate::ast::QuoteForm::iac_forge_tag`] via `map` as the
15161 /// `crate::interop` (removed) `From<&Sexp> for iac_forge::SExpr` impl does
15162 /// for its four quote-family arms via [`Self::expect_quote_form`]
15163 /// composed with [`crate::ast::QuoteForm::iac_forge_tag`]. Post-lift
15164 /// the outer-`Sexp` canonical-form tag projection binds at ONE
15165 /// typed-algebra method on the outer value-carrier — the SEVENTH
15166 /// consumer of the outer-`Sexp` projection surface (sibling of
15167 /// [`Self::shape`], [`Self::type_name`],
15168 /// [`Self::hash_discriminator`], [`Self::as_atom`], [`Self::as_list`],
15169 /// [`Self::as_quote_form`], [`Self::as_quote_form_marker`],
15170 /// [`Self::as_unquote`], [`Self::as_unquote_form`]), matching the
15171 /// same shape-composition posture [`Self::hash_discriminator`] takes
15172 /// through the outer → shape one-step delegation.
15173 ///
15174 /// The `Option<&'static str>` return shape mirrors
15175 /// [`crate::error::SexpShape::iac_forge_tag`]'s partial-projection
15176 /// shape one algebra level down — the outer-`Sexp` seven-arm closed
15177 /// set's projection PARTIALIZES on the three non-quote-family shapes
15178 /// (`Nil`, `Atom`, `List`) exactly as the shape-level twelve-arm
15179 /// closed set's projection PARTIALIZES on the eight non-quote-family
15180 /// shapes. The kernel's outer cardinality (three: `Nil` / `Atom` /
15181 /// `List`) matches the shape-level kernel's cardinality (eight)
15182 /// through [`Self::shape`]'s six-atomic-arms → outer `Atom` collapse
15183 /// — the outer three-arm kernel `{Nil, Atom, List}` corresponds to
15184 /// the shape-level eight-arm kernel `{Nil, Symbol, Keyword, String,
15185 /// Int, Float, Bool, List}` under the outer → shape projection.
15186 ///
15187 /// The `&'static str` lifetime is load-bearing: every iac-forge
15188 /// consumer projects through this method into the canonical
15189 /// 2-element-list head without an allocation, parallel to how
15190 /// [`crate::ast::QuoteForm::iac_forge_tag`] on the sub-carving,
15191 /// [`crate::error::SexpShape::iac_forge_tag`] on the shape-level
15192 /// projection, and [`crate::error::UnquoteForm::iac_forge_tag`] on
15193 /// the template-substitution subset project their respective closed
15194 /// sets. A future eighth [`Sexp`] variant (e.g. a hypothetical
15195 /// `Vector` for `#(...)` reader syntax, `Map` for `{...}`, `Char` for
15196 /// `#\x`) extends [`crate::error::SexpShape`] (adding a `None`-arm
15197 /// non-quote-family shape) — this method picks up the new arm's
15198 /// `None` mechanically through the shape composition, with rustc's
15199 /// exhaustiveness binding the extension end-to-end at
15200 /// [`crate::error::SexpShape::as_quote_form`]'s closed match.
15201 ///
15202 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (outer
15203 /// `Sexp` variant, canonical iac-forge tag) pairing becomes a TYPE
15204 /// projection on the outermost value-carrier algebra rather than an
15205 /// inline `.shape().iac_forge_tag()` two-step at every consumer. A
15206 /// typo or swap at the projection site is no longer a runtime tag
15207 /// drift but a compile error against the typed composition — the
15208 /// `Sexp` ↔ `SexpShape` ↔ `QuoteForm` ↔ tag string chain is rustc-
15209 /// enforced end-to-end. THEORY.md §II.1 invariant 2 — free middle;
15210 /// the (outer value, canonical iac-forge tag) pairing now binds at
15211 /// ONE site on the outer-`Sexp` algebra, composing through the pre-
15212 /// existing shape-level partial projection rather than duplicating
15213 /// the four-arm match here. THEORY.md §VI.1 — generation over
15214 /// composition; the outer-`Sexp` cross-crate canonical-form tag
15215 /// surface closes at FOUR typed layers (outer → shape → carving →
15216 /// sub-carving-tag), each keyed on the SAME canonical-form tag
15217 /// projection carried at the closed-set sub-carving level.
15218 ///
15219 /// Frontier inspiration: MLIR's `mlir::Operation::getName()` typed
15220 /// projection composed with `mlir::OperationName::getStringRef()` —
15221 /// narrowing an operation-carrier value through its typed op-name
15222 /// identity yields the canonical cross-boundary string identity in
15223 /// ONE typed composition. [`Self::iac_forge_tag`] is the Rust-typed
15224 /// peer where the "project to shape" step ([`Self::shape`]) composes
15225 /// with the "read the shape's canonical tag" step
15226 /// ([`crate::error::SexpShape::iac_forge_tag`]) into ONE outer-value
15227 /// projection.
15228 #[must_use]
15229 pub fn iac_forge_tag(&self) -> Option<&'static str> {
15230 self.shape().iac_forge_tag()
15231 }
15232
15233 /// Canonical reader-punctuation prefix for the outer [`Sexp`] value —
15234 /// the OUTER-VALUE peer of the shape-level
15235 /// [`crate::error::SexpShape::prefix`] one algebra layer down.
15236 /// `Some(&'static str)` for the four homoiconic prefix-wrapper arms —
15237 /// `Self::Quote → Some("'")`, `Self::Quasiquote → Some("`")`,
15238 /// `Self::Unquote → Some(",")`, `Self::UnquoteSplice → Some(",@")` —
15239 /// and `None` for the outer atomic-payload arm ([`Self::Atom`]) AND
15240 /// the two structural-residual arms ([`Self::Nil`], [`Self::List`]).
15241 /// The 4-of-7 partial projection on the outer-`Sexp` algebra surfaces
15242 /// [`crate::ast::QuoteForm::prefix`]'s reader-punctuation surface at
15243 /// the outermost value-carrier algebra level, composed through the
15244 /// pre-existing [`Self::shape`] projection and
15245 /// [`crate::error::SexpShape::prefix`]'s shape-level partial
15246 /// projection.
15247 ///
15248 /// Composition law: `sexp.prefix() == sexp.shape().prefix()` for
15249 /// every `sexp: &Sexp` — the outer-`Sexp` reader-punctuation surface
15250 /// routes through [`Self::shape`] into the shape-level partial
15251 /// projection, which in turn composes through
15252 /// [`crate::error::SexpShape::as_quote_form`] with
15253 /// [`crate::ast::QuoteForm::prefix`]'s canonical 4-of-4 closed-set
15254 /// prefix projection. Post-lift the outer-`Sexp` reader-punctuation
15255 /// surface closes at FOUR typed layers: outer [`Self::prefix`]
15256 /// (7-arm outer dispatch on the outer [`Sexp`] algebra, this method)
15257 /// → shape-level [`crate::error::SexpShape::prefix`] (12-arm shape-
15258 /// level dispatch on the [`crate::error::SexpShape`] algebra) →
15259 /// quote-family carving [`crate::error::SexpShape::as_quote_form`]
15260 /// (4-of-12 quote-family sub-carving) → sub-carving prefix
15261 /// [`crate::ast::QuoteForm::prefix`] (4-arm quote-family sub-
15262 /// carving's canonical reader-punctuation projection).
15263 ///
15264 /// Pre-lift a consumer with a typed [`Sexp`] value in hand (an
15265 /// [`fmt::Display for Sexp`] impl that renders the four quote-family
15266 /// arms as `<prefix><inner>`, an LSP hover / REPL completion that
15267 /// echoes the source-punctuation prefix of a wrapper value, an
15268 /// audit-trail metric keyed on the observed outer value) wanting
15269 /// the canonical reader-punctuation prefix string had to spell the
15270 /// two-step composition `sexp.shape().prefix()` at every callsite,
15271 /// or route through [`Self::as_quote_form_marker`] composed with
15272 /// [`crate::ast::QuoteForm::prefix`] via `map` as the
15273 /// [`fmt::Display for Sexp`] impl does for its four quote-family
15274 /// arms via [`Self::expect_quote_form`] composed with
15275 /// [`crate::ast::QuoteForm::prefix`]. Post-lift the outer-`Sexp`
15276 /// reader-punctuation projection binds at ONE typed-algebra method
15277 /// on the outer value-carrier — the natural next rung on the
15278 /// trajectory mirroring the [`Self::iac_forge_tag`] →
15279 /// [`crate::error::SexpShape::iac_forge_tag`] ladder one vocabulary
15280 /// axis over, matching the same shape-composition posture
15281 /// [`Self::hash_discriminator`] and [`Self::iac_forge_tag`] take
15282 /// through the outer → shape one-step delegation.
15283 ///
15284 /// The `Option<&'static str>` return shape mirrors
15285 /// [`crate::error::SexpShape::prefix`]'s partial-projection shape one
15286 /// algebra level down — the outer-`Sexp` seven-arm closed set's
15287 /// projection PARTIALIZES on the three non-quote-family shapes
15288 /// (`Nil`, `Atom`, `List`) exactly as the shape-level twelve-arm
15289 /// closed set's projection PARTIALIZES on the eight non-quote-family
15290 /// shapes. The kernel's outer cardinality (three: `Nil` / `Atom` /
15291 /// `List`) matches the shape-level kernel's cardinality (eight)
15292 /// through [`Self::shape`]'s six-atomic-arms → outer `Atom` collapse
15293 /// — the outer three-arm kernel `{Nil, Atom, List}` corresponds to
15294 /// the shape-level eight-arm kernel `{Nil, Symbol, Keyword, String,
15295 /// Int, Float, Bool, List}` under the outer → shape projection.
15296 ///
15297 /// The reader-punctuation vocabulary this method projects (`"'"` /
15298 /// `` "`" `` / `","` / `",@"`) is INTENTIONALLY DISJOINT from the
15299 /// two sibling `&'static str` outer-value projection axes:
15300 ///
15301 /// * [`Self::iac_forge_tag`] — cross-crate canonical form
15302 /// (`"quote"` / `"quasiquote"` / `"unquote"` /
15303 /// `"unquote-splicing"`), BLAKE3 attestation keys, render-cache
15304 /// shape (load-bearing for byte-identical inter-crate compatibility
15305 /// with the iac-forge ecosystem);
15306 /// * [`Self::type_name`] — operator-facing diagnostic label
15307 /// (`"nil"` / `"atom"` / `"list"` / `"quote"` / `"quasiquote"` /
15308 /// `"unquote"` / `"unquote-splice"`) on the outer 7-arm surface,
15309 /// [`crate::error::LispError::TypeMismatch`]'s `got` rendering,
15310 /// REPL / LSP shape-of-witness surface.
15311 ///
15312 /// This method projects the reader's SOURCE-TEXT vocabulary — the
15313 /// four punctuation characters that appear literally in Lisp source
15314 /// at each variant's homoiconic prefix. The three outer-value
15315 /// closed-set projections key the SAME seven-arm outer algebra on
15316 /// THREE distinct `&'static str` vocabularies (source-punctuation,
15317 /// diagnostic-label, cross-crate canonical-form); consolidating any
15318 /// two would silently break either the reader round-trip, the
15319 /// operator-facing diagnostic surface, OR the iac-forge attestation
15320 /// pipeline. The three vocabularies' distinctness is pinned bit-for-
15321 /// bit through the composition law across the closed-set typed
15322 /// algebra.
15323 ///
15324 /// The `&'static str` lifetime is load-bearing: every reader / LSP
15325 /// / REPL / [`fmt::Display for Sexp`] consumer projects through this
15326 /// method into the canonical prefix character without an allocation,
15327 /// parallel to how [`crate::ast::QuoteForm::prefix`] on the sub-
15328 /// carving, [`crate::error::SexpShape::prefix`] on the shape-level
15329 /// projection, [`Self::iac_forge_tag`] on the cross-crate canonical-
15330 /// form axis, and [`crate::error::UnquoteForm::marker`] on the
15331 /// template-marker axis project their respective closed sets. A
15332 /// future eighth [`Sexp`] variant (e.g. a hypothetical `Vector` for
15333 /// `#(...)` reader syntax, `Map` for `{...}`, `Char` for `#\x`)
15334 /// extends [`crate::error::SexpShape`] (adding a `None`-arm non-
15335 /// quote-family shape) — this method picks up the new arm's `None`
15336 /// mechanically through the shape composition, with rustc's
15337 /// exhaustiveness binding the extension end-to-end at
15338 /// [`crate::error::SexpShape::as_quote_form`]'s closed match.
15339 ///
15340 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (outer
15341 /// `Sexp` variant, reader-punctuation prefix) pairing becomes a
15342 /// TYPE projection on the outermost value-carrier algebra rather
15343 /// than an inline `.shape().prefix()` two-step at every consumer.
15344 /// A typo or swap at the projection site is no longer a runtime
15345 /// prefix drift but a compile error against the typed composition
15346 /// — the `Sexp` ↔ `SexpShape` ↔ `QuoteForm` ↔ prefix character
15347 /// chain is rustc-enforced end-to-end. THEORY.md §II.1 invariant 2
15348 /// — free middle; the (outer value, reader-punctuation prefix)
15349 /// pairing now binds at ONE site on the outer-`Sexp` algebra,
15350 /// composing through the pre-existing shape-level partial
15351 /// projection rather than duplicating the four-arm match here.
15352 /// THEORY.md §VI.1 — generation over composition; the outer-`Sexp`
15353 /// reader-punctuation surface closes at FOUR typed layers (outer
15354 /// → shape → carving → sub-carving-prefix), each keyed on the SAME
15355 /// reader-punctuation projection carried at the closed-set sub-
15356 /// carving level.
15357 ///
15358 /// Frontier inspiration: MLIR's `mlir::Operation::getName()` typed
15359 /// projection composed with `mlir::OperationName::getStringRef()`
15360 /// — narrowing an operation-carrier value through its typed op-name
15361 /// identity yields the canonical cross-boundary string identity in
15362 /// ONE typed composition. [`Self::prefix`] is the Rust-typed peer
15363 /// where the "project to shape" step ([`Self::shape`]) composes
15364 /// with the "read the shape's canonical reader-punctuation" step
15365 /// ([`crate::error::SexpShape::prefix`]) into ONE outer-value
15366 /// projection — sibling of [`Self::iac_forge_tag`] one vocabulary
15367 /// axis over on the cross-crate canonical-form surface.
15368 #[must_use]
15369 pub fn prefix(&self) -> Option<&'static str> {
15370 self.shape().prefix()
15371 }
15372
15373 /// Total structural node count of the outer [`Sexp`] value — one
15374 /// node per outer-algebra arm plus the recursive node count of
15375 /// each child. [`Self::Nil`] and [`Self::Atom`] contribute one
15376 /// node apiece (the outer arm itself); [`Self::List`] contributes
15377 /// one node for the outer arm plus the summed node count of each
15378 /// child element; the four homoiconic wrapper arms
15379 /// ([`Self::Quote`], [`Self::Quasiquote`], [`Self::Unquote`],
15380 /// [`Self::UnquoteSplice`]) each contribute one node for the
15381 /// outer arm plus the node count of the wrapped inner form. The
15382 /// projection is a structural size on the AST — every closed-set
15383 /// arm counts as one, so the count is well-defined on ANY
15384 /// [`Sexp`] value regardless of how it was constructed.
15385 ///
15386 /// Load-bearing arithmetic identities:
15387 /// * `Sexp::Nil.node_count() == 1`
15388 /// * `Sexp::Atom(_).node_count() == 1`
15389 /// * `Sexp::list(items).node_count() == 1 + sum(item.node_count())`
15390 /// * `Sexp::quote(inner).node_count() == 1 + inner.node_count()`
15391 /// * (peer identity for each other quote-family arm)
15392 ///
15393 /// The identities compose: `node_count` is monotone in tree
15394 /// growth (a strictly-larger tree — one containing more arms —
15395 /// has a strictly-larger count), so a resource ceiling keyed on
15396 /// `node_count` bounds the total AST arm-count reachable at that
15397 /// ceiling. A future [`Self::UnquoteSplice`] wrapper appearing
15398 /// inside a list contributes one node for the wrapper AND one
15399 /// node for the outer list arm plus the node count of the
15400 /// wrapper's inner form — the identity holds compositionally
15401 /// through the wrapper's `Box<Sexp>` payload.
15402 ///
15403 /// Consumers so far: [`crate::macro_expand::Expander`]'s
15404 /// `max_expansion_size` ceiling — the RESOURCE-axis peer of the
15405 /// `max_expansion_depth` (recursion length) and
15406 /// `max_cache_entries` (memoization width) ceilings — projects
15407 /// the freshly-applied macro expansion through `node_count` to
15408 /// decide whether the result crosses the "expansion bomb"
15409 /// threshold on the OUTPUT-SIZE axis. A `#[derive(TataraDomain)]`
15410 /// consumer that wants to reject "this macro produced a giant
15411 /// blob" at the expander boundary now inherits the projection
15412 /// mechanically through the ceiling; no per-consumer walker
15413 /// discipline required.
15414 ///
15415 /// The `usize` return shape is the natural resource-count
15416 /// carrier — sibling to `HashMap::len` (the cache-width ceiling
15417 /// consumes) and to the `usize` depth counter (the recursion
15418 /// ceiling consumes) — so all three ceilings compose against a
15419 /// single arithmetic type without cross-cast overhead.
15420 ///
15421 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
15422 /// structural size of a value on the outer [`Sexp`] algebra
15423 /// becomes a first-class TYPED projection rather than a
15424 /// per-consumer hand-rolled walker. THEORY.md §VI.1 — generation
15425 /// over composition; a future ceiling on a subtree count (e.g.
15426 /// "reject any expansion whose LIST arms exceed N") emerges
15427 /// naturally as a peer projection on the same closed-set
15428 /// algebra, not as a separate walker.
15429 #[must_use]
15430 pub fn node_count(&self) -> usize {
15431 match self {
15432 Self::Nil | Self::Atom(_) => 1,
15433 Self::List(items) => 1 + items.iter().map(Self::node_count).sum::<usize>(),
15434 Self::Quote(inner)
15435 | Self::Quasiquote(inner)
15436 | Self::Unquote(inner)
15437 | Self::UnquoteSplice(inner) => 1 + inner.node_count(),
15438 }
15439 }
15440}
15441
15442/// Static panic message for [`Sexp::expect_quote_form`]'s asserted-total
15443/// face of the quote-family projection. Pre-lift this literal appeared
15444/// inline at five `.expect(...)` callsites (`Hash for Sexp`,
15445/// `Display for Sexp`, `domain::sexp_shape`, `domain::sexp_to_json`,
15446/// `interop::iac_forge_tag`); post-lift it lives at ONE named const so a
15447/// regression that drifts the diagnostic at one site silently from the
15448/// others becomes structurally impossible. Sibling to the per-projection
15449/// asserted-total faces across the substrate's typed algebras — the
15450/// message names the invariant the outer pattern proves, not the
15451/// substring grep'able by tests.
15452pub const QUOTE_FAMILY_PROJECTION_INVARIANT: &str =
15453 "matched quote-family variant must project to Some via as_quote_form";
15454
15455/// Closed-set typed identifier for the four homoiconic prefix-wrappers in
15456/// the substrate's `Sexp` algebra — `'x` ([`Sexp::Quote`]), `` `x ``
15457/// ([`Sexp::Quasiquote`]), `,x` ([`Sexp::Unquote`]), `,@x`
15458/// ([`Sexp::UnquoteSplice`]) — paired with the projections each consumer
15459/// surface needs ([`Self::prefix`] for [`crate::ast::Sexp`]'s `Display`
15460/// impl AND the reader's prefix dispatch dual, [`Self::hash_discriminator`]
15461/// for [`crate::ast::Sexp`]'s `Hash` impl, [`Self::as_unquote_form`] for
15462/// the 2-of-4 subset gate the template-substitution surface keys on).
15463///
15464/// Mirror at the homoiconic-prefix-wrapper boundary of the prior-run
15465/// `UnquoteForm` (template-marker subset, 2 variants),
15466/// `CompilerSpecIoStage` (disk-persistence surface),
15467/// `TemplateInvariantKind` (bytecode-runtime surface), `MacroDefHead`
15468/// (macro-definition-head closed set), and `KwargPath` (kwargs-path-shape
15469/// surface) closed-set lifts: those enums key their respective rejection
15470/// or projection variants on a typed identity carried inside the variant's
15471/// data shape; this enum keys the FOUR distinct quote-family rendering /
15472/// hashing / template-substitution sites on a typed marker identity.
15473/// Adding a fifth homoiconic prefix-wrapper (e.g., a hypothetical `,~`
15474/// reverse-unquote) requires extending this enum, which rustc-enforces
15475/// matching at every projection site (`prefix`, `hash_discriminator`,
15476/// `as_unquote_form`, plus `Sexp::as_quote_form`'s match arm) — the closed
15477/// set becomes a TYPE rather than four `&'static str` / `u8` literals that
15478/// could drift independently across `Sexp::Display`'s prefix arm and
15479/// `Sexp::Hash`'s discriminator arm and the reader's prefix dispatch.
15480///
15481/// Subset-gate relationship to [`UnquoteForm`]: the template-substitution
15482/// surface's [`Sexp::as_unquote`] is now `as_quote_form().and_then(|(qf,
15483/// inner)| qf.as_unquote_form().map(|uf| (uf, inner)))` — the 2-of-4
15484/// projection lives at ONE site on this algebra ([`Self::as_unquote_form`])
15485/// rather than being re-derived at every consumer that wants only the
15486/// `{Unquote, UnquoteSplice}` subset. A future enum variant that joins
15487/// the template-substitution subset (e.g. a typed `defalias`-projected
15488/// fifth marker) extends [`UnquoteForm`] AND
15489/// [`Self::as_unquote_form`]'s arm together, with rustc binding the
15490/// extension through the projection's `Option` return type.
15491///
15492/// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
15493/// homoiconic-prefix-wrapper dispatch (the reader's prefix-to-variant
15494/// gate AND the Display impl's variant-to-prefix dual) IS the rust-level
15495/// typed-entry / typed-exit gate, and naming its closed-set identity
15496/// lifts the gate from per-site literal-pair discipline to ONE typed
15497/// enum the substrate's diagnostic promotions hang off of.
15498/// THEORY.md §V.1 — knowable platform; the closed set of homoiconic
15499/// prefix-wrappers becomes a TYPE rather than four `&'static str` / `u8`
15500/// literals scattered across Hash / Display / interop / sexp_shape — a
15501/// typo in any one site is no longer a runtime drift but a compile error
15502/// against the typed projection. THEORY.md §VI.1 — generation over
15503/// composition; the typed enum lands the structural-completeness floor
15504/// for the quote-family surface, parallel to how `UnquoteForm` lands it
15505/// for the template-marker subset and `MacroDefHead` for the
15506/// macro-definition-head surface.
15507#[derive(Debug, Clone, Copy, PartialEq, Eq, tatara_closed_set::DeriveClosedSet)]
15508#[closed_set(via = "prefix", display, generate_unknown = "quote form")]
15509pub enum QuoteForm {
15510 /// `'x` — literal-quote prefix. The `'` marker; the inner expression
15511 /// is NOT subject to macro substitution. Projects to NO
15512 /// `UnquoteForm` (the template-substitution surface ignores quote).
15513 Quote,
15514 /// `` `x `` — quasi-quote prefix. The `` ` `` marker; the inner
15515 /// expression is the template body inside which `,` and `,@` mark
15516 /// substitution points. Projects to NO `UnquoteForm` (a quasi-quote
15517 /// is the substitution SCOPE, not a substitution itself).
15518 Quasiquote,
15519 /// `,x` — single-value substitution. The `,` marker; the inner
15520 /// symbol is substituted with its bound value at template
15521 /// expansion. Projects to `UnquoteForm::Unquote` for the
15522 /// template-substitution surface.
15523 Unquote,
15524 /// `,@x` — list-splice substitution. The `,@` marker; the inner
15525 /// symbol must be bound to a list, whose elements are flattened
15526 /// into the containing list at template expansion. Projects to
15527 /// `UnquoteForm::Splice` for the template-substitution surface.
15528 UnquoteSplice,
15529}
15530
15531impl QuoteForm {
15532 /// The closed set of four homoiconic prefix-wrappers — single
15533 /// source of truth that drives every per-variant projection
15534 /// ([`Self::prefix`] / [`fmt::Display`], [`Self::hash_discriminator`],
15535 /// [`Self::as_unquote_form`], [`Self::iac_forge_tag`],
15536 /// [`Self::sexp_shape`], [`Self::wrap`], and the [`Self::FromStr`]
15537 /// decode sweep keyed on [`Self::prefix`]).
15538 ///
15539 /// Adding a hypothetical fifth homoiconic prefix-wrapper (e.g.
15540 /// a `,~` reverse-unquote, a `,?` conditional-unquote, or a
15541 /// `#'` Common-Lisp function-quote literal) lands at one
15542 /// [`Self::ALL`] entry plus one arm per projection — exhaustively
15543 /// checked by the compiler (the `[Self; 4]` array literal forces
15544 /// the arity) AND by the per-variant truth-table tests below.
15545 ///
15546 /// Sibling closed-set lift to every other typed-shape enum the
15547 /// substrate carries: this crate's own
15548 /// [`crate::error::SexpShape::ALL`] (the twelve reachable outer
15549 /// shapes — superset of this enum's four via [`Self::sexp_shape`]),
15550 /// [`AtomKind::ALL`] (the six atomic-payload kinds — peer axis
15551 /// on the same algebra, also a 6-of-12 carving of `SexpShape`),
15552 /// [`crate::error::UnquoteForm::ALL`] (the two template-substitution
15553 /// markers — proper 2-of-4 subset of THIS enum via
15554 /// [`Self::as_unquote_form`]), and the cross-crate `tatara-process`
15555 /// family (`ConditionKind::ALL`, `ProcessPhase::ALL`,
15556 /// `ProcessSignal::ALL`, `ChannelKind::ALL`, `IntentKind::ALL`,
15557 /// `LifetimeKind::ALL`, `RequestorKind::ALL`, `ReceiptKind::ALL`,
15558 /// …) every one of which paired its typed projection with `ALL`
15559 /// before this lift.
15560 ///
15561 /// Future consumers that compose against `ALL`: LSP / REPL
15562 /// completion for the operator-facing rendered homoiconic prefix
15563 /// (every `'`/`` ` ``/`,`/`,@` substring an authoring tool would
15564 /// surface in a completion list keys on this set's projection
15565 /// through [`Self::prefix`]); `tatara-check` coverage assertions
15566 /// over which quote-family wrappers reach a `Sexp::Display` /
15567 /// `Hash for Sexp` / `as_unquote_form` consumer arm at all — the
15568 /// typed sweep replaces a per-callsite vocabulary of four
15569 /// `&'static str` / `u8` literals; any future audit-trail metric
15570 /// jointly labeled by [`Self::prefix`] (e.g.
15571 /// `tatara_lisp_quote_family_total{prefix="'"}`) — the metric
15572 /// label set IS [`Self::ALL`] mapped through [`Self::prefix`];
15573 /// any future structural rewriter (typed analogue of MLIR's
15574 /// `op.walk<QuoteFormOp>()`) that wants to sweep over every
15575 /// quote-family wrapper in a typed sequence.
15576 pub const ALL: [Self; 4] = [
15577 Self::Quote,
15578 Self::Quasiquote,
15579 Self::Unquote,
15580 Self::UnquoteSplice,
15581 ];
15582
15583 /// Canonical `&'static str` reader-prefix of [`Self::Quote`] —
15584 /// `"'"`. The ONE canonical bytes-payload on the closed-set
15585 /// [`QuoteForm`] algebra shared by [`Self::prefix`]'s [`Self::Quote`]
15586 /// arm AND the [`crate::ast::Sexp`] `Display` arm the arm feeds.
15587 ///
15588 /// Sibling posture to the closed set of per-role `pub const`
15589 /// bytes on the substrate's other closed-set outer algebras:
15590 /// [`crate::error::MacroDefHead::DEFMACRO_KEYWORD`] /
15591 /// [`crate::error::MacroDefHead::DEFPOINT_TEMPLATE_KEYWORD`] /
15592 /// [`crate::error::MacroDefHead::DEFCHECK_KEYWORD`] (per-role
15593 /// head-keyword algebra on the CL macro-definition surface),
15594 /// [`crate::ast::Atom::TRUE_LITERAL`] /
15595 /// [`crate::ast::Atom::FALSE_LITERAL`] (per-role Scheme-bool
15596 /// spelling algebra on the atomic-payload surface),
15597 /// [`crate::macro_expand::MacroParams::REST_MARKER`] /
15598 /// [`crate::macro_expand::MacroParams::OPTIONAL_MARKER`] (per-role
15599 /// CL lambda-list-keyword algebra on the macro-param surface).
15600 ///
15601 /// The `char`-level peer of THIS `&'static str` constant is
15602 /// [`Self::QUOTE_LEAD`] — the first (and only) char of this
15603 /// prefix. The structural round-trip law between the two is
15604 /// `Self::QUOTE_PREFIX.chars().next() == Some(Self::QUOTE_LEAD)`
15605 /// AND `Self::QUOTE_PREFIX.len() ==
15606 /// Self::QUOTE_LEAD.len_utf8()` — the ONE `char` byte
15607 /// composes the ONE `&'static str` prefix. Pinned by
15608 /// `quote_form_per_role_prefixes_route_through_matching_lead_char_for_single_char_prefixes`.
15609 ///
15610 /// A regression that inlines the `"'"` literal at
15611 /// [`Self::prefix`]'s [`Self::Quote`] arm and drifts the constant
15612 /// silently (e.g. an ELisp-compat port of the quote prefix to
15613 /// `#'`, a hypothetical Racket-compat swap to a distinct byte)
15614 /// fails at the algebra's `prefix()` path-uniformity pin
15615 /// (`quote_form_prefix_routes_through_typed_per_role_constants`)
15616 /// rather than at silent reader-family drift where the
15617 /// [`Sexp::Display`] round-trip breaks.
15618 pub const QUOTE_PREFIX: &'static str = "'";
15619
15620 /// Canonical `&'static str` reader-prefix of [`Self::Quasiquote`]
15621 /// — `` "`" ``. Sibling of [`Self::QUOTE_PREFIX`] on the closed-set
15622 /// per-role quote-family prefix-bytes axis; see
15623 /// [`Self::QUOTE_PREFIX`] for the algebra-level round-trip +
15624 /// disjointness contracts every sibling shares. The `char`-level
15625 /// peer is [`Self::QUASIQUOTE_LEAD`].
15626 pub const QUASIQUOTE_PREFIX: &'static str = "`";
15627
15628 /// Canonical `&'static str` reader-prefix of [`Self::Unquote`] —
15629 /// `","`. Sibling of [`Self::QUOTE_PREFIX`] on the closed-set
15630 /// per-role quote-family prefix-bytes axis; see
15631 /// [`Self::QUOTE_PREFIX`] for the algebra-level round-trip +
15632 /// disjointness contracts every sibling shares. The `char`-level
15633 /// peer is [`Self::UNQUOTE_LEAD`] (shared with
15634 /// [`Self::UNQUOTE_SPLICE_PREFIX`]'s lead byte — see the
15635 /// [`Self::UNQUOTE_LEAD`] docstring for the shared-lead-char
15636 /// discipline the two prefixes disambiguate on).
15637 pub const UNQUOTE_PREFIX: &'static str = ",";
15638
15639 /// Canonical `&'static str` reader-prefix of [`Self::UnquoteSplice`]
15640 /// — `",@"`. The ONLY two-char prefix on the closed set; every
15641 /// other [`Self::PREFIXES`] entry is a single `char` rendered as
15642 /// `&'static str`. Sibling of [`Self::QUOTE_PREFIX`] on the closed-
15643 /// set per-role quote-family prefix-bytes axis.
15644 ///
15645 /// Structural composition law: `Self::UNQUOTE_SPLICE_PREFIX ==
15646 /// format!("{}{}", Self::UNQUOTE_LEAD, Self::SPLICE_DISCRIMINATOR)`
15647 /// — the two-char prefix decomposes cleanly into the ONE shared
15648 /// lead byte [`Self::UNQUOTE_LEAD`] + the ONE splice-promotion
15649 /// discriminator [`Self::SPLICE_DISCRIMINATOR`], both `char`-level
15650 /// constants on this algebra. Pinned by
15651 /// `quote_form_unquote_splice_prefix_constant_composes_from_unquote_lead_and_splice_discriminator`
15652 /// (byte-level composition through the per-role `pub const`) as a
15653 /// section-for-retraction peer of the pre-existing
15654 /// `quote_form_unquote_splice_prefix_composes_from_unquote_lead_and_splice_discriminator`
15655 /// pin (byte-level composition through the [`Self::prefix`]
15656 /// method).
15657 pub const UNQUOTE_SPLICE_PREFIX: &'static str = ",@";
15658
15659 /// The closed-set forced-arity ALL array over the quote-family
15660 /// reader-prefix `&'static str` bytes in canonical declaration
15661 /// order matching [`Self::ALL`] element-wise. Sibling posture to
15662 /// [`crate::error::MacroDefHead::KEYWORDS`] (`[&'static str; 3]`
15663 /// on the CL macro-definition head algebra),
15664 /// [`crate::ast::Atom::BOOL_LITERALS`] (`[&'static str; 2]` on the
15665 /// Scheme-bool spelling algebra), and
15666 /// [`crate::macro_expand::MacroParams::LAMBDA_LIST_KEYWORDS`]
15667 /// (`[&'static str; 2]` on the CL lambda-list-keyword algebra) —
15668 /// every closed-set outer projection on the substrate now pins
15669 /// its canonical bytes at ONE `pub const` per role plus an ALL
15670 /// array for family-wide consumers.
15671 ///
15672 /// Adding a hypothetical fifth homoiconic prefix (a `,~`
15673 /// reverse-unquote, a `,?` conditional-unquote, a `#'` Common-
15674 /// Lisp function-quote) extends [`Self::ALL`] AND
15675 /// [`Self::PREFIXES`] AND [`Self::prefix`]'s arm AND one new
15676 /// per-role `pub const` in lockstep — rustc's forced-arity check
15677 /// on `[&'static str; N]` fails compilation if either ALL array
15678 /// grows without the other.
15679 ///
15680 /// Future consumers that compose against [`Self::PREFIXES`]:
15681 /// - LSP / REPL completion for the operator-facing rendered
15682 /// homoiconic prefix bar — the completion set IS
15683 /// [`Self::PREFIXES`] rather than four hand-enumerated
15684 /// `&'static str` literals per completion provider.
15685 /// - `tatara-check` coverage assertions that sweep workspace
15686 /// `.lisp` files for every canonical quote-family prefix — the
15687 /// typed sweep replaces per-consumer inline enumeration of the
15688 /// four literals.
15689 /// - Any future audit-trail metric jointly labeled by
15690 /// [`Self::prefix`] (e.g.
15691 /// `tatara_lisp_quote_family_total{prefix="'"}`) — the metric
15692 /// label set IS [`Self::PREFIXES`] mapped through
15693 /// [`Self::prefix`].
15694 pub const PREFIXES: [&'static str; 4] = [
15695 Self::QUOTE_PREFIX,
15696 Self::QUASIQUOTE_PREFIX,
15697 Self::UNQUOTE_PREFIX,
15698 Self::UNQUOTE_SPLICE_PREFIX,
15699 ];
15700
15701 /// Canonical `&'static str` prefix that paired with the variant
15702 /// renders the homoiconic form — [`Self::QUOTE_PREFIX`] for
15703 /// [`Self::Quote`], [`Self::QUASIQUOTE_PREFIX`] for
15704 /// [`Self::Quasiquote`], [`Self::UNQUOTE_PREFIX`] for
15705 /// [`Self::Unquote`], [`Self::UNQUOTE_SPLICE_PREFIX`] for
15706 /// [`Self::UnquoteSplice`]. Threaded through
15707 /// [`crate::ast::Sexp`]'s `Display` impl so the per-variant prefix
15708 /// rendering lives at ONE site on this algebra rather than four
15709 /// inline literal strings across the Display arms.
15710 ///
15711 /// Post-lift the four arms route through the per-role `pub const`
15712 /// bytes on the closed-set [`QuoteForm`] algebra rather than
15713 /// inline `&'static str` literals — so a rename of ONE canonical
15714 /// prefix bytes (an ELisp-compat port of `Quote` to `"#'"`, a
15715 /// hypothetical Racket-compat swap of `Quasiquote`, a Common-Lisp-
15716 /// standard rename of `UnquoteSplice` to `",."`) lands as ONE
15717 /// edit to the matching `pub const` — every downstream consumer
15718 /// that binds to the algebra ([`crate::ast::Sexp`]'s `Display`
15719 /// impl, the reader's tokenizer round-trip law, the future
15720 /// canonical-form taggers) inherits the rename mechanically.
15721 ///
15722 /// Structural dual of the reader's [`crate::reader::read_quoted`]
15723 /// dispatch: the reader maps prefix-tokens to `Sexp::{Quote,
15724 /// Quasiquote, Unquote, UnquoteSplice}` constructors; this method
15725 /// maps the typed `QuoteForm` marker back to its canonical prefix
15726 /// string. Adding a fifth prefix extends both sides — the reader's
15727 /// tokenizer + dispatch AND this method — with rustc enforcing
15728 /// the pair through the closed-set enum. Round-trip:
15729 /// `read(format!("{}{inner}", qf.prefix()))` produces the
15730 /// `Sexp::*` variant matching `qf`, by construction.
15731 ///
15732 /// The `&'static str` lifetime is load-bearing: it lets every
15733 /// consumer (Display arm, future format strings, future interop
15734 /// canonical-form taggers) project through this method without
15735 /// an allocation, parallel to how [`UnquoteForm::marker`]
15736 /// projects its 2-of-4 subset surface.
15737 #[must_use]
15738 pub fn prefix(self) -> &'static str {
15739 match self {
15740 Self::Quote => Self::QUOTE_PREFIX,
15741 Self::Quasiquote => Self::QUASIQUOTE_PREFIX,
15742 Self::Unquote => Self::UNQUOTE_PREFIX,
15743 Self::UnquoteSplice => Self::UNQUOTE_SPLICE_PREFIX,
15744 }
15745 }
15746
15747 /// Canonical `'` LEAD `char` of [`Self::Quote`]'s [`Self::prefix`]
15748 /// (`"'"`) — the ONE canonical `char` on the [`QuoteForm`] algebra the
15749 /// substrate's Quote-family single-quote lead-byte disjointness
15750 /// contract binds to.
15751 ///
15752 /// Sibling posture to the closed set of `pub const` reader-punctuation
15753 /// canonical `char` bytes on the substrate:
15754 /// [`Self::SPLICE_DISCRIMINATOR`] (`'@'`),
15755 /// [`crate::ast::Atom::STR_DELIMITER`] (`'"'`),
15756 /// [`crate::ast::Atom::STR_ESCAPE_LEAD`] (`'\\'`),
15757 /// [`crate::ast::Atom::KEYWORD_MARKER_LEAD`] (`':'`),
15758 /// [`crate::ast::Atom::BOOL_LITERAL_LEAD`] (`'#'`),
15759 /// [`crate::ast::Sexp::LIST_OPEN`] (`'('`),
15760 /// [`crate::ast::Sexp::LIST_CLOSE`] (`')'`),
15761 /// [`crate::ast::Sexp::COMMENT_LEAD`] (`';'`),
15762 /// [`crate::ast::Sexp::COMMENT_TERM`] (`'\n'`) — every canonical per-
15763 /// role byte the reader's tokenizer specialises on is a `pub const`
15764 /// on its owning closed-set algebra. This constant closes the Quote-
15765 /// family single-quote lead byte at the SAME algebra as the
15766 /// [`Self::lead_char`] projection (whose [`Self::Quote`] arm returns
15767 /// this byte) AND the [`Self::from_lead_char`] inverse (whose match
15768 /// arm decodes this byte back to [`Self::Quote`]).
15769 ///
15770 /// Structural round-trip contract:
15771 /// `Self::from_lead_char(Self::QUOTE_LEAD) == Some(Self::Quote)`
15772 /// AND `Self::Quote.lead_char() == Self::QUOTE_LEAD` — pinned by
15773 /// `quote_form_lead_constants_round_trip_through_lead_char_projections`.
15774 /// A regression that drifts EITHER the constant OR the paired
15775 /// projection surfaces at the pin rather than at a silent tokenizer
15776 /// drift where `'foo` classifies as a bare atom instead of
15777 /// [`crate::ast::Sexp::Quote`].
15778 ///
15779 /// Disjointness contract: `QUOTE_LEAD`'s byte MUST differ from
15780 /// [`Self::QUASIQUOTE_LEAD`], [`Self::UNQUOTE_LEAD`],
15781 /// [`Self::SPLICE_DISCRIMINATOR`],
15782 /// [`crate::ast::Atom::STR_DELIMITER`],
15783 /// [`crate::ast::Atom::STR_ESCAPE_LEAD`],
15784 /// [`crate::ast::Atom::KEYWORD_MARKER_LEAD`],
15785 /// [`crate::ast::Atom::BOOL_LITERAL_LEAD`],
15786 /// [`crate::ast::Sexp::LIST_OPEN`], [`crate::ast::Sexp::LIST_CLOSE`],
15787 /// [`crate::ast::Sexp::COMMENT_LEAD`], and
15788 /// [`crate::ast::Sexp::COMMENT_TERM`] — every other closed-set outer-
15789 /// marker byte the reader's tokenizer specialises on. A collision
15790 /// would silently break the reader's outer dispatch. Pinned by
15791 /// `quote_form_lead_constants_distinct_from_every_other_algebra_marker_char`.
15792 ///
15793 /// Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
15794 /// (Quote-family single-quote lead byte, canonical `'\''`) pairing
15795 /// binds at ONE constant on the closed-set [`QuoteForm`] algebra
15796 /// regardless of which paired projection reaches in. THEORY.md §V.1
15797 /// — knowable platform; the canonical Quote-family lead byte becomes
15798 /// a TYPE-level constant on the substrate algebra rather than an
15799 /// inline `'\''` char literal at [`Self::lead_char`]'s [`Self::Quote`]
15800 /// arm AND at [`Self::from_lead_char`]'s decode arm.
15801 pub const QUOTE_LEAD: char = '\'';
15802
15803 /// Canonical `` ` `` LEAD `char` of [`Self::Quasiquote`]'s
15804 /// [`Self::prefix`] (`` "`" ``) — sibling of [`Self::QUOTE_LEAD`] on
15805 /// the closed-set quote-family lead-byte axis. See
15806 /// [`Self::QUOTE_LEAD`] for the algebra-level round-trip +
15807 /// disjointness contracts every sibling shares. Bound by
15808 /// [`Self::lead_char`]'s [`Self::Quasiquote`] arm AND
15809 /// [`Self::from_lead_char`]'s decode arm.
15810 pub const QUASIQUOTE_LEAD: char = '`';
15811
15812 /// Canonical `,` LEAD `char` SHARED by [`Self::Unquote`]'s
15813 /// [`Self::prefix`] (`","`) AND [`Self::UnquoteSplice`]'s
15814 /// [`Self::prefix`] (`",@"`) — the splice's two-char prefix opens
15815 /// with this byte and disambiguates on the peek-then-consume
15816 /// [`Self::SPLICE_DISCRIMINATOR`] second char inside
15817 /// [`crate::reader::tokenize`]. Sibling of [`Self::QUOTE_LEAD`] on
15818 /// the closed-set quote-family lead-byte axis; see
15819 /// [`Self::QUOTE_LEAD`] for the algebra-level round-trip +
15820 /// disjointness contracts every sibling shares. Bound by
15821 /// [`Self::lead_char`]'s `Self::Unquote | Self::UnquoteSplice` merged
15822 /// arm AND [`Self::from_lead_char`]'s decode arm.
15823 ///
15824 /// Composition identity with [`Self::SPLICE_DISCRIMINATOR`]:
15825 /// `format!("{}{}", Self::UNQUOTE_LEAD, Self::SPLICE_DISCRIMINATOR)
15826 /// == Self::UnquoteSplice.prefix()` — the two byte-level constants
15827 /// on the closed-set [`QuoteForm`] algebra compose the ONLY two-char
15828 /// [`Self::prefix`] in the closed set. Pinned by
15829 /// `quote_form_unquote_splice_prefix_composes_from_unquote_lead_and_splice_discriminator`.
15830 /// A regression that renames EITHER constant without touching the
15831 /// paired [`Self::UnquoteSplice`]'s [`Self::prefix`] arm surfaces
15832 /// here rather than as a silent `,@` reader drift.
15833 pub const UNQUOTE_LEAD: char = ',';
15834
15835 /// The closed-set forced-arity ALL array over the quote-family
15836 /// DISTINCT reader-lead-byte `char`s in canonical declaration
15837 /// order matching [`Self::ALL`]'s three-of-four distinct-lead-
15838 /// byte projection through [`Self::lead_char`] — [`Self::QUOTE_LEAD`]
15839 /// (`'\''` — the [`Self::Quote`] lead byte), [`Self::QUASIQUOTE_LEAD`]
15840 /// (`` '`' `` — the [`Self::Quasiquote`] lead byte),
15841 /// [`Self::UNQUOTE_LEAD`] (`','` — the SHARED lead byte of BOTH
15842 /// [`Self::Unquote`] AND [`Self::UnquoteSplice`], with the splice
15843 /// promotion living at the reader's peek-then-consume
15844 /// [`Self::SPLICE_DISCRIMINATOR`] second-char arm rather than at a
15845 /// distinct lead byte).
15846 ///
15847 /// The `[char; 3]` cardinality (vs the peer [`Self::PREFIXES`]
15848 /// `[&'static str; 4]`) IS the structural axis distinguishing the
15849 /// DISTINCT-lead-byte sub-vocabulary from the PER-VARIANT-prefix
15850 /// sub-vocabulary — three-of-four distinct-lead-byte collapse is
15851 /// definitional (only [`Self::UnquoteSplice`]'s two-char `,@`
15852 /// prefix shares its lead byte with a sibling variant; every other
15853 /// variant owns its lead byte outright). The shape asymmetry
15854 /// between the two ALL arrays encodes the shared-lead-byte
15855 /// collapse identity on the closed-set [`QuoteForm`] algebra at
15856 /// the type-system level: a consumer that reaches for
15857 /// [`Self::LEADS`] reads the distinct-lead-byte cardinality
15858 /// directly off the array's forced arity rather than through a
15859 /// per-consumer `HashSet`-then-count over [`Self::PREFIXES`]'s
15860 /// first chars.
15861 ///
15862 /// Sibling posture to [`Self::PREFIXES`] (`[&'static str; 4]` on
15863 /// the per-variant reader-prefix axis) AND [`Self::IAC_FORGE_TAGS`]
15864 /// (`[&'static str; 4]` on the per-variant canonical-form tag
15865 /// axis) — those two ALL arrays close the per-variant axes of the
15866 /// outer-tokenizer `QuoteForm` closed set; this ALL array closes
15867 /// the peer DISTINCT-lead-byte axis at the SHAPE-ASYMMETRIC
15868 /// `[char; N]` cardinality. Also sibling-shape to
15869 /// [`crate::ast::Sexp::LIST_DELIMITERS`] (`[char; 2]` on the outer-
15870 /// structural paired-delimiter axis), [`Atom::SELF_ESCAPE_TABLE`]
15871 /// (`[char; 2]` on the inner-Str-payload self-escape axis), and
15872 /// [`Atom::BOOL_LITERALS`] (`[&'static str; 2]` on the Scheme-bool
15873 /// spelling axis) — every closed-set outer projection on the
15874 /// substrate now pins its canonical bytes at ONE `pub const` per
15875 /// role plus an ALL array for family-wide consumers.
15876 ///
15877 /// Adding a hypothetical fifth homoiconic prefix with a DISTINCT
15878 /// lead byte (a `~` reverse-unquote, a `?` conditional-unquote, a
15879 /// `#` reader-macro-lead) extends [`Self::ALL`] AND
15880 /// [`Self::PREFIXES`] AND [`Self::LEADS`] AND [`Self::lead_char`]'s
15881 /// arm AND [`Self::from_lead_char`]'s arm AND one new per-role
15882 /// `pub const` in lockstep — rustc's forced-arity check on
15883 /// `[char; N]` fails compilation if the LEADS array grows without
15884 /// the paired algebra constant, and the paired PREFIXES /
15885 /// IAC_FORGE_TAGS arrays extend by ONE row each in lockstep. A
15886 /// fifth prefix that SHARES its lead byte with an existing variant
15887 /// (like the splice's `,@` sharing with unquote's `,`) leaves
15888 /// [`Self::LEADS`]'s cardinality unchanged — the DISTINCT-lead-
15889 /// byte set is invariant under such an extension, closing the
15890 /// splice-family promotion pattern at the ALL-array level.
15891 ///
15892 /// Future consumers that compose against [`Self::LEADS`]:
15893 /// - LSP / REPL completion for the operator-facing reader-entry
15894 /// lead-byte set — the completion set IS [`Self::LEADS`] rather
15895 /// than three hand-enumerated `char` literals per completion
15896 /// provider.
15897 /// - The reader's outer tokenizer pre-match check that gates the
15898 /// quote-family dispatch — the check IS
15899 /// `Self::LEADS.contains(&ch)` rather than three inline
15900 /// `ch == Self::QUOTE_LEAD || ch == Self::QUASIQUOTE_LEAD ||
15901 /// ch == Self::UNQUOTE_LEAD` disjuncts, and the sweep binds
15902 /// through the ALL array's forced arity.
15903 /// - A hypothetical `tatara_lisp_quote_family_lead_total{lead="'"}`
15904 /// metric surface — the label-set generator sweeps
15905 /// [`Self::LEADS`] verbatim rather than re-typing the three
15906 /// distinct-lead bytes inline at each recorder.
15907 /// - Any future syntax-highlighter / structural editor that needs
15908 /// the reader-entry lead-byte set for classification — the
15909 /// editor's per-char classifier binds through [`Self::LEADS`]
15910 /// rather than three parallel `char`-literal patterns.
15911 ///
15912 /// Theory anchor: THEORY.md §III — the typescape; the three
15913 /// distinct quote-family reader-lead bytes now bind at ONE typed
15914 /// `[char; 3]` array on the closed-set [`QuoteForm`] algebra
15915 /// rather than as three inline algebra-constant enumerations at
15916 /// every consumer that iterates the distinct-lead-byte sub-
15917 /// vocabulary. THEORY.md §V.1 — knowable platform; the distinct-
15918 /// lead-byte sub-vocabulary becomes load-bearing typed data on
15919 /// the closed-set outer [`QuoteForm`] algebra. THEORY.md §VI.1 —
15920 /// generation over composition; the shared-lead-byte collapse
15921 /// identity (four variants → three distinct lead bytes)
15922 /// composes at ONE typed ALL array whose shape-asymmetric
15923 /// cardinality (3 vs [`Self::PREFIXES`]'s 4) IS the collapse
15924 /// invariant carried at the type-system level.
15925 pub const LEADS: [char; 3] = [Self::QUOTE_LEAD, Self::QUASIQUOTE_LEAD, Self::UNQUOTE_LEAD];
15926
15927 /// Canonical FIRST-char of [`Self::prefix`] — [`Self::QUOTE_LEAD`]
15928 /// for [`Self::Quote`], [`Self::QUASIQUOTE_LEAD`] for
15929 /// [`Self::Quasiquote`], [`Self::UNQUOTE_LEAD`] for BOTH
15930 /// [`Self::Unquote`] AND [`Self::UnquoteSplice`] (the splice's two-
15931 /// char `,@` prefix shares its lead byte with bare unquote and
15932 /// disambiguates on the peek-then-consume `@` second char inside
15933 /// [`crate::reader::tokenize`]).
15934 /// The three-of-four collapse onto three distinct lead chars is
15935 /// structurally fixed — the reader's outer tokenizer dispatch
15936 /// selects between quote-family entry and every non-quote-family
15937 /// arm on lead char alone, with the `,`-vs-`,@` disambiguation
15938 /// falling out of the reader's second-char peek.
15939 ///
15940 /// Structural dual of [`Self::from_lead_char`]: this method projects
15941 /// the closed-set marker to its canonical reader-punctuation lead
15942 /// char; the sibling projects the lead char back to the DEFAULT
15943 /// marker on that char (`,` → [`Self::Unquote`], with the splice
15944 /// promotion living at the reader's peek arm rather than at
15945 /// [`Self::from_lead_char`]'s decode). Every variant round-trips
15946 /// through the composition `Self::from_lead_char(qf.lead_char())`,
15947 /// with the `{Unquote, UnquoteSplice}` two-of-four collapsing onto
15948 /// `Some(Unquote)` per the shared-lead-char structural identity.
15949 ///
15950 /// The `const` qualifier is load-bearing: [`crate::reader::tokenize`]
15951 /// binds its outer-match quote-family dispatch to this projection
15952 /// via a pre-match `Self::from_lead_char` check, and future consumer
15953 /// sites (e.g. `const` array literals of every reader-recognized
15954 /// lead byte the tokenizer could dispatch on, LSP completion
15955 /// generators that pre-materialize the lead-char set) route through
15956 /// this projection at compile time. Sibling posture to
15957 /// [`crate::ast::Atom::STR_DELIMITER`] one axis over on the same
15958 /// closed-set-lead-char algebra — that constant is the ONE `char`
15959 /// the `Token::Str` open/close/self-escape/bare-atom-terminator
15960 /// FOUR sites in the reader pair with; this method is the ONE
15961 /// projection the `Token::Quoted(QuoteForm)` outer-dispatch AND
15962 /// the same bare-atom-terminator disjunct pair with across FOUR
15963 /// per-variant lead chars.
15964 ///
15965 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
15966 /// reader's per-char quote-family dispatch IS the typed-entry gate,
15967 /// and lifting its (char, `QuoteForm`) pairing to ONE projection
15968 /// method plus one inverse (see [`Self::from_lead_char`]) closes
15969 /// the tokenizer's outer-arm entry surface onto the closed-set
15970 /// algebra rather than four inline `char` literals scattered across
15971 /// three tokenizer arms (`'\''` / `` '`' `` / `','` outer-match arms)
15972 /// AND three bare-atom-terminator disjuncts (`ch == '\''` / `ch ==
15973 /// '`'` / `ch == ','`). THEORY.md §V.1 — knowable platform; the
15974 /// closed set of quote-family lead chars becomes a TYPE (the
15975 /// enum's arms projected through this method) rather than four
15976 /// literal `char` values scattered across the reader's outer
15977 /// dispatch AND its bare-atom terminator. THEORY.md §VI.1 —
15978 /// generation over composition; a fifth homoiconic prefix
15979 /// (hypothetical `,~` reverse-unquote, `#'` function-quote,
15980 /// `#[…]` vector-quote) extends [`Self`] AND this method AND
15981 /// [`Self::from_lead_char`] AND the tokenizer's pre-match check
15982 /// in lockstep, with rustc binding the extension through
15983 /// exhaustiveness over the closed enum.
15984 #[must_use]
15985 pub const fn lead_char(self) -> char {
15986 match self {
15987 Self::Quote => Self::QUOTE_LEAD,
15988 Self::Quasiquote => Self::QUASIQUOTE_LEAD,
15989 Self::Unquote | Self::UnquoteSplice => Self::UNQUOTE_LEAD,
15990 }
15991 }
15992
15993 /// Inverse of [`Self::lead_char`] on the three-of-four distinct
15994 /// lead chars — `'\''` decodes to `Some(Self::Quote)`, `` '`' ``
15995 /// decodes to `Some(Self::Quasiquote)`, `','` decodes to
15996 /// `Some(Self::Unquote)` (the DEFAULT variant on the shared `,`
15997 /// lead char; the two-char `,@` splice promotion lives at
15998 /// [`crate::reader::tokenize`]'s peek-then-consume `@` disambiguator
15999 /// rather than at this decode). Every other `char` yields `None` —
16000 /// the closed-set guarantee on [`Self`] AND on the tokenizer's
16001 /// outer-arm set (whitespace, `(`, `)`, [`crate::ast::Atom::STR_DELIMITER`],
16002 /// `;`, bare atom) ensures the four typed markers partition the
16003 /// three distinct lead chars injectively against every other
16004 /// tokenizer-recognized entry char.
16005 ///
16006 /// ONE consumer entrypoint the reader's `tokenize` binds against:
16007 /// the outer-match quote-family dispatch was pre-lift a hand-rolled
16008 /// three-arm cascade (`'\''` / `` '`' `` / `','`) with per-arm
16009 /// `Token::Quoted(QuoteForm::*)` construction and a fourth
16010 /// `Token::Quoted(QuoteForm::UnquoteSplice)` arm buried inside the
16011 /// `','`-arm's peek branch; post-lift the tokenizer pre-checks
16012 /// `Self::from_lead_char(c)` before the outer match, promotes the
16013 /// returned `Self::Unquote` to `Self::UnquoteSplice` on second-char
16014 /// `@`, and emits ONE `Token::Quoted(final_qf)` — the (lead char,
16015 /// [`Self`] marker) pairing binds at ONE site on the closed-set
16016 /// algebra rather than at three inline `char` literals across
16017 /// three outer-match arms. The bare-atom terminator disjunct at
16018 /// the reader's `Token::Atom` accumulator loop routes through
16019 /// `Self::from_lead_char(ch).is_some()` so the three
16020 /// quote-family-lead disjuncts (`ch == '\''` / `ch == '`'` /
16021 /// `ch == ','`) collapse to ONE gate — a regression that drifts
16022 /// one bare-atom-terminator disjunct from the outer-dispatch's
16023 /// quote-family arm becomes structurally impossible because
16024 /// there is exactly ONE decode both sites consume.
16025 ///
16026 /// The two-of-four collapse onto `Some(Self::Unquote)` for the
16027 /// `,` lead char is INTENTIONAL: `Self::UnquoteSplice` has NO
16028 /// distinct lead char; the tokenizer must see two consecutive
16029 /// chars (`,` then `@`) to promote the decoded `Self::Unquote`
16030 /// to `Self::UnquoteSplice`. Placing the promotion at the
16031 /// reader's peek arm rather than at this decode keeps the
16032 /// (char → marker) projection at the closed-set algebra's
16033 /// character-boundary surface (one char in, one variant out)
16034 /// and the (two-char sequence → splice) promotion at the
16035 /// tokenizer's streaming surface (peek and consume the second
16036 /// char). This split parallels the reader's split of `Token::Str`
16037 /// into open-delimiter dispatch ([`crate::ast::Atom::STR_DELIMITER`])
16038 /// AND inner-payload accumulation — the closed-set char algebra
16039 /// decodes the entry char; the streaming reader handles multi-
16040 /// char follow-through.
16041 ///
16042 /// Sibling to [`crate::ast::Atom::from_lexeme`] one axis over on
16043 /// the same typed-entry family — that method decodes a bare-atom
16044 /// lexeme into a typed [`crate::ast::Atom`] variant; this method
16045 /// decodes a single lead char into a typed [`Self`] variant. Both
16046 /// map the reader's per-char / per-lexeme classification surface
16047 /// onto the substrate's closed-set algebra so the reader's outer
16048 /// dispatch binds through ONE typed decode rather than through
16049 /// scattered per-arm `char` / `&str` literal patterns.
16050 ///
16051 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
16052 /// reader's per-char quote-family classification IS the typed-entry
16053 /// gate. THEORY.md §V.1 — knowable platform; the reader's outer
16054 /// dispatch AND the bare-atom terminator each route through ONE
16055 /// typed decode against the closed-set algebra rather than through
16056 /// three (or four) parallel `char`-literal patterns that could
16057 /// drift independently — a regression that renames one lead char
16058 /// without updating the sibling site fails at rustc / test time
16059 /// rather than as a silent tokenizer drift.
16060 #[must_use]
16061 pub const fn from_lead_char(c: char) -> Option<Self> {
16062 match c {
16063 Self::QUOTE_LEAD => Some(Self::Quote),
16064 Self::QUASIQUOTE_LEAD => Some(Self::Quasiquote),
16065 Self::UNQUOTE_LEAD => Some(Self::Unquote),
16066 _ => None,
16067 }
16068 }
16069
16070 /// Canonical SECOND char of [`Self::UnquoteSplice`]'s two-char `,@`
16071 /// [`Self::prefix`] — the ONE `'@'` byte the reader's peek-then-
16072 /// consume splice-promotion arm inside [`crate::reader::tokenize`]
16073 /// disambiguates on. Sibling posture to [`crate::ast::Atom::STR_DELIMITER`]
16074 /// (one-char Str-payload delimiter shared across four `"`-round-
16075 /// trip sites) AND to [`crate::ast::Sexp::COMMENT_LEAD`] (one-char
16076 /// line-comment lead shared across two `;`-boundary sites) — those
16077 /// two constants project a single byte onto their respective closed-
16078 /// set algebras (`Atom` and outer-`Sexp`); this constant projects
16079 /// the single byte that composes the `,` [`Self::Unquote`]
16080 /// [`Self::lead_char`] into the two-char [`Self::UnquoteSplice`]
16081 /// [`Self::prefix`] onto the same closed-set [`Self`] algebra.
16082 ///
16083 /// The `,@` splice is the ONLY multi-char [`Self::prefix`] in the
16084 /// closed set — [`Self::Quote`] / [`Self::Quasiquote`] / [`Self::Unquote`]
16085 /// each render as a single [`Self::lead_char`] byte; only
16086 /// [`Self::UnquoteSplice`] appends this discriminator. The
16087 /// composition [`Self::Unquote::prefix()`] + `SPLICE_DISCRIMINATOR`
16088 /// == [`Self::UnquoteSplice::prefix()`] IS the structural identity
16089 /// the reader's peek arm depends on — pinned by
16090 /// `quote_form_unquote_splice_prefix_composes_from_unquote_prefix_and_splice_discriminator`.
16091 /// A future hypothetical fifth homoiconic prefix with its own two-
16092 /// char extension (e.g. `,~` reverse-unquote via a `~` discriminator,
16093 /// `#'` function-quote via a `'` discriminator) extends [`Self`]
16094 /// AND a per-variant promotion peer (extending
16095 /// [`Self::promote_via_next_char`]) in lockstep — rustc binds the
16096 /// extension through exhaustiveness over the closed enum.
16097 ///
16098 /// The `const` qualifier is load-bearing: [`Self::promote_via_next_char`]'s
16099 /// body binds through this constant in a `const fn` context so the
16100 /// reader's peek arm consumes the promotion table at compile time.
16101 /// Sibling posture to [`crate::ast::Atom::STR_DELIMITER`],
16102 /// [`crate::ast::Atom::KEYWORD_MARKER`], [`crate::ast::Sexp::LIST_OPEN`],
16103 /// [`crate::ast::Sexp::LIST_CLOSE`], [`crate::ast::Sexp::COMMENT_LEAD`] —
16104 /// every canonical reader-punctuation constant on the substrate is a
16105 /// `pub const` on its owning closed-set algebra.
16106 ///
16107 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
16108 /// reader's two-char splice-promotion gate IS the typed-entry gate
16109 /// on the `,@` boundary, and lifting the `@` discriminator to ONE
16110 /// canonical byte on the closed-set algebra closes the gate's
16111 /// entry-char identity onto the algebra rather than at an inline
16112 /// `char` literal at the reader's peek arm. THEORY.md §V.1 —
16113 /// knowable platform; the splice-promotion discriminator becomes a
16114 /// TYPED byte on the substrate algebra rather than an inline `'@'`
16115 /// scattered across the reader — a regression that renames the byte
16116 /// without updating the sibling promotion peer fails at rustc /
16117 /// test time rather than as a silent tokenizer drift where `,@xs`
16118 /// forms silently degrade to `,` + `@xs` two-token sequences.
16119 pub const SPLICE_DISCRIMINATOR: char = '@';
16120
16121 /// Closed-set forced-arity ALL array over the canonical promotion
16122 /// triples on the substrate's quote-family algebra —
16123 /// `(head_variant, next_char_discriminator, promoted_variant)` for
16124 /// every `(head, next)` pair whose [`Self::promote_via_next_char`]
16125 /// projection yields `Some(promoted)`. Post-lift the promotion
16126 /// algebra's canonical triples bind at ONE typed
16127 /// `[(Self, char, Self); N]` array on the closed-set [`QuoteForm`]
16128 /// algebra rather than at zero-primitive-plus-inline-arm-literals
16129 /// inside [`Self::promote_via_next_char`]'s match body.
16130 ///
16131 /// The substrate's current promotion algebra is the singleton
16132 /// `[(Self::Unquote, Self::SPLICE_DISCRIMINATOR, Self::UnquoteSplice)]`
16133 /// — the ONLY (variant, next-char) → longer-variant mapping the
16134 /// reader's peek-then-consume `@` promotion arm depends on. Its
16135 /// forced-arity `1` is INTENTIONAL and load-bearing: [`Self::UnquoteSplice`]
16136 /// is the ONLY variant with a two-char [`Self::prefix`], so the
16137 /// promotion table has exactly ONE `Some` arm and every other
16138 /// pairing rejects — the closed set of promotions is the singleton
16139 /// `{(Unquote, '@') → UnquoteSplice}` on the `Self × char →
16140 /// Option<Self>` product. Pinned bit-for-bit by
16141 /// `quote_form_promotions_has_expected_cardinality` (forced-arity)
16142 /// AND `quote_form_promotions_pin_legacy_splice_promotion_triple`
16143 /// (identity of the singleton entry). A future fifth homoiconic
16144 /// prefix with its own two-char extension (a hypothetical `,~`
16145 /// reverse-unquote via a `~` discriminator, a `#'` function-quote
16146 /// via a `'` discriminator, a `,?` conditional-unquote via a `?`
16147 /// discriminator) extends [`Self::ALL`] AND appends ONE new
16148 /// promotion triple to [`Self::PROMOTIONS`] AND extends
16149 /// [`Self::promote_via_next_char`]'s match body in lockstep —
16150 /// rustc's forced-arity check on the `[(Self, char, Self); N]`
16151 /// array fails compilation if the array's cardinality grows
16152 /// without a matching arm on the projection method.
16153 ///
16154 /// Sibling posture to the closed-set forced-arity ALL arrays across
16155 /// the substrate's [`QuoteForm`] algebra — [`Self::ALL`]
16156 /// (`[Self; 4]` — the closed set of variants),
16157 /// [`Self::PREFIXES`] (`[&'static str; 4]` — the reader-prefix
16158 /// `&'static str` axis), [`Self::LABELS`] (`[&'static str; 4]` —
16159 /// the diagnostic-label `&'static str` axis),
16160 /// [`Self::IAC_FORGE_TAGS`] (`[&'static str; 4]` — the iac-forge
16161 /// canonical-form tag `&'static str` axis),
16162 /// [`Self::LEADS`] (`[char; 3]` — the reader-lead `char` axis with
16163 /// shape-asymmetric cardinality reflecting the shared-lead-byte
16164 /// collapse of the `,` prefix across [`Self::Unquote`] AND
16165 /// [`Self::UnquoteSplice`]), and [`Self::HASH_DISCRIMINATORS`]
16166 /// (`[u8; 4]` — the outer-Sexp cache-key byte axis). This lift adds
16167 /// the SEVENTH per-family axis on the algebra — the (head, disc,
16168 /// promoted) triple axis on the closed-set promotion product.
16169 /// Each of the seven axes now pins its per-role canonical data at
16170 /// ONE `pub const` per role PLUS an ALL array for family-wide
16171 /// consumers, across the same closed set of four variants (or
16172 /// three-of-four for the shape-asymmetric [`Self::LEADS`] axis's
16173 /// shared-lead-char collapse, or one-of-four for the promotion-
16174 /// asymmetric [`Self::PROMOTIONS`] axis's single-arm collapse).
16175 ///
16176 /// Composition identity (pinned by
16177 /// `quote_form_promotions_align_with_promote_via_next_char_for_every_entry`):
16178 /// for every `(head, disc, promoted)` in [`Self::PROMOTIONS`],
16179 /// `head.promote_via_next_char(disc) == Some(promoted)`. The
16180 /// projection's Some-arm binds through [`Self::PROMOTIONS`]`[i].2`
16181 /// (the promoted-variant column of the ONE promotion triple) — a
16182 /// regression that drifts the triple's promoted-variant column
16183 /// silently redirects every reader `,@` sequence to a phantom
16184 /// variant AND fails the alignment pin at rustc / test time
16185 /// rather than at silent tokenizer drift where every `,@xs` form
16186 /// tokenizes to the wrong closed-set marker.
16187 ///
16188 /// Rejection contract (pinned by
16189 /// `quote_form_promotions_close_promote_via_next_char_against_every_non_promotion_pair`):
16190 /// for every `(head, next)` pair NOT in [`Self::PROMOTIONS`]'s
16191 /// projection to `(Self × char)`, `head.promote_via_next_char(next)
16192 /// == None`. Sweeps the [`Self::ALL`] × (rejection-char sweep)
16193 /// product against the promotion set's complement — a regression
16194 /// that widened the promotion algebra (e.g. phantom-promoted
16195 /// [`Self::Quote`] on `'@'` after a copy-paste drift on the match
16196 /// arm) surfaces at test time rather than at silent tokenizer
16197 /// drift where bare `'@xs` forms silently degrade to a phantom
16198 /// [`Self::UnquoteSplice`]-shaped sequence.
16199 ///
16200 /// Composition law (rendered-prefix identity, pinned by
16201 /// `quote_form_promotions_compose_prefix_from_source_prefix_and_discriminator_for_every_entry`):
16202 /// for every `(head, disc, promoted)` in [`Self::PROMOTIONS`],
16203 /// `format!("{}{}", head.prefix(), disc) == promoted.prefix()`.
16204 /// The (head prefix + discriminator) source-text composition
16205 /// agrees byte-for-byte with the promoted variant's rendered
16206 /// prefix — the reader's peek-then-consume arm's rendered
16207 /// prefix identity closes the read↔write duality across the
16208 /// promotion algebra. Sibling to the pre-existing
16209 /// `quote_form_promote_via_next_char_composes_prefix_from_source_prefix_and_next_char`
16210 /// which pins the same law through the projection method rather
16211 /// than through the triple's data directly — this pin closes the
16212 /// law at the constant, that pin closes it at the projection.
16213 ///
16214 /// `pub(crate)` because the promotion algebra is an implementation
16215 /// detail of the substrate's reader — exposing it publicly would
16216 /// leak the promotion-table shape through the API without enabling
16217 /// any external consumer the public projections
16218 /// ([`Self::promote_via_next_char`], [`Self::prefix`],
16219 /// [`Self::from_lead_char`]) don't already serve — same visibility
16220 /// rationale as [`Self::HASH_DISCRIMINATORS`] on the sibling
16221 /// cache-key axis.
16222 ///
16223 /// The `#[allow(dead_code)]` posture matches
16224 /// [`Self::HASH_DISCRIMINATORS`] / [`AtomKind::HASH_DISCRIMINATORS`]:
16225 /// the substrate's current [`Self::promote_via_next_char`] body
16226 /// dispatches through ONE match arm bound to the ONE promotion
16227 /// triple's promoted-variant column ([`Self::PROMOTIONS`]`[0].2`),
16228 /// with the head-pattern + discriminator-pattern arm literals
16229 /// preserved for the const-fn match's pattern surface (patterns
16230 /// cannot be array-indexing expressions in the current const-fn
16231 /// grammar). The lift lands the substrate primitive so future
16232 /// consumers keyed on the whole promotion algebra (a future
16233 /// `tatara-check` predicate `(check-promotion-algebra-injective …)`
16234 /// that verifies each `(head, disc)` pair projects to a unique
16235 /// promoted variant, a future LSP structural-navigation filter
16236 /// that keys on the promotion algebra's cardinality, a future
16237 /// `TypedRewriter<PromotionOp>` sweep zipping ALL / PREFIXES /
16238 /// LABELS / IAC_FORGE_TAGS / HASH_DISCRIMINATORS / PROMOTIONS in
16239 /// lockstep for a family-wide render) bind to ONE `[(Self, char,
16240 /// Self); N]` primitive rather than re-deriving the promotion
16241 /// triples inline per callsite. Matches the preemptive-primitive
16242 /// posture the prior-run [`Self::HASH_DISCRIMINATORS`] +
16243 /// [`AtomKind::HASH_DISCRIMINATORS`] +
16244 /// [`crate::error::StructuralKind::HASH_DISCRIMINATORS`] lifts
16245 /// carried before their downstream consumers materialized.
16246 ///
16247 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
16248 /// reader's two-char quote-family classification IS the typed-
16249 /// entry gate on the `,@` boundary, and lifting the promotion
16250 /// algebra's canonical triples to ONE typed `[(Self, char, Self);
16251 /// N]` primitive on the closed-set algebra closes the gate's
16252 /// two-char entry surface onto the algebra rather than at inline
16253 /// arm literals scattered across the const-fn match body.
16254 /// THEORY.md §III — the typescape; the singleton promotion triple
16255 /// binds at ONE typed `pub const` on the closed-set [`QuoteForm`]
16256 /// algebra rather than at zero-primitive-plus-inline-arm-literals
16257 /// at [`Self::promote_via_next_char`]'s match arm. THEORY.md §V.1
16258 /// — knowable platform; the family's cardinality becomes a
16259 /// TYPE-level constant on the substrate algebra rather than a
16260 /// per-consumer runtime dispatch through the match table.
16261 /// THEORY.md §VI.1 — generation over composition; the family-wide
16262 /// contract sweeps (alignment with
16263 /// [`Self::promote_via_next_char`], pairwise disjointness across
16264 /// the head-discriminator product, rendered-prefix composition
16265 /// identity) emerge from the composition of ONE substrate
16266 /// primitive (this `pub(crate) const` array) rather than as
16267 /// per-arm inline assertions duplicated at each call site.
16268 ///
16269 /// Frontier inspiration: MLIR's typed rewriter registry
16270 /// (`mlir::PatternApplicator`) carries a per-op-family
16271 /// `[(source_pattern, matcher, rewritten_op)]` static rewrite
16272 /// table at the closed-set boundary — the (source, matcher,
16273 /// target) triple axis becomes typed data on the IR algebra
16274 /// rather than dispatch tables scattered across per-pattern
16275 /// callsites. Translated through the substrate's [`QuoteForm`]
16276 /// closed-set marker, the reader's promotion rewrite table
16277 /// becomes ONE typed `[(head_variant, next_char, promoted_variant);
16278 /// N]` array on the algebra. Where MLIR's registry carries the
16279 /// rewrite table dynamically on the pattern applicator's runtime
16280 /// state, this substrate carries it statically as `pub const` on
16281 /// the closed-set marker — the pattern-matching evaluation lands
16282 /// at rustc-time through const-fn match arm binding to
16283 /// [`Self::PROMOTIONS`]`[i].2` rather than at runtime through a
16284 /// dynamic registry lookup.
16285 #[allow(dead_code)]
16286 pub(crate) const PROMOTIONS: [(Self, char, Self); 1] = [(
16287 Self::Unquote,
16288 Self::SPLICE_DISCRIMINATOR,
16289 Self::UnquoteSplice,
16290 )];
16291
16292 /// Promotion table on the closed-set quote-family algebra —
16293 /// `Some(Self::UnquoteSplice)` iff `self == Self::Unquote &&
16294 /// next == Self::SPLICE_DISCRIMINATOR`, else `None`. Encodes the
16295 /// substrate's ONE (variant, next-char) → longer-variant mapping —
16296 /// `,` [`Self::Unquote`] followed by `@` [`Self::SPLICE_DISCRIMINATOR`]
16297 /// promotes to `,@` [`Self::UnquoteSplice`]. Every other pairing
16298 /// (including [`Self::Quote`] / [`Self::Quasiquote`] / [`Self::UnquoteSplice`]
16299 /// on ANY next-char, AND [`Self::Unquote`] on any non-discriminator
16300 /// next-char) yields `None` — the closed set of promotions is the
16301 /// singleton `{(Unquote, '@') → UnquoteSplice}` on the `Self × char
16302 /// → Option<Self>` product.
16303 ///
16304 /// Structural sibling of [`Self::from_lead_char`] one axis over on
16305 /// the same typed-entry family: [`Self::from_lead_char`] decodes
16306 /// ONE lead char to its DEFAULT variant on that char; this method
16307 /// decodes ONE (default variant, second char) pair to its PROMOTED
16308 /// variant. Together the two methods close the reader's outer
16309 /// quote-family entry surface onto the algebra: the tokenizer
16310 /// consumes ONE lead char through [`Self::from_lead_char`], then
16311 /// OPTIONALLY consumes one second char through this method — the
16312 /// (lead char, second char) → typed marker projection binds at TWO
16313 /// typed decodes rather than at inline `char`-literal patterns
16314 /// scattered across the outer-match dispatch arm.
16315 ///
16316 /// ONE consumer entrypoint the reader's `tokenize` binds against:
16317 /// the peek-then-consume `@` promotion inside the outer-match
16318 /// quote-family dispatch was pre-lift a hand-rolled inline check
16319 /// `matches!(qf_head, QuoteForm::Unquote) &&
16320 /// chars.peek().map(|&(_, c)| c) == Some('@')` paired with a
16321 /// per-branch `QuoteForm::UnquoteSplice` construction. The pairing
16322 /// was load-bearing yet only enforced by callsite discipline at a
16323 /// SEVENTH consumer site (alongside `Hash`, `Display`, `sexp_shape`,
16324 /// `wrap`, `iac_forge_tag`, `as_unquote_form`) the prior closed-set
16325 /// `QuoteForm` lifts did not reach. Post-lift the reader's peek
16326 /// arm routes through this method, so the (Unquote, '@') →
16327 /// UnquoteSplice promotion binds at ONE site on the typed algebra.
16328 /// A regression that drifts the promotion table (e.g. re-inlines
16329 /// `matches!(qf_head, QuoteForm::Quote)` at the peek arm and
16330 /// silently promotes bare `'` to a phantom variant) becomes a
16331 /// typed compile error against the `Option<Self>` return type.
16332 ///
16333 /// The single-promotion collapse (only `(Unquote, '@')` triggers)
16334 /// is INTENTIONAL: [`Self::UnquoteSplice`] is the ONLY variant with
16335 /// a two-char [`Self::prefix`], so the promotion table has exactly
16336 /// ONE `Some` arm and every other pairing rejects. Placing the
16337 /// promotion at the closed-set algebra rather than at the reader's
16338 /// peek arm keeps the streaming reader's two-char peek-then-consume
16339 /// shape at ONE site (the reader) while the (variant × second
16340 /// char) → promoted variant projection lives on the substrate
16341 /// algebra — parallel to the split that [`Self::from_lead_char`]
16342 /// closes for the one-char entry surface. This split parallels the
16343 /// reader's split of `Token::Str` into open-delimiter dispatch
16344 /// ([`crate::ast::Atom::STR_DELIMITER`]) AND inner-payload
16345 /// accumulation — the closed-set char algebra decodes the entry
16346 /// chars; the streaming reader handles the peek-and-consume
16347 /// follow-through.
16348 ///
16349 /// Composition identity: for every `qf: QuoteForm` and every
16350 /// `c: char`, if `qf.promote_via_next_char(c) == Some(promoted)`
16351 /// then `format!("{}{}", qf.prefix(), c) == promoted.prefix()`.
16352 /// Pinned by
16353 /// `quote_form_promote_via_next_char_composes_prefix_from_source_prefix_and_next_char`
16354 /// across the singleton promotion arm — the pin asserts the
16355 /// (variant, next char) → promoted-variant projection agrees with
16356 /// the reader's rendered [`Self::prefix`] composition, so a
16357 /// regression that drifts one side of the identity (a promotion
16358 /// arm rerouted through the wrong variant, or a prefix renamed
16359 /// without updating the promotion table) surfaces immediately.
16360 ///
16361 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
16362 /// reader's two-char quote-family classification IS the typed-
16363 /// entry gate on the `,@` boundary. THEORY.md §V.1 — knowable
16364 /// platform; the (variant, next char) → promoted variant table
16365 /// becomes a TYPE projection on the substrate algebra rather than
16366 /// at an inline `matches!(qf, Unquote) && c == '@'` pattern
16367 /// scattered at the reader's peek arm. THEORY.md §VI.1 —
16368 /// generation over composition; a fifth homoiconic prefix with its
16369 /// own two-char extension extends [`Self`] AND this method's match
16370 /// arm in lockstep — rustc binds the extension through
16371 /// exhaustiveness over the closed enum, and the `Option<Self>`
16372 /// return shape leaves the promotion table structurally open for
16373 /// future variants to append their own `Some` arms without
16374 /// touching the existing arms' semantics.
16375 ///
16376 /// Frontier inspiration: Racket's `read-syntax` two-char
16377 /// discriminator table (`(quote-abbrev-mapping char) → syntax`)
16378 /// that maps `(#\' → 'quote)`, `(#\` → 'quasiquote)`, `(#\, →
16379 /// 'unquote)`, `(#\, #\@) → 'unquote-splicing)` on the reader's
16380 /// typed abbreviation surface. Translated through the substrate's
16381 /// [`QuoteForm`] outer-marker algebra, the `(#\, #\@) → 'unquote-
16382 /// splicing)` two-char arm becomes ONE typed `(Self::Unquote, '@')
16383 /// → Some(Self::UnquoteSplice)` promotion on the closed-set
16384 /// algebra with rustc binding the promotion identity through
16385 /// exhaustiveness. Where Racket carries the promotion table
16386 /// dynamically on the reader's abbreviation-mapping parameter,
16387 /// this substrate carries it statically as `pub const fn` on the
16388 /// closed-set marker.
16389 #[must_use]
16390 pub const fn promote_via_next_char(self, next: char) -> Option<Self> {
16391 // The Some-arm's promoted-variant column routes through
16392 // `Self::PROMOTIONS[0].2` — the ONE promotion triple on the
16393 // closed-set algebra — so a regression that drifts the
16394 // promoted-variant column of the singleton triple silently
16395 // redirects every reader `,@` sequence to a phantom variant AND
16396 // fails the alignment pin
16397 // `quote_form_promotions_align_with_promote_via_next_char_for_every_entry`
16398 // at rustc / test time rather than at silent tokenizer drift.
16399 // The head-pattern (`Self::Unquote`) + discriminator-pattern
16400 // (`Self::SPLICE_DISCRIMINATOR`) arm literals stay inline
16401 // because patterns cannot be array-indexing expressions in the
16402 // current const-fn grammar; the alignment pin catches head /
16403 // discriminator column drift by construction.
16404 match (self, next) {
16405 (Self::Unquote, Self::SPLICE_DISCRIMINATOR) => Some(Self::PROMOTIONS[0].2),
16406 _ => None,
16407 }
16408 }
16409
16410 /// Canonical `u8` cache-key byte for [`Self::Quote`]'s
16411 /// [`Self::hash_discriminator`] arm — `3`. ONE canonical byte on
16412 /// the closed-set [`QuoteForm`] algebra shared by
16413 /// [`Self::hash_discriminator`]'s [`Self::Quote`] arm AND every
16414 /// downstream consumer (the [`Hash for Sexp`](crate::ast::Sexp)
16415 /// cache-key body, the expansion cache
16416 /// (`crate::macro_expand::Expander::cache`) that keys on that hash).
16417 ///
16418 /// Sibling posture to the closed set of per-role `pub(crate) const`
16419 /// / `pub const` bytes on the substrate's other closed-set outer
16420 /// algebras: [`Self::QUOTE_PREFIX`] / [`Self::QUASIQUOTE_PREFIX`]
16421 /// / [`Self::UNQUOTE_PREFIX`] / [`Self::UNQUOTE_SPLICE_PREFIX`]
16422 /// (per-role reader-prefix `&'static str` algebra on the SAME
16423 /// [`QuoteForm`] closed set — commit a08e61f),
16424 /// [`Self::QUOTE_LABEL`] / [`Self::QUASIQUOTE_LABEL`] /
16425 /// [`Self::UNQUOTE_LABEL`] / [`Self::UNQUOTE_SPLICE_LABEL`] (per-
16426 /// role diagnostic-label `&'static str` algebra on the SAME closed
16427 /// set — commit 70be157), [`Self::QUOTE_IAC_FORGE_TAG`] /
16428 /// [`Self::QUASIQUOTE_IAC_FORGE_TAG`] / [`Self::UNQUOTE_IAC_FORGE_TAG`]
16429 /// / [`Self::UNQUOTE_SPLICE_IAC_FORGE_TAG`] (per-role iac-forge
16430 /// canonical-form tag `&'static str` algebra on the SAME closed set
16431 /// — commit bdd624b). This constant closes the FOURTH per-role
16432 /// axis on [`QuoteForm`] — the `u8` cache-key axis paired with the
16433 /// three pre-existing `&'static str` axes.
16434 ///
16435 /// The FOUR canonical bytes `{3, 4, 5, 6}` partition the outer-
16436 /// [`crate::ast::Sexp`] `Hash` body's quote-family arm-set against
16437 /// the reserved bytes the non-quote-family arms use (`0u8` for
16438 /// [`crate::error::StructuralKind::Nil`] via
16439 /// [`crate::error::StructuralKind::hash_discriminator`], `1u8` for
16440 /// [`crate::ast::Sexp::Atom`]'s outer-carve marker via the
16441 /// pre-existing inline `1u8` at [`Hash for Sexp`](crate::ast::Sexp)'s
16442 /// atom arm, `2u8` for [`crate::error::StructuralKind::List`] via
16443 /// [`crate::error::StructuralKind::hash_discriminator`]) — the
16444 /// three carvings of the outer-[`crate::ast::Sexp`] cache-key
16445 /// space jointly cover the `{0, 1, 2, 3, 4, 5, 6}` byte-set with
16446 /// no gaps AND no overlaps.
16447 ///
16448 /// A regression that inlines the `3` literal at
16449 /// [`Self::hash_discriminator`]'s [`Self::Quote`] arm and drifts
16450 /// the constant silently (e.g. a re-numbering that collides with
16451 /// the reserved `2u8` for [`crate::error::StructuralKind::List`],
16452 /// silently mis-hashing every cached expansion across the substrate)
16453 /// fails at the algebra's `hash_discriminator()` path-uniformity
16454 /// pin
16455 /// (`quote_form_hash_discriminator_routes_through_typed_per_role_constants`)
16456 /// rather than at silent cache-key drift where
16457 /// `crate::macro_expand::Expander::cache` mis-collides live
16458 /// expansions.
16459 ///
16460 /// `pub(crate)` because the byte-discriminator surface is an
16461 /// implementation detail of the substrate's [`Hash for Sexp`](crate::ast::Sexp)
16462 /// cache-key contract; exposing it publicly would leak the cache-
16463 /// key shape through the API without enabling any external
16464 /// consumer the public projections ([`Self::as_quote_form`],
16465 /// [`Self::prefix`], [`Self::as_unquote_form`]) don't already
16466 /// serve — same visibility rationale as [`Self::hash_discriminator`]
16467 /// itself.
16468 ///
16469 /// Theory anchor: THEORY.md §II.1 invariant 5 — composition
16470 /// preserves proofs; the alias-chain composition law
16471 /// `QuoteForm::HASH_DISCRIMINATORS[i] ==
16472 /// QuoteForm::ALL[i].hash_discriminator()` binds the family-wide
16473 /// array to the projection method at rustc time, pinned by byte
16474 /// equality. THEORY.md §III — the typescape; the four canonical
16475 /// cache-key bytes bind at ONE `pub(crate) const` per role on the
16476 /// typed algebra rather than as inline `u8` literals in the
16477 /// `hash_discriminator` match arms.
16478 pub(crate) const QUOTE_HASH_DISCRIMINATOR: u8 = 3;
16479
16480 /// Canonical `u8` cache-key byte for [`Self::Quasiquote`]'s
16481 /// [`Self::hash_discriminator`] arm — `4`. Sibling of
16482 /// [`Self::QUOTE_HASH_DISCRIMINATOR`] on the closed-set per-role
16483 /// quote-family cache-key-byte axis; see
16484 /// [`Self::QUOTE_HASH_DISCRIMINATOR`] for the algebra-level round-
16485 /// trip + disjointness contracts every sibling shares.
16486 pub(crate) const QUASIQUOTE_HASH_DISCRIMINATOR: u8 = 4;
16487
16488 /// Canonical `u8` cache-key byte for [`Self::Unquote`]'s
16489 /// [`Self::hash_discriminator`] arm — `5`. Sibling of
16490 /// [`Self::QUOTE_HASH_DISCRIMINATOR`] on the closed-set per-role
16491 /// quote-family cache-key-byte axis. Byte-for-byte distinct from
16492 /// [`Self::UNQUOTE_SPLICE_HASH_DISCRIMINATOR`] — the two template-
16493 /// substitution arms partition `{5, 6}` on the outer-Sexp cache-
16494 /// key space.
16495 pub(crate) const UNQUOTE_HASH_DISCRIMINATOR: u8 = 5;
16496
16497 /// Canonical `u8` cache-key byte for [`Self::UnquoteSplice`]'s
16498 /// [`Self::hash_discriminator`] arm — `6`. Sibling of
16499 /// [`Self::QUOTE_HASH_DISCRIMINATOR`] on the closed-set per-role
16500 /// quote-family cache-key-byte axis. The HIGHEST byte on the
16501 /// closed set — a future fifth quote-family variant would extend
16502 /// the partition to `{3, 4, 5, 6, 7}` and land the new
16503 /// discriminator at `7u8`.
16504 pub(crate) const UNQUOTE_SPLICE_HASH_DISCRIMINATOR: u8 = 6;
16505
16506 /// Closed-set forced-arity ALL array over the canonical cache-key
16507 /// `u8` bytes, in declaration order matching [`Self::ALL`] element-
16508 /// wise (pinned by `quote_form_hash_discriminators_align_with_all_by_index`).
16509 /// Sibling posture to [`Self::PREFIXES`] (`[&'static str; 4]` —
16510 /// the reader-prefix `&'static str` axis on the SAME closed set),
16511 /// [`Self::LABELS`] (`[&'static str; 4]` — the diagnostic-label
16512 /// `&'static str` axis), [`Self::IAC_FORGE_TAGS`] (`[&'static str;
16513 /// 4]` — the iac-forge canonical-form tag `&'static str` axis) —
16514 /// every closed-set outer projection on the substrate's
16515 /// [`QuoteForm`] algebra now pins its per-role canonical bytes at
16516 /// ONE `pub(crate) const` / `pub const` per role PLUS an ALL array
16517 /// for family-wide consumers, across ALL FOUR production
16518 /// vocabularies the closed set carries (reader prefix, diagnostic
16519 /// label, iac-forge canonical-form tag, outer-Sexp cache-key byte).
16520 ///
16521 /// Pre-lift the four cache-key bytes had NO per-role primitive on
16522 /// this closed-set algebra — a consumer with a [`QuoteForm`]
16523 /// variant in hand at compile time reaching for the canonical byte
16524 /// had to spell `QuoteForm::Unquote.hash_discriminator()` (runtime
16525 /// dispatch through the match arm) OR reach across into the inline
16526 /// `5u8` at the pre-lift match arm's [`Self::Unquote`] branch and
16527 /// re-derive the (variant, byte) pairing at the call site.
16528 /// Post-lift the FOUR canonical bytes bind at ONE `pub(crate) const`
16529 /// per role on the typed [`QuoteForm`] algebra AND at
16530 /// [`Self::HASH_DISCRIMINATORS`] as a family-wide forced-arity
16531 /// array — a future substrate-facing cache-key introspection tool
16532 /// (a `tatara-check` predicate that asserts every quote-family
16533 /// arm's discriminator disjoint from the reserved
16534 /// [`crate::ast::Sexp::Atom`] byte, a Sekiban audit-trail metric
16535 /// jointly labeled by the cache-key partition, a future
16536 /// `TypedRewriter<QuoteFormOp>` sweep zipping ALL / PREFIXES /
16537 /// LABELS / IAC_FORGE_TAGS / HASH_DISCRIMINATORS in lockstep for a
16538 /// family-wide (variant, four-vocabulary quadruple) render) reads
16539 /// through the typed constants without re-deriving the four-arm
16540 /// carving inline.
16541 ///
16542 /// Each entry is byte-for-byte identical to the pre-lift inline
16543 /// `u8` literal at the corresponding [`Self::hash_discriminator`]
16544 /// arm — pinned by
16545 /// `quote_form_hash_discriminators_pin_legacy_cache_key_bytes` so
16546 /// a regression that drifts ONE `pub(crate) const` from its pre-
16547 /// lift byte silently invalidates every cached expansion AND mis-
16548 /// collides with the reserved bytes the non-quote-family arms use,
16549 /// fails-loudly at the alias test rather than at a silent
16550 /// [`crate::macro_expand::Expander::cache`] mis-hash. Adding a
16551 /// hypothetical fifth homoiconic prefix (a `,~` reverse-unquote, a
16552 /// `,?` conditional-unquote) extends [`Self::ALL`] AND
16553 /// [`Self::HASH_DISCRIMINATORS`] AND adds ONE per-role
16554 /// `pub(crate) const` in lockstep — rustc's forced-arity check on
16555 /// the two `[_; N]` arrays fails compilation if EITHER array grows
16556 /// without the other, closing the extensibility gap that pre-lift
16557 /// silently allowed a discriminator collision on `7u8` (the next
16558 /// free byte).
16559 ///
16560 /// Theory anchor: THEORY.md §III — the typescape; the four
16561 /// canonical cache-key bytes bind at ONE typed `[u8; 4]` array on
16562 /// the closed-set [`QuoteForm`] algebra rather than at zero-
16563 /// primitive-plus-four-inline-`u8`-literals scattered across the
16564 /// [`Self::hash_discriminator`] match arms. THEORY.md §V.1 —
16565 /// knowable platform; the family's cardinality becomes a TYPE-
16566 /// level constant on the substrate algebra rather than a per-
16567 /// consumer runtime dispatch through the match table. THEORY.md
16568 /// §V.3 — three-pillar attestation; the cache-key partition is
16569 /// the substrate's outer-Sexp `intent_hash` composition axis for
16570 /// every quote-family arm — binding the four bytes on the typed
16571 /// algebra makes attestation-key drift a compile error rather
16572 /// than a silent BLAKE3 mis-hash. THEORY.md §VI.1 — generation
16573 /// over composition; the family-wide contract sweeps (alignment
16574 /// with `ALL`, pairwise disjointness, membership through
16575 /// [`Self::hash_discriminator`]) emerge from the composition of
16576 /// TWO substrate primitives (this `pub(crate) const` array + the
16577 /// four per-role `pub(crate) const *_HASH_DISCRIMINATOR` aliases)
16578 /// rather than as per-variant inline assertions duplicated at
16579 /// each call site.
16580 ///
16581 /// The `#[allow(dead_code)]` posture is deliberate: the substrate's
16582 /// current [`Hash for Sexp`](crate::ast::Sexp) body composes
16583 /// through the per-variant [`Self::hash_discriminator`] projection
16584 /// arm-by-arm rather than sweeping the family-wide array, so no
16585 /// non-test caller currently reaches this ALL array directly. The
16586 /// lift lands the substrate primitive so future consumers keyed
16587 /// on the whole family (a future
16588 /// [`crate::macro_expand::Expander`] cache-warmup pass that hashes
16589 /// the quote-family byte-set upfront, a future `tatara-check`
16590 /// predicate `(check-cache-key-partition-disjoint …)` that
16591 /// verifies the `{3, 4, 5, 6}` partition against the reserved
16592 /// `{0, 1, 2}` bytes structurally, a future
16593 /// `TypedRewriter<QuoteFormOp>` sweep zipping ALL / PREFIXES /
16594 /// LABELS / IAC_FORGE_TAGS / HASH_DISCRIMINATORS in lockstep for a
16595 /// family-wide (variant, four-vocabulary quadruple) render) bind
16596 /// to ONE `[u8; 4]` primitive rather than re-deriving the array
16597 /// inline per callsite. Matches the preemptive-primitive posture
16598 /// the prior-run [`crate::error::UnquoteForm::hash_discriminator`]
16599 /// lift carried before its downstream consumers materialized.
16600 #[allow(dead_code)]
16601 pub(crate) const HASH_DISCRIMINATORS: [u8; 4] = [
16602 Self::QUOTE_HASH_DISCRIMINATOR,
16603 Self::QUASIQUOTE_HASH_DISCRIMINATOR,
16604 Self::UNQUOTE_HASH_DISCRIMINATOR,
16605 Self::UNQUOTE_SPLICE_HASH_DISCRIMINATOR,
16606 ];
16607
16608 /// Stable, per-variant byte discriminator that paired with the
16609 /// recursive inner hash builds the substrate's `Hash for Sexp`
16610 /// projection — `3` for [`Self::Quote`], `4` for
16611 /// [`Self::Quasiquote`], `5` for [`Self::Unquote`], `6` for
16612 /// [`Self::UnquoteSplice`]. The byte values are load-bearing
16613 /// because the expansion cache (`Expander::cache`) keys on the
16614 /// hash of `(macro_name, args)` — changing a discriminator silently
16615 /// invalidates every cached expansion AND mis-collides with the
16616 /// reserved bytes the non-quote-family Hash arms use (`0` for
16617 /// `Nil`, `1` for `Atom`, `2` for `List`). The closed set ensures
16618 /// the four arms partition `{3, 4, 5, 6}` injectively against the
16619 /// reserved bytes — a future quote-family extension must extend
16620 /// this method AND the non-quote-family arms in lockstep, with
16621 /// rustc binding the consistency through exhaustiveness over the
16622 /// closed enum.
16623 ///
16624 /// Post-lift the four arms route through the per-role
16625 /// `pub(crate) const` bytes on the closed-set [`QuoteForm`]
16626 /// algebra ([`Self::QUOTE_HASH_DISCRIMINATOR`],
16627 /// [`Self::QUASIQUOTE_HASH_DISCRIMINATOR`],
16628 /// [`Self::UNQUOTE_HASH_DISCRIMINATOR`],
16629 /// [`Self::UNQUOTE_SPLICE_HASH_DISCRIMINATOR`]) rather than
16630 /// inline `u8` literals — so a re-numbering that would silently
16631 /// invalidate every cached expansion lands as ONE edit to the
16632 /// matching `pub(crate) const` rather than at four scattered
16633 /// arm-literals. Every downstream consumer that binds to the
16634 /// algebra ([`Hash for Sexp`](crate::ast::Sexp)'s outer sweep,
16635 /// the [`crate::macro_expand::Expander::cache`] cache-key
16636 /// composition, the future coverage-tool sweeps) inherits the
16637 /// rename mechanically.
16638 ///
16639 /// `pub(crate)` because the byte-discriminator surface is an
16640 /// implementation detail of the substrate's `Hash for Sexp` cache-
16641 /// key contract; exposing it publicly would leak the cache-key
16642 /// shape through the API without enabling any external consumer
16643 /// the public projections (`Sexp::as_quote_form`, `Self::prefix`,
16644 /// `Self::as_unquote_form`) don't already serve.
16645 #[must_use]
16646 pub(crate) fn hash_discriminator(self) -> u8 {
16647 match self {
16648 Self::Quote => Self::QUOTE_HASH_DISCRIMINATOR,
16649 Self::Quasiquote => Self::QUASIQUOTE_HASH_DISCRIMINATOR,
16650 Self::Unquote => Self::UNQUOTE_HASH_DISCRIMINATOR,
16651 Self::UnquoteSplice => Self::UNQUOTE_SPLICE_HASH_DISCRIMINATOR,
16652 }
16653 }
16654
16655 /// Project the 4-of-4 quote-family marker into the 2-of-4
16656 /// template-substitution subset — `Some(UnquoteForm::Unquote)` for
16657 /// [`Self::Unquote`], `Some(UnquoteForm::Splice)` for
16658 /// [`Self::UnquoteSplice`], `None` for [`Self::Quote`] /
16659 /// [`Self::Quasiquote`] (the literal-quote and quasi-quote
16660 /// prefixes are wrappers, NOT substitution points). ONE projection
16661 /// on this algebra the [`crate::ast::Sexp::as_unquote`] derivation
16662 /// routes through — the (Sexp variant, UnquoteForm marker) pairing
16663 /// now binds at the typed [`crate::ast::Sexp::as_quote_form`]
16664 /// projection's output composed with this method's output, instead
16665 /// of being re-derived per-arm inside `Sexp::as_unquote`.
16666 ///
16667 /// The closed-set guarantee on [`UnquoteForm`] (exactly
16668 /// `Unquote ⊎ Splice`) AND on [`Self`] (exactly
16669 /// `Quote ⊎ Quasiquote ⊎ Unquote ⊎ UnquoteSplice`) ensures that the
16670 /// 2-of-4 subset is structurally fixed: a future variant joining
16671 /// the template-substitution surface extends both enums AND this
16672 /// method's match arm together, with rustc binding the extension
16673 /// through the projection's `Option` return type.
16674 #[must_use]
16675 pub fn as_unquote_form(self) -> Option<UnquoteForm> {
16676 match self {
16677 Self::Unquote => Some(UnquoteForm::Unquote),
16678 Self::UnquoteSplice => Some(UnquoteForm::Splice),
16679 Self::Quote | Self::Quasiquote => None,
16680 }
16681 }
16682
16683 /// Canonical `&'static str` iac-forge canonical-form tag of
16684 /// [`Self::Quote`] — `"quote"`. The ONE canonical bytes-payload on
16685 /// the closed-set [`QuoteForm`] algebra shared by [`Self::iac_forge_tag`]'s
16686 /// [`Self::Quote`] arm AND the `crate::interop` (removed) `From<&Sexp> for
16687 /// iac_forge::sexpr::SExpr` arm the projection feeds.
16688 ///
16689 /// Sibling posture to the closed set of per-role `pub const` bytes
16690 /// on the substrate's other closed-set outer algebras:
16691 /// [`Self::QUOTE_PREFIX`] / [`Self::QUASIQUOTE_PREFIX`] /
16692 /// [`Self::UNQUOTE_PREFIX`] / [`Self::UNQUOTE_SPLICE_PREFIX`] (per-
16693 /// role reader-prefix algebra on the SAME [`QuoteForm`] closed set),
16694 /// [`crate::error::MacroDefHead::DEFMACRO_KEYWORD`] /
16695 /// [`crate::error::MacroDefHead::DEFPOINT_TEMPLATE_KEYWORD`] /
16696 /// [`crate::error::MacroDefHead::DEFCHECK_KEYWORD`] (per-role
16697 /// head-keyword algebra on the CL macro-definition surface),
16698 /// [`Atom::TRUE_LITERAL`] / [`Atom::FALSE_LITERAL`] (per-role
16699 /// Scheme-bool spelling algebra on the atomic-payload surface).
16700 ///
16701 /// The (canonical iac-forge tag) axis lives ORTHOGONAL to the
16702 /// (canonical reader prefix) axis: `Self::QUOTE_PREFIX` (`"'"`) and
16703 /// `Self::QUOTE_IAC_FORGE_TAG` (`"quote"`) both project the same
16704 /// variant but through two distinct byte vocabularies — the reader
16705 /// axis for the Lisp source-code surface, the iac-forge axis for
16706 /// the cross-crate canonical-form surface (BLAKE3 attestation,
16707 /// render cache). A regression that inlines the `"quote"` literal
16708 /// at [`Self::iac_forge_tag`]'s [`Self::Quote`] arm and drifts the
16709 /// constant silently (e.g. a hypothetical rename to `"literal-quote"`
16710 /// on the iac-forge side while leaving the prefix `"'"` intact)
16711 /// fails at the algebra's `iac_forge_tag()` path-uniformity pin
16712 /// (`quote_form_iac_forge_tag_routes_through_typed_per_role_constants`)
16713 /// rather than at silent canonical-form drift where downstream
16714 /// BLAKE3 attestation keys silently mis-hash.
16715 pub const QUOTE_IAC_FORGE_TAG: &'static str = "quote";
16716
16717 /// Canonical `&'static str` iac-forge canonical-form tag of
16718 /// [`Self::Quasiquote`] — `"quasiquote"`. Sibling of
16719 /// [`Self::QUOTE_IAC_FORGE_TAG`] on the closed-set per-role
16720 /// quote-family iac-forge tag-bytes axis; see
16721 /// [`Self::QUOTE_IAC_FORGE_TAG`] for the algebra-level round-trip +
16722 /// disjointness contracts every sibling shares.
16723 pub const QUASIQUOTE_IAC_FORGE_TAG: &'static str = "quasiquote";
16724
16725 /// Canonical `&'static str` iac-forge canonical-form tag of
16726 /// [`Self::Unquote`] — `"unquote"`. Sibling of
16727 /// [`Self::QUOTE_IAC_FORGE_TAG`] on the closed-set per-role
16728 /// quote-family iac-forge tag-bytes axis.
16729 ///
16730 /// Byte-identical to the substrate's shorter diagnostic label
16731 /// [`crate::error::SexpShape::Unquote`]'s label projection
16732 /// (`SexpShape::label` returns `"unquote"` for this variant) — the
16733 /// two projections happen to agree on this variant's bytes but
16734 /// live at distinct algebraic layers (iac-forge canonical form vs
16735 /// substrate diagnostic surface); the divergence is load-bearing on
16736 /// the [`Self::UnquoteSplice`] arm (`"unquote-splicing"` vs
16737 /// `"unquote-splice"`) and this byte-level agreement here does not
16738 /// license a consolidation of the two axes. Pinned by
16739 /// `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`.
16740 pub const UNQUOTE_IAC_FORGE_TAG: &'static str = "unquote";
16741
16742 /// Canonical `&'static str` iac-forge canonical-form tag of
16743 /// [`Self::UnquoteSplice`] — `"unquote-splicing"`. The Common-Lisp-
16744 /// canonical spelling: a `,@x` form encodes as `(unquote-splicing x)`
16745 /// rather than `(unquote-splice x)`. That tag-string choice is
16746 /// INTENTIONALLY DISTINCT from the substrate's shorter diagnostic
16747 /// label projected by [`crate::error::SexpShape::label`] (which
16748 /// renders `[`Self::UnquoteSplice`]` as `"unquote-splice"` — the
16749 /// shorter idiom appropriate for `expected …, got unquote-splice`
16750 /// error surfaces). The two projections key the SAME closed set on
16751 /// TWO distinct boundaries — pinning the divergence at the typed
16752 /// per-role `pub const` documents the intent structurally: a
16753 /// future "consolidation" PR that homogenizes them would have to
16754 /// touch this constant explicitly, surfacing the boundary-distinct
16755 /// invariant at code-review time rather than silently.
16756 ///
16757 /// Sibling of [`Self::QUOTE_IAC_FORGE_TAG`] on the closed-set
16758 /// per-role quote-family iac-forge tag-bytes axis. The ONLY entry
16759 /// on this axis whose bytes disagree with the peer-axis
16760 /// [`crate::error::SexpShape::label`] projection — pinned by
16761 /// `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`
16762 /// alongside the three matched-arm agreements.
16763 pub const UNQUOTE_SPLICE_IAC_FORGE_TAG: &'static str = "unquote-splicing";
16764
16765 /// The closed-set forced-arity ALL array over the quote-family
16766 /// iac-forge canonical-form tag `&'static str` bytes in canonical
16767 /// declaration order matching [`Self::ALL`] element-wise. Sibling
16768 /// posture to [`Self::PREFIXES`] (`[&'static str; 4]` on the
16769 /// reader-prefix axis of the SAME [`QuoteForm`] closed set),
16770 /// [`crate::error::MacroDefHead::KEYWORDS`] (`[&'static str; 3]`
16771 /// on the CL macro-definition head algebra),
16772 /// [`Atom::BOOL_LITERALS`] (`[&'static str; 2]` on the Scheme-bool
16773 /// spelling algebra), and
16774 /// [`crate::macro_expand::MacroParams::LAMBDA_LIST_KEYWORDS`]
16775 /// (`[&'static str; 2]` on the CL lambda-list-keyword algebra) —
16776 /// every closed-set outer projection on the substrate now pins its
16777 /// canonical bytes at ONE `pub const` per role plus an ALL array
16778 /// for family-wide consumers.
16779 ///
16780 /// The (canonical iac-forge tag) axis + the (canonical reader
16781 /// prefix) axis together span the two production byte-vocabularies
16782 /// the [`QuoteForm`] closed set carries — [`Self::PREFIXES`] holds
16783 /// the Lisp source-code prefixes (`"'"`, `` "`" ``, `","`, `",@"`)
16784 /// the reader tokenizes on, [`Self::IAC_FORGE_TAGS`] holds the
16785 /// cross-crate canonical-form tag strings (`"quote"`,
16786 /// `"quasiquote"`, `"unquote"`, `"unquote-splicing"`) the
16787 /// iac-forge interop layer round-trips through. Adding a
16788 /// hypothetical fifth homoiconic prefix (a `,~` reverse-unquote, a
16789 /// `,?` conditional-unquote, a `#'` Common-Lisp function-quote)
16790 /// extends [`Self::ALL`] AND [`Self::PREFIXES`] AND
16791 /// [`Self::IAC_FORGE_TAGS`] AND [`Self::prefix`]'s arm AND
16792 /// [`Self::iac_forge_tag`]'s arm AND two new per-role `pub const`s
16793 /// (one on each axis) in lockstep — rustc's forced-arity check on
16794 /// `[&'static str; N]` fails compilation if any of the three ALL
16795 /// arrays grows without the others.
16796 ///
16797 /// Future consumers that compose against [`Self::IAC_FORGE_TAGS`]:
16798 /// - Cross-crate canonical-form completion (an authoring tool
16799 /// surfacing every legal iac-forge tag in a `(<tag> <inner>)`
16800 /// template — the completion set IS [`Self::IAC_FORGE_TAGS`]
16801 /// rather than four hand-enumerated `&'static str` literals per
16802 /// completion provider).
16803 /// - `tatara-check` coverage assertions that sweep workspace
16804 /// attestation payloads for every canonical iac-forge tag —
16805 /// the typed sweep replaces per-consumer inline enumeration of
16806 /// the four literals.
16807 /// - Any future audit-trail metric jointly labeled by
16808 /// [`Self::iac_forge_tag`] (e.g.
16809 /// `tatara_lisp_iac_forge_tag_total{tag="quote"}`) — the metric
16810 /// label set IS [`Self::IAC_FORGE_TAGS`] mapped through
16811 /// [`Self::iac_forge_tag`].
16812 pub const IAC_FORGE_TAGS: [&'static str; 4] = [
16813 Self::QUOTE_IAC_FORGE_TAG,
16814 Self::QUASIQUOTE_IAC_FORGE_TAG,
16815 Self::UNQUOTE_IAC_FORGE_TAG,
16816 Self::UNQUOTE_SPLICE_IAC_FORGE_TAG,
16817 ];
16818
16819 /// Canonical iac-forge interop tag — the symbol head the canonical
16820 /// 2-element-list encoding of a quote-family wrapper uses when
16821 /// projecting `tatara_lisp::Sexp` into `iac_forge::sexpr::SExpr`:
16822 /// `"quote"` for [`Self::Quote`], `"quasiquote"` for
16823 /// [`Self::Quasiquote`], `"unquote"` for [`Self::Unquote`],
16824 /// `"unquote-splicing"` for [`Self::UnquoteSplice`].
16825 ///
16826 /// The mapping is Common-Lisp-canonical: a `,@x` form encodes as
16827 /// `(unquote-splicing x)` rather than `(unquote-splice x)`. That
16828 /// tag-string choice is INTENTIONALLY DISTINCT from the substrate's
16829 /// shorter diagnostic label projected by
16830 /// [`crate::error::SexpShape::label`] (which renders
16831 /// `[`Self::UnquoteSplice`]` as `"unquote-splice"` — the shorter
16832 /// idiom appropriate for `expected …, got unquote-splice` error
16833 /// surfaces). The two projections key the SAME closed set on TWO
16834 /// distinct boundaries:
16835 ///
16836 /// * `iac_forge_tag` — cross-crate canonical form, BLAKE3 attestation
16837 /// keys, render-cache shape (load-bearing for byte-identical
16838 /// inter-crate compatibility with the iac-forge ecosystem).
16839 /// * `SexpShape::label` — operator-facing diagnostic label,
16840 /// `LispError::TypeMismatch.got` rendering, REPL/LSP
16841 /// shape-of-witness surface.
16842 ///
16843 /// Pre-lift the four canonical iac-forge tag strings lived inline
16844 /// across four arms in `crate::interop` (removed)'s
16845 /// `From<&Sexp> for iac_forge::sexpr::SExpr` impl, paired with the
16846 /// matching `Sexp::{Quote, Quasiquote, Unquote, UnquoteSplice}`
16847 /// patterns. The pairing was load-bearing yet only enforced by
16848 /// callsite discipline at a FOURTH consumer site (alongside `Hash`,
16849 /// `Display`, and `Sexp::as_unquote`) the prior closed-set
16850 /// `QuoteForm` lift did not reach (the `iac-forge` feature gate
16851 /// kept that site's drift risk silent in the default build). After
16852 /// this lift the interop arms collapse to ONE arm routing through
16853 /// [`crate::ast::Sexp::as_quote_form`] + this method, so the
16854 /// (Sexp variant, canonical tag string) pairing binds at ONE site
16855 /// on the substrate algebra regardless of which consumer surface
16856 /// (`Hash`, `Display`, `Sexp::as_unquote`, iac-forge interop)
16857 /// needs it.
16858 ///
16859 /// The `&'static str` lifetime is load-bearing: every iac-forge
16860 /// consumer projects through this method into the canonical
16861 /// 2-element-list head without an allocation, parallel to how
16862 /// [`Self::prefix`], [`UnquoteForm::marker`], and
16863 /// [`crate::error::SexpShape::label`] project their respective
16864 /// closed-set surfaces. A future homoiconic prefix-wrapper (e.g.
16865 /// hypothetical `,~` reverse-unquote) extends [`Self`] AND this
16866 /// method's match arm together — rustc binds the iac-forge
16867 /// canonical-form surface to the algebra through exhaustiveness.
16868 ///
16869 /// Theory anchor: THEORY.md §V.1 — knowable platform; the
16870 /// quote-family canonical-form tag set becomes a TYPE projection
16871 /// on the substrate algebra rather than four `&'static str`
16872 /// literals scattered across the `interop` arms (parallel to how
16873 /// `Self::prefix` lifts the Display↔reader prefix and
16874 /// `Self::hash_discriminator` lifts the cache-key bytes).
16875 /// THEORY.md §VI.1 — generation over composition; the (Sexp
16876 /// variant, iac-forge tag) pairing appeared at the four
16877 /// `interop.rs` arms — past the ≥2 PRIME-DIRECTIVE trigger once
16878 /// the structural shape is named. THEORY.md §II.1 invariant 1 —
16879 /// typed entry; the cross-crate canonical-form projection IS the
16880 /// typed-exit gate at the iac-forge boundary, and naming its
16881 /// closed-set tag identity lifts the gate from per-site literal
16882 /// discipline to ONE method the iac-forge round-trip discipline
16883 /// binds against.
16884 #[must_use]
16885 pub fn iac_forge_tag(self) -> &'static str {
16886 match self {
16887 Self::Quote => Self::QUOTE_IAC_FORGE_TAG,
16888 Self::Quasiquote => Self::QUASIQUOTE_IAC_FORGE_TAG,
16889 Self::Unquote => Self::UNQUOTE_IAC_FORGE_TAG,
16890 Self::UnquoteSplice => Self::UNQUOTE_SPLICE_IAC_FORGE_TAG,
16891 }
16892 }
16893
16894 /// Inverse of [`Self::iac_forge_tag`] on the four-arm canonical CL
16895 /// tag closed set — `"quote"` decodes to `Some(Self::Quote)`,
16896 /// `"quasiquote"` decodes to `Some(Self::Quasiquote)`, `"unquote"`
16897 /// decodes to `Some(Self::Unquote)`, `"unquote-splicing"` decodes to
16898 /// `Some(Self::UnquoteSplice)`. Every other `tag` (empty string,
16899 /// PascalCase drift, the shorter substrate diagnostic label
16900 /// `"unquote-splice"` — which is INTENTIONALLY distinct from the
16901 /// CL canonical `"unquote-splicing"` per the substrate's
16902 /// two-vocabulary axis pinned by
16903 /// `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`,
16904 /// every arbitrary word not in the four-arm image) yields `None`.
16905 ///
16906 /// Structural roundtrip law (pinned by
16907 /// `quote_form_iac_forge_tag_round_trips_through_from_iac_forge_tag`):
16908 /// for every `qf: QuoteForm`,
16909 /// `Self::from_iac_forge_tag(qf.iac_forge_tag()) == Some(qf)`.
16910 /// Sibling posture to [`Self::from_lead_char`]'s inverse-of-
16911 /// [`Self::lead_char`] contract on the reader-lead-char axis —
16912 /// both name the typed inverse decoder on a per-role projection
16913 /// axis of the same closed set, with `Option<Self>` shape mirroring
16914 /// the partial decode over an unbounded string codomain into the
16915 /// four-arm typed closed set.
16916 ///
16917 /// Load-bearing use case: cross-crate iac-forge canonical-form
16918 /// inbound decoding. Pre-lift a consumer parsing a canonical
16919 /// `(<tag> <inner>)` list from an `iac_forge::sexpr::SExpr` (a
16920 /// downstream deserialization codepath, an LSP quick-fix that
16921 /// completes an iac-forge canonical-form skeleton, a
16922 /// `tatara-check` predicate that reads back an attested
16923 /// canonical form and re-typed-witnesses its shape) would have
16924 /// to hand-roll `match tag { "quote" => QuoteForm::Quote,
16925 /// "quasiquote" => …, "unquote-splicing" => …, _ => return None
16926 /// }` at each callsite; post-lift the (tag, typed variant)
16927 /// decode binds at ONE typed method on the substrate algebra
16928 /// composed as a linear sweep over [`Self::ALL`] keyed on
16929 /// [`Self::iac_forge_tag`]. The (tag literals, decode arms)
16930 /// pairing lives at ONE canonical site (the four
16931 /// [`Self::QUOTE_IAC_FORGE_TAG`] / [`Self::QUASIQUOTE_IAC_FORGE_TAG`]
16932 /// / [`Self::UNQUOTE_IAC_FORGE_TAG`] /
16933 /// [`Self::UNQUOTE_SPLICE_IAC_FORGE_TAG`] per-role constants
16934 /// [`Self::iac_forge_tag`]'s outbound arms bind to) rather than
16935 /// at TWO — the outbound projection (existing) plus a hand-rolled
16936 /// inbound decoder duplicated per callsite.
16937 ///
16938 /// Boundary distinction with [`Self::from_str`] (the substrate's
16939 /// [`FromStr`] impl derived via `#[closed_set(via = "prefix")]`):
16940 /// [`Self::from_str`] decodes the reader-punctuation vocabulary
16941 /// (`"'"`, `` "`" ``, `","`, `",@"`); THIS method decodes the
16942 /// cross-crate iac-forge canonical-form vocabulary (`"quote"`,
16943 /// `"quasiquote"`, `"unquote"`, `"unquote-splicing"`). The two
16944 /// closed-set inverse decoders key the SAME four-arm outer set
16945 /// through TWO orthogonal byte vocabularies — pinning them at
16946 /// distinct methods documents the axis-orthogonality
16947 /// [`Self::PREFIXES`] vs [`Self::IAC_FORGE_TAGS`] carries at the
16948 /// per-role forced-arity ALL array level. A consumer with a
16949 /// reader-punctuation byte in hand routes through [`FromStr`];
16950 /// a consumer with an iac-forge canonical tag in hand routes
16951 /// through THIS method — the vocabulary axis binds at the
16952 /// decoder-method boundary rather than at per-consumer inline
16953 /// dispatch.
16954 ///
16955 /// Case-sensitive by design — matches the case-sensitive
16956 /// [`FromStr`] posture (which decodes reader punctuation) and
16957 /// every other closed-set FromStr on the substrate. Non-const
16958 /// because `&str` equality is not const-evaluable on stable at
16959 /// substrate MSRV (parallel to how [`Self::FromStr`]'s decode
16960 /// body is non-const while [`Self::from_lead_char`] is
16961 /// `const fn` because `char` equality IS const-evaluable);
16962 /// callers that need a decode-at-compile-time surface stay on
16963 /// the reader-lead-char decoder.
16964 ///
16965 /// Post-lift the (iac-forge tag, typed variant) inverse decoder
16966 /// closes the FIFTH inverse-projection axis on the outer-`QuoteForm`
16967 /// algebra alongside [`Self::from_lead_char`] (the reader-lead-char
16968 /// axis inverse), [`Self::FromStr`] (the reader-prefix axis
16969 /// inverse, derived via `#[closed_set(via = "prefix")]`),
16970 /// [`crate::error::SexpShape::as_quote_form`] (the outer-shape
16971 /// carving inverse embedding), and
16972 /// [`crate::error::UnquoteForm::to_quote_form`] (the
16973 /// substitution-subset embedding inverse). The full outer
16974 /// quote-family algebra now closes ALL FIVE inverse-projection
16975 /// axes matched with their forward-projection siblings
16976 /// ([`Self::lead_char`] / [`Self::prefix`] / [`Self::sexp_shape`]
16977 /// / [`Self::as_unquote_form`] on the forward side).
16978 ///
16979 /// Theory anchor: THEORY.md §II.1 invariant 3 — typed exit; the
16980 /// inbound iac-forge canonical-form decode surface becomes a
16981 /// TYPE projection on the closed-set [`QuoteForm`] algebra
16982 /// rather than an inline match at every downstream consumer.
16983 /// THEORY.md §V.1 — knowable platform; the closed set of
16984 /// canonical CL tags becomes a decoder codomain rather than
16985 /// four inline `&'static str` literals scattered across future
16986 /// consumers that could drift independently. THEORY.md §VI.1 —
16987 /// generation over composition; the (tag, typed variant)
16988 /// pairing decodes at ONE typed method on the algebra composed
16989 /// from the pre-existing [`Self::ALL`] typed set and the
16990 /// [`Self::iac_forge_tag`] outbound projection — no new
16991 /// per-role primitive, the decode is a typed CONSEQUENCE of
16992 /// the existing family-wide primitives. Sibling posture to
16993 /// [`Self::from_lead_char`] which similarly composes as an
16994 /// inverse over [`Self::lead_char`] without introducing a new
16995 /// per-role primitive.
16996 ///
16997 /// Frontier inspiration: MLIR's typed-attribute
16998 /// `parseType(str) -> Optional<Type>` factory on the closed-set
16999 /// typed-attribute registry — the same inverse-decode shape on
17000 /// a Rust closed-set enum, where the (tag, typed variant)
17001 /// decode binds at ONE typed factory rather than at every
17002 /// downstream operation's parseAttribute callback. Racket's
17003 /// `(assq tag tag-alist)` typed lookup over a closed
17004 /// association list — the inverse decode projects through the
17005 /// ALL array without hand-rolling a per-tag match; `Self::ALL
17006 /// .iter().find(qf.iac_forge_tag() == tag)` is the Rust-typed
17007 /// peer on the closed-set outer-[`QuoteForm`] algebra with the
17008 /// ALL array standing in for Racket's typed association-list
17009 /// spine.
17010 #[must_use]
17011 pub fn from_iac_forge_tag(tag: &str) -> Option<Self> {
17012 Self::ALL
17013 .iter()
17014 .copied()
17015 .find(|qf| qf.iac_forge_tag() == tag)
17016 }
17017
17018 /// Project the typed marker into its matching [`crate::error::SexpShape`]
17019 /// variant — `Quote → SexpShape::Quote`, `Quasiquote → SexpShape::Quasiquote`,
17020 /// `Unquote → SexpShape::Unquote`, `UnquoteSplice → SexpShape::UnquoteSplice`.
17021 /// ONE projection on the closed-set quote-family algebra the substrate's
17022 /// outer-shape projection ([`crate::domain::sexp_shape`]) routes through
17023 /// for the four quote-family arms — so the (Sexp variant, SexpShape
17024 /// variant) pairing binds at ONE site on the typed algebra rather than
17025 /// at four byte-identical inline arms in [`crate::domain::sexp_shape`].
17026 ///
17027 /// The SIXTH consumer of the closed-set [`QuoteForm`] algebra, sibling
17028 /// of [`Self::prefix`] (Display / reader prefix-string surface),
17029 /// [`Self::hash_discriminator`] (Hash cache-key bytes surface),
17030 /// [`Self::as_unquote_form`] (2-of-4 template-substitution subset gate),
17031 /// [`Self::iac_forge_tag`] (cross-crate canonical-form tag surface), and
17032 /// [`Self::wrap`] (reader's marker → `Sexp::*` constructor surface).
17033 /// Composes with [`SexpShape::label`] to yield the short diagnostic
17034 /// label string the substrate's `LispError::TypeMismatch.got` slot
17035 /// renders — the (QuoteForm variant, SexpShape variant, short label)
17036 /// triple binds end-to-end through the typed algebra so a regression
17037 /// that drifts the short label silently between the typed marker and
17038 /// the diagnostic surface is structurally impossible.
17039 ///
17040 /// Bidirectional dual: the inverse projection
17041 /// [`crate::error::SexpShape::as_quote_form`] (12→4, partial)
17042 /// covers the 4-of-12 carving of [`SexpShape`] this embed reaches.
17043 /// The pair `(QuoteForm::sexp_shape,
17044 /// SexpShape::as_quote_form)` forms an `Iso(QuoteForm, QuoteShape ⊂
17045 /// SexpShape)`: every typed marker round-trips through the embed
17046 /// (`QuoteForm::sexp_shape(qf).as_quote_form() == Some(qf)` for
17047 /// every `qf: QuoteForm`), every quote-shape pre-image recovers
17048 /// the typed marker. The non-quote-family shapes (`Nil`, `List`,
17049 /// every atomic-payload variant) form the kernel of the inverse —
17050 /// `as_quote_form` returns `None` for them. See
17051 /// [`crate::error::SexpShape::as_quote_form`]'s docstring for the
17052 /// composition law's other direction + disjointness with the
17053 /// atomic-payload sibling `SexpShape::as_atom_kind`.
17054 ///
17055 /// Canonical [`SexpShape`] embed target for the [`Self::Quote`]
17056 /// quote-family arm on the QuoteForm ⊂ SexpShape carving —
17057 /// [`SexpShape::Quote`]. Per-role peer of `Self::Quote` on the
17058 /// closed-set outer-shape embed axis; consumers with a `QuoteForm`
17059 /// variant in hand at compile time bind the canonical embed target
17060 /// through ONE typed `pub const` per role rather than through
17061 /// runtime dispatch via [`Self::sexp_shape`] or by re-deriving the
17062 /// QuoteForm ⊂ SexpShape variant pairing inline.
17063 ///
17064 /// Sibling posture to [`Self::QUOTE_LABEL`] (the per-role
17065 /// diagnostic label alias) and [`Self::QUOTE_HASH_DISCRIMINATOR`]
17066 /// (the per-role outer-Sexp cache-key byte) on the closed-set
17067 /// QuoteForm algebra — each closes a distinct per-role
17068 /// sub-vocabulary axis on the QuoteForm carving. This constant
17069 /// closes the FOURTH per-role axis on [`QuoteForm`] (the
17070 /// `SexpShape`-embed axis, paired with the pre-existing
17071 /// `&'static str` reader-prefix + diagnostic-label +
17072 /// cross-crate iac-forge-tag axes AND the `u8` cache-key axis) at
17073 /// ONE typed alias through the peer superset variant on the
17074 /// [`SexpShape`] closed set.
17075 ///
17076 /// Sibling posture to the peer 6-of-12 atomic-payload carving's
17077 /// per-role SHAPE aliases ([`AtomKind::SYMBOL_SHAPE`] …
17078 /// [`AtomKind::BOOL_SHAPE`] — every one an alias of its
17079 /// [`SexpShape`] peer on the AtomKind ⊂ SexpShape 6-of-12
17080 /// carving). Post-lift the SexpShape's per-carving embed-target
17081 /// axis is uniformly surfaced through per-role `pub const *_SHAPE`
17082 /// aliases on every sub-carving that carries a bidirectional
17083 /// (embed, project) `Iso(_, _ ⊂ SexpShape)` — first `AtomKind` (6),
17084 /// now `QuoteForm` (4).
17085 pub const QUOTE_SHAPE: SexpShape = SexpShape::Quote;
17086
17087 /// Canonical [`SexpShape`] embed target for the [`Self::Quasiquote`]
17088 /// quote-family arm on the QuoteForm ⊂ SexpShape carving —
17089 /// [`SexpShape::Quasiquote`]. Per-role peer of `Self::Quasiquote`.
17090 /// See [`Self::QUOTE_SHAPE`] for the alias-chain shape every
17091 /// sibling shares.
17092 pub const QUASIQUOTE_SHAPE: SexpShape = SexpShape::Quasiquote;
17093
17094 /// Canonical [`SexpShape`] embed target for the [`Self::Unquote`]
17095 /// quote-family arm on the QuoteForm ⊂ SexpShape carving —
17096 /// [`SexpShape::Unquote`]. Per-role peer of `Self::Unquote`.
17097 pub const UNQUOTE_SHAPE: SexpShape = SexpShape::Unquote;
17098
17099 /// Canonical [`SexpShape`] embed target for the [`Self::UnquoteSplice`]
17100 /// quote-family arm on the QuoteForm ⊂ SexpShape carving —
17101 /// [`SexpShape::UnquoteSplice`]. Per-role peer of
17102 /// `Self::UnquoteSplice`.
17103 pub const UNQUOTE_SPLICE_SHAPE: SexpShape = SexpShape::UnquoteSplice;
17104
17105 /// Closed-set forced-arity ALL array over the canonical
17106 /// [`SexpShape`] embed targets on the QuoteForm ⊂ SexpShape
17107 /// 4-of-12 carving, in declaration order matching [`Self::ALL`]
17108 /// element-wise (pinned by
17109 /// `quote_form_shapes_align_with_all_by_index`). Sibling posture
17110 /// to [`Self::LABELS`] (`[&'static str; 4]` — per-role diagnostic
17111 /// bytes), [`Self::PREFIXES`] (`[&'static str; 4]` — per-role
17112 /// reader-punctuation bytes), [`Self::IAC_FORGE_TAGS`] (`[&'static
17113 /// str; 4]` — cross-crate iac-forge canonical-form tag bytes), and
17114 /// [`Self::HASH_DISCRIMINATORS`] (`[u8; 4]` — per-role outer-Sexp
17115 /// cache-key bytes) on the SAME closed-set QuoteForm algebra;
17116 /// where those four arrays lift per-role `&'static str` and `u8`
17117 /// sub-vocabularies onto the substrate, this array lifts the
17118 /// per-role [`SexpShape`] embed-target sub-vocabulary at the same
17119 /// `[_; 4]` forced arity.
17120 ///
17121 /// Sibling posture to [`AtomKind::SHAPES`] (`[SexpShape; 6]`) —
17122 /// the peer atomic-payload carving's family-wide embed-target
17123 /// array on the AtomKind ⊂ SexpShape 6-of-12 carving. Together the
17124 /// two `SHAPES` arrays cover the TWO bidirectional sub-carvings of
17125 /// [`SexpShape`] (`Iso(AtomKind, AtomShape ⊂ SexpShape)` + `Iso(QuoteForm,
17126 /// QuoteShape ⊂ SexpShape)`) — a family-wide sweep zipping every
17127 /// carving's `ALL` + `SHAPES` in lockstep now closes over TWO
17128 /// carvings' 10-of-12 embed targets at ONE typed pair-of-arrays
17129 /// each.
17130 ///
17131 /// Pre-lift the four [`SexpShape`] embed targets had NO per-role
17132 /// primitive on this closed-set algebra — a consumer with a
17133 /// `QuoteForm` variant in hand at compile time reaching for the
17134 /// canonical embed target had to spell
17135 /// `QuoteForm::Quote.sexp_shape()` (runtime dispatch through the
17136 /// four-arm match body) OR re-derive the QuoteForm ⊂ SexpShape
17137 /// variant pairing at the call site by importing both enums and
17138 /// spelling `SexpShape::Quote` inline. Post-lift the FOUR canonical
17139 /// embed targets bind at ONE `pub const` per role on the typed
17140 /// [`QuoteForm`] algebra AND at [`Self::SHAPES`] as a family-wide
17141 /// forced-arity array — a future LSP / REPL completion bar keyed
17142 /// on `QuoteForm::SHAPES` for the "which SexpShape does this
17143 /// QuoteForm embed into?" outer-shape column, a `tatara-check`
17144 /// coverage sweep zipping `QuoteForm::ALL` / `LABELS` / `PREFIXES`
17145 /// / `IAC_FORGE_TAGS` / `HASH_DISCRIMINATORS` / `SHAPES` in
17146 /// lockstep for a family-wide (variant, label, prefix, iac-forge
17147 /// tag, byte, embed-target) sextuple render, or a Sekiban
17148 /// audit-trail metric jointly labeled by the embed-target's
17149 /// SexpShape identity reads through the typed constants on this
17150 /// subset algebra without re-deriving the 4-of-12 carving inline.
17151 ///
17152 /// Round-trip identity with the inverse projection
17153 /// [`crate::error::SexpShape::as_quote_form`]: for every index `i`,
17154 /// `Self::SHAPES[i].as_quote_form() == Some(Self::ALL[i])`
17155 /// (pinned by
17156 /// `quote_form_shapes_align_with_all_by_index_through_as_quote_form`) —
17157 /// the embed / project section closes as a family-wide array-
17158 /// indexed law rather than as a per-variant assertion sweep.
17159 /// Adding a hypothetical fifth quote-family wrapper (e.g. `,~`
17160 /// reverse-unquote, `,?` conditional-unquote, `#'` Common-Lisp
17161 /// function-quote) extends [`Self::ALL`] AND [`Self::SHAPES`] AND
17162 /// [`SexpShape::ALL`] AND adds ONE per-role `pub const *_SHAPE` in
17163 /// lockstep — rustc's forced-arity check on the two `[_; N]`
17164 /// arrays fails compilation if EITHER ALL array grows without the
17165 /// other, AND the peer [`SexpShape::as_quote_form`] arm must grow
17166 /// in lockstep to preserve the round-trip identity.
17167 ///
17168 /// Theory anchor: THEORY.md §III — the typescape; the four
17169 /// canonical [`SexpShape`] embed targets bind at ONE typed
17170 /// `[SexpShape; 4]` array on the closed-set QuoteForm algebra
17171 /// rather than at zero-primitive-on-this-subset-plus-four-inline-
17172 /// lookups scattered across the substrate. Closes the FOURTH
17173 /// per-role `pub const` axis on the QuoteForm carving alongside
17174 /// the pre-existing LABELS + PREFIXES + IAC_FORGE_TAGS +
17175 /// HASH_DISCRIMINATORS axes. THEORY.md §V.1 — knowable platform;
17176 /// the family's cardinality becomes a TYPE-level constant on the
17177 /// substrate algebra rather than a per-consumer runtime dispatch
17178 /// through the composition. THEORY.md §II.1 invariant 2 — free
17179 /// middle; the (embed, project) pair binds at THREE typed sites
17180 /// now — the projection method [`Self::sexp_shape`], this family-
17181 /// wide array, AND the peer inverse
17182 /// [`crate::error::SexpShape::as_quote_form`] — with rustc-enforced
17183 /// consistency across all three. THEORY.md §VI.1 — generation
17184 /// over composition; the family-wide contract sweeps (alignment
17185 /// with `ALL`, round-trip through `as_quote_form`, membership
17186 /// through `sexp_shape`, pairwise injectivity across the four
17187 /// embed targets) emerge from the composition of TWO substrate
17188 /// primitives (this `pub const` array + the four per-role
17189 /// `pub const *_SHAPE` aliases) rather than as per-variant inline
17190 /// assertions duplicated at each call site.
17191 pub const SHAPES: [SexpShape; 4] = [
17192 Self::QUOTE_SHAPE,
17193 Self::QUASIQUOTE_SHAPE,
17194 Self::UNQUOTE_SHAPE,
17195 Self::UNQUOTE_SPLICE_SHAPE,
17196 ];
17197
17198 /// Project the typed marker into its matching [`crate::error::SexpShape`]
17199 /// variant — `Quote → SexpShape::Quote`, `Quasiquote → SexpShape::Quasiquote`,
17200 /// `Unquote → SexpShape::Unquote`, `UnquoteSplice → SexpShape::UnquoteSplice`.
17201 /// ONE projection on the closed-set quote-family algebra the substrate's
17202 /// outer-shape projection ([`crate::domain::sexp_shape`]) routes through
17203 /// for the four quote-family arms — so the (Sexp variant, SexpShape
17204 /// variant) pairing binds at ONE site on the typed algebra rather than
17205 /// at four byte-identical inline arms in [`crate::domain::sexp_shape`].
17206 ///
17207 /// The SIXTH consumer of the closed-set [`QuoteForm`] algebra, sibling
17208 /// of [`Self::prefix`] (Display / reader prefix-string surface),
17209 /// [`Self::hash_discriminator`] (Hash cache-key bytes surface),
17210 /// [`Self::as_unquote_form`] (2-of-4 template-substitution subset gate),
17211 /// [`Self::iac_forge_tag`] (cross-crate canonical-form tag surface), and
17212 /// [`Self::wrap`] (reader's marker → `Sexp::*` constructor surface).
17213 /// Composes with [`SexpShape::label`] to yield the short diagnostic
17214 /// label string the substrate's `LispError::TypeMismatch.got` slot
17215 /// renders — the (QuoteForm variant, SexpShape variant, short label)
17216 /// triple binds end-to-end through the typed algebra so a regression
17217 /// that drifts the short label silently between the typed marker and
17218 /// the diagnostic surface is structurally impossible.
17219 ///
17220 /// Each arm routes through the per-role `pub const` on `impl Self`
17221 /// ([`Self::QUOTE_SHAPE`], [`Self::QUASIQUOTE_SHAPE`],
17222 /// [`Self::UNQUOTE_SHAPE`], [`Self::UNQUOTE_SPLICE_SHAPE`]) so the
17223 /// four canonical embed targets bind at ONE typed source of truth
17224 /// per role rather than as inline `SexpShape::X` literals scattered
17225 /// across the `match` body. Sibling posture to
17226 /// [`AtomKind::sexp_shape`]'s post-lift routing through
17227 /// [`AtomKind::SYMBOL_SHAPE`] … [`AtomKind::BOOL_SHAPE`] on the peer
17228 /// 6-of-12 atomic-payload carving — the per-role `pub const *_SHAPE`
17229 /// routing is now uniform across every sub-carving of [`SexpShape`]
17230 /// that has a bidirectional (embed, project) isomorphism, closing
17231 /// the (embed-target constant, embed-target array, projection
17232 /// method) trio on each sub-carving in lockstep.
17233 ///
17234 /// Post-lift routing pin
17235 /// `quote_form_sexp_shape_routes_through_typed_per_role_constants`
17236 /// catches a regression that re-inlines the four `SexpShape::X` arm
17237 /// literals here and silently drifts ONE arm from the per-role
17238 /// `pub const` alias — the routing agreement is a TYPED CONSEQUENCE
17239 /// of the composition rather than literal discipline at two sites.
17240 ///
17241 /// Bidirectional dual: the inverse projection
17242 /// [`crate::error::SexpShape::as_quote_form`] (12→4, partial)
17243 /// covers the 4-of-12 carving of [`SexpShape`] this embed reaches.
17244 /// The pair `(QuoteForm::sexp_shape,
17245 /// SexpShape::as_quote_form)` forms an `Iso(QuoteForm, QuoteShape ⊂
17246 /// SexpShape)`: every typed marker round-trips through the embed
17247 /// (`QuoteForm::sexp_shape(qf).as_quote_form() == Some(qf)` for
17248 /// every `qf: QuoteForm`), every quote-shape pre-image recovers
17249 /// the typed marker. The non-quote-family shapes (`Nil`, `List`,
17250 /// every atomic-payload variant) form the kernel of the inverse —
17251 /// `as_quote_form` returns `None` for them. See
17252 /// [`crate::error::SexpShape::as_quote_form`]'s docstring for the
17253 /// composition law's other direction + disjointness with the
17254 /// atomic-payload sibling `SexpShape::as_atom_kind`.
17255 ///
17256 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (QuoteForm
17257 /// variant, SexpShape variant) pairing becomes a TYPE projection on
17258 /// the substrate algebra rather than four inline arms in
17259 /// [`crate::domain::sexp_shape`]. A typo or swap at the shape-projection
17260 /// site is no longer a runtime drift but a compile error against the
17261 /// typed projection. THEORY.md §II.1 invariant 2 — free middle; SIX
17262 /// consumers of the [`QuoteForm`] algebra now route through ONE typed
17263 /// closed-set match family, so a regression that drifts ONE consumer's
17264 /// pairing from the others cannot reach the substrate's runtime.
17265 /// THEORY.md §VI.1 — generation over composition; the (Sexp variant,
17266 /// SexpShape variant) pairing appeared at four arms in `sexp_shape` —
17267 /// past the ≥2 PRIME-DIRECTIVE trigger once the structural shape is
17268 /// named.
17269 #[must_use]
17270 pub fn sexp_shape(self) -> SexpShape {
17271 match self {
17272 Self::Quote => Self::QUOTE_SHAPE,
17273 Self::Quasiquote => Self::QUASIQUOTE_SHAPE,
17274 Self::Unquote => Self::UNQUOTE_SHAPE,
17275 Self::UnquoteSplice => Self::UNQUOTE_SPLICE_SHAPE,
17276 }
17277 }
17278
17279 /// Project the typed marker to its canonical short diagnostic label —
17280 /// `"quote"` for [`Self::Quote`], `"quasiquote"` for
17281 /// [`Self::Quasiquote`], `"unquote"` for [`Self::Unquote`],
17282 /// `"unquote-splice"` for [`Self::UnquoteSplice`]. Body composes
17283 /// through `self.sexp_shape().label()` — routing through
17284 /// [`Self::sexp_shape`] (the typed 4-of-12 outer-value → SexpShape
17285 /// projection) then [`SexpShape::label`] (the canonical 12-arm
17286 /// diagnostic-label projection) so the (QuoteForm variant, short
17287 /// diagnostic string) pairing lives at ONE canonical site
17288 /// ([`SexpShape::label`]'s four quote-family arms in `error.rs`)
17289 /// rather than at four inline `&'static str` arms on the closed-set
17290 /// `QuoteForm` algebra.
17291 ///
17292 /// The outer-shape peer of [`crate::ast::Sexp::type_name`] one
17293 /// algebra layer up (`self.shape().label()` on outer-`Sexp`) and of
17294 /// [`crate::ast::Atom::label`] one algebra layer down
17295 /// (`self.kind().label()` on outer-`Atom` through [`AtomKind`]).
17296 /// Where `Atom::label` composes through the atomic-payload 6-of-12
17297 /// carving via [`AtomKind`] into [`SexpShape::label`], this method
17298 /// composes through the quote-family 4-of-12 carving directly onto
17299 /// [`SexpShape::label`] — the (label, sexp_shape, hash_discriminator)
17300 /// trio the outer-`Atom` algebra closed one lift back
17301 /// (`Atom::hash_discriminator`, e49f550) is now mirrored on the
17302 /// `QuoteForm` algebra: `prefix` (reader punctuation) and
17303 /// `iac_forge_tag` (CL canonical form) key the SAME closed set on
17304 /// their own boundaries, and `label` keys it on the substrate's
17305 /// operator-facing diagnostic boundary.
17306 ///
17307 /// Composition law: `qf.label() == qf.sexp_shape().label()` for every
17308 /// `qf: QuoteForm`. Pinned by
17309 /// `quote_form_label_composes_through_sexp_shape_label_for_every_variant`
17310 /// across all four variants — the pin asserts pointer-equality on the
17311 /// returned `&'static str` so a regression that re-inlines the four
17312 /// literals here (and gains its own drift surface separate from the
17313 /// canonical [`SexpShape::label`] site) surfaces immediately. Sibling
17314 /// of `atom_label_composes_through_kind_label_for_every_variant` one
17315 /// algebra layer down (on the outer-`Atom` value / `AtomKind` marker
17316 /// pair) and
17317 /// `sexp_type_name_method_composes_through_shape_label_for_every_outer_shape`
17318 /// one algebra layer up (on the outer-`Sexp` value / `SexpShape`
17319 /// marker pair).
17320 ///
17321 /// Cross-algebra agreement law: for every `qf: QuoteForm` and every
17322 /// `inner: Sexp`, `qf.label() == qf.wrap(inner).type_name()`. The
17323 /// (QuoteForm variant, canonical label) pairing lands at the SAME
17324 /// `&'static str` regardless of whether the consumer holds the typed
17325 /// marker directly or an outer-`Sexp` wrapper produced from
17326 /// [`Self::wrap`] — so a regression that drifts one algebra layer's
17327 /// label from the other (a `QuoteForm::label` re-inlined onto a
17328 /// different literal, a `Sexp::type_name` re-routed through a stale
17329 /// shape projection, a `QuoteForm::sexp_shape` arm that swaps two
17330 /// markers) fails-loudly here rather than as a silent operator-facing
17331 /// diagnostic drift at every consumer that pattern-matches on the
17332 /// outer-`Sexp` label vs the outer-`QuoteForm` label independently.
17333 /// Pinned by `quote_form_label_agrees_with_sexp_type_name_at_every_quote_form_arm`.
17334 ///
17335 /// Divergence law (boundary distinction with [`Self::iac_forge_tag`]):
17336 /// at the [`Self::UnquoteSplice`] arm, `qf.label() == "unquote-splice"`
17337 /// while `qf.iac_forge_tag() == "unquote-splicing"`. The two
17338 /// projections key the SAME closed-set on TWO distinct boundaries
17339 /// (substrate diagnostic surface vs cross-crate CL canonical form)
17340 /// and their intentional divergence at the `Splice` arm is pinned by
17341 /// `quote_form_label_diverges_from_iac_forge_tag_for_unquote_splice`
17342 /// — sibling posture to
17343 /// `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`
17344 /// which pinned the divergence at the `sexp_shape().label()`
17345 /// composition; this pin lifts the divergence contract onto the new
17346 /// typed peer.
17347 ///
17348 /// The `&'static str` lifetime is load-bearing: every future consumer
17349 /// with a `QuoteForm` in hand wanting the substrate's short
17350 /// diagnostic string projects through this method into the
17351 /// `LispError::TypeMismatch.got` slot / REPL / LSP surface without an
17352 /// allocation, parallel to how [`Self::prefix`] projects the reader
17353 /// punctuation and [`Self::iac_forge_tag`] projects the CL canonical
17354 /// tag. A future homoiconic prefix-wrapper (e.g. hypothetical `,~`
17355 /// reverse-unquote) extends [`Self`] AND [`SexpShape::label`]
17356 /// together — rustc binds the diagnostic surface to the algebra
17357 /// through the closed-set composition without touching this method.
17358 ///
17359 /// Theory anchor: THEORY.md §V.1 — knowable platform; the (QuoteForm
17360 /// variant, canonical short label) pairing becomes a TYPE projection
17361 /// on the substrate algebra composed through the pre-existing outer-
17362 /// shape projection, rather than at a per-callsite
17363 /// `.sexp_shape().label()` two-hop the load-bearing pin already
17364 /// carries as a composition-law contract. THEORY.md §II.1 invariant 2
17365 /// — free middle; the outer-`QuoteForm` diagnostic-label algebra now
17366 /// closes over THREE typed layers (`QuoteForm` → [`SexpShape`] →
17367 /// `&'static str`) with rustc-enforced consistency across each — a
17368 /// regression that drifts ONE layer's mapping from the others cannot
17369 /// reach the substrate's runtime typed-witness surface,
17370 /// `LispError::TypeMismatch.got` slot, or [`crate::error::SexpWitness::shape`]
17371 /// projection. THEORY.md §VI.1 — generation over composition; the
17372 /// outer-value diagnostic-label projection is the missing algebra
17373 /// layer between the outer `QuoteForm` and the pre-existing marker-
17374 /// level label projection — the two pre-existing typed layers become
17375 /// a full THREE-layer typed composition through ONE new named
17376 /// projection, closing the (prefix, iac_forge_tag, sexp_shape,
17377 /// hash_discriminator, label) quintet on the outer-`QuoteForm`
17378 /// algebra.
17379 ///
17380 /// Frontier inspiration: MLIR's `mlir::OperationName::getStringRef()`
17381 /// composed with an op-family typed projection — narrowing a
17382 /// closed-set op-family value through its typed identity yields the
17383 /// canonical diagnostic string identity in ONE typed composition on
17384 /// the op-family algebra. Translated through the substrate's
17385 /// [`QuoteForm`] outer-marker algebra, `qf.sexp_shape().label()`
17386 /// closes the (typed marker, canonical diagnostic label) pairing at
17387 /// ONE typed projection on the marker algebra composed through the
17388 /// outer-shape's per-carving canonical site. Racket's `(quote-kind
17389 /// qf)` composed with `(kind-label kind)` on the quote-family
17390 /// taxonomy — the typed diagnostic label emerges from a two-hop
17391 /// composition on the closed-set marker through the typed outer-shape
17392 /// identity. `QuoteForm::label` is the Rust-typed peer on the
17393 /// closed-set outer-[`QuoteForm`] algebra with [`SexpShape`] standing
17394 /// in for Racket's quote-family taxonomy.
17395 #[must_use]
17396 pub fn label(self) -> &'static str {
17397 self.sexp_shape().label()
17398 }
17399
17400 /// Canonical `&'static str` bytes for the [`Self::Quote`] quote-family
17401 /// marker — aliases [`SexpShape::QUOTE_LABEL`] on the QuoteForm ⊂
17402 /// SexpShape carving so the marker-level per-role bytes bind at ONE
17403 /// `pub const` on the parent superset's quote-family arm rather than
17404 /// at TWO sites (the per-role `pub const` AND a parallel inline
17405 /// literal). Per-role peer of `Self::Quote` on the closed-set quote-
17406 /// family algebra; consumers reach for `QuoteForm::QUOTE_LABEL` when
17407 /// the caller has a variant in hand at compile time and wants the
17408 /// canonical diagnostic bytes without runtime dispatch through
17409 /// [`Self::label`].
17410 ///
17411 /// Sibling posture to the peer 6-of-12 atomic-payload carving's per-
17412 /// role LABEL aliases ([`crate::ast::AtomKind::SYMBOL_LABEL`] …
17413 /// [`crate::ast::AtomKind::BOOL_LABEL`] — every one an alias of its
17414 /// [`SexpShape`] peer) and the peer 2-of-12 structural-residual
17415 /// carving's per-role LABEL aliases
17416 /// ([`crate::error::StructuralKind::NIL_LABEL`] +
17417 /// [`crate::error::StructuralKind::LIST_LABEL`]) — this closes the
17418 /// fourth and final closed-set sub-carving of [`SexpShape`] whose
17419 /// per-role diagnostic-label bytes are surfaced through the same
17420 /// alias-chain shape rather than reachable only through the
17421 /// composition [`Self::sexp_shape`] + [`SexpShape::label`]. Every
17422 /// SexpShape sub-carving (atomic payload, quote family, structural
17423 /// residual) now exposes its per-role LABEL bytes at ONE `pub const`
17424 /// per role on its subset algebra AS WELL AS at the parent
17425 /// superset's `SexpShape::*_LABEL`.
17426 ///
17427 /// The prefix-family peer of THIS `&'static str` constant is
17428 /// [`Self::QUOTE_PREFIX`] (`"'"` — reader-punctuation byte); the
17429 /// canonical-form peer is [`Self::QUOTE_IAC_FORGE_TAG`] (`"quote"` —
17430 /// cross-crate iac-forge tag). At the `Quote` arm the label and
17431 /// the iac-forge tag agree byte-for-byte (`"quote"`); the divergence
17432 /// axis lives at [`Self::UNQUOTE_SPLICE_LABEL`] (`"unquote-splice"`)
17433 /// vs [`Self::UNQUOTE_SPLICE_IAC_FORGE_TAG`] (`"unquote-splicing"`).
17434 /// The three parallel per-role `pub const` families (prefix, label,
17435 /// iac-forge tag) close the (reader, diagnostic, canonical-form)
17436 /// triple on the outer-`QuoteForm` algebra.
17437 pub const QUOTE_LABEL: &'static str = SexpShape::QUOTE_LABEL;
17438
17439 /// Canonical `&'static str` bytes for the [`Self::Quasiquote`] quote-
17440 /// family marker — aliases [`SexpShape::QUASIQUOTE_LABEL`] on the
17441 /// QuoteForm ⊂ SexpShape carving. Per-role peer of `Self::Quasiquote`.
17442 /// See [`Self::QUOTE_LABEL`] for the alias-chain shape every sibling
17443 /// shares.
17444 pub const QUASIQUOTE_LABEL: &'static str = SexpShape::QUASIQUOTE_LABEL;
17445
17446 /// Canonical `&'static str` bytes for the [`Self::Unquote`] quote-
17447 /// family marker — aliases [`SexpShape::UNQUOTE_LABEL`] on the
17448 /// QuoteForm ⊂ SexpShape carving. Per-role peer of `Self::Unquote`.
17449 /// See [`Self::QUOTE_LABEL`] for the alias-chain shape every sibling
17450 /// shares.
17451 pub const UNQUOTE_LABEL: &'static str = SexpShape::UNQUOTE_LABEL;
17452
17453 /// Canonical `&'static str` bytes for the [`Self::UnquoteSplice`]
17454 /// quote-family marker — aliases [`SexpShape::UNQUOTE_SPLICE_LABEL`]
17455 /// on the QuoteForm ⊂ SexpShape carving. Per-role peer of
17456 /// `Self::UnquoteSplice`; the `"unquote-splice"` short label matches
17457 /// [`SexpShape::UNQUOTE_SPLICE_LABEL`] byte-for-byte and diverges
17458 /// INTENTIONALLY from [`Self::UNQUOTE_SPLICE_IAC_FORGE_TAG`]
17459 /// (`"unquote-splicing"`) — the two projections key the SAME closed
17460 /// set on TWO distinct boundaries (substrate diagnostic surface vs
17461 /// cross-crate Common-Lisp canonical form). The divergence is pinned
17462 /// by `quote_form_label_diverges_from_iac_forge_tag_for_unquote_splice`
17463 /// on the runtime projection and by the byte-equality pins on THIS
17464 /// constant vs its iac-forge peer on the per-role `pub const`
17465 /// surface. See [`Self::QUOTE_LABEL`] for the alias-chain shape
17466 /// every sibling shares.
17467 pub const UNQUOTE_SPLICE_LABEL: &'static str = SexpShape::UNQUOTE_SPLICE_LABEL;
17468
17469 /// Closed-set forced-arity ALL array over the canonical quote-family
17470 /// marker `&'static str` bytes, in declaration order matching
17471 /// [`Self::ALL`] element-wise (pinned by
17472 /// `quote_form_labels_align_with_all_by_index`). Sibling posture to
17473 /// [`crate::error::SexpShape::LABELS`] (`[&'static str; 12]` — the
17474 /// superset carving this QuoteForm subset embeds into),
17475 /// [`crate::ast::AtomKind::LABELS`] (`[&'static str; 6]` — the peer
17476 /// 6-of-12 atomic-payload carving's ALL array),
17477 /// [`crate::error::StructuralKind::LABELS`] (`[&'static str; 2]` —
17478 /// the peer 2-of-12 structural-residual carving's ALL array),
17479 /// [`Self::PREFIXES`] (`[&'static str; 4]` — reader-prefix axis on
17480 /// this same algebra), and [`Self::IAC_FORGE_TAGS`]
17481 /// (`[&'static str; 4]` — canonical-form tag axis on this same
17482 /// algebra) — every closed-set outer projection on the substrate
17483 /// that carries an `&'static str`-per-variant label now pins its
17484 /// per-role canonical bytes at ONE `pub const` per role PLUS an ALL
17485 /// array for family-wide consumers.
17486 ///
17487 /// Pre-lift the four quote-family marker labels had NO per-role
17488 /// primitive on this closed-set algebra — a consumer with a
17489 /// [`QuoteForm`] variant in hand at compile time reaching for the
17490 /// canonical diagnostic bytes had to spell `QuoteForm::Quote.label()`
17491 /// (runtime dispatch through the composition [`Self::sexp_shape`] +
17492 /// [`SexpShape::label`]) OR reach across the algebra boundary into
17493 /// [`SexpShape::QUOTE_LABEL`] and re-derive the QuoteForm ⊂
17494 /// SexpShape variant pairing at the call site. Post-lift the FOUR
17495 /// canonical labels bind at ONE `pub const` per role on the typed
17496 /// [`QuoteForm`] algebra AND at [`Self::LABELS`] as a family-wide
17497 /// forced-arity array — a future LSP / REPL completion bar keyed on
17498 /// `QuoteForm::LABELS` for the "quote-family" carving-axis column,
17499 /// a `tatara-check` coverage sweep over the quote-family arms of a
17500 /// `TypeMismatch.got` corpus, or a Sekiban audit-trail metric
17501 /// jointly labeled by the quote-family marker
17502 /// (`tatara_lisp_quote_family_label_total{label="quote"}`) reads
17503 /// through the typed constants on this subset algebra without re-
17504 /// deriving the 4-of-12 carving inline OR reaching across into the
17505 /// superset's twelve-entry `SexpShape::LABELS` array + filtering.
17506 ///
17507 /// Each entry is byte-for-byte identical to the corresponding
17508 /// [`SexpShape`] quote-family arm — an intentional cross-axis
17509 /// overlap pinned by
17510 /// `quote_form_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
17511 /// so a future label rename on EITHER side (a `SexpShape`
17512 /// `"quote"` → `"cite"` drift, a `QuoteForm` rename that skips the
17513 /// alias, a hypothetical Racket-compat swap of `"quasiquote"`)
17514 /// fails-loudly at the alias test rather than as a silent operator-
17515 /// facing vocabulary fracture. Adding a hypothetical fifth
17516 /// homoiconic prefix-wrapper (a `,~` reverse-unquote, a `,?`
17517 /// conditional-unquote, a `#'` Common-Lisp function-quote) extends
17518 /// [`Self::ALL`] AND [`Self::LABELS`] AND adds ONE per-role
17519 /// `pub const` alias in lockstep — rustc's forced-arity check on
17520 /// the two `[_; N]` arrays fails compilation if EITHER ALL array
17521 /// grows without the other.
17522 ///
17523 /// Theory anchor: THEORY.md §III — the typescape; the four
17524 /// canonical quote-family marker labels bind at ONE typed
17525 /// `[&'static str; 4]` array on the closed-set [`QuoteForm`]
17526 /// algebra rather than at zero-primitive-on-this-subset-plus-four-
17527 /// inline-lookups scattered across the substrate. Closes the
17528 /// fourth SexpShape sub-carving's per-role LABEL parity with
17529 /// [`AtomKind`] and [`crate::error::StructuralKind`]. THEORY.md
17530 /// §V.1 — knowable platform; the family's cardinality becomes a
17531 /// TYPE-level constant on the substrate algebra rather than a per-
17532 /// consumer runtime dispatch through the composition. The alias-
17533 /// chain shape is load-bearing: a [`SexpShape`]-side rename
17534 /// propagates through the const-eval alias chain byte-for-byte
17535 /// without silent drift. THEORY.md §VI.1 — generation over
17536 /// composition; the family-wide contract sweeps (alignment with
17537 /// [`Self::ALL`], pairwise disjointness, membership through
17538 /// [`Self::label`]) emerge from the composition of TWO substrate
17539 /// primitives (this `pub const` array + the four per-role
17540 /// `pub const *_LABEL` aliases) rather than as per-variant inline
17541 /// assertions duplicated at each call site. THEORY.md §II.1
17542 /// invariant 5 — composition preserves proofs; the alias-chain
17543 /// composition law `QuoteForm::LABELS[i] ==
17544 /// QuoteForm::ALL[i].sexp_shape().label()` binds the family-wide
17545 /// array to the composition through [`Self::sexp_shape`] +
17546 /// [`SexpShape::label`] at rustc time.
17547 pub const LABELS: [&'static str; 4] = [
17548 Self::QUOTE_LABEL,
17549 Self::QUASIQUOTE_LABEL,
17550 Self::UNQUOTE_LABEL,
17551 Self::UNQUOTE_SPLICE_LABEL,
17552 ];
17553
17554 /// Project the typed marker back into its matching `Sexp::*` wrapper
17555 /// variant applied to `inner` — the structural inverse of
17556 /// [`crate::ast::Sexp::as_quote_form`]. [`Self::Quote`] yields
17557 /// [`Sexp::Quote`], [`Self::Quasiquote`] yields [`Sexp::Quasiquote`],
17558 /// [`Self::Unquote`] yields [`Sexp::Unquote`], [`Self::UnquoteSplice`]
17559 /// yields [`Sexp::UnquoteSplice`], each boxing `inner` into the
17560 /// corresponding tuple-variant constructor (`fn(Box<Sexp>) -> Sexp`).
17561 ///
17562 /// Round-trip identity with [`crate::ast::Sexp::as_quote_form`] — the
17563 /// structural law every consumer can pin against:
17564 ///
17565 /// ```ignore
17566 /// // for every (qf, inner): qf.wrap(inner.clone()).as_quote_form() == Some((qf, &inner))
17567 /// // for every Sexp s matching the quote family:
17568 /// // let (qf, inner) = s.as_quote_form().unwrap();
17569 /// // qf.wrap(inner.clone()) == s
17570 /// ```
17571 ///
17572 /// Consumer: [`crate::reader::read_quoted`] — the FIFTH consumer site
17573 /// of the closed-set `QuoteForm` algebra (sibling to `Hash for Sexp`'s
17574 /// `hash_discriminator` arm, `Display for Sexp`'s `prefix` arm,
17575 /// `Sexp::as_unquote`'s `as_unquote_form` subset-gate composition, and
17576 /// the feature-gated `From<&Sexp> for iac_forge::SExpr`'s
17577 /// `iac_forge_tag` arm). Pre-lift the reader's parse dispatch carried
17578 /// its own parallel closed set: a local `Token::{Quote, Quasiquote,
17579 /// Unquote, UnquoteSplice}` enum paired with the matching `Sexp::*`
17580 /// tuple-variant constructors threaded as `fn(Box<Sexp>) -> Sexp`
17581 /// arguments to `read_quoted`. The (Token variant, Sexp::* constructor)
17582 /// pairing was load-bearing yet only enforced by callsite discipline
17583 /// at the FIFTH consumer site the prior `QuoteForm` lifts did not
17584 /// reach — a regression that swapped `Sexp::Quote` and
17585 /// `Sexp::Quasiquote` between the parser arms type-checked but
17586 /// silently corrupted every program's quote-family parse.
17587 ///
17588 /// Post-lift the reader's `Token` collapses to ONE typed variant
17589 /// `Token::Quoted(QuoteForm)`, the parser's four prefix arms collapse
17590 /// to ONE arm `Some((Token::Quoted(qf), _)) => read_quoted(it,
17591 /// eof_pos, qf)`, and `read_quoted` routes through this projection to
17592 /// produce the matching `Sexp::*` variant. The (QuoteForm variant,
17593 /// Sexp::* constructor) pairing now binds at ONE site on the typed
17594 /// algebra — rustc enforces exhaustiveness across [`Self`]'s closed
17595 /// set, so a regression that drifts the (marker, constructor) pair
17596 /// becomes a typed compile error rather than a silent program-text
17597 /// corruption.
17598 ///
17599 /// The `Sexp` (owned) return type complements [`Sexp::as_quote_form`]'s
17600 /// `&Sexp` (borrowed) — `wrap` consumes the inner body to build the
17601 /// new wrapper, `as_quote_form` borrows the inner body from the
17602 /// existing wrapper. The asymmetry is intentional: at the reader's
17603 /// parse-then-wrap boundary the inner is fresh from `parse(...)?` and
17604 /// has no caller-owned binding; the typed `Box::new(inner)` allocation
17605 /// lives at ONE site rather than four (one per pre-lift parser arm),
17606 /// so a future allocation-policy change (e.g. arena-allocated wrappers
17607 /// for span-aware Sexp) lands as ONE edit.
17608 ///
17609 /// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
17610 /// reader's prefix-token → Sexp-wrapper gate IS the rust-level
17611 /// typed-entry gate at the source-text boundary, and naming the
17612 /// typed projection from [`QuoteForm`] back to the `Sexp::*` wrapper
17613 /// lifts the gate from per-arm constructor literals to ONE method
17614 /// the closed-set algebra owns — parallel to how [`Self::prefix`]
17615 /// lifts the Display↔reader prefix-string surface. THEORY.md §II.1
17616 /// invariant 2 — free middle; ALL FIVE consumers (Hash, Display,
17617 /// as_unquote, iac-forge interop, reader's parse) now route through
17618 /// the SAME closed-set algebra so a regression that drifts ONE
17619 /// consumer's pairing from the others cannot reach the substrate's
17620 /// runtime. THEORY.md §V.1 — knowable platform; the (QuoteForm
17621 /// variant, Sexp::* constructor) pairing becomes a TYPE projection on
17622 /// the substrate algebra rather than four `fn(Box<Sexp>) -> Sexp`
17623 /// function pointers threaded as call arguments. A typo or
17624 /// swap is no longer a runtime drift but a compile error against the
17625 /// typed projection. THEORY.md §VI.1 — generation over composition;
17626 /// the (QuoteForm variant, Sexp::* constructor) pairing appeared at
17627 /// the four reader arms — past the ≥2 PRIME-DIRECTIVE trigger once
17628 /// the structural shape is named. The typed projection lands the
17629 /// structural-completeness floor for the reader's quote-family
17630 /// surface, completing the FIVE-consumer closure of the
17631 /// `QuoteForm` algebra.
17632 #[must_use]
17633 pub fn wrap(self, inner: Sexp) -> Sexp {
17634 let boxed = Box::new(inner);
17635 match self {
17636 Self::Quote => Sexp::Quote(boxed),
17637 Self::Quasiquote => Sexp::Quasiquote(boxed),
17638 Self::Unquote => Sexp::Unquote(boxed),
17639 Self::UnquoteSplice => Sexp::UnquoteSplice(boxed),
17640 }
17641 }
17642}
17643
17644// `impl fmt::Display for QuoteForm` is generated by
17645// `#[derive(tatara_closed_set::DeriveClosedSet)]` + `#[closed_set(display)]` on
17646// the enum declaration above — emits the substrate-wide
17647// `f.write_str(Self::prefix(*self))` block byte-for-byte.
17648
17649// `impl std::str::FromStr for QuoteForm` + `impl tatara_closed_set::ClosedSet for
17650// QuoteForm` + `pub struct UnknownQuoteForm(pub String)` are generated by
17651// `#[derive(tatara_closed_set::DeriveClosedSet)]` on the enum declaration
17652// above. `label` delegates to the inherent `QuoteForm::prefix` via
17653// `#[closed_set(via = "prefix")]` so the domain-canonical
17654// reader-punctuation projection (`"'" / "`" / "," / ",@"`) stays
17655// load-bearing at the inherent surface while the trait surface unifies
17656// every closed-set implementor's projection name onto `label`.
17657// `#[closed_set(generate_unknown = "quote form")]` emits the typed
17658// parse-rejection carrier with the substrate-wide `Debug + Clone +
17659// PartialEq + Eq + thiserror::Error` derives and the `#[error("unknown
17660// quote form: {0}")]` annotation byte-for-byte; the explicit label pins
17661// the pre-lift wording even though the auto-derived
17662// `pascal_to_spaced_lowercase("QuoteForm")` projects to the same
17663// `"quote form"` literal. The FromStr decode is a linear sweep over
17664// `QuoteForm::ALL` keyed on `prefix`: every successful decode round-trips
17665// through `prefix()`, cross-axis labels from `SexpShape` (`"quote" /
17666// "quasiquote" / ...`) and `iac_forge_tag` (`"unquote-splicing"`) reject —
17667// pinned by `quote_form_prefix_round_trips_through_from_str` +
17668// `quote_form_from_str_rejects_sexp_shape_labels_on_homoiconic_prefix_axis`.
17669
17670/// Iterate over the argument tails of every form in `forms` whose call head
17671/// matches `keyword` — the *slice-side* sibling of [`Sexp::as_call_to`].
17672/// Where [`Sexp::as_call_to`] answers "is THIS form a call to `K`, and what
17673/// are its arguments?" on ONE form, `iter_calls_to` answers "which forms
17674/// in this SLICE are calls to `K`, and what are their arguments?" on a
17675/// `&[Sexp]`. Yields `&[Sexp]` for each matching form's argument tail
17676/// (`&form_list[1..]`, the empty slice for a singleton call like `(K)`);
17677/// non-matching forms — every shape [`Sexp::as_call_to`] rejects — are
17678/// skipped silently, matching the soft-projection posture the per-form
17679/// sibling carries.
17680///
17681/// Two consumers in [`compile.rs`](crate::compile) route through this
17682/// primitive:
17683/// * [`compile_typed::<T>`](crate::compile::compile_typed) — walks every
17684/// expanded top-level form and compiles every `(T::KEYWORD :k v …)`
17685/// form into a typed `T`.
17686/// * [`compile_named_from_forms::<T>`](crate::compile::compile_named_from_forms)
17687/// — walks every expanded form and compiles every
17688/// `(T::KEYWORD NAME :k v …)` form into a [`NamedDefinition<T>`](crate::compile::NamedDefinition).
17689///
17690/// Before this lift both consumers opened the same `for form in &expanded
17691/// { if let Some(args) = form.as_call_to(T::KEYWORD) { … } }` walk inline
17692/// — well past the ≥2 PRIME-DIRECTIVE trigger once the per-form sibling
17693/// had a name. After this lift the walk lives in ONE function the two
17694/// dispatchers route through; a regression that drifts ONE consumer's
17695/// walk from the other (a future emitter that inlines a partial filter,
17696/// a debug-mode logger that loses track of non-matching forms, a span-
17697/// aware walk that threads a borrowed `&Sexp` position alongside the
17698/// tail) becomes structurally impossible because there is exactly ONE
17699/// implementation both dispatchers consume. A future authoring tool
17700/// (LSP / REPL / `tatara-check`) that wants to surface "which forms in
17701/// this program invoke `K`?" binds to ONE function on the slice algebra
17702/// instead of re-deriving the walk per consumer.
17703///
17704/// Closes the soft-dispatch family at the slice level: the per-form
17705/// projections `{head_symbol, as_call, as_call_to, as_call_to_any}` each
17706/// answer "what does THIS form's head say?", and the slice-side
17707/// `iter_calls_to` extends them to "what do THESE forms' heads say,
17708/// projected through one keyword?". Typed-decoded sibling on the
17709/// slice algebra: [`iter_calls_to_any`] — the closure-typed extension
17710/// of THIS function the same way [`Sexp::as_call_to_any`] extends
17711/// [`Sexp::as_call_to`] on the per-form algebra. The (per-form,
17712/// slice-side) × (keyword, classifier) 2×2 of soft-dispatch
17713/// primitives is closed at the slice corner this lift establishes;
17714/// the closed-form composition binding the slice-side projection to
17715/// its per-form sibling is the structural identity every consumer
17716/// can pin against:
17717///
17718/// ```ignore
17719/// iter_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_call_to(k))
17720/// ```
17721///
17722/// Post-lift `iter_calls_to`'s body composes
17723/// [`iter_calls_to_any`] with a keyword-equality decoder
17724/// (`|h| (h == keyword).then_some(())`) and drops the decoded unit, so
17725/// the keyword-typed slice walk IS the typed-decoded slice walk
17726/// restricted to a constant-keyword classifier. The (slice-side
17727/// keyword projection, slice-side typed-decoded projection) pair
17728/// binds at ONE filter-and-fuse implementation on the algebra
17729/// rather than at two parallel `forms.iter().filter_map(_)` triples
17730/// that the type system would not catch when one drifts from the
17731/// other (a future emitter that adds debug logging at one site but
17732/// not the other, a future span-aware walk that threads borrowed
17733/// positional metadata through one site but skips the other).
17734///
17735/// The yielded `&[Sexp]` slices borrow `&forms[i][1..]` verbatim — no
17736/// copy, no allocation, same lifetime as [`Sexp::as_call_to`]'s tail.
17737/// The iterator's lifetime `'a` is the unified outer lifetime of `forms`
17738/// AND `keyword`: the keyword string must outlive the iterator's borrow
17739/// of the slice (typical caller passes `T::KEYWORD: &'static str`, which
17740/// unifies trivially; a caller passing a locally-allocated `&str` ties
17741/// the iterator to that local). The closure captures `keyword` by move
17742/// (the `move` keyword on the `filter_map` closure), so each invocation
17743/// re-derives the head comparison via [`Sexp::as_call_to`]'s `head ==
17744/// keyword` check at every form — no shared-state, fully Iterator-fused.
17745///
17746/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
17747/// two-site `for + as_call_to` inline walk is past the ≥2 PRIME-DIRECTIVE
17748/// trigger once the per-form sibling has a name. THEORY.md §V.1 —
17749/// knowable platform / "make invalid states unrepresentable"; the
17750/// slice-side projection becomes a NAMED primitive on the substrate's
17751/// `&[Sexp]` algebra rather than a re-derived for-loop at every consumer
17752/// site, so authoring tools (REPL, LSP, `tatara-check`) bind to ONE
17753/// function instead of re-implementing the walk. THEORY.md §II.1
17754/// invariant 1 — typed entry; the typed-keyword filter on a slice IS the
17755/// rust-level typed-entry-batch gate (the batch sibling of `as_call_to`'s
17756/// per-form gate), and naming its single shape lifts the gate from
17757/// two-site duplication to one rust function the substrate's diagnostic
17758/// promotions hang off of. THEORY.md §II.1 invariant 2 — free middle;
17759/// both dispatchers route through the SAME projection, so a regression
17760/// that drifts one consumer's walk from the other cannot reach the
17761/// substrate's runtime: the type system binds every consumer to the
17762/// projection's single emission shape.
17763///
17764/// Frontier inspiration: MLIR's `op.getOps<NamedOp>()` — every rewrite
17765/// pattern over a typed-op block binds to ONE typed-filter iterator
17766/// regardless of whether it's matching one op kind or batching across a
17767/// region's contents; the substrate's `iter_calls_to` is the
17768/// unstructured-projection peer of that iterator, lifted onto the
17769/// substrate's typed `&[Sexp]` algebra. Racket's `syntax-parse`
17770/// `~seq (defmacro id args …) …` ellipsis-form — the slice-level
17771/// matched-keyword filter is the closed-form sibling of `~seq`'s
17772/// repeated-pattern matcher, translated through pleme-io primitives as
17773/// ONE `iter_calls_to(forms, keyword)` projection. Tree-sitter's
17774/// `Query::matches` over a node sequence — the same "iterate the
17775/// matched forms in a parent" projection, inherited here for the typed
17776/// `Sexp` algebra without a new IR layer.
17777pub fn iter_calls_to<'a>(
17778 forms: &'a [Sexp],
17779 keyword: &'a str,
17780) -> impl Iterator<Item = &'a [Sexp]> + 'a {
17781 iter_calls_to_any(forms, move |h| (h == keyword).then_some(())).map(|(_, args)| args)
17782}
17783
17784/// Iterate over the `(decoded, args)` pairs of every form in `forms` whose
17785/// call head decodes through `decode` — the *slice-side* sibling of
17786/// [`Sexp::as_call_to_any`]. Where [`Sexp::as_call_to_any`] answers "is
17787/// THIS form a call whose head decodes through `F`, and what are its
17788/// arguments?" on ONE form, `iter_calls_to_any` answers "which forms in
17789/// this SLICE are calls whose heads decode through `F`, and what do they
17790/// decode to alongside their arguments?" on a `&[Sexp]`. Yields
17791/// `(decoded, &[Sexp])` for each matching form — the decoded typed
17792/// witness alongside the matched form's argument tail (`&form_list[1..]`,
17793/// the empty slice for a singleton call like `(K)`); non-matching forms
17794/// — every shape [`Sexp::as_call_to_any`] rejects, including calls whose
17795/// head is present but `decode` returns `None` for — are skipped silently,
17796/// matching the soft-projection posture the per-form sibling carries.
17797///
17798/// Closes the soft-dispatch family at the slice corner this lift
17799/// establishes — the (per-form, slice-side) × (keyword, classifier) 2×2
17800/// of soft-dispatch primitives on the `Sexp`/`&[Sexp]` algebras:
17801///
17802/// | | per-form | slice-side |
17803/// |----------------|-----------------------|--------------------------|
17804/// | keyword | [`Sexp::as_call_to`] | [`iter_calls_to`] |
17805/// | classifier `F` | [`Sexp::as_call_to_any`] | `iter_calls_to_any` (this) |
17806///
17807/// The keyword corner is the constant-classifier projection of the
17808/// classifier corner: [`iter_calls_to`] now composes through THIS
17809/// primitive with a `move |h| (h == keyword).then_some(())` decoder
17810/// and drops the decoded unit, parallel to how
17811/// `Sexp::as_call_to(k) == Sexp::as_call_to_any(|h| (h ==
17812/// k).then_some(())).map(|(_, a)| a)` (modulo the discarded `()`) on
17813/// the per-form algebra. The slice-side filter-and-fuse implementation
17814/// now lives at ONE site, so a regression that drifts a debug-logging
17815/// instrumentation, span-aware borrow threading, or fused-iterator
17816/// invariant from one slice consumer to the other becomes
17817/// structurally impossible.
17818///
17819/// Two plausible future consumer shapes the typed-decoded slice walk
17820/// admits with no boilerplate:
17821/// * **Closed-set classifier** — `iter_calls_to_any(forms,
17822/// MacroDefHead::from_keyword)` walks a slice yielding `(head: MacroDefHead,
17823/// args: &[Sexp])` for every `(defmacro …)` / `(defpoint-template …)`
17824/// / `(defcheck …)` form, decoded to the typed `MacroDefHead` enum.
17825/// Future LSP / `tatara-check` consumers that surface "every
17826/// defmacro-family form in this buffer with its kind tag" bind to
17827/// ONE projection rather than a hand-rolled
17828/// `forms.iter().filter_map(|f| f.as_call_to_any(MacroDefHead::from_keyword))`
17829/// triple at each consumer site.
17830/// * **Live-registry classifier** — `iter_calls_to_any(forms, |h|
17831/// registry.get(h))` walks a slice yielding `(handler: &Handler,
17832/// args: &[Sexp])` for every form whose head matches a runtime
17833/// registry. Future REPL / `tatara-check` consumers that route
17834/// every form through a registry dispatcher bind to ONE
17835/// projection rather than re-deriving the `filter_map` pattern
17836/// per consumer surface — sibling shape to
17837/// [`Expander::expand`](crate::macro_expand::Expander::expand)'s
17838/// per-form `as_call_to_any(|h| self.macros.get(h))` macro-call
17839/// dispatch, lifted onto the slice algebra so a batch walk picks
17840/// up the same dispatch shape without re-derivation.
17841///
17842/// The closed-form composition binding the slice-side projection to
17843/// its per-form sibling is the structural identity every consumer can
17844/// pin against:
17845///
17846/// ```ignore
17847/// iter_calls_to_any(forms, decode) ==
17848/// forms.iter().filter_map(|f| f.as_call_to_any(&mut decode))
17849/// ```
17850///
17851/// The yielded `&[Sexp]` slices borrow `&forms[i][1..]` verbatim — no
17852/// copy, no allocation, same lifetime as [`Sexp::as_call_to_any`]'s
17853/// tail. `T` is owned because `decode` is `FnMut(&str) -> Option<T>`
17854/// and a `&'_ str` borrow into the head symbol would not outlive the
17855/// helper boundary; consumers projecting to a typed `Copy` enum
17856/// (e.g. `MacroDefHead`) get the value directly per form, consumers
17857/// projecting to a borrowed `&'static str` (a closed-set head)
17858/// project to `&'static str` and inherit the static lifetime through
17859/// the classifier. The closure is `FnMut` (rather than the per-form
17860/// sibling's `FnOnce`) because the slice walk calls it once per form
17861/// — a closure that captures mutable state (a counter, a registry
17862/// cache) maintains that state across the batch walk; a closure with
17863/// no mutable state is admitted trivially.
17864///
17865/// The iterator's lifetime `'a` unifies `forms`'s borrow lifetime
17866/// with the closure `F`'s captures lifetime: the decoder must outlive
17867/// the iterator's borrow of the slice, the typical caller passes a
17868/// `'static` decoder (a `fn` item like `MacroDefHead::from_keyword`,
17869/// or a closure capturing nothing) which unifies trivially. The
17870/// closure captures `decode` by move (the `move` keyword on the
17871/// `filter_map` closure), so each invocation re-borrows it as
17872/// `&mut decode` and calls [`Sexp::as_call_to_any`] with a fresh
17873/// `FnOnce`-coerced borrow — no shared-state hazard, fully
17874/// Iterator-fused.
17875///
17876/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
17877/// per-form classifier sibling [`Sexp::as_call_to_any`] has two
17878/// production consumers (`macro_def_from` via closed-set classifier
17879/// `MacroDefHead::from_keyword`, `Expander::expand` via live-registry
17880/// classifier `|h| self.macros.get(h)`) — past the ≥2 PRIME-DIRECTIVE
17881/// trigger once the slice-side projection is named. Future
17882/// authoring-tool surfaces (LSP buffer walks, `tatara-check` batch
17883/// dispatchers, REPL exhaustive listers) join the family without
17884/// re-deriving the `filter_map(|f| f.as_call_to_any(_))` triple per
17885/// consumer. THEORY.md §V.1 — knowable platform; the slice-side
17886/// typed-decoded projection becomes a NAMED primitive on the
17887/// substrate's `&[Sexp]` algebra, closing the 2×2 of soft-dispatch
17888/// primitives the per-form algebra already establishes. THEORY.md
17889/// §II.1 invariant 2 — free middle; the slice-side keyword filter
17890/// ([`iter_calls_to`]) now routes through the slice-side classifier
17891/// filter (THIS function) via the constant-classifier composition, so
17892/// a regression that drifts the keyword filter's instrumentation
17893/// from the classifier filter's instrumentation becomes structurally
17894/// impossible.
17895///
17896/// Frontier inspiration: MLIR's
17897/// `op.walk<OpInterface, OpInterface2, …>([&](auto op) { … })` — the
17898/// typed-IR walk over a region yielding ops decoded to their typed
17899/// interface witness IS the slice-side typed-decoded projection on
17900/// MLIR's op algebra; `iter_calls_to_any` is the unstructured-Rust
17901/// peer on the substrate's typed `&[Sexp]` algebra, with `decode:
17902/// FnMut(&str) -> Option<T>` standing in for MLIR's typed-interface
17903/// dyn-cast bag. Racket's `syntax-parse` `~or* (~datum defmacro)
17904/// (~datum defpoint-template) (~datum defcheck) (head args …)` over
17905/// an ellipsis-form — the slice-level matched-set filter decoded to
17906/// a typed witness is the closed-form sibling of `~or*`'s
17907/// typed-choice repeater, translated through pleme-io primitives as
17908/// ONE `iter_calls_to_any(forms, F)` projection.
17909pub fn iter_calls_to_any<'a, F, T>(
17910 forms: &'a [Sexp],
17911 mut decode: F,
17912) -> impl Iterator<Item = (T, &'a [Sexp])> + 'a
17913where
17914 F: FnMut(&str) -> Option<T> + 'a,
17915 T: 'a,
17916{
17917 forms
17918 .iter()
17919 .filter_map(move |f| f.as_call_to_any(&mut decode))
17920}
17921
17922/// Iterate over the `Result<(decoded, NAME, spec_args)>` triples of every
17923/// form in `forms` whose call head decodes through `decode` AND carries a
17924/// positional NAME slot — the *slice-side* sibling of
17925/// [`Sexp::as_call_to_any`] specialized to the named NAME-then-kwargs
17926/// form shape, with the named-form structural gate
17927/// [`crate::compile::split_name_slot`] composed in. Where
17928/// [`iter_calls_to_any`] answers "which forms in this SLICE are calls
17929/// whose heads decode through `F`, and what do they decode to alongside
17930/// their args tail?" on a `&[Sexp]`, `iter_named_calls_to_any` answers
17931/// the same question AND extracts the borrowed NAME slot AND the
17932/// remaining spec args tail in ONE projection per matched form, lifting
17933/// the named-form gate from inside the projection at every consumer
17934/// site to the slice algebra itself.
17935///
17936/// The yielded `Result<(T, &'a str, &'a [Sexp])>` shape carries the
17937/// classifier's typed witness `T` alongside the BORROWED NAME slot AND
17938/// the BORROWED spec args tail. Non-matching forms (every shape
17939/// [`Sexp::as_call_to_any`] rejects, AND every call whose head is
17940/// present but `decode` returns `None` for) are skipped silently — the
17941/// classifier filter precedes the named gate, mirroring how
17942/// [`crate::compile::split_name_slot`] is composed into the projection
17943/// AFTER the classifier-decoded args tail is already in hand. Matched
17944/// forms whose NAME slot is missing yield `Err(NamedFormMissingName {
17945/// keyword })` carrying the classifier-supplied keyword; matched forms
17946/// whose NAME slot is a non-symbol-or-string yield `Err(NamedFormNonSymbolName
17947/// { keyword, got })` carrying the same keyword and the typed
17948/// [`SexpShape`](crate::error::SexpShape) projection of the offending
17949/// slot. Consumers `.collect::<Result<Vec<_>, _>>()` to short-circuit
17950/// at the first malformed NAME slot, exactly as
17951/// [`Expander::expand_and_collect_named_calls_to_any`](crate::macro_expand::Expander::expand_and_collect_named_calls_to_any)
17952/// short-circuits today via the same `split_name_slot` gate composed
17953/// inside its projection closure.
17954///
17955/// Decoder signature `FnMut(&str) -> Option<(T, &'static str)>` pairs
17956/// the typed witness `T` with the canonical static keyword threaded
17957/// through the `NamedFormMissingName.keyword` /
17958/// `NamedFormNonSymbolName.keyword` slots of the named-form gate — the
17959/// `&'static` constraint pins the same compile-time discipline
17960/// [`crate::compile::split_name_slot`]'s `keyword: &'static str`
17961/// parameter pins at its boundary. A classifier consumer that wants
17962/// "filter forms by a constant keyword" supplies a constant-classifier
17963/// decoder `|h| (h == keyword).then_some(((), keyword))`; the
17964/// [`iter_named_calls_to`] sibling below is exactly that specialization.
17965///
17966/// Closes the (per-form, slice-side) × (keyword, classifier) × (bare,
17967/// named) 2×2×2 cube of soft-dispatch primitives on the substrate's
17968/// `Sexp`/`&[Sexp]` algebras at the slice-side × classifier × named
17969/// corner — the cube the per-form algebra
17970/// (`as_call_to{,_any}`), the slice algebra
17971/// (`iter_calls_to{,_any}`), and the Expander surface
17972/// (`expand_and_collect_calls_to{,_any}` /
17973/// `expand_and_collect_named_calls_to{,_any}`) collectively shape:
17974///
17975/// | | bare-kwargs | named NAME-then-kwargs |
17976/// |----------------|--------------------------|--------------------------------------------------|
17977/// | per-form | [`Sexp::as_call_to_any`] | [`Sexp::as_named_call_to_any`] |
17978/// | slice | [`iter_calls_to_any`] | `iter_named_calls_to_any` (this) |
17979/// | expander | `expand_and_collect_calls_to_any` | `expand_and_collect_named_calls_to_any` |
17980///
17981/// Pre-lift the bare expander surface (`expand_and_collect_calls_to_any`)
17982/// routed through the slice primitive ([`iter_calls_to_any`]) via a
17983/// uniform `expand_program + iter_calls_to_any + map + collect`
17984/// pipeline; the named expander surface
17985/// (`expand_and_collect_named_calls_to_any`) routed through the
17986/// BARE expander surface and welded
17987/// [`crate::compile::split_name_slot`] INSIDE the projection closure —
17988/// the named gate composition lived at the expander level rather than
17989/// at the slice level the bare row sat at. Post-lift the named expander
17990/// surface routes through THIS slice primitive via the SAME
17991/// `expand_program + iter_named_calls_to_any + map + collect`
17992/// pipeline shape, so both rows now share the same composition skeleton
17993/// on the slice algebra — a regression that drifts a future debug-mode
17994/// logger, span-aware borrow walker, or fused-iterator invariant from
17995/// one row to the other becomes structurally impossible at the slice
17996/// boundary.
17997///
17998/// Two plausible future consumer shapes the slice-side named-classifier
17999/// walk admits with no boilerplate:
18000/// * **Closed-set classifier** — `iter_named_calls_to_any(forms, |h|
18001/// match h { "defmonitor" => Some((Kind::Monitor, "defmonitor")),
18002/// "defalertpolicy" => Some((Kind::Alert, "defalertpolicy")), _ =>
18003/// None }).collect::<Result<Vec<_>, _>>()?` walks a slice of
18004/// already-expanded forms, yielding the `(typed Kind, NAME, spec
18005/// args)` triple for every `(defmonitor NAME …)` / `(defalertpolicy
18006/// NAME …)` form. Future `tatara-check` consumers that already hold
18007/// expanded forms (the workspace coherence checker walks
18008/// `checks.lisp`'s post-expansion top-level) bind to ONE projection
18009/// on the slice algebra rather than re-deriving the
18010/// `iter_calls_to_any(forms, decode).map(|(decoded, args)| {
18011/// split_name_slot(args, kw).map(|(name, rest)| (decoded, name,
18012/// rest)) })` four-step inline composition.
18013/// * **Live-registry classifier** — `iter_named_calls_to_any(forms,
18014/// |h| registry.lookup(h).map(|h| (h, h.canonical_label())))` walks
18015/// a slice of expanded forms, yielding the `(handler reference, NAME,
18016/// spec args)` triple for every form whose head matches a runtime
18017/// registry. Future REPL / authoring-tool surfaces that dispatch
18018/// named forms through a live registry bind to ONE projection,
18019/// sibling shape to how the macro expander already routes through
18020/// a live-registry classifier via
18021/// [`Sexp::as_call_to_any`].
18022///
18023/// The closed-form composition binding this slice primitive to its
18024/// per-form sibling AND to the bare-kwargs slice primitive is the
18025/// structural identity every consumer can pin against:
18026///
18027/// ```ignore
18028/// iter_named_calls_to_any(forms, decode) ==
18029/// iter_calls_to_any(forms, decode).map(|(decoded, args)| {
18030/// let kw = /* keyword the decoder returned alongside decoded */;
18031/// split_name_slot(args, kw).map(|(name, rest)| (decoded, name, rest))
18032/// })
18033/// ```
18034///
18035/// The yielded `&'a str` NAME slot and `&'a [Sexp]` spec args tail
18036/// borrow from `&forms[i]` verbatim — no copy, no allocation, same
18037/// lifetime as [`Sexp::as_call_to_any`]'s tail. Consumers that need
18038/// owned ownership of the NAME (`NamedDefinition.name: String`,
18039/// JSON-serialized payloads) `.to_string()` themselves — pushing the
18040/// clone to the consumer boundary keeps the primitive allocation-free.
18041///
18042/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
18043/// named-form gate composition lived at the Expander level pre-lift
18044/// (inside `expand_and_collect_named_calls_to_any`'s projection
18045/// closure); the slice algebra had no named sibling to the bare
18046/// [`iter_calls_to_any`]. Post-lift the slice algebra closes at the
18047/// named corner, and the Expander surface routes through it via the
18048/// SAME `expand_program + iter + map + collect` pipeline the bare
18049/// expander surface uses. THEORY.md §V.1 — knowable platform; the
18050/// slice-side named-classifier walk becomes a NAMED primitive on the
18051/// substrate's `&[Sexp]` algebra, discoverable by any future authoring
18052/// tool (LSP, REPL, `tatara-check`) that already holds expanded forms.
18053/// THEORY.md §II.1 invariant 2 — free middle; the bare and named slice
18054/// projections share the same `forms.iter().filter_map(_)` skeleton, so
18055/// a regression that drifts ONE row's instrumentation from the other
18056/// becomes structurally impossible.
18057///
18058/// Frontier inspiration: MLIR's
18059/// `region.walk<NamedOp>([&](auto op) { auto name = op.getName(); … })`
18060/// — the typed-IR walk over a region yielding ops decoded to their
18061/// typed kind with the NAMED-symbol accessor pre-extracted is the MLIR
18062/// idiom for a named-op visitor; `iter_named_calls_to_any` is the
18063/// unstructured-Rust peer on the substrate's `&[Sexp]` algebra, with
18064/// `decode: FnMut(&str) -> Option<(T, &'static str)>` standing in for
18065/// MLIR's typed-interface dyn-cast bag AND `split_name_slot` standing
18066/// in for the named accessor. Racket's `syntax-parse` `~or* ((~datum
18067/// defX) name:id arg ...) ((~datum defY) name:id arg ...)` over an
18068/// ellipsis-form — the slice-level matched-set named-form filter
18069/// decoded to a typed witness is the closed-form sibling of `~or*`'s
18070/// typed-choice repeater with the `name:id` capture binder, translated
18071/// through pleme-io primitives as ONE projection on the `&[Sexp]`
18072/// algebra.
18073pub fn iter_named_calls_to_any<'a, F, T>(
18074 forms: &'a [Sexp],
18075 mut decode: F,
18076) -> impl Iterator<Item = crate::error::Result<(T, &'a str, &'a [Sexp])>> + 'a
18077where
18078 F: FnMut(&str) -> Option<(T, &'static str)> + 'a,
18079 T: 'a,
18080{
18081 forms
18082 .iter()
18083 .filter_map(move |f| f.as_named_call_to_any(&mut decode))
18084}
18085
18086/// Iterate over the `Result<(NAME, spec_args)>` pairs of every form in
18087/// `forms` whose call head matches `keyword` AND carries a positional
18088/// NAME slot — the *slice-side* sibling of [`Sexp::as_call_to`]
18089/// specialized to the named NAME-then-kwargs form shape, with the
18090/// named-form structural gate [`crate::compile::split_name_slot`]
18091/// composed in. Where [`iter_calls_to`] answers "which forms in this
18092/// SLICE are calls to `K`, and what are their args tails?" on a
18093/// `&[Sexp]`, `iter_named_calls_to` answers the same question AND
18094/// extracts the borrowed NAME slot AND the remaining spec args tail in
18095/// ONE projection per matched form.
18096///
18097/// Routes through the typed-decoded sibling [`iter_named_calls_to_any`]
18098/// with a constant-classifier decoder — the same constant-classifier
18099/// composition [`iter_calls_to`] uses to route through
18100/// [`iter_calls_to_any`] on the bare-kwargs axis, and that
18101/// [`crate::macro_expand::Expander::expand_and_collect_named_calls_to`]
18102/// uses to route through
18103/// [`crate::macro_expand::Expander::expand_and_collect_named_calls_to_any`]
18104/// on the Expander surface. The discarded `()` typed witness
18105/// (`then_some(((), keyword))`) is consumed by the wrapper projection so
18106/// the consumer's per-form mapper sees only the `(name, spec_args)`
18107/// borrowed pair, matching the bare projection signature on the named
18108/// axis.
18109///
18110/// `keyword: &'static str` threads verbatim through the
18111/// `NamedFormMissingName.keyword` / `NamedFormNonSymbolName.keyword`
18112/// slots of the named-form gate — same `&'static` discipline
18113/// [`crate::compile::split_name_slot`] pins at its boundary. Consumers
18114/// that want a runtime keyword whose lifetime is `&'static` (typical:
18115/// `T::KEYWORD` of a typed-domain witness, a hardcoded literal like
18116/// `"defcheck"`) bind to this primitive; consumers that want a runtime
18117/// keyword whose lifetime is shorter use [`iter_named_calls_to_any`]
18118/// directly with a constant-classifier decoder that converts
18119/// post-resolution.
18120///
18121/// Closes the (slice-side × constant-keyword × named) corner of the
18122/// soft-dispatch cube — see [`iter_named_calls_to_any`]'s docstring for
18123/// the cube shape. The closed-form composition binding this primitive
18124/// to the typed-decoded sibling is the structural identity every
18125/// consumer can pin against:
18126///
18127/// ```ignore
18128/// iter_named_calls_to(forms, k) ==
18129/// iter_named_calls_to_any(forms, |h| (h == k).then_some(((), k)))
18130/// .map(|maybe_triple| maybe_triple.map(|(_, name, args)| (name, args)))
18131/// ```
18132///
18133/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
18134/// constant-keyword named slice projection is a CONSEQUENCE of the
18135/// typed-decoded named slice projection + a constant-classifier
18136/// decoder, parallel to how [`iter_calls_to`] is a consequence of
18137/// [`iter_calls_to_any`] on the bare-kwargs axis. THEORY.md §II.1
18138/// invariant 2 — free middle; both rows of the slice algebra
18139/// (bare-kwargs, named) route through their classifier sibling via
18140/// constant-classifier composition, so a regression that drifts ONE
18141/// row's pipeline from the other becomes structurally impossible.
18142pub fn iter_named_calls_to<'a>(
18143 forms: &'a [Sexp],
18144 keyword: &'static str,
18145) -> impl Iterator<Item = crate::error::Result<(&'a str, &'a [Sexp])>> + 'a {
18146 iter_named_calls_to_any(forms, move |h| (h == keyword).then_some(((), keyword)))
18147 .map(|maybe_triple| maybe_triple.map(|(_, name, args)| (name, args)))
18148}
18149
18150/// Render an `Atom::Float`'s `f64` value to a form that re-reads as
18151/// `Atom::Float` — preserves the float-vs-int typed identity across the
18152/// `Sexp::Display` → [`crate::reader::read`] round-trip.
18153///
18154/// Rust's stdlib `Display` impl for `f64` elides the trailing `.0` for
18155/// finite integral values: `format!("{}", 1.0_f64) == "1"`,
18156/// `format!("{}", 100.0_f64) == "100"`. The substrate's reader
18157/// (via the typed-entry classifier [`Atom::from_lexeme`]) tries
18158/// `i64::parse` BEFORE `f64::parse`, so a bare `1` re-reads as
18159/// `Atom::Int(1)` — NOT as `Atom::Float(1.0)`. The default Display rendering therefore drifts the
18160/// typed identity at the Display→read boundary: `Float(1.0)` round-trips
18161/// to `Int(1)` and a regression silently coerces an authoring-surface
18162/// `1.0` slot into the typed `Int` track.
18163///
18164/// This helper emits `1.0` for `1.0_f64` and `1.5` for `1.5_f64` — the
18165/// `.0` suffix is appended IFF the value is finite AND already integral
18166/// (`n == n.trunc()`). Non-integral values render through the default
18167/// `f64` Display impl, which already preserves the fractional component
18168/// (`1.5`, `0.99`, etc.) round-trippably. Non-finite values (`NaN`,
18169/// `inf`, `-inf`) also fall through to the default impl — they cannot be
18170/// reliably round-tripped through the reader regardless (the Hash impl
18171/// already warns about NaN's PartialEq irregularity at the cache-key
18172/// boundary), so the helper does not paper over that prior limitation.
18173///
18174/// Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
18175/// substrate's typed-entry gate distinguishes `Atom::Int` from
18176/// `Atom::Float`, and the Display→read round-trip is the typed-exit-side
18177/// mirror that must preserve the distinction. Pre-lift the
18178/// `Float(integral) → Int(integral)` collapse silently violated the
18179/// invariant at the round-trip boundary; post-lift the typed identity is
18180/// preserved. THEORY.md §V.1 — knowable platform; diagnostics that
18181/// project a `Float(1.0)` slot through `SexpWitness::display` (sourced
18182/// from `Sexp::to_string()`) used to surface as `got 1` — confusingly
18183/// identical to the typed `Int(1)` projection. Post-lift the diagnostic
18184/// shape names the offender's typed identity (`got 1.0`) so operators
18185/// distinguish "you wrote 1.0 in an int slot" from "you wrote 1 in a
18186/// kwarg slot the kwarg gate rejected" without re-reading source.
18187fn fmt_float(n: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18188 if n.is_finite() && n == n.trunc() {
18189 write!(f, "{n}.0")
18190 } else {
18191 write!(f, "{n}")
18192 }
18193}
18194
18195/// Canonical reader-round-trippable rendering of a single atomic payload —
18196/// `Symbol(s) → "{s}"`, `Keyword(s) → ":{s}"`, `Str(s) → "{s:?}"` (the
18197/// debug-quoted form: `\"…\"` with embedded `"` and `\` escaped), `Int(n)
18198/// → "{n}"`, `Float(n)` through [`fmt_float`] so integral values render
18199/// with the `.0` suffix that preserves the typed-`Float`-vs-typed-`Int`
18200/// distinction at the Display→read boundary, `Bool(true) → "#t"`,
18201/// `Bool(false) → "#f"` (the Scheme bool spellings the reader's
18202/// typed-entry classifier [`Atom::from_lexeme`] dispatches on — `true`
18203/// / `false` re-read as symbols, NOT as bools — see the CLAUDE.md
18204/// "Lisp bools" warning).
18205///
18206/// This is the *atomic-payload Display surface* — the typed-exit-side
18207/// peer of [`Atom::from_lexeme`]'s atomic-payload typed-entry surface
18208/// (the FOURTH and LAST of the per-`Atom`-variant projection sites
18209/// lifted onto the closed-set algebra, after the typed-exit Display
18210/// [this impl], JSON [`Atom::to_json`], and iac-forge canonical
18211/// attestation `Atom::to_iac_forge_sexpr` (removed) projections — completing
18212/// the bidirectional typed-entry/typed-exit sweep). Before this lift
18213/// the per-variant rendering arms
18214/// lived inline at the `Sexp::Atom(a) => match a { … }` arm of
18215/// [`fmt::Display for Sexp`]; routing the outer arm through this impl
18216/// lifts the seven inline sub-arms (the Bool variant splits into
18217/// `true`/`false` to short-circuit the `if-else` branch) into ONE
18218/// typed-algebra method the `Sexp` Display arm calls into via
18219/// `fmt::Display::fmt(a, f)`. Sibling closed-set lift to
18220/// [`QuoteForm::prefix`] (the four homoiconic prefix wrappers) and
18221/// [`AtomKind::label`] (the six diagnostic labels) — those name the
18222/// quote-family and atomic-discriminator pairings at the `Sexp` and
18223/// `Atom` algebras respectively; this names the atomic-payload
18224/// rendering at the `Atom` algebra so future consumers of "render a
18225/// bare atom" land on this impl directly without unwrapping through
18226/// `Sexp::Atom(_).to_string()` and stripping the outer wrap.
18227///
18228/// Three production-site sibling shapes the substrate carries that
18229/// route through a per-`Atom`-variant projection, all 6/7-arm inline
18230/// matches pre-lift:
18231/// * [`fmt::Display for Sexp`]'s atom arm — 7 sub-arms (Bool splits),
18232/// produces a `fmt::Formatter` body. Post-lift collapses to
18233/// ONE `fmt::Display::fmt(a, f)` delegation.
18234/// * [`crate::domain::sexp_to_json`]'s atom arms — 6 inline arms
18235/// producing `serde_json::Value`. Now lifted onto [`Atom::to_json`]
18236/// in the sibling pattern this impl's docstring named; the
18237/// `sexp_to_json` site collapses to ONE `Sexp::Atom(a) =>
18238/// a.to_json()` arm.
18239/// * `crate::interop`'s `From<&Sexp> for SExpr` (removed)'s
18240/// atom arm (feature-gated `iac-forge`) — 6 inline arms producing
18241/// `iac_forge::sexpr::SExpr`. Now lifted onto
18242/// `Atom::to_iac_forge_sexpr` (removed) in the sibling pattern this impl's
18243/// docstring named; the interop site collapses to ONE
18244/// `Sexp::Atom(a) => a.to_iac_forge_sexpr()` arm. THIRD and LAST
18245/// of the three production-site atom-arm shapes lifted onto the
18246/// typed `Atom` algebra; the sweep across the Lisp / JSON /
18247/// iac-forge canonical-form surfaces is complete.
18248///
18249/// The (Atom variant, rendered prefix/suffix/body) quadruple now lives
18250/// at ONE typed-algebra Display impl rather than at seven inline
18251/// sub-arms inside `Display for Sexp`'s outer Atom arm. A regression
18252/// that drifts the Bool spelling (`#t`/`#f` vs `true`/`false`) — the
18253/// CLAUDE.md-pinned reader-round-trip invariant — now lands at ONE
18254/// site, and the test surface pins each variant's canonical rendering
18255/// AND the round-trip identity through the reader at the Atom level
18256/// directly (no Sexp wrap required to exercise the round-trip).
18257///
18258/// Bidirectional contract anchored by tests in this module:
18259/// * `atom_display_renders_each_variant_to_canonical_form` —
18260/// sweeps `AtomKind::ALL` and pins each variant's canonical
18261/// rendering byte-for-byte against the pre-lift inline literal,
18262/// so a future regression that drifts ONE arm (e.g. swaps
18263/// `#t`/`#f` for `true`/`false`, or strips `Str`'s quote marks)
18264/// fails loudly.
18265/// * `sexp_atom_display_arm_routes_through_atom_display_for_every_variant`
18266/// — pins the lifted boundary: `Sexp::Atom(a).to_string() ==
18267/// a.to_string()` for every atomic payload variant, AND that
18268/// both equal the legacy inline rendering. Catches a future
18269/// drift where one surface's per-variant body changes without
18270/// the other.
18271/// * `atom_display_round_trips_through_reader_preserving_typed_identity`
18272/// — sweeps a representative atom of each variant, renders it
18273/// via `Atom::Display`, parses the rendering through
18274/// [`crate::reader::read`], and pins the parsed atom equals
18275/// the seed atom (modulo `Str`'s debug-quoted spelling — pinned
18276/// separately because the reader expects unquoted source-level
18277/// `"foo"`). Pins that the (`Atom::Display`, reader) pair forms
18278/// a typed round-trip at the atom layer, the same invariant
18279/// [`fmt_float`]'s `.0` suffix preserves for the float-vs-int
18280/// distinction at the Sexp layer.
18281///
18282/// Theory anchor: THEORY.md §VI.1 — generation over composition; the
18283/// (Atom variant, canonical rendering) pair appeared inline at THREE
18284/// production sites (`Display for Sexp`'s 7-sub-arm atom arm,
18285/// `sexp_to_json`'s 6 atom arms, `From<&Sexp> for SExpr`'s 6 atom arms)
18286/// — well past the ≥2 PRIME-DIRECTIVE trigger once the structural
18287/// shape is named. THIS lift retires the Display-surface site by
18288/// naming the typed primitive on the `Atom` algebra; future runs route
18289/// the JSON and iac-forge sites through parallel sibling projections
18290/// (`Atom::to_json`, `Atom::to_iac_forge_sexpr`) the same pattern
18291/// names. THEORY.md §II.1 invariant 1 — typed entry; the substrate's
18292/// [`Atom::from_lexeme`] is the typed-entry gate at the atomic-payload
18293/// boundary (lifted onto the typed [`Atom`] algebra from the reader's
18294/// pre-lift free function), and this impl is the typed-exit-side
18295/// mirror — the closed-set [`AtomKind`] algebra now threads BOTH gates
18296/// through ONE projection family, so a regression that drifts one side's
18297/// per-variant rendering from the other (e.g. extends `Atom` with a
18298/// `Char` variant the reader accepts but the writer can't emit) is no
18299/// longer a silent two-site divergence — rustc binds both sides to
18300/// the same closed-set enum. THEORY.md §II.1 invariant 2 — free middle;
18301/// the typed-exit rendering, the reader, the diagnostic surface
18302/// (`LispError::TypeMismatch.got` slot rendering an atomic witness),
18303/// and any future authoring tool (LSP / REPL pretty-printer) all
18304/// route through ONE per-variant rendering rather than per-callsite
18305/// re-derivation.
18306///
18307/// Frontier inspiration: Racket's `(syntax->datum stx)` / `write` pair
18308/// — where `syntax->datum` unwraps the homoiconic surface to its
18309/// atomic-payload layer and `write` emits the canonical S-expression
18310/// rendering bound to the reader's `read` inverse; `Atom::Display`
18311/// is the substrate's typed-algebra peer at the atomic-payload boundary,
18312/// with the closed-set [`AtomKind`] standing in for Racket's
18313/// datum-prim taxonomy. MLIR's `mlir::AsmPrinter::printAttribute` — the
18314/// typed-IR attribute printer dispatches on the closed-set
18315/// `AttributeKind` so every printer body for a kind lives at ONE
18316/// implementation site; `Atom::Display` is the unstructured Rust peer
18317/// for the `Sexp`/`Atom` algebra, with `fmt::Display` standing in for
18318/// MLIR's `AsmPrinter` interface.
18319impl fmt::Display for Atom {
18320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18321 match self {
18322 Self::Symbol(s) => f.write_str(s),
18323 Self::Keyword(s) => write!(f, "{}{s}", Self::KEYWORD_MARKER),
18324 // NOT `{s:?}`. Rust's `str` Debug escapes NUL as `\0`, and this
18325 // reader deliberately decodes `\0` as the DIGIT `0` — see
18326 // `decode_str_escape`'s passthrough arm and the test pinning it.
18327 // So `{s:?}` emitted an escape that means something else here, and
18328 // `"nul\0byte"` round-tripped to `"nul0byte"`: silent corruption
18329 // from two locally-correct decisions disagreeing.
18330 //
18331 // This escaper emits exactly what THIS reader decodes: the five
18332 // named/self escapes, and `\u{…}` for anything else non-printable
18333 // (which the reader gained an arm for alongside this fix). Display
18334 // and read are inverses again.
18335 Self::Str(s) => write!(f, "{}", Self::escape_str_payload(s)),
18336 Self::Int(n) => write!(f, "{n}"),
18337 Self::Float(n) => fmt_float(*n, f),
18338 // Bool arm collapses to ONE branch routing through
18339 // `Self::bool_literal` — the closed-set `bool` fork happens
18340 // at the typed projection on the [`Atom`] algebra, not at
18341 // this consumer's match body. Sibling lift to the Keyword
18342 // arm, which routes through `Self::KEYWORD_MARKER` at the
18343 // same algebra layer.
18344 Self::Bool(b) => f.write_str(Self::bool_literal(*b)),
18345 }
18346 }
18347}
18348
18349impl fmt::Display for Sexp {
18350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18351 match self {
18352 // The empty-list rendering `()` composes BOTH structural
18353 // delimiters — [`Self::LIST_OPEN`] followed by
18354 // [`Self::LIST_CLOSE`] — on the closed-set outer [`Sexp`]
18355 // algebra. Pre-lift the same two bytes lived inline as one
18356 // `"()"` string literal at this arm; post-lift each byte
18357 // binds to its typed constant, so a delimiter swap flips
18358 // both this arm AND the `Self::List(_)` opener/closer arm
18359 // below AND the reader's `Token::LParen` / `Token::RParen`
18360 // outer-dispatch arms in lockstep — at ONE constant per
18361 // side rather than at four inline bytes across two files.
18362 Self::Nil => {
18363 f.write_char(Self::LIST_OPEN)?;
18364 f.write_char(Self::LIST_CLOSE)
18365 }
18366 // The atomic-payload rendering lives at the typed
18367 // [`fmt::Display for Atom`] impl above — the seven inline
18368 // sub-arms `Symbol → s`, `Keyword → ":{s}"`, `Str → "{s:?}"`,
18369 // `Int → "{n}"`, `Float → fmt_float`, `Bool(true) → "#t"`,
18370 // `Bool(false) → "#f"` all bind at ONE site on the closed-set
18371 // `Atom` algebra rather than at this outer arm. A future
18372 // atomic-kind extension (e.g. `Char` for `#\x` reader syntax,
18373 // `Bigint` for arbitrary-precision integers) extends `Atom`'s
18374 // Display impl once and this arm picks up the new variant
18375 // for free.
18376 Self::Atom(a) => fmt::Display::fmt(a, f),
18377 // The `Self::List(_)` opener AND closer arms bind to
18378 // [`Self::LIST_OPEN`] AND [`Self::LIST_CLOSE`] on the
18379 // closed-set outer [`Sexp`] algebra — the SAME two typed
18380 // constants the reader's `Token::LParen` / `Token::RParen`
18381 // outer-dispatch arms AND the `Self::Nil` two-char
18382 // rendering all route through. Adding a fifth structural
18383 // outer-shape (e.g. an eventual `Self::Vector` for `[…]`
18384 // reader syntax) lands as ONE new pair of `Sexp::VEC_OPEN`
18385 // / `Sexp::VEC_CLOSE` constants with the reader arms +
18386 // Display arms binding through them, extending the
18387 // outer-structural algebra by ONE axis without touching
18388 // this arm's `LIST_OPEN` / `LIST_CLOSE` binding.
18389 Self::List(xs) => {
18390 f.write_char(Self::LIST_OPEN)?;
18391 for (i, x) in xs.iter().enumerate() {
18392 if i > 0 {
18393 f.write_str(" ")?;
18394 }
18395 write!(f, "{x}")?;
18396 }
18397 f.write_char(Self::LIST_CLOSE)
18398 }
18399 // The four quote-family variants share the
18400 // `write!(f, "<prefix>{inner}")` Display shape — all route
18401 // through `as_quote_form`'s typed-marker projection so the
18402 // per-variant prefix (`'`, `` ` ``, `,`, `,@`) binds at ONE
18403 // site on the closed-set `QuoteForm` algebra and the
18404 // recursive `inner` rendering composes through the unified
18405 // Display arm. The (prefix, variant) pairing IS the structural
18406 // dual of the reader's `read_quoted` (prefix, variant-ctor)
18407 // dispatch — naming it once threads the round-trip discipline
18408 // through ONE rust function the reader and the Display impl
18409 // both bind against.
18410 Self::Quote(_) | Self::Quasiquote(_) | Self::Unquote(_) | Self::UnquoteSplice(_) => {
18411 let (qf, inner) = self.expect_quote_form();
18412 write!(f, "{}{inner}", qf.prefix())
18413 }
18414 }
18415 }
18416}
18417
18418#[cfg(test)]
18419mod tests {
18420 use super::*;
18421
18422 // ── head_symbol: the operator-position projection ───────────────────
18423 //
18424 // `head_symbol` lifts the `self.as_list()?.first()?.as_symbol()` chain
18425 // that recurred at four soft-dispatch sites (compile.rs `compile_typed`
18426 // + `compile_named_from_forms`, macro_expand.rs `Expander::expand` +
18427 // `macro_def_from`) into ONE named query on the Sexp algebra. These
18428 // tests pin its contract directly; the existing dispatch tests in
18429 // compile.rs / macro_expand.rs are the path-uniformity guards proving
18430 // the four sites route through it without behavior drift.
18431
18432 #[test]
18433 fn head_symbol_returns_operator_for_list_form() {
18434 // `(defpoint obs :class x)` — the operator is the head symbol.
18435 let form = Sexp::List(vec![
18436 Sexp::symbol("defpoint"),
18437 Sexp::symbol("obs"),
18438 Sexp::keyword("class"),
18439 Sexp::symbol("x"),
18440 ]);
18441 assert_eq!(form.head_symbol(), Some("defpoint"));
18442 }
18443
18444 #[test]
18445 fn head_symbol_none_for_non_list_shapes() {
18446 // A bare atom is not an invocation — there is no operator position.
18447 assert_eq!(Sexp::symbol("foo").head_symbol(), None);
18448 assert_eq!(Sexp::int(5).head_symbol(), None);
18449 assert_eq!(Sexp::keyword("k").head_symbol(), None);
18450 assert_eq!(Sexp::string("s").head_symbol(), None);
18451 assert_eq!(Sexp::boolean(true).head_symbol(), None);
18452 assert_eq!(Sexp::float(1.5).head_symbol(), None);
18453 assert_eq!(Sexp::Nil.head_symbol(), None);
18454 // Quote-family wrappers are not lists at the outer layer either.
18455 assert_eq!(Sexp::Quote(Box::new(Sexp::symbol("x"))).head_symbol(), None);
18456 }
18457
18458 #[test]
18459 fn head_symbol_none_for_empty_list() {
18460 // `()` has no first element to read an operator from.
18461 assert_eq!(Sexp::List(vec![]).head_symbol(), None);
18462 }
18463
18464 #[test]
18465 fn head_symbol_none_for_non_symbol_head() {
18466 // A list whose head is present but not a symbol is not a dispatchable
18467 // invocation — the soft projection yields None (the STRICT sibling
18468 // `compile_from_sexp` is the one that rejects these loudly).
18469 assert_eq!(
18470 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]).head_symbol(),
18471 None
18472 );
18473 assert_eq!(
18474 Sexp::List(vec![Sexp::keyword("kw"), Sexp::symbol("a")]).head_symbol(),
18475 None
18476 );
18477 assert_eq!(
18478 Sexp::List(vec![Sexp::string("s"), Sexp::symbol("a")]).head_symbol(),
18479 None
18480 );
18481 assert_eq!(
18482 Sexp::List(vec![
18483 Sexp::List(vec![Sexp::symbol("nested")]),
18484 Sexp::symbol("a")
18485 ])
18486 .head_symbol(),
18487 None
18488 );
18489 assert_eq!(
18490 Sexp::List(vec![Sexp::Nil, Sexp::symbol("a")]).head_symbol(),
18491 None
18492 );
18493 }
18494
18495 #[test]
18496 fn head_symbol_reads_singleton_list_operator() {
18497 // `(defcompiler)` — a keyword-only form still has an operator head;
18498 // this is exactly the arity-gate input compile_named dispatches on
18499 // before rejecting the missing NAME.
18500 assert_eq!(
18501 Sexp::List(vec![Sexp::symbol("defcompiler")]).head_symbol(),
18502 Some("defcompiler")
18503 );
18504 }
18505
18506 #[test]
18507 fn head_symbol_borrows_the_actual_head_string() {
18508 // The returned &str borrows the head atom's contents verbatim — no
18509 // copy, no normalization. Pin that a multi-segment symbol round-trips
18510 // unchanged so the dispatch comparison against `T::KEYWORD` is exact.
18511 let form = Sexp::List(vec![Sexp::symbol("defalert-policy"), Sexp::symbol("p")]);
18512 assert_eq!(form.head_symbol(), Some("defalert-policy"));
18513 }
18514
18515 // ── as_call: the call-form decomposition ────────────────────────────
18516 //
18517 // `as_call` pairs `head_symbol` (the operator projection) with the
18518 // argument tail every dispatch site reads right after matching the
18519 // operator — `Some((op, &args))` for a symbol-headed list, `None` for
18520 // everything else. It lifts the `as_list()`-for-the-tail +
18521 // `head_symbol()`-for-the-operator pairing that recurred at the three
18522 // soft-dispatch sites (compile.rs `compile_typed` + `compile_named_
18523 // from_forms`, macro_expand.rs `Expander::expand`) into ONE match.
18524 // `head_symbol` now delegates to it, so the `as_list()?.first()?.
18525 // as_symbol()` chain lives in exactly one place. These tests pin the
18526 // decomposition's contract directly; the existing dispatch tests in
18527 // compile.rs / macro_expand.rs are the path-uniformity guards proving
18528 // the three sites route through it without behavior drift.
18529
18530 #[test]
18531 fn as_call_decomposes_list_form_into_operator_and_args() {
18532 // `(defpoint obs :class x)` — the operator is the head symbol and
18533 // the args are everything after it.
18534 let args = [
18535 Sexp::symbol("obs"),
18536 Sexp::keyword("class"),
18537 Sexp::symbol("x"),
18538 ];
18539 let form = Sexp::List(
18540 std::iter::once(Sexp::symbol("defpoint"))
18541 .chain(args.iter().cloned())
18542 .collect(),
18543 );
18544 assert_eq!(form.as_call(), Some(("defpoint", &args[..])));
18545 }
18546
18547 #[test]
18548 fn as_call_none_for_non_call_shapes() {
18549 // Every shape `head_symbol` rejects, `as_call` rejects identically:
18550 // non-lists, the empty list, and non-symbol heads have no operator
18551 // to apply, hence no call decomposition.
18552 assert_eq!(Sexp::symbol("foo").as_call(), None);
18553 assert_eq!(Sexp::int(5).as_call(), None);
18554 assert_eq!(Sexp::keyword("k").as_call(), None);
18555 assert_eq!(Sexp::string("s").as_call(), None);
18556 assert_eq!(Sexp::Nil.as_call(), None);
18557 assert_eq!(Sexp::Quote(Box::new(Sexp::symbol("x"))).as_call(), None);
18558 assert_eq!(Sexp::List(vec![]).as_call(), None);
18559 assert_eq!(
18560 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]).as_call(),
18561 None
18562 );
18563 assert_eq!(
18564 Sexp::List(vec![Sexp::keyword("kw"), Sexp::symbol("a")]).as_call(),
18565 None
18566 );
18567 }
18568
18569 #[test]
18570 fn as_call_yields_empty_args_for_singleton_list() {
18571 // `(defcompiler)` — a keyword-only form decomposes to its operator
18572 // with an EMPTY argument tail. This is exactly the arity-gate input
18573 // `compile_named_from_forms` dispatches on before rejecting the
18574 // missing NAME via `rest.split_first()` returning `None`.
18575 assert_eq!(
18576 Sexp::List(vec![Sexp::symbol("defcompiler")]).as_call(),
18577 Some(("defcompiler", &[][..]))
18578 );
18579 }
18580
18581 #[test]
18582 fn as_call_args_are_exactly_the_tail_after_the_operator() {
18583 // The args slice borrows `&list[1..]` verbatim — the head is
18584 // excluded, every following element is included in order.
18585 let form = Sexp::List(vec![
18586 Sexp::symbol("defmonitor"),
18587 Sexp::symbol("cpu"),
18588 Sexp::keyword("threshold"),
18589 Sexp::int(90),
18590 ]);
18591 let (op, args) = form.as_call().expect("symbol-headed list decomposes");
18592 assert_eq!(op, "defmonitor");
18593 assert_eq!(args.len(), 3);
18594 assert_eq!(args[0], Sexp::symbol("cpu"));
18595 assert_eq!(args[2], Sexp::int(90));
18596 }
18597
18598 #[test]
18599 fn head_symbol_is_the_operator_projection_of_as_call() {
18600 // The structural relationship the lift establishes: `head_symbol`
18601 // is `as_call().map(|(h, _)| h)`. Pin it across every shape so a
18602 // regression that drifts one query's head-recognition from the
18603 // other — e.g. `as_call` accepting a keyword head that `head_symbol`
18604 // still rejects — fails loudly. The two share ONE chain.
18605 let shapes = [
18606 Sexp::symbol("foo"),
18607 Sexp::int(5),
18608 Sexp::keyword("k"),
18609 Sexp::Nil,
18610 Sexp::List(vec![]),
18611 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18612 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::symbol("p")]),
18613 Sexp::List(vec![Sexp::symbol("solo")]),
18614 ];
18615 for s in &shapes {
18616 assert_eq!(
18617 s.head_symbol(),
18618 s.as_call().map(|(h, _)| h),
18619 "head_symbol must equal the operator component of as_call for {s}"
18620 );
18621 }
18622 }
18623
18624 // ── as_call_to: the keyword-typed call decomposition ────────────────
18625 //
18626 // `as_call_to(keyword)` answers "is this a call to ONE specific
18627 // operator, and what are its arguments?" — the keyword-aware sibling
18628 // of `as_call`. It lifts the `as_call() + head == T::KEYWORD` two-step
18629 // chain that recurred at the two `compile.rs` dispatch sites
18630 // (`compile_typed` and `compile_named_from_forms`) into ONE structural
18631 // query on the Sexp algebra. The tests below pin its contract
18632 // directly; the existing `compile_*` tests are the path-uniformity
18633 // guards proving the two production sites route through it without
18634 // behavior drift.
18635
18636 #[test]
18637 fn as_call_to_returns_args_for_matching_head() {
18638 // `(defmonitor :name "x")` — head is the exact symbol `defmonitor`,
18639 // so `as_call_to("defmonitor")` returns `Some(args)` with the tail
18640 // after the head verbatim.
18641 let form = Sexp::List(vec![
18642 Sexp::symbol("defmonitor"),
18643 Sexp::keyword("name"),
18644 Sexp::string("x"),
18645 ]);
18646 let args = form
18647 .as_call_to("defmonitor")
18648 .expect("matching head must yield Some(args)");
18649 assert_eq!(args.len(), 2);
18650 assert_eq!(args[0], Sexp::keyword("name"));
18651 assert_eq!(args[1], Sexp::string("x"));
18652 }
18653
18654 #[test]
18655 fn as_call_to_returns_none_for_mismatched_head() {
18656 // `(defmonitor …)` against keyword `"defpoint"` — same form is a
18657 // call (so `as_call().is_some()`), but the head doesn't equal the
18658 // requested keyword. `as_call_to` is the keyword-typed projection,
18659 // so it yields `None` exactly when the head doesn't match. Pin the
18660 // gate: the two pre-lift inline sites both rejected this case via
18661 // `if head != T::KEYWORD { continue }` / `if head == T::KEYWORD`,
18662 // and the lifted primitive must reject identically.
18663 let form = Sexp::List(vec![
18664 Sexp::symbol("defmonitor"),
18665 Sexp::keyword("name"),
18666 Sexp::string("x"),
18667 ]);
18668 assert!(form.as_call().is_some());
18669 assert_eq!(form.as_call_to("defpoint"), None);
18670 assert_eq!(form.as_call_to(""), None);
18671 assert_eq!(form.as_call_to("DEFMONITOR"), None);
18672 }
18673
18674 #[test]
18675 fn as_call_to_yields_empty_args_for_singleton_matching_call() {
18676 // `(defcompiler)` against keyword `"defcompiler"` — the head
18677 // matches and the argument tail is the empty slice. Pin the
18678 // empty-tail posture: this is exactly the input
18679 // `compile_named_from_forms` dispatches on before rejecting the
18680 // missing NAME via `rest.split_first()` returning `None`, so the
18681 // lifted primitive must yield `Some(&[])` here (NOT `None`) so
18682 // the downstream split-first gate fires structurally.
18683 let form = Sexp::List(vec![Sexp::symbol("defcompiler")]);
18684 assert_eq!(form.as_call_to("defcompiler"), Some(&[][..]));
18685 }
18686
18687 #[test]
18688 fn as_call_to_returns_none_for_non_call_shapes() {
18689 // Every shape `as_call` rejects, `as_call_to` rejects identically
18690 // regardless of the requested keyword: non-lists, the empty list,
18691 // and non-symbol heads have no operator to compare to. Pin
18692 // path-uniformity with the `as_call` sibling so a regression that
18693 // narrows the keyword-typed projection to admit a shape the bare
18694 // soft projection rejected (e.g. accepting a keyword head when
18695 // `keyword` matches the keyword's symbol-string projection) fails
18696 // here.
18697 let shapes = [
18698 Sexp::symbol("foo"),
18699 Sexp::int(5),
18700 Sexp::keyword("k"),
18701 Sexp::string("s"),
18702 Sexp::boolean(true),
18703 Sexp::float(1.5),
18704 Sexp::Nil,
18705 Sexp::Quote(Box::new(Sexp::symbol("foo"))),
18706 Sexp::List(vec![]),
18707 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18708 Sexp::List(vec![Sexp::keyword("foo"), Sexp::symbol("a")]),
18709 Sexp::List(vec![Sexp::string("foo"), Sexp::symbol("a")]),
18710 ];
18711 for s in &shapes {
18712 assert_eq!(
18713 s.as_call_to("foo"),
18714 None,
18715 "non-call shape must yield None for any keyword, got Some for {s}"
18716 );
18717 assert_eq!(s.as_call_to("anything"), None);
18718 }
18719 }
18720
18721 #[test]
18722 fn as_call_to_args_borrow_is_same_pointer_as_as_call_tail() {
18723 // The structural identity binding `as_call_to` to its `as_call`
18724 // sibling: on the matching-head path, the returned `args` slice IS
18725 // the same `&[Sexp]` slice `as_call` would return as the tail
18726 // component. Pin pointer equality so a regression that
18727 // re-allocates or copies the tail in the keyword-typed projection
18728 // fails loudly — the soft-projection contract is borrow, not
18729 // clone, AND `as_call_to` inherits the contract verbatim from
18730 // `as_call`.
18731 let form = Sexp::List(vec![
18732 Sexp::symbol("defmonitor"),
18733 Sexp::keyword("name"),
18734 Sexp::string("x"),
18735 ]);
18736 let (_, via_as_call) = form.as_call().expect("call shape");
18737 let via_as_call_to = form
18738 .as_call_to("defmonitor")
18739 .expect("matching keyword shape");
18740 assert!(
18741 std::ptr::eq(via_as_call.as_ptr(), via_as_call_to.as_ptr()),
18742 "as_call_to args must borrow the SAME slice as as_call's tail"
18743 );
18744 assert_eq!(via_as_call.len(), via_as_call_to.len());
18745 }
18746
18747 #[test]
18748 fn as_call_to_is_the_keyword_typed_projection_of_as_call() {
18749 // The structural identity the lift establishes:
18750 // `as_call_to(k) == as_call().and_then(|(h, args)| (h == k).then_some(args))`
18751 // `as_call_to(k).is_some() == (head_symbol() == Some(k))`
18752 // Pin both across every shape so a regression that drifts the
18753 // keyword-typed projection from its closed-form definition fails
18754 // loudly. The three soft-projection primitives — `head_symbol`,
18755 // `as_call`, `as_call_to` — must agree on operator-position
18756 // recognition at every shape they share.
18757 let shapes = [
18758 Sexp::symbol("foo"),
18759 Sexp::int(5),
18760 Sexp::keyword("k"),
18761 Sexp::Nil,
18762 Sexp::List(vec![]),
18763 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18764 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::symbol("p")]),
18765 Sexp::List(vec![Sexp::symbol("defmonitor"), Sexp::keyword("name")]),
18766 Sexp::List(vec![Sexp::symbol("solo")]),
18767 ];
18768 for s in &shapes {
18769 for k in ["defpoint", "defmonitor", "solo", "foo", ""] {
18770 let via_chain = s.as_call().and_then(|(h, args)| (h == k).then_some(args));
18771 assert_eq!(
18772 s.as_call_to(k),
18773 via_chain,
18774 "as_call_to({k:?}) must equal as_call+filter for {s}"
18775 );
18776 assert_eq!(
18777 s.as_call_to(k).is_some(),
18778 s.head_symbol() == Some(k),
18779 "as_call_to({k:?}).is_some() must equal (head_symbol() == Some({k:?})) for {s}"
18780 );
18781 }
18782 }
18783 }
18784
18785 // ── as_call_to_any: the typed-decoded call decomposition ────────────
18786 //
18787 // `as_call_to_any(decode)` answers "is this a call whose head decodes
18788 // through `decode`, and what are its arguments?" — the closure-typed
18789 // sibling of `as_call_to`. It lifts the
18790 // `as_list() + head_symbol() + decode(head)` three-step chain that
18791 // recurred at the macro-expander's `macro_def_from` site (the typed
18792 // `MacroDefHead::from_keyword` dispatch surface) into ONE structural
18793 // query on the Sexp algebra. The tests below pin its contract
18794 // directly; the existing macro-expansion tests are the path-
18795 // uniformity guards proving the production site routes through it
18796 // without behavior drift.
18797 //
18798 // The test classifier `Op::from_keyword` mirrors `MacroDefHead::from_keyword`
18799 // — a closed-set typed enum projection from a `&str` head — so the
18800 // tests cover the macro-expander's real consumer shape rather than a
18801 // synthetic predicate.
18802 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
18803 enum Op {
18804 Quote,
18805 If,
18806 Let,
18807 }
18808 impl Op {
18809 fn from_keyword(head: &str) -> Option<Self> {
18810 match head {
18811 "quote" => Some(Self::Quote),
18812 "if" => Some(Self::If),
18813 "let" => Some(Self::Let),
18814 _ => None,
18815 }
18816 }
18817 }
18818
18819 #[test]
18820 fn as_call_to_any_returns_decoded_head_and_args_for_matching_head() {
18821 // `(if c t e)` — head `if` decodes to `Op::If`, args are the
18822 // three-element tail verbatim. Pin both halves of the returned
18823 // tuple: the decoded typed witness AND the borrowed args slice.
18824 let form = Sexp::List(vec![
18825 Sexp::symbol("if"),
18826 Sexp::symbol("c"),
18827 Sexp::symbol("t"),
18828 Sexp::symbol("e"),
18829 ]);
18830 let (op, args) = form
18831 .as_call_to_any(Op::from_keyword)
18832 .expect("matching head must yield Some((decoded, args))");
18833 assert_eq!(op, Op::If);
18834 assert_eq!(args.len(), 3);
18835 assert_eq!(args[0], Sexp::symbol("c"));
18836 assert_eq!(args[2], Sexp::symbol("e"));
18837 }
18838
18839 #[test]
18840 fn as_call_to_any_returns_none_when_decoder_rejects_head() {
18841 // `(defmonitor :name "x")` — head `defmonitor` is a valid symbol
18842 // (so `as_call().is_some()`), but `Op::from_keyword` rejects it
18843 // (it's not one of the closed `{quote, if, let}` set). Pin the
18844 // gate: `as_call_to_any` yields `None` exactly when the decoder
18845 // rejects the head, mirroring how the pre-lift inline chain in
18846 // `macro_def_from` returned `Ok(None)` when
18847 // `MacroDefHead::from_keyword(head_str)` returned `None`.
18848 let form = Sexp::List(vec![
18849 Sexp::symbol("defmonitor"),
18850 Sexp::keyword("name"),
18851 Sexp::string("x"),
18852 ]);
18853 assert!(form.as_call().is_some());
18854 assert!(form.as_call_to_any(Op::from_keyword).is_none());
18855 }
18856
18857 #[test]
18858 fn as_call_to_any_yields_empty_args_for_singleton_decoded_call() {
18859 // `(quote)` against the classifier — head decodes to `Op::Quote`
18860 // and the argument tail is the empty slice. Pin the empty-tail
18861 // posture: a downstream arity gate (analogous to
18862 // `if list.len() < 4` inside `macro_def_from`) dispatches on
18863 // `args.is_empty()` AFTER the decoder accepts the head; the
18864 // helper must yield `Some((decoded, &[]))` (NOT `None`) so that
18865 // gate fires structurally.
18866 let form = Sexp::List(vec![Sexp::symbol("quote")]);
18867 let (op, args) = form
18868 .as_call_to_any(Op::from_keyword)
18869 .expect("singleton matching call must decompose");
18870 assert_eq!(op, Op::Quote);
18871 assert_eq!(args.len(), 0);
18872 }
18873
18874 #[test]
18875 fn as_call_to_any_returns_none_for_non_call_shapes() {
18876 // Every shape `as_call` rejects, `as_call_to_any` rejects
18877 // identically regardless of the decoder: non-lists, the empty
18878 // list, and non-symbol heads have no operator string to feed
18879 // the decoder. Pin path-uniformity with the `as_call` sibling so
18880 // a regression that admits a non-call shape (e.g. accepting a
18881 // bare symbol via a permissive decoder) fails here. Pass
18882 // `Some` for every input to prove the call-shape gate fires
18883 // BEFORE the decoder runs — the decoder cannot rescue a
18884 // non-call.
18885 let shapes = [
18886 Sexp::symbol("foo"),
18887 Sexp::int(5),
18888 Sexp::keyword("k"),
18889 Sexp::string("s"),
18890 Sexp::boolean(true),
18891 Sexp::float(1.5),
18892 Sexp::Nil,
18893 Sexp::Quote(Box::new(Sexp::symbol("foo"))),
18894 Sexp::List(vec![]),
18895 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18896 Sexp::List(vec![Sexp::keyword("foo"), Sexp::symbol("a")]),
18897 Sexp::List(vec![Sexp::string("foo"), Sexp::symbol("a")]),
18898 ];
18899 for s in &shapes {
18900 // The promiscuous decoder accepts every &str head, so the
18901 // only way to see `None` here is if the call-shape gate
18902 // rejects the shape upstream of the decoder.
18903 assert_eq!(
18904 s.as_call_to_any(|h: &str| Some(h.to_string())),
18905 None,
18906 "non-call shape must yield None even for a promiscuous decoder, got Some for {s}"
18907 );
18908 }
18909 }
18910
18911 #[test]
18912 fn as_call_to_any_args_borrow_is_same_pointer_as_as_call_tail() {
18913 // The structural identity binding `as_call_to_any` to its
18914 // `as_call` sibling: on the decoded path, the returned `args`
18915 // slice IS the same `&[Sexp]` slice `as_call` would return as
18916 // the tail component. Pin pointer equality so a regression that
18917 // re-allocates or copies the tail in the typed-decoded
18918 // projection fails loudly — the soft-projection contract is
18919 // borrow, not clone, AND `as_call_to_any` inherits the contract
18920 // verbatim from `as_call`. Parallel to the
18921 // `as_call_to_args_borrow_is_same_pointer_as_as_call_tail` pin
18922 // for `as_call_to`.
18923 let form = Sexp::List(vec![
18924 Sexp::symbol("if"),
18925 Sexp::symbol("c"),
18926 Sexp::symbol("t"),
18927 ]);
18928 let (_, via_as_call) = form.as_call().expect("call shape");
18929 let (_, via_as_call_to_any) = form
18930 .as_call_to_any(Op::from_keyword)
18931 .expect("decoded shape");
18932 assert!(
18933 std::ptr::eq(via_as_call.as_ptr(), via_as_call_to_any.as_ptr()),
18934 "as_call_to_any args must borrow the SAME slice as as_call's tail"
18935 );
18936 assert_eq!(via_as_call.len(), via_as_call_to_any.len());
18937 }
18938
18939 #[test]
18940 fn as_call_to_any_is_the_decoded_projection_of_as_call() {
18941 // The structural identity the lift establishes:
18942 // `as_call_to_any(decode) == as_call().and_then(|(h, args)| decode(h).map(|d| (d, args)))`
18943 // `as_call_to_any(decode).is_some() == as_call().map_or(false, |(h, _)| decode(h).is_some())`
18944 // Pin both across every shape so a regression that drifts the
18945 // typed-decoded projection from its closed-form definition fails
18946 // loudly. The four soft-projection primitives — `head_symbol`,
18947 // `as_call`, `as_call_to`, `as_call_to_any` — must agree on
18948 // operator-position recognition at every shape they share.
18949 let shapes = [
18950 Sexp::symbol("foo"),
18951 Sexp::int(5),
18952 Sexp::keyword("k"),
18953 Sexp::Nil,
18954 Sexp::List(vec![]),
18955 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18956 Sexp::List(vec![Sexp::symbol("if"), Sexp::symbol("c")]),
18957 Sexp::List(vec![Sexp::symbol("quote"), Sexp::symbol("x")]),
18958 Sexp::List(vec![Sexp::symbol("let"), Sexp::List(vec![])]),
18959 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::symbol("p")]),
18960 Sexp::List(vec![Sexp::symbol("solo")]),
18961 ];
18962 for s in &shapes {
18963 let via_chain = s
18964 .as_call()
18965 .and_then(|(h, args)| Op::from_keyword(h).map(|d| (d, args)));
18966 assert_eq!(
18967 s.as_call_to_any(Op::from_keyword),
18968 via_chain,
18969 "as_call_to_any(Op::from_keyword) must equal as_call+decode for {s}"
18970 );
18971 }
18972 }
18973
18974 #[test]
18975 fn as_call_to_any_subsumes_as_call_to_via_unit_decoder() {
18976 // The closed-form composition `as_call_to(k) == as_call_to_any
18977 // (|h| (h == k).then_some(())).map(|(_, a)| a)` (modulo the
18978 // discarded `()` decoded witness). Pin it across every shape ×
18979 // keyword pair so a regression that drifts the typed-decoded
18980 // projection from its single-keyword sibling fails loudly. This
18981 // makes the family closure: `as_call_to` is the trivial-decoder
18982 // instance of `as_call_to_any`, and naming both lets each
18983 // consumer pick the projection that fits its call site.
18984 let shapes = [
18985 Sexp::symbol("foo"),
18986 Sexp::Nil,
18987 Sexp::List(vec![]),
18988 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
18989 Sexp::List(vec![Sexp::symbol("if"), Sexp::symbol("c")]),
18990 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::symbol("p")]),
18991 ];
18992 for s in &shapes {
18993 for k in ["if", "defpoint", "let", "foo", "", "DEFPOINT"] {
18994 let via_unit_decoder = s
18995 .as_call_to_any(|h: &str| (h == k).then_some(()))
18996 .map(|(_, args)| args);
18997 assert_eq!(
18998 s.as_call_to(k),
18999 via_unit_decoder,
19000 "as_call_to({k:?}) must equal as_call_to_any+unit-decoder for {s}"
19001 );
19002 }
19003 }
19004 }
19005
19006 // ── iter_calls_to: the slice-side projection of as_call_to ──────────
19007 //
19008 // `iter_calls_to(forms, keyword)` lifts the per-form projection
19009 // `as_call_to` onto a `&[Sexp]`, yielding the args tails of every
19010 // matching form in source order — the substrate's typed-keyword
19011 // filter over a batch of forms. The two inline `for form in
19012 // &expanded { if let Some(args) = form.as_call_to(T::KEYWORD) { … } }`
19013 // walks at the `compile_typed` + `compile_named_from_forms` dispatch
19014 // sites (compile.rs) collapse to ONE `iter_calls_to(&expanded,
19015 // T::KEYWORD)` call. Tests pin the slice-side primitive's contract
19016 // directly; the existing dispatch tests in compile.rs are the
19017 // path-uniformity guards proving the two consumers route through it
19018 // without behavior drift.
19019
19020 #[test]
19021 fn iter_calls_to_yields_args_for_every_matching_form_in_slice() {
19022 // Three forms: two match "defmonitor", one matches "defalert".
19023 // `iter_calls_to("defmonitor")` yields the two matching args
19024 // slices in source order — the matched forms' tails verbatim,
19025 // skipping the non-matching `defalert` form silently.
19026 let forms = vec![
19027 Sexp::List(vec![
19028 Sexp::symbol("defmonitor"),
19029 Sexp::keyword("name"),
19030 Sexp::string("a"),
19031 ]),
19032 Sexp::List(vec![
19033 Sexp::symbol("defalert"),
19034 Sexp::keyword("name"),
19035 Sexp::string("p"),
19036 ]),
19037 Sexp::List(vec![
19038 Sexp::symbol("defmonitor"),
19039 Sexp::keyword("name"),
19040 Sexp::string("b"),
19041 ]),
19042 ];
19043 let args: Vec<&[Sexp]> = iter_calls_to(&forms, "defmonitor").collect();
19044 assert_eq!(args.len(), 2);
19045 assert_eq!(args[0], &[Sexp::keyword("name"), Sexp::string("a")][..]);
19046 assert_eq!(args[1], &[Sexp::keyword("name"), Sexp::string("b")][..]);
19047 }
19048
19049 #[test]
19050 fn iter_calls_to_skips_every_non_call_shape_silently() {
19051 // Every shape `as_call_to` rejects, `iter_calls_to` skips: non-
19052 // lists (atoms across all 6 atom kinds, Nil, quote-family
19053 // wrapper), the empty list, and non-symbol-head lists. Pin
19054 // path-uniformity with the per-form sibling: passing ANY keyword
19055 // against a slice of non-call shapes yields zero items. Closes
19056 // the soft-projection posture at the slice level — a regression
19057 // that admits a non-call shape (e.g. accepting a bare symbol
19058 // whose name matches the keyword) fails here.
19059 let forms = vec![
19060 Sexp::symbol("foo"),
19061 Sexp::int(5),
19062 Sexp::keyword("k"),
19063 Sexp::string("s"),
19064 Sexp::boolean(true),
19065 Sexp::float(1.5),
19066 Sexp::Nil,
19067 Sexp::Quote(Box::new(Sexp::symbol("foo"))),
19068 Sexp::List(vec![]),
19069 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
19070 Sexp::List(vec![Sexp::keyword("foo"), Sexp::symbol("a")]),
19071 ];
19072 for k in ["foo", "anything", "", "defpoint"] {
19073 let args: Vec<&[Sexp]> = iter_calls_to(&forms, k).collect();
19074 assert!(
19075 args.is_empty(),
19076 "non-call slice must yield zero items for keyword {k:?}, got {} items",
19077 args.len()
19078 );
19079 }
19080 }
19081
19082 #[test]
19083 fn iter_calls_to_yields_empty_args_slice_for_singleton_matching_call() {
19084 // `(defcompiler)` — the head matches and the args tail is the
19085 // empty slice. Pin the empty-tail posture: `iter_calls_to` must
19086 // yield `Some(&[])` for the matching singleton (NOT skip it),
19087 // mirroring `as_call_to`'s contract — the (possibly-empty) args
19088 // slice on a match, NOT `None` on an empty tail. This is exactly
19089 // the input `compile_named_from_forms` dispatches on before
19090 // rejecting the missing NAME via `rest.split_first()`'s `None`.
19091 let forms = vec![Sexp::List(vec![Sexp::symbol("defcompiler")])];
19092 let args: Vec<&[Sexp]> = iter_calls_to(&forms, "defcompiler").collect();
19093 assert_eq!(args.len(), 1);
19094 assert_eq!(args[0], &[][..]);
19095 }
19096
19097 #[test]
19098 fn iter_calls_to_yields_nothing_for_empty_slice() {
19099 // An empty forms slice yields zero items regardless of keyword.
19100 // Pin the slice-side primitive's degenerate boundary: empty in,
19101 // empty out — the iterator is fused-empty without consulting
19102 // `as_call_to` at all.
19103 let forms: Vec<Sexp> = vec![];
19104 let mut iter = iter_calls_to(&forms, "anything");
19105 assert!(iter.next().is_none());
19106 }
19107
19108 #[test]
19109 fn iter_calls_to_yields_nothing_when_keyword_matches_no_form() {
19110 // A slice of valid call forms whose heads none match the
19111 // requested keyword yields zero items. Pin path-uniformity with
19112 // the per-form sibling: every form's `as_call_to(missing)` is
19113 // `None`, so the slice-side iterator yields nothing — the filter
19114 // fires uniformly across the batch.
19115 let forms = vec![
19116 Sexp::List(vec![Sexp::symbol("defmonitor"), Sexp::int(1)]),
19117 Sexp::List(vec![Sexp::symbol("defalert"), Sexp::int(2)]),
19118 Sexp::List(vec![Sexp::symbol("defpoint"), Sexp::int(3)]),
19119 ];
19120 let args: Vec<&[Sexp]> = iter_calls_to(&forms, "missing").collect();
19121 assert!(args.is_empty());
19122 }
19123
19124 #[test]
19125 fn iter_calls_to_args_borrow_is_same_pointer_as_per_form_as_call_to_tail() {
19126 // The structural identity binding `iter_calls_to` to its per-form
19127 // sibling: each yielded `&[Sexp]` IS the same slice `as_call_to`
19128 // would return as the tail component for the corresponding form
19129 // (pinned via `std::ptr::eq` on `as_ptr()`). The soft-projection
19130 // contract is borrow, not clone, AND `iter_calls_to` inherits the
19131 // contract verbatim from `as_call_to`. Parallel to the
19132 // `as_call_to_args_borrow_is_same_pointer_as_as_call_tail` pin
19133 // for `as_call_to`.
19134 let forms = vec![Sexp::List(vec![
19135 Sexp::symbol("defmonitor"),
19136 Sexp::keyword("name"),
19137 Sexp::string("a"),
19138 ])];
19139 let via_iter: &[Sexp] = iter_calls_to(&forms, "defmonitor")
19140 .next()
19141 .expect("one match");
19142 let via_per_form: &[Sexp] = forms[0].as_call_to("defmonitor").expect("one match");
19143 assert!(
19144 std::ptr::eq(via_iter.as_ptr(), via_per_form.as_ptr()),
19145 "iter_calls_to args must borrow the SAME slice as as_call_to's tail"
19146 );
19147 assert_eq!(via_iter.len(), via_per_form.len());
19148 }
19149
19150 #[test]
19151 fn iter_calls_to_is_the_slice_side_projection_of_as_call_to() {
19152 // The structural identity the lift establishes:
19153 // `iter_calls_to(forms, k) == forms.iter().filter_map(|f| f.as_call_to(k))`
19154 // Pin shape AND ordering AND pointer-identity across mixed inputs
19155 // and a range of keywords (including matching, non-matching, and
19156 // edge-case empty/case-mismatched keywords) so a regression that
19157 // drifts the slice-side projection from its closed-form
19158 // definition fails loudly. The five soft-projection primitives —
19159 // `head_symbol`, `as_call`, `as_call_to`, `as_call_to_any`, AND
19160 // `iter_calls_to` — must agree on operator-position recognition
19161 // at every shape/slice they share.
19162 let forms = vec![
19163 Sexp::symbol("foo"),
19164 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
19165 Sexp::Nil,
19166 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(2)]),
19167 Sexp::int(99),
19168 Sexp::List(vec![Sexp::symbol("b"), Sexp::int(3)]),
19169 Sexp::List(vec![Sexp::symbol("a")]),
19170 Sexp::List(vec![]),
19171 Sexp::List(vec![Sexp::keyword("a"), Sexp::int(4)]),
19172 ];
19173 for k in ["a", "b", "c", "", "A"] {
19174 let via_iter: Vec<&[Sexp]> = iter_calls_to(&forms, k).collect();
19175 let via_chain: Vec<&[Sexp]> = forms.iter().filter_map(|f| f.as_call_to(k)).collect();
19176 assert_eq!(
19177 via_iter.len(),
19178 via_chain.len(),
19179 "len drift for keyword {k:?}"
19180 );
19181 for (a, b) in via_iter.iter().zip(via_chain.iter()) {
19182 assert!(
19183 std::ptr::eq(a.as_ptr(), b.as_ptr()),
19184 "ptr drift at keyword {k:?}: iter slice does not borrow the SAME tail as the per-form chain"
19185 );
19186 assert_eq!(a.len(), b.len(), "len drift at keyword {k:?}");
19187 }
19188 }
19189 }
19190
19191 // ── iter_calls_to_any: the typed-decoded slice-side projection ──────
19192 //
19193 // `iter_calls_to_any(forms, decode)` lifts the per-form projection
19194 // `as_call_to_any` onto a `&[Sexp]`, yielding the `(decoded,
19195 // &[Sexp])` pair of every form whose head decodes through `decode`
19196 // — the substrate's typed-decoded filter over a batch of forms,
19197 // closing the (per-form, slice-side) × (keyword, classifier) 2×2
19198 // of soft-dispatch primitives at the slice-side classifier corner.
19199 // The slice-side keyword projection `iter_calls_to` now routes
19200 // through THIS primitive with a constant-keyword decoder, so the
19201 // filter-and-fuse implementation lives at ONE site on the slice
19202 // algebra. Tests pin the slice-side primitive's contract directly
19203 // alongside the (slice-side keyword, slice-side classifier)
19204 // composition law that the keyword projection's re-routing
19205 // establishes.
19206
19207 #[test]
19208 fn iter_calls_to_any_yields_decoded_pair_for_every_matching_form_in_slice() {
19209 // Three forms: two decode through `Op::from_keyword`, one does
19210 // not (the head `"defalert"` is outside the closed set). The
19211 // typed-decoded slice walk yields the `(decoded, args)` pair
19212 // for each matching form in source order, skipping non-decoding
19213 // forms silently — parallel to how `iter_calls_to` yields ONLY
19214 // the args slice for keyword-matching forms.
19215 #[derive(Debug, PartialEq, Eq)]
19216 enum Op {
19217 Defmonitor,
19218 Defpoint,
19219 }
19220 impl Op {
19221 fn from_keyword(h: &str) -> Option<Self> {
19222 match h {
19223 "defmonitor" => Some(Self::Defmonitor),
19224 "defpoint" => Some(Self::Defpoint),
19225 _ => None,
19226 }
19227 }
19228 }
19229 let forms = vec![
19230 Sexp::List(vec![
19231 Sexp::symbol("defmonitor"),
19232 Sexp::keyword("name"),
19233 Sexp::string("a"),
19234 ]),
19235 Sexp::List(vec![
19236 Sexp::symbol("defalert"),
19237 Sexp::keyword("name"),
19238 Sexp::string("p"),
19239 ]),
19240 Sexp::List(vec![
19241 Sexp::symbol("defpoint"),
19242 Sexp::keyword("name"),
19243 Sexp::string("b"),
19244 ]),
19245 ];
19246 let decoded: Vec<(Op, &[Sexp])> = iter_calls_to_any(&forms, Op::from_keyword).collect();
19247 assert_eq!(decoded.len(), 2);
19248 assert_eq!(decoded[0].0, Op::Defmonitor);
19249 assert_eq!(
19250 decoded[0].1,
19251 &[Sexp::keyword("name"), Sexp::string("a")][..]
19252 );
19253 assert_eq!(decoded[1].0, Op::Defpoint);
19254 assert_eq!(
19255 decoded[1].1,
19256 &[Sexp::keyword("name"), Sexp::string("b")][..]
19257 );
19258 }
19259
19260 #[test]
19261 fn iter_calls_to_any_skips_every_shape_per_form_sibling_rejects() {
19262 // Every shape `as_call_to_any` rejects, `iter_calls_to_any`
19263 // skips: non-list shapes, the empty list, non-symbol-head
19264 // lists, AND lists whose head is a symbol the decoder rejects.
19265 // Pin the soft-projection contract at the slice level —
19266 // parallel to `iter_calls_to_skips_every_non_call_shape_silently`
19267 // but with the decoder rejection axis added so the per-form
19268 // sibling's two rejection sources (shape-level + decoder-level)
19269 // both route through the slice-side filter uniformly.
19270 let forms = vec![
19271 Sexp::symbol("foo"),
19272 Sexp::int(5),
19273 Sexp::keyword("k"),
19274 Sexp::string("s"),
19275 Sexp::boolean(true),
19276 Sexp::float(1.5),
19277 Sexp::Nil,
19278 Sexp::Quote(Box::new(Sexp::symbol("foo"))),
19279 Sexp::List(vec![]),
19280 Sexp::List(vec![Sexp::int(5), Sexp::symbol("a")]),
19281 Sexp::List(vec![Sexp::keyword("foo"), Sexp::symbol("a")]),
19282 // A call whose head IS a symbol but the decoder rejects —
19283 // this is the decoder-level rejection axis the per-form
19284 // sibling's classifier closure adds beyond the keyword
19285 // sibling's `head == k` axis.
19286 Sexp::List(vec![Sexp::symbol("unknown-head"), Sexp::int(1)]),
19287 ];
19288 let decoded: Vec<(&'static str, &[Sexp])> =
19289 iter_calls_to_any(&forms, |_h: &str| None::<&'static str>).collect();
19290 assert!(
19291 decoded.is_empty(),
19292 "non-call / decoder-rejecting slice must yield zero items, got {} items",
19293 decoded.len()
19294 );
19295 }
19296
19297 #[test]
19298 fn iter_calls_to_any_yields_empty_args_slice_for_singleton_decoded_call() {
19299 // `(defcompiler)` decoded through a classifier that accepts
19300 // the head — the args tail is the empty slice. Pin the
19301 // empty-tail posture: the typed-decoded slice walk must yield
19302 // `(decoded, &[])` for the matching singleton (NOT skip it),
19303 // mirroring the per-form sibling's contract — the
19304 // (possibly-empty) args slice on a decoded match, NOT `None`
19305 // on an empty tail. Parallel to
19306 // `iter_calls_to_yields_empty_args_slice_for_singleton_matching_call`
19307 // for the keyword sibling and
19308 // `as_call_to_any_yields_empty_args_for_singleton_decoded_call`
19309 // for the per-form sibling.
19310 let forms = vec![Sexp::List(vec![Sexp::symbol("defcompiler")])];
19311 let decoded: Vec<(&'static str, &[Sexp])> = iter_calls_to_any(&forms, |h: &str| {
19312 (h == "defcompiler").then_some("defcompiler")
19313 })
19314 .collect();
19315 assert_eq!(decoded.len(), 1);
19316 assert_eq!(decoded[0].0, "defcompiler");
19317 assert_eq!(decoded[0].1, &[][..]);
19318 }
19319
19320 #[test]
19321 fn iter_calls_to_any_yields_nothing_for_empty_slice() {
19322 // An empty forms slice yields zero items regardless of
19323 // decoder. Pin the slice-side primitive's degenerate boundary:
19324 // empty in, empty out — the iterator is fused-empty without
19325 // consulting `as_call_to_any` at all. The decoder's body must
19326 // never run (we assert with an explicitly-panicking closure
19327 // body to prove the fused-empty contract holds before the
19328 // per-form sibling is consulted). Parallel to
19329 // `iter_calls_to_yields_nothing_for_empty_slice` for the
19330 // keyword sibling.
19331 let forms: Vec<Sexp> = vec![];
19332 let mut iter = iter_calls_to_any(&forms, |_h: &str| -> Option<()> {
19333 panic!("decoder must not run on an empty forms slice")
19334 });
19335 assert!(iter.next().is_none());
19336 }
19337
19338 #[test]
19339 fn iter_calls_to_any_args_borrow_is_same_pointer_as_per_form_as_call_to_any_tail() {
19340 // The structural identity binding `iter_calls_to_any` to its
19341 // per-form sibling: each yielded `&[Sexp]` IS the same slice
19342 // `as_call_to_any` would return as the tail component for the
19343 // corresponding form (pinned via `std::ptr::eq` on `as_ptr()`).
19344 // The soft-projection contract is borrow, not clone, AND
19345 // `iter_calls_to_any` inherits the contract verbatim from
19346 // `as_call_to_any`. Parallel to the
19347 // `iter_calls_to_args_borrow_is_same_pointer_as_per_form_as_call_to_tail`
19348 // pin for the keyword sibling and the
19349 // `as_call_to_any_args_borrow_is_same_pointer_as_as_call_tail`
19350 // pin for the per-form sibling.
19351 let forms = vec![Sexp::List(vec![
19352 Sexp::symbol("defmonitor"),
19353 Sexp::keyword("name"),
19354 Sexp::string("a"),
19355 ])];
19356 let (_, via_iter): (&'static str, &[Sexp]) = iter_calls_to_any(&forms, |h: &str| {
19357 (h == "defmonitor").then_some("defmonitor")
19358 })
19359 .next()
19360 .expect("one decoded match");
19361 let (_, via_per_form): (&'static str, &[Sexp]) = forms[0]
19362 .as_call_to_any(|h: &str| (h == "defmonitor").then_some("defmonitor"))
19363 .expect("one decoded match");
19364 assert!(
19365 std::ptr::eq(via_iter.as_ptr(), via_per_form.as_ptr()),
19366 "iter_calls_to_any args must borrow the SAME slice as as_call_to_any's tail"
19367 );
19368 assert_eq!(via_iter.len(), via_per_form.len());
19369 }
19370
19371 #[test]
19372 fn iter_calls_to_any_is_the_slice_side_projection_of_as_call_to_any() {
19373 // The structural identity the lift establishes:
19374 // iter_calls_to_any(forms, decode) ==
19375 // forms.iter().filter_map(|f| f.as_call_to_any(&mut decode))
19376 // Pin shape AND ordering AND pointer-identity across mixed
19377 // inputs and a range of decoders (closed-set classifier,
19378 // always-accept identity, always-reject `None`, partial
19379 // closed-set on a single head) so a regression that drifts
19380 // the slice-side projection from its closed-form definition
19381 // fails loudly. The six soft-projection primitives —
19382 // `head_symbol`, `as_call`, `as_call_to`, `as_call_to_any`,
19383 // `iter_calls_to`, AND `iter_calls_to_any` — must agree on
19384 // operator-position recognition at every shape/slice they
19385 // share. Parallel to
19386 // `iter_calls_to_is_the_slice_side_projection_of_as_call_to`
19387 // for the keyword sibling.
19388 let forms = vec![
19389 Sexp::symbol("foo"),
19390 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
19391 Sexp::Nil,
19392 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(2)]),
19393 Sexp::int(99),
19394 Sexp::List(vec![Sexp::symbol("b"), Sexp::int(3)]),
19395 Sexp::List(vec![Sexp::symbol("a")]),
19396 Sexp::List(vec![]),
19397 Sexp::List(vec![Sexp::keyword("a"), Sexp::int(4)]),
19398 Sexp::List(vec![Sexp::symbol("c"), Sexp::int(5)]),
19399 ];
19400 // Closed-set classifier: accept "a" and "c", reject everything
19401 // else (including the call whose head is "b", to pin the
19402 // decoder-level rejection axis the keyword sibling does not
19403 // have).
19404 let decode_set =
19405 |h: &str| -> Option<&'static str> { matches!(h, "a" | "c").then_some("ac") };
19406 let via_iter: Vec<(&'static str, &[Sexp])> =
19407 iter_calls_to_any(&forms, decode_set).collect();
19408 let via_chain: Vec<(&'static str, &[Sexp])> = forms
19409 .iter()
19410 .filter_map(|f| f.as_call_to_any(decode_set))
19411 .collect();
19412 assert_eq!(
19413 via_iter.len(),
19414 via_chain.len(),
19415 "len drift between slice-side and per-form-chain"
19416 );
19417 for (a, b) in via_iter.iter().zip(via_chain.iter()) {
19418 assert_eq!(a.0, b.0, "decoded drift");
19419 assert!(
19420 std::ptr::eq(a.1.as_ptr(), b.1.as_ptr()),
19421 "ptr drift: slice-side does not borrow the SAME tail as the per-form chain"
19422 );
19423 assert_eq!(a.1.len(), b.1.len(), "len drift");
19424 }
19425 }
19426
19427 #[test]
19428 fn iter_calls_to_routes_through_iter_calls_to_any_via_constant_classifier_composition() {
19429 // The post-lift composition law binding the slice-side
19430 // keyword projection to the slice-side classifier projection:
19431 //
19432 // iter_calls_to(forms, k) ==
19433 // iter_calls_to_any(forms, |h| (h == k).then_some(())).map(|(_, a)| a)
19434 //
19435 // Pin shape AND ordering AND pointer-identity across a mixed
19436 // slice and three representative keywords (matching some,
19437 // matching none, edge-case empty string) so a regression that
19438 // drifts `iter_calls_to`'s body away from the typed-decoded
19439 // routing (e.g. re-inlines the `forms.iter().filter_map(|f|
19440 // f.as_call_to(keyword))` triple directly) fails loudly even
19441 // though the rendered slice-of-slices would still match the
19442 // keyword sibling's output. The pointer-equality axis is
19443 // load-bearing: a regression that re-derives the filter at
19444 // both sites would yield byte-identical slices but with
19445 // distinct closure-capture state, which the
19446 // pointer-identity check rejects only because both routes
19447 // share the SAME underlying form-tail borrow chain.
19448 //
19449 // Sibling-shape lift to prior-run `UnquoteForm::marker` ⊂
19450 // `to_quote_form().prefix()` composition (commit 250c001) and
19451 // `AtomKind::label` ⊂ `sexp_shape().label()` composition
19452 // (commit 1db697f): both pin the invariant that a typed
19453 // subset/keyword projection is structurally derived from its
19454 // parent superset/classifier projection, not a parallel
19455 // implementation the type system happens to not catch when
19456 // the two drift.
19457 let forms = vec![
19458 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
19459 Sexp::List(vec![Sexp::symbol("b"), Sexp::int(2)]),
19460 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(3)]),
19461 Sexp::List(vec![Sexp::symbol("c"), Sexp::int(4)]),
19462 Sexp::int(99),
19463 ];
19464 for k in ["a", "missing", ""] {
19465 let via_keyword: Vec<&[Sexp]> = iter_calls_to(&forms, k).collect();
19466 let via_classifier: Vec<&[Sexp]> =
19467 iter_calls_to_any(&forms, |h: &str| (h == k).then_some(()))
19468 .map(|(_, a)| a)
19469 .collect();
19470 assert_eq!(
19471 via_keyword.len(),
19472 via_classifier.len(),
19473 "len drift between keyword projection and classifier composition for k={k:?}"
19474 );
19475 for (a, b) in via_keyword.iter().zip(via_classifier.iter()) {
19476 assert!(
19477 std::ptr::eq(a.as_ptr(), b.as_ptr()),
19478 "ptr drift at k={k:?}: keyword projection does not share the SAME borrow with the classifier composition"
19479 );
19480 assert_eq!(a.len(), b.len(), "len drift at k={k:?}");
19481 }
19482 }
19483 }
19484
19485 #[test]
19486 fn iter_calls_to_any_admits_fnmut_classifier_maintaining_state_across_batch_walk() {
19487 // The slice-side primitive's `FnMut` constraint (vs the
19488 // per-form sibling's `FnOnce`) admits a classifier that
19489 // captures mutable state — a counter, a registry cache, a
19490 // visited-set. Pin the mutable-state contract: a counter
19491 // closure increments once per matching form (NOT once per
19492 // call to `f.as_call_to_any(decode)` at every form, since
19493 // `as_call_to_any` short-circuits before running `decode` on
19494 // non-list / empty-list / non-symbol-head shapes — only forms
19495 // that pass the shape gate reach the decoder). The counter's
19496 // post-walk value pins the exact number of forms that
19497 // (a) passed the shape gate AND (b) had a head matching the
19498 // classifier's predicate.
19499 let forms = vec![
19500 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
19501 Sexp::int(99), // not a call — `as_call_to_any` short-circuits, decoder never runs
19502 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(2)]),
19503 Sexp::List(vec![Sexp::symbol("b"), Sexp::int(3)]),
19504 Sexp::List(vec![]), // empty list — `as_call_to_any` short-circuits before decoder
19505 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(4)]),
19506 ];
19507 let mut decoder_calls = 0usize;
19508 // Consume the iterator into a count (NOT a Vec) so the closure
19509 // capture of `decoder_calls` is dropped at the iterator's end,
19510 // releasing the mutable borrow before the post-walk assertions
19511 // re-read `decoder_calls` immutably. A `Vec<((), &[Sexp])>`
19512 // collection would inherit the closure's `'a` lifetime through
19513 // the `iter_calls_to_any` return type's unified lifetime
19514 // parameter and keep the mutable borrow live across the assert
19515 // (the rust-borrow-checker contract — `decoded`'s lifetime
19516 // ties to `min(forms, closure)` even though the items
19517 // themselves only borrow from `forms`).
19518 let decoded_count = iter_calls_to_any(&forms, |h: &str| {
19519 decoder_calls += 1;
19520 (h == "a").then_some(())
19521 })
19522 .count();
19523 // Three forms have head "a"; one form has head "b"; the
19524 // non-call shapes (Int + empty list) short-circuit before the
19525 // decoder runs. Decoder is called 4 times (the 4 shape-gate-
19526 // passing forms); yields 3 matches.
19527 assert_eq!(
19528 decoder_calls, 4,
19529 "decoder must run once per shape-gate-passing form"
19530 );
19531 assert_eq!(
19532 decoded_count, 3,
19533 "three forms decode through the classifier"
19534 );
19535 }
19536
19537 // ── iter_named_calls_to_any / iter_named_calls_to: slice-side closure
19538 // of the (slice × classifier × named) and (slice × constant × named)
19539 // corners of the soft-dispatch cube. Pre-lift the named gate
19540 // composition (`split_name_slot` over a classifier-decoded args
19541 // tail) lived ONLY inside `Expander::expand_and_collect_named_calls_to_any`'s
19542 // projection closure — the slice algebra had no named sibling to
19543 // the bare [`iter_calls_to_any`]. Post-lift the gate is composed
19544 // at the slice level and the Expander surface routes through it
19545 // via the SAME `expand_program + iter + map + collect` pipeline
19546 // the bare expander surface uses. The tests below pin the slice
19547 // primitive's contract DIRECTLY — independent of the Expander
19548 // surface — so a classifier-NAME consumer that already holds
19549 // expanded forms (a `tatara-check` runner, an LSP buffer walker,
19550 // a REPL exhaustive lister) sees the SAME `NamedFormMissingName`
19551 // / `NamedFormNonSymbolName` rejection chain the Expander
19552 // consumer sees through the surface method.
19553
19554 #[test]
19555 fn iter_named_calls_to_any_yields_decoded_triple_for_every_matching_named_form_in_slice() {
19556 // Closed-set classifier (`Kind::{Foo, Bar}`) that rejects one head
19557 // out of three on a slice. Every matching form yields a
19558 // `Result<(Kind, &str, &[Sexp])>` triple in source order; the
19559 // unmatched form is skipped silently (NOT yielded as `Err`).
19560 // Fail-before-pass-after: this assert requires the slice
19561 // primitive to exist AND to yield the typed witness ALONGSIDE
19562 // the borrowed NAME slot AND the borrowed spec args tail — pre-
19563 // lift the slice algebra had no named sibling; consumers had
19564 // to re-derive the four-step `iter_calls_to_any(forms,
19565 // decode).map(|(d, args)| split_name_slot(args, k).map(|(n,
19566 // r)| (d, n, r)))` composition at their call site.
19567 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
19568 enum Kind {
19569 Foo,
19570 Bar,
19571 }
19572 let forms = crate::reader::read(
19573 "(deffoo alpha 1) (defbaz gamma 2) (defbar beta 3) (deffoo delta 4)",
19574 )
19575 .unwrap();
19576 let yielded: Vec<(Kind, String, usize)> =
19577 super::iter_named_calls_to_any(&forms, |h: &str| match h {
19578 "deffoo" => Some((Kind::Foo, "deffoo")),
19579 "defbar" => Some((Kind::Bar, "defbar")),
19580 _ => None,
19581 })
19582 .map(|maybe_triple| {
19583 maybe_triple.map(|(kind, name, args)| (kind, name.to_string(), args.len()))
19584 })
19585 .collect::<crate::error::Result<Vec<_>>>()
19586 .expect("slice-side named-classifier walk must succeed on well-formed forms");
19587 assert_eq!(
19588 yielded,
19589 vec![
19590 (Kind::Foo, "alpha".to_string(), 1),
19591 (Kind::Bar, "beta".into(), 1),
19592 (Kind::Foo, "delta".into(), 1),
19593 ],
19594 "iter_named_calls_to_any must yield (decoded, NAME, args_len) in source order, skipping defbaz",
19595 );
19596 }
19597
19598 #[test]
19599 fn iter_named_calls_to_any_skips_every_non_matching_form_shape_silently() {
19600 // Soft-projection contract: the slice primitive must skip every
19601 // shape the classifier rejects — non-list atoms, empty lists,
19602 // lists with non-symbol heads, lists with unrecognized symbol
19603 // heads — WITHOUT emitting the `NamedFormMissingName` /
19604 // `NamedFormNonSymbolName` variants. The named gate fires ONLY
19605 // for matched-keyword forms whose NAME slot is malformed, NEVER
19606 // for forms the classifier filtered out first.
19607 let forms = crate::reader::read(r#":kw "str" 42 () (unrecognized x) (5 y)"#).unwrap();
19608 let yielded: Vec<()> = super::iter_named_calls_to_any(&forms, |h: &str| {
19609 (h == "deffoo").then_some(((), "deffoo"))
19610 })
19611 .map(|maybe_triple| maybe_triple.map(|_| ()))
19612 .collect::<crate::error::Result<Vec<_>>>()
19613 .expect("slice-side named-classifier walk must succeed when zero forms match");
19614 assert!(
19615 yielded.is_empty(),
19616 "slice-side named-classifier walk must yield empty Vec when zero forms match",
19617 );
19618 }
19619
19620 #[test]
19621 fn iter_named_calls_to_any_emits_named_form_missing_name_for_matched_form_with_no_name_slot() {
19622 // `(deffoo)` — head matches the classifier (yielding the typed
19623 // witness AND the classifier-supplied static keyword), but the
19624 // NAME slot is missing. `split_name_slot`'s arity gate fires
19625 // inside the slice primitive and emits `NamedFormMissingName {
19626 // keyword: "deffoo" }`. Pin that the keyword threaded through
19627 // is the CLASSIFIER-supplied keyword (NOT a hardcoded fallback,
19628 // NOT the form's head symbol) — a regression that drifted the
19629 // keyword binding from `decode`'s tuple's second element to the
19630 // head symbol or to a constant would fail loudly here.
19631 let forms = crate::reader::read("(deffoo)").unwrap();
19632 let mut iter = super::iter_named_calls_to_any(&forms, |h: &str| {
19633 (h == "deffoo").then_some(((), "deffoo"))
19634 });
19635 let first = iter.next().expect("matched form must yield an item");
19636 let err = first.expect_err("matched form with missing NAME must yield Err");
19637 assert!(
19638 matches!(
19639 err,
19640 crate::error::LispError::NamedFormMissingName { keyword: "deffoo" }
19641 ),
19642 "expected NamedFormMissingName {{ keyword: \"deffoo\" }} through slice primitive, got: {err:?}"
19643 );
19644 }
19645
19646 #[test]
19647 fn iter_named_calls_to_any_emits_named_form_non_symbol_name_for_matched_form_with_int_name() {
19648 // `(deffoo 42)` — head matches and the NAME-slot arity gate
19649 // passes, but the NAME slot's shape gate rejects the int
19650 // literal. Pin that BOTH the classifier-supplied keyword AND
19651 // the typed `SexpShape::Int` projection flow into the
19652 // structural variant, identically to how
19653 // `Expander::expand_and_collect_named_calls_to_any` emits the
19654 // same variant when its projection composes the same gate.
19655 let forms = crate::reader::read("(deffoo 42)").unwrap();
19656 let mut iter = super::iter_named_calls_to_any(&forms, |h: &str| {
19657 (h == "deffoo").then_some(((), "deffoo"))
19658 });
19659 let first = iter.next().expect("matched form must yield an item");
19660 let err = first.expect_err("matched form with non-symbol NAME must yield Err");
19661 assert!(
19662 matches!(
19663 err,
19664 crate::error::LispError::NamedFormNonSymbolName {
19665 keyword: "deffoo",
19666 got: crate::error::SexpShape::Int,
19667 }
19668 ),
19669 "expected NamedFormNonSymbolName {{ keyword: \"deffoo\", got: Int }} through slice primitive, got: {err:?}"
19670 );
19671 }
19672
19673 #[test]
19674 fn iter_named_calls_to_any_emits_named_form_non_symbol_name_for_matched_form_with_keyword_name()
19675 {
19676 // `(deffoo :name)` — sibling shape pin to the int case: a
19677 // matched form whose NAME slot is a keyword. Together with the
19678 // int case this closes path-uniformity across distinct
19679 // non-symbol-or-string `SexpShape` cells at the slice primitive
19680 // boundary — every consumer routes through the SAME gate
19681 // composition regardless of the offending shape.
19682 let forms = crate::reader::read("(deffoo :name)").unwrap();
19683 let mut iter = super::iter_named_calls_to_any(&forms, |h: &str| {
19684 (h == "deffoo").then_some(((), "deffoo"))
19685 });
19686 let first = iter.next().expect("matched form must yield an item");
19687 let err = first.expect_err("matched form with keyword NAME must yield Err");
19688 assert!(
19689 matches!(
19690 err,
19691 crate::error::LispError::NamedFormNonSymbolName {
19692 keyword: "deffoo",
19693 got: crate::error::SexpShape::Keyword,
19694 }
19695 ),
19696 "expected NamedFormNonSymbolName {{ keyword: \"deffoo\", got: Keyword }} through slice primitive, got: {err:?}"
19697 );
19698 }
19699
19700 #[test]
19701 fn iter_named_calls_to_any_accepts_string_name_slot_routing_past_the_gate() {
19702 // `(deffoo "quoted-name" :k v)` — NAME slot is a string
19703 // literal, which `as_symbol_or_string` (inside `split_name_slot`)
19704 // accepts alongside symbols. Pin that the slice primitive
19705 // erases the quote-vs-symbol distinction at the boundary so a
19706 // consumer sees ONE `&str` shape regardless of authoring
19707 // choice, matching the equivalent gate in the typed-domain
19708 // consumer downstream of `named_form_projection<T>`.
19709 let forms = crate::reader::read(r#"(deffoo "quoted-name" :k "v")"#).unwrap();
19710 let yielded: Vec<(String, usize)> = super::iter_named_calls_to_any(&forms, |h: &str| {
19711 (h == "deffoo").then_some(((), "deffoo"))
19712 })
19713 .map(|maybe_triple| maybe_triple.map(|(_, name, args)| (name.to_string(), args.len())))
19714 .collect::<crate::error::Result<Vec<_>>>()
19715 .expect("string-author NAME slot must route past gate");
19716 assert_eq!(yielded, vec![("quoted-name".into(), 2)]);
19717 }
19718
19719 #[test]
19720 fn iter_named_calls_to_any_short_circuits_on_first_malformed_name_under_collect() {
19721 // `(deffoo good 1) (deffoo) (deffoo also-good 2)` — three
19722 // matched forms; the SECOND has no NAME slot. Pin that
19723 // `.collect::<Result<Vec<_>, _>>()` short-circuits at the
19724 // second form (yielding `Err`) WITHOUT yielding the third
19725 // form's payload. The iterator's lazy iteration combined with
19726 // `Result::collect`'s short-circuit gives consumers
19727 // first-failure semantics at the slice boundary, identical to
19728 // how `Expander::expand_and_collect_named_calls_to_any` already
19729 // short-circuits.
19730 let forms = crate::reader::read("(deffoo good 1) (deffoo) (deffoo also-good 2)").unwrap();
19731 let collected: crate::error::Result<Vec<()>> =
19732 super::iter_named_calls_to_any(&forms, |h: &str| {
19733 (h == "deffoo").then_some(((), "deffoo"))
19734 })
19735 .map(|maybe_triple| maybe_triple.map(|_| ()))
19736 .collect();
19737 let err = collected.expect_err("collect must surface the first failure");
19738 assert!(
19739 matches!(
19740 err,
19741 crate::error::LispError::NamedFormMissingName { keyword: "deffoo" }
19742 ),
19743 "expected NamedFormMissingName at the first malformed NAME, got: {err:?}"
19744 );
19745 }
19746
19747 #[test]
19748 fn iter_named_calls_to_yields_name_and_spec_args_for_every_matching_form_in_slice() {
19749 // Constant-keyword sibling of `iter_named_calls_to_any` —
19750 // discards the `()` typed witness and yields `Result<(&str,
19751 // &[Sexp])>` per matching form. Pin that the constant-keyword
19752 // primitive yields the SAME source-ordered set of triples the
19753 // typed-decoded sibling does on the same source, modulo the
19754 // discarded typed witness.
19755 let forms =
19756 crate::reader::read("(defcheck alpha 1) (other beta) (defcheck gamma 2 3)").unwrap();
19757 let yielded: Vec<(String, usize)> = super::iter_named_calls_to(&forms, "defcheck")
19758 .map(|maybe_pair| maybe_pair.map(|(name, args)| (name.to_string(), args.len())))
19759 .collect::<crate::error::Result<Vec<_>>>()
19760 .expect("constant-keyword named slice walk must succeed on well-formed forms");
19761 assert_eq!(
19762 yielded,
19763 vec![("alpha".into(), 1), ("gamma".into(), 2)],
19764 "iter_named_calls_to must yield (NAME, args_len) in source order, skipping unrelated forms",
19765 );
19766 }
19767
19768 #[test]
19769 fn iter_named_calls_to_routes_through_iter_named_calls_to_any_via_constant_classifier_composition(
19770 ) {
19771 // Pin the closed-form composition law binding the constant-
19772 // keyword named cell to the typed-decoded named-classifier cell
19773 // at the slice algebra boundary:
19774 //
19775 // iter_named_calls_to(forms, k) ==
19776 // iter_named_calls_to_any(forms, |h| (h == k).then_some(((), k)))
19777 // .map(|maybe| maybe.map(|(_, n, a)| (n, a)))
19778 //
19779 // This makes the typed-decoded named-classifier slice primitive
19780 // the CANONICAL composition point the constant-keyword sibling
19781 // routes through — parallel to how `iter_calls_to` /
19782 // `iter_calls_to_any` bind their composition law on the bare-
19783 // kwargs axis at the slice level. A regression that drifts ONE
19784 // sibling's pipeline from the other becomes loudly visible at
19785 // this assertion.
19786 let forms =
19787 crate::reader::read("(defcheck alpha 1) (other beta) (defcheck gamma 2 3)").unwrap();
19788 let via_constant: Vec<(String, usize)> = super::iter_named_calls_to(&forms, "defcheck")
19789 .map(|maybe| maybe.map(|(name, args)| (name.to_string(), args.len())))
19790 .collect::<crate::error::Result<Vec<_>>>()
19791 .expect("constant-keyword named slice walk must succeed");
19792 let via_classifier: Vec<(String, usize)> =
19793 super::iter_named_calls_to_any(&forms, |h: &str| {
19794 (h == "defcheck").then_some(((), "defcheck"))
19795 })
19796 .map(|maybe| maybe.map(|(_, name, args)| (name.to_string(), args.len())))
19797 .collect::<crate::error::Result<Vec<_>>>()
19798 .expect("typed-decoded named slice walk with constant-classifier decoder must succeed");
19799 assert_eq!(
19800 via_constant, via_classifier,
19801 "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)",
19802 );
19803 }
19804
19805 #[test]
19806 fn iter_named_calls_to_threads_static_keyword_through_missing_variant() {
19807 // Path-uniformity at the constant-keyword slice primitive
19808 // boundary: a static `&'static str` keyword threaded into the
19809 // primitive routes verbatim through the
19810 // `NamedFormMissingName.keyword` slot when a matched form has
19811 // no NAME — same threading discipline `split_name_slot` pins at
19812 // its boundary. Pin three distinct keywords ALL round-trip
19813 // through the variant's keyword slot.
19814 for keyword in ["defmonitor", "defalertpolicy", "defcheck"] {
19815 let src = format!("({keyword})");
19816 let forms = crate::reader::read(&src).unwrap();
19817 let mut iter = super::iter_named_calls_to(&forms, keyword);
19818 let first = iter.next().expect("matched form must yield an item");
19819 let err = first.expect_err("matched form with missing NAME must yield Err");
19820 match err {
19821 crate::error::LispError::NamedFormMissingName { keyword: got } => {
19822 assert_eq!(
19823 got, keyword,
19824 "constant-keyword slice primitive must thread keyword verbatim"
19825 );
19826 }
19827 other => {
19828 panic!("expected NamedFormMissingName for keyword {keyword:?}, got: {other:?}")
19829 }
19830 }
19831 }
19832 }
19833
19834 #[test]
19835 fn iter_named_calls_to_any_admits_fnmut_classifier_maintaining_state_across_batch_walk() {
19836 // The slice-side typed-decoded named primitive's `FnMut`
19837 // classifier constraint admits a closure that captures mutable
19838 // state across the batch walk — counter, registry cache,
19839 // visited-set — matching the bare-kwargs slice sibling's
19840 // contract. Pin: a counter-bumping decoder increments once per
19841 // shape-gate-passing form (NOT once per slice element, since
19842 // `iter_calls_to_any` short-circuits before the decoder on
19843 // non-list / empty-list / non-symbol-head shapes), and the
19844 // post-walk counter equals the number of forms that reached
19845 // the decoder.
19846 let forms =
19847 crate::reader::read("(deffoo a 1) 42 (deffoo b 2) () (defbar c 3) (deffoo d 4)")
19848 .unwrap();
19849 let mut decoder_calls = 0usize;
19850 let yielded: Vec<String> = super::iter_named_calls_to_any(&forms, |h: &str| {
19851 decoder_calls += 1;
19852 (h == "deffoo").then_some(((), "deffoo"))
19853 })
19854 .map(|maybe| maybe.map(|(_, name, _)| name.to_string()))
19855 .collect::<crate::error::Result<Vec<_>>>()
19856 .expect("FnMut classifier dispatch must succeed on well-formed NAME slots");
19857 // Four (defX …) call forms in the slice pass the shape gate;
19858 // the int atom and empty list short-circuit before the
19859 // decoder. Three of the four pass-through-decoder forms
19860 // dispatch to deffoo; one dispatches to defbar (rejected by
19861 // the decoder).
19862 assert_eq!(
19863 decoder_calls, 4,
19864 "FnMut decoder must run once per shape-gate-passing form (4 call forms)"
19865 );
19866 assert_eq!(
19867 yielded,
19868 vec!["a".to_string(), "b".into(), "d".into()],
19869 "three (deffoo …) forms match; one (defbar …) form is rejected by the decoder",
19870 );
19871 }
19872
19873 #[test]
19874 fn iter_named_calls_to_any_yields_borrowed_name_and_args_with_form_lifetime() {
19875 // Pin the borrow-lifetime contract at the slice primitive
19876 // boundary: the yielded `&'a str` NAME slot and `&'a [Sexp]`
19877 // spec args tail must borrow from the input slice verbatim —
19878 // no copy, no allocation. A consumer that holds the iterator's
19879 // yields alongside the input slice borrow can use the NAME as
19880 // a lookup key against a registry without paying for a clone.
19881 let forms = crate::reader::read("(deffoo my-name :k 1 :j 2)").unwrap();
19882 let mut iter = super::iter_named_calls_to_any(&forms, |h: &str| {
19883 (h == "deffoo").then_some(((), "deffoo"))
19884 });
19885 let (_, name, spec_args) = iter
19886 .next()
19887 .expect("matched form must yield an item")
19888 .expect("well-formed NAME slot must split");
19889 // Identity-check the NAME borrow: it must point at the same
19890 // bytes the form's NAME slot symbol borrows from.
19891 let form_list = forms[0].as_list().expect("form must be a list");
19892 let form_name = form_list[1]
19893 .as_symbol()
19894 .expect("form NAME must be a symbol");
19895 assert!(
19896 std::ptr::eq(name.as_ptr(), form_name.as_ptr()),
19897 "iter_named_calls_to_any must yield the borrowed NAME, NOT an allocated copy"
19898 );
19899 // Spec args tail must borrow from the form's tail starting at
19900 // index 2 (after the NAME slot at index 1).
19901 assert!(
19902 std::ptr::eq(spec_args.as_ptr(), &form_list[2] as *const Sexp),
19903 "iter_named_calls_to_any must yield the borrowed spec args tail, NOT an allocated copy"
19904 );
19905 assert_eq!(spec_args.len(), 4);
19906 }
19907
19908 // ── as_named_call_to_any / as_named_call_to: per-form × named cell ──
19909 //
19910 // The per-form × named corner of the soft-dispatch cube the slice
19911 // primitive `iter_named_calls_to_any`'s docstring table identified as
19912 // the documented gap pre-lift ("(composed inline at each named
19913 // consumer)"). Post-lift the per-form × named row binds to ONE
19914 // primitive every per-form named consumer composes through, and the
19915 // slice-side `iter_named_calls_to_any` routes through it via the SAME
19916 // `forms.iter().filter_map(_)` skeleton `iter_calls_to_any` uses to
19917 // route through `as_call_to_any`. These tests pin: (a) the three-arm
19918 // result shape (None for non-match, Some(Ok) for matched-and-
19919 // well-formed, Some(Err) for matched-but-malformed-NAME) across each
19920 // distinct shape, (b) the constant-keyword sibling routes through
19921 // the typed-decoded sibling via constant-classifier composition, (c)
19922 // the slice-side `iter_named_calls_to_any` IS the
19923 // `forms.iter().filter_map(|f| f.as_named_call_to_any(_))` projection
19924 // — the structural identity binding the per-form to the slice row.
19925
19926 #[test]
19927 fn as_named_call_to_any_returns_decoded_triple_for_matched_well_formed_form() {
19928 // `(deffoo my-name :k 1)` — head matches the classifier's `deffoo`
19929 // arm, NAME slot is the symbol `my-name`, spec args tail is the
19930 // two-element `:k 1` pair. Pin Some(Ok((decoded, name, args))).
19931 // Fail-before-pass-after: this assert requires the per-form
19932 // method to exist AND to thread the typed witness + borrowed
19933 // NAME + borrowed spec args through ONE projection.
19934 #[derive(Debug, PartialEq, Eq)]
19935 enum Kind {
19936 Foo,
19937 }
19938 let form = crate::reader::read("(deffoo my-name :k 1)").unwrap()[0].clone();
19939 let res = form
19940 .as_named_call_to_any(|h: &str| match h {
19941 "deffoo" => Some((Kind::Foo, "deffoo")),
19942 _ => None,
19943 })
19944 .expect("matched head must yield Some(_)")
19945 .expect("well-formed NAME slot must split");
19946 assert_eq!(res.0, Kind::Foo);
19947 assert_eq!(res.1, "my-name");
19948 assert_eq!(res.2.len(), 2);
19949 }
19950
19951 #[test]
19952 fn as_named_call_to_any_returns_none_when_decoder_rejects_head() {
19953 // `(unrelated my-name :k 1)` — head is a symbol, but the
19954 // classifier returns `None`. Pin: the classifier filter face is
19955 // identical to `as_call_to_any` — `None` short-circuits BEFORE
19956 // the named gate runs, so a non-matching head with a malformed
19957 // NAME slot still yields `None`, NOT `Some(Err)`. The soft-
19958 // filter face is preserved across the cube row.
19959 let form = crate::reader::read("(unrelated my-name :k 1)").unwrap()[0].clone();
19960 assert!(form
19961 .as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
19962 .is_none());
19963 }
19964
19965 #[test]
19966 fn as_named_call_to_any_returns_none_for_non_call_shapes() {
19967 // Every shape `as_call_to_any` rejects, `as_named_call_to_any`
19968 // rejects identically — atom, keyword, empty list, list with
19969 // non-symbol head. The classifier-filter face is uniformly the
19970 // soft per-form posture of every other `as_*` method on `Sexp`.
19971 let shapes: Vec<Sexp> = vec![
19972 Sexp::int(5),
19973 Sexp::keyword("deffoo"),
19974 Sexp::Nil,
19975 Sexp::List(vec![]),
19976 Sexp::List(vec![Sexp::int(1), Sexp::symbol("my-name")]),
19977 ];
19978 for s in shapes {
19979 assert!(
19980 s.as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
19981 .is_none(),
19982 "non-call shape must yield None for as_named_call_to_any: {s}"
19983 );
19984 }
19985 }
19986
19987 #[test]
19988 fn as_named_call_to_any_returns_some_err_for_matched_head_with_no_name_slot() {
19989 // `(deffoo)` — head matches the classifier's `deffoo` arm but
19990 // the form is a singleton: NO NAME slot at all. The named gate
19991 // (`split_name_slot`'s arity gate) fires structurally, yielding
19992 // `Some(Err(LispError::NamedFormMissingName { keyword: "deffoo" }))`.
19993 // Pin the strict-gate face on the named row: matched-and-
19994 // malformed yields the typed structural rejection variant, NOT
19995 // `None` (which would conflate "not our head" with "our head
19996 // but missing NAME" and break the cube's strict-vs-soft split).
19997 let form = crate::reader::read("(deffoo)").unwrap()[0].clone();
19998 let err = form
19999 .as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
20000 .expect("matched head must yield Some(_)")
20001 .expect_err("missing NAME slot must yield Err");
20002 assert!(
20003 matches!(
20004 err,
20005 crate::error::LispError::NamedFormMissingName { keyword: "deffoo" }
20006 ),
20007 "expected NamedFormMissingName through per-form primitive, got: {err:?}"
20008 );
20009 }
20010
20011 #[test]
20012 fn as_named_call_to_any_returns_some_err_for_matched_head_with_non_symbol_name() {
20013 // `(deffoo 5 :k 1)` — head matches but NAME slot is an int
20014 // literal. The named gate's `as_symbol_or_string` shape gate
20015 // fires, yielding `Some(Err(LispError::NamedFormNonSymbolName
20016 // { keyword: "deffoo", got: SexpShape::Int }))`. Pin the strict-
20017 // gate face for the second structural rejection variant of the
20018 // named gate AND the typed `SexpShape` projection of the
20019 // offending slot.
20020 let form = crate::reader::read("(deffoo 5 :k 1)").unwrap()[0].clone();
20021 let err = form
20022 .as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
20023 .expect("matched head must yield Some(_)")
20024 .expect_err("non-symbol NAME slot must yield Err");
20025 assert!(
20026 matches!(
20027 err,
20028 crate::error::LispError::NamedFormNonSymbolName {
20029 keyword: "deffoo",
20030 got: crate::error::SexpShape::Int,
20031 }
20032 ),
20033 "expected NamedFormNonSymbolName through per-form primitive, got: {err:?}"
20034 );
20035 }
20036
20037 #[test]
20038 fn as_named_call_to_constant_keyword_routes_through_as_named_call_to_any() {
20039 // Pin the closed-form composition binding the constant-keyword
20040 // sibling to the typed-decoded sibling:
20041 // as_named_call_to(k) ==
20042 // as_named_call_to_any(|h| (h == k).then_some(((), k)))
20043 // .map(|res| res.map(|(_, name, rest)| (name, rest)))
20044 // across every shape in the test fixture set. A regression
20045 // that re-implements the constant-keyword sibling without
20046 // routing through the classifier sibling fails this assertion
20047 // for the matched-and-well-formed AND matched-but-malformed
20048 // AND non-match arms simultaneously.
20049 let shapes: Vec<Sexp> = vec![
20050 crate::reader::read("(defcompiler my-comp :a 1)").unwrap()[0].clone(),
20051 crate::reader::read("(defcompiler)").unwrap()[0].clone(),
20052 crate::reader::read("(defcompiler 5)").unwrap()[0].clone(),
20053 crate::reader::read("(unrelated my-name :k 1)").unwrap()[0].clone(),
20054 Sexp::int(99),
20055 Sexp::List(vec![]),
20056 ];
20057 // `LispError` is not `PartialEq` (it transitively wraps `Sexp`,
20058 // which carries an `Atom::Float` whose `f64` is not `Eq`).
20059 // Compare via formatted-debug strings on the Err arm; Ok arms and
20060 // None arm compare structurally. The closed-form composition
20061 // `as_named_call_to(k) == as_named_call_to_any+unit-decoder` is
20062 // pinned across all three arms.
20063 for s in &shapes {
20064 let via_constant = s.as_named_call_to("defcompiler").map(|res| {
20065 res.map(|(name, rest)| (name.to_string(), rest.len()))
20066 .map_err(|e| format!("{e:?}"))
20067 });
20068 let via_classifier = s
20069 .as_named_call_to_any(|h: &str| (h == "defcompiler").then_some(((), "defcompiler")))
20070 .map(|res| {
20071 res.map(|(_, name, rest)| (name.to_string(), rest.len()))
20072 .map_err(|e| format!("{e:?}"))
20073 });
20074 assert_eq!(
20075 via_constant, via_classifier,
20076 "as_named_call_to(k) must equal as_named_call_to_any+unit-decoder for {s}"
20077 );
20078 }
20079 }
20080
20081 #[test]
20082 fn iter_named_calls_to_any_is_the_slice_side_filter_map_of_as_named_call_to_any() {
20083 // Pin the structural identity binding the slice algebra to the
20084 // per-form algebra:
20085 // iter_named_calls_to_any(forms, decode) ==
20086 // forms.iter().filter_map(|f| f.as_named_call_to_any(&mut decode))
20087 // Both sides must yield the SAME Result shape per element in
20088 // source order — `Ok(triple)` for matched-and-well-formed,
20089 // `Err(LispError)` for matched-but-malformed, with non-matches
20090 // skipped by the filter_map. Sibling pin to
20091 // `iter_calls_to_any_is_the_slice_side_projection_of_as_call_to_any`
20092 // on the bare-kwargs row — both rows now share ONE
20093 // `forms.iter().filter_map(_)` skeleton.
20094 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
20095 enum Kind {
20096 Foo,
20097 Bar,
20098 }
20099 let src = "(deffoo a :k 1)
20100 (other thing)
20101 (defbar 7 :j 2)
20102 (deffoo b)
20103 (defbaz c :m 3)";
20104 let forms = crate::reader::read(src).unwrap();
20105 let decode = |h: &str| match h {
20106 "deffoo" => Some((Kind::Foo, "deffoo")),
20107 "defbar" => Some((Kind::Bar, "defbar")),
20108 _ => None,
20109 };
20110 let via_iter: Vec<crate::error::Result<(Kind, String, usize)>> =
20111 super::iter_named_calls_to_any(&forms, decode)
20112 .map(|res| res.map(|(k, name, args)| (k, name.to_string(), args.len())))
20113 .collect();
20114 let via_filter_map: Vec<crate::error::Result<(Kind, String, usize)>> = forms
20115 .iter()
20116 .filter_map(|f| f.as_named_call_to_any(decode))
20117 .map(|res| res.map(|(k, name, args)| (k, name.to_string(), args.len())))
20118 .collect();
20119 assert_eq!(
20120 via_iter.len(),
20121 via_filter_map.len(),
20122 "slice-side iter must yield the same number of items as the per-form filter_map",
20123 );
20124 for (a, b) in via_iter.iter().zip(via_filter_map.iter()) {
20125 match (a, b) {
20126 (Ok(ta), Ok(tb)) => assert_eq!(ta, tb),
20127 (Err(ea), Err(eb)) => assert_eq!(format!("{ea:?}"), format!("{eb:?}")),
20128 _ => panic!(
20129 "variant drift between slice iter and per-form filter_map: {a:?} vs {b:?}"
20130 ),
20131 }
20132 }
20133 // Concretely: 3 matched forms (deffoo a, defbar 7, deffoo b);
20134 // `defbar 7` yields Err (int NAME), other two yield Ok.
20135 assert_eq!(via_iter.len(), 3);
20136 assert!(via_iter[0].is_ok());
20137 assert!(via_iter[1].is_err());
20138 assert!(via_iter[2].is_ok());
20139 }
20140
20141 #[test]
20142 fn as_named_call_to_any_borrows_name_and_spec_args_from_form_verbatim() {
20143 // Pin the borrow-lifetime contract at the per-form primitive
20144 // boundary: the yielded `&str` NAME slot and `&[Sexp]` spec
20145 // args tail must borrow from the underlying form verbatim — no
20146 // copy, no allocation. Sibling pin to
20147 // `iter_named_calls_to_any_yields_borrowed_name_and_args_with_form_lifetime`
20148 // on the slice algebra — both rows preserve the borrow
20149 // contract.
20150 let forms = crate::reader::read("(deffoo my-name :k 1 :j 2)").unwrap();
20151 let form = &forms[0];
20152 let (_, name, spec_args) = form
20153 .as_named_call_to_any(|h: &str| (h == "deffoo").then_some(((), "deffoo")))
20154 .expect("matched head must yield Some(_)")
20155 .expect("well-formed NAME slot must split");
20156 let form_list = form.as_list().expect("form must be a list");
20157 let form_name = form_list[1]
20158 .as_symbol()
20159 .expect("form NAME must be a symbol");
20160 assert!(
20161 std::ptr::eq(name.as_ptr(), form_name.as_ptr()),
20162 "as_named_call_to_any must yield the borrowed NAME, NOT an allocated copy"
20163 );
20164 assert!(
20165 std::ptr::eq(spec_args.as_ptr(), &form_list[2] as *const Sexp),
20166 "as_named_call_to_any must yield the borrowed spec args tail, NOT an allocated copy"
20167 );
20168 assert_eq!(spec_args.len(), 4);
20169 }
20170
20171 // ── as_unquote: the unquote-family projection ───────────────────────
20172 //
20173 // `as_unquote` lifts the per-callsite `Sexp::Unquote(inner) /
20174 // Sexp::UnquoteSplice(inner)` arms paired with their `UnquoteForm::
20175 // Unquote / UnquoteForm::Splice` literals — three sites pre-lift
20176 // (`compile_node` 2 arms + `substitute` top-level + `substitute`
20177 // list-inner) — into ONE typed projection on the `Sexp` algebra.
20178 // These tests pin its contract; the existing path tests in
20179 // macro_expand.rs are the path-uniformity guards proving the three
20180 // sites route through it without behavior drift.
20181
20182 #[test]
20183 fn as_unquote_decomposes_unquote_into_typed_marker_and_inner() {
20184 // `,x` — Sexp::Unquote wrapping a symbol. Pin Some((Unquote, &inner)).
20185 let inner = Sexp::symbol("x");
20186 let form = Sexp::Unquote(Box::new(inner.clone()));
20187 let (marker, body) = form
20188 .as_unquote()
20189 .expect("`,x` must project to Some((Unquote, _))");
20190 assert_eq!(marker, UnquoteForm::Unquote);
20191 assert_eq!(body, &inner);
20192 }
20193
20194 #[test]
20195 fn as_unquote_decomposes_unquote_splice_into_typed_marker_and_inner() {
20196 // `,@xs` — Sexp::UnquoteSplice wrapping a symbol. Pin
20197 // Some((Splice, &inner)). Sibling positive control to the Unquote
20198 // arm: pins BOTH unquote-family variants project to their typed
20199 // closed-set UnquoteForm pair through ONE projection function.
20200 let inner = Sexp::symbol("xs");
20201 let form = Sexp::UnquoteSplice(Box::new(inner.clone()));
20202 let (marker, body) = form
20203 .as_unquote()
20204 .expect("`,@xs` must project to Some((Splice, _))");
20205 assert_eq!(marker, UnquoteForm::Splice);
20206 assert_eq!(body, &inner);
20207 }
20208
20209 #[test]
20210 fn as_unquote_none_for_non_unquote_shapes() {
20211 // Every Sexp shape OUTSIDE the unquote family — atoms, lists, nil,
20212 // and the OTHER quote-family variants (Quote `'x`, Quasiquote ``x`) —
20213 // yields None. Pins the projection's exhaustive negative coverage:
20214 // a regression that drifts the matched-variant set (e.g. a future
20215 // emitter that projects `'x` into Some((Unquote, _))) would fail
20216 // here, even before any downstream dispatcher tests fire.
20217 assert_eq!(Sexp::symbol("foo").as_unquote(), None);
20218 assert_eq!(Sexp::int(5).as_unquote(), None);
20219 assert_eq!(Sexp::keyword("k").as_unquote(), None);
20220 assert_eq!(Sexp::string("s").as_unquote(), None);
20221 assert_eq!(Sexp::boolean(true).as_unquote(), None);
20222 assert_eq!(Sexp::float(1.5).as_unquote(), None);
20223 assert_eq!(Sexp::Nil.as_unquote(), None);
20224 assert_eq!(Sexp::List(vec![]).as_unquote(), None);
20225 assert_eq!(
20226 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]).as_unquote(),
20227 None
20228 );
20229 // `'x` — Quote-family but NOT unquote-family. The closed-set
20230 // UnquoteForm projection covers only `,` and `,@`; `'` and `` ` ``
20231 // are siblings that this projection does NOT match.
20232 assert_eq!(Sexp::Quote(Box::new(Sexp::symbol("x"))).as_unquote(), None);
20233 assert_eq!(
20234 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))).as_unquote(),
20235 None
20236 );
20237 }
20238
20239 #[test]
20240 fn as_unquote_is_some_iff_matches_unquote_family() {
20241 // Structural identity: as_unquote().is_some() agrees with the
20242 // pre-lift `matches!(self, Sexp::Unquote(_) | Sexp::UnquoteSplice(_))`
20243 // discriminant across the closed Sexp variant set. Sweep every
20244 // representative Sexp shape and pin equality of the two discriminants
20245 // — a regression that drifts ONE shape's projection (e.g. adds
20246 // Quasiquote to the matched set) becomes a typed test failure.
20247 let shapes: Vec<(&str, Sexp, bool)> = vec![
20248 ("nil", Sexp::Nil, false),
20249 ("symbol", Sexp::symbol("x"), false),
20250 ("keyword", Sexp::keyword("k"), false),
20251 ("string", Sexp::string("s"), false),
20252 ("int", Sexp::int(7), false),
20253 ("float", Sexp::float(2.5), false),
20254 ("bool", Sexp::boolean(true), false),
20255 ("empty list", Sexp::List(vec![]), false),
20256 (
20257 "non-empty list",
20258 Sexp::List(vec![Sexp::symbol("op")]),
20259 false,
20260 ),
20261 ("quote", Sexp::Quote(Box::new(Sexp::symbol("x"))), false),
20262 (
20263 "quasiquote",
20264 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
20265 false,
20266 ),
20267 ("unquote", Sexp::Unquote(Box::new(Sexp::symbol("x"))), true),
20268 (
20269 "unquote-splice",
20270 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
20271 true,
20272 ),
20273 ];
20274 for (label, sexp, expect_some) in &shapes {
20275 let via_proj = sexp.as_unquote().is_some();
20276 let via_pat = matches!(sexp, Sexp::Unquote(_) | Sexp::UnquoteSplice(_));
20277 assert_eq!(
20278 via_proj, *expect_some,
20279 "as_unquote().is_some() drifted from expected at {label}"
20280 );
20281 assert_eq!(
20282 via_proj, via_pat,
20283 "as_unquote().is_some() != pre-lift `matches!(_, Unquote | UnquoteSplice)` at {label}"
20284 );
20285 }
20286 }
20287
20288 #[test]
20289 fn as_unquote_inner_pointer_is_the_boxed_body() {
20290 // The returned `&Sexp` borrows the inner box's body verbatim — no
20291 // clone, no allocation, same lifetime as `&self`. Pin pointer
20292 // identity: the returned `&Sexp` shares its address with the
20293 // contents of the original Box, proving no intermediate copy fires
20294 // at the projection boundary (so consumers walking deeply nested
20295 // template bodies pay zero allocation per unquote node).
20296 let inner = Sexp::symbol("payload");
20297 let boxed = Box::new(inner);
20298 let inner_ptr: *const Sexp = boxed.as_ref();
20299 let form = Sexp::Unquote(boxed);
20300 let (_, body) = form
20301 .as_unquote()
20302 .expect("Sexp::Unquote must project to Some");
20303 assert!(
20304 std::ptr::eq(body, inner_ptr),
20305 "as_unquote inner pointer drifted from the boxed body — projection allocates or clones"
20306 );
20307
20308 let inner_splice = Sexp::symbol("payload-splice");
20309 let boxed_splice = Box::new(inner_splice);
20310 let inner_splice_ptr: *const Sexp = boxed_splice.as_ref();
20311 let form_splice = Sexp::UnquoteSplice(boxed_splice);
20312 let (_, body_splice) = form_splice
20313 .as_unquote()
20314 .expect("Sexp::UnquoteSplice must project to Some");
20315 assert!(
20316 std::ptr::eq(body_splice, inner_splice_ptr),
20317 "as_unquote inner pointer drifted from the boxed body (splice arm)"
20318 );
20319 }
20320
20321 // ── fmt_float: Display→read round-trip preserves Float identity ──────
20322 //
20323 // Rust's stdlib Display for f64 elides trailing `.0` on integral
20324 // floats — `format!("{}", 1.0_f64) == "1"` — and the substrate's
20325 // reader tries `i64::parse` before `f64::parse`, so a bare `1` re-reads
20326 // as `Atom::Int(1)`, NOT `Atom::Float(1.0)`. The Display→read
20327 // round-trip pre-lift dropped the typed Float identity on every
20328 // integral float: `Float(1.0)` displayed as `"1"`, re-read as `Int(1)`,
20329 // and downstream consumers silently typed the slot as Int. The
20330 // `fmt_float` helper appends `.0` for finite integral values so the
20331 // round-trip preserves the typed identity. Tests below pin:
20332 // (a) Display of `Float(1.0)` is `"1.0"` (fail-before-pass-after);
20333 // (b) the Display→read round-trip lands as `Float(1.0)`, NOT
20334 // `Int(1)` (the typed-identity preservation contract);
20335 // (c) non-integral floats render unchanged through the default
20336 // impl (`Float(1.5)` is still `"1.5"`);
20337 // (d) negative integral floats inherit the `.0` suffix
20338 // (`Float(-2.0)` is `"-2.0"`);
20339 // (e) integer Display is unaffected (`Int(1)` is still `"1"`) —
20340 // pin path-uniformity so the helper is precisely scoped to
20341 // the Float arm.
20342
20343 #[test]
20344 fn fmt_float_renders_integral_float_with_trailing_zero() {
20345 // Fail-before-pass-after: pre-lift `Sexp::float(1.0).to_string()`
20346 // was `"1"`; post-lift the typed Float identity is preserved by
20347 // the `.0` suffix.
20348 assert_eq!(Sexp::float(1.0).to_string(), "1.0");
20349 assert_eq!(Sexp::float(100.0).to_string(), "100.0");
20350 assert_eq!(Sexp::float(0.0).to_string(), "0.0");
20351 }
20352
20353 #[test]
20354 fn fmt_float_round_trips_integral_float_through_reader_as_float() {
20355 // The structural contract the lift establishes: a `Float`
20356 // serialized via `Display` re-reads as `Float`, NOT `Int`. Pin
20357 // the round-trip via the reader so a regression that drops the
20358 // `.0` suffix (or that re-orders the reader's i64/f64 parse
20359 // attempts to drop the float arm) surfaces here.
20360 let orig = Sexp::float(1.0);
20361 let rendered = orig.to_string();
20362 let forms =
20363 crate::reader::read(&rendered).expect("integral float must round-trip through reader");
20364 assert_eq!(forms.len(), 1);
20365 match &forms[0] {
20366 Sexp::Atom(Atom::Float(n)) => assert_eq!(*n, 1.0),
20367 other => panic!("Display->read round-trip dropped the Float identity, got: {other:?}"),
20368 }
20369 // Sibling-shape control: a SECOND integral magnitude reinforces
20370 // that the round-trip preserves the value, not only the type.
20371 let orig2 = Sexp::float(-42.0);
20372 let rendered2 = orig2.to_string();
20373 let forms2 = crate::reader::read(&rendered2)
20374 .expect("negative integral float must round-trip through reader");
20375 match &forms2[0] {
20376 Sexp::Atom(Atom::Float(n)) => assert_eq!(*n, -42.0),
20377 other => panic!(
20378 "Display->read of negative integral float dropped Float identity, got: {other:?}"
20379 ),
20380 }
20381 }
20382
20383 #[test]
20384 fn fmt_float_preserves_non_integral_float_display() {
20385 // Path-uniformity: non-integral floats (the case the stdlib impl
20386 // already handled correctly) must render unchanged. A regression
20387 // that always-appends `.0` would write `"1.5.0"` and fail
20388 // here AND fail the reader round-trip below.
20389 assert_eq!(Sexp::float(1.5).to_string(), "1.5");
20390 assert_eq!(Sexp::float(0.99).to_string(), "0.99");
20391 assert_eq!(Sexp::float(-2.75).to_string(), "-2.75");
20392
20393 // Round-trip control for the non-integral case stays valid: the
20394 // helper is precisely scoped, so the fractional component is
20395 // preserved verbatim through the reader.
20396 let orig = Sexp::float(0.99);
20397 let forms = crate::reader::read(&orig.to_string())
20398 .expect("non-integral float must round-trip through reader");
20399 match &forms[0] {
20400 Sexp::Atom(Atom::Float(n)) => assert_eq!(*n, 0.99),
20401 other => panic!("non-integral float round-trip drift, got: {other:?}"),
20402 }
20403 }
20404
20405 // ── QuoteForm + as_quote_form: closed-set quote-family projection ─────
20406 //
20407 // `as_quote_form` lifts the per-callsite `Sexp::Quote(inner)
20408 // / Sexp::Quasiquote(inner) / Sexp::Unquote(inner) /
20409 // Sexp::UnquoteSplice(inner)` arm-set paired with their
20410 // per-variant prefix string (`'`, `` ` ``, `,`, `,@`) and
20411 // discriminator byte (3, 4, 5, 6) into ONE typed projection on
20412 // the `Sexp` algebra. Three consumers in this file route through
20413 // it (`Hash for Sexp`, `Display for Sexp`, `Sexp::as_unquote`)
20414 // so the (Sexp variant, marker, prefix, discriminator) tuple
20415 // binds at ONE site. Tests below pin:
20416 // (a) the projection lands `Some((QuoteForm::*, inner))` for
20417 // each of the four wrapper variants AND `None` for every
20418 // non-quote-family shape;
20419 // (b) `QuoteForm::prefix` returns the canonical reader-token
20420 // prefix for each variant — load-bearing for the round-trip
20421 // property the `Display`→reader dual encodes;
20422 // (c) `QuoteForm::hash_discriminator` returns the same byte
20423 // values the pre-lift Hash arms emitted (3, 4, 5, 6) — pin
20424 // the cache-key contract so a regression that drifts a
20425 // discriminator silently invalidates every cached expansion
20426 // fails loudly here;
20427 // (d) `QuoteForm::as_unquote_form` projects the 2-of-4 subset
20428 // `{Unquote → UnquoteForm::Unquote, UnquoteSplice →
20429 // UnquoteForm::Splice}` and yields `None` for `{Quote,
20430 // Quasiquote}` — the structural-subset gate the
20431 // `Sexp::as_unquote` derivation routes through;
20432 // (e) `Sexp::as_unquote` derived from `as_quote_form +
20433 // QuoteForm::as_unquote_form` agrees with the pre-lift
20434 // arm-based semantic across every Sexp shape — path
20435 // uniformity across the subset gate;
20436 // (f) the four homoiconic prefixes round-trip through the
20437 // reader via `read(format!("{prefix}{inner}"))` into the
20438 // matching `Sexp::*` variant — the typed dual of the
20439 // reader's prefix dispatch, pinned end-to-end on the four
20440 // wrappers (sibling to `fmt_float`'s Float round-trip pin
20441 // at the Display→read boundary).
20442
20443 #[test]
20444 fn as_quote_form_projects_each_wrapper_variant_to_typed_marker_and_inner() {
20445 // `'foo` — Sexp::Quote wrapping a symbol. Pin Some((Quote, &inner))
20446 // with the typed marker AND the borrowed inner body.
20447 let inner = Sexp::symbol("foo");
20448 let form = Sexp::Quote(Box::new(inner.clone()));
20449 let (qf, body) = form.as_quote_form().expect("Sexp::Quote must project");
20450 assert_eq!(qf, QuoteForm::Quote);
20451 assert_eq!(body, &inner);
20452
20453 // `` `foo `` — Sexp::Quasiquote wrapping a symbol.
20454 let form_qq = Sexp::Quasiquote(Box::new(inner.clone()));
20455 let (qf_qq, body_qq) = form_qq
20456 .as_quote_form()
20457 .expect("Sexp::Quasiquote must project");
20458 assert_eq!(qf_qq, QuoteForm::Quasiquote);
20459 assert_eq!(body_qq, &inner);
20460
20461 // `,foo` — Sexp::Unquote wrapping a symbol.
20462 let form_u = Sexp::Unquote(Box::new(inner.clone()));
20463 let (qf_u, body_u) = form_u.as_quote_form().expect("Sexp::Unquote must project");
20464 assert_eq!(qf_u, QuoteForm::Unquote);
20465 assert_eq!(body_u, &inner);
20466
20467 // `,@xs` — Sexp::UnquoteSplice wrapping a symbol.
20468 let form_us = Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs")));
20469 let (qf_us, body_us) = form_us
20470 .as_quote_form()
20471 .expect("Sexp::UnquoteSplice must project");
20472 assert_eq!(qf_us, QuoteForm::UnquoteSplice);
20473 assert_eq!(body_us, &Sexp::symbol("xs"));
20474 }
20475
20476 #[test]
20477 fn as_quote_form_none_for_non_quote_family_shapes() {
20478 // Every shape OUTSIDE the closed quote-family must project to
20479 // None: Nil, every Atom variant, and List (empty + populated).
20480 // Pin the closed-set boundary so a regression that accidentally
20481 // promotes a non-wrapper variant into the quote family becomes
20482 // a typed test failure.
20483 assert_eq!(Sexp::Nil.as_quote_form(), None);
20484 assert_eq!(Sexp::symbol("x").as_quote_form(), None);
20485 assert_eq!(Sexp::keyword("k").as_quote_form(), None);
20486 assert_eq!(Sexp::string("s").as_quote_form(), None);
20487 assert_eq!(Sexp::int(7).as_quote_form(), None);
20488 assert_eq!(Sexp::float(2.5).as_quote_form(), None);
20489 assert_eq!(Sexp::boolean(true).as_quote_form(), None);
20490 assert_eq!(Sexp::List(vec![]).as_quote_form(), None);
20491 assert_eq!(
20492 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]).as_quote_form(),
20493 None
20494 );
20495 }
20496
20497 #[test]
20498 fn as_quote_form_inner_pointer_is_the_boxed_body() {
20499 // The returned `&Sexp` borrows the inner box's body verbatim —
20500 // no clone, no allocation, same lifetime as `&self`. Pin
20501 // pointer identity for each of the four wrapper variants so a
20502 // regression that adds an intermediate copy at the projection
20503 // boundary surfaces here. Same posture as
20504 // `as_unquote_inner_pointer_is_the_boxed_body` for its 2-of-4
20505 // subset.
20506 let payload = Sexp::symbol("payload");
20507 let boxed = Box::new(payload);
20508 let inner_ptr: *const Sexp = boxed.as_ref();
20509 let form = Sexp::Quote(boxed);
20510 let (_, body) = form.as_quote_form().expect("Sexp::Quote must project");
20511 assert!(
20512 std::ptr::eq(body, inner_ptr),
20513 "as_quote_form inner pointer drifted from the boxed body — projection allocates or clones"
20514 );
20515
20516 let payload_qq = Sexp::symbol("payload-qq");
20517 let boxed_qq = Box::new(payload_qq);
20518 let inner_ptr_qq: *const Sexp = boxed_qq.as_ref();
20519 let form_qq = Sexp::Quasiquote(boxed_qq);
20520 let (_, body_qq) = form_qq
20521 .as_quote_form()
20522 .expect("Sexp::Quasiquote must project");
20523 assert!(
20524 std::ptr::eq(body_qq, inner_ptr_qq),
20525 "as_quote_form inner pointer drifted (quasiquote arm)"
20526 );
20527 }
20528
20529 #[test]
20530 fn expect_quote_form_projects_each_quote_family_variant_identically_to_as_quote_form() {
20531 // ASSERTED-TOTAL-FACE CONTRACT: `expect_quote_form` is the
20532 // asserted-total face of `as_quote_form` — for every quote-family
20533 // variant it MUST yield the same `(QuoteForm, &Sexp)` projection
20534 // that `as_quote_form` yields wrapped in `Some`. A regression
20535 // that drifts the two projections (e.g. a future variant
20536 // extension that updates `as_quote_form` but forgets to align
20537 // `expect_quote_form`'s body) surfaces here.
20538 let inner = Sexp::symbol("payload");
20539 for variant in [
20540 Sexp::Quote(Box::new(inner.clone())),
20541 Sexp::Quasiquote(Box::new(inner.clone())),
20542 Sexp::Unquote(Box::new(inner.clone())),
20543 Sexp::UnquoteSplice(Box::new(inner.clone())),
20544 ] {
20545 let via_total = variant.expect_quote_form();
20546 let via_soft = variant.as_quote_form().expect("variant is quote-family");
20547 assert_eq!(
20548 via_total.0, via_soft.0,
20549 "expect_quote_form's QuoteForm drifted from as_quote_form's at {variant}"
20550 );
20551 assert!(
20552 std::ptr::eq(via_total.1, via_soft.1),
20553 "expect_quote_form's inner pointer drifted from as_quote_form's at {variant}"
20554 );
20555 }
20556 }
20557
20558 #[test]
20559 fn expect_quote_form_panics_with_invariant_const_on_non_quote_family_variants() {
20560 // STATIC-INVARIANT CONTRACT: every non-quote-family variant
20561 // (Nil, every Atom subkind, List empty + populated) MUST trigger
20562 // the asserted-total panic with the named
20563 // `QUOTE_FAMILY_PROJECTION_INVARIANT` message. The const-vs-
20564 // panic-payload pin catches a future drift where the const is
20565 // edited without the projection picking it up (or vice versa).
20566 for variant in [
20567 Sexp::Nil,
20568 Sexp::symbol("x"),
20569 Sexp::keyword("k"),
20570 Sexp::string("s"),
20571 Sexp::int(7),
20572 Sexp::float(2.5),
20573 Sexp::boolean(true),
20574 Sexp::List(vec![]),
20575 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
20576 ] {
20577 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
20578 let _ = variant.expect_quote_form();
20579 }));
20580 let payload = result.expect_err("expect_quote_form must panic on non-quote-family");
20581 let msg = payload
20582 .downcast_ref::<String>()
20583 .map(String::as_str)
20584 .or_else(|| payload.downcast_ref::<&'static str>().copied())
20585 .expect("panic payload must be a string");
20586 assert!(
20587 msg.contains(QUOTE_FAMILY_PROJECTION_INVARIANT),
20588 "expect_quote_form panic message {msg:?} did not name \
20589 QUOTE_FAMILY_PROJECTION_INVARIANT at variant {variant:?}"
20590 );
20591 }
20592 }
20593
20594 #[test]
20595 fn quote_family_projection_invariant_const_matches_legacy_inline_literal() {
20596 // CONST-PIN: pre-lift the panic literal "matched quote-family
20597 // variant must project to Some via as_quote_form" appeared inline
20598 // at FIVE production sites (`Hash for Sexp`, `Display for Sexp`,
20599 // `domain::sexp_shape`, `domain::sexp_to_json`,
20600 // `interop::iac_forge_tag`). Pin the lifted const to the legacy
20601 // inline literal bit-for-bit so a regression that drifts the
20602 // const silently from the historical diagnostic string surfaces
20603 // here. Sibling shape to `quote_form_hash_discriminator_pins_
20604 // legacy_cache_key_bytes` for the discriminator-byte algebra.
20605 assert_eq!(
20606 QUOTE_FAMILY_PROJECTION_INVARIANT,
20607 "matched quote-family variant must project to Some via as_quote_form"
20608 );
20609 }
20610
20611 #[test]
20612 fn quote_form_prefix_pins_canonical_reader_tokens_for_every_variant() {
20613 // Pin every prefix string load-bearing for the Display→read
20614 // round-trip. A regression that drifts the prefix (e.g. swaps
20615 // `'` and `` ` `` between Quote and Quasiquote) silently
20616 // re-routes every renderer through the wrong variant; this
20617 // test fails loudly. Sibling-arm sweep so the (variant,
20618 // prefix) pair stays load-bearing under reordering refactors.
20619 assert_eq!(QuoteForm::Quote.prefix(), "'");
20620 assert_eq!(QuoteForm::Quasiquote.prefix(), "`");
20621 assert_eq!(QuoteForm::Unquote.prefix(), ",");
20622 assert_eq!(QuoteForm::UnquoteSplice.prefix(), ",@");
20623 }
20624
20625 // ── `QuoteForm::{QUOTE_PREFIX, QUASIQUOTE_PREFIX, UNQUOTE_PREFIX,
20626 // UNQUOTE_SPLICE_PREFIX, PREFIXES}` — per-role `&'static str`
20627 // reader-prefix algebra on the closed-set outer [`QuoteForm`]. Peer
20628 // of [`crate::error::MacroDefHead::KEYWORDS`] (head-keyword
20629 // algebra), [`Atom::BOOL_LITERALS`] (Scheme-bool spelling algebra),
20630 // and [`crate::macro_expand::MacroParams::LAMBDA_LIST_KEYWORDS`]
20631 // (CL lambda-list-keyword algebra) — every closed-set outer
20632 // projection on the substrate now pins its canonical bytes at ONE
20633 // `pub const` per role plus an ALL array for family-wide consumers.
20634
20635 #[test]
20636 fn quote_form_quote_prefix_projects_canonical_single_quote_bytes() {
20637 // Pin the exact `"'"` bytes at the typed constant. A regression
20638 // that renames the constant to a different byte fails HERE
20639 // rather than at silent reader-family drift where `'foo`
20640 // classifies as a bare atom (or through a different quote-family
20641 // variant) instead of `Sexp::Quote`.
20642 assert_eq!(QuoteForm::QUOTE_PREFIX, "'");
20643 }
20644
20645 #[test]
20646 fn quote_form_quasiquote_prefix_projects_canonical_backtick_bytes() {
20647 // Pin the exact `` "`" `` bytes at the typed constant. Sibling
20648 // posture to `quote_form_quote_prefix_projects_canonical_single_quote_bytes`.
20649 assert_eq!(QuoteForm::QUASIQUOTE_PREFIX, "`");
20650 }
20651
20652 #[test]
20653 fn quote_form_unquote_prefix_projects_canonical_comma_bytes() {
20654 // Pin the exact `","` bytes at the typed constant.
20655 assert_eq!(QuoteForm::UNQUOTE_PREFIX, ",");
20656 }
20657
20658 #[test]
20659 fn quote_form_unquote_splice_prefix_projects_canonical_comma_at_bytes() {
20660 // Pin the exact `",@"` bytes at the typed constant — the ONLY
20661 // two-char prefix on the closed set. A regression that lost the
20662 // `@` discriminator (dropping to just `","`, colliding with
20663 // `UNQUOTE_PREFIX`) surfaces HERE rather than as a silent
20664 // reader classifier collision.
20665 assert_eq!(QuoteForm::UNQUOTE_SPLICE_PREFIX, ",@");
20666 }
20667
20668 #[test]
20669 fn quote_form_prefix_routes_through_typed_per_role_constants() {
20670 // PATH-UNIFORMITY: `Self::prefix(self)` returns the per-role
20671 // `pub const` byte-for-byte per variant, catching a regression
20672 // that reverts ONE arm to an inline `"'"` / `` "`" `` / `","`
20673 // / `",@"` string literal (or drifts one arm's bytes silently).
20674 // Sibling posture to
20675 // `atom_bool_literal_routes_through_typed_per_variant_constants`
20676 // on the Scheme-bool spelling algebra.
20677 for (qf, expected) in [
20678 (QuoteForm::Quote, QuoteForm::QUOTE_PREFIX),
20679 (QuoteForm::Quasiquote, QuoteForm::QUASIQUOTE_PREFIX),
20680 (QuoteForm::Unquote, QuoteForm::UNQUOTE_PREFIX),
20681 (QuoteForm::UnquoteSplice, QuoteForm::UNQUOTE_SPLICE_PREFIX),
20682 ] {
20683 let actual = qf.prefix();
20684 assert_eq!(
20685 actual, expected,
20686 "QuoteForm::{qf:?}.prefix() `{actual}` drifted from \
20687 per-role constant `{expected}` — the arm must route \
20688 through the typed constant rather than an inline literal",
20689 );
20690 }
20691 }
20692
20693 #[test]
20694 fn quote_form_prefixes_has_expected_cardinality() {
20695 // Cardinality contract: `Self::PREFIXES.len() == 4` — pinned at
20696 // the declaration site by rustc's forced-arity check on
20697 // `[&'static str; 4]`. This test surfaces the arity as a
20698 // fail-loud runtime pin so a future refactor that switches the
20699 // array type to `&[&'static str]` (dropping the compile-time
20700 // arity forcing) doesn't silently loosen the closed-set
20701 // discipline the family relies on. Sibling posture to
20702 // `atom_bool_literals_has_expected_cardinality` and
20703 // `macro_def_head_keywords_has_expected_cardinality`.
20704 assert_eq!(
20705 QuoteForm::PREFIXES.len(),
20706 4,
20707 "QuoteForm::PREFIXES cardinality drifted from 4 — the \
20708 closed homoiconic-prefix domain admits exactly four \
20709 wrappers by construction; a fifth extension surfaces here"
20710 );
20711 }
20712
20713 #[test]
20714 fn quote_form_prefixes_align_with_all_by_index() {
20715 // ALIGNMENT CONTRACT: `Self::PREFIXES[i] == Self::ALL[i].prefix()`
20716 // element-wise. Pins that the typed variant ALL and the
20717 // `&'static str` PREFIXES ALL stay in lockstep under any
20718 // reorder — a regression that reorders ONE array without
20719 // reordering the other silently misaligns every `zip(ALL,
20720 // PREFIXES)` consumer (LSP completion providers, metric-label
20721 // emitters, coverage reporters). Sibling posture to
20722 // `macro_def_head_keywords_align_with_all_by_index`.
20723 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
20724 assert_eq!(
20725 QuoteForm::PREFIXES[i],
20726 qf.prefix(),
20727 "QuoteForm::PREFIXES[{i}] `{prefix}` drifted from \
20728 QuoteForm::ALL[{i}] ({qf:?}).prefix() `{via_variant}` \
20729 — the canonical declaration order of the ALL array \
20730 and the prefix projection must match element-wise",
20731 prefix = QuoteForm::PREFIXES[i],
20732 via_variant = qf.prefix(),
20733 );
20734 }
20735 }
20736
20737 #[test]
20738 fn quote_form_prefixes_pairwise_distinct() {
20739 // PAIRWISE DISJOINTNESS: every entry of the `PREFIXES` array
20740 // must differ so the reader-entry classifier (whether inline
20741 // as at [`crate::reader::tokenize`]'s outer arm or via a
20742 // hypothetical future `PREFIXES.iter()` sweep) cannot route
20743 // two homoiconic prefixes through the same arm. Family-wide
20744 // sweep over `PREFIXES × PREFIXES` — supersedes any per-pair
20745 // pin and picks up new prefixes mechanically. Sibling posture
20746 // to `atom_bool_literals_pairwise_distinct` on the Scheme-bool
20747 // algebra AND `macro_def_head_keywords_pairwise_distinct` on
20748 // the head-keyword algebra.
20749 for (i, a) in QuoteForm::PREFIXES.iter().enumerate() {
20750 for (j, b) in QuoteForm::PREFIXES.iter().enumerate() {
20751 if i == j {
20752 continue;
20753 }
20754 assert_ne!(
20755 a, b,
20756 "QuoteForm::PREFIXES[{i}] `{a}` collides with \
20757 QuoteForm::PREFIXES[{j}] `{b}` — the reader-entry \
20758 classifier's cascade would route two homoiconic \
20759 prefixes through the same arm"
20760 );
20761 }
20762 }
20763 }
20764
20765 #[test]
20766 fn quote_form_per_role_prefixes_route_through_matching_lead_char_for_every_variant() {
20767 // CROSS-AXIS ROUND-TRIP: every entry of `Self::PREFIXES` MUST
20768 // start with the corresponding variant's `lead_char()` — the
20769 // per-role `pub const` on the reader-prefix axis composes byte-
20770 // for-byte with the per-role `char` on the reader-lead-byte axis
20771 // ([`Self::QUOTE_LEAD`], [`Self::QUASIQUOTE_LEAD`],
20772 // [`Self::UNQUOTE_LEAD`]) via the [`Self::lead_char`] projection.
20773 // Both `Unquote` AND `UnquoteSplice` start with `UNQUOTE_LEAD`
20774 // (the shared `,` lead byte) — the two-char splice prefix's
20775 // second byte is [`Self::SPLICE_DISCRIMINATOR`], which the
20776 // reader's peek-then-consume arm disambiguates on. Sibling
20777 // posture to `atom_bool_literals_all_route_through_bool_literal_leading_byte`
20778 // on the Scheme-bool spelling algebra.
20779 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
20780 let prefix = QuoteForm::PREFIXES[i];
20781 let expected_lead = qf.lead_char();
20782 let actual_lead = prefix.chars().next().unwrap_or_else(|| {
20783 panic!("QuoteForm::PREFIXES[{i}] `{prefix}` for {qf:?} must have at least one char")
20784 });
20785 assert_eq!(
20786 actual_lead, expected_lead,
20787 "QuoteForm::PREFIXES[{i}] `{prefix}` for {qf:?} — first \
20788 char {actual_lead:?} drifted from lead_char {expected_lead:?} — \
20789 the per-role prefix constant drifted from the shared \
20790 lead byte"
20791 );
20792 }
20793 }
20794
20795 #[test]
20796 fn quote_form_unquote_splice_prefix_constant_composes_from_unquote_lead_and_splice_discriminator(
20797 ) {
20798 // STRUCTURAL COMPOSITION LAW at the `pub const` level:
20799 // [`Self::UNQUOTE_SPLICE_PREFIX`] decomposes cleanly into
20800 // [`Self::UNQUOTE_LEAD`] + [`Self::SPLICE_DISCRIMINATOR`]. The
20801 // ONLY two-char prefix on the closed set composes from the two
20802 // `char`-level constants on the algebra. Section-for-retraction
20803 // peer of the pre-existing
20804 // `quote_form_unquote_splice_prefix_composes_from_unquote_lead_and_splice_discriminator`
20805 // pin (which composes at the [`Self::prefix`] method level);
20806 // where that pin composes through the runtime projection, this
20807 // pin composes at the `pub const` level so a regression that
20808 // drifts the two-char constant WITHOUT drifting the runtime
20809 // projection (unlikely but structurally distinct) surfaces
20810 // here.
20811 let composed = format!(
20812 "{}{}",
20813 QuoteForm::UNQUOTE_LEAD,
20814 QuoteForm::SPLICE_DISCRIMINATOR,
20815 );
20816 assert_eq!(
20817 composed,
20818 QuoteForm::UNQUOTE_SPLICE_PREFIX,
20819 "QuoteForm::UNQUOTE_SPLICE_PREFIX drifted from UNQUOTE_LEAD + \
20820 SPLICE_DISCRIMINATOR — the reader's two-char splice \
20821 promotion identity is broken at the pub-const byte level",
20822 );
20823 }
20824
20825 #[test]
20826 fn quote_form_lead_char_pins_first_char_of_prefix_for_every_variant() {
20827 // Pin the (variant, lead char) pairing threaded through
20828 // [`crate::reader::tokenize`]'s outer quote-family dispatch AND
20829 // its bare-atom terminator disjunct. Quote/Quasiquote's
20830 // singleton chars project to `'\''` / `` '`' `` respectively;
20831 // Unquote AND UnquoteSplice BOTH project to `','` because the
20832 // splice's two-char `,@` prefix shares its lead byte with bare
20833 // unquote and the reader disambiguates on the peek-then-consume
20834 // `@` second char. A regression that split the shared-lead-char
20835 // collapse (e.g. gave UnquoteSplice a distinct lead char)
20836 // silently re-shapes every splice tokenization; this test
20837 // catches the drift at rustc + `cargo test` time rather than
20838 // as an off-by-one reader miscue that surfaces only when a
20839 // `,@xs` form parses wrong.
20840 assert_eq!(QuoteForm::Quote.lead_char(), '\'');
20841 assert_eq!(QuoteForm::Quasiquote.lead_char(), '`');
20842 assert_eq!(QuoteForm::Unquote.lead_char(), ',');
20843 assert_eq!(QuoteForm::UnquoteSplice.lead_char(), ',');
20844 }
20845
20846 #[test]
20847 fn quote_form_lead_char_is_first_char_of_prefix_for_every_variant() {
20848 // COMPOSITION CONTRACT: `lead_char` MUST equal the first char of
20849 // `prefix()` for every variant. Pin the composition so a
20850 // regression that drifts one of the two projections (e.g. a
20851 // rename of `Quote`'s prefix from `"'"` to `"‛"` without
20852 // updating `lead_char`, or vice versa) surfaces immediately.
20853 // The typed composition binds the (`QuoteForm`, lead char,
20854 // full prefix) triple at ONE consistency check across every
20855 // arm of the closed set — a future fifth homoiconic prefix
20856 // extension must extend `prefix` AND `lead_char` in lockstep,
20857 // and this sweep pins the invariant that connects them.
20858 for qf in QuoteForm::ALL {
20859 let prefix_first =
20860 qf.prefix().chars().next().unwrap_or_else(|| {
20861 panic!("QuoteForm::{qf:?} prefix must have at least one char")
20862 });
20863 assert_eq!(
20864 qf.lead_char(),
20865 prefix_first,
20866 "QuoteForm::{qf:?} — lead_char {:?} drifted from first char of prefix {:?}",
20867 qf.lead_char(),
20868 qf.prefix(),
20869 );
20870 }
20871 }
20872
20873 #[test]
20874 fn quote_form_from_lead_char_decodes_every_distinct_lead_char_to_default_variant() {
20875 // Pin the inverse projection at every distinct lead char across
20876 // the closed set. Quote/Quasiquote decode to their singleton
20877 // owners; `,` decodes to `Some(Unquote)` (the DEFAULT variant on
20878 // the shared `,` lead char) — the `,@` splice promotion lives
20879 // at the reader's peek arm, NOT at this decode. Every other
20880 // char yields `None`. Includes the tokenizer's non-quote entry
20881 // chars (`'('`, `')'`, `';'`, `Atom::STR_DELIMITER`, ` `) as
20882 // the rejection sweep — a regression that leaks a quote-family
20883 // variant onto a non-quote lead char silently re-shapes every
20884 // top-level program's tokenization; this rejection sweep
20885 // catches the drift at test time.
20886 assert_eq!(QuoteForm::from_lead_char('\''), Some(QuoteForm::Quote));
20887 assert_eq!(QuoteForm::from_lead_char('`'), Some(QuoteForm::Quasiquote));
20888 assert_eq!(QuoteForm::from_lead_char(','), Some(QuoteForm::Unquote));
20889
20890 // Non-quote reader entry chars must reject.
20891 assert_eq!(QuoteForm::from_lead_char('('), None);
20892 assert_eq!(QuoteForm::from_lead_char(')'), None);
20893 assert_eq!(QuoteForm::from_lead_char(';'), None);
20894 assert_eq!(QuoteForm::from_lead_char(Atom::STR_DELIMITER), None);
20895 assert_eq!(QuoteForm::from_lead_char(' '), None);
20896 assert_eq!(QuoteForm::from_lead_char('a'), None);
20897 assert_eq!(QuoteForm::from_lead_char('@'), None);
20898 assert_eq!(QuoteForm::from_lead_char(':'), None);
20899 assert_eq!(QuoteForm::from_lead_char('#'), None);
20900 }
20901
20902 #[test]
20903 fn quote_form_lead_char_round_trips_through_from_lead_char_with_shared_lead_char_collapse() {
20904 // ROUND-TRIP CONTRACT: for every variant, decoding its
20905 // `lead_char()` back through `from_lead_char` produces
20906 // `Some(default_variant_on_that_lead_char)`. For the three
20907 // variants with singleton lead chars (`Quote`, `Quasiquote`,
20908 // `Unquote`) the round-trip is the identity. For `UnquoteSplice`
20909 // — which shares `,` with `Unquote` — the round-trip yields
20910 // `Some(QuoteForm::Unquote)` because `,` alone cannot signal
20911 // splice; the reader's peek-then-consume `@` disambiguator is
20912 // where the splice promotion happens. Pin this asymmetry so
20913 // a regression that pushed the splice promotion into
20914 // `from_lead_char` (a natural but wrong refactor) surfaces
20915 // here at test time — decoupling the char-level decode from
20916 // the streaming reader's two-char sequence is load-bearing
20917 // for the tokenizer's structure.
20918 for qf in QuoteForm::ALL {
20919 let decoded = QuoteForm::from_lead_char(qf.lead_char());
20920 let expected = match qf {
20921 QuoteForm::Quote => Some(QuoteForm::Quote),
20922 QuoteForm::Quasiquote => Some(QuoteForm::Quasiquote),
20923 // Both `,`-lead-char variants collapse onto Unquote;
20924 // splice promotion lives at the reader's peek arm.
20925 QuoteForm::Unquote | QuoteForm::UnquoteSplice => Some(QuoteForm::Unquote),
20926 };
20927 assert_eq!(
20928 decoded, expected,
20929 "QuoteForm::{qf:?} — from_lead_char(lead_char) round-trip drifted",
20930 );
20931 }
20932 }
20933
20934 #[test]
20935 fn quote_form_from_lead_char_is_const_fn_over_the_closed_set() {
20936 // Pin the `const fn` posture of both projections by binding a
20937 // `const` array literal keyed on the closed set. A regression
20938 // that removed the `const` qualifier (dropping the compile-
20939 // time evaluability the reader's outer dispatch AND future
20940 // static lookup tables key on) fails to compile HERE — the
20941 // `const` context enforces the qualifier without a test-time
20942 // assertion.
20943 const _QUOTE: char = QuoteForm::Quote.lead_char();
20944 const _QUASIQUOTE: char = QuoteForm::Quasiquote.lead_char();
20945 const _UNQUOTE: char = QuoteForm::Unquote.lead_char();
20946 const _SPLICE: char = QuoteForm::UnquoteSplice.lead_char();
20947 const _FROM: Option<QuoteForm> = QuoteForm::from_lead_char(',');
20948 assert_eq!(_QUOTE, '\'');
20949 assert_eq!(_QUASIQUOTE, '`');
20950 assert_eq!(_UNQUOTE, ',');
20951 assert_eq!(_SPLICE, ',');
20952 assert_eq!(_FROM, Some(QuoteForm::Unquote));
20953 }
20954
20955 #[test]
20956 fn quote_form_lead_constants_project_canonical_chars() {
20957 // Pin each constant's byte identity so a typo (`'‛'` for
20958 // `QUOTE_LEAD`, `'’'` for `QUASIQUOTE_LEAD`, `';'` for
20959 // `UNQUOTE_LEAD`) or accidental redefinition surfaces
20960 // immediately. Every canonical per-role reader-punctuation byte
20961 // on the substrate has its own byte-identity pin at its owning
20962 // algebra
20963 // (`atom_str_delimiter_projects_canonical_quote_char`,
20964 // `atom_str_escape_lead_projects_canonical_backslash_char`,
20965 // `atom_keyword_marker_lead_projects_canonical_colon_char`,
20966 // `atom_bool_literal_lead_projects_canonical_hash_char`,
20967 // `sexp_list_open_projects_canonical_char`,
20968 // `sexp_list_close_projects_canonical_char`,
20969 // `sexp_comment_lead_projects_canonical_char`,
20970 // `sexp_comment_term_projects_canonical_char`,
20971 // `quote_form_splice_discriminator_projects_canonical_at_char`);
20972 // this pin closes the three quote-family lead-byte constants at
20973 // the SAME shape.
20974 assert_eq!(QuoteForm::QUOTE_LEAD, '\'');
20975 assert_eq!(QuoteForm::QUASIQUOTE_LEAD, '`');
20976 assert_eq!(QuoteForm::UNQUOTE_LEAD, ',');
20977 }
20978
20979 #[test]
20980 fn quote_form_lead_constants_round_trip_through_lead_char_projections() {
20981 // ROUND-TRIP CONTRACT: for each of the three distinct-lead-byte
20982 // variants (`Quote`, `Quasiquote`, `Unquote`) the (variant →
20983 // constant → variant) triangle closes exactly.
20984 // `Self::from_lead_char(Self::X_LEAD) == Some(Self::X)` AND
20985 // `Self::X.lead_char() == Self::X_LEAD` — the constants ARE the
20986 // canonical per-variant lead byte both projections route
20987 // through. `UnquoteSplice` shares its lead byte with `Unquote`
20988 // (see the merged arm in `lead_char`), so `UnquoteSplice.lead_char()
20989 // == Self::UNQUOTE_LEAD` too — the splice's SECOND-char
20990 // `SPLICE_DISCRIMINATOR` promotion lives at the reader's peek
20991 // arm, not at this decode. Pin the triangle at every variant so
20992 // a regression that drifts EITHER a constant OR one of the two
20993 // projection sites surfaces at test time.
20994 assert_eq!(
20995 QuoteForm::from_lead_char(QuoteForm::QUOTE_LEAD),
20996 Some(QuoteForm::Quote),
20997 );
20998 assert_eq!(QuoteForm::Quote.lead_char(), QuoteForm::QUOTE_LEAD);
20999
21000 assert_eq!(
21001 QuoteForm::from_lead_char(QuoteForm::QUASIQUOTE_LEAD),
21002 Some(QuoteForm::Quasiquote),
21003 );
21004 assert_eq!(
21005 QuoteForm::Quasiquote.lead_char(),
21006 QuoteForm::QUASIQUOTE_LEAD,
21007 );
21008
21009 assert_eq!(
21010 QuoteForm::from_lead_char(QuoteForm::UNQUOTE_LEAD),
21011 Some(QuoteForm::Unquote),
21012 );
21013 assert_eq!(QuoteForm::Unquote.lead_char(), QuoteForm::UNQUOTE_LEAD);
21014 // UnquoteSplice's `lead_char` collapses onto the shared
21015 // `UNQUOTE_LEAD` byte because the splice's `,@` prefix opens
21016 // with `,`; the promotion is a two-char peek in the reader.
21017 assert_eq!(
21018 QuoteForm::UnquoteSplice.lead_char(),
21019 QuoteForm::UNQUOTE_LEAD,
21020 );
21021 }
21022
21023 #[test]
21024 fn quote_form_lead_constants_distinct_from_every_other_algebra_marker_char() {
21025 // CROSS-AXIS DISJOINTNESS CONTRACT: each of the three quote-
21026 // family lead bytes MUST differ from every other canonical
21027 // reader-punctuation constant on the substrate — the other two
21028 // quote-family lead bytes, `SPLICE_DISCRIMINATOR`,
21029 // `Atom::STR_DELIMITER`, `Atom::STR_ESCAPE_LEAD`,
21030 // `Atom::KEYWORD_MARKER_LEAD`, `Atom::BOOL_LITERAL_LEAD`,
21031 // `Sexp::LIST_OPEN`, `Sexp::LIST_CLOSE`, `Sexp::COMMENT_LEAD`,
21032 // and `Sexp::COMMENT_TERM`. Otherwise the reader's outer
21033 // dispatch would ambiguously route a `'` / `` ` `` / `,` lead
21034 // byte through the aliased sibling arm — e.g. if `QUOTE_LEAD`
21035 // aliased `Sexp::LIST_OPEN`, a source `'foo` would ambiguously
21036 // trigger the list-open arm before the quote-family arm ran.
21037 // Sibling-shape peer of
21038 // `quote_form_splice_discriminator_distinct_from_every_algebra_marker_char`
21039 // one axis over.
21040 // The three distinct-lead-byte rows bind through the typed
21041 // [`QuoteForm::LEADS`] ALL array — the sub-vocabulary sweep
21042 // now iterates ONE forced-arity `[char; 3]` array rather than
21043 // three inline algebra-constant enumerations.
21044 let leads = QuoteForm::LEADS;
21045 // Within-family: the three lead bytes are pairwise distinct.
21046 for (i, a) in leads.iter().enumerate() {
21047 for b in &leads[i + 1..] {
21048 assert_ne!(a, b, "quote-family lead bytes must be pairwise distinct",);
21049 }
21050 }
21051 // Cross-family disjointness against every other single-char
21052 // algebra marker on the substrate.
21053 for lead in leads {
21054 assert_ne!(lead, QuoteForm::SPLICE_DISCRIMINATOR);
21055 assert_ne!(lead, Atom::STR_DELIMITER);
21056 assert_ne!(lead, Atom::STR_ESCAPE_LEAD);
21057 assert_ne!(lead, Atom::KEYWORD_MARKER_LEAD);
21058 assert_ne!(lead, Atom::BOOL_LITERAL_LEAD);
21059 assert_ne!(lead, Sexp::LIST_OPEN);
21060 assert_ne!(lead, Sexp::LIST_CLOSE);
21061 assert_ne!(lead, Sexp::COMMENT_LEAD);
21062 assert_ne!(lead, Sexp::COMMENT_TERM);
21063 }
21064 }
21065
21066 #[test]
21067 fn quote_form_unquote_splice_prefix_composes_from_unquote_lead_and_splice_discriminator() {
21068 // STRUCTURAL COMPOSITION LAW: [`QuoteForm::UnquoteSplice`]'s
21069 // two-char prefix `",@"` decomposes cleanly into
21070 // [`QuoteForm::UNQUOTE_LEAD`] (the `,` byte shared with
21071 // [`QuoteForm::Unquote`]) + [`QuoteForm::SPLICE_DISCRIMINATOR`]
21072 // (the `@` byte promoted by the reader's peek arm). The two
21073 // BYTE-LEVEL constants on the closed-set [`QuoteForm`] algebra
21074 // compose the ONLY two-char [`Self::prefix`] in the closed set
21075 // — a stronger form of the pre-existing
21076 // `quote_form_unquote_splice_prefix_composes_from_unquote_prefix_and_splice_discriminator`
21077 // pin (which composes through the `&'static str` prefix of the
21078 // Unquote variant). Where that pin binds the `&'static str`-level
21079 // composition, this pin binds the `char`-level composition; both
21080 // pins fail loudly on any drift of the `,` or `@` bytes. The
21081 // reader's two-char peek-then-consume splice-promotion arm
21082 // reads `Self::UNQUOTE_LEAD` then peeks `Self::SPLICE_DISCRIMINATOR`
21083 // to promote to `UnquoteSplice` — this identity closes the
21084 // read↔write duality at the byte level.
21085 let composed = format!(
21086 "{}{}",
21087 QuoteForm::UNQUOTE_LEAD,
21088 QuoteForm::SPLICE_DISCRIMINATOR,
21089 );
21090 assert_eq!(
21091 composed,
21092 QuoteForm::UnquoteSplice.prefix(),
21093 "UnquoteSplice.prefix() drifted from UNQUOTE_LEAD + \
21094 SPLICE_DISCRIMINATOR — the reader's two-char splice \
21095 promotion identity is broken at the byte level",
21096 );
21097 }
21098
21099 #[test]
21100 fn quote_form_lead_constants_are_const_evaluable_over_the_closed_set() {
21101 // Pin the `const` posture of the three lead constants by binding
21102 // `const` bindings that ROUTE through the `const fn` lead_char /
21103 // from_lead_char projections — the compile-time evaluation
21104 // context enforces the qualifier without a test-time assertion.
21105 // Sibling posture to
21106 // `quote_form_from_lead_char_is_const_fn_over_the_closed_set`.
21107 const _QUOTE: char = QuoteForm::QUOTE_LEAD;
21108 const _QUASIQUOTE: char = QuoteForm::QUASIQUOTE_LEAD;
21109 const _UNQUOTE: char = QuoteForm::UNQUOTE_LEAD;
21110 const _FROM_QUOTE: Option<QuoteForm> = QuoteForm::from_lead_char(QuoteForm::QUOTE_LEAD);
21111 const _FROM_QUASIQUOTE: Option<QuoteForm> =
21112 QuoteForm::from_lead_char(QuoteForm::QUASIQUOTE_LEAD);
21113 const _FROM_UNQUOTE: Option<QuoteForm> = QuoteForm::from_lead_char(QuoteForm::UNQUOTE_LEAD);
21114 assert_eq!(_QUOTE, '\'');
21115 assert_eq!(_QUASIQUOTE, '`');
21116 assert_eq!(_UNQUOTE, ',');
21117 assert_eq!(_FROM_QUOTE, Some(QuoteForm::Quote));
21118 assert_eq!(_FROM_QUASIQUOTE, Some(QuoteForm::Quasiquote));
21119 assert_eq!(_FROM_UNQUOTE, Some(QuoteForm::Unquote));
21120 }
21121
21122 #[test]
21123 fn quote_form_leads_composes_from_algebra_constants_in_declaration_order() {
21124 // FAMILY COMPOSITION LAW: pin that the ALL array's rows are the
21125 // three distinct-lead-byte algebra constants (`Self::QUOTE_LEAD`,
21126 // `Self::QUASIQUOTE_LEAD`, `Self::UNQUOTE_LEAD`) in canonical
21127 // declaration order matching [`QuoteForm::ALL`]'s three-of-four
21128 // distinct-lead-byte projection through [`QuoteForm::lead_char`].
21129 // A reorder of ONE row without reordering the underlying algebra
21130 // constants silently misaligns every index-sweep consumer (a
21131 // hypothetical reader outer-dispatch pre-check that keys on
21132 // `QuoteForm::LEADS[0]`, an LSP completion generator that
21133 // materialises the distinct-lead-byte set in this order). Sibling-
21134 // shape pin to
21135 // `sexp_list_delimiters_composes_from_algebra_constants_in_declaration_order`
21136 // on the peer `[char; 2]` sub-vocabulary at
21137 // [`Sexp::LIST_DELIMITERS`].
21138 assert_eq!(
21139 QuoteForm::LEADS,
21140 [
21141 QuoteForm::QUOTE_LEAD,
21142 QuoteForm::QUASIQUOTE_LEAD,
21143 QuoteForm::UNQUOTE_LEAD,
21144 ],
21145 "QuoteForm::LEADS composition drifted from the canonical \
21146 (QUOTE_LEAD, QUASIQUOTE_LEAD, UNQUOTE_LEAD) triple — the \
21147 distinct-lead-byte sub-vocabulary lift must route through \
21148 the three typed algebra constants in that order.",
21149 );
21150 }
21151
21152 #[test]
21153 fn quote_form_leads_has_expected_cardinality() {
21154 // CARDINALITY PIN: `[char; 3]` at rustc — this assert pins the
21155 // runtime observable so a refactor that loosens the array's type
21156 // to `&[char]` (dropping the compile-time arity forcing) fails
21157 // HERE at the runtime cardinality assertion rather than silently
21158 // allowing a fourth or absent row. The 3 vs [`QuoteForm::PREFIXES`]'s
21159 // 4 shape asymmetry IS the structural axis distinguishing the
21160 // DISTINCT-lead-byte sub-vocabulary from the PER-VARIANT-prefix
21161 // sub-vocabulary — three-of-four distinct-lead-byte collapse is
21162 // definitional (only [`QuoteForm::UnquoteSplice`]'s two-char
21163 // `,@` prefix shares its lead byte with a sibling variant). A
21164 // regression that grew LEADS to 4 without the shared-lead-byte
21165 // collapse breaking (i.e. by adding a fourth distinct-lead-byte
21166 // variant WITHOUT drifting [`QuoteForm::PREFIXES`]'s cardinality
21167 // to 5 in lockstep) fails at this cardinality pin. Sibling-shape
21168 // pin to `sexp_list_delimiters_has_expected_cardinality`.
21169 assert_eq!(
21170 QuoteForm::LEADS.len(),
21171 3,
21172 "QuoteForm::LEADS cardinality drifted from 3 — the distinct-\
21173 lead-byte sub-vocabulary MUST be exactly three rows because \
21174 UnquoteSplice shares its lead byte with Unquote by the \
21175 splice's two-char `,@` prefix construction.",
21176 );
21177 }
21178
21179 #[test]
21180 fn quote_form_leads_pairwise_distinct() {
21181 // PAIRWISE DISJOINTNESS: the three distinct-lead-byte rows MUST
21182 // NOT alias — the closed-set outer [`QuoteForm`] algebra's
21183 // three-of-four collapse identity depends on Quote / Quasiquote /
21184 // Unquote owning distinct lead bytes (only UnquoteSplice shares
21185 // Unquote's lead byte, via the splice's two-char `,@` prefix).
21186 // A regression that collapsed Quote and Quasiquote onto the same
21187 // lead byte would silently break the reader's outer dispatch
21188 // (the tokenizer would ambiguously route both `'foo` and
21189 // `` `foo `` through the same arm). Sibling-shape pin to
21190 // `sexp_list_delimiters_pairwise_distinct` on the peer
21191 // `[char; 2]` sub-vocabulary at [`Sexp::LIST_DELIMITERS`].
21192 for (i, a) in QuoteForm::LEADS.iter().enumerate() {
21193 for (j, b) in QuoteForm::LEADS.iter().enumerate() {
21194 if i == j {
21195 continue;
21196 }
21197 assert_ne!(
21198 a, b,
21199 "QuoteForm::LEADS rows [{i}] and [{j}] share a byte \
21200 ({a:?} == {b:?}) — the distinct-lead-byte contract \
21201 across Quote / Quasiquote / Unquote would collapse.",
21202 );
21203 }
21204 }
21205 }
21206
21207 #[test]
21208 fn quote_form_leads_disjoint_from_splice_discriminator() {
21209 // CROSS-AXIS DISJOINTNESS (splice discriminator): no row of
21210 // [`QuoteForm::LEADS`] may alias
21211 // [`QuoteForm::SPLICE_DISCRIMINATOR`] — otherwise the reader's
21212 // two-char peek-then-consume splice-promotion arm inside
21213 // [`crate::reader::tokenize`] would ambiguously promote on a
21214 // sibling lead byte (e.g. if `SPLICE_DISCRIMINATOR` aliased
21215 // [`QuoteForm::UNQUOTE_LEAD`], a source `,,foo` would silently
21216 // promote to `UnquoteSplice(,foo)` rather than parsing as
21217 // `Unquote(Unquote(foo))`). Sibling-shape pin to
21218 // `sexp_list_delimiters_disjoint_from_str_delimiter`: both close
21219 // the cross-sub-vocabulary disjointness contract at the ALL-array
21220 // level rather than as an inline disjunction per consumer.
21221 for (i, ch) in QuoteForm::LEADS.iter().enumerate() {
21222 assert_ne!(
21223 *ch,
21224 QuoteForm::SPLICE_DISCRIMINATOR,
21225 "QuoteForm::LEADS[{i}] ({ch:?}) aliases \
21226 QuoteForm::SPLICE_DISCRIMINATOR ({:?}) — the reader's \
21227 distinct-lead-byte arm would collide with the two-char \
21228 splice promotion arm at the same byte.",
21229 QuoteForm::SPLICE_DISCRIMINATOR,
21230 );
21231 }
21232 }
21233
21234 #[test]
21235 fn quote_form_lead_char_routes_through_leads_for_every_variant() {
21236 // PATH-UNIFORMITY PIN: every [`QuoteForm::lead_char`] projection
21237 // over the closed set MUST land on a row of [`QuoteForm::LEADS`]
21238 // — the shared-lead-byte collapse identity (Quote / Quasiquote /
21239 // Unquote / UnquoteSplice → three distinct lead bytes) binds
21240 // through the ALL array's `.contains` sweep. A regression that
21241 // reverted `lead_char`'s arms to inline `char` literals AND
21242 // drifted one of the four arms without drifting a paired algebra
21243 // constant fails HERE at the first mismatched variant rather than
21244 // at a distant reader outer-dispatch drift where a quote-family
21245 // form silently routes through a bare-atom arm. Sibling-shape
21246 // pin to
21247 // `sexp_is_bare_atom_boundary_routes_through_list_delimiters_for_every_row`
21248 // on the peer `[char; 2]` sub-vocabulary at
21249 // [`Sexp::LIST_DELIMITERS`], lifted to the four-variant closed
21250 // set: every one of the four variants collapses onto one of the
21251 // three LEADS rows.
21252 for qf in QuoteForm::ALL {
21253 assert!(
21254 QuoteForm::LEADS.contains(&qf.lead_char()),
21255 "QuoteForm::{qf:?}.lead_char() = {:?} is NOT a row of \
21256 QuoteForm::LEADS — the shared-lead-byte collapse drifted \
21257 from the ALL array's distinct-lead-byte sub-vocabulary.",
21258 qf.lead_char(),
21259 );
21260 }
21261 }
21262
21263 #[test]
21264 fn quote_form_from_lead_char_decodes_every_row_of_leads_to_some() {
21265 // INVERSE PATH-UNIFORMITY PIN: every row of [`QuoteForm::LEADS`]
21266 // MUST decode through [`QuoteForm::from_lead_char`] to
21267 // `Some(_variant_)` (the DEFAULT variant on that lead byte —
21268 // Unquote on `,`, Quote on `'`, Quasiquote on `` ` ``). A
21269 // regression that dropped one of the three arms in
21270 // `from_lead_char`'s match without shrinking [`QuoteForm::LEADS`]
21271 // in lockstep fails HERE at the first mismatched row rather than
21272 // as a silent reader outer-dispatch drift where a quote-family
21273 // lead byte silently falls through to the None arm. This pin
21274 // closes the round-trip from the DISTINCT-lead-byte axis back
21275 // through the decode projection at the ALL-array level. Sibling-
21276 // shape pin to
21277 // `atom_decode_str_escape_routes_through_self_escape_table_for_every_row`
21278 // on the peer sub-vocabulary at [`Atom::SELF_ESCAPE_TABLE`].
21279 for (i, ch) in QuoteForm::LEADS.iter().enumerate() {
21280 assert!(
21281 QuoteForm::from_lead_char(*ch).is_some(),
21282 "QuoteForm::LEADS[{i}] ({ch:?}) decodes to None through \
21283 QuoteForm::from_lead_char — the distinct-lead-byte \
21284 sub-vocabulary drifted from the decode projection's \
21285 non-None arm-set.",
21286 );
21287 }
21288 }
21289
21290 #[test]
21291 fn quote_form_leads_matches_dedup_of_prefixes_first_char_set() {
21292 // CROSS-ARRAY IDENTITY: [`QuoteForm::LEADS`] IS the deduplicated
21293 // first-char set of [`QuoteForm::PREFIXES`] — the four per-
21294 // variant prefixes' first chars (`'\''`, `` '`' ``, `','`, `','`)
21295 // deduplicate onto the three DISTINCT lead bytes exactly matching
21296 // [`QuoteForm::LEADS`]'s rows. This pin binds the SHAPE-ASYMMETRIC
21297 // (3 vs 4) relationship between the two ALL arrays: [`QuoteForm::PREFIXES`]
21298 // enumerates the per-variant prefix-bytes axis; [`QuoteForm::LEADS`]
21299 // enumerates the DISTINCT lead-byte axis; the two ALL arrays
21300 // compose through the (first-char, dedup) projection. A regression
21301 // that drifted EITHER array without drifting the other (e.g. an
21302 // ELisp-compat port of Quote's prefix to `"#'"` without updating
21303 // [`QuoteForm::QUOTE_LEAD`]) surfaces here rather than as a
21304 // silent reader outer-dispatch drift. Sibling-shape pin to
21305 // `quote_form_per_role_prefixes_route_through_matching_lead_char_for_every_variant`
21306 // one axis over — that pin binds per-variant; this pin binds
21307 // per-distinct-lead-byte via the dedup composition. Peer at the
21308 // ALL-array level to the pre-existing composition tests between
21309 // NAMED_ESCAPE_TABLE + SELF_ESCAPE_TABLE (which SPAN a total
21310 // arm-set cardinality; this pin composes a DEDUP arm-set
21311 // cardinality).
21312 let mut deduped_first_chars: Vec<char> = QuoteForm::PREFIXES
21313 .iter()
21314 .map(|p| {
21315 p.chars().next().unwrap_or_else(|| {
21316 panic!("QuoteForm::PREFIXES entry `{p}` must have at least one char")
21317 })
21318 })
21319 .collect();
21320 deduped_first_chars.sort_unstable();
21321 deduped_first_chars.dedup();
21322
21323 let mut leads_sorted: Vec<char> = QuoteForm::LEADS.to_vec();
21324 leads_sorted.sort_unstable();
21325
21326 assert_eq!(
21327 deduped_first_chars, leads_sorted,
21328 "QuoteForm::LEADS drifted from the deduplicated first-char \
21329 set of QuoteForm::PREFIXES — the (per-variant prefix axis) \
21330 × (distinct-lead-byte axis) shape-asymmetric composition \
21331 identity is broken.",
21332 );
21333 }
21334
21335 #[test]
21336 fn quote_form_splice_discriminator_projects_canonical_at_char() {
21337 // Pin the constant's byte identity so a typo (`'!'`, `'?'`,
21338 // `'~'`) or accidental redefinition surfaces immediately. The
21339 // reader's peek arm inside [`crate::reader::tokenize`] AND the
21340 // splice-promotion table [`QuoteForm::promote_via_next_char`]
21341 // BOTH bind to this constant; a drift here would silently re-
21342 // shape every `,@xs` tokenization into a `,` + `@xs` two-token
21343 // sequence (or into a phantom promotion on a different
21344 // second-char), and this pin catches the drift at test time.
21345 assert_eq!(QuoteForm::SPLICE_DISCRIMINATOR, '@');
21346 }
21347
21348 #[test]
21349 fn quote_form_splice_discriminator_distinct_from_every_algebra_marker_char() {
21350 // CROSS-AXIS DISJOINTNESS CONTRACT: the splice discriminator
21351 // `@` must NOT alias any other canonical reader-punctuation
21352 // constant on the substrate — every [`QuoteForm::lead_char`]
21353 // projection, [`crate::ast::Atom::STR_DELIMITER`],
21354 // [`crate::ast::Atom::KEYWORD_MARKER`]'s lead byte,
21355 // [`crate::ast::Sexp::LIST_OPEN`], [`crate::ast::Sexp::LIST_CLOSE`],
21356 // [`crate::ast::Sexp::COMMENT_LEAD`], and both
21357 // [`crate::ast::Atom::bool_literal`] spellings' lead bytes.
21358 // Otherwise the two-char `,@` splice-promotion arm inside the
21359 // reader would ambiguously route through the splice arm AND a
21360 // sibling algebra's arm at the outer dispatch — e.g. if
21361 // `SPLICE_DISCRIMINATOR` aliased [`Sexp::LIST_OPEN`], a source
21362 // `,(a b)` would ambiguously promote-and-consume the `(` before
21363 // the list-opening arm ran. Pin the disjointness across every
21364 // sibling constant so a future rename catches the collision at
21365 // test time rather than as a silent tokenizer drift.
21366 assert_ne!(
21367 QuoteForm::SPLICE_DISCRIMINATOR,
21368 QuoteForm::Quote.lead_char()
21369 );
21370 assert_ne!(
21371 QuoteForm::SPLICE_DISCRIMINATOR,
21372 QuoteForm::Quasiquote.lead_char()
21373 );
21374 assert_ne!(
21375 QuoteForm::SPLICE_DISCRIMINATOR,
21376 QuoteForm::Unquote.lead_char()
21377 );
21378 assert_ne!(
21379 QuoteForm::SPLICE_DISCRIMINATOR,
21380 QuoteForm::UnquoteSplice.lead_char()
21381 );
21382 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Atom::STR_DELIMITER);
21383 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Sexp::LIST_OPEN);
21384 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Sexp::LIST_CLOSE);
21385 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Sexp::COMMENT_LEAD);
21386 // The KEYWORD_MARKER prefix's canonical LEAD `char` lives at
21387 // the typed `Atom::KEYWORD_MARKER_LEAD` constant on the closed-
21388 // set outer [`Atom`] algebra. Pre-lift this slot held an inline
21389 // `Atom::KEYWORD_MARKER.chars().next().expect(_)` extraction;
21390 // post-lift the byte lives at ONE named constant that the
21391 // [`Atom::KEYWORD_MARKER`] `&'static str` projects to (pinned
21392 // by `atom_keyword_marker_lead_prefixes_keyword_marker`).
21393 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Atom::KEYWORD_MARKER_LEAD);
21394 // Both `#t` / `#f` spellings share `Atom::BOOL_LITERAL_LEAD`
21395 // (`'#'`) as the lead byte. Pre-lift each spelling was
21396 // extracted via `Atom::bool_literal(b).chars().next().expect(_)`
21397 // — TWO assertions for the SAME byte by construction. Post-
21398 // lift both collapse to ONE assertion routing through the
21399 // typed constant; the structural invariant "both spellings
21400 // share the lead byte" lives at
21401 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`
21402 // rather than as a per-spelling boilerplate duplication here.
21403 assert_ne!(QuoteForm::SPLICE_DISCRIMINATOR, Atom::BOOL_LITERAL_LEAD);
21404 }
21405
21406 #[test]
21407 fn quote_form_promote_via_next_char_only_promotes_unquote_on_splice_discriminator() {
21408 // Pin the closed-set promotion table: EXACTLY the singleton
21409 // `(Unquote, SPLICE_DISCRIMINATOR) → Some(UnquoteSplice)` arm
21410 // triggers; every other `(variant, char)` pairing yields
21411 // `None`. Sweeps every `QuoteForm::ALL` variant against the
21412 // splice discriminator AND against a broad rejection set
21413 // (whitespace, `(`, `)`, `;`, `Atom::STR_DELIMITER`, `a`, `,`,
21414 // `'`, `` ` ``) so a regression that widens the promotion
21415 // table (e.g. promotes `Quote` on `@` to a phantom variant,
21416 // or promotes `Unquote` on `'` after a copy-paste drift)
21417 // surfaces at test time. Sibling to
21418 // `quote_form_from_lead_char_decodes_every_distinct_lead_char_to_default_variant`
21419 // one axis over on the closed-set entry-char algebra — this
21420 // sweep pins the two-char extension of that one-char decode.
21421 for qf in QuoteForm::ALL {
21422 let promoted = qf.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21423 let expected = if matches!(qf, QuoteForm::Unquote) {
21424 Some(QuoteForm::UnquoteSplice)
21425 } else {
21426 None
21427 };
21428 assert_eq!(
21429 promoted, expected,
21430 "QuoteForm::{qf:?} — promotion table drifted on SPLICE_DISCRIMINATOR",
21431 );
21432 // Every non-discriminator char must reject for every variant.
21433 for rejection_char in [
21434 ' ',
21435 '\n',
21436 '\t',
21437 Sexp::LIST_OPEN,
21438 Sexp::LIST_CLOSE,
21439 Sexp::COMMENT_LEAD,
21440 Atom::STR_DELIMITER,
21441 'a',
21442 ',',
21443 '\'',
21444 '`',
21445 '#',
21446 ':',
21447 '!',
21448 '?',
21449 '~',
21450 ] {
21451 assert_eq!(
21452 qf.promote_via_next_char(rejection_char),
21453 None,
21454 "QuoteForm::{qf:?} — promotion table leaked on non-\
21455 discriminator char {rejection_char:?}",
21456 );
21457 }
21458 }
21459 }
21460
21461 #[test]
21462 fn quote_form_promote_via_next_char_is_const_fn_over_the_closed_set() {
21463 // Pin the `const fn` posture by binding a `const` array
21464 // literal of promotions keyed on the closed set. A regression
21465 // that removed the `const` qualifier (dropping the compile-
21466 // time evaluability the reader's peek arm AND future static
21467 // lookup tables key on) fails to compile HERE — the `const`
21468 // context enforces the qualifier without a test-time
21469 // assertion. Sibling posture to
21470 // `quote_form_from_lead_char_is_const_fn_over_the_closed_set`.
21471 const _QUOTE: Option<QuoteForm> =
21472 QuoteForm::Quote.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21473 const _QUASIQUOTE: Option<QuoteForm> =
21474 QuoteForm::Quasiquote.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21475 const _UNQUOTE: Option<QuoteForm> =
21476 QuoteForm::Unquote.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21477 const _SPLICE: Option<QuoteForm> =
21478 QuoteForm::UnquoteSplice.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21479 assert_eq!(_QUOTE, None);
21480 assert_eq!(_QUASIQUOTE, None);
21481 assert_eq!(_UNQUOTE, Some(QuoteForm::UnquoteSplice));
21482 assert_eq!(_SPLICE, None);
21483 }
21484
21485 #[test]
21486 fn quote_form_promote_via_next_char_composes_prefix_from_source_prefix_and_next_char() {
21487 // COMPOSITION IDENTITY: for every `qf: QuoteForm` and every
21488 // `c: char`, if `qf.promote_via_next_char(c) == Some(promoted)`
21489 // then `format!("{}{}", qf.prefix(), c) == promoted.prefix()`.
21490 // Pin the (variant, next char) → promoted-variant projection
21491 // agrees with the reader's rendered `Self::prefix` composition,
21492 // so a regression that drifts one side of the identity
21493 // surfaces immediately. Sibling to
21494 // `quote_form_lead_char_is_first_char_of_prefix_for_every_variant`
21495 // one axis up on the closed-set entry-char algebra.
21496 for qf in QuoteForm::ALL {
21497 if let Some(promoted) = qf.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR) {
21498 let composed = format!("{}{}", qf.prefix(), QuoteForm::SPLICE_DISCRIMINATOR);
21499 assert_eq!(
21500 composed,
21501 promoted.prefix(),
21502 "QuoteForm::{qf:?} — promotion composition drifted from \
21503 promoted prefix ({promoted:?}.prefix() = {:?})",
21504 promoted.prefix(),
21505 );
21506 }
21507 }
21508 }
21509
21510 #[test]
21511 fn quote_form_unquote_splice_prefix_composes_from_unquote_prefix_and_splice_discriminator() {
21512 // STRUCTURAL COMPOSITION LAW: [`QuoteForm::UnquoteSplice`]'s
21513 // two-char prefix `",@"` decomposes cleanly into
21514 // [`QuoteForm::Unquote`]'s prefix `","` + the splice
21515 // discriminator byte `'@'`. Pin the identity directly rather
21516 // than through the promotion table so a regression that
21517 // renamed the discriminator without touching the promotion
21518 // table (or vice versa) surfaces here. This IS the structural
21519 // identity the reader's peek-then-consume arm depends on: the
21520 // tokenizer sees `,` (Unquote lead char), peeks `@`
21521 // (SPLICE_DISCRIMINATOR), and emits UnquoteSplice — the
21522 // rendered prefix identity closes the read↔write duality.
21523 let composed = format!(
21524 "{}{}",
21525 QuoteForm::Unquote.prefix(),
21526 QuoteForm::SPLICE_DISCRIMINATOR,
21527 );
21528 assert_eq!(
21529 composed,
21530 QuoteForm::UnquoteSplice.prefix(),
21531 "UnquoteSplice.prefix() drifted from Unquote.prefix() + \
21532 SPLICE_DISCRIMINATOR — the reader's two-char splice \
21533 promotion identity is broken",
21534 );
21535 }
21536
21537 // ── `QuoteForm::PROMOTIONS` — the closed-set forced-arity array of
21538 // promotion triples on the substrate's quote-family algebra. Pins
21539 // the singleton `(Unquote, SPLICE_DISCRIMINATOR, UnquoteSplice)`
21540 // entry AND its alignment with `promote_via_next_char`'s Some-arm
21541 // AND the family-wide contract sweeps below. Sibling-shape tests to
21542 // the `quote_form_hash_discriminators_*` block on the outer-Sexp
21543 // cache-key axis — that block anchors the `[u8; 4]` byte algebra;
21544 // this block anchors the `[(Self, char, Self); 1]` promotion
21545 // algebra one axis over on the same closed set.
21546
21547 #[test]
21548 fn quote_form_promotions_has_expected_cardinality() {
21549 // FORCED-ARITY CONTRACT: [`QuoteForm::PROMOTIONS`]'s
21550 // cardinality is `1` at the type level — the substrate's
21551 // current promotion algebra has EXACTLY ONE promotion arm
21552 // (`(Unquote, SPLICE_DISCRIMINATOR, UnquoteSplice)`). A
21553 // regression that widened the array without extending
21554 // [`QuoteForm::promote_via_next_char`]'s match body (or vice
21555 // versa) fails compilation because the array literal's arity
21556 // is forced by the `[_; 1]` type annotation. This runtime
21557 // pin closes the same law at a runtime cardinality check
21558 // so a callsite that expects the singleton shape without
21559 // static-arity inference (a dynamic iteration site, an
21560 // audit-log emitter that reports the promotion-algebra size
21561 // for observability) reads through ONE substrate primitive
21562 // rather than through a hand-rolled `1usize` literal.
21563 assert_eq!(
21564 QuoteForm::PROMOTIONS.len(),
21565 1,
21566 "QuoteForm::PROMOTIONS cardinality drifted from the \
21567 substrate's singleton promotion algebra — the closed-set \
21568 array's forced arity is load-bearing on the `(Unquote, \
21569 SPLICE_DISCRIMINATOR) → UnquoteSplice` singleton identity",
21570 );
21571 }
21572
21573 #[test]
21574 fn quote_form_promotions_pin_legacy_splice_promotion_triple() {
21575 // LEGACY-TRIPLE CONTRACT: [`QuoteForm::PROMOTIONS`]'s
21576 // singleton entry is byte-for-byte `(Self::Unquote,
21577 // Self::SPLICE_DISCRIMINATOR, Self::UnquoteSplice)`. Pin each
21578 // column of the triple against its typed source:
21579 // * head == QuoteForm::Unquote (the `,` variant)
21580 // * disc == QuoteForm::SPLICE_DISCRIMINATOR (`'@'`)
21581 // * promoted == QuoteForm::UnquoteSplice (the `,@` variant)
21582 // A regression that drifts ONE column of the triple silently
21583 // redirects the reader's promotion arm to a phantom variant
21584 // AND fails HERE at the per-column identity check rather
21585 // than at silent tokenizer drift where every `,@xs` source
21586 // tokenizes to the wrong closed-set marker.
21587 assert_eq!(
21588 QuoteForm::PROMOTIONS[0].0,
21589 QuoteForm::Unquote,
21590 "QuoteForm::PROMOTIONS[0].0 (head) drifted from Unquote — \
21591 the substrate's singleton promotion arm's head variant \
21592 MUST be Unquote (the only variant whose prefix is the \
21593 lead byte of a longer variant's prefix)",
21594 );
21595 assert_eq!(
21596 QuoteForm::PROMOTIONS[0].1,
21597 QuoteForm::SPLICE_DISCRIMINATOR,
21598 "QuoteForm::PROMOTIONS[0].1 (discriminator) drifted from \
21599 SPLICE_DISCRIMINATOR — the substrate's singleton \
21600 promotion arm's discriminator MUST be the byte the \
21601 reader's peek arm consumes to promote the head variant \
21602 (the ONE `'@'` byte on the closed-set algebra)",
21603 );
21604 assert_eq!(
21605 QuoteForm::PROMOTIONS[0].2,
21606 QuoteForm::UnquoteSplice,
21607 "QuoteForm::PROMOTIONS[0].2 (promoted) drifted from \
21608 UnquoteSplice — the substrate's singleton promotion \
21609 arm's promoted variant MUST be UnquoteSplice (the only \
21610 two-char-prefix variant on the closed-set algebra)",
21611 );
21612 }
21613
21614 #[test]
21615 fn quote_form_promotions_align_with_promote_via_next_char_for_every_entry() {
21616 // ALIGNMENT CONTRACT: sweep [`QuoteForm::PROMOTIONS`] and
21617 // assert that for every `(head, disc, promoted)` entry,
21618 // `head.promote_via_next_char(disc) == Some(promoted)`. This
21619 // is the projection method's forward composition law at the
21620 // closed set — a regression that drifts the promoted-variant
21621 // column of the constant (or the projection method's Some-arm
21622 // return literal) surfaces here rather than as a silent
21623 // tokenizer redirect. Sibling-shape pin to
21624 // `quote_form_hash_discriminators_align_with_all_by_index`
21625 // one axis over on the cache-key byte algebra — that pin
21626 // aligns the `[u8; 4]` array with the projection method's
21627 // per-variant arm; this pin aligns the `[(Self, char, Self);
21628 // 1]` array with the projection method's per-triple arm.
21629 for (head, disc, promoted) in QuoteForm::PROMOTIONS {
21630 let projected = head.promote_via_next_char(disc);
21631 assert_eq!(
21632 projected,
21633 Some(promoted),
21634 "QuoteForm::PROMOTIONS[({head:?}, {disc:?}, \
21635 {promoted:?})] — `promote_via_next_char` drifted \
21636 from the constant's promoted-variant column (got \
21637 {projected:?})",
21638 );
21639 }
21640 }
21641
21642 #[test]
21643 fn quote_form_promotions_compose_prefix_from_source_prefix_and_discriminator_for_every_entry() {
21644 // COMPOSITION LAW (rendered-prefix identity): sweep
21645 // [`QuoteForm::PROMOTIONS`] and assert that for every
21646 // `(head, disc, promoted)` entry, `format!("{}{}",
21647 // head.prefix(), disc) == promoted.prefix()`. The
21648 // (head prefix + discriminator) source-text composition
21649 // agrees byte-for-byte with the promoted variant's rendered
21650 // prefix — the reader's peek-then-consume arm's rendered
21651 // prefix identity closes the read↔write duality across
21652 // every entry in the promotion algebra.
21653 //
21654 // Sibling to the pre-existing
21655 // `quote_form_promote_via_next_char_composes_prefix_from_source_prefix_and_next_char`
21656 // which pins the same law through
21657 // [`QuoteForm::promote_via_next_char`]'s Some-arm rather
21658 // than through the constant's triple directly — this pin
21659 // closes the law at the constant, that pin closes it at the
21660 // projection method. Together the two pins bind the
21661 // rendered-prefix identity to BOTH the substrate primitive
21662 // AND the projection method so a regression that drifts
21663 // ONE side of the identity fails at BOTH pins rather than at
21664 // silent read/write drift where a reader-tokenized `,@xs`
21665 // form's rendered prefix disagrees with its typed marker's
21666 // rendered prefix.
21667 for (head, disc, promoted) in QuoteForm::PROMOTIONS {
21668 let composed = format!("{}{}", head.prefix(), disc);
21669 assert_eq!(
21670 composed,
21671 promoted.prefix(),
21672 "QuoteForm::PROMOTIONS[({head:?}, {disc:?}, \
21673 {promoted:?})] — head.prefix() + disc drifted from \
21674 promoted.prefix() ({:?})",
21675 promoted.prefix(),
21676 );
21677 }
21678 }
21679
21680 #[test]
21681 fn quote_form_promotions_close_promote_via_next_char_against_every_non_promotion_pair() {
21682 // REJECTION CONTRACT: sweep [`QuoteForm::ALL`] × (every
21683 // (head, disc) pair from [`QuoteForm::PROMOTIONS`] plus every
21684 // rejection discriminator distinct from the promotion set's
21685 // discriminator column) and assert that every pair NOT in
21686 // [`QuoteForm::PROMOTIONS`]'s `(head, disc)` projection
21687 // rejects with `None`. A regression that widened the
21688 // promotion algebra (e.g. phantom-promoted [`QuoteForm::Quote`]
21689 // on `'@'` after a copy-paste drift on the match arm's head
21690 // pattern, OR silently promoted `Unquote` on a non-`@`
21691 // discriminator after a drift on the match arm's char
21692 // pattern) fails HERE at the sweep-time rejection assertion
21693 // rather than at silent tokenizer drift where bare `'@xs`
21694 // forms degrade to a phantom `UnquoteSplice`-shaped sequence
21695 // OR bare `,'xs` forms silently promote through the reader.
21696 //
21697 // Sibling-shape pin to the pre-existing
21698 // `quote_form_promote_via_next_char_only_promotes_unquote_on_splice_discriminator`
21699 // which sweeps every variant against SPLICE_DISCRIMINATOR
21700 // AND a hand-rolled rejection char set; this pin extends the
21701 // rejection sweep to compose the rejection set STRUCTURALLY
21702 // from [`QuoteForm::PROMOTIONS`]'s complement rather than
21703 // hand-rolling a rejection char literal list at a callsite
21704 // that would silently drift as the algebra grows.
21705 //
21706 // The rejection char set is composed as: every
21707 // [`QuoteForm::ALL`] variant's [`QuoteForm::lead_char`] (the
21708 // three quote-family lead bytes `{'\'', '`', ','}`), every
21709 // char in [`QuoteForm::PROMOTIONS`]'s discriminator column
21710 // (the ONE `'@'` byte — used to verify that variants NOT in
21711 // the promotion set's head column reject on the same
21712 // discriminator), and a hand-rolled sweep of non-quote-family
21713 // rejection chars (whitespace, structural, reader-punctuation)
21714 // to cover the closed-set-complement rejection surface.
21715 let mut discriminators: Vec<char> =
21716 QuoteForm::ALL.iter().map(|qf| qf.lead_char()).collect();
21717 for (_, disc, _) in QuoteForm::PROMOTIONS {
21718 if !discriminators.contains(&disc) {
21719 discriminators.push(disc);
21720 }
21721 }
21722 for extra in [
21723 ' ',
21724 '\n',
21725 '\t',
21726 Sexp::LIST_OPEN,
21727 Sexp::LIST_CLOSE,
21728 Sexp::COMMENT_LEAD,
21729 Atom::STR_DELIMITER,
21730 'a',
21731 '#',
21732 ':',
21733 '!',
21734 '?',
21735 '~',
21736 ] {
21737 if !discriminators.contains(&extra) {
21738 discriminators.push(extra);
21739 }
21740 }
21741
21742 let promotion_pairs: Vec<(QuoteForm, char)> = QuoteForm::PROMOTIONS
21743 .iter()
21744 .map(|(head, disc, _)| (*head, *disc))
21745 .collect();
21746
21747 for head in QuoteForm::ALL {
21748 for disc in &discriminators {
21749 let in_promotion_set = promotion_pairs.contains(&(head, *disc));
21750 let projected = head.promote_via_next_char(*disc);
21751 if in_promotion_set {
21752 // Positive arms are covered by
21753 // `quote_form_promotions_align_with_promote_via_next_char_for_every_entry`;
21754 // skip here to keep this pin's focus on the
21755 // rejection surface exclusively.
21756 continue;
21757 }
21758 assert_eq!(
21759 projected, None,
21760 "QuoteForm::{head:?}.promote_via_next_char({disc:?}) \
21761 — promotion algebra leaked on a pair NOT in \
21762 QuoteForm::PROMOTIONS (got {projected:?}, \
21763 expected None)",
21764 );
21765 }
21766 }
21767 }
21768
21769 #[test]
21770 fn quote_form_promote_via_next_char_routes_promoted_variant_through_promotions_constant() {
21771 // ROUTING CONTRACT: pin that
21772 // [`QuoteForm::promote_via_next_char`]'s Some-arm return
21773 // BINDS through [`QuoteForm::PROMOTIONS`]`[0].2` rather than
21774 // through an inline [`QuoteForm::UnquoteSplice`] literal.
21775 // Post-lift the projection method's Some-arm reads:
21776 // `Some(Self::PROMOTIONS[0].2)`
21777 // — so a regression that reverts the arm to an inline
21778 // `Some(Self::UnquoteSplice)` fails HERE at the byte-identity
21779 // sweep, WHERE the projected value is compared against
21780 // [`QuoteForm::PROMOTIONS`]`[0].2` directly rather than
21781 // against an inline literal.
21782 //
21783 // Sibling-shape pin to
21784 // `sexp_shape_hash_discriminator_atomic_arms_route_through_atom_kind_outer_hash_discriminator`
21785 // (prior-run 39537b2) one axis over on the cache-key byte
21786 // algebra — that pin binds the shape-level projection's
21787 // atomic-arm collapse to the typed constant; this pin binds
21788 // the promotion projection's Some-arm to the typed triple's
21789 // promoted-variant column. Together the two pins close the
21790 // "projection routes through the substrate primitive" pattern
21791 // across the cache-key axis AND the promotion axis on the
21792 // closed-set algebra.
21793 let projected = QuoteForm::Unquote.promote_via_next_char(QuoteForm::SPLICE_DISCRIMINATOR);
21794 assert_eq!(
21795 projected,
21796 Some(QuoteForm::PROMOTIONS[0].2),
21797 "QuoteForm::Unquote.promote_via_next_char(SPLICE_DISCRIMINATOR) \
21798 — Some-arm return drifted from routing through \
21799 QuoteForm::PROMOTIONS[0].2 (got {projected:?})",
21800 );
21801 }
21802
21803 #[test]
21804 fn quote_form_hash_discriminator_pins_legacy_cache_key_bytes() {
21805 // CACHE-KEY CONTRACT: pre-lift `Hash for Sexp` used the literal
21806 // byte values 3/4/5/6 for Quote/Quasiquote/Unquote/UnquoteSplice
21807 // as the per-variant discriminator. The expansion cache
21808 // (`Expander::cache`) keys on Hash; ANY change to a
21809 // discriminator byte silently invalidates every cached
21810 // expansion across the substrate AND risks collision with the
21811 // reserved bytes the non-quote-family Hash arms use (0=Nil,
21812 // 1=Atom, 2=List). Pin the four legacy values explicitly so a
21813 // regression that re-numbers them surfaces immediately — the
21814 // `QuoteForm` algebra MUST preserve the prior byte mapping
21815 // bit-for-bit.
21816 assert_eq!(QuoteForm::Quote.hash_discriminator(), 3);
21817 assert_eq!(QuoteForm::Quasiquote.hash_discriminator(), 4);
21818 assert_eq!(QuoteForm::Unquote.hash_discriminator(), 5);
21819 assert_eq!(QuoteForm::UnquoteSplice.hash_discriminator(), 6);
21820 }
21821
21822 // ── `QuoteForm::{QUOTE_HASH_DISCRIMINATOR,
21823 // QUASIQUOTE_HASH_DISCRIMINATOR, UNQUOTE_HASH_DISCRIMINATOR,
21824 // UNQUOTE_SPLICE_HASH_DISCRIMINATOR, HASH_DISCRIMINATORS}` —
21825 // per-role `u8` cache-key byte algebra on the closed-set outer
21826 // [`QuoteForm`]. Fourth per-role axis on the algebra alongside the
21827 // reader-prefix (commit a08e61f), diagnostic-label (commit
21828 // 70be157), and iac-forge canonical-form tag (commit bdd624b)
21829 // `&'static str` axes — closes the FOUR production
21830 // byte-vocabularies the closed set carries at ONE `pub(crate)
21831 // const` per (role, vocabulary) pair plus a family-wide ALL array
21832 // per vocabulary.
21833
21834 #[test]
21835 fn quote_form_hash_discriminators_pin_legacy_cache_key_bytes() {
21836 // Pin each per-role `pub(crate) const` at its exact canonical
21837 // `u8` byte. Sibling of
21838 // `quote_form_hash_discriminator_pins_legacy_cache_key_bytes`
21839 // (which pins the method's projection) — this pin asserts the
21840 // `pub(crate) const` value itself, so a regression that drifts
21841 // the constant but leaves the method's arm literal in place
21842 // (unlikely post-lift but structurally distinct) surfaces
21843 // here. The cache-key partition `{3, 4, 5, 6}` is load-bearing
21844 // for the outer-`Sexp` `Hash` body's disjointness contract
21845 // with the reserved bytes `{0, 1, 2}` the non-quote-family
21846 // arms use — a `4u8` drift to `2u8` would silently collide
21847 // with `StructuralKind::List`'s cache-key byte and mis-hash
21848 // every quasi-quote through the list-arm's path.
21849 assert_eq!(QuoteForm::QUOTE_HASH_DISCRIMINATOR, 3);
21850 assert_eq!(QuoteForm::QUASIQUOTE_HASH_DISCRIMINATOR, 4);
21851 assert_eq!(QuoteForm::UNQUOTE_HASH_DISCRIMINATOR, 5);
21852 assert_eq!(QuoteForm::UNQUOTE_SPLICE_HASH_DISCRIMINATOR, 6);
21853 }
21854
21855 #[test]
21856 fn quote_form_hash_discriminator_routes_through_typed_per_role_constants() {
21857 // PATH-UNIFORMITY: `Self::hash_discriminator(self)` returns
21858 // the per-role `pub(crate) const` byte-for-byte per variant,
21859 // catching a regression that reverts ONE arm to an inline
21860 // `3` / `4` / `5` / `6` `u8` literal (or drifts one arm's
21861 // byte silently). Sibling posture to
21862 // `quote_form_prefix_routes_through_typed_per_role_constants`
21863 // on the reader-prefix axis of the SAME closed set.
21864 for (qf, expected) in [
21865 (QuoteForm::Quote, QuoteForm::QUOTE_HASH_DISCRIMINATOR),
21866 (
21867 QuoteForm::Quasiquote,
21868 QuoteForm::QUASIQUOTE_HASH_DISCRIMINATOR,
21869 ),
21870 (QuoteForm::Unquote, QuoteForm::UNQUOTE_HASH_DISCRIMINATOR),
21871 (
21872 QuoteForm::UnquoteSplice,
21873 QuoteForm::UNQUOTE_SPLICE_HASH_DISCRIMINATOR,
21874 ),
21875 ] {
21876 let actual = qf.hash_discriminator();
21877 assert_eq!(
21878 actual, expected,
21879 "QuoteForm::{qf:?}.hash_discriminator() `{actual}` \
21880 drifted from per-role constant `{expected}` — the \
21881 arm must route through the typed `pub(crate) const` \
21882 rather than an inline `u8` literal",
21883 );
21884 }
21885 }
21886
21887 #[test]
21888 fn quote_form_hash_discriminators_has_expected_cardinality() {
21889 // Cardinality contract: `Self::HASH_DISCRIMINATORS.len() == 4`
21890 // — pinned at the declaration site by rustc's forced-arity
21891 // check on `[u8; 4]`. This test surfaces the arity as a
21892 // fail-loud runtime pin so a future refactor that switches
21893 // the array type to `&[u8]` (dropping the compile-time arity
21894 // forcing) doesn't silently loosen the closed-set discipline
21895 // the family relies on. Sibling posture to
21896 // `quote_form_prefixes_has_expected_cardinality`,
21897 // `quote_form_labels_has_expected_cardinality`, and
21898 // `quote_form_iac_forge_tags_has_expected_cardinality` on the
21899 // other three per-role axes of the SAME [`QuoteForm`] closed
21900 // set.
21901 assert_eq!(
21902 QuoteForm::HASH_DISCRIMINATORS.len(),
21903 4,
21904 "QuoteForm::HASH_DISCRIMINATORS cardinality drifted from \
21905 4 — the closed homoiconic-prefix domain admits exactly \
21906 four wrappers by construction; a fifth extension \
21907 surfaces here"
21908 );
21909 }
21910
21911 #[test]
21912 fn quote_form_hash_discriminators_align_with_all_by_index() {
21913 // ALIGNMENT CONTRACT: `Self::HASH_DISCRIMINATORS[i] ==
21914 // Self::ALL[i].hash_discriminator()` element-wise. Pins that
21915 // the typed variant ALL and the `u8` HASH_DISCRIMINATORS ALL
21916 // stay in lockstep under any reorder — a regression that
21917 // reorders ONE array without reordering the other silently
21918 // misaligns every `zip(ALL, HASH_DISCRIMINATORS)` consumer.
21919 // Sibling posture to `quote_form_prefixes_align_with_all_by_index`
21920 // on the reader-prefix axis.
21921 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
21922 assert_eq!(
21923 QuoteForm::HASH_DISCRIMINATORS[i],
21924 qf.hash_discriminator(),
21925 "QuoteForm::HASH_DISCRIMINATORS[{i}] `{disc}` drifted \
21926 from QuoteForm::ALL[{i}] ({qf:?}).hash_discriminator() \
21927 `{via_variant}` — the canonical declaration order of \
21928 the ALL array and the hash_discriminator projection \
21929 must match element-wise",
21930 disc = QuoteForm::HASH_DISCRIMINATORS[i],
21931 via_variant = qf.hash_discriminator(),
21932 );
21933 }
21934 }
21935
21936 #[test]
21937 fn quote_form_hash_discriminators_pairwise_distinct() {
21938 // PAIRWISE DISJOINTNESS: every entry of the
21939 // `HASH_DISCRIMINATORS` array must differ so the outer-`Sexp`
21940 // `Hash` body cannot route two homoiconic prefixes through
21941 // the same cache-key byte — a collision would silently mis-
21942 // hash two structurally-distinct forms to the same
21943 // `Expander::cache` slot. Family-wide sweep over
21944 // `HASH_DISCRIMINATORS × HASH_DISCRIMINATORS` — supersedes
21945 // any per-pair pin and picks up new discriminators
21946 // mechanically. Sibling posture to
21947 // `quote_form_prefixes_pairwise_distinct`.
21948 for (i, a) in QuoteForm::HASH_DISCRIMINATORS.iter().enumerate() {
21949 for (j, b) in QuoteForm::HASH_DISCRIMINATORS.iter().enumerate() {
21950 if i == j {
21951 continue;
21952 }
21953 assert_ne!(
21954 a, b,
21955 "QuoteForm::HASH_DISCRIMINATORS[{i}] `{a}` \
21956 collides with QuoteForm::HASH_DISCRIMINATORS[{j}] \
21957 `{b}` — the outer-Sexp Hash body's cache-key \
21958 partition would route two homoiconic prefixes \
21959 through the same slot"
21960 );
21961 }
21962 }
21963 }
21964
21965 #[test]
21966 fn quote_form_hash_discriminators_disjoint_from_reserved_outer_sexp_bytes() {
21967 // CROSS-AXIS DISJOINTNESS CONTRACT: every entry of
21968 // `Self::HASH_DISCRIMINATORS` must differ from the reserved
21969 // outer-`Sexp` bytes the non-quote-family arms use — `0u8`
21970 // for [`crate::error::StructuralKind::Nil`], `1u8` for the
21971 // [`crate::ast::Sexp::Atom`] outer-carve marker, `2u8` for
21972 // [`crate::error::StructuralKind::List`]. The three carvings
21973 // of the outer-`Sexp` cache-key space jointly cover
21974 // `{0, 1, 2, 3, 4, 5, 6}` with no gaps AND no overlaps; a
21975 // regression that re-numbers a quote-family discriminator
21976 // into the reserved region silently mis-hashes every affected
21977 // form. Pin the disjointness across every reserved byte so a
21978 // future rename catches the collision at test time rather
21979 // than as a silent cache-key drift where
21980 // `Expander::cache` mis-collides live expansions.
21981 let reserved_non_quote_family_bytes: [u8; 3] = [
21982 crate::error::StructuralKind::Nil.hash_discriminator(),
21983 1u8, // Sexp::Atom outer-carve marker (pre-lift inline literal in Hash for Sexp)
21984 crate::error::StructuralKind::List.hash_discriminator(),
21985 ];
21986 for (i, quote_family_byte) in QuoteForm::HASH_DISCRIMINATORS.iter().enumerate() {
21987 for reserved_byte in reserved_non_quote_family_bytes {
21988 assert_ne!(
21989 *quote_family_byte, reserved_byte,
21990 "QuoteForm::HASH_DISCRIMINATORS[{i}] `{quote_family_byte}` \
21991 collides with reserved non-quote-family cache-key byte \
21992 `{reserved_byte}` — the outer-Sexp Hash body's three-\
21993 carving partition is broken"
21994 );
21995 }
21996 }
21997 }
21998
21999 #[test]
22000 fn quote_form_as_unquote_form_projects_two_of_four_subset() {
22001 // The structural-subset gate: only `{Unquote, UnquoteSplice}`
22002 // are template-substitution markers; `{Quote, Quasiquote}` are
22003 // wrappers whose semantic does NOT include substitution. Pin
22004 // the 2-of-4 partition so the `Sexp::as_unquote` derivation's
22005 // closed-set arithmetic stays correct.
22006 assert_eq!(
22007 QuoteForm::Unquote.as_unquote_form(),
22008 Some(UnquoteForm::Unquote)
22009 );
22010 assert_eq!(
22011 QuoteForm::UnquoteSplice.as_unquote_form(),
22012 Some(UnquoteForm::Splice)
22013 );
22014 assert_eq!(QuoteForm::Quote.as_unquote_form(), None);
22015 assert_eq!(QuoteForm::Quasiquote.as_unquote_form(), None);
22016 }
22017
22018 #[test]
22019 fn quote_form_iac_forge_tag_pins_canonical_lisp_tag_strings_for_every_variant() {
22020 // CROSS-CRATE CANONICAL-FORM CONTRACT: the four canonical
22021 // iac-forge tags are load-bearing for inter-crate compatibility
22022 // — `iac_forge::sexpr::SExpr` consumers (BLAKE3 attestation,
22023 // render cache) key on the canonical 2-element-list shape
22024 // `(<tag> <inner>)`. A regression that drifts ONE tag silently
22025 // invalidates every cached canonical form across the substrate
22026 // AND mis-collides with the legacy `SexpShape::label` projection
22027 // that uses the shorter `"unquote-splice"` for the diagnostic
22028 // surface. Pin the four legacy tag values explicitly so a
22029 // regression that re-spells them surfaces immediately.
22030 assert_eq!(QuoteForm::Quote.iac_forge_tag(), "quote");
22031 assert_eq!(QuoteForm::Quasiquote.iac_forge_tag(), "quasiquote");
22032 assert_eq!(QuoteForm::Unquote.iac_forge_tag(), "unquote");
22033 assert_eq!(QuoteForm::UnquoteSplice.iac_forge_tag(), "unquote-splicing");
22034 }
22035
22036 // ── `QuoteForm::{QUOTE_IAC_FORGE_TAG, QUASIQUOTE_IAC_FORGE_TAG,
22037 // UNQUOTE_IAC_FORGE_TAG, UNQUOTE_SPLICE_IAC_FORGE_TAG,
22038 // IAC_FORGE_TAGS}` — per-role `&'static str` iac-forge canonical-
22039 // form tag algebra on the closed-set outer [`QuoteForm`]. Peer of
22040 // the reader-prefix axis's [`QuoteForm::{QUOTE_PREFIX,
22041 // QUASIQUOTE_PREFIX, UNQUOTE_PREFIX, UNQUOTE_SPLICE_PREFIX,
22042 // PREFIXES}`] block above — the same closed set carries TWO
22043 // orthogonal byte vocabularies (the Lisp reader prefixes the
22044 // tokenizer classifies on, the iac-forge canonical-form tags the
22045 // cross-crate attestation layer round-trips through), each now
22046 // pinned at a per-role `pub const` plus a paired ALL array.
22047 #[test]
22048 fn quote_form_per_role_iac_forge_tags_pin_canonical_bytes() {
22049 // Pin each per-role `pub const` at its exact canonical byte
22050 // sequence. Sweeping via a per-variant pair rather than four
22051 // hand-rolled `assert_eq!(QuoteForm::X_IAC_FORGE_TAG, "x")`
22052 // asserts (a) that each constant IS the load-bearing byte
22053 // string, and (b) that the pairing between the const and the
22054 // spelling is enforced at rustc's constant-folding-level so a
22055 // future rename of the const surfaces here as a spelling drift
22056 // rather than as a silent canonical-form regression.
22057 for (label, actual, expected) in [
22058 (
22059 "QUOTE_IAC_FORGE_TAG",
22060 QuoteForm::QUOTE_IAC_FORGE_TAG,
22061 "quote",
22062 ),
22063 (
22064 "QUASIQUOTE_IAC_FORGE_TAG",
22065 QuoteForm::QUASIQUOTE_IAC_FORGE_TAG,
22066 "quasiquote",
22067 ),
22068 (
22069 "UNQUOTE_IAC_FORGE_TAG",
22070 QuoteForm::UNQUOTE_IAC_FORGE_TAG,
22071 "unquote",
22072 ),
22073 (
22074 "UNQUOTE_SPLICE_IAC_FORGE_TAG",
22075 QuoteForm::UNQUOTE_SPLICE_IAC_FORGE_TAG,
22076 "unquote-splicing",
22077 ),
22078 ] {
22079 assert_eq!(
22080 actual, expected,
22081 "QuoteForm::{label} drifted from canonical `{expected}` bytes"
22082 );
22083 }
22084 }
22085
22086 #[test]
22087 fn quote_form_iac_forge_tag_routes_through_typed_per_role_constants() {
22088 // PATH-UNIFORMITY: `Self::iac_forge_tag(self)` returns the
22089 // per-role `pub const` byte-for-byte per variant, catching a
22090 // regression that reverts ONE arm to an inline `"quote"` /
22091 // `"quasiquote"` / `"unquote"` / `"unquote-splicing"` string
22092 // literal (or drifts one arm's bytes silently). Sibling posture
22093 // to `quote_form_prefix_routes_through_typed_per_role_constants`
22094 // on the reader-prefix axis of the SAME closed set.
22095 for (qf, expected) in [
22096 (QuoteForm::Quote, QuoteForm::QUOTE_IAC_FORGE_TAG),
22097 (QuoteForm::Quasiquote, QuoteForm::QUASIQUOTE_IAC_FORGE_TAG),
22098 (QuoteForm::Unquote, QuoteForm::UNQUOTE_IAC_FORGE_TAG),
22099 (
22100 QuoteForm::UnquoteSplice,
22101 QuoteForm::UNQUOTE_SPLICE_IAC_FORGE_TAG,
22102 ),
22103 ] {
22104 let actual = qf.iac_forge_tag();
22105 assert_eq!(
22106 actual, expected,
22107 "QuoteForm::{qf:?}.iac_forge_tag() `{actual}` drifted from \
22108 per-role constant `{expected}` — the arm must route \
22109 through the typed constant rather than an inline literal",
22110 );
22111 }
22112 }
22113
22114 #[test]
22115 fn quote_form_iac_forge_tags_has_expected_cardinality() {
22116 // Cardinality contract: `Self::IAC_FORGE_TAGS.len() == 4` —
22117 // pinned at the declaration site by rustc's forced-arity check
22118 // on `[&'static str; 4]`. This test surfaces the arity as a
22119 // fail-loud runtime pin so a future refactor that switches the
22120 // array type to `&[&'static str]` (dropping the compile-time
22121 // arity forcing) doesn't silently loosen the closed-set
22122 // discipline the family relies on. Sibling posture to
22123 // `quote_form_prefixes_has_expected_cardinality` on the peer
22124 // reader-prefix axis.
22125 assert_eq!(
22126 QuoteForm::IAC_FORGE_TAGS.len(),
22127 4,
22128 "QuoteForm::IAC_FORGE_TAGS cardinality drifted from 4 — the \
22129 closed homoiconic-prefix domain admits exactly four \
22130 wrappers by construction; a fifth extension surfaces here"
22131 );
22132 }
22133
22134 #[test]
22135 fn quote_form_iac_forge_tags_align_with_all_by_index() {
22136 // ALIGNMENT CONTRACT: `Self::IAC_FORGE_TAGS[i] ==
22137 // Self::ALL[i].iac_forge_tag()` element-wise. Pins that the
22138 // typed variant ALL and the `&'static str` IAC_FORGE_TAGS ALL
22139 // stay in lockstep under any reorder — a regression that
22140 // reorders ONE array without reordering the other silently
22141 // misaligns every `zip(ALL, IAC_FORGE_TAGS)` consumer (cross-
22142 // crate attestation renderers, LSP canonical-form completion
22143 // providers, metric-label emitters, coverage reporters).
22144 // Sibling posture to `quote_form_prefixes_align_with_all_by_index`
22145 // on the peer reader-prefix axis.
22146 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
22147 assert_eq!(
22148 QuoteForm::IAC_FORGE_TAGS[i],
22149 qf.iac_forge_tag(),
22150 "QuoteForm::IAC_FORGE_TAGS[{i}] `{tag}` drifted from \
22151 QuoteForm::ALL[{i}] ({qf:?}).iac_forge_tag() `{via_variant}` \
22152 — the canonical declaration order of the ALL array \
22153 and the iac-forge tag projection must match element-wise",
22154 tag = QuoteForm::IAC_FORGE_TAGS[i],
22155 via_variant = qf.iac_forge_tag(),
22156 );
22157 }
22158 }
22159
22160 #[test]
22161 fn quote_form_iac_forge_tags_pairwise_distinct() {
22162 // PAIRWISE DISJOINTNESS: every entry of the `IAC_FORGE_TAGS`
22163 // array must differ so the cross-crate canonical-form decoder
22164 // (any future `IAC_FORGE_TAGS.iter().find(|t| *t == head)`
22165 // sweep, any BLAKE3 attestation key comparison) cannot route
22166 // two homoiconic prefixes through the same tag arm. Family-wide
22167 // sweep over `IAC_FORGE_TAGS × IAC_FORGE_TAGS` — supersedes any
22168 // per-pair pin and picks up new tags mechanically. Sibling
22169 // posture to `quote_form_prefixes_pairwise_distinct` on the
22170 // peer reader-prefix axis.
22171 for (i, a) in QuoteForm::IAC_FORGE_TAGS.iter().enumerate() {
22172 for (j, b) in QuoteForm::IAC_FORGE_TAGS.iter().enumerate() {
22173 if i == j {
22174 continue;
22175 }
22176 assert_ne!(
22177 a, b,
22178 "QuoteForm::IAC_FORGE_TAGS[{i}] `{a}` collides with \
22179 QuoteForm::IAC_FORGE_TAGS[{j}] `{b}` — the \
22180 canonical-form decoder's cascade would route two \
22181 homoiconic prefixes through the same tag arm"
22182 );
22183 }
22184 }
22185 }
22186
22187 #[test]
22188 fn quote_form_iac_forge_tags_diverge_from_prefixes_pairwise() {
22189 // AXIS-ORTHOGONALITY: `IAC_FORGE_TAGS` (the cross-crate
22190 // canonical-form axis) and `PREFIXES` (the Lisp reader-prefix
22191 // axis) span two distinct byte vocabularies on the SAME closed
22192 // set. Every per-variant pair must disagree: the canonical
22193 // tags are word-length identifiers (`"quote"`, `"quasiquote"`,
22194 // `"unquote"`, `"unquote-splicing"`) while the reader prefixes
22195 // are punctuation (`"'"`, `` "`" ``, `","`, `",@"`). A
22196 // regression that collapsed the two axes (a hypothetical
22197 // consolidation PR that reused `iac_forge_tag()`'s bytes at
22198 // the reader prefix site, or vice versa) would silently break
22199 // either the source-code round-trip (readers no longer see
22200 // `"'"`) OR the canonical-form round-trip (attestation keys
22201 // no longer see `"quote"`). Sweep every variant's per-axis
22202 // pair — supersedes any per-variant pin and picks up new
22203 // prefix/tag pairs mechanically.
22204 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
22205 let prefix = QuoteForm::PREFIXES[i];
22206 let tag = QuoteForm::IAC_FORGE_TAGS[i];
22207 assert_ne!(
22208 prefix, tag,
22209 "QuoteForm::{qf:?} — reader prefix `{prefix}` collides \
22210 with iac-forge tag `{tag}`; the two axes must span \
22211 distinct byte vocabularies on the closed set",
22212 );
22213 }
22214 }
22215
22216 #[test]
22217 fn quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice() {
22218 // BOUNDARY-DISTINCT CONTRACT: the iac-forge canonical tag for
22219 // `UnquoteSplice` is `"unquote-splicing"` (Common Lisp idiom,
22220 // load-bearing for canonical-form round-trip with the iac-forge
22221 // ecosystem), distinct from `SexpShape::label`'s shorter
22222 // `"unquote-splice"` (the substrate's diagnostic label idiom).
22223 // The two projections key the SAME closed-set on TWO distinct
22224 // boundaries — pinning the divergence here documents the
22225 // intent: a future "consolidation" PR that homogenizes them
22226 // would silently break either the iac-forge canonical-form
22227 // round-trip OR the operator-facing diagnostic surface. The
22228 // three other variants (Quote, Quasiquote, Unquote) DO match
22229 // across both projections — pin that path-uniformity too so a
22230 // regression that drifts one of the three matched arms surfaces
22231 // immediately. Sibling-arm sweep so the (variant, tag) AND
22232 // (variant, label) pairings stay load-bearing under reordering
22233 // refactors.
22234 use crate::error::SexpShape;
22235 assert_eq!(
22236 QuoteForm::Quote.iac_forge_tag(),
22237 SexpShape::Quote.label(),
22238 "quote tag/label agreement"
22239 );
22240 assert_eq!(
22241 QuoteForm::Quasiquote.iac_forge_tag(),
22242 SexpShape::Quasiquote.label(),
22243 "quasiquote tag/label agreement"
22244 );
22245 assert_eq!(
22246 QuoteForm::Unquote.iac_forge_tag(),
22247 SexpShape::Unquote.label(),
22248 "unquote tag/label agreement"
22249 );
22250 // The intentional divergence — load-bearing for the iac-forge
22251 // canonical form vs the substrate's diagnostic label.
22252 assert_eq!(QuoteForm::UnquoteSplice.iac_forge_tag(), "unquote-splicing");
22253 assert_eq!(SexpShape::UnquoteSplice.label(), "unquote-splice");
22254 assert_ne!(
22255 QuoteForm::UnquoteSplice.iac_forge_tag(),
22256 SexpShape::UnquoteSplice.label(),
22257 "the two projections must disagree at UnquoteSplice — the CL canonical \
22258 form requires '-splicing' while the substrate's diagnostic label uses \
22259 the shorter '-splice'; consolidating them would break either side",
22260 );
22261 }
22262
22263 #[test]
22264 fn quote_form_from_iac_forge_tag_decodes_each_canonical_tag_to_its_variant() {
22265 // TYPED INVERSE CONTRACT: the four canonical CL tag literals
22266 // `"quote"` / `"quasiquote"` / `"unquote"` / `"unquote-splicing"`
22267 // decode through `QuoteForm::from_iac_forge_tag` to their exact
22268 // `QuoteForm` variant — the inbound iac-forge canonical-form
22269 // decode surface's per-arm truth table. Sibling posture to
22270 // `quote_form_iac_forge_tag_pins_canonical_lisp_tag_strings_for_every_variant`
22271 // on the OUTBOUND projection axis: that pin binds each variant to
22272 // its canonical tag; THIS pin binds each canonical tag to its
22273 // variant, closing the (outbound, inbound) roundtrip pair at ONE
22274 // typed method on the algebra rather than at TWO surfaces the
22275 // consumer would have to hand-roll independently.
22276 //
22277 // A regression that drifts ONE arm's inbound decode (a future
22278 // refactor that inlines the four-arm sweep and drops the
22279 // `"quasiquote"` arm, a byte-drifted rename that leaves the
22280 // outbound `iac_forge_tag()` unchanged but breaks the inverse)
22281 // fails-loudly here on the affected arm before any downstream
22282 // iac-forge canonical-form consumer surfaces the drift.
22283 assert_eq!(
22284 QuoteForm::from_iac_forge_tag("quote"),
22285 Some(QuoteForm::Quote),
22286 );
22287 assert_eq!(
22288 QuoteForm::from_iac_forge_tag("quasiquote"),
22289 Some(QuoteForm::Quasiquote),
22290 );
22291 assert_eq!(
22292 QuoteForm::from_iac_forge_tag("unquote"),
22293 Some(QuoteForm::Unquote),
22294 );
22295 assert_eq!(
22296 QuoteForm::from_iac_forge_tag("unquote-splicing"),
22297 Some(QuoteForm::UnquoteSplice),
22298 );
22299 }
22300
22301 #[test]
22302 fn quote_form_from_iac_forge_tag_round_trips_through_iac_forge_tag_for_every_variant() {
22303 // TYPED ROUNDTRIP CONTRACT: for every `qf` in `QuoteForm::ALL`,
22304 // `QuoteForm::from_iac_forge_tag(qf.iac_forge_tag()) == Some(qf)`
22305 // — the composition of the outbound projection with the inbound
22306 // inverse decoder yields the identity on the closed four-arm
22307 // domain. Sibling posture to
22308 // `quote_form_lead_char_round_trips_through_from_lead_char_for_every_variant`
22309 // one axis over on the reader-lead-char inverse decoder — both
22310 // pin the (forward, inverse) composition-identity at the closed
22311 // set's canonical carrier variants without relying on the
22312 // per-arm inbound truth table above (which pins the arm-by-arm
22313 // decoder mapping; this pin binds the closed-set-wide roundtrip
22314 // property that emerges from the arm mapping).
22315 //
22316 // A regression that breaks ONE variant's roundtrip (a future
22317 // refactor that drops `QuoteForm::Unquote` from `Self::ALL`
22318 // silently while leaving the outbound `iac_forge_tag()` arm
22319 // intact, an inbound decoder that returns `Some(Self::Quote)`
22320 // for an unrelated variant's tag) fails-loudly here through the
22321 // closed-set sweep, catching the drift on the affected variant.
22322 for &qf in QuoteForm::ALL.iter() {
22323 let outbound = qf.iac_forge_tag();
22324 let inbound = QuoteForm::from_iac_forge_tag(outbound);
22325 assert_eq!(
22326 inbound,
22327 Some(qf),
22328 "QuoteForm::{qf:?} — outbound iac_forge_tag `{outbound}` \
22329 failed to round-trip through from_iac_forge_tag",
22330 );
22331 }
22332 }
22333
22334 #[test]
22335 fn quote_form_from_iac_forge_tag_rejects_empty_input() {
22336 // EMPTY-INPUT REJECTION: the empty string `""` is structurally
22337 // outside the four-arm canonical CL tag closed set — no variant
22338 // projects to `""` through `iac_forge_tag`, so the inverse
22339 // decode rejects cleanly with `None`. Pins the empty-input
22340 // boundary case operators hit when a canonical-form field is
22341 // absent or blank but the decode is reached anyway (a
22342 // deserialization codepath that reads an empty tag slot, an
22343 // LSP quick-fix that surfaces an empty completion buffer).
22344 // Sibling posture to `parse_label_rejects_empty_input` on the
22345 // closed-set trait's parse-rejection axis one vocabulary over
22346 // (reader-punctuation) — both pin the empty-input rejection so
22347 // no future implementor can drift the decoder's empty-input
22348 // behavior accidentally.
22349 assert_eq!(QuoteForm::from_iac_forge_tag(""), None);
22350 }
22351
22352 #[test]
22353 fn quote_form_from_iac_forge_tag_is_case_sensitive() {
22354 // CASE-SENSITIVE CONTRACT: the four canonical CL tag literals
22355 // are the exact byte sequences the iac-forge canonical form
22356 // renders; case drift between the caller's input and the
22357 // canonical literal is a REJECTION, not a normalization. Pin
22358 // the case-sensitive contract across (a) an all-uppercase
22359 // drift (`"QUOTE"`), (b) a title-case drift (`"Quote"`), and
22360 // (c) an internal-uppercase drift on the multi-word tag
22361 // (`"Unquote-Splicing"`, `"unquote-Splicing"`) — the three
22362 // representative case-drift shapes future operator input
22363 // could reach.
22364 //
22365 // A regression that relaxes the decode to case-insensitive (an
22366 // overzealous normalization pass, an `eq_ignore_ascii_case`
22367 // introduction) silently subsumes the substrate-wide
22368 // case-sensitive convention that binds the iac-forge
22369 // canonical-form bytes to the SexpShape diagnostic-label bytes'
22370 // path-uniformity (the two vocabularies share three of four
22371 // arms byte-for-byte and disagree at UnquoteSplice — any
22372 // future case-insensitive relaxation on ONE axis would silently
22373 // bifurcate the two vocabularies' convention).
22374 assert_eq!(QuoteForm::from_iac_forge_tag("QUOTE"), None);
22375 assert_eq!(QuoteForm::from_iac_forge_tag("Quote"), None);
22376 assert_eq!(QuoteForm::from_iac_forge_tag("Unquote-Splicing"), None);
22377 assert_eq!(QuoteForm::from_iac_forge_tag("unquote-Splicing"), None);
22378 }
22379
22380 #[test]
22381 fn quote_form_from_iac_forge_tag_rejects_substrate_diagnostic_label_bytes() {
22382 // AXIS-BOUNDARY CONTRACT: the SHORTER substrate diagnostic
22383 // label `"unquote-splice"` (which `SexpShape::UnquoteSplice.label()`
22384 // renders) MUST reject through the iac-forge canonical-form
22385 // decoder — the CL canonical form requires the LONGER
22386 // `"unquote-splicing"` per the intentional divergence pinned by
22387 // `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`.
22388 // The two vocabularies are ORTHOGONAL axes on the SAME closed
22389 // four-arm outer set; a decoder keyed on the iac-forge axis
22390 // MUST reject an input on the diagnostic-label axis, or the
22391 // (canonical-form, diagnostic-label) axis-orthogonality
22392 // collapses silently.
22393 //
22394 // A regression that accepts the shorter substrate diagnostic
22395 // label at the iac-forge inbound decode (a hypothetical
22396 // "consolidation" that merges the two vocabularies at the
22397 // decoder boundary, a future decoder that walks BOTH
22398 // `SexpShape::LABELS` AND `Self::IAC_FORGE_TAGS`) would
22399 // silently bifurcate every iac-forge round-trip consumer:
22400 // the outbound `iac_forge_tag()` still renders
22401 // `"unquote-splicing"`, but the inbound decode accepts BOTH
22402 // spellings — a canonical form emitted with the shorter
22403 // substrate diagnostic label would re-decode as
22404 // `QuoteForm::UnquoteSplice` even though it was never a valid
22405 // iac-forge canonical form to begin with. Pin the rejection
22406 // explicitly so the axis boundary stays enforced at the
22407 // decoder itself, not just at the outbound projection.
22408 assert_eq!(QuoteForm::from_iac_forge_tag("unquote-splice"), None);
22409 }
22410
22411 #[test]
22412 fn quote_form_from_iac_forge_tag_rejects_non_canonical_tag_strings() {
22413 // GENERAL-REJECTION CONTRACT: inputs outside the four-arm
22414 // canonical CL tag image reject cleanly with `None`. Pins a
22415 // representative sweep across (a) arbitrary non-tag words that
22416 // share no substring with the canonical vocabulary
22417 // (`"hello"`, `"foo"`), (b) reader-punctuation bytes that
22418 // belong to the ORTHOGONAL `Self::PREFIXES` axis and MUST
22419 // route through `FromStr` (not this decoder) — the vocabulary
22420 // boundary the axis-orthogonality contract carries, (c) the
22421 // SexpShape diagnostic labels for non-quote-family arms
22422 // (`"symbol"`, `"list"`) which project to `None` through
22423 // `SexpShape::iac_forge_tag` and therefore MUST reject here
22424 // too.
22425 //
22426 // A regression that accepts a reader-punctuation byte here (a
22427 // future consolidated decoder that walks BOTH the prefix
22428 // vocabulary AND the iac-forge-tag vocabulary) would silently
22429 // subsume the [`FromStr`] surface's exclusive claim to the
22430 // reader-punctuation axis, breaking every consumer that binds
22431 // decoder identity to vocabulary axis. Pin the rejections
22432 // explicitly so the vocabulary axis stays enforced at the
22433 // decoder boundary itself, not just at the outbound projection.
22434 for input in [
22435 "hello", "foo",
22436 // Reader-punctuation vocabulary — belongs to
22437 // `Self::PREFIXES` / `Self::FromStr`, MUST reject here.
22438 "'", "`", ",", ",@",
22439 // SexpShape labels for non-quote-family shapes — no
22440 // iac-forge tag projection at these variants, MUST reject.
22441 "symbol", "list", "nil",
22442 ] {
22443 assert_eq!(
22444 QuoteForm::from_iac_forge_tag(input),
22445 None,
22446 "QuoteForm::from_iac_forge_tag({input:?}) accepted a \
22447 non-canonical iac-forge tag input — the decoder must \
22448 reject every string outside the four-arm canonical CL \
22449 tag image",
22450 );
22451 }
22452 }
22453
22454 #[test]
22455 fn quote_form_from_iac_forge_tag_composes_through_iac_forge_tags_array() {
22456 // COMPOSITION-LAW CONTRACT: sweeping the family-wide
22457 // `Self::IAC_FORGE_TAGS` array through `from_iac_forge_tag`
22458 // yields the parallel `Self::ALL` array element-wise —
22459 // `from_iac_forge_tag(Self::IAC_FORGE_TAGS[i]) ==
22460 // Some(Self::ALL[i])` for every `i in 0..4`. The composition
22461 // binds the two family-wide forced-arity arrays through the
22462 // typed inverse decoder at ONE typed sweep on the algebra,
22463 // matching the alignment law
22464 // `quote_form_iac_forge_tags_align_with_all_by_index` on the
22465 // outbound projection sibling.
22466 //
22467 // Enforces the (typed variant, canonical tag) forward-and-
22468 // back closure at the array level: a regression that drifts
22469 // ONE array's contents against the other (a future refactor
22470 // that reorders `Self::IAC_FORGE_TAGS` without reordering
22471 // `Self::ALL`, a rename that breaks the alignment) fails-
22472 // loudly here through the composition sweep before any
22473 // downstream `zip(ALL, IAC_FORGE_TAGS)` consumer with an
22474 // inbound decoder in hand surfaces the misalignment.
22475 for (i, &tag) in QuoteForm::IAC_FORGE_TAGS.iter().enumerate() {
22476 let decoded = QuoteForm::from_iac_forge_tag(tag);
22477 let expected = QuoteForm::ALL[i];
22478 assert_eq!(
22479 decoded,
22480 Some(expected),
22481 "QuoteForm::from_iac_forge_tag(IAC_FORGE_TAGS[{i}] = {tag:?}) \
22482 decoded to {decoded:?}, expected Some({expected:?}) \
22483 (the composition of IAC_FORGE_TAGS with from_iac_forge_tag \
22484 must yield ALL element-wise)",
22485 );
22486 }
22487 }
22488
22489 #[test]
22490 fn quote_form_from_iac_forge_tag_is_injective_on_canonical_domain() {
22491 // INJECTIVITY CONTRACT: distinct canonical CL tags in
22492 // `Self::IAC_FORGE_TAGS` decode to distinct typed variants
22493 // through `from_iac_forge_tag` — the inverse decoder is
22494 // injective on the closed four-arm canonical-tag domain.
22495 // Sibling posture to the outbound
22496 // `quote_form_iac_forge_tags_pairwise_distinct` on the same
22497 // closed set: that pin asserts the four canonical-tag literals
22498 // are pairwise distinct; THIS pin asserts the DECODED variants
22499 // are also pairwise distinct — the pair together enforces
22500 // BOTH sides of the two-way injectivity contract that the
22501 // (typed variant, canonical tag) bijection carries on the
22502 // closed set.
22503 //
22504 // A regression that decodes two distinct canonical tags to
22505 // the same typed variant (a future refactor that collapses
22506 // `Quasiquote` and `Quote` decode arms silently, an off-by-
22507 // one in the linear sweep that returns the wrong variant for
22508 // ONE tag) fails-loudly here at the decoded-set cardinality
22509 // check before any downstream consumer surfaces the decoded-
22510 // side collapse.
22511 let decoded: Vec<QuoteForm> = QuoteForm::IAC_FORGE_TAGS
22512 .iter()
22513 .filter_map(|tag| QuoteForm::from_iac_forge_tag(tag))
22514 .collect();
22515 assert_eq!(
22516 decoded.len(),
22517 QuoteForm::ALL.len(),
22518 "QuoteForm::from_iac_forge_tag failed to decode one or more \
22519 canonical tags — the composition of IAC_FORGE_TAGS with \
22520 from_iac_forge_tag lost {} arms (expected {} — the closed \
22521 set's cardinality)",
22522 QuoteForm::ALL.len() - decoded.len(),
22523 QuoteForm::ALL.len(),
22524 );
22525 for i in 0..decoded.len() {
22526 for j in (i + 1)..decoded.len() {
22527 assert_ne!(
22528 decoded[i], decoded[j],
22529 "QuoteForm::from_iac_forge_tag is not injective on the \
22530 canonical-tag domain — decoded[{i}] ({:?}) and \
22531 decoded[{j}] ({:?}) collide",
22532 decoded[i], decoded[j],
22533 );
22534 }
22535 }
22536 }
22537
22538 #[test]
22539 fn quote_form_sexp_shape_pins_canonical_shape_identity_for_every_variant() {
22540 // CLOSED-SET SHAPE-PROJECTION CONTRACT: each `QuoteForm` variant
22541 // projects to its matching `SexpShape` variant — load-bearing for
22542 // the (Sexp variant, SexpShape variant) pairing the substrate's
22543 // outer-shape projection `domain::sexp_shape` routes through.
22544 // Sibling-arm sweep so the four pairings stay load-bearing under
22545 // reordering refactors. A regression that drifts ONE arm (e.g.
22546 // routes `QuoteForm::Quote` to `SexpShape::Quasiquote`) surfaces
22547 // here immediately rather than as a silent operator-facing
22548 // diagnostic drift at every `LispError::TypeMismatch.got` slot
22549 // for a quote-family witness.
22550 use crate::error::SexpShape;
22551 assert_eq!(QuoteForm::Quote.sexp_shape(), SexpShape::Quote);
22552 assert_eq!(QuoteForm::Quasiquote.sexp_shape(), SexpShape::Quasiquote);
22553 assert_eq!(QuoteForm::Unquote.sexp_shape(), SexpShape::Unquote);
22554 assert_eq!(
22555 QuoteForm::UnquoteSplice.sexp_shape(),
22556 SexpShape::UnquoteSplice
22557 );
22558 }
22559
22560 #[test]
22561 fn quote_form_sexp_shape_composes_with_label_for_canonical_short_diagnostic_string() {
22562 // COMPOSITION-LAW CONTRACT: `qf.sexp_shape().label()` is the
22563 // canonical short diagnostic string for the quote-family marker
22564 // — `"quote"`, `"quasiquote"`, `"unquote"`, `"unquote-splice"`.
22565 // The composition law binds the substrate's typed marker
22566 // (`QuoteForm`) to its diagnostic surface (`SexpShape::label`)
22567 // through ONE algebra so a future change to either projection's
22568 // label (e.g. a substrate-wide rename of `"unquote-splice"` to
22569 // `"splice"`) rides through the typed composition rather than
22570 // requiring an inline match at every diagnostic-construction
22571 // site that previously hand-paired the marker with its label.
22572 // Pin the short labels here — DISTINCT from the iac-forge tag's
22573 // `"unquote-splicing"` (load-bearing for the boundary distinction
22574 // already pinned by
22575 // `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`).
22576 assert_eq!(QuoteForm::Quote.sexp_shape().label(), "quote");
22577 assert_eq!(QuoteForm::Quasiquote.sexp_shape().label(), "quasiquote");
22578 assert_eq!(QuoteForm::Unquote.sexp_shape().label(), "unquote");
22579 assert_eq!(
22580 QuoteForm::UnquoteSplice.sexp_shape().label(),
22581 "unquote-splice"
22582 );
22583 }
22584
22585 #[test]
22586 fn quote_form_label_projects_each_variant_to_canonical_diagnostic_label() {
22587 // PER-ARM CONTRACT: pin the outer-`QuoteForm` `Self::label`
22588 // projection produces the FOUR canonical short diagnostic labels
22589 // byte-for-byte across every reachable quote-family variant.
22590 // Pre-lift the outer-`QuoteForm` diagnostic-label projection had
22591 // no typed primitive on the marker algebra — a consumer with a
22592 // `QuoteForm` in hand wanting the canonical short label had to
22593 // spell the two-step composition `qf.sexp_shape().label()` at
22594 // every callsite (a shape pinned as a load-bearing composition
22595 // law by `quote_form_sexp_shape_composes_with_label_for_canonical_short_diagnostic_string`
22596 // one arm above), OR go through `qf.wrap(inner).type_name()`
22597 // which wraps and projects for no runtime purpose. Post-lift the
22598 // FOUR arms bind at ONE typed projection on the outer-`QuoteForm`
22599 // algebra that routes through `SexpShape::label` — the
22600 // (QuoteForm variant, label string) pairing binds at ONE typed
22601 // algebra composition spanning THREE typed layers (`QuoteForm`
22602 // → `SexpShape` → `&'static str`).
22603 //
22604 // Sibling-shape pin to
22605 // `atom_label_projects_each_variant_to_canonical_diagnostic_label`
22606 // one algebra layer down (outer-`Atom` label pin) and
22607 // `sexp_type_name_covers_every_variant` one algebra layer up
22608 // (outer-`Sexp` type_name pin). A regression that drifts ONE
22609 // arm's mapping (e.g. renaming `"unquote-splice"` to `"splice"`
22610 // inline here, dropping the `Unquote → "unquote"` boundary
22611 // rename) fails-loudly at THIS test AND the sibling
22612 // `SexpShape::label` per-arm pin.
22613 assert_eq!(QuoteForm::Quote.label(), "quote");
22614 assert_eq!(QuoteForm::Quasiquote.label(), "quasiquote");
22615 assert_eq!(QuoteForm::Unquote.label(), "unquote");
22616 assert_eq!(QuoteForm::UnquoteSplice.label(), "unquote-splice");
22617 }
22618
22619 #[test]
22620 fn quote_form_label_composes_through_sexp_shape_label_for_every_variant() {
22621 // COMPOSITION-LAW CONTRACT: `qf.label() == qf.sexp_shape().label()`
22622 // for every reachable quote-family marker — the outer-`QuoteForm`
22623 // label projection is structurally derived through `Self::sexp_shape`
22624 // + `SexpShape::label` rather than through a parallel four-arm
22625 // inline match on the outer-`QuoteForm` algebra. Pin the
22626 // composition law so a future refactor that re-inlines the four
22627 // quote-family literals here (and gains its own drift surface
22628 // separate from the `SexpShape::label` canonical site) surfaces
22629 // immediately. The pointer-equality check pins the composition
22630 // produces the SAME `&'static str` (not just a byte-equal copy)
22631 // for every variant — proof the routing hits ONE static literal
22632 // site (`SexpShape::label` via `QuoteForm::sexp_shape().label()`)
22633 // rather than a parallel inline table on the outer-`QuoteForm`
22634 // algebra.
22635 //
22636 // Sibling-shape pin to
22637 // `atom_label_composes_through_kind_label_for_every_variant` on
22638 // the outer-`Atom` value / `AtomKind` marker pair and
22639 // `sexp_type_name_method_composes_through_shape_label_for_every_outer_shape`
22640 // on the outer-`Sexp` value / `SexpShape` marker pair. The three
22641 // routing pins jointly enforce the (outer-value, canonical label)
22642 // pairing stays a full three-layer typed composition on every
22643 // typed-value algebra rather than degrading to a per-layer inline
22644 // literal table.
22645 for qf in QuoteForm::ALL {
22646 let via_label = qf.label();
22647 let via_composition = qf.sexp_shape().label();
22648 assert_eq!(
22649 via_label, via_composition,
22650 "QuoteForm::label() must route through self.sexp_shape().label() \
22651 for {qf:?} — drift here means the lift was reverted to inline arms",
22652 );
22653 assert!(
22654 std::ptr::eq(via_label.as_ptr(), via_composition.as_ptr()),
22655 "QuoteForm::label() must return the SAME `&'static str` as \
22656 self.sexp_shape().label() for {qf:?} — pointer drift means \
22657 the lift composes through a parallel literal table rather \
22658 than routing into the canonical SexpShape::label site",
22659 );
22660 }
22661 }
22662
22663 #[test]
22664 fn quote_form_label_agrees_with_sexp_type_name_at_every_quote_form_arm() {
22665 // CROSS-ALGEBRA AGREEMENT CONTRACT: for every quote-family marker
22666 // `qf` and every inner body `inner`, `qf.label() ==
22667 // qf.wrap(inner.clone()).type_name()`. The agreement is a TYPED
22668 // CONSEQUENCE of the two typed compositions —
22669 // `qf.wrap(inner).type_name()` routes through `Sexp::shape()`'s
22670 // quote-family arms which compose with `SexpShape::label`
22671 // byte-for-byte with `qf.sexp_shape().label()` (which itself IS
22672 // the body of `qf.label()`). A regression that drifts either side
22673 // of the cross-algebra bridge (an outer-`QuoteForm` label
22674 // re-inlined onto a different literal, an outer-`Sexp` quote-arm
22675 // re-routed through a stale shape projection, a
22676 // `QuoteForm::sexp_shape` arm that swaps two markers) fails-
22677 // loudly here rather than as a silent operator-facing diagnostic
22678 // drift at every consumer that pattern-matches on the outer-
22679 // `Sexp` label vs the outer-`QuoteForm` label independently.
22680 //
22681 // Sibling posture to
22682 // `atom_label_agrees_with_sexp_type_name_at_every_atom_arm` on
22683 // the atomic-payload carving — that pin binds the outer-value-
22684 // level vocabulary containment (`Atom::label ==
22685 // Sexp::Atom(_).type_name()`), this pin binds the same
22686 // containment on the quote-family carving (`QuoteForm::label ==
22687 // QuoteForm::wrap(_).type_name()`) so the THREE-layer typed
22688 // composition on the outer-`QuoteForm` algebra and the FOUR-
22689 // layer typed composition on the outer-`Sexp` algebra agree at
22690 // their common quote-family arms.
22691 let inner = Sexp::symbol("x");
22692 for qf in QuoteForm::ALL {
22693 let via_quote_form = qf.label();
22694 let via_sexp = qf.wrap(inner.clone()).type_name();
22695 assert_eq!(
22696 via_quote_form, via_sexp,
22697 "QuoteForm::label() must agree with QuoteForm::wrap(_).type_name() \
22698 for {qf:?} — cross-algebra label drift at the quote-family arms \
22699 would fracture the typed diagnostic vocabulary between the \
22700 outer-QuoteForm and outer-Sexp algebras",
22701 );
22702 assert!(
22703 std::ptr::eq(via_quote_form.as_ptr(), via_sexp.as_ptr()),
22704 "QuoteForm::label() must return the SAME `&'static str` as \
22705 QuoteForm::wrap(_).type_name() for {qf:?} — pointer drift means \
22706 one algebra layer re-inlined the literal rather than routing \
22707 into the canonical `SexpShape::label` site",
22708 );
22709 }
22710 }
22711
22712 #[test]
22713 fn quote_form_label_diverges_from_iac_forge_tag_for_unquote_splice() {
22714 // BOUNDARY-DISTINCT CONTRACT: at the `UnquoteSplice` arm,
22715 // `qf.label() == "unquote-splice"` (the substrate's diagnostic
22716 // label idiom) while `qf.iac_forge_tag() == "unquote-splicing"`
22717 // (the Common-Lisp canonical form, load-bearing for canonical-
22718 // form round-trip with the iac-forge ecosystem). The two
22719 // projections key the SAME closed-set on TWO distinct boundaries
22720 // — pinning the divergence on the NEW typed peer documents the
22721 // intent: a future "consolidation" PR that homogenizes `label`
22722 // and `iac_forge_tag` at the `UnquoteSplice` arm would silently
22723 // break either the iac-forge canonical-form round-trip OR the
22724 // operator-facing diagnostic surface. Sibling-arm posture to
22725 // `quote_form_iac_forge_tag_diverges_from_sexp_shape_label_for_unquote_splice`
22726 // which pinned the divergence at the `qf.sexp_shape().label()`
22727 // composition; this pin lifts the divergence contract onto the
22728 // NEW `QuoteForm::label` typed peer. The three other variants
22729 // (Quote, Quasiquote, Unquote) DO match across both projections
22730 // — pin that path-uniformity too so a regression that drifts one
22731 // of the three matched arms surfaces immediately.
22732 assert_eq!(
22733 QuoteForm::Quote.iac_forge_tag(),
22734 QuoteForm::Quote.label(),
22735 "quote tag/label agreement",
22736 );
22737 assert_eq!(
22738 QuoteForm::Quasiquote.iac_forge_tag(),
22739 QuoteForm::Quasiquote.label(),
22740 "quasiquote tag/label agreement",
22741 );
22742 assert_eq!(
22743 QuoteForm::Unquote.iac_forge_tag(),
22744 QuoteForm::Unquote.label(),
22745 "unquote tag/label agreement",
22746 );
22747 // The intentional divergence — load-bearing for the iac-forge
22748 // canonical form vs the substrate's diagnostic label.
22749 assert_eq!(QuoteForm::UnquoteSplice.iac_forge_tag(), "unquote-splicing");
22750 assert_eq!(QuoteForm::UnquoteSplice.label(), "unquote-splice");
22751 assert_ne!(
22752 QuoteForm::UnquoteSplice.iac_forge_tag(),
22753 QuoteForm::UnquoteSplice.label(),
22754 "the two projections must disagree at UnquoteSplice — the CL canonical \
22755 form requires '-splicing' while the substrate's diagnostic label uses \
22756 the shorter '-splice'; consolidating them would break either side",
22757 );
22758 }
22759
22760 #[test]
22761 fn quote_form_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte() {
22762 // ALIAS CONTRACT: pin every one of the four per-role
22763 // `pub const QuoteForm::*_LABEL` aliases equals the corresponding
22764 // `pub const SexpShape::*_LABEL` byte-for-byte — so the QuoteForm
22765 // ⊂ SexpShape marker-vocabulary containment routes through the
22766 // typed `pub const QuoteForm::V_LABEL: &'static str =
22767 // SexpShape::V_LABEL` alias chain rather than through two
22768 // independent literal-discipline sites. A regression that renames
22769 // the SexpShape side without updating the QuoteForm alias
22770 // pointing at it fails-loudly here with the exact axis identified
22771 // (QUOTE / QUASIQUOTE / UNQUOTE / UNQUOTE_SPLICE); a regression
22772 // that re-inlines the QuoteForm constant to a fresh literal still
22773 // passes this pin but loses the alias-chain typing (which is what
22774 // `quote_form_label_arms_route_through_per_role_labels_for_every_variant`
22775 // + `quote_form_labels_align_with_all_by_index` catch in
22776 // combination).
22777 //
22778 // Sibling-shape pin to
22779 // `atom_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
22780 // on the peer 6-of-12 atomic-payload carving and
22781 // `structural_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
22782 // on the peer 2-of-12 structural-residual carving — this pin
22783 // closes the fourth and final SexpShape sub-carving's alias-chain
22784 // contract at the exact same shape.
22785 assert_eq!(QuoteForm::QUOTE_LABEL, SexpShape::QUOTE_LABEL);
22786 assert_eq!(QuoteForm::QUASIQUOTE_LABEL, SexpShape::QUASIQUOTE_LABEL);
22787 assert_eq!(QuoteForm::UNQUOTE_LABEL, SexpShape::UNQUOTE_LABEL);
22788 assert_eq!(
22789 QuoteForm::UNQUOTE_SPLICE_LABEL,
22790 SexpShape::UNQUOTE_SPLICE_LABEL,
22791 );
22792 }
22793
22794 #[test]
22795 fn quote_form_label_arms_route_through_per_role_labels_for_every_variant() {
22796 // PATH-UNIFORMITY: `QuoteForm::V.label()` MUST equal the per-role
22797 // `pub const QuoteForm::V_LABEL` for every `v: QuoteForm`. Pre-
22798 // lift the four quote-family marker labels were reachable through
22799 // `QuoteForm::label` (the composition `self.sexp_shape().label()`
22800 // — routing into `SexpShape::*_LABEL`) OR through direct
22801 // `SexpShape::*_LABEL` reach-across; post-lift each variant's
22802 // canonical bytes are reachable through the per-role
22803 // `QuoteForm::*_LABEL` alias too. Pin the byte-equality between
22804 // the runtime projection and the compile-time alias so a
22805 // regression that renames the alias without updating the arm (or
22806 // vice versa) fails-loudly at the exact axis.
22807 //
22808 // Sibling-shape pin to
22809 // `atom_kind_label_arms_route_through_per_role_labels_for_every_variant`
22810 // and
22811 // `structural_kind_label_arms_route_through_per_role_labels_for_every_variant`
22812 // — those pin the peer 6-of-12 and 2-of-12 sub-carvings; this pin
22813 // binds the QuoteForm 4-of-12 subset algebra's per-role aliases
22814 // against `QuoteForm::label`'s composition-routed arms so the
22815 // four quote-family marker labels project through ONE aliased
22816 // typed source of truth per role rather than through per-consumer
22817 // inline literals.
22818 assert_eq!(QuoteForm::Quote.label(), QuoteForm::QUOTE_LABEL);
22819 assert_eq!(QuoteForm::Quasiquote.label(), QuoteForm::QUASIQUOTE_LABEL);
22820 assert_eq!(QuoteForm::Unquote.label(), QuoteForm::UNQUOTE_LABEL);
22821 assert_eq!(
22822 QuoteForm::UnquoteSplice.label(),
22823 QuoteForm::UNQUOTE_SPLICE_LABEL,
22824 );
22825 }
22826
22827 #[test]
22828 fn quote_form_labels_has_expected_cardinality() {
22829 // Cardinality pin: `LABELS.len() == 4` matches `ALL.len()` so a
22830 // refactor that loosens the type to `&'static [&'static str]`
22831 // fails HERE (the `[_; 4]` slot cannot be sliced silently), and
22832 // a variant added to `ALL` without a matching `LABELS` row fails
22833 // the pair-arity gate at the array literal itself before this
22834 // test even runs. The pin doubles as an operator-visible mark of
22835 // the family's cardinality across the substrate — four quote-
22836 // family markers, matching the four-arm carving of the parent
22837 // `SexpShape::LABELS` (the quote-family subset of the twelve
22838 // canonical outer-shape labels).
22839 assert_eq!(QuoteForm::LABELS.len(), 4);
22840 assert_eq!(QuoteForm::LABELS.len(), QuoteForm::ALL.len());
22841 }
22842
22843 #[test]
22844 fn quote_form_labels_align_with_all_by_index() {
22845 // ALIGNMENT PIN: sweep `LABELS[i] == ALL[i].label()` so any
22846 // `zip(ALL, LABELS)` consumer reads a coherent (variant, label)
22847 // pair off ONE forced-arity array pair. The declaration-order
22848 // pin makes a family-wide consumer that walks the ALL / LABELS
22849 // pair in lockstep (an LSP completion bar keyed on
22850 // `QuoteForm::LABELS`, a Sekiban metric emitter labeling
22851 // `tatara_lisp_quote_family_label_total{label}` by the per-index
22852 // label) read one canonical (variant, bytes) pair per slot
22853 // rather than routing through per-consumer paired-iteration. A
22854 // regression that reorders LABELS without also reordering ALL
22855 // (or vice versa) fails-loudly at the exact index that drifted.
22856 assert_eq!(QuoteForm::LABELS.len(), QuoteForm::ALL.len());
22857 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
22858 assert_eq!(
22859 QuoteForm::LABELS[i],
22860 qf.label(),
22861 "QuoteForm::LABELS[{i}] `{lbl}` drifted from \
22862 QuoteForm::ALL[{i}].label() `{via_variant}` — the \
22863 canonical ALL ordering and the LABELS ordering must \
22864 match element-wise",
22865 lbl = QuoteForm::LABELS[i],
22866 via_variant = qf.label(),
22867 );
22868 }
22869 }
22870
22871 #[test]
22872 fn quote_form_labels_pairwise_distinct() {
22873 // 4x4 pairwise sweep so a collision between any two labels
22874 // (which would silently degrade two distinct quote-family
22875 // markers to the SAME diagnostic bytes and violate the closed-
22876 // set FromStr round-trip through `SexpShape::from_str`) fails-
22877 // loudly at the exact pair. Distinctness is already enforced
22878 // structurally at the parent superset by
22879 // `sexp_shape_labels_pairwise_distinct` (the twelve-variant
22880 // sweep), but this pin is a secondary guard focused on the per-
22881 // role `pub const` surface of the QuoteForm 4-of-12 subset
22882 // directly rather than the runtime projection through
22883 // `SexpShape::label`.
22884 for (i, a) in QuoteForm::LABELS.iter().enumerate() {
22885 for (j, b) in QuoteForm::LABELS.iter().enumerate() {
22886 if i == j {
22887 continue;
22888 }
22889 assert_ne!(
22890 a, b,
22891 "QuoteForm::LABELS[{i}] ({a:?}) collides with \
22892 QuoteForm::LABELS[{j}] ({b:?}) — two distinct \
22893 quote-family markers cannot share diagnostic bytes",
22894 );
22895 }
22896 }
22897 }
22898
22899 #[test]
22900 fn quote_form_labels_match_sexp_shape_labels_element_wise_via_alias_chain() {
22901 // CROSS-AXIS PIN: `QuoteForm::LABELS[i] ==
22902 // QuoteForm::ALL[i].sexp_shape().label()` for every index. Closes
22903 // the alias-chain identity at the family-wide array level:
22904 // LABELS is NOT a fresh literal table but the projection of ALL
22905 // through the composition, materialized once at declaration time
22906 // through the four per-role aliases. A regression that re-inlines
22907 // LABELS to fresh literals (or that re-inlines each per-role
22908 // constant off the aliased `SexpShape::*_LABEL` source of truth)
22909 // still passes the pairwise-distinct + cardinality + alignment
22910 // pins but drifts the alias chain — this pin catches that drift.
22911 //
22912 // Sibling-shape pin to
22913 // `atom_kind_labels_match_sexp_shape_labels_element_wise_via_alias_chain`
22914 // and
22915 // `structural_kind_labels_match_sexp_shape_labels_element_wise_via_alias_chain`
22916 // — those pin the peer 6-of-12 and 2-of-12 sub-carvings' alias
22917 // chains through their `SexpShape` parents; this pin closes the
22918 // fourth and final SexpShape sub-carving's alias-chain identity
22919 // at the same shape.
22920 assert_eq!(QuoteForm::LABELS.len(), QuoteForm::ALL.len());
22921 for (i, qf) in QuoteForm::ALL.iter().enumerate() {
22922 let via_composition = qf.sexp_shape().label();
22923 assert_eq!(
22924 QuoteForm::LABELS[i],
22925 via_composition,
22926 "QuoteForm::LABELS[{i}] `{lbl}` drifted from \
22927 QuoteForm::ALL[{i}].sexp_shape().label() `{via}` — \
22928 the alias-chain composition law `LABELS[i] == \
22929 ALL[i].sexp_shape().label()` binds the family-wide \
22930 array to the composition through sexp_shape + \
22931 SexpShape::label; a drift here means the per-role \
22932 aliases were re-inlined off their SexpShape source of \
22933 truth",
22934 lbl = QuoteForm::LABELS[i],
22935 via = via_composition,
22936 );
22937 }
22938 }
22939
22940 #[test]
22941 fn quote_form_sexp_shape_paired_with_as_quote_form_preserves_pre_lift_pairing_for_every_sexp() {
22942 // PATH-UNIFORMITY CONTRACT: the (Sexp variant, SexpShape variant)
22943 // pairing the pre-lift `sexp_shape` arms encoded inline is now
22944 // structurally derived via
22945 // `s.as_quote_form().map(|(qf, _)| qf.sexp_shape())` for every
22946 // quote-family `Sexp` shape. Pin the derivation against the
22947 // pre-lift pairing across all four quote-family wrapper variants
22948 // so a regression that drifts ONE side of the typed algebra
22949 // (e.g. a `QuoteForm::Quote → SexpShape::Quasiquote` typo, or a
22950 // `Sexp::as_quote_form` arm that swaps two markers) surfaces
22951 // immediately. Non-quote-family shapes project to `None` from
22952 // `as_quote_form`, which the assertion arm skips — the typed
22953 // closed-set partition is load-bearing for the early-return
22954 // shape of the lifted `domain::sexp_shape`.
22955 use crate::error::SexpShape;
22956 let cases: &[(&str, Sexp, SexpShape)] = &[
22957 (
22958 "quote",
22959 Sexp::Quote(Box::new(Sexp::symbol("x"))),
22960 SexpShape::Quote,
22961 ),
22962 (
22963 "quasiquote",
22964 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
22965 SexpShape::Quasiquote,
22966 ),
22967 (
22968 "unquote",
22969 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
22970 SexpShape::Unquote,
22971 ),
22972 (
22973 "unquote-splice",
22974 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
22975 SexpShape::UnquoteSplice,
22976 ),
22977 ];
22978 for (label, sexp, expected_shape) in cases {
22979 let (qf, _) = sexp
22980 .as_quote_form()
22981 .unwrap_or_else(|| panic!("{label} must project through as_quote_form"));
22982 assert_eq!(
22983 qf.sexp_shape(),
22984 *expected_shape,
22985 "{label} drifted from typed (QuoteForm, SexpShape) pairing"
22986 );
22987 }
22988 }
22989
22990 #[test]
22991 fn as_unquote_derives_from_as_quote_form_composed_with_subset_gate() {
22992 // Path-uniformity: `Sexp::as_unquote` is now derived from
22993 // `as_quote_form().and_then(|(qf, inner)| qf.as_unquote_form()
22994 // .map(|uf| (uf, inner)))`. Pin that the derived semantic
22995 // agrees with the pre-lift arm-based one across the closed
22996 // Sexp variant set — every shape's projection through
22997 // `as_unquote` must equal the manual composition through
22998 // `as_quote_form` + `QuoteForm::as_unquote_form`. A regression
22999 // that drifts ONE projection's posture from the composition
23000 // becomes a typed test failure.
23001 let shapes: Vec<(&str, Sexp)> = vec![
23002 ("nil", Sexp::Nil),
23003 ("symbol", Sexp::symbol("x")),
23004 ("keyword", Sexp::keyword("k")),
23005 ("string", Sexp::string("s")),
23006 ("int", Sexp::int(7)),
23007 ("float", Sexp::float(2.5)),
23008 ("bool", Sexp::boolean(true)),
23009 ("empty list", Sexp::List(vec![])),
23010 ("non-empty list", Sexp::List(vec![Sexp::symbol("op")])),
23011 ("quote", Sexp::Quote(Box::new(Sexp::symbol("x")))),
23012 ("quasiquote", Sexp::Quasiquote(Box::new(Sexp::symbol("x")))),
23013 ("unquote", Sexp::Unquote(Box::new(Sexp::symbol("x")))),
23014 (
23015 "unquote-splice",
23016 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
23017 ),
23018 ];
23019 for (label, sexp) in &shapes {
23020 let via_direct = sexp.as_unquote();
23021 let via_composed = sexp
23022 .as_quote_form()
23023 .and_then(|(qf, inner)| qf.as_unquote_form().map(|uf| (uf, inner)));
23024 assert_eq!(
23025 via_direct, via_composed,
23026 "as_unquote drifted from composed as_quote_form+as_unquote_form at {label}"
23027 );
23028 }
23029 }
23030
23031 #[test]
23032 fn hash_for_sexp_structural_arms_route_through_structural_kind_hash_discriminator() {
23033 // CACHE-KEY CONTRACT (Hash side, structural axis): pin that
23034 // the lifted `Hash for Sexp` impl produces byte-identical
23035 // hashes for the two structural-residual arms (`Sexp::Nil`,
23036 // `Sexp::List(_)`) as the pre-lift implementation, routing
23037 // through `StructuralKind::hash_discriminator` so the
23038 // (Sexp variant, cache-key byte) pairing is structurally
23039 // bound to the algebra rather than threaded through inline
23040 // `0u8` / `2u8` literals. We compute the expected hash via a
23041 // SECOND hasher that manually drives the pre-lift `<discr>
23042 // .hash(h); <rest>.hash(h)` sequence, then compare. A
23043 // regression that drifts the discriminator (e.g. renumbers
23044 // `StructuralKind::List` to `1u8` and collides with the
23045 // atomic-carve outer marker byte) OR re-orders the (discr,
23046 // rest) sequence surfaces here as a hash-value mismatch.
23047 // Sibling arm-sweep to
23048 // `hash_for_sexp_preserves_legacy_quote_family_discriminator_bytes`
23049 // on the quote-family axis (four wrapper variants) — the
23050 // three closed-set carvings' hash arms all route through
23051 // ONE typed method per carving, and this pin binds the
23052 // structural-residual arm's post-lift shape against the
23053 // pre-lift byte-stream.
23054 use crate::error::StructuralKind;
23055 use std::collections::hash_map::DefaultHasher;
23056 use std::hash::{Hash, Hasher};
23057 // (label, sexp, expected-first-discr-byte, extra-hash-sequence
23058 // closure that drives the residual hash body after the
23059 // discriminator byte)
23060 #[allow(clippy::type_complexity)]
23061 let cases: [(&str, Sexp, u8, Box<dyn Fn(&mut DefaultHasher)>); 2] = [
23062 ("nil", Sexp::Nil, 0u8, Box::new(|_h: &mut DefaultHasher| {})),
23063 (
23064 "list",
23065 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
23066 2u8,
23067 Box::new(|h: &mut DefaultHasher| {
23068 let items = vec![Sexp::symbol("a"), Sexp::int(1)];
23069 items.len().hash(h);
23070 for i in &items {
23071 i.hash(h);
23072 }
23073 }),
23074 ),
23075 ];
23076 for (label, sexp, expected_discr, extra) in &cases {
23077 let mut via_impl = DefaultHasher::new();
23078 sexp.hash(&mut via_impl);
23079
23080 let mut via_legacy = DefaultHasher::new();
23081 expected_discr.hash(&mut via_legacy);
23082 extra(&mut via_legacy);
23083
23084 assert_eq!(
23085 via_impl.finish(),
23086 via_legacy.finish(),
23087 "Hash for Sexp drifted from legacy (discr={expected_discr}, rest) sequence at {label}",
23088 );
23089 }
23090 // Composition pin: pointer-independent structural equality —
23091 // the discriminator byte value MUST agree between the typed
23092 // projection and the pre-lift literal, so a regression that
23093 // re-inlines the two arm literals as a parallel match-table
23094 // (`Sexp::Nil => 0u8`, `Sexp::List(_) => 2u8`) still passes
23095 // the hash-value sweep above but drifts if the future
23096 // `StructuralKind::hash_discriminator` is re-numbered — this
23097 // pin binds the composition IDENTITY (not just the value
23098 // equality) between the outer `Hash for Sexp` body and the
23099 // typed algebra.
23100 assert_eq!(StructuralKind::Nil.hash_discriminator(), 0u8);
23101 assert_eq!(StructuralKind::List.hash_discriminator(), 2u8);
23102 }
23103
23104 #[test]
23105 fn hash_for_sexp_preserves_legacy_quote_family_discriminator_bytes() {
23106 // CACHE-KEY CONTRACT (Hash side): pin that the lifted
23107 // `Hash for Sexp` impl produces byte-identical hashes for the
23108 // four quote-family variants as the pre-lift implementation.
23109 // We compute the expected hash via a SECOND hasher that
23110 // manually drives the pre-lift `<discr>.hash(h); inner.hash(h)`
23111 // sequence, then compare. A regression that drifts the
23112 // discriminator OR re-orders the (discr, inner) sequence
23113 // surfaces here as a hash-value mismatch.
23114 use std::collections::hash_map::DefaultHasher;
23115 let inner = Sexp::symbol("payload");
23116 for (label, sexp, expected_discr) in [
23117 ("quote", Sexp::Quote(Box::new(inner.clone())), 3u8),
23118 ("quasiquote", Sexp::Quasiquote(Box::new(inner.clone())), 4u8),
23119 ("unquote", Sexp::Unquote(Box::new(inner.clone())), 5u8),
23120 (
23121 "unquote-splice",
23122 Sexp::UnquoteSplice(Box::new(inner.clone())),
23123 6u8,
23124 ),
23125 ] {
23126 let mut via_impl = DefaultHasher::new();
23127 sexp.hash(&mut via_impl);
23128
23129 let mut via_legacy = DefaultHasher::new();
23130 expected_discr.hash(&mut via_legacy);
23131 inner.hash(&mut via_legacy);
23132
23133 assert_eq!(
23134 via_impl.finish(),
23135 via_legacy.finish(),
23136 "Hash for Sexp drifted from legacy (discr={expected_discr}, inner) sequence at {label}"
23137 );
23138 }
23139 }
23140
23141 #[test]
23142 fn sexp_hash_discriminator_pins_legacy_outer_cache_key_bytes() {
23143 // CACHE-KEY CONTRACT: pre-lift `Hash for Sexp` used the literal
23144 // byte values 0/1/2 for Nil/Atom/List AND delegated 3/4/5/6 for
23145 // Quote/Quasiquote/Unquote/UnquoteSplice through
23146 // `QuoteForm::hash_discriminator`. The macro-expansion cache
23147 // (`Expander::cache`) keys on Hash; ANY change to a discriminator
23148 // byte silently invalidates every cached expansion across the
23149 // substrate. Pin the seven legacy values explicitly so a
23150 // regression that re-numbers them surfaces immediately — the
23151 // outer-`Sexp` algebra MUST preserve the prior byte mapping bit-
23152 // for-bit. Sibling posture to
23153 // `atom_kind_hash_discriminator_pins_legacy_atom_cache_key_bytes`
23154 // and `quote_form_hash_discriminator_pins_legacy_cache_key_bytes`
23155 // on the two sub-carvings.
23156 assert_eq!(Sexp::Nil.hash_discriminator(), 0);
23157 assert_eq!(Sexp::symbol("x").hash_discriminator(), 1);
23158 assert_eq!(Sexp::keyword("k").hash_discriminator(), 1);
23159 assert_eq!(Sexp::string("s").hash_discriminator(), 1);
23160 assert_eq!(Sexp::int(7).hash_discriminator(), 1);
23161 assert_eq!(Sexp::float(2.5).hash_discriminator(), 1);
23162 assert_eq!(Sexp::boolean(true).hash_discriminator(), 1);
23163 assert_eq!(Sexp::List(vec![]).hash_discriminator(), 2);
23164 assert_eq!(Sexp::Quote(Box::new(Sexp::Nil)).hash_discriminator(), 3);
23165 assert_eq!(
23166 Sexp::Quasiquote(Box::new(Sexp::Nil)).hash_discriminator(),
23167 4
23168 );
23169 assert_eq!(Sexp::Unquote(Box::new(Sexp::Nil)).hash_discriminator(), 5);
23170 assert_eq!(
23171 Sexp::UnquoteSplice(Box::new(Sexp::Nil)).hash_discriminator(),
23172 6
23173 );
23174 }
23175
23176 #[test]
23177 fn sexp_hash_discriminator_bytes_partition_zero_through_six_injectively() {
23178 // Closed-set injectivity across the seven outer-`Sexp` variants:
23179 // the seven discriminator bytes MUST partition `{0, 1, 2, 3, 4,
23180 // 5, 6}` injectively so two distinct outer variants never
23181 // conflate their outer cache-key byte — a violation here means
23182 // the cache could conflate e.g. `Sexp::List(vec![])` and
23183 // `Sexp::Quote(Box::new(Sexp::Nil))` at the outer discriminator
23184 // slot. Uses ONE seed per outer-variant sweep. Sibling pin to
23185 // `atom_kind_hash_discriminator_bytes_are_pairwise_disjoint` (six-
23186 // arm partition of `{0..=5}` nested inside the Atom outer byte
23187 // `1`) and `quote_form_hash_discriminator_bytes_are_pairwise_
23188 // disjoint` (four-arm partition of `{3..=6}` surfaced through
23189 // this outer method's quote-family arms). Together the three
23190 // partitions jointly cover the outer-Sexp discriminator space
23191 // `{0..=6}` — the joint partition contract is pinned by
23192 // `sexp_hash_discriminator_partitions_the_full_outer_discriminator_space_zero_through_six`
23193 // below.
23194 let bytes: Vec<u8> = [
23195 Sexp::Nil,
23196 Sexp::symbol("x"),
23197 Sexp::List(vec![]),
23198 Sexp::Quote(Box::new(Sexp::Nil)),
23199 Sexp::Quasiquote(Box::new(Sexp::Nil)),
23200 Sexp::Unquote(Box::new(Sexp::Nil)),
23201 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
23202 ]
23203 .iter()
23204 .map(Sexp::hash_discriminator)
23205 .collect();
23206 let mut sorted = bytes.clone();
23207 sorted.sort_unstable();
23208 let mut deduped = sorted.clone();
23209 deduped.dedup();
23210 assert_eq!(
23211 sorted, deduped,
23212 "Sexp hash discriminator bytes must be pairwise disjoint across the seven outer variants"
23213 );
23214 assert_eq!(sorted, vec![0, 1, 2, 3, 4, 5, 6]);
23215 }
23216
23217 #[test]
23218 fn sexp_hash_discriminator_partitions_the_full_outer_discriminator_space_zero_through_six() {
23219 // JOINT PARTITION CONTRACT: the outer-`Sexp` discriminator byte
23220 // space `{0..=6}` is jointly covered by the three carvings' typed
23221 // discriminator methods — pinning the joint contract makes the
23222 // prefix-uniqueness invariant a compile-time-verified theorem
23223 // rather than a per-carving isolated pin.
23224 //
23225 // Sexp-outer: `{0, 1, 2, 3, 4, 5, 6}` via `Sexp::hash_discriminator`
23226 // (the outer arm-partition method; the entire outer space).
23227 // AtomKind: `{0, 1, 2, 3, 4, 5}` via `AtomKind::hash_discriminator`
23228 // (nested inside the Atom outer byte `1`; NOT part of the outer
23229 // partition, but pinned here to document the sub-carving space).
23230 // QuoteForm: `{3, 4, 5, 6}` via `QuoteForm::hash_discriminator`
23231 // (surfaced through the four quote-family arms of the outer
23232 // method; MUST equal the outer sweep's `{3..=6}` slice).
23233 //
23234 // A regression that drifts the outer method's quote-family arms
23235 // from the delegated `QuoteForm::hash_discriminator` bytes (e.g.
23236 // routes `Sexp::Quote(_)` to `7u8` inline) fails-loudly here.
23237 // Sibling posture to
23238 // `structural_kind_hash_discriminator_disjoint_from_atom_outer_carve_byte_and_quote_form_hash_discriminator_partition`
23239 // on the sub-carving axis — this pin binds the OUTER joint
23240 // partition; that pin would bind a hypothetical structural sub-
23241 // carving's disjointness.
23242 let outer_seeds: Vec<Sexp> = vec![
23243 Sexp::Nil,
23244 Sexp::symbol("x"),
23245 Sexp::List(vec![]),
23246 Sexp::Quote(Box::new(Sexp::Nil)),
23247 Sexp::Quasiquote(Box::new(Sexp::Nil)),
23248 Sexp::Unquote(Box::new(Sexp::Nil)),
23249 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
23250 ];
23251 let outer_bytes: std::collections::BTreeSet<u8> =
23252 outer_seeds.iter().map(Sexp::hash_discriminator).collect();
23253 let expected: std::collections::BTreeSet<u8> = (0u8..=6u8).collect();
23254 assert_eq!(
23255 outer_bytes, expected,
23256 "Sexp::hash_discriminator must cover exactly the outer discriminator space {{0..=6}}"
23257 );
23258
23259 let quote_bytes: std::collections::BTreeSet<u8> = QuoteForm::ALL
23260 .iter()
23261 .map(|qf| qf.hash_discriminator())
23262 .collect();
23263 let quote_slice: std::collections::BTreeSet<u8> = (3u8..=6u8).collect();
23264 assert_eq!(
23265 quote_bytes, quote_slice,
23266 "QuoteForm::hash_discriminator must cover {{3..=6}} — the quote-family slice of the outer Sexp partition"
23267 );
23268 assert!(
23269 quote_bytes.is_subset(&outer_bytes),
23270 "QuoteForm::hash_discriminator bytes must be a subset of Sexp::hash_discriminator's outer partition"
23271 );
23272 }
23273
23274 #[test]
23275 fn sexp_hash_discriminator_atom_arm_collapses_over_every_atom_kind() {
23276 // OUTER-CARVING CONTRACT (atomic arm): every `AtomKind` variant
23277 // projects through `Sexp::Atom` to the SAME outer discriminator
23278 // byte `1u8` — the atomic outer arm is a single-byte marker on
23279 // the outer partition, with the per-atom-kind inner byte
23280 // (`AtomKind::hash_discriminator`'s `{0..=5}`) nested INSIDE
23281 // `Atom::hash` — NOT surfaced through this method. Pin the six-
23282 // way collapse so a regression that drifts ONE atom kind's outer
23283 // routing (e.g. routes `Sexp::Atom(Atom::Int(_))` to `7u8`
23284 // inline) surfaces here immediately. Sibling posture to
23285 // `sexp_hash_discriminator_quote_arm_delegates_to_quote_form_
23286 // hash_discriminator` on the quote-family arm — that arm
23287 // DELEGATES to `QuoteForm::hash_discriminator` for `{3..=6}`;
23288 // this arm COLLAPSES to a single outer byte `1`.
23289 for (kind, sexp) in [
23290 (AtomKind::Symbol, Sexp::symbol("s")),
23291 (AtomKind::Keyword, Sexp::keyword("k")),
23292 (AtomKind::Str, Sexp::string("t")),
23293 (AtomKind::Int, Sexp::int(7)),
23294 (AtomKind::Float, Sexp::float(2.5)),
23295 (AtomKind::Bool, Sexp::boolean(true)),
23296 ] {
23297 assert_eq!(
23298 sexp.hash_discriminator(),
23299 1,
23300 "Sexp::Atom({kind:?}) must collapse to outer byte 1"
23301 );
23302 }
23303 }
23304
23305 #[test]
23306 fn sexp_hash_discriminator_quote_arm_delegates_to_quote_form_hash_discriminator() {
23307 // OUTER-CARVING CONTRACT (quote-family arm): every `QuoteForm`
23308 // variant projects through `QuoteForm::wrap` to a `Sexp::Quote_*`
23309 // whose `hash_discriminator` equals `qf.hash_discriminator()` —
23310 // the four quote-family arms DELEGATE to the sub-algebra's
23311 // discriminator method rather than inline four literals. Pin
23312 // the delegation-identity across the closed set so a regression
23313 // that inlines a byte at ONE arm (e.g. routes
23314 // `Sexp::UnquoteSplice(_)` to `6u8` inline instead of through
23315 // `QuoteForm::UnquoteSplice.hash_discriminator()`) fails-loudly
23316 // here — it would type-check but silently drift if the sub-
23317 // algebra's byte is renumbered.
23318 for qf in QuoteForm::ALL {
23319 let sexp = qf.wrap(Sexp::Nil);
23320 assert_eq!(
23321 sexp.hash_discriminator(),
23322 qf.hash_discriminator(),
23323 "Sexp {qf:?}-arm must delegate to QuoteForm::hash_discriminator"
23324 );
23325 }
23326 }
23327
23328 #[test]
23329 fn hash_for_sexp_routes_outer_discriminator_through_sexp_hash_discriminator() {
23330 // ROUTING-LAW CONTRACT: pin the outer-`Sexp` routing IDENTITY —
23331 // for every reachable outer-variant shape, `Hash for Sexp`
23332 // produces byte-identical output to a hand-driven
23333 // `<sexp.hash_discriminator()>.hash(h); <inner-payload-hash>`
23334 // sequence. Binds the composition IDENTITY (not just value
23335 // equality) between the outer Hash body and the typed algebra
23336 // method — a regression that re-inlines the three literals
23337 // (`0u8` / `1u8` / `2u8`) at the outer arms still drifts
23338 // detectably if the future `Sexp::hash_discriminator` is
23339 // re-numbered. Sibling posture to
23340 // `hash_for_sexp_preserves_legacy_quote_family_discriminator_bytes`
23341 // — that pin binds the quote-family arms against the pre-lift
23342 // literal bytes; this pin binds ALL SEVEN outer arms against the
23343 // post-lift typed method.
23344 use std::collections::hash_map::DefaultHasher;
23345 let payload = Sexp::symbol("payload");
23346 let seeds: Vec<(&str, Sexp)> = vec![
23347 ("nil", Sexp::Nil),
23348 ("atom-symbol", Sexp::symbol("s")),
23349 ("atom-int", Sexp::int(7)),
23350 ("atom-float", Sexp::float(2.5)),
23351 ("atom-bool", Sexp::boolean(true)),
23352 ("empty list", Sexp::List(vec![])),
23353 (
23354 "non-empty list",
23355 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23356 ),
23357 ("quote", Sexp::Quote(Box::new(payload.clone()))),
23358 ("quasiquote", Sexp::Quasiquote(Box::new(payload.clone()))),
23359 ("unquote", Sexp::Unquote(Box::new(payload.clone()))),
23360 (
23361 "unquote-splice",
23362 Sexp::UnquoteSplice(Box::new(payload.clone())),
23363 ),
23364 ];
23365 for (label, sexp) in seeds {
23366 let mut via_impl = DefaultHasher::new();
23367 sexp.hash(&mut via_impl);
23368
23369 let mut via_lifted = DefaultHasher::new();
23370 sexp.hash_discriminator().hash(&mut via_lifted);
23371 match &sexp {
23372 Sexp::Nil => {}
23373 Sexp::Atom(a) => a.hash(&mut via_lifted),
23374 Sexp::List(items) => {
23375 items.len().hash(&mut via_lifted);
23376 for i in items {
23377 i.hash(&mut via_lifted);
23378 }
23379 }
23380 Sexp::Quote(_)
23381 | Sexp::Quasiquote(_)
23382 | Sexp::Unquote(_)
23383 | Sexp::UnquoteSplice(_) => {
23384 let (_, inner) = sexp.expect_quote_form();
23385 inner.hash(&mut via_lifted);
23386 }
23387 }
23388
23389 assert_eq!(
23390 via_impl.finish(),
23391 via_lifted.finish(),
23392 "Hash for Sexp drifted from routed-through-hash_discriminator sequence at {label}"
23393 );
23394 }
23395 }
23396
23397 #[test]
23398 fn sexp_hash_discriminator_routes_through_shape_hash_discriminator_via_composition() {
23399 // COMPOSITION-IDENTITY CONTRACT (five-layer post-lift): pin the
23400 // outer-`Sexp` cache-key routing IDENTITY through the new shape-
23401 // level algebra layer — for every reachable outer-variant shape,
23402 // `Sexp::hash_discriminator` MUST agree byte-for-byte with
23403 // `self.shape().hash_discriminator()`. Post-lift the outer
23404 // method's body is EXACTLY `self.shape().hash_discriminator()`,
23405 // and this pin binds the routing identity across every reachable
23406 // shape so a regression that re-inlines the seven arm literals
23407 // (e.g. reverts to an inline match returning `0u8`/`1u8`/`2u8`
23408 // and the four quote-family sub-carving delegations) still
23409 // drifts detectably if the future `SexpShape::hash_discriminator`
23410 // is re-numbered — the composition identity is what closes the
23411 // outer-`Sexp` cache-key algebra at five typed layers (outer →
23412 // shape → three sub-carvings). Sibling posture to
23413 // `hash_for_sexp_routes_outer_discriminator_through_sexp_hash_discriminator`
23414 // — that pin binds the `Hash for Sexp` body against the outer
23415 // method; this pin binds the outer method against the shape-
23416 // level method.
23417 let payload = Sexp::symbol("payload");
23418 let seeds: Vec<(&str, Sexp)> = vec![
23419 ("nil", Sexp::Nil),
23420 ("atom-symbol", Sexp::symbol("s")),
23421 ("atom-keyword", Sexp::keyword("k")),
23422 ("atom-string", Sexp::string("t")),
23423 ("atom-int", Sexp::int(7)),
23424 ("atom-float", Sexp::float(2.5)),
23425 ("atom-bool", Sexp::boolean(true)),
23426 ("empty list", Sexp::List(vec![])),
23427 (
23428 "non-empty list",
23429 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23430 ),
23431 ("quote", Sexp::Quote(Box::new(payload.clone()))),
23432 ("quasiquote", Sexp::Quasiquote(Box::new(payload.clone()))),
23433 ("unquote", Sexp::Unquote(Box::new(payload.clone()))),
23434 (
23435 "unquote-splice",
23436 Sexp::UnquoteSplice(Box::new(payload.clone())),
23437 ),
23438 ];
23439 for (label, sexp) in seeds {
23440 let outer = sexp.hash_discriminator();
23441 let via_shape = sexp.shape().hash_discriminator();
23442 assert_eq!(
23443 outer, via_shape,
23444 "Sexp::hash_discriminator at {label} drifted from self.shape().hash_discriminator() — the five-layer typed cache-key composition is broken",
23445 );
23446 }
23447 }
23448
23449 #[test]
23450 fn sexp_iac_forge_tag_routes_through_shape_iac_forge_tag_via_composition() {
23451 // COMPOSITION-IDENTITY CONTRACT (outer-value peer): pin the
23452 // outer-`Sexp` cross-crate canonical-form tag routing IDENTITY
23453 // through the pre-existing shape-level projection — for every
23454 // reachable outer-variant shape, `Sexp::iac_forge_tag` MUST agree
23455 // arm-for-arm with `self.shape().iac_forge_tag()`. Post-lift the
23456 // outer method's body is EXACTLY `self.shape().iac_forge_tag()`,
23457 // and this pin binds the routing identity across every reachable
23458 // shape so a regression that re-inlines a parallel four-arm
23459 // match on the outer `Self::Quote | Self::Quasiquote | ...` set
23460 // returning literal tag strings inline still drifts detectably
23461 // if the shape-level projection's tag composition is re-numbered
23462 // — the composition identity is what closes the outer-`Sexp`
23463 // cross-crate canonical-form tag surface at four typed layers
23464 // (outer → shape → carving → sub-carving-tag). Sibling posture
23465 // to `sexp_hash_discriminator_routes_through_shape_hash_discriminator_via_composition`
23466 // — that pin binds the outer method against the shape-level
23467 // method on the cache-key byte axis; this pin binds the outer
23468 // method against the shape-level method on the cross-crate
23469 // canonical-form tag axis.
23470 let payload = Sexp::symbol("payload");
23471 let seeds: Vec<(&str, Sexp)> = vec![
23472 ("nil", Sexp::Nil),
23473 ("atom-symbol", Sexp::symbol("s")),
23474 ("atom-keyword", Sexp::keyword("k")),
23475 ("atom-string", Sexp::string("t")),
23476 ("atom-int", Sexp::int(7)),
23477 ("atom-float", Sexp::float(2.5)),
23478 ("atom-bool", Sexp::boolean(true)),
23479 ("empty list", Sexp::List(vec![])),
23480 (
23481 "non-empty list",
23482 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23483 ),
23484 ("quote", Sexp::Quote(Box::new(payload.clone()))),
23485 ("quasiquote", Sexp::Quasiquote(Box::new(payload.clone()))),
23486 ("unquote", Sexp::Unquote(Box::new(payload.clone()))),
23487 (
23488 "unquote-splice",
23489 Sexp::UnquoteSplice(Box::new(payload.clone())),
23490 ),
23491 ];
23492 for (label, sexp) in seeds {
23493 let outer = sexp.iac_forge_tag();
23494 let via_shape = sexp.shape().iac_forge_tag();
23495 assert_eq!(
23496 outer, via_shape,
23497 "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",
23498 );
23499 }
23500 }
23501
23502 #[test]
23503 fn sexp_iac_forge_tag_pins_canonical_cl_tags_for_every_quote_family_arm() {
23504 // CANONICAL-TAG CONTRACT (outer-value peer): the outer-value
23505 // `Sexp::iac_forge_tag` MUST project each of the four homoiconic
23506 // prefix-wrapper arms to the SAME canonical Common-Lisp tag
23507 // string `crate::error::SexpShape::iac_forge_tag` projects at the
23508 // shape-level (and `crate::ast::QuoteForm::iac_forge_tag` at the
23509 // sub-carving level) — `Sexp::Quote → Some("quote")`,
23510 // `Sexp::Quasiquote → Some("quasiquote")`, `Sexp::Unquote →
23511 // Some("unquote")`, `Sexp::UnquoteSplice → Some("unquote-
23512 // splicing")`. A regression that inlines a byte-drifted spelling
23513 // here (e.g. `Sexp::UnquoteSplice → Some("unquote-splice")`
23514 // conflating the substrate's shorter diagnostic label with the
23515 // CL canonical form) silently breaks every cross-crate iac-forge
23516 // consumer keyed on `(unquote-splicing ...)`. Sibling posture to
23517 // `sexp_shape_iac_forge_tag_pins_canonical_cl_tags_for_every_quote_family_arm`
23518 // one algebra level down — that pin binds the shape-level
23519 // projection's canonical tag surface; this pin binds the outer-
23520 // value projection's canonical tag surface across the closed
23521 // four-arm quote-family sweep on the outer `Sexp` algebra.
23522 let inner = Sexp::symbol("payload");
23523 assert_eq!(
23524 Sexp::Quote(Box::new(inner.clone())).iac_forge_tag(),
23525 Some("quote"),
23526 );
23527 assert_eq!(
23528 Sexp::Quasiquote(Box::new(inner.clone())).iac_forge_tag(),
23529 Some("quasiquote"),
23530 );
23531 assert_eq!(
23532 Sexp::Unquote(Box::new(inner.clone())).iac_forge_tag(),
23533 Some("unquote"),
23534 );
23535 assert_eq!(
23536 Sexp::UnquoteSplice(Box::new(inner)).iac_forge_tag(),
23537 Some("unquote-splicing"),
23538 );
23539 }
23540
23541 #[test]
23542 fn sexp_iac_forge_tag_returns_none_on_every_non_quote_family_variant() {
23543 // PARTIAL-PROJECTION KERNEL CONTRACT (outer-value peer): every
23544 // `Sexp` variant OUTSIDE the four-arm quote-family carving MUST
23545 // project through `Sexp::iac_forge_tag` to `None` — the three-
23546 // arm outer kernel `{Nil, Atom, List}` (which corresponds to the
23547 // eight-shape kernel at the shape-level projection through the
23548 // six-atomic-arms → outer `Atom` collapse of `Self::shape`). Pin
23549 // representative seeds for each kernel arm — `Nil`, one atom per
23550 // `AtomKind`, one empty + one non-empty list — so a regression
23551 // that surfaces a bogus tag for a non-quote-family arm (e.g.
23552 // `Sexp::List → Some("list")` conflating the outer-shape
23553 // diagnostic label with the quote-family canonical form) fails-
23554 // loudly here. Sibling posture to
23555 // `sexp_shape_iac_forge_tag_returns_none_on_every_non_quote_family_shape`
23556 // one algebra level down — that pin binds the shape-level
23557 // projection's kernel; this pin binds the outer-value
23558 // projection's kernel on the three-arm outer partition.
23559 assert_eq!(Sexp::Nil.iac_forge_tag(), None);
23560 assert_eq!(Sexp::symbol("s").iac_forge_tag(), None);
23561 assert_eq!(Sexp::keyword("k").iac_forge_tag(), None);
23562 assert_eq!(Sexp::string("t").iac_forge_tag(), None);
23563 assert_eq!(Sexp::int(7).iac_forge_tag(), None);
23564 assert_eq!(Sexp::float(2.5).iac_forge_tag(), None);
23565 assert_eq!(Sexp::boolean(true).iac_forge_tag(), None);
23566 assert_eq!(Sexp::List(vec![]).iac_forge_tag(), None);
23567 assert_eq!(
23568 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]).iac_forge_tag(),
23569 None,
23570 );
23571 }
23572
23573 #[test]
23574 fn sexp_iac_forge_tag_partitions_quote_family_and_kernel_disjointly() {
23575 // IMAGE-PARTITION CONTRACT (outer-value peer): sweeping a
23576 // representative seed per outer `Sexp` variant through
23577 // `Sexp::iac_forge_tag` MUST partition into EXACTLY the four-arm
23578 // quote-family image (four distinct canonical CL tag strings —
23579 // the pre-image of `Some(_)`) AND the outer three-arm non-quote-
23580 // family kernel (all `None` — `Nil` + one atom per `AtomKind` +
23581 // one list). The image's `is_some()` count MUST be four
23582 // (surjective onto the four-tag closed set), the kernel's
23583 // `is_none()` count MUST cover every non-quote-family seed, and
23584 // the total sweep sums to the thirteen-seed representative sweep
23585 // covering all seven outer variants (six `Atom` payloads +
23586 // `Nil` + one `List` + four quote-family arms). A regression that
23587 // leaks a bogus `Some(_)` from a non-quote-family arm or drops a
23588 // `Some(_)` from a quote-family arm fails-loudly here on the
23589 // partition-cardinality axis before any downstream iac-forge
23590 // consumer would surface the drift. Sibling posture to
23591 // `sexp_shape_iac_forge_tag_partitions_quote_family_and_kernel_disjointly`
23592 // one algebra level down — that pin binds the shape-level
23593 // image-partition on the twelve-shape closed sweep; this pin
23594 // binds the outer-value image-partition on the representative
23595 // outer-variant sweep.
23596 let payload = Sexp::symbol("payload");
23597 let seeds: Vec<Sexp> = vec![
23598 Sexp::Nil,
23599 Sexp::symbol("s"),
23600 Sexp::keyword("k"),
23601 Sexp::string("t"),
23602 Sexp::int(7),
23603 Sexp::float(2.5),
23604 Sexp::boolean(true),
23605 Sexp::List(vec![]),
23606 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23607 Sexp::Quote(Box::new(payload.clone())),
23608 Sexp::Quasiquote(Box::new(payload.clone())),
23609 Sexp::Unquote(Box::new(payload.clone())),
23610 Sexp::UnquoteSplice(Box::new(payload.clone())),
23611 ];
23612 let tag_image: std::collections::BTreeSet<&'static str> =
23613 seeds.iter().filter_map(Sexp::iac_forge_tag).collect();
23614 let expected_tag_image: std::collections::BTreeSet<&'static str> =
23615 ["quote", "quasiquote", "unquote", "unquote-splicing"]
23616 .into_iter()
23617 .collect();
23618 assert_eq!(
23619 tag_image, expected_tag_image,
23620 "Sexp::iac_forge_tag image must exactly cover the four canonical CL quote-family tags",
23621 );
23622 let some_count = seeds
23623 .iter()
23624 .filter(|sexp| sexp.iac_forge_tag().is_some())
23625 .count();
23626 assert_eq!(
23627 some_count, 4,
23628 "Sexp::iac_forge_tag must return `Some(_)` on exactly the four-arm quote-family carving",
23629 );
23630 let none_count = seeds
23631 .iter()
23632 .filter(|sexp| sexp.iac_forge_tag().is_none())
23633 .count();
23634 assert_eq!(
23635 none_count,
23636 seeds.len() - 4,
23637 "Sexp::iac_forge_tag must return `None` on every seed outside the four-arm quote-family carving",
23638 );
23639 assert_eq!(
23640 some_count + none_count,
23641 seeds.len(),
23642 "Sexp::iac_forge_tag's image + kernel must partition the representative outer-variant sweep exactly",
23643 );
23644 }
23645
23646 #[test]
23647 fn sexp_prefix_routes_through_shape_prefix_via_composition() {
23648 // COMPOSITION-IDENTITY CONTRACT (outer-value peer): pin the
23649 // outer-`Sexp` reader-punctuation surface routing IDENTITY
23650 // through the pre-existing shape-level projection — for every
23651 // reachable outer-variant shape, `Sexp::prefix` MUST agree arm-
23652 // for-arm with `self.shape().prefix()`. Post-lift the outer
23653 // method's body is EXACTLY `self.shape().prefix()`, and this
23654 // pin binds the routing identity across every reachable shape so
23655 // a regression that re-inlines a parallel four-arm match on the
23656 // outer `Self::Quote | Self::Quasiquote | ...` set returning
23657 // literal reader-punctuation strings inline still drifts
23658 // detectably if the shape-level projection's prefix composition
23659 // is re-numbered — the composition identity is what closes the
23660 // outer-`Sexp` reader-punctuation surface at four typed layers
23661 // (outer → shape → carving → sub-carving-prefix). Sibling
23662 // posture to
23663 // `sexp_iac_forge_tag_routes_through_shape_iac_forge_tag_via_composition`
23664 // one vocabulary axis over — that pin binds the outer method
23665 // against the shape-level method on the cross-crate canonical-
23666 // form tag axis; this pin binds the outer method against the
23667 // shape-level method on the reader-punctuation axis.
23668 let payload = Sexp::symbol("payload");
23669 let seeds: Vec<(&str, Sexp)> = vec![
23670 ("nil", Sexp::Nil),
23671 ("atom-symbol", Sexp::symbol("s")),
23672 ("atom-keyword", Sexp::keyword("k")),
23673 ("atom-string", Sexp::string("t")),
23674 ("atom-int", Sexp::int(7)),
23675 ("atom-float", Sexp::float(2.5)),
23676 ("atom-bool", Sexp::boolean(true)),
23677 ("empty list", Sexp::List(vec![])),
23678 (
23679 "non-empty list",
23680 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23681 ),
23682 ("quote", Sexp::Quote(Box::new(payload.clone()))),
23683 ("quasiquote", Sexp::Quasiquote(Box::new(payload.clone()))),
23684 ("unquote", Sexp::Unquote(Box::new(payload.clone()))),
23685 (
23686 "unquote-splice",
23687 Sexp::UnquoteSplice(Box::new(payload.clone())),
23688 ),
23689 ];
23690 for (label, sexp) in seeds {
23691 let outer = sexp.prefix();
23692 let via_shape = sexp.shape().prefix();
23693 assert_eq!(
23694 outer, via_shape,
23695 "Sexp::prefix at {label} drifted from self.shape().prefix() — the four-layer typed reader-punctuation composition is broken",
23696 );
23697 }
23698 }
23699
23700 #[test]
23701 fn sexp_prefix_pins_canonical_reader_prefixes_for_every_quote_family_arm() {
23702 // CANONICAL-PREFIX CONTRACT (outer-value peer): the outer-value
23703 // `Sexp::prefix` MUST project each of the four homoiconic
23704 // prefix-wrapper arms to the SAME canonical reader-punctuation
23705 // string `crate::error::SexpShape::prefix` projects at the
23706 // shape-level (and `crate::ast::QuoteForm::prefix` at the sub-
23707 // carving level) — `Sexp::Quote → Some("'")`,
23708 // `Sexp::Quasiquote → Some("`")`, `Sexp::Unquote → Some(",")`,
23709 // `Sexp::UnquoteSplice → Some(",@")`. A regression that inlines
23710 // a byte-drifted spelling here (e.g. `Sexp::UnquoteSplice →
23711 // Some(", @")` inserting a spurious space, or `Sexp::Quote →
23712 // Some("`")` swapping arms between Quote and Quasiquote)
23713 // silently breaks the `Display for Sexp` round-trip against the
23714 // reader's prefix dispatch. Sibling posture to
23715 // `sexp_shape_prefix_pins_canonical_reader_prefixes_for_every_quote_family_arm`
23716 // one algebra level down — that pin binds the shape-level
23717 // projection's canonical reader-punctuation surface; this pin
23718 // binds the outer-value projection's canonical reader-
23719 // punctuation surface across the closed four-arm quote-family
23720 // sweep on the outer `Sexp` algebra.
23721 let inner = Sexp::symbol("payload");
23722 assert_eq!(Sexp::Quote(Box::new(inner.clone())).prefix(), Some("'"),);
23723 assert_eq!(
23724 Sexp::Quasiquote(Box::new(inner.clone())).prefix(),
23725 Some("`"),
23726 );
23727 assert_eq!(Sexp::Unquote(Box::new(inner.clone())).prefix(), Some(","),);
23728 assert_eq!(Sexp::UnquoteSplice(Box::new(inner)).prefix(), Some(",@"),);
23729 }
23730
23731 #[test]
23732 fn sexp_prefix_returns_none_on_every_non_quote_family_variant() {
23733 // PARTIAL-PROJECTION KERNEL CONTRACT (outer-value peer): every
23734 // `Sexp` variant OUTSIDE the four-arm quote-family carving MUST
23735 // project through `Sexp::prefix` to `None` — the three-arm
23736 // outer kernel `{Nil, Atom, List}` (which corresponds to the
23737 // eight-shape kernel at the shape-level projection through the
23738 // six-atomic-arms → outer `Atom` collapse of `Self::shape`).
23739 // Pin representative seeds for each kernel arm — `Nil`, one
23740 // atom per `AtomKind`, one empty + one non-empty list — so a
23741 // regression that surfaces a bogus prefix for a non-quote-
23742 // family arm (e.g. `Sexp::List → Some("(")` conflating the
23743 // outer-shape structural delimiter with the quote-family
23744 // reader-punctuation) fails-loudly here. Sibling posture to
23745 // `sexp_shape_prefix_returns_none_on_every_non_quote_family_shape`
23746 // one algebra level down — that pin binds the shape-level
23747 // projection's kernel; this pin binds the outer-value
23748 // projection's kernel on the three-arm outer partition.
23749 assert_eq!(Sexp::Nil.prefix(), None);
23750 assert_eq!(Sexp::symbol("s").prefix(), None);
23751 assert_eq!(Sexp::keyword("k").prefix(), None);
23752 assert_eq!(Sexp::string("t").prefix(), None);
23753 assert_eq!(Sexp::int(7).prefix(), None);
23754 assert_eq!(Sexp::float(2.5).prefix(), None);
23755 assert_eq!(Sexp::boolean(true).prefix(), None);
23756 assert_eq!(Sexp::List(vec![]).prefix(), None);
23757 assert_eq!(
23758 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]).prefix(),
23759 None,
23760 );
23761 }
23762
23763 #[test]
23764 fn sexp_prefix_partitions_quote_family_and_kernel_disjointly() {
23765 // IMAGE-PARTITION CONTRACT (outer-value peer): sweeping a
23766 // representative seed per outer `Sexp` variant through
23767 // `Sexp::prefix` MUST partition into EXACTLY the four-arm
23768 // quote-family image (four distinct canonical reader-punctuation
23769 // strings — the pre-image of `Some(_)`) AND the outer three-arm
23770 // non-quote-family kernel (all `None` — `Nil` + one atom per
23771 // `AtomKind` + one list). The image's `is_some()` count MUST be
23772 // four (surjective onto the four-prefix closed set), the
23773 // kernel's `is_none()` count MUST cover every non-quote-family
23774 // seed, and the total sweep sums to the thirteen-seed
23775 // representative sweep covering all seven outer variants (six
23776 // `Atom` payloads + `Nil` + one `List` + four quote-family
23777 // arms). A regression that leaks a bogus `Some(_)` from a non-
23778 // quote-family arm or drops a `Some(_)` from a quote-family arm
23779 // fails-loudly here on the partition-cardinality axis before
23780 // any downstream reader-round-trip or Display consumer would
23781 // surface the drift. Sibling posture to
23782 // `sexp_shape_prefix_partitions_quote_family_and_kernel_disjointly`
23783 // one algebra level down — that pin binds the shape-level
23784 // image-partition on the twelve-shape closed sweep; this pin
23785 // binds the outer-value image-partition on the representative
23786 // outer-variant sweep.
23787 let payload = Sexp::symbol("payload");
23788 let seeds: Vec<Sexp> = vec![
23789 Sexp::Nil,
23790 Sexp::symbol("s"),
23791 Sexp::keyword("k"),
23792 Sexp::string("t"),
23793 Sexp::int(7),
23794 Sexp::float(2.5),
23795 Sexp::boolean(true),
23796 Sexp::List(vec![]),
23797 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
23798 Sexp::Quote(Box::new(payload.clone())),
23799 Sexp::Quasiquote(Box::new(payload.clone())),
23800 Sexp::Unquote(Box::new(payload.clone())),
23801 Sexp::UnquoteSplice(Box::new(payload.clone())),
23802 ];
23803 let prefix_image: std::collections::BTreeSet<&'static str> =
23804 seeds.iter().filter_map(Sexp::prefix).collect();
23805 let expected_prefix_image: std::collections::BTreeSet<&'static str> =
23806 ["'", "`", ",", ",@"].into_iter().collect();
23807 assert_eq!(
23808 prefix_image, expected_prefix_image,
23809 "Sexp::prefix image must exactly cover the four canonical reader-punctuation quote-family prefixes",
23810 );
23811 let some_count = seeds.iter().filter(|sexp| sexp.prefix().is_some()).count();
23812 assert_eq!(
23813 some_count, 4,
23814 "Sexp::prefix must return `Some(_)` on exactly the four-arm quote-family carving",
23815 );
23816 let none_count = seeds.iter().filter(|sexp| sexp.prefix().is_none()).count();
23817 assert_eq!(
23818 none_count,
23819 seeds.len() - 4,
23820 "Sexp::prefix must return `None` on every seed outside the four-arm quote-family carving",
23821 );
23822 assert_eq!(
23823 some_count + none_count,
23824 seeds.len(),
23825 "Sexp::prefix's image + kernel must partition the representative outer-variant sweep exactly",
23826 );
23827 }
23828
23829 #[test]
23830 fn display_for_sexp_renders_each_quote_family_variant_with_canonical_prefix() {
23831 // Pin the post-lift Display rendering: every wrapper variant
23832 // renders as `<prefix><inner>` with the prefix sourced from
23833 // `QuoteForm::prefix`. A regression that drifts the prefix
23834 // arm-routing (e.g. routes Quote through `` ` `` instead of
23835 // `'`) fails loudly here. The literal `inner` rendering is
23836 // the symbol `foo` so the prefix is the only diff between
23837 // arms — pin path-uniformity across the closed set.
23838 let inner = Sexp::symbol("foo");
23839 assert_eq!(Sexp::Quote(Box::new(inner.clone())).to_string(), "'foo");
23840 assert_eq!(
23841 Sexp::Quasiquote(Box::new(inner.clone())).to_string(),
23842 "`foo"
23843 );
23844 assert_eq!(Sexp::Unquote(Box::new(inner.clone())).to_string(), ",foo");
23845 assert_eq!(Sexp::UnquoteSplice(Box::new(inner)).to_string(), ",@foo");
23846 }
23847
23848 #[test]
23849 fn display_for_sexp_round_trips_each_quote_family_variant_through_reader() {
23850 // ROUND-TRIP CONTRACT: every wrapper variant's Display →
23851 // reader path produces the matching `Sexp::*` variant. The
23852 // reader's prefix-dispatch (in `reader::parse`) consumes the
23853 // canonical `'` / `` ` `` / `,` / `,@` tokens and produces
23854 // the corresponding wrapper; the Display impl emits the same
23855 // tokens via `QuoteForm::prefix`. Pin the round-trip
23856 // end-to-end so a regression that drifts the prefix on
23857 // either side (Display or reader) fails loudly here. Sibling
23858 // posture to `fmt_float_round_trips_integral_float_through
23859 // _reader_as_float` — the Float round-trip pin at the
23860 // Display→read boundary; this test pins the four
23861 // quote-family round-trips at the same boundary.
23862 let inner_body = Sexp::symbol("payload");
23863
23864 let quote = Sexp::Quote(Box::new(inner_body.clone()));
23865 let forms = crate::reader::read("e.to_string()).expect("quote must round-trip");
23866 assert_eq!(forms.len(), 1);
23867 assert_eq!(forms[0], quote);
23868
23869 let quasiquote = Sexp::Quasiquote(Box::new(inner_body.clone()));
23870 let forms =
23871 crate::reader::read(&quasiquote.to_string()).expect("quasiquote must round-trip");
23872 assert_eq!(forms.len(), 1);
23873 assert_eq!(forms[0], quasiquote);
23874
23875 let unquote = Sexp::Unquote(Box::new(inner_body.clone()));
23876 let forms = crate::reader::read(&unquote.to_string()).expect("unquote must round-trip");
23877 assert_eq!(forms.len(), 1);
23878 assert_eq!(forms[0], unquote);
23879
23880 let splice = Sexp::UnquoteSplice(Box::new(inner_body));
23881 let forms =
23882 crate::reader::read(&splice.to_string()).expect("unquote-splice must round-trip");
23883 assert_eq!(forms.len(), 1);
23884 assert_eq!(forms[0], splice);
23885 }
23886
23887 #[test]
23888 fn quote_form_wrap_projects_each_typed_marker_into_matching_sexp_wrapper() {
23889 // CLOSED-SET CONSTRUCTOR CONTRACT: pin that `QuoteForm::wrap` is
23890 // the structural inverse of `Sexp::as_quote_form` at the
23891 // marker→wrapper boundary. Every variant of the closed-set
23892 // `QuoteForm` algebra projects to its matching `Sexp::*` wrapper
23893 // applied to the supplied inner — `Quote → Sexp::Quote`,
23894 // `Quasiquote → Sexp::Quasiquote`, `Unquote → Sexp::Unquote`,
23895 // `UnquoteSplice → Sexp::UnquoteSplice`. A regression that swaps
23896 // two arms (e.g. `Self::Quote → Sexp::Quasiquote`) type-checks
23897 // but silently corrupts every consumer that constructs a quote-
23898 // family Sexp through the projection — fails loudly here.
23899 // Sibling-arm sweep so the (marker, constructor) pair stays
23900 // load-bearing under reordering refactors.
23901 let inner = Sexp::symbol("payload");
23902 assert_eq!(
23903 QuoteForm::Quote.wrap(inner.clone()),
23904 Sexp::Quote(Box::new(inner.clone()))
23905 );
23906 assert_eq!(
23907 QuoteForm::Quasiquote.wrap(inner.clone()),
23908 Sexp::Quasiquote(Box::new(inner.clone()))
23909 );
23910 assert_eq!(
23911 QuoteForm::Unquote.wrap(inner.clone()),
23912 Sexp::Unquote(Box::new(inner.clone()))
23913 );
23914 assert_eq!(
23915 QuoteForm::UnquoteSplice.wrap(inner.clone()),
23916 Sexp::UnquoteSplice(Box::new(inner))
23917 );
23918 }
23919
23920 #[test]
23921 fn quote_form_wrap_round_trips_through_as_quote_form_for_every_variant() {
23922 // ROUND-TRIP CONTRACT: pin the structural identity
23923 // `qf.wrap(inner.clone()).as_quote_form() == Some((qf, &inner))`
23924 // for every variant of the closed-set `QuoteForm` algebra. This
23925 // is the canonical law binding the marker→wrapper projection
23926 // (`wrap`) to its wrapper→marker dual (`as_quote_form`) on the
23927 // substrate's `Sexp` algebra. A regression that drifts the
23928 // (marker, constructor) pair on EITHER side — `wrap` routing
23929 // `Quote` to `Sexp::Quasiquote`, OR `as_quote_form` routing
23930 // `Sexp::Quote(_)` to `QuoteForm::Quasiquote` — surfaces as a
23931 // round-trip mismatch here. Sweep all four variants so the
23932 // round-trip stays load-bearing across the closed set. Same
23933 // posture as the `display_for_sexp_round_trips_each_quote_family
23934 // _variant_through_reader` round-trip pin at the Display→read
23935 // boundary; this test pins the round-trip at the marker→Sexp
23936 // projection boundary.
23937 let inner_body = Sexp::symbol("payload");
23938 for qf in [
23939 QuoteForm::Quote,
23940 QuoteForm::Quasiquote,
23941 QuoteForm::Unquote,
23942 QuoteForm::UnquoteSplice,
23943 ] {
23944 let wrapped = qf.wrap(inner_body.clone());
23945 let projected = wrapped
23946 .as_quote_form()
23947 .expect("wrap output must project back through as_quote_form");
23948 assert_eq!(
23949 projected.0, qf,
23950 "wrap→as_quote_form drifted at marker for variant {qf:?}"
23951 );
23952 assert_eq!(
23953 projected.1, &inner_body,
23954 "wrap→as_quote_form drifted at inner body for variant {qf:?}"
23955 );
23956 }
23957 }
23958
23959 #[test]
23960 fn quote_form_all_is_unique_and_complete() {
23961 // CLOSED-SET TRUTH-TABLE: pin that `QuoteForm::ALL` carries
23962 // exactly the four reachable quote-family wrappers — no duplicates,
23963 // byte-equal coverage of `{Quote, Quasiquote, Unquote, UnquoteSplice}`.
23964 // The `[Self; 4]` array-literal arity already binds the count at
23965 // compile time; this test pins the *identity* of each slot so a
23966 // future re-ordering refactor (e.g. swapping `Unquote` and
23967 // `UnquoteSplice` positions) that leaves the cardinality intact
23968 // still fails loudly. Sibling discipline to
23969 // `unquote_form_all_is_unique_and_complete` (the 2-of-4 subset
23970 // sibling) and `atom_kind_all_is_unique_and_complete` (the peer
23971 // atomic-payload axis).
23972 //
23973 // The `iter+map+collect+sort_unstable` quadruple this test inlined
23974 // pre-lift now binds at `<QuoteForm as ClosedSet>::sorted_labels()`
23975 // — the canonical-ordered candidate-list projection on the trait.
23976 // Distinctness of the sorted result is covered by
23977 // `assert_closed_set_well_formed::<QuoteForm>()` (the workspace-wide
23978 // testkit), so this test reduces to the per-implementor unique
23979 // payload (the four reader-punctuation literals in lexicographic
23980 // order — the load-bearing per-enum ground truth the substrate-wide
23981 // sort lift does NOT subsume).
23982 assert_eq!(QuoteForm::ALL.len(), 4);
23983 assert_eq!(
23984 <QuoteForm as tatara_closed_set::ClosedSet>::sorted_labels(),
23985 vec!["'", ",", ",@", "`"],
23986 "QuoteForm::ALL must cover every reachable homoiconic prefix-wrapper"
23987 );
23988 }
23989
23990 #[test]
23991 fn quote_form_display_matches_prefix_for_every_variant() {
23992 // DISPLAY-EQUALS-PREFIX CONTRACT: pin that
23993 // `<QuoteForm as fmt::Display>::fmt` projects through
23994 // `QuoteForm::prefix` byte-for-byte for every variant in
23995 // `QuoteForm::ALL`. The Display impl is the canonical rendering
23996 // surface a future diagnostic annotation (`#[error("... {prefix}")]`
23997 // shape) threads through; pinning the equality here means a
23998 // regression that drifts EITHER the Display arm OR the `prefix`
23999 // arm independently surfaces at this test rather than silently
24000 // bifurcating the operator-facing rendered marker. Sibling
24001 // discipline to `unquote_form_display_renders_canonical_marker_
24002 // for_each_variant` (the 2-of-4 subset sibling) and
24003 // `atom_kind_display_matches_label_for_every_variant` (the peer
24004 // atomic-payload axis).
24005 for qf in QuoteForm::ALL {
24006 assert_eq!(
24007 qf.to_string(),
24008 qf.prefix(),
24009 "Display rendering for {qf:?} diverged from prefix() projection"
24010 );
24011 }
24012 }
24013
24014 #[test]
24015 fn quote_form_prefix_round_trips_through_from_str() {
24016 // BIDIRECTIONAL ROUND-TRIP: pin the structural identity
24017 // `qf.prefix().parse() == Ok(qf)` for every variant in
24018 // `QuoteForm::ALL`. This is the canonical law binding the
24019 // marker→string projection (`prefix`) to its string→marker dual
24020 // (`FromStr`). A regression that drifts EITHER side — `prefix`
24021 // routing `Quote` to `` "`" ``, OR `FromStr` decoding `"'"` to
24022 // `Quasiquote` — surfaces as a round-trip mismatch here. Sweep
24023 // all four variants so the round-trip stays load-bearing across
24024 // the closed set. Same posture as the
24025 // `unquote_form_marker_round_trips_through_from_str` sibling on
24026 // the 2-of-4 template-substitution subset axis and
24027 // `atom_kind_label_round_trips_through_from_str` on the peer
24028 // atomic-payload axis.
24029 for qf in QuoteForm::ALL {
24030 let prefix = qf.prefix();
24031 let decoded: QuoteForm = prefix
24032 .parse()
24033 .expect("canonical prefix must decode through FromStr");
24034 assert_eq!(
24035 decoded, qf,
24036 "FromStr ↔ prefix round-trip drifted for variant {qf:?} (prefix {prefix:?})"
24037 );
24038 }
24039 }
24040
24041 #[test]
24042 fn unknown_quote_form_carries_offending_input_verbatim() {
24043 // TYPED PARSE-FAILURE CONTRACT: pin the exact rendered shape of
24044 // `UnknownQuoteForm`'s `#[error(...)]` annotation AND the
24045 // verbatim `.0` field projection — no normalization, no case-
24046 // folding, no whitespace trimming. The error is part of the
24047 // substrate-wide `Unknown*` parse-rejection family
24048 // (`UnknownSexpShape`, `UnknownAtomKind`, `UnknownUnquoteForm`,
24049 // `UnknownRequestorKind`, `UnknownReceiptKind`, `UnknownPhase`,
24050 // `UnknownConditionKind`, `UnknownTeardownPolicy`, …) and the
24051 // joint rendered shape (`"unknown <thing>: {0}"`) is the
24052 // operator-facing diagnostic idiom every member preserves. A
24053 // regression that case-folds, trims, or strips the offending
24054 // input would silently rewrite an operator's literal value at
24055 // the diagnostic boundary — fails loudly here.
24056 let offending = "not-a-quote-prefix";
24057 let err: UnknownQuoteForm = offending
24058 .parse::<QuoteForm>()
24059 .expect_err("non-canonical input must reject through FromStr");
24060 assert_eq!(
24061 err.0, offending,
24062 "offending input was not preserved verbatim"
24063 );
24064 assert_eq!(
24065 err.to_string(),
24066 "unknown quote form: not-a-quote-prefix",
24067 "Display rendering diverged from the substrate-wide Unknown* idiom"
24068 );
24069 }
24070
24071 #[test]
24072 fn quote_form_is_well_formed_closed_set() {
24073 // Structural contract: QuoteForm's four variants are pairwise
24074 // distinct, round-trip through the trait's `label` ↔
24075 // `parse_label`, and reject the empty string — the
24076 // workspace-wide `assert_closed_set_well_formed::<T>()` testkit
24077 // pinned across every `tatara-process` closed-set implementor
24078 // (`AllocationPhase`, `RequestorKind`, `ProcessPhase`,
24079 // `ConditionKind`, `WorkloadKind`, …). The substrate-level
24080 // assertion runs on the auto-derived `impl ClosedSet for
24081 // QuoteForm` emitted by `#[derive(tatara_closed_set::DeriveClosedSet)]`
24082 // — a regression that drifts the derive's `make_unknown`
24083 // delegation, the `via = "prefix"` projection
24084 // (`"'" / "`" / "," / ",@"`), or the variant listing forced
24085 // through `Self::ALL` fails-loudly here in isolation from the
24086 // per-variant truth tables above.
24087 tatara_closed_set::assert_closed_set_well_formed::<QuoteForm>();
24088 }
24089
24090 #[test]
24091 fn quote_form_from_str_rejects_sexp_shape_labels_on_homoiconic_prefix_axis() {
24092 // CROSS-AXIS DISJOINTNESS: pin that `QuoteForm::FromStr` decodes
24093 // the homoiconic punctuation markers `'` / `` ` `` / `,` / `,@`
24094 // but rejects the `SexpShape` structural-identity vocabulary
24095 // (`"quote"` / `"quasiquote"` / `"unquote"` / `"unquote-splice"`)
24096 // AND the `iac_forge_tag` cross-crate canonical-form vocabulary
24097 // (`"quote"` / `"quasiquote"` / `"unquote"` / `"unquote-splicing"`).
24098 // The three closed sets project the SAME four `Sexp::*` quote-
24099 // family constructors on DISTINCT axes — a regression that
24100 // conflated them would let `"quote".parse::<QuoteForm>()` succeed
24101 // (silently bifurcating the diagnostic surface) or
24102 // `"'".parse::<SexpShape>()` succeed (silently colliding the
24103 // punctuation and structural-identity vocabularies). Sibling
24104 // discipline to `unquote_form_from_str_rejects_sexp_shape_labels_
24105 // on_template_marker_axis` (the 2-of-4 subset's matching
24106 // cross-axis pin).
24107 use crate::error::SexpShape;
24108 for shape in [
24109 SexpShape::Quote,
24110 SexpShape::Quasiquote,
24111 SexpShape::Unquote,
24112 SexpShape::UnquoteSplice,
24113 ] {
24114 let label = shape.label();
24115 assert!(
24116 label.parse::<QuoteForm>().is_err(),
24117 "SexpShape label {label:?} unexpectedly decoded through QuoteForm::FromStr — cross-axis vocabulary collision"
24118 );
24119 }
24120 for qf in QuoteForm::ALL {
24121 let tag = qf.iac_forge_tag();
24122 assert!(
24123 tag.parse::<QuoteForm>().is_err(),
24124 "iac_forge_tag {tag:?} unexpectedly decoded through QuoteForm::FromStr — cross-axis vocabulary collision"
24125 );
24126 }
24127 }
24128
24129 #[test]
24130 fn quote_form_from_str_extends_unquote_form_from_str_on_the_2_of_4_subset() {
24131 // SUBSET-CONTAINMENT CONTRACT: pin that every successful
24132 // `UnquoteForm::FromStr` input is ALSO a successful
24133 // `QuoteForm::FromStr` input, AND the resulting variants project
24134 // to each other through `QuoteForm::as_unquote_form` (the 2-of-4
24135 // subset gate). This binds the two homoiconic-prefix axes
24136 // (`UnquoteForm`'s 2-of-2 template-substitution subset and
24137 // `QuoteForm`'s full 4-of-4 quote-family) at the FromStr
24138 // boundary: a regression that drifts EITHER FromStr's vocabulary
24139 // from the other (e.g. `UnquoteForm::FromStr` adding a spelling
24140 // `","` rejects in `QuoteForm::FromStr` would surface) fails
24141 // loudly here. Composition law: for every `uf` in
24142 // `UnquoteForm::ALL`, `uf.marker().parse::<QuoteForm>()` is
24143 // `Ok(qf)` where `qf.as_unquote_form() == Some(uf)`.
24144 use crate::error::UnquoteForm;
24145 for uf in UnquoteForm::ALL {
24146 let marker = uf.marker();
24147 let qf: QuoteForm = marker.parse().unwrap_or_else(|_| {
24148 panic!(
24149 "UnquoteForm marker {marker:?} for {uf:?} did not decode through QuoteForm::FromStr — 2-of-4 subset containment violated"
24150 )
24151 });
24152 assert_eq!(
24153 qf.as_unquote_form(),
24154 Some(uf),
24155 "QuoteForm decoded from {marker:?} did not project back to UnquoteForm::{uf:?} via as_unquote_form"
24156 );
24157 }
24158 }
24159
24160 #[test]
24161 fn quote_form_wrap_derives_each_arm_to_its_pre_lift_box_new_form() {
24162 // PATH-UNIFORMITY CONTRACT: pin that `QuoteForm::wrap` is
24163 // observably equivalent to the pre-lift four-arm reader pattern
24164 // `Sexp::<Variant>(Box::new(inner))` across every variant of the
24165 // closed set. The reader's pre-lift parse arms each constructed
24166 // their corresponding wrapper inline; post-lift the parse routes
24167 // through `QuoteForm::wrap`. A regression that drifts the
24168 // projection's allocation posture (e.g. wraps in an extra layer,
24169 // or skips the `Box::new`) fails loudly here. Companion to the
24170 // `wrap` projection test above — that test pins the (marker,
24171 // constructor) pairing; this test pins the structural shape of
24172 // each wrap output bit-for-bit against the pre-lift inline form.
24173 let inner = Sexp::List(vec![Sexp::symbol("inner"), Sexp::int(7)]);
24174 for (qf, expected) in [
24175 (QuoteForm::Quote, Sexp::Quote(Box::new(inner.clone()))),
24176 (
24177 QuoteForm::Quasiquote,
24178 Sexp::Quasiquote(Box::new(inner.clone())),
24179 ),
24180 (QuoteForm::Unquote, Sexp::Unquote(Box::new(inner.clone()))),
24181 (
24182 QuoteForm::UnquoteSplice,
24183 Sexp::UnquoteSplice(Box::new(inner.clone())),
24184 ),
24185 ] {
24186 assert_eq!(
24187 qf.wrap(inner.clone()),
24188 expected,
24189 "wrap drifted from pre-lift Sexp::<Variant>(Box::new(inner)) form for {qf:?}"
24190 );
24191 }
24192 }
24193
24194 #[test]
24195 fn fmt_float_leaves_int_display_unchanged() {
24196 // Path-uniformity sibling: `Atom::Int` Display is unaffected by
24197 // the `fmt_float` introduction — the helper is wired only into
24198 // the `Atom::Float` arm of the Display match. A regression that
24199 // accidentally routes `Atom::Int` through `fmt_float` would
24200 // render `"1.0"` here and break every consumer that authored an
24201 // int kwarg expecting the bare-integer rendering.
24202 assert_eq!(Sexp::int(1).to_string(), "1");
24203 assert_eq!(Sexp::int(0).to_string(), "0");
24204 assert_eq!(Sexp::int(-42).to_string(), "-42");
24205 }
24206
24207 // ── AtomKind + Atom::kind: closed-set atomic-payload projection ─────
24208 //
24209 // `AtomKind` is the closed-set typed discriminator for `Atom`'s six
24210 // payload variants — `Symbol`, `Keyword`, `Str`, `Int`, `Float`,
24211 // `Bool`. It is the atomic-payload peer of `QuoteForm` (the four
24212 // homoiconic prefix wrappers), and the two closed sets together
24213 // carve every non-Nil non-List arm of `SexpShape`'s twelve-variant
24214 // closed set via their typed `sexp_shape` projections. Lifting the
24215 // (Atom variant, byte-discriminator, canonical-label,
24216 // SexpShape variant) quadruple onto ONE typed algebra collapses:
24217 // - `Hash for Atom`'s six byte literals (0/1/2/3/4/5) onto
24218 // `AtomKind::hash_discriminator` via `self.kind()` — ONE arm
24219 // at the discriminator site;
24220 // - `domain::sexp_shape`'s six `Atom::X(_) → SexpShape::X` arms
24221 // onto `a.kind().sexp_shape()` — ONE arm at the projection
24222 // site;
24223 // - any future LSP / REPL / metric-aggregator consumer that
24224 // needs to round-trip a rendered diagnostic label back into
24225 // the typed discriminator onto `AtomKind::FromStr` — ONE
24226 // decode site keyed on `AtomKind::ALL` + `AtomKind::label`.
24227 //
24228 // Tests below pin:
24229 // (a) `Atom::kind` projects every Atom variant to its typed
24230 // discriminator, regardless of inner payload contents;
24231 // (b) `AtomKind::ALL` enumerates every variant EXACTLY ONCE;
24232 // (c) `AtomKind::label` returns the canonical
24233 // lowercase / kebab string for every variant — byte-for-byte
24234 // identical to the corresponding `SexpShape::label`;
24235 // (d) `Display for AtomKind` delegates to `label`;
24236 // (e) `AtomKind::hash_discriminator` returns the same byte
24237 // values the pre-lift `Hash for Atom` arms emitted
24238 // (0/1/2/3/4/5) — pin the cache-key contract so a
24239 // regression that drifts a discriminator silently
24240 // invalidates every cached macro expansion fails loudly
24241 // here;
24242 // (f) `AtomKind::sexp_shape` projects every variant to the
24243 // matching `SexpShape` — the typed pairing the
24244 // `domain::sexp_shape` collapse relies on;
24245 // (g) `AtomKind::FromStr` round-trips every variant through its
24246 // label; rejects non-canonical capitalizations, empty input,
24247 // and the non-atom `SexpShape` labels (`"nil"`, `"list"`,
24248 // `"quote"`, `"quasiquote"`, `"unquote"`, `"unquote-splice"`);
24249 // (h) `UnknownAtomKind` carries the offending input verbatim and
24250 // renders the `#[error(...)]` annotation byte-exactly;
24251 // (i) `Hash for Atom` produces byte-identical hashes for every
24252 // atomic variant as the pre-lift implementation — pin the
24253 // cache-key contract end-to-end so the post-lift routing
24254 // through `AtomKind::hash_discriminator` cannot drift the
24255 // cache;
24256 // (j) the cross-projection composition law
24257 // `crate::domain::sexp_shape(&Sexp::Atom(a)) ==
24258 // a.kind().sexp_shape()` holds for every atomic kind.
24259
24260 #[test]
24261 fn atom_kind_projects_each_atom_variant_to_typed_marker() {
24262 // The structural identity `Atom::kind` establishes:
24263 // `Symbol(_) → AtomKind::Symbol`, `Keyword(_) →
24264 // AtomKind::Keyword`, etc. Pin every arm with a representative
24265 // payload + an empty / boundary payload so a regression that
24266 // matches on the payload rather than the variant identity
24267 // (e.g. a typo that routes `Str("")` to a different marker
24268 // than `Str("nonempty")`) surfaces immediately.
24269 assert_eq!(Atom::Symbol("foo".into()).kind(), AtomKind::Symbol);
24270 assert_eq!(Atom::Symbol(String::new()).kind(), AtomKind::Symbol);
24271 assert_eq!(Atom::Keyword("k".into()).kind(), AtomKind::Keyword);
24272 assert_eq!(Atom::Str("s".into()).kind(), AtomKind::Str);
24273 assert_eq!(Atom::Str(String::new()).kind(), AtomKind::Str);
24274 assert_eq!(Atom::Int(0).kind(), AtomKind::Int);
24275 assert_eq!(Atom::Int(i64::MIN).kind(), AtomKind::Int);
24276 assert_eq!(Atom::Int(i64::MAX).kind(), AtomKind::Int);
24277 assert_eq!(Atom::Float(0.0).kind(), AtomKind::Float);
24278 assert_eq!(Atom::Float(f64::NAN).kind(), AtomKind::Float);
24279 assert_eq!(Atom::Float(f64::INFINITY).kind(), AtomKind::Float);
24280 assert_eq!(Atom::Bool(true).kind(), AtomKind::Bool);
24281 assert_eq!(Atom::Bool(false).kind(), AtomKind::Bool);
24282 }
24283
24284 #[test]
24285 fn atom_kind_all_is_unique_and_complete() {
24286 // Closed-set posture: `ALL` enumerates every reachable variant
24287 // EXACTLY ONCE — no duplicates, no omissions. The `[Self; 6]`
24288 // array literal in the declaration forces the arity at compile
24289 // time; this test catches the orthogonal failure modes — a
24290 // future variant added at the type without being added to ALL
24291 // (silently dropped from every consumer's sweep), or a typo
24292 // that duplicates an entry (silently double-counted). Same
24293 // truth-table pinning every sibling closed-set lift in the
24294 // workspace uses (`SexpShape::ALL`, `RequestorKind::ALL`,
24295 // `ReceiptKind::ALL`, `ConditionKind::ALL`, `ProcessPhase::ALL`,
24296 // `ChannelKind::ALL`, …).
24297 //
24298 // The `iter+map+collect+sort_unstable` quadruple this test inlined
24299 // pre-lift now binds at `<AtomKind as ClosedSet>::sorted_labels()`
24300 // — the canonical-ordered candidate-list projection on the trait.
24301 // Distinctness of the sorted result is covered by
24302 // `assert_closed_set_well_formed::<AtomKind>()`, so this test
24303 // reduces to the per-implementor unique payload (the six diagnostic
24304 // labels in lexicographic order).
24305 assert_eq!(AtomKind::ALL.len(), 6);
24306 assert_eq!(
24307 <AtomKind as tatara_closed_set::ClosedSet>::sorted_labels(),
24308 vec!["bool", "float", "int", "keyword", "string", "symbol"],
24309 "AtomKind::ALL must cover every reachable Atom payload kind"
24310 );
24311 }
24312
24313 #[test]
24314 fn atom_kind_label_renders_canonical_string_for_every_variant() {
24315 // Pin every variant's canonical `&'static str` projection — a
24316 // regression that drifts any label (typo `"sym"` for
24317 // `"symbol"`, swap of `"int"` ↔ `"float"`, capitalization
24318 // drift `"String"` for `"string"`, or the `Str → "string"`
24319 // boundary rename being reversed to a literal `"str"`) fails-
24320 // loudly here. The six labels are byte-for-byte identical to
24321 // the corresponding `SexpShape::label` arms so the typed
24322 // diagnostic vocabulary stays unified across the AtomKind ⊂
24323 // SexpShape containment.
24324 assert_eq!(AtomKind::Symbol.label(), "symbol");
24325 assert_eq!(AtomKind::Keyword.label(), "keyword");
24326 assert_eq!(AtomKind::Str.label(), "string");
24327 assert_eq!(AtomKind::Int.label(), "int");
24328 assert_eq!(AtomKind::Float.label(), "float");
24329 assert_eq!(AtomKind::Bool.label(), "bool");
24330 }
24331
24332 #[test]
24333 fn atom_kind_label_agrees_with_sexp_shape_label_for_every_atom_arm() {
24334 // CROSS-PROJECTION VOCABULARY CONTRACT: each `AtomKind`
24335 // variant's `label()` is byte-for-byte identical to the
24336 // corresponding `SexpShape` variant's `label()` (after the
24337 // `Str → String` typed-variant rename which is intentional
24338 // — the wire vocabulary is `"string"` on both axes). Pin the
24339 // six-way agreement so a future label rename on EITHER side
24340 // (a SexpShape `"string"` → `"str"` drift, or an AtomKind
24341 // `"int"` → `"i64"` drift) fails-loudly here, NOT silently
24342 // at every cross-axis consumer. The pairing is load-bearing
24343 // for the typed-projection composition
24344 // `AtomKind::sexp_shape().label() == AtomKind::label()`.
24345 //
24346 // Post-lift this contract is structurally true by composition
24347 // (`AtomKind::label`'s body IS `self.sexp_shape().label()`),
24348 // so the cross-axis sweep is a tautology — the regression
24349 // surface lives at `SexpShape::label`'s atomic arms now,
24350 // pinned by `atom_kind_label_renders_canonical_string_for_every_variant`
24351 // (which keys the same six literals through the composition).
24352 // The sweep stays in place as a structural invariant pin in
24353 // case a future implementor reverses the lift and re-inlines
24354 // the per-variant arms here — drift between the two sites
24355 // would re-emerge and this test catches it.
24356 for kind in AtomKind::ALL {
24357 assert_eq!(
24358 kind.label(),
24359 kind.sexp_shape().label(),
24360 "label vocabulary drift between AtomKind::{kind:?} \
24361 and its SexpShape projection",
24362 );
24363 }
24364 }
24365
24366 #[test]
24367 fn atom_kind_label_routes_through_sexp_shape_label_via_sexp_shape_projection() {
24368 // ROUTING-PIN CONTRACT: post-lift `AtomKind::label`'s body
24369 // composes `Self::sexp_shape()` with `SexpShape::label()`
24370 // verbatim — no inline per-arm literal table. The composition
24371 // law `AtomKind::label(k) == AtomKind::sexp_shape(k).label()`
24372 // is structurally true for every `k: AtomKind`; pinning the
24373 // routing means a regression that re-inlines the six atomic-
24374 // arm literals here surfaces as a drift between the inline
24375 // copy and the `SexpShape::label` canonical site rather than
24376 // surviving silently.
24377 //
24378 // Six representative cases — one per variant — walked through
24379 // the composition manually and through the direct projection,
24380 // then byte-compared. A drift in EITHER half of the composition
24381 // (a typo in `Self::sexp_shape`'s match arms swapping
24382 // `Self::Int → SexpShape::Float`, OR a typo in `SexpShape::label`
24383 // dropping the `Int → "int"` arm) fails this assertion AND every
24384 // sibling per-arm assertion in
24385 // `atom_kind_label_renders_canonical_string_for_every_variant`
24386 // — but THIS test names the routing axis explicitly so a
24387 // regression to inline-literal-arms shows up as a failure of
24388 // the routing pin alongside the per-arm pin.
24389 //
24390 // Sibling-lift posture to the prior-run routing pins:
24391 // `sexp_to_json_object_arm_routes_through_is_kwargs_list_method`
24392 // (commit 4a11f5b) pins `Sexp::to_json`'s kwargs gate through
24393 // the lifted predicate. This pin extends the same posture to
24394 // `AtomKind::label`'s structural routing through the
24395 // `Self::sexp_shape() ∘ SexpShape::label` composition.
24396 //
24397 // Theory anchor: THEORY.md §V.1 — knowable platform; the
24398 // label-projection routing is a NAMED structural contract
24399 // pinned alongside the per-arm vocabulary contract, so
24400 // operators reading the test surface see BOTH the load-bearing
24401 // identity AND the load-bearing composition. THEORY.md §VI.1
24402 // — generation over composition; the label projection emerges
24403 // from the typed pairing rather than per-arm literal discipline,
24404 // and the routing pin enforces the lift stays in effect.
24405 for kind in AtomKind::ALL {
24406 let via_label = kind.label();
24407 let via_composition = kind.sexp_shape().label();
24408 assert_eq!(
24409 via_label, via_composition,
24410 "AtomKind::{kind:?}::label() must route through \
24411 Self::sexp_shape().label() — drift here means the \
24412 lift was reverted to inline arms",
24413 );
24414 // The pointer-equality check pins the composition produces
24415 // the SAME `&'static str` (not just a byte-equal copy) for
24416 // every variant — proof the routing hits ONE static literal
24417 // site (`SexpShape::label`) rather than a parallel inline
24418 // table.
24419 assert!(
24420 std::ptr::eq(via_label.as_ptr(), via_composition.as_ptr()),
24421 "AtomKind::{kind:?}::label() must return the SAME \
24422 `&'static str` as Self::sexp_shape().label() — \
24423 pointer drift means the lift composes through a \
24424 parallel literal table rather than routing into the \
24425 canonical SexpShape::label site",
24426 );
24427 }
24428 }
24429
24430 #[test]
24431 fn atom_kind_display_matches_label_for_every_variant() {
24432 // Pin Display-equals-label: any future
24433 // `#[error("... got {got}")]` annotation that threads through
24434 // this projection projects through Display, and Display
24435 // delegates to `label()`. A regression that introduces a
24436 // Display impl that deviates from `label()` (e.g. capitalizing
24437 // one variant) would drift any future diagnostic surface;
24438 // this test pins the contract. Sibling posture to
24439 // `sexp_shape_display_matches_label_for_every_variant` in
24440 // `error.rs`.
24441 assert_eq!(format!("{}", AtomKind::Symbol), "symbol");
24442 assert_eq!(format!("{}", AtomKind::Keyword), "keyword");
24443 assert_eq!(format!("{}", AtomKind::Str), "string");
24444 assert_eq!(format!("{}", AtomKind::Int), "int");
24445 assert_eq!(format!("{}", AtomKind::Float), "float");
24446 assert_eq!(format!("{}", AtomKind::Bool), "bool");
24447 }
24448
24449 #[test]
24450 fn atom_kind_hash_discriminator_pins_legacy_atom_cache_key_bytes() {
24451 // CACHE-KEY CONTRACT: pre-lift `Hash for Atom` used the literal
24452 // byte values 0/1/2/3/4/5 for Symbol/Keyword/Str/Int/Float/Bool
24453 // as the per-variant discriminator. The macro-expansion cache
24454 // (`Expander::cache`) keys on Hash; ANY change to a
24455 // discriminator byte silently invalidates every cached
24456 // expansion across the substrate. Pin the six legacy values
24457 // explicitly so a regression that re-numbers them surfaces
24458 // immediately — the `AtomKind` algebra MUST preserve the prior
24459 // byte mapping bit-for-bit. Sibling posture to
24460 // `quote_form_hash_discriminator_pins_legacy_cache_key_bytes`
24461 // on the quote-family axis.
24462 assert_eq!(AtomKind::Symbol.hash_discriminator(), 0);
24463 assert_eq!(AtomKind::Keyword.hash_discriminator(), 1);
24464 assert_eq!(AtomKind::Str.hash_discriminator(), 2);
24465 assert_eq!(AtomKind::Int.hash_discriminator(), 3);
24466 assert_eq!(AtomKind::Float.hash_discriminator(), 4);
24467 assert_eq!(AtomKind::Bool.hash_discriminator(), 5);
24468 }
24469
24470 #[test]
24471 fn atom_kind_hash_discriminator_bytes_are_pairwise_disjoint() {
24472 // Closed-set injectivity: the six discriminator bytes must
24473 // partition `{0, 1, 2, 3, 4, 5}` injectively so two distinct
24474 // `Atom` variants never produce the SAME hash discriminator —
24475 // a violation here means the cache could conflate two atomic
24476 // kinds with identical payloads (`Symbol("x")` and `Str("x")`
24477 // would silently share a cache slot). Sibling pin to
24478 // `atom_kind_all_is_unique_and_complete` on the label axis.
24479 let bytes: Vec<u8> = AtomKind::ALL
24480 .iter()
24481 .map(|k| k.hash_discriminator())
24482 .collect();
24483 let mut sorted = bytes.clone();
24484 sorted.sort_unstable();
24485 let mut deduped = sorted.clone();
24486 deduped.dedup();
24487 assert_eq!(
24488 sorted, deduped,
24489 "AtomKind hash discriminator bytes must be pairwise disjoint"
24490 );
24491 assert_eq!(sorted, vec![0, 1, 2, 3, 4, 5]);
24492 }
24493
24494 // ── `AtomKind::{SYMBOL_HASH_DISCRIMINATOR,
24495 // KEYWORD_HASH_DISCRIMINATOR, STR_HASH_DISCRIMINATOR,
24496 // INT_HASH_DISCRIMINATOR, FLOAT_HASH_DISCRIMINATOR,
24497 // BOOL_HASH_DISCRIMINATOR, HASH_DISCRIMINATORS}` — per-role `u8`
24498 // cache-key byte algebra on the closed-set atomic-payload
24499 // [`AtomKind`]. Second per-role axis on the algebra alongside the
24500 // diagnostic-label (commit fc126b8) `&'static str` axis — direct
24501 // sibling of the prior-run
24502 // [`crate::error::QuoteForm::HASH_DISCRIMINATORS`] lift (commit
24503 // cb9b026) on the quote-family sub-carving of the same outer-Sexp
24504 // Hash body.
24505 //
24506 // The two families partition their respective cache-key spaces
24507 // independently: `AtomKind` at `{0..=5}` NESTED inside
24508 // `Sexp::Atom`'s outer `1u8` byte (`Hash for Atom` runs on the
24509 // `Atom` type, not `Sexp`), `QuoteForm` at `{3..=6}` at the outer
24510 // `Sexp` cache-key space itself. Post-lift ALL FOUR closed-set
24511 // carvings that participate in `Hash for Sexp` / `Hash for Atom`
24512 // (`AtomKind` here, `QuoteForm` prior-run, `StructuralKind` still
24513 // inline `{0, 2}`, outer-`SexpShape` composed through the three
24514 // carvings) name their canonical bytes on the typed algebra rather
24515 // than at scattered inline literals.
24516
24517 #[test]
24518 fn atom_kind_hash_discriminators_pin_legacy_cache_key_bytes() {
24519 // Pin each per-role `pub(crate) const` at its exact canonical
24520 // `u8` byte. Sibling of
24521 // `atom_kind_hash_discriminator_pins_legacy_atom_cache_key_bytes`
24522 // (which pins the method's projection) — this pin asserts the
24523 // `pub(crate) const` value itself, so a regression that drifts
24524 // the constant but leaves the method's arm literal in place
24525 // (unlikely post-lift but structurally distinct) surfaces here.
24526 // The cache-key partition `{0..=5}` is load-bearing for
24527 // `Hash for Atom`'s injectivity contract — a `2u8` drift to
24528 // `0u8` would silently collide `AtomKind::Str` with
24529 // `AtomKind::Symbol`'s cache-key byte and mis-hash every
24530 // `Str(x)` through the `Symbol(x)` arm's cache slot.
24531 assert_eq!(AtomKind::SYMBOL_HASH_DISCRIMINATOR, 0);
24532 assert_eq!(AtomKind::KEYWORD_HASH_DISCRIMINATOR, 1);
24533 assert_eq!(AtomKind::STR_HASH_DISCRIMINATOR, 2);
24534 assert_eq!(AtomKind::INT_HASH_DISCRIMINATOR, 3);
24535 assert_eq!(AtomKind::FLOAT_HASH_DISCRIMINATOR, 4);
24536 assert_eq!(AtomKind::BOOL_HASH_DISCRIMINATOR, 5);
24537 }
24538
24539 #[test]
24540 fn atom_kind_hash_discriminator_routes_through_typed_per_role_constants() {
24541 // PATH-UNIFORMITY: `Self::hash_discriminator(self)` returns the
24542 // per-role `pub(crate) const` byte-for-byte per variant,
24543 // catching a regression that reverts ONE arm to an inline
24544 // `0` / `1` / `2` / `3` / `4` / `5` `u8` literal (or drifts one
24545 // arm's byte silently). Sibling posture to
24546 // `quote_form_hash_discriminator_routes_through_typed_per_role_constants`
24547 // on the quote-family sub-carving of the SAME outer-Sexp Hash
24548 // body.
24549 for (kind, expected) in [
24550 (AtomKind::Symbol, AtomKind::SYMBOL_HASH_DISCRIMINATOR),
24551 (AtomKind::Keyword, AtomKind::KEYWORD_HASH_DISCRIMINATOR),
24552 (AtomKind::Str, AtomKind::STR_HASH_DISCRIMINATOR),
24553 (AtomKind::Int, AtomKind::INT_HASH_DISCRIMINATOR),
24554 (AtomKind::Float, AtomKind::FLOAT_HASH_DISCRIMINATOR),
24555 (AtomKind::Bool, AtomKind::BOOL_HASH_DISCRIMINATOR),
24556 ] {
24557 let actual = kind.hash_discriminator();
24558 assert_eq!(
24559 actual, expected,
24560 "AtomKind::{kind:?}.hash_discriminator() `{actual}` \
24561 drifted from per-role constant `{expected}` — the \
24562 arm must route through the typed `pub(crate) const` \
24563 rather than an inline `u8` literal",
24564 );
24565 }
24566 }
24567
24568 #[test]
24569 fn atom_kind_hash_discriminators_has_expected_cardinality() {
24570 // Cardinality contract: `Self::HASH_DISCRIMINATORS.len() == 6`
24571 // — pinned at the declaration site by rustc's forced-arity
24572 // check on `[u8; 6]`. This test surfaces the arity as a
24573 // fail-loud runtime pin so a future refactor that switches the
24574 // array type to `&[u8]` (dropping the compile-time arity
24575 // forcing) doesn't silently loosen the closed-set discipline
24576 // the family relies on. Sibling posture to
24577 // `atom_kind_labels_has_expected_cardinality` on the diagnostic
24578 // label axis of the SAME closed set, and to
24579 // `quote_form_hash_discriminators_has_expected_cardinality`
24580 // on the quote-family sub-carving's cache-key-byte peer.
24581 assert_eq!(
24582 AtomKind::HASH_DISCRIMINATORS.len(),
24583 6,
24584 "AtomKind::HASH_DISCRIMINATORS cardinality drifted from 6 \
24585 — the closed atomic-payload domain admits exactly six \
24586 kinds by construction; a seventh extension surfaces here"
24587 );
24588 }
24589
24590 #[test]
24591 fn atom_kind_hash_discriminators_align_with_all_by_index() {
24592 // ALIGNMENT CONTRACT: `Self::HASH_DISCRIMINATORS[i] ==
24593 // Self::ALL[i].hash_discriminator()` element-wise. Pins that
24594 // the typed variant ALL and the `u8` HASH_DISCRIMINATORS ALL
24595 // stay in lockstep under any reorder — a regression that
24596 // reorders ONE array without reordering the other silently
24597 // misaligns every `zip(ALL, HASH_DISCRIMINATORS)` consumer.
24598 // Sibling posture to `atom_kind_labels_align_with_all_by_index`
24599 // on the diagnostic label axis of the SAME closed set.
24600 for (i, kind) in AtomKind::ALL.iter().enumerate() {
24601 assert_eq!(
24602 AtomKind::HASH_DISCRIMINATORS[i],
24603 kind.hash_discriminator(),
24604 "AtomKind::HASH_DISCRIMINATORS[{i}] `{disc}` drifted \
24605 from AtomKind::ALL[{i}] ({kind:?}).hash_discriminator() \
24606 `{via_variant}` — the canonical declaration order of \
24607 the ALL array and the hash_discriminator projection \
24608 must match element-wise",
24609 disc = AtomKind::HASH_DISCRIMINATORS[i],
24610 via_variant = kind.hash_discriminator(),
24611 );
24612 }
24613 }
24614
24615 #[test]
24616 fn atom_kind_hash_discriminators_pairwise_distinct() {
24617 // PAIRWISE DISJOINTNESS: every entry of the
24618 // `HASH_DISCRIMINATORS` array must differ so the nested `Atom`
24619 // Hash body cannot route two atomic-payload variants through
24620 // the same cache-key byte — a collision would silently mis-
24621 // hash two structurally-distinct atoms (`Symbol("x")` and
24622 // `Str("x")`) to the same `Expander::cache` slot. Family-wide
24623 // sweep over `HASH_DISCRIMINATORS × HASH_DISCRIMINATORS` —
24624 // supersedes any per-pair pin and picks up new discriminators
24625 // mechanically. Sibling posture to
24626 // `atom_kind_hash_discriminator_bytes_are_pairwise_disjoint`
24627 // (which sweeps via the projection) — this sweep pins the array
24628 // itself so a regression that lifts a fresh entry with a
24629 // colliding byte surfaces at the ALL sweep.
24630 for (i, a) in AtomKind::HASH_DISCRIMINATORS.iter().enumerate() {
24631 for (j, b) in AtomKind::HASH_DISCRIMINATORS.iter().enumerate() {
24632 if i == j {
24633 continue;
24634 }
24635 assert_ne!(
24636 a, b,
24637 "AtomKind::HASH_DISCRIMINATORS[{i}] `{a}` collides \
24638 with AtomKind::HASH_DISCRIMINATORS[{j}] `{b}` — \
24639 the nested Atom Hash body's cache-key partition \
24640 would route two atomic-payload variants through \
24641 the same slot"
24642 );
24643 }
24644 }
24645 }
24646
24647 #[test]
24648 fn atom_kind_outer_hash_discriminator_pins_legacy_atomic_carve_outer_marker_byte() {
24649 // Pin `AtomKind::OUTER_HASH_DISCRIMINATOR` at its exact canonical
24650 // `u8` byte — the outer-Sexp cache-key byte at which ALL SIX
24651 // atomic-payload shapes collapse when hashed at the outer
24652 // `Hash for Sexp` level. Pre-lift the same byte lived at four
24653 // sites: the inline `1u8` literal at
24654 // `SexpShape::hash_discriminator`'s six-arm atomic collapse (in
24655 // error.rs), the inline `1u8` literal at
24656 // `sexp_shape_hash_discriminator_atomic_arms_collapse_to_outer_atom_marker`'s
24657 // assertion body, the inline `1u8` literal at
24658 // `sexp_shape_hash_discriminator_partitions_by_three_way_carving_disjointly`'s
24659 // `expected_atomic` fixture, PLUS a duplicated local `const
24660 // ATOM_OUTER_CARVE_BYTE: u8 = 1` inside
24661 // `structural_kind_hash_discriminator_disjoint_from_atom_outer_carve_byte_and_quote_form_hash_discriminator_partition`.
24662 // Post-lift the byte binds at ONE `pub(crate) const` on the
24663 // closed-set `AtomKind` algebra; every downstream consumer
24664 // (the shape-level projection's atomic collapse arm, the two
24665 // three-way carving pins in error.rs, the joint-partition
24666 // disjointness pin in error.rs, a future `tatara-check`
24667 // predicate on the outer-Sexp cache-key partition) picks up
24668 // the same canonical byte from ONE source of truth. Sibling
24669 // posture to `atom_kind_hash_discriminators_pin_legacy_cache_key_bytes`
24670 // (which pins the six NESTED INNER `{0..=5}` bytes on the
24671 // per-atom-kind inner algebra) — this pin closes the (nested,
24672 // outer) pairing on the same `AtomKind` algebra by naming the
24673 // outer-Sexp marker byte alongside the nested inner carve.
24674 // The cache-key partition is load-bearing for the outer
24675 // `Hash for Sexp` prefix-uniqueness contract — a `1u8` drift
24676 // to `0u8` would silently collide the atomic-carve outer
24677 // marker with `StructuralKind::Nil`'s outer byte (`0`) and
24678 // mis-hash every `Sexp::Atom(_)` through the `Sexp::Nil`
24679 // arm's cache slot; a drift to `2u8` would collide with
24680 // `StructuralKind::List`'s outer byte and mis-hash through
24681 // the `Sexp::List(_)` arm's slot; a drift to `3u8` /
24682 // `4u8` / `5u8` / `6u8` would collide with the quote-family
24683 // carve's four bytes.
24684 assert_eq!(AtomKind::OUTER_HASH_DISCRIMINATOR, 1);
24685 }
24686
24687 #[test]
24688 fn atom_kind_outer_hash_discriminator_disjoint_from_inner_hash_discriminators_at_outer_sexp_level(
24689 ) {
24690 // OUTER-vs-INNER SEPARATION: the outer-Sexp cache-key algebra
24691 // uses TWO distinct byte spaces at TWO hash-sequence positions
24692 // for the atomic-carve: `AtomKind::OUTER_HASH_DISCRIMINATOR`
24693 // (`1u8` at the outer `Hash for Sexp` position) AND the
24694 // NESTED INNER `AtomKind::HASH_DISCRIMINATORS` bytes (`{0..=5}`
24695 // at the inner `Hash for Atom` position). The two byte spaces
24696 // OVERLAP numerically (both contain `1u8`) but do NOT collide
24697 // at the cache because they live at DIFFERENT hash-sequence
24698 // positions in the composed `(outer_discriminator,
24699 // inner_discriminator, inner_payload)` triple. Pin the outer
24700 // scalar's byte at `AtomKind::OUTER_HASH_DISCRIMINATOR` and
24701 // the inner set at `AtomKind::HASH_DISCRIMINATORS` so a future
24702 // refactor that conflates the two axes (e.g. drops the outer
24703 // marker byte at `Hash for Sexp`'s Atom arm and expects the
24704 // inner byte to distinguish outer-Sexp variants directly)
24705 // surfaces at THIS test as a documentation-of-intent failure.
24706 // The check is intentionally structural: it asserts the outer
24707 // scalar is IN the same numeric range as the inner set (both
24708 // are `u8`, both live within `{0..=6}` on the outer cache-key
24709 // partition) BUT that the outer scalar sits at position 1
24710 // where the inner byte space would otherwise have
24711 // `AtomKind::KEYWORD_HASH_DISCRIMINATOR` collide. That
24712 // numeric-collision without cache-collision is the
24713 // load-bearing property this lift documents: the two axes are
24714 // typed distinct because they live at typed distinct
24715 // positions in the composed hash sequence, even though their
24716 // byte spaces overlap.
24717 assert!(
24718 AtomKind::HASH_DISCRIMINATORS.contains(&AtomKind::OUTER_HASH_DISCRIMINATOR),
24719 "the outer-carve marker byte `{outer}` MUST lie within the \
24720 inner cache-key partition `{inner:?}` — the two axes' \
24721 byte spaces overlap by design (the outer distinguishes \
24722 `Sexp::Atom(_)` from every other outer-Sexp variant at \
24723 the outer hash position; the inner distinguishes the six \
24724 atomic-payload variants at the nested inner hash \
24725 position). If this contains check fails, the outer \
24726 scalar has drifted OUTSIDE the inner partition and the \
24727 (outer, inner) numeric-overlap-without-cache-collision \
24728 property this lift documents no longer holds — which \
24729 would in turn mean either the outer byte drifted out of \
24730 `{{0..=5}}` (compile the substrate against the resulting \
24731 outer-Sexp partition to find the drift) or the inner \
24732 partition shrank below `{{0..=5}}` (`atom_kind_hash_discriminators_align_with_all_by_index` \
24733 fails-loudly first).",
24734 outer = AtomKind::OUTER_HASH_DISCRIMINATOR,
24735 inner = AtomKind::HASH_DISCRIMINATORS,
24736 );
24737 }
24738
24739 #[test]
24740 fn atom_kind_sexp_shape_pins_canonical_shape_identity_for_every_variant() {
24741 // CLOSED-SET SHAPE-PROJECTION CONTRACT: each `AtomKind` variant
24742 // projects to its matching `SexpShape` variant — load-bearing
24743 // for the (Atom variant, SexpShape variant) pairing the
24744 // substrate's outer-shape projection `domain::sexp_shape` routes
24745 // through. Sibling-arm sweep so the six pairings stay
24746 // load-bearing under reordering refactors. A regression that
24747 // drifts ONE arm (e.g. routes `AtomKind::Int` to
24748 // `SexpShape::Float`) surfaces here immediately rather than as
24749 // a silent operator-facing diagnostic drift at every
24750 // `LispError::TypeMismatch.got` slot for an atomic witness.
24751 // Sibling posture to
24752 // `quote_form_sexp_shape_pins_canonical_shape_identity_for_every_variant`.
24753 assert_eq!(AtomKind::Symbol.sexp_shape(), SexpShape::Symbol);
24754 assert_eq!(AtomKind::Keyword.sexp_shape(), SexpShape::Keyword);
24755 assert_eq!(AtomKind::Str.sexp_shape(), SexpShape::String);
24756 assert_eq!(AtomKind::Int.sexp_shape(), SexpShape::Int);
24757 assert_eq!(AtomKind::Float.sexp_shape(), SexpShape::Float);
24758 assert_eq!(AtomKind::Bool.sexp_shape(), SexpShape::Bool);
24759 }
24760
24761 #[test]
24762 fn atom_kind_per_role_shapes_pin_canonical_sexp_shape_variants() {
24763 // PER-ROLE ALIAS CONTRACT: each `AtomKind::*_SHAPE` per-role
24764 // `pub const` binds byte-for-byte to its canonical `SexpShape`
24765 // variant on the AtomKind ⊂ SexpShape carving. Pin so a
24766 // regression that swaps ONE alias (e.g. re-aims `STR_SHAPE`
24767 // at `SexpShape::Symbol`) surfaces at rustc / test time
24768 // rather than as a silent operator-facing diagnostic drift at
24769 // every consumer keyed on the typed embed target. Sibling
24770 // posture to `atom_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
24771 // on the diagnostic label axis of the SAME closed set — this
24772 // pin is the peer on the `SexpShape` embed-target axis.
24773 assert_eq!(AtomKind::SYMBOL_SHAPE, SexpShape::Symbol);
24774 assert_eq!(AtomKind::KEYWORD_SHAPE, SexpShape::Keyword);
24775 assert_eq!(AtomKind::STR_SHAPE, SexpShape::String);
24776 assert_eq!(AtomKind::INT_SHAPE, SexpShape::Int);
24777 assert_eq!(AtomKind::FLOAT_SHAPE, SexpShape::Float);
24778 assert_eq!(AtomKind::BOOL_SHAPE, SexpShape::Bool);
24779 }
24780
24781 #[test]
24782 fn atom_kind_shapes_has_expected_cardinality() {
24783 // CLOSED-SET CARDINALITY CONTRACT: `AtomKind::SHAPES` carries
24784 // exactly SIX entries — one per variant in the closed-set
24785 // atomic-payload carving. Runtime companion to the `[SexpShape;
24786 // 6]` type annotation's compile-time forced arity. A regression
24787 // that widens the array without adding a matching per-role
24788 // `*_SHAPE` alias would fail the `[SexpShape; 6]` type check
24789 // at rustc time; this runtime pin catches a silent shrink or
24790 // duplicate-arm coalesce. Sibling posture to
24791 // `atom_kind_labels_has_expected_cardinality` and
24792 // `atom_kind_hash_discriminators_has_expected_cardinality` on
24793 // the other two per-role axes of the SAME closed set.
24794 assert_eq!(AtomKind::SHAPES.len(), 6);
24795 assert_eq!(AtomKind::SHAPES.len(), AtomKind::ALL.len());
24796 }
24797
24798 #[test]
24799 fn atom_kind_shapes_align_with_all_by_index() {
24800 // ALIGNMENT CONTRACT: `Self::SHAPES[i] ==
24801 // Self::ALL[i].sexp_shape()` element-wise. Pins that the typed
24802 // variant ALL and the `SexpShape` SHAPES ALL stay in lockstep
24803 // under any reorder — a regression that reorders ONE array
24804 // without reordering the other silently misaligns every
24805 // `zip(ALL, SHAPES)` consumer that wants to project each
24806 // atomic variant to its canonical outer-shape identity
24807 // (LSP completion, `tatara-check` predicate over the
24808 // atomic ⊂ outer partition, Sekiban audit-trail metric
24809 // jointly labeled by the embed target). Sibling posture to
24810 // `atom_kind_labels_align_with_all_by_index` and
24811 // `atom_kind_hash_discriminators_align_with_all_by_index` on
24812 // the other two per-role axes of the SAME closed set.
24813 for (i, kind) in AtomKind::ALL.iter().enumerate() {
24814 assert_eq!(
24815 AtomKind::SHAPES[i],
24816 kind.sexp_shape(),
24817 "AtomKind::SHAPES[{i}] `{shape:?}` drifted from \
24818 AtomKind::ALL[{i}] ({kind:?}).sexp_shape() \
24819 `{via_variant:?}` — the canonical declaration order \
24820 of the ALL array and the sexp_shape projection must \
24821 match element-wise",
24822 shape = AtomKind::SHAPES[i],
24823 via_variant = kind.sexp_shape(),
24824 );
24825 }
24826 }
24827
24828 #[test]
24829 fn atom_kind_shapes_align_with_all_by_index_through_as_atom_kind() {
24830 // ROUND-TRIP CONTRACT: `Self::SHAPES[i].as_atom_kind() ==
24831 // Some(Self::ALL[i])` element-wise. Pins the embed / project
24832 // section of the (`AtomKind::sexp_shape`,
24833 // `SexpShape::as_atom_kind`) `Iso(AtomKind, AtomShape ⊂
24834 // SexpShape)` as a family-wide array-indexed law rather than
24835 // as a per-variant assertion sweep. A regression that drifts
24836 // EITHER the `SHAPES` array entries OR the peer inverse
24837 // `as_atom_kind` arms silently breaks the (embed, project)
24838 // section — this pin catches both directions at ONCE.
24839 // Sibling posture to the pre-existing per-variant round-trip
24840 // sweep `atom_kind_sexp_shape_round_trips_through_sexp_shape_as_atom_kind`
24841 // (which sweeps through the projection method); this sweep
24842 // pins the round-trip through the SHAPES array directly so a
24843 // regression on ANY of the three surfaces (per-role alias,
24844 // family-wide array, projection method) fails-loudly at the
24845 // array-indexed sweep.
24846 for (i, kind) in AtomKind::ALL.iter().enumerate() {
24847 assert_eq!(
24848 AtomKind::SHAPES[i].as_atom_kind(),
24849 Some(*kind),
24850 "AtomKind::SHAPES[{i}] `{shape:?}`.as_atom_kind() = \
24851 {actual:?} drifted from Some(AtomKind::ALL[{i}]) = \
24852 Some({expected:?}) — the (SHAPES entry, ALL variant) \
24853 round-trip through the peer inverse SexpShape::as_atom_kind \
24854 must hold element-wise",
24855 shape = AtomKind::SHAPES[i],
24856 actual = AtomKind::SHAPES[i].as_atom_kind(),
24857 expected = kind,
24858 );
24859 }
24860 }
24861
24862 #[test]
24863 fn atom_kind_sexp_shape_routes_through_typed_per_role_constants() {
24864 // ROUTING CONTRACT: `AtomKind::sexp_shape()`'s six arms bind
24865 // through the per-role `pub const *_SHAPE` aliases rather
24866 // than through inline `SexpShape::X` literals. Pin so a
24867 // regression that re-inlines the six literals here (and
24868 // gains its own drift surface separate from the canonical
24869 // per-role alias site) surfaces immediately at variant
24870 // equality. Sibling posture to
24871 // `atom_kind_label_arms_route_through_per_role_labels_for_every_variant`
24872 // and `atom_kind_hash_discriminator_routes_through_typed_per_role_constants`
24873 // on the other two per-role axes of the SAME closed set.
24874 assert_eq!(AtomKind::Symbol.sexp_shape(), AtomKind::SYMBOL_SHAPE);
24875 assert_eq!(AtomKind::Keyword.sexp_shape(), AtomKind::KEYWORD_SHAPE);
24876 assert_eq!(AtomKind::Str.sexp_shape(), AtomKind::STR_SHAPE);
24877 assert_eq!(AtomKind::Int.sexp_shape(), AtomKind::INT_SHAPE);
24878 assert_eq!(AtomKind::Float.sexp_shape(), AtomKind::FLOAT_SHAPE);
24879 assert_eq!(AtomKind::Bool.sexp_shape(), AtomKind::BOOL_SHAPE);
24880 }
24881
24882 #[test]
24883 fn atom_kind_shapes_pairwise_distinct() {
24884 // PAIRWISE DISJOINTNESS: every entry of the `SHAPES` array
24885 // must differ so the (AtomKind variant, SexpShape embed
24886 // target) mapping stays injective — a collision would silently
24887 // route two distinct AtomKind variants through the SAME
24888 // outer-shape identity, breaking the AtomKind ⊂ SexpShape
24889 // 6-of-12 carving. Family-wide sweep over `SHAPES × SHAPES` —
24890 // supersedes any per-pair pin and picks up new embed targets
24891 // mechanically. Sibling posture to
24892 // `atom_kind_labels_pairwise_distinct` and
24893 // `atom_kind_hash_discriminators_pairwise_distinct` on the
24894 // other two per-role axes of the SAME closed set.
24895 for (i, a) in AtomKind::SHAPES.iter().enumerate() {
24896 for (j, b) in AtomKind::SHAPES.iter().enumerate() {
24897 if i == j {
24898 continue;
24899 }
24900 assert_ne!(
24901 a, b,
24902 "AtomKind::SHAPES[{i}] `{a:?}` collides with \
24903 AtomKind::SHAPES[{j}] `{b:?}` — the AtomKind ⊂ \
24904 SexpShape 6-of-12 carving would route two atomic \
24905 variants through the same outer-shape identity"
24906 );
24907 }
24908 }
24909 }
24910
24911 #[test]
24912 fn quote_form_per_role_shapes_pin_canonical_sexp_shape_variants() {
24913 // PER-ROLE ALIAS CONTRACT: each `QuoteForm::*_SHAPE` per-role
24914 // `pub const` binds byte-for-byte to its canonical `SexpShape`
24915 // variant on the QuoteForm ⊂ SexpShape 4-of-12 carving. Pin
24916 // so a regression that swaps ONE alias (e.g. re-aims
24917 // `UNQUOTE_SPLICE_SHAPE` at `SexpShape::Unquote`) surfaces at
24918 // rustc / test time rather than as a silent operator-facing
24919 // diagnostic drift at every consumer keyed on the typed embed
24920 // target. Sibling posture to
24921 // `quote_form_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte`
24922 // on the diagnostic label axis of the SAME closed set — this
24923 // pin is the peer on the `SexpShape` embed-target axis.
24924 assert_eq!(QuoteForm::QUOTE_SHAPE, SexpShape::Quote);
24925 assert_eq!(QuoteForm::QUASIQUOTE_SHAPE, SexpShape::Quasiquote);
24926 assert_eq!(QuoteForm::UNQUOTE_SHAPE, SexpShape::Unquote);
24927 assert_eq!(QuoteForm::UNQUOTE_SPLICE_SHAPE, SexpShape::UnquoteSplice);
24928 }
24929
24930 #[test]
24931 fn quote_form_shapes_has_expected_cardinality() {
24932 // CLOSED-SET CARDINALITY CONTRACT: `QuoteForm::SHAPES` carries
24933 // exactly FOUR entries — one per variant in the closed-set
24934 // quote-family carving. Runtime companion to the `[SexpShape;
24935 // 4]` type annotation's compile-time forced arity. A regression
24936 // that widens the array without adding a matching per-role
24937 // `*_SHAPE` alias would fail the `[SexpShape; 4]` type check
24938 // at rustc time; this runtime pin catches a silent shrink or
24939 // duplicate-arm coalesce. Sibling posture to
24940 // `quote_form_labels_has_expected_cardinality` /
24941 // `quote_form_prefixes_has_expected_cardinality` /
24942 // `quote_form_iac_forge_tags_has_expected_cardinality` /
24943 // `quote_form_hash_discriminators_has_expected_cardinality` on
24944 // the other four per-role axes of the SAME closed set.
24945 assert_eq!(QuoteForm::SHAPES.len(), 4);
24946 assert_eq!(QuoteForm::SHAPES.len(), QuoteForm::ALL.len());
24947 }
24948
24949 #[test]
24950 fn quote_form_shapes_align_with_all_by_index() {
24951 // ALIGNMENT CONTRACT: `Self::SHAPES[i] ==
24952 // Self::ALL[i].sexp_shape()` element-wise. Pins that the typed
24953 // variant ALL and the `SexpShape` SHAPES ALL stay in lockstep
24954 // under any reorder — a regression that reorders ONE array
24955 // without reordering the other silently misaligns every
24956 // `zip(ALL, SHAPES)` consumer that wants to project each
24957 // quote-family variant to its canonical outer-shape identity
24958 // (LSP completion, `tatara-check` predicate over the quote-
24959 // family ⊂ outer partition, Sekiban audit-trail metric jointly
24960 // labeled by the embed target). Sibling posture to
24961 // `quote_form_labels_align_with_all_by_index` /
24962 // `quote_form_prefixes_align_with_all_by_index` on the other
24963 // per-role axes of the SAME closed set.
24964 for (i, form) in QuoteForm::ALL.iter().enumerate() {
24965 assert_eq!(
24966 QuoteForm::SHAPES[i],
24967 form.sexp_shape(),
24968 "QuoteForm::SHAPES[{i}] `{shape:?}` drifted from \
24969 QuoteForm::ALL[{i}] ({form:?}).sexp_shape() \
24970 `{via_variant:?}` — the canonical declaration order \
24971 of the ALL array and the sexp_shape projection must \
24972 match element-wise",
24973 shape = QuoteForm::SHAPES[i],
24974 via_variant = form.sexp_shape(),
24975 );
24976 }
24977 }
24978
24979 #[test]
24980 fn quote_form_shapes_align_with_all_by_index_through_as_quote_form() {
24981 // ROUND-TRIP CONTRACT: `Self::SHAPES[i].as_quote_form() ==
24982 // Some(Self::ALL[i])` element-wise. Pins the embed / project
24983 // section of the (`QuoteForm::sexp_shape`,
24984 // `SexpShape::as_quote_form`) `Iso(QuoteForm, QuoteShape ⊂
24985 // SexpShape)` as a family-wide array-indexed law rather than
24986 // as a per-variant assertion sweep. A regression that drifts
24987 // EITHER the `SHAPES` array entries OR the peer inverse
24988 // `as_quote_form` arms silently breaks the (embed, project)
24989 // section — this pin catches both directions at ONCE. Sibling
24990 // posture to
24991 // `atom_kind_shapes_align_with_all_by_index_through_as_atom_kind`
24992 // on the peer 6-of-12 atomic-payload carving.
24993 for (i, form) in QuoteForm::ALL.iter().enumerate() {
24994 assert_eq!(
24995 QuoteForm::SHAPES[i].as_quote_form(),
24996 Some(*form),
24997 "QuoteForm::SHAPES[{i}] `{shape:?}`.as_quote_form() = \
24998 {actual:?} drifted from Some(QuoteForm::ALL[{i}]) = \
24999 Some({expected:?}) — the (SHAPES entry, ALL variant) \
25000 round-trip through the peer inverse SexpShape::as_quote_form \
25001 must hold element-wise",
25002 shape = QuoteForm::SHAPES[i],
25003 actual = QuoteForm::SHAPES[i].as_quote_form(),
25004 expected = form,
25005 );
25006 }
25007 }
25008
25009 #[test]
25010 fn quote_form_sexp_shape_routes_through_typed_per_role_constants() {
25011 // ROUTING CONTRACT: `QuoteForm::sexp_shape()`'s four arms bind
25012 // through the per-role `pub const *_SHAPE` aliases rather than
25013 // through inline `SexpShape::X` literals. Pin so a regression
25014 // that re-inlines the four literals here (and gains its own
25015 // drift surface separate from the canonical per-role alias
25016 // site) surfaces immediately at variant equality. Sibling
25017 // posture to
25018 // `quote_form_label_composes_through_sexp_shape_label_for_every_variant`
25019 // (which pins label routing through sexp_shape().label()) and
25020 // `atom_kind_sexp_shape_routes_through_typed_per_role_constants`
25021 // on the peer 6-of-12 atomic-payload carving.
25022 assert_eq!(QuoteForm::Quote.sexp_shape(), QuoteForm::QUOTE_SHAPE);
25023 assert_eq!(
25024 QuoteForm::Quasiquote.sexp_shape(),
25025 QuoteForm::QUASIQUOTE_SHAPE
25026 );
25027 assert_eq!(QuoteForm::Unquote.sexp_shape(), QuoteForm::UNQUOTE_SHAPE);
25028 assert_eq!(
25029 QuoteForm::UnquoteSplice.sexp_shape(),
25030 QuoteForm::UNQUOTE_SPLICE_SHAPE
25031 );
25032 }
25033
25034 #[test]
25035 fn quote_form_shapes_pairwise_distinct() {
25036 // PAIRWISE DISJOINTNESS: every entry of the `SHAPES` array
25037 // must differ so the (QuoteForm variant, SexpShape embed
25038 // target) mapping stays injective — a collision would silently
25039 // route two distinct QuoteForm variants through the SAME
25040 // outer-shape identity, breaking the QuoteForm ⊂ SexpShape
25041 // 4-of-12 carving. Family-wide sweep over `SHAPES × SHAPES` —
25042 // supersedes any per-pair pin and picks up new embed targets
25043 // mechanically. Sibling posture to
25044 // `atom_kind_shapes_pairwise_distinct` on the peer 6-of-12
25045 // atomic-payload carving.
25046 for (i, a) in QuoteForm::SHAPES.iter().enumerate() {
25047 for (j, b) in QuoteForm::SHAPES.iter().enumerate() {
25048 if i == j {
25049 continue;
25050 }
25051 assert_ne!(
25052 a, b,
25053 "QuoteForm::SHAPES[{i}] `{a:?}` collides with \
25054 QuoteForm::SHAPES[{j}] `{b:?}` — the QuoteForm ⊂ \
25055 SexpShape 4-of-12 carving would route two quote-\
25056 family variants through the same outer-shape identity"
25057 );
25058 }
25059 }
25060 }
25061
25062 #[test]
25063 fn atom_kind_label_round_trips_through_from_str() {
25064 // Bidirectional `label` ↔ `FromStr` contract: for every variant
25065 // in ALL, `kind.label().parse() == Ok(kind)`. A regression that
25066 // drifts the (variant, literal) pairing at ONE arm of `label`
25067 // (typo, capitalization drift) OR at the `FromStr` decode body
25068 // (off-by-one, missing variant in the sweep) fails-loudly here.
25069 // The canonical-literal site is singular (`label`) so the
25070 // round-trip is the only way the typed surface and the
25071 // rendered diagnostic literal can drift apart — pinning it
25072 // here means they cannot. Sibling posture to
25073 // `sexp_shape_label_round_trips_through_from_str`.
25074 for kind in AtomKind::ALL {
25075 let parsed: AtomKind = kind
25076 .label()
25077 .parse()
25078 .expect("every ALL variant's label must round-trip through FromStr");
25079 assert_eq!(
25080 parsed,
25081 kind,
25082 "FromStr({}) must round-trip to the same variant",
25083 kind.label()
25084 );
25085 }
25086 }
25087
25088 #[test]
25089 fn unknown_atom_kind_carries_offending_input_verbatim() {
25090 // Operator-facing diagnostic contract: the offending input
25091 // lands in the typed error verbatim — no normalization, no
25092 // case-folding, no truncation. Pin the exact `#[error(...)]`
25093 // rendering AND the typed `.0` field projection so a future
25094 // refactor that normalizes (e.g. `.to_lowercase()`) before
25095 // building the error or that drops the input fails-loudly
25096 // here. Symmetric to every sibling `Unknown*` carrier in the
25097 // workspace.
25098 let err: UnknownAtomKind = "Symbol".parse::<AtomKind>().expect_err(
25099 "capitalized `Symbol` must NOT decode — labels are byte-equal case-sensitive",
25100 );
25101 assert_eq!(err.0, "Symbol");
25102 assert_eq!(format!("{err}"), "unknown atom kind: Symbol");
25103
25104 let err: UnknownAtomKind = "str"
25105 .parse::<AtomKind>()
25106 .expect_err("`str` is not a canonical AtomKind label — `string` is");
25107 assert_eq!(err.0, "str");
25108 assert_eq!(format!("{err}"), "unknown atom kind: str");
25109
25110 let err: UnknownAtomKind = ""
25111 .parse::<AtomKind>()
25112 .expect_err("empty input must NOT decode to an AtomKind");
25113 assert_eq!(err.0, "");
25114 assert_eq!(format!("{err}"), "unknown atom kind: ");
25115 }
25116
25117 #[test]
25118 fn atom_kind_from_str_rejects_non_atom_sexp_shape_labels() {
25119 // CROSS-AXIS GUARD: `SexpShape::label()`'s vocabulary is the
25120 // SUPERSET of `AtomKind::label()`'s — every AtomKind label
25121 // decodes successfully through SexpShape's FromStr to the
25122 // matching SexpShape variant (because the typed projections
25123 // agree), but the SIX non-atom SexpShape labels (`"nil"`,
25124 // `"list"`, `"quote"`, `"quasiquote"`, `"unquote"`,
25125 // `"unquote-splice"`) MUST reject through AtomKind's FromStr
25126 // — they have no atomic-kind preimage. A FromStr that
25127 // silently accepted `"list"` as an AtomKind would corrupt
25128 // the typed identity downstream of any future diagnostic
25129 // round-trip. Pin BOTH directions: the six atom labels
25130 // decode successfully (and to the matching `AtomKind`
25131 // variant), the six non-atom labels reject.
25132 assert_eq!("symbol".parse::<AtomKind>().unwrap(), AtomKind::Symbol);
25133 assert_eq!("keyword".parse::<AtomKind>().unwrap(), AtomKind::Keyword);
25134 assert_eq!("string".parse::<AtomKind>().unwrap(), AtomKind::Str);
25135 assert_eq!("int".parse::<AtomKind>().unwrap(), AtomKind::Int);
25136 assert_eq!("float".parse::<AtomKind>().unwrap(), AtomKind::Float);
25137 assert_eq!("bool".parse::<AtomKind>().unwrap(), AtomKind::Bool);
25138
25139 // Non-atom SexpShape labels (the six structural shapes
25140 // OUTSIDE the AtomKind closed set) must reject.
25141 for label in [
25142 "nil",
25143 "list",
25144 "quote",
25145 "quasiquote",
25146 "unquote",
25147 "unquote-splice",
25148 ] {
25149 assert!(
25150 label.parse::<AtomKind>().is_err(),
25151 "non-atom SexpShape label {label:?} must NOT decode to an AtomKind",
25152 );
25153 }
25154
25155 // Sanity: typed peers' labels (`UnquoteForm::marker`'s
25156 // `,` / `,@` punctuation, `ExpectedKwargShape`'s
25157 // `"number"` / `"list of strings"` vocabulary) live on
25158 // different axes and MUST reject too — pin the closed-set
25159 // boundary.
25160 for label in [",", ",@", "number", "list of strings", "atom", "Atom"] {
25161 assert!(
25162 label.parse::<AtomKind>().is_err(),
25163 "cross-axis label {label:?} must NOT decode to an AtomKind",
25164 );
25165 }
25166 }
25167
25168 #[test]
25169 fn atom_kind_is_well_formed_closed_set() {
25170 // Structural contract: AtomKind's six variants are pairwise
25171 // distinct, round-trip through the trait's `label` ↔
25172 // `parse_label`, and reject the empty string — the
25173 // workspace-wide `assert_closed_set_well_formed::<T>()` testkit
25174 // pinned across every `tatara-process` closed-set implementor
25175 // (`AllocationPhase`, `RequestorKind`, `ProcessPhase`,
25176 // `ConditionKind`, `WorkloadKind`, …). The substrate-level
25177 // assertion runs on the auto-derived `impl ClosedSet for
25178 // AtomKind` emitted by `#[derive(tatara_closed_set::DeriveClosedSet)]`
25179 // — a regression that drifts the derive's `make_unknown`
25180 // delegation, the `via = "label"` projection, or the variant
25181 // listing forced through `Self::ALL` fails-loudly here in
25182 // isolation from the per-variant truth tables above.
25183 tatara_closed_set::assert_closed_set_well_formed::<AtomKind>();
25184 }
25185
25186 #[test]
25187 fn atom_kind_per_role_labels_alias_sexp_shape_per_role_labels_byte_for_byte() {
25188 // ALIAS CONTRACT: pin every one of the six per-role
25189 // `pub const AtomKind::*_LABEL` aliases equals the corresponding
25190 // `pub const SexpShape::*_LABEL` byte-for-byte — so the AtomKind
25191 // ⊂ SexpShape marker-vocabulary containment routes through the
25192 // typed `pub const AtomKind::V_LABEL: &'static str =
25193 // SexpShape::V_LABEL` alias chain rather than through two
25194 // independent literal-discipline sites. A regression that
25195 // renames the SexpShape side without updating the AtomKind
25196 // alias pointing at it fails-loudly here with the exact axis
25197 // identified (SYMBOL / KEYWORD / STRING / INT / FLOAT / BOOL);
25198 // a regression that re-inlines the AtomKind constant to a
25199 // fresh literal still passes this pin but loses the alias-
25200 // chain typing (which is what
25201 // `atom_kind_label_arms_route_through_per_role_labels_for_every_variant`
25202 // + `atom_kind_labels_align_with_all_by_index` catch in
25203 // combination).
25204 //
25205 // Six per-role checks, each spelled out so a regression on ONE
25206 // variant surfaces the exact axis rather than through a
25207 // variant-loop that hides which arm drifted. The `Str →
25208 // String` boundary rename is intentional and load-bearing (the
25209 // wire vocabulary is `"string"` on both axes) — the
25210 // STRING_LABEL alias is the canonical bridge, so a future
25211 // rename that reverses the `Str → "string"` rename to a
25212 // literal `"str"` fails the byte-equality pin at THIS test.
25213 assert_eq!(AtomKind::SYMBOL_LABEL, SexpShape::SYMBOL_LABEL);
25214 assert_eq!(AtomKind::KEYWORD_LABEL, SexpShape::KEYWORD_LABEL);
25215 assert_eq!(AtomKind::STRING_LABEL, SexpShape::STRING_LABEL);
25216 assert_eq!(AtomKind::INT_LABEL, SexpShape::INT_LABEL);
25217 assert_eq!(AtomKind::FLOAT_LABEL, SexpShape::FLOAT_LABEL);
25218 assert_eq!(AtomKind::BOOL_LABEL, SexpShape::BOOL_LABEL);
25219 }
25220
25221 #[test]
25222 fn atom_kind_label_arms_route_through_per_role_labels_for_every_variant() {
25223 // PATH-UNIFORMITY: `AtomKind::V.label()` MUST equal the per-
25224 // role `pub const AtomKind::V_LABEL` for every `v: AtomKind`.
25225 // Pre-lift the six atomic-payload marker bytes were reachable
25226 // through `AtomKind::label` (the composition
25227 // `self.sexp_shape().label()` — routing into
25228 // `SexpShape::*_LABEL`) OR through direct
25229 // `SexpShape::*_LABEL` reach-across; post-lift each variant's
25230 // canonical bytes are reachable through the per-role
25231 // `AtomKind::*_LABEL` alias too. Pin the byte-equality between
25232 // the runtime projection and the compile-time alias so a
25233 // regression that renames the alias without updating the arm
25234 // (or vice versa) fails-loudly at the exact axis.
25235 //
25236 // Sibling-shape pin to
25237 // `sexp_shape_label_routes_through_typed_per_variant_constants`
25238 // one algebra layer up — the parent superset's per-role
25239 // constants are pinned against `SexpShape::label`'s arms
25240 // there; this pin binds the AtomKind subset algebra's per-
25241 // role aliases against `AtomKind::label`'s composition-routed
25242 // arms so the six atomic-payload marker labels project through
25243 // ONE aliased typed source of truth per role rather than
25244 // through per-consumer inline literals.
25245 assert_eq!(AtomKind::Symbol.label(), AtomKind::SYMBOL_LABEL);
25246 assert_eq!(AtomKind::Keyword.label(), AtomKind::KEYWORD_LABEL);
25247 assert_eq!(AtomKind::Str.label(), AtomKind::STRING_LABEL);
25248 assert_eq!(AtomKind::Int.label(), AtomKind::INT_LABEL);
25249 assert_eq!(AtomKind::Float.label(), AtomKind::FLOAT_LABEL);
25250 assert_eq!(AtomKind::Bool.label(), AtomKind::BOOL_LABEL);
25251 }
25252
25253 #[test]
25254 fn atom_kind_labels_has_expected_cardinality() {
25255 // Cardinality pin: `LABELS.len() == 6` matches `ALL.len()` so a
25256 // refactor that loosens the type to `&'static [&'static str]`
25257 // fails HERE (the `[_; 6]` slot cannot be sliced silently), and
25258 // a variant added to `ALL` without a matching `LABELS` row fails
25259 // the pair-arity gate at the array literal itself before this
25260 // test even runs. The pin doubles as an operator-visible mark
25261 // of the family's cardinality across the substrate — six
25262 // atomic-payload markers, matching the six-arm carving of the
25263 // parent `SexpShape::LABELS` (the atomic subset of the twelve
25264 // canonical outer-shape labels).
25265 assert_eq!(AtomKind::LABELS.len(), 6);
25266 assert_eq!(AtomKind::LABELS.len(), AtomKind::ALL.len());
25267 }
25268
25269 #[test]
25270 fn atom_kind_labels_align_with_all_by_index() {
25271 // ALIGNMENT PIN: sweep `LABELS[i] == ALL[i].label()` so any
25272 // `zip(ALL, LABELS)` consumer reads a coherent (variant, label)
25273 // pair off ONE forced-arity array pair. The declaration-order
25274 // pin makes a family-wide consumer that walks the ALL /
25275 // LABELS pair in lockstep (an LSP completion bar keyed on
25276 // `AtomKind::LABELS`, a Sekiban metric emitter labeling
25277 // `tatara_lisp_atom_type_mismatch_total{kind}` by the
25278 // per-index label) read one canonical (variant, bytes) pair per
25279 // slot rather than routing through per-consumer paired-
25280 // iteration. A regression that reorders LABELS without also
25281 // reordering ALL (or vice versa) fails-loudly at the exact
25282 // index that drifted.
25283 assert_eq!(AtomKind::LABELS.len(), AtomKind::ALL.len());
25284 for (i, kind) in AtomKind::ALL.iter().enumerate() {
25285 assert_eq!(
25286 AtomKind::LABELS[i],
25287 kind.label(),
25288 "AtomKind::LABELS[{i}] `{lbl}` drifted from \
25289 AtomKind::ALL[{i}].label() `{via_variant}` — the \
25290 canonical ALL ordering and the LABELS ordering must \
25291 match element-wise",
25292 lbl = AtomKind::LABELS[i],
25293 via_variant = kind.label(),
25294 );
25295 }
25296 }
25297
25298 #[test]
25299 fn atom_kind_labels_pairwise_distinct() {
25300 // 6x6 pairwise sweep so a collision between any two labels
25301 // (which would silently degrade two distinct atomic-payload
25302 // markers to the SAME diagnostic bytes and violate the
25303 // closed-set FromStr round-trip) fails-loudly at the exact
25304 // pair. Distinctness is already enforced structurally by
25305 // `assert_closed_set_well_formed::<AtomKind>()` (clause 3), so
25306 // this pin is a secondary guard focused on the per-role
25307 // `pub const` surface directly rather than the runtime
25308 // projection through the trait's default `labels()`.
25309 for (i, a) in AtomKind::LABELS.iter().enumerate() {
25310 for (j, b) in AtomKind::LABELS.iter().enumerate() {
25311 if i == j {
25312 continue;
25313 }
25314 assert_ne!(
25315 a, b,
25316 "AtomKind::LABELS[{i}] ({a:?}) collides with \
25317 AtomKind::LABELS[{j}] ({b:?}) — two distinct \
25318 atomic-payload markers cannot share diagnostic bytes",
25319 );
25320 }
25321 }
25322 }
25323
25324 #[test]
25325 fn atom_label_projects_each_variant_to_canonical_diagnostic_label() {
25326 // PER-ARM CONTRACT: pin the outer-`Atom` `Self::label`
25327 // projection produces the SIX canonical `&'static str` labels
25328 // byte-for-byte across every reachable atomic-payload variant.
25329 // Pre-lift the outer-`Atom` label projection had no typed
25330 // primitive on the value-carrier algebra — a consumer with an
25331 // `Atom` value in hand wanting the canonical diagnostic label
25332 // had to spell the two-step composition `atom.kind().label()`
25333 // at every callsite, OR go through
25334 // `Sexp::Atom(atom.clone()).type_name()` which wraps and
25335 // unwraps for no runtime purpose. Post-lift the SIX arms bind
25336 // at ONE typed projection on the outer-`Atom` algebra that
25337 // routes through `AtomKind::label` (which itself composes
25338 // through `AtomKind::sexp_shape().label()` into the canonical
25339 // `SexpShape::label` site) — the (Atom variant, diagnostic
25340 // label) pairing binds at ONE typed algebra composition
25341 // spanning FOUR typed layers.
25342 //
25343 // Sibling-shape pin to
25344 // `atom_kind_label_renders_canonical_string_for_every_variant`
25345 // one algebra layer down and
25346 // `sexp_type_name_method_projects_each_outer_arm_to_canonical_label`
25347 // one algebra layer up. A regression that drifts ONE arm's
25348 // label (e.g. Symbol → "sym", swapping Int ↔ Float, dropping
25349 // the `Str → "string"` boundary rename) fails-loudly at THIS
25350 // test AND the sibling `AtomKind::label` per-arm pin.
25351 assert_eq!(Atom::Symbol("foo".to_owned()).label(), "symbol");
25352 assert_eq!(Atom::Keyword("kw".to_owned()).label(), "keyword");
25353 assert_eq!(Atom::Str("hi".to_owned()).label(), "string");
25354 assert_eq!(Atom::Int(42).label(), "int");
25355 assert_eq!(Atom::Float(1.5).label(), "float");
25356 assert_eq!(Atom::Bool(true).label(), "bool");
25357 assert_eq!(Atom::Bool(false).label(), "bool");
25358 }
25359
25360 #[test]
25361 fn atom_label_composes_through_kind_label_for_every_variant() {
25362 // COMPOSITION-LAW CONTRACT: `atom.label() == atom.kind().label()`
25363 // for every reachable atomic payload — the outer-`Atom` label
25364 // projection is structurally derived through `Self::kind` +
25365 // `AtomKind::label` rather than through a parallel six-arm
25366 // inline match on the outer-`Atom` algebra. Pin the composition
25367 // law so a future refactor that re-inlines the six atomic-arm
25368 // literals here (and gains its own drift surface separate from
25369 // the `AtomKind::label` canonical site) surfaces immediately.
25370 // The pointer-equality check pins the composition produces the
25371 // SAME `&'static str` (not just a byte-equal copy) for every
25372 // variant — proof the routing hits ONE static literal site
25373 // (`SexpShape::label` via `AtomKind::sexp_shape().label()` via
25374 // `AtomKind::label`'s composition) rather than a parallel inline
25375 // table on the outer-`Atom` algebra.
25376 //
25377 // Sibling-shape pin to
25378 // `atom_kind_label_routes_through_sexp_shape_label_via_sexp_shape_projection`
25379 // one algebra layer down (which pins `AtomKind::label`'s routing
25380 // through `SexpShape::label`) and
25381 // `sexp_type_name_method_composes_through_shape_label_for_every_outer_shape`
25382 // one algebra layer up (which pins `Sexp::type_name`'s routing
25383 // through `Sexp::shape().label()`). The three routing pins jointly
25384 // enforce the (outer-`Atom` value, canonical label) pairing
25385 // stays a full four-layer typed composition (`Atom` → `AtomKind`
25386 // → `SexpShape` → `&'static str`) rather than degrading to a
25387 // per-layer inline literal table.
25388 let samples: Vec<Atom> = vec![
25389 Atom::Symbol("foo".to_owned()),
25390 Atom::Keyword("kw".to_owned()),
25391 Atom::Str("hi".to_owned()),
25392 Atom::Int(0),
25393 Atom::Int(-7),
25394 Atom::Int(42),
25395 Atom::Float(0.0),
25396 Atom::Float(-1.5),
25397 Atom::Float(f64::INFINITY),
25398 Atom::Bool(true),
25399 Atom::Bool(false),
25400 ];
25401 for atom in &samples {
25402 let via_label = atom.label();
25403 let via_composition = atom.kind().label();
25404 assert_eq!(
25405 via_label, via_composition,
25406 "Atom::label() must route through self.kind().label() \
25407 for {atom:?} — drift here means the lift was reverted \
25408 to inline arms",
25409 );
25410 assert!(
25411 std::ptr::eq(via_label.as_ptr(), via_composition.as_ptr()),
25412 "Atom::label() must return the SAME `&'static str` as \
25413 self.kind().label() for {atom:?} — pointer drift \
25414 means the lift composes through a parallel literal \
25415 table rather than routing into the canonical \
25416 AtomKind::label site",
25417 );
25418 }
25419 }
25420
25421 #[test]
25422 fn atom_label_agrees_with_sexp_type_name_at_every_atom_arm() {
25423 // CROSS-ALGEBRA AGREEMENT CONTRACT: for every atomic payload
25424 // `a`, `a.label() == Sexp::Atom(a.clone()).type_name()`. The
25425 // agreement is a TYPED CONSEQUENCE of the two typed
25426 // compositions — `Sexp::Atom(a).type_name()` routes through
25427 // `Sexp::shape()`'s `Self::Atom(a) => a.kind().sexp_shape()`
25428 // arm which composes with `SexpShape::label` byte-for-byte
25429 // with `a.kind().label()` (which itself composes through
25430 // `AtomKind::sexp_shape().label()`). A regression that drifts
25431 // either side of the cross-algebra bridge (an outer-`Atom`
25432 // label re-inlined onto a different literal, an outer-`Sexp`
25433 // Atom-arm re-routed through a stale shape projection, an
25434 // `AtomKind::sexp_shape` arm that swaps Int ↔ Float) fails-
25435 // loudly here rather than as a silent operator-facing
25436 // diagnostic drift at every consumer that pattern-matches on
25437 // the outer-`Sexp` label vs the outer-`Atom` label
25438 // independently.
25439 //
25440 // Sibling posture to
25441 // `atom_kind_label_agrees_with_sexp_shape_label_for_every_atom_arm`
25442 // one algebra layer down — that pin binds the marker-level
25443 // vocabulary containment (`AtomKind::label ==
25444 // AtomKind::sexp_shape().label()`), this pin binds the
25445 // outer-value-level vocabulary containment (`Atom::label ==
25446 // Sexp::Atom(_).type_name()`) so the FOUR-layer typed
25447 // composition on the outer-`Atom` algebra and the FIVE-layer
25448 // typed composition on the outer-`Sexp` algebra agree at their
25449 // common atomic-payload arms.
25450 for atom in [
25451 Atom::Symbol("foo".to_owned()),
25452 Atom::Keyword("kw".to_owned()),
25453 Atom::Str("hi".to_owned()),
25454 Atom::Int(42),
25455 Atom::Float(2.5),
25456 Atom::Bool(true),
25457 Atom::Bool(false),
25458 ] {
25459 let via_atom = atom.label();
25460 let via_sexp = Sexp::Atom(atom.clone()).type_name();
25461 assert_eq!(
25462 via_atom, via_sexp,
25463 "Atom::label() must agree with Sexp::Atom(_).type_name() \
25464 for {atom:?} — cross-algebra label drift at the \
25465 atomic-payload arms would fracture the typed diagnostic \
25466 vocabulary between the outer-Atom and outer-Sexp \
25467 algebras",
25468 );
25469 assert!(
25470 std::ptr::eq(via_atom.as_ptr(), via_sexp.as_ptr()),
25471 "Atom::label() must return the SAME `&'static str` as \
25472 Sexp::Atom(_).type_name() for {atom:?} — pointer drift \
25473 means one algebra layer re-inlined the literal rather \
25474 than routing into the canonical `SexpShape::label` \
25475 site",
25476 );
25477 }
25478 }
25479
25480 #[test]
25481 fn atom_sexp_shape_projects_each_variant_to_canonical_outer_shape() {
25482 // PER-ARM CONTRACT: pin the outer-`Atom` `Self::sexp_shape`
25483 // projection produces the SIX canonical `SexpShape` variants
25484 // byte-for-byte across every reachable atomic-payload variant.
25485 // Pre-lift the outer-`Atom` outer-shape projection had no typed
25486 // primitive on the value-carrier algebra — a consumer with an
25487 // `Atom` value in hand wanting the canonical outer-shape had to
25488 // spell the two-step composition `atom.kind().sexp_shape()` at
25489 // every callsite, OR go through `Sexp::Atom(atom.clone()).shape()`
25490 // which wraps and unwraps for no runtime purpose. Post-lift the
25491 // SIX arms bind at ONE typed projection on the outer-`Atom`
25492 // algebra that routes through `AtomKind::sexp_shape` — the
25493 // (Atom variant, SexpShape variant) pairing binds at ONE typed
25494 // algebra composition spanning THREE typed layers.
25495 //
25496 // Sibling-shape pin to
25497 // `atom_kind_sexp_shape_projects_each_variant_to_canonical_outer_shape`
25498 // one algebra layer down and `atom_label_projects_each_variant_to_canonical_diagnostic_label`
25499 // one vocabulary axis over. A regression that drifts ONE arm's
25500 // mapping (e.g. swapping Int ↔ Float, dropping the `Str →
25501 // SexpShape::String` boundary rename) fails-loudly at THIS
25502 // test AND the sibling `AtomKind::sexp_shape` per-arm pin.
25503 assert_eq!(
25504 Atom::Symbol("foo".to_owned()).sexp_shape(),
25505 SexpShape::Symbol
25506 );
25507 assert_eq!(
25508 Atom::Keyword("kw".to_owned()).sexp_shape(),
25509 SexpShape::Keyword
25510 );
25511 assert_eq!(Atom::Str("hi".to_owned()).sexp_shape(), SexpShape::String);
25512 assert_eq!(Atom::Int(42).sexp_shape(), SexpShape::Int);
25513 assert_eq!(Atom::Float(1.5).sexp_shape(), SexpShape::Float);
25514 assert_eq!(Atom::Bool(true).sexp_shape(), SexpShape::Bool);
25515 assert_eq!(Atom::Bool(false).sexp_shape(), SexpShape::Bool);
25516 }
25517
25518 #[test]
25519 fn atom_sexp_shape_composes_through_kind_sexp_shape_for_every_variant() {
25520 // COMPOSITION-LAW CONTRACT: `atom.sexp_shape() ==
25521 // atom.kind().sexp_shape()` for every reachable atomic payload
25522 // — the outer-`Atom` outer-shape projection is structurally
25523 // derived through `Self::kind` + `AtomKind::sexp_shape` rather
25524 // than through a parallel six-arm inline match on the outer-
25525 // `Atom` algebra. Pin the composition law so a future refactor
25526 // that re-inlines the six atomic-arm literals here (and gains
25527 // its own drift surface separate from the `AtomKind::sexp_shape`
25528 // canonical site) surfaces immediately.
25529 //
25530 // `SexpShape` carries the `String`-carrying `Unknown` arm so
25531 // it can't be `Copy`; the pointer-equality axis
25532 // `atom_label_composes_through_kind_label_for_every_variant`
25533 // uses on the `&'static str` axis doesn't apply here. Byte-
25534 // equality on the `SexpShape` discriminant IS the routing
25535 // contract this pin binds: a regression that re-inlines the
25536 // mapping produces byte-equal SexpShape values yet gains its
25537 // own drift surface at the outer-`Atom` layer separate from
25538 // the canonical `AtomKind::sexp_shape` site.
25539 //
25540 // Sibling-shape pin to
25541 // `atom_label_composes_through_kind_label_for_every_variant`
25542 // one vocabulary axis over (the diagnostic-label axis) and
25543 // `atom_kind_sexp_shape_partition_matches_sexp_shape_atomic_carving`
25544 // one algebra layer down (which pins `AtomKind::sexp_shape`'s
25545 // partition-membership against `SexpShape::as_atom_kind`).
25546 // The three pins jointly enforce the (outer-`Atom` value,
25547 // outer-shape) pairing stays a full three-layer typed
25548 // composition (`Atom` → `AtomKind` → `SexpShape`) rather than
25549 // degrading to a per-layer inline literal table on the
25550 // outer-`Atom` algebra.
25551 let samples: Vec<Atom> = vec![
25552 Atom::Symbol("foo".to_owned()),
25553 Atom::Symbol(String::new()),
25554 Atom::Keyword("kw".to_owned()),
25555 Atom::Keyword(String::new()),
25556 Atom::Str("hi".to_owned()),
25557 Atom::Str(String::new()),
25558 Atom::Int(0),
25559 Atom::Int(-7),
25560 Atom::Int(i64::MIN),
25561 Atom::Int(i64::MAX),
25562 Atom::Float(0.0),
25563 Atom::Float(-1.5),
25564 Atom::Float(f64::INFINITY),
25565 Atom::Float(f64::from_bits(f64::NAN.to_bits())),
25566 Atom::Bool(true),
25567 Atom::Bool(false),
25568 ];
25569 for atom in &samples {
25570 let via_sexp_shape = atom.sexp_shape();
25571 let via_composition = atom.kind().sexp_shape();
25572 assert_eq!(
25573 via_sexp_shape, via_composition,
25574 "Atom::sexp_shape() must route through self.kind().sexp_shape() \
25575 for {atom:?} — drift here means the lift was reverted \
25576 to inline arms",
25577 );
25578 // Cross-projection agreement: the routed shape's diagnostic
25579 // label is byte-equal to `atom.label()` (the sibling
25580 // vocabulary axis' composition), pinning that the two typed
25581 // projections through `AtomKind` (one via `label`, one via
25582 // `sexp_shape`) agree at the canonical `SexpShape::label`
25583 // site.
25584 assert_eq!(
25585 via_sexp_shape.label(),
25586 atom.label(),
25587 "atom.sexp_shape().label() must agree with atom.label() \
25588 for {atom:?} — cross-axis vocabulary drift at the \
25589 shape-projection site would fracture the FOUR-layer \
25590 diagnostic composition on the outer-Atom algebra",
25591 );
25592 }
25593 }
25594
25595 #[test]
25596 fn atom_sexp_shape_agrees_with_sexp_shape_at_every_atom_arm() {
25597 // CROSS-ALGEBRA AGREEMENT CONTRACT: for every atomic payload
25598 // `a`, `a.sexp_shape() == Sexp::Atom(a.clone()).shape()`. The
25599 // agreement is a TYPED CONSEQUENCE of the two typed
25600 // compositions — `Sexp::Atom(a).shape()` routes through
25601 // `Sexp::shape()`'s `Self::Atom(a) => a.kind().sexp_shape()`
25602 // arm which byte-for-byte matches `a.sexp_shape()`'s composition
25603 // through `Self::kind` + `AtomKind::sexp_shape`. A regression
25604 // that drifts either side of the cross-algebra bridge (an
25605 // outer-`Atom` shape re-inlined onto a different projection,
25606 // an outer-`Sexp` Atom-arm re-routed through a stale kind
25607 // projection, an `AtomKind::sexp_shape` arm that swaps Int ↔
25608 // Float) fails-loudly here rather than as a silent operator-
25609 // facing drift at every consumer that pattern-matches on the
25610 // outer-`Sexp` shape vs the outer-`Atom` shape independently.
25611 //
25612 // Sibling posture to
25613 // `atom_label_agrees_with_sexp_type_name_at_every_atom_arm`
25614 // one vocabulary axis over — that pin binds the (outer-`Atom`,
25615 // outer-`Sexp`) cross-algebra bridge on the diagnostic-label
25616 // axis, this pin binds it on the outer-shape axis.
25617 for atom in [
25618 Atom::Symbol("foo".to_owned()),
25619 Atom::Keyword("kw".to_owned()),
25620 Atom::Str("hi".to_owned()),
25621 Atom::Int(42),
25622 Atom::Float(2.5),
25623 Atom::Bool(true),
25624 Atom::Bool(false),
25625 ] {
25626 let via_atom = atom.sexp_shape();
25627 let via_sexp = Sexp::Atom(atom.clone()).shape();
25628 assert_eq!(
25629 via_atom, via_sexp,
25630 "Atom::sexp_shape() must agree with Sexp::Atom(_).shape() \
25631 for {atom:?} — cross-algebra shape drift at the \
25632 atomic-payload arms would fracture the typed shape \
25633 vocabulary between the outer-Atom and outer-Sexp \
25634 algebras",
25635 );
25636 }
25637 }
25638
25639 #[test]
25640 fn atom_sexp_shape_round_trips_through_sexp_shape_as_atom_kind() {
25641 // ROUND-TRIP CONTRACT: for every atomic payload `a`,
25642 // `a.sexp_shape().as_atom_kind() == Some(a.kind())`. The typed
25643 // embed `Atom → AtomKind → SexpShape` inverts through the
25644 // soft-projection retraction `SexpShape → AtomKind` exactly on
25645 // the 6-of-12 atomic-payload image. A regression that ANY of
25646 // the three embeds (`Self::kind`, `AtomKind::sexp_shape`) OR
25647 // the soft-projection retraction `SexpShape::as_atom_kind`
25648 // drifts on any arm fails-loudly here — the structural
25649 // round-trip is the invariant that holds the closed-set-
25650 // lattice's atomic-payload cell load-bearing across future
25651 // edits.
25652 //
25653 // Peer to `unquote_form_sexp_shape_round_trips_through_sexp_shape_as_quote_form_and_as_unquote_form`
25654 // (error.rs) one carving axis over on the substitution-subset
25655 // side of the outer-shape lattice, and to `atom_kind_sexp_shape_round_trips_through_sexp_shape_as_atom_kind`
25656 // one algebra layer down (which pins the marker-level round-
25657 // trip). This pin extends the round-trip up to the outer-
25658 // `Atom` value carrier.
25659 for atom in [
25660 Atom::Symbol("foo".to_owned()),
25661 Atom::Keyword("kw".to_owned()),
25662 Atom::Str("hi".to_owned()),
25663 Atom::Int(0),
25664 Atom::Int(i64::MIN),
25665 Atom::Float(0.0),
25666 Atom::Float(f64::INFINITY),
25667 Atom::Bool(true),
25668 Atom::Bool(false),
25669 ] {
25670 let shape = atom.sexp_shape();
25671 let round_tripped = shape.as_atom_kind();
25672 assert_eq!(
25673 round_tripped,
25674 Some(atom.kind()),
25675 "Atom::sexp_shape() must round-trip through \
25676 SexpShape::as_atom_kind for {atom:?} — the typed embed \
25677 Atom → AtomKind → SexpShape is no longer a section of \
25678 SexpShape::as_atom_kind's inverse on the atomic \
25679 6-of-12 image",
25680 );
25681 }
25682 }
25683
25684 #[test]
25685 fn hash_for_atom_preserves_legacy_discriminator_bytes() {
25686 // CACHE-KEY CONTRACT (Hash side): pin that the lifted
25687 // `Hash for Atom` impl produces byte-identical hashes for the
25688 // six atomic variants as the pre-lift implementation. We
25689 // compute the expected hash via a SECOND hasher that manually
25690 // drives the pre-lift `<discr>u8.hash(h); <inner>.hash(h)`
25691 // sequence (with `Float`'s `to_bits()` projection preserved
25692 // and `String` payloads hashed via `String::hash`), then
25693 // compare. A regression that drifts the discriminator OR
25694 // re-orders the (discr, inner) sequence surfaces here as a
25695 // hash-value mismatch. Sibling posture to
25696 // `hash_for_sexp_preserves_legacy_quote_family_discriminator_bytes`
25697 // on the quote-family axis.
25698 use std::collections::hash_map::DefaultHasher;
25699
25700 let payload = String::from("payload");
25701
25702 // Helper: hash the legacy `<discr>u8.hash(h); <inner>` shape
25703 // through a fresh DefaultHasher and finish.
25704 let legacy_hash = |atom: &Atom, expected_discr: u8| -> u64 {
25705 let mut h = DefaultHasher::new();
25706 expected_discr.hash(&mut h);
25707 match atom {
25708 Atom::Symbol(s) | Atom::Keyword(s) | Atom::Str(s) => s.hash(&mut h),
25709 Atom::Int(n) => n.hash(&mut h),
25710 Atom::Float(f) => f.to_bits().hash(&mut h),
25711 Atom::Bool(b) => b.hash(&mut h),
25712 }
25713 h.finish()
25714 };
25715
25716 // (label, atom, pre-lift discriminator byte)
25717 let cases: &[(&str, Atom, u8)] = &[
25718 ("symbol", Atom::Symbol(payload.clone()), 0u8),
25719 ("keyword", Atom::Keyword(payload.clone()), 1u8),
25720 ("str", Atom::Str(payload.clone()), 2u8),
25721 ("int", Atom::Int(42), 3u8),
25722 ("float", Atom::Float(1.5), 4u8),
25723 ("bool-true", Atom::Bool(true), 5u8),
25724 ("bool-false", Atom::Bool(false), 5u8),
25725 ];
25726
25727 for (label, atom, expected_discr) in cases {
25728 let mut via_impl = DefaultHasher::new();
25729 atom.hash(&mut via_impl);
25730
25731 let via_legacy = legacy_hash(atom, *expected_discr);
25732
25733 assert_eq!(
25734 via_impl.finish(),
25735 via_legacy,
25736 "Hash for Atom drifted from legacy \
25737 (discr={expected_discr}, inner) sequence at {label}"
25738 );
25739 }
25740 }
25741
25742 #[test]
25743 fn atom_hash_discriminator_composes_through_kind_hash_discriminator_for_every_variant() {
25744 // COMPOSITION-LAW CONTRACT: `atom.hash_discriminator() ==
25745 // atom.kind().hash_discriminator()` for every reachable atomic
25746 // payload — the outer-`Atom` cache-key byte projection is
25747 // structurally derived through `Self::kind` +
25748 // `AtomKind::hash_discriminator` rather than through a parallel
25749 // six-arm inline match on the outer-`Atom` algebra. Pin the
25750 // composition law so a future refactor that re-inlines the six
25751 // atomic-arm literals here (and gains its own drift surface
25752 // separate from the `AtomKind::hash_discriminator` canonical
25753 // site) surfaces immediately.
25754 //
25755 // Sibling-shape pin to
25756 // `atom_label_composes_through_kind_label_for_every_variant`
25757 // (diagnostic-label axis) and
25758 // `atom_sexp_shape_composes_through_kind_sexp_shape_for_every_variant`
25759 // (outer-shape axis) one vocabulary axis over — the three pins
25760 // jointly enforce the outer-`Atom` algebra closes the (label,
25761 // sexp_shape, hash_discriminator) trio through the SAME typed
25762 // marker layer (`Self::kind` into `AtomKind`) rather than
25763 // degrading to a per-layer inline literal table on the
25764 // outer-`Atom` algebra. The sweep includes NaN and ±∞ Float
25765 // payloads (matching `Hash for Atom`'s `f64::to_bits()`
25766 // posture), both empty and non-empty String/Symbol/Keyword
25767 // arms, `i64::{MIN, MAX}` on the Int arm, and both Bool arms —
25768 // exhausting the byte-partition surface at every reachable
25769 // atomic-payload witness.
25770 let samples: Vec<Atom> = vec![
25771 Atom::Symbol("foo".to_owned()),
25772 Atom::Symbol(String::new()),
25773 Atom::Keyword("kw".to_owned()),
25774 Atom::Keyword(String::new()),
25775 Atom::Str("hi".to_owned()),
25776 Atom::Str(String::new()),
25777 Atom::Int(0),
25778 Atom::Int(-7),
25779 Atom::Int(42),
25780 Atom::Int(i64::MIN),
25781 Atom::Int(i64::MAX),
25782 Atom::Float(0.0),
25783 Atom::Float(-1.5),
25784 Atom::Float(f64::INFINITY),
25785 Atom::Float(f64::NEG_INFINITY),
25786 Atom::Float(f64::NAN),
25787 Atom::Bool(true),
25788 Atom::Bool(false),
25789 ];
25790 for atom in &samples {
25791 let via_outer = atom.hash_discriminator();
25792 let via_composition = atom.kind().hash_discriminator();
25793 assert_eq!(
25794 via_outer, via_composition,
25795 "Atom::hash_discriminator() must route through \
25796 self.kind().hash_discriminator() for {atom:?} — drift \
25797 here means the lift was reverted to inline arms and \
25798 the outer-`Atom` cache-key algebra fractured from the \
25799 canonical AtomKind::hash_discriminator site",
25800 );
25801 }
25802 }
25803
25804 #[test]
25805 fn hash_for_atom_routes_atom_discriminator_through_atom_hash_discriminator() {
25806 // ROUTING-LAW CONTRACT: pin the outer-`Atom` routing IDENTITY —
25807 // for every reachable atomic payload, `Hash for Atom` produces
25808 // byte-identical output to a hand-driven
25809 // `atom.hash_discriminator().hash(h); <inner-payload-hash>`
25810 // sequence. Binds the composition IDENTITY (not just value
25811 // equality) between the outer Hash body and the typed algebra
25812 // method — a regression that re-inlines the two-hop
25813 // `self.kind().hash_discriminator()` chain at the outer arm
25814 // still drifts detectably if the future
25815 // `Atom::hash_discriminator` composes through a different site.
25816 // Sibling posture to
25817 // `hash_for_sexp_routes_outer_discriminator_through_sexp_hash_discriminator`
25818 // — that pin binds the `Hash for Sexp` body against the
25819 // outer-`Sexp` cache-key method; this pin binds the
25820 // `Hash for Atom` body against the outer-`Atom` cache-key
25821 // method. Together the two routing pins enforce the outer-value
25822 // Hash bodies at BOTH algebras stay structurally parallel
25823 // (`self.hash_discriminator().hash(h); <inner>`).
25824 use std::collections::hash_map::DefaultHasher;
25825 let seeds: Vec<(&str, Atom)> = vec![
25826 ("symbol", Atom::Symbol("s".to_owned())),
25827 ("symbol-empty", Atom::Symbol(String::new())),
25828 ("keyword", Atom::Keyword("kw".to_owned())),
25829 ("str", Atom::Str("hi".to_owned())),
25830 ("int-zero", Atom::Int(0)),
25831 ("int-min", Atom::Int(i64::MIN)),
25832 ("int-max", Atom::Int(i64::MAX)),
25833 ("float", Atom::Float(2.5)),
25834 ("float-nan", Atom::Float(f64::NAN)),
25835 ("float-inf", Atom::Float(f64::INFINITY)),
25836 ("bool-true", Atom::Bool(true)),
25837 ("bool-false", Atom::Bool(false)),
25838 ];
25839 for (label, atom) in &seeds {
25840 let mut via_impl = DefaultHasher::new();
25841 atom.hash(&mut via_impl);
25842
25843 let mut via_lifted = DefaultHasher::new();
25844 atom.hash_discriminator().hash(&mut via_lifted);
25845 match atom {
25846 Atom::Symbol(s) | Atom::Keyword(s) | Atom::Str(s) => s.hash(&mut via_lifted),
25847 Atom::Int(n) => n.hash(&mut via_lifted),
25848 Atom::Float(f) => f.to_bits().hash(&mut via_lifted),
25849 Atom::Bool(b) => b.hash(&mut via_lifted),
25850 }
25851
25852 assert_eq!(
25853 via_impl.finish(),
25854 via_lifted.finish(),
25855 "Hash for Atom drifted from routed-through-hash_discriminator sequence at {label}"
25856 );
25857 }
25858 }
25859
25860 #[test]
25861 fn atom_kind_composes_with_domain_sexp_shape_for_every_atomic_arm() {
25862 // PATH-UNIFORMITY / COMPOSITION-LAW CONTRACT: the substrate's
25863 // outer-shape projection `domain::sexp_shape` now routes the
25864 // six atomic arms through `Atom::kind` + `AtomKind::sexp_shape`.
25865 // Pin that the composed projection produces the SAME
25866 // `SexpShape` variant that the pre-lift inline six-arm match
25867 // produced for every `Atom` payload. A regression that drifts
25868 // ONE arm of either `Atom::kind` (e.g. routes `Atom::Int(_)`
25869 // through `AtomKind::Float`) or `AtomKind::sexp_shape` (e.g.
25870 // routes `AtomKind::Symbol` through `SexpShape::Keyword`)
25871 // surfaces as an immediate inequality between
25872 // `domain::sexp_shape(&Sexp::Atom(a))` and
25873 // `a.kind().sexp_shape()` — and since both projections are
25874 // load-bearing for the diagnostic surface, the test pins both
25875 // sides of the typed algebra at once. Sibling posture to
25876 // `quote_form_sexp_shape_paired_with_as_quote_form_preserves_
25877 // pre_lift_pairing_for_every_sexp` on the quote-family axis.
25878 let cases: &[(Atom, SexpShape)] = &[
25879 (Atom::Symbol("x".into()), SexpShape::Symbol),
25880 (Atom::Keyword("k".into()), SexpShape::Keyword),
25881 (Atom::Str("s".into()), SexpShape::String),
25882 (Atom::Int(7), SexpShape::Int),
25883 (Atom::Float(2.5), SexpShape::Float),
25884 (Atom::Bool(true), SexpShape::Bool),
25885 ];
25886 for (atom, expected_shape) in cases {
25887 let via_composed = atom.kind().sexp_shape();
25888 assert_eq!(
25889 via_composed, *expected_shape,
25890 "Atom::kind().sexp_shape() drifted for {atom:?}"
25891 );
25892 // Cross-projection identity with the public
25893 // `domain::sexp_shape` projection — pins that the lifted
25894 // arm routes through `AtomKind` exactly as the inline
25895 // arms did pre-lift.
25896 let via_domain = crate::domain::sexp_shape(&Sexp::Atom(atom.clone()));
25897 assert_eq!(
25898 via_domain, via_composed,
25899 "domain::sexp_shape vs Atom::kind().sexp_shape() drift for {atom:?}"
25900 );
25901 }
25902 }
25903
25904 #[test]
25905 fn atom_display_renders_each_variant_to_canonical_form() {
25906 // CANONICAL-RENDERING CONTRACT: pin that the lifted
25907 // `fmt::Display for Atom` impl produces byte-identical
25908 // canonical output for the seven atomic variant cases
25909 // (Bool splits into true/false) as the pre-lift inline
25910 // sub-arms inside `Display for Sexp`'s atom arm. Sibling-arm
25911 // sweep so the seven pairings stay load-bearing under
25912 // reordering refactors. A regression that drifts the Bool
25913 // spelling (`#t`/`#f` vs Rust's `true`/`false`) — the
25914 // CLAUDE.md-pinned reader-round-trip invariant — fails
25915 // loudly here. Direct sibling to `atom_kind_label_renders_
25916 // canonical_string_for_every_variant` on the diagnostic-
25917 // label axis: this pins the rendered SOURCE (`#t`), that pins
25918 // the rendered LABEL (`bool`); the two projections share the
25919 // closed-set `AtomKind` algebra but render to distinct
25920 // surfaces (source vs diagnostic vocabulary).
25921 let cases: &[(Atom, &str)] = &[
25922 (Atom::Symbol("foo".into()), "foo"),
25923 (Atom::Keyword("k".into()), ":k"),
25924 (Atom::Str("hello".into()), "\"hello\""),
25925 (Atom::Int(42), "42"),
25926 (Atom::Int(-7), "-7"),
25927 (Atom::Float(1.5), "1.5"),
25928 (Atom::Bool(true), "#t"),
25929 (Atom::Bool(false), "#f"),
25930 ];
25931 for (atom, expected) in cases {
25932 assert_eq!(
25933 atom.to_string(),
25934 *expected,
25935 "Atom::Display drifted from canonical rendering for {atom:?}"
25936 );
25937 }
25938 }
25939
25940 #[test]
25941 fn atom_display_renders_integral_float_with_dot_zero_suffix() {
25942 // ROUND-TRIP-INVARIANT PIN: `fmt_float`'s `.0`-suffix
25943 // discipline composes through `Atom::Display` — `Float(1.0)`
25944 // renders as `"1.0"`, NOT `"1"` (which the reader would
25945 // re-parse as `Atom::Int(1)`, silently coercing the typed
25946 // `Float` track into the `Int` track at the Display→read
25947 // boundary). Direct sibling pin to the existing Display-for-
25948 // Sexp round-trip tests that exercise the same invariant
25949 // through the `Sexp::Atom` outer wrap. Lifting the rendering
25950 // onto the typed `Atom` algebra surfaces a future regression
25951 // (e.g. an Atom::Display arm that bypasses `fmt_float` and
25952 // formats `f64` directly) at the atom layer without
25953 // requiring a Sexp wrap to reproduce.
25954 assert_eq!(Atom::Float(1.0).to_string(), "1.0");
25955 assert_eq!(Atom::Float(-42.0).to_string(), "-42.0");
25956 assert_eq!(Atom::Float(0.99).to_string(), "0.99");
25957 }
25958
25959 #[test]
25960 fn sexp_atom_display_arm_routes_through_atom_display_for_every_variant() {
25961 // LIFTED-BOUNDARY CONTRACT: pin that `Sexp::Atom(a).to_string()
25962 // == a.to_string()` for every atomic payload variant. Pre-
25963 // lift the per-variant body lived inline at the `Sexp::Atom(a)
25964 // => match a { … }` arm of `Display for Sexp`; post-lift the
25965 // outer arm delegates to `fmt::Display::fmt(a, f)`. A
25966 // regression that drifts the outer arm (e.g. wraps the atom
25967 // rendering in parens, or routes Symbol through a Sexp-
25968 // specific arm before delegating) surfaces as an inequality
25969 // here. The cases sweep all six `Atom` variants (Bool unified
25970 // — both true/false agree under the impl). Sibling posture
25971 // to the quote-family routing test
25972 // `sexp_to_json_routes_quote_family_arms_through_as_quote_form_typed_marker`
25973 // that pins the analogous `Sexp` outer arm routing through
25974 // a typed algebra projection.
25975 let cases: &[Atom] = &[
25976 Atom::Symbol("name".into()),
25977 Atom::Keyword("kw".into()),
25978 Atom::Str("body".into()),
25979 Atom::Int(7),
25980 Atom::Float(2.5),
25981 Atom::Float(1.0),
25982 Atom::Bool(true),
25983 Atom::Bool(false),
25984 ];
25985 for atom in cases {
25986 let via_sexp = Sexp::Atom(atom.clone()).to_string();
25987 let via_atom = atom.to_string();
25988 assert_eq!(
25989 via_sexp, via_atom,
25990 "Sexp::Atom Display arm drifted from Atom::Display for {atom:?}"
25991 );
25992 }
25993 }
25994
25995 #[test]
25996 fn atom_display_round_trips_through_reader_preserving_typed_identity() {
25997 // BIDIRECTIONAL TYPED-IDENTITY CONTRACT: render an atom via
25998 // `Atom::Display`, parse the rendering through
25999 // `crate::reader::read`, and pin that the parsed value's
26000 // outer shape is `Sexp::Atom(_)` carrying the SAME variant
26001 // discriminator as the seed (via `Atom::kind`) AND that the
26002 // payload round-trips bit-for-bit. This is the typed-exit /
26003 // typed-entry mirror at the atomic-payload boundary — the
26004 // load-bearing invariant the `fmt_float` `.0`-suffix
26005 // discipline already exists to preserve. A regression that
26006 // drifts ONE side (Display arm OR reader arm) corrupts the
26007 // round-trip; pin it at the typed boundary directly. Sibling
26008 // posture to the existing Sexp-layer round-trip tests:
26009 // `float_display_round_trips_through_reader_into_typed_float`,
26010 // `quote_prefix_round_trips_through_read_quoted_into_sexp_quote`.
26011 let cases: &[Atom] = &[
26012 Atom::Symbol("foo-bar".into()),
26013 Atom::Keyword("kw".into()),
26014 Atom::Int(42),
26015 Atom::Int(-7),
26016 Atom::Int(0),
26017 Atom::Float(1.0),
26018 Atom::Float(1.5),
26019 Atom::Float(-42.0),
26020 Atom::Bool(true),
26021 Atom::Bool(false),
26022 ];
26023 for seed in cases {
26024 let rendered = seed.to_string();
26025 let mut parsed = crate::reader::read(&rendered)
26026 .unwrap_or_else(|e| panic!("reader rejected {rendered:?} for {seed:?}: {e}"));
26027 assert_eq!(
26028 parsed.len(),
26029 1,
26030 "rendered {rendered:?} for {seed:?} re-read as != 1 form"
26031 );
26032 let Sexp::Atom(round_tripped) = parsed.remove(0) else {
26033 panic!("rendered {rendered:?} for {seed:?} re-read as non-Atom");
26034 };
26035 assert_eq!(
26036 round_tripped.kind(),
26037 seed.kind(),
26038 "Atom::Display→reader drifted variant for {seed:?} via {rendered:?}"
26039 );
26040 assert_eq!(
26041 round_tripped, *seed,
26042 "Atom::Display→reader drifted payload for {seed:?} via {rendered:?}"
26043 );
26044 }
26045 }
26046
26047 #[test]
26048 fn atom_to_json_projects_each_variant_to_canonical_json_value() {
26049 // CANONICAL-MAPPING CONTRACT: pin that `Atom::to_json` produces
26050 // byte-identical `serde_json::Value` outputs for each
26051 // `AtomKind` variant as the pre-lift inline arms inside
26052 // `crate::domain::sexp_to_json` did. Sweeps a representative
26053 // atom of each variant so a regression that drifts ONE arm
26054 // (e.g. swaps `Symbol`'s mapping to a Number, or drops
26055 // `Keyword`'s `:` prefix that `json_to_sexp`'s inverse strips
26056 // — silently breaking every `:values-overlay` payload pinned
26057 // by the CLAUDE.md bool warning) fails loudly. Sibling-arm
26058 // sweep to `atom_display_renders_each_variant_to_canonical_form`
26059 // — both pin the typed-algebra rendering of the atomic
26060 // payload at its canonical projection. The float case uses
26061 // `1.5` (finite) here; NaN / ±∞ get their own pin below.
26062 use serde_json::Value as JValue;
26063 assert_eq!(
26064 Atom::Symbol("name".into()).to_json(),
26065 JValue::String("name".into()),
26066 );
26067 assert_eq!(
26068 Atom::Keyword("parent".into()).to_json(),
26069 JValue::String(":parent".into()),
26070 );
26071 assert_eq!(
26072 Atom::Str("body".into()).to_json(),
26073 JValue::String("body".into()),
26074 );
26075 assert_eq!(Atom::Int(42).to_json(), JValue::Number(42i64.into()));
26076 assert_eq!(Atom::Int(-7).to_json(), JValue::Number((-7i64).into()));
26077 assert_eq!(
26078 Atom::Float(1.5).to_json(),
26079 JValue::Number(serde_json::Number::from_f64(1.5).unwrap()),
26080 );
26081 assert_eq!(Atom::Bool(true).to_json(), JValue::Bool(true));
26082 assert_eq!(Atom::Bool(false).to_json(), JValue::Bool(false));
26083 }
26084
26085 #[test]
26086 fn atom_from_json_number_int_arm_projects_i64_backed_numbers_to_atom_int() {
26087 // TYPED-INVERSE CONTRACT (Int arm): pin that `Atom::from_json_number`
26088 // decodes every `serde_json::Number` whose `.as_i64()` returns
26089 // `Some(i)` to `Atom::Int(i)`. Sweeps every i64-boundary value
26090 // the substrate pinned in the sibling `Atom::to_json` sweep
26091 // (0, ±1, ±42, i64::MAX, i64::MIN) plus a representative
26092 // interior sample; the sweep pins that the `as_i64()` arm
26093 // fires eagerly BEFORE the `as_f64()` arm, so an
26094 // integer-valued `Number` never sinks to `Atom::Float` at the
26095 // atomic-algebra boundary. A regression that drifts the arm
26096 // (e.g. swaps the `as_i64` / `as_f64` order) fails at the
26097 // `i64::MAX` / `i64::MIN` boundary samples because those two
26098 // values exceed `f64`'s 53-bit mantissa and would silently
26099 // round through the `as_f64` sink.
26100 for i in [0i64, 1, -1, 42, -7, i64::MAX, i64::MIN] {
26101 let n: serde_json::Number = i.into();
26102 assert_eq!(
26103 Atom::from_json_number(&n),
26104 Atom::Int(i),
26105 "Atom::from_json_number drifted Int arm for i64 sample {i}",
26106 );
26107 }
26108 }
26109
26110 #[test]
26111 fn atom_from_json_number_float_arm_projects_finite_non_integer_f64_backed_numbers_to_atom_float(
26112 ) {
26113 // TYPED-INVERSE CONTRACT (Float arm): pin that
26114 // `Atom::from_json_number` decodes every `serde_json::Number`
26115 // whose `.as_i64()` returns `None` but `.as_f64()` returns
26116 // `Some(f)` to `Atom::Float(f)`. Sweeps a representative set of
26117 // finite non-integer-valued f64 samples the substrate pinned
26118 // in the sibling `Atom::to_json` sweep (1.5, -2.5, positive
26119 // and negative fractional values, subnormal, `f64::MIN_POSITIVE`).
26120 // Every sample is constructed via `serde_json::Number::from_f64`
26121 // which the standard library documents as f64-backed
26122 // (`.as_i64()` returns `None`, `.as_f64()` returns
26123 // `Some(input)`) so the Float arm fires deterministically.
26124 // A regression that drifts the arm (e.g. drops the `as_f64`
26125 // sink entirely and falls through to the `Int(0)` typed floor)
26126 // fails HERE at the fractional-value assertions with an
26127 // `Int(0)` mismatch.
26128 for f in [
26129 1.5f64,
26130 -2.5,
26131 0.1,
26132 -0.1,
26133 f64::MIN_POSITIVE,
26134 1.234_567_890_123,
26135 ] {
26136 let n = serde_json::Number::from_f64(f)
26137 .unwrap_or_else(|| panic!("Number::from_f64({f}) must accept finite float"));
26138 assert_eq!(
26139 Atom::from_json_number(&n),
26140 Atom::Float(f),
26141 "Atom::from_json_number drifted Float arm for f64 sample {f}",
26142 );
26143 }
26144 }
26145
26146 #[test]
26147 fn atom_from_json_number_round_trips_atom_to_json_int_arm() {
26148 // ROUND-TRIP LAW (Int axis): pin the paired-projection identity
26149 // `Atom::from_json_number(&<Atom::Int(i).to_json() as Number>)
26150 // == Atom::Int(i)` for every i64 boundary sample. The pair
26151 // `Atom::to_json` (forward) + `Atom::from_json_number` (inverse)
26152 // now lives on the SAME closed-set [`Atom`] algebra — this
26153 // pin proves the closure at ONE algebra layer without a
26154 // `Sexp::from_json` intermediary. A regression that drifts
26155 // either side of the pair (e.g. `Atom::to_json` emits `Int(n)`
26156 // as a JSON string, or `Atom::from_json_number` inverts the
26157 // `as_i64` / `as_f64` cascade order) surfaces here at the
26158 // boundary-value mismatch. Sibling-shape pin to
26159 // `atom_display_round_trips_through_reader_preserving_typed_identity`
26160 // — where that pin closes the `Atom → Display → reader → Atom`
26161 // round-trip on the Display axis, THIS pin closes the
26162 // `Atom::Int → to_json → Number → from_json_number → Atom::Int`
26163 // round-trip on the JSON numeric axis, both on the SAME [`Atom`]
26164 // algebra.
26165 for i in [0i64, 1, -1, 42, -7, i64::MAX, i64::MIN] {
26166 let atom_before = Atom::Int(i);
26167 let via_forward = atom_before.to_json();
26168 let n = match via_forward {
26169 serde_json::Value::Number(n) => n,
26170 other => {
26171 panic!("Atom::Int({i}).to_json() must project to JValue::Number, got {other:?}",)
26172 }
26173 };
26174 assert_eq!(
26175 Atom::from_json_number(&n),
26176 atom_before,
26177 "Atom::Int({i}) round-trip through to_json + from_json_number drifted",
26178 );
26179 }
26180 }
26181
26182 #[test]
26183 fn atom_from_json_number_round_trips_atom_to_json_float_arm_for_non_integer_finite_samples() {
26184 // ROUND-TRIP LAW (Float axis, non-integer subset): pin the
26185 // paired-projection identity `Atom::from_json_number(&<Atom::Float(f)
26186 // .to_json() as Number>) == Atom::Float(f)` for every finite
26187 // non-integer f64 sample. The non-integer restriction is
26188 // load-bearing: `serde_json::Number::from_f64` on an
26189 // integer-valued f64 like `1.0` produces a Number whose
26190 // `.as_i64()` may return `Some(1)` (the exact behavior depends
26191 // on `serde_json`'s internal representation of the JSON
26192 // number tower — integer-valued floats can round-trip through
26193 // the `as_i64` arm, sinking to `Atom::Int(1)` instead of
26194 // `Atom::Float(1.0)`). The three-way (`Symbol` / `Keyword` /
26195 // `Str`) collapse on the string side of `Sexp::from_json`'s
26196 // docstring is one axis; THIS pin covers the (`Int` / `Float`)
26197 // collapse on the numeric side for the round-trippable subset
26198 // (non-integer-valued finite floats). Together with the Int
26199 // round-trip pin above the two floors close the numeric-axis
26200 // round-trip closure at the algebra layer. NaN / ±∞ are
26201 // pinned separately at `atom_to_json_float_nan_and_infinity_collapse_to_null`
26202 // — those don't produce a `Number` from `to_json` so the
26203 // round-trip law does NOT apply to them.
26204 for f in [
26205 1.5f64,
26206 -2.5,
26207 0.1,
26208 -0.1,
26209 f64::MIN_POSITIVE,
26210 1.234_567_890_123,
26211 ] {
26212 let atom_before = Atom::Float(f);
26213 let via_forward = atom_before.to_json();
26214 let n = match via_forward {
26215 serde_json::Value::Number(n) => n,
26216 other => panic!(
26217 "Atom::Float({f}).to_json() must project to JValue::Number, got {other:?}",
26218 ),
26219 };
26220 assert_eq!(
26221 Atom::from_json_number(&n),
26222 atom_before,
26223 "Atom::Float({f}) round-trip through to_json + from_json_number drifted",
26224 );
26225 }
26226 }
26227
26228 #[test]
26229 fn atom_to_json_float_nan_and_infinity_collapse_to_null() {
26230 // JSON-INEXPRESSIBILITY PIN: JSON has no canonical form for
26231 // `NaN` / `±∞` — `serde_json::Number::from_f64` returns `None`
26232 // for those values, and the substrate's pre-lift behavior at
26233 // `sexp_to_json` mapped them to `JValue::Null` via
26234 // `unwrap_or(JValue::Null)`. Pin the special-case branch at
26235 // the typed-algebra boundary directly so a future refactor
26236 // that bypasses `serde_json::Number::from_f64` (e.g. emits
26237 // `NaN` as the string `"NaN"`, which the JSON deserializer
26238 // would silently re-read as a String at the round-trip
26239 // boundary) surfaces at this test without requiring a Sexp
26240 // wrap to reproduce. Sibling-shape pin to
26241 // `atom_display_renders_integral_float_with_dot_zero_suffix`
26242 // — both pin a non-default branch of the float projection's
26243 // canonical rendering. The branch IS load-bearing for the
26244 // `sexp_to_json` → `serde_json::from_value::<T>` bridge the
26245 // derive-macro fallthrough uses: a downstream `f64` field
26246 // that the operator wrote `:rate :nan` for collapses to
26247 // `JValue::Null` HERE rather than at the serde boundary,
26248 // emitting a clean structural diagnostic instead of a JSON
26249 // parse error miles downstream.
26250 use serde_json::Value as JValue;
26251 assert_eq!(Atom::Float(f64::NAN).to_json(), JValue::Null);
26252 assert_eq!(Atom::Float(f64::INFINITY).to_json(), JValue::Null);
26253 assert_eq!(Atom::Float(f64::NEG_INFINITY).to_json(), JValue::Null);
26254 }
26255
26256 #[test]
26257 fn atom_from_lexeme_classifies_each_atom_kind_for_canonical_lexeme() {
26258 // CANONICAL-CLASSIFICATION CONTRACT: pin that `Atom::from_lexeme`
26259 // produces byte-identical typed `Atom` outputs for a canonical
26260 // lexeme of each `AtomKind` variant against the pre-lift
26261 // `crate::reader::atom_from_str` cascade. Sweeps a representative
26262 // lexeme of each variant so a regression that drifts ONE arm
26263 // (e.g. swaps `"#t"` to `Atom::Symbol("#t")` silently breaking
26264 // every `:values-overlay` payload pinned by the CLAUDE.md bool
26265 // warning, or strips `":kw"`'s prefix when classifying to
26266 // `Atom::Symbol` rather than `Atom::Keyword`) fails loudly.
26267 // Sibling-arm sweep to
26268 // `atom_display_renders_each_variant_to_canonical_form` and
26269 // `atom_to_json_projects_each_variant_to_canonical_json_value` —
26270 // all three pin the typed-algebra at its canonical per-variant
26271 // projection. This is the typed-ENTRY side of the bidirectional
26272 // sweep; those are the typed-EXIT sides.
26273 //
26274 // `Atom::Str` is intentionally absent — `Atom::from_lexeme`'s
26275 // typed-entry surface processes BARE reader-token lexemes, and
26276 // string literals take the reader's `"`-quoted tokenizer branch
26277 // (a `Token::Str(_)`, NOT a `Token::Atom(_)`). The reader's
26278 // string round-trip is pinned by `string_escapes` in
26279 // `crate::reader::tests`.
26280 assert_eq!(Atom::from_lexeme("foo"), Atom::Symbol("foo".into()));
26281 assert_eq!(
26282 Atom::from_lexeme("defpoint"),
26283 Atom::Symbol("defpoint".into())
26284 );
26285 assert_eq!(Atom::from_lexeme("seph.1"), Atom::Symbol("seph.1".into()));
26286 assert_eq!(Atom::from_lexeme(":parent"), Atom::Keyword("parent".into()));
26287 assert_eq!(Atom::from_lexeme(":kw"), Atom::Keyword("kw".into()));
26288 assert_eq!(Atom::from_lexeme("42"), Atom::Int(42));
26289 assert_eq!(Atom::from_lexeme("-7"), Atom::Int(-7));
26290 assert_eq!(Atom::from_lexeme("0"), Atom::Int(0));
26291 assert_eq!(Atom::from_lexeme("1.5"), Atom::Float(1.5));
26292 assert_eq!(Atom::from_lexeme("-2.5"), Atom::Float(-2.5));
26293 assert_eq!(Atom::from_lexeme("#t"), Atom::Bool(true));
26294 assert_eq!(Atom::from_lexeme("#f"), Atom::Bool(false));
26295 }
26296
26297 #[test]
26298 fn atom_from_lexeme_prefers_int_over_float_for_integer_lexeme() {
26299 // LOAD-BEARING DISPATCH-ORDERING PIN: `Atom::from_lexeme` tries
26300 // `i64::from_str` BEFORE `f64::from_str` so a bare `"1"`
26301 // classifies as `Atom::Int(1)`, NOT `Atom::Float(1.0)`. The
26302 // typed-int-vs-typed-float distinction at the typed-entry
26303 // boundary is the dual of `fmt_float`'s `.0`-suffix discipline
26304 // on the typed-exit side — together the two projections form
26305 // the round-trip identity `from_lexeme(a.to_string()) == a`
26306 // for both `Int(_)` and `Float(_)` payloads pinned by
26307 // `atom_from_lexeme_round_trips_with_atom_display_for_every_non_str_variant`
26308 // below. A regression that reorders the parse-cascade (e.g.
26309 // tries `f64::from_str` first, or unifies both via
26310 // `f64::from_str` alone since `f64` parse accepts integer
26311 // lexemes too) silently demotes every integer authoring slot
26312 // into the float track at the reader, corrupting every
26313 // downstream `i64` field's serde round-trip without a
26314 // structural error to point to.
26315 assert_eq!(Atom::from_lexeme("1"), Atom::Int(1));
26316 assert_eq!(Atom::from_lexeme("0"), Atom::Int(0));
26317 assert_eq!(Atom::from_lexeme("-100"), Atom::Int(-100));
26318 // The bare-int lexeme MUST NOT classify to `Atom::Float`.
26319 assert_ne!(Atom::from_lexeme("1"), Atom::Float(1.0));
26320 // Float lexemes (with explicit `.` or scientific notation)
26321 // route through the f64 arm — pin the cascade's fallthrough
26322 // ordering so the int-shortcut doesn't swallow them.
26323 assert_eq!(Atom::from_lexeme("1.0"), Atom::Float(1.0));
26324 assert_eq!(Atom::from_lexeme("1.5"), Atom::Float(1.5));
26325 assert_eq!(Atom::from_lexeme("1e3"), Atom::Float(1e3));
26326 }
26327
26328 #[test]
26329 fn atom_from_lexeme_routes_unknown_lexeme_to_symbol_default() {
26330 // CLOSED-SET DEFAULT-ARM PIN: every lexeme that didn't match a
26331 // structural prefix (`"#t"`/`"#f"` for Bool, `":"` prefix for
26332 // Keyword) or parse as a number (`i64` then `f64`) classifies
26333 // to `Atom::Symbol(_)` by default — the closed-set fallthrough
26334 // arm the reader has shipped with from inception. Pin the
26335 // default-arm projection so a future refactor that adds a new
26336 // structural prefix (e.g. `"#["` for vector literals, `"#\\x"`
26337 // for char literals) without updating the default-arm wording
26338 // cannot silently drift previously-Symbol lexemes into a new
26339 // bucket — the regression surfaces at this test, which sweeps
26340 // the structural-prefix non-matches every closed-set extension
26341 // must continue to classify as Symbol unless the extension
26342 // explicitly claims them. Sibling-shape pin to
26343 // `atom_from_lexeme_classifies_each_atom_kind_for_canonical_lexeme`
26344 // — that pins the structural-prefix MATCHES, this pins the
26345 // structural-prefix NON-MATCHES.
26346 //
26347 // The CLAUDE.md-pinned `true`/`false` round-trip discipline
26348 // also rides this default arm: bare `true`/`false` re-read as
26349 // `Atom::Symbol("true")` / `Atom::Symbol("false")` because the
26350 // Scheme bool spellings are `"#t"`/`"#f"`. The pin guards the
26351 // `serde_json::Value::Bool` field round-trip every
26352 // `:values-overlay` payload depends on.
26353 assert_eq!(Atom::from_lexeme("foo"), Atom::Symbol("foo".into()));
26354 assert_eq!(
26355 Atom::from_lexeme("defpoint"),
26356 Atom::Symbol("defpoint".into())
26357 );
26358 // The CLAUDE.md `true`/`false` warning — these lexemes MUST
26359 // route through the default Symbol arm, NOT through the Bool
26360 // arm. A regression that adds `"true"`/`"false"` recognition
26361 // silently flips every `:values-overlay` Bool field to the
26362 // wrong serde shape.
26363 assert_eq!(Atom::from_lexeme("true"), Atom::Symbol("true".into()));
26364 assert_eq!(Atom::from_lexeme("false"), Atom::Symbol("false".into()));
26365 // Non-structural-prefix shapes — pin a sampling so the
26366 // default arm continues to absorb every shape the prefix
26367 // arms haven't claimed.
26368 assert_eq!(Atom::from_lexeme("seph.1"), Atom::Symbol("seph.1".into()));
26369 assert_eq!(Atom::from_lexeme("a-b"), Atom::Symbol("a-b".into()));
26370 assert_eq!(Atom::from_lexeme("+"), Atom::Symbol("+".into()));
26371 }
26372
26373 #[test]
26374 fn atom_from_lexeme_round_trips_with_atom_display_for_every_non_str_variant() {
26375 // BIDIRECTIONAL TYPED-IDENTITY CONTRACT: render each `Atom`
26376 // (excluding `Atom::Str` — see below) via `fmt::Display`, parse
26377 // the rendering through `Atom::from_lexeme`, and pin that the
26378 // round-trip preserves the typed identity exactly. This is the
26379 // typed-exit / typed-entry mirror at the atomic-payload
26380 // boundary AT THE ALGEBRA LEVEL — sibling-shape pin to
26381 // `atom_display_round_trips_through_reader_preserving_typed_identity`
26382 // which exercises the same round-trip through the full reader.
26383 // Lifting the typed-entry surface onto `Atom::from_lexeme`
26384 // means the round-trip law now lives at the algebra rather
26385 // than at the reader's free-function boundary — a future
26386 // tool that wants to round-trip an `Atom` through its
26387 // canonical lexeme spelling (LSP token-completion, REPL
26388 // pretty-printer, structural editor) binds to `from_lexeme` +
26389 // `Display` directly without crossing through the reader's
26390 // tokenizer.
26391 //
26392 // `Atom::Str` is intentionally absent — `Display for Atom`
26393 // renders `Str(s)` as `"{s:?}"` (debug-quoted, with quote
26394 // marks around the content). The quoted form is NOT a bare
26395 // reader-token lexeme: it's a `Token::Str(_)` to the
26396 // tokenizer, taking a distinct branch. The Str round-trip
26397 // through the FULL reader is pinned by `string_escapes` in
26398 // `crate::reader::tests`.
26399 let cases: &[Atom] = &[
26400 Atom::Symbol("foo-bar".into()),
26401 Atom::Symbol("defpoint".into()),
26402 Atom::Symbol("seph.1".into()),
26403 Atom::Keyword("parent".into()),
26404 Atom::Keyword("kw".into()),
26405 Atom::Int(0),
26406 Atom::Int(42),
26407 Atom::Int(-7),
26408 Atom::Float(1.0),
26409 Atom::Float(1.5),
26410 Atom::Float(-42.0),
26411 Atom::Bool(true),
26412 Atom::Bool(false),
26413 ];
26414 for seed in cases {
26415 let rendered = seed.to_string();
26416 let round_tripped = Atom::from_lexeme(&rendered);
26417 assert_eq!(
26418 round_tripped.kind(),
26419 seed.kind(),
26420 "Atom::from_lexeme∘Display drifted variant for {seed:?} via {rendered:?}"
26421 );
26422 assert_eq!(
26423 round_tripped, *seed,
26424 "Atom::from_lexeme∘Display drifted payload for {seed:?} via {rendered:?}"
26425 );
26426 }
26427 }
26428
26429 // ── Atom::as_X soft-projection family + Sexp::as_atom structural lift ──
26430 //
26431 // The six per-variant soft-projection methods on the typed `Atom` algebra
26432 // (`as_symbol` / `as_keyword` / `as_string` / `as_int` / `as_float` /
26433 // `as_bool`) lift the inline `Self::Atom(Atom::X(s)) => Some(s)` arms
26434 // that previously lived at the six `Sexp::as_X` consumer sites onto ONE
26435 // method per closed-set arm. The `Sexp::as_atom` structural lift gives
26436 // the consumer family a uniform two-step composition `as_atom().and_then
26437 // (Atom::as_X)`. The tests below pin:
26438 //
26439 // (1) per-variant typed projection — `Atom::as_X` returns `Some(payload)`
26440 // iff the variant matches AND `None` for every other closed-set arm
26441 // (path-uniformity over `AtomKind::ALL`);
26442 // (2) the `Sexp::as_atom` projection — `Some(&Atom)` iff `Sexp::Atom(_)`
26443 // AND `None` for the structural shapes (`Nil` / `List` /
26444 // `Quote` / `Quasiquote` / `Unquote` / `UnquoteSplice`);
26445 // (3) lifted-boundary composition — `Sexp::as_<X>(s) == s.as_atom()
26446 // .and_then(Atom::as_<X>)` for every atomic variant, AND the
26447 // `Sexp::as_float` widening specialization (`Atom::Int(n)` →
26448 // `Some(n as f64)`) lives at the consumer layer, NOT the algebra
26449 // layer (per the typed-identity discipline pinned at
26450 // `Atom::as_int`'s docstring).
26451
26452 #[test]
26453 fn atom_as_symbol_returns_payload_iff_symbol_variant() {
26454 // PER-VARIANT PROJECTION CONTRACT: `Atom::as_symbol` projects
26455 // `Atom::Symbol(s)` to `Some(&s)` and every other `AtomKind`
26456 // variant to `None`. Sweeps `AtomKind::ALL` for the path-
26457 // uniformity guard — catches a regression that mis-routes ONE
26458 // arm (e.g. accepts `Atom::Keyword(s)` thinking it's "also a
26459 // symbol-like identifier", or rejects `Atom::Symbol("foo")` if
26460 // a future closed-set sweep accidentally narrows the projection
26461 // by an `s.is_empty()` filter).
26462 assert_eq!(Atom::Symbol("foo".into()).as_symbol(), Some("foo"));
26463 assert_eq!(Atom::Symbol("seph.1".into()).as_symbol(), Some("seph.1"));
26464 assert_eq!(Atom::Symbol(String::new()).as_symbol(), Some(""));
26465 for kind in AtomKind::ALL {
26466 if kind == AtomKind::Symbol {
26467 continue;
26468 }
26469 let probe: Atom = match kind {
26470 AtomKind::Symbol => unreachable!(),
26471 AtomKind::Keyword => Atom::Keyword("kw".into()),
26472 AtomKind::Str => Atom::Str("body".into()),
26473 AtomKind::Int => Atom::Int(42),
26474 AtomKind::Float => Atom::Float(1.5),
26475 AtomKind::Bool => Atom::Bool(true),
26476 };
26477 assert_eq!(
26478 probe.as_symbol(),
26479 None,
26480 "Atom::as_symbol must reject non-Symbol variant {kind:?}",
26481 );
26482 }
26483 }
26484
26485 #[test]
26486 fn atom_as_keyword_returns_payload_iff_keyword_variant() {
26487 // PER-VARIANT PROJECTION CONTRACT: `Atom::as_keyword` projects
26488 // `Atom::Keyword(s)` to `Some(&s)` and every other `AtomKind`
26489 // variant to `None`. The returned `&str` is the BARE identifier
26490 // (the `:` prefix was already stripped at the typed-ENTRY
26491 // classifier boundary, `Atom::from_lexeme`); this projection
26492 // does not re-add or re-strip the prefix — pinned by the empty
26493 // probe to catch a regression that accidentally trims a leading
26494 // char.
26495 assert_eq!(Atom::Keyword("parent".into()).as_keyword(), Some("parent"));
26496 assert_eq!(Atom::Keyword(String::new()).as_keyword(), Some(""));
26497 for kind in AtomKind::ALL {
26498 if kind == AtomKind::Keyword {
26499 continue;
26500 }
26501 let probe: Atom = match kind {
26502 AtomKind::Symbol => Atom::Symbol("foo".into()),
26503 AtomKind::Keyword => unreachable!(),
26504 AtomKind::Str => Atom::Str("body".into()),
26505 AtomKind::Int => Atom::Int(42),
26506 AtomKind::Float => Atom::Float(1.5),
26507 AtomKind::Bool => Atom::Bool(true),
26508 };
26509 assert_eq!(
26510 probe.as_keyword(),
26511 None,
26512 "Atom::as_keyword must reject non-Keyword variant {kind:?}",
26513 );
26514 }
26515 }
26516
26517 #[test]
26518 fn atom_as_string_returns_payload_iff_str_variant() {
26519 // PER-VARIANT PROJECTION CONTRACT: `Atom::as_string` projects
26520 // `Atom::Str(s)` to `Some(&s)` and every other `AtomKind`
26521 // variant (including `Symbol` and `Keyword`, which also carry
26522 // `String` payloads) to `None`. The closed-set discriminator is
26523 // load-bearing: a `Symbol("foo")` MUST NOT route through this
26524 // projection — a regression that conflates the three string-
26525 // carrying variants would silently re-classify operator-position
26526 // symbols as string-typed kwarg values at every `extract_string`
26527 // boundary.
26528 assert_eq!(Atom::Str("body".into()).as_string(), Some("body"));
26529 assert_eq!(
26530 Atom::Str("with\nnewline".into()).as_string(),
26531 Some("with\nnewline"),
26532 );
26533 assert_eq!(Atom::Str(String::new()).as_string(), Some(""));
26534 assert_eq!(
26535 Atom::Symbol("looks-like-a-string".into()).as_string(),
26536 None,
26537 "Atom::as_string MUST NOT conflate Symbol with Str — load-bearing typed-identity",
26538 );
26539 assert_eq!(
26540 Atom::Keyword("looks-like-a-string".into()).as_string(),
26541 None,
26542 "Atom::as_string MUST NOT conflate Keyword with Str — load-bearing typed-identity",
26543 );
26544 for kind in [AtomKind::Int, AtomKind::Float, AtomKind::Bool] {
26545 let probe: Atom = match kind {
26546 AtomKind::Int => Atom::Int(42),
26547 AtomKind::Float => Atom::Float(1.5),
26548 AtomKind::Bool => Atom::Bool(true),
26549 _ => unreachable!(),
26550 };
26551 assert_eq!(
26552 probe.as_string(),
26553 None,
26554 "Atom::as_string must reject non-Str variant {kind:?}",
26555 );
26556 }
26557 }
26558
26559 #[test]
26560 fn atom_as_int_returns_payload_iff_int_variant_strict_no_float_widening() {
26561 // PER-VARIANT PROJECTION CONTRACT (STRICT): `Atom::as_int`
26562 // projects `Atom::Int(n)` to `Some(n)` and every other variant
26563 // to `None`. STRICT typed identity: `Atom::Float(1.0)` does
26564 // NOT project through (stays `None`) — the typed-identity
26565 // distinction `Int(1)` vs `Float(1.0)` (load-bearing at the
26566 // `Atom::from_lexeme` ⇄ `Atom::Display` round-trip boundary, dual of
26567 // `fmt_float`'s `.0`-suffix discipline) is preserved at the
26568 // algebra layer. The widening face lives at the
26569 // `Sexp::as_float` consumer (which accepts both `Float` AND
26570 // `Int`); the strict typed identity at the `Atom` algebra is
26571 // load-bearing.
26572 assert_eq!(Atom::Int(42).as_int(), Some(42));
26573 assert_eq!(Atom::Int(-7).as_int(), Some(-7));
26574 assert_eq!(Atom::Int(0).as_int(), Some(0));
26575 assert_eq!(
26576 Atom::Float(1.0).as_int(),
26577 None,
26578 "Atom::as_int MUST be strict — Float(1.0) is NOT Int(1) at the algebra layer",
26579 );
26580 for kind in [
26581 AtomKind::Symbol,
26582 AtomKind::Keyword,
26583 AtomKind::Str,
26584 AtomKind::Float,
26585 AtomKind::Bool,
26586 ] {
26587 let probe: Atom = match kind {
26588 AtomKind::Symbol => Atom::Symbol("foo".into()),
26589 AtomKind::Keyword => Atom::Keyword("kw".into()),
26590 AtomKind::Str => Atom::Str("body".into()),
26591 AtomKind::Float => Atom::Float(1.5),
26592 AtomKind::Bool => Atom::Bool(true),
26593 _ => unreachable!(),
26594 };
26595 assert_eq!(
26596 probe.as_int(),
26597 None,
26598 "Atom::as_int must reject non-Int variant {kind:?}",
26599 );
26600 }
26601 }
26602
26603 #[test]
26604 fn atom_as_float_returns_payload_iff_float_variant_strict_no_int_widening() {
26605 // PER-VARIANT PROJECTION CONTRACT (STRICT): `Atom::as_float`
26606 // projects `Atom::Float(n)` to `Some(n)` and every other
26607 // variant to `None`. STRICT typed identity: `Atom::Int(1)`
26608 // does NOT project through (stays `None`) — see
26609 // `atom_as_int_returns_payload_iff_int_variant_strict_no_float_widening`
26610 // for the symmetric discipline. The widening face
26611 // (`Atom::Int(n) → Some(n as f64)`) lives at the `Sexp::as_float`
26612 // consumer layer, NOT the algebra layer.
26613 assert_eq!(Atom::Float(1.5).as_float(), Some(1.5));
26614 assert_eq!(Atom::Float(1.0).as_float(), Some(1.0));
26615 assert_eq!(Atom::Float(-42.0).as_float(), Some(-42.0));
26616 assert_eq!(
26617 Atom::Int(1).as_float(),
26618 None,
26619 "Atom::as_float MUST be strict — Int(1) is NOT Float(1.0) at the algebra layer",
26620 );
26621 for kind in [
26622 AtomKind::Symbol,
26623 AtomKind::Keyword,
26624 AtomKind::Str,
26625 AtomKind::Int,
26626 AtomKind::Bool,
26627 ] {
26628 let probe: Atom = match kind {
26629 AtomKind::Symbol => Atom::Symbol("foo".into()),
26630 AtomKind::Keyword => Atom::Keyword("kw".into()),
26631 AtomKind::Str => Atom::Str("body".into()),
26632 AtomKind::Int => Atom::Int(42),
26633 AtomKind::Bool => Atom::Bool(true),
26634 _ => unreachable!(),
26635 };
26636 assert_eq!(
26637 probe.as_float(),
26638 None,
26639 "Atom::as_float must reject non-Float variant {kind:?}",
26640 );
26641 }
26642 }
26643
26644 #[test]
26645 fn atom_as_bool_returns_payload_iff_bool_variant() {
26646 // PER-VARIANT PROJECTION CONTRACT: `Atom::as_bool` projects
26647 // `Atom::Bool(b)` to `Some(b)` and every other variant to
26648 // `None`. Both spellings (`true` / `false`) project through
26649 // the SAME projection — the variant identity (`Bool`) is what
26650 // routes; the inner payload (`true` / `false`) is the
26651 // projected value. CLAUDE.md "Lisp bools": at the reader
26652 // boundary the typed-entry classifier `Atom::from_lexeme`
26653 // routes `"#t"` / `"#f"` to `Atom::Bool(_)` and bare
26654 // `"true"` / `"false"` to `Atom::Symbol(_)`; this projection
26655 // does NOT re-classify the symbol-spelled bools — they STAY
26656 // symbols. The negative test (`Atom::Symbol("true")` rejects)
26657 // pins the discriminator discipline.
26658 assert_eq!(Atom::Bool(true).as_bool(), Some(true));
26659 assert_eq!(Atom::Bool(false).as_bool(), Some(false));
26660 assert_eq!(
26661 Atom::Symbol("true".into()).as_bool(),
26662 None,
26663 "Atom::as_bool MUST reject Symbol(\"true\") — CLAUDE.md typed-identity discipline",
26664 );
26665 assert_eq!(
26666 Atom::Symbol("false".into()).as_bool(),
26667 None,
26668 "Atom::as_bool MUST reject Symbol(\"false\") — CLAUDE.md typed-identity discipline",
26669 );
26670 for kind in [
26671 AtomKind::Symbol,
26672 AtomKind::Keyword,
26673 AtomKind::Str,
26674 AtomKind::Int,
26675 AtomKind::Float,
26676 ] {
26677 let probe: Atom = match kind {
26678 AtomKind::Symbol => Atom::Symbol("foo".into()),
26679 AtomKind::Keyword => Atom::Keyword("kw".into()),
26680 AtomKind::Str => Atom::Str("body".into()),
26681 AtomKind::Int => Atom::Int(42),
26682 AtomKind::Float => Atom::Float(1.5),
26683 _ => unreachable!(),
26684 };
26685 assert_eq!(
26686 probe.as_bool(),
26687 None,
26688 "Atom::as_bool must reject non-Bool variant {kind:?}",
26689 );
26690 }
26691 }
26692
26693 #[test]
26694 fn atom_as_symbol_or_string_returns_payload_iff_symbol_or_str_variant() {
26695 // UNION-PROJECTION CONTRACT: `Atom::as_symbol_or_string` projects
26696 // BOTH `Atom::Symbol(s)` AND `Atom::Str(s)` to `Some(s)` and every
26697 // other atomic kind (`Keyword`, `Int`, `Float`, `Bool`) to `None`.
26698 // The disjunctive composition `as_symbol().or_else(||
26699 // as_string())` lives at ONE typed-algebra projection on the
26700 // closed-set `Atom` algebra; pre-lift the composition lived at
26701 // `Sexp::as_symbol_or_string`'s consumer body and traversed
26702 // `Sexp::as_atom` TWICE (once per per-variant projection),
26703 // post-lift it traverses `Sexp::as_atom` ONCE through the
26704 // algebra-level union projection. Pin the algebra-level contract
26705 // sweep so a regression that drifts ONE union arm (e.g. drops the
26706 // `Str` arm, accidentally widens to accept `Keyword`) surfaces
26707 // structurally.
26708 assert_eq!(
26709 Atom::Symbol("my-name".into()).as_symbol_or_string(),
26710 Some("my-name"),
26711 "Atom::as_symbol_or_string must accept Atom::Symbol",
26712 );
26713 assert_eq!(
26714 Atom::Str("my-name".into()).as_symbol_or_string(),
26715 Some("my-name"),
26716 "Atom::as_symbol_or_string must accept Atom::Str",
26717 );
26718 // Empty payloads project through too — the union projection
26719 // is keyed on variant identity, not payload contents.
26720 assert_eq!(
26721 Atom::Symbol(String::new()).as_symbol_or_string(),
26722 Some(""),
26723 "Atom::as_symbol_or_string must accept empty Symbol payload",
26724 );
26725 assert_eq!(
26726 Atom::Str(String::new()).as_symbol_or_string(),
26727 Some(""),
26728 "Atom::as_symbol_or_string must accept empty Str payload",
26729 );
26730 // Negative sweep: the four non-Symbol-non-Str variants reject.
26731 for kind in [
26732 AtomKind::Keyword,
26733 AtomKind::Int,
26734 AtomKind::Float,
26735 AtomKind::Bool,
26736 ] {
26737 let probe: Atom = match kind {
26738 AtomKind::Keyword => Atom::Keyword("kw".into()),
26739 AtomKind::Int => Atom::Int(42),
26740 AtomKind::Float => Atom::Float(1.5),
26741 AtomKind::Bool => Atom::Bool(true),
26742 _ => unreachable!(),
26743 };
26744 assert_eq!(
26745 probe.as_symbol_or_string(),
26746 None,
26747 "Atom::as_symbol_or_string must reject non-Symbol-non-Str variant {kind:?}",
26748 );
26749 }
26750 }
26751
26752 #[test]
26753 fn atom_as_symbol_or_string_borrow_ptr_eq_payload() {
26754 // BORROW-LIFETIME CONTRACT: the yielded `&str` borrows the inner
26755 // `String` payload's `&str` view verbatim — no copy, no
26756 // allocation, no `to_string()` round-trip. Pin via `ptr::eq` on
26757 // both projection sides (Symbol arm AND Str arm) so a regression
26758 // that re-inlines the union as `match self { Symbol(s) =>
26759 // Some(s.clone().as_str()), … }` (a `String::clone` reborrow that
26760 // changes the byte-identity) surfaces structurally. Same posture
26761 // as `as_call_to_args_borrow_is_same_pointer_as_as_call_tail` on
26762 // the call-form algebra.
26763 let sym = Atom::Symbol("my-name".into());
26764 let projected = sym.as_symbol_or_string().expect("Symbol arm projects");
26765 match &sym {
26766 Atom::Symbol(s) => assert!(
26767 std::ptr::eq(projected.as_ptr(), s.as_ptr()),
26768 "Atom::as_symbol_or_string must borrow Atom::Symbol payload verbatim",
26769 ),
26770 _ => unreachable!(),
26771 }
26772 let str_atom = Atom::Str("my-name".into());
26773 let projected_str = str_atom.as_symbol_or_string().expect("Str arm projects");
26774 match &str_atom {
26775 Atom::Str(s) => assert!(
26776 std::ptr::eq(projected_str.as_ptr(), s.as_ptr()),
26777 "Atom::as_symbol_or_string must borrow Atom::Str payload verbatim",
26778 ),
26779 _ => unreachable!(),
26780 }
26781 }
26782
26783 #[test]
26784 fn atom_as_symbol_or_string_is_the_disjunction_of_as_symbol_and_as_string() {
26785 // COMPOSITION LAW: pin that the union projection's value AGREES
26786 // byte-for-byte with the explicit disjunctive composition
26787 // `as_symbol().or_else(|| as_string())` across every atom kind.
26788 // A regression that drifts the union from its disjunctive
26789 // composition (e.g. swaps the `or_else` order so an
26790 // `Atom::Symbol` somehow routes through the `Str` arm first, or
26791 // adds a phantom arm that accepts `Keyword` payloads) surfaces
26792 // here. Same posture as `is_kwargs_list` composing through
26793 // `as_list ∘ atom_as_keyword`.
26794 for atom in [
26795 Atom::Symbol("foo".into()),
26796 Atom::Keyword("kw".into()),
26797 Atom::Str("body".into()),
26798 Atom::Int(42),
26799 Atom::Float(1.5),
26800 Atom::Bool(true),
26801 Atom::Bool(false),
26802 Atom::Symbol(String::new()),
26803 Atom::Str(String::new()),
26804 ] {
26805 let by_hand = atom.as_symbol().or_else(|| atom.as_string());
26806 assert_eq!(
26807 atom.as_symbol_or_string(),
26808 by_hand,
26809 "Atom::as_symbol_or_string drifted from as_symbol().or_else(|| as_string()) for {atom:?}",
26810 );
26811 }
26812 }
26813
26814 #[test]
26815 fn sexp_as_symbol_or_string_routes_through_atom_as_symbol_or_string_via_as_atom_composition() {
26816 // CONSUMER-LAYER COMPOSITION LAW: pin that `Sexp::as_symbol_or_string`
26817 // routes through the structural lift `Sexp::as_atom` + the
26818 // algebra-level `Atom::as_symbol_or_string` union projection —
26819 // a regression that re-inlines the pre-lift body
26820 // `self.as_symbol().or_else(|| self.as_string())` (TWO
26821 // `Sexp::as_atom` traversals) at the `Sexp` consumer layer
26822 // becomes detectable here. Sweeps every reachable outer shape so
26823 // the closed-form composition is pinned across Nil + every Atom
26824 // variant + every quote-family wrapper + List + the Sexp::Atom
26825 // arms a regression could route to.
26826 let cases = [
26827 Sexp::Nil,
26828 Sexp::symbol("foo"),
26829 Sexp::symbol(""),
26830 Sexp::string("body"),
26831 Sexp::string(""),
26832 Sexp::keyword("kw"),
26833 Sexp::int(7),
26834 Sexp::float(2.5),
26835 Sexp::boolean(true),
26836 Sexp::Quote(Box::new(Sexp::symbol("x"))),
26837 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
26838 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
26839 Sexp::UnquoteSplice(Box::new(Sexp::symbol("x"))),
26840 Sexp::List(vec![Sexp::symbol("a")]),
26841 Sexp::List(vec![]),
26842 ];
26843 for s in &cases {
26844 let by_composition = s.as_atom().and_then(Atom::as_symbol_or_string);
26845 assert_eq!(
26846 s.as_symbol_or_string(),
26847 by_composition,
26848 "Sexp::as_symbol_or_string drifted from as_atom().and_then(Atom::as_symbol_or_string) for {s}",
26849 );
26850 }
26851 }
26852
26853 #[test]
26854 fn sexp_as_symbol_or_string_yields_none_for_non_atom_outer_shapes() {
26855 // OUTER-SHAPE NEGATIVE SWEEP: pin that every non-Atom outer
26856 // shape (`Nil`, `List`, every quote-family wrapper) projects to
26857 // `None` — the structural-lift `Sexp::as_atom` rejects them at
26858 // the outer match before the union projection even runs. Pins
26859 // the soft-projection face: the named-form NAME gate
26860 // (`crate::compile::split_name_slot`'s `as_symbol_or_string`
26861 // consumer at compile.rs:671) sees `None` for these shapes and
26862 // emits `NamedFormNonSymbolName` with the projected `SexpShape`
26863 // — the lift preserves the same rejection arm boundary.
26864 for outer in [
26865 Sexp::Nil,
26866 Sexp::List(vec![Sexp::symbol("a")]),
26867 Sexp::List(vec![]),
26868 Sexp::Quote(Box::new(Sexp::symbol("x"))),
26869 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
26870 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
26871 Sexp::UnquoteSplice(Box::new(Sexp::symbol("x"))),
26872 ] {
26873 assert_eq!(
26874 outer.as_symbol_or_string(),
26875 None,
26876 "Sexp::as_symbol_or_string must reject non-Atom outer shape {outer:?}",
26877 );
26878 }
26879 }
26880
26881 #[test]
26882 fn sexp_as_symbol_or_string_borrow_ptr_eq_atom_payload() {
26883 // BORROW-LIFETIME CONTRACT: the yielded `&str` borrows the inner
26884 // `Atom::Symbol` / `Atom::Str` payload verbatim — no copy, no
26885 // allocation, same lifetime as the outer `&Sexp`. Pin via
26886 // `ptr::eq` on both projection sides so a regression that
26887 // re-inlines the union as a `String`-allocating reborrow (e.g.
26888 // `.map(|s| s.to_owned())` somewhere along the chain) surfaces
26889 // structurally. Sibling pin to
26890 // `atom_as_symbol_or_string_borrow_ptr_eq_payload` at the outer
26891 // (`&Sexp`) layer rather than the inner (`&Atom`) layer.
26892 let sym_sexp = Sexp::symbol("my-name");
26893 let projected = sym_sexp.as_symbol_or_string().expect("Symbol arm projects");
26894 match &sym_sexp {
26895 Sexp::Atom(Atom::Symbol(s)) => assert!(
26896 std::ptr::eq(projected.as_ptr(), s.as_ptr()),
26897 "Sexp::as_symbol_or_string must borrow Atom::Symbol payload verbatim",
26898 ),
26899 _ => unreachable!(),
26900 }
26901 let str_sexp = Sexp::string("my-name");
26902 let projected_str = str_sexp.as_symbol_or_string().expect("Str arm projects");
26903 match &str_sexp {
26904 Sexp::Atom(Atom::Str(s)) => assert!(
26905 std::ptr::eq(projected_str.as_ptr(), s.as_ptr()),
26906 "Sexp::as_symbol_or_string must borrow Atom::Str payload verbatim",
26907 ),
26908 _ => unreachable!(),
26909 }
26910 }
26911
26912 #[test]
26913 fn sexp_as_atom_projects_inner_atom_iff_outer_is_atom_variant() {
26914 // STRUCTURAL-LIFT CONTRACT: `Sexp::as_atom` projects
26915 // `Sexp::Atom(a)` to `Some(&a)` and every other outer shape
26916 // (`Nil` / `List` / `Quote` / `Quasiquote` / `Unquote` /
26917 // `UnquoteSplice`) to `None`. Sweeps each outer shape so a
26918 // regression that mis-routes ONE arm (e.g. accepts the
26919 // singleton list `(a)` thinking the inner counts as the
26920 // "wrapped atom", or rejects an `Atom` whose payload is empty)
26921 // fails loudly. The `&Atom` borrow is rooted at the outer
26922 // `&Sexp` — the projection does not clone, allocate, or take
26923 // ownership.
26924 let atom = Atom::Symbol("foo".into());
26925 let sexp = Sexp::Atom(atom.clone());
26926 assert_eq!(sexp.as_atom(), Some(&atom));
26927
26928 for outer in [
26929 Sexp::Nil,
26930 Sexp::List(vec![Sexp::symbol("a")]),
26931 Sexp::List(vec![]),
26932 Sexp::Quote(Box::new(Sexp::symbol("x"))),
26933 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
26934 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
26935 Sexp::UnquoteSplice(Box::new(Sexp::symbol("x"))),
26936 ] {
26937 assert_eq!(
26938 outer.as_atom(),
26939 None,
26940 "Sexp::as_atom must reject non-Atom outer shape {outer:?}",
26941 );
26942 }
26943 }
26944
26945 #[test]
26946 fn sexp_shape_method_projects_each_outer_arm_to_canonical_sexp_shape() {
26947 // CANONICAL-MAPPING CONTRACT: pin that `Sexp::shape()` produces
26948 // byte-identical `SexpShape` markers for each outer-arm of the
26949 // closed `Sexp` algebra. Sweeps every reachable outer shape
26950 // (`Nil`, every `AtomKind` payload, `List`, every `QuoteForm`
26951 // wrapper) so a regression that drifts ONE arm (e.g. routes the
26952 // `Atom::Keyword` arm through `Atom::kind().sexp_shape()` to the
26953 // wrong `SexpShape` variant, or drops the `expect_quote_form`
26954 // projection's marker for a quote-family wrapper) fails loudly.
26955 // Sibling-arm sweep to
26956 // `quote_form_sexp_shape_pins_canonical_shape_identity_for_every_variant`
26957 // (the four quote-family arms in isolation) AND
26958 // `atom_kind_sexp_shape_pins_canonical_atom_payload_shape_for_every_variant`
26959 // (the six atomic-payload arms in isolation) — this test pins
26960 // the OUTER projection that COMPOSES both peer algebras + the
26961 // `Nil` / `List` arms into ONE typed method on the `Sexp`
26962 // algebra.
26963 use crate::error::SexpShape;
26964 assert_eq!(Sexp::Nil.shape(), SexpShape::Nil);
26965 assert_eq!(Sexp::symbol("foo").shape(), SexpShape::Symbol);
26966 assert_eq!(Sexp::keyword("k").shape(), SexpShape::Keyword);
26967 assert_eq!(Sexp::string("s").shape(), SexpShape::String);
26968 assert_eq!(Sexp::int(7).shape(), SexpShape::Int);
26969 assert_eq!(Sexp::float(7.5).shape(), SexpShape::Float);
26970 assert_eq!(Sexp::boolean(true).shape(), SexpShape::Bool);
26971 assert_eq!(Sexp::List(vec![]).shape(), SexpShape::List);
26972 assert_eq!(
26973 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]).shape(),
26974 SexpShape::List,
26975 "non-empty list must project to SexpShape::List — payload count is irrelevant",
26976 );
26977 assert_eq!(Sexp::Quote(Box::new(Sexp::Nil)).shape(), SexpShape::Quote);
26978 assert_eq!(
26979 Sexp::Quasiquote(Box::new(Sexp::Nil)).shape(),
26980 SexpShape::Quasiquote
26981 );
26982 assert_eq!(
26983 Sexp::Unquote(Box::new(Sexp::Nil)).shape(),
26984 SexpShape::Unquote
26985 );
26986 assert_eq!(
26987 Sexp::UnquoteSplice(Box::new(Sexp::Nil)).shape(),
26988 SexpShape::UnquoteSplice
26989 );
26990 }
26991
26992 #[test]
26993 fn sexp_shape_method_agrees_with_domain_sexp_shape_for_every_outer_shape() {
26994 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
26995 // `Sexp::shape()` method agrees with the free-function
26996 // delegate `crate::domain::sexp_shape` for every reachable
26997 // outer shape. Pre-lift the dispatcher lived as a free
26998 // function in `domain.rs`; post-lift the canonical site is
26999 // the inherent method on the `Sexp` algebra and the free
27000 // function is a one-line delegate. Pin that the delegation
27001 // stays byte-for-byte equivalent across every outer arm so a
27002 // regression where the free function drifts from the inherent
27003 // method (or vice versa) surfaces here immediately. Catches
27004 // a future "consolidation" that removes the free function
27005 // without updating the method, or vice versa.
27006 let samples = [
27007 Sexp::Nil,
27008 Sexp::symbol("foo"),
27009 Sexp::keyword("k"),
27010 Sexp::string("s"),
27011 Sexp::int(7),
27012 Sexp::int(-1),
27013 Sexp::float(7.5),
27014 Sexp::float(0.0),
27015 Sexp::boolean(true),
27016 Sexp::boolean(false),
27017 Sexp::List(vec![]),
27018 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
27019 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
27020 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
27021 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
27022 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
27023 ];
27024 for s in &samples {
27025 let via_method = s.shape();
27026 let via_delegate = crate::domain::sexp_shape(s);
27027 assert_eq!(
27028 via_method, via_delegate,
27029 "Sexp::shape and domain::sexp_shape drifted at {s:?}",
27030 );
27031 }
27032 }
27033
27034 #[test]
27035 fn sexp_shape_method_routes_atom_arm_through_atom_kind_sexp_shape_projection() {
27036 // PATH-UNIFORMITY CONTRACT (atomic axis): the lifted
27037 // `Sexp::shape()` routes its Atom arm through
27038 // `Atom::kind().sexp_shape()` — the typed closed-set projection
27039 // on the `AtomKind` algebra. Pin that the composition agrees
27040 // bit-for-bit with the direct `Sexp::shape()` projection across
27041 // every atomic kind variant. A regression in EITHER projection
27042 // direction (an `Atom::kind` arm that swaps markers, or an
27043 // `AtomKind::sexp_shape` arm that drifts its `SexpShape` mapping)
27044 // surfaces here immediately. Sibling shape to
27045 // `sexp_shape_method_routes_quote_family_arms_through_quote_form_sexp_shape_projection`
27046 // for the quote-family axis.
27047 for kind in AtomKind::ALL {
27048 let atom = match kind {
27049 AtomKind::Symbol => Atom::Symbol("name".into()),
27050 AtomKind::Keyword => Atom::Keyword("parent".into()),
27051 AtomKind::Str => Atom::Str("body".into()),
27052 AtomKind::Int => Atom::Int(42),
27053 AtomKind::Float => Atom::Float(1.5),
27054 AtomKind::Bool => Atom::Bool(true),
27055 };
27056 let via_outer = Sexp::Atom(atom.clone()).shape();
27057 let via_composed = atom.kind().sexp_shape();
27058 assert_eq!(
27059 via_outer, via_composed,
27060 "Sexp::shape's Atom arm drifted from Atom::kind().sexp_shape() at {kind:?}",
27061 );
27062 }
27063 }
27064
27065 #[test]
27066 fn sexp_shape_method_routes_quote_family_arms_through_quote_form_sexp_shape_projection() {
27067 // PATH-UNIFORMITY CONTRACT (quote-family axis): the lifted
27068 // `Sexp::shape()` routes its four quote-family arms through
27069 // `as_quote_form() + QuoteForm::sexp_shape()`. Pin that the
27070 // composition agrees bit-for-bit with the direct `Sexp::shape()`
27071 // projection across every quote-family wrapper variant. A
27072 // regression in EITHER projection direction (an `as_quote_form`
27073 // arm that swaps markers, or a `QuoteForm::sexp_shape` arm that
27074 // drifts its `SexpShape` mapping) surfaces here immediately.
27075 // Mirrors the atomic-axis test
27076 // `sexp_shape_method_routes_atom_arm_through_atom_kind_sexp_shape_projection`.
27077 let samples = [
27078 (
27079 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
27080 QuoteForm::Quote,
27081 ),
27082 (
27083 Sexp::Quasiquote(Box::new(Sexp::symbol("payload"))),
27084 QuoteForm::Quasiquote,
27085 ),
27086 (
27087 Sexp::Unquote(Box::new(Sexp::symbol("payload"))),
27088 QuoteForm::Unquote,
27089 ),
27090 (
27091 Sexp::UnquoteSplice(Box::new(Sexp::symbol("payload"))),
27092 QuoteForm::UnquoteSplice,
27093 ),
27094 ];
27095 for (sexp, expected_qf) in &samples {
27096 let via_outer = sexp.shape();
27097 let (qf, _) = sexp
27098 .as_quote_form()
27099 .expect("quote-family sample must project through as_quote_form");
27100 assert_eq!(
27101 qf, *expected_qf,
27102 "as_quote_form drifted typed marker at {sexp:?}"
27103 );
27104 let via_composed = qf.sexp_shape();
27105 assert_eq!(
27106 via_outer, via_composed,
27107 "Sexp::shape drifted from as_quote_form + QuoteForm::sexp_shape at {sexp:?}"
27108 );
27109 }
27110 }
27111
27112 #[test]
27113 fn sexp_shape_method_routes_structural_arms_through_structural_kind_sexp_shape_projection() {
27114 // PATH-UNIFORMITY CONTRACT (structural-residual axis): the
27115 // lifted `Sexp::shape()` routes its two structural-residual
27116 // arms (Nil, List) through `StructuralKind::sexp_shape()`. Pin
27117 // that the composition agrees bit-for-bit with the direct
27118 // `Sexp::shape()` projection across the two structural-residual
27119 // variants. A regression that drifts EITHER projection direction
27120 // (a `Sexp::shape` arm that inlines `SexpShape::Nil` /
27121 // `SexpShape::List` back as a raw literal, or a
27122 // `StructuralKind::sexp_shape` arm that drifts its `SexpShape`
27123 // mapping) surfaces here immediately. Sibling-shape pin to the
27124 // atomic-axis routing test
27125 // `sexp_shape_method_routes_atom_arm_through_atom_kind_sexp_shape_projection`
27126 // and the quote-family-axis routing test
27127 // `sexp_shape_method_routes_quote_family_arms_through_quote_form_sexp_shape_projection`
27128 // — together the three tests pin ALL THREE closed-set
27129 // carving-marker `sexp_shape` compositions the lifted
27130 // `Sexp::shape()` body owns.
27131 let samples = [
27132 (Sexp::Nil, StructuralKind::Nil),
27133 (Sexp::List(vec![]), StructuralKind::List),
27134 (Sexp::List(vec![Sexp::symbol("a")]), StructuralKind::List),
27135 ];
27136 for (sexp, expected_sk) in &samples {
27137 let via_outer = sexp.shape();
27138 let sk = sexp
27139 .as_structural_kind()
27140 .expect("structural-residual sample must project through as_structural_kind");
27141 assert_eq!(
27142 sk, *expected_sk,
27143 "as_structural_kind drifted typed marker at {sexp:?}"
27144 );
27145 let via_composed = sk.sexp_shape();
27146 assert_eq!(
27147 via_outer, via_composed,
27148 "Sexp::shape drifted from as_structural_kind + StructuralKind::sexp_shape at {sexp:?}"
27149 );
27150 }
27151 }
27152
27153 #[test]
27154 fn sexp_as_structural_kind_projects_nil_and_list_to_canonical_structural_kind() {
27155 // PER-ARM CONTRACT: pin that `Sexp::as_structural_kind()`
27156 // projects `Sexp::Nil` to `Some(StructuralKind::Nil)` and
27157 // `Sexp::List(_)` to `Some(StructuralKind::List)` — the two
27158 // structural-residual arms of the `Sexp` algebra. A regression
27159 // that swaps the two arms (routes `Nil` to `Some(List)` or
27160 // vice versa), returns `None` for either, or projects to a
27161 // wrong `StructuralKind` variant surfaces here immediately.
27162 // The List arm is exercised with an empty AND a non-empty
27163 // items slice so a body that gates on `items.is_empty()`
27164 // (rather than the outer arm) fails loudly.
27165 assert_eq!(Sexp::Nil.as_structural_kind(), Some(StructuralKind::Nil));
27166 assert_eq!(
27167 Sexp::List(vec![]).as_structural_kind(),
27168 Some(StructuralKind::List)
27169 );
27170 assert_eq!(
27171 Sexp::List(vec![Sexp::symbol("a")]).as_structural_kind(),
27172 Some(StructuralKind::List)
27173 );
27174 assert_eq!(
27175 Sexp::List(vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)]).as_structural_kind(),
27176 Some(StructuralKind::List)
27177 );
27178 }
27179
27180 #[test]
27181 fn sexp_as_structural_kind_rejects_non_structural_outer_shapes() {
27182 // KERNEL CONTRACT: pin that `Sexp::as_structural_kind()`
27183 // returns `None` for every non-structural outer shape — every
27184 // `Sexp::Atom` variant (the atomic-payload carving) AND every
27185 // quote-family wrapper (the quote-family carving). Sweeps
27186 // every non-residual arm so a regression that accepts an atom
27187 // (e.g. routes `Sexp::Atom(_)` to `Some(List)` because the
27188 // outer arm is misread as a "container" of an atomic payload)
27189 // or a quote-family wrapper (e.g. routes `Sexp::Quote(_)`
27190 // through `_ => Some(_)` because the residual match falls
27191 // through) fails loudly. Sibling-cohort sweep to
27192 // `sexp_as_atom_projects_inner_atom_iff_outer_is_atom_variant`
27193 // — that test pins the atomic-projection kernel, this one
27194 // pins the structural-residual kernel.
27195 for outer in [
27196 Sexp::symbol("foo"),
27197 Sexp::keyword("k"),
27198 Sexp::string("s"),
27199 Sexp::int(7),
27200 Sexp::float(7.5),
27201 Sexp::boolean(true),
27202 Sexp::Quote(Box::new(Sexp::symbol("x"))),
27203 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
27204 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
27205 Sexp::UnquoteSplice(Box::new(Sexp::symbol("x"))),
27206 ] {
27207 assert_eq!(
27208 outer.as_structural_kind(),
27209 None,
27210 "Sexp::as_structural_kind must reject non-structural outer shape {outer:?}",
27211 );
27212 }
27213 }
27214
27215 #[test]
27216 fn sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant() {
27217 // COMPOSITION-LAW CONTRACT: `s.as_structural_kind() ==
27218 // s.shape().as_structural_kind()` for every reachable Sexp
27219 // outer shape. The value-level projection and the shape-level
27220 // projection MUST agree bit-for-bit — the substrate's
27221 // (Sexp value, StructuralKind marker) pairing binds at TWO
27222 // typed methods (one on `Sexp`, one on `SexpShape`) that must
27223 // stay in lockstep. Sweeps every outer shape (residual + atom
27224 // + quote-family) so a drift on ANY arm surfaces immediately.
27225 // Sibling-shape pin to the (Sexp → SexpShape → label) path-
27226 // uniformity test
27227 // `sexp_shape_method_label_composes_with_sexp_type_name_for_every_outer_shape`
27228 // — where that test pins the label-projection composition,
27229 // this one pins the structural-carving-marker projection
27230 // composition.
27231 let samples = [
27232 Sexp::Nil,
27233 Sexp::List(vec![]),
27234 Sexp::List(vec![Sexp::symbol("a")]),
27235 Sexp::symbol("foo"),
27236 Sexp::keyword("k"),
27237 Sexp::string("s"),
27238 Sexp::int(7),
27239 Sexp::float(7.5),
27240 Sexp::boolean(true),
27241 Sexp::Quote(Box::new(Sexp::Nil)),
27242 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27243 Sexp::Unquote(Box::new(Sexp::Nil)),
27244 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27245 ];
27246 for s in &samples {
27247 assert_eq!(
27248 s.as_structural_kind(),
27249 s.shape().as_structural_kind(),
27250 "Sexp::as_structural_kind and Sexp::shape().as_structural_kind must agree at {s:?}",
27251 );
27252 }
27253 }
27254
27255 #[test]
27256 fn sexp_as_structural_kind_partitions_outer_shapes_jointly_with_as_atom_and_as_quote_form() {
27257 // PARTITION-TOTAL CONTRACT (value-level): pin that for every
27258 // reachable Sexp outer shape, EXACTLY ONE of `as_atom`,
27259 // `as_quote_form`, `as_structural_kind` returns `Some(_)`.
27260 // Post-lift the three carving-marker projections at the value
27261 // level form a partition of the `Sexp` variant algebra —
27262 // symmetric with the partition-total invariant pinned at the
27263 // shape level by
27264 // `sexp_shape_partition_is_total_across_atom_quote_structural_carvings`
27265 // (in `error.rs`). A regression that drifts any carving's
27266 // membership (an `as_atom` arm that accepts a non-atom, an
27267 // `as_quote_form` arm that misses a quote-family wrapper, an
27268 // `as_structural_kind` arm that swaps its Nil/List
27269 // membership) surfaces here immediately, so the value-level
27270 // partition invariant is a TYPED THEOREM (rustc-enforced
27271 // exhaustiveness through the joint sweep) rather than a
27272 // runtime `matches!` assertion.
27273 let samples = [
27274 Sexp::Nil,
27275 Sexp::List(vec![]),
27276 Sexp::List(vec![Sexp::symbol("a")]),
27277 Sexp::symbol("foo"),
27278 Sexp::keyword("k"),
27279 Sexp::string("s"),
27280 Sexp::int(7),
27281 Sexp::float(7.5),
27282 Sexp::boolean(true),
27283 Sexp::Quote(Box::new(Sexp::Nil)),
27284 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27285 Sexp::Unquote(Box::new(Sexp::Nil)),
27286 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27287 ];
27288 for s in &samples {
27289 let hits = [
27290 s.as_atom().is_some(),
27291 s.as_quote_form().is_some(),
27292 s.as_structural_kind().is_some(),
27293 ];
27294 let hit_count: usize = hits.iter().filter(|b| **b).count();
27295 assert_eq!(
27296 hit_count, 1,
27297 "value-level carvings must partition Sexp variants — {s:?} matched {hit_count} carvings (as_atom/as_quote_form/as_structural_kind = {hits:?})",
27298 );
27299 }
27300 }
27301
27302 #[test]
27303 fn sexp_as_structural_kind_composes_with_label_via_structural_kind_label() {
27304 // CROSS-PROJECTION COHERENCE: pin that
27305 // `s.as_structural_kind().map(StructuralKind::label)` agrees
27306 // with `s.shape().label()` for every residual-carving Sexp
27307 // (and returns `None` for every non-residual Sexp). Composes
27308 // the new value-level projection with the closed-set
27309 // `StructuralKind::label` projection (which itself composes
27310 // through `sexp_shape().label()`) so the label vocabulary
27311 // stays load-bearing at ONE canonical site
27312 // (`SexpShape::label`) rather than a parallel per-projection
27313 // literal table.
27314 let residual = [
27315 (Sexp::Nil, "nil"),
27316 (Sexp::List(vec![]), "list"),
27317 (Sexp::List(vec![Sexp::symbol("a")]), "list"),
27318 ];
27319 for (sexp, expected_label) in &residual {
27320 let via_carving = sexp.as_structural_kind().map(StructuralKind::label);
27321 assert_eq!(
27322 via_carving,
27323 Some(*expected_label),
27324 "structural-carving-marker label drifted at {sexp:?}"
27325 );
27326 assert_eq!(
27327 via_carving,
27328 Some(sexp.shape().label()),
27329 "as_structural_kind.map(label) must equal shape().label() for residual sample {sexp:?}"
27330 );
27331 }
27332 for non_residual in [
27333 Sexp::symbol("foo"),
27334 Sexp::keyword("k"),
27335 Sexp::string("s"),
27336 Sexp::int(7),
27337 Sexp::float(7.5),
27338 Sexp::boolean(true),
27339 Sexp::Quote(Box::new(Sexp::Nil)),
27340 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27341 Sexp::Unquote(Box::new(Sexp::Nil)),
27342 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27343 ] {
27344 assert_eq!(
27345 non_residual.as_structural_kind().map(StructuralKind::label),
27346 None,
27347 "non-residual Sexp must project to None on as_structural_kind.map(label) — {non_residual:?}"
27348 );
27349 }
27350 }
27351
27352 #[test]
27353 fn sexp_as_atom_kind_projects_each_atom_variant_to_canonical_atom_kind() {
27354 // PER-VARIANT TRUTH-TABLE (atomic axis): pin byte-for-byte per-
27355 // Sexp-atom-arm mapping — Symbol payload → Some(AtomKind::Symbol),
27356 // Keyword payload → Some(AtomKind::Keyword), Str payload →
27357 // Some(AtomKind::Str), Int payload → Some(AtomKind::Int), Float
27358 // payload → Some(AtomKind::Float), Bool payload →
27359 // Some(AtomKind::Bool). Value-level peer of the shape-level
27360 // sweep `as_atom_kind_projects_each_atom_shape_to_canonical_atom_kind_and_rejects_non_atom_shapes`
27361 // in error.rs — each atomic Sexp value's carving-marker
27362 // projection must land on the matching AtomKind arm the shape-
27363 // level projection lands on. A future thirteenth Atom variant
27364 // extends both this sweep + the composition body via the
27365 // as_atom + Atom::kind primitives, with rustc enforcing the
27366 // match arms in lockstep.
27367 assert_eq!(Sexp::symbol("foo").as_atom_kind(), Some(AtomKind::Symbol));
27368 assert_eq!(Sexp::keyword("k").as_atom_kind(), Some(AtomKind::Keyword));
27369 assert_eq!(Sexp::string("s").as_atom_kind(), Some(AtomKind::Str));
27370 assert_eq!(Sexp::int(7).as_atom_kind(), Some(AtomKind::Int));
27371 assert_eq!(Sexp::float(7.5).as_atom_kind(), Some(AtomKind::Float));
27372 assert_eq!(Sexp::boolean(true).as_atom_kind(), Some(AtomKind::Bool));
27373 // Empty-payload edge cases (empty-string vs Symbol vs Keyword)
27374 // — pin the projection ignores payload content entirely (it
27375 // reads only the outer variant discriminant), so a body that
27376 // gates on payload emptiness fails loudly.
27377 assert_eq!(Sexp::symbol("").as_atom_kind(), Some(AtomKind::Symbol));
27378 assert_eq!(Sexp::string("").as_atom_kind(), Some(AtomKind::Str));
27379 }
27380
27381 #[test]
27382 fn sexp_as_atom_kind_rejects_non_atom_outer_shapes() {
27383 // KERNEL: every non-atom outer shape (Nil, List, every quote-
27384 // family wrapper) projects to `None`. Sibling kernel-pin to
27385 // `sexp_as_structural_kind_rejects_non_structural_outer_shapes`
27386 // on the residual axis. Together the two kernel pins bracket
27387 // the atomic-carving membership from BOTH sides of the
27388 // partition — the atomic-arm membership from
27389 // `sexp_as_atom_kind_projects_each_atom_variant_to_canonical_atom_kind`
27390 // and the non-atomic-arm kernel from THIS test.
27391 for non_atom in [
27392 Sexp::Nil,
27393 Sexp::List(vec![]),
27394 Sexp::List(vec![Sexp::symbol("a")]),
27395 Sexp::Quote(Box::new(Sexp::Nil)),
27396 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27397 Sexp::Unquote(Box::new(Sexp::Nil)),
27398 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27399 ] {
27400 assert_eq!(
27401 non_atom.as_atom_kind(),
27402 None,
27403 "non-atom Sexp must project to None on as_atom_kind — {non_atom:?}"
27404 );
27405 }
27406 }
27407
27408 #[test]
27409 fn sexp_as_atom_kind_agrees_with_as_atom_map_kind_for_every_variant() {
27410 // COMPOSITION-LAW CONTRACT (atomic-axis peer of the shape-
27411 // agreement law): `s.as_atom_kind() == s.as_atom().map(Atom::kind)`
27412 // for every reachable Sexp outer shape. Pre-lift the atomic
27413 // carving marker at the value level was reachable via this
27414 // two-step composition through the Atom algebra; post-lift the
27415 // new projection MUST agree bit-for-bit — the substrate's
27416 // (Sexp value, AtomKind marker) pairing binds at TWO
27417 // compositions (this Atom-axis composition AND the shape-axis
27418 // composition pinned by the sibling test below) that must stay
27419 // in lockstep. Sweeps every outer shape (atom + residual +
27420 // quote-family) so a drift on ANY arm surfaces immediately.
27421 let samples = [
27422 Sexp::Nil,
27423 Sexp::List(vec![]),
27424 Sexp::List(vec![Sexp::symbol("a")]),
27425 Sexp::symbol("foo"),
27426 Sexp::keyword("k"),
27427 Sexp::string("s"),
27428 Sexp::int(7),
27429 Sexp::float(7.5),
27430 Sexp::boolean(true),
27431 Sexp::Quote(Box::new(Sexp::Nil)),
27432 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27433 Sexp::Unquote(Box::new(Sexp::Nil)),
27434 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27435 ];
27436 for s in &samples {
27437 assert_eq!(
27438 s.as_atom_kind(),
27439 s.as_atom().map(Atom::kind),
27440 "Sexp::as_atom_kind and Sexp::as_atom().map(Atom::kind) must agree at {s:?}",
27441 );
27442 }
27443 }
27444
27445 #[test]
27446 fn sexp_as_atom_kind_agrees_with_shape_as_atom_kind_for_every_variant() {
27447 // COMPOSITION-LAW CONTRACT (shape-axis peer): `s.as_atom_kind()
27448 // == s.shape().as_atom_kind()` for every reachable Sexp outer
27449 // shape. Sibling to
27450 // `sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant`
27451 // on the atomic axis. Pre-lift the atomic carving marker at
27452 // the value level was reachable via this two-step composition
27453 // through the shape algebra; post-lift the new projection MUST
27454 // agree bit-for-bit — the substrate's (Sexp value, AtomKind
27455 // marker) pairing binds at THREE typed methods (Sexp::as_atom_kind,
27456 // Sexp::as_atom + Atom::kind composition, Sexp::shape +
27457 // SexpShape::as_atom_kind composition) that must ALL stay in
27458 // lockstep.
27459 let samples = [
27460 Sexp::Nil,
27461 Sexp::List(vec![]),
27462 Sexp::List(vec![Sexp::symbol("a")]),
27463 Sexp::symbol("foo"),
27464 Sexp::keyword("k"),
27465 Sexp::string("s"),
27466 Sexp::int(7),
27467 Sexp::float(7.5),
27468 Sexp::boolean(true),
27469 Sexp::Quote(Box::new(Sexp::Nil)),
27470 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27471 Sexp::Unquote(Box::new(Sexp::Nil)),
27472 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27473 ];
27474 for s in &samples {
27475 assert_eq!(
27476 s.as_atom_kind(),
27477 s.shape().as_atom_kind(),
27478 "Sexp::as_atom_kind and Sexp::shape().as_atom_kind must agree at {s:?}",
27479 );
27480 }
27481 }
27482
27483 #[test]
27484 fn sexp_as_atom_kind_partitions_outer_shapes_jointly_with_as_quote_form_and_as_structural_kind()
27485 {
27486 // PARTITION-TOTAL CONTRACT (value-level, marker-only axis):
27487 // pin that for every reachable Sexp outer shape, EXACTLY ONE
27488 // of `as_atom_kind`, `as_quote_form`, `as_structural_kind`
27489 // returns `Some(_)`. Post-lift ALL THREE carving-marker
27490 // projections at the value level form a partition of the
27491 // `Sexp` variant algebra using ONLY the marker-only siblings —
27492 // symmetric with the shape-level partition-total invariant
27493 // pinned by
27494 // `sexp_shape_partition_is_total_across_atom_quote_structural_carvings`
27495 // (in error.rs). The pre-existing value-level partition pin
27496 // `sexp_as_structural_kind_partitions_outer_shapes_jointly_with_as_atom_and_as_quote_form`
27497 // uses `as_atom().is_some()` on the atomic axis (the
27498 // structural-lift projection); THIS pin uses `as_atom_kind()
27499 // .is_some()` (the marker-only projection). Both partition
27500 // invariants must hold — they pin the atomic axis's TWO
27501 // value-level projections (structural + marker) as jointly
27502 // partition-consistent with the residual and quote-family
27503 // siblings. A regression that drifts any carving's
27504 // marker-only membership (an `as_atom_kind` arm that accepts
27505 // a non-atom, an `as_quote_form` arm that misses a quote-
27506 // family wrapper, an `as_structural_kind` arm that swaps its
27507 // Nil/List membership) surfaces here immediately.
27508 let samples = [
27509 Sexp::Nil,
27510 Sexp::List(vec![]),
27511 Sexp::List(vec![Sexp::symbol("a")]),
27512 Sexp::symbol("foo"),
27513 Sexp::keyword("k"),
27514 Sexp::string("s"),
27515 Sexp::int(7),
27516 Sexp::float(7.5),
27517 Sexp::boolean(true),
27518 Sexp::Quote(Box::new(Sexp::Nil)),
27519 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27520 Sexp::Unquote(Box::new(Sexp::Nil)),
27521 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27522 ];
27523 for s in &samples {
27524 let hits = [
27525 s.as_atom_kind().is_some(),
27526 s.as_quote_form().is_some(),
27527 s.as_structural_kind().is_some(),
27528 ];
27529 let hit_count: usize = hits.iter().filter(|b| **b).count();
27530 assert_eq!(
27531 hit_count, 1,
27532 "value-level marker-only carvings must partition Sexp variants — {s:?} matched {hit_count} carvings (as_atom_kind/as_quote_form/as_structural_kind = {hits:?})",
27533 );
27534 }
27535 }
27536
27537 #[test]
27538 fn sexp_as_atom_kind_composes_with_label_via_atom_kind_label() {
27539 // CROSS-PROJECTION COHERENCE: pin that
27540 // `s.as_atom_kind().map(AtomKind::label)` agrees with
27541 // `s.shape().label()` for every atomic Sexp (and returns
27542 // `None` for every non-atomic Sexp). Sibling to
27543 // `sexp_as_structural_kind_composes_with_label_via_structural_kind_label`
27544 // on the atomic axis. Composes the new value-level marker
27545 // projection with the closed-set `AtomKind::label` projection
27546 // (which itself composes through `sexp_shape().label()`) so
27547 // the label vocabulary stays load-bearing at ONE canonical
27548 // site (`SexpShape::label`) rather than a parallel per-
27549 // projection literal table.
27550 let atomic = [
27551 (Sexp::symbol("foo"), "symbol"),
27552 (Sexp::keyword("k"), "keyword"),
27553 (Sexp::string("s"), "string"),
27554 (Sexp::int(7), "int"),
27555 (Sexp::float(7.5), "float"),
27556 (Sexp::boolean(true), "bool"),
27557 ];
27558 for (sexp, expected_label) in &atomic {
27559 let via_carving = sexp.as_atom_kind().map(AtomKind::label);
27560 assert_eq!(
27561 via_carving,
27562 Some(*expected_label),
27563 "atomic-carving-marker label drifted at {sexp:?}"
27564 );
27565 assert_eq!(
27566 via_carving,
27567 Some(sexp.shape().label()),
27568 "as_atom_kind.map(label) must equal shape().label() for atomic sample {sexp:?}"
27569 );
27570 }
27571 for non_atomic in [
27572 Sexp::Nil,
27573 Sexp::List(vec![]),
27574 Sexp::List(vec![Sexp::symbol("a")]),
27575 Sexp::Quote(Box::new(Sexp::Nil)),
27576 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27577 Sexp::Unquote(Box::new(Sexp::Nil)),
27578 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27579 ] {
27580 assert_eq!(
27581 non_atomic.as_atom_kind().map(AtomKind::label),
27582 None,
27583 "non-atomic Sexp must project to None on as_atom_kind.map(label) — {non_atomic:?}"
27584 );
27585 }
27586 }
27587
27588 #[test]
27589 fn sexp_as_unquote_form_projects_each_variant_to_canonical_unquote_form() {
27590 // PER-VARIANT TRUTH-TABLE (unquote-subset axis): pin byte-for-
27591 // byte per-Sexp-substitution-arm mapping — `Sexp::Unquote(inner)`
27592 // → `Some(UnquoteForm::Unquote)`, `Sexp::UnquoteSplice(inner)`
27593 // → `Some(UnquoteForm::Splice)`. Value-level peer of the shape-
27594 // level sweep
27595 // `as_unquote_form_projects_each_unquote_shape_to_canonical_unquote_form_and_rejects_non_unquote_shapes`
27596 // in error.rs — each substitution-wrapper Sexp value's carving-
27597 // marker projection must land on the matching UnquoteForm arm
27598 // the shape-level projection lands on. A future third UnquoteForm
27599 // variant (e.g. `,~` reverse-unquote) extends both this sweep +
27600 // the composition body via the as_unquote + QuoteForm::as_unquote_form
27601 // primitives, with rustc enforcing the match arms in lockstep.
27602 assert_eq!(
27603 Sexp::Unquote(Box::new(Sexp::symbol("x"))).as_unquote_form(),
27604 Some(UnquoteForm::Unquote)
27605 );
27606 assert_eq!(
27607 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))).as_unquote_form(),
27608 Some(UnquoteForm::Splice)
27609 );
27610 // Inner-payload invariance edge cases — pin the projection
27611 // ignores inner payload content entirely (it reads only the
27612 // outer wrapper variant discriminant), so a body that gates on
27613 // inner payload shape fails loudly.
27614 assert_eq!(
27615 Sexp::Unquote(Box::new(Sexp::Nil)).as_unquote_form(),
27616 Some(UnquoteForm::Unquote)
27617 );
27618 assert_eq!(
27619 Sexp::UnquoteSplice(Box::new(Sexp::List(vec![]))).as_unquote_form(),
27620 Some(UnquoteForm::Splice)
27621 );
27622 assert_eq!(
27623 Sexp::Unquote(Box::new(Sexp::List(vec![
27624 Sexp::symbol("nested"),
27625 Sexp::int(42),
27626 ])))
27627 .as_unquote_form(),
27628 Some(UnquoteForm::Unquote)
27629 );
27630 }
27631
27632 #[test]
27633 fn sexp_as_unquote_form_rejects_non_unquote_subset_outer_shapes() {
27634 // KERNEL: every non-unquote-subset outer shape (Nil, every Atom
27635 // variant, List, AND the two non-substitution quote-family
27636 // wrappers `Sexp::Quote` and `Sexp::Quasiquote`) projects to
27637 // `None`. Sibling kernel-pin to
27638 // `sexp_as_atom_kind_rejects_non_atom_outer_shapes` and
27639 // `sexp_as_structural_kind_rejects_non_structural_outer_shapes`
27640 // on the substitution axis. The two non-substitution quote-
27641 // family wrappers ARE quote-family (`as_quote_form` accepts
27642 // them) but NOT substitution-subset (`as_unquote_form` must
27643 // reject them) — pin the 2-of-4 subset gate operates at the
27644 // value level exactly as the shape-level
27645 // `QuoteForm::as_unquote_form` gate operates on the closed-set
27646 // marker enum.
27647 for non_unquote in [
27648 Sexp::Nil,
27649 Sexp::symbol("foo"),
27650 Sexp::keyword("k"),
27651 Sexp::string("s"),
27652 Sexp::int(7),
27653 Sexp::float(7.5),
27654 Sexp::boolean(true),
27655 Sexp::List(vec![]),
27656 Sexp::List(vec![Sexp::symbol("a")]),
27657 Sexp::Quote(Box::new(Sexp::Nil)),
27658 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27659 ] {
27660 assert_eq!(
27661 non_unquote.as_unquote_form(),
27662 None,
27663 "non-substitution-subset Sexp must project to None on as_unquote_form — {non_unquote:?}"
27664 );
27665 }
27666 }
27667
27668 #[test]
27669 fn sexp_as_unquote_form_agrees_with_as_unquote_map_marker_for_every_variant() {
27670 // COMPOSITION-LAW CONTRACT (parent-projection peer): pin
27671 // `s.as_unquote_form() == s.as_unquote().map(|(uf, _)| uf)` for
27672 // every reachable Sexp outer shape. Pre-lift the substitution
27673 // carving marker at the value level was reachable via this
27674 // two-step composition through the parent [`Sexp::as_unquote`]
27675 // projection (discarding the wrapped inner); post-lift the new
27676 // marker-only projection MUST agree bit-for-bit — the
27677 // substrate's (Sexp value, UnquoteForm marker) pairing binds at
27678 // FOUR compositions (this parent-projection composition AND the
27679 // shape-axis composition AND the quote-family + subset-gate
27680 // composition, all pinned by the sibling tests below) that must
27681 // stay in lockstep. Sweeps every outer shape (atom + residual +
27682 // quote-family) so a drift on ANY arm surfaces immediately.
27683 let samples = [
27684 Sexp::Nil,
27685 Sexp::List(vec![]),
27686 Sexp::List(vec![Sexp::symbol("a")]),
27687 Sexp::symbol("foo"),
27688 Sexp::keyword("k"),
27689 Sexp::string("s"),
27690 Sexp::int(7),
27691 Sexp::float(7.5),
27692 Sexp::boolean(true),
27693 Sexp::Quote(Box::new(Sexp::Nil)),
27694 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27695 Sexp::Unquote(Box::new(Sexp::Nil)),
27696 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27697 ];
27698 for s in &samples {
27699 assert_eq!(
27700 s.as_unquote_form(),
27701 s.as_unquote().map(|(uf, _)| uf),
27702 "Sexp::as_unquote_form and Sexp::as_unquote().map(|(uf, _)| uf) must agree at {s:?}",
27703 );
27704 }
27705 }
27706
27707 #[test]
27708 fn sexp_as_unquote_form_agrees_with_shape_as_unquote_form_for_every_variant() {
27709 // COMPOSITION-LAW CONTRACT (shape-axis peer): `s.as_unquote_form()
27710 // == s.shape().as_unquote_form()` for every reachable Sexp outer
27711 // shape. Sibling to
27712 // `sexp_as_atom_kind_agrees_with_shape_as_atom_kind_for_every_variant`
27713 // and
27714 // `sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant`
27715 // on the substitution axis. Pre-lift the substitution carving
27716 // marker at the value level was reachable via this two-step
27717 // composition through the shape algebra; post-lift the new
27718 // projection MUST agree bit-for-bit — the substrate's (Sexp
27719 // value, UnquoteForm marker) pairing binds at FOUR typed methods
27720 // (Sexp::as_unquote_form, Sexp::as_unquote + `|(uf, _)| uf`
27721 // composition, Sexp::shape + SexpShape::as_unquote_form
27722 // composition, Sexp::as_quote_form +
27723 // QuoteForm::as_unquote_form composition) that must ALL stay
27724 // in lockstep.
27725 let samples = [
27726 Sexp::Nil,
27727 Sexp::List(vec![]),
27728 Sexp::List(vec![Sexp::symbol("a")]),
27729 Sexp::symbol("foo"),
27730 Sexp::keyword("k"),
27731 Sexp::string("s"),
27732 Sexp::int(7),
27733 Sexp::float(7.5),
27734 Sexp::boolean(true),
27735 Sexp::Quote(Box::new(Sexp::Nil)),
27736 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27737 Sexp::Unquote(Box::new(Sexp::Nil)),
27738 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27739 ];
27740 for s in &samples {
27741 assert_eq!(
27742 s.as_unquote_form(),
27743 s.shape().as_unquote_form(),
27744 "Sexp::as_unquote_form and Sexp::shape().as_unquote_form must agree at {s:?}",
27745 );
27746 }
27747 }
27748
27749 #[test]
27750 fn sexp_as_unquote_form_agrees_with_as_quote_form_and_quote_form_as_unquote_form_for_every_variant(
27751 ) {
27752 // COMPOSITION-LAW CONTRACT (parent-family + subset-gate peer):
27753 // pin `s.as_unquote_form() ==
27754 // s.as_quote_form().and_then(|(qf, _)| qf.as_unquote_form())`
27755 // for every reachable Sexp outer shape. Value-level peer of the
27756 // shape-level route
27757 // `as_unquote_form_routes_through_as_quote_form_and_quote_form_as_unquote_form_via_composition`
27758 // in error.rs — where that test pins the shape-level
27759 // `SexpShape::as_unquote_form` routes through the shape-level
27760 // `SexpShape::as_quote_form` + `QuoteForm::as_unquote_form`
27761 // subset gate, THIS test pins the value-level
27762 // `Sexp::as_unquote_form` routes through the value-level
27763 // `Sexp::as_quote_form` + the SAME subset gate. Pre-lift the
27764 // substitution carving marker at the value level was reachable
27765 // via this three-step composition through the parent quote-
27766 // family projection [`Sexp::as_quote_form`] composed with the
27767 // 2-of-4 subset gate [`QuoteForm::as_unquote_form`]; post-lift
27768 // the new marker-only projection MUST agree bit-for-bit — the
27769 // subset-gate composition (which the pre-existing
27770 // [`Sexp::as_unquote`] projection ALSO routes through, per its
27771 // body `let (qf, inner) = self.as_quote_form()?;
27772 // qf.as_unquote_form().map(|uf| (uf, inner))`) must land the
27773 // same marker as the new value-level projection.
27774 let samples = [
27775 Sexp::Nil,
27776 Sexp::List(vec![]),
27777 Sexp::List(vec![Sexp::symbol("a")]),
27778 Sexp::symbol("foo"),
27779 Sexp::keyword("k"),
27780 Sexp::string("s"),
27781 Sexp::int(7),
27782 Sexp::float(7.5),
27783 Sexp::boolean(true),
27784 Sexp::Quote(Box::new(Sexp::Nil)),
27785 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27786 Sexp::Unquote(Box::new(Sexp::Nil)),
27787 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
27788 ];
27789 for s in &samples {
27790 assert_eq!(
27791 s.as_unquote_form(),
27792 s.as_quote_form().and_then(|(qf, _)| qf.as_unquote_form()),
27793 "Sexp::as_unquote_form and Sexp::as_quote_form().and_then(|(qf, _)| qf.as_unquote_form()) must agree at {s:?}",
27794 );
27795 }
27796 }
27797
27798 #[test]
27799 fn sexp_as_unquote_form_composes_with_marker_via_unquote_form_marker() {
27800 // CROSS-PROJECTION COHERENCE: pin that
27801 // `s.as_unquote_form().map(UnquoteForm::marker)` agrees with
27802 // `s.shape().label()` for every substitution-subset Sexp (and
27803 // returns `None` for every non-substitution-subset Sexp).
27804 // Sibling to `sexp_as_atom_kind_composes_with_label_via_atom_kind_label`
27805 // on the substitution axis. Composes the new value-level
27806 // marker projection with the closed-set `UnquoteForm::marker`
27807 // projection (which itself composes through
27808 // `to_quote_form().prefix()` — see `UnquoteForm::marker`'s
27809 // docstring for the composition route) so the marker vocabulary
27810 // (`","` / `",@"`) stays load-bearing at ONE canonical site
27811 // (`QuoteForm::prefix`'s Unquote/UnquoteSplice arms) rather
27812 // than a parallel per-projection literal table.
27813 //
27814 // Note: `UnquoteForm::marker` returns the READER prefix (`,` or
27815 // `,@`) which is ALSO the canonical `SexpShape::label` for the
27816 // Unquote / UnquoteSplice arms — the shape-label vocabulary
27817 // was pinned to the reader-prefix vocabulary in the
27818 // `SexpShape::label` truth-table (Unquote → "unquote",
27819 // UnquoteSplice → "unquote-splice"). This test uses
27820 // `UnquoteForm::marker` = reader prefix directly (`,` /
27821 // `,@`), NOT the shape label — the two are distinct
27822 // vocabularies, both derived from the closed-set carving
27823 // marker, both stable across the lift.
27824 let substitution = [
27825 (
27826 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
27827 UnquoteForm::Unquote,
27828 ),
27829 (
27830 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
27831 UnquoteForm::Splice,
27832 ),
27833 ];
27834 for (sexp, expected_uf) in &substitution {
27835 let via_carving = sexp.as_unquote_form().map(UnquoteForm::marker);
27836 assert_eq!(
27837 via_carving,
27838 Some(expected_uf.marker()),
27839 "substitution-carving-marker string drifted at {sexp:?}"
27840 );
27841 }
27842 for non_substitution in [
27843 Sexp::Nil,
27844 Sexp::symbol("foo"),
27845 Sexp::keyword("k"),
27846 Sexp::string("s"),
27847 Sexp::int(7),
27848 Sexp::float(7.5),
27849 Sexp::boolean(true),
27850 Sexp::List(vec![]),
27851 Sexp::List(vec![Sexp::symbol("a")]),
27852 Sexp::Quote(Box::new(Sexp::Nil)),
27853 Sexp::Quasiquote(Box::new(Sexp::Nil)),
27854 ] {
27855 assert_eq!(
27856 non_substitution.as_unquote_form().map(UnquoteForm::marker),
27857 None,
27858 "non-substitution-subset Sexp must project to None on as_unquote_form.map(marker) — {non_substitution:?}"
27859 );
27860 }
27861 }
27862
27863 #[test]
27864 fn sexp_as_unquote_form_narrows_as_quote_form_to_substitution_subset() {
27865 // SUBSET-GATE CONTRACT (value-level): pin that at every
27866 // reachable Sexp outer shape, `as_unquote_form().is_some()`
27867 // implies `as_quote_form().is_some()` (subset containment) AND
27868 // `as_quote_form().is_some() && !as_unquote_form().is_some()`
27869 // holds exactly for the two non-substitution quote-family
27870 // wrappers (`Sexp::Quote` and `Sexp::Quasiquote`) — the 2-of-4
27871 // subset gate at the VALUE level, symmetric with the shape-
27872 // level subset gate pinned by the sibling
27873 // `QuoteForm::as_unquote_form` truth-table in error.rs. Pins the
27874 // (substitution-subset ⊂ quote-family) inclusion as an invariant
27875 // on the value algebra so a regression that widens
27876 // `as_unquote_form` beyond its 2-of-4 subset (e.g. an emitter
27877 // that starts accepting `Sexp::Quote` as substitution) surfaces
27878 // immediately as a subset-inclusion drift.
27879 let samples = [
27880 (Sexp::Nil, false, false),
27881 (Sexp::List(vec![]), false, false),
27882 (Sexp::List(vec![Sexp::symbol("a")]), false, false),
27883 (Sexp::symbol("foo"), false, false),
27884 (Sexp::keyword("k"), false, false),
27885 (Sexp::string("s"), false, false),
27886 (Sexp::int(7), false, false),
27887 (Sexp::float(7.5), false, false),
27888 (Sexp::boolean(true), false, false),
27889 // Quote-family, NOT substitution-subset
27890 (Sexp::Quote(Box::new(Sexp::Nil)), true, false),
27891 (Sexp::Quasiquote(Box::new(Sexp::Nil)), true, false),
27892 // Quote-family AND substitution-subset
27893 (Sexp::Unquote(Box::new(Sexp::Nil)), true, true),
27894 (Sexp::UnquoteSplice(Box::new(Sexp::Nil)), true, true),
27895 ];
27896 for (s, quote_expected, unquote_expected) in &samples {
27897 let quote_hit = s.as_quote_form().is_some();
27898 let unquote_hit = s.as_unquote_form().is_some();
27899 assert_eq!(
27900 quote_hit, *quote_expected,
27901 "as_quote_form membership drifted at {s:?}"
27902 );
27903 assert_eq!(
27904 unquote_hit, *unquote_expected,
27905 "as_unquote_form membership drifted at {s:?}"
27906 );
27907 // Subset containment: substitution ⊂ quote-family.
27908 assert!(
27909 !unquote_hit || quote_hit,
27910 "subset containment violated at {s:?}: as_unquote_form Some but as_quote_form None",
27911 );
27912 }
27913 }
27914
27915 #[test]
27916 fn sexp_as_quote_form_marker_projects_each_variant_to_canonical_quote_form() {
27917 // PER-VARIANT TRUTH-TABLE (quote-family axis): pin byte-for-byte
27918 // per-Sexp-quote-family-arm mapping — `Sexp::Quote(inner)`
27919 // → `Some(QuoteForm::Quote)`, `Sexp::Quasiquote(inner)`
27920 // → `Some(QuoteForm::Quasiquote)`, `Sexp::Unquote(inner)`
27921 // → `Some(QuoteForm::Unquote)`, `Sexp::UnquoteSplice(inner)`
27922 // → `Some(QuoteForm::UnquoteSplice)`. Value-level marker-only
27923 // peer of the pre-existing tuple projection
27924 // `Sexp::as_quote_form` — each quote-family-wrapper Sexp value's
27925 // carving-marker projection must land on the matching QuoteForm
27926 // arm the parent projection's tuple carries. A future fifth
27927 // QuoteForm variant (e.g. `,~` reverse-unquote) extends both
27928 // this sweep + the composition body via the as_quote_form
27929 // primitive, with rustc enforcing the match arms in lockstep.
27930 assert_eq!(
27931 Sexp::Quote(Box::new(Sexp::symbol("x"))).as_quote_form_marker(),
27932 Some(QuoteForm::Quote)
27933 );
27934 assert_eq!(
27935 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))).as_quote_form_marker(),
27936 Some(QuoteForm::Quasiquote)
27937 );
27938 assert_eq!(
27939 Sexp::Unquote(Box::new(Sexp::symbol("x"))).as_quote_form_marker(),
27940 Some(QuoteForm::Unquote)
27941 );
27942 assert_eq!(
27943 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))).as_quote_form_marker(),
27944 Some(QuoteForm::UnquoteSplice)
27945 );
27946 // Inner-payload invariance edge cases — pin the projection
27947 // ignores inner payload content entirely (it reads only the
27948 // outer wrapper variant discriminant), so a body that gates on
27949 // inner payload shape fails loudly.
27950 assert_eq!(
27951 Sexp::Quote(Box::new(Sexp::Nil)).as_quote_form_marker(),
27952 Some(QuoteForm::Quote)
27953 );
27954 assert_eq!(
27955 Sexp::Quasiquote(Box::new(Sexp::List(vec![]))).as_quote_form_marker(),
27956 Some(QuoteForm::Quasiquote)
27957 );
27958 assert_eq!(
27959 Sexp::Unquote(Box::new(Sexp::List(vec![
27960 Sexp::symbol("nested"),
27961 Sexp::int(42),
27962 ])))
27963 .as_quote_form_marker(),
27964 Some(QuoteForm::Unquote)
27965 );
27966 assert_eq!(
27967 Sexp::UnquoteSplice(Box::new(Sexp::Quote(Box::new(Sexp::symbol("y")))))
27968 .as_quote_form_marker(),
27969 Some(QuoteForm::UnquoteSplice)
27970 );
27971 }
27972
27973 #[test]
27974 fn sexp_as_quote_form_marker_rejects_non_quote_family_outer_shapes() {
27975 // KERNEL: every non-quote-family outer shape (Nil, every Atom
27976 // variant, List — empty and non-empty) projects to `None`.
27977 // Sibling kernel-pin to
27978 // `sexp_as_atom_kind_rejects_non_atom_outer_shapes`,
27979 // `sexp_as_structural_kind_rejects_non_structural_outer_shapes`,
27980 // and `sexp_as_unquote_form_rejects_non_unquote_subset_outer_shapes`
27981 // on the quote-family axis. A body that widens the projection to
27982 // any non-quote-family arm (e.g. `Sexp::List` starts returning
27983 // `Some(QuoteForm::Quote)`) surfaces as a `None` expectation
27984 // failure at the specific offending variant.
27985 for non_quote in [
27986 Sexp::Nil,
27987 Sexp::symbol("foo"),
27988 Sexp::keyword("k"),
27989 Sexp::string("s"),
27990 Sexp::int(7),
27991 Sexp::float(7.5),
27992 Sexp::boolean(true),
27993 Sexp::List(vec![]),
27994 Sexp::List(vec![Sexp::symbol("a")]),
27995 Sexp::List(vec![Sexp::symbol("a"), Sexp::int(1)]),
27996 ] {
27997 assert_eq!(
27998 non_quote.as_quote_form_marker(),
27999 None,
28000 "non-quote-family Sexp must project to None on as_quote_form_marker — {non_quote:?}"
28001 );
28002 }
28003 }
28004
28005 #[test]
28006 fn sexp_as_quote_form_marker_agrees_with_as_quote_form_map_marker_for_every_variant() {
28007 // COMPOSITION-LAW CONTRACT (parent-projection peer): pin
28008 // `s.as_quote_form_marker() == s.as_quote_form().map(|(qf, _)| qf)`
28009 // for every reachable Sexp outer shape. Pre-lift the quote-
28010 // family carving marker at the value level was reachable via
28011 // this two-step composition through the parent
28012 // [`Sexp::as_quote_form`] projection (discarding the wrapped
28013 // inner via `.map(|(qf, _)| qf)`); post-lift the new marker-
28014 // only projection MUST agree bit-for-bit — the substrate's
28015 // (Sexp value, QuoteForm marker) pairing binds at THREE
28016 // compositions (this parent-projection composition AND the
28017 // shape-axis composition, both pinned in this module, AND the
28018 // direct match in the new method's body) that must stay in
28019 // lockstep. Sweeps every outer shape (atom + residual +
28020 // quote-family) so a drift on ANY arm surfaces immediately.
28021 let samples = [
28022 Sexp::Nil,
28023 Sexp::List(vec![]),
28024 Sexp::List(vec![Sexp::symbol("a")]),
28025 Sexp::symbol("foo"),
28026 Sexp::keyword("k"),
28027 Sexp::string("s"),
28028 Sexp::int(7),
28029 Sexp::float(7.5),
28030 Sexp::boolean(true),
28031 Sexp::Quote(Box::new(Sexp::Nil)),
28032 Sexp::Quasiquote(Box::new(Sexp::Nil)),
28033 Sexp::Unquote(Box::new(Sexp::Nil)),
28034 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
28035 ];
28036 for s in &samples {
28037 assert_eq!(
28038 s.as_quote_form_marker(),
28039 s.as_quote_form().map(|(qf, _)| qf),
28040 "Sexp::as_quote_form_marker and Sexp::as_quote_form().map(|(qf, _)| qf) must agree at {s:?}",
28041 );
28042 }
28043 }
28044
28045 #[test]
28046 fn sexp_as_quote_form_marker_agrees_with_shape_as_quote_form_for_every_variant() {
28047 // COMPOSITION-LAW CONTRACT (shape-axis peer):
28048 // `s.as_quote_form_marker() == s.shape().as_quote_form()` for
28049 // every reachable Sexp outer shape. Sibling to
28050 // `sexp_as_atom_kind_agrees_with_shape_as_atom_kind_for_every_variant`,
28051 // `sexp_as_structural_kind_agrees_with_shape_as_structural_kind_for_every_variant`,
28052 // and `sexp_as_unquote_form_agrees_with_shape_as_unquote_form_for_every_variant`
28053 // on the quote-family axis. Pre-lift the quote-family carving
28054 // marker at the value level was reachable via this two-step
28055 // composition through the shape algebra (`shape().as_quote_form()`,
28056 // walking the full 12-variant [`SexpShape`](crate::error::SexpShape)
28057 // closed set to arrive at the 4-of-12 carving marker); post-
28058 // lift the new projection MUST agree bit-for-bit — the
28059 // substrate's (Sexp value, QuoteForm marker) pairing now binds
28060 // at ONE typed method on the value algebra, with both
28061 // compositions (this shape-axis peer and the parent-projection
28062 // peer pinned above) staying in lockstep.
28063 use crate::error::SexpShape;
28064 let samples = [
28065 Sexp::Nil,
28066 Sexp::List(vec![]),
28067 Sexp::List(vec![Sexp::symbol("a")]),
28068 Sexp::symbol("foo"),
28069 Sexp::keyword("k"),
28070 Sexp::string("s"),
28071 Sexp::int(7),
28072 Sexp::float(7.5),
28073 Sexp::boolean(true),
28074 Sexp::Quote(Box::new(Sexp::Nil)),
28075 Sexp::Quasiquote(Box::new(Sexp::Nil)),
28076 Sexp::Unquote(Box::new(Sexp::Nil)),
28077 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
28078 ];
28079 for s in &samples {
28080 let via_value: Option<QuoteForm> = s.as_quote_form_marker();
28081 let via_shape: Option<QuoteForm> = SexpShape::as_quote_form(s.shape());
28082 assert_eq!(
28083 via_value, via_shape,
28084 "Sexp::as_quote_form_marker and Sexp::shape().as_quote_form must agree at {s:?}",
28085 );
28086 }
28087 }
28088
28089 #[test]
28090 fn sexp_as_quote_form_marker_composes_with_prefix_via_quote_form_prefix() {
28091 // CROSS-PROJECTION COHERENCE: pin that
28092 // `s.as_quote_form_marker().map(QuoteForm::prefix)` agrees with
28093 // the reader-prefix vocabulary carried on [`QuoteForm::prefix`]
28094 // for every quote-family Sexp (and returns `None` for every
28095 // non-quote-family Sexp). Sibling to
28096 // `sexp_as_unquote_form_composes_with_marker_via_unquote_form_marker`
28097 // on the quote-family axis. Composes the new value-level marker
28098 // projection with the closed-set [`QuoteForm::prefix`]
28099 // projection so the reader/writer prefix vocabulary (`'` / `` ` ``
28100 // / `,` / `,@`) stays load-bearing at ONE canonical site
28101 // ([`QuoteForm::prefix`]'s four arms) rather than a parallel
28102 // per-projection literal table on the value algebra.
28103 let quote_family = [
28104 (Sexp::Quote(Box::new(Sexp::symbol("x"))), QuoteForm::Quote),
28105 (
28106 Sexp::Quasiquote(Box::new(Sexp::symbol("x"))),
28107 QuoteForm::Quasiquote,
28108 ),
28109 (
28110 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28111 QuoteForm::Unquote,
28112 ),
28113 (
28114 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28115 QuoteForm::UnquoteSplice,
28116 ),
28117 ];
28118 for (sexp, expected_qf) in "e_family {
28119 let via_carving = sexp.as_quote_form_marker().map(QuoteForm::prefix);
28120 assert_eq!(
28121 via_carving,
28122 Some(expected_qf.prefix()),
28123 "quote-family carving-marker prefix drifted at {sexp:?}"
28124 );
28125 }
28126 for non_quote in [
28127 Sexp::Nil,
28128 Sexp::symbol("foo"),
28129 Sexp::keyword("k"),
28130 Sexp::string("s"),
28131 Sexp::int(7),
28132 Sexp::float(7.5),
28133 Sexp::boolean(true),
28134 Sexp::List(vec![]),
28135 Sexp::List(vec![Sexp::symbol("a")]),
28136 ] {
28137 assert_eq!(
28138 non_quote.as_quote_form_marker().map(QuoteForm::prefix),
28139 None,
28140 "non-quote-family Sexp must project to None on as_quote_form_marker.map(prefix) — {non_quote:?}"
28141 );
28142 }
28143 }
28144
28145 #[test]
28146 fn sexp_as_quote_form_marker_extends_as_unquote_form_to_full_quote_family() {
28147 // SUPERSET-GATE CONTRACT (value-level): pin that at every
28148 // reachable Sexp outer shape,
28149 // `as_unquote_form().is_some()` implies
28150 // `as_quote_form_marker().is_some()` (the 2-of-12 substitution
28151 // subset is a proper subset of the 4-of-12 quote family) AND
28152 // `as_quote_form_marker().is_some() && !as_unquote_form().is_some()`
28153 // holds exactly for the two non-substitution quote-family
28154 // wrappers (`Sexp::Quote` and `Sexp::Quasiquote`) — the value-
28155 // level image of the 2-of-4 subset gate
28156 // [`QuoteForm::as_unquote_form`], mirroring
28157 // `sexp_as_unquote_form_narrows_as_quote_form_to_substitution_subset`
28158 // from the substitution-axis side. Pins the (substitution-
28159 // subset ⊂ quote-family) inclusion as an invariant on the
28160 // value algebra where the SUPERSET side is now a NAMED typed
28161 // method — so a regression that widens either projection
28162 // beyond its cell (e.g. `as_quote_form_marker` starts accepting
28163 // `Sexp::List`, or `as_unquote_form` starts accepting
28164 // `Sexp::Quote`) surfaces immediately as a subset-inclusion
28165 // drift. Also pin that
28166 // `as_unquote_form() == as_quote_form_marker().and_then(
28167 // QuoteForm::as_unquote_form)` — the value-level projection
28168 // composes with the 2-of-4 subset gate at the marker algebra
28169 // level, so the substrate's (Sexp value, UnquoteForm marker)
28170 // pairing derives from the (Sexp value, QuoteForm marker)
28171 // pairing at ONE composition rather than two parallel value-
28172 // level projections.
28173 let samples = [
28174 (Sexp::Nil, false, false),
28175 (Sexp::List(vec![]), false, false),
28176 (Sexp::List(vec![Sexp::symbol("a")]), false, false),
28177 (Sexp::symbol("foo"), false, false),
28178 (Sexp::keyword("k"), false, false),
28179 (Sexp::string("s"), false, false),
28180 (Sexp::int(7), false, false),
28181 (Sexp::float(7.5), false, false),
28182 (Sexp::boolean(true), false, false),
28183 // Quote-family, NOT substitution-subset
28184 (Sexp::Quote(Box::new(Sexp::Nil)), true, false),
28185 (Sexp::Quasiquote(Box::new(Sexp::Nil)), true, false),
28186 // Quote-family AND substitution-subset
28187 (Sexp::Unquote(Box::new(Sexp::Nil)), true, true),
28188 (Sexp::UnquoteSplice(Box::new(Sexp::Nil)), true, true),
28189 ];
28190 for (s, quote_expected, unquote_expected) in &samples {
28191 let quote_hit = s.as_quote_form_marker().is_some();
28192 let unquote_hit = s.as_unquote_form().is_some();
28193 assert_eq!(
28194 quote_hit, *quote_expected,
28195 "as_quote_form_marker membership drifted at {s:?}"
28196 );
28197 assert_eq!(
28198 unquote_hit, *unquote_expected,
28199 "as_unquote_form membership drifted at {s:?}"
28200 );
28201 // Superset containment: substitution ⊂ quote-family.
28202 assert!(
28203 !unquote_hit || quote_hit,
28204 "subset containment violated at {s:?}: as_unquote_form Some but as_quote_form_marker None",
28205 );
28206 // Composition through the 2-of-4 subset gate:
28207 // `s.as_unquote_form() == s.as_quote_form_marker().and_then(QuoteForm::as_unquote_form)`.
28208 assert_eq!(
28209 s.as_unquote_form(),
28210 s.as_quote_form_marker()
28211 .and_then(QuoteForm::as_unquote_form),
28212 "as_unquote_form and as_quote_form_marker + QuoteForm::as_unquote_form composition disagree at {s:?}",
28213 );
28214 }
28215 }
28216
28217 #[test]
28218 fn sexp_shape_method_label_composes_with_sexp_type_name_for_every_outer_shape() {
28219 // COMPOSITION-LAW CONTRACT: `s.shape().label() ==
28220 // crate::domain::sexp_type_name(&s)` for every reachable Sexp
28221 // outer shape. Post-lift `sexp_type_name` routes through
28222 // `s.shape().label()` directly (no longer through the free-
28223 // function `sexp_shape`). Pin the composition law so a future
28224 // refactor that drifts either projection (e.g. a label typo
28225 // in `SexpShape::label`, a change in `sexp_type_name`'s
28226 // delegation) surfaces here immediately.
28227 let samples = [
28228 Sexp::Nil,
28229 Sexp::symbol("foo"),
28230 Sexp::keyword("k"),
28231 Sexp::string("s"),
28232 Sexp::int(7),
28233 Sexp::float(7.5),
28234 Sexp::boolean(true),
28235 Sexp::List(vec![]),
28236 Sexp::Quote(Box::new(Sexp::Nil)),
28237 Sexp::Quasiquote(Box::new(Sexp::Nil)),
28238 Sexp::Unquote(Box::new(Sexp::Nil)),
28239 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
28240 ];
28241 for s in &samples {
28242 assert_eq!(
28243 s.shape().label(),
28244 crate::domain::sexp_type_name(s),
28245 "Sexp::shape().label() must equal domain::sexp_type_name for {s:?}",
28246 );
28247 }
28248 }
28249
28250 #[test]
28251 fn sexp_type_name_method_projects_each_outer_arm_to_canonical_label() {
28252 // PER-ARM CONTRACT: pin that the inherent `Sexp::type_name()`
28253 // method projects each reachable outer Sexp shape to its
28254 // canonical `&'static str` label. Pre-lift the projection
28255 // lived as a free function `domain::sexp_type_name`; post-
28256 // lift the canonical site is the inherent method on the
28257 // `Sexp` algebra and the free function delegates. A
28258 // regression that drifts a per-arm label (e.g. a typo in
28259 // `SexpShape::label`, a stale arm in `Sexp::shape`'s match,
28260 // a change in the body away from `self.shape().label()`)
28261 // surfaces here immediately. Sweeps every outer shape and
28262 // every atomic payload kind so all 8 `SexpShape` variants
28263 // are covered.
28264 assert_eq!(Sexp::Nil.type_name(), "nil");
28265 assert_eq!(Sexp::symbol("foo").type_name(), "symbol");
28266 assert_eq!(Sexp::keyword("k").type_name(), "keyword");
28267 assert_eq!(Sexp::string("s").type_name(), "string");
28268 assert_eq!(Sexp::int(7).type_name(), "int");
28269 assert_eq!(Sexp::float(7.5).type_name(), "float");
28270 assert_eq!(Sexp::boolean(true).type_name(), "bool");
28271 assert_eq!(Sexp::List(vec![]).type_name(), "list");
28272 assert_eq!(Sexp::Quote(Box::new(Sexp::Nil)).type_name(), "quote");
28273 assert_eq!(
28274 Sexp::Quasiquote(Box::new(Sexp::Nil)).type_name(),
28275 "quasiquote",
28276 );
28277 assert_eq!(Sexp::Unquote(Box::new(Sexp::Nil)).type_name(), "unquote");
28278 assert_eq!(
28279 Sexp::UnquoteSplice(Box::new(Sexp::Nil)).type_name(),
28280 "unquote-splice",
28281 );
28282 }
28283
28284 #[test]
28285 fn sexp_type_name_method_composes_through_shape_label_for_every_outer_shape() {
28286 // COMPOSITION-LAW CONTRACT: `s.type_name() == s.shape().label()`
28287 // for every reachable Sexp outer shape — the method body is
28288 // structurally derived through `Self::shape` + `SexpShape::label`
28289 // rather than re-matching `Sexp` arms directly. Pin the
28290 // composition law so a future refactor that re-inlines the
28291 // match (and gains its own drift surface) surfaces here
28292 // immediately. Sibling-shape pin to the existing
28293 // `sexp_shape_method_label_composes_with_sexp_type_name_for_every_outer_shape`
28294 // pin (which pins the inverse direction: the free function
28295 // routes through the inherent method).
28296 let samples = [
28297 Sexp::Nil,
28298 Sexp::symbol("foo"),
28299 Sexp::keyword("k"),
28300 Sexp::string("s"),
28301 Sexp::int(7),
28302 Sexp::float(7.5),
28303 Sexp::boolean(true),
28304 Sexp::List(vec![]),
28305 Sexp::Quote(Box::new(Sexp::Nil)),
28306 Sexp::Quasiquote(Box::new(Sexp::Nil)),
28307 Sexp::Unquote(Box::new(Sexp::Nil)),
28308 Sexp::UnquoteSplice(Box::new(Sexp::Nil)),
28309 ];
28310 for s in &samples {
28311 assert_eq!(
28312 s.type_name(),
28313 s.shape().label(),
28314 "Sexp::type_name() must compose through Sexp::shape().label() for {s:?}",
28315 );
28316 }
28317 }
28318
28319 #[test]
28320 fn sexp_type_name_method_agrees_with_domain_sexp_type_name_for_every_outer_shape() {
28321 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28322 // `Sexp::type_name()` method agrees with the free-function
28323 // delegate `crate::domain::sexp_type_name` for every
28324 // reachable outer shape. Pre-lift the dispatcher lived as a
28325 // free function in `domain.rs`; post-lift the canonical site
28326 // is the inherent method on the `Sexp` algebra and the free
28327 // function is a one-line delegate. Pin that the delegation
28328 // stays byte-for-byte equivalent across every outer arm so
28329 // a regression where the free function drifts from the
28330 // inherent method (or vice versa) surfaces here immediately.
28331 // Mirrors `sexp_witness_method_agrees_with_domain_sexp_witness_for_every_outer_shape`
28332 // for the canonical-label-only peer projection.
28333 let samples = [
28334 Sexp::Nil,
28335 Sexp::symbol("foo"),
28336 Sexp::keyword("k"),
28337 Sexp::string("s"),
28338 Sexp::int(7),
28339 Sexp::int(-1),
28340 Sexp::float(7.5),
28341 Sexp::boolean(true),
28342 Sexp::boolean(false),
28343 Sexp::List(vec![]),
28344 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
28345 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
28346 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
28347 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28348 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28349 ];
28350 for s in &samples {
28351 assert_eq!(
28352 s.type_name(),
28353 crate::domain::sexp_type_name(s),
28354 "Sexp::type_name() must equal domain::sexp_type_name for {s:?}",
28355 );
28356 }
28357 }
28358
28359 #[test]
28360 fn sexp_witness_method_pairs_shape_with_display_for_every_outer_shape() {
28361 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28362 // `Sexp::witness()` method projects each reachable outer Sexp
28363 // shape to a `SexpWitness` whose `shape` field equals
28364 // `s.shape()` AND whose `display` field equals
28365 // `s.to_string()` for every variant + payload combination.
28366 // Pre-lift the projection lived as a free function in
28367 // `domain.rs`; post-lift the canonical site is the inherent
28368 // method on the `Sexp` algebra. A regression where the method
28369 // drifts EITHER half of the joint identity (a stale `shape`
28370 // projection that re-inlines without composing through
28371 // `Sexp::shape`, a `display` projection that diverges from
28372 // `Sexp::Display`) surfaces here immediately. Sweeps every
28373 // outer shape, every atomic payload kind, and every
28374 // quote-family wrapper.
28375 let samples = [
28376 Sexp::Nil,
28377 Sexp::symbol("foo"),
28378 Sexp::keyword("k"),
28379 Sexp::string("s"),
28380 Sexp::int(7),
28381 Sexp::int(-1),
28382 Sexp::float(7.5),
28383 Sexp::boolean(true),
28384 Sexp::boolean(false),
28385 Sexp::List(vec![]),
28386 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
28387 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
28388 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
28389 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28390 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28391 ];
28392 for s in &samples {
28393 let w = s.witness();
28394 assert_eq!(
28395 w.shape,
28396 s.shape(),
28397 "Sexp::witness().shape drifted from Sexp::shape() for {s:?}",
28398 );
28399 assert_eq!(
28400 w.display,
28401 s.to_string(),
28402 "Sexp::witness().display drifted from Sexp::Display for {s:?}",
28403 );
28404 }
28405 }
28406
28407 #[test]
28408 fn sexp_witness_method_agrees_with_domain_sexp_witness_for_every_outer_shape() {
28409 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28410 // `Sexp::witness()` method agrees with the free-function
28411 // delegate `crate::domain::sexp_witness` for every reachable
28412 // outer shape. Pre-lift the dispatcher lived as a free
28413 // function in `domain.rs`; post-lift the canonical site is
28414 // the inherent method on the `Sexp` algebra and the free
28415 // function is a one-line delegate. Pin that the delegation
28416 // stays byte-for-byte equivalent across every outer arm so
28417 // a regression where the free function drifts from the
28418 // inherent method (or vice versa) surfaces here immediately.
28419 // Mirrors `sexp_shape_method_agrees_with_domain_sexp_shape_for_every_outer_shape`
28420 // for the joint-identity peer projection. Catches a future
28421 // "consolidation" that removes the free function without
28422 // updating the method, or vice versa.
28423 let samples = [
28424 Sexp::Nil,
28425 Sexp::symbol("foo"),
28426 Sexp::keyword("k"),
28427 Sexp::string("s"),
28428 Sexp::int(7),
28429 Sexp::int(-1),
28430 Sexp::float(7.5),
28431 Sexp::float(0.0),
28432 Sexp::boolean(true),
28433 Sexp::boolean(false),
28434 Sexp::List(vec![]),
28435 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
28436 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
28437 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
28438 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28439 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28440 ];
28441 for s in &samples {
28442 let via_method = s.witness();
28443 let via_delegate = crate::domain::sexp_witness(s);
28444 assert_eq!(
28445 via_method.shape, via_delegate.shape,
28446 "Sexp::witness().shape drifted from domain::sexp_witness().shape at {s:?}",
28447 );
28448 assert_eq!(
28449 via_method.display, via_delegate.display,
28450 "Sexp::witness().display drifted from domain::sexp_witness().display at {s:?}",
28451 );
28452 }
28453 }
28454
28455 #[test]
28456 fn sexp_witness_method_routes_through_shape_and_display_projections() {
28457 // PATH-UNIFORMITY CONTRACT: the lifted `Sexp::witness()` body
28458 // composes the two algebra-level projections `Sexp::shape()`
28459 // (structural identity) + `Sexp::Display` (renderable
28460 // identity) into ONE `SexpWitness::new(shape, display)`
28461 // value. Pin that the composition agrees bit-for-bit with
28462 // the direct `SexpWitness::new(s.shape(), s.to_string())`
28463 // construction across a sweep covering every outer shape.
28464 // A regression in EITHER projection direction (a
28465 // `Sexp::witness` arm that bypasses `Sexp::shape` and
28466 // re-inlines the dispatch, a `Sexp::witness` arm that
28467 // bypasses `Sexp::Display` and re-formats the literal) is
28468 // structurally impossible — the typed joint primitive
28469 // composes through the typed primitive halves once.
28470 // Sibling shape to `sexp_shape_method_routes_atom_arm_through_atom_kind_sexp_shape_projection`
28471 // for the joint-identity axis.
28472 let samples = [
28473 Sexp::Nil,
28474 Sexp::symbol("x"),
28475 Sexp::keyword("kw"),
28476 Sexp::string("text"),
28477 Sexp::int(0),
28478 Sexp::float(2.5),
28479 Sexp::boolean(true),
28480 Sexp::List(vec![Sexp::symbol("f"), Sexp::int(1)]),
28481 Sexp::Quote(Box::new(Sexp::symbol("q"))),
28482 Sexp::Quasiquote(Box::new(Sexp::symbol("qq"))),
28483 Sexp::Unquote(Box::new(Sexp::symbol("uq"))),
28484 Sexp::UnquoteSplice(Box::new(Sexp::symbol("uqs"))),
28485 ];
28486 for s in &samples {
28487 let via_method = s.witness();
28488 let via_composed = crate::error::SexpWitness::new(s.shape(), s.to_string());
28489 assert_eq!(
28490 via_method.shape, via_composed.shape,
28491 "Sexp::witness drifted shape from SexpWitness::new(s.shape(), s.to_string()) at {s:?}",
28492 );
28493 assert_eq!(
28494 via_method.display, via_composed.display,
28495 "Sexp::witness drifted display from SexpWitness::new(s.shape(), s.to_string()) at {s:?}",
28496 );
28497 }
28498 }
28499
28500 #[test]
28501 fn sexp_witness_distinguishes_int_atom_from_symbol_with_identical_display() {
28502 // STRUCTURAL-IDENTITY CONTRACT: `Sexp::int(5)` and
28503 // `Sexp::symbol("5")` Display-render identically (`"5"`) but
28504 // are STRUCTURALLY DISTINCT — one is `SexpShape::Int`, the
28505 // other is `SexpShape::Symbol`. Pin that `Sexp::witness()`
28506 // carries the structural identity through the `shape` slot
28507 // so the rejection diagnostic distinguishes the two even
28508 // when the rendered literal collides. Mirrors the
28509 // free-function-delegate sibling test
28510 // `sexp_witness_distinguishes_int_atom_from_symbol_with_same_display`
28511 // in `domain.rs::tests` — that test pins the delegate; this
28512 // one pins the inherent method on the algebra. Both stay
28513 // load-bearing across the lifted boundary.
28514 let w_int = Sexp::int(5).witness();
28515 let w_sym = Sexp::symbol("5").witness();
28516 assert_eq!(
28517 w_int.display, w_sym.display,
28518 "display collision precondition"
28519 );
28520 assert_ne!(
28521 w_int.shape, w_sym.shape,
28522 "Sexp::witness must distinguish Int from Symbol via shape even when display collides",
28523 );
28524 assert_eq!(w_int.shape, crate::error::SexpShape::Int);
28525 assert_eq!(w_sym.shape, crate::error::SexpShape::Symbol);
28526 }
28527
28528 #[test]
28529 fn sexp_as_x_family_routes_through_atom_as_x_for_every_atomic_variant() {
28530 // LIFTED-BOUNDARY CONTRACT: pin that the six `Sexp::as_X`
28531 // consumer-side projections equal the two-step composition
28532 // `s.as_atom().and_then(Atom::as_X)` for every atomic payload
28533 // variant. Pre-lift the six methods opened the same `Self::Atom
28534 // (Atom::X(s)) => Some(s)` inline arm; post-lift they delegate
28535 // through the typed projection family on the closed-set `Atom`
28536 // algebra. A regression that drifts the outer arm (e.g. re-
28537 // inlines one variant's match without updating the typed
28538 // projection) surfaces as an inequality here. Sweeps every
28539 // atomic variant + every consumer projection, AND pins the
28540 // `Sexp::as_float` widening specialization (`Atom::Int(n)` →
28541 // `Some(n as f64)`) lives at the consumer layer.
28542 let cases: &[Atom] = &[
28543 Atom::Symbol("name".into()),
28544 Atom::Keyword("kw".into()),
28545 Atom::Str("body".into()),
28546 Atom::Int(42),
28547 Atom::Int(-7),
28548 Atom::Float(1.5),
28549 Atom::Float(1.0),
28550 Atom::Bool(true),
28551 Atom::Bool(false),
28552 ];
28553 for atom in cases {
28554 let sexp = Sexp::Atom(atom.clone());
28555
28556 assert_eq!(
28557 sexp.as_symbol(),
28558 sexp.as_atom().and_then(Atom::as_symbol),
28559 "Sexp::as_symbol drifted from as_atom().and_then(Atom::as_symbol) for {atom:?}",
28560 );
28561 assert_eq!(
28562 sexp.as_keyword(),
28563 sexp.as_atom().and_then(Atom::as_keyword),
28564 "Sexp::as_keyword drifted from as_atom().and_then(Atom::as_keyword) for {atom:?}",
28565 );
28566 assert_eq!(
28567 sexp.as_string(),
28568 sexp.as_atom().and_then(Atom::as_string),
28569 "Sexp::as_string drifted from as_atom().and_then(Atom::as_string) for {atom:?}",
28570 );
28571 assert_eq!(
28572 sexp.as_int(),
28573 sexp.as_atom().and_then(Atom::as_int),
28574 "Sexp::as_int drifted from as_atom().and_then(Atom::as_int) for {atom:?}",
28575 );
28576 assert_eq!(
28577 sexp.as_bool(),
28578 sexp.as_atom().and_then(Atom::as_bool),
28579 "Sexp::as_bool drifted from as_atom().and_then(Atom::as_bool) for {atom:?}",
28580 );
28581
28582 // `Sexp::as_float` specializes through the widening composition
28583 // `s.as_atom().and_then(|a| a.as_float().or_else(|| a.as_int()
28584 // .map(|n| n as f64)))` so the algebra-level `Atom::as_float`
28585 // stays strict and the typed-identity distinction `Int(1)` vs
28586 // `Float(1.0)` is preserved at the algebra layer.
28587 let expected_float = sexp
28588 .as_atom()
28589 .and_then(|a| a.as_float().or_else(|| a.as_int().map(|n| n as f64)));
28590 assert_eq!(
28591 sexp.as_float(),
28592 expected_float,
28593 "Sexp::as_float drifted from widening composition for {atom:?}",
28594 );
28595 }
28596 }
28597
28598 #[test]
28599 fn sexp_as_float_widens_int_to_float_at_consumer_layer_only() {
28600 // CONSUMER-LAYER WIDENING CONTRACT: pin that the `Sexp::as_float`
28601 // consumer DOES widen `Atom::Int(n)` to `Some(n as f64)` (the
28602 // load-bearing widening at the numeric-kwarg boundary the
28603 // `extract_float` extractor depends on) AND that the algebra-
28604 // level `Atom::as_float` does NOT (the strict typed-identity
28605 // discipline pinned at `atom_as_float_returns_payload_iff_float_variant_strict_no_int_widening`).
28606 // The widening lives at the CONSUMER layer ONLY; a regression
28607 // that drifts the widening into the algebra layer (e.g. re-
28608 // adds an `Atom::Int(n) => Some(n as f64)` arm at
28609 // `Atom::as_float`) would silently coerce `Int(1)` slots into
28610 // the `Float` track at every `Atom` consumer that bypasses
28611 // `Sexp`, breaking the typed-identity discipline at the
28612 // canonical-form rendering surfaces (Display, JSON,
28613 // iac-forge).
28614 let int_sexp = Sexp::int(7);
28615 assert_eq!(
28616 int_sexp.as_float(),
28617 Some(7.0),
28618 "Sexp::as_float must widen Atom::Int to f64 at the consumer layer",
28619 );
28620 assert_eq!(
28621 Atom::Int(7).as_float(),
28622 None,
28623 "Atom::as_float must stay strict at the algebra layer",
28624 );
28625
28626 // The widening sweeps the int domain — pin a few canonical
28627 // values so a regression that loses the `as f64` cast (e.g. an
28628 // accidental `usize` round-trip) surfaces directly.
28629 for n in [-42i64, -1, 0, 1, 42] {
28630 assert_eq!(
28631 Sexp::int(n).as_float(),
28632 Some(n as f64),
28633 "Sexp::as_float widening drifted for Int({n})",
28634 );
28635 }
28636 }
28637
28638 #[test]
28639 fn sexp_to_json_method_projects_each_outer_arm_to_canonical_json() {
28640 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28641 // `Sexp::to_json()` method projects each reachable outer Sexp
28642 // shape to a `serde_json::Value` byte-identical to the
28643 // pre-lift inline rule at `crate::domain::sexp_to_json`'s
28644 // outer match — Nil → Null, Atom → `Atom::to_json` (composed
28645 // through the typed-algebra projection), List(kwargs) →
28646 // Object keyed by kebab→camel, List(other) → Array, and
28647 // each quote-family wrapper → recurse on inner (the wrapper
28648 // is structurally erased into JSON). A regression that
28649 // drifts ANY outer arm (e.g. emits Nil as `"nil"` instead of
28650 // Null, swaps List(kwargs) for Array unconditionally, drops
28651 // a quote-family arm's recursion) surfaces here. Pre-lift
28652 // the dispatcher lived as a free function in `domain.rs`;
28653 // post-lift the canonical site is the inherent method on
28654 // the `Sexp` algebra (same posture as the prior
28655 // `Sexp::shape` (121bb60) and `Sexp::witness` (a427e3b)
28656 // lifts).
28657 assert_eq!(
28658 Sexp::Nil.to_json().expect("nil to_json"),
28659 serde_json::Value::Null,
28660 );
28661 assert_eq!(
28662 Sexp::symbol("foo").to_json().expect("symbol to_json"),
28663 serde_json::Value::String("foo".into()),
28664 );
28665 assert_eq!(
28666 Sexp::keyword("k").to_json().expect("keyword to_json"),
28667 serde_json::Value::String(":k".into()),
28668 );
28669 assert_eq!(
28670 Sexp::string("body").to_json().expect("string to_json"),
28671 serde_json::Value::String("body".into()),
28672 );
28673 assert_eq!(
28674 Sexp::int(7).to_json().expect("int to_json"),
28675 serde_json::json!(7),
28676 );
28677 assert_eq!(
28678 Sexp::float(1.5).to_json().expect("float to_json"),
28679 serde_json::json!(1.5),
28680 );
28681 assert_eq!(
28682 Sexp::boolean(true).to_json().expect("true to_json"),
28683 serde_json::Value::Bool(true),
28684 );
28685
28686 // List(kwargs) → Object with kebab→camel keys.
28687 let kwargs = Sexp::List(vec![
28688 Sexp::keyword("point-type"),
28689 Sexp::symbol("Gate"),
28690 Sexp::keyword("must-reach"),
28691 Sexp::boolean(true),
28692 ]);
28693 assert_eq!(
28694 kwargs.to_json().expect("kwargs list to_json"),
28695 serde_json::json!({"pointType": "Gate", "mustReach": true}),
28696 );
28697
28698 // List(non-kwargs) → Array.
28699 let arr = Sexp::List(vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)]);
28700 assert_eq!(
28701 arr.to_json().expect("non-kwargs list to_json"),
28702 serde_json::json!([1, 2, 3]),
28703 );
28704
28705 // Empty list → Array (kwargs guard rejects empty lists).
28706 let empty = Sexp::List(vec![]);
28707 assert_eq!(
28708 empty.to_json().expect("empty list to_json"),
28709 serde_json::json!([]),
28710 );
28711
28712 // Quote-family wrappers strip and recurse.
28713 let payload = Sexp::List(vec![Sexp::keyword("k"), Sexp::int(42)]);
28714 let expected = serde_json::json!({"k": 42});
28715 for wrapped in [
28716 Sexp::Quote(Box::new(payload.clone())),
28717 Sexp::Quasiquote(Box::new(payload.clone())),
28718 Sexp::Unquote(Box::new(payload.clone())),
28719 Sexp::UnquoteSplice(Box::new(payload.clone())),
28720 ] {
28721 assert_eq!(
28722 wrapped.to_json().expect("quote-family to_json"),
28723 expected,
28724 "quote-family wrapper {wrapped:?} drifted from inner-recursion shape",
28725 );
28726 }
28727 }
28728
28729 #[test]
28730 fn sexp_to_json_method_agrees_with_domain_sexp_to_json_for_every_outer_shape() {
28731 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28732 // `Sexp::to_json()` method agrees with the free-function
28733 // delegate `crate::domain::sexp_to_json` for every reachable
28734 // outer shape. Pre-lift the dispatcher lived as a free
28735 // function in `domain.rs`; post-lift the canonical site is
28736 // the inherent method and the free function is a one-line
28737 // delegate. Pin that the delegation stays byte-for-byte
28738 // equivalent across every outer arm so a regression where
28739 // the free function drifts from the inherent method (or
28740 // vice versa) surfaces here immediately. Mirrors
28741 // `sexp_shape_method_agrees_with_domain_sexp_shape_for_every_outer_shape`
28742 // and
28743 // `sexp_witness_method_agrees_with_domain_sexp_witness_for_every_outer_shape`
28744 // for the JSON canonical-form projection peer.
28745 let samples = [
28746 Sexp::Nil,
28747 Sexp::symbol("foo"),
28748 Sexp::keyword("k"),
28749 Sexp::string("s"),
28750 Sexp::int(7),
28751 Sexp::int(-1),
28752 Sexp::float(7.5),
28753 Sexp::float(0.0),
28754 Sexp::boolean(true),
28755 Sexp::boolean(false),
28756 Sexp::List(vec![]),
28757 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)]),
28758 Sexp::List(vec![
28759 Sexp::keyword("point-type"),
28760 Sexp::symbol("Gate"),
28761 Sexp::keyword("must-reach"),
28762 Sexp::boolean(true),
28763 ]),
28764 Sexp::Quote(Box::new(Sexp::symbol("payload"))),
28765 Sexp::Quasiquote(Box::new(Sexp::List(vec![Sexp::symbol("foo")]))),
28766 Sexp::Unquote(Box::new(Sexp::symbol("x"))),
28767 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
28768 ];
28769 for s in &samples {
28770 let via_method = s.to_json().expect("method projection must succeed");
28771 let via_delegate =
28772 crate::domain::sexp_to_json(s).expect("delegate projection must succeed");
28773 assert_eq!(
28774 via_method, via_delegate,
28775 "Sexp::to_json drifted from domain::sexp_to_json at {s:?}",
28776 );
28777 }
28778 }
28779
28780 #[test]
28781 fn sexp_to_json_method_routes_atom_arm_through_atom_to_json() {
28782 // PATH-UNIFORMITY CONTRACT: the lifted `Sexp::to_json()`
28783 // body composes through the typed-algebra primitive
28784 // [`Atom::to_json`] at the Atom arm — `Sexp::Atom(a).to_json()
28785 // == Ok(a.to_json())` for every atomic payload variant. A
28786 // regression in EITHER direction (a `Sexp::to_json` arm
28787 // that bypasses `Atom::to_json` and re-inlines a per-variant
28788 // mapping, or an `Atom::to_json` projection that diverges
28789 // from the rendering the outer arm depends on) is
28790 // structurally impossible — the typed JSON primitive composes
28791 // through the typed primitive halves once. Sibling-shape pin
28792 // to `sexp_to_json_atom_arms_route_through_atom_to_json` in
28793 // `domain.rs` (the free-function-delegate peer that pinned
28794 // the same identity at the pre-lift site).
28795 let cases: &[Atom] = &[
28796 Atom::Symbol("name".into()),
28797 Atom::Keyword("kw".into()),
28798 Atom::Str("body".into()),
28799 Atom::Int(7),
28800 Atom::Int(-3),
28801 Atom::Float(2.5),
28802 Atom::Float(1.0),
28803 Atom::Bool(true),
28804 Atom::Bool(false),
28805 ];
28806 for atom in cases {
28807 let via_method = Sexp::Atom(atom.clone())
28808 .to_json()
28809 .expect("atom must serialize through Sexp::to_json");
28810 let via_atom = atom.to_json();
28811 assert_eq!(
28812 via_method, via_atom,
28813 "Sexp::to_json Atom arm drifted from Atom::to_json for {atom:?}",
28814 );
28815 }
28816 }
28817
28818 #[test]
28819 fn sexp_to_json_method_routes_quote_family_arms_through_inner_recursion() {
28820 // PATH-UNIFORMITY CONTRACT: the four quote-family arms each
28821 // strip the wrapper and recurse on the projected `inner`
28822 // (via `Self::expect_quote_form`), NOT on the outer `self`.
28823 // Pin that this binding semantic is observable across all
28824 // four wrappers: `wrap_qf(inner).to_json() == inner.to_json()`
28825 // for every `QuoteForm` variant. A regression that lifted
28826 // the recursion onto `self` (the outer wrapper) instead of
28827 // the projected inner would infinite-loop or surface as a
28828 // structural mismatch here. Sibling shape to
28829 // `sexp_to_json_routes_quote_family_arms_through_as_quote_form_typed_marker`
28830 // in `domain.rs::tests` (the free-function-delegate peer)
28831 // — both pin the same invariant at the lifted boundary.
28832 let inner = Sexp::List(vec![Sexp::keyword("k"), Sexp::int(42)]);
28833 let expected = inner.to_json().expect("inner serializes");
28834 for wrap in [
28835 Sexp::Quote(Box::new(inner.clone())),
28836 Sexp::Quasiquote(Box::new(inner.clone())),
28837 Sexp::Unquote(Box::new(inner.clone())),
28838 Sexp::UnquoteSplice(Box::new(inner.clone())),
28839 ] {
28840 let via_method = wrap
28841 .to_json()
28842 .expect("quote-family wrapper must serialize via Sexp::to_json");
28843 assert_eq!(
28844 via_method, expected,
28845 "Sexp::to_json drifted from inner-recursion shape at {wrap:?}",
28846 );
28847 }
28848 }
28849
28850 #[test]
28851 fn sexp_to_json_method_rejects_duplicate_kwargs_at_lifted_boundary() {
28852 // TYPED-ENTRY CONTRACT: the duplicate-keyword rejection at
28853 // the kwargs-list arm fires at the inherent method directly,
28854 // not at the delegate — the canonical typed-entry gate lives
28855 // on the algebra. Pin that two `:k` entries in the same
28856 // kwargs list collapse to `LispError::DuplicateKwarg { key }`
28857 // with `key == "notify-ref"` (the kebab spelling, before
28858 // kebab→camel conversion — the diagnostic surface matches
28859 // the spelling the operator typed). The error type
28860 // discriminator is checked via debug-format substring so a
28861 // future LispError variant rename doesn't silently break
28862 // this pin. Mirrors `sexp_to_json_nested_duplicate_emits_structural_variant`
28863 // in `domain.rs::tests` (the free-function delegate peer at
28864 // the pre-lift site) at the lifted boundary.
28865 let dup = Sexp::List(vec![
28866 Sexp::keyword("notify-ref"),
28867 Sexp::string("a"),
28868 Sexp::keyword("notify-ref"),
28869 Sexp::string("b"),
28870 ]);
28871 let err = dup.to_json().expect_err("duplicate kwarg must reject");
28872 let rendered = format!("{err:?}");
28873 assert!(
28874 rendered.contains("DuplicateKwarg"),
28875 "expected DuplicateKwarg variant, got {rendered}",
28876 );
28877 assert!(
28878 rendered.contains("notify-ref"),
28879 "expected diagnostic to name the kebab-spelled duplicate key, got {rendered}",
28880 );
28881 }
28882
28883 // ── Sexp::from_json: the inverse JSON-projection on the algebra ─────
28884 //
28885 // `Sexp::from_json` lifts the `domain::json_to_sexp` free-function
28886 // dispatcher onto the inherent-method canonical site on the [`Sexp`]
28887 // algebra — sibling-lift posture to the prior `sexp_to_json` →
28888 // `Sexp::to_json` (875ee3b), `sexp_witness` → `Sexp::witness`
28889 // (a427e3b), and `sexp_shape` → `Sexp::shape` (121bb60). The tests
28890 // below pin the per-arm contract on the new canonical site directly;
28891 // the free function delegates so the existing path-uniformity tests
28892 // at `domain::json_to_sexp_*` continue to pass post-lift unchanged.
28893
28894 #[test]
28895 fn sexp_from_json_projects_each_outer_arm_to_canonical_sexp() {
28896 // LIFTED-BOUNDARY CONTRACT: pin that the inherent
28897 // `Sexp::from_json` associated function projects each reachable
28898 // outer `serde_json::Value` shape to a `Sexp` byte-identical to
28899 // the pre-lift inline rule at `crate::domain::json_to_sexp`'s
28900 // outer match — Null → Nil, Bool → boolean, Number(i64) → int,
28901 // Number(f64-only) → float, String → string, Array → List(map),
28902 // Object → List of alternating `:k v` pairs in iteration order
28903 // via `camel_to_kebab` on each key. A regression that drifts ANY
28904 // outer arm (e.g. emits Null as Sexp::string(""), swaps Array
28905 // for a kwargs-shaped List, drops the camel→kebab projection on
28906 // Object keys) surfaces here. Pre-lift the dispatcher lived as a
28907 // free function in `domain.rs`; post-lift the canonical site is
28908 // the inherent associated function on the `Sexp` algebra.
28909 assert_eq!(Sexp::from_json(&serde_json::Value::Null), Sexp::Nil);
28910 assert_eq!(
28911 Sexp::from_json(&serde_json::Value::Bool(true)),
28912 Sexp::boolean(true),
28913 );
28914 assert_eq!(
28915 Sexp::from_json(&serde_json::Value::Bool(false)),
28916 Sexp::boolean(false),
28917 );
28918 assert_eq!(Sexp::from_json(&serde_json::json!(42)), Sexp::int(42));
28919 assert_eq!(Sexp::from_json(&serde_json::json!(-1)), Sexp::int(-1));
28920 assert_eq!(Sexp::from_json(&serde_json::json!(0)), Sexp::int(0));
28921 // Float that does NOT fit i64 falls through to the float arm.
28922 assert_eq!(Sexp::from_json(&serde_json::json!(1.5)), Sexp::float(1.5));
28923 assert_eq!(
28924 Sexp::from_json(&serde_json::Value::String("body".into())),
28925 Sexp::string("body"),
28926 );
28927
28928 // Array → List with each element projected recursively.
28929 let arr = serde_json::json!([1, "x", true, null]);
28930 assert_eq!(
28931 Sexp::from_json(&arr),
28932 Sexp::List(vec![
28933 Sexp::int(1),
28934 Sexp::string("x"),
28935 Sexp::boolean(true),
28936 Sexp::Nil,
28937 ]),
28938 );
28939
28940 // Object → List of alternating `:k v` pairs, JSON key projected
28941 // through camel→kebab so the kwarg authoring shape is recovered.
28942 // The iteration order of the JSON object is implementation-
28943 // defined here (no `preserve_order` feature on `serde_json`), so
28944 // pin the SET of (kebab-key, value) pairs rather than the
28945 // sequence — order-uniformity vs. the delegate is pinned in the
28946 // path-uniformity test below.
28947 let obj = serde_json::json!({"pointType": "Gate", "mustReach": true});
28948 let result = Sexp::from_json(&obj);
28949 let items = match &result {
28950 Sexp::List(items) => items.clone(),
28951 other => panic!("expected List, got {other:?}"),
28952 };
28953 assert_eq!(items.len(), 4);
28954 let mut pairs: Vec<(String, Sexp)> = items
28955 .chunks_exact(2)
28956 .map(|c| (c[0].as_keyword().expect("kw").to_string(), c[1].clone()))
28957 .collect();
28958 pairs.sort_by(|a, b| a.0.cmp(&b.0));
28959 assert_eq!(
28960 pairs,
28961 vec![
28962 ("must-reach".to_string(), Sexp::boolean(true)),
28963 ("point-type".to_string(), Sexp::string("Gate")),
28964 ],
28965 );
28966 }
28967
28968 #[test]
28969 fn sexp_from_json_agrees_with_domain_json_to_sexp_for_every_outer_shape() {
28970 // PATH-UNIFORMITY GUARD: pin that the free-function delegate
28971 // `crate::domain::json_to_sexp(v) == Sexp::from_json(v)` for
28972 // every reachable `serde_json::Value` outer shape. Post-lift the
28973 // free function delegates to the inherent associated function;
28974 // this test pins the delegation byte-for-byte so a future
28975 // regression that drifts the delegate (e.g. inlines a stale
28976 // pre-lift body, swaps the iteration order at one site) fires
28977 // here, parallel to `sexp_to_json_method_agrees_with_domain_
28978 // sexp_to_json_for_every_outer_shape`'s posture for the forward
28979 // direction.
28980 let shapes = [
28981 serde_json::Value::Null,
28982 serde_json::Value::Bool(true),
28983 serde_json::Value::Bool(false),
28984 serde_json::json!(7),
28985 serde_json::json!(-3),
28986 serde_json::json!(2.5),
28987 serde_json::Value::String("body".into()),
28988 serde_json::json!([1, 2, 3]),
28989 serde_json::json!({"camelCase": "v", "another-key": 5}),
28990 serde_json::json!({"nested": {"inner": [1, 2]}}),
28991 serde_json::json!([]),
28992 serde_json::json!({}),
28993 ];
28994 for v in &shapes {
28995 assert_eq!(
28996 Sexp::from_json(v),
28997 crate::domain::json_to_sexp(v),
28998 "delegate drifted from inherent associated function for {v}",
28999 );
29000 }
29001 }
29002
29003 #[test]
29004 fn sexp_from_json_object_keys_route_through_camel_to_kebab() {
29005 // KEY-PROJECTION CONTRACT: pin that JSON object keys land in
29006 // the resulting `Sexp::List` as `Sexp::keyword(camel_to_kebab(k))`
29007 // — the inverse of `Sexp::to_json`'s kebab→camel projection.
29008 // A regression that drops the projection (writes the JSON key
29009 // verbatim, breaking the kwarg round-trip), substitutes a
29010 // different camel→kebab implementation at this site, or routes
29011 // through `kebab_to_camel` (the wrong direction) surfaces here.
29012 let obj = serde_json::json!({
29013 "pointType": 1,
29014 "mustReach": 2,
29015 "already-kebab": 3,
29016 "withABC": 4,
29017 });
29018 let result = Sexp::from_json(&obj);
29019 let items = match &result {
29020 Sexp::List(items) => items,
29021 other => panic!("expected List, got {other:?}"),
29022 };
29023 // Even-position elements are keywords; odd-position elements are
29024 // values. Pin the keyword spellings against the camel→kebab
29025 // projection (camel boundaries become `-`; consecutive uppercase
29026 // each get a leading `-` per the implementation in
29027 // `domain::camel_to_kebab`).
29028 let kws: Vec<&str> = items
29029 .iter()
29030 .step_by(2)
29031 .map(|s| s.as_keyword().expect("even position must be keyword"))
29032 .collect();
29033 // Match the order JSON preserve_order gives us — sortable for
29034 // stability; the contract is just that each key landed through
29035 // camel→kebab, not the insertion order itself.
29036 let mut sorted = kws.clone();
29037 sorted.sort();
29038 assert_eq!(
29039 sorted,
29040 vec!["already-kebab", "must-reach", "point-type", "with-a-b-c"],
29041 );
29042 }
29043
29044 #[test]
29045 fn sexp_from_json_number_arm_routes_through_atom_from_json_number() {
29046 // LIFTED-BOUNDARY CONTRACT: pin that the outer `Sexp::from_json`'s
29047 // `serde_json::Value::Number(_)` arm delegates through the
29048 // typed-algebra method `Atom::from_json_number` for every
29049 // reachable `serde_json::Number` shape (i64-backed, finite
29050 // f64-backed, and the empty structural-impossibility residual —
29051 // although `serde_json::Number`'s closed-set discriminator
29052 // excludes the last case in practice, so it's exercised only
29053 // via the direct algebra method). Pre-lift the outer arm
29054 // carried its own inline three-branch cascade (`n.as_i64` then
29055 // `n.as_f64` then `Self::int(0)` typed floor); post-lift the
29056 // arm collapses to `Self::Atom(Atom::from_json_number(n))` and
29057 // the per-variant numeric-axis body binds at ONE typed
29058 // projection on the [`Atom`] algebra. A regression that drifts
29059 // the outer arm (e.g. re-inlines ONE variant's rendering
29060 // without updating `Atom::from_json_number`, or introduces a
29061 // spurious f64→JValue::Null re-wrap) surfaces as an inequality
29062 // here. The sweep covers every Number shape the substrate
29063 // encounters in practice.
29064 //
29065 // Sibling-shape pin to `sexp_to_json_atom_arms_route_through_atom_to_json`
29066 // (in `crate::domain::tests`) — where that pin closes the
29067 // FORWARD `Sexp::Atom → JValue` routing through
29068 // `Atom::to_json`, THIS pin closes the INVERSE
29069 // `JValue::Number → Sexp::Atom` routing through
29070 // `Atom::from_json_number`. Together the two pins pin the
29071 // round-trip closure `Sexp::from_json ∘ Sexp::to_json` for
29072 // the numeric-axis subset AT the algebra layer rather than
29073 // per consumer.
29074 let number_shapes: Vec<serde_json::Number> = vec![
29075 0i64.into(),
29076 1i64.into(),
29077 (-1i64).into(),
29078 42i64.into(),
29079 i64::MAX.into(),
29080 i64::MIN.into(),
29081 serde_json::Number::from_f64(1.5).unwrap(),
29082 serde_json::Number::from_f64(-2.5).unwrap(),
29083 serde_json::Number::from_f64(1.234_567_890_123).unwrap(),
29084 ];
29085 for n in &number_shapes {
29086 let via_outer = Sexp::from_json(&serde_json::Value::Number(n.clone()));
29087 let via_algebra = Sexp::Atom(Atom::from_json_number(n));
29088 assert_eq!(
29089 via_outer, via_algebra,
29090 "Sexp::from_json Number arm drifted from \
29091 Sexp::Atom(Atom::from_json_number(n)) for {n:?}",
29092 );
29093 }
29094 }
29095
29096 // ── Sexp::is_kwargs_list: the kwargs-shape predicate on the algebra ─
29097 //
29098 // `Sexp::is_kwargs_list` lifts the `pub(crate) domain::is_kwargs_list`
29099 // free function onto the inherent-method canonical site on the
29100 // [`Sexp`] algebra — sibling-shape predicate peer of [`Sexp::is_list`]
29101 // narrowing the structural witness to the kwargs-shaped sub-cohort.
29102 // The tests below pin the per-arm contract on the new canonical site
29103 // directly; the `pub(crate)` free function has zero remaining callers
29104 // post-lift and is removed in the same patch so the substrate's
29105 // "kwargs-shape predicate" lives at exactly one canonical site on the
29106 // algebra rather than splitting across a `domain.rs` helper and the
29107 // `Sexp::to_json` call site.
29108
29109 #[test]
29110 fn sexp_is_kwargs_list_method_returns_true_for_canonical_kwargs_shape() {
29111 // PER-ARM CONTRACT (true cell): pin that a `Sexp::List` whose
29112 // even-indexed items are all keywords and whose length is non-zero
29113 // even returns `true` — the canonical kwargs shape `(:k v :k v …)`.
29114 // Covers the two-arity and four-arity baseline cases plus a mixed
29115 // payload (keyword odd index — even-index check is keyword-only,
29116 // odd-index payload is unconstrained per the kwargs convention).
29117 // A regression that drifts the predicate (incorrect parity check,
29118 // wrong keyword-position check, off-by-one in the step) surfaces
29119 // here immediately.
29120 let two = Sexp::List(vec![Sexp::keyword("k"), Sexp::int(1)]);
29121 assert!(two.is_kwargs_list());
29122 let four = Sexp::List(vec![
29123 Sexp::keyword("k1"),
29124 Sexp::int(1),
29125 Sexp::keyword("k2"),
29126 Sexp::string("v2"),
29127 ]);
29128 assert!(four.is_kwargs_list());
29129 // Odd-position values can themselves be keywords; the convention
29130 // only constrains the EVEN positions.
29131 let mixed = Sexp::List(vec![
29132 Sexp::keyword("k1"),
29133 Sexp::keyword("v-is-keyword-too"),
29134 Sexp::keyword("k2"),
29135 Sexp::Nil,
29136 ]);
29137 assert!(mixed.is_kwargs_list());
29138 }
29139
29140 #[test]
29141 fn sexp_is_kwargs_list_method_returns_false_for_non_list_outer_shapes_and_violating_lists() {
29142 // PER-ARM CONTRACT (false cell): pin that every non-`Self::List`
29143 // outer shape (Nil, every Atom payload variant, every quote-family
29144 // wrapper) returns `false`, and that every `Self::List` violating
29145 // the kwargs convention (empty, odd length, non-keyword at any
29146 // even index) also returns `false`. A regression that returns
29147 // `true` for a wrong shape (e.g. claiming a Nil or a non-kwargs
29148 // list satisfies the predicate, opening the door to a
29149 // `Sexp::to_json` arm misrouting) surfaces here immediately.
29150 // Non-list outer shapes:
29151 assert!(!Sexp::Nil.is_kwargs_list());
29152 assert!(!Sexp::symbol("s").is_kwargs_list());
29153 assert!(!Sexp::keyword("k").is_kwargs_list());
29154 assert!(!Sexp::string("body").is_kwargs_list());
29155 assert!(!Sexp::int(0).is_kwargs_list());
29156 assert!(!Sexp::float(0.0).is_kwargs_list());
29157 assert!(!Sexp::boolean(true).is_kwargs_list());
29158 assert!(!Sexp::Quote(Box::new(Sexp::keyword("k"))).is_kwargs_list());
29159 assert!(!Sexp::Quasiquote(Box::new(Sexp::keyword("k"))).is_kwargs_list());
29160 assert!(!Sexp::Unquote(Box::new(Sexp::keyword("k"))).is_kwargs_list());
29161 assert!(!Sexp::UnquoteSplice(Box::new(Sexp::keyword("k"))).is_kwargs_list());
29162 // List arm violations:
29163 assert!(!Sexp::List(vec![]).is_kwargs_list()); // empty
29164 assert!(!Sexp::List(vec![Sexp::keyword("k")]).is_kwargs_list()); // odd length 1
29165 assert!(
29166 !Sexp::List(vec![Sexp::keyword("k1"), Sexp::int(1), Sexp::keyword("k2")])
29167 .is_kwargs_list()
29168 ); // odd length 3
29169 assert!(!Sexp::List(vec![Sexp::int(1), Sexp::int(2)]).is_kwargs_list()); // non-keyword at even 0
29170 assert!(!Sexp::List(vec![
29171 Sexp::keyword("k1"),
29172 Sexp::int(1),
29173 Sexp::symbol("not-kw"),
29174 Sexp::int(2)
29175 ])
29176 .is_kwargs_list()); // non-keyword at even 2
29177 }
29178
29179 #[test]
29180 fn sexp_is_kwargs_list_method_composes_through_as_list_and_atom_as_keyword() {
29181 // COMPOSITION LAW: pin that the lifted predicate composes through
29182 // the already-lifted `Self::as_list` (structural projection onto
29183 // `&[Sexp]`) and `Atom::as_keyword` (typed projection onto the
29184 // keyword payload) primitives — a regression that re-inlines the
29185 // body without routing through the algebra-level soft-projection
29186 // family becomes detectable here. Sweeps every reachable outer
29187 // shape (Nil, every Atom variant, every quote-family wrapper, a
29188 // selection of List shapes covering the true + false cells) and
29189 // asserts the predicate's value agrees with the by-hand
29190 // `as_list().is_some_and(...)` recomposition.
29191 fn by_hand(s: &Sexp) -> bool {
29192 s.as_list().is_some_and(|items| {
29193 !items.is_empty()
29194 && items.len().is_multiple_of(2)
29195 && items.iter().step_by(2).all(|e| e.as_keyword().is_some())
29196 })
29197 }
29198 let cases = [
29199 Sexp::Nil,
29200 Sexp::symbol("s"),
29201 Sexp::keyword("k"),
29202 Sexp::string("body"),
29203 Sexp::int(7),
29204 Sexp::float(2.5),
29205 Sexp::boolean(false),
29206 Sexp::Quote(Box::new(Sexp::keyword("k"))),
29207 Sexp::Quasiquote(Box::new(Sexp::keyword("k"))),
29208 Sexp::Unquote(Box::new(Sexp::keyword("k"))),
29209 Sexp::UnquoteSplice(Box::new(Sexp::keyword("k"))),
29210 Sexp::List(vec![]),
29211 Sexp::List(vec![Sexp::int(1)]),
29212 Sexp::List(vec![Sexp::keyword("k"), Sexp::int(1)]),
29213 Sexp::List(vec![
29214 Sexp::keyword("k1"),
29215 Sexp::int(1),
29216 Sexp::keyword("k2"),
29217 Sexp::int(2),
29218 ]),
29219 Sexp::List(vec![Sexp::int(1), Sexp::int(2)]),
29220 Sexp::List(vec![Sexp::keyword("k1"), Sexp::int(1), Sexp::symbol("x")]),
29221 ];
29222 for s in &cases {
29223 assert_eq!(
29224 s.is_kwargs_list(),
29225 by_hand(s),
29226 "predicate drifted from as_list ∘ atom_as_keyword composition for {s}",
29227 );
29228 }
29229 }
29230
29231 #[test]
29232 fn sexp_to_json_object_arm_routes_through_is_kwargs_list_method() {
29233 // CALLSITE-CONTRACT: pin that `Sexp::to_json`'s kwargs-vs-array
29234 // bifurcation routes through the lifted `Sexp::is_kwargs_list`
29235 // method — the kwargs-shape witness that gates the
29236 // `serde_json::Value::Object` arm vs the `serde_json::Value::Array`
29237 // arm at the `Sexp::List` outer shape. The pin walks the gate
29238 // both directions: a kwargs-shaped list must project as `Object`
29239 // (and the inherent predicate must agree, `true`); a non-kwargs
29240 // list (empty, odd-length, or even-index non-keyword) must
29241 // project as `Array` (and the predicate must agree, `false`). A
29242 // regression that decouples the two paths (e.g. `to_json` routes
29243 // through a re-inlined check while `is_kwargs_list` continues to
29244 // delegate, or vice versa) surfaces here.
29245 // Kwargs-shaped: Object projection, predicate true.
29246 let kw = Sexp::List(vec![Sexp::keyword("foo-bar"), Sexp::int(1)]);
29247 assert!(kw.is_kwargs_list());
29248 assert!(matches!(
29249 kw.to_json().expect("kwargs list projects"),
29250 serde_json::Value::Object(_)
29251 ));
29252 // Non-kwargs (empty list): Array projection, predicate false.
29253 let empty = Sexp::List(vec![]);
29254 assert!(!empty.is_kwargs_list());
29255 assert!(matches!(
29256 empty.to_json().expect("empty list projects"),
29257 serde_json::Value::Array(arr) if arr.is_empty(),
29258 ));
29259 // Non-kwargs (positional): Array projection, predicate false.
29260 let positional = Sexp::List(vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)]);
29261 assert!(!positional.is_kwargs_list());
29262 assert!(matches!(
29263 positional.to_json().expect("positional list projects"),
29264 serde_json::Value::Array(arr) if arr.len() == 3,
29265 ));
29266 // Non-kwargs (even-index non-keyword): Array projection.
29267 let mixed = Sexp::List(vec![
29268 Sexp::keyword("k"),
29269 Sexp::int(1),
29270 Sexp::symbol("x"),
29271 Sexp::int(2),
29272 ]);
29273 assert!(!mixed.is_kwargs_list());
29274 assert!(matches!(
29275 mixed.to_json().expect("mixed list projects"),
29276 serde_json::Value::Array(_)
29277 ));
29278 }
29279
29280 #[test]
29281 fn sexp_from_json_round_trips_to_json_for_canonical_subset() {
29282 // ROUND-TRIP LAW: pin `Sexp::to_json(s)?.from_json() == s` for
29283 // the round-trippable subset of Sexp shapes — Nil, Atom::Str
29284 // (the lossless atomic floor that absorbs Symbol/Keyword on
29285 // re-projection, so this test stays inside the lossless cell),
29286 // Atom::Int, Atom::Float, Atom::Bool, and recursively
29287 // Sexp::List of round-trippable elements. Pin that the inverse
29288 // composes byte-for-byte against the forward projection inside
29289 // the lossless cell — the round-trip law's structural anchor
29290 // documented at `Sexp::from_json`'s docstring.
29291 let cases = [
29292 Sexp::Nil,
29293 Sexp::string("body"),
29294 Sexp::int(42),
29295 Sexp::float(1.5),
29296 Sexp::boolean(true),
29297 Sexp::List(vec![Sexp::int(1), Sexp::string("x"), Sexp::Nil]),
29298 // Empty list → empty array → empty list. Round-trips cleanly.
29299 Sexp::List(vec![]),
29300 ];
29301 for s in &cases {
29302 let projected = s
29303 .to_json()
29304 .expect("round-trippable Sexp must project to JSON");
29305 let recovered = Sexp::from_json(&projected);
29306 assert_eq!(recovered, *s, "round-trip drifted at {s}");
29307 }
29308 }
29309
29310 // ── Atom typed-construct family + Sexp outer-constructor routing ─────
29311 //
29312 // The six `Atom::{symbol, keyword, string, int, float, boolean}`
29313 // typed-construct methods are the section sibling of the existing
29314 // six `Atom::as_{symbol, keyword, string, int, float, bool}` soft-
29315 // projection family — closing the (construct, project) algebra dual
29316 // on the closed-set `Atom` algebra. The six `Sexp::{symbol, ...,
29317 // boolean}` outer constructors now route through
29318 // `Self::Atom(Atom::X(_))` so the `impl Into<String>` ergonomy +
29319 // tuple-variant constructor pair lives at ONE site per kind on the
29320 // `Atom` algebra. Pin the four structural laws:
29321 // (a) each `Atom::X` constructor produces the canonical tuple
29322 // variant payload byte-for-byte (`Atom::symbol("foo") ==
29323 // Atom::Symbol("foo".into())`, etc.) — pre-lift behavior
29324 // under the new construction face;
29325 // (b) the (construct, kind-project) round-trip
29326 // `Atom::X(_).kind() == AtomKind::X` for every (kind, payload)
29327 // pair — the typed-construct family pairs section-for-
29328 // retraction with the `Atom::kind` projection;
29329 // (c) the (construct, soft-project) round-trip
29330 // `Atom::X(payload).as_X() == Some(payload)` for every kind —
29331 // the typed-construct family pairs section-for-retraction
29332 // with the `Atom::as_X` family it now siblings;
29333 // (d) the outer-constructor composition law `Sexp::X(p) ==
29334 // Sexp::Atom(Atom::X(p))` for every kind — the `Sexp` outer
29335 // constructors route through the typed `Atom` constructors
29336 // rather than re-deriving the `Self::Atom(Atom::X(_))` pair
29337 // inline.
29338
29339 #[test]
29340 fn atom_typed_constructors_emit_canonical_tuple_variant_for_every_kind() {
29341 // STRUCTURAL CONSTRUCT CONTRACT: each `Atom::X` constructor
29342 // emits the matching `Atom::Variant(payload)` tuple-variant
29343 // value byte-for-byte. A regression that drifts ONE arm (e.g.
29344 // a typo routing `Atom::keyword(s)` to `Self::Symbol(s.into())`
29345 // — type-checks but silently mis-classifies every kwarg key
29346 // authored through the algebra-level constructor) surfaces
29347 // here. The `impl Into<String>` arms also accept `String`
29348 // payloads — pinned alongside `&str` so the `.into()` ergonomy
29349 // is exercised across both source types.
29350 assert_eq!(Atom::symbol("foo"), Atom::Symbol("foo".into()));
29351 assert_eq!(
29352 Atom::symbol(String::from("seph.1")),
29353 Atom::Symbol("seph.1".into()),
29354 );
29355 assert_eq!(Atom::symbol(""), Atom::Symbol(String::new()));
29356 assert_eq!(Atom::keyword("parent"), Atom::Keyword("parent".into()));
29357 assert_eq!(
29358 Atom::keyword(String::from("attr")),
29359 Atom::Keyword("attr".into()),
29360 );
29361 assert_eq!(Atom::keyword(""), Atom::Keyword(String::new()));
29362 assert_eq!(Atom::string("body"), Atom::Str("body".into()));
29363 assert_eq!(
29364 Atom::string(String::from("with\nnewline")),
29365 Atom::Str("with\nnewline".into()),
29366 );
29367 assert_eq!(Atom::string(""), Atom::Str(String::new()));
29368 assert_eq!(Atom::int(0), Atom::Int(0));
29369 assert_eq!(Atom::int(42), Atom::Int(42));
29370 assert_eq!(Atom::int(-7), Atom::Int(-7));
29371 assert_eq!(Atom::int(i64::MIN), Atom::Int(i64::MIN));
29372 assert_eq!(Atom::int(i64::MAX), Atom::Int(i64::MAX));
29373 assert_eq!(Atom::float(0.0), Atom::Float(0.0));
29374 assert_eq!(Atom::float(1.5), Atom::Float(1.5));
29375 assert_eq!(Atom::float(-2.5), Atom::Float(-2.5));
29376 // NaN compares unequal to itself; pin via `to_bits` round-trip,
29377 // matching the `Hash for Atom` Float-arm posture
29378 // (`f.to_bits().hash(...)`).
29379 assert_eq!(Atom::float(f64::NAN).kind(), AtomKind::Float);
29380 match Atom::float(f64::NAN) {
29381 Atom::Float(n) => assert!(n.is_nan()),
29382 _ => panic!("Atom::float must emit Atom::Float"),
29383 }
29384 assert_eq!(Atom::float(f64::INFINITY), Atom::Float(f64::INFINITY));
29385 assert_eq!(Atom::boolean(true), Atom::Bool(true));
29386 assert_eq!(Atom::boolean(false), Atom::Bool(false));
29387 }
29388
29389 #[test]
29390 fn atom_typed_constructors_round_trip_through_kind_projection() {
29391 // SECTION LAW (construct → kind): every typed constructor's
29392 // output projects through `Atom::kind` to its matching
29393 // `AtomKind` variant. The `(construct, kind-project)` pair
29394 // forms a deterministic surjection from the construct face
29395 // onto the closed-set `AtomKind` algebra — six (kind,
29396 // representative payload) probes sweep `AtomKind::ALL` so a
29397 // future seventh atomic kind landing on the algebra extends
29398 // BOTH the construct face AND this sweep in lockstep (rustc-
29399 // enforced through the closed-set match below).
29400 for kind in AtomKind::ALL {
29401 let constructed = match kind {
29402 AtomKind::Symbol => Atom::symbol("foo"),
29403 AtomKind::Keyword => Atom::keyword("parent"),
29404 AtomKind::Str => Atom::string("body"),
29405 AtomKind::Int => Atom::int(42),
29406 AtomKind::Float => Atom::float(1.5),
29407 AtomKind::Bool => Atom::boolean(true),
29408 };
29409 assert_eq!(
29410 constructed.kind(),
29411 kind,
29412 "Atom typed constructor for {kind:?} drifted from its closed-set kind projection",
29413 );
29414 }
29415 }
29416
29417 #[test]
29418 fn atom_typed_constructors_round_trip_through_per_variant_soft_projection() {
29419 // RETRACTION LAW (construct → soft-project): every typed
29420 // constructor's output projects through its matching `Atom::as_X`
29421 // soft projection to `Some(payload)` — the (construct, soft-
29422 // project) pair forms an `Iso(payload, Atom::Variant(payload))`
29423 // on the typed-payload axis. Sibling-axis to the
29424 // `(construct, kind-project)` pair above and to the
29425 // `Sexp::as_quote_form / QuoteForm::wrap` round-trip on the
29426 // outer-shape axis (`QuoteForm::wrap(inner).as_quote_form()
29427 // == Some((qf, &inner))`). The retraction's load-bearing
29428 // contract is what the substrate's named-form NAME gate
29429 // (`split_name_slot` → `as_symbol_or_string`) depends on at
29430 // every typed-domain dispatcher.
29431 assert_eq!(Atom::symbol("foo").as_symbol(), Some("foo"));
29432 assert_eq!(Atom::symbol("").as_symbol(), Some(""));
29433 assert_eq!(Atom::keyword("parent").as_keyword(), Some("parent"));
29434 assert_eq!(Atom::keyword("").as_keyword(), Some(""));
29435 assert_eq!(Atom::string("body").as_string(), Some("body"));
29436 assert_eq!(Atom::string("").as_string(), Some(""));
29437 assert_eq!(Atom::int(42).as_int(), Some(42));
29438 assert_eq!(Atom::int(0).as_int(), Some(0));
29439 assert_eq!(Atom::int(i64::MIN).as_int(), Some(i64::MIN));
29440 assert_eq!(Atom::float(1.5).as_float(), Some(1.5));
29441 assert_eq!(Atom::float(0.0).as_float(), Some(0.0));
29442 assert_eq!(Atom::boolean(true).as_bool(), Some(true));
29443 assert_eq!(Atom::boolean(false).as_bool(), Some(false));
29444 }
29445
29446 #[test]
29447 fn sexp_outer_constructors_route_through_atom_typed_construct_family() {
29448 // OUTER-CONSTRUCTOR COMPOSITION LAW: pin that each `Sexp::X`
29449 // outer constructor emits `Sexp::Atom(Atom::X(_))` byte-for-byte
29450 // — a regression that re-inlines the pre-lift body
29451 // `Self::Atom(Atom::Variant(s.into()))` and drifts ONE arm
29452 // (e.g. a future copy-edit that swaps `Sexp::symbol` to route
29453 // through `Atom::Keyword` after a refactor) becomes detectable
29454 // at this site. Sibling-shape pin to the `Sexp::as_X` family's
29455 // structural-lift composition through `Sexp::as_atom +
29456 // Atom::as_X` on the projection axis (sweep posture in
29457 // `sexp_as_symbol_or_string_routes_through_atom_as_symbol_or_string_via_as_atom_composition`).
29458 assert_eq!(Sexp::symbol("foo"), Sexp::Atom(Atom::symbol("foo")));
29459 assert_eq!(Sexp::symbol(""), Sexp::Atom(Atom::symbol("")));
29460 assert_eq!(
29461 Sexp::symbol(String::from("seph.1")),
29462 Sexp::Atom(Atom::symbol("seph.1")),
29463 );
29464 assert_eq!(Sexp::keyword("parent"), Sexp::Atom(Atom::keyword("parent")),);
29465 assert_eq!(Sexp::string("body"), Sexp::Atom(Atom::string("body")));
29466 assert_eq!(Sexp::int(42), Sexp::Atom(Atom::int(42)));
29467 assert_eq!(Sexp::int(i64::MIN), Sexp::Atom(Atom::int(i64::MIN)));
29468 assert_eq!(Sexp::float(1.5), Sexp::Atom(Atom::float(1.5)));
29469 assert_eq!(Sexp::boolean(true), Sexp::Atom(Atom::boolean(true)));
29470 assert_eq!(Sexp::boolean(false), Sexp::Atom(Atom::boolean(false)));
29471 }
29472
29473 #[test]
29474 fn atom_typed_constructors_partition_atom_kind_across_constructed_payloads() {
29475 // PARTITION LAW: every typed constructor's output projects to
29476 // `Some(_)` on its matching soft projection AND to `None` on
29477 // every other soft projection. The (construct, soft-project)
29478 // matrix is the diagonal of `AtomKind::ALL × AtomKind::ALL`:
29479 // on-diagonal cells return `Some`, off-diagonal cells return
29480 // `None`. Pin the full matrix so a regression that conflates
29481 // two construct arms (e.g. a future `Atom::keyword(s)` typo
29482 // routing to `Self::Symbol(s.into())` — type-checks, passes
29483 // the kind-projection sweep above iff the typo also drifts
29484 // `Atom::kind`, but fails THIS sweep because the off-diagonal
29485 // `Atom::keyword(s).as_symbol() == None` cell flips to `Some`)
29486 // surfaces structurally. The matrix's diagonal-restriction
29487 // form rebuilds the closed-set partition law every soft-
29488 // projection sweep above pins per-axis into ONE joint pin
29489 // across the (construct, project) algebra dual.
29490 let constructed = [
29491 (AtomKind::Symbol, Atom::symbol("foo")),
29492 (AtomKind::Keyword, Atom::keyword("parent")),
29493 (AtomKind::Str, Atom::string("body")),
29494 (AtomKind::Int, Atom::int(42)),
29495 (AtomKind::Float, Atom::float(1.5)),
29496 (AtomKind::Bool, Atom::boolean(true)),
29497 ];
29498 for (built_kind, a) in &constructed {
29499 assert_eq!(
29500 a.as_symbol().is_some(),
29501 *built_kind == AtomKind::Symbol,
29502 "as_symbol partition row drifted for {built_kind:?}",
29503 );
29504 assert_eq!(
29505 a.as_keyword().is_some(),
29506 *built_kind == AtomKind::Keyword,
29507 "as_keyword partition row drifted for {built_kind:?}",
29508 );
29509 assert_eq!(
29510 a.as_string().is_some(),
29511 *built_kind == AtomKind::Str,
29512 "as_string partition row drifted for {built_kind:?}",
29513 );
29514 assert_eq!(
29515 a.as_int().is_some(),
29516 *built_kind == AtomKind::Int,
29517 "as_int partition row drifted for {built_kind:?}",
29518 );
29519 assert_eq!(
29520 a.as_float().is_some(),
29521 *built_kind == AtomKind::Float,
29522 "as_float partition row drifted for {built_kind:?}",
29523 );
29524 assert_eq!(
29525 a.as_bool().is_some(),
29526 *built_kind == AtomKind::Bool,
29527 "as_bool partition row drifted for {built_kind:?}",
29528 );
29529 }
29530 }
29531
29532 // ── Sexp quote-family typed-construct algebra ────────────────────────
29533 //
29534 // `Sexp::quote` / `Sexp::quasiquote` / `Sexp::unquote` /
29535 // `Sexp::unquote_splice` are the outer-Sexp typed-construct family for
29536 // the four homoiconic prefix wrappers, section-for-retraction with the
29537 // `Sexp::as_quote_form` soft-projection sibling. Each routes through
29538 // `QuoteForm::X.wrap(inner)` so the (marker, `Sexp::* tuple-variant
29539 // constructor + `Box::new`) welded triple lives at ONE site on the
29540 // closed-set `QuoteForm` algebra. Pin FOUR structural laws:
29541 // (a) the canonical-tuple emission
29542 // `Sexp::quote(inner) == Sexp::Quote(Box::new(inner))` for
29543 // every wrapper marker — the typed constructor pairs section-
29544 // for-retraction with the tuple-variant constructor;
29545 // (b) the composition law
29546 // `Sexp::X_variant(inner) == QuoteForm::X.wrap(inner)` for
29547 // every marker — the outer typed constructor routes through
29548 // the inner-algebra `QuoteForm::wrap` typed dispatch;
29549 // (c) the round-trip law
29550 // `Sexp::X_variant(inner).as_quote_form() == Some((QuoteForm::X,
29551 // &inner))` for every marker — the (construct, soft-project)
29552 // algebra dual closes on the outer [`Sexp`] algebra with
29553 // marker + inner-body cross-projection preserved;
29554 // (d) the outer-shape pairing
29555 // `Sexp::X_variant(inner).shape() == QuoteForm::X.sexp_shape()`
29556 // for every marker — the construct family composes coherently
29557 // through the outer-shape projection on the typed-shape
29558 // lattice, so a regression that drifts ONE marker's outer-
29559 // shape pairing from `QuoteForm::sexp_shape` surfaces here.
29560
29561 #[test]
29562 fn sexp_quote_family_constructors_emit_canonical_tuple_variant_for_every_marker() {
29563 // STRUCTURAL CONSTRUCT CONTRACT: each `Sexp::X_variant`
29564 // constructor emits the matching `Sexp::X(Box::new(inner))`
29565 // tuple-variant value byte-for-byte. A regression that drifts
29566 // ONE arm (e.g. a typo routing `Sexp::unquote(inner)` to
29567 // `Sexp::UnquoteSplice(Box::new(inner))` — type-checks but
29568 // silently mis-classifies every macro-template substitution
29569 // authored through the algebra-level constructor) surfaces
29570 // here. Sibling-shape pin to the `Atom` typed-construct
29571 // family's canonical-tuple-variant test posture
29572 // (`atom_typed_constructors_emit_canonical_tuple_variant_for_every_kind`).
29573 let payloads = [
29574 Sexp::Nil,
29575 Sexp::symbol("x"),
29576 Sexp::keyword("k"),
29577 Sexp::string("body"),
29578 Sexp::int(42),
29579 Sexp::boolean(true),
29580 Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]),
29581 ];
29582 for inner in &payloads {
29583 assert_eq!(
29584 Sexp::quote(inner.clone()),
29585 Sexp::Quote(Box::new(inner.clone())),
29586 "Sexp::quote drifted from canonical tuple variant for {inner:?}",
29587 );
29588 assert_eq!(
29589 Sexp::quasiquote(inner.clone()),
29590 Sexp::Quasiquote(Box::new(inner.clone())),
29591 "Sexp::quasiquote drifted from canonical tuple variant for {inner:?}",
29592 );
29593 assert_eq!(
29594 Sexp::unquote(inner.clone()),
29595 Sexp::Unquote(Box::new(inner.clone())),
29596 "Sexp::unquote drifted from canonical tuple variant for {inner:?}",
29597 );
29598 assert_eq!(
29599 Sexp::unquote_splice(inner.clone()),
29600 Sexp::UnquoteSplice(Box::new(inner.clone())),
29601 "Sexp::unquote_splice drifted from canonical tuple variant for {inner:?}",
29602 );
29603 }
29604 }
29605
29606 #[test]
29607 fn sexp_quote_family_constructors_route_through_quote_form_wrap() {
29608 // COMPOSITION LAW: pin that each `Sexp::X_variant` outer
29609 // constructor emits `QuoteForm::X.wrap(inner)` byte-for-byte —
29610 // a regression that re-inlines the pre-lift body
29611 // `Self::X(Box::new(inner))` and drifts ONE arm (e.g. a future
29612 // copy-edit that swaps `Sexp::quote` to route through
29613 // `QuoteForm::Quasiquote` after a refactor) becomes detectable
29614 // at this site. Sibling-shape pin to the `Sexp::X_atom` family's
29615 // composition-through-`Atom::X` posture
29616 // (`sexp_outer_constructors_route_through_atom_typed_construct_family`).
29617 let inner = Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]);
29618 assert_eq!(
29619 Sexp::quote(inner.clone()),
29620 QuoteForm::Quote.wrap(inner.clone())
29621 );
29622 assert_eq!(
29623 Sexp::quasiquote(inner.clone()),
29624 QuoteForm::Quasiquote.wrap(inner.clone()),
29625 );
29626 assert_eq!(
29627 Sexp::unquote(inner.clone()),
29628 QuoteForm::Unquote.wrap(inner.clone())
29629 );
29630 assert_eq!(
29631 Sexp::unquote_splice(inner.clone()),
29632 QuoteForm::UnquoteSplice.wrap(inner.clone()),
29633 );
29634 }
29635
29636 #[test]
29637 fn sexp_quote_family_constructors_round_trip_through_as_quote_form() {
29638 // ROUND-TRIP LAW (construct → soft-project): every quote-family
29639 // typed constructor's output projects through `Sexp::as_quote_form`
29640 // to `Some((matching QuoteForm, &inner))`. Sweeps `QuoteForm::ALL`
29641 // paired with a representative inner payload — the four
29642 // (construct, project) pairs form an `Iso(inner, Sexp::X(inner))`
29643 // on the typed-marker axis at the outer [`Sexp`] algebra. A
29644 // regression that drifts ONE marker's construct arm (marker/
29645 // constructor swap) fails BOTH the marker-projection AND the
29646 // inner-borrow round-trip. Sibling-shape pin to the `Atom` typed-
29647 // construct family's per-variant soft-projection round-trip test
29648 // posture
29649 // (`atom_typed_constructors_round_trip_through_per_variant_soft_projection`).
29650 let inner = Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]);
29651 let constructed: [(QuoteForm, Sexp); 4] = [
29652 (QuoteForm::Quote, Sexp::quote(inner.clone())),
29653 (QuoteForm::Quasiquote, Sexp::quasiquote(inner.clone())),
29654 (QuoteForm::Unquote, Sexp::unquote(inner.clone())),
29655 (
29656 QuoteForm::UnquoteSplice,
29657 Sexp::unquote_splice(inner.clone()),
29658 ),
29659 ];
29660 for qf in QuoteForm::ALL {
29661 let (built_qf, sexp) = constructed
29662 .iter()
29663 .find(|(m, _)| *m == qf)
29664 .expect("QuoteForm::ALL sweep must reach every marker");
29665 assert_eq!(*built_qf, qf);
29666 let (proj_qf, proj_inner) = sexp
29667 .as_quote_form()
29668 .unwrap_or_else(|| panic!("construct→as_quote_form drifted at {qf:?}"));
29669 assert_eq!(
29670 proj_qf, qf,
29671 "typed-marker round-trip drifted at {qf:?} — construct+project pair broken",
29672 );
29673 assert_eq!(
29674 proj_inner, &inner,
29675 "inner-body round-trip drifted at {qf:?} — construct+project pair broken",
29676 );
29677 }
29678 }
29679
29680 #[test]
29681 fn sexp_quote_family_constructors_compose_with_shape_via_quote_form_sexp_shape() {
29682 // OUTER-SHAPE COMPOSITION LAW: every quote-family typed
29683 // constructor's output projects through `Sexp::shape` to the
29684 // matching `QuoteForm::X.sexp_shape()` — the (construct,
29685 // outer-shape) composition binds through the closed-set
29686 // `QuoteForm::sexp_shape` embed already lifted onto the
29687 // typed-shape lattice. A regression that drifts ONE construct
29688 // arm's outer-shape from `QuoteForm::sexp_shape` (e.g. a future
29689 // marker/wrapper swap that surfaces through the typed-shape
29690 // lattice but not through the tuple-variant emission itself)
29691 // surfaces here alongside the round-trip pin. Sibling-shape pin
29692 // to `quote_form_sexp_shape_paired_with_as_quote_form_preserves_pre_lift_pairing_for_every_sexp`
29693 // on the projection axis — this pin closes the same axis on the
29694 // outer construct family.
29695 let inner = Sexp::List(vec![Sexp::symbol("op"), Sexp::int(1)]);
29696 let constructed: [(QuoteForm, Sexp); 4] = [
29697 (QuoteForm::Quote, Sexp::quote(inner.clone())),
29698 (QuoteForm::Quasiquote, Sexp::quasiquote(inner.clone())),
29699 (QuoteForm::Unquote, Sexp::unquote(inner.clone())),
29700 (
29701 QuoteForm::UnquoteSplice,
29702 Sexp::unquote_splice(inner.clone()),
29703 ),
29704 ];
29705 for (qf, sexp) in &constructed {
29706 assert_eq!(
29707 sexp.shape(),
29708 qf.sexp_shape(),
29709 "Sexp::X_variant→shape drifted from QuoteForm::sexp_shape at {qf:?}",
29710 );
29711 }
29712 }
29713
29714 // ── Sexp::list residual-axis typed-construct algebra ─────────────────
29715 //
29716 // `Sexp::list(items)` is the residual-axis section-for-retraction
29717 // sibling of the pre-existing `Sexp::as_list` soft-projection — the
29718 // (construct, project) algebra dual on the 2-of-12 residual carving of
29719 // the [`SexpShape`] closed set now closes at ONE constructor + ONE
29720 // projection on the outer [`Sexp`] algebra, symmetric with the atomic-
29721 // payload carving's (six `Sexp::X_atom(payload)` constructors +
29722 // `Sexp::as_atom` / `Sexp::as_atom_kind` projections) and the quote-
29723 // family carving's (four `Sexp::X_variant(inner)` constructors +
29724 // `Sexp::as_quote_form` / `Sexp::as_quote_form_marker` projections).
29725 // [`Sexp::Nil`] is a unit variant with no payload — the residual-axis
29726 // construct family closes at ONE constructor (the sole payload-bearing
29727 // residual arm). Pin FIVE structural laws:
29728 // (a) the canonical-tuple emission
29729 // `Sexp::list(items) == Sexp::List(items.into_iter().collect())`
29730 // across representative empty / single-element / multi-element /
29731 // heterogeneous-inner samples — the typed constructor pairs
29732 // section-for-retraction with the tuple-variant constructor;
29733 // (b) the round-trip law
29734 // `Sexp::list(items.clone()).as_list() == Some(items.as_slice())`
29735 // — the (construct, soft-project) algebra dual closes on the
29736 // outer [`Sexp`] algebra with the borrowed-slice cross-
29737 // projection preserving identity;
29738 // (c) the outer-shape law
29739 // `Sexp::list(items).shape() == SexpShape::List` — the residual-
29740 // arm outer-shape identity binds through the typed-shape
29741 // lattice at ONE arm, symmetric with the quote-family
29742 // construct family's `Sexp::X_variant(inner).shape() ==
29743 // QuoteForm::X.sexp_shape()`;
29744 // (d) the structural-kind law
29745 // `Sexp::list(items).as_structural_kind() == Some(
29746 // StructuralKind::List)` — the residual carving marker binds
29747 // through the closed-set [`StructuralKind`] algebra at ONE
29748 // arm, symmetric with the atomic-axis's
29749 // `Sexp::X_atom(payload).as_atom_kind() == Some(AtomKind::X)`;
29750 // (e) the input-shape flexibility
29751 // `Sexp::list(&Vec<Sexp>)` / `Sexp::list([Sexp; N])` /
29752 // `Sexp::list(iter::map(...))` all agree with the canonical
29753 // tuple-variant emission — the `impl IntoIterator<Item = Sexp>`
29754 // bound accepts every reasonable owned-sequence shape without a
29755 // per-consumer `.collect::<Vec<Sexp>>()` coercion.
29756
29757 #[test]
29758 fn sexp_list_constructor_emits_canonical_tuple_variant_across_representative_inputs() {
29759 // STRUCTURAL CONSTRUCT CONTRACT: `Sexp::list(items)` emits
29760 // `Sexp::List(items.into_iter().collect::<Vec<Sexp>>())` byte-
29761 // for-byte across representative empty, single-element, multi-
29762 // element, and heterogeneous-inner samples. A regression that
29763 // drifts the body (e.g. wrapping items in an extra `Sexp::Nil`
29764 // sentinel, deduplicating, filtering) surfaces here. Sibling-
29765 // shape pin to the quote-family construct family's canonical-
29766 // tuple-variant test posture
29767 // (`sexp_quote_family_constructors_emit_canonical_tuple_variant_for_every_marker`).
29768 let samples: [Vec<Sexp>; 5] = [
29769 vec![],
29770 vec![Sexp::symbol("only")],
29771 vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)],
29772 vec![
29773 Sexp::Nil,
29774 Sexp::keyword("k"),
29775 Sexp::string("body"),
29776 Sexp::boolean(true),
29777 Sexp::List(vec![Sexp::symbol("nested")]),
29778 ],
29779 vec![
29780 Sexp::Quote(Box::new(Sexp::symbol("x"))),
29781 Sexp::Quasiquote(Box::new(Sexp::List(vec![
29782 Sexp::symbol("template"),
29783 Sexp::Unquote(Box::new(Sexp::symbol("var"))),
29784 ]))),
29785 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
29786 ],
29787 ];
29788 for items in &samples {
29789 assert_eq!(
29790 Sexp::list(items.clone()),
29791 Sexp::List(items.clone()),
29792 "Sexp::list drifted from canonical Sexp::List(_) tuple variant for {items:?}",
29793 );
29794 }
29795 }
29796
29797 #[test]
29798 fn sexp_list_constructor_round_trips_through_as_list() {
29799 // ROUND-TRIP LAW (section-for-retraction on the residual axis):
29800 // `Sexp::list(items.clone()).as_list() == Some(items.as_slice())`
29801 // sweeps the same representative input matrix as the canonical-
29802 // tuple pin — proves the (construct, soft-project) pair forms an
29803 // `Iso(Vec<Sexp>, Sexp::List(Vec<Sexp>))` on the residual axis,
29804 // symmetric with the quote-family axis's `Sexp::X_variant(inner)
29805 // .as_quote_form() == Some((QuoteForm::X, &inner))` round-trip
29806 // (pinned by `sexp_quote_family_constructors_round_trip_through_as_quote_form`).
29807 // A regression that mis-implements `Sexp::list` (e.g. dropping
29808 // items, cloning off-by-one) fails here on top of the canonical-
29809 // tuple pin.
29810 let samples: [Vec<Sexp>; 4] = [
29811 vec![],
29812 vec![Sexp::symbol("solo")],
29813 vec![Sexp::symbol("op"), Sexp::int(1), Sexp::int(2)],
29814 vec![
29815 Sexp::Nil,
29816 Sexp::List(vec![Sexp::symbol("nested"), Sexp::int(7)]),
29817 Sexp::Quote(Box::new(Sexp::symbol("q"))),
29818 ],
29819 ];
29820 for items in &samples {
29821 let built = Sexp::list(items.clone());
29822 assert_eq!(
29823 built.as_list(),
29824 Some(items.as_slice()),
29825 "Sexp::list→as_list round-trip drifted for {items:?}",
29826 );
29827 }
29828 }
29829
29830 #[test]
29831 fn sexp_list_constructor_composes_with_shape_via_sexp_shape_list() {
29832 // OUTER-SHAPE COMPOSITION LAW: every `Sexp::list(items)` output
29833 // projects through `Sexp::shape` to `SexpShape::List` regardless
29834 // of inner-item content — the (construct, outer-shape)
29835 // composition binds through the typed-shape lattice's residual-
29836 // arm at ONE arm. Sibling-shape pin to the quote-family construct
29837 // family's outer-shape composition
29838 // (`sexp_quote_family_constructors_compose_with_shape_via_quote_form_sexp_shape`).
29839 // A regression that reroutes `Sexp::list` through another shape
29840 // arm (e.g. wrapping in `Sexp::Quote` after a copy-edit that
29841 // type-checks) surfaces here alongside the canonical-tuple pin.
29842 let samples: [Vec<Sexp>; 4] = [
29843 vec![],
29844 vec![Sexp::symbol("only")],
29845 vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)],
29846 vec![
29847 Sexp::Nil,
29848 Sexp::Quote(Box::new(Sexp::symbol("x"))),
29849 Sexp::List(vec![Sexp::symbol("nested")]),
29850 ],
29851 ];
29852 for items in &samples {
29853 assert_eq!(
29854 Sexp::list(items.clone()).shape(),
29855 SexpShape::List,
29856 "Sexp::list→shape drifted from SexpShape::List for {items:?}",
29857 );
29858 }
29859 }
29860
29861 #[test]
29862 fn sexp_list_constructor_composes_with_as_structural_kind() {
29863 // STRUCTURAL-KIND COMPOSITION LAW: every `Sexp::list(items)`
29864 // output projects through `Sexp::as_structural_kind` to
29865 // `Some(StructuralKind::List)` regardless of inner-item content
29866 // — the residual carving marker binds through the closed-set
29867 // `StructuralKind` algebra at ONE arm. Sibling-shape pin to the
29868 // atomic-axis's `Sexp::X_atom(payload).as_atom_kind() ==
29869 // Some(AtomKind::X)` marker composition. A regression that
29870 // reroutes `Sexp::list` through a non-residual arm (e.g. a copy-
29871 // edit that wraps items in `Sexp::Quote`) surfaces here through
29872 // the returned marker no longer being `StructuralKind::List`.
29873 let samples: [Vec<Sexp>; 4] = [
29874 vec![],
29875 vec![Sexp::symbol("only")],
29876 vec![Sexp::keyword("k"), Sexp::string("v")],
29877 vec![
29878 Sexp::Nil,
29879 Sexp::List(vec![Sexp::symbol("nested")]),
29880 Sexp::Unquote(Box::new(Sexp::symbol("var"))),
29881 ],
29882 ];
29883 for items in &samples {
29884 assert_eq!(
29885 Sexp::list(items.clone()).as_structural_kind(),
29886 Some(StructuralKind::List),
29887 "Sexp::list→as_structural_kind drifted from Some(StructuralKind::List) for {items:?}",
29888 );
29889 }
29890 }
29891
29892 #[test]
29893 fn sexp_list_constructor_accepts_diverse_intoiterator_input_shapes() {
29894 // INPUT-SHAPE FLEXIBILITY: the `impl IntoIterator<Item = Sexp>`
29895 // bound accepts every reasonable owned-sequence shape without a
29896 // per-consumer `.collect::<Vec<Sexp>>()` coercion at the call
29897 // site — pin that `Vec<Sexp>`, `[Sexp; N]` array, `iter::empty
29898 // ::<Sexp>()`, and `.map(...)` iterator chains all reach the
29899 // same canonical tuple-variant output. A regression that
29900 // narrows the bound (e.g. taking `&[Sexp]` or `Vec<Sexp>` only)
29901 // fails this pin. The IntoIterator bound is load-bearing for the
29902 // ergonomy claim in the docstring — consumers threading a `.map`
29903 // chain through the outer algebra must not need an intermediate
29904 // `.collect()` before handing the result to `Sexp::list`.
29905 let expected = Sexp::List(vec![
29906 Sexp::symbol("a"),
29907 Sexp::symbol("b"),
29908 Sexp::symbol("c"),
29909 ]);
29910 // Vec<Sexp> — the canonical owned-sequence shape.
29911 assert_eq!(
29912 Sexp::list(vec![
29913 Sexp::symbol("a"),
29914 Sexp::symbol("b"),
29915 Sexp::symbol("c"),
29916 ]),
29917 expected,
29918 "Sexp::list drifted for Vec<Sexp> input",
29919 );
29920 // [Sexp; N] — array-literal shape (elements moved out of the
29921 // fixed-size array via the `IntoIterator` impl on `[T; N]`).
29922 assert_eq!(
29923 Sexp::list([Sexp::symbol("a"), Sexp::symbol("b"), Sexp::symbol("c"),]),
29924 expected,
29925 "Sexp::list drifted for [Sexp; N] input",
29926 );
29927 // `iter::empty::<Sexp>()` — the zero-item iterator shape.
29928 assert_eq!(
29929 Sexp::list(std::iter::empty::<Sexp>()),
29930 Sexp::List(vec![]),
29931 "Sexp::list drifted for iter::empty input",
29932 );
29933 // `.map(...)` iterator chain — the composition shape the
29934 // docstring's ergonomy claim rests on.
29935 assert_eq!(
29936 Sexp::list(["a", "b", "c"].iter().map(|s| Sexp::symbol(*s))),
29937 expected,
29938 "Sexp::list drifted for iterator-map chain input",
29939 );
29940 // `once(head).chain(tail)` — the head-then-rest shape a builder
29941 // consuming `head_symbol` + the tail slice threads through.
29942 assert_eq!(
29943 Sexp::list(
29944 std::iter::once(Sexp::symbol("a")).chain([Sexp::symbol("b"), Sexp::symbol("c")]),
29945 ),
29946 expected,
29947 "Sexp::list drifted for once+chain input",
29948 );
29949 }
29950
29951 // ── Sexp::call — call-form (symbol-headed list) construct ──────────
29952 //
29953 // `Sexp::call(head, args)` is the section-for-retraction dual of the
29954 // soft-projection `Sexp::as_call() -> Option<(&str, &[Sexp])>` — it
29955 // embeds a fresh `(head string, item sequence)` pair into a symbol-
29956 // headed `Sexp::List` value at ONE site on the outer `Sexp` algebra,
29957 // composing the atomic-payload construct family's `Sexp::symbol` (for
29958 // the head position) with the residual-axis construct family's
29959 // `Sexp::list` (for the list wrapper) via `std::iter::once(head_sexp)
29960 // .chain(args)`. Pre-lift the composition lived inline at every
29961 // consumer that built a `(defX …)` typed-domain call form, a
29962 // macroexpander template head, or a synthetic dispatch form —
29963 // `Sexp::List(vec![Sexp::symbol(head), args...])` or `Sexp::List(
29964 // std::iter::once(Sexp::symbol(head)).chain(args).collect())` was the
29965 // welded three-method open coding. Post-lift the closure binds at
29966 // ONE typed-algebra method.
29967 //
29968 // These pins cover:
29969 // (a) the composition law
29970 // `Sexp::call(head, args) == Sexp::list(std::iter::once(
29971 // Sexp::symbol(head)).chain(args))` — the constructor body is
29972 // BY DEFINITION the two-method composition;
29973 // (b) the round-trip law
29974 // `Sexp::call(head, args.clone()).as_call() == Some((head,
29975 // args.as_slice()))` — the (construct, project) call-form
29976 // algebra dual closes at this pair, symmetric with the
29977 // residual-axis's `Sexp::list(items.clone()).as_list() ==
29978 // Some(items.as_slice())` round-trip;
29979 // (c) the keyword-matched round-trip law
29980 // `Sexp::call(head, args.clone()).as_call_to(head) == Some(
29981 // args.as_slice())` — the keyword-typed projection recovers
29982 // the args tail iff its argument matches the constructor's
29983 // head;
29984 // (d) the head-symbol composition law
29985 // `Sexp::call(head, args).head_symbol() == Some(head)` — the
29986 // head-position projection recovers the constructor's head
29987 // byte-for-byte;
29988 // (e) the outer-shape composition law
29989 // `Sexp::call(head, args).shape() == SexpShape::List` — a
29990 // call form is a list-shaped `Sexp`;
29991 // (f) the structural-kind composition law
29992 // `Sexp::call(head, args).as_structural_kind() == Some(
29993 // StructuralKind::List)` — the residual carving marker binds
29994 // through the closed-set `StructuralKind` algebra at ONE
29995 // arm, symmetric with the residual-axis's `Sexp::list(items)
29996 // .as_structural_kind() == Some(StructuralKind::List)` marker
29997 // composition;
29998 // (g) the input-shape flexibility
29999 // `Sexp::call("h", Vec<Sexp>)` / `Sexp::call(String, [Sexp;
30000 // N])` / `Sexp::call(&String, iter::map(...))` all agree with
30001 // the canonical composition emission — the `impl Into<String>`
30002 // head bound + `impl IntoIterator<Item = Sexp>` args bound
30003 // accept every reasonable input shape without a per-consumer
30004 // `.to_string()` / `.collect::<Vec<Sexp>>()` coercion.
30005
30006 #[test]
30007 fn sexp_call_constructor_body_matches_canonical_two_method_composition_across_representative_inputs(
30008 ) {
30009 // COMPOSITION LAW: `Sexp::call(head, args) == Sexp::list(
30010 // std::iter::once(Sexp::symbol(head)).chain(args))` for every
30011 // representative (empty-args, single-arg, multi-arg,
30012 // heterogeneous-inner, quote-family-wrapping-inner) sample. A
30013 // regression that drifts the body (e.g. a copy-edit that
30014 // switches to `Sexp::keyword(head)` for the head position, or
30015 // that reorders `head` and `args` in the chain) surfaces here
30016 // BEFORE the projection pins fail. Sibling-shape pin to the
30017 // residual-axis's canonical-composition test posture
30018 // (`sexp_list_constructor_emits_canonical_tuple_variant_across_representative_inputs`).
30019 let samples: [(&str, Vec<Sexp>); 5] = [
30020 ("defcompiler", vec![]),
30021 ("defpoint", vec![Sexp::symbol("obs")]),
30022 (
30023 "defpoint",
30024 vec![
30025 Sexp::symbol("obs"),
30026 Sexp::keyword("class"),
30027 Sexp::symbol("Gate"),
30028 ],
30029 ),
30030 (
30031 "defcheck",
30032 vec![
30033 Sexp::List(vec![Sexp::symbol("crd-in-sync")]),
30034 Sexp::keyword("params"),
30035 Sexp::int(42),
30036 Sexp::string("body"),
30037 Sexp::boolean(true),
30038 ],
30039 ),
30040 (
30041 "defalert-policy",
30042 vec![
30043 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30044 Sexp::Quasiquote(Box::new(Sexp::List(vec![
30045 Sexp::symbol("template"),
30046 Sexp::Unquote(Box::new(Sexp::symbol("var"))),
30047 ]))),
30048 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
30049 ],
30050 ),
30051 ];
30052 for (head, args) in &samples {
30053 let expected =
30054 Sexp::list(std::iter::once(Sexp::symbol(*head)).chain(args.iter().cloned()));
30055 assert_eq!(
30056 Sexp::call(*head, args.clone()),
30057 expected,
30058 "Sexp::call drifted from Sexp::list(once(symbol(head)).chain(args)) for head={head:?} args={args:?}",
30059 );
30060 }
30061 }
30062
30063 #[test]
30064 fn sexp_call_constructor_round_trips_through_as_call() {
30065 // ROUND-TRIP LAW (section-for-retraction with the call-form
30066 // soft-projection): `Sexp::call(head, args.clone()).as_call()
30067 // == Some((head, args.as_slice()))` sweeps the same
30068 // representative input matrix as the composition-law pin —
30069 // proves the (construct, soft-project) pair forms an
30070 // `Iso((&str, Vec<Sexp>), symbol-headed Sexp::List)` on the
30071 // call-form typed decomposition. Sibling-shape pin to the
30072 // residual-axis's `Sexp::list(items.clone()).as_list() ==
30073 // Some(items.as_slice())` round-trip
30074 // (`sexp_list_constructor_round_trips_through_as_list`).
30075 let samples: [(&str, Vec<Sexp>); 4] = [
30076 ("defcompiler", vec![]),
30077 ("defpoint", vec![Sexp::symbol("solo")]),
30078 (
30079 "defmonitor",
30080 vec![Sexp::symbol("m"), Sexp::int(1), Sexp::int(2)],
30081 ),
30082 (
30083 "defnotify",
30084 vec![
30085 Sexp::Nil,
30086 Sexp::List(vec![Sexp::symbol("nested"), Sexp::int(7)]),
30087 Sexp::Quote(Box::new(Sexp::symbol("q"))),
30088 ],
30089 ),
30090 ];
30091 for (head, args) in &samples {
30092 let built = Sexp::call(*head, args.clone());
30093 assert_eq!(
30094 built.as_call(),
30095 Some((*head, args.as_slice())),
30096 "Sexp::call→as_call round-trip drifted for head={head:?} args={args:?}",
30097 );
30098 }
30099 }
30100
30101 #[test]
30102 fn sexp_call_constructor_round_trips_through_as_call_to_matching_keyword() {
30103 // KEYWORD-MATCHED ROUND-TRIP LAW: `Sexp::call(head, args
30104 // .clone()).as_call_to(head) == Some(args.as_slice())` for the
30105 // head-matched keyword, and `.as_call_to(other)` returns `None`
30106 // for every other keyword. Pins the (construct, keyword-typed-
30107 // project) pair on the outer algebra — the same dispatch
30108 // shape `compile_typed` / `compile_named_from_forms` route
30109 // through post-macroexpansion.
30110 let samples: [(&str, Vec<Sexp>); 4] = [
30111 ("defcompiler", vec![]),
30112 ("defpoint", vec![Sexp::symbol("obs")]),
30113 ("defmonitor", vec![Sexp::keyword("k"), Sexp::string("v")]),
30114 (
30115 "defalert-policy",
30116 vec![Sexp::Nil, Sexp::List(vec![Sexp::symbol("body")])],
30117 ),
30118 ];
30119 for (head, args) in &samples {
30120 let built = Sexp::call(*head, args.clone());
30121 assert_eq!(
30122 built.as_call_to(head),
30123 Some(args.as_slice()),
30124 "Sexp::call→as_call_to(head) round-trip drifted for head={head:?} args={args:?}",
30125 );
30126 // Cross-keyword rejection: every DIFFERENT keyword misses.
30127 let mismatched = format!("{head}-mismatch");
30128 assert_eq!(
30129 built.as_call_to(&mismatched),
30130 None,
30131 "Sexp::call→as_call_to(mismatch) leaked args for head={head:?}",
30132 );
30133 }
30134 }
30135
30136 #[test]
30137 fn sexp_call_constructor_composes_with_head_symbol_and_shape_and_structural_kind() {
30138 // OUTER-ALGEBRA PROJECTION COMPOSITIONS: every `Sexp::call(head,
30139 // args)` output projects through `head_symbol` /
30140 // `shape` / `as_structural_kind` to the shape-invariants that
30141 // pin the constructor's structural identity:
30142 // * `head_symbol() == Some(head)` — the head-position
30143 // projection recovers the constructor's head byte-for-byte;
30144 // * `shape() == SexpShape::List` — a call form is a list-
30145 // shaped `Sexp` on the residual carving;
30146 // * `as_structural_kind() == Some(StructuralKind::List)` — the
30147 // residual carving marker binds through the closed-set
30148 // `StructuralKind` algebra at ONE arm.
30149 // A regression that reroutes `Sexp::call` through a non-list
30150 // arm (e.g. wrapping in `Sexp::Quote` after a copy-edit that
30151 // type-checks) fails ALL THREE pins simultaneously. Sibling to
30152 // the residual-axis's `Sexp::list` shape-composition pins
30153 // (`sexp_list_constructor_composes_with_shape_via_sexp_shape_list`
30154 // + `sexp_list_constructor_composes_with_as_structural_kind`).
30155 let samples: [(&str, Vec<Sexp>); 4] = [
30156 ("head", vec![]),
30157 ("head", vec![Sexp::symbol("only")]),
30158 (
30159 "head",
30160 vec![Sexp::keyword("k"), Sexp::string("v"), Sexp::boolean(false)],
30161 ),
30162 (
30163 "head",
30164 vec![
30165 Sexp::Nil,
30166 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30167 Sexp::List(vec![Sexp::symbol("nested")]),
30168 ],
30169 ),
30170 ];
30171 for (head, args) in &samples {
30172 let built = Sexp::call(*head, args.clone());
30173 assert_eq!(
30174 built.head_symbol(),
30175 Some(*head),
30176 "Sexp::call→head_symbol drifted from Some({head:?}) for args={args:?}",
30177 );
30178 assert_eq!(
30179 built.shape(),
30180 SexpShape::List,
30181 "Sexp::call→shape drifted from SexpShape::List for head={head:?} args={args:?}",
30182 );
30183 assert_eq!(
30184 built.as_structural_kind(),
30185 Some(StructuralKind::List),
30186 "Sexp::call→as_structural_kind drifted from Some(StructuralKind::List) for head={head:?} args={args:?}",
30187 );
30188 }
30189 }
30190
30191 #[test]
30192 fn sexp_call_constructor_accepts_diverse_head_and_arg_input_shapes() {
30193 // INPUT-SHAPE FLEXIBILITY: the `impl Into<String>` head bound
30194 // absorbs `&str` / `String` / `&String`, and the `impl
30195 // IntoIterator<Item = Sexp>` args bound absorbs `Vec<Sexp>` /
30196 // `[Sexp; N]` / `iter::empty()` / `.map(...)` chains — pin that
30197 // all six representative input shapes reach the same canonical
30198 // composition output. A regression that narrows either bound
30199 // (e.g. requiring `String` on the head or `Vec<Sexp>` on the
30200 // args) fails this pin. The two bounds are load-bearing for the
30201 // ergonomy claim in the docstring — consumers threading a
30202 // borrowed head + a `.map` chain must not need `.to_string()` /
30203 // `.collect()` coercions before handing the pair to
30204 // `Sexp::call`. Sibling to `Sexp::list`'s input-shape pin
30205 // (`sexp_list_constructor_accepts_diverse_intoiterator_input_shapes`)
30206 // and `Sexp::symbol`'s head-string absorption posture.
30207 let expected = Sexp::List(vec![
30208 Sexp::symbol("head"),
30209 Sexp::symbol("a"),
30210 Sexp::symbol("b"),
30211 ]);
30212 // (&str, Vec<Sexp>) — the canonical borrowed-head + owned-args
30213 // shape.
30214 assert_eq!(
30215 Sexp::call("head", vec![Sexp::symbol("a"), Sexp::symbol("b")]),
30216 expected,
30217 "Sexp::call drifted for (&str, Vec<Sexp>) input",
30218 );
30219 // (String, [Sexp; N]) — the owned-head + array-literal shape.
30220 assert_eq!(
30221 Sexp::call(String::from("head"), [Sexp::symbol("a"), Sexp::symbol("b")],),
30222 expected,
30223 "Sexp::call drifted for (String, [Sexp; N]) input",
30224 );
30225 // (&String, .map(...)) — the borrowed-owned-head + iterator-map
30226 // chain shape.
30227 let owned_head = String::from("head");
30228 assert_eq!(
30229 Sexp::call(&owned_head, ["a", "b"].iter().map(|s| Sexp::symbol(*s))),
30230 expected,
30231 "Sexp::call drifted for (&String, iter::map) input",
30232 );
30233 // (&str, iter::empty::<Sexp>()) — the zero-arg iterator shape,
30234 // pinning the singleton-list emission (`(head)`) via the
30235 // composition path.
30236 assert_eq!(
30237 Sexp::call("head", std::iter::empty::<Sexp>()),
30238 Sexp::List(vec![Sexp::symbol("head")]),
30239 "Sexp::call drifted for zero-arg iter::empty input",
30240 );
30241 // (&str, once(head_of_args).chain(tail_of_args)) — the head-
30242 // then-rest args shape a builder decomposing an existing call
30243 // form via `as_call` and re-emitting through this constructor
30244 // threads through.
30245 assert_eq!(
30246 Sexp::call(
30247 "head",
30248 std::iter::once(Sexp::symbol("a")).chain([Sexp::symbol("b")]),
30249 ),
30250 expected,
30251 "Sexp::call drifted for (&str, once+chain) args input",
30252 );
30253 }
30254
30255 #[test]
30256 fn sexp_call_constructor_body_matches_typed_composition_through_list_and_symbol() {
30257 // EXPLICIT COMPOSITION-LAW PIN: `Sexp::call(head, args) ==
30258 // Sexp::list(std::iter::once(Sexp::symbol(head)).chain(args))`
30259 // BY DEFINITION — the constructor body IS this composition, and
30260 // the pin exists so a regression that in-lines a hand-authored
30261 // `Sexp::List(vec![Sexp::symbol(head), args...])` body (which
30262 // would type-check and pass the projection round-trips) still
30263 // surfaces here through the composition-path drift. This closes
30264 // the "the constructor routes through the outer-algebra's
30265 // atomic + residual construct families" invariant as a typed
30266 // pin rather than a docstring claim.
30267 let head = "defpoint";
30268 let args = vec![
30269 Sexp::symbol("obs"),
30270 Sexp::keyword("class"),
30271 Sexp::List(vec![Sexp::symbol("Gate"), Sexp::symbol("Observability")]),
30272 ];
30273 assert_eq!(
30274 Sexp::call(head, args.clone()),
30275 Sexp::list(std::iter::once(Sexp::symbol(head)).chain(args.iter().cloned())),
30276 "Sexp::call body drifted from the Sexp::list ∘ once(Sexp::symbol) ∘ chain composition for head={head:?}",
30277 );
30278 }
30279
30280 // ── Sexp::named_call — named-call-form (symbol-headed + NAME slot)
30281 // construct ───────────────────────────────────────────────────────
30282 //
30283 // `Sexp::named_call(head, name, spec_args)` is the section-for-
30284 // retraction dual of the soft-projection `Sexp::as_named_call_to(
30285 // keyword) -> Option<Result<(&str, &[Sexp])>>` — it embeds a fresh
30286 // `(head string, NAME string, spec args sequence)` triple into a
30287 // symbol-headed `(head NAME spec_args…)` `Sexp::List` value at ONE
30288 // site on the outer `Sexp` algebra, composing the call-form
30289 // typed constructor `Sexp::call` (which itself composes the atomic
30290 // `Sexp::symbol` head with the residual `Sexp::list` wrapper) with
30291 // a NAME-slot `Sexp::symbol` embedding via `std::iter::once(
30292 // Sexp::symbol(name)).chain(spec_args)`. Pre-lift the composition
30293 // lived inline at every consumer that built a `(defX NAME …)`
30294 // typed-domain named authoring form or a synthetic named-dispatch
30295 // form — `Sexp::List(vec![Sexp::symbol(head), Sexp::symbol(name),
30296 // spec_args...])` or `Sexp::call(head, std::iter::once(
30297 // Sexp::symbol(name)).chain(spec_args))` was the welded quadruple
30298 // open coding. Post-lift the closure binds at ONE typed-algebra
30299 // method.
30300 //
30301 // These pins cover:
30302 // (a) the composition law
30303 // `Sexp::named_call(head, name, spec_args) == Sexp::call(
30304 // head, std::iter::once(Sexp::symbol(name)).chain(spec_args))`
30305 // — the constructor body is BY DEFINITION the two-method
30306 // composition;
30307 // (b) the round-trip law
30308 // `Sexp::named_call(head, name, spec_args.clone())
30309 // .as_named_call_to(head) == Some(Ok((name, spec_args
30310 // .as_slice())))` — the (construct, named-project) named-
30311 // call-form algebra dual closes at this pair, symmetric with
30312 // the call-form's `Sexp::call(head, args.clone()).as_call()
30313 // == Some((head, args.as_slice()))` round-trip;
30314 // (c) the call-form projection composition
30315 // `Sexp::named_call(head, name, spec_args)
30316 // .as_call() == Some((head, [Sexp::symbol(name),
30317 // spec_args…].as_slice()))` — the call-form soft-projection
30318 // recovers `(head, [name, spec_args…])` with the NAME symbol
30319 // as the first arg, threading the constructor's output
30320 // through the encompassing call-form projection;
30321 // (d) the keyword-matched round-trip law
30322 // `Sexp::named_call(head, name, spec_args)
30323 // .as_call_to(head) == Some([Sexp::symbol(name),
30324 // spec_args…].as_slice())` — the keyword-typed projection
30325 // recovers the NAME-headed args tail iff its argument
30326 // matches the constructor's head;
30327 // (e) the head-symbol composition law
30328 // `Sexp::named_call(head, name, spec_args).head_symbol()
30329 // == Some(head)` — the head-position projection recovers
30330 // the constructor's head byte-for-byte;
30331 // (f) the named-form gate composition law
30332 // `crate::compile::split_name_slot(&Sexp::named_call(head,
30333 // name, spec_args).as_call_to(head).unwrap(), head) == Ok((
30334 // name, spec_args.as_slice()))` — the substrate's named-
30335 // form arity + NAME-shape gate accepts every output of this
30336 // constructor byte-for-byte, closing the section-for-
30337 // retraction pair at the gate level as well as the
30338 // projection level;
30339 // (g) the outer-shape composition law
30340 // `Sexp::named_call(head, name, spec_args).shape() ==
30341 // SexpShape::List` and `.as_structural_kind() == Some(
30342 // StructuralKind::List)` — the residual carving marker binds
30343 // through the closed-set `StructuralKind` algebra at ONE
30344 // arm, symmetric with `Sexp::call`'s residual-arm marker
30345 // composition;
30346 // (h) the input-shape flexibility
30347 // `Sexp::named_call("h", "n", Vec<Sexp>)` / `Sexp::
30348 // named_call(String, String, [Sexp; N])` / `Sexp::
30349 // named_call(&str, &String, iter::map(...))` all agree with
30350 // the canonical composition emission — the two `impl
30351 // Into<String>` bounds + `impl IntoIterator<Item = Sexp>`
30352 // args bound accept every reasonable input shape without a
30353 // per-consumer `.to_string()` / `.collect::<Vec<Sexp>>()`
30354 // coercion.
30355
30356 #[test]
30357 fn sexp_named_call_constructor_body_matches_canonical_two_method_composition_across_representative_inputs(
30358 ) {
30359 // COMPOSITION LAW: `Sexp::named_call(head, name, spec_args) ==
30360 // Sexp::call(head, std::iter::once(Sexp::symbol(name)).chain(
30361 // spec_args))` for every representative (empty-spec-args,
30362 // single-spec-arg, multi-spec-arg, heterogeneous-inner,
30363 // quote-family-wrapping-inner) sample. A regression that
30364 // drifts the body (e.g. a copy-edit that switches to
30365 // `Sexp::keyword(name)` for the NAME position, or that
30366 // reorders `name` and `spec_args` in the chain) surfaces here
30367 // BEFORE the projection pins fail. Sibling-shape pin to
30368 // `Sexp::call`'s canonical-composition test posture.
30369 let samples: [(&'static str, &'static str, Vec<Sexp>); 5] = [
30370 ("defcompiler", "solo", vec![]),
30371 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30372 (
30373 "defmonitor",
30374 "m",
30375 vec![
30376 Sexp::keyword("severity"),
30377 Sexp::symbol("Warning"),
30378 Sexp::keyword("threshold"),
30379 Sexp::int(42),
30380 ],
30381 ),
30382 (
30383 "defcheck",
30384 "coherent",
30385 vec![
30386 Sexp::List(vec![Sexp::symbol("crd-in-sync")]),
30387 Sexp::string("body"),
30388 Sexp::boolean(true),
30389 ],
30390 ),
30391 (
30392 "defalert-policy",
30393 "outage",
30394 vec![
30395 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30396 Sexp::Quasiquote(Box::new(Sexp::List(vec![
30397 Sexp::symbol("template"),
30398 Sexp::Unquote(Box::new(Sexp::symbol("var"))),
30399 ]))),
30400 Sexp::UnquoteSplice(Box::new(Sexp::symbol("xs"))),
30401 ],
30402 ),
30403 ];
30404 for (head, name, spec_args) in &samples {
30405 let expected = Sexp::call(
30406 *head,
30407 std::iter::once(Sexp::symbol(*name)).chain(spec_args.iter().cloned()),
30408 );
30409 assert_eq!(
30410 Sexp::named_call(*head, *name, spec_args.clone()),
30411 expected,
30412 "Sexp::named_call drifted from Sexp::call(head, once(symbol(name)).chain(spec_args)) for head={head:?} name={name:?} spec_args={spec_args:?}",
30413 );
30414 }
30415 }
30416
30417 #[test]
30418 fn sexp_named_call_constructor_round_trips_through_as_named_call_to() {
30419 // ROUND-TRIP LAW (section-for-retraction with the named-form
30420 // soft-projection): `Sexp::named_call(head, name, spec_args
30421 // .clone()).as_named_call_to(head) == Some(Ok((name,
30422 // spec_args.as_slice())))` sweeps the same representative
30423 // input matrix — proves the (construct, named-project) pair
30424 // forms an `Iso((&'static str, &str, Vec<Sexp>),
30425 // (head-symbol-headed + NAME-symbol-second Sexp::List))` on
30426 // the named-call-form typed decomposition. Sibling-shape pin
30427 // to `Sexp::call`'s round-trip through `as_call` posture.
30428 let samples: [(&'static str, &'static str, Vec<Sexp>); 4] = [
30429 ("defcompiler", "solo", vec![]),
30430 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30431 (
30432 "defmonitor",
30433 "m",
30434 vec![Sexp::keyword("k"), Sexp::string("v")],
30435 ),
30436 (
30437 "defalert-policy",
30438 "outage",
30439 vec![Sexp::Nil, Sexp::List(vec![Sexp::symbol("body")])],
30440 ),
30441 ];
30442 for (head, name, spec_args) in &samples {
30443 let built = Sexp::named_call(*head, *name, spec_args.clone());
30444 assert_eq!(
30445 built.as_named_call_to(head).and_then(|res| res.ok()),
30446 Some((*name, spec_args.as_slice())),
30447 "Sexp::named_call→as_named_call_to round-trip drifted for head={head:?} name={name:?} spec_args={spec_args:?}",
30448 );
30449 }
30450 }
30451
30452 #[test]
30453 fn sexp_named_call_constructor_projects_through_as_call_with_name_first_arg() {
30454 // CALL-FORM PROJECTION COMPOSITION: `Sexp::named_call(head,
30455 // name, spec_args).as_call() == Some((head, [Sexp::symbol(
30456 // name), spec_args…].as_slice()))` — the call-form soft-
30457 // projection recovers `(head, [name, spec_args…])` with the
30458 // NAME symbol as the first arg. Sibling-shape pin to the
30459 // call-form encompassing algebra: the named-call constructor
30460 // routes cleanly through the call-form projection AS A
30461 // COMPOSITION.
30462 let samples: [(&'static str, &'static str, Vec<Sexp>); 3] = [
30463 ("defcompiler", "solo", vec![]),
30464 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30465 (
30466 "defmonitor",
30467 "m",
30468 vec![Sexp::keyword("threshold"), Sexp::int(42)],
30469 ),
30470 ];
30471 for (head, name, spec_args) in &samples {
30472 let built = Sexp::named_call(*head, *name, spec_args.clone());
30473 let expected_args: Vec<Sexp> = std::iter::once(Sexp::symbol(*name))
30474 .chain(spec_args.iter().cloned())
30475 .collect();
30476 assert_eq!(
30477 built.as_call(),
30478 Some((*head, expected_args.as_slice())),
30479 "Sexp::named_call→as_call drifted for head={head:?} name={name:?} spec_args={spec_args:?}",
30480 );
30481 }
30482 }
30483
30484 #[test]
30485 fn sexp_named_call_constructor_round_trips_through_as_call_to_matching_keyword() {
30486 // KEYWORD-MATCHED ROUND-TRIP LAW: `Sexp::named_call(head,
30487 // name, spec_args.clone()).as_call_to(head) == Some([
30488 // Sexp::symbol(name), spec_args…].as_slice())` for the head-
30489 // matched keyword, and `.as_call_to(other) == None` for every
30490 // other keyword. Pins the (construct, keyword-typed-project)
30491 // pair on the outer algebra threading through the NAMED axis
30492 // — the same dispatch shape `compile_named_from_forms` routes
30493 // through post-macroexpansion.
30494 let samples: [(&'static str, &'static str, Vec<Sexp>); 3] = [
30495 ("defcompiler", "solo", vec![]),
30496 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30497 (
30498 "defmonitor",
30499 "m",
30500 vec![Sexp::keyword("k"), Sexp::string("v")],
30501 ),
30502 ];
30503 for (head, name, spec_args) in &samples {
30504 let built = Sexp::named_call(*head, *name, spec_args.clone());
30505 let expected_args: Vec<Sexp> = std::iter::once(Sexp::symbol(*name))
30506 .chain(spec_args.iter().cloned())
30507 .collect();
30508 assert_eq!(
30509 built.as_call_to(head),
30510 Some(expected_args.as_slice()),
30511 "Sexp::named_call→as_call_to(head) round-trip drifted for head={head:?} name={name:?} spec_args={spec_args:?}",
30512 );
30513 // Cross-keyword rejection: every DIFFERENT keyword misses.
30514 let mismatched = format!("{head}-mismatch");
30515 assert_eq!(
30516 built.as_call_to(&mismatched),
30517 None,
30518 "Sexp::named_call→as_call_to(mismatch) leaked args for head={head:?} name={name:?}",
30519 );
30520 }
30521 }
30522
30523 #[test]
30524 fn sexp_named_call_constructor_composes_with_head_symbol_and_shape_and_structural_kind() {
30525 // OUTER-ALGEBRA PROJECTION COMPOSITIONS: every `Sexp::
30526 // named_call(head, name, spec_args)` output projects through
30527 // `head_symbol` / `shape` / `as_structural_kind` to the shape-
30528 // invariants that pin the constructor's structural identity:
30529 // * `head_symbol() == Some(head)` — the head-position
30530 // projection recovers the constructor's head byte-for-byte;
30531 // * `shape() == SexpShape::List` — a named call form is a
30532 // list-shaped `Sexp` on the residual carving;
30533 // * `as_structural_kind() == Some(StructuralKind::List)` —
30534 // the residual carving marker binds through the closed-
30535 // set `StructuralKind` algebra at ONE arm.
30536 let samples: [(&'static str, &'static str, Vec<Sexp>); 3] = [
30537 ("head", "n", vec![]),
30538 ("head", "n", vec![Sexp::keyword("k"), Sexp::string("v")]),
30539 (
30540 "head",
30541 "n",
30542 vec![
30543 Sexp::Nil,
30544 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30545 Sexp::List(vec![Sexp::symbol("nested")]),
30546 ],
30547 ),
30548 ];
30549 for (head, name, spec_args) in &samples {
30550 let built = Sexp::named_call(*head, *name, spec_args.clone());
30551 assert_eq!(
30552 built.head_symbol(),
30553 Some(*head),
30554 "Sexp::named_call→head_symbol drifted from Some({head:?}) for name={name:?} spec_args={spec_args:?}",
30555 );
30556 assert_eq!(
30557 built.shape(),
30558 SexpShape::List,
30559 "Sexp::named_call→shape drifted from SexpShape::List for head={head:?} name={name:?} spec_args={spec_args:?}",
30560 );
30561 assert_eq!(
30562 built.as_structural_kind(),
30563 Some(StructuralKind::List),
30564 "Sexp::named_call→as_structural_kind drifted from Some(StructuralKind::List) for head={head:?} name={name:?} spec_args={spec_args:?}",
30565 );
30566 }
30567 }
30568
30569 #[test]
30570 fn sexp_named_call_constructor_output_passes_the_split_name_slot_gate() {
30571 // NAMED-FORM GATE COMPOSITION LAW: `crate::compile::
30572 // split_name_slot(&Sexp::named_call(head, name, spec_args)
30573 // .as_call_to(head).unwrap(), head) == Ok((name, spec_args
30574 // .as_slice()))` — the substrate's named-form arity + NAME-
30575 // shape gate accepts every output of this constructor byte-
30576 // for-byte, closing the section-for-retraction pair at the
30577 // GATE level as well as the projection level. A regression
30578 // that emits a value the gate rejects (e.g. a
30579 // `NamedFormNonSymbolName` from a `Sexp::keyword(name)` NAME
30580 // slot copy-edit) surfaces here even when the projection
30581 // pins pass.
30582 let samples: [(&'static str, &'static str, Vec<Sexp>); 4] = [
30583 ("defcompiler", "solo", vec![]),
30584 ("defpoint", "obs", vec![Sexp::keyword("class")]),
30585 (
30586 "defmonitor",
30587 "m",
30588 vec![Sexp::keyword("severity"), Sexp::symbol("Warning")],
30589 ),
30590 (
30591 "defalert-policy",
30592 "outage",
30593 vec![
30594 Sexp::List(vec![Sexp::symbol("body")]),
30595 Sexp::Quote(Box::new(Sexp::symbol("x"))),
30596 ],
30597 ),
30598 ];
30599 for (head, name, spec_args) in &samples {
30600 let built = Sexp::named_call(*head, *name, spec_args.clone());
30601 let args_tail = built
30602 .as_call_to(head)
30603 .expect("Sexp::named_call output must pass Sexp::as_call_to(head)");
30604 let gated = crate::compile::split_name_slot(args_tail, head)
30605 .expect("Sexp::named_call output must pass split_name_slot");
30606 assert_eq!(
30607 gated,
30608 (*name, spec_args.as_slice()),
30609 "Sexp::named_call→split_name_slot round-trip drifted for head={head:?} name={name:?} spec_args={spec_args:?}",
30610 );
30611 }
30612 }
30613
30614 #[test]
30615 fn sexp_named_call_constructor_accepts_diverse_head_name_and_arg_input_shapes() {
30616 // INPUT-SHAPE FLEXIBILITY: the two `impl Into<String>` bounds
30617 // absorb `&str` / `String` / `&String` on both head + NAME
30618 // positions, and the `impl IntoIterator<Item = Sexp>` spec-
30619 // args bound absorbs `Vec<Sexp>` / `[Sexp; N]` / `iter::
30620 // empty()` / `.map(...)` chains — pin that all five
30621 // representative input shapes reach the same canonical
30622 // composition output. A regression that narrows any bound
30623 // fails this pin. Sibling to `Sexp::call`'s input-shape pin.
30624 let expected = Sexp::List(vec![
30625 Sexp::symbol("head"),
30626 Sexp::symbol("name"),
30627 Sexp::symbol("a"),
30628 Sexp::symbol("b"),
30629 ]);
30630 // (&str, &str, Vec<Sexp>) — the canonical borrowed shape.
30631 assert_eq!(
30632 Sexp::named_call("head", "name", vec![Sexp::symbol("a"), Sexp::symbol("b")]),
30633 expected,
30634 "Sexp::named_call drifted for (&str, &str, Vec<Sexp>) input",
30635 );
30636 // (String, String, [Sexp; N]) — the owned + array-literal
30637 // shape.
30638 assert_eq!(
30639 Sexp::named_call(
30640 String::from("head"),
30641 String::from("name"),
30642 [Sexp::symbol("a"), Sexp::symbol("b")],
30643 ),
30644 expected,
30645 "Sexp::named_call drifted for (String, String, [Sexp; N]) input",
30646 );
30647 // (&str, &String, .map(...)) — the borrowed-owned-name +
30648 // iterator-map chain shape.
30649 let owned_name = String::from("name");
30650 assert_eq!(
30651 Sexp::named_call(
30652 "head",
30653 &owned_name,
30654 ["a", "b"].iter().map(|s| Sexp::symbol(*s))
30655 ),
30656 expected,
30657 "Sexp::named_call drifted for (&str, &String, iter::map) input",
30658 );
30659 // (&str, &str, iter::empty::<Sexp>()) — the zero-spec-args
30660 // iterator shape, pinning the two-element list emission
30661 // (`(head name)`) via the composition path.
30662 assert_eq!(
30663 Sexp::named_call("head", "name", std::iter::empty::<Sexp>()),
30664 Sexp::List(vec![Sexp::symbol("head"), Sexp::symbol("name")]),
30665 "Sexp::named_call drifted for zero-spec-args iter::empty input",
30666 );
30667 // (&str, &str, once+chain) — the head-then-rest spec-args
30668 // shape a builder decomposing an existing named call form
30669 // via `as_named_call_to` and re-emitting through this
30670 // constructor threads through.
30671 assert_eq!(
30672 Sexp::named_call(
30673 "head",
30674 "name",
30675 std::iter::once(Sexp::symbol("a")).chain([Sexp::symbol("b")]),
30676 ),
30677 expected,
30678 "Sexp::named_call drifted for (&str, &str, once+chain) spec-args input",
30679 );
30680 }
30681
30682 #[test]
30683 fn sexp_named_call_constructor_body_matches_typed_composition_through_call_and_symbol() {
30684 // EXPLICIT COMPOSITION-LAW PIN: `Sexp::named_call(head, name,
30685 // spec_args) == Sexp::call(head, std::iter::once(Sexp::symbol(
30686 // name)).chain(spec_args))` BY DEFINITION — the constructor
30687 // body IS this composition, and the pin exists so a
30688 // regression that in-lines a hand-authored `Sexp::List(vec![
30689 // Sexp::symbol(head), Sexp::symbol(name), spec_args...])`
30690 // body (which would type-check and pass the projection round-
30691 // trips) still surfaces here through the composition-path
30692 // drift. Closes the "the constructor routes through
30693 // `Sexp::call` + `Sexp::symbol`" invariant as a typed pin
30694 // rather than a docstring claim.
30695 let head = "defpoint";
30696 let name = "observability-stack";
30697 let spec_args = vec![
30698 Sexp::keyword("class"),
30699 Sexp::List(vec![Sexp::symbol("Gate"), Sexp::symbol("Observability")]),
30700 ];
30701 assert_eq!(
30702 Sexp::named_call(head, name, spec_args.clone()),
30703 Sexp::call(
30704 head,
30705 std::iter::once(Sexp::symbol(name)).chain(spec_args.iter().cloned()),
30706 ),
30707 "Sexp::named_call body drifted from the Sexp::call ∘ once(Sexp::symbol) ∘ chain composition for head={head:?} name={name:?}",
30708 );
30709 }
30710
30711 #[test]
30712 fn sexp_quote_form_constructor_body_matches_quote_form_wrap_across_every_marker() {
30713 // COMPOSITION-LAW PIN: `Sexp::quote_form(marker, inner) ==
30714 // marker.wrap(inner)` for every `marker: QuoteForm` and every
30715 // representative `inner: Sexp`. Sweeps `QuoteForm::ALL` × a
30716 // representative gallery of inner bodies (atomic-payload,
30717 // residual-Nil, residual-List, quote-family-nested,
30718 // named-call-shaped) so a regression in the constructor body
30719 // that inlined a per-variant match arm — e.g. `match marker {
30720 // QuoteForm::Quote => Sexp::Quote(Box::new(inner)), … }` —
30721 // that drifts one arm's tuple-variant target from the closed-
30722 // set `QuoteForm::wrap` marker-to-wrapper mapping fails
30723 // loudly at the first drifted variant. Pointer-inequality
30724 // safe: `assert_eq!` compares by value, so the pin binds the
30725 // structural composition path rather than any borrowed
30726 // pointer identity.
30727 let inners: Vec<Sexp> = vec![
30728 Sexp::symbol("x"),
30729 Sexp::keyword("k"),
30730 Sexp::string("hello"),
30731 Sexp::int(42),
30732 Sexp::float(2.5),
30733 Sexp::boolean(true),
30734 Sexp::Nil,
30735 Sexp::list(vec![Sexp::symbol("a"), Sexp::int(1)]),
30736 Sexp::quote(Sexp::symbol("nested")),
30737 Sexp::named_call(
30738 "defpoint",
30739 "observability-stack",
30740 std::iter::empty::<Sexp>(),
30741 ),
30742 ];
30743 for marker in QuoteForm::ALL {
30744 for inner in &inners {
30745 assert_eq!(
30746 Sexp::quote_form(marker, inner.clone()),
30747 marker.wrap(inner.clone()),
30748 "Sexp::quote_form body drifted from QuoteForm::wrap composition at marker={marker:?} inner={inner:?}",
30749 );
30750 }
30751 }
30752 }
30753
30754 #[test]
30755 fn sexp_quote_form_constructor_round_trips_through_as_quote_form_for_every_marker() {
30756 // ROUND-TRIP LAW PIN (section-for-retraction with the outer-
30757 // algebra soft-projection): for every `marker: QuoteForm` +
30758 // representative `inner: Sexp`, `Sexp::quote_form(marker,
30759 // inner.clone()).as_quote_form() == Some((marker, &inner))`.
30760 // Proves the (construct, project) pair forms an isomorphism
30761 // between (QuoteForm × Sexp) and the closed-set 4-of-12 quote-
30762 // family carving of the outer `Sexp` algebra — a regression
30763 // that emits a value the projection rejects (unreachable by
30764 // the closed-set structure, since `QuoteForm::wrap` targets a
30765 // quote-family arm exactly) or that drifts the marker
30766 // recovered from the projection (e.g. swapping `Quote` ↔
30767 // `Quasiquote` in the constructor's dispatch) fails loudly
30768 // at the first drifted variant.
30769 for marker in QuoteForm::ALL {
30770 let inner = Sexp::List(vec![Sexp::keyword("body"), Sexp::string("data")]);
30771 let wrapped = Sexp::quote_form(marker, inner.clone());
30772 assert_eq!(
30773 wrapped.as_quote_form(),
30774 Some((marker, &inner)),
30775 "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",
30776 );
30777 }
30778 }
30779
30780 #[test]
30781 fn sexp_quote_form_constructor_composes_with_as_quote_form_marker_and_shape() {
30782 // MARKER-RECOVERING + OUTER-SHAPE COMPOSITION PIN: for every
30783 // `marker: QuoteForm` + representative `inner: Sexp`, the
30784 // constructor's output projects through the marker-only sibling
30785 // `Sexp::as_quote_form_marker` back to the constructor's marker
30786 // AND through the outer-shape projection `Sexp::shape` to the
30787 // canonical `marker.sexp_shape()`. Pins the two independent
30788 // projection compositions simultaneously so a regression that
30789 // reroutes through a non-quote-family arm (which the outer-
30790 // shape lattice would surface as `SexpShape::List` or
30791 // `SexpShape::Nil`) fails BOTH pins at once.
30792 for marker in QuoteForm::ALL {
30793 let inner = Sexp::symbol("body");
30794 let wrapped = Sexp::quote_form(marker, inner.clone());
30795 assert_eq!(
30796 wrapped.as_quote_form_marker(),
30797 Some(marker),
30798 "Sexp::quote_form({marker:?}, _).as_quote_form_marker() drifted from Some({marker:?})",
30799 );
30800 assert_eq!(
30801 wrapped.shape(),
30802 marker.sexp_shape(),
30803 "Sexp::quote_form({marker:?}, _).shape() drifted from {marker:?}.sexp_shape()",
30804 );
30805 }
30806 }
30807
30808 #[test]
30809 fn sexp_quote_form_constructor_specializes_to_each_per_variant_sibling() {
30810 // PER-VARIANT RESTRICTION LAW PIN: the four per-variant
30811 // siblings ARE the marker-driven parent specialized on a
30812 // compile-time-known marker — `Sexp::quote_form(QuoteForm::X,
30813 // inner) == Sexp::x_variant(inner)` for every X ∈
30814 // {Quote, Quasiquote, Unquote, UnquoteSplice}. Any regression
30815 // that drifts the marker-driven parent from its per-variant
30816 // siblings' single canonical composition site
30817 // (`QuoteForm::X.wrap(inner)`) fails at the first drifted
30818 // variant.
30819 let inner = Sexp::symbol("body");
30820 assert_eq!(
30821 Sexp::quote_form(QuoteForm::Quote, inner.clone()),
30822 Sexp::quote(inner.clone()),
30823 "Sexp::quote_form(QuoteForm::Quote, _) drifted from Sexp::quote(_)",
30824 );
30825 assert_eq!(
30826 Sexp::quote_form(QuoteForm::Quasiquote, inner.clone()),
30827 Sexp::quasiquote(inner.clone()),
30828 "Sexp::quote_form(QuoteForm::Quasiquote, _) drifted from Sexp::quasiquote(_)",
30829 );
30830 assert_eq!(
30831 Sexp::quote_form(QuoteForm::Unquote, inner.clone()),
30832 Sexp::unquote(inner.clone()),
30833 "Sexp::quote_form(QuoteForm::Unquote, _) drifted from Sexp::unquote(_)",
30834 );
30835 assert_eq!(
30836 Sexp::quote_form(QuoteForm::UnquoteSplice, inner.clone()),
30837 Sexp::unquote_splice(inner),
30838 "Sexp::quote_form(QuoteForm::UnquoteSplice, _) drifted from Sexp::unquote_splice(_)",
30839 );
30840 }
30841
30842 #[test]
30843 fn sexp_quote_form_constructor_targets_matching_tuple_variant_for_every_marker() {
30844 // TUPLE-VARIANT-TARGET PIN: `Sexp::quote_form(marker, inner)`
30845 // must be structurally equal to `Sexp::X(Box::new(inner))` for
30846 // the X matching the marker — pinned per variant against the
30847 // hand-authored tuple-variant literal so a regression that
30848 // reroutes the wrap through an off-by-one closed-set match
30849 // (e.g. `QuoteForm::Quote → Sexp::Quasiquote`) surfaces at
30850 // this shape pin even when the round-trip law happens to
30851 // still project through the projection sibling (it wouldn't —
30852 // but the pin gives a distinct, tuple-variant-anchored
30853 // failure signature). Sibling-shape lift to the same-anchor
30854 // pin the `QuoteForm::wrap` inner algebra already carries.
30855 let inner = Sexp::string("payload");
30856 assert_eq!(
30857 Sexp::quote_form(QuoteForm::Quote, inner.clone()),
30858 Sexp::Quote(Box::new(inner.clone())),
30859 "Sexp::quote_form(QuoteForm::Quote, _) drifted from Sexp::Quote(Box::new(_)) canonical tuple-variant shape",
30860 );
30861 assert_eq!(
30862 Sexp::quote_form(QuoteForm::Quasiquote, inner.clone()),
30863 Sexp::Quasiquote(Box::new(inner.clone())),
30864 "Sexp::quote_form(QuoteForm::Quasiquote, _) drifted from Sexp::Quasiquote(Box::new(_)) canonical tuple-variant shape",
30865 );
30866 assert_eq!(
30867 Sexp::quote_form(QuoteForm::Unquote, inner.clone()),
30868 Sexp::Unquote(Box::new(inner.clone())),
30869 "Sexp::quote_form(QuoteForm::Unquote, _) drifted from Sexp::Unquote(Box::new(_)) canonical tuple-variant shape",
30870 );
30871 assert_eq!(
30872 Sexp::quote_form(QuoteForm::UnquoteSplice, inner.clone()),
30873 Sexp::UnquoteSplice(Box::new(inner)),
30874 "Sexp::quote_form(QuoteForm::UnquoteSplice, _) drifted from Sexp::UnquoteSplice(Box::new(_)) canonical tuple-variant shape",
30875 );
30876 }
30877
30878 #[test]
30879 fn sexp_unquote_form_constructor_body_matches_unquote_form_wrap_across_every_marker() {
30880 // COMPOSITION-LAW PIN: `Sexp::unquote_form(marker, inner) ==
30881 // marker.wrap(inner)` for every `marker: UnquoteForm` and every
30882 // representative `inner: Sexp`. Sweeps `UnquoteForm::ALL` × a
30883 // representative gallery of inner bodies (atomic-payload,
30884 // residual-Nil, residual-List, quote-family-nested, unquote-
30885 // subset-nested, named-call-shaped) so a regression that
30886 // inlined a per-variant match arm — e.g. `match marker {
30887 // UnquoteForm::Unquote => Sexp::Unquote(Box::new(inner)),
30888 // UnquoteForm::Splice => Sexp::UnquoteSplice(Box::new(inner)) }`
30889 // — that drifts one arm's tuple-variant target from the closed-
30890 // set `UnquoteForm::wrap` marker-to-wrapper mapping fails
30891 // loudly at the first drifted variant.
30892 let inners: Vec<Sexp> = vec![
30893 Sexp::symbol("x"),
30894 Sexp::keyword("k"),
30895 Sexp::string("hello"),
30896 Sexp::int(42),
30897 Sexp::float(2.5),
30898 Sexp::boolean(true),
30899 Sexp::Nil,
30900 Sexp::list(vec![Sexp::symbol("a"), Sexp::int(1)]),
30901 Sexp::quote(Sexp::symbol("nested")),
30902 Sexp::unquote(Sexp::symbol("subnested")),
30903 Sexp::named_call(
30904 "defpoint",
30905 "observability-stack",
30906 std::iter::empty::<Sexp>(),
30907 ),
30908 ];
30909 for marker in UnquoteForm::ALL {
30910 for inner in &inners {
30911 assert_eq!(
30912 Sexp::unquote_form(marker, inner.clone()),
30913 marker.wrap(inner.clone()),
30914 "Sexp::unquote_form body drifted from UnquoteForm::wrap composition at marker={marker:?} inner={inner:?}",
30915 );
30916 }
30917 }
30918 }
30919
30920 #[test]
30921 fn sexp_unquote_form_constructor_round_trips_through_as_unquote_for_every_marker() {
30922 // ROUND-TRIP LAW PIN (section-for-retraction with the outer-
30923 // algebra soft-projection): for every `marker: UnquoteForm` +
30924 // representative `inner: Sexp`, `Sexp::unquote_form(marker,
30925 // inner.clone()).as_unquote() == Some((marker, &inner))`.
30926 // Proves the (construct, project) pair forms an isomorphism
30927 // between (UnquoteForm × Sexp) and the closed-set 2-of-12
30928 // template-substitution subset carving of the outer `Sexp`
30929 // algebra — a regression that emits a value the projection
30930 // rejects (e.g. drifting to a non-substitution quote-family
30931 // arm like `Sexp::Quote`, which `as_unquote` filters out via
30932 // `QuoteForm::as_unquote_form`) or that drifts the marker
30933 // recovered from the projection (e.g. swapping `Unquote` ↔
30934 // `Splice`) fails loudly at the first drifted variant.
30935 for marker in UnquoteForm::ALL {
30936 let inner = Sexp::List(vec![Sexp::keyword("body"), Sexp::string("data")]);
30937 let wrapped = Sexp::unquote_form(marker, inner.clone());
30938 assert_eq!(
30939 wrapped.as_unquote(),
30940 Some((marker, &inner)),
30941 "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",
30942 );
30943 }
30944 }
30945
30946 #[test]
30947 fn sexp_unquote_form_constructor_composes_with_as_unquote_form_and_shape() {
30948 // MARKER-RECOVERING + OUTER-SHAPE COMPOSITION PIN: for every
30949 // `marker: UnquoteForm` + representative `inner: Sexp`, the
30950 // constructor's output projects through the marker-only sibling
30951 // `Sexp::as_unquote_form` back to the constructor's marker AND
30952 // through the outer-shape projection `Sexp::shape` to the
30953 // canonical `marker.sexp_shape()`. Pins the two independent
30954 // projection compositions simultaneously so a regression that
30955 // reroutes through a non-substitution quote-family arm
30956 // (`SexpShape::Quote` / `SexpShape::Quasiquote`) or through a
30957 // non-quote-family arm (`SexpShape::List` / `SexpShape::Nil`)
30958 // fails BOTH pins at once.
30959 for marker in UnquoteForm::ALL {
30960 let inner = Sexp::symbol("body");
30961 let wrapped = Sexp::unquote_form(marker, inner.clone());
30962 assert_eq!(
30963 wrapped.as_unquote_form(),
30964 Some(marker),
30965 "Sexp::unquote_form({marker:?}, _).as_unquote_form() drifted from Some({marker:?})",
30966 );
30967 assert_eq!(
30968 wrapped.shape(),
30969 marker.sexp_shape(),
30970 "Sexp::unquote_form({marker:?}, _).shape() drifted from {marker:?}.sexp_shape()",
30971 );
30972 }
30973 }
30974
30975 #[test]
30976 fn sexp_unquote_form_constructor_routes_through_superset_quote_form_via_to_quote_form() {
30977 // SUPERSET-ROUTING COMPOSITION-LAW PIN: for every `marker:
30978 // UnquoteForm` + representative `inner: Sexp`, `Sexp::unquote_form(
30979 // marker, inner) == Sexp::quote_form(marker.to_quote_form(),
30980 // inner)`. The subset-algebra construct routes through the
30981 // SAME closed-set `QuoteForm::wrap` composition site the
30982 // superset construct routes through — threaded via the typed
30983 // 2-of-4 subset → superset projection `UnquoteForm::to_quote_form`.
30984 // A regression that re-implements the subset construct on a
30985 // parallel dispatch table (rather than composing through the
30986 // superset construct's composition site) can still project
30987 // through `as_unquote` correctly on the round-trip pin above,
30988 // but will fail this pin because the constructed values compare
30989 // equal only when both routes bind at the same closed-set
30990 // `QuoteForm::wrap` arm. Structural sibling of the composition
30991 // law `UnquoteForm::wrap` itself carries at ast.rs:2469 —
30992 // `self.to_quote_form().wrap(inner)`.
30993 for marker in UnquoteForm::ALL {
30994 let inner = Sexp::List(vec![Sexp::symbol("outer"), Sexp::int(7)]);
30995 assert_eq!(
30996 Sexp::unquote_form(marker, inner.clone()),
30997 Sexp::quote_form(marker.to_quote_form(), inner.clone()),
30998 "Sexp::unquote_form({marker:?}, _) drifted from Sexp::quote_form({:?}, _) — subset-construct did not route through superset-construct via UnquoteForm::to_quote_form",
30999 marker.to_quote_form(),
31000 );
31001 }
31002 }
31003
31004 #[test]
31005 fn sexp_unquote_form_constructor_specializes_to_each_per_variant_sibling() {
31006 // PER-VARIANT RESTRICTION LAW PIN: the two per-variant siblings
31007 // ARE the marker-driven parent specialized on a compile-time-
31008 // known subset marker — `Sexp::unquote_form(UnquoteForm::X,
31009 // inner) == Sexp::x_variant(inner)` for every X ∈ {Unquote,
31010 // Splice}. Any regression that drifts the marker-driven parent
31011 // from its per-variant siblings' single canonical composition
31012 // site (`UnquoteForm::X.wrap(inner)` → `QuoteForm::X.wrap(inner)`)
31013 // fails at the first drifted variant.
31014 let inner = Sexp::symbol("body");
31015 assert_eq!(
31016 Sexp::unquote_form(UnquoteForm::Unquote, inner.clone()),
31017 Sexp::unquote(inner.clone()),
31018 "Sexp::unquote_form(UnquoteForm::Unquote, _) drifted from Sexp::unquote(_)",
31019 );
31020 assert_eq!(
31021 Sexp::unquote_form(UnquoteForm::Splice, inner.clone()),
31022 Sexp::unquote_splice(inner),
31023 "Sexp::unquote_form(UnquoteForm::Splice, _) drifted from Sexp::unquote_splice(_)",
31024 );
31025 }
31026
31027 #[test]
31028 fn sexp_unquote_form_constructor_targets_matching_tuple_variant_for_every_marker() {
31029 // TUPLE-VARIANT-TARGET PIN: `Sexp::unquote_form(marker, inner)`
31030 // must be structurally equal to `Sexp::X(Box::new(inner))` for
31031 // the X matching the subset marker (`Unquote → Sexp::Unquote`,
31032 // `Splice → Sexp::UnquoteSplice`) — pinned per variant against
31033 // the hand-authored tuple-variant literal so a regression that
31034 // reroutes the wrap through an off-by-one closed-set match
31035 // (e.g. `UnquoteForm::Unquote → Sexp::UnquoteSplice`, or the
31036 // subset→superset projection drifting `UnquoteForm::Unquote →
31037 // QuoteForm::Quote` inside `to_quote_form`) surfaces at this
31038 // shape pin with a distinct, tuple-variant-anchored failure
31039 // signature. Sibling-shape lift to the same-anchor pin the
31040 // `QuoteForm::wrap` inner algebra already carries on the
31041 // superset 4-of-4 arms.
31042 let inner = Sexp::string("payload");
31043 assert_eq!(
31044 Sexp::unquote_form(UnquoteForm::Unquote, inner.clone()),
31045 Sexp::Unquote(Box::new(inner.clone())),
31046 "Sexp::unquote_form(UnquoteForm::Unquote, _) drifted from Sexp::Unquote(Box::new(_)) canonical tuple-variant shape",
31047 );
31048 assert_eq!(
31049 Sexp::unquote_form(UnquoteForm::Splice, inner.clone()),
31050 Sexp::UnquoteSplice(Box::new(inner)),
31051 "Sexp::unquote_form(UnquoteForm::Splice, _) drifted from Sexp::UnquoteSplice(Box::new(_)) canonical tuple-variant shape",
31052 );
31053 }
31054
31055 // ── `Atom::KEYWORD_MARKER` — the canonical `:` prefix routed through
31056 // the four Keyword-round-trip sites (reader-entry classifier, Lisp
31057 // canonical Display, JSON canonical projection, iac-forge canonical
31058 // projection). Pins the constant value AND the four sites' composition
31059 // through it so a regression that re-inlines any single site's byte
31060 // literal drifts against these pins even when the rendered bytes still
31061 // agree at that site.
31062
31063 #[test]
31064 fn atom_keyword_marker_projects_canonical_colon_byte() {
31065 assert_eq!(
31066 Atom::KEYWORD_MARKER,
31067 ":",
31068 "KEYWORD_MARKER byte drifted from the substrate-canonical `:` \
31069 prefix — the reader-round-trip contract at Self::from_lexeme \
31070 + fmt::Display for Atom + Self::to_json + \
31071 Self::to_iac_forge_sexpr all bind to this one constant.",
31072 );
31073 }
31074
31075 #[test]
31076 fn atom_display_keyword_arm_routes_through_keyword_marker_constant() {
31077 for name in ["parent", "class", "intent", "x", ""] {
31078 let rendered = Atom::keyword(name).to_string();
31079 let expected = format!("{}{name}", Atom::KEYWORD_MARKER);
31080 assert_eq!(
31081 rendered, expected,
31082 "fmt::Display for Atom's Keyword arm drifted from the \
31083 KEYWORD_MARKER composition at name={name:?}",
31084 );
31085 }
31086 }
31087
31088 #[test]
31089 fn atom_to_json_keyword_arm_routes_through_keyword_marker_constant() {
31090 for name in ["parent", "class", "intent", "x", ""] {
31091 let projected = Atom::keyword(name).to_json();
31092 let expected = serde_json::Value::String(format!("{}{name}", Atom::KEYWORD_MARKER));
31093 assert_eq!(
31094 projected, expected,
31095 "Atom::to_json's Keyword arm drifted from the \
31096 KEYWORD_MARKER composition at name={name:?}",
31097 );
31098 }
31099 }
31100
31101 #[test]
31102 fn atom_from_lexeme_keyword_classifier_routes_through_keyword_marker_constant() {
31103 for name in ["parent", "class", "intent", "x", ""] {
31104 let lexeme = format!("{}{name}", Atom::KEYWORD_MARKER);
31105 let classified = Atom::from_lexeme(&lexeme);
31106 assert_eq!(
31107 classified,
31108 Atom::keyword(name),
31109 "Atom::from_lexeme's Keyword classifier drifted from the \
31110 KEYWORD_MARKER strip at lexeme={lexeme:?}",
31111 );
31112 }
31113 }
31114
31115 #[test]
31116 fn atom_keyword_marker_closes_reader_display_round_trip_for_every_name() {
31117 // The load-bearing round-trip contract:
31118 // Atom::from_lexeme(&Atom::keyword(name).to_string())
31119 // == Atom::keyword(name)
31120 // Both sides bind to Atom::KEYWORD_MARKER — the reader-entry
31121 // classifier strips it via strip_prefix, the canonical-form
31122 // Display re-emits it via write!. A future refactor that
31123 // silently drifts ONE site's byte (e.g. by re-inlining `":"` at
31124 // Display while migrating `strip_prefix` to a different byte)
31125 // breaks THIS round-trip even when both bytes happen to agree on
31126 // the surface — because the round-trip binds to the composition
31127 // through the constant at BOTH endpoints.
31128 for name in ["parent", "class", "intent", "x", "kebab-cased-name"] {
31129 let a = Atom::keyword(name);
31130 let round_tripped = Atom::from_lexeme(&a.to_string());
31131 assert_eq!(
31132 round_tripped, a,
31133 "keyword round-trip through KEYWORD_MARKER drifted at \
31134 name={name:?}",
31135 );
31136 }
31137 }
31138
31139 // ── `Atom::KEYWORD_MARKER_LEAD` — the canonical `:` LEAD `char`
31140 // of the `Atom::KEYWORD_MARKER` `&'static str` prefix, routed
31141 // through the SEVEN test-surface sites that pre-lift each extracted
31142 // the byte via `Atom::KEYWORD_MARKER.chars().next().expect(_)`
31143 // (or `.unwrap()`) — the Keyword arm of the
31144 // `Sexp::is_bare_atom_boundary` negative sweep AND the six cross-
31145 // axis disjointness pins on the sibling marker-byte algebras
31146 // (`SPLICE_DISCRIMINATOR`, `BOOL_LITERAL_LEAD`, `STR_DELIMITER`,
31147 // `STR_ESCAPE_LEAD`, `LIST_OPEN` / `LIST_CLOSE`, `COMMENT_LEAD`,
31148 // `COMMENT_TERM`). Sibling-shape tests to the `atom_bool_literal_lead_*`
31149 // block below (Bool-family shared LEAD-byte axis) and the
31150 // `atom_str_delimiter_*` / `atom_str_escape_lead_*` blocks below
31151 // (Str-payload delimiter + escape-lead axes) — pins the SAME shape
31152 // on the Keyword-prefix LEAD-byte axis of the closed-set outer
31153 // [`Atom`] algebra.
31154
31155 #[test]
31156 fn atom_keyword_marker_lead_projects_canonical_colon_char() {
31157 // Pins the constant's exact `char` value so a typo (`';'`,
31158 // `'.'`, `'!'`) or an accidental redefinition surfaces
31159 // immediately. Sibling-shape pin to
31160 // `atom_bool_literal_lead_projects_canonical_hash_char`
31161 // (the Bool-family shared LEAD-byte axis) and
31162 // `atom_str_delimiter_projects_canonical_double_quote_char`
31163 // (the Str-payload delimiter axis) — pins the SAME shape on
31164 // the Keyword-prefix LEAD-byte axis of the closed-set outer
31165 // [`Atom`] algebra.
31166 assert_eq!(
31167 Atom::KEYWORD_MARKER_LEAD,
31168 ':',
31169 "KEYWORD_MARKER_LEAD char drifted from the substrate- \
31170 canonical `:` LEAD byte — the seven test-surface sites \
31171 that pre-lift each extracted this byte from \
31172 Atom::KEYWORD_MARKER via `.chars().next().expect(_)` all \
31173 bind to this ONE constant.",
31174 );
31175 }
31176
31177 #[test]
31178 fn atom_keyword_marker_lead_prefixes_keyword_marker() {
31179 // STRUCTURAL ROUND-TRIP CONTRACT: the `&'static str` prefix
31180 // `Atom::KEYWORD_MARKER` starts with the `char` LEAD byte
31181 // `Atom::KEYWORD_MARKER_LEAD` — the projection law that binds
31182 // the two typed constants on the outer [`Atom`] algebra. A
31183 // regression that renames the `&'static str` (e.g. `"#:"` for
31184 // a Racket-compat `#:name` keyword-arg port) OR the `char`
31185 // (e.g. to `'.'` for a dotted-path prefix) without updating the
31186 // other fails HERE — the structural invariant that
31187 // `KEYWORD_MARKER_LEAD` IS the lead byte of `KEYWORD_MARKER`
31188 // is what makes the seven consumer sites safe to route
31189 // through the `char` constant instead of the `&'static str`
31190 // projection. Sibling-shape pin to
31191 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`
31192 // on the Bool-family axis: where that pin sweeps every `b:
31193 // bool` spelling to prove BOTH spellings share the lead byte,
31194 // this pin binds the one-char `KEYWORD_MARKER` prefix to its
31195 // projected lead byte via a single `starts_with` call.
31196 //
31197 // Load-bearing: this pin IS the "the two typed constants
31198 // project onto each other" invariant that lets the seven
31199 // consumer sites bind to the `char` constant instead of the
31200 // `.chars().next().expect(_)` chain on the `&'static str`.
31201 assert!(
31202 Atom::KEYWORD_MARKER.starts_with(Atom::KEYWORD_MARKER_LEAD),
31203 "Atom::KEYWORD_MARKER `{}` does NOT start with \
31204 Atom::KEYWORD_MARKER_LEAD `{:?}` — the two typed constants \
31205 have drifted apart on the closed-set outer [`Atom`] \
31206 algebra; the seven consumer sites that route through the \
31207 `char` constant instead of the `&'static str` projection \
31208 would silently disagree with the reader's actual `:foo` \
31209 classification.",
31210 Atom::KEYWORD_MARKER,
31211 Atom::KEYWORD_MARKER_LEAD,
31212 );
31213 }
31214
31215 #[test]
31216 fn atom_keyword_marker_lead_distinct_from_every_other_algebra_marker() {
31217 // CROSS-AXIS DISJOINTNESS PIN: `Atom::KEYWORD_MARKER_LEAD`
31218 // MUST NOT alias any sibling outer-marker `char` on the
31219 // substrate's other closed-set algebras — the Str-payload
31220 // delimiter (`Atom::STR_DELIMITER`), Str-payload escape lead
31221 // (`Atom::STR_ESCAPE_LEAD`), Bool-family shared lead
31222 // (`Atom::BOOL_LITERAL_LEAD`), the paired list delimiters
31223 // (`Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`), the paired line-
31224 // comment delimiters (`Sexp::COMMENT_LEAD` /
31225 // `Sexp::COMMENT_TERM`), every `QuoteForm::lead_char`
31226 // projection, AND `QuoteForm::SPLICE_DISCRIMINATOR`. A
31227 // collision would silently break the reader's outer dispatch:
31228 // a `:`-prefixed bare atom `:foo` would collide with whichever
31229 // marker it aliased. Sibling-shape pin to
31230 // `atom_bool_literal_lead_distinct_from_every_other_algebra_marker`
31231 // (the Bool-family LEAD-byte axis) — pins the SAME shape at
31232 // the Keyword-prefix LEAD-byte axis. A future outer-marker
31233 // extension that collided with `':'` fails HERE at the cross-
31234 // axis enumeration.
31235 assert_ne!(
31236 Atom::KEYWORD_MARKER_LEAD,
31237 Atom::STR_DELIMITER,
31238 "KEYWORD_MARKER_LEAD collides with STR_DELIMITER — a bare \
31239 `:foo` would ambiguously begin a keyword AND open a \
31240 string.",
31241 );
31242 assert_ne!(
31243 Atom::KEYWORD_MARKER_LEAD,
31244 Atom::STR_ESCAPE_LEAD,
31245 "KEYWORD_MARKER_LEAD collides with STR_ESCAPE_LEAD — the \
31246 reader's Str-escape lead byte would alias the Keyword- \
31247 prefix lead byte.",
31248 );
31249 assert_ne!(
31250 Atom::KEYWORD_MARKER_LEAD,
31251 Atom::BOOL_LITERAL_LEAD,
31252 "KEYWORD_MARKER_LEAD collides with BOOL_LITERAL_LEAD — a \
31253 bare `:foo` would ambiguously begin a keyword AND \
31254 classify as a Bool.",
31255 );
31256 assert_ne!(
31257 Atom::KEYWORD_MARKER_LEAD,
31258 Sexp::LIST_OPEN,
31259 "KEYWORD_MARKER_LEAD collides with LIST_OPEN — a bare \
31260 `:foo` would ambiguously begin a keyword AND open a list.",
31261 );
31262 assert_ne!(
31263 Atom::KEYWORD_MARKER_LEAD,
31264 Sexp::LIST_CLOSE,
31265 "KEYWORD_MARKER_LEAD collides with LIST_CLOSE — a bare \
31266 `:foo` would ambiguously begin a keyword AND close a list.",
31267 );
31268 assert_ne!(
31269 Atom::KEYWORD_MARKER_LEAD,
31270 Sexp::COMMENT_LEAD,
31271 "KEYWORD_MARKER_LEAD collides with COMMENT_LEAD — a bare \
31272 `:foo` would ambiguously begin a keyword AND begin a \
31273 comment.",
31274 );
31275 assert_ne!(
31276 Atom::KEYWORD_MARKER_LEAD,
31277 Sexp::COMMENT_TERM,
31278 "KEYWORD_MARKER_LEAD collides with COMMENT_TERM — the \
31279 reader's line-comment discard loop would terminate on \
31280 the SAME byte the from_lexeme keyword-prefix arm binds to.",
31281 );
31282 for qf in QuoteForm::ALL {
31283 assert_ne!(
31284 Atom::KEYWORD_MARKER_LEAD,
31285 qf.lead_char(),
31286 "KEYWORD_MARKER_LEAD collides with QuoteForm::{qf:?}'s \
31287 lead_char — a bare `:foo` would ambiguously begin a \
31288 keyword AND begin a quote-family prefix.",
31289 );
31290 }
31291 assert_ne!(
31292 Atom::KEYWORD_MARKER_LEAD,
31293 QuoteForm::SPLICE_DISCRIMINATOR,
31294 "KEYWORD_MARKER_LEAD collides with SPLICE_DISCRIMINATOR — \
31295 the reader's `,@` splice-promotion peek byte would alias \
31296 the Keyword-prefix lead byte.",
31297 );
31298 }
31299
31300 // ── `Atom::keyword_qualified` — the ONE projection composing
31301 // `Atom::KEYWORD_MARKER` with a bare keyword name across the three
31302 // canonical-rendering Keyword-arm sites (JSON, iac-forge, Lisp
31303 // Display). Pins the composition + the round-trip law with
31304 // `Atom::from_lexeme` + the path-uniformity at every routed site so
31305 // a regression that re-inlines any single site's `format!("{}{s}",
31306 // KEYWORD_MARKER)` composition drifts against these pins even when
31307 // the rendered bytes still agree at that site.
31308
31309 #[test]
31310 fn atom_keyword_qualified_composes_keyword_marker_with_bare_name() {
31311 // BYTE-COMPOSITION CONTRACT: `Atom::keyword_qualified(name)`
31312 // renders exactly `Atom::KEYWORD_MARKER ++ name` for every
31313 // bare-name input — the ONE typed composition of the Keyword-
31314 // prefix constant with a bare-name payload on the [`Atom`]
31315 // algebra. Sibling-shape pin to
31316 // `atom_bool_literal_projects_canonical_scheme_spellings`
31317 // (the Bool-family canonical-rendering axis): where that pin
31318 // sweeps the CLOSED `bool` domain to its canonical spellings,
31319 // this pin sweeps a representative set of bare-name inputs
31320 // (empty, single-char, dashed, dotted, unicode) through the
31321 // Keyword-family projection to prove the composition holds
31322 // uniformly over the open-set bare-name domain.
31323 for name in [
31324 "",
31325 "x",
31326 "class",
31327 "parent",
31328 "point-type",
31329 "kebab-case-name",
31330 "dotted.path",
31331 "with_underscore",
31332 "α",
31333 ] {
31334 let expected = format!("{}{name}", Atom::KEYWORD_MARKER);
31335 assert_eq!(
31336 Atom::keyword_qualified(name),
31337 expected,
31338 "Atom::keyword_qualified({name:?}) drifted from the \
31339 substrate-canonical composition \
31340 `Atom::KEYWORD_MARKER ++ name` — the three canonical- \
31341 rendering Keyword-arm sites (to_json, to_iac_forge_sexpr, \
31342 Display) all bind to this ONE projection.",
31343 );
31344 }
31345 }
31346
31347 #[test]
31348 fn atom_keyword_qualified_starts_with_keyword_marker() {
31349 // STRUCTURAL PREFIX CONTRACT: for every bare-name input,
31350 // `Atom::keyword_qualified(name).starts_with(Atom::KEYWORD_MARKER)`.
31351 // The projection MUST begin with the canonical
31352 // [`Atom::KEYWORD_MARKER`] prefix so
31353 // [`Atom::from_lexeme`]'s `s.strip_prefix(Self::KEYWORD_MARKER)`
31354 // classifier gate matches every rendered qualified keyword.
31355 // Sibling-shape pin to
31356 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`
31357 // (the Bool-family shared-lead-byte axis): where that pin
31358 // sweeps every `b: bool` to prove BOTH spellings share the
31359 // lead byte, this pin sweeps every representative bare-name
31360 // to prove EVERY qualified rendering starts with the shared
31361 // Keyword-family prefix.
31362 for name in ["", "x", "class", "parent", "point-type", "α"] {
31363 let qualified = Atom::keyword_qualified(name);
31364 assert!(
31365 qualified.starts_with(Atom::KEYWORD_MARKER),
31366 "Atom::keyword_qualified({name:?}) = {qualified:?} does \
31367 NOT start with Atom::KEYWORD_MARKER `{}` — the \
31368 projection has drifted from its structural prefix \
31369 contract; Atom::from_lexeme's `strip_prefix` classifier \
31370 gate would silently disagree with the rendered \
31371 qualified keyword.",
31372 Atom::KEYWORD_MARKER,
31373 );
31374 }
31375 }
31376
31377 #[test]
31378 fn atom_from_lexeme_inverts_keyword_qualified_on_bare_name() {
31379 // ROUND-TRIP CONTRACT (typed-EXIT rendering ↔ typed-ENTRY
31380 // classification): [`Atom::from_lexeme`] is the LEFT-inverse
31381 // of [`Atom::keyword_qualified`] on the Keyword-payload
31382 // subset — i.e. `Atom::from_lexeme(&Atom::keyword_qualified(n))
31383 // == Atom::Keyword(n.to_owned())` for every bare `name` that
31384 // does NOT itself parse as a Bool spelling, integer, or float
31385 // (the four typed-entry classification arms preceding the
31386 // KEYWORD_MARKER-prefix arm at `from_lexeme`).
31387 //
31388 // A regression that drifts EITHER the composition (this
31389 // projection) OR the classification (from_lexeme's
31390 // strip_prefix arm) surfaces at THIS pin rather than as a
31391 // silent Keyword-round-trip drift at a downstream consumer.
31392 // Sibling-shape pin to
31393 // `atom_from_lexeme_round_trips_through_bool_literal_for_every_bool`
31394 // (the Bool-family round-trip axis) — pins the SAME shape at
31395 // the Keyword-family round-trip axis of the closed-set outer
31396 // [`Atom`] algebra.
31397 for name in [
31398 "x",
31399 "class",
31400 "parent",
31401 "point-type",
31402 "kebab-case-name",
31403 "dotted.path",
31404 "with_underscore",
31405 "α",
31406 ] {
31407 let qualified = Atom::keyword_qualified(name);
31408 let classified = Atom::from_lexeme(&qualified);
31409 assert_eq!(
31410 classified,
31411 Atom::Keyword(name.to_owned()),
31412 "Atom::from_lexeme({qualified:?}) drifted from \
31413 Atom::Keyword({name:?}) — the round-trip law between \
31414 Atom::keyword_qualified (typed-EXIT canonical \
31415 rendering) and Atom::from_lexeme's strip_prefix arm \
31416 (typed-ENTRY classification) has broken on the \
31417 Keyword-family axis of the closed-set outer [`Atom`] \
31418 algebra.",
31419 );
31420 }
31421 }
31422
31423 #[test]
31424 fn atom_display_keyword_arm_agrees_with_keyword_qualified_bytes() {
31425 // DISPLAY BYTE-IDENTITY PIN: [`fmt::Display for Atom`]'s
31426 // [`Atom::Keyword`] arm keeps its allocation-free
31427 // `write!(f, "{}{s}", Self::KEYWORD_MARKER)` composition
31428 // (Display is called at every canonical-rendering surface
31429 // that composes via `format!("{sexp}")` — avoiding the
31430 // allocation is load-bearing on tokenizer-adjacent hot paths
31431 // like `checks.lisp`'s exhaustive `defcheck` rendering) but
31432 // MUST produce byte-identical output to
31433 // [`Atom::keyword_qualified`]. Otherwise the three canonical-
31434 // rendering surfaces (Display, to_json, to_iac_forge_sexpr)
31435 // would silently disagree on the qualified-keyword bytes even
31436 // though two of the three route through the typed projection.
31437 //
31438 // Load-bearing: this pin IS the "Display's inline write! and
31439 // the typed projection agree byte-for-byte" invariant that
31440 // lets Display keep the zero-alloc path while to_json /
31441 // to_iac_forge_sexpr collapse onto the ONE algebra site.
31442 for name in [
31443 "",
31444 "x",
31445 "class",
31446 "parent",
31447 "point-type",
31448 "kebab-case-name",
31449 "α",
31450 ] {
31451 let atom = Atom::Keyword(name.to_owned());
31452 let via_display = atom.to_string();
31453 let via_projection = Atom::keyword_qualified(name);
31454 assert_eq!(
31455 via_display, via_projection,
31456 "fmt::Display for Atom's Keyword arm drifted from \
31457 Atom::keyword_qualified on name {name:?} — the \
31458 write! path and the typed projection have disagreed \
31459 on the qualified-keyword bytes.",
31460 );
31461 }
31462 }
31463
31464 #[test]
31465 fn atom_to_json_keyword_arm_routes_through_keyword_qualified() {
31466 // PATH-UNIFORMITY GUARD: [`Atom::to_json`]'s [`Atom::Keyword`]
31467 // arm's rendered String value MUST equal
31468 // [`Atom::keyword_qualified`] on the same bare name — the
31469 // FIRST of the two routed sites is bound to the typed
31470 // projection. A regression that re-inlines the `format!("{}{s}",
31471 // Self::KEYWORD_MARKER)` composition at this arm without
31472 // updating the projection (or vice versa) fails HERE.
31473 // Sibling-shape pin to
31474 // `atom_to_iac_forge_sexpr_keyword_arm_routes_through_keyword_qualified`
31475 // on the iac-forge canonical-attestation-form axis: where that
31476 // pin binds the iac-forge arm's Symbol payload, this pin binds
31477 // the JSON arm's String payload.
31478 for name in ["", "x", "class", "parent", "α"] {
31479 let atom = Atom::Keyword(name.to_owned());
31480 let via_json = atom.to_json();
31481 let expected = serde_json::Value::String(Atom::keyword_qualified(name));
31482 assert_eq!(
31483 via_json, expected,
31484 "Atom::to_json's Keyword arm drifted from \
31485 Atom::keyword_qualified on name {name:?} — the JSON \
31486 canonical-rendering site has diverged from the typed \
31487 algebra projection.",
31488 );
31489 }
31490 }
31491
31492 // ── `Atom::bool_literal` — the ONE projection routing the closed-set
31493 // `bool` domain through its canonical Scheme spelling across the two
31494 // Bool-round-trip sites (reader-entry classifier, Lisp canonical
31495 // Display). Pins the projection's spellings for BOTH bool values AND
31496 // both sites' composition through it so a regression that re-inlines
31497 // any single site's byte literal drifts against these pins even when
31498 // the rendered bytes still agree at that site.
31499
31500 #[test]
31501 fn atom_bool_literal_projects_canonical_scheme_spellings() {
31502 assert_eq!(
31503 Atom::bool_literal(true),
31504 "#t",
31505 "Atom::bool_literal(true) drifted from the substrate-canonical \
31506 Scheme spelling `#t` — the reader-round-trip contract at \
31507 Self::from_lexeme + fmt::Display for Atom both bind to this \
31508 projection.",
31509 );
31510 assert_eq!(
31511 Atom::bool_literal(false),
31512 "#f",
31513 "Atom::bool_literal(false) drifted from the substrate-canonical \
31514 Scheme spelling `#f` — the reader-round-trip contract at \
31515 Self::from_lexeme + fmt::Display for Atom both bind to this \
31516 projection.",
31517 );
31518 }
31519
31520 #[test]
31521 fn atom_bool_literal_partitions_the_closed_bool_domain_injectively() {
31522 // Sanity pin on the projection's shape: the two spellings partition
31523 // the closed-set `bool` domain injectively (`true` and `false` do
31524 // NOT alias to the same byte) — otherwise `from_lexeme` on either
31525 // spelling would classify to a single Bool variant, silently
31526 // collapsing the typed distinction at the reader-entry boundary.
31527 assert_ne!(
31528 Atom::bool_literal(true),
31529 Atom::bool_literal(false),
31530 "Atom::bool_literal collapsed the closed-set `bool` domain — \
31531 both bools projected to the same Scheme spelling, breaking \
31532 the reader-entry classifier's injection property.",
31533 );
31534 }
31535
31536 #[test]
31537 fn atom_display_bool_arm_routes_through_bool_literal_projection() {
31538 for b in [true, false] {
31539 let rendered = Atom::boolean(b).to_string();
31540 let expected = Atom::bool_literal(b);
31541 assert_eq!(
31542 rendered, expected,
31543 "fmt::Display for Atom's Bool arm drifted from the \
31544 bool_literal composition at b={b:?}",
31545 );
31546 }
31547 }
31548
31549 #[test]
31550 fn atom_from_lexeme_bool_classifier_routes_through_bool_literal_projection() {
31551 for b in [true, false] {
31552 let lexeme = Atom::bool_literal(b);
31553 let classified = Atom::from_lexeme(lexeme);
31554 assert_eq!(
31555 classified,
31556 Atom::boolean(b),
31557 "Atom::from_lexeme's Bool classifier drifted from the \
31558 bool_literal composition at lexeme={lexeme:?}",
31559 );
31560 }
31561 }
31562
31563 #[test]
31564 fn atom_bool_literal_closes_reader_display_round_trip_for_both_variants() {
31565 // The load-bearing round-trip contract:
31566 // Atom::from_lexeme(&Atom::boolean(b).to_string())
31567 // == Atom::boolean(b)
31568 // Both sides bind to Atom::bool_literal — the reader-entry
31569 // classifier gates on `s == Self::bool_literal(true|false)`, the
31570 // canonical-form Display re-emits it via
31571 // `f.write_str(Self::bool_literal(*b))`. A future refactor that
31572 // silently drifts ONE site's byte (e.g. by re-inlining `"#t"` at
31573 // Display while migrating the classifier gate to a different
31574 // spelling) breaks THIS round-trip even when both bytes happen to
31575 // agree on the surface — because the round-trip binds to the
31576 // composition through the projection at BOTH endpoints.
31577 for b in [true, false] {
31578 let a = Atom::boolean(b);
31579 let round_tripped = Atom::from_lexeme(&a.to_string());
31580 assert_eq!(
31581 round_tripped, a,
31582 "bool round-trip through bool_literal drifted at b={b:?}",
31583 );
31584 }
31585 }
31586
31587 #[test]
31588 fn atom_true_literal_projects_canonical_pound_t_bytes() {
31589 // Pins the exact `"#t"` bytes at the typed constant. A
31590 // regression that drifts the constant (e.g. Common-Lisp-compat
31591 // typo `"T"`, JSON-compat typo `"true"`, Racket-compat typo
31592 // `"#true"`, or an accidental case swap `"#T"`) fails-loudly
31593 // here. This is the single site the substrate's canonical-
31594 // Scheme `true` spelling resolves to; every downstream consumer
31595 // (`Atom::bool_literal`'s `true`-arm, `Atom::from_lexeme`'s
31596 // reader-entry gate, `fmt::Display for Atom`'s `Bool(true)`
31597 // arm, `Atom::BOOL_LITERALS[0]`) routes through this constant.
31598 // Sibling posture to
31599 // `macro_def_head_defmacro_keyword_projects_canonical_defmacro_bytes`
31600 // on the head-keyword algebra.
31601 assert_eq!(
31602 Atom::TRUE_LITERAL,
31603 "#t",
31604 "Atom::TRUE_LITERAL drifted from the substrate-canonical \
31605 Scheme spelling `#t` — the reader-entry classifier at \
31606 Atom::from_lexeme + fmt::Display for Atom + \
31607 Atom::bool_literal's true-arm ALL bind to this constant."
31608 );
31609 }
31610
31611 #[test]
31612 fn atom_false_literal_projects_canonical_pound_f_bytes() {
31613 // Pins the exact `"#f"` bytes at the typed constant. Peer of
31614 // `atom_true_literal_projects_canonical_pound_t_bytes` on the
31615 // `false` element of the closed `bool` domain.
31616 assert_eq!(
31617 Atom::FALSE_LITERAL,
31618 "#f",
31619 "Atom::FALSE_LITERAL drifted from the substrate-canonical \
31620 Scheme spelling `#f` — the reader-entry classifier at \
31621 Atom::from_lexeme + fmt::Display for Atom + \
31622 Atom::bool_literal's false-arm ALL bind to this constant."
31623 );
31624 }
31625
31626 #[test]
31627 fn atom_bool_literal_routes_through_typed_per_variant_constants() {
31628 // PATH-UNIFORMITY: the inherent `Atom::bool_literal(b)` method
31629 // MUST return the per-variant `pub const` byte-for-byte for
31630 // each `b: bool`. A regression that reverts ONE arm to an
31631 // inline `"#t"` / `"#f"` string literal (e.g. a merge-conflict
31632 // resolution that picked the pre-lift form) silently
31633 // reintroduces the ≥2 PRIME-DIRECTIVE trigger the lift
31634 // resolved — this test catches that by pinning each arm's
31635 // return value to the constant, so the two paths (inline vs.
31636 // typed constant) cannot both hold. Sibling posture to
31637 // `macro_def_head_keyword_method_routes_through_typed_constants`
31638 // on the head-keyword algebra.
31639 assert_eq!(
31640 Atom::bool_literal(true),
31641 Atom::TRUE_LITERAL,
31642 "Atom::bool_literal(true) drifted from Atom::TRUE_LITERAL — \
31643 the `true`-arm reverted to an inline literal"
31644 );
31645 assert_eq!(
31646 Atom::bool_literal(false),
31647 Atom::FALSE_LITERAL,
31648 "Atom::bool_literal(false) drifted from Atom::FALSE_LITERAL \
31649 — the `false`-arm reverted to an inline literal"
31650 );
31651 }
31652
31653 #[test]
31654 fn atom_bool_literals_has_expected_cardinality() {
31655 // Cardinality contract: `Self::BOOL_LITERALS.len() == 2` —
31656 // pinned at the declaration site by rustc's forced-arity check
31657 // on `[&'static str; 2]`. This test surfaces the arity as a
31658 // fail-loud runtime pin so a future refactor that switches the
31659 // array type to `&[&'static str]` (dropping the compile-time
31660 // arity forcing) doesn't silently loosen the closed-set
31661 // discipline the family relies on. The `N == 2` is pinned by
31662 // the mathematics of the closed `bool` domain; a future tri-
31663 // valued-logic extension surfaces at THIS pin. Sibling posture
31664 // to `macro_def_head_keywords_has_expected_cardinality` on the
31665 // head-keyword algebra AND to
31666 // `macro_params_lambda_list_keywords_has_expected_cardinality`
31667 // on the CL lambda-list-keyword family.
31668 assert_eq!(
31669 Atom::BOOL_LITERALS.len(),
31670 2,
31671 "Atom::BOOL_LITERALS cardinality drifted from 2 — the \
31672 closed `bool` domain admits exactly two spellings by \
31673 construction; a tri-valued extension surfaces here"
31674 );
31675 }
31676
31677 #[test]
31678 fn atom_bool_literals_align_with_bool_literal_by_index() {
31679 // ALIGNMENT CONTRACT: `Self::BOOL_LITERALS[i] ==
31680 // Self::bool_literal([true, false][i])` element-wise. The
31681 // `[true, false]` sweep order is the canonical declaration
31682 // order every existing sibling test in the crate uses; a
31683 // regression that reorders ONE array without reordering the
31684 // other silently misaligns every `zip([true, false],
31685 // Self::BOOL_LITERALS)` consumer (LSP completion providers,
31686 // metric-label emitters, coverage reporters). Sibling posture
31687 // to `macro_def_head_keywords_align_with_all_by_index` on the
31688 // head-keyword algebra.
31689 for (i, b) in [true, false].iter().enumerate() {
31690 assert_eq!(
31691 Atom::BOOL_LITERALS[i],
31692 Atom::bool_literal(*b),
31693 "Atom::BOOL_LITERALS[{i}] `{kw}` drifted from \
31694 Atom::bool_literal({b:?}) `{via_variant}` — the \
31695 canonical declaration order of the ALL array and the \
31696 bool_literal projection must match element-wise",
31697 kw = Atom::BOOL_LITERALS[i],
31698 via_variant = Atom::bool_literal(*b),
31699 );
31700 }
31701 }
31702
31703 #[test]
31704 fn atom_bool_literals_pairwise_distinct() {
31705 // PAIRWISE DISJOINTNESS: the two `&'static str` spellings on
31706 // the bool-spelling algebra MUST differ so the reader-entry
31707 // classifier's `s == Self::bool_literal(true|false)` cascade
31708 // cannot route both bools through the same arm — otherwise
31709 // `from_lexeme` on either spelling would classify to a single
31710 // Bool variant, silently collapsing the typed distinction at
31711 // the reader-entry boundary. Family-wide sweep over
31712 // `BOOL_LITERALS × BOOL_LITERALS` — supersedes any single
31713 // per-pair assertion and picks up new spellings mechanically
31714 // (should the closed `bool` domain ever extend). Sibling
31715 // posture to `macro_def_head_keywords_pairwise_distinct` on the
31716 // head-keyword algebra AND to
31717 // `atom_bool_literal_partitions_the_closed_bool_domain_injectively`
31718 // on the direct-projection axis.
31719 for (i, a) in Atom::BOOL_LITERALS.iter().enumerate() {
31720 for (j, b) in Atom::BOOL_LITERALS.iter().enumerate() {
31721 if i == j {
31722 continue;
31723 }
31724 assert_ne!(
31725 a, b,
31726 "Atom::BOOL_LITERALS[{i}] `{a}` collides with \
31727 Atom::BOOL_LITERALS[{j}] `{b}` — the reader-entry \
31728 classifier's cascade would route two bools through \
31729 the same arm"
31730 );
31731 }
31732 }
31733 }
31734
31735 #[test]
31736 fn atom_bool_literals_all_route_through_bool_literal_leading_byte() {
31737 // Structural round-trip pin composed with the algebra's
31738 // `Atom::BOOL_LITERAL_LEAD` axis peer: every entry of
31739 // `Self::BOOL_LITERALS` MUST start with `Self::BOOL_LITERAL_LEAD`.
31740 // The lead-byte-prefixes-spelling law from
31741 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`
31742 // sweeps [true, false] through `bool_literal`; THIS test
31743 // sweeps the same invariant over the `BOOL_LITERALS` array
31744 // directly, catching a regression where a per-role constant
31745 // drifts from the shared lead byte (e.g. `FALSE_LITERAL` set
31746 // to `"~f"` while `bool_literal(false)` still returns
31747 // `FALSE_LITERAL`, which would preserve the projection
31748 // contract but break the lead-byte contract). Sibling posture
31749 // to `macro_def_head_keywords_all_round_trip_through_from_str`
31750 // on the head-keyword algebra.
31751 for kw in Atom::BOOL_LITERALS {
31752 assert!(
31753 kw.starts_with(Atom::BOOL_LITERAL_LEAD),
31754 "Atom::BOOL_LITERALS entry `{kw}` does NOT start with \
31755 Atom::BOOL_LITERAL_LEAD ({lead:?}) — the per-role \
31756 constant drifted from the shared lead byte",
31757 lead = Atom::BOOL_LITERAL_LEAD,
31758 );
31759 }
31760 }
31761
31762 // ── `Atom::BOOL_LITERAL_LEAD` — the canonical `'#'` char shared
31763 // across BOTH `Atom::bool_literal` spellings (`"#t"` for `true`,
31764 // `"#f"` for `false`). Sibling-shape tests to the
31765 // `atom_bool_literal_*` block above (the two-char spelling axis
31766 // of the Bool payload) — where those pins bind the two-char
31767 // Scheme spelling of each `bool` payload, THESE tests bind the
31768 // ONE shared lead byte of the two spellings onto the closed-set
31769 // outer [`Atom`] algebra so a hash-prefix reader-family
31770 // extension (`#\char`, `#(vector)`, `#|block-comment|#`, `#;
31771 // datum-comment`) lands at ONE constant on the algebra.
31772
31773 #[test]
31774 fn atom_bool_literal_lead_projects_canonical_hash_char() {
31775 // Pins the constant's exact `char` value so a typo (`'#'`
31776 // vs. `'$'`, `'@'`, or `'!'`) or an accidental redefinition
31777 // surfaces immediately. Sibling-shape pin to
31778 // `atom_str_delimiter_projects_canonical_double_quote_char`
31779 // (Str-delimiter axis) and
31780 // `atom_str_escape_lead_projects_canonical_backslash_char`
31781 // (Str-escape-lead axis) — pins the SAME shape on the
31782 // Bool-family lead-byte axis of the closed-set [`Atom`]
31783 // algebra.
31784 assert_eq!(
31785 Atom::BOOL_LITERAL_LEAD,
31786 '#',
31787 "BOOL_LITERAL_LEAD char drifted from the substrate-\
31788 canonical `#` Bool-family lead byte — the disjointness \
31789 contract at is_bare_atom_boundary's negative sweep AND \
31790 the QuoteForm::SPLICE_DISCRIMINATOR non-collision pin \
31791 both bind to this ONE constant.",
31792 );
31793 }
31794
31795 #[test]
31796 fn atom_bool_literal_lead_prefixes_every_bool_literal_spelling() {
31797 // Structural round-trip pin — the (BOOL_LITERAL_LEAD,
31798 // bool_literal spelling) pairing binds at ONE algebra layer:
31799 // for every `b: bool`, `Atom::bool_literal(b)` MUST start
31800 // with `Atom::BOOL_LITERAL_LEAD`. A regression that drifts
31801 // EITHER the constant OR the two `bool_literal` arms
31802 // surfaces here rather than at a silent bool-family reader
31803 // drift where `#t` / `#f` classify as `Atom::Symbol` instead
31804 // of `Atom::Bool`.
31805 //
31806 // Load-bearing across both `b: bool` values because the
31807 // structural invariant IS "both spellings share the lead
31808 // byte" — sweeps the closed `bool` domain so a hypothetical
31809 // regression that renamed ONE spelling only (e.g. moved
31810 // `"#f"` to `"~f"`) fails at THIS pin even though the
31811 // constant AND the sibling spelling both agreed.
31812 //
31813 // Sibling-shape peer of
31814 // `atom_bool_literal_closes_reader_display_round_trip_for_both_variants`
31815 // one axis over: that test binds the (spelling, typed Bool
31816 // variant) round-trip through the reader/Display; THIS test
31817 // binds the (lead byte, spelling) prefix invariant at the
31818 // atomic-algebra layer directly.
31819 for b in [true, false] {
31820 let spelling = Atom::bool_literal(b);
31821 assert!(
31822 spelling.starts_with(Atom::BOOL_LITERAL_LEAD),
31823 "Atom::bool_literal({b:?}) = {spelling:?} does NOT \
31824 start with BOOL_LITERAL_LEAD ({:?}) — the structural \
31825 invariant \"both bool_literal spellings share the \
31826 lead byte\" broke at b={b:?}",
31827 Atom::BOOL_LITERAL_LEAD,
31828 );
31829 }
31830 }
31831
31832 #[test]
31833 fn atom_bool_literal_lead_distinct_from_every_other_algebra_marker() {
31834 // Cross-axis disjointness pin: BOOL_LITERAL_LEAD's byte MUST
31835 // NOT alias any other closed-set outer-marker byte the
31836 // reader's tokenizer specialises on — otherwise a bare `#t`
31837 // / `#f` lexeme would ambiguously route through the
31838 // colliding arm AND the bool classifier. Sibling-shape pin
31839 // to `sexp_comment_term_distinct_from_every_non_whitespace_algebra_marker`
31840 // (COMMENT_TERM axis) — enumerates every closed-set outer-
31841 // marker char AND asserts non-collision on the Bool-family
31842 // lead-byte axis.
31843 //
31844 // The enumerated set spans THREE type namespaces and every
31845 // canonical reader byte the substrate exposes:
31846 // * `Sexp::LIST_OPEN` / `LIST_CLOSE` / `COMMENT_LEAD` /
31847 // `COMMENT_TERM` — outer-structural + reader-discard
31848 // * `Atom::STR_DELIMITER` / `STR_ESCAPE_LEAD` — atomic-
31849 // payload delimiter + escape lead
31850 // * `Atom::KEYWORD_MARKER`'s lead byte — atomic-payload
31851 // prefix marker
31852 // * every `QuoteForm::lead_char` projection AND
31853 // `QuoteForm::SPLICE_DISCRIMINATOR` — homoiconic
31854 // prefix + splice-discriminator
31855 assert_ne!(
31856 Atom::BOOL_LITERAL_LEAD,
31857 Sexp::LIST_OPEN,
31858 "BOOL_LITERAL_LEAD collides with LIST_OPEN — a bare `#t` \
31859 would ambiguously open a list AND classify as a Bool.",
31860 );
31861 assert_ne!(
31862 Atom::BOOL_LITERAL_LEAD,
31863 Sexp::LIST_CLOSE,
31864 "BOOL_LITERAL_LEAD collides with LIST_CLOSE — a bare `#t` \
31865 would ambiguously close a list AND classify as a Bool.",
31866 );
31867 assert_ne!(
31868 Atom::BOOL_LITERAL_LEAD,
31869 Sexp::COMMENT_LEAD,
31870 "BOOL_LITERAL_LEAD collides with COMMENT_LEAD — a bare \
31871 `#t` would ambiguously open a line comment AND classify \
31872 as a Bool.",
31873 );
31874 assert_ne!(
31875 Atom::BOOL_LITERAL_LEAD,
31876 Sexp::COMMENT_TERM,
31877 "BOOL_LITERAL_LEAD collides with COMMENT_TERM — the \
31878 reader's line-comment discard-loop terminator would \
31879 alias the Bool-family lead byte.",
31880 );
31881 assert_ne!(
31882 Atom::BOOL_LITERAL_LEAD,
31883 Atom::STR_DELIMITER,
31884 "BOOL_LITERAL_LEAD collides with STR_DELIMITER — a bare \
31885 `#t` would ambiguously open a string AND classify as a \
31886 Bool.",
31887 );
31888 assert_ne!(
31889 Atom::BOOL_LITERAL_LEAD,
31890 Atom::STR_ESCAPE_LEAD,
31891 "BOOL_LITERAL_LEAD collides with STR_ESCAPE_LEAD — the \
31892 reader's Str-escape lead byte would alias the Bool-\
31893 family lead byte.",
31894 );
31895 assert_ne!(
31896 Atom::BOOL_LITERAL_LEAD,
31897 Atom::KEYWORD_MARKER_LEAD,
31898 "BOOL_LITERAL_LEAD collides with KEYWORD_MARKER_LEAD — a \
31899 bare `#t` would ambiguously begin a keyword AND classify \
31900 as a Bool.",
31901 );
31902 for qf in QuoteForm::ALL {
31903 assert_ne!(
31904 Atom::BOOL_LITERAL_LEAD,
31905 qf.lead_char(),
31906 "BOOL_LITERAL_LEAD collides with QuoteForm::{qf:?}'s \
31907 lead_char — a bare `#t` would ambiguously begin a \
31908 quote-family prefix AND classify as a Bool.",
31909 );
31910 }
31911 assert_ne!(
31912 Atom::BOOL_LITERAL_LEAD,
31913 QuoteForm::SPLICE_DISCRIMINATOR,
31914 "BOOL_LITERAL_LEAD collides with SPLICE_DISCRIMINATOR — \
31915 the reader's `,@` splice-promotion peek byte would alias \
31916 the Bool-family lead byte.",
31917 );
31918 }
31919
31920 // ── `Atom::STR_DELIMITER` — the canonical `"` char routed through
31921 // the four Str-round-trip sites inside `crate::reader::tokenize`
31922 // (string-opening arm, escape-handler self-escape mapping, string-
31923 // closing arm, bare-atom terminator disjunct). Pins the constant
31924 // value AND the composition against a byte-identical drift at any
31925 // one of the four sites. Sibling-shape tests to the
31926 // `atom_keyword_marker_*` block above (Keyword prefix axis) and
31927 // the `atom_bool_literal_*` block above (Bool spelling axis).
31928
31929 #[test]
31930 fn atom_str_delimiter_projects_canonical_double_quote_char() {
31931 // Pins the constant's exact `char` value so a typo (`'\''`,
31932 // `'\`'`, `'#'`) or an accidental redefinition surfaces
31933 // immediately. Sibling-shape pin to
31934 // `atom_keyword_marker_projects_canonical_colon_byte` (the
31935 // Keyword prefix axis) and
31936 // `atom_bool_literal_projects_canonical_scheme_spellings`
31937 // (the Bool spelling axis) — pins the SAME shape on the
31938 // Str-delimiter axis of the closed-set [`Atom`] algebra.
31939 assert_eq!(
31940 Atom::STR_DELIMITER,
31941 '"',
31942 "STR_DELIMITER char drifted from the substrate-canonical `\"` \
31943 delimiter — the reader-round-trip contract at \
31944 crate::reader::tokenize (string-opening arm, escape-handler \
31945 self-escape, string-closing arm, bare-atom terminator \
31946 disjunct) all bind to this ONE constant.",
31947 );
31948 }
31949
31950 #[test]
31951 fn atom_str_delimiter_distinct_from_every_other_atom_marker() {
31952 // Cross-axis disjointness pin: the Str-delimiter byte
31953 // (`Atom::STR_DELIMITER`) must NOT alias the Keyword-marker
31954 // prefix byte (`Atom::KEYWORD_MARKER`) or the two Bool-
31955 // literal spellings (`Atom::bool_literal(true|false)`) —
31956 // otherwise a bare `:foo` or `#t` lexeme starting with the
31957 // Str-delimiter would ambiguously route through the reader's
31958 // `Token::Str` branch AND the `Token::Atom` classifier's
31959 // Keyword / Bool arms in `Atom::from_lexeme`. Guards the
31960 // structural disjointness of the atomic-payload marker
31961 // family on the closed-set [`Atom`] algebra so a future
31962 // marker-swap that accidentally collides two axes surfaces
31963 // at this pin rather than as a silent reader misclassification.
31964 //
31965 // `KEYWORD_MARKER` is `":"` (single-char) — its LEAD `char`
31966 // lives at `Atom::KEYWORD_MARKER_LEAD` on the closed-set outer
31967 // [`Atom`] algebra AND MUST differ from `STR_DELIMITER`'s
31968 // (`'"'`) so `:foo` never opens a string. The two
31969 // `bool_literal` spellings (`"#t"`, `"#f"`) begin with `'#'` —
31970 // a two-char prefix distinct from the single `'"'`
31971 // STR_DELIMITER — so a bare `#t` never opens a string either.
31972 // Pin the byte-level disjointness directly here so any future
31973 // refactor that swaps a marker to collide with STR_DELIMITER
31974 // fails loudly.
31975 assert_ne!(
31976 Atom::STR_DELIMITER,
31977 Atom::KEYWORD_MARKER_LEAD,
31978 "STR_DELIMITER and KEYWORD_MARKER_LEAD share a byte — a \
31979 bare `{}foo` lexeme would ambiguously open a string AND \
31980 begin a keyword classification.",
31981 Atom::KEYWORD_MARKER,
31982 );
31983 for b in [true, false] {
31984 assert!(
31985 !Atom::bool_literal(b).starts_with(Atom::STR_DELIMITER),
31986 "bool_literal({b:?}) begins with STR_DELIMITER — a bare \
31987 `{}` lexeme would ambiguously open a string AND classify \
31988 as a Bool.",
31989 Atom::bool_literal(b),
31990 );
31991 }
31992 }
31993
31994 #[test]
31995 fn atom_str_delimiter_closes_reader_display_round_trip_for_escape_free_str_payloads() {
31996 // Load-bearing round-trip contract for the reader's four
31997 // Str-round-trip sites — the reader's string-opening AND
31998 // string-closing arms both bind to `Atom::STR_DELIMITER`, so
31999 // wrapping an escape-free payload in the constant's byte on
32000 // both sides recovers the SAME `Atom::string(s)` value the
32001 // typed constructor produces. A regression that drifts ONE
32002 // of the two arms (e.g. re-inlines `'"'` at the opener while
32003 // migrating the closer to a different delimiter) breaks the
32004 // opener-must-match-closer contract even when the byte-value
32005 // at the drifted site still agrees at the surface, because
32006 // this round-trip binds to the constant at BOTH endpoints.
32007 //
32008 // Escape-free payload sweep — the reader's escape-handler
32009 // arm's self-escape (`\"` → `"`) is pinned separately at
32010 // `reader_str_escape_self_escape_arm_routes_through_atom_str_delimiter`
32011 // in `crate::reader::tests`; here we sweep the payloads that
32012 // do NOT hit the escape-handler branch so the opener/closer
32013 // pairing is isolated as the load-bearing composition.
32014 for payload in ["hello", "", "foo bar", "kw", "seph.1", "42"] {
32015 let source = format!("{}{payload}{}", Atom::STR_DELIMITER, Atom::STR_DELIMITER,);
32016 let forms = crate::reader::read(&source).unwrap_or_else(|e| {
32017 panic!(
32018 "reader rejected `{source}` composed from \
32019 Atom::STR_DELIMITER at payload={payload:?}: {e}"
32020 )
32021 });
32022 assert_eq!(
32023 forms.len(),
32024 1,
32025 "STR_DELIMITER-wrapped payload {payload:?} must read as \
32026 exactly one form, got {forms:?}",
32027 );
32028 assert_eq!(
32029 forms[0],
32030 Sexp::Atom(Atom::string(payload)),
32031 "STR_DELIMITER-wrapped payload {payload:?} drifted from \
32032 the Sexp::Atom(Atom::string(_)) typed-constructor shape",
32033 );
32034 }
32035 }
32036
32037 // ── `Atom::STR_ESCAPE_LEAD` — the canonical `\` char routed
32038 // through the TWO Str-escape-lead round-trip sites inside
32039 // `crate::reader::tokenize` (escape-handler outer arm's escape-lead
32040 // pattern, escape-handler's self-escape arm's pattern + value pair).
32041 // Pins the constant value AND the composition against a byte-
32042 // identical drift at either site. Sibling-shape peer of the
32043 // `atom_str_delimiter_*` block above on the Str-payload delimiter
32044 // axis — where those pin the OPENER/CLOSER byte's four round-trip
32045 // sites, these pin the ESCAPE-LEAD byte's two round-trip sites.
32046 // The two constants together span the reader's `Token::Str`
32047 // tokenization boundary.
32048
32049 #[test]
32050 fn atom_str_escape_lead_projects_canonical_backslash_char() {
32051 // Pins the constant's exact `char` value so a typo (`'/'`,
32052 // `'|'`, `'^'`) or an accidental redefinition surfaces
32053 // immediately. Sibling-shape pin to
32054 // `atom_str_delimiter_projects_canonical_double_quote_char`
32055 // — pins the SAME shape on the Str-escape-lead axis of the
32056 // closed-set [`Atom`] algebra.
32057 assert_eq!(
32058 Atom::STR_ESCAPE_LEAD,
32059 '\\',
32060 "STR_ESCAPE_LEAD char drifted from the substrate-canonical \
32061 `\\` escape lead — the reader-round-trip contract at \
32062 crate::reader::tokenize (escape-handler outer arm, escape-\
32063 handler self-escape arm's pattern + value pair) all bind \
32064 to this ONE constant.",
32065 );
32066 }
32067
32068 #[test]
32069 fn atom_str_escape_lead_distinct_from_every_other_atom_marker() {
32070 // Cross-axis disjointness pin: the Str-escape-lead byte
32071 // (`Atom::STR_ESCAPE_LEAD`) must NOT alias the Str-delimiter
32072 // (`Atom::STR_DELIMITER`), Keyword-marker prefix
32073 // (`Atom::KEYWORD_MARKER`), or Bool-literal spellings
32074 // (`Atom::bool_literal(true|false)`) — otherwise the reader's
32075 // escape-lead outer arm would ambiguously route the alias
32076 // byte through the escape-handler branch AND the alias's
32077 // corresponding classifier arm. Guards the structural
32078 // disjointness of the atomic-payload marker family on the
32079 // closed-set [`Atom`] algebra so a future marker-swap that
32080 // accidentally collides two axes surfaces at this pin rather
32081 // than as a silent reader misclassification.
32082 //
32083 // In particular: `STR_ESCAPE_LEAD` (`'\\'`) MUST differ from
32084 // `STR_DELIMITER` (`'"'`) so that the reader's `Token::Str`
32085 // accumulation loop's inner branch dispatch (escape-lead
32086 // outer arm vs string-closing arm vs passthrough) remains
32087 // structurally disjoint — collapsing the two would make the
32088 // opener/closer AND the escape-lead the SAME byte, breaking
32089 // both dispatch axes at once.
32090 assert_ne!(
32091 Atom::STR_ESCAPE_LEAD,
32092 Atom::STR_DELIMITER,
32093 "STR_ESCAPE_LEAD and STR_DELIMITER share a byte — the \
32094 reader's Token::Str inner loop's escape-lead outer arm \
32095 would ambiguously route through the string-closing arm.",
32096 );
32097 assert_ne!(
32098 Atom::STR_ESCAPE_LEAD,
32099 Atom::KEYWORD_MARKER_LEAD,
32100 "STR_ESCAPE_LEAD and KEYWORD_MARKER_LEAD share a byte — a \
32101 bare `{}foo` lexeme would ambiguously match the escape- \
32102 lead AND begin a keyword classification.",
32103 Atom::KEYWORD_MARKER,
32104 );
32105 for b in [true, false] {
32106 assert!(
32107 !Atom::bool_literal(b).starts_with(Atom::STR_ESCAPE_LEAD),
32108 "bool_literal({b:?}) begins with STR_ESCAPE_LEAD — a bare \
32109 `{}` lexeme would ambiguously match the escape-lead AND \
32110 classify as a Bool.",
32111 Atom::bool_literal(b),
32112 );
32113 }
32114 }
32115
32116 #[test]
32117 fn atom_str_escape_lead_closes_reader_self_escape_round_trip_for_backslash_payload() {
32118 // Load-bearing round-trip contract for the reader's two
32119 // Str-escape-lead round-trip sites — the reader's escape-lead
32120 // outer arm AND the escape-handler's self-escape arm both bind
32121 // to `Atom::STR_ESCAPE_LEAD`, so wrapping the constant's byte
32122 // TWICE (i.e. the two-byte `\\` sequence) between two
32123 // `STR_DELIMITER` bytes recovers a Str payload holding ONE
32124 // `\` byte through the reader. A regression that drifts EITHER
32125 // of the two arms (outer arm's pattern OR self-escape arm's
32126 // pattern + value) fails HERE — even when the byte-value at
32127 // the drifted site still agrees at the surface — because the
32128 // round-trip binds to the constant at BOTH sites.
32129 //
32130 // Sibling-shape pin to
32131 // `atom_str_delimiter_closes_reader_display_round_trip_for_escape_free_str_payloads`
32132 // (the Str-payload delimiter axis's opener/closer round-trip)
32133 // — where that pin sweeps escape-FREE payloads through the
32134 // opener/closer pair, this pin exercises the SINGLE escape-lead
32135 // self-escape composition end-to-end.
32136 let source = format!(
32137 "{}{}{}{}",
32138 Atom::STR_DELIMITER,
32139 Atom::STR_ESCAPE_LEAD,
32140 Atom::STR_ESCAPE_LEAD,
32141 Atom::STR_DELIMITER,
32142 );
32143 let forms = crate::reader::read(&source).unwrap_or_else(|e| {
32144 panic!(
32145 "reader rejected `{source}` composed from \
32146 STR_DELIMITER + STR_ESCAPE_LEAD self-escape: {e}"
32147 )
32148 });
32149 assert_eq!(
32150 forms.len(),
32151 1,
32152 "STR_ESCAPE_LEAD-self-escape source must read as exactly one \
32153 form, got {forms:?}",
32154 );
32155 assert_eq!(
32156 forms[0],
32157 Sexp::Atom(Atom::string(Atom::STR_ESCAPE_LEAD.to_string())),
32158 "STR_ESCAPE_LEAD self-escape drifted from the \
32159 Sexp::Atom(Atom::string(str_escape_lead)) typed-constructor \
32160 shape — the reader's escape-lead outer arm OR the escape-\
32161 handler's self-escape arm's pattern + value pair drifted \
32162 from the Atom::STR_ESCAPE_LEAD constant",
32163 );
32164 }
32165
32166 // ── `Atom::decode_str_escape` — the ONE typed Str-escape decode
32167 // projection on the closed-set [`Atom`] algebra. Pins the six-arm
32168 // decode table (three named-escape arms `'n' / 't' / 'r'`, two
32169 // pattern-equals-value self-escape arms on
32170 // [`Atom::STR_DELIMITER`] + [`Atom::STR_ESCAPE_LEAD`], one
32171 // passthrough `other`) AND the reader-level composition through
32172 // [`crate::reader::tokenize`]'s escape-handler branch. Sibling-shape
32173 // peer of the `atom_str_escape_lead_*` block above on the same
32174 // Str-payload tokenization boundary: where those pin the
32175 // ESCAPE-LEAD byte's two round-trip sites, these pin the escape
32176 // TABLE's decode arms end-to-end.
32177
32178 #[test]
32179 fn atom_decode_str_escape_named_escape_arms_project_canonical_c0_control_bytes() {
32180 // NAMED-ESCAPE CONTRACT: the three canonical whitespace-
32181 // shorthand arms map each ASCII letter to its corresponding
32182 // C0 control byte. Pins the ONE typed projection's arms so a
32183 // regression that swaps ONE arm's decoded byte (e.g. drifts
32184 // `'n' → '\r'`, breaking the substrate's canonical newline
32185 // shorthand) surfaces at this pin rather than at some
32186 // downstream Str-payload round-trip.
32187 assert_eq!(
32188 Atom::decode_str_escape(Atom::NEWLINE_ESCAPE_SOURCE),
32189 Atom::NEWLINE_ESCAPE_DECODED,
32190 "decode_str_escape(NEWLINE_ESCAPE_SOURCE) drifted from the \
32191 substrate-canonical newline (`\\n`) shorthand — the reader's \
32192 escape-handler branch's `'n' → '\\n'` arm binds to THIS \
32193 projection.",
32194 );
32195 assert_eq!(
32196 Atom::decode_str_escape(Atom::TAB_ESCAPE_SOURCE),
32197 Atom::TAB_ESCAPE_DECODED,
32198 "decode_str_escape(TAB_ESCAPE_SOURCE) drifted from the \
32199 substrate-canonical tab (`\\t`) shorthand.",
32200 );
32201 assert_eq!(
32202 Atom::decode_str_escape(Atom::CARRIAGE_RETURN_ESCAPE_SOURCE),
32203 Atom::CARRIAGE_RETURN_ESCAPE_DECODED,
32204 "decode_str_escape(CARRIAGE_RETURN_ESCAPE_SOURCE) drifted from \
32205 the substrate-canonical carriage-return (`\\r`) shorthand.",
32206 );
32207 }
32208
32209 #[test]
32210 fn atom_decode_str_escape_self_escape_arms_route_through_atom_algebra_constants() {
32211 // SELF-ESCAPE CONTRACT: the two pattern-equals-value arms in
32212 // the escape table bind through the closed-set [`Atom`]
32213 // algebra constants at BOTH pattern AND value. Pin that
32214 // decoding the delimiter byte OR the escape-lead byte from
32215 // its escaped form recovers the SAME algebra constant — a
32216 // delimiter-swap on the algebra propagates through pattern
32217 // AND value at ONE site (the `decode_str_escape` match arm)
32218 // rather than as scattered inline byte literals. Sibling-
32219 // shape pin to
32220 // `reader_str_escape_self_escape_arm_routes_through_atom_str_delimiter`
32221 // AND
32222 // `reader_str_escape_lead_outer_arm_and_self_escape_arm_route_through_atom_str_escape_lead`
32223 // — where those anchor the reader's inner-loop dispatch to
32224 // the constants, this anchors the algebra-level projection's
32225 // pattern-equals-value arms to the SAME constants so the
32226 // reader-round-trip through `decode_str_escape` cannot drift
32227 // if the constants ever change.
32228 assert_eq!(
32229 Atom::decode_str_escape(Atom::STR_DELIMITER),
32230 Atom::STR_DELIMITER,
32231 "decode_str_escape(STR_DELIMITER) drifted from the self-\
32232 escape identity on the Str-payload delimiter axis — the \
32233 `\\\"` sequence must decode to the STR_DELIMITER byte.",
32234 );
32235 assert_eq!(
32236 Atom::decode_str_escape(Atom::STR_ESCAPE_LEAD),
32237 Atom::STR_ESCAPE_LEAD,
32238 "decode_str_escape(STR_ESCAPE_LEAD) drifted from the self-\
32239 escape identity on the Str-payload escape-lead axis — \
32240 the `\\\\` sequence must decode to the STR_ESCAPE_LEAD \
32241 byte.",
32242 );
32243 }
32244
32245 #[test]
32246 fn atom_decode_str_escape_passthrough_arm_returns_source_byte_unchanged_for_non_named_chars() {
32247 // PASSTHROUGH CONTRACT: every `esc` NOT bound by the five
32248 // typed arms (three named + two self-escape) decodes to
32249 // itself. Pins the total-function property of the projection
32250 // — every `char` maps to exactly one decoded `char`, and the
32251 // decode is identity outside the closed-set table. A
32252 // regression that stripped the `other => other` fallthrough
32253 // (e.g. swapped it for a diagnostic path or a `Result` return
32254 // shape) surfaces at this pin. Sweep a representative
32255 // cross-section of ASCII printable + non-ASCII payload bytes
32256 // that MUST NOT alias any of the five typed arms — the
32257 // reader's pre-lift six-arm table pushed each of these
32258 // through unchanged, and the typed projection must preserve
32259 // that identity end-to-end.
32260 for esc in ['a', 'z', '0', '9', '!', '/', '<', '≠', 'π', '🌱'] {
32261 // Cross-arm disjointness precondition — none of the swept
32262 // chars may alias the five typed arms; otherwise the sweep
32263 // conflates passthrough with a typed decode and no longer
32264 // pins the fallthrough identity.
32265 assert!(
32266 !Atom::NAMED_ESCAPE_TABLE.iter().any(|&(src, _)| src == esc)
32267 && !Atom::SELF_ESCAPE_TABLE.contains(&esc),
32268 "passthrough sweep char `{esc}` aliases a typed \
32269 escape-table arm — the sweep no longer pins the \
32270 `other => other` fallthrough",
32271 );
32272 assert_eq!(
32273 Atom::decode_str_escape(esc),
32274 esc,
32275 "decode_str_escape({esc:?}) drifted from the passthrough \
32276 identity — every non-named-escape byte must decode to \
32277 itself so the reader's `\\{esc}` sequence yields the \
32278 payload byte `{esc}`.",
32279 );
32280 }
32281 }
32282
32283 #[test]
32284 fn atom_named_escape_per_role_constants_pin_canonical_bytes() {
32285 // PER-ROLE CANONICAL-BYTE PIN: each of the six per-role
32286 // `pub const`s carries its exact substrate-canonical byte.
32287 // A regression that swaps a SOURCE letter (e.g. drifts
32288 // `NEWLINE_ESCAPE_SOURCE` from `'n'` to `'N'`) or a DECODED
32289 // C0 control (e.g. drifts `TAB_ESCAPE_DECODED` from `'\t'`
32290 // to `'\0'`) surfaces at this pin before the round-trip
32291 // sweep below has a chance to conflate the two. Sibling
32292 // shape to `atom_str_delimiter_projects_canonical_double_quote_char`
32293 // on the same closed-set [`Atom`] algebra.
32294 assert_eq!(
32295 Atom::NEWLINE_ESCAPE_SOURCE,
32296 'n',
32297 "NEWLINE_ESCAPE_SOURCE drifted from the substrate-canonical `n` letter.",
32298 );
32299 assert_eq!(
32300 Atom::NEWLINE_ESCAPE_DECODED,
32301 '\n',
32302 "NEWLINE_ESCAPE_DECODED drifted from the substrate-canonical `\\n` C0 control byte.",
32303 );
32304 assert_eq!(
32305 Atom::TAB_ESCAPE_SOURCE,
32306 't',
32307 "TAB_ESCAPE_SOURCE drifted from the substrate-canonical `t` letter.",
32308 );
32309 assert_eq!(
32310 Atom::TAB_ESCAPE_DECODED,
32311 '\t',
32312 "TAB_ESCAPE_DECODED drifted from the substrate-canonical `\\t` C0 control byte.",
32313 );
32314 assert_eq!(
32315 Atom::CARRIAGE_RETURN_ESCAPE_SOURCE,
32316 'r',
32317 "CARRIAGE_RETURN_ESCAPE_SOURCE drifted from the substrate-canonical `r` letter.",
32318 );
32319 assert_eq!(
32320 Atom::CARRIAGE_RETURN_ESCAPE_DECODED,
32321 '\r',
32322 "CARRIAGE_RETURN_ESCAPE_DECODED drifted from the substrate-canonical `\\r` C0 control byte.",
32323 );
32324 }
32325
32326 #[test]
32327 fn atom_named_escape_table_composes_from_per_role_constants_in_declaration_order() {
32328 // COMPOSITION LAW: the ALL array's rows are exactly the
32329 // per-role (SOURCE, DECODED) pairings in canonical
32330 // declaration order. Pins the composition at ONE site so a
32331 // reorder of ONE row without reordering the per-role
32332 // `pub const`s silently misaligns every consumer that sweeps
32333 // the array by index. Sibling posture to
32334 // `quote_form_iac_forge_tags_align_with_all_by_index` on the
32335 // outer-tokenizer quote-family axis.
32336 assert_eq!(
32337 Atom::NAMED_ESCAPE_TABLE,
32338 [
32339 (Atom::NEWLINE_ESCAPE_SOURCE, Atom::NEWLINE_ESCAPE_DECODED),
32340 (Atom::TAB_ESCAPE_SOURCE, Atom::TAB_ESCAPE_DECODED),
32341 (
32342 Atom::CARRIAGE_RETURN_ESCAPE_SOURCE,
32343 Atom::CARRIAGE_RETURN_ESCAPE_DECODED,
32344 ),
32345 ],
32346 "NAMED_ESCAPE_TABLE drifted from its per-role (SOURCE, \
32347 DECODED) `pub const` composition in canonical declaration \
32348 order (newline / tab / carriage-return).",
32349 );
32350 }
32351
32352 #[test]
32353 fn atom_named_escape_table_has_expected_cardinality() {
32354 // ARITY PIN: the forced-arity `[(char, char); 3]` shape
32355 // survives at runtime. Pins the closed-set size against a
32356 // refactor that loosens the array's type to
32357 // `&'static [(char, char)]` or `Vec<(char, char)>` (which
32358 // would drop the compile-time arity forcing rustc bakes
32359 // into `[T; N]` declarations). Sibling posture to
32360 // `quote_form_iac_forge_tags_has_expected_cardinality` on
32361 // the outer-tokenizer quote-family axis.
32362 assert_eq!(
32363 Atom::NAMED_ESCAPE_TABLE.len(),
32364 3,
32365 "NAMED_ESCAPE_TABLE cardinality drifted from the \
32366 substrate-canonical THREE named-escape arms (newline / \
32367 tab / carriage-return).",
32368 );
32369 }
32370
32371 #[test]
32372 fn atom_named_escape_table_sources_pairwise_distinct() {
32373 // SOURCE DISJOINTNESS: every SOURCE char in the array is
32374 // distinct from every other SOURCE. A regression that
32375 // aliased two source letters (e.g. drifts
32376 // `TAB_ESCAPE_SOURCE` to `'n'`, colliding with
32377 // `NEWLINE_ESCAPE_SOURCE`) would silently route two escape
32378 // sequences through the first-matching decoded byte in
32379 // `decode_str_escape`'s match. Pin the closed-set injective
32380 // shape on the SOURCE axis. Sibling posture to
32381 // `quote_form_iac_forge_tags_pairwise_distinct` on the outer
32382 // quote-family axis.
32383 let sources: Vec<char> = Atom::NAMED_ESCAPE_TABLE
32384 .iter()
32385 .map(|&(src, _)| src)
32386 .collect();
32387 for i in 0..sources.len() {
32388 for j in (i + 1)..sources.len() {
32389 assert_ne!(
32390 sources[i], sources[j],
32391 "NAMED_ESCAPE_TABLE SOURCE chars at indices {i} and {j} \
32392 alias — every named-escape arm must specialize on a \
32393 distinct SOURCE letter.",
32394 );
32395 }
32396 }
32397 }
32398
32399 #[test]
32400 fn atom_named_escape_table_decoded_pairwise_distinct() {
32401 // DECODED DISJOINTNESS: every DECODED byte in the array is
32402 // distinct from every other DECODED byte. A regression that
32403 // aliased two decoded bytes (e.g. drifts
32404 // `TAB_ESCAPE_DECODED` to `'\n'`, colliding with
32405 // `NEWLINE_ESCAPE_DECODED`) would silently collapse two
32406 // canonical whitespace shorthands to the same emitted byte
32407 // — a `\t` in a Str payload would decode to a newline.
32408 // Sibling posture to the SOURCE-axis disjointness pin above.
32409 let decoded: Vec<char> = Atom::NAMED_ESCAPE_TABLE
32410 .iter()
32411 .map(|&(_, dec)| dec)
32412 .collect();
32413 for i in 0..decoded.len() {
32414 for j in (i + 1)..decoded.len() {
32415 assert_ne!(
32416 decoded[i], decoded[j],
32417 "NAMED_ESCAPE_TABLE DECODED bytes at indices {i} and {j} \
32418 alias — every named-escape arm must emit a distinct \
32419 DECODED byte.",
32420 );
32421 }
32422 }
32423 }
32424
32425 #[test]
32426 fn atom_named_escape_table_pattern_distinct_from_value_per_row() {
32427 // PATTERN-DISTINCT-FROM-VALUE INVARIANT: every named-escape
32428 // row's SOURCE differs from its DECODED byte. This is the
32429 // structural axis that distinguishes the three named-escape
32430 // rows from the two pattern-EQUALS-value self-escape arms
32431 // (`STR_DELIMITER → STR_DELIMITER`,
32432 // `STR_ESCAPE_LEAD → STR_ESCAPE_LEAD`) EXCLUDED from this
32433 // array by design. A regression that drifted a named row
32434 // into pattern-equals-value (e.g. `TAB_ESCAPE_DECODED = 't'`)
32435 // silently promoted it to a passthrough and lost the
32436 // canonical whitespace shorthand — pinned HERE at the
32437 // typed-algebra level.
32438 for (i, &(src, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32439 assert_ne!(
32440 src, dec,
32441 "NAMED_ESCAPE_TABLE row {i} pattern-EQUALS-value — the \
32442 named-escape rows are structurally pattern-DISTINCT; \
32443 the two pattern-equals-value arms live at \
32444 STR_DELIMITER / STR_ESCAPE_LEAD one axis over.",
32445 );
32446 }
32447 }
32448
32449 #[test]
32450 fn atom_named_escape_table_disjoint_from_self_escape_algebra_constants() {
32451 // CROSS-AXIS DISJOINTNESS: no SOURCE or DECODED byte in the
32452 // named-escape table aliases either of the two self-escape
32453 // algebra constants (`Self::STR_DELIMITER`,
32454 // `Self::STR_ESCAPE_LEAD`). A regression that drifted ONE
32455 // named-escape SOURCE to `'\\'` (colliding with
32456 // `STR_ESCAPE_LEAD`) or ONE DECODED to `'"'` (colliding
32457 // with `STR_DELIMITER`) would silently reshuffle the
32458 // decode-arm dispatch order in `decode_str_escape` (the
32459 // named arm would fire before the self-escape arm, or vice
32460 // versa). Pins the two escape-family axes as structurally
32461 // disjoint sub-vocabularies of the Str-payload tokenization
32462 // boundary.
32463 for (i, &(src, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32464 assert_ne!(
32465 src,
32466 Atom::STR_DELIMITER,
32467 "NAMED_ESCAPE_TABLE row {i} SOURCE aliases STR_DELIMITER",
32468 );
32469 assert_ne!(
32470 src,
32471 Atom::STR_ESCAPE_LEAD,
32472 "NAMED_ESCAPE_TABLE row {i} SOURCE aliases STR_ESCAPE_LEAD",
32473 );
32474 assert_ne!(
32475 dec,
32476 Atom::STR_DELIMITER,
32477 "NAMED_ESCAPE_TABLE row {i} DECODED aliases STR_DELIMITER",
32478 );
32479 assert_ne!(
32480 dec,
32481 Atom::STR_ESCAPE_LEAD,
32482 "NAMED_ESCAPE_TABLE row {i} DECODED aliases STR_ESCAPE_LEAD",
32483 );
32484 }
32485 }
32486
32487 #[test]
32488 fn atom_decode_str_escape_routes_through_named_escape_table_for_every_row() {
32489 // PATH-UNIFORMITY PIN: `decode_str_escape` returns the
32490 // DECODED byte of every `NAMED_ESCAPE_TABLE` row when called
32491 // with that row's SOURCE. Path-uniformity contract between
32492 // the projection method and the closed-set algebra — a
32493 // regression that reverted `decode_str_escape`'s named arms
32494 // to inline `'n' => '\n'` literals AND drifted the SOURCE
32495 // per-role `pub const` (or vice versa) fails HERE at the
32496 // first mismatched row rather than at a distant Str-payload
32497 // round-trip. Sibling posture to
32498 // `quote_form_iac_forge_tag_routes_through_typed_per_role_constants`
32499 // on the outer-tokenizer quote-family axis.
32500 for (i, &(src, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32501 assert_eq!(
32502 Atom::decode_str_escape(src),
32503 dec,
32504 "decode_str_escape drifted from NAMED_ESCAPE_TABLE row {i} \
32505 — the projection's named-escape arm must route through \
32506 the per-role (SOURCE, DECODED) `pub const` pairing.",
32507 );
32508 }
32509 }
32510
32511 #[test]
32512 fn atom_self_escape_table_composes_from_algebra_constants_in_declaration_order() {
32513 // COMPOSITION LAW: the ALL array's rows are the two closed-set
32514 // [`Atom`] algebra constants in canonical declaration order
32515 // ([`Atom::STR_DELIMITER`], [`Atom::STR_ESCAPE_LEAD`]) matching
32516 // `decode_str_escape`'s match-arm order. Pins the composition
32517 // at ONE site so a reorder of ONE row without reordering the
32518 // constants silently misaligns every consumer that sweeps the
32519 // array by index. Sibling posture to
32520 // `atom_named_escape_table_composes_from_per_role_constants_in_declaration_order`
32521 // one axis over.
32522 assert_eq!(
32523 Atom::SELF_ESCAPE_TABLE,
32524 [Atom::STR_DELIMITER, Atom::STR_ESCAPE_LEAD],
32525 "SELF_ESCAPE_TABLE drifted from its algebra-constant \
32526 composition in canonical declaration order \
32527 (STR_DELIMITER / STR_ESCAPE_LEAD).",
32528 );
32529 }
32530
32531 #[test]
32532 fn atom_self_escape_table_has_expected_cardinality() {
32533 // ARITY PIN: the forced-arity `[char; 2]` shape survives at
32534 // runtime. Pins the closed-set size against a refactor that
32535 // loosens the array's type to `&'static [char]` or
32536 // `Vec<char>` (which would drop the compile-time arity
32537 // forcing rustc bakes into `[T; N]` declarations). Sibling
32538 // posture to `atom_named_escape_table_has_expected_cardinality`
32539 // on the peer pattern-DISTINCT-from-value sub-vocabulary.
32540 assert_eq!(
32541 Atom::SELF_ESCAPE_TABLE.len(),
32542 2,
32543 "SELF_ESCAPE_TABLE cardinality drifted from the substrate-\
32544 canonical TWO self-escape arms (STR_DELIMITER / \
32545 STR_ESCAPE_LEAD).",
32546 );
32547 }
32548
32549 #[test]
32550 fn atom_self_escape_table_pairwise_distinct() {
32551 // DISJOINTNESS: every byte in the array is distinct from every
32552 // other byte. A regression that aliased the two self-escape
32553 // bytes (e.g. adopting `'"'` as ALSO the escape lead in a
32554 // hypothetical smart-quote reader mode) would silently collapse
32555 // the two pattern-EQUALS-value arms of `decode_str_escape` to
32556 // one and lose the ability to escape one delimiter without
32557 // shadowing the other. Sibling posture to
32558 // `atom_named_escape_table_sources_pairwise_distinct` on the
32559 // peer sub-vocabulary.
32560 for i in 0..Atom::SELF_ESCAPE_TABLE.len() {
32561 for j in (i + 1)..Atom::SELF_ESCAPE_TABLE.len() {
32562 assert_ne!(
32563 Atom::SELF_ESCAPE_TABLE[i],
32564 Atom::SELF_ESCAPE_TABLE[j],
32565 "SELF_ESCAPE_TABLE bytes at indices {i} and {j} \
32566 alias — every self-escape arm must specialize on \
32567 a distinct byte.",
32568 );
32569 }
32570 }
32571 }
32572
32573 #[test]
32574 fn atom_self_escape_table_disjoint_from_named_escape_table() {
32575 // CROSS-AXIS DISJOINTNESS: no byte in the self-escape table
32576 // aliases any SOURCE or DECODED byte in the named-escape
32577 // table. A regression that drifted a self-escape byte into
32578 // the named vocabulary (e.g. adopted `'n'` as an additional
32579 // self-escaping delimiter) OR a named byte into the self
32580 // vocabulary would reshuffle the decode-arm dispatch order in
32581 // `decode_str_escape` — the two sub-vocabularies must remain
32582 // structurally disjoint sub-tables of the FIVE non-passthrough
32583 // arms. Sibling posture (inverse direction) to
32584 // `atom_named_escape_table_disjoint_from_self_escape_algebra_constants`
32585 // — where that pin sweeps from the named side outward, this
32586 // sweeps from the self side outward; both pin the same
32587 // cross-axis disjointness invariant.
32588 for (i, &self_byte) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
32589 for (j, &(src, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32590 assert_ne!(
32591 self_byte, src,
32592 "SELF_ESCAPE_TABLE row {i} aliases NAMED_ESCAPE_TABLE \
32593 row {j} SOURCE — the pattern-EQUALS-value and \
32594 pattern-DISTINCT-from-value sub-vocabularies must \
32595 remain disjoint.",
32596 );
32597 assert_ne!(
32598 self_byte, dec,
32599 "SELF_ESCAPE_TABLE row {i} aliases NAMED_ESCAPE_TABLE \
32600 row {j} DECODED — the pattern-EQUALS-value and \
32601 pattern-DISTINCT-from-value sub-vocabularies must \
32602 remain disjoint.",
32603 );
32604 }
32605 }
32606 }
32607
32608 #[test]
32609 fn atom_decode_str_escape_routes_through_self_escape_table_for_every_row() {
32610 // PATH-UNIFORMITY PIN: `decode_str_escape` returns the SAME
32611 // byte for every `SELF_ESCAPE_TABLE` row — the pattern-EQUALS-
32612 // value invariant is exercised on every row. A regression
32613 // that reverted `decode_str_escape`'s two self-escape arms to
32614 // inline `'"' => '"'` / `'\\' => '\\'` literals AND drifted
32615 // one of the two constants (or vice versa) fails HERE at the
32616 // first mismatched row rather than at a downstream Str-payload
32617 // round-trip. Sibling posture to
32618 // `atom_decode_str_escape_routes_through_named_escape_table_for_every_row`
32619 // — where that pin exercises the pattern-DISTINCT-from-value
32620 // path-uniformity contract on the peer sub-vocabulary, this
32621 // pins the pattern-EQUALS-value axis on the self-escape
32622 // sub-vocabulary.
32623 for (i, &esc) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
32624 assert_eq!(
32625 Atom::decode_str_escape(esc),
32626 esc,
32627 "decode_str_escape drifted from SELF_ESCAPE_TABLE row \
32628 {i} — the projection's self-escape arm must map \
32629 pattern to the SAME byte (definitional identity).",
32630 );
32631 }
32632 }
32633
32634 #[test]
32635 fn atom_named_and_self_escape_tables_span_the_five_non_passthrough_arms() {
32636 // TOTAL-DECODE PIN: the two sub-vocabulary ALL arrays
32637 // together account for exactly the FIVE non-passthrough arms
32638 // of [`Atom::decode_str_escape`]. Pins the closed-set
32639 // decomposition against a refactor that adds a sixth typed
32640 // arm to `decode_str_escape` without extending one of the
32641 // ALL arrays (which would leak a stale byte through
32642 // `other => other` at the sweep sites). The three named arms
32643 // + two self arms = five typed arms; the sixth branch is the
32644 // `other => other` passthrough at the algebra's projection.
32645 assert_eq!(
32646 Atom::NAMED_ESCAPE_TABLE.len() + Atom::SELF_ESCAPE_TABLE.len(),
32647 5,
32648 "NAMED_ESCAPE_TABLE + SELF_ESCAPE_TABLE cardinality drifted \
32649 from the substrate-canonical FIVE non-passthrough arms of \
32650 Atom::decode_str_escape.",
32651 );
32652 }
32653
32654 // ── `Atom::ESCAPE_SOURCES` — the closed-set forced-arity ALL array
32655 // over every escape-SOURCE byte `Atom::decode_str_escape` has a
32656 // non-passthrough arm for. Cross-sub-vocabulary SPAN peer of
32657 // `NAMED_ESCAPE_TABLE` (three pattern-DISTINCT-from-value rows'
32658 // SOURCE column) + `SELF_ESCAPE_TABLE` (two pattern-EQUALS-value
32659 // rows) at ONE typed `[char; 5]` on the SAME closed-set [`Atom`]
32660 // algebra. The pins below anchor (a) the SPAN composition against
32661 // the two peer sub-vocabulary arrays' SOURCE columns in canonical
32662 // declaration order, (b) the forced-arity cardinality against a
32663 // refactor that loosens the array to `&[char]`, (c) pairwise
32664 // disjointness inherited from the two peer sub-vocabularies' own
32665 // pairwise disjointness + cross-sub-vocabulary disjointness, (d)
32666 // the "every row hits a non-passthrough arm" closure identity
32667 // against a refactor that added a stale ESCAPE_SOURCES row without
32668 // extending `decode_str_escape`, and (e) the load-bearing partition
32669 // NAMED-source-column prefix / SELF-source-column suffix that the
32670 // declaration order encodes for consumers that walk the array by
32671 // index. Sibling-shape to the composition-pin block above for
32672 // `SELF_ESCAPE_TABLE` — those pins bind the peer sub-vocabulary at
32673 // the same closed-set algebra; this block binds the SPAN of the
32674 // two sub-vocabularies at ONE array up.
32675
32676 #[test]
32677 fn atom_escape_sources_composes_from_named_and_self_escape_sub_vocabularies_in_declaration_order(
32678 ) {
32679 // COMPOSITION LAW: the ALL array's rows are (NAMED_ESCAPE_TABLE
32680 // SOURCE column, then SELF_ESCAPE_TABLE rows) in canonical
32681 // declaration order matching `decode_str_escape`'s match-arm
32682 // order. Pins the SPAN composition at ONE site so a reorder
32683 // that broke the (named prefix, self suffix) partition (e.g.
32684 // interleaving the two sub-vocabularies' rows, sorting
32685 // alphabetically) silently misaligns every consumer that
32686 // sweeps the array by index. Sibling-shape pin to
32687 // `atom_self_escape_table_composes_from_algebra_constants_in_declaration_order`
32688 // one axis over on the peer sub-vocabulary at
32689 // `Atom::SELF_ESCAPE_TABLE`, and to
32690 // `atom_named_escape_table_composes_from_per_role_constants_in_declaration_order`
32691 // on the SOURCE column of the other peer sub-vocabulary at
32692 // `Atom::NAMED_ESCAPE_TABLE`.
32693 assert_eq!(
32694 Atom::ESCAPE_SOURCES,
32695 [
32696 Atom::NAMED_ESCAPE_TABLE[0].0,
32697 Atom::NAMED_ESCAPE_TABLE[1].0,
32698 Atom::NAMED_ESCAPE_TABLE[2].0,
32699 Atom::SELF_ESCAPE_TABLE[0],
32700 Atom::SELF_ESCAPE_TABLE[1],
32701 ],
32702 "ESCAPE_SOURCES composition drifted from the canonical \
32703 (NAMED SOURCE column, then SELF rows) SPAN in declaration \
32704 order — the SPAN lift must route through the two peer \
32705 sub-vocabulary arrays' rows in the exact order \
32706 decode_str_escape's match arms fire on them.",
32707 );
32708 }
32709
32710 #[test]
32711 fn atom_escape_sources_has_expected_cardinality() {
32712 // ARITY PIN: the forced-arity `[char; 5]` shape survives at
32713 // runtime AND matches the two peer sub-vocabularies' summed
32714 // cardinality. Pins the closed-set size against a refactor
32715 // that loosens the array's type to `&'static [char]` or
32716 // `Vec<char>` (which would drop the compile-time arity
32717 // forcing rustc bakes into `[T; N]` declarations) AND against
32718 // a refactor that drifted this array's arity without extending
32719 // one of the two peer sub-vocabularies (which would silently
32720 // desynchronize the SPAN identity from its two source arrays).
32721 // Sibling posture to
32722 // `atom_named_and_self_escape_tables_span_the_five_non_passthrough_arms`
32723 // — where that pin binds the summed cardinality at ONE
32724 // assert_eq!, this pin binds the SPAN's OWN cardinality at
32725 // the ALL array level so a future refactor that added a
32726 // NAMED_ESCAPE_TABLE row without extending ESCAPE_SOURCES
32727 // fails HERE at the paired-length equality rather than at a
32728 // distant sweep site.
32729 assert_eq!(
32730 Atom::ESCAPE_SOURCES.len(),
32731 5,
32732 "ESCAPE_SOURCES cardinality drifted from the substrate-\
32733 canonical FIVE non-passthrough arms of \
32734 Atom::decode_str_escape.",
32735 );
32736 assert_eq!(
32737 Atom::ESCAPE_SOURCES.len(),
32738 Atom::NAMED_ESCAPE_TABLE.len() + Atom::SELF_ESCAPE_TABLE.len(),
32739 "ESCAPE_SOURCES cardinality drifted from the SUM of the two \
32740 peer sub-vocabulary arrays' cardinalities — the SPAN \
32741 identity is broken.",
32742 );
32743 }
32744
32745 #[test]
32746 fn atom_escape_sources_pairwise_distinct() {
32747 // PAIRWISE DISJOINTNESS: every row is distinct from every
32748 // other row. The closed-set SPAN inherits pairwise
32749 // disjointness from the two peer sub-vocabularies (each
32750 // pinned pairwise distinct at their own sibling tests) PLUS
32751 // the cross-sub-vocabulary disjointness pinned by
32752 // `atom_self_escape_table_disjoint_from_named_escape_table` —
32753 // this test closes the disjointness contract at the SPAN
32754 // level so a future refactor that added a sixth arm whose
32755 // SOURCE aliased an existing row surfaces HERE rather than
32756 // at a distant reader round-trip. Sibling posture to
32757 // `atom_self_escape_table_pairwise_distinct` +
32758 // `atom_named_escape_table_sources_pairwise_distinct` on the
32759 // two peer sub-vocabularies.
32760 for i in 0..Atom::ESCAPE_SOURCES.len() {
32761 for j in (i + 1)..Atom::ESCAPE_SOURCES.len() {
32762 assert_ne!(
32763 Atom::ESCAPE_SOURCES[i],
32764 Atom::ESCAPE_SOURCES[j],
32765 "ESCAPE_SOURCES bytes at indices {i} and {j} alias \
32766 — every non-passthrough arm must specialize on a \
32767 distinct SOURCE byte.",
32768 );
32769 }
32770 }
32771 }
32772
32773 #[test]
32774 fn atom_escape_sources_partitions_into_named_source_prefix_and_self_source_suffix() {
32775 // PARTITION PIN: the SPAN's canonical declaration order
32776 // encodes a (named prefix, self suffix) partition —
32777 // `ESCAPE_SOURCES[0..3]` IS `NAMED_ESCAPE_TABLE`'s SOURCE
32778 // column, `ESCAPE_SOURCES[3..5]` IS `SELF_ESCAPE_TABLE`. Pin
32779 // the partition at the index level so a reorder that broke
32780 // the sub-vocabulary boundary (e.g. moved `STR_DELIMITER` to
32781 // index 2 to sort alphabetically, or interleaved a self row
32782 // between two named rows) fails HERE rather than as silent
32783 // drift where consumers that walk the array by index would
32784 // route the wrong sub-vocabulary's row through the wrong
32785 // downstream handler.
32786 for (i, &(src, _)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
32787 assert_eq!(
32788 Atom::ESCAPE_SOURCES[i],
32789 src,
32790 "ESCAPE_SOURCES[{i}] ({}) drifted from \
32791 NAMED_ESCAPE_TABLE[{i}].0 ({src}) — the named-prefix \
32792 partition is broken.",
32793 Atom::ESCAPE_SOURCES[i],
32794 );
32795 }
32796 for (i, &self_byte) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
32797 let span_index = Atom::NAMED_ESCAPE_TABLE.len() + i;
32798 assert_eq!(
32799 Atom::ESCAPE_SOURCES[span_index],
32800 self_byte,
32801 "ESCAPE_SOURCES[{span_index}] ({}) drifted from \
32802 SELF_ESCAPE_TABLE[{i}] ({self_byte}) — the self-suffix \
32803 partition is broken.",
32804 Atom::ESCAPE_SOURCES[span_index],
32805 );
32806 }
32807 }
32808
32809 #[test]
32810 fn atom_escape_sources_every_row_projects_through_a_non_passthrough_arm_of_decode_str_escape() {
32811 // NON-PASSTHROUGH-CLOSURE CONTRACT: every row of
32812 // `ESCAPE_SOURCES` MUST project through a NON-passthrough arm
32813 // of `decode_str_escape` — the SPAN is definitionally the set
32814 // of SOURCE bytes for which `decode_str_escape` has a typed
32815 // arm. Pin the closure structurally so a refactor that added
32816 // an `ESCAPE_SOURCES` row without extending
32817 // `decode_str_escape`'s match (or vice versa) surfaces HERE at
32818 // the first drifted row rather than at a distant sweep-site
32819 // regression. The row projects through a non-passthrough arm
32820 // iff EITHER (a) it's a `NAMED_ESCAPE_TABLE` SOURCE and
32821 // `decode_str_escape` returns the paired DECODED byte
32822 // (`decode(src) != src`), OR (b) it's a `SELF_ESCAPE_TABLE`
32823 // row and `decode_str_escape` returns the SAME byte
32824 // (`decode(esc) == esc` by definitional identity — the arm
32825 // exists AND fires, but the projection is pattern-EQUALS-
32826 // value). Both arm-kinds are non-passthrough because they
32827 // have a TYPED arm in the match; the `other => other`
32828 // passthrough only fires for chars NOT in the SPAN. Pin the
32829 // dispatch identity by checking each row lives in exactly ONE
32830 // of the two peer sub-vocabularies.
32831 for (i, &esc) in Atom::ESCAPE_SOURCES.iter().enumerate() {
32832 let in_named = Atom::NAMED_ESCAPE_TABLE.iter().any(|&(src, _)| src == esc);
32833 let in_self = Atom::SELF_ESCAPE_TABLE.contains(&esc);
32834 assert!(
32835 in_named ^ in_self,
32836 "ESCAPE_SOURCES[{i}] ({esc:?}) must belong to EXACTLY \
32837 ONE peer sub-vocabulary (in_named={in_named}, \
32838 in_self={in_self}) — the SPAN identity requires each \
32839 row to fire a typed non-passthrough arm on either \
32840 the pattern-DISTINCT-from-value axis (NAMED) OR the \
32841 pattern-EQUALS-value axis (SELF), never both, never \
32842 neither.",
32843 );
32844 // Consistency check: the arm's projection must agree with
32845 // the row's sub-vocabulary identity. NAMED rows project to
32846 // the paired DECODED byte (which the pairwise-distinct pin
32847 // guarantees differs from SOURCE); SELF rows project to
32848 // the row byte itself.
32849 let decoded = Atom::decode_str_escape(esc);
32850 if in_named {
32851 assert_ne!(
32852 decoded, esc,
32853 "ESCAPE_SOURCES[{i}] ({esc:?}) is a NAMED SOURCE — \
32854 decode_str_escape MUST project to the paired \
32855 DECODED byte (distinct from SOURCE) but returned \
32856 {decoded:?}.",
32857 );
32858 } else {
32859 assert_eq!(
32860 decoded, esc,
32861 "ESCAPE_SOURCES[{i}] ({esc:?}) is a SELF row — \
32862 decode_str_escape MUST project to the same byte \
32863 (pattern-EQUALS-value by definitional identity) \
32864 but returned {decoded:?}.",
32865 );
32866 }
32867 }
32868 }
32869
32870 #[test]
32871 fn atom_decode_str_escape_composes_end_to_end_through_reader_for_every_named_arm() {
32872 // END-TO-END COMPOSITION CONTRACT: pin that every typed
32873 // escape-table arm — the three named-escape arms + the two
32874 // pattern-equals-value self-escape arms + a representative
32875 // passthrough — decodes through the reader's full
32876 // escape-handler pipeline via ONE
32877 // `Atom::decode_str_escape(esc)` call. Wrap each `esc` in a
32878 // STR_DELIMITER-wrapped, STR_ESCAPE_LEAD-led source, run it
32879 // through [`crate::reader::read`], and assert the resulting
32880 // Str payload equals `decode_str_escape(esc).to_string()`.
32881 // A regression that re-inlined the reader's table (e.g.
32882 // reverted the escape-handler branch to per-arm literals AND
32883 // added a sixth named-escape arm to
32884 // [`Atom::decode_str_escape`] without updating the reader)
32885 // fails HERE at the first arm whose decode diverges.
32886 //
32887 // Sibling-shape pin to
32888 // `atom_str_escape_lead_closes_reader_self_escape_round_trip_for_backslash_payload`
32889 // — where that pin exercises the SINGLE self-escape arm on
32890 // the escape-lead axis end-to-end, this sweep exercises the
32891 // FULL closed-set table on the same axis.
32892 // Pre-lift the (NAMED_ESCAPE_TABLE.iter().map(|&(src, _)| src)
32893 // .chain(SELF_ESCAPE_TABLE.iter().copied())) runtime iterator
32894 // chain reassembled the FIVE non-passthrough source bytes at
32895 // this callsite; post-lift the SPAN binds at ONE forced-arity
32896 // `Atom::ESCAPE_SOURCES: [char; 5]` on the closed-set [`Atom`]
32897 // algebra so the sweep iterates the typed ALL array directly.
32898 // The trailing `'x'` is a representative PASSTHROUGH byte
32899 // (deliberately NOT in `ESCAPE_SOURCES`) — the sweep exercises
32900 // BOTH the non-passthrough closed set AND the passthrough
32901 // default-arm through the same reader pipeline in ONE loop.
32902 let escs: Vec<char> = Atom::ESCAPE_SOURCES
32903 .iter()
32904 .copied()
32905 .chain(std::iter::once('x'))
32906 .collect();
32907 for esc in escs {
32908 let source = format!(
32909 "{}{}{}{}",
32910 Atom::STR_DELIMITER,
32911 Atom::STR_ESCAPE_LEAD,
32912 esc,
32913 Atom::STR_DELIMITER,
32914 );
32915 let forms = crate::reader::read(&source).unwrap_or_else(|e| {
32916 panic!(
32917 "reader rejected `{source}` composed from \
32918 STR_DELIMITER + STR_ESCAPE_LEAD + `{esc}`: {e}"
32919 )
32920 });
32921 assert_eq!(
32922 forms.len(),
32923 1,
32924 "escape sweep for `{esc}` must read as exactly one form, \
32925 got {forms:?}",
32926 );
32927 let decoded = Atom::decode_str_escape(esc);
32928 assert_eq!(
32929 forms[0],
32930 Sexp::Atom(Atom::string(decoded.to_string())),
32931 "escape sweep for `{esc}` drifted from \
32932 Sexp::Atom(Atom::string(decode_str_escape({esc:?}) = \
32933 {decoded:?}).to_string()) — the reader's escape-handler \
32934 branch OR Atom::decode_str_escape drifted from the ONE \
32935 shared closed-set escape-table projection",
32936 );
32937 }
32938 }
32939
32940 // ── `Atom::ESCAPE_DECODED` — the closed-set forced-arity ALL array
32941 // over every DECODED byte `Atom::decode_str_escape` can emit from a
32942 // non-passthrough arm. Column-dual peer of `Atom::ESCAPE_SOURCES` at
32943 // the SAME `[char; 5]` shape on the SAME closed-set [`Atom`] algebra:
32944 // together the two arrays close the (SOURCE, DECODED) cross-product
32945 // of `decode_str_escape`'s non-passthrough arm-set at two byte-
32946 // identical `[char; 5]` shapes. Cross-sub-vocabulary SPAN peer of
32947 // `NAMED_ESCAPE_TABLE` (three pattern-DISTINCT-from-value rows'
32948 // DECODED column) + `SELF_ESCAPE_TABLE` (two pattern-EQUALS-value
32949 // rows whose DECODED column is definitionally the row byte itself)
32950 // at ONE typed `[char; 5]` on the SAME closed-set [`Atom`] algebra.
32951 // The pins below anchor (a) the SPAN composition against the two
32952 // peer sub-vocabulary arrays' DECODED columns in canonical
32953 // declaration order, (b) the forced-arity cardinality against a
32954 // refactor that loosens the array to `&[char]`, (c) pairwise
32955 // disjointness on the DECODED column, (d) the load-bearing
32956 // partition NAMED-decoded-column prefix / SELF-decoded-column suffix
32957 // that the declaration order encodes for consumers that walk the
32958 // array by index, and (e) the column-dual POINTWISE projection
32959 // identity that pins `ESCAPE_DECODED[i] ==
32960 // decode_str_escape(ESCAPE_SOURCES[i])` for every index — the
32961 // NEW load-bearing invariant this SPAN adds on top of `ESCAPE_SOURCES`.
32962
32963 #[test]
32964 fn atom_escape_decoded_composes_from_named_and_self_escape_sub_vocabularies_in_declaration_order(
32965 ) {
32966 // COMPOSITION LAW: the ALL array's rows are (NAMED_ESCAPE_TABLE
32967 // DECODED column, then SELF_ESCAPE_TABLE rows) in canonical
32968 // declaration order matching `decode_str_escape`'s match-arm
32969 // order. Pins the SPAN composition at ONE site so a reorder
32970 // that broke the (named prefix, self suffix) partition (e.g.
32971 // interleaving the two sub-vocabularies' rows, sorting by C0
32972 // byte value) silently misaligns every consumer that sweeps the
32973 // array by index. Sibling-shape pin to
32974 // `atom_escape_sources_composes_from_named_and_self_escape_sub_vocabularies_in_declaration_order`
32975 // one column over on the peer SOURCE-column SPAN at
32976 // `Atom::ESCAPE_SOURCES`.
32977 assert_eq!(
32978 Atom::ESCAPE_DECODED,
32979 [
32980 Atom::NAMED_ESCAPE_TABLE[0].1,
32981 Atom::NAMED_ESCAPE_TABLE[1].1,
32982 Atom::NAMED_ESCAPE_TABLE[2].1,
32983 Atom::SELF_ESCAPE_TABLE[0],
32984 Atom::SELF_ESCAPE_TABLE[1],
32985 ],
32986 "ESCAPE_DECODED composition drifted from the canonical \
32987 (NAMED DECODED column, then SELF rows) SPAN in declaration \
32988 order — the SPAN lift must route through the two peer \
32989 sub-vocabulary arrays' rows in the exact order \
32990 decode_str_escape's match arms fire on them.",
32991 );
32992 }
32993
32994 #[test]
32995 fn atom_escape_decoded_has_expected_cardinality() {
32996 // ARITY PIN: the forced-arity `[char; 5]` shape survives at
32997 // runtime AND matches the two peer sub-vocabularies' summed
32998 // cardinality AND matches the column-dual peer
32999 // `ESCAPE_SOURCES`'s arity. Pins the closed-set size against a
33000 // refactor that loosens the array's type to `&'static [char]`
33001 // or `Vec<char>` (which would drop the compile-time arity
33002 // forcing rustc bakes into `[T; N]` declarations) AND against a
33003 // refactor that drifted this array's arity without extending
33004 // one of the two peer sub-vocabularies (which would silently
33005 // desynchronize the SPAN identity from its two source arrays)
33006 // AND against a refactor that broke the column-dual shape
33007 // symmetry with `ESCAPE_SOURCES`. Sibling posture to
33008 // `atom_escape_sources_has_expected_cardinality` on the peer
33009 // SOURCE-column SPAN.
33010 assert_eq!(
33011 Atom::ESCAPE_DECODED.len(),
33012 5,
33013 "ESCAPE_DECODED cardinality drifted from the substrate-\
33014 canonical FIVE non-passthrough arms of \
33015 Atom::decode_str_escape.",
33016 );
33017 assert_eq!(
33018 Atom::ESCAPE_DECODED.len(),
33019 Atom::NAMED_ESCAPE_TABLE.len() + Atom::SELF_ESCAPE_TABLE.len(),
33020 "ESCAPE_DECODED cardinality drifted from the SUM of the two \
33021 peer sub-vocabulary arrays' cardinalities — the SPAN \
33022 identity is broken.",
33023 );
33024 assert_eq!(
33025 Atom::ESCAPE_DECODED.len(),
33026 Atom::ESCAPE_SOURCES.len(),
33027 "ESCAPE_DECODED cardinality drifted from the column-dual peer \
33028 ESCAPE_SOURCES's cardinality — the column-dual shape \
33029 symmetry `[char; 5]` × `[char; 5]` on decode_str_escape's \
33030 non-passthrough arm-set is broken.",
33031 );
33032 }
33033
33034 #[test]
33035 fn atom_escape_decoded_pairwise_distinct() {
33036 // PAIRWISE DISJOINTNESS: every row is distinct from every
33037 // other row. On the DECODED column this is TIGHTER than on
33038 // the SOURCE column: the NAMED sub-vocabulary's DECODED rows
33039 // are C0 control bytes (`'\n'`, `'\t'`, `'\r'` — bytes 0x0A,
33040 // 0x09, 0x0D) and the SELF sub-vocabulary's rows are printable
33041 // ASCII bytes (`'"'`, `'\\'` — bytes 0x22, 0x5C), so no
33042 // NAMED-DECODED byte can alias a SELF byte by byte-class
33043 // disjointness. This test closes the disjointness contract at
33044 // the SPAN level so a future refactor that added a sixth arm
33045 // whose DECODED aliased an existing row (e.g. drifted
33046 // TAB_ESCAPE_DECODED from `'\t'` to `'\n'`, collapsing two
33047 // arms onto the same DECODED byte) surfaces HERE rather than
33048 // at a distant reader round-trip. Sibling posture to
33049 // `atom_escape_sources_pairwise_distinct` on the peer
33050 // SOURCE-column SPAN.
33051 for i in 0..Atom::ESCAPE_DECODED.len() {
33052 for j in (i + 1)..Atom::ESCAPE_DECODED.len() {
33053 assert_ne!(
33054 Atom::ESCAPE_DECODED[i],
33055 Atom::ESCAPE_DECODED[j],
33056 "ESCAPE_DECODED bytes at indices {i} and {j} alias \
33057 — every non-passthrough arm must emit a distinct \
33058 DECODED byte.",
33059 );
33060 }
33061 }
33062 }
33063
33064 #[test]
33065 fn atom_escape_decoded_partitions_into_named_decoded_prefix_and_self_decoded_suffix() {
33066 // PARTITION PIN: the SPAN's canonical declaration order
33067 // encodes a (named prefix, self suffix) partition —
33068 // `ESCAPE_DECODED[0..3]` IS `NAMED_ESCAPE_TABLE`'s DECODED
33069 // column, `ESCAPE_DECODED[3..5]` IS `SELF_ESCAPE_TABLE` (the
33070 // SELF sub-vocabulary's DECODED column IS its row column by
33071 // pattern-EQUALS-value identity). Pin the partition at the
33072 // index level so a reorder that broke the sub-vocabulary
33073 // boundary (e.g. moved a SELF row to index 2 to sort by C0
33074 // byte value, or interleaved a self row between two named
33075 // rows) fails HERE rather than as silent drift where consumers
33076 // that walk the array by index would route the wrong
33077 // sub-vocabulary's row through the wrong downstream handler.
33078 // Sibling-shape pin to
33079 // `atom_escape_sources_partitions_into_named_source_prefix_and_self_source_suffix`
33080 // one column over on the peer SOURCE-column SPAN.
33081 for (i, &(_, dec)) in Atom::NAMED_ESCAPE_TABLE.iter().enumerate() {
33082 assert_eq!(
33083 Atom::ESCAPE_DECODED[i],
33084 dec,
33085 "ESCAPE_DECODED[{i}] ({}) drifted from \
33086 NAMED_ESCAPE_TABLE[{i}].1 ({dec}) — the named-decoded-\
33087 prefix partition is broken.",
33088 Atom::ESCAPE_DECODED[i],
33089 );
33090 }
33091 for (i, &self_byte) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
33092 let span_index = Atom::NAMED_ESCAPE_TABLE.len() + i;
33093 assert_eq!(
33094 Atom::ESCAPE_DECODED[span_index],
33095 self_byte,
33096 "ESCAPE_DECODED[{span_index}] ({}) drifted from \
33097 SELF_ESCAPE_TABLE[{i}] ({self_byte}) — the self-decoded-\
33098 suffix partition is broken (SELF rows are pattern-\
33099 EQUALS-value so DECODED == SOURCE by definitional \
33100 identity).",
33101 Atom::ESCAPE_DECODED[span_index],
33102 );
33103 }
33104 }
33105
33106 #[test]
33107 fn atom_escape_decoded_projects_pointwise_from_escape_sources_through_decode_str_escape() {
33108 // COLUMN-DUAL POINTWISE PROJECTION LAW: the two forced-arity
33109 // `[char; 5]` peer arrays [`Atom::ESCAPE_SOURCES`] +
33110 // [`Atom::ESCAPE_DECODED`] are the SOURCE column and DECODED
33111 // column of `decode_str_escape`'s non-passthrough arm-set — for
33112 // every index `i` in `0..5`,
33113 // `ESCAPE_DECODED[i] == Atom::decode_str_escape(
33114 // ESCAPE_SOURCES[i])`. This is the NEW load-bearing invariant
33115 // this SPAN adds on top of `ESCAPE_SOURCES`: the column-dual
33116 // pointwise projection identity that pins the two `[char; 5]`
33117 // arrays as the two columns of the SAME arm-set at the SAME
33118 // row-order at rustc time. Pin the projection identity
33119 // structurally so a refactor that drifted either array's
33120 // declaration order OR drifted `decode_str_escape`'s match-arm
33121 // ordering surfaces HERE at the first drifted index rather
33122 // than at a distant sweep site. A regression that swapped
33123 // (e.g.) rows 3 and 4 in `ESCAPE_DECODED` without swapping
33124 // them in `ESCAPE_SOURCES` (or vice versa) would silently
33125 // collapse the column-dual identity; this pin catches the
33126 // divergence at the first drifted index.
33127 for (i, &src) in Atom::ESCAPE_SOURCES.iter().enumerate() {
33128 let expected = Atom::decode_str_escape(src);
33129 assert_eq!(
33130 Atom::ESCAPE_DECODED[i],
33131 expected,
33132 "ESCAPE_DECODED[{i}] ({}) drifted from \
33133 decode_str_escape(ESCAPE_SOURCES[{i}] = {src:?}) = \
33134 {expected:?} — the column-dual pointwise projection \
33135 law from ESCAPE_SOURCES onto ESCAPE_DECODED through \
33136 decode_str_escape is broken at index {i}.",
33137 Atom::ESCAPE_DECODED[i],
33138 );
33139 }
33140 }
33141
33142 // ── `Atom::ESCAPE_TABLE` — the paired-column SPAN closing BOTH
33143 // columns of `decode_str_escape`'s FIVE non-passthrough arms at
33144 // ONE typed forced-arity `[(char, char); 5]` on the closed-set
33145 // [`Atom`] algebra. Peer-collapse of the two column-dual
33146 // `[char; 5]` peer arrays `Atom::ESCAPE_SOURCES` and
33147 // `Atom::ESCAPE_DECODED` at the paired-shape level. Tests pin
33148 // (a) the pointwise composition law `ESCAPE_TABLE[i] ==
33149 // (ESCAPE_SOURCES[i], ESCAPE_DECODED[i])`, (b) the forced-arity
33150 // `[(char, char); 5]` shape, (c) the sub-vocabulary partition into
33151 // NAMED-paired prefix + SELF-reshape suffix (the SELF sub-
33152 // vocabulary's rows re-shape from `char` to `(row, row)` via the
33153 // definitional-identity collapse), (d) the pattern-classification
33154 // partition where NAMED rows carry `row.0 != row.1` and SELF rows
33155 // carry `row.0 == row.1`, and (e) the pointwise projection law
33156 // `decode_str_escape(row.0) == row.1` for every row.
33157
33158 #[test]
33159 fn atom_escape_table_composes_pointwise_from_escape_sources_and_escape_decoded_column_duals() {
33160 // POINTWISE COMPOSITION LAW: the paired-column SPAN's rows are
33161 // the two column-dual peer arrays zipped pointwise — for every
33162 // index `i` in `0..5`, `ESCAPE_TABLE[i] ==
33163 // (ESCAPE_SOURCES[i], ESCAPE_DECODED[i])`. This is the load-
33164 // bearing pointwise identity carrying the two-column
33165 // composition relation between the paired SPAN and its two
33166 // column-dual peer SPANs. A refactor that drifted any of the
33167 // three arrays' declaration orders OR drifted
33168 // `decode_str_escape`'s match-arm ordering surfaces HERE at
33169 // the first drifted index rather than at a distant sweep site.
33170 // Sibling-shape pin to
33171 // `atom_escape_decoded_projects_pointwise_from_escape_sources_through_decode_str_escape`
33172 // one composition layer up: where that pin binds the DECODED-
33173 // column SPAN to the SOURCE-column SPAN through
33174 // `decode_str_escape`, this pin binds the paired-column SPAN
33175 // to the two column-dual peer SPANs through pointwise
33176 // composition.
33177 assert_eq!(Atom::ESCAPE_TABLE.len(), Atom::ESCAPE_SOURCES.len());
33178 assert_eq!(Atom::ESCAPE_TABLE.len(), Atom::ESCAPE_DECODED.len());
33179 for (i, &(src, decoded)) in Atom::ESCAPE_TABLE.iter().enumerate() {
33180 assert_eq!(
33181 src,
33182 Atom::ESCAPE_SOURCES[i],
33183 "ESCAPE_TABLE[{i}].0 ({src:?}) drifted from \
33184 ESCAPE_SOURCES[{i}] ({:?}) — the paired-column SPAN's \
33185 SOURCE-column projection is broken at index {i}.",
33186 Atom::ESCAPE_SOURCES[i],
33187 );
33188 assert_eq!(
33189 decoded,
33190 Atom::ESCAPE_DECODED[i],
33191 "ESCAPE_TABLE[{i}].1 ({decoded:?}) drifted from \
33192 ESCAPE_DECODED[{i}] ({:?}) — the paired-column SPAN's \
33193 DECODED-column projection is broken at index {i}.",
33194 Atom::ESCAPE_DECODED[i],
33195 );
33196 }
33197 }
33198
33199 #[test]
33200 fn atom_escape_table_has_expected_cardinality() {
33201 // ARITY PIN: the forced-arity `[(char, char); 5]` shape
33202 // survives at runtime AND matches the two column-dual peer
33203 // arrays' arities AND matches the summed cardinality of the
33204 // two sub-vocabulary arrays. Pins the closed-set size against
33205 // a refactor that loosens the array's type to
33206 // `&'static [(char, char)]` or `Vec<(char, char)>` (which
33207 // would drop the compile-time arity forcing rustc bakes into
33208 // `[T; N]` declarations) AND against a refactor that drifted
33209 // this array's arity without extending one of the two peer
33210 // column-dual arrays or one of the two peer sub-vocabulary
33211 // arrays. Sibling posture to
33212 // `atom_escape_sources_has_expected_cardinality` +
33213 // `atom_escape_decoded_has_expected_cardinality` on the two
33214 // column-dual peer SPANs.
33215 assert_eq!(
33216 Atom::ESCAPE_TABLE.len(),
33217 5,
33218 "ESCAPE_TABLE cardinality drifted from the substrate-\
33219 canonical FIVE non-passthrough arms of \
33220 Atom::decode_str_escape.",
33221 );
33222 assert_eq!(
33223 Atom::ESCAPE_TABLE.len(),
33224 Atom::ESCAPE_SOURCES.len(),
33225 "ESCAPE_TABLE cardinality drifted from the column-dual \
33226 peer ESCAPE_SOURCES's cardinality — the paired-column \
33227 shape symmetry `[(char, char); 5]` × `[char; 5]` on \
33228 decode_str_escape's non-passthrough arm-set is broken on \
33229 the SOURCE column.",
33230 );
33231 assert_eq!(
33232 Atom::ESCAPE_TABLE.len(),
33233 Atom::ESCAPE_DECODED.len(),
33234 "ESCAPE_TABLE cardinality drifted from the column-dual \
33235 peer ESCAPE_DECODED's cardinality — the paired-column \
33236 shape symmetry `[(char, char); 5]` × `[char; 5]` on \
33237 decode_str_escape's non-passthrough arm-set is broken on \
33238 the DECODED column.",
33239 );
33240 assert_eq!(
33241 Atom::ESCAPE_TABLE.len(),
33242 Atom::NAMED_ESCAPE_TABLE.len() + Atom::SELF_ESCAPE_TABLE.len(),
33243 "ESCAPE_TABLE cardinality drifted from the SUM of the two \
33244 peer sub-vocabulary arrays' cardinalities — the paired-\
33245 column SPAN identity is broken.",
33246 );
33247 }
33248
33249 #[test]
33250 fn atom_escape_table_partitions_into_named_paired_prefix_and_self_reshape_suffix() {
33251 // SUB-VOCABULARY PARTITION LAW: `ESCAPE_TABLE[0..3]` IS the
33252 // NAMED_ESCAPE_TABLE (pattern-DISTINCT-from-value sub-
33253 // vocabulary at its native paired shape passes through
33254 // identically); `ESCAPE_TABLE[3..5]` re-shapes the SELF_ESCAPE_TABLE
33255 // rows from `char` to `(row, row)` via the definitional-
33256 // identity collapse (pattern-EQUALS-value sub-vocabulary). The
33257 // index-level partition pin binds every consumer walking the
33258 // paired-column SPAN by index to the (named-paired prefix,
33259 // self-reshape suffix) partition at rustc time. Sibling
33260 // posture to
33261 // `atom_escape_sources_partitions_into_named_source_prefix_and_self_source_suffix`
33262 // + `atom_escape_decoded_partitions_into_named_decoded_prefix_and_self_decoded_suffix`
33263 // on the two column-dual peer SPANs.
33264 assert_eq!(
33265 &Atom::ESCAPE_TABLE[0..3],
33266 &Atom::NAMED_ESCAPE_TABLE[..],
33267 "ESCAPE_TABLE named-paired prefix drifted from \
33268 NAMED_ESCAPE_TABLE — the sub-vocabulary partition is \
33269 broken.",
33270 );
33271 for (self_index, &row) in Atom::SELF_ESCAPE_TABLE.iter().enumerate() {
33272 let span_index = Atom::NAMED_ESCAPE_TABLE.len() + self_index;
33273 assert_eq!(
33274 Atom::ESCAPE_TABLE[span_index],
33275 (row, row),
33276 "ESCAPE_TABLE[{span_index}] ({:?}) drifted from the \
33277 SELF-reshape suffix pair (SELF_ESCAPE_TABLE[{self_index}], \
33278 SELF_ESCAPE_TABLE[{self_index}]) = ({row:?}, \
33279 {row:?}) — the pattern-EQUALS-value definitional-\
33280 identity reshape is broken.",
33281 Atom::ESCAPE_TABLE[span_index],
33282 );
33283 }
33284 }
33285
33286 #[test]
33287 fn atom_escape_table_named_prefix_is_pattern_distinct_and_self_suffix_is_pattern_equals() {
33288 // PATTERN-CLASSIFICATION PARTITION LAW: the paired-column
33289 // SPAN's rows encode their sub-vocabulary identity in the
33290 // per-row per-column identity relation — NAMED rows (indices
33291 // 0..3) carry `row.0 != row.1` (pattern-DISTINCT-from-value),
33292 // SELF rows (indices 3..5) carry `row.0 == row.1` (pattern-
33293 // EQUALS-value). A consumer classifying an escape arm reads
33294 // the sub-vocabulary off `row.0 == row.1` rather than off an
33295 // index range or a separate tag — the classification IS the
33296 // pair's per-column identity relation. Pins the load-bearing
33297 // structural invariant that the paired-column SPAN encodes
33298 // both sub-vocabularies AT the paired shape.
33299 for i in 0..Atom::NAMED_ESCAPE_TABLE.len() {
33300 let (src, decoded) = Atom::ESCAPE_TABLE[i];
33301 assert_ne!(
33302 src, decoded,
33303 "ESCAPE_TABLE[{i}] ({src:?}, {decoded:?}) is in the \
33304 NAMED prefix but its per-column identity relation \
33305 collapsed to `src == decoded` — the pattern-DISTINCT-\
33306 from-value classification is broken.",
33307 );
33308 }
33309 for self_index in 0..Atom::SELF_ESCAPE_TABLE.len() {
33310 let span_index = Atom::NAMED_ESCAPE_TABLE.len() + self_index;
33311 let (src, decoded) = Atom::ESCAPE_TABLE[span_index];
33312 assert_eq!(
33313 src, decoded,
33314 "ESCAPE_TABLE[{span_index}] ({src:?}, {decoded:?}) is \
33315 in the SELF suffix but its per-column identity \
33316 relation split to `src != decoded` — the pattern-\
33317 EQUALS-value definitional-identity classification is \
33318 broken.",
33319 );
33320 }
33321 }
33322
33323 #[test]
33324 fn atom_escape_table_every_row_projects_through_decode_str_escape_pointwise() {
33325 // POINTWISE PROJECTION LAW: for every `(src, decoded)` row in
33326 // ESCAPE_TABLE, `Atom::decode_str_escape(src) == decoded`. The
33327 // paired-column SPAN closes the (input, output) cross-product
33328 // of `decode_str_escape`'s non-passthrough arm-set at ONE typed
33329 // array so a refactor that drifted the arm-set (swapped rows
33330 // in ESCAPE_TABLE without swapping them in `decode_str_escape`
33331 // or vice versa) surfaces HERE at the first drifted pair
33332 // rather than at a distant sweep site. Sibling-shape pin to
33333 // `atom_escape_decoded_projects_pointwise_from_escape_sources_through_decode_str_escape`
33334 // one composition layer over: where that pin binds the two
33335 // column-dual peer SPANs through `decode_str_escape`, this pin
33336 // binds the paired-column SPAN's per-row `(src, decoded)`
33337 // shape to `decode_str_escape`'s projection at the row level.
33338 for (i, &(src, decoded)) in Atom::ESCAPE_TABLE.iter().enumerate() {
33339 let projected = Atom::decode_str_escape(src);
33340 assert_eq!(
33341 projected, decoded,
33342 "ESCAPE_TABLE[{i}] = ({src:?}, {decoded:?}) drifted \
33343 from Atom::decode_str_escape({src:?}) = {projected:?} \
33344 — the paired-column SPAN's per-row projection law is \
33345 broken at index {i}.",
33346 );
33347 }
33348 }
33349
33350 #[test]
33351 fn atom_escape_table_sources_and_decoded_columns_pairwise_distinct() {
33352 // COLUMN-WISE PAIRWISE DISJOINTNESS: the paired-column SPAN's
33353 // SOURCE column AND DECODED column are each pairwise distinct.
33354 // Inherited disjointness from the two column-dual peer
33355 // `[char; 5]` SPANs already pinned at
33356 // `atom_escape_sources_pairwise_distinct` +
33357 // `atom_escape_decoded_pairwise_distinct`, but pinned HERE at
33358 // the paired-array level so a refactor that drifted the paired
33359 // SPAN's declaration order (collapsing two arms onto the same
33360 // SOURCE or DECODED byte) surfaces at this test rather than
33361 // at a distant sweep site. Sibling posture to the two column-
33362 // dual peer SPANs' pairwise-distinctness tests.
33363 for i in 0..Atom::ESCAPE_TABLE.len() {
33364 for j in (i + 1)..Atom::ESCAPE_TABLE.len() {
33365 assert_ne!(
33366 Atom::ESCAPE_TABLE[i].0,
33367 Atom::ESCAPE_TABLE[j].0,
33368 "ESCAPE_TABLE SOURCE column collision: \
33369 ESCAPE_TABLE[{i}].0 and ESCAPE_TABLE[{j}].0 are \
33370 both {:?} — the paired-column SPAN's SOURCE \
33371 column pairwise disjointness is broken.",
33372 Atom::ESCAPE_TABLE[i].0,
33373 );
33374 assert_ne!(
33375 Atom::ESCAPE_TABLE[i].1,
33376 Atom::ESCAPE_TABLE[j].1,
33377 "ESCAPE_TABLE DECODED column collision: \
33378 ESCAPE_TABLE[{i}].1 and ESCAPE_TABLE[{j}].1 are \
33379 both {:?} — the paired-column SPAN's DECODED \
33380 column pairwise disjointness is broken.",
33381 Atom::ESCAPE_TABLE[i].1,
33382 );
33383 }
33384 }
33385 }
33386
33387 // ── `Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE` — the paired canonical
33388 // `(` / `)` chars routed through the FOUR outer-structural round-
33389 // trip sites: two `crate::reader::tokenize` outer-dispatch arms
33390 // (`Token::LParen`, `Token::RParen`), two bare-atom terminator
33391 // disjuncts (`|| ch == LIST_OPEN` / `|| ch == LIST_CLOSE`), and
33392 // two `fmt::Display for Sexp` arms (`Self::List(_)` opener +
33393 // closer AND `Self::Nil` two-char `()`). Sibling-shape tests to
33394 // the `atom_str_delimiter_*` block above (Str-payload delimiter
33395 // axis), lifted onto the outer-structural [`Sexp`] algebra.
33396
33397 #[test]
33398 fn sexp_list_open_projects_canonical_open_paren_char() {
33399 // Pins the constant's exact `char` value so a typo (`'['`,
33400 // `'{'`, `';'`) or an accidental redefinition surfaces
33401 // immediately. Sibling-shape pin to
33402 // `atom_str_delimiter_projects_canonical_double_quote_char`
33403 // (the Str-payload delimiter axis) — pins the SAME shape on
33404 // the outer list-opener axis of the closed-set outer [`Sexp`]
33405 // algebra.
33406 assert_eq!(
33407 Sexp::LIST_OPEN,
33408 '(',
33409 "LIST_OPEN char drifted from the substrate-canonical `(` \
33410 opener — the reader-round-trip contract at \
33411 crate::reader::tokenize (Token::LParen outer arm, bare-\
33412 atom terminator disjunct) AND fmt::Display for Sexp \
33413 (Self::List(_) opener, Self::Nil two-char left) all bind \
33414 to this ONE constant.",
33415 );
33416 }
33417
33418 #[test]
33419 fn sexp_list_close_projects_canonical_close_paren_char() {
33420 // Pins the constant's exact `char` value so a typo (`']'`,
33421 // `'}'`, `';'`) or an accidental redefinition surfaces
33422 // immediately. Section-for-retraction sibling pin of the
33423 // opener above; the paired-delimiter round-trip contract
33424 // holds iff both constants pin their canonical bytes here.
33425 assert_eq!(
33426 Sexp::LIST_CLOSE,
33427 ')',
33428 "LIST_CLOSE char drifted from the substrate-canonical `)` \
33429 closer — the reader-round-trip contract at \
33430 crate::reader::tokenize (Token::RParen outer arm, bare-\
33431 atom terminator disjunct) AND fmt::Display for Sexp \
33432 (Self::List(_) closer, Self::Nil two-char right) all \
33433 bind to this ONE constant.",
33434 );
33435 }
33436
33437 #[test]
33438 fn sexp_list_delimiters_distinct_from_every_other_algebra_marker() {
33439 // Cross-axis disjointness pin: neither `Sexp::LIST_OPEN` nor
33440 // `Sexp::LIST_CLOSE` may alias any sibling outer-marker
33441 // char on the substrate's other closed-set algebras — the
33442 // Str-payload delimiter (`Atom::STR_DELIMITER`), the
33443 // Keyword-marker prefix (`Atom::KEYWORD_MARKER`), the two
33444 // Bool-literal spellings (`Atom::bool_literal(true|false)`),
33445 // AND every quote-family lead char
33446 // (`QuoteForm::lead_char(qf)` for each `qf` in
33447 // `QuoteForm::ALL`). Otherwise a bare `(`/`)`-starting lexeme
33448 // would ambiguously route through TWO outer-dispatch arms in
33449 // `crate::reader::tokenize`. Guards the paired disjointness
33450 // across the substrate's outer-marker axes so a future
33451 // refactor that swaps a marker to collide with either list
33452 // delimiter surfaces at this pin rather than as a silent
33453 // reader misclassification.
33454 //
33455 // First: opener/closer disjoint from each other — pairs
33456 // MUST be structurally distinct.
33457 assert_ne!(
33458 Sexp::LIST_OPEN,
33459 Sexp::LIST_CLOSE,
33460 "LIST_OPEN and LIST_CLOSE share a byte — the paired-\
33461 delimiter contract would collapse.",
33462 );
33463
33464 // Second: opener/closer disjoint from Str-delimiter.
33465 assert_ne!(
33466 Sexp::LIST_OPEN,
33467 Atom::STR_DELIMITER,
33468 "LIST_OPEN and STR_DELIMITER share a byte — a bare `{}foo` \
33469 lexeme would ambiguously begin a list AND open a string.",
33470 Atom::STR_DELIMITER,
33471 );
33472 assert_ne!(
33473 Sexp::LIST_CLOSE,
33474 Atom::STR_DELIMITER,
33475 "LIST_CLOSE and STR_DELIMITER share a byte — a bare `{}foo` \
33476 lexeme would ambiguously close a list AND open a string.",
33477 Atom::STR_DELIMITER,
33478 );
33479
33480 // Third: opener/closer disjoint from KEYWORD_MARKER's LEAD
33481 // `char` (single-char `":"` on the outer axis) — the
33482 // `Atom::KEYWORD_MARKER` `&'static str`'s projected lead byte
33483 // lives at `Atom::KEYWORD_MARKER_LEAD` on the closed-set
33484 // outer [`Atom`] algebra.
33485 assert_ne!(
33486 Sexp::LIST_OPEN,
33487 Atom::KEYWORD_MARKER_LEAD,
33488 "LIST_OPEN and KEYWORD_MARKER_LEAD share a byte — a bare \
33489 `{}foo` lexeme would ambiguously begin a list AND begin \
33490 a keyword.",
33491 Atom::KEYWORD_MARKER_LEAD,
33492 );
33493 assert_ne!(
33494 Sexp::LIST_CLOSE,
33495 Atom::KEYWORD_MARKER_LEAD,
33496 "LIST_CLOSE and KEYWORD_MARKER_LEAD share a byte — a bare \
33497 `{}foo` lexeme would ambiguously close a list AND begin \
33498 a keyword.",
33499 Atom::KEYWORD_MARKER_LEAD,
33500 );
33501
33502 // Fourth: opener/closer disjoint from every Bool-literal
33503 // spelling's first char (`'#'` for both `"#t"` / `"#f"`).
33504 for b in [true, false] {
33505 let lead = Atom::bool_literal(b)
33506 .chars()
33507 .next()
33508 .expect("bool_literal must be non-empty");
33509 assert_ne!(
33510 Sexp::LIST_OPEN,
33511 lead,
33512 "LIST_OPEN and bool_literal({b:?}) share a lead byte — a \
33513 bare `{lead}...` lexeme would ambiguously begin a list \
33514 AND classify as a Bool.",
33515 );
33516 assert_ne!(
33517 Sexp::LIST_CLOSE,
33518 lead,
33519 "LIST_CLOSE and bool_literal({b:?}) share a lead byte — a \
33520 bare `{lead}...` lexeme would ambiguously close a list \
33521 AND classify as a Bool.",
33522 );
33523 }
33524
33525 // Fifth: opener/closer disjoint from every quote-family
33526 // lead char (`'\''`, `` '`' ``, `','` — three distinct lead
33527 // chars across the four `QuoteForm` variants). The reader's
33528 // outer-dispatch orders the quote-family arm BEFORE the
33529 // list-delimiter arms, so a collision here would silently
33530 // route `(`/`)` through the quote-family branch.
33531 for qf in QuoteForm::ALL {
33532 assert_ne!(
33533 Sexp::LIST_OPEN,
33534 qf.lead_char(),
33535 "LIST_OPEN and QuoteForm::{qf:?}::lead_char share a byte — \
33536 a bare `(...)` list would silently route through the \
33537 quote-family outer-dispatch arm.",
33538 );
33539 assert_ne!(
33540 Sexp::LIST_CLOSE,
33541 qf.lead_char(),
33542 "LIST_CLOSE and QuoteForm::{qf:?}::lead_char share a byte — \
33543 a bare `(...)` list-closer would silently route through \
33544 the quote-family outer-dispatch arm.",
33545 );
33546 }
33547 }
33548
33549 #[test]
33550 fn sexp_display_nil_arm_binds_to_both_list_delimiter_constants() {
33551 // NIL DISPLAY CONTRACT: pin that `format!("{}", Sexp::Nil)`
33552 // produces the two-char rendering composed of BOTH typed
33553 // delimiters — `LIST_OPEN` followed by `LIST_CLOSE`. Pre-lift
33554 // this arm carried the two bytes inline as ONE `"()"` string
33555 // literal; post-lift each byte binds to its typed constant,
33556 // so a delimiter swap flips both the opener AND the closer in
33557 // lockstep at the typed algebra rather than at this arm's
33558 // inline literal. A regression that re-inlines the two bytes
33559 // OR drifts ONE of the two typed-constant bindings fails
33560 // loudly at this composition pin.
33561 let rendered = format!("{}", Sexp::Nil);
33562 let expected: String = Sexp::LIST_DELIMITERS.iter().collect();
33563 assert_eq!(
33564 rendered, expected,
33565 "Sexp::Nil Display drifted from the [LIST_OPEN, LIST_CLOSE] \
33566 composition — the two-char `()` rendering must be the \
33567 string composed of the two typed constants in order.",
33568 );
33569 // Cross-check against the bare byte-string to catch a
33570 // regression that silently swaps the two constants' values.
33571 assert_eq!(
33572 rendered, "()",
33573 "Sexp::Nil Display drifted from the canonical `()` two-char \
33574 rendering — a typed-constant swap would produce e.g. `)(` \
33575 which passes the `[LIST_OPEN, LIST_CLOSE]` compose test \
33576 but fails this hard-coded reference check.",
33577 );
33578 }
33579
33580 #[test]
33581 fn sexp_display_list_arms_bind_to_sexp_list_delimiter_constants() {
33582 // LIST DISPLAY CONTRACT: pin that `format!("{}", Sexp::List(_))`
33583 // opens with `LIST_OPEN`, closes with `LIST_CLOSE`, and
33584 // interleaves the children through the arm's ` `-separator
33585 // loop. Sweeps a representative small list plus the singleton
33586 // list plus the empty list — the empty list is IMPORTANT
33587 // because `Sexp::List(vec![])` is a distinct `Sexp` shape from
33588 // `Sexp::Nil`, and both must render as `()` through the outer
33589 // algebra (the reader's `()` source produces `Sexp::List(vec![])`
33590 // — see the `read` pipeline in `crate::reader`). A regression
33591 // that drifts either arm's binding surfaces here.
33592 for list in [
33593 Sexp::List(vec![]),
33594 Sexp::List(vec![Sexp::symbol("x")]),
33595 Sexp::List(vec![Sexp::symbol("a"), Sexp::symbol("b")]),
33596 Sexp::List(vec![Sexp::int(1), Sexp::int(2), Sexp::int(3)]),
33597 ] {
33598 let rendered = format!("{list}");
33599 let first = rendered
33600 .chars()
33601 .next()
33602 .unwrap_or_else(|| panic!("empty rendering for {list:?}"));
33603 let last = rendered
33604 .chars()
33605 .last()
33606 .unwrap_or_else(|| panic!("empty rendering for {list:?}"));
33607 assert_eq!(
33608 first,
33609 Sexp::LIST_OPEN,
33610 "List Display opener drifted from LIST_OPEN for {list:?}: \
33611 got {rendered:?}",
33612 );
33613 assert_eq!(
33614 last,
33615 Sexp::LIST_CLOSE,
33616 "List Display closer drifted from LIST_CLOSE for {list:?}: \
33617 got {rendered:?}",
33618 );
33619 }
33620 }
33621
33622 #[test]
33623 fn sexp_list_delimiters_close_reader_display_round_trip_for_lists_of_atoms() {
33624 // Load-bearing round-trip contract for the four outer-
33625 // structural sites — the reader's `Token::LParen` /
33626 // `Token::RParen` outer-dispatch arms both bind to
33627 // `Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`, AND the Display
33628 // impl's `Self::List(_)` arms bind to the SAME two constants,
33629 // so wrapping a sequence of atom lexemes in the constants on
33630 // both sides recovers a `Sexp::List(_)` shape that
33631 // round-trips through Display and the reader without drift.
33632 // A regression that swaps ONE of the four arms (e.g.
33633 // re-inlines `'('` at the reader opener while migrating the
33634 // Display opener to a different delimiter) breaks the
33635 // opener-must-match-closer contract across two files.
33636 for children in [
33637 vec![],
33638 vec![Sexp::symbol("foo")],
33639 vec![Sexp::symbol("a"), Sexp::symbol("b"), Sexp::int(42)],
33640 ] {
33641 let original = Sexp::List(children);
33642 let rendered = format!("{original}");
33643 let reread = crate::reader::read(&rendered).unwrap_or_else(|e| {
33644 panic!(
33645 "reader rejected Display-rendered list `{rendered}` \
33646 for `{original:?}`: {e}"
33647 )
33648 });
33649 assert_eq!(
33650 reread.len(),
33651 1,
33652 "Display-rendered list `{rendered}` must read as exactly \
33653 one form, got {reread:?}",
33654 );
33655 assert_eq!(
33656 reread[0], original,
33657 "read(display(list)) drifted from list for {original:?} \
33658 — rendered={rendered:?} reread={reread:?}",
33659 );
33660 }
33661 }
33662
33663 #[test]
33664 fn sexp_list_delimiters_composes_from_algebra_constants_in_declaration_order() {
33665 // FAMILY COMPOSITION LAW: pin that the ALL array's rows are the
33666 // two paired-delimiter algebra constants (`Self::LIST_OPEN`,
33667 // `Self::LIST_CLOSE`) in canonical declaration order matching
33668 // the substrate-canonical (opener, closer) pair shape every
33669 // consumer expects. A reorder of ONE row without reordering
33670 // the underlying algebra constants silently misaligns every
33671 // index-sweep consumer (`Nil` Display's `.iter().collect()`
33672 // routing, a hypothetical `Sexp::LIST_DELIMITERS[0]` opener
33673 // lookup, the sub-vocabulary sweep at
33674 // `is_bare_atom_boundary`). Sibling-shape pin to
33675 // `atom_self_escape_table_composes_from_algebra_constants_in_declaration_order`
33676 // on the peer `[char; 2]` sub-vocabulary at
33677 // [`Atom::SELF_ESCAPE_TABLE`].
33678 assert_eq!(
33679 Sexp::LIST_DELIMITERS,
33680 [Sexp::LIST_OPEN, Sexp::LIST_CLOSE],
33681 "LIST_DELIMITERS composition drifted from the canonical \
33682 (LIST_OPEN, LIST_CLOSE) pair — the paired-delimiter \
33683 sub-vocabulary lift must route through the two typed \
33684 algebra constants in that order.",
33685 );
33686 }
33687
33688 #[test]
33689 fn sexp_list_delimiters_has_expected_cardinality() {
33690 // CARDINALITY PIN: `[char; 2]` at rustc — this assert pins the
33691 // runtime observable so a refactor that loosens the array's
33692 // type to `&[char]` (dropping the compile-time arity forcing)
33693 // fails HERE at the runtime cardinality assertion rather than
33694 // silently allowing a third or absent row. Sibling-shape pin
33695 // to `atom_self_escape_table_has_expected_cardinality`.
33696 assert_eq!(
33697 Sexp::LIST_DELIMITERS.len(),
33698 2,
33699 "LIST_DELIMITERS cardinality drifted from 2 — the paired \
33700 (opener, closer) sub-vocabulary MUST be exactly two rows.",
33701 );
33702 }
33703
33704 #[test]
33705 fn sexp_list_delimiters_pairwise_distinct() {
33706 // PAIRWISE DISJOINTNESS: the two paired-delimiter rows MUST NOT
33707 // alias (a hypothetical `[` opener + `[` closer degenerate
33708 // list-mode would silently collapse the paired-delimiter
33709 // contract and lose the ability to bracket a well-formed list
33710 // — the reader's `Token::LParen` and `Token::RParen` arms
33711 // would collide at the same byte). Sibling-shape pin to
33712 // `atom_self_escape_table_pairwise_distinct` on the peer
33713 // `[char; 2]` sub-vocabulary at [`Atom::SELF_ESCAPE_TABLE`].
33714 for (i, a) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33715 for (j, b) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33716 if i == j {
33717 continue;
33718 }
33719 assert_ne!(
33720 a, b,
33721 "LIST_DELIMITERS rows [{i}] and [{j}] share a byte \
33722 ({a:?} == {b:?}) — the paired-delimiter contract \
33723 would collapse.",
33724 );
33725 }
33726 }
33727 }
33728
33729 #[test]
33730 fn sexp_list_delimiters_disjoint_from_str_delimiter() {
33731 // CROSS-AXIS DISJOINTNESS (Str-delimiter): no row of
33732 // `LIST_DELIMITERS` may alias `Atom::STR_DELIMITER` — otherwise
33733 // the reader's `Token::LParen` / `Token::RParen` outer-dispatch
33734 // arms would collide with `Token::Str`'s opener/closer arm at
33735 // the same byte. Sibling-shape pin to
33736 // `atom_named_escape_table_disjoint_from_self_escape_algebra_constants`:
33737 // both close the cross-sub-vocabulary disjointness contract at
33738 // the ALL-array level rather than as an inline disjunction per
33739 // consumer.
33740 for (i, ch) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33741 assert_ne!(
33742 *ch,
33743 Atom::STR_DELIMITER,
33744 "LIST_DELIMITERS[{i}] ({ch:?}) aliases Atom::STR_DELIMITER \
33745 ({:?}) — the reader's list-delimiter arm would collide \
33746 with the Str-payload delimiter arm at the same byte.",
33747 Atom::STR_DELIMITER,
33748 );
33749 }
33750 }
33751
33752 #[test]
33753 fn sexp_list_delimiters_disjoint_from_comment_lead() {
33754 // CROSS-AXIS DISJOINTNESS (comment lead): no row of
33755 // `LIST_DELIMITERS` may alias `Sexp::COMMENT_LEAD` — otherwise
33756 // the reader's list-delimiter arm would collide with the
33757 // line-comment discard arm at the same byte. Closes the
33758 // structural coherence contract between the outer-structural
33759 // list-delimiter sub-vocabulary and the reader-discard
33760 // sub-vocabulary on the SAME closed-set outer [`Sexp`]
33761 // algebra.
33762 for (i, ch) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33763 assert_ne!(
33764 *ch,
33765 Sexp::COMMENT_LEAD,
33766 "LIST_DELIMITERS[{i}] ({ch:?}) aliases Sexp::COMMENT_LEAD \
33767 ({:?}) — the reader's list-delimiter arm would collide \
33768 with the line-comment lead arm at the same byte.",
33769 Sexp::COMMENT_LEAD,
33770 );
33771 }
33772 }
33773
33774 #[test]
33775 fn sexp_is_bare_atom_boundary_routes_through_list_delimiters_for_every_row() {
33776 // PATH-UNIFORMITY PIN: every row of `LIST_DELIMITERS` MUST
33777 // classify as a bare-atom boundary via
33778 // `Sexp::is_bare_atom_boundary`. A regression that reverted the
33779 // projection's list-delimiter disjunct to two inline
33780 // `|| ch == Self::LIST_OPEN || ch == Self::LIST_CLOSE`
33781 // enumerations AND drifted one of the two constants (or vice
33782 // versa) fails HERE at the first mismatched row rather than at
33783 // a distant tokenize-round-trip. Sibling-shape pin to
33784 // `atom_decode_str_escape_routes_through_self_escape_table_for_every_row`
33785 // on the peer `[char; 2]` sub-vocabulary at
33786 // [`Atom::SELF_ESCAPE_TABLE`].
33787 for (i, ch) in Sexp::LIST_DELIMITERS.iter().enumerate() {
33788 assert!(
33789 Sexp::is_bare_atom_boundary(*ch),
33790 "LIST_DELIMITERS[{i}] ({ch:?}) does NOT classify as a \
33791 bare-atom boundary via Sexp::is_bare_atom_boundary — \
33792 the paired-delimiter sub-vocabulary sweep drifted from \
33793 the reader's outer-dispatch arm-set.",
33794 );
33795 }
33796 }
33797
33798 // ── `Sexp::COMMENT_LEAD` — the canonical `;` char routed through
33799 // the reader's TWO comment-boundary sites: the outer-dispatch arm
33800 // that begins a line-comment run AND the bare-atom terminator
33801 // disjunct that breaks a `Token::Atom` accumulator on this byte.
33802 // Sibling-shape tests to the `sexp_list_open_close` block above
33803 // (outer-structural paired-delimiter axis), lifted onto the reader-
33804 // discard axis of the closed-set outer [`Sexp`] algebra.
33805
33806 #[test]
33807 fn sexp_comment_lead_projects_canonical_semicolon_char() {
33808 // Pins the constant's exact `char` value so a typo (`'#'`,
33809 // `'!'`, `':'`) or an accidental redefinition surfaces
33810 // immediately. Sibling-shape pin to
33811 // `sexp_list_open_projects_canonical_open_paren_char` on the
33812 // outer-structural axis — pins the SAME shape on the reader-
33813 // discard axis of the closed-set outer [`Sexp`] algebra.
33814 assert_eq!(
33815 Sexp::COMMENT_LEAD,
33816 ';',
33817 "COMMENT_LEAD char drifted from the substrate-canonical `;` \
33818 line-comment lead — the reader-discard contract at \
33819 crate::reader::tokenize (line-comment outer arm, bare-atom \
33820 terminator disjunct) binds to this ONE constant.",
33821 );
33822 }
33823
33824 #[test]
33825 fn sexp_comment_lead_distinct_from_every_other_algebra_marker() {
33826 // Cross-axis disjointness pin: `Sexp::COMMENT_LEAD` may NOT
33827 // alias any sibling outer-marker char on the substrate's other
33828 // closed-set algebras — the paired list delimiters
33829 // (`Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`), the Str-payload
33830 // delimiter (`Atom::STR_DELIMITER`), the Keyword-marker prefix
33831 // (`Atom::KEYWORD_MARKER`'s lead byte), the two Bool-literal
33832 // spellings' lead byte (`Atom::bool_literal(true|false)`'s first
33833 // char), AND every quote-family lead char
33834 // (`QuoteForm::lead_char(qf)` for each `qf` in `QuoteForm::ALL`).
33835 // Otherwise a bare `;`-starting lexeme would ambiguously route
33836 // through the line-comment arm AND a sibling algebra's arm in
33837 // `crate::reader::tokenize`. Guards the paired disjointness
33838 // across the substrate's outer-marker axes so a future refactor
33839 // that swaps a marker to collide with the comment lead surfaces
33840 // at this pin rather than as a silent reader misclassification.
33841 assert_ne!(
33842 Sexp::COMMENT_LEAD,
33843 Sexp::LIST_OPEN,
33844 "COMMENT_LEAD and LIST_OPEN share a byte — a bare `{}foo` \
33845 lexeme would ambiguously begin a list AND begin a comment.",
33846 Sexp::LIST_OPEN,
33847 );
33848 assert_ne!(
33849 Sexp::COMMENT_LEAD,
33850 Sexp::LIST_CLOSE,
33851 "COMMENT_LEAD and LIST_CLOSE share a byte — a bare `{}foo` \
33852 lexeme would ambiguously close a list AND begin a comment.",
33853 Sexp::LIST_CLOSE,
33854 );
33855 assert_ne!(
33856 Sexp::COMMENT_LEAD,
33857 Atom::STR_DELIMITER,
33858 "COMMENT_LEAD and STR_DELIMITER share a byte — a bare `{}foo` \
33859 lexeme would ambiguously begin a comment AND open a string.",
33860 Atom::STR_DELIMITER,
33861 );
33862 assert_ne!(
33863 Sexp::COMMENT_LEAD,
33864 Atom::KEYWORD_MARKER_LEAD,
33865 "COMMENT_LEAD and KEYWORD_MARKER_LEAD share a byte — a bare \
33866 `{lead}foo` lexeme would ambiguously begin a comment AND \
33867 begin a keyword.",
33868 lead = Atom::KEYWORD_MARKER_LEAD,
33869 );
33870 for b in [true, false] {
33871 let lead = Atom::bool_literal(b)
33872 .chars()
33873 .next()
33874 .expect("bool_literal must be non-empty");
33875 assert_ne!(
33876 Sexp::COMMENT_LEAD,
33877 lead,
33878 "COMMENT_LEAD and bool_literal({b:?}) share a lead byte — a \
33879 bare `{lead}...` lexeme would ambiguously begin a comment \
33880 AND classify as a Bool.",
33881 );
33882 }
33883 for qf in QuoteForm::ALL {
33884 assert_ne!(
33885 Sexp::COMMENT_LEAD,
33886 qf.lead_char(),
33887 "COMMENT_LEAD and QuoteForm::{qf:?}::lead_char share a byte — \
33888 a bare `;`-starting source would silently route through the \
33889 quote-family outer-dispatch arm.",
33890 );
33891 }
33892 }
33893
33894 // ── `Sexp::COMMENT_TERM` — the canonical `\n` char that terminates
33895 // a line-comment run in the reader's tokenizer. Section-for-retraction
33896 // sibling of `Sexp::COMMENT_LEAD` on the reader-discard axis; paired
33897 // opener/terminator peer of `Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`
33898 // on the outer-structural axis. The pins below anchor the constant's
33899 // exact byte AND its cross-axis disjointness against every
33900 // non-whitespace closed-set outer-marker char.
33901
33902 #[test]
33903 fn sexp_comment_term_projects_canonical_line_feed_char() {
33904 // Pins the constant's exact `char` value so a typo (`'\r'`,
33905 // `'\0'`, a display-glyph substitution) or an accidental
33906 // redefinition surfaces immediately. Sibling-shape pin to
33907 // `sexp_comment_lead_projects_canonical_semicolon_char` on the
33908 // reader-discard axis — pins the SAME shape at the terminator
33909 // side of the (COMMENT_LEAD, COMMENT_TERM) paired-delimiter
33910 // algebra on the closed-set outer [`Sexp`] algebra.
33911 assert_eq!(
33912 Sexp::COMMENT_TERM,
33913 '\n',
33914 "COMMENT_TERM char drifted from the substrate-canonical `\\n` \
33915 line-feed byte — the reader-discard contract at \
33916 crate::reader::tokenize's line-comment discard loop binds \
33917 to this ONE constant.",
33918 );
33919 }
33920
33921 #[test]
33922 fn sexp_comment_term_is_whitespace_family_char() {
33923 // WHITESPACE-FAMILY PIN: the line-comment terminator MUST be a
33924 // whitespace char. The reader's outer-match's `ws if
33925 // ws.is_whitespace()` arm absorbs any lingering COMMENT_TERM
33926 // byte after the discard loop terminates on it — a regression
33927 // that repointed COMMENT_TERM at a NON-whitespace byte would
33928 // BOTH break the discard loop's semantics AND leak the byte
33929 // into the token stream as an outer-dispatch dispatch (either a
33930 // specific arm firing on it, or the bare-atom accumulator
33931 // consuming it). Pin the property structurally so a byte swap
33932 // that lost whitespace-family membership surfaces here rather
33933 // than as a downstream tokenizer misclassification.
33934 assert!(
33935 Sexp::COMMENT_TERM.is_whitespace(),
33936 "COMMENT_TERM `{:?}` is NOT a whitespace char — the reader's \
33937 outer-match whitespace arm would fail to absorb it AND the \
33938 line-comment discard loop's terminator would leak the byte \
33939 into the token stream.",
33940 Sexp::COMMENT_TERM,
33941 );
33942 }
33943
33944 #[test]
33945 fn sexp_comment_term_distinct_from_every_non_whitespace_algebra_marker() {
33946 // Cross-axis disjointness pin: `Sexp::COMMENT_TERM` may NOT
33947 // alias any NON-whitespace sibling outer-marker char on the
33948 // substrate's other closed-set algebras — the paired list
33949 // delimiters (`Sexp::LIST_OPEN` / `Sexp::LIST_CLOSE`), the
33950 // line-comment lead (`Sexp::COMMENT_LEAD`), the Str-payload
33951 // delimiter + escape-lead (`Atom::STR_DELIMITER` /
33952 // `Atom::STR_ESCAPE_LEAD`), the Keyword-marker prefix
33953 // (`Atom::KEYWORD_MARKER`'s lead byte), the two Bool-literal
33954 // spellings' lead byte (`Atom::bool_literal(true|false)`'s
33955 // first char), AND every quote-family lead char
33956 // (`QuoteForm::lead_char(qf)` for each `qf` in `QuoteForm::ALL`).
33957 // The disjointness contract EXCLUDES the whitespace-family
33958 // axis — the terminator's role IS to be whitespace-family, so
33959 // the outer-match's `ws if ws.is_whitespace()` arm absorbs it
33960 // by design (see `sexp_comment_term_is_whitespace_family_char`
33961 // above). Sibling-shape pin to
33962 // `sexp_comment_lead_distinct_from_every_other_algebra_marker`
33963 // on the lead side of the paired-delimiter algebra — pins the
33964 // SAME shape at the terminator side.
33965 assert_ne!(
33966 Sexp::COMMENT_TERM,
33967 Sexp::LIST_OPEN,
33968 "COMMENT_TERM and LIST_OPEN share a byte — the reader's \
33969 line-comment discard loop would terminate on the SAME byte \
33970 the outer-dispatch's list-opening arm binds to.",
33971 );
33972 assert_ne!(
33973 Sexp::COMMENT_TERM,
33974 Sexp::LIST_CLOSE,
33975 "COMMENT_TERM and LIST_CLOSE share a byte — the reader's \
33976 line-comment discard loop would terminate on the SAME byte \
33977 the outer-dispatch's list-closing arm binds to.",
33978 );
33979 assert_ne!(
33980 Sexp::COMMENT_TERM,
33981 Sexp::COMMENT_LEAD,
33982 "COMMENT_TERM and COMMENT_LEAD share a byte — a bare \
33983 `{lead}{lead}` two-char source would AMBIGUOUSLY begin AND \
33984 terminate the SAME comment run at the SAME byte, collapsing \
33985 the paired-delimiter algebra onto ONE char.",
33986 lead = Sexp::COMMENT_LEAD,
33987 );
33988 assert_ne!(
33989 Sexp::COMMENT_TERM,
33990 Atom::STR_DELIMITER,
33991 "COMMENT_TERM and STR_DELIMITER share a byte — the reader's \
33992 line-comment discard loop would terminate on the SAME byte \
33993 the outer-dispatch's string-opening arm binds to.",
33994 );
33995 assert_ne!(
33996 Sexp::COMMENT_TERM,
33997 Atom::STR_ESCAPE_LEAD,
33998 "COMMENT_TERM and STR_ESCAPE_LEAD share a byte — the reader's \
33999 line-comment discard loop would terminate on the SAME byte \
34000 the Str-payload's escape-handler outer arm binds to.",
34001 );
34002 assert_ne!(
34003 Sexp::COMMENT_TERM,
34004 Atom::KEYWORD_MARKER_LEAD,
34005 "COMMENT_TERM and KEYWORD_MARKER_LEAD share a byte — the \
34006 reader's line-comment discard loop would terminate on the \
34007 SAME byte the from_lexeme keyword-prefix arm binds to.",
34008 );
34009 for b in [true, false] {
34010 let lead = Atom::bool_literal(b)
34011 .chars()
34012 .next()
34013 .expect("bool_literal must be non-empty");
34014 assert_ne!(
34015 Sexp::COMMENT_TERM,
34016 lead,
34017 "COMMENT_TERM and bool_literal({b:?}) share a lead byte — \
34018 the reader's line-comment discard loop would terminate \
34019 on the SAME byte the from_lexeme bool-literal arm binds \
34020 to.",
34021 );
34022 }
34023 for qf in QuoteForm::ALL {
34024 assert_ne!(
34025 Sexp::COMMENT_TERM,
34026 qf.lead_char(),
34027 "COMMENT_TERM and QuoteForm::{qf:?}::lead_char share a \
34028 byte — the reader's line-comment discard loop would \
34029 terminate on the SAME byte the quote-family outer- \
34030 dispatch arm binds to.",
34031 );
34032 }
34033 }
34034
34035 // ── `Sexp::COMMENT_DELIMITERS` — the paired (opener, terminator)
34036 // reader-discard sub-vocabulary on the closed-set outer [`Sexp`]
34037 // algebra. Sibling-shape tests to the `sexp_list_delimiters_*` block
34038 // above (outer-structural paired-delimiter axis), lifted onto the
34039 // reader-discard axis of the SAME algebra at the SAME `[char; 2]`
34040 // shape.
34041
34042 #[test]
34043 fn sexp_comment_delimiters_composes_from_algebra_constants_in_declaration_order() {
34044 // FAMILY COMPOSITION LAW: pin that the ALL array's rows are the
34045 // two paired reader-discard algebra constants
34046 // (`Self::COMMENT_LEAD`, `Self::COMMENT_TERM`) in canonical
34047 // (opener, terminator) declaration order matching the
34048 // substrate-canonical paired-role shape every consumer expects.
34049 // A reorder of ONE row without reordering the underlying
34050 // algebra constants silently misaligns every index-sweep
34051 // consumer (a hypothetical `Sexp::COMMENT_DELIMITERS[0]` opener
34052 // lookup, an LSP span-highlighter that renders the (opener,
34053 // terminator) span as-is, the metric label-set generator that
34054 // walks the array). Sibling-shape pin to
34055 // `sexp_list_delimiters_composes_from_algebra_constants_in_declaration_order`
34056 // on the outer-structural axis; both close the (opener,
34057 // closer_or_terminator) pair contract at ONE typed ALL array.
34058 assert_eq!(
34059 Sexp::COMMENT_DELIMITERS,
34060 [Sexp::COMMENT_LEAD, Sexp::COMMENT_TERM],
34061 "COMMENT_DELIMITERS composition drifted from the canonical \
34062 (COMMENT_LEAD, COMMENT_TERM) pair — the paired-discard \
34063 sub-vocabulary lift must route through the two typed \
34064 algebra constants in that order.",
34065 );
34066 }
34067
34068 #[test]
34069 fn sexp_comment_delimiters_has_expected_cardinality() {
34070 // CARDINALITY PIN: `[char; 2]` at rustc — this assert pins the
34071 // runtime observable so a refactor that loosens the array's
34072 // type to `&[char]` (dropping the compile-time arity forcing)
34073 // fails HERE at the runtime cardinality assertion rather than
34074 // silently allowing a third or absent row. Sibling-shape pin
34075 // to `sexp_list_delimiters_has_expected_cardinality` on the
34076 // outer-structural axis.
34077 assert_eq!(
34078 Sexp::COMMENT_DELIMITERS.len(),
34079 2,
34080 "COMMENT_DELIMITERS cardinality drifted from 2 — the paired \
34081 (opener, terminator) reader-discard sub-vocabulary MUST be \
34082 exactly two rows.",
34083 );
34084 }
34085
34086 #[test]
34087 fn sexp_comment_delimiters_pairwise_distinct() {
34088 // PAIRWISE DISJOINTNESS: the two paired reader-discard rows
34089 // MUST NOT alias (a hypothetical degenerate `;` opener + `;`
34090 // terminator convention would collapse the paired-delimiter
34091 // contract — the discard loop's terminator check `ch ==
34092 // Sexp::COMMENT_TERM` inside a run led by `Sexp::COMMENT_LEAD`
34093 // would fire on the very byte that opened the run, breaking
34094 // the loop after zero characters and leaving the actual comment
34095 // body in the token stream). Sibling-shape pin to
34096 // `sexp_list_delimiters_pairwise_distinct` on the outer-
34097 // structural axis; both close the pairwise-distinctness
34098 // contract at the ALL-array level rather than at an inline
34099 // `assert_ne!(LEAD, TERM)` per consumer.
34100 for (i, a) in Sexp::COMMENT_DELIMITERS.iter().enumerate() {
34101 for (j, b) in Sexp::COMMENT_DELIMITERS.iter().enumerate() {
34102 if i == j {
34103 continue;
34104 }
34105 assert_ne!(
34106 a, b,
34107 "COMMENT_DELIMITERS rows [{i}] and [{j}] share a byte \
34108 ({a:?} == {b:?}) — the paired-discard contract \
34109 would collapse.",
34110 );
34111 }
34112 }
34113 }
34114
34115 #[test]
34116 fn sexp_comment_delimiters_lead_row_is_bare_atom_boundary() {
34117 // PATH-UNIFORMITY PIN (LEAD row, non-whitespace): the [0] row
34118 // (`Sexp::COMMENT_LEAD`, `;`) MUST classify as a bare-atom
34119 // boundary via `Sexp::is_bare_atom_boundary` — the reader's
34120 // outer-dispatch cascade has a DEDICATED line-comment arm
34121 // keyed on this byte, so the projection's disjunction
34122 // enumerates it. A regression that dropped the LEAD from
34123 // `is_bare_atom_boundary`'s disjunction OR that pointed
34124 // `COMMENT_DELIMITERS[0]` at a byte the projection doesn't
34125 // recognize as a boundary fails HERE rather than at a distant
34126 // tokenize-round-trip. Sibling-shape pin to
34127 // `sexp_is_bare_atom_boundary_routes_through_list_delimiters_for_every_row`
34128 // — the LIST_DELIMITERS peer sweeps BOTH rows through the
34129 // predicate; this pin sweeps ONLY the LEAD row (index 0),
34130 // because the TERM row (index 1) classifies through the
34131 // whitespace-family axis, NOT the bare-atom-boundary predicate
34132 // directly.
34133 assert!(
34134 Sexp::is_bare_atom_boundary(Sexp::COMMENT_DELIMITERS[0]),
34135 "COMMENT_DELIMITERS[0] ({:?}) does NOT classify as a \
34136 bare-atom boundary via Sexp::is_bare_atom_boundary — the \
34137 reader-discard opener row drifted from the reader's \
34138 outer-dispatch arm-set.",
34139 Sexp::COMMENT_DELIMITERS[0],
34140 );
34141 }
34142
34143 #[test]
34144 fn sexp_comment_delimiters_term_row_is_whitespace_family_char() {
34145 // PATH-UNIFORMITY PIN (TERM row, whitespace): the [1] row
34146 // (`Sexp::COMMENT_TERM`, `'\n'`) MUST classify as a whitespace
34147 // char via `char::is_whitespace` — the reader's line-comment
34148 // discard loop consumes bytes up to and including the FIRST
34149 // COMMENT_TERM, then hands control back to the outer-dispatch's
34150 // `ws if ws.is_whitespace()` arm which absorbs any lingering
34151 // COMMENT_TERM byte cleanly. A regression that repointed
34152 // `COMMENT_DELIMITERS[1]` at a NON-whitespace byte (e.g. a `#`
34153 // reader-macro-lead) would break the post-discard hand-off:
34154 // the reader would either loop indefinitely on the byte or
34155 // silently tokenize it into the next Token::Atom. Pinning the
34156 // whitespace-family membership at the array's TERM row keeps
34157 // the outer-dispatch's cascading arm-set structurally coherent
34158 // through the ALL array. Sibling-shape pin to
34159 // `sexp_comment_term_is_whitespace_family_char` above; that
34160 // pin binds the whitespace-family membership to the
34161 // `Self::COMMENT_TERM` constant directly, this pin binds it to
34162 // the ALL array's [1] row so any override of the TERM row via
34163 // a refactored `pub const` misalignment surfaces HERE too.
34164 assert!(
34165 Sexp::COMMENT_DELIMITERS[1].is_whitespace(),
34166 "COMMENT_DELIMITERS[1] ({:?}) does NOT classify as a \
34167 whitespace char via char::is_whitespace — the reader's \
34168 line-comment discard loop's post-loop hand-off to the \
34169 outer-dispatch's whitespace arm would break at the \
34170 non-whitespace terminator.",
34171 Sexp::COMMENT_DELIMITERS[1],
34172 );
34173 }
34174
34175 #[test]
34176 fn sexp_comment_delimiters_disjoint_from_list_delimiters() {
34177 // CROSS-AXIS DISJOINTNESS (same-algebra): no row of
34178 // `COMMENT_DELIMITERS` may alias any row of
34179 // `LIST_DELIMITERS` — the reader-discard axis and the
34180 // outer-structural list-delimiter axis partition their
34181 // respective bytes disjointly on the SAME closed-set outer
34182 // [`Sexp`] algebra. Otherwise the reader's dedicated line-
34183 // comment arm would collide with the `Token::LParen` /
34184 // `Token::RParen` arms at the same byte, silently reclassifying
34185 // a bare `(` or `)` as a comment lead or vice versa. Sibling-
34186 // shape pin to `sexp_list_delimiters_disjoint_from_comment_lead`
34187 // above (the outer-structural axis's mirror pin against the
34188 // LEAD row); this pin closes the FULL 2×2 cross-axis
34189 // disjointness contract rather than only the LEAD row against
34190 // both LIST rows.
34191 for (i, ch) in Sexp::COMMENT_DELIMITERS.iter().enumerate() {
34192 for (j, other) in Sexp::LIST_DELIMITERS.iter().enumerate() {
34193 assert_ne!(
34194 *ch, *other,
34195 "COMMENT_DELIMITERS[{i}] ({ch:?}) aliases \
34196 LIST_DELIMITERS[{j}] ({other:?}) — the reader- \
34197 discard axis and the outer-structural list- \
34198 delimiter axis share a byte, silently reclassifying \
34199 the shared byte at the reader's outer dispatch.",
34200 );
34201 }
34202 }
34203 }
34204
34205 #[test]
34206 fn sexp_comment_delimiters_disjoint_from_str_delimiter() {
34207 // CROSS-ALGEBRA DISJOINTNESS (Str-delimiter): no row of
34208 // `COMMENT_DELIMITERS` may alias `Atom::STR_DELIMITER` —
34209 // otherwise the reader's line-comment discard arm would
34210 // collide with `Token::Str`'s opener/closer arm at the same
34211 // byte. The disjointness contract binds ACROSS the two closed-
34212 // set algebras (outer [`Sexp`] discard vocabulary vs. inner
34213 // [`Atom`] Str-payload vocabulary), matching the sibling-shape
34214 // pin `sexp_list_delimiters_disjoint_from_str_delimiter` on
34215 // the outer-structural axis of the SAME [`Sexp`] algebra.
34216 for (i, ch) in Sexp::COMMENT_DELIMITERS.iter().enumerate() {
34217 assert_ne!(
34218 *ch,
34219 Atom::STR_DELIMITER,
34220 "COMMENT_DELIMITERS[{i}] ({ch:?}) aliases \
34221 Atom::STR_DELIMITER ({:?}) — the reader's line- \
34222 comment discard arm would collide with the Str-payload \
34223 delimiter arm at the same byte across the two closed- \
34224 set algebras.",
34225 Atom::STR_DELIMITER,
34226 );
34227 }
34228 }
34229
34230 // ── `Sexp::is_bare_atom_boundary` — the ONE typed projection on the
34231 // outer [`Sexp`] algebra that names the SIX-fold outer-dispatch
34232 // category-leading char disjunction. The exhaustive pin below
34233 // anchors each of the six categories AS a positive arm AND the
34234 // load-bearing substrate marker LEAD bytes (`:` / `#` / `@`) AS
34235 // negative arms — the three lead bytes are first-char classifiers
34236 // for bare-atom payload families (Keyword / Bool / splice second-
34237 // char), NOT outer-dispatch category-leading chars, and a
34238 // regression that promoted any to a boundary would break their
34239 // tokenization. The composition end-to-end pin (in reader tests)
34240 // sweeps every boundary char through the bare-atom accumulator and
34241 // asserts the atom terminates at exactly the boundary byte.
34242
34243 #[test]
34244 fn sexp_is_bare_atom_boundary_matches_outer_dispatch_arm_set_exhaustively() {
34245 // OUTER-DISPATCH COHERENCE PIN: the SIX outer-dispatch category-
34246 // leading char families the reader's tokenizer specialises on
34247 // MUST ALL classify as boundaries; no other char (bare-atom
34248 // chars) may classify as one. This test enumerates the FULL
34249 // outer-dispatch arm-set as a typed table AND sweeps a
34250 // representative negative set, asserting the projection's
34251 // predicate matches the outer-dispatch's specific-arm firing
34252 // exactly. A refactor that added a SEVENTH outer-dispatch arm
34253 // (e.g. `#|…|#` block-comment) to the reader without extending
34254 // the projection's disjunction would fail HERE at the coherence
34255 // sweep: the new lead byte would EITHER be a boundary the
34256 // projection missed (test asserts positive, projection returns
34257 // false → panic) OR a bare-atom char the reader stole from the
34258 // default arm (silent tokenizer drift the projection doesn't
34259 // catch). Either way the coherence pin catches the drift and
34260 // forces the projection + outer-dispatch to stay in lockstep.
34261 let positive_arms: Vec<(char, String)> = {
34262 let mut arms: Vec<(char, String)> = vec![
34263 (' ', "whitespace".to_string()),
34264 ('\t', "whitespace-tab".to_string()),
34265 ('\n', "whitespace-newline".to_string()),
34266 (Sexp::LIST_OPEN, "LIST_OPEN".to_string()),
34267 (Sexp::LIST_CLOSE, "LIST_CLOSE".to_string()),
34268 (Atom::STR_DELIMITER, "STR_DELIMITER".to_string()),
34269 (Sexp::COMMENT_LEAD, "COMMENT_LEAD".to_string()),
34270 ];
34271 for qf in QuoteForm::ALL {
34272 arms.push((qf.lead_char(), format!("QuoteForm::{qf:?}")));
34273 }
34274 arms
34275 };
34276 for (ch, name) in &positive_arms {
34277 assert!(
34278 Sexp::is_bare_atom_boundary(*ch),
34279 "outer-dispatch arm `{name}` (`{ch:?}`) must classify as \
34280 a boundary — the projection missed a category the \
34281 reader's outer-dispatch specialises on",
34282 );
34283 }
34284 // Negative sweep: every char below MUST fall through to the
34285 // default bare-atom arm. Includes typical alpha, digit, and
34286 // sign chars AND the three load-bearing substrate marker LEAD
34287 // bytes (`:` / `#` / `@`) that are first-char classifiers for
34288 // bare-atom payload families rather than outer-dispatch
34289 // categories.
34290 let negative_arms: &[char] = &[
34291 'a',
34292 'z',
34293 'A',
34294 'Z',
34295 '0',
34296 '9',
34297 '-',
34298 '+',
34299 '_',
34300 '.',
34301 '=',
34302 '<',
34303 '>',
34304 '!',
34305 '?',
34306 '/',
34307 '*',
34308 '%',
34309 '&',
34310 '|',
34311 '^',
34312 '~',
34313 // The `KEYWORD_MARKER` prefix's lead byte lives at the
34314 // typed `Atom::KEYWORD_MARKER_LEAD` constant on the closed-
34315 // set outer [`Atom`] algebra. Pre-lift this slot held an
34316 // inline `Atom::KEYWORD_MARKER.chars().next().unwrap()`
34317 // chain extracting the byte from the `&'static str`
34318 // projection; post-lift the byte lives at ONE named
34319 // constant the `&'static str` projects to (pinned by
34320 // `atom_keyword_marker_lead_prefixes_keyword_marker`).
34321 Atom::KEYWORD_MARKER_LEAD,
34322 // The bool_literal spellings' shared lead byte lives at
34323 // the typed `Atom::BOOL_LITERAL_LEAD` constant on the
34324 // closed-set outer [`Atom`] algebra. Pre-lift this slot
34325 // held an inline `Atom::bool_literal(true).chars().next()
34326 // .unwrap()` chain extracting the byte from ONE spelling;
34327 // post-lift the byte lives at ONE named constant that
34328 // BOTH spellings project through (pinned by
34329 // `atom_bool_literal_lead_prefixes_every_bool_literal_spelling`).
34330 Atom::BOOL_LITERAL_LEAD,
34331 QuoteForm::SPLICE_DISCRIMINATOR,
34332 ];
34333 for ch in negative_arms {
34334 assert!(
34335 !Sexp::is_bare_atom_boundary(*ch),
34336 "bare-atom char {ch:?} must NOT classify as a boundary — \
34337 the reader's outer-dispatch has NO specific arm on this \
34338 byte; the default bare-atom arm must accept it",
34339 );
34340 }
34341 }
34342
34343 #[test]
34344 fn sexp_non_whitespace_bare_atom_terminators_has_expected_cardinality() {
34345 // Cardinality contract: `Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.len()
34346 // == 7` — pinned at the declaration site by rustc's forced-arity
34347 // check on `[char; 7]`. This test surfaces the arity as a fail-
34348 // loud runtime pin so a future refactor that switches the array
34349 // type to `&[char]` (dropping the compile-time arity forcing)
34350 // doesn't silently loosen the closed-set discipline the family
34351 // relies on. The seven arms are the FULL non-whitespace category-
34352 // leading char set the reader's outer-dispatch specialises on
34353 // (two structural `Sexp::LIST_{OPEN,CLOSE}`, three
34354 // `QuoteForm::{QUOTE,QUASIQUOTE,UNQUOTE}_LEAD`, one
34355 // `Atom::STR_DELIMITER`, one `Sexp::COMMENT_LEAD`). Sibling
34356 // posture to `sexp_list_delimiters_has_expected_cardinality`
34357 // and `sexp_comment_delimiters_has_expected_cardinality` on the
34358 // paired-role sub-arrays of the SAME closed set.
34359 assert_eq!(
34360 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS.len(),
34361 7,
34362 "Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS cardinality \
34363 drifted from 7 — the reader's outer-dispatch specialises \
34364 on exactly seven non-whitespace category-leading chars by \
34365 construction; an extension surfaces here"
34366 );
34367 }
34368
34369 #[test]
34370 fn sexp_non_whitespace_bare_atom_terminators_route_through_typed_sub_algebra_constants() {
34371 // PATH-UNIFORMITY (family-wide ARRAY-side): the seven entries
34372 // of `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS` MUST bind
34373 // byte-for-byte to the seven typed sub-algebra `pub const`
34374 // primitives — no inline `char` literals. Catches a regression
34375 // that inlines `['(', ')', '\'', '`', ',', '"', ';']`: both
34376 // the alignment property AND the numeric bytes would still
34377 // hold, but the algebra-provenance would disappear, so a
34378 // future rename (e.g. `Sexp::LIST_OPEN → Sexp::PAREN_OPEN`,
34379 // `Sexp::COMMENT_LEAD → Sexp::LINE_COMMENT_OPEN`) at any of
34380 // the three sibling algebras would silently drift the array's
34381 // provenance from the algebra's canonical spelling. Sibling
34382 // posture to the `LIST_DELIMITERS` / `COMMENT_DELIMITERS`
34383 // ARRAY-side provenance pins on the paired-role sub-arrays,
34384 // AND to the shape-level HASH_DISCRIMINATORS ARRAY-side
34385 // provenance pin
34386 // `sexp_shape_hash_discriminators_align_with_typed_per_role_constants_by_index`
34387 // on the outer-`Sexp` cache-key axis.
34388 assert_eq!(
34389 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[0],
34390 Sexp::LIST_OPEN
34391 );
34392 assert_eq!(
34393 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[1],
34394 Sexp::LIST_CLOSE
34395 );
34396 assert_eq!(
34397 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[2],
34398 QuoteForm::QUOTE_LEAD,
34399 );
34400 assert_eq!(
34401 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[3],
34402 QuoteForm::QUASIQUOTE_LEAD,
34403 );
34404 assert_eq!(
34405 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[4],
34406 QuoteForm::UNQUOTE_LEAD,
34407 );
34408 assert_eq!(
34409 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[5],
34410 Atom::STR_DELIMITER,
34411 );
34412 assert_eq!(
34413 Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS[6],
34414 Sexp::COMMENT_LEAD,
34415 );
34416 }
34417
34418 #[test]
34419 fn sexp_non_whitespace_bare_atom_terminators_are_pairwise_distinct() {
34420 // INJECTIVITY CONTRACT: the seven entries of
34421 // `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS` MUST be pairwise
34422 // distinct — no char appears twice across the (structural,
34423 // quote-family, atomic-delimiter, comment-lead) sub-vocabularies.
34424 // A silent collision (e.g. a hypothetical Racket-compat port
34425 // moving `QuoteForm::QUOTE_LEAD` from `'\''` to `';'` conflating
34426 // it with `Sexp::COMMENT_LEAD`, or a future block-comment
34427 // extension re-using `Atom::STR_DELIMITER`) would break the
34428 // reader's outer-dispatch's implicit "each specific arm fires
34429 // on a DISTINCT lead char" contract — the `match c { … }`
34430 // outer-dispatch in `crate::reader::tokenize` cannot admit
34431 // duplicate patterns without breaking exhaustiveness. This pin
34432 // surfaces the distinctness as a substrate-level structural
34433 // theorem so a cross-algebra rename that flips the injectivity
34434 // is a compile-time-verified regression at the sub-carving
34435 // level (rustc-forbidden duplicate `match` patterns) AND a
34436 // runtime fail-loud regression here.
34437 let mut seen: std::collections::HashSet<char> = std::collections::HashSet::new();
34438 for ch in Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS {
34439 assert!(
34440 seen.insert(ch),
34441 "Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS carries a \
34442 duplicate char {ch:?} — the reader's outer-dispatch \
34443 arms MUST be pairwise disjoint by construction",
34444 );
34445 }
34446 assert_eq!(seen.len(), Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS.len(),);
34447 }
34448
34449 #[test]
34450 fn sexp_is_bare_atom_boundary_agrees_with_terminators_array_on_non_whitespace_partition() {
34451 // BOUNDARY-PREDICATE COMPOSITION LAW: for every char `ch`,
34452 // `Sexp::is_bare_atom_boundary(ch) == (ch.is_whitespace() ||
34453 // Self::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch))`.
34454 // Pins the ARRAY-driven refactor of the boundary predicate
34455 // against a regression that reverts the disjunction to the
34456 // pre-lift three-part sub-expression composition (or drifts
34457 // ONE sub-expression's arm silently). Sibling posture to
34458 // `sexp_is_bare_atom_boundary_matches_outer_dispatch_arm_set_exhaustively`
34459 // — that pin binds the boundary predicate's TRUTH-TABLE against
34460 // the reader's outer-dispatch categories; this pin binds the
34461 // boundary predicate's DEFINITION against the family-wide
34462 // terminators ARRAY as the sole non-whitespace clause. The
34463 // (positive, negative) sweep covers a whitespace char + the
34464 // seven terminator entries as positives, and a representative
34465 // negative set (alpha, digit, sign, and the three substrate
34466 // marker LEADs `:` / `#` / `@`) as negatives.
34467 for &ch in &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS {
34468 assert!(
34469 Sexp::is_bare_atom_boundary(ch),
34470 "Sexp::is_bare_atom_boundary({ch:?}) must return true \
34471 for every char in NON_WHITESPACE_BARE_ATOM_TERMINATORS",
34472 );
34473 }
34474 assert!(Sexp::is_bare_atom_boundary(' '));
34475 assert!(Sexp::is_bare_atom_boundary('\t'));
34476 assert!(Sexp::is_bare_atom_boundary('\n'));
34477 for ch in ['a', 'Z', '0', '9', '-', '_', ':', '#', '@'] {
34478 let expected =
34479 ch.is_whitespace() || Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS.contains(&ch);
34480 assert_eq!(
34481 Sexp::is_bare_atom_boundary(ch),
34482 expected,
34483 "Sexp::is_bare_atom_boundary({ch:?}) drifted from the \
34484 ARRAY-driven composition law",
34485 );
34486 }
34487 }
34488
34489 // ── `assert_char_array_pairwise_distinct` — the const-fn compile-
34490 // time pairwise-distinctness contract lifter. Sibling posture to
34491 // the runtime `_pairwise_distinct` tests: those pin the property
34492 // at test-run time on each individual array; these pins bind the
34493 // lift's OWN contract (accept-distinct, reject-collision, runtime-
34494 // callability, cross-array coverage) so a regression that silently
34495 // weakens the helper (e.g. flipping `!=` to `==`, dropping the
34496 // inner `j` loop, or returning early on collision) is caught by
34497 // the helper's OWN test surface rather than only surfacing as a
34498 // false-positive on some future array's distinctness pin.
34499
34500 #[test]
34501 fn assert_char_array_pairwise_distinct_accepts_the_empty_array() {
34502 // Empty array — vacuously pairwise distinct (no pair to
34503 // collide). The compile-time `const _: () = assert_char_array_
34504 // pairwise_distinct(&EMPTY);` would land on this arm, so the
34505 // runtime call MUST return normally.
34506 assert_char_array_pairwise_distinct::<0>(&[]);
34507 }
34508
34509 #[test]
34510 fn assert_char_array_pairwise_distinct_accepts_singleton_arrays() {
34511 // Singleton array — vacuously pairwise distinct (only one
34512 // element, no pair). Cross-arity coverage on the `[char; 1]`
34513 // corner of the const-N generic.
34514 assert_char_array_pairwise_distinct(&['a']);
34515 assert_char_array_pairwise_distinct(&[Sexp::LIST_OPEN]);
34516 }
34517
34518 #[test]
34519 fn assert_char_array_pairwise_distinct_accepts_every_family_wide_substrate_array() {
34520 // Runtime cross-check that the SAME seven arrays the module-
34521 // level `const _: () = ...` witnesses cover at COMPILE time
34522 // are pairwise distinct. A regression that removes ONE of the
34523 // `const _` witnesses would still leave THIS runtime pin as a
34524 // safety net; the const witness fires FIRST at `cargo check`,
34525 // this runtime pin catches the collision at `cargo test`. The
34526 // pair enforces the theorem at TWO stages of the toolchain.
34527 assert_char_array_pairwise_distinct(&Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS);
34528 assert_char_array_pairwise_distinct(&Sexp::LIST_DELIMITERS);
34529 assert_char_array_pairwise_distinct(&Sexp::COMMENT_DELIMITERS);
34530 assert_char_array_pairwise_distinct(&QuoteForm::LEADS);
34531 assert_char_array_pairwise_distinct(&Atom::SELF_ESCAPE_TABLE);
34532 assert_char_array_pairwise_distinct(&Atom::ESCAPE_SOURCES);
34533 assert_char_array_pairwise_distinct(&Atom::ESCAPE_DECODED);
34534 }
34535
34536 #[test]
34537 #[should_panic(expected = "assert_char_array_pairwise_distinct")]
34538 fn assert_char_array_pairwise_distinct_panics_at_runtime_on_binary_collision() {
34539 // NEGATIVE PIN — binary corner: a two-element array carrying
34540 // the same char twice MUST panic at runtime (the const-eval
34541 // panic surfaces normally when the function is invoked from
34542 // a runtime context, not just a `const _` context). Pins the
34543 // helper's OWN reject-collision arm — a regression that
34544 // silently returns without panicking on a duplicate would
34545 // slip through the compile-time witnesses' failure mode too.
34546 assert_char_array_pairwise_distinct(&['x', 'x']);
34547 }
34548
34549 #[test]
34550 #[should_panic(expected = "assert_char_array_pairwise_distinct")]
34551 fn assert_char_array_pairwise_distinct_panics_at_runtime_on_non_adjacent_collision() {
34552 // NEGATIVE PIN — non-adjacent corner: the collision fires on
34553 // ANY (i, j) pair with i < j, not just the adjacent (0, 1)
34554 // corner. Pins the nested-loop shape of the helper — a
34555 // regression that walked ONLY the adjacent pairs (i.e., swept
34556 // `while i + 1 < N { if arr[i] == arr[i+1] { panic } … }`)
34557 // would silently accept `['a', 'b', 'a']` (non-adjacent
34558 // collision at positions 0 and 2), missing the contract.
34559 assert_char_array_pairwise_distinct(&['a', 'b', 'a']);
34560 }
34561
34562 #[test]
34563 #[should_panic(expected = "assert_char_array_pairwise_distinct")]
34564 fn assert_char_array_pairwise_distinct_panics_at_runtime_on_terminal_collision() {
34565 // NEGATIVE PIN — terminal corner: the collision at the LAST
34566 // pair (positions N-2 and N-1) MUST also fire. Pins the outer
34567 // `while i < N` bound — a regression that walked `while i <
34568 // N - 1` (dropping the last row) would silently accept a
34569 // collision at the tail.
34570 assert_char_array_pairwise_distinct(&['a', 'b', 'c', 'd', 'd']);
34571 }
34572
34573 #[test]
34574 fn assert_char_array_pairwise_distinct_panic_message_names_the_helper() {
34575 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST begin
34576 // with the helper's own name so downstream diagnostics
34577 // (`cargo check` const-eval error output, test-suite failure
34578 // reports) route the drift back to the helper by string
34579 // search — the family-wide contract's failure mode surfaces
34580 // as an identifiable panic-message prefix rather than as an
34581 // opaque const-eval error. Sibling posture to the runtime
34582 // pairwise-distinctness tests that name the ARRAY in their
34583 // failure message; this pin names the HELPER.
34584 let outcome = std::panic::catch_unwind(|| {
34585 assert_char_array_pairwise_distinct(&['x', 'x']);
34586 });
34587 let payload = outcome.expect_err(
34588 "assert_char_array_pairwise_distinct must panic on a \
34589 duplicate — the reject-collision arm is the point of the \
34590 helper",
34591 );
34592 let msg = payload
34593 .downcast_ref::<&'static str>()
34594 .map(|s| (*s).to_owned())
34595 .or_else(|| payload.downcast_ref::<String>().cloned())
34596 .expect(
34597 "assert_char_array_pairwise_distinct panic payload \
34598 must be a static &str or String",
34599 );
34600 assert!(
34601 msg.contains("assert_char_array_pairwise_distinct"),
34602 "assert_char_array_pairwise_distinct panic message \
34603 {msg:?} must name the helper for provenance-preserving \
34604 failure diagnostics",
34605 );
34606 }
34607
34608 // ── assert_char_array_all_ascii — the per-entry ASCII-SCALAR-RANGE
34609 // gate row-dual peer of `assert_str_array_all_ascii` on the
34610 // (element-type ∈ {char, `&'static str`}) axis of the (per-entry ×
34611 // contract-shape) matrix at the (per-entry, ASCII) column. Contract-
34612 // orthogonal sibling of `assert_char_array_pairwise_distinct` on
34613 // the (INJECTIVITY, ASCII) axis of the SAME (`char`) row. ──
34614
34615 #[test]
34616 fn assert_char_array_all_ascii_accepts_the_empty_array() {
34617 // Empty array — vacuously all-ASCII (no entry to fail the
34618 // scalar-range gate). The compile-time `const _: () =
34619 // assert_char_array_all_ascii(&EMPTY);` would land on this
34620 // arm, so the runtime call MUST return normally. Sibling
34621 // posture to `assert_str_array_all_ascii_accepts_the_empty_
34622 // array` on the (`&'static str`) row-dual ASCII helper — the
34623 // two share the trivial-arity arm across the element-type
34624 // column.
34625 assert_char_array_all_ascii::<0>(&[]);
34626 }
34627
34628 #[test]
34629 fn assert_char_array_all_ascii_accepts_ascii_singleton_arrays() {
34630 // Singleton array carrying an all-ASCII entry — the sole
34631 // entry clears the scalar-range gate. Cross-arity coverage
34632 // on the `[char; 1]` corner of the const-N generic. Includes
34633 // a substrate-scalar entry (`Sexp::LIST_OPEN`) to cover the
34634 // named-constant projection alongside the char literal
34635 // projection.
34636 assert_char_array_all_ascii(&['a']);
34637 assert_char_array_all_ascii(&[Sexp::LIST_OPEN]);
34638 }
34639
34640 #[test]
34641 fn assert_char_array_all_ascii_accepts_every_family_wide_substrate_array() {
34642 // Runtime cross-check that the SAME seven arrays the module-
34643 // level `const _: () = ...` witnesses cover at COMPILE time
34644 // are all-ASCII. Sibling posture to the runtime
34645 // `_pairwise_distinct_accepts_every_family_wide_substrate_
34646 // array` cross-check — the two together pin BOTH the SET-
34647 // LEVEL INJECTIVITY axis AND the PER-ENTRY ASCII-SCALAR-RANGE
34648 // axis on the SAME seven arrays, at TWO stages of the
34649 // toolchain (compile-time `const _` line + this runtime
34650 // safety-net). Row-dual posture to
34651 // `assert_str_array_all_ascii_accepts_every_family_wide_
34652 // substrate_array` on the (`&'static str`) row — the two
34653 // together sweep the ASCII contract across every reader-
34654 // boundary AND every outer-algebra vocabulary the substrate
34655 // ships.
34656 assert_char_array_all_ascii(&Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS);
34657 assert_char_array_all_ascii(&Sexp::LIST_DELIMITERS);
34658 assert_char_array_all_ascii(&Sexp::COMMENT_DELIMITERS);
34659 assert_char_array_all_ascii(&QuoteForm::LEADS);
34660 assert_char_array_all_ascii(&Atom::SELF_ESCAPE_TABLE);
34661 assert_char_array_all_ascii(&Atom::ESCAPE_SOURCES);
34662 assert_char_array_all_ascii(&Atom::ESCAPE_DECODED);
34663 }
34664
34665 #[test]
34666 fn assert_char_array_all_ascii_accepts_the_ascii_boundary_scalar() {
34667 // Boundary-inclusive pin on the ASCII scalar range's upper
34668 // edge — U+007F (DEL, the last ASCII scalar) MUST be
34669 // accepted. Guards against an off-by-one regression that
34670 // walked `arr[i] as u32 >= 0x7F` and spuriously rejected the
34671 // sole `'\u{7F}'` character. Together with the negative pins
34672 // below (which fire on U+0080 — the first non-ASCII scalar),
34673 // the two pin the scalar-range boundary at both edges.
34674 assert_char_array_all_ascii(&['\u{7F}']);
34675 }
34676
34677 #[test]
34678 #[should_panic(expected = "assert_char_array_all_ascii")]
34679 fn assert_char_array_all_ascii_panics_at_runtime_on_singleton_non_ascii() {
34680 // NEGATIVE PIN — singleton corner: a one-element array
34681 // carrying a non-ASCII scalar MUST panic. Pins the helper's
34682 // own reject-non-ascii arm on the smallest possible array
34683 // shape. The scalar `'é'` (U+00E9) has `as u32 == 0xE9 >
34684 // 0x7F` and fires the range gate.
34685 assert_char_array_all_ascii(&['é']);
34686 }
34687
34688 #[test]
34689 #[should_panic(expected = "assert_char_array_all_ascii")]
34690 fn assert_char_array_all_ascii_panics_at_runtime_on_head_non_ascii() {
34691 // NEGATIVE PIN — head corner: the non-ASCII scalar at
34692 // position 0 MUST fire even when subsequent entries are
34693 // ASCII. Pins the sweep's inclusive-start behavior — a
34694 // regression that walked `while i < N { … i += 1 }` from
34695 // an off-by-one start (`i = 1`) would silently accept a
34696 // leading non-ASCII entry.
34697 assert_char_array_all_ascii(&['é', 'a', 'b']);
34698 }
34699
34700 #[test]
34701 #[should_panic(expected = "assert_char_array_all_ascii")]
34702 fn assert_char_array_all_ascii_panics_at_runtime_on_interior_non_ascii() {
34703 // NEGATIVE PIN — interior corner: the non-ASCII scalar at a
34704 // strictly-interior position MUST fire. Pins the sweep's
34705 // non-early-exit behavior at the head-arm — a regression
34706 // that returned `Ok` on the first ASCII entry (bailing out
34707 // of the sweep prematurely) would silently accept an
34708 // interior non-ASCII entry.
34709 assert_char_array_all_ascii(&['a', 'é', 'b']);
34710 }
34711
34712 #[test]
34713 #[should_panic(expected = "assert_char_array_all_ascii")]
34714 fn assert_char_array_all_ascii_panics_at_runtime_on_tail_non_ascii() {
34715 // NEGATIVE PIN — tail corner: the non-ASCII scalar at
34716 // position `N - 1` MUST fire. Pins the outer `while i < N`
34717 // upper bound — a regression that walked `while i < N - 1`
34718 // (dropping the last slot) would silently accept a trailing
34719 // non-ASCII entry.
34720 assert_char_array_all_ascii(&['a', 'b', 'c', 'é']);
34721 }
34722
34723 #[test]
34724 #[should_panic(expected = "assert_char_array_all_ascii")]
34725 fn assert_char_array_all_ascii_panics_on_the_first_non_ascii_scalar() {
34726 // NEGATIVE PIN — first-non-ASCII-scalar boundary: U+0080
34727 // (the smallest non-ASCII scalar) MUST fire. Guards against
34728 // an off-by-one regression that walked `arr[i] as u32 >
34729 // 0x80` (dropping U+0080 from the reject set). Together
34730 // with `_accepts_the_ascii_boundary_scalar` (which pins
34731 // U+007F acceptance), the two pin the scalar-range boundary
34732 // at both edges. Row-dual posture to `assert_str_array_all_
34733 // ascii_panics_on_the_first_non_ascii_byte` — the two
34734 // together pin the boundary at both element-type projections.
34735 assert_char_array_all_ascii(&['\u{80}']);
34736 }
34737
34738 #[test]
34739 fn assert_char_array_all_ascii_rejects_the_all_non_ascii_array() {
34740 // POSITIVE-ORTHOGONAL PIN — the ALL-non-ASCII corner: an
34741 // array whose every entry is a non-ASCII scalar fires the
34742 // helper on the FIRST entry (the head-arm). Confirms the
34743 // helper does not silently accept an array whose every
34744 // entry ships non-ASCII scalars. Row-dual posture to
34745 // `assert_str_array_all_ascii_rejects_the_all_non_ascii_
34746 // array` on the (`&'static str`) row — the two together pin
34747 // the all-reject corner across the element-type column.
34748 let outcome = std::panic::catch_unwind(|| {
34749 assert_char_array_all_ascii(&['é', 'ü']);
34750 });
34751 outcome.expect_err(
34752 "assert_char_array_all_ascii must panic on an array \
34753 whose every entry is a non-ASCII scalar — the head-arm \
34754 fires on the first non-ASCII scalar at position 0",
34755 );
34756 }
34757
34758 #[test]
34759 fn assert_char_array_all_ascii_panic_message_names_the_helper_and_axis() {
34760 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST
34761 // begin with the helper's own name AND name the failed axis
34762 // as `"CHAR-NON-ASCII-SCALAR"` (chosen DISTINCT from every
34763 // sibling helper's axis vocabulary: `"duplicate"` on the
34764 // pairwise-distinct sibling; `"CHAR-SUBSET-VIOLATION"` on
34765 // the within-finite-set sibling; `"CHAR-DISJOINTNESS-
34766 // VIOLATION"` on the arrays-disjoint sibling; `"STR-NON-
34767 // ASCII-ENTRY"` on the (`&'static str`) row-dual ASCII
34768 // sibling) so a diagnostic that names the failed axis routes
34769 // UNAMBIGUOUSLY to THIS specific (`char`)-row ASCII helper.
34770 // The `"CHAR-"` prefix disambiguates from the (`&'static
34771 // str`) row-dual ASCII sibling; the shared `"-NON-ASCII-"`
34772 // infix lets callers grep any row's ASCII sibling by `"NON-
34773 // ASCII"` alone.
34774 let outcome = std::panic::catch_unwind(|| {
34775 assert_char_array_all_ascii(&['é']);
34776 });
34777 let payload = outcome.expect_err(
34778 "assert_char_array_all_ascii must panic on a non-ASCII \
34779 scalar — the reject-non-ascii arm is the point of the \
34780 helper",
34781 );
34782 let msg = payload
34783 .downcast_ref::<&'static str>()
34784 .map(|s| (*s).to_owned())
34785 .or_else(|| payload.downcast_ref::<String>().cloned())
34786 .expect(
34787 "assert_char_array_all_ascii panic payload must be \
34788 a static &str or String",
34789 );
34790 assert!(
34791 msg.contains("assert_char_array_all_ascii"),
34792 "assert_char_array_all_ascii panic message {msg:?} must \
34793 name the helper for provenance-preserving failure \
34794 diagnostics",
34795 );
34796 assert!(
34797 msg.contains("CHAR-NON-ASCII-SCALAR"),
34798 "assert_char_array_all_ascii panic message {msg:?} must \
34799 name the failed axis as `CHAR-NON-ASCII-SCALAR` \
34800 DISTINCT from every sibling helper's axis vocabulary",
34801 );
34802 assert!(
34803 !msg.contains("STR-NON-ASCII-ENTRY"),
34804 "assert_char_array_all_ascii panic message {msg:?} must \
34805 NOT name the (`&'static str`) row-dual sibling's axis \
34806 — the two row-dual ASCII helpers must keep their axis-\
34807 provenance strings lexically distinct on the element-\
34808 type column",
34809 );
34810 assert!(
34811 !msg.contains("CHAR-SUBSET-VIOLATION"),
34812 "assert_char_array_all_ascii panic message {msg:?} must \
34813 NOT name the SUBSET-embedding sibling's axis — the \
34814 per-entry ASCII helper and the SUBSET-embedding helper \
34815 must keep their axis-provenance strings lexically \
34816 distinct on the contract-shape column",
34817 );
34818 }
34819
34820 // ── `assert_char_array_within_char_finite_set` — the char-element
34821 // SUBSET-EMBEDDING verifier that binds `arr ⊆ set` at compile time
34822 // on the reader-boundary `char` vocabulary, peer to the (u8) row's
34823 // `assert_u8_array_within_u8_finite_set` on the (element-type ×
34824 // contract-shape) matrix. The runtime test surface pins each of
34825 // the helper's arms (accept-empty, accept-singleton-in-set,
34826 // accept-arr-equals-set, accept-each-family-wide-substrate-subset,
34827 // accept-array-duplicates-in-set, reject-single-out-of-set-entry,
34828 // reject-terminal-out-of-set-entry, panic-message-provenance on
34829 // the CHAR-SUBSET-VIOLATION axis, negative pin on the DELEGATED
34830 // SET-side well-formedness arm) so a regression that silently
34831 // weakened the helper on ANY arm is caught by the helper's OWN
34832 // test surface rather than only surfacing as a false-positive on
34833 // some future subset-embedded `[char; N]` array's compound pin.
34834
34835 #[test]
34836 fn assert_char_array_within_char_finite_set_accepts_the_empty_array_within_any_set() {
34837 // Empty array `arr = []` at the `[char; 0]` corner — vacuously
34838 // a subset of every set (no `i` position exists to test).
34839 // Cross-arity coverage on the trivial ARRAY corner of the
34840 // const-N generic across three witness-set widths (empty,
34841 // singleton, multi-element) to pin the helper's OUTER-sweep
34842 // arm across the whole (`N == 0` × `M`) axis. Turbofish
34843 // binding required because there's no other cue for the const
34844 // parameters on the empty array literal. Sibling posture to
34845 // `assert_u8_array_within_u8_finite_set_accepts_the_empty_array_within_any_set`
34846 // on the (u8) row's SUBSET-EMBEDDING helper — the two share
34847 // the trivial-arity arm across the element-type column.
34848 assert_char_array_within_char_finite_set::<0, 0>(&[], &[]);
34849 assert_char_array_within_char_finite_set::<0, 1>(&[], &['x']);
34850 assert_char_array_within_char_finite_set::<0, 3>(&[], &['a', 'b', 'c']);
34851 }
34852
34853 #[test]
34854 fn assert_char_array_within_char_finite_set_accepts_singleton_array_when_char_in_set() {
34855 // Singleton array `arr = [K]` at the `[char; 1]` corner MUST
34856 // pass when `K ∈ set`. Cross-position coverage: the char can
34857 // sit at the FIRST, MIDDLE, or LAST position of the `set` —
34858 // pins the INNER `while j < M` sweep terminates at the first-
34859 // match position rather than always at position `0` OR always
34860 // at position `M - 1`. A regression that narrowed the inner
34861 // sweep to `j == 0` would silently reject singleton arrays
34862 // hitting non-first set positions.
34863 assert_char_array_within_char_finite_set::<1, 3>(&['a'], &['a', 'b', 'c']);
34864 assert_char_array_within_char_finite_set::<1, 3>(&['b'], &['a', 'b', 'c']);
34865 assert_char_array_within_char_finite_set::<1, 3>(&['c'], &['a', 'b', 'c']);
34866 }
34867
34868 #[test]
34869 fn assert_char_array_within_char_finite_set_accepts_arr_equals_set() {
34870 // Boundary corner where `arr` and `set` cover byte-for-byte
34871 // identical distinct-value sets — the SUBSET relation
34872 // degenerates to EQUALITY. Pins that the helper does NOT
34873 // gratuitously require the SUBSET to be PROPER (strict):
34874 // equal-multisets pass the SUBSET check. Sibling posture to
34875 // `assert_u8_array_within_u8_finite_set_accepts_arr_equals_set`
34876 // on the (u8) row's SUBSET-EMBEDDING helper — the two share
34877 // the EQUAL-SETS corner across the element-type column.
34878 assert_char_array_within_char_finite_set(&['a', 'b'], &['a', 'b']);
34879 assert_char_array_within_char_finite_set(&[Sexp::LIST_OPEN], &[Sexp::LIST_OPEN]);
34880 }
34881
34882 #[test]
34883 fn assert_char_array_within_char_finite_set_accepts_each_family_wide_substrate_subset() {
34884 // Runtime cross-check that the THREE (subset, superset) pairs
34885 // the substrate's module-level `const _` witnesses pin at
34886 // COMPILE time are PROPER SUBSET embeddings at runtime too.
34887 // The pairs enforce the theorem at TWO stages of the
34888 // toolchain: the const witnesses fire FIRST at `cargo check`
34889 // (through the three module-level `const _: () =
34890 // assert_char_array_within_char_finite_set::<N, M>(...)`
34891 // lines), this runtime pin catches the drift at `cargo test`
34892 // as a safety net. Sibling posture to
34893 // `assert_char_array_pairwise_distinct_accepts_every_family_wide_substrate_array`
34894 // which sweeps the seven family-wide `[char; N]` arrays at
34895 // the INJECTIVITY axis; this pin sweeps the three (subset,
34896 // superset) PAIRS at the SUBSET-EMBEDDING axis.
34897 assert_char_array_within_char_finite_set::<2, 7>(
34898 &Sexp::LIST_DELIMITERS,
34899 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
34900 );
34901 assert_char_array_within_char_finite_set::<3, 7>(
34902 &QuoteForm::LEADS,
34903 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
34904 );
34905 assert_char_array_within_char_finite_set::<2, 5>(
34906 &Atom::SELF_ESCAPE_TABLE,
34907 &Atom::ESCAPE_SOURCES,
34908 );
34909 }
34910
34911 #[test]
34912 fn assert_char_array_within_char_finite_set_accepts_repeated_array_entries_in_set() {
34913 // Peer corner to a (future-lift) `_covers_char_finite_set`:
34914 // this helper permits duplicates in `arr` because SUBSET-
34915 // membership is a DISTINCT-value predicate — `['a', 'a', 'b']`
34916 // is a subset of `{'a', 'b', 'c'}` even though the array is
34917 // not pairwise-distinct. Pins that the helper does NOT
34918 // gratuitously require INJECTIVITY on `arr` (the injectivity
34919 // axis is a DIFFERENT compile-time contract bound by
34920 // `assert_char_array_pairwise_distinct`; combining both binds
34921 // BOTH axes). Sibling posture to
34922 // `assert_u8_array_within_u8_finite_set_accepts_repeated_array_entries_in_set`
34923 // on the (u8) row's SUBSET-EMBEDDING peer.
34924 assert_char_array_within_char_finite_set(&['a', 'a', 'b'], &['a', 'b', 'c']);
34925 }
34926
34927 #[test]
34928 #[should_panic(expected = "CHAR-SUBSET-VIOLATION")]
34929 fn assert_char_array_within_char_finite_set_panics_at_runtime_on_out_of_set_entry() {
34930 // NEGATIVE PIN — CHAR-SUBSET-VIOLATION corner: an array
34931 // carrying a single entry NOT in the target set MUST panic at
34932 // runtime with the CHAR-SUBSET-VIOLATION-named message. Pins
34933 // the helper's OWN reject arm — a regression that silently
34934 // returned without panicking on an out-of-set entry would
34935 // slip through the compile-time witnesses' failure mode too.
34936 // The offending char `'z'` is intentionally chosen OUTSIDE
34937 // the target set to pin the OUT-OF-SET drift mode.
34938 assert_char_array_within_char_finite_set(&['a', 'z'], &['a', 'b', 'c']);
34939 }
34940
34941 #[test]
34942 #[should_panic(expected = "CHAR-SUBSET-VIOLATION")]
34943 fn assert_char_array_within_char_finite_set_panics_at_runtime_on_terminal_out_of_set_entry() {
34944 // NEGATIVE PIN — terminal-position drift: an out-of-set entry
34945 // at the LAST array position MUST panic — pins that the outer
34946 // `while i < N` loop reaches `i = N - 1` (else the terminal
34947 // drift would slip through). A regression that narrowed the
34948 // outer sweep to `while i < N - 1` (off-by-one on the OUTER
34949 // bound) would silently accept this array. Sibling posture to
34950 // `assert_u8_array_within_u8_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
34951 // on the (u8) row's terminal-position pin — both bind the
34952 // outer-sweep terminal bound at the ONE array-side outer loop
34953 // the helper carries.
34954 assert_char_array_within_char_finite_set(&['a', 'b', 'c', 'z'], &['a', 'b', 'c']);
34955 }
34956
34957 #[test]
34958 fn assert_char_array_within_char_finite_set_panic_message_names_the_helper_and_char_subset_violation_axis(
34959 ) {
34960 // PANIC-MESSAGE PROVENANCE PIN — CHAR-SUBSET-VIOLATION arm:
34961 // the panic message MUST begin with the helper's own name AND
34962 // identify the failed AXIS as "CHAR-SUBSET-VIOLATION" so
34963 // downstream diagnostics route the drift back to (a) the
34964 // helper by string search on
34965 // `"assert_char_array_within_char_finite_set"` and (b) the
34966 // axis by string search on `"CHAR-SUBSET-VIOLATION"`. Sibling
34967 // posture to
34968 // `assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis`
34969 // on the (u8) row's provenance pin — the two pins together
34970 // bind the (helper, failed-axis) provenance pair at ONE test
34971 // per SUBSET helper on the (element-type) 2×1 face. The axis-
34972 // provenance string `"CHAR-SUBSET-VIOLATION"` is chosen
34973 // DISTINCT from EVERY sibling helper's axis vocabulary
34974 // (`"duplicate"` on the ARRAY-side pairwise-distinct sibling;
34975 // `"SUBSET-VIOLATION"` on the (u8) finite-set SUBSET-only
34976 // sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8) range
34977 // SUBSET-only sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"`
34978 // on the (u8) covers-finite-set sibling; `"OUT-OF-RANGE"` /
34979 // `"MISSING"` on the (u8) covers-inclusive-range sibling;
34980 // `"ARITY-MISMATCH"` on both (u8) `_permutes_*` compound
34981 // helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on the (u8) SET-side
34982 // well-formedness sibling) so a diagnostic that names the
34983 // failed axis routes UNAMBIGUOUSLY to (a) this specific char
34984 // SUBSET-embedding helper, (b) the `arr` argument as the
34985 // drift site rather than the `set` argument specifying the
34986 // target superset.
34987 let outcome = std::panic::catch_unwind(|| {
34988 assert_char_array_within_char_finite_set(&['a', 'z'], &['a', 'b', 'c']);
34989 });
34990 let payload = outcome.expect_err(
34991 "assert_char_array_within_char_finite_set must panic on \
34992 an out-of-set entry — the reject-out-of-set arm is the \
34993 sole CHAR-SUBSET-VIOLATION failure mode of the helper",
34994 );
34995 let msg = payload
34996 .downcast_ref::<&'static str>()
34997 .map(|s| (*s).to_owned())
34998 .or_else(|| payload.downcast_ref::<String>().cloned())
34999 .expect(
35000 "assert_char_array_within_char_finite_set panic \
35001 payload must be a static &str or String",
35002 );
35003 assert!(
35004 msg.contains("assert_char_array_within_char_finite_set"),
35005 "assert_char_array_within_char_finite_set panic message \
35006 {msg:?} must name the helper for provenance-preserving \
35007 failure diagnostics",
35008 );
35009 assert!(
35010 msg.contains("CHAR-SUBSET-VIOLATION"),
35011 "assert_char_array_within_char_finite_set panic message \
35012 {msg:?} must name the failed AXIS (\"CHAR-SUBSET-\
35013 VIOLATION\") for axis-provenance-preserving failure \
35014 diagnostics",
35015 );
35016 }
35017
35018 #[test]
35019 #[should_panic(expected = "assert_char_array_pairwise_distinct")]
35020 fn assert_char_array_within_char_finite_set_panics_on_malformed_target_set_spec() {
35021 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
35022 // malformed target-set spec `['a', 'a', 'b']` fed into the
35023 // ARRAY-side within helper MUST panic on the DELEGATED
35024 // pairwise-distinct arm BEFORE the CHAR-SUBSET-VIOLATION arm
35025 // fires. Pins the delegation chain: a regression that dropped
35026 // the `assert_char_array_pairwise_distinct(set)` call at the
35027 // top of `assert_char_array_within_char_finite_set` would
35028 // silently accept a malformed set and produce a false-positive
35029 // verdict on any `arr` embedded in the DISTINCT-value subset.
35030 // The panic message here surfaces from the SIBLING helper
35031 // (containing the ARRAY-side helper's `"assert_char_array_
35032 // pairwise_distinct"` panic-name prefix rather than a bespoke
35033 // `"SET-NOT-PAIRWISE-DISTINCT"` axis string) because the char
35034 // row does NOT yet carry a separate `assert_char_finite_set_
35035 // pairwise_distinct` alias — the delegation reuses the
35036 // ARRAY-side helper directly per the design choice documented
35037 // on the SET-side-well-formedness section of the helper's
35038 // docstring. Sibling posture to
35039 // `assert_u8_array_within_u8_finite_set_panics_on_malformed_target_set_spec`
35040 // on the (u8) row's delegated-SET-well-formedness pin — the
35041 // two pins together bind the delegation chain at ONE test per
35042 // element-type row.
35043 assert_char_array_within_char_finite_set::<2, 3>(&['a', 'b'], &['a', 'b', 'b']);
35044 }
35045
35046 // ── `assert_char_arrays_disjoint` — the CHAR-DISJOINTNESS-VIOLATION
35047 // verifier that binds `a ∩ b = ∅` at compile time on the reader-
35048 // boundary `char` vocabulary, peer to
35049 // `assert_char_array_within_char_finite_set` on the (char) row of
35050 // the (subset, disjointness) 2-corner face of the (contract-shape)
35051 // axis. The runtime test surface pins each of the helper's arms
35052 // (accept-both-empty, accept-either-empty, accept-disjoint-
35053 // singletons, accept-each-family-wide-substrate-pair, accept-arg-
35054 // order-symmetry, reject-single-collision, reject-terminal-a-
35055 // collision, reject-terminal-b-collision, panic-message-provenance
35056 // on the CHAR-DISJOINTNESS-VIOLATION axis) so a regression that
35057 // silently weakened the helper on ANY arm is caught by the
35058 // helper's OWN test surface rather than only surfacing as a false-
35059 // positive on some future disjoint `[char; N] × [char; M]` pair's
35060 // compound pin.
35061
35062 #[test]
35063 fn assert_char_arrays_disjoint_accepts_both_empty_arrays() {
35064 // Both arrays empty at the `[char; 0] × [char; 0]` corner —
35065 // vacuously disjoint (no `(i, j)` pair exists to test). The
35066 // compile-time `const _: () = assert_char_arrays_disjoint(&[],
35067 // &[]);` would land on this arm, so the runtime call MUST
35068 // return normally. Turbofish binding required because there's
35069 // no other cue for the const parameters on the empty array
35070 // literals. Sibling posture to
35071 // `assert_char_array_within_char_finite_set_accepts_the_empty_array_within_any_set`
35072 // on the peer SUBSET-embedding helper — the two share the
35073 // trivial-arity arm across the (subset, disjointness) 2-corner
35074 // face on the (char) row.
35075 assert_char_arrays_disjoint::<0, 0>(&[], &[]);
35076 }
35077
35078 #[test]
35079 fn assert_char_arrays_disjoint_accepts_either_side_empty() {
35080 // Either side empty at the `[char; 0] × [char; M]` OR
35081 // `[char; N] × [char; 0]` corners — vacuously disjoint (the
35082 // OUTER `while i < N` OR the INNER `while j < M` sweep is a
35083 // no-op). Pins BOTH SIDES of the (a-empty, b-empty) 2-corner
35084 // sub-face on the trivial-arity axis so a regression that
35085 // narrowed the sweep to only-a-non-empty OR only-b-non-empty
35086 // fails HERE at the empty-side corner rather than at a distant
35087 // false-positive.
35088 assert_char_arrays_disjoint::<0, 3>(&[], &['a', 'b', 'c']);
35089 assert_char_arrays_disjoint::<3, 0>(&['a', 'b', 'c'], &[]);
35090 }
35091
35092 #[test]
35093 fn assert_char_arrays_disjoint_accepts_disjoint_singletons() {
35094 // Singleton `a = [K]` and singleton `b = [L]` with `K != L` at
35095 // the `[char; 1] × [char; 1]` corner — the minimal non-empty
35096 // disjointness relation. Pins the INNER `if a[i] != b[j]` gate
35097 // returns without panicking on a single distinct-pair check.
35098 // A regression that flipped the equality direction (`==` vs
35099 // `!=`) would silently reject every disjoint singleton pair.
35100 assert_char_arrays_disjoint(&['a'], &['b']);
35101 }
35102
35103 #[test]
35104 fn assert_char_arrays_disjoint_accepts_each_family_wide_substrate_pair() {
35105 // Runtime cross-check that the FIVE (a, b) `[char; N] ×
35106 // [char; M]` pairs the substrate's module-level `const _`
35107 // witnesses pin at COMPILE time are proper DISJOINTNESS
35108 // embeddings at runtime too. The pairs enforce the theorem at
35109 // TWO stages of the toolchain: the const witnesses fire FIRST
35110 // at `cargo check` (through the five module-level `const _:
35111 // () = assert_char_arrays_disjoint::<N, M>(...)` lines), this
35112 // runtime pin catches the drift at `cargo test` as a safety
35113 // net. Sibling posture to
35114 // `assert_char_array_within_char_finite_set_accepts_each_family_wide_substrate_subset`
35115 // which sweeps the three (subset, superset) PAIRS at the
35116 // SUBSET-EMBEDDING axis; this pin sweeps the five (a, b)
35117 // PAIRS at the DISJOINTNESS axis on the SAME (char) row.
35118 assert_char_arrays_disjoint::<2, 2>(&Sexp::LIST_DELIMITERS, &Sexp::COMMENT_DELIMITERS);
35119 assert_char_arrays_disjoint::<2, 3>(&Sexp::LIST_DELIMITERS, &QuoteForm::LEADS);
35120 assert_char_arrays_disjoint::<2, 3>(&Sexp::COMMENT_DELIMITERS, &QuoteForm::LEADS);
35121 assert_char_arrays_disjoint::<2, 2>(&Sexp::LIST_DELIMITERS, &Atom::SELF_ESCAPE_TABLE);
35122 assert_char_arrays_disjoint::<3, 2>(&QuoteForm::LEADS, &Atom::SELF_ESCAPE_TABLE);
35123 }
35124
35125 #[test]
35126 fn assert_char_arrays_disjoint_is_symmetric_in_argument_order() {
35127 // SYMMETRY PIN: swapping the two arguments produces the SAME
35128 // verdict. Pins that the disjointness relation is truly
35129 // symmetric across the two array arguments (the helper's
35130 // nested-sweep implementation does NOT gratuitously depend on
35131 // argument order). A regression that narrowed the sweep to
35132 // `for i in a { if !b.contains(a[i]) }` (subset-shape, not
35133 // disjointness-shape) would fail the swap on one direction
35134 // only. Runs each substrate pair BOTH ways.
35135 assert_char_arrays_disjoint(&Sexp::COMMENT_DELIMITERS, &Sexp::LIST_DELIMITERS);
35136 assert_char_arrays_disjoint(&QuoteForm::LEADS, &Sexp::LIST_DELIMITERS);
35137 assert_char_arrays_disjoint(&Atom::SELF_ESCAPE_TABLE, &QuoteForm::LEADS);
35138 }
35139
35140 #[test]
35141 #[should_panic(expected = "CHAR-DISJOINTNESS-VIOLATION")]
35142 fn assert_char_arrays_disjoint_panics_at_runtime_on_collision() {
35143 // NEGATIVE PIN — CHAR-DISJOINTNESS-VIOLATION corner: two arrays
35144 // sharing a single entry MUST panic at runtime with the
35145 // CHAR-DISJOINTNESS-VIOLATION-named message. Pins the helper's
35146 // OWN reject arm — a regression that silently returned
35147 // without panicking on a cross-array collision would slip
35148 // through the compile-time witnesses' failure mode too. The
35149 // shared entry `'a'` is intentionally placed at the FIRST
35150 // position of BOTH arrays to pin the initial-position drift
35151 // mode.
35152 assert_char_arrays_disjoint(&['a', 'b'], &['a', 'c']);
35153 }
35154
35155 #[test]
35156 #[should_panic(expected = "CHAR-DISJOINTNESS-VIOLATION")]
35157 fn assert_char_arrays_disjoint_panics_at_runtime_on_terminal_a_collision() {
35158 // NEGATIVE PIN — terminal-position drift on the OUTER `a` side:
35159 // a shared entry at the LAST position of `a` MUST panic — pins
35160 // that the outer `while i < N` loop reaches `i = N - 1` (else
35161 // the terminal drift on `a` would slip through). A regression
35162 // that narrowed the outer sweep to `while i < N - 1` (off-by-
35163 // one on the OUTER bound) would silently accept this pair.
35164 assert_char_arrays_disjoint(&['x', 'y', 'z'], &['z']);
35165 }
35166
35167 #[test]
35168 #[should_panic(expected = "CHAR-DISJOINTNESS-VIOLATION")]
35169 fn assert_char_arrays_disjoint_panics_at_runtime_on_terminal_b_collision() {
35170 // NEGATIVE PIN — terminal-position drift on the INNER `b` side:
35171 // a shared entry at the LAST position of `b` MUST panic — pins
35172 // that the inner `while j < M` loop reaches `j = M - 1` (else
35173 // the terminal drift on `b` would slip through). A regression
35174 // that narrowed the inner sweep to `while j < M - 1` (off-by-
35175 // one on the INNER bound) would silently accept this pair.
35176 // Sibling posture to the outer-terminal pin above — the two
35177 // pins together bind the terminal bounds on BOTH loops.
35178 assert_char_arrays_disjoint(&['x'], &['a', 'b', 'x']);
35179 }
35180
35181 #[test]
35182 fn assert_char_arrays_disjoint_panic_message_names_the_helper_and_char_disjointness_violation_axis(
35183 ) {
35184 // PANIC-MESSAGE PROVENANCE PIN — CHAR-DISJOINTNESS-VIOLATION
35185 // arm: the panic message MUST begin with the helper's own name
35186 // AND identify the failed AXIS as "CHAR-DISJOINTNESS-VIOLATION"
35187 // so downstream diagnostics route the drift back to (a) the
35188 // helper by string search on `"assert_char_arrays_disjoint"`
35189 // and (b) the axis by string search on `"CHAR-DISJOINTNESS-
35190 // VIOLATION"`. Sibling posture to
35191 // `assert_char_array_within_char_finite_set_panic_message_names_the_helper_and_char_subset_violation_axis`
35192 // on the peer SUBSET-embedding helper's provenance pin — the
35193 // two pins together bind the (helper, failed-axis) provenance
35194 // pair at ONE test per corner of the (subset, disjointness)
35195 // 2-corner face on the (char) row. The axis-provenance string
35196 // `"CHAR-DISJOINTNESS-VIOLATION"` is chosen DISTINCT from EVERY
35197 // sibling helper's axis vocabulary (`"duplicate"` on the
35198 // ARRAY-side pairwise-distinct sibling; `"CHAR-SUBSET-
35199 // VIOLATION"` on the (char) SUBSET-embedding sibling;
35200 // `"SUBSET-VIOLATION"` on the (u8) finite-set SUBSET-only
35201 // sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8) range
35202 // SUBSET-only sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"`
35203 // on the (u8) covers-finite-set sibling; `"OUT-OF-RANGE"` /
35204 // `"MISSING"` on the (u8) covers-inclusive-range sibling;
35205 // `"ARITY-MISMATCH"` on both (u8) `_permutes_*` compound
35206 // helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on the (u8) SET-side
35207 // well-formedness sibling) so a diagnostic that names the
35208 // failed axis routes UNAMBIGUOUSLY to (a) this specific char
35209 // DISJOINTNESS helper.
35210 let outcome = std::panic::catch_unwind(|| {
35211 assert_char_arrays_disjoint(&['a', 'b'], &['a', 'c']);
35212 });
35213 let payload = outcome.expect_err(
35214 "assert_char_arrays_disjoint must panic on a cross-array \
35215 collision — the reject-collision arm is the sole CHAR-\
35216 DISJOINTNESS-VIOLATION failure mode of the helper",
35217 );
35218 let msg = payload
35219 .downcast_ref::<&'static str>()
35220 .map(|s| (*s).to_owned())
35221 .or_else(|| payload.downcast_ref::<String>().cloned())
35222 .expect(
35223 "assert_char_arrays_disjoint panic payload must be a \
35224 static &str or String",
35225 );
35226 assert!(
35227 msg.contains("assert_char_arrays_disjoint"),
35228 "assert_char_arrays_disjoint panic message {msg:?} must \
35229 name the helper for provenance-preserving failure \
35230 diagnostics",
35231 );
35232 assert!(
35233 msg.contains("CHAR-DISJOINTNESS-VIOLATION"),
35234 "assert_char_arrays_disjoint panic message {msg:?} must \
35235 name the failed AXIS (\"CHAR-DISJOINTNESS-VIOLATION\") \
35236 for axis-provenance-preserving failure diagnostics",
35237 );
35238 }
35239
35240 // ── `assert_char_array_slice_equals_char_array` — the CHAR-SLICE-
35241 // EQUALS-ARRAY-VIOLATION verifier that binds the sub-slice
35242 // `full[START..START + M) == sub[..]` positionwise-composition
35243 // contract at compile time on the substrate's reader-boundary
35244 // `[char; N]` scalar-composed vocabulary, row-dual peer to
35245 // `assert_u8_array_slice_equals_u8_array` on the (u8) row of the
35246 // (element-type) axis of the SAME (SUB-SLICE ARRAY-image) column
35247 // of the (element-type × contract-shape) matrix. The runtime test
35248 // surface pins each of the helper's arms (accept-canonical-
35249 // middle-slice, accept-empty-sub-array-at-three-start-positions,
35250 // accept-full-array-degenerate-at-three-arities, accept-each-of-
35251 // the-eight-family-wide-substrate-arrays, reject-positionwise-
35252 // drift, reject-start-out-of-bounds, reject-slice-length-out-of-
35253 // bounds, panic-message-provenance on the CHAR-SLICE-EQUALS-
35254 // ARRAY-VIOLATION axis) so a regression that silently weakened
35255 // the helper on ANY arm (e.g. flipping `!=` to `==` on the char
35256 // comparison, dropping the `START` offset from the `full[START +
35257 // i]` read, returning early past ANY bounds gate, or dropping the
35258 // `as u32` char-to-scalar bridge that lets the const-eval sweep
35259 // proceed byte-for-byte) is caught by the helper's OWN test
35260 // surface rather than only surfacing as a false-positive on some
35261 // future `[char; N]`-typed reader-boundary array's per-position
35262 // ORDER pin.
35263
35264 #[test]
35265 fn assert_char_array_slice_equals_char_array_accepts_a_canonical_middle_slice() {
35266 // Canonical sub-slice `full[START..START + M) == sub[..]`
35267 // inside a longer array `full` whose ENDPOINTS carry
35268 // DIFFERENT chars than the peer sub-array. Pins the outer
35269 // `while i < M` sweep reads `full[START + i]` at the OFFSET
35270 // position (not `full[i]`) — a regression that dropped the
35271 // `START` offset would compare `full[0..M)` against `sub[..]`
35272 // and pass on `full[0]='z' != sub[0]='b'` silently or panic
35273 // on the wrong axis. `START = 1` pins the sweep skips
35274 // position `[0..START)` and reads only `[1..1+3) = [1..4)`.
35275 assert_char_array_slice_equals_char_array::<7, 3, 1>(
35276 &['z', 'b', 'c', 'd', 'z', 'z', 'z'],
35277 &['b', 'c', 'd'],
35278 );
35279 }
35280
35281 #[test]
35282 fn assert_char_array_slice_equals_char_array_accepts_the_empty_sub_array() {
35283 // LEGAL degenerate: `M == 0` collapses the sub-array into an
35284 // empty listing `[]`. The sweep never enters the loop body
35285 // and the helper accepts. Cross-position coverage pins the
35286 // empty-sub-array acceptance at THREE distinct `START`
35287 // positions (`START == 0` at the left endpoint, `START == 3`
35288 // in the interior, `START == N` at the right endpoint — the
35289 // latter is the corner `START == N` combined with `M == 0`
35290 // that the START-OUT-OF-BOUNDS gate's inclusive upper bound
35291 // must accept). A regression that hard-coded `START < N` OR
35292 // panicked on the `M == 0` corner is caught on ALL THREE
35293 // arms.
35294 assert_char_array_slice_equals_char_array::<5, 0, 0>(&['x', 'x', 'x', 'x', 'x'], &[]);
35295 assert_char_array_slice_equals_char_array::<5, 0, 3>(&['x', 'x', 'x', 'x', 'x'], &[]);
35296 assert_char_array_slice_equals_char_array::<5, 0, 5>(&['x', 'x', 'x', 'x', 'x'], &[]);
35297 }
35298
35299 #[test]
35300 fn assert_char_array_slice_equals_char_array_accepts_the_full_array_degenerate() {
35301 // Full-array-covering slice `M == N, START == 0` collapses
35302 // to the ALL-positions-equal-peer-array shape `full == sub`
35303 // pointwise. Pins that the sweep proceeds through EVERY
35304 // position of the outer array when `START = 0` and `M = N`.
35305 // Cross-arity coverage on `N ∈ {1, 3, 7}` pins the sweep's
35306 // terminal-position visit across the range of char arities
35307 // the substrate's reader-boundary arrays span (`N = 1` for
35308 // `UnquoteForm::LEADS`, `N = 2` for the delimiter/escape
35309 // pairs, `N = 3` for `QuoteForm::LEADS`, `N = 5` for
35310 // `ESCAPE_SOURCES` / `ESCAPE_DECODED`, `N = 7` for
35311 // `NON_WHITESPACE_BARE_ATOM_TERMINATORS`).
35312 assert_char_array_slice_equals_char_array::<1, 1, 0>(&[','], &[',']);
35313 assert_char_array_slice_equals_char_array::<3, 3, 0>(&['\'', '`', ','], &['\'', '`', ',']);
35314 assert_char_array_slice_equals_char_array::<7, 7, 0>(
35315 &['(', ')', '\'', '`', ',', '"', ';'],
35316 &['(', ')', '\'', '`', ',', '"', ';'],
35317 );
35318 }
35319
35320 #[test]
35321 fn assert_char_array_slice_equals_char_array_accepts_each_family_wide_substrate_array() {
35322 // Runtime cross-check that the EIGHT reader-boundary
35323 // `[char; N]` scalar-composed substrate arrays each byte-
35324 // equal their canonical literal-char listing pointwise at the
35325 // FULL-ARRAY corner (`M == N`, `START == 0`). Runs the SAME
35326 // helper the eight `const _` witnesses at line ~616 in this
35327 // file run at rustc time — a runtime safety net enforcing
35328 // the theorem at BOTH stages of the toolchain (const at
35329 // `cargo check`, runtime at `cargo test`). A regression that
35330 // renamed one of the per-role `*_LEAD` / `*_DELIMITER` /
35331 // `*_ESCAPE_LEAD` / `*_ESCAPE_SOURCE` / `*_ESCAPE_DECODED`
35332 // aliases (or drifted its literal char value at the
35333 // declaration site, or reordered a slot in the outer array's
35334 // initializer) fails HERE at the substrate callsite AND at
35335 // the const witness above. Peer of
35336 // `assert_u8_array_slice_equals_u8_array_accepts_sub_carving_hash_discriminators_per_position_order`
35337 // on the (u8) row — that witness carries the FULL-ARRAY per-
35338 // position ORDER theorem for the FOUR sub-carving
35339 // `HASH_DISCRIMINATORS` arrays; this witness carries the same
35340 // theorem for the EIGHT reader-boundary `char` arrays.
35341 //
35342 // The eight reader-boundary scalar-composed arrays appear
35343 // here in canonical (owning-algebra, per-role-alias-count-
35344 // ascending) order:
35345 // * `Sexp::LIST_DELIMITERS == ['(', ')']` — the outer-`Sexp`
35346 // list-delimiter pair.
35347 // * `Sexp::COMMENT_DELIMITERS == [';', '\n']` — the outer-
35348 // `Sexp` comment-boundary pair.
35349 // * `Atom::SELF_ESCAPE_TABLE == ['"', '\\']` — the `Atom`
35350 // escape-self pair.
35351 // * `Atom::ESCAPE_SOURCES == ['n', 't', 'r', '"', '\\']` —
35352 // the `Atom` escape-source column.
35353 // * `Atom::ESCAPE_DECODED == ['\n', '\t', '\r', '"', '\\']`
35354 // — the `Atom` escape-decoded column.
35355 // * `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS == ['(',
35356 // ')', '\'', '`', ',', '"', ';']` — the outer-`Sexp`
35357 // reader-boundary category-leading seven-char SPAN.
35358 // * `QuoteForm::LEADS == ['\'', '`', ',']` — the quote-
35359 // family reader-lead-char triple.
35360 // * `UnquoteForm::LEADS == [',']` — the substitution-subset
35361 // shared-lead singleton.
35362 assert_char_array_slice_equals_char_array::<2, 2, 0>(&Sexp::LIST_DELIMITERS, &['(', ')']);
35363 assert_char_array_slice_equals_char_array::<2, 2, 0>(
35364 &Sexp::COMMENT_DELIMITERS,
35365 &[';', '\n'],
35366 );
35367 assert_char_array_slice_equals_char_array::<2, 2, 0>(
35368 &Atom::SELF_ESCAPE_TABLE,
35369 &['"', '\\'],
35370 );
35371 assert_char_array_slice_equals_char_array::<5, 5, 0>(
35372 &Atom::ESCAPE_SOURCES,
35373 &['n', 't', 'r', '"', '\\'],
35374 );
35375 assert_char_array_slice_equals_char_array::<5, 5, 0>(
35376 &Atom::ESCAPE_DECODED,
35377 &['\n', '\t', '\r', '"', '\\'],
35378 );
35379 assert_char_array_slice_equals_char_array::<7, 7, 0>(
35380 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35381 &['(', ')', '\'', '`', ',', '"', ';'],
35382 );
35383 assert_char_array_slice_equals_char_array::<3, 3, 0>(&QuoteForm::LEADS, &['\'', '`', ',']);
35384 assert_char_array_slice_equals_char_array::<1, 1, 0>(
35385 &crate::error::UnquoteForm::LEADS,
35386 &[','],
35387 );
35388 }
35389
35390 #[test]
35391 fn assert_char_array_slice_equals_char_array_accepts_terminator_span_sub_carving_positional_composition(
35392 ) {
35393 // Runtime cross-check that the outer-`Sexp` reader-boundary
35394 // terminator SPAN `Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS`
35395 // (`[char; 7]`) positionally composes as the segmented
35396 // concatenation of its FOUR sub-carvings:
35397 //
35398 // NON_WHITESPACE_BARE_ATOM_TERMINATORS
35399 // == Sexp::LIST_DELIMITERS // slots [0..2)
35400 // ++ QuoteForm::LEADS // slots [2..5)
35401 // ++ [Atom::STR_DELIMITER] // slot [5..6)
35402 // ++ [Sexp::COMMENT_LEAD] // slot [6..7)
35403 //
35404 // Runs the SAME helper the FOUR `const _` witnesses at line
35405 // ~915 in this file run at rustc time — a runtime safety net
35406 // enforcing the ARRAY-LEVEL POSITIONAL-COMPOSITION theorem at
35407 // BOTH stages of the toolchain (const at `cargo check`,
35408 // runtime at `cargo test`). Strictly STRONGER on the
35409 // (contract-strength) axis than the sibling
35410 // `sexp_list_delimiters_positionally_align_with_terminator_head`
35411 // /
35412 // `quote_form_leads_positionally_align_with_terminator_mid`
35413 // -shape pre-lift runtime pins that lived only in prose in the
35414 // sub-carvings' composition-rule docstrings: those pin the
35415 // SUBSET containment `LIST_DELIMITERS ⊆ NON_WHITESPACE_BARE_
35416 // ATOM_TERMINATORS` (and `QuoteForm::LEADS ⊆
35417 // NON_WHITESPACE_BARE_ATOM_TERMINATORS`) as a SET-level
35418 // theorem, order-invariant on the sub-carving side; this pin
35419 // binds the ARRAY-LEVEL POSITIONAL identity at the CANONICAL
35420 // slot segments. A regression that reorders any sub-carving's
35421 // declaration (e.g. `Sexp::LIST_DELIMITERS = [LIST_CLOSE,
35422 // LIST_OPEN]` swapping the pair, or `QuoteForm::LEADS =
35423 // [QUASIQUOTE_LEAD, QUOTE_LEAD, UNQUOTE_LEAD]` permuting the
35424 // triple) preserves the SET-level SUBSET theorem AND the FULL-
35425 // ARRAY LITERAL witness on `NON_WHITESPACE_BARE_ATOM_
35426 // TERMINATORS` (which peer-compares against a HARDCODED
35427 // `[char; 7]` literal via its inline listing rather than
35428 // against the SUB-CARVING arrays) but silently misaligns every
35429 // consumer that treats the composite's slot segment as
35430 // positionally-interchangeable with its sub-carving. Peer
35431 // posture to `assert_u8_array_slice_equals_u8_array_accepts_
35432 // sexp_shape_hash_discriminators_per_position_order`-shape
35433 // sibling on the (u8) row — that pin carries the SUB-CARVING
35434 // per-position POSITIONAL-COMPOSITION theorem for the twelve-
35435 // slot outer `SexpShape::HASH_DISCRIMINATORS` container; this
35436 // pin carries the same theorem for the seven-slot outer
35437 // `NON_WHITESPACE_BARE_ATOM_TERMINATORS` SPAN.
35438 assert_char_array_slice_equals_char_array::<7, 2, 0>(
35439 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35440 &Sexp::LIST_DELIMITERS,
35441 );
35442 assert_char_array_slice_equals_char_array::<7, 3, 2>(
35443 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35444 &QuoteForm::LEADS,
35445 );
35446 assert_char_array_slice_equals_char_array::<7, 1, 5>(
35447 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35448 &[Atom::STR_DELIMITER],
35449 );
35450 assert_char_array_slice_equals_char_array::<7, 1, 6>(
35451 &Sexp::NON_WHITESPACE_BARE_ATOM_TERMINATORS,
35452 &[Sexp::COMMENT_LEAD],
35453 );
35454 }
35455
35456 #[test]
35457 #[should_panic(expected = "CHAR-SLICE-EQUALS-ARRAY-VIOLATION")]
35458 fn assert_char_array_slice_equals_char_array_panics_at_runtime_on_positionwise_drift() {
35459 // NEGATIVE PIN — CHAR-SLICE-EQUALS-ARRAY-VIOLATION corner: a
35460 // char at some position in `full[START..START + M)` that
35461 // does NOT byte-equal the peer sub-array `sub` at the
35462 // offset-matched position MUST panic at runtime with the
35463 // axis-named message. Pins the helper's positionwise-drift
35464 // reject arm — a regression that silently short-circuited on
35465 // the first slice position without checking the middle or
35466 // terminal slice positions would slip through the compile-
35467 // time witness's failure mode too. The offending char `'!'`
35468 // at outer position `3` (interior of the sub-slice `[1..4)`,
35469 // offset `2` inside `sub`) pins the middle-of-slice drift
35470 // mode.
35471 assert_char_array_slice_equals_char_array::<5, 3, 1>(
35472 &['z', 'b', 'c', '!', 'z'],
35473 &['b', 'c', 'd'],
35474 );
35475 }
35476
35477 #[test]
35478 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
35479 fn assert_char_array_slice_equals_char_array_panics_at_runtime_on_start_out_of_bounds() {
35480 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
35481 // turbofish arity slip on the `START` const-generic where
35482 // `START > N` MUST panic at runtime with the START-OUT-OF-
35483 // BOUNDS-named message BEFORE the peer SLICE-LENGTH-OUT-OF-
35484 // BOUNDS gate reads `N - START` (which would `usize`-
35485 // underflow had this gate not caught the slip first). Pins
35486 // the gate's placement at the TOP of the helper — a
35487 // regression that dropped the gate would either underflow
35488 // subtraction at the peer gate OR panic deeper in
35489 // `full[START + i]` bounds-checking with a helper-name-less
35490 // panic message. The offending `START = 7` against `N = 5`
35491 // pins the strict `START > N` reject arm; the LEGAL
35492 // `START == N` empty-slice-at-right-endpoint corner is
35493 // covered by the peer acceptance test above.
35494 assert_char_array_slice_equals_char_array::<5, 0, 7>(&['x', 'x', 'x', 'x', 'x'], &[]);
35495 }
35496
35497 #[test]
35498 #[should_panic(expected = "SLICE-LENGTH-OUT-OF-BOUNDS")]
35499 fn assert_char_array_slice_equals_char_array_panics_at_runtime_on_slice_length_out_of_bounds() {
35500 // NEGATIVE PIN — SLICE-LENGTH-OUT-OF-BOUNDS gate: a peer
35501 // sub-array arity `M` that exceeds the outer array's tail
35502 // cardinality `N - START` MUST panic at runtime with the
35503 // slice-length-out-of-bounds-named message. Peer gate to the
35504 // START-OUT-OF-BOUNDS arm above — the two gates jointly
35505 // enforce `START ≤ N` and `M ≤ N - START` before any content
35506 // sweep. The offending `M = 5` against `N - START = 5 - 3 =
35507 // 2` pins the strict `M > N - START` reject arm; the LEGAL
35508 // exact-fit corner `M == N - START` is covered by the
35509 // middle-slice acceptance test above.
35510 assert_char_array_slice_equals_char_array::<5, 5, 3>(
35511 &['x', 'x', 'x', 'x', 'x'],
35512 &['x', 'x', 'x', 'x', 'x'],
35513 );
35514 }
35515
35516 #[test]
35517 fn assert_char_array_slice_equals_char_array_panic_message_names_the_helper_and_char_slice_equals_array_violation_axis(
35518 ) {
35519 // PANIC-MESSAGE PROVENANCE PIN — CHAR-SLICE-EQUALS-ARRAY-
35520 // VIOLATION arm: the panic message MUST begin with the
35521 // helper's own name AND identify the failed AXIS as "CHAR-
35522 // SLICE-EQUALS-ARRAY-VIOLATION" so downstream diagnostics
35523 // route the drift back to (a) the helper by string search on
35524 // `"assert_char_array_slice_equals_char_array"` and (b) the
35525 // failed axis by string search on `"CHAR-SLICE-EQUALS-ARRAY-
35526 // VIOLATION"`. Sibling posture to the u8-row peer's
35527 // provenance pin
35528 // `assert_u8_array_slice_equals_u8_array_panic_message_names_the_helper_and_slice_equals_array_violation_axis`
35529 // — the two pins together bind the (helper, failed-axis)
35530 // provenance pair at ONE test per corner of the (SUB-SLICE
35531 // ARRAY-image) column on both the (u8) row AND the (char)
35532 // row of the (element-type × contract-shape) matrix. The
35533 // `CHAR-` prefix on this axis disambiguates it from the u8-
35534 // row sibling's plain `SLICE-EQUALS-ARRAY-VIOLATION` axis
35535 // vocabulary; the shared `-SLICE-EQUALS-ARRAY-VIOLATION`
35536 // infix lets callers grep either element-type variant by
35537 // the shared axis substring.
35538 let outcome = std::panic::catch_unwind(|| {
35539 assert_char_array_slice_equals_char_array::<5, 3, 1>(
35540 &['z', 'b', 'c', '!', 'z'],
35541 &['b', 'c', 'd'],
35542 );
35543 });
35544 let payload = outcome.expect_err(
35545 "assert_char_array_slice_equals_char_array must panic on \
35546 a positionwise drift — the reject-positionwise-drift arm \
35547 is the CONTENT failure mode of the helper",
35548 );
35549 let msg = payload
35550 .downcast_ref::<&'static str>()
35551 .map(|s| (*s).to_owned())
35552 .or_else(|| payload.downcast_ref::<String>().cloned())
35553 .expect(
35554 "assert_char_array_slice_equals_char_array panic \
35555 payload must be a static &str or String",
35556 );
35557 assert!(
35558 msg.contains("assert_char_array_slice_equals_char_array"),
35559 "assert_char_array_slice_equals_char_array panic message \
35560 {msg:?} must name the helper for provenance-preserving \
35561 failure diagnostics",
35562 );
35563 assert!(
35564 msg.contains("CHAR-SLICE-EQUALS-ARRAY-VIOLATION"),
35565 "assert_char_array_slice_equals_char_array panic message \
35566 {msg:?} must name the failed AXIS (\"CHAR-SLICE-EQUALS-\
35567 ARRAY-VIOLATION\") for axis-provenance-preserving \
35568 failure diagnostics",
35569 );
35570 }
35571
35572 // ── `assert_str_array_slice_equals_str_array` — the (str)-row
35573 // peer to `assert_u8_array_slice_equals_u8_array` +
35574 // `assert_char_array_slice_equals_char_array` on the (element-
35575 // type) axis of the SUB-SLICE ARRAY-image column of the
35576 // (element-type × contract-shape) matrix. The runtime test
35577 // surface pins each of the helper's arms (accept-middle-slice,
35578 // accept-empty-sub-array, accept-full-array-degenerate, accept-
35579 // sexp-shape-labels-positional-decomposition, reject-positionwise-
35580 // drift, reject-start-out-of-bounds, reject-slice-length-out-of-
35581 // bounds, panic-message-provenance on the STR-SLICE-EQUALS-ARRAY-
35582 // VIOLATION axis) so a regression that silently weakened the
35583 // helper on ANY arm is caught by the helper's OWN test surface
35584 // rather than only surfacing as a false-positive on some future
35585 // sub-slice `[&'static str; N]` pair's compound pin.
35586
35587 #[test]
35588 fn assert_str_array_slice_equals_str_array_accepts_a_canonical_middle_slice() {
35589 // Canonical sub-slice `full[START..START + M) == sub[..]`
35590 // inside a longer array `full` whose ENDPOINTS carry DIFFERENT
35591 // strings than the peer sub-array. Pins the outer `while i <
35592 // M` sweep reads `full[START + i]` at the OFFSET position
35593 // (not `full[i]`) — a regression that dropped the `START`
35594 // offset would compare `full[0..M)` against `sub[..]` and
35595 // pass on `full[0]="z" != sub[0]="b"` silently or panic on
35596 // the wrong axis. `START = 1` pins the sweep skips position
35597 // `[0..START)` and reads only `[1..1+3) = [1..4)`. Sibling
35598 // posture to
35599 // `assert_char_array_slice_equals_char_array_accepts_a_canonical_middle_slice`
35600 // and
35601 // `assert_u8_array_slice_equals_u8_array_accepts_a_canonical_middle_slice`
35602 // — the three share the middle-slice acceptance arm across
35603 // the (element-type × contract-shape) 3-row × 1-column face
35604 // at the (SUB-SLICE ARRAY-image) column.
35605 assert_str_array_slice_equals_str_array::<7, 3, 1>(
35606 &["z", "b", "c", "d", "z", "z", "z"],
35607 &["b", "c", "d"],
35608 );
35609 }
35610
35611 #[test]
35612 fn assert_str_array_slice_equals_str_array_accepts_the_empty_sub_array() {
35613 // LEGAL degenerate: `M == 0` collapses the sub-array into an
35614 // empty listing `[]`. The sweep never enters the loop body
35615 // and the helper accepts. Cross-position coverage pins the
35616 // empty-sub-array acceptance at THREE distinct `START`
35617 // positions (`START == 0` at the left endpoint, `START == 3`
35618 // in the interior, `START == N` at the right endpoint — the
35619 // latter is the corner `START == N` combined with `M == 0`
35620 // that the START-OUT-OF-BOUNDS gate's inclusive upper bound
35621 // must accept). A regression that hard-coded `START < N` OR
35622 // panicked on the `M == 0` corner is caught on ALL THREE
35623 // arms.
35624 assert_str_array_slice_equals_str_array::<5, 0, 0>(&["x", "x", "x", "x", "x"], &[]);
35625 assert_str_array_slice_equals_str_array::<5, 0, 3>(&["x", "x", "x", "x", "x"], &[]);
35626 assert_str_array_slice_equals_str_array::<5, 0, 5>(&["x", "x", "x", "x", "x"], &[]);
35627 }
35628
35629 #[test]
35630 fn assert_str_array_slice_equals_str_array_accepts_the_full_array_degenerate() {
35631 // Full-array-covering slice `M == N, START == 0` collapses
35632 // to the ALL-positions-equal-peer-array shape `full == sub`
35633 // pointwise. Pins that the sweep proceeds through EVERY
35634 // position of the outer array when `START = 0` and `M = N`.
35635 // Cross-arity coverage on `N ∈ {1, 3, 6}` pins the sweep's
35636 // terminal-position visit across the range of str arities
35637 // the substrate's LABELS arrays span (`N = 1` for the
35638 // singleton sub-carvings, `N = 4` for `QuoteForm::LABELS`,
35639 // `N = 6` for `AtomKind::LABELS`).
35640 assert_str_array_slice_equals_str_array::<1, 1, 0>(&["nil"], &["nil"]);
35641 assert_str_array_slice_equals_str_array::<3, 3, 0>(
35642 &["quote", "quasiquote", "unquote"],
35643 &["quote", "quasiquote", "unquote"],
35644 );
35645 assert_str_array_slice_equals_str_array::<6, 6, 0>(
35646 &["symbol", "keyword", "string", "int", "float", "bool"],
35647 &["symbol", "keyword", "string", "int", "float", "bool"],
35648 );
35649 }
35650
35651 #[test]
35652 fn assert_str_array_slice_equals_str_array_accepts_sexp_shape_labels_positional_decomposition()
35653 {
35654 // Runtime cross-check that the FOUR canonical sub-slices of
35655 // `crate::error::SexpShape::LABELS` (`[&'static str; 12]`)
35656 // each byte-equal their sub-carving's canonical
35657 // `[&'static str; M]` listing pointwise. Runs the SAME
35658 // helper the FOUR `const _` witnesses in `error.rs` (in the
35659 // block titled "Compile-time SLICE-EQUALS-ARRAY witnesses
35660 // closing the twelve-arm `SexpShape::LABELS` POSITIONAL
35661 // decomposition") run at rustc time — a runtime safety net
35662 // enforcing the theorem at BOTH stages of the toolchain
35663 // (const at `cargo check`, runtime at `cargo test`). A
35664 // regression that reordered any of the twelve slots in the
35665 // outer array's initializer, or drifted any sub-carving's
35666 // per-role LABEL alias, fails HERE at the substrate callsite
35667 // AND at the const witness in `error.rs`. Sibling posture to
35668 // `assert_u8_array_slice_equals_u8_array_accepts_sub_carving_hash_discriminators_per_position_order`
35669 // on the (u8) row — that witness carries the positional
35670 // decomposition theorem for the FOUR sub-carving
35671 // `HASH_DISCRIMINATORS` arrays; this witness carries the
35672 // SAME theorem for the FOUR sub-carving LABELS arrays
35673 // (strictly STRONGER at the `[1..7)` slice: the (u8) row's
35674 // sibling collapses to a SCALAR replica witness on the six-
35675 // slot atomic-payload middle slice, but the (str) row's
35676 // per-slot label listing distinguishes ALL SIX slots
35677 // individually).
35678 //
35679 // The four canonical sub-slices are (all under the shared
35680 // `&'static str` element-type and shared parent
35681 // `[&'static str; 12]` outer container `SexpShape::LABELS`):
35682 // 1. `SexpShape::LABELS[0..1) ==
35683 // [StructuralKind::NIL_LABEL]`
35684 // 2. `SexpShape::LABELS[1..7) == AtomKind::LABELS`
35685 // 3. `SexpShape::LABELS[7..8) ==
35686 // [StructuralKind::LIST_LABEL]`
35687 // 4. `SexpShape::LABELS[8..12) == QuoteForm::LABELS`
35688 assert_str_array_slice_equals_str_array::<12, 1, 0>(
35689 &crate::error::SexpShape::LABELS,
35690 &[crate::error::StructuralKind::NIL_LABEL],
35691 );
35692 assert_str_array_slice_equals_str_array::<12, 6, 1>(
35693 &crate::error::SexpShape::LABELS,
35694 &AtomKind::LABELS,
35695 );
35696 assert_str_array_slice_equals_str_array::<12, 1, 7>(
35697 &crate::error::SexpShape::LABELS,
35698 &[crate::error::StructuralKind::LIST_LABEL],
35699 );
35700 assert_str_array_slice_equals_str_array::<12, 4, 8>(
35701 &crate::error::SexpShape::LABELS,
35702 &QuoteForm::LABELS,
35703 );
35704 }
35705
35706 #[test]
35707 fn assert_str_array_slice_equals_str_array_accepts_every_family_wide_substrate_array_full_array_literal_listing(
35708 ) {
35709 // Runtime cross-check that the SAME five (str)-row FULL-ARRAY
35710 // LITERAL witnesses covered at COMPILE time by the module-
35711 // level `const _: () = ...` cluster immediately below the
35712 // (str)-row `assert_str_array_pairwise_distinct` witnesses
35713 // (`Atom::BOOL_LITERALS` / `AtomKind::LABELS` /
35714 // `QuoteForm::PREFIXES` / `QuoteForm::LABELS` /
35715 // `QuoteForm::IAC_FORGE_TAGS`) also hold when exercised at
35716 // runtime. A regression that removes ONE of the const
35717 // witnesses would still leave THIS runtime pin as a safety
35718 // net; the const witness fires FIRST at `cargo check`, this
35719 // runtime pin catches the positionwise drift at `cargo
35720 // test`. The pair enforces the theorem at TWO stages of the
35721 // toolchain (const at `cargo check`, runtime at `cargo
35722 // test`). Sibling posture to
35723 // `assert_char_array_slice_equals_char_array_accepts_every_family_wide_substrate_array_full_array_literal_listing`
35724 // (if / when that (char)-row runtime-safety-net peer is
35725 // added) — the two would jointly enforce the FULL-ARRAY per-
35726 // position ORDER column at runtime across the (char, str)
35727 // 2-row face of the (element-type × contract-shape) matrix.
35728 //
35729 // A regression that (a) reorders one of the outer arrays'
35730 // slots away from canonical declaration order, or (b) drifts
35731 // one of the per-role scalar `pub const *_LABEL` / `*_PREFIX`
35732 // / `*_TAG` / `TRUE_LITERAL` / `FALSE_LITERAL` aliases the
35733 // outer array's slots re-export, fails HERE with the
35734 // `STR-SLICE-EQUALS-ARRAY-VIOLATION` axis panic naming the
35735 // drifted position.
35736 assert_str_array_slice_equals_str_array::<2, 2, 0>(&Atom::BOOL_LITERALS, &["#t", "#f"]);
35737 assert_str_array_slice_equals_str_array::<6, 6, 0>(
35738 &AtomKind::LABELS,
35739 &["symbol", "keyword", "string", "int", "float", "bool"],
35740 );
35741 assert_str_array_slice_equals_str_array::<4, 4, 0>(
35742 &QuoteForm::PREFIXES,
35743 &["'", "`", ",", ",@"],
35744 );
35745 assert_str_array_slice_equals_str_array::<4, 4, 0>(
35746 &QuoteForm::LABELS,
35747 &["quote", "quasiquote", "unquote", "unquote-splice"],
35748 );
35749 assert_str_array_slice_equals_str_array::<4, 4, 0>(
35750 &QuoteForm::IAC_FORGE_TAGS,
35751 &["quote", "quasiquote", "unquote", "unquote-splicing"],
35752 );
35753 }
35754
35755 #[test]
35756 #[should_panic(expected = "STR-SLICE-EQUALS-ARRAY-VIOLATION")]
35757 fn assert_str_array_slice_equals_str_array_panics_at_runtime_on_positionwise_drift() {
35758 // NEGATIVE PIN — STR-SLICE-EQUALS-ARRAY-VIOLATION corner: a
35759 // str at some position in `full[START..START + M)` that does
35760 // NOT byte-equal the peer sub-array `sub` at the offset-
35761 // matched position MUST panic at runtime with the axis-named
35762 // message. Pins the helper's positionwise-drift reject arm —
35763 // a regression that silently short-circuited on the first
35764 // slice position without checking the middle or terminal
35765 // slice positions would slip through the compile-time
35766 // witness's failure mode too. The offending str `"!"` at
35767 // outer position `3` (interior of the sub-slice `[1..4)`,
35768 // offset `2` inside `sub`) pins the middle-of-slice drift
35769 // mode.
35770 assert_str_array_slice_equals_str_array::<5, 3, 1>(
35771 &["z", "b", "c", "!", "z"],
35772 &["b", "c", "d"],
35773 );
35774 }
35775
35776 #[test]
35777 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
35778 fn assert_str_array_slice_equals_str_array_panics_at_runtime_on_start_out_of_bounds() {
35779 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
35780 // turbofish arity slip on the `START` const-generic where
35781 // `START > N` MUST panic at runtime with the START-OUT-OF-
35782 // BOUNDS-named message BEFORE the peer SLICE-LENGTH-OUT-OF-
35783 // BOUNDS gate reads `N - START` (which would `usize`-
35784 // underflow had this gate not caught the slip first). Pins
35785 // the gate's placement at the TOP of the helper — a
35786 // regression that dropped the gate would either underflow
35787 // subtraction at the peer gate OR panic deeper in
35788 // `full[START + i]` bounds-checking with a helper-name-less
35789 // panic message. The offending `START = 7` against `N = 5`
35790 // pins the strict `START > N` reject arm; the LEGAL
35791 // `START == N` empty-slice-at-right-endpoint corner is
35792 // covered by the peer acceptance test above.
35793 assert_str_array_slice_equals_str_array::<5, 0, 7>(&["x", "x", "x", "x", "x"], &[]);
35794 }
35795
35796 #[test]
35797 #[should_panic(expected = "SLICE-LENGTH-OUT-OF-BOUNDS")]
35798 fn assert_str_array_slice_equals_str_array_panics_at_runtime_on_slice_length_out_of_bounds() {
35799 // NEGATIVE PIN — SLICE-LENGTH-OUT-OF-BOUNDS gate: a peer
35800 // sub-array arity `M` that exceeds the outer array's tail
35801 // cardinality `N - START` MUST panic at runtime with the
35802 // slice-length-out-of-bounds-named message. Peer gate to the
35803 // START-OUT-OF-BOUNDS arm above — the two gates jointly
35804 // enforce `START ≤ N` and `M ≤ N - START` before any content
35805 // sweep. The offending `M = 5` against `N - START = 5 - 3 =
35806 // 2` pins the strict `M > N - START` reject arm; the LEGAL
35807 // exact-fit corner `M == N - START` is covered by the middle-
35808 // slice acceptance test above.
35809 assert_str_array_slice_equals_str_array::<5, 5, 3>(
35810 &["x", "x", "x", "x", "x"],
35811 &["x", "x", "x", "x", "x"],
35812 );
35813 }
35814
35815 #[test]
35816 fn assert_str_array_slice_equals_str_array_panic_message_names_the_helper_and_str_slice_equals_array_violation_axis(
35817 ) {
35818 // PANIC-MESSAGE PROVENANCE PIN — STR-SLICE-EQUALS-ARRAY-
35819 // VIOLATION arm: the panic message MUST begin with the
35820 // helper's own name AND identify the failed AXIS as "STR-
35821 // SLICE-EQUALS-ARRAY-VIOLATION" so downstream diagnostics
35822 // route the drift back to (a) the helper by string search on
35823 // `"assert_str_array_slice_equals_str_array"` and (b) the
35824 // failed axis by string search on `"STR-SLICE-EQUALS-ARRAY-
35825 // VIOLATION"`. Sibling posture to the u8-row peer's
35826 // provenance pin
35827 // `assert_u8_array_slice_equals_u8_array_panic_message_names_the_helper_and_slice_equals_array_violation_axis`
35828 // AND the char-row peer's provenance pin
35829 // `assert_char_array_slice_equals_char_array_panic_message_names_the_helper_and_char_slice_equals_array_violation_axis`
35830 // — the three pins together bind the (helper, failed-axis)
35831 // provenance triple at ONE test per corner of the (SUB-SLICE
35832 // ARRAY-image) column on the (u8) + (char) + (str) rows of
35833 // the (element-type × contract-shape) matrix. The `STR-`
35834 // prefix on this axis disambiguates it from the u8-row
35835 // sibling's plain `SLICE-EQUALS-ARRAY-VIOLATION` axis
35836 // vocabulary AND the char-row sibling's `CHAR-SLICE-EQUALS-
35837 // ARRAY-VIOLATION` axis vocabulary; the shared `-SLICE-
35838 // EQUALS-ARRAY-VIOLATION` infix lets callers grep any of
35839 // the three element-type variants by the shared axis
35840 // substring.
35841 let outcome = std::panic::catch_unwind(|| {
35842 assert_str_array_slice_equals_str_array::<5, 3, 1>(
35843 &["z", "b", "c", "!", "z"],
35844 &["b", "c", "d"],
35845 );
35846 });
35847 let payload = outcome.expect_err(
35848 "assert_str_array_slice_equals_str_array must panic on a \
35849 positionwise drift — the reject-positionwise-drift arm \
35850 is the CONTENT failure mode of the helper",
35851 );
35852 let msg = payload
35853 .downcast_ref::<&'static str>()
35854 .map(|s| (*s).to_owned())
35855 .or_else(|| payload.downcast_ref::<String>().cloned())
35856 .expect(
35857 "assert_str_array_slice_equals_str_array panic \
35858 payload must be a static &str or String",
35859 );
35860 assert!(
35861 msg.contains("assert_str_array_slice_equals_str_array"),
35862 "assert_str_array_slice_equals_str_array panic message \
35863 {msg:?} must name the helper for provenance-preserving \
35864 failure diagnostics",
35865 );
35866 assert!(
35867 msg.contains("STR-SLICE-EQUALS-ARRAY-VIOLATION"),
35868 "assert_str_array_slice_equals_str_array panic message \
35869 {msg:?} must name the failed AXIS (\"STR-SLICE-EQUALS-\
35870 ARRAY-VIOLATION\") for axis-provenance-preserving \
35871 failure diagnostics",
35872 );
35873 }
35874
35875 // ── `assert_u8_arrays_disjoint` — the U8-DISJOINTNESS-VIOLATION
35876 // verifier that binds `a ∩ b = ∅` at compile time on the outer-
35877 // `Sexp` cache-key `u8` vocabulary, peer to
35878 // `assert_u8_array_within_u8_finite_set` on the (u8) row of the
35879 // (subset, disjointness) 2-corner face of the (contract-shape)
35880 // axis AND row-dual peer to `assert_char_arrays_disjoint` on the
35881 // (element-type) axis of the SAME (contract-shape) column. The
35882 // runtime test surface pins each of the helper's arms (accept-
35883 // both-empty, accept-either-empty, accept-disjoint-singletons,
35884 // accept-each-family-wide-substrate-pair, accept-arg-order-
35885 // symmetry, reject-single-collision, reject-terminal-a-collision,
35886 // reject-terminal-b-collision, panic-message-provenance on the
35887 // U8-DISJOINTNESS-VIOLATION axis) so a regression that silently
35888 // weakened the helper on ANY arm is caught by the helper's OWN
35889 // test surface rather than only surfacing as a false-positive on
35890 // some future disjoint `[u8; N] × [u8; M]` pair's compound pin.
35891
35892 #[test]
35893 fn assert_u8_arrays_disjoint_accepts_both_empty_arrays() {
35894 // Both arrays empty at the `[u8; 0] × [u8; 0]` corner —
35895 // vacuously disjoint (no `(i, j)` pair exists to test). The
35896 // compile-time `const _: () = assert_u8_arrays_disjoint(&[],
35897 // &[]);` would land on this arm, so the runtime call MUST
35898 // return normally. Turbofish binding required because there's
35899 // no other cue for the const parameters on the empty array
35900 // literals. Sibling posture to
35901 // `assert_char_arrays_disjoint_accepts_both_empty_arrays` on
35902 // the (char) row-dual peer — the two share the trivial-arity
35903 // arm across the (element-type × contract-shape) 4-corner
35904 // face at the (disjointness) column.
35905 assert_u8_arrays_disjoint::<0, 0>(&[], &[]);
35906 }
35907
35908 #[test]
35909 fn assert_u8_arrays_disjoint_accepts_either_side_empty() {
35910 // Either side empty at the `[u8; 0] × [u8; M]` OR
35911 // `[u8; N] × [u8; 0]` corners — vacuously disjoint (the
35912 // OUTER `while i < N` OR the INNER `while j < M` sweep is a
35913 // no-op). Pins BOTH SIDES of the (a-empty, b-empty) 2-corner
35914 // sub-face on the trivial-arity axis so a regression that
35915 // narrowed the sweep to only-a-non-empty OR only-b-non-empty
35916 // fails HERE at the empty-side corner rather than at a
35917 // distant false-positive.
35918 assert_u8_arrays_disjoint::<0, 3>(&[], &[0, 1, 2]);
35919 assert_u8_arrays_disjoint::<3, 0>(&[0, 1, 2], &[]);
35920 }
35921
35922 #[test]
35923 fn assert_u8_arrays_disjoint_accepts_disjoint_singletons() {
35924 // Singleton `a = [K]` and singleton `b = [L]` with `K != L`
35925 // at the `[u8; 1] × [u8; 1]` corner — the minimal non-empty
35926 // disjointness relation. Pins the INNER `if a[i] != b[j]`
35927 // gate returns without panicking on a single distinct-pair
35928 // check. A regression that flipped the equality direction
35929 // (`==` vs `!=`) would silently reject every disjoint
35930 // singleton pair.
35931 assert_u8_arrays_disjoint(&[0u8], &[1u8]);
35932 }
35933
35934 #[test]
35935 fn assert_u8_arrays_disjoint_accepts_each_family_wide_substrate_pair() {
35936 // Runtime cross-check that the TWO (a, b) `[u8; N] × [u8; M]`
35937 // pairs the substrate's module-level `const _` witnesses pin
35938 // at COMPILE time are proper DISJOINTNESS embeddings at
35939 // runtime too. The pairs enforce the theorem at TWO stages
35940 // of the toolchain: the const witnesses fire FIRST at `cargo
35941 // check` (through the two module-level `const _: () =
35942 // assert_u8_arrays_disjoint::<N, M>(...)` lines), this
35943 // runtime pin catches the drift at `cargo test` as a safety
35944 // net. Sibling posture to
35945 // `assert_char_arrays_disjoint_accepts_each_family_wide_substrate_pair`
35946 // which sweeps the FIVE (a, b) PAIRS at the (char) row's
35947 // DISJOINTNESS axis; this pin sweeps the two (a, b) PAIRS
35948 // at the (u8) row's DISJOINTNESS axis on the SAME contract-
35949 // shape column.
35950 assert_u8_arrays_disjoint::<2, 4>(
35951 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
35952 &QuoteForm::HASH_DISCRIMINATORS,
35953 );
35954 assert_u8_arrays_disjoint::<2, 2>(
35955 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
35956 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
35957 );
35958 }
35959
35960 #[test]
35961 fn assert_u8_arrays_disjoint_is_symmetric_in_argument_order() {
35962 // SYMMETRY PIN: swapping the two arguments produces the SAME
35963 // verdict. Pins that the disjointness relation is truly
35964 // symmetric across the two array arguments (the helper's
35965 // nested-sweep implementation does NOT gratuitously depend on
35966 // argument order). A regression that narrowed the sweep to
35967 // `for i in a { if !b.contains(a[i]) }` (subset-shape, not
35968 // disjointness-shape) would fail the swap on one direction
35969 // only. Runs each substrate pair BOTH ways. Sibling posture
35970 // to `assert_char_arrays_disjoint_is_symmetric_in_argument_order`
35971 // on the (char) row-dual peer.
35972 assert_u8_arrays_disjoint(
35973 &QuoteForm::HASH_DISCRIMINATORS,
35974 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
35975 );
35976 assert_u8_arrays_disjoint(
35977 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
35978 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
35979 );
35980 }
35981
35982 #[test]
35983 #[should_panic(expected = "U8-DISJOINTNESS-VIOLATION")]
35984 fn assert_u8_arrays_disjoint_panics_at_runtime_on_collision() {
35985 // NEGATIVE PIN — U8-DISJOINTNESS-VIOLATION corner: two arrays
35986 // sharing a single byte MUST panic at runtime with the
35987 // U8-DISJOINTNESS-VIOLATION-named message. Pins the helper's
35988 // OWN reject arm — a regression that silently returned
35989 // without panicking on a cross-array collision would slip
35990 // through the compile-time witnesses' failure mode too. The
35991 // shared byte `0u8` is intentionally placed at the FIRST
35992 // position of BOTH arrays to pin the initial-position drift
35993 // mode.
35994 assert_u8_arrays_disjoint(&[0u8, 1u8], &[0u8, 2u8]);
35995 }
35996
35997 #[test]
35998 #[should_panic(expected = "U8-DISJOINTNESS-VIOLATION")]
35999 fn assert_u8_arrays_disjoint_panics_at_runtime_on_terminal_a_collision() {
36000 // NEGATIVE PIN — terminal-position drift on the OUTER `a`
36001 // side: a shared byte at the LAST position of `a` MUST panic
36002 // — pins that the outer `while i < N` loop reaches `i = N -
36003 // 1` (else the terminal drift on `a` would slip through). A
36004 // regression that narrowed the outer sweep to `while i < N
36005 // - 1` (off-by-one on the OUTER bound) would silently accept
36006 // this pair.
36007 assert_u8_arrays_disjoint(&[10u8, 20u8, 30u8], &[30u8]);
36008 }
36009
36010 #[test]
36011 #[should_panic(expected = "U8-DISJOINTNESS-VIOLATION")]
36012 fn assert_u8_arrays_disjoint_panics_at_runtime_on_terminal_b_collision() {
36013 // NEGATIVE PIN — terminal-position drift on the INNER `b`
36014 // side: a shared byte at the LAST position of `b` MUST panic
36015 // — pins that the inner `while j < M` loop reaches `j = M -
36016 // 1` (else the terminal drift on `b` would slip through). A
36017 // regression that narrowed the inner sweep to `while j < M
36018 // - 1` (off-by-one on the INNER bound) would silently accept
36019 // this pair. Sibling posture to the outer-terminal pin above
36020 // — the two pins together bind the terminal bounds on BOTH
36021 // loops.
36022 assert_u8_arrays_disjoint(&[30u8], &[10u8, 20u8, 30u8]);
36023 }
36024
36025 #[test]
36026 fn assert_u8_arrays_disjoint_panic_message_names_the_helper_and_u8_disjointness_violation_axis()
36027 {
36028 // PANIC-MESSAGE PROVENANCE PIN — U8-DISJOINTNESS-VIOLATION
36029 // arm: the panic message MUST begin with the helper's own
36030 // name AND identify the failed AXIS as "U8-DISJOINTNESS-
36031 // VIOLATION" so downstream diagnostics route the drift back
36032 // to (a) the helper by string search on
36033 // `"assert_u8_arrays_disjoint"` and (b) the axis by string
36034 // search on `"U8-DISJOINTNESS-VIOLATION"`. Sibling posture to
36035 // `assert_char_arrays_disjoint_panic_message_names_the_helper_and_char_disjointness_violation_axis`
36036 // on the (char) row-dual peer's provenance pin AND to
36037 // `assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis`
36038 // on the (u8, subset) contract-orthogonal peer's provenance
36039 // pin — the three pins together bind the (helper, failed-
36040 // axis) provenance triple across the (element-type × contract-
36041 // shape) 2×2 = 4-corner face's three currently-populated
36042 // corners with matching provenance-preservation discipline.
36043 // The axis-provenance string `"U8-DISJOINTNESS-VIOLATION"` is
36044 // chosen DISTINCT from EVERY sibling helper's axis vocabulary
36045 // (`"duplicate"` on the ARRAY-side pairwise-distinct sibling;
36046 // `"CHAR-DISJOINTNESS-VIOLATION"` on the (char) row-dual
36047 // DISJOINTNESS sibling; `"CHAR-SUBSET-VIOLATION"` on the
36048 // (char) SUBSET-embedding sibling; `"SUBSET-VIOLATION"` on
36049 // the (u8) finite-set SUBSET-only sibling; `"RANGE-SUBSET-
36050 // VIOLATION"` on the (u8) range SUBSET-only sibling;
36051 // `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the (u8) covers-
36052 // finite-set sibling; `"OUT-OF-RANGE"` / `"MISSING"` on the
36053 // (u8) covers-inclusive-range sibling; `"ARITY-MISMATCH"` on
36054 // both (u8) `_permutes_*` compound helpers; `"SET-NOT-
36055 // PAIRWISE-DISTINCT"` on the (u8) SET-side well-formedness
36056 // sibling) so a diagnostic that names the failed axis routes
36057 // UNAMBIGUOUSLY to (a) this specific u8 DISJOINTNESS helper.
36058 let outcome = std::panic::catch_unwind(|| {
36059 assert_u8_arrays_disjoint(&[0u8, 1u8], &[0u8, 2u8]);
36060 });
36061 let payload = outcome.expect_err(
36062 "assert_u8_arrays_disjoint must panic on a cross-array \
36063 collision — the reject-collision arm is the sole U8-\
36064 DISJOINTNESS-VIOLATION failure mode of the helper",
36065 );
36066 let msg = payload
36067 .downcast_ref::<&'static str>()
36068 .map(|s| (*s).to_owned())
36069 .or_else(|| payload.downcast_ref::<String>().cloned())
36070 .expect(
36071 "assert_u8_arrays_disjoint panic payload must be a \
36072 static &str or String",
36073 );
36074 assert!(
36075 msg.contains("assert_u8_arrays_disjoint"),
36076 "assert_u8_arrays_disjoint panic message {msg:?} must \
36077 name the helper for provenance-preserving failure \
36078 diagnostics",
36079 );
36080 assert!(
36081 msg.contains("U8-DISJOINTNESS-VIOLATION"),
36082 "assert_u8_arrays_disjoint panic message {msg:?} must \
36083 name the failed AXIS (\"U8-DISJOINTNESS-VIOLATION\") \
36084 for axis-provenance-preserving failure diagnostics",
36085 );
36086 }
36087
36088 // ── `assert_str_array_pairwise_distinct` — the `&'static str`
36089 // element-type sibling of `assert_char_array_pairwise_distinct`.
36090 // Sibling posture: same runtime-test surface (accept-empty,
36091 // accept-singleton, accept-every-family-wide-substrate-array,
36092 // reject-binary, reject-non-adjacent, reject-terminal, panic-
36093 // message-provenance) restricted to the `&'static str` element
36094 // type. A regression that silently weakens the helper (e.g.
36095 // dropping the byte-length gate, flipping `!=` to `==`, or
36096 // returning early on collision) is caught by the helper's OWN
36097 // test surface rather than only surfacing as a false-positive
36098 // on some future string-typed array's distinctness pin.
36099
36100 #[test]
36101 fn assert_str_array_pairwise_distinct_accepts_the_empty_array() {
36102 // Empty array — vacuously pairwise distinct (no pair to
36103 // collide). The compile-time `const _: () =
36104 // assert_str_array_pairwise_distinct(&EMPTY);` would land on
36105 // this arm, so the runtime call MUST return normally.
36106 assert_str_array_pairwise_distinct::<0>(&[]);
36107 }
36108
36109 #[test]
36110 fn assert_str_array_pairwise_distinct_accepts_singleton_arrays() {
36111 // Singleton array — vacuously pairwise distinct (only one
36112 // element, no pair). Cross-arity coverage on the
36113 // `[&'static str; 1]` corner of the const-N generic.
36114 assert_str_array_pairwise_distinct(&["a"]);
36115 assert_str_array_pairwise_distinct(&[Atom::TRUE_LITERAL]);
36116 }
36117
36118 #[test]
36119 fn assert_str_array_pairwise_distinct_accepts_every_family_wide_substrate_array() {
36120 // Runtime cross-check that the SAME five arrays the module-
36121 // level `const _: () = ...` witnesses cover at COMPILE time
36122 // are pairwise distinct. A regression that removes ONE of
36123 // the `const _` witnesses would still leave THIS runtime pin
36124 // as a safety net; the const witness fires FIRST at `cargo
36125 // check`, this runtime pin catches the collision at `cargo
36126 // test`. The pair enforces the theorem at TWO stages of the
36127 // toolchain.
36128 assert_str_array_pairwise_distinct(&Atom::BOOL_LITERALS);
36129 assert_str_array_pairwise_distinct(&AtomKind::LABELS);
36130 assert_str_array_pairwise_distinct(&QuoteForm::PREFIXES);
36131 assert_str_array_pairwise_distinct(&QuoteForm::IAC_FORGE_TAGS);
36132 assert_str_array_pairwise_distinct(&QuoteForm::LABELS);
36133 }
36134
36135 #[test]
36136 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
36137 fn assert_str_array_pairwise_distinct_panics_at_runtime_on_binary_collision() {
36138 // NEGATIVE PIN — binary corner: a two-element array carrying
36139 // the same string twice MUST panic at runtime (the const-eval
36140 // panic surfaces normally when the function is invoked from a
36141 // runtime context, not just a `const _` context). Pins the
36142 // helper's OWN reject-collision arm — a regression that
36143 // silently returned without panicking on a duplicate would
36144 // slip through the compile-time witnesses' failure mode too.
36145 assert_str_array_pairwise_distinct(&["dup", "dup"]);
36146 }
36147
36148 #[test]
36149 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
36150 fn assert_str_array_pairwise_distinct_panics_at_runtime_on_non_adjacent_collision() {
36151 // NEGATIVE PIN — non-adjacent corner: the collision fires on
36152 // ANY (i, j) pair with i < j, not just the adjacent (0, 1)
36153 // corner. Pins the nested-loop shape of the helper — a
36154 // regression that walked ONLY the adjacent pairs (i.e., swept
36155 // `while i + 1 < N { if arr[i] == arr[i+1] { panic } … }`)
36156 // would silently accept `["a", "b", "a"]` (non-adjacent
36157 // collision at positions 0 and 2), missing the contract.
36158 assert_str_array_pairwise_distinct(&["a", "b", "a"]);
36159 }
36160
36161 #[test]
36162 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
36163 fn assert_str_array_pairwise_distinct_panics_at_runtime_on_terminal_collision() {
36164 // NEGATIVE PIN — terminal corner: the collision at the LAST
36165 // pair (positions N-2 and N-1) MUST also fire. Pins the outer
36166 // `while i < N` bound — a regression that walked `while i <
36167 // N - 1` (dropping the last row) would silently accept a
36168 // collision at the tail.
36169 assert_str_array_pairwise_distinct(&["a", "b", "c", "d", "d"]);
36170 }
36171
36172 #[test]
36173 fn assert_str_array_pairwise_distinct_rejects_length_zero_collision() {
36174 // POSITIVE-ORTHOGONAL PIN — the byte-length gate at
36175 // `str_bytes_equal`: two empty strings must be flagged as
36176 // EQUAL (both are the zero-length byte sequence). A
36177 // regression that mishandled `a.len() == 0 && b.len() == 0`
36178 // (e.g. by returning `false` when both lengths are 0, or by
36179 // panicking on the zero-length index) would silently accept
36180 // a `["", ""]` duplicate. Pins the vacuous-length corner of
36181 // the byte-equality helper via the outer helper's runtime
36182 // panic surface.
36183 let outcome = std::panic::catch_unwind(|| {
36184 assert_str_array_pairwise_distinct(&["", ""]);
36185 });
36186 outcome.expect_err(
36187 "assert_str_array_pairwise_distinct must panic on a \
36188 duplicate at the zero-length string corner — the \
36189 `str_bytes_equal` helper's `a.len() == b.len()` gate \
36190 holds vacuously at length 0, so the byte-walk falls \
36191 through to a positive equality verdict",
36192 );
36193 }
36194
36195 #[test]
36196 fn assert_str_array_pairwise_distinct_accepts_prefix_pair_without_collision() {
36197 // POSITIVE-ORTHOGONAL PIN — the byte-length gate at
36198 // `str_bytes_equal`: two strings where one is a strict
36199 // prefix of the other must be flagged as DISTINCT. A
36200 // regression that dropped the `a.len() != b.len()` gate
36201 // (e.g. by comparing bytes only up to the shorter length)
36202 // would silently accept `["ab", "abc"]` as equal. This pin
36203 // fires the outer helper on a prefix pair; the runtime call
36204 // MUST return normally.
36205 assert_str_array_pairwise_distinct(&["ab", "abc"]);
36206 assert_str_array_pairwise_distinct(&["", "a"]);
36207 }
36208
36209 #[test]
36210 fn assert_str_array_pairwise_distinct_panic_message_names_the_helper() {
36211 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST begin
36212 // with the helper's own name so downstream diagnostics
36213 // (`cargo check` const-eval error output, test-suite failure
36214 // reports) route the drift back to the helper by string
36215 // search — the family-wide contract's failure mode surfaces
36216 // as an identifiable panic-message prefix rather than as an
36217 // opaque const-eval error. Sibling posture to the runtime
36218 // pairwise-distinctness tests that name the ARRAY in their
36219 // failure message; this pin names the HELPER.
36220 let outcome = std::panic::catch_unwind(|| {
36221 assert_str_array_pairwise_distinct(&["x", "x"]);
36222 });
36223 let payload = outcome.expect_err(
36224 "assert_str_array_pairwise_distinct must panic on a \
36225 duplicate — the reject-collision arm is the point of \
36226 the helper",
36227 );
36228 let msg = payload
36229 .downcast_ref::<&'static str>()
36230 .map(|s| (*s).to_owned())
36231 .or_else(|| payload.downcast_ref::<String>().cloned())
36232 .expect(
36233 "assert_str_array_pairwise_distinct panic payload \
36234 must be a static &str or String",
36235 );
36236 assert!(
36237 msg.contains("assert_str_array_pairwise_distinct"),
36238 "assert_str_array_pairwise_distinct panic message \
36239 {msg:?} must name the helper for provenance-preserving \
36240 failure diagnostics",
36241 );
36242 }
36243
36244 #[test]
36245 fn assert_str_array_all_nonempty_accepts_the_empty_array() {
36246 // Empty array — vacuously all-nonempty (no entry to be empty).
36247 // The compile-time `const _: () = assert_str_array_all_nonempty
36248 // (&EMPTY);` would land on this arm, so the runtime call MUST
36249 // return normally.
36250 assert_str_array_all_nonempty::<0>(&[]);
36251 }
36252
36253 #[test]
36254 fn assert_str_array_all_nonempty_accepts_nonempty_singleton_arrays() {
36255 // Singleton array carrying a nonempty entry — the sole entry
36256 // clears the length-gate. Cross-arity coverage on the
36257 // `[&'static str; 1]` corner of the const-N generic.
36258 assert_str_array_all_nonempty(&["a"]);
36259 assert_str_array_all_nonempty(&[Atom::TRUE_LITERAL]);
36260 }
36261
36262 #[test]
36263 fn assert_str_array_all_nonempty_accepts_every_family_wide_substrate_array() {
36264 // Runtime cross-check that the SAME five arrays the module-
36265 // level `const _: () = ...` witnesses cover at COMPILE time
36266 // are all-nonempty. Sibling posture to the runtime
36267 // `_pairwise_distinct` cross-check above — the two together
36268 // pin BOTH the INJECTIVITY axis AND the NONEMPTY-CARDINALITY-
36269 // LOWER-BOUND axis on the SAME five arrays, at TWO stages of
36270 // the toolchain (compile-time `const _` line + this runtime
36271 // safety-net).
36272 assert_str_array_all_nonempty(&Atom::BOOL_LITERALS);
36273 assert_str_array_all_nonempty(&AtomKind::LABELS);
36274 assert_str_array_all_nonempty(&QuoteForm::PREFIXES);
36275 assert_str_array_all_nonempty(&QuoteForm::IAC_FORGE_TAGS);
36276 assert_str_array_all_nonempty(&QuoteForm::LABELS);
36277 }
36278
36279 #[test]
36280 #[should_panic(expected = "assert_str_array_all_nonempty")]
36281 fn assert_str_array_all_nonempty_panics_at_runtime_on_singleton_empty() {
36282 // NEGATIVE PIN — singleton corner: a one-element array
36283 // carrying the empty string MUST panic. Pins the helper's own
36284 // reject-empty arm on the smallest possible array shape.
36285 assert_str_array_all_nonempty(&[""]);
36286 }
36287
36288 #[test]
36289 #[should_panic(expected = "assert_str_array_all_nonempty")]
36290 fn assert_str_array_all_nonempty_panics_at_runtime_on_head_empty() {
36291 // NEGATIVE PIN — head corner: the empty entry at position 0
36292 // MUST fire even when subsequent entries are nonempty. Pins
36293 // the outer sweep's inclusive-start behavior — a regression
36294 // that walked `while i < N { … i += 1 }` from an off-by-one
36295 // start (`i = 1`) would silently accept a leading `""` entry.
36296 assert_str_array_all_nonempty(&["", "a", "b"]);
36297 }
36298
36299 #[test]
36300 #[should_panic(expected = "assert_str_array_all_nonempty")]
36301 fn assert_str_array_all_nonempty_panics_at_runtime_on_interior_empty() {
36302 // NEGATIVE PIN — interior corner: the empty entry at a
36303 // strictly-interior position MUST fire. Pins the outer
36304 // sweep's non-early-exit behavior at the head-arm — a
36305 // regression that returned `Ok` on the first nonempty entry
36306 // (bailing out of the sweep prematurely) would silently
36307 // accept an interior `""` entry.
36308 assert_str_array_all_nonempty(&["a", "", "b"]);
36309 }
36310
36311 #[test]
36312 #[should_panic(expected = "assert_str_array_all_nonempty")]
36313 fn assert_str_array_all_nonempty_panics_at_runtime_on_tail_empty() {
36314 // NEGATIVE PIN — tail corner: the empty entry at position
36315 // `N - 1` MUST fire. Pins the outer `while i < N` upper bound
36316 // — a regression that walked `while i < N - 1` (dropping the
36317 // last slot) would silently accept a trailing `""` entry.
36318 assert_str_array_all_nonempty(&["a", "b", "c", ""]);
36319 }
36320
36321 #[test]
36322 fn assert_str_array_all_nonempty_rejects_the_all_empty_array() {
36323 // POSITIVE-ORTHOGONAL PIN — the ALL-empty corner: an array
36324 // whose every entry is `""` fires the helper on the FIRST
36325 // entry (the head-arm). Confirms the helper does not silently
36326 // accept an array whose every entry alias-collapses onto the
36327 // zero-length byte sequence — a corner that composes with
36328 // `assert_str_array_pairwise_distinct_rejects_length_zero_
36329 // collision` (that pin rejects `["", ""]` on the pairwise-
36330 // distinctness axis; this pin rejects `["", ""]` on the
36331 // NONEMPTY axis — the two contracts pin the `""`-repeat
36332 // failure mode at BOTH the INJECTIVITY axis AND the
36333 // NONEMPTY axis).
36334 let outcome = std::panic::catch_unwind(|| {
36335 assert_str_array_all_nonempty(&["", ""]);
36336 });
36337 outcome.expect_err(
36338 "assert_str_array_all_nonempty must panic on an array \
36339 whose every entry is `\"\"` — the head-arm fires on the \
36340 zero-length entry at position 0",
36341 );
36342 }
36343
36344 #[test]
36345 fn assert_str_array_all_nonempty_panic_message_names_the_helper_and_axis() {
36346 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST begin
36347 // with the helper's own name AND name the failed axis as
36348 // `"STR-EMPTY-ENTRY"` (chosen DISTINCT from every sibling
36349 // helper's axis vocabulary: `"duplicate"` on the pairwise-
36350 // distinct sibling; `"STR-DISJOINTNESS-VIOLATION"` on the
36351 // arrays-disjoint sibling; `"STR-SUBSET-VIOLATION"` on the
36352 // within-finite-set sibling) so a diagnostic that names the
36353 // failed axis routes UNAMBIGUOUSLY to THIS specific helper.
36354 let outcome = std::panic::catch_unwind(|| {
36355 assert_str_array_all_nonempty(&[""]);
36356 });
36357 let payload = outcome.expect_err(
36358 "assert_str_array_all_nonempty must panic on an empty \
36359 entry — the reject-empty arm is the point of the helper",
36360 );
36361 let msg = payload
36362 .downcast_ref::<&'static str>()
36363 .map(|s| (*s).to_owned())
36364 .or_else(|| payload.downcast_ref::<String>().cloned())
36365 .expect(
36366 "assert_str_array_all_nonempty panic payload must be \
36367 a static &str or String",
36368 );
36369 assert!(
36370 msg.contains("assert_str_array_all_nonempty"),
36371 "assert_str_array_all_nonempty panic message {msg:?} must \
36372 name the helper for provenance-preserving failure \
36373 diagnostics",
36374 );
36375 assert!(
36376 msg.contains("STR-EMPTY-ENTRY"),
36377 "assert_str_array_all_nonempty panic message {msg:?} must \
36378 name the failed axis as `STR-EMPTY-ENTRY` DISTINCT from \
36379 every sibling helper's axis vocabulary",
36380 );
36381 // Trailing gate — the sibling `_all_ascii` axis vocabulary
36382 // (`STR-NON-ASCII-ENTRY`) must NOT appear in the NONEMPTY
36383 // helper's panic message. Guards against a copy-paste
36384 // regression that drifted the two helpers' axis-provenance
36385 // strings onto the same slot.
36386 assert!(
36387 !msg.contains("STR-NON-ASCII-ENTRY"),
36388 "assert_str_array_all_nonempty panic message {msg:?} \
36389 must NOT name the ASCII-sibling axis — the two per-\
36390 entry helpers must keep their axis-provenance strings \
36391 lexically distinct",
36392 );
36393 }
36394
36395 // ── assert_str_array_all_ascii — the per-entry ASCII-BYTE-RANGE
36396 // gate sibling of `assert_str_array_all_nonempty` on the
36397 // (`&'static str`) row's (per-entry × contract-shape) axis. ──
36398
36399 #[test]
36400 fn assert_str_array_all_ascii_accepts_the_empty_array() {
36401 // Empty array — vacuously all-ASCII (no entry to fail the
36402 // byte-range gate). The compile-time `const _: () =
36403 // assert_str_array_all_ascii(&EMPTY);` would land on this
36404 // arm, so the runtime call MUST return normally.
36405 assert_str_array_all_ascii::<0>(&[]);
36406 }
36407
36408 #[test]
36409 fn assert_str_array_all_ascii_accepts_ascii_singleton_arrays() {
36410 // Singleton array carrying an all-ASCII entry — the sole
36411 // entry clears the byte-range gate. Cross-arity coverage on
36412 // the `[&'static str; 1]` corner of the const-N generic.
36413 assert_str_array_all_ascii(&["a"]);
36414 assert_str_array_all_ascii(&[Atom::TRUE_LITERAL]);
36415 }
36416
36417 #[test]
36418 fn assert_str_array_all_ascii_accepts_the_empty_entry() {
36419 // Vacuous-inner-loop corner — a zero-length entry has no
36420 // byte to fail the range gate, so the helper MUST accept.
36421 // Positive-orthogonal pin against the NONEMPTY sibling — a
36422 // regression that fused the two contracts into one (rejecting
36423 // `""` on the ASCII sweep) would fail this pin. The two
36424 // per-entry contracts stay independent axes on the SAME row.
36425 assert_str_array_all_ascii(&[""]);
36426 assert_str_array_all_ascii(&["", "a", ""]);
36427 }
36428
36429 #[test]
36430 fn assert_str_array_all_ascii_accepts_every_family_wide_substrate_array() {
36431 // Runtime cross-check that the SAME five arrays the module-
36432 // level `const _: () = ...` witnesses cover at COMPILE time
36433 // are all-ASCII. Sibling posture to the runtime
36434 // `_pairwise_distinct` + `_all_nonempty` cross-checks — the
36435 // three together pin BOTH the INJECTIVITY axis AND the
36436 // NONEMPTY-CARDINALITY-LOWER-BOUND axis AND the ASCII-BYTE-
36437 // RANGE axis on the SAME five arrays, at TWO stages of the
36438 // toolchain (compile-time `const _` line + this runtime
36439 // safety-net).
36440 assert_str_array_all_ascii(&Atom::BOOL_LITERALS);
36441 assert_str_array_all_ascii(&AtomKind::LABELS);
36442 assert_str_array_all_ascii(&QuoteForm::PREFIXES);
36443 assert_str_array_all_ascii(&QuoteForm::IAC_FORGE_TAGS);
36444 assert_str_array_all_ascii(&QuoteForm::LABELS);
36445 }
36446
36447 #[test]
36448 fn assert_str_array_all_ascii_accepts_the_ascii_boundary_byte() {
36449 // Boundary-inclusive pin on the ASCII byte range's upper
36450 // edge — byte `0x7F` (DEL, the last ASCII byte) MUST be
36451 // accepted. Guards against an off-by-one regression that
36452 // walked `bytes[j] >= 0x7F` and spuriously rejected the
36453 // sole `0x7F` character. Together with the negative pins
36454 // below (which fire on `0x80` — the first non-ASCII byte),
36455 // the two pin the byte-range boundary at both edges.
36456 assert_str_array_all_ascii(&["\u{7F}"]);
36457 }
36458
36459 #[test]
36460 #[should_panic(expected = "assert_str_array_all_ascii")]
36461 fn assert_str_array_all_ascii_panics_at_runtime_on_singleton_non_ascii() {
36462 // NEGATIVE PIN — singleton corner: a one-element array
36463 // carrying a non-ASCII entry MUST panic. Pins the helper's
36464 // own reject-non-ascii arm on the smallest possible array
36465 // shape. The two-byte UTF-8 sequence `"é"` (0xC3 0xA9) fires
36466 // on the leading high-byte `0xC3`.
36467 assert_str_array_all_ascii(&["é"]);
36468 }
36469
36470 #[test]
36471 #[should_panic(expected = "assert_str_array_all_ascii")]
36472 fn assert_str_array_all_ascii_panics_at_runtime_on_head_non_ascii() {
36473 // NEGATIVE PIN — head corner: the non-ASCII entry at
36474 // position 0 MUST fire even when subsequent entries are
36475 // ASCII. Pins the outer sweep's inclusive-start behavior
36476 // — a regression that walked `while i < N { … i += 1 }`
36477 // from an off-by-one start (`i = 1`) would silently accept
36478 // a leading non-ASCII entry.
36479 assert_str_array_all_ascii(&["café", "a", "b"]);
36480 }
36481
36482 #[test]
36483 #[should_panic(expected = "assert_str_array_all_ascii")]
36484 fn assert_str_array_all_ascii_panics_at_runtime_on_interior_non_ascii() {
36485 // NEGATIVE PIN — interior corner: the non-ASCII entry at a
36486 // strictly-interior position MUST fire. Pins the outer
36487 // sweep's non-early-exit behavior at the head-arm — a
36488 // regression that returned `Ok` on the first ASCII entry
36489 // (bailing out of the sweep prematurely) would silently
36490 // accept an interior non-ASCII entry.
36491 assert_str_array_all_ascii(&["a", "café", "b"]);
36492 }
36493
36494 #[test]
36495 #[should_panic(expected = "assert_str_array_all_ascii")]
36496 fn assert_str_array_all_ascii_panics_at_runtime_on_tail_non_ascii() {
36497 // NEGATIVE PIN — tail corner: the non-ASCII entry at
36498 // position `N - 1` MUST fire. Pins the outer `while i < N`
36499 // upper bound — a regression that walked `while i < N - 1`
36500 // (dropping the last slot) would silently accept a trailing
36501 // non-ASCII entry.
36502 assert_str_array_all_ascii(&["a", "b", "c", "café"]);
36503 }
36504
36505 #[test]
36506 #[should_panic(expected = "assert_str_array_all_ascii")]
36507 fn assert_str_array_all_ascii_panics_at_runtime_on_interior_byte_non_ascii() {
36508 // NEGATIVE PIN — interior-byte corner: the non-ASCII byte at
36509 // a strictly-interior position WITHIN a single entry MUST
36510 // fire. Pins the inner `while j < bytes.len()` sweep's
36511 // non-early-exit behavior — a regression that returned on
36512 // the first ASCII byte within an entry (bailing out of the
36513 // inner sweep prematurely at `bytes[0] <= 0x7F`) would
36514 // silently accept a non-ASCII byte in a strictly-interior
36515 // slot of the entry.
36516 assert_str_array_all_ascii(&["asdéf"]);
36517 }
36518
36519 #[test]
36520 #[should_panic(expected = "assert_str_array_all_ascii")]
36521 fn assert_str_array_all_ascii_panics_on_the_first_non_ascii_byte() {
36522 // NEGATIVE PIN — first-non-ASCII-byte boundary: byte `0x80`
36523 // (the smallest non-ASCII byte) MUST fire. Guards against
36524 // an off-by-one regression that walked `bytes[j] > 0x80`
36525 // (dropping `0x80` from the reject set). Together with
36526 // `_accepts_the_ascii_boundary_byte` (which pins `0x7F`
36527 // acceptance), the two pin the byte-range boundary at both
36528 // edges. UTF-8 lowest continuation byte in isolation is not
36529 // well-formed, so we use `"\u{80}"` — which is the two-byte
36530 // sequence `0xC2 0x80` — either byte is `> 0x7F` so the
36531 // sweep fires on the leading byte `0xC2` regardless.
36532 assert_str_array_all_ascii(&["\u{80}"]);
36533 }
36534
36535 #[test]
36536 fn assert_str_array_all_ascii_rejects_the_all_non_ascii_array() {
36537 // POSITIVE-ORTHOGONAL PIN — the ALL-non-ASCII corner: an
36538 // array whose every entry contains a non-ASCII byte fires
36539 // the helper on the FIRST entry (the head-arm). Confirms
36540 // the helper does not silently accept an array whose every
36541 // entry ships non-ASCII bytes.
36542 let outcome = std::panic::catch_unwind(|| {
36543 assert_str_array_all_ascii(&["café", "über"]);
36544 });
36545 outcome.expect_err(
36546 "assert_str_array_all_ascii must panic on an array whose \
36547 every entry carries a non-ASCII byte — the head-arm \
36548 fires on the first non-ASCII byte at position 0",
36549 );
36550 }
36551
36552 #[test]
36553 fn assert_str_array_all_ascii_panic_message_names_the_helper_and_axis() {
36554 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST
36555 // begin with the helper's own name AND name the failed axis
36556 // as `"STR-NON-ASCII-ENTRY"` (chosen DISTINCT from every
36557 // sibling helper's axis vocabulary: `"duplicate"` on the
36558 // pairwise-distinct sibling; `"STR-EMPTY-ENTRY"` on the
36559 // per-entry NONEMPTY sibling; `"STR-DISJOINTNESS-VIOLATION"`
36560 // on the arrays-disjoint sibling; `"STR-SUBSET-VIOLATION"`
36561 // on the within-finite-set sibling) so a diagnostic that
36562 // names the failed axis routes UNAMBIGUOUSLY to THIS
36563 // specific ASCII helper.
36564 let outcome = std::panic::catch_unwind(|| {
36565 assert_str_array_all_ascii(&["é"]);
36566 });
36567 let payload = outcome.expect_err(
36568 "assert_str_array_all_ascii must panic on a non-ASCII \
36569 entry — the reject-non-ascii arm is the point of the \
36570 helper",
36571 );
36572 let msg = payload
36573 .downcast_ref::<&'static str>()
36574 .map(|s| (*s).to_owned())
36575 .or_else(|| payload.downcast_ref::<String>().cloned())
36576 .expect(
36577 "assert_str_array_all_ascii panic payload must be a \
36578 static &str or String",
36579 );
36580 assert!(
36581 msg.contains("assert_str_array_all_ascii"),
36582 "assert_str_array_all_ascii panic message {msg:?} must \
36583 name the helper for provenance-preserving failure \
36584 diagnostics",
36585 );
36586 assert!(
36587 msg.contains("STR-NON-ASCII-ENTRY"),
36588 "assert_str_array_all_ascii panic message {msg:?} must \
36589 name the failed axis as `STR-NON-ASCII-ENTRY` DISTINCT \
36590 from every sibling helper's axis vocabulary",
36591 );
36592 assert!(
36593 !msg.contains("STR-EMPTY-ENTRY"),
36594 "assert_str_array_all_ascii panic message {msg:?} must \
36595 NOT name the NONEMPTY-sibling axis — the two per-entry \
36596 helpers must keep their axis-provenance strings \
36597 lexically distinct",
36598 );
36599 }
36600
36601 #[test]
36602 fn str_bytes_equal_accepts_the_empty_pair() {
36603 // Direct pin on `str_bytes_equal`'s zero-length corner —
36604 // both inputs are the empty byte sequence, so the helper
36605 // MUST return `true`. Verifies the `a.len() == b.len()`
36606 // gate does not spuriously reject at `len == 0`.
36607 assert!(str_bytes_equal("", ""));
36608 }
36609
36610 #[test]
36611 fn str_bytes_equal_rejects_the_prefix_pair() {
36612 // Direct pin on `str_bytes_equal`'s length-gate: a strict
36613 // prefix pair MUST be rejected. Guards against a regression
36614 // that dropped the length gate and compared bytes only up
36615 // to the shorter length.
36616 assert!(!str_bytes_equal("ab", "abc"));
36617 assert!(!str_bytes_equal("abc", "ab"));
36618 }
36619
36620 #[test]
36621 fn str_bytes_equal_accepts_the_identical_pair() {
36622 // Direct pin on `str_bytes_equal`'s positive-match arm —
36623 // two identical strings MUST be flagged as equal at every
36624 // canonical shape the substrate carries (bool literal,
36625 // atomic-kind label, quote-family prefix). Sibling posture
36626 // to the `_accepts_every_family_wide_substrate_array` pin
36627 // on the outer helper: this pin fires the inner helper on
36628 // the SAME string values that back the outer witnesses'
36629 // per-array entries.
36630 assert!(str_bytes_equal(Atom::TRUE_LITERAL, Atom::TRUE_LITERAL));
36631 assert!(str_bytes_equal(
36632 AtomKind::SYMBOL_LABEL,
36633 AtomKind::SYMBOL_LABEL,
36634 ));
36635 assert!(str_bytes_equal(
36636 QuoteForm::QUOTE_PREFIX,
36637 QuoteForm::QUOTE_PREFIX,
36638 ));
36639 }
36640
36641 #[test]
36642 fn str_bytes_equal_rejects_the_distinct_pair() {
36643 // Direct pin on `str_bytes_equal`'s reject-arm — two
36644 // distinct family-wide substrate strings MUST be flagged
36645 // as unequal. Guards against a regression that returned
36646 // `true` unconditionally (which would silently pass every
36647 // caller through the collision arm).
36648 assert!(!str_bytes_equal(Atom::TRUE_LITERAL, Atom::FALSE_LITERAL,));
36649 assert!(!str_bytes_equal(
36650 AtomKind::SYMBOL_LABEL,
36651 AtomKind::KEYWORD_LABEL,
36652 ));
36653 assert!(!str_bytes_equal(
36654 QuoteForm::QUOTE_PREFIX,
36655 QuoteForm::QUASIQUOTE_PREFIX,
36656 ));
36657 }
36658
36659 // ── `assert_str_arrays_disjoint` — the `&'static str` element-
36660 // type sibling of `assert_char_arrays_disjoint` and
36661 // `assert_u8_arrays_disjoint`. Sibling posture: same runtime-test
36662 // surface (accept-both-empty, accept-either-side-empty, accept-
36663 // disjoint-singletons, accept-every-family-wide-substrate-pair,
36664 // symmetric-in-argument-order, reject-cross-collision, reject-
36665 // terminal-a-collision, reject-terminal-b-collision, panic-message-
36666 // provenance) restricted to the `&'static str` element type. A
36667 // regression that silently weakens the helper (e.g. flipping
36668 // `str_bytes_equal` polarity, dropping the outer OR inner sweep,
36669 // or returning early on collision) is caught by the helper's OWN
36670 // test surface rather than only surfacing as a false-positive on
36671 // some future disjoint `[&'static str; N] × [&'static str; M]`
36672 // pair's compound pin.
36673
36674 #[test]
36675 fn assert_str_arrays_disjoint_accepts_both_empty_arrays() {
36676 // Both arrays empty at the `[&'static str; 0] × [&'static str; 0]`
36677 // corner — vacuously disjoint (no `(i, j)` pair exists to
36678 // test). The compile-time `const _: () =
36679 // assert_str_arrays_disjoint(&[], &[]);` would land on this
36680 // arm, so the runtime call MUST return normally. Turbofish
36681 // binding required because there's no other cue for the const
36682 // parameters on the empty array literals. Sibling posture to
36683 // `assert_char_arrays_disjoint_accepts_both_empty_arrays` and
36684 // `assert_u8_arrays_disjoint_accepts_both_empty_arrays` on
36685 // the (char) and (u8) row-dual peers — the three share the
36686 // trivial-arity arm across the (element-type × contract-shape)
36687 // 3-row × (disjointness)-column face.
36688 assert_str_arrays_disjoint::<0, 0>(&[], &[]);
36689 }
36690
36691 #[test]
36692 fn assert_str_arrays_disjoint_accepts_either_side_empty() {
36693 // Either side empty at the `[&'static str; 0] × [&'static str; M]`
36694 // OR `[&'static str; N] × [&'static str; 0]` corners —
36695 // vacuously disjoint (the OUTER `while i < N` OR the INNER
36696 // `while j < M` sweep is a no-op). Pins BOTH SIDES of the
36697 // (a-empty, b-empty) 2-corner sub-face on the trivial-arity
36698 // axis so a regression that narrowed the sweep to only-a-non-
36699 // empty OR only-b-non-empty fails HERE at the empty-side
36700 // corner rather than at a distant false-positive.
36701 assert_str_arrays_disjoint::<0, 3>(&[], &["a", "b", "c"]);
36702 assert_str_arrays_disjoint::<3, 0>(&["a", "b", "c"], &[]);
36703 }
36704
36705 #[test]
36706 fn assert_str_arrays_disjoint_accepts_disjoint_singletons() {
36707 // Singleton `a = [K]` and singleton `b = [L]` with `K != L`
36708 // at the `[&'static str; 1] × [&'static str; 1]` corner — the
36709 // minimal non-empty disjointness relation. Pins the INNER
36710 // `if str_bytes_equal(a[i], b[j])` gate returns without
36711 // panicking on a single distinct-pair check. A regression
36712 // that flipped the equality direction (`!=` vs `==` on the
36713 // byte-equality delegate) would silently reject every
36714 // disjoint singleton pair.
36715 assert_str_arrays_disjoint(&["a"], &["b"]);
36716 }
36717
36718 #[test]
36719 fn assert_str_arrays_disjoint_accepts_each_family_wide_substrate_pair() {
36720 // Runtime cross-check that the FOUR (a, b) `[&'static str; N]
36721 // × [&'static str; M]` pairs the substrate's module-level
36722 // `const _` witnesses pin at COMPILE time are proper
36723 // DISJOINTNESS embeddings at runtime too. The pairs enforce
36724 // the theorem at TWO stages of the toolchain: the const
36725 // witnesses fire FIRST at `cargo check` (through the four
36726 // module-level `const _: () = assert_str_arrays_disjoint::
36727 // <N, M>(...)` lines), this runtime pin catches the drift at
36728 // `cargo test` as a safety net. Sibling posture to
36729 // `assert_char_arrays_disjoint_accepts_each_family_wide_substrate_pair`
36730 // which sweeps the FIVE (a, b) PAIRS at the (char) row and
36731 // `assert_u8_arrays_disjoint_accepts_each_family_wide_substrate_pair`
36732 // which sweeps the TWO (a, b) PAIRS at the (u8) row; this
36733 // pin sweeps the four (a, b) PAIRS at the (`&'static str`)
36734 // row on the SAME contract-shape column.
36735 assert_str_arrays_disjoint::<4, 4>(&QuoteForm::PREFIXES, &QuoteForm::LABELS);
36736 assert_str_arrays_disjoint::<4, 4>(&QuoteForm::PREFIXES, &QuoteForm::IAC_FORGE_TAGS);
36737 assert_str_arrays_disjoint::<4, 6>(&QuoteForm::PREFIXES, &AtomKind::LABELS);
36738 assert_str_arrays_disjoint::<6, 4>(&AtomKind::LABELS, &QuoteForm::LABELS);
36739 }
36740
36741 #[test]
36742 fn assert_str_arrays_disjoint_is_symmetric_in_argument_order() {
36743 // SYMMETRY PIN: swapping the two arguments produces the SAME
36744 // verdict. Pins that the disjointness relation is truly
36745 // symmetric across the two array arguments (the helper's
36746 // nested-sweep implementation does NOT gratuitously depend on
36747 // argument order). A regression that narrowed the sweep to
36748 // `for i in a { if !b.contains(a[i]) }` (subset-shape, not
36749 // disjointness-shape) would fail the swap on one direction
36750 // only. Runs each substrate pair BOTH ways. Sibling posture
36751 // to `assert_char_arrays_disjoint_is_symmetric_in_argument_order`
36752 // on the (char) row-dual peer and
36753 // `assert_u8_arrays_disjoint_is_symmetric_in_argument_order`
36754 // on the (u8) row-dual peer.
36755 assert_str_arrays_disjoint(&QuoteForm::LABELS, &QuoteForm::PREFIXES);
36756 assert_str_arrays_disjoint(&QuoteForm::IAC_FORGE_TAGS, &QuoteForm::PREFIXES);
36757 assert_str_arrays_disjoint(&AtomKind::LABELS, &QuoteForm::PREFIXES);
36758 assert_str_arrays_disjoint(&QuoteForm::LABELS, &AtomKind::LABELS);
36759 }
36760
36761 #[test]
36762 #[should_panic(expected = "STR-DISJOINTNESS-VIOLATION")]
36763 fn assert_str_arrays_disjoint_panics_at_runtime_on_collision() {
36764 // NEGATIVE PIN — STR-DISJOINTNESS-VIOLATION corner: two arrays
36765 // sharing a single string MUST panic at runtime with the
36766 // STR-DISJOINTNESS-VIOLATION-named message. Pins the helper's
36767 // OWN reject arm — a regression that silently returned
36768 // without panicking on a cross-array collision would slip
36769 // through the compile-time witnesses' failure mode too. The
36770 // shared string `"dup"` is intentionally placed at the FIRST
36771 // position of BOTH arrays to pin the initial-position drift
36772 // mode.
36773 assert_str_arrays_disjoint(&["dup", "x"], &["dup", "y"]);
36774 }
36775
36776 #[test]
36777 #[should_panic(expected = "STR-DISJOINTNESS-VIOLATION")]
36778 fn assert_str_arrays_disjoint_panics_at_runtime_on_terminal_a_collision() {
36779 // NEGATIVE PIN — terminal-position drift on the OUTER `a`
36780 // side: a shared string at the LAST position of `a` MUST
36781 // panic — pins that the outer `while i < N` loop reaches
36782 // `i = N - 1` (else the terminal drift on `a` would slip
36783 // through). A regression that narrowed the outer sweep to
36784 // `while i < N - 1` (off-by-one on the OUTER bound) would
36785 // silently accept this pair.
36786 assert_str_arrays_disjoint(&["a", "b", "shared"], &["shared"]);
36787 }
36788
36789 #[test]
36790 #[should_panic(expected = "STR-DISJOINTNESS-VIOLATION")]
36791 fn assert_str_arrays_disjoint_panics_at_runtime_on_terminal_b_collision() {
36792 // NEGATIVE PIN — terminal-position drift on the INNER `b`
36793 // side: a shared string at the LAST position of `b` MUST
36794 // panic — pins that the inner `while j < M` loop reaches
36795 // `j = M - 1` (else the terminal drift on `b` would slip
36796 // through). A regression that narrowed the inner sweep to
36797 // `while j < M - 1` (off-by-one on the INNER bound) would
36798 // silently accept this pair. Sibling posture to the outer-
36799 // terminal pin above — the two pins together bind the
36800 // terminal bounds on BOTH loops.
36801 assert_str_arrays_disjoint(&["shared"], &["a", "b", "shared"]);
36802 }
36803
36804 #[test]
36805 #[should_panic(expected = "STR-DISJOINTNESS-VIOLATION")]
36806 fn assert_str_arrays_disjoint_panics_at_runtime_on_length_zero_collision() {
36807 // NEGATIVE PIN — the byte-length gate at `str_bytes_equal`
36808 // must classify TWO empty strings across the (a, b) boundary
36809 // as equal too (both are the zero-length byte sequence). A
36810 // regression that treated `""` as never-colliding across the
36811 // arrays would silently accept the empty-string collision.
36812 // Delegated to the SAME `str_bytes_equal` helper the
36813 // pairwise-distinct sibling uses — this pin exercises the
36814 // cross-array direction of the same zero-length arm.
36815 assert_str_arrays_disjoint(&[""], &[""]);
36816 }
36817
36818 #[test]
36819 fn assert_str_arrays_disjoint_accepts_prefix_pair_without_collision() {
36820 // POSITIVE-ORTHOGONAL PIN — the byte-length gate at
36821 // `str_bytes_equal` across the (a, b) boundary: two arrays
36822 // where entries of one are strict prefixes of entries of the
36823 // other but no full-length equal pair exists must be flagged
36824 // as DISJOINT. A regression that dropped the
36825 // `a.len() != b.len()` gate (e.g. by comparing bytes only up
36826 // to the shorter length) would silently reject
36827 // `(["ab"], ["abc"])` as a collision. Sibling to the (str,
36828 // pairwise-distinct) sibling's `_accepts_prefix_pair_without_
36829 // collision` pin — the two share the byte-length-gate
36830 // theorem across (intra-array, inter-array) row-dual peers.
36831 assert_str_arrays_disjoint(&["ab"], &["abc"]);
36832 assert_str_arrays_disjoint(&["abc"], &["ab"]);
36833 assert_str_arrays_disjoint(&[""], &["a"]);
36834 }
36835
36836 #[test]
36837 fn assert_str_arrays_disjoint_panic_message_names_the_helper_and_str_disjointness_violation_axis(
36838 ) {
36839 // PANIC-MESSAGE PROVENANCE PIN — STR-DISJOINTNESS-VIOLATION
36840 // arm: the panic message MUST begin with the helper's own
36841 // name AND identify the failed AXIS as "STR-DISJOINTNESS-
36842 // VIOLATION" so downstream diagnostics route the drift back
36843 // to (a) the helper by string search on
36844 // `"assert_str_arrays_disjoint"` and (b) the axis by string
36845 // search on `"STR-DISJOINTNESS-VIOLATION"`. Sibling posture
36846 // to `assert_char_arrays_disjoint_panic_message_names_the_helper_and_char_disjointness_violation_axis`
36847 // on the (char) row-dual peer's provenance pin AND to
36848 // `assert_u8_arrays_disjoint_panic_message_names_the_helper_and_u8_disjointness_violation_axis`
36849 // on the (u8) row-dual peer's provenance pin — the three
36850 // pins together bind the (helper, failed-axis) provenance
36851 // triple across the (element-type ∈ {char, u8, `&'static
36852 // str`}) × (disjointness)-column 3-row face with matching
36853 // provenance-preservation discipline. The axis-provenance
36854 // string `"STR-DISJOINTNESS-VIOLATION"` is chosen DISTINCT
36855 // from EVERY sibling helper's axis vocabulary (`"duplicate"`
36856 // on the (str) ARRAY-side pairwise-distinct sibling;
36857 // `"CHAR-DISJOINTNESS-VIOLATION"` on the (char) row-dual
36858 // DISJOINTNESS sibling; `"U8-DISJOINTNESS-VIOLATION"` on
36859 // the (u8) row-dual DISJOINTNESS sibling; `"CHAR-SUBSET-
36860 // VIOLATION"` on the (char) SUBSET-embedding sibling;
36861 // `"SUBSET-VIOLATION"` on the (u8) finite-set SUBSET-only
36862 // sibling; `"RANGE-SUBSET-VIOLATION"` on the (u8) range
36863 // SUBSET-only sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"`
36864 // on the (u8) covers-finite-set sibling; `"OUT-OF-RANGE"` /
36865 // `"MISSING"` on the (u8) covers-inclusive-range sibling;
36866 // `"ARITY-MISMATCH"` on both (u8) `_permutes_*` compound
36867 // helpers; `"SET-NOT-PAIRWISE-DISTINCT"` on the (u8) SET-side
36868 // well-formedness sibling) so a diagnostic that names the
36869 // failed axis routes UNAMBIGUOUSLY to (a) this specific
36870 // `&'static str` DISJOINTNESS helper.
36871 let outcome = std::panic::catch_unwind(|| {
36872 assert_str_arrays_disjoint(&["dup", "x"], &["dup", "y"]);
36873 });
36874 let payload = outcome.expect_err(
36875 "assert_str_arrays_disjoint must panic on a cross-array \
36876 collision — the reject-collision arm is the sole STR-\
36877 DISJOINTNESS-VIOLATION failure mode of the helper",
36878 );
36879 let msg = payload
36880 .downcast_ref::<&'static str>()
36881 .map(|s| (*s).to_owned())
36882 .or_else(|| payload.downcast_ref::<String>().cloned())
36883 .expect(
36884 "assert_str_arrays_disjoint panic payload must be a \
36885 static &str or String",
36886 );
36887 assert!(
36888 msg.contains("assert_str_arrays_disjoint"),
36889 "assert_str_arrays_disjoint panic message {msg:?} must \
36890 name the helper for provenance-preserving failure \
36891 diagnostics",
36892 );
36893 assert!(
36894 msg.contains("STR-DISJOINTNESS-VIOLATION"),
36895 "assert_str_arrays_disjoint panic message {msg:?} must \
36896 name the failed AXIS (\"STR-DISJOINTNESS-VIOLATION\") \
36897 for axis-provenance-preserving failure diagnostics",
36898 );
36899 }
36900
36901 // ── `assert_str_array_within_str_finite_set` — the `&'static str`
36902 // element-type sibling of `assert_char_array_within_char_finite_
36903 // set` and `assert_u8_array_within_u8_finite_set` on the (element-
36904 // type) axis of the (element-type × contract-shape) matrix at the
36905 // (subset-embedding) column. Sibling posture: same runtime-test
36906 // surface (accept-empty, accept-singleton, accept-arr-equals-set,
36907 // accept-each-family-wide-substrate-subset, accept-repeated-array-
36908 // entries, reject-out-of-set, reject-terminal-out-of-set, panic-
36909 // message-provenance, delegated-set-well-formedness) restricted to
36910 // the `&'static str` element type. A regression that silently
36911 // weakens the helper (e.g. flipping the found flag, dropping the
36912 // outer `i` loop, or short-circuiting on the first-position match)
36913 // is caught by the helper's OWN test surface rather than only
36914 // surfacing as a false-positive on some future `[&'static str; N]`
36915 // sub-vocabulary array's subset-embedding pin.
36916
36917 #[test]
36918 fn assert_str_array_within_str_finite_set_accepts_the_empty_array_within_any_set() {
36919 // Empty array `arr = []` at the `[&'static str; 0]` corner —
36920 // vacuously a subset of every set (no `i` position exists to
36921 // test). Cross-arity coverage on the trivial ARRAY corner of
36922 // the const-N generic across three witness-set widths (empty,
36923 // singleton, multi-element) to pin the helper's OUTER-sweep
36924 // arm across the whole (`N == 0` × `M`) axis. Turbofish
36925 // binding required because there's no other cue for the const
36926 // parameters on the empty array literal. Sibling posture to
36927 // `assert_char_array_within_char_finite_set_accepts_the_empty_array_within_any_set`
36928 // and `assert_u8_array_within_u8_finite_set_accepts_the_empty_array_within_any_set`
36929 // on the (char) + (u8) row-dual peers of the SUBSET-EMBEDDING
36930 // helper — the three share the trivial-arity arm across the
36931 // element-type column.
36932 assert_str_array_within_str_finite_set::<0, 0>(&[], &[]);
36933 assert_str_array_within_str_finite_set::<0, 1>(&[], &["x"]);
36934 assert_str_array_within_str_finite_set::<0, 3>(&[], &["a", "b", "c"]);
36935 }
36936
36937 #[test]
36938 fn assert_str_array_within_str_finite_set_accepts_singleton_array_when_str_in_set() {
36939 // Singleton array `arr = [K]` at the `[&'static str; 1]`
36940 // corner MUST pass when `K ∈ set`. Cross-position coverage:
36941 // the str can sit at the FIRST, MIDDLE, or LAST position of
36942 // the `set` — pins the INNER `while j < M` sweep terminates
36943 // at the first-match position rather than always at position
36944 // `0` OR always at position `M - 1`. A regression that
36945 // narrowed the inner sweep to `j == 0` would silently reject
36946 // singleton arrays hitting non-first set positions.
36947 assert_str_array_within_str_finite_set::<1, 3>(&["a"], &["a", "b", "c"]);
36948 assert_str_array_within_str_finite_set::<1, 3>(&["b"], &["a", "b", "c"]);
36949 assert_str_array_within_str_finite_set::<1, 3>(&["c"], &["a", "b", "c"]);
36950 }
36951
36952 #[test]
36953 fn assert_str_array_within_str_finite_set_accepts_arr_equals_set() {
36954 // Boundary corner where `arr` and `set` cover byte-for-byte
36955 // identical distinct-value sets — the SUBSET relation
36956 // degenerates to EQUALITY. Pins that the helper does NOT
36957 // gratuitously require the SUBSET to be PROPER (strict):
36958 // equal-multisets pass the SUBSET check. Sibling posture to
36959 // `assert_char_array_within_char_finite_set_accepts_arr_equals_set`
36960 // and `assert_u8_array_within_u8_finite_set_accepts_arr_equals_set`
36961 // on the (char) + (u8) row-dual peers of the SUBSET-EMBEDDING
36962 // helper — the three share the EQUAL-SETS corner across the
36963 // element-type column.
36964 assert_str_array_within_str_finite_set(&["a", "b"], &["a", "b"]);
36965 assert_str_array_within_str_finite_set(&[Atom::TRUE_LITERAL], &[Atom::TRUE_LITERAL]);
36966 }
36967
36968 #[test]
36969 fn assert_str_array_within_str_finite_set_accepts_each_family_wide_substrate_subset() {
36970 // Runtime cross-check that the THREE (subset, superset) pairs
36971 // the substrate's module-level `const _` witnesses at
36972 // `error.rs` pin at COMPILE time are PROPER SUBSET embeddings
36973 // at runtime too. The pairs enforce the theorem at TWO stages
36974 // of the toolchain: the const witnesses fire FIRST at `cargo
36975 // check` (through the three module-level `const _: () =
36976 // crate::ast::assert_str_array_within_str_finite_set::<N, M>
36977 // (...)` lines in `error.rs`), this runtime pin catches the
36978 // drift at `cargo test` as a safety net. Sibling posture to
36979 // `assert_char_array_within_char_finite_set_accepts_each_family_wide_substrate_subset`
36980 // and `assert_str_array_pairwise_distinct_accepts_every_family_wide_substrate_array`
36981 // — the first sweeps the (char) subset pairs at the SUBSET-
36982 // EMBEDDING axis; the second sweeps the (str) five arrays at
36983 // the INJECTIVITY axis; this pin sweeps the (str) three
36984 // (subset, superset) PAIRS at the SUBSET-EMBEDDING axis.
36985 // Cardinality composition: 6 + 4 + 2 = 12 =
36986 // `SexpShape::LABELS.len()` — the three subsets partition the
36987 // outer twelve-arm vocabulary (composed with the pre-existing
36988 // pairwise-disjointness witness of `AtomKind::LABELS ∩
36989 // QuoteForm::LABELS`).
36990 assert_str_array_within_str_finite_set::<6, 12>(&AtomKind::LABELS, &SexpShape::LABELS);
36991 assert_str_array_within_str_finite_set::<4, 12>(&QuoteForm::LABELS, &SexpShape::LABELS);
36992 assert_str_array_within_str_finite_set::<2, 12>(
36993 &StructuralKind::LABELS,
36994 &SexpShape::LABELS,
36995 );
36996 }
36997
36998 #[test]
36999 fn assert_str_array_within_str_finite_set_accepts_repeated_array_entries_in_set() {
37000 // Peer corner to a (future-lift) `_covers_str_finite_set`:
37001 // this helper permits duplicates in `arr` because SUBSET-
37002 // membership is a DISTINCT-value predicate — `["a", "a", "b"]`
37003 // is a subset of `{"a", "b", "c"}` even though the array is
37004 // not pairwise-distinct. Pins that the helper does NOT
37005 // gratuitously require INJECTIVITY on `arr` (the injectivity
37006 // axis is a DIFFERENT compile-time contract bound by
37007 // `assert_str_array_pairwise_distinct`; combining both binds
37008 // BOTH axes). Sibling posture to
37009 // `assert_char_array_within_char_finite_set_accepts_repeated_array_entries_in_set`
37010 // and `assert_u8_array_within_u8_finite_set_accepts_repeated_array_entries_in_set`
37011 // on the (char) + (u8) row-dual peers of the SUBSET-EMBEDDING
37012 // helper.
37013 assert_str_array_within_str_finite_set(&["a", "a", "b"], &["a", "b", "c"]);
37014 }
37015
37016 #[test]
37017 #[should_panic(expected = "STR-SUBSET-VIOLATION")]
37018 fn assert_str_array_within_str_finite_set_panics_at_runtime_on_out_of_set_entry() {
37019 // NEGATIVE PIN — STR-SUBSET-VIOLATION corner: an array
37020 // carrying a single entry NOT in the target set MUST panic
37021 // at runtime with the STR-SUBSET-VIOLATION-named message.
37022 // Pins the helper's OWN reject arm — a regression that
37023 // silently returned without panicking on an out-of-set entry
37024 // would slip through the compile-time witnesses' failure mode
37025 // too. The offending str `"z"` is intentionally chosen
37026 // OUTSIDE the target set to pin the OUT-OF-SET drift mode.
37027 assert_str_array_within_str_finite_set(&["a", "z"], &["a", "b", "c"]);
37028 }
37029
37030 #[test]
37031 #[should_panic(expected = "STR-SUBSET-VIOLATION")]
37032 fn assert_str_array_within_str_finite_set_panics_at_runtime_on_terminal_out_of_set_entry() {
37033 // NEGATIVE PIN — terminal-position drift: an out-of-set entry
37034 // at the LAST array position MUST panic — pins that the
37035 // outer `while i < N` loop reaches `i = N - 1` (else the
37036 // terminal drift would slip through). A regression that
37037 // narrowed the outer sweep to `while i < N - 1` (off-by-one
37038 // on the OUTER bound) would silently accept this array.
37039 // Sibling posture to
37040 // `assert_char_array_within_char_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
37041 // and `assert_u8_array_within_u8_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
37042 // on the (char) + (u8) row-dual peers' terminal-position
37043 // pins — all three bind the outer-sweep terminal bound at
37044 // the ONE array-side outer loop the helper carries.
37045 assert_str_array_within_str_finite_set(&["a", "b", "c", "z"], &["a", "b", "c"]);
37046 }
37047
37048 #[test]
37049 fn assert_str_array_within_str_finite_set_rejects_length_prefix_shorter_than_set_entry() {
37050 // Byte-length gate corner: an `arr` entry that shares a
37051 // strict-prefix with a `set` entry but is BYTE-SHORTER than
37052 // that `set` entry MUST fail the subset check (prefix ≠
37053 // equal-bytes at the `str_bytes_equal` byte-length gate).
37054 // Pins the cross-array direction of the SAME byte-length
37055 // gate the pairwise-distinct sibling's test surface
37056 // (`assert_str_array_pairwise_distinct_rejects_length_zero_collision`,
37057 // `assert_str_array_pairwise_distinct_accepts_prefix_pair_without_collision`)
37058 // pins on the intra-array direction. Cross-array prefix
37059 // mismatch → out-of-set → STR-SUBSET-VIOLATION panic.
37060 let outcome = std::panic::catch_unwind(|| {
37061 assert_str_array_within_str_finite_set(&["quo"], &["quote", "quasiquote"]);
37062 });
37063 outcome.expect_err(
37064 "assert_str_array_within_str_finite_set must panic on a \
37065 STRICT-PREFIX-only entry outside the target set — the \
37066 byte-length gate at str_bytes_equal separates \
37067 `\"quo\"` from `\"quote\"` at the length-check arm before \
37068 any byte comparison, routing to the STR-SUBSET-VIOLATION \
37069 out-of-set panic",
37070 );
37071 }
37072
37073 #[test]
37074 fn assert_str_array_within_str_finite_set_accepts_length_zero_entry_in_length_zero_set() {
37075 // BYTE-LENGTH-ZERO corner: the empty-string entry `""` in
37076 // `arr` MUST match the empty-string entry `""` in `set` at
37077 // the byte-length gate (both length 0). Pins the LOWER
37078 // boundary of the byte-length axis on the cross-array
37079 // direction of the SAME byte-length gate. A regression that
37080 // gated `str_bytes_equal` on `a.len() > 0` (rather than
37081 // `a.len() == b.len()`) would silently reject this pair.
37082 assert_str_array_within_str_finite_set(&[""], &[""]);
37083 }
37084
37085 #[test]
37086 fn assert_str_array_within_str_finite_set_panic_message_names_the_helper_and_str_subset_violation_axis(
37087 ) {
37088 // PANIC-MESSAGE PROVENANCE PIN — STR-SUBSET-VIOLATION arm:
37089 // the panic message MUST begin with the helper's own name AND
37090 // identify the failed AXIS as "STR-SUBSET-VIOLATION" so
37091 // downstream diagnostics route the drift back to (a) the
37092 // helper by string search on
37093 // `"assert_str_array_within_str_finite_set"` and (b) the axis
37094 // by string search on `"STR-SUBSET-VIOLATION"`. Sibling
37095 // posture to
37096 // `assert_char_array_within_char_finite_set_panic_message_names_the_helper_and_char_subset_violation_axis`
37097 // on the (char) row-dual peer's provenance pin AND to
37098 // `assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis`
37099 // on the (u8) row-dual peer's provenance pin — the three
37100 // pins together bind the (helper, failed-axis) provenance
37101 // triple across the (element-type ∈ {char, u8, `&'static
37102 // str`}) × (subset-embedding)-column 3-row face with matching
37103 // provenance-preservation discipline. The axis-provenance
37104 // string `"STR-SUBSET-VIOLATION"` is chosen DISTINCT from
37105 // EVERY sibling helper's axis vocabulary (`"duplicate"` on
37106 // the (str) ARRAY-side pairwise-distinct sibling; `"STR-
37107 // DISJOINTNESS-VIOLATION"` on the (str) row DISJOINTNESS
37108 // sibling; `"CHAR-SUBSET-VIOLATION"` on the (char) row-dual
37109 // SUBSET sibling; `"SUBSET-VIOLATION"` on the (u8) finite-
37110 // set SUBSET-only sibling; `"RANGE-SUBSET-VIOLATION"` on the
37111 // (u8) range SUBSET-only sibling; `"CHAR-DISJOINTNESS-
37112 // VIOLATION"` / `"U8-DISJOINTNESS-VIOLATION"` on the (char)
37113 // / (u8) row-dual DISJOINTNESS siblings; `"OUT-OF-SET"` /
37114 // `"SET-BYTE-MISSING"` on the (u8) covers-finite-set
37115 // sibling; `"OUT-OF-RANGE"` / `"MISSING"` on the (u8)
37116 // covers-inclusive-range sibling; `"ARITY-MISMATCH"` on both
37117 // (u8) `_permutes_*` compound helpers; `"SET-NOT-PAIRWISE-
37118 // DISTINCT"` on the (u8) SET-side well-formedness sibling)
37119 // so a diagnostic that names the failed axis routes
37120 // UNAMBIGUOUSLY to (a) this specific `&'static str` SUBSET-
37121 // embedding helper, (b) the `arr` argument as the drift site
37122 // rather than the `set` argument specifying the target
37123 // superset.
37124 let outcome = std::panic::catch_unwind(|| {
37125 assert_str_array_within_str_finite_set(&["a", "z"], &["a", "b", "c"]);
37126 });
37127 let payload = outcome.expect_err(
37128 "assert_str_array_within_str_finite_set must panic on an \
37129 out-of-set entry — the reject-out-of-set arm is the \
37130 sole STR-SUBSET-VIOLATION failure mode of the helper",
37131 );
37132 let msg = payload
37133 .downcast_ref::<&'static str>()
37134 .map(|s| (*s).to_owned())
37135 .or_else(|| payload.downcast_ref::<String>().cloned())
37136 .expect(
37137 "assert_str_array_within_str_finite_set panic payload \
37138 must be a static &str or String",
37139 );
37140 assert!(
37141 msg.contains("assert_str_array_within_str_finite_set"),
37142 "assert_str_array_within_str_finite_set panic message \
37143 {msg:?} must name the helper for provenance-preserving \
37144 failure diagnostics",
37145 );
37146 assert!(
37147 msg.contains("STR-SUBSET-VIOLATION"),
37148 "assert_str_array_within_str_finite_set panic message \
37149 {msg:?} must name the failed AXIS (\"STR-SUBSET-\
37150 VIOLATION\") for axis-provenance-preserving failure \
37151 diagnostics",
37152 );
37153 }
37154
37155 #[test]
37156 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
37157 fn assert_str_array_within_str_finite_set_panics_on_malformed_target_set_spec() {
37158 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
37159 // malformed target-set spec `["a", "a", "b"]` fed into the
37160 // ARRAY-side within helper MUST panic on the DELEGATED
37161 // pairwise-distinct arm BEFORE the STR-SUBSET-VIOLATION arm
37162 // fires. Pins the delegation chain: a regression that
37163 // dropped the `assert_str_array_pairwise_distinct(set)` call
37164 // at the top of `assert_str_array_within_str_finite_set`
37165 // would silently accept a malformed set and produce a false-
37166 // positive verdict on any `arr` embedded in the DISTINCT-
37167 // value subset. The panic message here surfaces from the
37168 // SIBLING helper (containing the ARRAY-side helper's
37169 // `"assert_str_array_pairwise_distinct"` panic-name prefix
37170 // rather than a bespoke `"SET-NOT-PAIRWISE-DISTINCT"` axis
37171 // string) because the (str) row does NOT yet carry a
37172 // separate `assert_str_finite_set_pairwise_distinct` alias —
37173 // the delegation reuses the ARRAY-side helper directly per
37174 // the design choice documented on the SET-side-well-
37175 // formedness section of the helper's docstring. Sibling
37176 // posture to
37177 // `assert_char_array_within_char_finite_set_panics_on_malformed_target_set_spec`
37178 // and `assert_u8_array_within_u8_finite_set_panics_on_malformed_target_set_spec`
37179 // on the (char) + (u8) row-dual peers' delegated-SET-well-
37180 // formedness pins — the three pins together bind the
37181 // delegation chain at ONE test per element-type row.
37182 assert_str_array_within_str_finite_set::<2, 3>(&["a", "b"], &["a", "b", "b"]);
37183 }
37184
37185 // ── `assert_str_finite_set_covered_by_three_str_arrays` — the
37186 // (⊆) SET-COVERAGE dual of the SUBSET-embedding sibling on the
37187 // (`&'static str`) row of the (element-type × contract-shape)
37188 // matrix, extended to the 3-array-union carrier shape. Sibling
37189 // posture: same runtime-test surface (accept-empty-parent,
37190 // accept-parent-covered-by-first-array, accept-parent-covered-
37191 // by-second-array, accept-parent-covered-by-third-array, accept-
37192 // sexp-shape-labels-partition, reject-uncovered-parent-entry,
37193 // reject-terminal-uncovered-parent-entry, panic-message-
37194 // provenance, delegated-set-well-formedness) specialised to the
37195 // 3-array coverage shape. A regression that silently weakens the
37196 // helper (e.g. dropping the second-array probe, dropping the
37197 // third-array probe, or short-circuiting on the first parent
37198 // position) is caught by the helper's OWN test surface rather
37199 // than only surfacing as a false-positive on the
37200 // `SexpShape::LABELS` disjoint-union witness.
37201
37202 #[test]
37203 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_the_empty_parent() {
37204 // Empty parent `set = []` at the `[&'static str; 0]` corner
37205 // — vacuously covered by any triple of sub-vocabulary arrays
37206 // (no `w` position exists to test). Cross-arity coverage on
37207 // the trivial PARENT corner of the const-W generic across
37208 // three sub-vocabulary-arity combinations. Turbofish binding
37209 // required because there's no other cue for the const
37210 // parameters on the empty array literal.
37211 assert_str_finite_set_covered_by_three_str_arrays::<0, 0, 0, 0>(&[], &[], &[], &[]);
37212 assert_str_finite_set_covered_by_three_str_arrays::<1, 0, 0, 0>(&["x"], &[], &[], &[]);
37213 assert_str_finite_set_covered_by_three_str_arrays::<1, 2, 3, 0>(
37214 &["a"],
37215 &["b", "c"],
37216 &["d", "e", "f"],
37217 &[],
37218 );
37219 }
37220
37221 #[test]
37222 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_parent_covered_by_first_array() {
37223 // Singleton parent `set = [K]` at the `[&'static str; 1]`
37224 // corner MUST pass when `K` is in the FIRST sub-vocabulary
37225 // array `a`. Pins the FIRST-ARRAY probe arm's short-circuit
37226 // discovery — the parent entry is reached without exhausting
37227 // the second- or third-array probes. Cross-position coverage
37228 // inside `a`: parent hit at FIRST, MIDDLE, LAST position of
37229 // `a` pins the INNER `while i < N` sweep terminates at the
37230 // first-match position rather than always at position `0` OR
37231 // always at position `N - 1`.
37232 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 1, 1>(
37233 &["k", "b", "c"],
37234 &["d", "e"],
37235 &["f"],
37236 &["k"],
37237 );
37238 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 1, 1>(
37239 &["a", "k", "c"],
37240 &["d", "e"],
37241 &["f"],
37242 &["k"],
37243 );
37244 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 1, 1>(
37245 &["a", "b", "k"],
37246 &["d", "e"],
37247 &["f"],
37248 &["k"],
37249 );
37250 }
37251
37252 #[test]
37253 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_parent_covered_by_second_array() {
37254 // Singleton parent MUST pass when `K` is in the SECOND sub-
37255 // vocabulary array `b` but NOT in `a`. Pins the SECOND-ARRAY
37256 // probe arm — the coverage sweep proceeds from `a` to `b`
37257 // when `a` returns no hit. A regression that dropped the
37258 // `if !found { … b sweep … }` block would silently reject
37259 // this case as SET-STR-MISSING even though `b` carries the
37260 // covering entry. Cross-position coverage inside `b`: FIRST,
37261 // MIDDLE, LAST positions of `b` pin the INNER `while j < M`
37262 // sweep terminates at the first-match position.
37263 assert_str_finite_set_covered_by_three_str_arrays::<3, 3, 1, 1>(
37264 &["a", "b", "c"],
37265 &["k", "e", "f"],
37266 &["g"],
37267 &["k"],
37268 );
37269 assert_str_finite_set_covered_by_three_str_arrays::<3, 3, 1, 1>(
37270 &["a", "b", "c"],
37271 &["d", "k", "f"],
37272 &["g"],
37273 &["k"],
37274 );
37275 assert_str_finite_set_covered_by_three_str_arrays::<3, 3, 1, 1>(
37276 &["a", "b", "c"],
37277 &["d", "e", "k"],
37278 &["g"],
37279 &["k"],
37280 );
37281 }
37282
37283 #[test]
37284 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_parent_covered_by_third_array() {
37285 // Singleton parent MUST pass when `K` is in the THIRD sub-
37286 // vocabulary array `c` but NOT in `a` or `b`. Pins the
37287 // THIRD-ARRAY probe arm — the coverage sweep proceeds all
37288 // the way to `c` when `a` and `b` both return no hit. A
37289 // regression that dropped the `if !found { … c sweep … }`
37290 // block would silently reject this case as SET-STR-MISSING
37291 // even though `c` carries the covering entry. Cross-position
37292 // coverage inside `c`: FIRST, MIDDLE, LAST positions of `c`
37293 // pin the INNER `while k < K` sweep terminates at the first-
37294 // match position.
37295 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 3, 1>(
37296 &["a", "b", "c"],
37297 &["d", "e"],
37298 &["k", "g", "h"],
37299 &["k"],
37300 );
37301 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 3, 1>(
37302 &["a", "b", "c"],
37303 &["d", "e"],
37304 &["f", "k", "h"],
37305 &["k"],
37306 );
37307 assert_str_finite_set_covered_by_three_str_arrays::<3, 2, 3, 1>(
37308 &["a", "b", "c"],
37309 &["d", "e"],
37310 &["f", "g", "k"],
37311 &["k"],
37312 );
37313 }
37314
37315 #[test]
37316 fn assert_str_finite_set_covered_by_three_str_arrays_accepts_sexp_shape_labels_partition() {
37317 // Runtime cross-check that the twelve-arm
37318 // (`AtomKind::LABELS`, `QuoteForm::LABELS`,
37319 // `StructuralKind::LABELS`, `SexpShape::LABELS`) partition
37320 // quadruple the substrate's module-level `const _` witness at
37321 // `error.rs` pins at COMPILE time is a well-formed SET-
37322 // COVERAGE relation at runtime too. The quadruple enforces the
37323 // (⊆) direction of the disjoint-union theorem at TWO stages
37324 // of the toolchain: the const witness fires FIRST at `cargo
37325 // check`, this runtime pin catches the drift at `cargo test`
37326 // as a safety net. Sibling posture to
37327 // `assert_str_array_within_str_finite_set_accepts_each_family_wide_substrate_subset`
37328 // — the sibling sweeps the (⊇) SUBSET direction for the three
37329 // sub-vocabularies; this pin sweeps the (⊆) COVERAGE direction
37330 // for the parent. Cardinality composition: 6 + 4 + 2 = 12 =
37331 // `SexpShape::LABELS.len()`.
37332 assert_str_finite_set_covered_by_three_str_arrays::<6, 4, 2, 12>(
37333 &AtomKind::LABELS,
37334 &QuoteForm::LABELS,
37335 &crate::error::StructuralKind::LABELS,
37336 &crate::error::SexpShape::LABELS,
37337 );
37338 }
37339
37340 #[test]
37341 #[should_panic(expected = "SET-STR-MISSING")]
37342 fn assert_str_finite_set_covered_by_three_str_arrays_panics_at_runtime_on_uncovered_parent_entry(
37343 ) {
37344 // NEGATIVE PIN — SET-STR-MISSING corner: a parent set carrying
37345 // an entry NOT in any of the three sub-vocabulary arrays MUST
37346 // panic at runtime with the SET-STR-MISSING-named message.
37347 // Pins the helper's OWN reject arm — a regression that
37348 // silently returned without panicking on an uncovered entry
37349 // would slip through the compile-time witness's failure mode
37350 // too. The offending str `"z"` is intentionally chosen ABSENT
37351 // from `a ∪ b ∪ c` to pin the SET-STR-MISSING drift mode.
37352 assert_str_finite_set_covered_by_three_str_arrays::<1, 1, 1, 2>(
37353 &["a"],
37354 &["b"],
37355 &["c"],
37356 &["a", "z"],
37357 );
37358 }
37359
37360 #[test]
37361 #[should_panic(expected = "SET-STR-MISSING")]
37362 fn assert_str_finite_set_covered_by_three_str_arrays_panics_at_runtime_on_terminal_uncovered_parent_entry(
37363 ) {
37364 // NEGATIVE PIN — terminal-position drift: an uncovered entry
37365 // at the LAST parent position MUST panic — pins that the
37366 // outer `while w < W` loop reaches `w = W - 1` (else the
37367 // terminal drift would slip through). A regression that
37368 // narrowed the outer sweep to `while w < W - 1` (off-by-one
37369 // on the OUTER bound) would silently accept this parent set
37370 // even though the trailing `"z"` is uncovered.
37371 assert_str_finite_set_covered_by_three_str_arrays::<1, 1, 1, 4>(
37372 &["a"],
37373 &["b"],
37374 &["c"],
37375 &["a", "b", "c", "z"],
37376 );
37377 }
37378
37379 #[test]
37380 fn assert_str_finite_set_covered_by_three_str_arrays_panic_message_names_the_helper_and_set_str_missing_axis(
37381 ) {
37382 // PANIC-MESSAGE PROVENANCE PIN — SET-STR-MISSING arm: the
37383 // panic message MUST begin with the helper's own name AND
37384 // identify the failed AXIS as "SET-STR-MISSING" so downstream
37385 // diagnostics route the drift back to (a) the helper by
37386 // string search on
37387 // `"assert_str_finite_set_covered_by_three_str_arrays"` and
37388 // (b) the axis by string search on `"SET-STR-MISSING"`.
37389 // Sibling posture to
37390 // `assert_str_array_within_str_finite_set_panic_message_names_the_helper_and_str_subset_violation_axis`
37391 // on the sibling (str)-row SUBSET-VIOLATION provenance pin
37392 // AND to the (u8) row-dual peer's `"SET-BYTE-MISSING"` axis
37393 // vocabulary on `assert_u8_array_covers_finite_set` — the
37394 // element-type infix `"STR"` vs `"BYTE"` disambiguates the
37395 // element-type peer while the shared `"SET-…-MISSING"` suffix
37396 // lets callers grep any row's COVERAGE-side SET-MISSING
37397 // sibling by the shared suffix pattern alone.
37398 let outcome = std::panic::catch_unwind(|| {
37399 assert_str_finite_set_covered_by_three_str_arrays::<1, 1, 1, 2>(
37400 &["a"],
37401 &["b"],
37402 &["c"],
37403 &["a", "z"],
37404 );
37405 });
37406 let payload = outcome.expect_err(
37407 "assert_str_finite_set_covered_by_three_str_arrays must \
37408 panic on an uncovered parent entry — the reject-uncovered \
37409 arm is the sole SET-STR-MISSING failure mode of the \
37410 helper",
37411 );
37412 let msg = payload
37413 .downcast_ref::<&'static str>()
37414 .map(|s| (*s).to_owned())
37415 .or_else(|| payload.downcast_ref::<String>().cloned())
37416 .expect(
37417 "assert_str_finite_set_covered_by_three_str_arrays \
37418 panic payload must be a static &str or String",
37419 );
37420 assert!(
37421 msg.contains("assert_str_finite_set_covered_by_three_str_arrays"),
37422 "assert_str_finite_set_covered_by_three_str_arrays panic \
37423 message {msg:?} must name the helper for provenance-\
37424 preserving failure diagnostics",
37425 );
37426 assert!(
37427 msg.contains("SET-STR-MISSING"),
37428 "assert_str_finite_set_covered_by_three_str_arrays panic \
37429 message {msg:?} must name the failed AXIS (\"SET-STR-\
37430 MISSING\") for axis-provenance-preserving failure \
37431 diagnostics",
37432 );
37433 }
37434
37435 #[test]
37436 #[should_panic(expected = "assert_str_array_pairwise_distinct")]
37437 fn assert_str_finite_set_covered_by_three_str_arrays_panics_on_malformed_target_set_spec() {
37438 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
37439 // malformed parent-set spec `["a", "a", "b"]` fed into the
37440 // COVERAGE helper MUST panic on the DELEGATED pairwise-
37441 // distinct arm BEFORE the SET-STR-MISSING arm fires. Pins the
37442 // delegation chain: a regression that dropped the
37443 // `assert_str_array_pairwise_distinct(set)` call at the top
37444 // of `assert_str_finite_set_covered_by_three_str_arrays`
37445 // would silently accept a malformed parent set with duplicate
37446 // entries (the duplicated byte counts as covered on the first
37447 // hit even when the second copy is absent from `a ∪ b ∪ c`).
37448 // The panic message surfaces from the sibling ARRAY-side
37449 // pairwise-distinct helper directly (containing its
37450 // `"assert_str_array_pairwise_distinct"` panic-name prefix)
37451 // because the (str) row does NOT carry a separate
37452 // `assert_str_finite_set_pairwise_distinct` alias — the
37453 // delegation reuses the ARRAY-side helper per the design
37454 // choice documented on the SET-side well-formedness section
37455 // of the helper's docstring.
37456 assert_str_finite_set_covered_by_three_str_arrays::<1, 1, 1, 3>(
37457 &["a"],
37458 &["b"],
37459 &["c"],
37460 &["a", "a", "b"],
37461 );
37462 }
37463
37464 // ── `assert_str_array_is_concatenation_of_two_scalar_replicas` —
37465 // the MANY-TO-ONE-BLOCK-CONSTANCY sibling of the INJECTIVITY
37466 // sibling on the (`&'static str`) row of the (element-type ×
37467 // contract-shape) matrix. Sibling posture: symmetric runtime-test
37468 // surface (accept-two-block-partition, accept-K-zero-degenerate,
37469 // accept-K-equals-N-degenerate, accept-empty-array, accept-
37470 // compiler-spec-io-stage-operations-partition, reject-head-drift,
37471 // reject-tail-drift, reject-arity-slip, panic-message-provenance)
37472 // specialised to the MANY-TO-ONE block-constant projection shape.
37473 // A regression that silently weakens the helper (e.g. flipping
37474 // `str_bytes_equal` to `!str_bytes_equal`, narrowing the outer
37475 // sweep to `while i < K - 1` on the HEAD segment, silently
37476 // skipping the TAIL segment, or returning early past the
37477 // CARDINALITY-MISMATCH gate) is caught by the helper's OWN test
37478 // surface rather than only surfacing as a false-positive on the
37479 // `CompilerSpecIoStage::OPERATIONS` witness.
37480
37481 #[test]
37482 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_the_two_block_partition() {
37483 // Canonical two-block partition `[head; K] ++ [tail; N - K]`
37484 // at the substrate's ONE non-injective per-index projection
37485 // array `CompilerSpecIoStage::OPERATIONS` — the `(N, K) =
37486 // (4, 2)` corner with `(head, tail) = (REALIZE_TO_DISK,
37487 // LOAD_FROM_DISK)` MUST pass. Cross-partition coverage on
37488 // arities (3, 1), (4, 2), (5, 3) pins the INNER `while i < K`
37489 // and `while j < N` sweeps proceed through EACH position of
37490 // BOTH segments rather than short-circuiting on ANY early
37491 // position.
37492 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 1>(
37493 &["a", "b", "b"],
37494 "a",
37495 "b",
37496 );
37497 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 2>(
37498 &["a", "a", "b", "b"],
37499 "a",
37500 "b",
37501 );
37502 assert_str_array_is_concatenation_of_two_scalar_replicas::<5, 3>(
37503 &["a", "a", "a", "b", "b"],
37504 "a",
37505 "b",
37506 );
37507 }
37508
37509 #[test]
37510 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_the_k_zero_all_tail_degenerate(
37511 ) {
37512 // Degenerate `K = 0` corner collapses the array into the ALL-
37513 // TAIL replica `arr == [tail; N]`. The HEAD segment is empty
37514 // (`while i < 0` never enters); the TAIL segment covers
37515 // positions `[0, N)`. Cross-arity coverage on the `K = 0`
37516 // axis pins the HEAD-segment sweep's outer `while i < K`
37517 // terminates at `i = 0` when `K = 0`, and the TAIL-segment
37518 // sweep's outer `while j < N` starts at `j = K = 0` and
37519 // proceeds through EVERY position. A regression that hard-
37520 // coded `K > 0` or short-circuited on the empty HEAD-segment
37521 // arm would silently reject this legal degenerate shape.
37522 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 0>(
37523 &["x", "x", "x"],
37524 "unused",
37525 "x",
37526 );
37527 }
37528
37529 #[test]
37530 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_the_k_equals_n_all_head_degenerate(
37531 ) {
37532 // Degenerate `K = N` corner collapses the array into the ALL-
37533 // HEAD replica `arr == [head; N]`. The HEAD segment covers
37534 // positions `[0, N)`; the TAIL segment is empty (`while j <
37535 // N` starts at `j = K = N` and never enters). Peer to the
37536 // `K = 0` degenerate corner — together the two corners pin
37537 // the ONE-BLOCK degenerate shapes on BOTH endpoints of the
37538 // `K ∈ [0, N]` const-generic range. A regression that hard-
37539 // coded `K < N` or short-circuited on the empty TAIL-segment
37540 // arm would silently reject this legal degenerate shape.
37541 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 3>(
37542 &["x", "x", "x"],
37543 "x",
37544 "unused",
37545 );
37546 }
37547
37548 #[test]
37549 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_the_empty_array() {
37550 // Trivial `N = 0, K = 0` corner: the empty array trivially
37551 // satisfies BOTH the empty HEAD segment `[head; 0]` and the
37552 // empty TAIL segment `[tail; 0]`. Pins that the vacuous case
37553 // on the `[&'static str; 0]` corner of the const-N generic
37554 // passes without either segment sweep entering. Turbofish
37555 // binding required because there's no cue for the const
37556 // parameters on the empty array literal.
37557 assert_str_array_is_concatenation_of_two_scalar_replicas::<0, 0>(
37558 &[],
37559 "unused-head",
37560 "unused-tail",
37561 );
37562 }
37563
37564 #[test]
37565 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_head_equals_tail_single_block(
37566 ) {
37567 // Corner where `head == tail` collapses the two-block
37568 // partition into a SINGLE-BLOCK replica: `arr == [head; N] ==
37569 // [tail; N]`. The partition boundary `K` is irrelevant to the
37570 // observable byte-shape when `head` and `tail` are byte-
37571 // equal, so ANY `K ∈ [0, N]` accepts. Pins the docstring
37572 // note "the two scalars `head` and `tail` MAY be byte-equal
37573 // (in which case the helper degenerates to a SINGLE-BLOCK
37574 // replica-check)". Cross-K coverage inside `N = 4`: K = 0
37575 // (empty HEAD), K = 2 (interior split), K = 4 (empty TAIL).
37576 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 0>(
37577 &["z", "z", "z", "z"],
37578 "z",
37579 "z",
37580 );
37581 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 2>(
37582 &["z", "z", "z", "z"],
37583 "z",
37584 "z",
37585 );
37586 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 4>(
37587 &["z", "z", "z", "z"],
37588 "z",
37589 "z",
37590 );
37591 }
37592
37593 #[test]
37594 fn assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_compiler_spec_io_stage_operations_partition(
37595 ) {
37596 // Runtime cross-check that the substrate's ONE non-injective
37597 // per-index projection array `CompilerSpecIoStage::OPERATIONS`
37598 // — the `(N, K) = (4, 2)` corner with `(head, tail) =
37599 // (REALIZE_TO_DISK_OPERATION, LOAD_FROM_DISK_OPERATION)` —
37600 // the substrate's module-level `const _` witness at `error.rs`
37601 // pins at COMPILE time is a well-formed BLOCK-CONSTANCY
37602 // relation at runtime too. The pair enforces the theorem at
37603 // TWO stages of the toolchain: the const witness fires FIRST
37604 // at `cargo check`, this runtime pin catches the drift at
37605 // `cargo test` as a safety net. Sibling posture to
37606 // `compiler_spec_io_stage_operations_align_with_all_by_index`
37607 // (which pins per-position equality via `stage.operation()`
37608 // at runtime) and to
37609 // `compiler_spec_io_stage_operations_partition_all_two_ways`
37610 // (which pins the two operation-label multiplicities at
37611 // runtime) — this witness carries the SAME theorem at the
37612 // ARRAY-LEVEL block-constant shape at COMPILE time.
37613 assert_str_array_is_concatenation_of_two_scalar_replicas::<4, 2>(
37614 &crate::error::CompilerSpecIoStage::OPERATIONS,
37615 crate::error::CompilerSpecIoStage::REALIZE_TO_DISK_OPERATION,
37616 crate::error::CompilerSpecIoStage::LOAD_FROM_DISK_OPERATION,
37617 );
37618 }
37619
37620 #[test]
37621 #[should_panic(expected = "HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION")]
37622 fn assert_str_array_is_concatenation_of_two_scalar_replicas_panics_at_runtime_on_head_segment_drift(
37623 ) {
37624 // NEGATIVE PIN — HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION
37625 // corner: an entry in the HEAD segment `arr[0..K)` that does
37626 // NOT byte-equal `head` MUST panic at runtime with the HEAD-
37627 // segment-named message. Pins the helper's HEAD-segment
37628 // reject arm — a regression that silently short-circuited on
37629 // the first HEAD position without checking the middle or
37630 // terminal HEAD positions would slip through the compile-
37631 // time witness's failure mode too. The offending str "x" at
37632 // position 1 (interior HEAD) pins the middle-of-HEAD drift
37633 // mode. `K = 3, N = 5` so the HEAD segment spans positions
37634 // `[0, 3)` — the interior drift at position 1 is inside the
37635 // HEAD, not the TAIL boundary.
37636 assert_str_array_is_concatenation_of_two_scalar_replicas::<5, 3>(
37637 &["a", "x", "a", "b", "b"],
37638 "a",
37639 "b",
37640 );
37641 }
37642
37643 #[test]
37644 #[should_panic(expected = "TAIL-SEGMENT-BLOCK-CONSTANCY-VIOLATION")]
37645 fn assert_str_array_is_concatenation_of_two_scalar_replicas_panics_at_runtime_on_tail_segment_drift(
37646 ) {
37647 // NEGATIVE PIN — TAIL-SEGMENT-BLOCK-CONSTANCY-VIOLATION
37648 // corner: an entry in the TAIL segment `arr[K..N)` that does
37649 // NOT byte-equal `tail` MUST panic at runtime with the TAIL-
37650 // segment-named message. Peer to the HEAD-segment drift arm
37651 // above — the SAME helper, DIFFERENT segment arm. Pins that
37652 // the outer `while j < N` sweep on the TAIL segment reaches
37653 // `j = N - 1` (else the terminal drift would slip through).
37654 // The offending str "x" at position 4 (terminal TAIL) with
37655 // `K = 3, N = 5` pins the terminal-TAIL drift mode.
37656 assert_str_array_is_concatenation_of_two_scalar_replicas::<5, 3>(
37657 &["a", "a", "a", "b", "x"],
37658 "a",
37659 "b",
37660 );
37661 }
37662
37663 #[test]
37664 #[should_panic(expected = "CARDINALITY-MISMATCH")]
37665 fn assert_str_array_is_concatenation_of_two_scalar_replicas_panics_at_runtime_on_arity_slip() {
37666 // NEGATIVE PIN — CARDINALITY-MISMATCH gate: a caller-side
37667 // turbofish arity slip on the `K` const-generic where `K > N`
37668 // MUST panic at runtime with the CARDINALITY-MISMATCH-named
37669 // message BEFORE any per-position sweep begins. Pins the
37670 // gate's placement at the TOP of the helper — a regression
37671 // that dropped the gate would silently degenerate into a
37672 // truncated HEAD-only sweep at the caller's `K` cap without
37673 // ever entering the TAIL sweep, silently accepting an array
37674 // whose TAIL positions carry arbitrary drift. The offending
37675 // `K = 5` against `N = 3` pins the strict `K > N` reject
37676 // arm; the LEGAL `K == N` degenerate is covered by a peer
37677 // acceptance test above.
37678 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 5>(
37679 &["a", "a", "a"],
37680 "a",
37681 "b",
37682 );
37683 }
37684
37685 #[test]
37686 fn assert_str_array_is_concatenation_of_two_scalar_replicas_panic_message_names_the_helper_and_block_constancy_violation_axis(
37687 ) {
37688 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-BLOCK-
37689 // CONSTANCY-VIOLATION arm: the panic message MUST begin with
37690 // the helper's own name AND identify the failed AXIS as
37691 // "HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION" so downstream
37692 // diagnostics route the drift back to (a) the helper by
37693 // string search on
37694 // `"assert_str_array_is_concatenation_of_two_scalar_replicas"`
37695 // and (b) the segment + axis by string search on
37696 // `"HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION"`. Sibling posture
37697 // to `assert_str_array_pairwise_distinct_panic_message_...`
37698 // on the sibling (str)-row INJECTIVITY provenance pin AND to
37699 // the sibling ROW-DUAL `_covered_by_three_str_arrays`'s
37700 // `"SET-STR-MISSING"` axis vocabulary — the shared
37701 // `"BLOCK-CONSTANCY-VIOLATION"` suffix lets callers grep
37702 // either the HEAD or TAIL segment arm by the shared suffix
37703 // pattern alone, while the `HEAD-` / `TAIL-` prefix
37704 // disambiguates the SEGMENT.
37705 let outcome = std::panic::catch_unwind(|| {
37706 assert_str_array_is_concatenation_of_two_scalar_replicas::<3, 2>(
37707 &["a", "x", "b"],
37708 "a",
37709 "b",
37710 );
37711 });
37712 let payload = outcome.expect_err(
37713 "assert_str_array_is_concatenation_of_two_scalar_replicas \
37714 must panic on a HEAD-segment drift — the reject-HEAD-drift \
37715 arm is one of the two BLOCK-CONSTANCY-VIOLATION failure \
37716 modes of the helper",
37717 );
37718 let msg = payload
37719 .downcast_ref::<&'static str>()
37720 .map(|s| (*s).to_owned())
37721 .or_else(|| payload.downcast_ref::<String>().cloned())
37722 .expect(
37723 "assert_str_array_is_concatenation_of_two_scalar_replicas \
37724 panic payload must be a static &str or String",
37725 );
37726 assert!(
37727 msg.contains("assert_str_array_is_concatenation_of_two_scalar_replicas"),
37728 "assert_str_array_is_concatenation_of_two_scalar_replicas \
37729 panic message {msg:?} must name the helper for \
37730 provenance-preserving failure diagnostics",
37731 );
37732 assert!(
37733 msg.contains("HEAD-SEGMENT-BLOCK-CONSTANCY-VIOLATION"),
37734 "assert_str_array_is_concatenation_of_two_scalar_replicas \
37735 panic message {msg:?} must name the failed AXIS (\"HEAD-\
37736 SEGMENT-BLOCK-CONSTANCY-VIOLATION\") for axis-provenance-\
37737 preserving failure diagnostics",
37738 );
37739 }
37740
37741 // ── `assert_u8_array_slice_is_scalar_replica` — the (`u8`)-row
37742 // SLICE-BLOCK-CONSTANCY sibling of the (`&'static str`)-row FULL-
37743 // ARRAY-two-block-partition sibling
37744 // `assert_str_array_is_concatenation_of_two_scalar_replicas`.
37745 // Sibling posture: symmetric runtime-test surface (accept-canonical-
37746 // slice, accept-empty-slice-corners, accept-full-array-degenerate,
37747 // accept-sexp-shape-atomic-collapse, reject-slice-content-drift,
37748 // reject-start-out-of-bounds, reject-end-out-of-bounds, reject-
37749 // inverted-range, panic-message-provenance) specialised to the (u8)
37750 // sub-slice SINGLE-scalar block-constant shape. A regression that
37751 // silently weakens the helper (e.g. flipping `arr[i] != scalar`
37752 // to `==`, moving the START gate BELOW the sweep, narrowing the
37753 // sweep to `while i < END - 1` and missing the terminal position,
37754 // or returning early past ANY bounds gate) is caught by the
37755 // helper's OWN test surface rather than only surfacing as a false-
37756 // positive on the `SexpShape::HASH_DISCRIMINATORS[1..7)` witness.
37757
37758 #[test]
37759 fn assert_u8_array_slice_is_scalar_replica_accepts_a_canonical_middle_slice() {
37760 // Canonical sub-slice `arr[START..END) == [scalar; END - START]`
37761 // inside a longer array `arr` whose ENDPOINTS carry DIFFERENT
37762 // bytes than the slice. Pins the outer `while i < END` sweep
37763 // enters at `i = START` (skipping positions `[0..START)`) AND
37764 // terminates at `i = END` (skipping positions `[END..N)`) —
37765 // BOTH endpoint bytes DIFFER from `scalar` so a regression
37766 // that widened the sweep beyond the slice would fail here.
37767 assert_u8_array_slice_is_scalar_replica::<7, 1, 6>(&[9, 1, 1, 1, 1, 1, 9], 1);
37768 }
37769
37770 #[test]
37771 fn assert_u8_array_slice_is_scalar_replica_accepts_the_empty_slice_at_start_equals_end() {
37772 // LEGAL degenerate: `START == END` collapses the slice into an
37773 // empty range `[START..START)`. The sweep never enters the
37774 // loop body and the helper accepts. Cross-position coverage
37775 // pins the empty-slice acceptance at THREE distinct positions
37776 // (`START == 0` at the left endpoint, `START == 3` in the
37777 // interior, `START == N` at the right endpoint) so a
37778 // regression that hard-coded `START < END` OR panicked on the
37779 // `START == END` corner is caught on ALL THREE arms. Peer to
37780 // the `K == N` / `K == 0` degenerate corners of the sibling
37781 // `assert_str_array_is_concatenation_of_two_scalar_replicas`.
37782 assert_u8_array_slice_is_scalar_replica::<5, 0, 0>(&[7, 7, 7, 7, 7], 42);
37783 assert_u8_array_slice_is_scalar_replica::<5, 3, 3>(&[7, 7, 7, 7, 7], 42);
37784 assert_u8_array_slice_is_scalar_replica::<5, 5, 5>(&[7, 7, 7, 7, 7], 42);
37785 }
37786
37787 #[test]
37788 fn assert_u8_array_slice_is_scalar_replica_accepts_the_full_array_slice() {
37789 // Full-array-covering slice `[0..N)` collapses to the ALL-
37790 // scalar-replica shape `arr == [scalar; N]` (peer to the
37791 // `K == N` degenerate of the sibling str-row helper). Pins
37792 // that the sweep proceeds through EVERY position of the array
37793 // when `START = 0` and `END = N`. Cross-arity coverage on
37794 // `N ∈ {3, 6, 8}` pins the sweep's terminal-position visit
37795 // across a range of array cardinalities.
37796 assert_u8_array_slice_is_scalar_replica::<3, 0, 3>(&[5, 5, 5], 5);
37797 assert_u8_array_slice_is_scalar_replica::<6, 0, 6>(&[0, 0, 0, 0, 0, 0], 0);
37798 assert_u8_array_slice_is_scalar_replica::<8, 0, 8>(
37799 &[255, 255, 255, 255, 255, 255, 255, 255],
37800 255,
37801 );
37802 }
37803
37804 #[test]
37805 fn assert_u8_array_slice_is_scalar_replica_accepts_sexp_shape_atomic_collapse_slice() {
37806 // Runtime cross-check that the substrate's ONE MANY-TO-ONE-
37807 // COLLAPSE `[u8; N]` sub-slice `SexpShape::HASH_DISCRIMINATORS
37808 // [1..7)` — the six-slot atomic-outer-shape collapse onto
37809 // `AtomKind::OUTER_HASH_DISCRIMINATOR` — the substrate's
37810 // module-level `const _` witness at `ast.rs` pins at COMPILE
37811 // time is a well-formed SLICE-BLOCK-CONSTANCY relation at
37812 // runtime too. The pair enforces the theorem at TWO stages of
37813 // the toolchain: the const witness fires FIRST at `cargo
37814 // check`, this runtime pin catches the drift at `cargo test`
37815 // as a safety net. Sibling posture to the peer runtime cross-
37816 // check on the sibling
37817 // `assert_str_array_is_concatenation_of_two_scalar_replicas_accepts_compiler_spec_io_stage_operations_partition`
37818 // — this witness carries the SAME class of MANY-TO-ONE-
37819 // COLLAPSE theorem at the ARRAY-slice level on the (u8) row
37820 // rather than the FULL-ARRAY-two-block level on the (str) row.
37821 assert_u8_array_slice_is_scalar_replica::<12, 1, 7>(
37822 &crate::error::SexpShape::HASH_DISCRIMINATORS,
37823 AtomKind::OUTER_HASH_DISCRIMINATOR,
37824 );
37825 }
37826
37827 #[test]
37828 #[should_panic(expected = "SLICE-BLOCK-CONSTANCY-VIOLATION")]
37829 fn assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_slice_content_drift() {
37830 // NEGATIVE PIN — SLICE-BLOCK-CONSTANCY-VIOLATION corner: an
37831 // entry in the slice `arr[START..END)` that does NOT byte-
37832 // equal `scalar` MUST panic at runtime with the slice-named
37833 // message. Pins the helper's slice-content reject arm — a
37834 // regression that silently short-circuited on the first slice
37835 // position without checking the middle or terminal slice
37836 // positions would slip through the compile-time witness's
37837 // failure mode too. The offending byte `9` at position `3`
37838 // (interior of the slice) with `START = 1, END = 6` pins the
37839 // middle-of-slice drift mode.
37840 assert_u8_array_slice_is_scalar_replica::<7, 1, 6>(&[0, 1, 1, 9, 1, 1, 0], 1);
37841 }
37842
37843 #[test]
37844 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
37845 fn assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_start_out_of_bounds() {
37846 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
37847 // turbofish arity slip on the `START` const-generic where
37848 // `START > N` MUST panic at runtime with the START-OUT-OF-
37849 // BOUNDS-named message BEFORE any per-position sweep begins.
37850 // Pins the gate's placement at the TOP of the helper — a
37851 // regression that dropped the gate would either silently
37852 // degenerate into a vacuous sweep OR panic deeper in `arr[i]`
37853 // bounds-checking with a helper-name-less panic message. The
37854 // offending `START = 7` against `N = 5` pins the strict
37855 // `START > N` reject arm; the LEGAL `START == N` empty
37856 // corner is covered by the peer acceptance test above.
37857 assert_u8_array_slice_is_scalar_replica::<5, 7, 7>(&[1, 1, 1, 1, 1], 1);
37858 }
37859
37860 #[test]
37861 #[should_panic(expected = "END-OUT-OF-BOUNDS")]
37862 fn assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_end_out_of_bounds() {
37863 // NEGATIVE PIN — END-OUT-OF-BOUNDS gate: a caller-side
37864 // turbofish arity slip on the `END` const-generic where
37865 // `END > N` MUST panic at runtime with the END-OUT-OF-BOUNDS-
37866 // named message. Peer to the START-OUT-OF-BOUNDS arm above —
37867 // the two gates jointly enforce `START, END ∈ [0..N]` before
37868 // any content sweep. The offending `END = 9` against `N = 5`
37869 // pins the strict `END > N` reject arm; the LEGAL `END == N`
37870 // slice-to-end corner is covered by the full-array acceptance
37871 // test above. `START = 0` sits in-bounds so the START gate
37872 // does not fire first.
37873 assert_u8_array_slice_is_scalar_replica::<5, 0, 9>(&[1, 1, 1, 1, 1], 1);
37874 }
37875
37876 #[test]
37877 #[should_panic(expected = "INVERTED-RANGE")]
37878 fn assert_u8_array_slice_is_scalar_replica_panics_at_runtime_on_inverted_range() {
37879 // NEGATIVE PIN — INVERTED-RANGE gate: a caller-side turbofish
37880 // typo that swaps `START` and `END` (both individually in-
37881 // bounds, but `START > END`) MUST panic at runtime with the
37882 // INVERTED-RANGE-named message. Pins that the strict `START
37883 // > END` slip fails-loud on a DISTINCT axis rather than
37884 // silently accepting the empty sweep — a regression that
37885 // dropped this gate would silently accept ANY array on the
37886 // swapped-turbofish call site. The offending `START = 5,
37887 // END = 2` against `N = 7` pins the strict `START > END`
37888 // reject arm; the LEGAL `START == END` empty corner is
37889 // covered by the peer acceptance test above.
37890 assert_u8_array_slice_is_scalar_replica::<7, 5, 2>(&[1, 1, 1, 1, 1, 1, 1], 1);
37891 }
37892
37893 #[test]
37894 fn assert_u8_array_slice_is_scalar_replica_panic_message_names_the_helper_and_slice_block_constancy_violation_axis(
37895 ) {
37896 // PANIC-MESSAGE PROVENANCE PIN — SLICE-BLOCK-CONSTANCY-
37897 // VIOLATION arm: the panic message MUST begin with the
37898 // helper's own name AND identify the failed AXIS as "SLICE-
37899 // BLOCK-CONSTANCY-VIOLATION" so downstream diagnostics route
37900 // the drift back to (a) the helper by string search on
37901 // `"assert_u8_array_slice_is_scalar_replica"` and (b) the
37902 // failed axis by string search on `"SLICE-BLOCK-CONSTANCY-
37903 // VIOLATION"`. Sibling posture to
37904 // `assert_str_array_is_concatenation_of_two_scalar_replicas_panic_message_...`
37905 // on the sibling (str)-row FULL-ARRAY BLOCK-CONSTANCY
37906 // provenance pin — the shared `"BLOCK-CONSTANCY-VIOLATION"`
37907 // suffix lets callers grep either the SUB-SLICE or FULL-
37908 // ARRAY variant by the shared suffix pattern alone, while
37909 // the `SLICE-` / `HEAD-SEGMENT-` / `TAIL-SEGMENT-` prefix
37910 // disambiguates the CONTRACT SHAPE.
37911 let outcome = std::panic::catch_unwind(|| {
37912 assert_u8_array_slice_is_scalar_replica::<5, 1, 4>(&[0, 1, 9, 1, 0], 1);
37913 });
37914 let payload = outcome.expect_err(
37915 "assert_u8_array_slice_is_scalar_replica must panic on a \
37916 slice-content drift — the reject-slice-drift arm is the \
37917 CONTENT failure mode of the helper",
37918 );
37919 let msg = payload
37920 .downcast_ref::<&'static str>()
37921 .map(|s| (*s).to_owned())
37922 .or_else(|| payload.downcast_ref::<String>().cloned())
37923 .expect(
37924 "assert_u8_array_slice_is_scalar_replica panic payload \
37925 must be a static &str or String",
37926 );
37927 assert!(
37928 msg.contains("assert_u8_array_slice_is_scalar_replica"),
37929 "assert_u8_array_slice_is_scalar_replica panic message \
37930 {msg:?} must name the helper for provenance-preserving \
37931 failure diagnostics",
37932 );
37933 assert!(
37934 msg.contains("SLICE-BLOCK-CONSTANCY-VIOLATION"),
37935 "assert_u8_array_slice_is_scalar_replica panic message \
37936 {msg:?} must name the failed AXIS (\"SLICE-BLOCK-\
37937 CONSTANCY-VIOLATION\") for axis-provenance-preserving \
37938 failure diagnostics",
37939 );
37940 }
37941
37942 // ── `assert_u8_array_slice_equals_u8_array` — the SLICE-EQUALS-
37943 // ARRAY sibling of `assert_u8_array_slice_is_scalar_replica` on
37944 // the SAME (u8) row. Sibling posture: same runtime-test surface
37945 // (accept-canonical-middle-slice, accept-empty-sub-array, accept-
37946 // full-array-degenerate, accept-sexp-shape-quote-tail-composition,
37947 // reject-positionwise-drift, reject-start-out-of-bounds, reject-
37948 // slice-length-out-of-bounds, panic-message-provenance)
37949 // specialised to the (u8) SUB-SLICE ARRAY-image composition shape.
37950 // A regression that silently weakens the helper (e.g. flipping
37951 // `full[START + i] != sub[i]` to `==`, dropping the `START`
37952 // offset from the outer read, moving the START gate BELOW the
37953 // sweep, narrowing the sweep to `while i < M - 1` and missing
37954 // the terminal position, or returning early past ANY bounds gate)
37955 // is caught by the helper's OWN test surface rather than only
37956 // surfacing as a false-positive on the
37957 // `SexpShape::HASH_DISCRIMINATORS[8..12] ==
37958 // QuoteForm::HASH_DISCRIMINATORS` witness.
37959
37960 #[test]
37961 fn assert_u8_array_slice_equals_u8_array_accepts_a_canonical_middle_slice() {
37962 // Canonical sub-slice `full[START..START + M) == sub[..]`
37963 // inside a longer array `full` whose ENDPOINTS carry
37964 // DIFFERENT bytes than the peer sub-array. Pins the outer
37965 // `while i < M` sweep reads `full[START + i]` at the
37966 // OFFSET position (not `full[i]`) — a regression that
37967 // dropped the `START` offset would compare `full[0..M)`
37968 // against `sub[..]` and pass on `full[0]=9 != sub[0]=1`
37969 // silently or panic on the wrong axis. `START = 1` pins
37970 // the sweep skips position `[0..START)` and reads only
37971 // `[1..1+3) = [1..4)`.
37972 assert_u8_array_slice_equals_u8_array::<7, 3, 1>(&[9, 3, 4, 5, 9, 9, 9], &[3, 4, 5]);
37973 }
37974
37975 #[test]
37976 fn assert_u8_array_slice_equals_u8_array_accepts_the_empty_sub_array() {
37977 // LEGAL degenerate: `M == 0` collapses the sub-array into
37978 // an empty listing `[]`. The sweep never enters the loop
37979 // body and the helper accepts. Cross-position coverage
37980 // pins the empty-sub-array acceptance at THREE distinct
37981 // `START` positions (`START == 0` at the left endpoint,
37982 // `START == 3` in the interior, `START == N` at the right
37983 // endpoint — the latter is the corner `START == N` combined
37984 // with `M == 0` that the START-OUT-OF-BOUNDS gate's
37985 // inclusive upper bound must accept). A regression that
37986 // hard-coded `START < N` OR panicked on the `M == 0` corner
37987 // is caught on ALL THREE arms.
37988 assert_u8_array_slice_equals_u8_array::<5, 0, 0>(&[7, 7, 7, 7, 7], &[]);
37989 assert_u8_array_slice_equals_u8_array::<5, 0, 3>(&[7, 7, 7, 7, 7], &[]);
37990 assert_u8_array_slice_equals_u8_array::<5, 0, 5>(&[7, 7, 7, 7, 7], &[]);
37991 }
37992
37993 #[test]
37994 fn assert_u8_array_slice_equals_u8_array_accepts_the_full_array_degenerate() {
37995 // Full-array-covering slice `M == N, START == 0` collapses
37996 // to the ALL-positions-equal-peer-array shape `full == sub`
37997 // pointwise. Pins that the sweep proceeds through EVERY
37998 // position of the outer array when `START = 0` and `M = N`.
37999 // Cross-arity coverage on `N ∈ {3, 4, 6}` pins the sweep's
38000 // terminal-position visit across a range of array
38001 // cardinalities.
38002 assert_u8_array_slice_equals_u8_array::<3, 3, 0>(&[10, 20, 30], &[10, 20, 30]);
38003 assert_u8_array_slice_equals_u8_array::<4, 4, 0>(&[3, 4, 5, 6], &[3, 4, 5, 6]);
38004 assert_u8_array_slice_equals_u8_array::<6, 6, 0>(&[0, 1, 2, 3, 4, 5], &[0, 1, 2, 3, 4, 5]);
38005 }
38006
38007 #[test]
38008 fn assert_u8_array_slice_equals_u8_array_accepts_sexp_shape_quote_tail_composition() {
38009 // Runtime cross-check that the substrate's ONE
38010 // POSITIONWISE-COMPOSITION `[u8; N]` sub-slice
38011 // `SexpShape::HASH_DISCRIMINATORS[8..12]` byte-for-byte
38012 // equal to the peer `QuoteForm::HASH_DISCRIMINATORS` is a
38013 // well-formed SLICE-EQUALS-ARRAY relation at runtime too.
38014 // The pair enforces the theorem at TWO stages of the
38015 // toolchain: the const witness fires FIRST at `cargo
38016 // check`, this runtime pin catches the drift at `cargo
38017 // test` as a safety net. Sibling posture to the peer
38018 // runtime cross-check
38019 // `assert_u8_array_slice_is_scalar_replica_accepts_sexp_shape_atomic_collapse_slice`
38020 // — this witness carries the SLICE-EQUALS-ARRAY theorem
38021 // (positionwise composition with a peer array of arity
38022 // `M`) on the SAME (u8) row's quote-family tail slice
38023 // rather than the MANY-TO-ONE-COLLAPSE theorem
38024 // (positionwise composition with a SCALAR) on the atomic-
38025 // collapse mid slice.
38026 assert_u8_array_slice_equals_u8_array::<12, 4, 8>(
38027 &crate::error::SexpShape::HASH_DISCRIMINATORS,
38028 &QuoteForm::HASH_DISCRIMINATORS,
38029 );
38030 }
38031
38032 #[test]
38033 fn assert_u8_array_slice_equals_u8_array_accepts_sub_carving_hash_discriminators_per_position_order(
38034 ) {
38035 // Runtime cross-check that the FOUR sub-carving
38036 // `HASH_DISCRIMINATORS` arrays each byte-equal their
38037 // canonical literal-byte listing pointwise at the FULL-ARRAY
38038 // corner (`M == N`, `START == 0`). Runs the SAME helper the
38039 // four `const _` witnesses above line 5842 in this file run
38040 // at rustc time — a runtime safety net enforcing the
38041 // theorem at BOTH stages of the toolchain (const at `cargo
38042 // check`, runtime at `cargo test`). A regression that
38043 // renamed one of the per-role `*_HASH_DISCRIMINATOR` aliases
38044 // (or drifted its literal byte value at the declaration
38045 // site, or reordered a slot in the outer array's
38046 // initializer) fails HERE at the substrate callsite AND at
38047 // the const witness above. Peer of
38048 // `assert_u8_array_slice_equals_u8_array_accepts_sexp_shape_quote_tail_composition`
38049 // above — that witness carries the SLICE-EQUALS-ARRAY
38050 // theorem for the OUTER container against a SUB-CARVING
38051 // array; this witness carries the theorem for each of the
38052 // FOUR sub-carving arrays against a literal-byte listing.
38053 //
38054 // The four sub-carvings appear here in canonical order
38055 // (`{0..=6}` outer-`Sexp` cache-key partition, top-to-
38056 // bottom):
38057 // * `StructuralKind::HASH_DISCRIMINATORS == [0u8, 2]` — the
38058 // structural-residual carving. Non-contiguous (gap at
38059 // `1u8` reserved for the atomic-carve outer marker).
38060 // * `AtomKind::HASH_DISCRIMINATORS == [0u8, 1, 2, 3, 4, 5]`
38061 // — the nested-inner atomic-payload carving specialising
38062 // the outer `1u8` atomic marker inside `Hash for Atom`.
38063 // * `QuoteForm::HASH_DISCRIMINATORS == [3u8, 4, 5, 6]` —
38064 // the quote-family carving covering the four homoiconic
38065 // prefixes.
38066 // * `UnquoteForm::HASH_DISCRIMINATORS == [5u8, 6]` — the
38067 // two-of-four substitution subset of `QuoteForm`
38068 // projecting through `to_quote_form()`.
38069 assert_u8_array_slice_equals_u8_array::<2, 2, 0>(
38070 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
38071 &[0u8, 2],
38072 );
38073 assert_u8_array_slice_equals_u8_array::<6, 6, 0>(
38074 &AtomKind::HASH_DISCRIMINATORS,
38075 &[0u8, 1, 2, 3, 4, 5],
38076 );
38077 assert_u8_array_slice_equals_u8_array::<4, 4, 0>(
38078 &QuoteForm::HASH_DISCRIMINATORS,
38079 &[3u8, 4, 5, 6],
38080 );
38081 assert_u8_array_slice_equals_u8_array::<2, 2, 0>(
38082 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
38083 &[5u8, 6],
38084 );
38085 }
38086
38087 /// Runtime SLICE-EQUALS-ARRAY safety net for the (UnquoteForm ⊂
38088 /// QuoteForm) 2-of-4 sub-carve on the (u8) HASH_DISCRIMINATORS
38089 /// vocabulary axis — mirrors the module-level `const _: () =
38090 /// assert_u8_array_slice_equals_u8_array::<4, 2, 2>(&QuoteForm::
38091 /// HASH_DISCRIMINATORS, &UnquoteForm::HASH_DISCRIMINATORS)` witness
38092 /// added below line 5617 in this file. The `const _` witness fires
38093 /// FIRST at `cargo check`; this runtime pin catches the ARRAY-LEVEL
38094 /// positionwise drift at `cargo test` as a safety net enforcing the
38095 /// theorem at BOTH stages of the toolchain.
38096 ///
38097 /// (U8)-row peer to `assert_str_array_slice_equals_str_array_
38098 /// accepts_unquote_form_sub_carve_of_quote_form` at `error.rs`'s
38099 /// tests submodule — that test sweeps the SAME 2-of-4 sub-carve at
38100 /// the (str) row across three vocabulary axes (`LABELS`,
38101 /// `MARKERS↔PREFIXES`, `IAC_FORGE_TAGS`); this test sweeps the same
38102 /// carve at the (u8) row on the ONE `HASH_DISCRIMINATORS` axis.
38103 /// Together the four runtime witnesses close the (element-type ×
38104 /// vocabulary-axis) matrix of the substitution-subset carve at the
38105 /// SLICE-EQUALS positionwise-composition contract, on the runtime-
38106 /// pin sibling face of the four-witness compile-time cluster.
38107 ///
38108 /// A regression that (a) swaps `UnquoteForm::HASH_DISCRIMINATORS`
38109 /// from `[UNQUOTE_HASH_DISCRIMINATOR, SPLICE_HASH_DISCRIMINATOR]`
38110 /// to `[SPLICE_HASH_DISCRIMINATOR, UNQUOTE_HASH_DISCRIMINATOR]`, or
38111 /// (b) reorders `QuoteForm::HASH_DISCRIMINATORS` such that the two
38112 /// `UnquoteForm` bytes no longer sit contiguously at slots
38113 /// `[2..4)`, fails HERE with the `SLICE-EQUALS-ARRAY-VIOLATION`
38114 /// axis panic naming the drifted position — where the sibling
38115 /// SET-level SUBSET safety-net stays silent (both bytes still
38116 /// appear in the superset, just at different positions).
38117 #[test]
38118 fn assert_u8_array_slice_equals_u8_array_accepts_unquote_form_sub_carve_of_quote_form() {
38119 assert_u8_array_slice_equals_u8_array::<4, 2, 2>(
38120 &QuoteForm::HASH_DISCRIMINATORS,
38121 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
38122 );
38123 }
38124
38125 #[test]
38126 #[should_panic(expected = "SLICE-EQUALS-ARRAY-VIOLATION")]
38127 fn assert_u8_array_slice_equals_u8_array_panics_at_runtime_on_positionwise_drift() {
38128 // NEGATIVE PIN — SLICE-EQUALS-ARRAY-VIOLATION corner: a
38129 // byte at some position in `full[START..START + M)` that
38130 // does NOT byte-equal the peer sub-array `sub` at the
38131 // offset-matched position MUST panic at runtime with the
38132 // slice-named message. Pins the helper's positionwise-
38133 // drift reject arm — a regression that silently short-
38134 // circuited on the first slice position without checking
38135 // the middle or terminal slice positions would slip
38136 // through the compile-time witness's failure mode too.
38137 // The offending byte `9` at outer position `3` (interior
38138 // of the sub-slice `[1..4)`, offset `2` inside `sub`)
38139 // pins the middle-of-slice drift mode.
38140 assert_u8_array_slice_equals_u8_array::<5, 3, 1>(&[0, 3, 4, 9, 0], &[3, 4, 5]);
38141 }
38142
38143 #[test]
38144 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
38145 fn assert_u8_array_slice_equals_u8_array_panics_at_runtime_on_start_out_of_bounds() {
38146 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
38147 // turbofish arity slip on the `START` const-generic where
38148 // `START > N` MUST panic at runtime with the START-OUT-OF-
38149 // BOUNDS-named message BEFORE the peer SLICE-LENGTH-OUT-OF-
38150 // BOUNDS gate reads `N - START` (which would `usize`-
38151 // underflow had this gate not caught the slip first). Pins
38152 // the gate's placement at the TOP of the helper — a
38153 // regression that dropped the gate would either underflow
38154 // subtraction at the peer gate OR panic deeper in
38155 // `full[START + i]` bounds-checking with a helper-name-less
38156 // panic message. The offending `START = 7` against `N = 5`
38157 // pins the strict `START > N` reject arm; the LEGAL
38158 // `START == N` empty-slice-at-right-endpoint corner is
38159 // covered by the peer acceptance test above.
38160 assert_u8_array_slice_equals_u8_array::<5, 0, 7>(&[1, 1, 1, 1, 1], &[]);
38161 }
38162
38163 #[test]
38164 #[should_panic(expected = "SLICE-LENGTH-OUT-OF-BOUNDS")]
38165 fn assert_u8_array_slice_equals_u8_array_panics_at_runtime_on_slice_length_out_of_bounds() {
38166 // NEGATIVE PIN — SLICE-LENGTH-OUT-OF-BOUNDS gate: a peer
38167 // sub-array arity `M` that exceeds the outer array's tail
38168 // cardinality `N - START` MUST panic at runtime with the
38169 // slice-length-out-of-bounds-named message. Peer gate to
38170 // the START-OUT-OF-BOUNDS arm above — the two gates
38171 // jointly enforce `START ≤ N` and `M ≤ N - START` before
38172 // any content sweep. The offending `M = 5` against
38173 // `N - START = 5 - 3 = 2` pins the strict `M > N - START`
38174 // reject arm; the LEGAL exact-fit corner `M == N - START`
38175 // is covered by the middle-slice acceptance test above.
38176 assert_u8_array_slice_equals_u8_array::<5, 5, 3>(&[1, 1, 1, 1, 1], &[1, 1, 1, 1, 1]);
38177 }
38178
38179 #[test]
38180 fn assert_u8_array_slice_equals_u8_array_panic_message_names_the_helper_and_slice_equals_array_violation_axis(
38181 ) {
38182 // PANIC-MESSAGE PROVENANCE PIN — SLICE-EQUALS-ARRAY-
38183 // VIOLATION arm: the panic message MUST begin with the
38184 // helper's own name AND identify the failed AXIS as
38185 // "SLICE-EQUALS-ARRAY-VIOLATION" so downstream diagnostics
38186 // route the drift back to (a) the helper by string search
38187 // on `"assert_u8_array_slice_equals_u8_array"` and (b) the
38188 // failed axis by string search on `"SLICE-EQUALS-ARRAY-
38189 // VIOLATION"`. Sibling posture to
38190 // `assert_u8_array_slice_is_scalar_replica_panic_message_names_the_helper_and_slice_block_constancy_violation_axis`
38191 // on the sibling SLICE-BLOCK-CONSTANCY corner — the shared
38192 // `"SLICE-"` prefix lets callers grep either the SINGLE-
38193 // scalar-image or ARRAY-of-length-`M`-image variant by
38194 // the shared prefix, while the `-BLOCK-CONSTANCY-` /
38195 // `-EQUALS-ARRAY-` infix disambiguates the CONTRACT
38196 // SHAPE.
38197 let outcome = std::panic::catch_unwind(|| {
38198 assert_u8_array_slice_equals_u8_array::<5, 3, 1>(&[0, 3, 4, 9, 0], &[3, 4, 5]);
38199 });
38200 let payload = outcome.expect_err(
38201 "assert_u8_array_slice_equals_u8_array must panic on a \
38202 positionwise drift — the reject-positionwise-drift arm \
38203 is the CONTENT failure mode of the helper",
38204 );
38205 let msg = payload
38206 .downcast_ref::<&'static str>()
38207 .map(|s| (*s).to_owned())
38208 .or_else(|| payload.downcast_ref::<String>().cloned())
38209 .expect(
38210 "assert_u8_array_slice_equals_u8_array panic payload \
38211 must be a static &str or String",
38212 );
38213 assert!(
38214 msg.contains("assert_u8_array_slice_equals_u8_array"),
38215 "assert_u8_array_slice_equals_u8_array panic message \
38216 {msg:?} must name the helper for provenance-preserving \
38217 failure diagnostics",
38218 );
38219 assert!(
38220 msg.contains("SLICE-EQUALS-ARRAY-VIOLATION"),
38221 "assert_u8_array_slice_equals_u8_array panic message \
38222 {msg:?} must name the failed AXIS (\"SLICE-EQUALS-\
38223 ARRAY-VIOLATION\") for axis-provenance-preserving \
38224 failure diagnostics",
38225 );
38226 }
38227
38228 // ── `assert_u8_array_pairwise_distinct` — the `u8` element-type
38229 // sibling of `assert_char_array_pairwise_distinct` and
38230 // `assert_str_array_pairwise_distinct`. Sibling posture: same
38231 // runtime-test surface (accept-empty, accept-singleton, accept-
38232 // every-family-wide-substrate-array, reject-binary, reject-non-
38233 // adjacent, reject-terminal, panic-message-provenance) restricted
38234 // to the `u8` element type. A regression that silently weakens the
38235 // helper (e.g. flipping `==` to `!=`, dropping the inner `j` loop,
38236 // or returning early on collision) is caught by the helper's OWN
38237 // test surface rather than only surfacing as a false-positive on
38238 // some future `[u8; N]`-typed discriminator array's distinctness pin.
38239
38240 #[test]
38241 fn assert_u8_array_pairwise_distinct_accepts_the_empty_array() {
38242 // Empty array — vacuously pairwise distinct (no pair to
38243 // collide). The compile-time `const _: () =
38244 // assert_u8_array_pairwise_distinct(&EMPTY);` would land on
38245 // this arm, so the runtime call MUST return normally.
38246 assert_u8_array_pairwise_distinct::<0>(&[]);
38247 }
38248
38249 #[test]
38250 fn assert_u8_array_pairwise_distinct_accepts_singleton_arrays() {
38251 // Singleton array — vacuously pairwise distinct (only one
38252 // element, no pair). Cross-arity coverage on the `[u8; 1]`
38253 // corner of the const-N generic.
38254 assert_u8_array_pairwise_distinct(&[0u8]);
38255 assert_u8_array_pairwise_distinct(&[AtomKind::SYMBOL_HASH_DISCRIMINATOR]);
38256 }
38257
38258 #[test]
38259 fn assert_u8_array_pairwise_distinct_accepts_every_family_wide_substrate_array() {
38260 // Runtime cross-check that the SAME four arrays the module-
38261 // level `const _: () = ...` witnesses cover at COMPILE time
38262 // are pairwise distinct. A regression that removes ONE of
38263 // the `const _` witnesses would still leave THIS runtime pin
38264 // as a safety net; the const witness fires FIRST at `cargo
38265 // check`, this runtime pin catches the collision at `cargo
38266 // test`. The pair enforces the theorem at TWO stages of the
38267 // toolchain. `SexpShape::HASH_DISCRIMINATORS` is excluded
38268 // per the intentionally-non-injective twelve-shape → seven-
38269 // byte collapse rule documented on the helper.
38270 assert_u8_array_pairwise_distinct(&AtomKind::HASH_DISCRIMINATORS);
38271 assert_u8_array_pairwise_distinct(&QuoteForm::HASH_DISCRIMINATORS);
38272 assert_u8_array_pairwise_distinct(&crate::error::StructuralKind::HASH_DISCRIMINATORS);
38273 assert_u8_array_pairwise_distinct(&crate::error::UnquoteForm::HASH_DISCRIMINATORS);
38274 }
38275
38276 #[test]
38277 #[should_panic(expected = "assert_u8_array_pairwise_distinct")]
38278 fn assert_u8_array_pairwise_distinct_panics_at_runtime_on_binary_collision() {
38279 // NEGATIVE PIN — binary corner: a two-element array carrying
38280 // the same byte twice MUST panic at runtime (the const-eval
38281 // panic surfaces normally when the function is invoked from a
38282 // runtime context, not just a `const _` context). Pins the
38283 // helper's OWN reject-collision arm — a regression that
38284 // silently returned without panicking on a duplicate would
38285 // slip through the compile-time witnesses' failure mode too.
38286 assert_u8_array_pairwise_distinct(&[0u8, 0u8]);
38287 }
38288
38289 #[test]
38290 #[should_panic(expected = "assert_u8_array_pairwise_distinct")]
38291 fn assert_u8_array_pairwise_distinct_panics_at_runtime_on_non_adjacent_collision() {
38292 // NEGATIVE PIN — non-adjacent corner: the collision fires on
38293 // ANY (i, j) pair with i < j, not just the adjacent (0, 1)
38294 // corner. Pins the nested-loop shape of the helper — a
38295 // regression that walked ONLY the adjacent pairs (i.e., swept
38296 // `while i + 1 < N { if arr[i] == arr[i+1] { panic } … }`)
38297 // would silently accept `[0, 1, 0]` (non-adjacent collision
38298 // at positions 0 and 2), missing the contract.
38299 assert_u8_array_pairwise_distinct(&[0u8, 1u8, 0u8]);
38300 }
38301
38302 #[test]
38303 #[should_panic(expected = "assert_u8_array_pairwise_distinct")]
38304 fn assert_u8_array_pairwise_distinct_panics_at_runtime_on_terminal_collision() {
38305 // NEGATIVE PIN — terminal corner: the collision at the LAST
38306 // pair (positions N-2 and N-1) MUST also fire. Pins the outer
38307 // `while i < N` bound — a regression that walked `while i <
38308 // N - 1` (dropping the last row) would silently accept a
38309 // collision at the tail.
38310 assert_u8_array_pairwise_distinct(&[0u8, 1u8, 2u8, 3u8, 3u8]);
38311 }
38312
38313 #[test]
38314 #[should_panic(expected = "assert_u8_array_pairwise_distinct")]
38315 fn assert_u8_array_pairwise_distinct_panics_at_runtime_on_boundary_byte_collision() {
38316 // NEGATIVE PIN — u8-domain boundary corner: the reject-arm
38317 // fires equivalently at the two u8-domain endpoints (`0x00`,
38318 // `0xff`). Pins the element-type-native `==` on `u8` — a
38319 // regression that widened the comparison via `as u32` (from
38320 // an over-mechanical copy of the char sibling's shape) or
38321 // via a signed-cast (`as i8` collapsing `0xff` to `-1`)
38322 // would still fire on the `0x00` corner AND on the
38323 // `0xff` corner because both survive any widening; a truly
38324 // load-bearing regression here would silently drop the
38325 // comparison altogether.
38326 assert_u8_array_pairwise_distinct(&[0xffu8, 0xffu8]);
38327 }
38328
38329 #[test]
38330 fn assert_u8_array_pairwise_distinct_panic_message_names_the_helper() {
38331 // PANIC-MESSAGE PROVENANCE PIN: the panic message MUST begin
38332 // with the helper's own name so downstream diagnostics
38333 // (`cargo check` const-eval error output, test-suite failure
38334 // reports) route the drift back to the helper by string
38335 // search — the family-wide contract's failure mode surfaces
38336 // as an identifiable panic-message prefix rather than as an
38337 // opaque const-eval error. Sibling posture to the runtime
38338 // pairwise-distinctness tests that name the ARRAY in their
38339 // failure message; this pin names the HELPER.
38340 let outcome = std::panic::catch_unwind(|| {
38341 assert_u8_array_pairwise_distinct(&[7u8, 7u8]);
38342 });
38343 let payload = outcome.expect_err(
38344 "assert_u8_array_pairwise_distinct must panic on a \
38345 duplicate — the reject-collision arm is the point of \
38346 the helper",
38347 );
38348 let msg = payload
38349 .downcast_ref::<&'static str>()
38350 .map(|s| (*s).to_owned())
38351 .or_else(|| payload.downcast_ref::<String>().cloned())
38352 .expect(
38353 "assert_u8_array_pairwise_distinct panic payload \
38354 must be a static &str or String",
38355 );
38356 assert!(
38357 msg.contains("assert_u8_array_pairwise_distinct"),
38358 "assert_u8_array_pairwise_distinct panic message \
38359 {msg:?} must name the helper for provenance-preserving \
38360 failure diagnostics",
38361 );
38362 }
38363
38364 // ── `assert_char_pair_array_bijective` — the product-element
38365 // sibling of the three scalar-element `assert_{char,str,u8}_array_
38366 // pairwise_distinct` compile-time contract verifiers, restricted
38367 // to the `(char, char)` product element type at the paired
38368 // escape-table substrate vocabulary (`Atom::NAMED_ESCAPE_TABLE`,
38369 // `Atom::ESCAPE_TABLE`). The runtime test surface here matches
38370 // the scalar-sibling shape (accept-empty, accept-singleton,
38371 // accept-every-family-wide-substrate-array, reject-left-column-
38372 // collision, reject-right-column-collision, reject-non-adjacent,
38373 // reject-terminal, panic-message-provenance-left, panic-message-
38374 // provenance-right) split across BOTH columns so a regression that
38375 // silently weakens the helper on EITHER column (e.g. dropping ONE
38376 // of the two `if arr[i].{0,1} == arr[j].{0,1}` checks, or
38377 // conflating the two `panic!` calls into one column-anonymous
38378 // message) is caught by the helper's OWN test surface rather than
38379 // only surfacing as a false-positive on some future
38380 // `[(char, char); N]`-typed paired array's bijection pin.
38381
38382 #[test]
38383 fn assert_char_pair_array_bijective_accepts_the_empty_array() {
38384 // Empty array — vacuously bijective (no pair to collide on
38385 // EITHER column). The compile-time `const _: () =
38386 // assert_char_pair_array_bijective(&EMPTY);` would land on
38387 // this arm, so the runtime call MUST return normally.
38388 assert_char_pair_array_bijective::<0>(&[]);
38389 }
38390
38391 #[test]
38392 fn assert_char_pair_array_bijective_accepts_singleton_arrays() {
38393 // Singleton array — vacuously bijective (only one pair, so
38394 // no cross-pair collision is possible on EITHER column even
38395 // if the two components alias each other WITHIN the pair —
38396 // that's not a bijection failure, it's the pattern-equals-
38397 // value SELF-escape shape at `SELF_ESCAPE_TABLE`'s two rows).
38398 // Cross-arity coverage on the `[(char, char); 1]` corner of
38399 // the const-N generic.
38400 assert_char_pair_array_bijective(&[('a', 'b')]);
38401 assert_char_pair_array_bijective(&[('x', 'x')]);
38402 }
38403
38404 #[test]
38405 fn assert_char_pair_array_bijective_accepts_every_family_wide_substrate_array() {
38406 // Runtime cross-check that the SAME two paired arrays the
38407 // module-level `const _: () = ...` witnesses cover at COMPILE
38408 // time are bijective. A regression that removes ONE of the
38409 // `const _` witnesses would still leave THIS runtime pin as a
38410 // safety net; the const witness fires FIRST at `cargo check`,
38411 // this runtime pin catches the collision at `cargo test`. The
38412 // pair enforces the theorem at TWO stages of the toolchain.
38413 assert_char_pair_array_bijective(&Atom::NAMED_ESCAPE_TABLE);
38414 assert_char_pair_array_bijective(&Atom::ESCAPE_TABLE);
38415 }
38416
38417 #[test]
38418 #[should_panic(expected = "assert_char_pair_array_bijective: LEFT column")]
38419 fn assert_char_pair_array_bijective_panics_at_runtime_on_left_column_collision() {
38420 // NEGATIVE PIN — LEFT-column corner: a two-element array
38421 // whose two SOURCE chars alias (regardless of whether the two
38422 // DECODED chars alias) MUST panic at runtime with the LEFT-
38423 // column-named message. Pins the helper's OWN LEFT-column
38424 // reject-arm — a regression that silently returned without
38425 // panicking on a source-column duplicate would slip through
38426 // the compile-time witnesses' failure mode too. The two
38427 // distinct DECODED chars witness that the RIGHT column is
38428 // INTACT — the panic MUST fire specifically on the LEFT-
38429 // column disjointness failure.
38430 assert_char_pair_array_bijective(&[('a', 'x'), ('a', 'y')]);
38431 }
38432
38433 #[test]
38434 #[should_panic(expected = "assert_char_pair_array_bijective: RIGHT column")]
38435 fn assert_char_pair_array_bijective_panics_at_runtime_on_right_column_collision() {
38436 // NEGATIVE PIN — RIGHT-column corner: a two-element array
38437 // whose two DECODED chars alias (with distinct SOURCE chars
38438 // so the LEFT column is intact) MUST panic at runtime with
38439 // the RIGHT-column-named message. Column-provenance in the
38440 // panic message is load-bearing: downstream diagnostics
38441 // route the drift back to the failed COLUMN (LEFT vs.
38442 // RIGHT) by string search — the two panic sites MUST NOT
38443 // collapse into one column-anonymous message.
38444 assert_char_pair_array_bijective(&[('a', 'x'), ('b', 'x')]);
38445 }
38446
38447 #[test]
38448 #[should_panic(expected = "assert_char_pair_array_bijective")]
38449 fn assert_char_pair_array_bijective_panics_at_runtime_on_non_adjacent_collision() {
38450 // NEGATIVE PIN — non-adjacent corner: the collision fires on
38451 // ANY (i, j) pair with i < j, not just the adjacent (0, 1)
38452 // corner. Pins the nested-loop shape of the helper — a
38453 // regression that walked ONLY the adjacent pairs (i.e., swept
38454 // `while i + 1 < N { if arr[i].0 == arr[i+1].0 { panic } … }`)
38455 // would silently accept the non-adjacent collision at
38456 // positions 0 and 2 tested here (a LEFT-column collision
38457 // `'a'` at [0].0 and [2].0, with `'b'` at [1].0 breaking
38458 // adjacency).
38459 assert_char_pair_array_bijective(&[('a', 'x'), ('b', 'y'), ('a', 'z')]);
38460 }
38461
38462 #[test]
38463 #[should_panic(expected = "assert_char_pair_array_bijective")]
38464 fn assert_char_pair_array_bijective_panics_at_runtime_on_terminal_collision() {
38465 // NEGATIVE PIN — terminal corner: the collision at the LAST
38466 // pair (positions N-2 and N-1) MUST also fire. Pins the outer
38467 // `while i < N` bound — a regression that walked `while i <
38468 // N - 1` (dropping the last row) would silently accept a
38469 // collision at the tail. Uses a RIGHT-column collision at
38470 // the terminal pair to also exercise the second (RIGHT)
38471 // panic arm's terminal-index reachability, symmetric to the
38472 // LEFT-column non-adjacent pin above.
38473 assert_char_pair_array_bijective(&[
38474 ('a', 'w'),
38475 ('b', 'x'),
38476 ('c', 'y'),
38477 ('d', 'z'),
38478 ('e', 'z'),
38479 ]);
38480 }
38481
38482 #[test]
38483 fn assert_char_pair_array_bijective_panic_message_names_the_helper_and_left_column() {
38484 // PANIC-MESSAGE PROVENANCE PIN — LEFT-column arm: the panic
38485 // message MUST begin with the helper's own name AND identify
38486 // the failed COLUMN as "LEFT column" so downstream diagnostics
38487 // route the drift back to (a) the helper by string search on
38488 // `"assert_char_pair_array_bijective"` and (b) the column by
38489 // string search on `"LEFT column"`. Sibling posture to the
38490 // scalar-sibling `_panic_message_names_the_helper` tests
38491 // (which name only the helper); the column-provenance
38492 // extension is load-bearing on the paired-array vocabulary
38493 // where a bijection failure has TWO distinguishable failure
38494 // modes (source-column non-injective vs. decoded-column
38495 // non-injective).
38496 let outcome = std::panic::catch_unwind(|| {
38497 assert_char_pair_array_bijective(&[('a', 'x'), ('a', 'y')]);
38498 });
38499 let payload = outcome.expect_err(
38500 "assert_char_pair_array_bijective must panic on a LEFT-\
38501 column duplicate — the reject-collision arm is the \
38502 point of the helper",
38503 );
38504 let msg = payload
38505 .downcast_ref::<&'static str>()
38506 .map(|s| (*s).to_owned())
38507 .or_else(|| payload.downcast_ref::<String>().cloned())
38508 .expect(
38509 "assert_char_pair_array_bijective panic payload \
38510 must be a static &str or String",
38511 );
38512 assert!(
38513 msg.contains("assert_char_pair_array_bijective"),
38514 "assert_char_pair_array_bijective LEFT-column panic \
38515 message {msg:?} must name the helper for provenance-\
38516 preserving failure diagnostics",
38517 );
38518 assert!(
38519 msg.contains("LEFT column"),
38520 "assert_char_pair_array_bijective LEFT-column panic \
38521 message {msg:?} must name the failed COLUMN (\"LEFT \
38522 column\") for column-provenance-preserving failure \
38523 diagnostics",
38524 );
38525 }
38526
38527 #[test]
38528 fn assert_char_pair_array_bijective_panic_message_names_the_helper_and_right_column() {
38529 // PANIC-MESSAGE PROVENANCE PIN — RIGHT-column arm: the panic
38530 // message MUST begin with the helper's own name AND identify
38531 // the failed COLUMN as "RIGHT column". Column-symmetric
38532 // sibling of the LEFT-column pin above — a regression that
38533 // silently unified the two panic sites into ONE column-
38534 // anonymous message would collapse THIS pin's RIGHT-column
38535 // substring assertion.
38536 let outcome = std::panic::catch_unwind(|| {
38537 assert_char_pair_array_bijective(&[('a', 'x'), ('b', 'x')]);
38538 });
38539 let payload = outcome.expect_err(
38540 "assert_char_pair_array_bijective must panic on a RIGHT-\
38541 column duplicate — the reject-collision arm is the \
38542 point of the helper",
38543 );
38544 let msg = payload
38545 .downcast_ref::<&'static str>()
38546 .map(|s| (*s).to_owned())
38547 .or_else(|| payload.downcast_ref::<String>().cloned())
38548 .expect(
38549 "assert_char_pair_array_bijective panic payload \
38550 must be a static &str or String",
38551 );
38552 assert!(
38553 msg.contains("assert_char_pair_array_bijective"),
38554 "assert_char_pair_array_bijective RIGHT-column panic \
38555 message {msg:?} must name the helper for provenance-\
38556 preserving failure diagnostics",
38557 );
38558 assert!(
38559 msg.contains("RIGHT column"),
38560 "assert_char_pair_array_bijective RIGHT-column panic \
38561 message {msg:?} must name the failed COLUMN (\"RIGHT \
38562 column\") for column-provenance-preserving failure \
38563 diagnostics",
38564 );
38565 }
38566
38567 // ── `assert_char_pair_array_pairwise_distinct` — the (char, char)
38568 // product-element TUPLE-level pairwise-distinctness verifier that
38569 // binds `∀ i < j. arr[i] ≠ arr[j]` at compile time on the paired-
38570 // array vocabulary via CONJOINED-tuple equality, WEAKER peer of
38571 // `assert_char_pair_array_bijective`'s column-INDEPENDENT
38572 // INJECTIVITY axis on the SAME (char, char) row of the
38573 // (element-type × contract-shape) matrix. Both bijective ⇒
38574 // pairwise-distinct-as-tuples witnesses hold on the substrate's
38575 // two family-wide `[(char, char); N]` arrays (NAMED_ESCAPE_TABLE
38576 // + ESCAPE_TABLE), so the runtime test surface pins ONLY the
38577 // helper's OWN reject-arm (tuple-collision at three (adjacent,
38578 // non-adjacent, terminal) positions) + accept-arms (empty,
38579 // singletons, per-column-alias-with-distinct-tuples, family-wide
38580 // substrate arrays) + panic-message-provenance on the CHAR-PAIR-
38581 // TUPLE-COLLISION axis so a regression that silently weakened
38582 // the helper on ANY arm is caught by the helper's OWN test
38583 // surface rather than only surfacing as a false-positive on
38584 // some future tuple-level pairwise-distinct-but-NOT-bijective
38585 // `[(char, char); N]` array's compound pin.
38586
38587 #[test]
38588 fn assert_char_pair_array_pairwise_distinct_accepts_the_empty_array() {
38589 // Empty array `arr = []` at the `[(char, char); 0]` corner —
38590 // vacuously pairwise-distinct at the tuple level (no (i, j)
38591 // pair with i < j exists to test). Pins the outer `while i <
38592 // N` guard's short-circuit on `N == 0` — a regression that
38593 // panicked on the empty array would collapse this pin.
38594 assert_char_pair_array_pairwise_distinct::<0>(&[]);
38595 }
38596
38597 #[test]
38598 fn assert_char_pair_array_pairwise_distinct_accepts_a_singleton_array() {
38599 // Singleton array `arr = [(a, b)]` — vacuously pairwise-
38600 // distinct at the tuple level (no `j > i == 0` position
38601 // exists to compare against). Cross-corner coverage on the
38602 // trivial-array face of the const-N generic past the empty
38603 // corner, closing the two edge cases (N == 0, N == 1) at
38604 // which the inner sweep is vacuous.
38605 assert_char_pair_array_pairwise_distinct(&[('a', 'x')]);
38606 }
38607
38608 #[test]
38609 fn assert_char_pair_array_pairwise_distinct_accepts_two_distinct_tuples() {
38610 // Two-element array with two BYTE-FOR-BYTE-DISTINCT tuples —
38611 // the smallest non-trivial well-formed case. Both column
38612 // projections happen to be distinct too (`'a' ≠ 'b'` on the
38613 // LEFT column, `'x' ≠ 'y'` on the RIGHT column) but the
38614 // helper reads ONLY the CONJOINED-tuple gate, not the per-
38615 // column gates — pinned in isolation from the sibling
38616 // bijective helper's stricter column-independent axis.
38617 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('b', 'y')]);
38618 }
38619
38620 #[test]
38621 fn assert_char_pair_array_pairwise_distinct_accepts_per_column_alias_when_conjoined_tuple_differs(
38622 ) {
38623 // Accept-corner load-bearing to the helper's DISTINCTION from
38624 // the sibling `assert_char_pair_array_bijective`: a pair-
38625 // array with LEFT-column collision (`'a'` at [0].0 and
38626 // [1].0) but with DISTINCT RIGHT columns (`'x' ≠ 'y'`) has
38627 // TWO CONJOINED-tuples `('a', 'x') ≠ ('a', 'y')` that DIFFER
38628 // — this helper MUST accept it. The bijective sibling would
38629 // REJECT this pair on its LEFT-column arm because
38630 // bijectivity is per-column-INDEPENDENT. The two helpers'
38631 // divergence here is THE point of opening the tuple-level
38632 // INJECTIVITY axis as a distinct sub-shape past the column-
38633 // INDEPENDENT INJECTIVITY axis.
38634 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('a', 'y')]);
38635 // Symmetric RIGHT-column-alias corner with distinct LEFT
38636 // columns — same accept posture on the OTHER column-alias
38637 // sibling.
38638 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('b', 'x')]);
38639 }
38640
38641 #[test]
38642 fn assert_char_pair_array_pairwise_distinct_accepts_the_family_wide_substrate_arrays() {
38643 // Positive pin on the TWO family-wide `[(char, char); N]`
38644 // paired arrays this helper is applied to at compile time
38645 // via the module-level `const _:` witnesses: `NAMED_ESCAPE_
38646 // TABLE` (`[(char, char); 3]`) and `ESCAPE_TABLE` (`[(char,
38647 // char); 5]`). Both bijective ⇒ pairwise-distinct-as-tuples
38648 // WITNESSES the compile-time `const _:` pass at runtime for
38649 // a second-stage safety net if the const-eval sweep is ever
38650 // silently dropped. Sibling posture to
38651 // `assert_char_pair_array_bijective`'s
38652 // `_accepts_the_family_wide_substrate_bijections` on the
38653 // SAME two arrays — this pin binds the WEAKER tuple-level
38654 // axis; that pin binds the STRONGER column-independent axis.
38655 assert_char_pair_array_pairwise_distinct(&Atom::NAMED_ESCAPE_TABLE);
38656 assert_char_pair_array_pairwise_distinct(&Atom::ESCAPE_TABLE);
38657 }
38658
38659 #[test]
38660 #[should_panic(expected = "assert_char_pair_array_pairwise_distinct")]
38661 fn assert_char_pair_array_pairwise_distinct_panics_at_runtime_on_adjacent_tuple_collision() {
38662 // NEGATIVE PIN — adjacent (0, 1) corner: two adjacent
38663 // CONJOINED-tuples byte-for-byte equal (BOTH columns match
38664 // in lockstep) MUST panic at runtime. Pins the helper's OWN
38665 // reject-arm — a regression that silently returned without
38666 // panicking on a tuple duplicate would slip through the
38667 // compile-time witnesses' failure mode too.
38668 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('a', 'x')]);
38669 }
38670
38671 #[test]
38672 #[should_panic(expected = "assert_char_pair_array_pairwise_distinct")]
38673 fn assert_char_pair_array_pairwise_distinct_panics_at_runtime_on_non_adjacent_tuple_collision()
38674 {
38675 // NEGATIVE PIN — non-adjacent (0, 2) corner: the collision
38676 // fires on ANY (i, j) pair with i < j, not just the
38677 // adjacent (0, 1) corner. Pins the nested-loop shape of the
38678 // helper — a regression that walked ONLY the adjacent pairs
38679 // (i.e., swept `while i + 1 < N { if arr[i] == arr[i+1] {
38680 // panic } … }`) would silently accept the non-adjacent
38681 // collision at positions 0 and 2 tested here (a tuple
38682 // collision at ('a', 'x') across [0] and [2], with the
38683 // distinct ('b', 'y') at [1] breaking adjacency). Sibling
38684 // posture to `assert_char_pair_array_bijective_panics_at_
38685 // runtime_on_non_adjacent_collision` on the column-
38686 // independent axis — the loop-shape pin is row-parallel.
38687 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('b', 'y'), ('a', 'x')]);
38688 }
38689
38690 #[test]
38691 #[should_panic(expected = "assert_char_pair_array_pairwise_distinct")]
38692 fn assert_char_pair_array_pairwise_distinct_panics_at_runtime_on_terminal_tuple_collision() {
38693 // NEGATIVE PIN — terminal corner: the collision at the LAST
38694 // pair (positions N-2 and N-1) MUST also fire. Pins the
38695 // outer `while i < N` bound — a regression that walked
38696 // `while i < N - 1` (dropping the last row) would silently
38697 // accept a collision at the tail. Uses a tuple collision at
38698 // the terminal pair (`('e', 'z')` at [3] and [4]).
38699 assert_char_pair_array_pairwise_distinct(&[
38700 ('a', 'w'),
38701 ('b', 'x'),
38702 ('c', 'y'),
38703 ('e', 'z'),
38704 ('e', 'z'),
38705 ]);
38706 }
38707
38708 #[test]
38709 fn assert_char_pair_array_pairwise_distinct_panic_message_names_the_helper_and_char_pair_tuple_collision_axis(
38710 ) {
38711 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-TUPLE-COLLISION
38712 // arm: the panic message MUST begin with the helper's own
38713 // name AND identify the failed AXIS as "CHAR-PAIR-TUPLE-
38714 // COLLISION" so downstream diagnostics route the drift back
38715 // to (a) the helper by string search on
38716 // `"assert_char_pair_array_pairwise_distinct"` and (b) the
38717 // axis by string search on `"CHAR-PAIR-TUPLE-COLLISION"`.
38718 // The axis-provenance string `"CHAR-PAIR-TUPLE-COLLISION"`
38719 // is chosen DISTINCT from EVERY sibling helper's axis
38720 // vocabulary (`"LEFT column"` / `"RIGHT column"` on the
38721 // paired-array BIJECTIVITY sibling; `"CHAR-PAIR-SUBSET-
38722 // VIOLATION"` on the paired-array SUBSET-embedding sibling;
38723 // `"CHAR-PAIR-DISJOINTNESS-VIOLATION"` on the paired-array
38724 // DISJOINTNESS sibling) so a diagnostic that names the
38725 // failed axis routes UNAMBIGUOUSLY to (a) this specific
38726 // paired-array TUPLE-LEVEL PAIRWISE-DISTINCTNESS helper,
38727 // (b) the failed axis-shape by the `"TUPLE-COLLISION"`
38728 // suffix stem distinguishing the CONJOINED-tuple axis from
38729 // the column-INDEPENDENT axis names used by the bijective
38730 // sibling.
38731 let outcome = std::panic::catch_unwind(|| {
38732 assert_char_pair_array_pairwise_distinct(&[('a', 'x'), ('a', 'x')]);
38733 });
38734 let payload = outcome.expect_err(
38735 "assert_char_pair_array_pairwise_distinct must panic on \
38736 a CONJOINED-tuple duplicate — the reject-collision arm \
38737 is the sole CHAR-PAIR-TUPLE-COLLISION failure mode of \
38738 the helper",
38739 );
38740 let msg = payload
38741 .downcast_ref::<&'static str>()
38742 .map(|s| (*s).to_owned())
38743 .or_else(|| payload.downcast_ref::<String>().cloned())
38744 .expect(
38745 "assert_char_pair_array_pairwise_distinct panic \
38746 payload must be a static &str or String",
38747 );
38748 assert!(
38749 msg.contains("assert_char_pair_array_pairwise_distinct"),
38750 "assert_char_pair_array_pairwise_distinct panic message \
38751 {msg:?} must name the helper for provenance-preserving \
38752 failure diagnostics",
38753 );
38754 assert!(
38755 msg.contains("CHAR-PAIR-TUPLE-COLLISION"),
38756 "assert_char_pair_array_pairwise_distinct panic message \
38757 {msg:?} must name the failed AXIS (\"CHAR-PAIR-TUPLE-\
38758 COLLISION\") for axis-provenance-preserving failure \
38759 diagnostics",
38760 );
38761 }
38762
38763 // ── `assert_char_pair_array_columns_equal_char_arrays` — the
38764 // `(char, char)` product-element COLUMN-PROJECTION-EQUALITY
38765 // verifier that binds a JOINT (LEFT_col == left) ∧ (RIGHT_col ==
38766 // right) POSITIONWISE-EQUALITY contract at compile time on the
38767 // (paired-array, peer-scalar-LEFT, peer-scalar-RIGHT) three-way
38768 // column bond. Opens the (column-projection-equality) column on
38769 // the (char, char) row of the (element-type × contract-shape)
38770 // matrix past the pre-existing (INJECTIVITY, SUBSET-EMBEDDING,
38771 // DISJOINTNESS) triple. Runtime test surface pins each of the
38772 // helper's arms (accept-empty, accept-singleton-equal, accept-
38773 // multi-element-equal, accept-family-wide-substrate-triple on
38774 // the pinned (`ESCAPE_TABLE`, `ESCAPE_SOURCES`, `ESCAPE_DECODED`)
38775 // triple, reject-LEFT-divergence at head / middle / tail, reject-
38776 // RIGHT-divergence at head / middle / tail, panic-message-
38777 // provenance on BOTH the LEFT-COLUMN-DIVERGENCE and RIGHT-
38778 // COLUMN-DIVERGENCE axes) so a regression that silently weakened
38779 // the helper on ANY arm is caught by the helper's OWN test
38780 // surface rather than only surfacing as a false-positive on some
38781 // future column-bonded `[(char, char); N]` array's compound pin.
38782
38783 #[test]
38784 fn assert_char_pair_array_columns_equal_char_arrays_accepts_the_empty_triple() {
38785 // Empty arrays `pairs = []`, `left = []`, `right = []` at
38786 // the `[(char, char); 0]` + two `[char; 0]` corner —
38787 // vacuously column-equal (no `i` position exists to test).
38788 // Pins the outer `while i < N` guard's short-circuit on
38789 // `N == 0`. Turbofish binding required because there's no
38790 // other cue for the const parameter on the three empty
38791 // array literals.
38792 assert_char_pair_array_columns_equal_char_arrays::<0>(&[], &[], &[]);
38793 }
38794
38795 #[test]
38796 fn assert_char_pair_array_columns_equal_char_arrays_accepts_a_singleton_triple() {
38797 // Singleton triple `pairs = [('a', 'x')]`, `left = ['a']`,
38798 // `right = ['x']` — the smallest non-trivial well-formed
38799 // case. Both column-projections match position-for-position.
38800 assert_char_pair_array_columns_equal_char_arrays(&[('a', 'x')], &['a'], &['x']);
38801 }
38802
38803 #[test]
38804 fn assert_char_pair_array_columns_equal_char_arrays_accepts_a_multi_element_triple() {
38805 // Three-element triple with three BYTE-FOR-BYTE-EQUAL
38806 // column projections — pins the inner loop's `i += 1`
38807 // advancement and the sweep's coverage of BOTH clauses at
38808 // EACH position (a regression that returned early after
38809 // position 0 would silently accept a mismatch at position
38810 // 1 or 2).
38811 assert_char_pair_array_columns_equal_char_arrays(
38812 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38813 &['a', 'b', 'c'],
38814 &['x', 'y', 'z'],
38815 );
38816 }
38817
38818 #[test]
38819 fn assert_char_pair_array_columns_equal_char_arrays_accepts_the_family_wide_substrate_triple() {
38820 // Positive pin on the family-wide (`ESCAPE_TABLE`,
38821 // `ESCAPE_SOURCES`, `ESCAPE_DECODED`) triple this helper is
38822 // applied to at compile time via the module-level `const _:`
38823 // witness. Runtime pin as a second-stage safety net if the
38824 // const-eval sweep is ever silently dropped. Sibling posture
38825 // to the `_bijective` + `_pairwise_distinct` family-wide pins
38826 // on the same paired array — the three pins bind
38827 // complementary axes of the same substrate table.
38828 assert_char_pair_array_columns_equal_char_arrays(
38829 &Atom::ESCAPE_TABLE,
38830 &Atom::ESCAPE_SOURCES,
38831 &Atom::ESCAPE_DECODED,
38832 );
38833 }
38834
38835 #[test]
38836 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38837 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_left_head_divergence()
38838 {
38839 // NEGATIVE PIN — LEFT-column head-position (i == 0) corner:
38840 // `pairs[0].0 = 'a'` diverges from `left[0] = 'z'` on the
38841 // FIRST position. Pins the LEFT arm firing at the head — a
38842 // regression that started the sweep at `i = 1` would
38843 // silently accept a head-position divergence.
38844 assert_char_pair_array_columns_equal_char_arrays(
38845 &[('a', 'x'), ('b', 'y')],
38846 &['z', 'b'],
38847 &['x', 'y'],
38848 );
38849 }
38850
38851 #[test]
38852 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38853 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_left_middle_divergence(
38854 ) {
38855 // NEGATIVE PIN — LEFT-column middle-position (i == 1)
38856 // corner: divergence fires on ANY interior position, not
38857 // just the head. Pins the loop-shape of the LEFT arm — a
38858 // regression that walked ONLY the head would silently
38859 // accept the middle-position divergence at position 1.
38860 assert_char_pair_array_columns_equal_char_arrays(
38861 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38862 &['a', 'q', 'c'],
38863 &['x', 'y', 'z'],
38864 );
38865 }
38866
38867 #[test]
38868 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38869 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_left_tail_divergence()
38870 {
38871 // NEGATIVE PIN — LEFT-column tail-position (i == N-1)
38872 // corner: divergence at the LAST position MUST also fire.
38873 // Pins the outer `while i < N` bound — a regression that
38874 // walked `while i < N - 1` (dropping the last row) would
38875 // silently accept a tail LEFT-column divergence.
38876 assert_char_pair_array_columns_equal_char_arrays(
38877 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38878 &['a', 'b', 'q'],
38879 &['x', 'y', 'z'],
38880 );
38881 }
38882
38883 #[test]
38884 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38885 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_right_head_divergence()
38886 {
38887 // NEGATIVE PIN — RIGHT-column head-position (i == 0) corner:
38888 // symmetric to the LEFT arm at the head — pins the RIGHT
38889 // arm firing at position 0 with the LEFT arm satisfied.
38890 assert_char_pair_array_columns_equal_char_arrays(
38891 &[('a', 'x'), ('b', 'y')],
38892 &['a', 'b'],
38893 &['q', 'y'],
38894 );
38895 }
38896
38897 #[test]
38898 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38899 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_right_middle_divergence(
38900 ) {
38901 // NEGATIVE PIN — RIGHT-column middle-position (i == 1)
38902 // corner: symmetric to the LEFT-middle arm — pins the
38903 // RIGHT arm firing at an interior position with the LEFT
38904 // arm satisfied at every position.
38905 assert_char_pair_array_columns_equal_char_arrays(
38906 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38907 &['a', 'b', 'c'],
38908 &['x', 'q', 'z'],
38909 );
38910 }
38911
38912 #[test]
38913 #[should_panic(expected = "assert_char_pair_array_columns_equal_char_arrays")]
38914 fn assert_char_pair_array_columns_equal_char_arrays_panics_at_runtime_on_right_tail_divergence()
38915 {
38916 // NEGATIVE PIN — RIGHT-column tail-position (i == N-1)
38917 // corner: symmetric to the LEFT-tail arm — pins the outer
38918 // sweep bound on the RIGHT arm with the LEFT arm satisfied.
38919 assert_char_pair_array_columns_equal_char_arrays(
38920 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
38921 &['a', 'b', 'c'],
38922 &['x', 'y', 'q'],
38923 );
38924 }
38925
38926 #[test]
38927 fn assert_char_pair_array_columns_equal_char_arrays_panic_message_names_the_helper_and_left_column_divergence_axis(
38928 ) {
38929 // PANIC-MESSAGE PROVENANCE PIN — LEFT-COLUMN-DIVERGENCE
38930 // arm: the panic message MUST begin with the helper's own
38931 // name AND identify the failed AXIS as "LEFT-COLUMN-
38932 // DIVERGENCE" so downstream diagnostics route the drift
38933 // back to (a) the helper by string search on
38934 // `"assert_char_pair_array_columns_equal_char_arrays"` and
38935 // (b) the axis by string search on
38936 // `"LEFT-COLUMN-DIVERGENCE"`. The axis-provenance string
38937 // is chosen DISTINCT from every sibling helper's axis
38938 // vocabulary — a diagnostic that names the failed axis
38939 // routes UNAMBIGUOUSLY to this specific paired-array
38940 // COLUMN-PROJECTION-EQUALITY helper's LEFT arm.
38941 let outcome = std::panic::catch_unwind(|| {
38942 assert_char_pair_array_columns_equal_char_arrays(
38943 &[('a', 'x'), ('b', 'y')],
38944 &['z', 'b'],
38945 &['x', 'y'],
38946 );
38947 });
38948 let payload = outcome.expect_err(
38949 "assert_char_pair_array_columns_equal_char_arrays must \
38950 panic on a LEFT-column POSITIONWISE-EQUALITY divergence",
38951 );
38952 let msg = payload
38953 .downcast_ref::<&'static str>()
38954 .map(|s| (*s).to_owned())
38955 .or_else(|| payload.downcast_ref::<String>().cloned())
38956 .expect(
38957 "assert_char_pair_array_columns_equal_char_arrays \
38958 panic payload must be a static &str or String",
38959 );
38960 assert!(
38961 msg.contains("assert_char_pair_array_columns_equal_char_arrays"),
38962 "assert_char_pair_array_columns_equal_char_arrays panic \
38963 message {msg:?} must name the helper for provenance-\
38964 preserving failure diagnostics",
38965 );
38966 assert!(
38967 msg.contains("LEFT-COLUMN-DIVERGENCE"),
38968 "assert_char_pair_array_columns_equal_char_arrays panic \
38969 message {msg:?} must name the failed AXIS (\"LEFT-\
38970 COLUMN-DIVERGENCE\") for axis-provenance-preserving \
38971 failure diagnostics",
38972 );
38973 }
38974
38975 #[test]
38976 fn assert_char_pair_array_columns_equal_char_arrays_panic_message_names_the_helper_and_right_column_divergence_axis(
38977 ) {
38978 // PANIC-MESSAGE PROVENANCE PIN — RIGHT-COLUMN-DIVERGENCE
38979 // arm: symmetric to the LEFT-column-divergence provenance
38980 // pin. The panic message MUST begin with the helper's own
38981 // name AND identify the failed AXIS as "RIGHT-COLUMN-
38982 // DIVERGENCE" — the two axis-provenance strings
38983 // (`"LEFT-COLUMN-DIVERGENCE"` + `"RIGHT-COLUMN-DIVERGENCE"`)
38984 // partition the helper's failure modes into TWO disjoint
38985 // arms so a diagnostic that names the axis routes
38986 // UNAMBIGUOUSLY to the specific COLUMN that diverged.
38987 let outcome = std::panic::catch_unwind(|| {
38988 assert_char_pair_array_columns_equal_char_arrays(
38989 &[('a', 'x'), ('b', 'y')],
38990 &['a', 'b'],
38991 &['q', 'y'],
38992 );
38993 });
38994 let payload = outcome.expect_err(
38995 "assert_char_pair_array_columns_equal_char_arrays must \
38996 panic on a RIGHT-column POSITIONWISE-EQUALITY \
38997 divergence",
38998 );
38999 let msg = payload
39000 .downcast_ref::<&'static str>()
39001 .map(|s| (*s).to_owned())
39002 .or_else(|| payload.downcast_ref::<String>().cloned())
39003 .expect(
39004 "assert_char_pair_array_columns_equal_char_arrays \
39005 panic payload must be a static &str or String",
39006 );
39007 assert!(
39008 msg.contains("assert_char_pair_array_columns_equal_char_arrays"),
39009 "assert_char_pair_array_columns_equal_char_arrays panic \
39010 message {msg:?} must name the helper for provenance-\
39011 preserving failure diagnostics",
39012 );
39013 assert!(
39014 msg.contains("RIGHT-COLUMN-DIVERGENCE"),
39015 "assert_char_pair_array_columns_equal_char_arrays panic \
39016 message {msg:?} must name the failed AXIS (\"RIGHT-\
39017 COLUMN-DIVERGENCE\") for axis-provenance-preserving \
39018 failure diagnostics",
39019 );
39020 }
39021
39022 // ── `assert_char_pair_array_columns_cross_disjoint` — the
39023 // `(char, char)` product-element WITHIN-ARRAY CROSS-COLUMN
39024 // disjointness verifier that binds `LEFT column ∩ RIGHT column
39025 // = ∅` at compile time on a pattern-DISTINCT-from-value paired-
39026 // array vocabulary. Peer to the four prior paired-array verifiers
39027 // on the SAME row (`_pairwise_distinct` — CONJOINED-tuple
39028 // INJECTIVITY; `_bijective` — per-column INJECTIVITY;
39029 // `_within_char_pair_finite_set` — SUBSET-EMBEDDING;
39030 // `_arrays_disjoint` — BETWEEN-ARRAY CONJOINED-tuple
39031 // DISJOINTNESS): where the four siblings close within-tuple /
39032 // within-column / between-array axes, this helper closes the
39033 // WITHIN-ARRAY CROSS-COLUMN-CHARACTER axis. The runtime test
39034 // surface pins each of the helper's arms (accept-empty-array,
39035 // accept-singleton-when-left-disjoint-from-right, accept-multi-
39036 // element-when-cross-disjoint, accept-substrate-family-wide-
39037 // NAMED_ESCAPE_TABLE, accept-when-left-column-duplicates-with-
39038 // cross-disjoint-preserved, reject-diagonal-collision, reject-
39039 // head-left-matches-head-right-off-diagonal, reject-head-left-
39040 // matches-tail-right, reject-tail-left-matches-head-right,
39041 // reject-tail-left-matches-tail-right, panic-message-provenance
39042 // on the CROSS-COLUMN-COLLISION axis) so a regression that
39043 // silently weakened the helper on ANY arm is caught by the
39044 // helper's OWN test surface rather than only surfacing as a
39045 // false-positive on some future pattern-DISTINCT-from-value
39046 // `[(char, char); N]` array's compound pin.
39047
39048 #[test]
39049 fn assert_char_pair_array_columns_cross_disjoint_accepts_the_empty_array() {
39050 // Empty array `arr = []` at the `[(char, char); 0]` corner —
39051 // vacuously cross-column-disjoint (no `(i, j)` position pair
39052 // exists to test). Pins the outer `while i < N` guard's
39053 // short-circuit on `N == 0`. Turbofish binding required
39054 // because there's no other cue for the const parameter on
39055 // the empty array literal.
39056 assert_char_pair_array_columns_cross_disjoint::<0>(&[]);
39057 }
39058
39059 #[test]
39060 fn assert_char_pair_array_columns_cross_disjoint_accepts_a_singleton_when_left_disjoint_from_right(
39061 ) {
39062 // Singleton `arr = [('a', 'x')]` — LEFT column = {'a'},
39063 // RIGHT column = {'x'}, disjoint. Pins the singleton corner
39064 // where the ONLY `(i, j)` position pair is `(0, 0)` on the
39065 // diagonal and `arr[0].0 != arr[0].1` so the diagonal arm
39066 // does NOT fire.
39067 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x')]);
39068 }
39069
39070 #[test]
39071 fn assert_char_pair_array_columns_cross_disjoint_accepts_a_multi_element_when_cross_disjoint() {
39072 // Three-element `arr = [('a', 'x'), ('b', 'y'), ('c', 'z')]`
39073 // with LEFT = {'a', 'b', 'c'}, RIGHT = {'x', 'y', 'z'},
39074 // disjoint. Pins the inner sweep's coverage of BOTH
39075 // diagonal AND off-diagonal `(i, j)` position pairs at
39076 // EACH `i` — a regression that returned early after
39077 // position 0 would silently accept a cross-column collision
39078 // at position 1 or 2 or an off-diagonal cross-column
39079 // collision at `(0, 1)`, `(1, 0)`, `(0, 2)`, `(2, 0)`,
39080 // `(1, 2)`, `(2, 1)`.
39081 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'y'), ('c', 'z')]);
39082 }
39083
39084 #[test]
39085 fn assert_char_pair_array_columns_cross_disjoint_accepts_the_family_wide_substrate_named_escape_table(
39086 ) {
39087 // Positive pin on the family-wide `Atom::NAMED_ESCAPE_TABLE`
39088 // paired array this helper is applied to at compile time via
39089 // the module-level `const _:` witness. Runtime pin as a
39090 // second-stage safety net if the const-eval sweep is ever
39091 // silently dropped. Sibling posture to the `_bijective` +
39092 // `_pairwise_distinct` family-wide pins on the same paired
39093 // array — the three pins bind complementary INJECTIVITY axes
39094 // (per-column, per-tuple, per-CROSS-column) of the same
39095 // substrate table. LEFT column = {'n', 't', 'r'} (printable
39096 // ASCII source chars), RIGHT column = {'\n', '\t', '\r'}
39097 // (control character decoded values), disjoint by algebra
39098 // design.
39099 assert_char_pair_array_columns_cross_disjoint(&Atom::NAMED_ESCAPE_TABLE);
39100 }
39101
39102 #[test]
39103 fn assert_char_pair_array_columns_cross_disjoint_accepts_when_left_column_duplicates_but_cross_disjoint_preserved(
39104 ) {
39105 // Orthogonality with `_bijective`: a table like
39106 // `[('a', 'x'), ('a', 'y')]` VIOLATES per-column INJECTIVITY
39107 // (LEFT column repeats 'a') but PASSES cross-column
39108 // disjointness (LEFT = {'a'}, RIGHT = {'x', 'y'} are
39109 // disjoint). Pins this helper's axis as INDEPENDENT of
39110 // `_bijective` — a diagnostic that names CROSS-COLUMN-
39111 // COLLISION routes UNAMBIGUOUSLY to a cross-column drift,
39112 // not to a per-column duplicate.
39113 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('a', 'y')]);
39114 // Symmetric orthogonality on the RIGHT column: a table like
39115 // `[('a', 'x'), ('b', 'x')]` VIOLATES per-column INJECTIVITY
39116 // (RIGHT column repeats 'x') but PASSES cross-column
39117 // disjointness (LEFT = {'a', 'b'}, RIGHT = {'x'} are
39118 // disjoint).
39119 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'x')]);
39120 }
39121
39122 #[test]
39123 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39124 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_diagonal_collision() {
39125 // NEGATIVE PIN — diagonal `(i == j == 0)` corner: `arr[0] =
39126 // ('a', 'a')` collides with itself across columns. Pins the
39127 // inner sweep INCLUDING the `j == i` case — a regression that
39128 // set `j = i + 1` (skipping the diagonal) would silently
39129 // accept a `('a', 'a')` self-mapping pair, which is EXACTLY
39130 // the SELF-arm identity-relation shape the pattern-DISTINCT-
39131 // from-value sub-vocabulary MUST NOT carry.
39132 assert_char_pair_array_columns_cross_disjoint(&[('a', 'a')]);
39133 }
39134
39135 #[test]
39136 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39137 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_diagonal_collision_at_interior_position(
39138 ) {
39139 // NEGATIVE PIN — interior diagonal `(i == j == 1)` corner:
39140 // pins the diagonal arm firing at ANY interior position,
39141 // not just the head. A regression that started the sweep
39142 // at `i = 1` for `j` (skipping the diagonal at each `i`)
39143 // would silently accept a `('b', 'b')` self-mapping pair at
39144 // position 1.
39145 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'b'), ('c', 'z')]);
39146 }
39147
39148 #[test]
39149 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39150 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_head_left_matches_tail_right(
39151 ) {
39152 // NEGATIVE PIN — off-diagonal `(i, j) = (0, 1)` corner:
39153 // `arr[0].0 == 'a'` collides with `arr[1].1 == 'a'` at
39154 // an off-diagonal position pair. Pins the inner sweep
39155 // covering `j > i` — a regression that only checked the
39156 // diagonal `i == j` would silently accept the head LEFT
39157 // aliasing the tail RIGHT.
39158 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'a')]);
39159 }
39160
39161 #[test]
39162 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39163 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_tail_left_matches_head_right(
39164 ) {
39165 // NEGATIVE PIN — off-diagonal `(i, j) = (1, 0)` corner:
39166 // `arr[1].0 == 'a'` collides with `arr[0].1 == 'a'` at the
39167 // OTHER off-diagonal orientation. Pins the inner sweep
39168 // covering `j < i` — a regression that only checked `j >=
39169 // i` would silently accept the tail LEFT aliasing the head
39170 // RIGHT.
39171 assert_char_pair_array_columns_cross_disjoint(&[('x', 'a'), ('a', 'y')]);
39172 }
39173
39174 #[test]
39175 #[should_panic(expected = "assert_char_pair_array_columns_cross_disjoint")]
39176 fn assert_char_pair_array_columns_cross_disjoint_panics_at_runtime_on_tail_position_diagonal_collision(
39177 ) {
39178 // NEGATIVE PIN — tail-position diagonal `(i == j == N-1)`
39179 // corner: pins the outer `while i < N` bound INCLUDING the
39180 // last row — a regression that walked `while i < N - 1`
39181 // (dropping the last row) would silently accept a tail-
39182 // position `('c', 'c')` diagonal collision.
39183 assert_char_pair_array_columns_cross_disjoint(&[('a', 'x'), ('b', 'y'), ('c', 'c')]);
39184 }
39185
39186 #[test]
39187 fn assert_char_pair_array_columns_cross_disjoint_panic_message_names_the_helper_and_cross_column_collision_axis(
39188 ) {
39189 // PANIC-MESSAGE PROVENANCE PIN — CROSS-COLUMN-COLLISION arm:
39190 // the panic message MUST begin with the helper's own name
39191 // AND identify the failed AXIS as "CROSS-COLUMN-COLLISION"
39192 // so downstream diagnostics route the drift back to (a) the
39193 // helper by string search on
39194 // `"assert_char_pair_array_columns_cross_disjoint"` and (b)
39195 // the axis by string search on `"CROSS-COLUMN-COLLISION"`.
39196 // The axis-provenance string is chosen DISTINCT from every
39197 // sibling helper's axis vocabulary (`"CHAR-PAIR-TUPLE-
39198 // COLLISION"` on `_pairwise_distinct`, `"LEFT column"` /
39199 // `"RIGHT column"` on `_bijective`, `"CHAR-PAIR-SUBSET-
39200 // VIOLATION"` on `_within_char_pair_finite_set`, `"CHAR-
39201 // PAIR-DISJOINTNESS-VIOLATION"` on `_arrays_disjoint`,
39202 // `"LEFT-COLUMN-DIVERGENCE"` / `"RIGHT-COLUMN-DIVERGENCE"`
39203 // on `_columns_equal_char_arrays`) — a diagnostic that
39204 // names the failed axis routes UNAMBIGUOUSLY to this
39205 // specific cross-column-disjointness helper's arm.
39206 let outcome = std::panic::catch_unwind(|| {
39207 assert_char_pair_array_columns_cross_disjoint(&[('a', 'a')]);
39208 });
39209 let payload = outcome.expect_err(
39210 "assert_char_pair_array_columns_cross_disjoint must \
39211 panic on a diagonal `('a', 'a')` self-mapping pair",
39212 );
39213 let msg = payload
39214 .downcast_ref::<&'static str>()
39215 .map(|s| (*s).to_owned())
39216 .or_else(|| payload.downcast_ref::<String>().cloned())
39217 .expect(
39218 "assert_char_pair_array_columns_cross_disjoint \
39219 panic payload must be a static &str or String",
39220 );
39221 assert!(
39222 msg.contains("assert_char_pair_array_columns_cross_disjoint"),
39223 "assert_char_pair_array_columns_cross_disjoint panic \
39224 message {msg:?} must name the helper for provenance-\
39225 preserving failure diagnostics",
39226 );
39227 assert!(
39228 msg.contains("CROSS-COLUMN-COLLISION"),
39229 "assert_char_pair_array_columns_cross_disjoint panic \
39230 message {msg:?} must name the failed AXIS (\"CROSS-\
39231 COLUMN-COLLISION\") for axis-provenance-preserving \
39232 failure diagnostics",
39233 );
39234 }
39235
39236 // ── `assert_char_pair_array_is_concatenation_of_char_pair_array_
39237 // and_char_array_diagonal` — the `(char, char)` product-element
39238 // SEGMENTED-CONCATENATION-with-DIAGONAL-TAIL verifier that binds
39239 // the composite-construction identity `arr == head ++
39240 // diagonal(tail_diag)` at compile time on a paired-array
39241 // vocabulary composed from a peer paired HEAD + a peer scalar
39242 // DIAGONAL TAIL. Peer to the `_columns_equal_char_arrays` cross-
39243 // row-bond sibling — where the sibling binds the FULL column-
39244 // projection bond across TWO peer scalar arrays at EVERY position,
39245 // this helper binds the SEGMENTED composite-construction bond
39246 // across a peer paired vocabulary (head) and a peer scalar
39247 // vocabulary (diagonally embedded tail). The runtime test surface
39248 // pins each of the helper's arms (accept-empty-triple, accept-
39249 // head-only, accept-diagonal-tail-only, accept-mixed-small-triple,
39250 // accept-family-wide-substrate-triple, reject-head-left-head-
39251 // position, reject-head-left-tail-position, reject-head-right-
39252 // head-position, reject-head-right-tail-position, reject-
39253 // diagonal-tail-left-head-position, reject-diagonal-tail-left-
39254 // tail-position, reject-diagonal-tail-right-head-position, reject-
39255 // diagonal-tail-right-tail-position, reject-cardinality-mismatch
39256 // on `K + M != N`, panic-message-provenance on FIVE distinct
39257 // AXES) so a regression that silently weakened the helper on ANY
39258 // arm is caught by the helper's OWN test surface rather than only
39259 // surfacing as a false-positive on some future composite-
39260 // constructed `[(char, char); N]` array's compound pin.
39261
39262 #[test]
39263 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_the_empty_triple(
39264 ) {
39265 // Empty arrays `arr = []`, `head = []`, `tail_diag = []` at
39266 // the `[(char, char); 0]` + `[(char, char); 0]` + `[char; 0]`
39267 // corner — vacuously composite (no position exists to test).
39268 // Pins the outer sweep bounds' short-circuit on `N == K ==
39269 // M == 0`. Turbofish binding required because there's no
39270 // other cue for the const parameters on the three empty
39271 // array literals.
39272 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<0, 0, 0>(
39273 &[],
39274 &[],
39275 &[],
39276 );
39277 }
39278
39279 #[test]
39280 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_head_only_triple(
39281 ) {
39282 // Head-only triple `arr = [('a','x')]`, `head = [('a','x')]`,
39283 // `tail_diag = []` at the `M == 0` corner — the composite
39284 // reduces to the peer paired HEAD verbatim, no diagonal-
39285 // embedding. Pins the outer `while j < M` guard's short-
39286 // circuit on empty tail. Turbofish binding required for the
39287 // three const parameters.
39288 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<1, 1, 0>(
39289 &[('a', 'x')],
39290 &[('a', 'x')],
39291 &[],
39292 );
39293 }
39294
39295 #[test]
39296 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_diagonal_tail_only_triple(
39297 ) {
39298 // Diagonal-tail-only triple `arr = [('q','q')]`, `head = []`,
39299 // `tail_diag = ['q']` at the `K == 0` corner — the composite
39300 // reduces to the DIAGONAL-EMBEDDING of the peer scalar
39301 // `tail_diag` verbatim, no head. Pins the outer `while i < K`
39302 // guard's short-circuit on empty head. Turbofish binding
39303 // required for the three const parameters.
39304 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<1, 0, 1>(
39305 &[('q', 'q')],
39306 &[],
39307 &['q'],
39308 );
39309 }
39310
39311 #[test]
39312 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_mixed_small_triple(
39313 ) {
39314 // Mixed small triple `arr = [('a','x'), ('q','q')]`, `head =
39315 // [('a','x')]`, `tail_diag = ['q']` at the `K == 1, M == 1`
39316 // corner — the smallest non-trivial case that exercises BOTH
39317 // segments. Pins that BOTH sweeps advance and that BOTH arms
39318 // cover their columns at their positions. A regression that
39319 // silently walked ONLY the head OR ONLY the tail would
39320 // silently accept a divergence on the un-swept segment.
39321 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<2, 1, 1>(
39322 &[('a', 'x'), ('q', 'q')],
39323 &[('a', 'x')],
39324 &['q'],
39325 );
39326 }
39327
39328 #[test]
39329 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_accepts_the_family_wide_substrate_triple(
39330 ) {
39331 // Positive pin on the family-wide (`Atom::ESCAPE_TABLE`,
39332 // `Atom::NAMED_ESCAPE_TABLE`, `Atom::SELF_ESCAPE_TABLE`)
39333 // triple this helper is applied to at compile time via the
39334 // module-level `const _:` witness. Runtime pin as a second-
39335 // stage safety net if the const-eval sweep is ever silently
39336 // dropped. SIXTH witness posture on the same paired array in
39337 // complementary posture to the FIVE prior sibling helper
39338 // family-wide pins (`_pairwise_distinct`, `_bijective`,
39339 // `_within_char_pair_finite_set`, `_arrays_disjoint`,
39340 // `_columns_equal_char_arrays`) — the six pins bind six
39341 // complementary axes of the same substrate table.
39342 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal(
39343 &Atom::ESCAPE_TABLE,
39344 &Atom::NAMED_ESCAPE_TABLE,
39345 &Atom::SELF_ESCAPE_TABLE,
39346 );
39347 }
39348
39349 #[test]
39350 #[should_panic(
39351 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39352 )]
39353 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_head_left_head_position_divergence(
39354 ) {
39355 // NEGATIVE PIN — HEAD-SEGMENT LEFT-column head-position
39356 // (i == 0) corner: `arr[0].0 = 'z'` diverges from `head[0].0
39357 // = 'a'` on the FIRST head-segment position. Pins the HEAD-
39358 // LEFT arm firing at the head — a regression that started
39359 // the head sweep at `i = 1` would silently accept a head-
39360 // position divergence.
39361 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 2, 1>(
39362 &[('z', 'x'), ('b', 'y'), ('q', 'q')],
39363 &[('a', 'x'), ('b', 'y')],
39364 &['q'],
39365 );
39366 }
39367
39368 #[test]
39369 #[should_panic(
39370 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39371 )]
39372 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_head_left_tail_position_divergence(
39373 ) {
39374 // NEGATIVE PIN — HEAD-SEGMENT LEFT-column tail-position
39375 // (i == K-1) corner: divergence at the LAST head-segment
39376 // position MUST also fire. Pins the outer head-sweep bound
39377 // `while i < K` — a regression that walked `while i < K - 1`
39378 // (dropping the last head row) would silently accept a tail-
39379 // of-head LEFT-column divergence.
39380 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 2, 1>(
39381 &[('a', 'x'), ('z', 'y'), ('q', 'q')],
39382 &[('a', 'x'), ('b', 'y')],
39383 &['q'],
39384 );
39385 }
39386
39387 #[test]
39388 #[should_panic(
39389 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39390 )]
39391 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_head_right_head_position_divergence(
39392 ) {
39393 // NEGATIVE PIN — HEAD-SEGMENT RIGHT-column head-position
39394 // (i == 0) corner: symmetric to the HEAD-LEFT arm at the
39395 // head — pins the HEAD-RIGHT arm firing at position 0 with
39396 // the HEAD-LEFT arm satisfied.
39397 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 2, 1>(
39398 &[('a', 'z'), ('b', 'y'), ('q', 'q')],
39399 &[('a', 'x'), ('b', 'y')],
39400 &['q'],
39401 );
39402 }
39403
39404 #[test]
39405 #[should_panic(
39406 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39407 )]
39408 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_head_right_tail_position_divergence(
39409 ) {
39410 // NEGATIVE PIN — HEAD-SEGMENT RIGHT-column tail-position
39411 // (i == K-1) corner: symmetric to the HEAD-LEFT-tail arm —
39412 // pins the outer head-sweep bound on the RIGHT arm with the
39413 // LEFT arm satisfied at every position.
39414 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 2, 1>(
39415 &[('a', 'x'), ('b', 'z'), ('q', 'q')],
39416 &[('a', 'x'), ('b', 'y')],
39417 &['q'],
39418 );
39419 }
39420
39421 #[test]
39422 #[should_panic(
39423 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39424 )]
39425 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(
39426 ) {
39427 // NEGATIVE PIN — DIAGONAL-TAIL LEFT-column head-position
39428 // (j == 0, arr[K + 0].0 diverges from `tail_diag[0]`) corner:
39429 // pins the DIAGONAL-TAIL-LEFT arm firing at the tail segment's
39430 // FIRST position — a regression that started the tail sweep
39431 // at `j = 1` would silently accept a head-of-tail LEFT-column
39432 // divergence.
39433 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 1, 2>(
39434 &[('a', 'x'), ('z', 'q'), ('r', 'r')],
39435 &[('a', 'x')],
39436 &['q', 'r'],
39437 );
39438 }
39439
39440 #[test]
39441 #[should_panic(
39442 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39443 )]
39444 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(
39445 ) {
39446 // NEGATIVE PIN — DIAGONAL-TAIL LEFT-column tail-position
39447 // (j == M-1, arr[N-1].0 diverges from `tail_diag[M-1]`)
39448 // corner: pins the outer tail-sweep bound `while j < M` — a
39449 // regression that walked `while j < M - 1` (dropping the last
39450 // tail row) would silently accept a tail-of-tail LEFT-column
39451 // divergence.
39452 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 1, 2>(
39453 &[('a', 'x'), ('q', 'q'), ('z', 'r')],
39454 &[('a', 'x')],
39455 &['q', 'r'],
39456 );
39457 }
39458
39459 #[test]
39460 #[should_panic(
39461 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39462 )]
39463 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(
39464 ) {
39465 // NEGATIVE PIN — DIAGONAL-TAIL RIGHT-column head-position
39466 // (j == 0, arr[K + 0].1 diverges from `tail_diag[0]`) corner:
39467 // symmetric to the DIAGONAL-TAIL-LEFT arm at the tail-head —
39468 // pins the DIAGONAL-TAIL-RIGHT arm firing at the tail
39469 // segment's FIRST position with the LEFT arm satisfied.
39470 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 1, 2>(
39471 &[('a', 'x'), ('q', 'z'), ('r', 'r')],
39472 &[('a', 'x')],
39473 &['q', 'r'],
39474 );
39475 }
39476
39477 #[test]
39478 #[should_panic(
39479 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39480 )]
39481 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(
39482 ) {
39483 // NEGATIVE PIN — DIAGONAL-TAIL RIGHT-column tail-position
39484 // (j == M-1, arr[N-1].1 diverges from `tail_diag[M-1]`)
39485 // corner: symmetric to the DIAGONAL-TAIL-LEFT-tail arm —
39486 // pins the outer tail-sweep bound on the RIGHT arm with the
39487 // LEFT arm satisfied at every position.
39488 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<3, 1, 2>(
39489 &[('a', 'x'), ('q', 'q'), ('r', 'z')],
39490 &[('a', 'x')],
39491 &['q', 'r'],
39492 );
39493 }
39494
39495 #[test]
39496 #[should_panic(
39497 expected = "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39498 )]
39499 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panics_at_runtime_on_cardinality_mismatch(
39500 ) {
39501 // NEGATIVE PIN — CARDINALITY-MISMATCH corner: `K + M != N`
39502 // (here `K == 3, M == 3, N == 5` so `K + M == 6 != 5`) fires
39503 // the CARDINALITY-MISMATCH panic BEFORE any per-position
39504 // sweep begins. Pins the FIRST guard arm — a regression that
39505 // silently omitted the arity check would OOB-panic instead
39506 // OR silently truncate the tail sweep, both of which would
39507 // corrupt the AXIS-provenance signal downstream diagnostics
39508 // depend on.
39509 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<5, 3, 3>(
39510 &[('a', 'x'), ('b', 'y'), ('c', 'z'), ('q', 'q'), ('r', 'r')],
39511 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39512 &['q', 'r', 's'],
39513 );
39514 }
39515
39516 #[test]
39517 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(
39518 ) {
39519 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-LEFT-DIVERGENCE
39520 // arm: the panic message MUST begin with the helper's own
39521 // name AND identify the failed AXIS as "HEAD-SEGMENT-LEFT-
39522 // DIVERGENCE" so downstream diagnostics route the drift back
39523 // to (a) the helper by string search AND (b) the axis by
39524 // string search on `"HEAD-SEGMENT-LEFT-DIVERGENCE"`.
39525 let outcome = std::panic::catch_unwind(|| {
39526 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39527 3,
39528 2,
39529 1,
39530 >(
39531 &[('z', 'x'), ('b', 'y'), ('q', 'q')],
39532 &[('a', 'x'), ('b', 'y')],
39533 &['q'],
39534 );
39535 });
39536 let payload = outcome.expect_err(
39537 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39538 must panic on a HEAD-SEGMENT LEFT-column divergence",
39539 );
39540 let msg = payload
39541 .downcast_ref::<&'static str>()
39542 .map(|s| (*s).to_owned())
39543 .or_else(|| payload.downcast_ref::<String>().cloned())
39544 .expect(
39545 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39546 panic payload must be a static &str or String",
39547 );
39548 assert!(
39549 msg.contains(
39550 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal"
39551 ),
39552 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39553 panic message {msg:?} must name the helper for \
39554 provenance-preserving failure diagnostics",
39555 );
39556 assert!(
39557 msg.contains("HEAD-SEGMENT-LEFT-DIVERGENCE"),
39558 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39559 panic message {msg:?} must name the failed AXIS \
39560 (\"HEAD-SEGMENT-LEFT-DIVERGENCE\") for axis-provenance-\
39561 preserving failure diagnostics",
39562 );
39563 }
39564
39565 #[test]
39566 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(
39567 ) {
39568 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-RIGHT-
39569 // DIVERGENCE arm: symmetric to the HEAD-LEFT arm's
39570 // provenance pin.
39571 let outcome = std::panic::catch_unwind(|| {
39572 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39573 3,
39574 2,
39575 1,
39576 >(
39577 &[('a', 'z'), ('b', 'y'), ('q', 'q')],
39578 &[('a', 'x'), ('b', 'y')],
39579 &['q'],
39580 );
39581 });
39582 let payload = outcome.expect_err(
39583 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39584 must panic on a HEAD-SEGMENT RIGHT-column divergence",
39585 );
39586 let msg = payload
39587 .downcast_ref::<&'static str>()
39588 .map(|s| (*s).to_owned())
39589 .or_else(|| payload.downcast_ref::<String>().cloned())
39590 .expect(
39591 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39592 panic payload must be a static &str or String",
39593 );
39594 assert!(
39595 msg.contains("HEAD-SEGMENT-RIGHT-DIVERGENCE"),
39596 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39597 panic message {msg:?} must name the failed AXIS \
39598 (\"HEAD-SEGMENT-RIGHT-DIVERGENCE\") for axis-provenance-\
39599 preserving failure diagnostics",
39600 );
39601 }
39602
39603 #[test]
39604 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(
39605 ) {
39606 // PANIC-MESSAGE PROVENANCE PIN — DIAGONAL-TAIL-SEGMENT-LEFT-
39607 // DIVERGENCE arm: partitions the failure vocabulary DISTINCT
39608 // from the HEAD-SEGMENT-LEFT-DIVERGENCE arm — a diagnostic
39609 // that reads the AXIS routes UNAMBIGUOUSLY to the TAIL
39610 // segment's LEFT column rather than the HEAD segment's.
39611 let outcome = std::panic::catch_unwind(|| {
39612 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39613 3,
39614 1,
39615 2,
39616 >(
39617 &[('a', 'x'), ('z', 'q'), ('r', 'r')],
39618 &[('a', 'x')],
39619 &['q', 'r'],
39620 );
39621 });
39622 let payload = outcome.expect_err(
39623 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39624 must panic on a DIAGONAL-TAIL LEFT-column divergence",
39625 );
39626 let msg = payload
39627 .downcast_ref::<&'static str>()
39628 .map(|s| (*s).to_owned())
39629 .or_else(|| payload.downcast_ref::<String>().cloned())
39630 .expect(
39631 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39632 panic payload must be a static &str or String",
39633 );
39634 assert!(
39635 msg.contains("DIAGONAL-TAIL-SEGMENT-LEFT-DIVERGENCE"),
39636 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39637 panic message {msg:?} must name the failed AXIS \
39638 (\"DIAGONAL-TAIL-SEGMENT-LEFT-DIVERGENCE\") for axis-\
39639 provenance-preserving failure diagnostics",
39640 );
39641 }
39642
39643 #[test]
39644 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(
39645 ) {
39646 // PANIC-MESSAGE PROVENANCE PIN — DIAGONAL-TAIL-SEGMENT-RIGHT-
39647 // DIVERGENCE arm: symmetric to the DIAGONAL-TAIL-LEFT arm's
39648 // provenance pin. The FOUR positionwise axes together
39649 // (HEAD-LEFT, HEAD-RIGHT, DIAGONAL-TAIL-LEFT, DIAGONAL-TAIL-
39650 // RIGHT) partition the helper's positionwise failure surface
39651 // into FOUR disjoint arms so a diagnostic reading the AXIS
39652 // routes UNAMBIGUOUSLY to the specific (segment, column)
39653 // corner that diverged.
39654 let outcome = std::panic::catch_unwind(|| {
39655 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39656 3,
39657 1,
39658 2,
39659 >(
39660 &[('a', 'x'), ('q', 'z'), ('r', 'r')],
39661 &[('a', 'x')],
39662 &['q', 'r'],
39663 );
39664 });
39665 let payload = outcome.expect_err(
39666 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39667 must panic on a DIAGONAL-TAIL RIGHT-column divergence",
39668 );
39669 let msg = payload
39670 .downcast_ref::<&'static str>()
39671 .map(|s| (*s).to_owned())
39672 .or_else(|| payload.downcast_ref::<String>().cloned())
39673 .expect(
39674 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39675 panic payload must be a static &str or String",
39676 );
39677 assert!(
39678 msg.contains("DIAGONAL-TAIL-SEGMENT-RIGHT-DIVERGENCE"),
39679 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39680 panic message {msg:?} must name the failed AXIS \
39681 (\"DIAGONAL-TAIL-SEGMENT-RIGHT-DIVERGENCE\") for axis-\
39682 provenance-preserving failure diagnostics",
39683 );
39684 }
39685
39686 #[test]
39687 fn assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal_panic_message_names_the_helper_and_cardinality_mismatch_axis(
39688 ) {
39689 // PANIC-MESSAGE PROVENANCE PIN — CARDINALITY-MISMATCH arm:
39690 // fires BEFORE any per-position sweep, at a distinct axis
39691 // vocabulary. Pins the FIRST guard's provenance so a
39692 // diagnostic reading the axis distinguishes ARITY drift
39693 // (mistyped turbofish) from CONTENT drift (segment / column
39694 // divergence).
39695 let outcome = std::panic::catch_unwind(|| {
39696 assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal::<
39697 5,
39698 3,
39699 3,
39700 >(
39701 &[('a', 'x'), ('b', 'y'), ('c', 'z'), ('q', 'q'), ('r', 'r')],
39702 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39703 &['q', 'r', 's'],
39704 );
39705 });
39706 let payload = outcome.expect_err(
39707 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39708 must panic on a CARDINALITY-MISMATCH `K + M != N`",
39709 );
39710 let msg = payload
39711 .downcast_ref::<&'static str>()
39712 .map(|s| (*s).to_owned())
39713 .or_else(|| payload.downcast_ref::<String>().cloned())
39714 .expect(
39715 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39716 panic payload must be a static &str or String",
39717 );
39718 assert!(
39719 msg.contains("CARDINALITY-MISMATCH"),
39720 "assert_char_pair_array_is_concatenation_of_char_pair_array_and_char_array_diagonal \
39721 panic message {msg:?} must name the failed AXIS \
39722 (\"CARDINALITY-MISMATCH\") for axis-provenance-preserving \
39723 failure diagnostics",
39724 );
39725 }
39726
39727 // ── `assert_char_pair_array_slice_equals_char_pair_array` — the
39728 // `(char, char)` product-element row's SLICE-EQUALS-ARRAY verifier
39729 // that binds the sub-slice `full[START..START + M) == sub[..]`
39730 // paired-positionwise-composition contract at compile time. Row-
39731 // dual peer to `assert_u8_array_slice_equals_u8_array`,
39732 // `assert_char_array_slice_equals_char_array`, and
39733 // `assert_str_array_slice_equals_str_array` on the (element-type)
39734 // axis of the SAME (SUB-SLICE ARRAY-image) column of the (element-
39735 // type × contract-shape) matrix. The runtime test surface pins
39736 // each of the helper's arms (accept-canonical-middle-slice,
39737 // accept-empty-sub-array-at-three-start-positions, accept-full-
39738 // array-degenerate-at-two-arities, accept-substrate-escape-table-
39739 // head-segment-decomposition, reject-left-column-drift, reject-
39740 // right-column-drift, reject-start-out-of-bounds, reject-slice-
39741 // length-out-of-bounds, panic-message-provenance on the LEFT-
39742 // COLUMN + RIGHT-COLUMN axes) so a regression that silently
39743 // weakened the helper on ANY arm (e.g. dropping the LEFT-column
39744 // check, dropping the RIGHT-column check, dropping the `START`
39745 // offset from the `full[START + i]` read, returning early past
39746 // ANY bounds gate, or dropping the `as u32` char-to-scalar bridge
39747 // that lets the const-eval sweep proceed byte-for-byte on each
39748 // column) is caught by the helper's OWN test surface rather than
39749 // only surfacing as a false-positive on some future paired
39750 // container-array sub-slice equality.
39751
39752 #[test]
39753 fn assert_char_pair_array_slice_equals_char_pair_array_accepts_a_canonical_middle_slice() {
39754 // Canonical paired sub-slice `full[START..START + M) == sub[..]`
39755 // inside a longer paired array `full` whose ENDPOINTS carry
39756 // DIFFERENT pairs than the peer sub-array. Pins the outer
39757 // `while i < M` sweep reads `full[START + i]` at the OFFSET
39758 // position (not `full[i]`) on BOTH columns — a regression that
39759 // dropped the `START` offset would compare `full[0..M)`
39760 // against `sub[..]` and panic on the wrong axis. `START = 1`
39761 // pins the sweep skips position `[0..START)` and reads only
39762 // `[1..1+3) = [1..4)` on both columns of the paired sub-array.
39763 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
39764 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('c', 'z'), ('z', 'Z')],
39765 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39766 );
39767 }
39768
39769 #[test]
39770 fn assert_char_pair_array_slice_equals_char_pair_array_accepts_the_empty_sub_array() {
39771 // LEGAL degenerate: `M == 0` collapses the paired sub-array
39772 // into an empty listing `[]`. The sweep never enters the loop
39773 // body and the helper accepts. Cross-position coverage pins
39774 // the empty-sub-array acceptance at THREE distinct `START`
39775 // positions (`START == 0` at the left endpoint, `START == 2`
39776 // in the interior, `START == N` at the right endpoint — the
39777 // latter is the corner `START == N` combined with `M == 0`
39778 // that the START-OUT-OF-BOUNDS gate's inclusive upper bound
39779 // must accept). A regression that hard-coded `START < N` OR
39780 // panicked on the `M == 0` corner is caught on ALL THREE
39781 // arms.
39782 assert_char_pair_array_slice_equals_char_pair_array::<5, 0, 0>(
39783 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39784 &[],
39785 );
39786 assert_char_pair_array_slice_equals_char_pair_array::<5, 0, 2>(
39787 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39788 &[],
39789 );
39790 assert_char_pair_array_slice_equals_char_pair_array::<5, 0, 5>(
39791 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39792 &[],
39793 );
39794 }
39795
39796 #[test]
39797 fn assert_char_pair_array_slice_equals_char_pair_array_accepts_the_full_array_degenerate() {
39798 // Full-array-covering slice `M == N, START == 0` collapses
39799 // to the ALL-positions-equal-peer-array shape `full == sub`
39800 // pointwise on BOTH columns. Pins that the sweep proceeds
39801 // through EVERY position of the outer paired array when
39802 // `START = 0` and `M = N`. Cross-arity coverage on `N ∈ {2,
39803 // 3}` pins the sweep's terminal-position visit across the
39804 // small arities the substrate's paired escape-table
39805 // vocabulary spans (`N = 2` for a hypothetical two-slot
39806 // paired table, `N = 3` for `NAMED_ESCAPE_TABLE`).
39807 assert_char_pair_array_slice_equals_char_pair_array::<2, 2, 0>(
39808 &[('a', 'x'), ('b', 'y')],
39809 &[('a', 'x'), ('b', 'y')],
39810 );
39811 assert_char_pair_array_slice_equals_char_pair_array::<3, 3, 0>(
39812 &[('n', '\n'), ('t', '\t'), ('r', '\r')],
39813 &[('n', '\n'), ('t', '\t'), ('r', '\r')],
39814 );
39815 }
39816
39817 #[test]
39818 fn assert_char_pair_array_slice_equals_char_pair_array_accepts_the_substrate_escape_table_head_segment(
39819 ) {
39820 // Runtime cross-check that the substrate's (ESCAPE_TABLE,
39821 // NAMED_ESCAPE_TABLE) HEAD-segment positionwise-composition
39822 // identity `ESCAPE_TABLE[0..3] == NAMED_ESCAPE_TABLE` holds
39823 // pointwise on BOTH columns. Runs the SAME helper the module-
39824 // level `const _` witness runs at rustc time — a runtime
39825 // safety net enforcing the theorem at BOTH stages of the
39826 // toolchain (const at `cargo check`, runtime at `cargo
39827 // test`). A regression that renamed one of the three
39828 // NAMED-escape SOURCE or DECODED entries (or drifted the
39829 // ordering across ESCAPE_TABLE's first three initializer
39830 // slots) fails HERE at the substrate callsite AND at the
39831 // const witness above. Peer of
39832 // `assert_char_array_slice_equals_char_array_accepts_each_family_wide_substrate_array`
39833 // on the (char) row — that witness carries the FULL-ARRAY
39834 // per-position ORDER theorem for the EIGHT reader-boundary
39835 // `[char; N]` arrays; this witness carries the HEAD-segment
39836 // per-position ORDER theorem for the substrate's SINGLE
39837 // paired-composite pair on the `(char, char)` product-
39838 // element row.
39839 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 0>(
39840 &Atom::ESCAPE_TABLE,
39841 &Atom::NAMED_ESCAPE_TABLE,
39842 );
39843 }
39844
39845 #[test]
39846 #[should_panic(expected = "CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-VIOLATION")]
39847 fn assert_char_pair_array_slice_equals_char_pair_array_panics_at_runtime_on_left_column_drift()
39848 {
39849 // NEGATIVE PIN — CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-
39850 // VIOLATION corner: a LEFT-column entry at some position in
39851 // `full[START..START + M)` that does NOT byte-equal the peer
39852 // sub-array `sub` at the offset-matched position MUST panic
39853 // at runtime with the axis-named message. Pins the helper's
39854 // LEFT-column reject arm — a regression that silently
39855 // dropped the LEFT-column check while retaining the RIGHT-
39856 // column check would slip through the compile-time witness's
39857 // LEFT-drift failure mode too. The offending LEFT-column
39858 // char `'!'` at outer position `3` (interior of the sub-
39859 // slice `[1..4)`, offset `2` inside `sub`) pins the middle-
39860 // of-slice LEFT-column drift mode.
39861 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
39862 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('!', 'z'), ('z', 'Z')],
39863 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39864 );
39865 }
39866
39867 #[test]
39868 #[should_panic(expected = "CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-VIOLATION")]
39869 fn assert_char_pair_array_slice_equals_char_pair_array_panics_at_runtime_on_right_column_drift()
39870 {
39871 // NEGATIVE PIN — CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-
39872 // VIOLATION corner: a RIGHT-column entry at some position in
39873 // `full[START..START + M)` that does NOT byte-equal the peer
39874 // sub-array `sub` at the offset-matched position MUST panic
39875 // at runtime with the axis-named message. Pins the helper's
39876 // RIGHT-column reject arm — a regression that silently
39877 // dropped the RIGHT-column check while retaining the LEFT-
39878 // column check would slip through the compile-time witness's
39879 // RIGHT-drift failure mode too. The LEFT column matches
39880 // exactly on every position so ONLY the RIGHT column drift
39881 // (`'!'` at outer position `3`, offset `2` inside `sub`)
39882 // fires the reject arm — routing the diagnostic to the
39883 // RIGHT-COLUMN-* axis rather than the sibling LEFT-COLUMN-*
39884 // axis.
39885 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
39886 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('c', '!'), ('z', 'Z')],
39887 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39888 );
39889 }
39890
39891 #[test]
39892 #[should_panic(expected = "START-OUT-OF-BOUNDS")]
39893 fn assert_char_pair_array_slice_equals_char_pair_array_panics_at_runtime_on_start_out_of_bounds(
39894 ) {
39895 // NEGATIVE PIN — START-OUT-OF-BOUNDS gate: a caller-side
39896 // turbofish arity slip on the `START` const-generic where
39897 // `START > N` MUST panic at runtime with the START-OUT-OF-
39898 // BOUNDS-named message BEFORE the peer SLICE-LENGTH-OUT-OF-
39899 // BOUNDS gate reads `N - START` (which would `usize`-
39900 // underflow had this gate not caught the slip first). Pins
39901 // the gate's placement at the TOP of the helper — a
39902 // regression that dropped the gate would either underflow
39903 // subtraction at the peer gate OR panic deeper in
39904 // `full[START + i]` bounds-checking with a helper-name-less
39905 // panic message. The offending `START = 7` against `N = 5`
39906 // pins the strict `START > N` reject arm; the LEGAL
39907 // `START == N` empty-slice-at-right-endpoint corner is
39908 // covered by the peer acceptance test above.
39909 assert_char_pair_array_slice_equals_char_pair_array::<5, 0, 7>(
39910 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39911 &[],
39912 );
39913 }
39914
39915 #[test]
39916 #[should_panic(expected = "SLICE-LENGTH-OUT-OF-BOUNDS")]
39917 fn assert_char_pair_array_slice_equals_char_pair_array_panics_at_runtime_on_slice_length_out_of_bounds(
39918 ) {
39919 // NEGATIVE PIN — SLICE-LENGTH-OUT-OF-BOUNDS gate: a peer
39920 // sub-array arity `M` that exceeds the outer array's tail
39921 // cardinality `N - START` MUST panic at runtime with the
39922 // slice-length-out-of-bounds-named message. Peer gate to the
39923 // START-OUT-OF-BOUNDS arm above — the two gates jointly
39924 // enforce `START ≤ N` and `M ≤ N - START` before any content
39925 // sweep. The offending `M = 5` against `N - START = 5 - 3 =
39926 // 2` pins the strict `M > N - START` reject arm; the LEGAL
39927 // exact-fit corner `M == N - START` is covered by the
39928 // middle-slice acceptance test above.
39929 assert_char_pair_array_slice_equals_char_pair_array::<5, 5, 3>(
39930 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39931 &[('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X'), ('x', 'X')],
39932 );
39933 }
39934
39935 #[test]
39936 fn assert_char_pair_array_slice_equals_char_pair_array_panic_message_names_the_helper_and_left_column_violation_axis(
39937 ) {
39938 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-SLICE-EQUALS-
39939 // ARRAY-LEFT-COLUMN-VIOLATION arm: the panic message MUST
39940 // begin with the helper's own name AND identify the failed
39941 // AXIS as "CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-
39942 // VIOLATION" so downstream diagnostics route the drift back
39943 // to (a) the helper by string search on
39944 // `"assert_char_pair_array_slice_equals_char_pair_array"`
39945 // and (b) the failed COLUMN of the paired sweep by string
39946 // search on `"CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-
39947 // VIOLATION"`. Sibling posture to the (u8) / (char) / (str)
39948 // row peers' provenance pins — the FOUR pins together bind
39949 // the (helper, failed-axis) provenance pair at ONE test per
39950 // corner of the (SUB-SLICE ARRAY-image) column across the
39951 // FOUR element-type rows of the matrix.
39952 let outcome = std::panic::catch_unwind(|| {
39953 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
39954 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('!', 'z'), ('z', 'Z')],
39955 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
39956 );
39957 });
39958 let payload = outcome.expect_err(
39959 "assert_char_pair_array_slice_equals_char_pair_array must \
39960 panic on a LEFT-column drift — the reject-left-column-\
39961 drift arm is a CONTENT failure mode of the helper",
39962 );
39963 let msg = payload
39964 .downcast_ref::<&'static str>()
39965 .map(|s| (*s).to_owned())
39966 .or_else(|| payload.downcast_ref::<String>().cloned())
39967 .expect(
39968 "assert_char_pair_array_slice_equals_char_pair_array \
39969 panic payload must be a static &str or String",
39970 );
39971 assert!(
39972 msg.contains("assert_char_pair_array_slice_equals_char_pair_array"),
39973 "assert_char_pair_array_slice_equals_char_pair_array panic \
39974 message {msg:?} must name the helper for provenance-\
39975 preserving failure diagnostics",
39976 );
39977 assert!(
39978 msg.contains("CHAR-PAIR-SLICE-EQUALS-ARRAY-LEFT-COLUMN-VIOLATION"),
39979 "assert_char_pair_array_slice_equals_char_pair_array panic \
39980 message {msg:?} must name the failed AXIS (\"CHAR-PAIR-\
39981 SLICE-EQUALS-ARRAY-LEFT-COLUMN-VIOLATION\") for axis-\
39982 provenance-preserving failure diagnostics",
39983 );
39984 }
39985
39986 #[test]
39987 fn assert_char_pair_array_slice_equals_char_pair_array_panic_message_names_the_helper_and_right_column_violation_axis(
39988 ) {
39989 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-SLICE-EQUALS-
39990 // ARRAY-RIGHT-COLUMN-VIOLATION arm: the panic message MUST
39991 // begin with the helper's own name AND identify the failed
39992 // AXIS as "CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-
39993 // VIOLATION" so downstream diagnostics route the drift back
39994 // to (a) the helper by string search AND (b) the failed
39995 // COLUMN of the paired sweep by string search on the
39996 // RIGHT-COLUMN-* axis. Peer pin to the LEFT-COLUMN
39997 // provenance pin above — the two pins bind the (LEFT,
39998 // RIGHT) column-axis provenance pair at ONE test per column.
39999 let outcome = std::panic::catch_unwind(|| {
40000 assert_char_pair_array_slice_equals_char_pair_array::<5, 3, 1>(
40001 &[('z', 'Z'), ('a', 'x'), ('b', 'y'), ('c', '!'), ('z', 'Z')],
40002 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40003 );
40004 });
40005 let payload = outcome.expect_err(
40006 "assert_char_pair_array_slice_equals_char_pair_array must \
40007 panic on a RIGHT-column drift — the reject-right-column-\
40008 drift arm is a CONTENT failure mode of the helper",
40009 );
40010 let msg = payload
40011 .downcast_ref::<&'static str>()
40012 .map(|s| (*s).to_owned())
40013 .or_else(|| payload.downcast_ref::<String>().cloned())
40014 .expect(
40015 "assert_char_pair_array_slice_equals_char_pair_array \
40016 panic payload must be a static &str or String",
40017 );
40018 assert!(
40019 msg.contains("assert_char_pair_array_slice_equals_char_pair_array"),
40020 "assert_char_pair_array_slice_equals_char_pair_array panic \
40021 message {msg:?} must name the helper for provenance-\
40022 preserving failure diagnostics",
40023 );
40024 assert!(
40025 msg.contains("CHAR-PAIR-SLICE-EQUALS-ARRAY-RIGHT-COLUMN-VIOLATION"),
40026 "assert_char_pair_array_slice_equals_char_pair_array panic \
40027 message {msg:?} must name the failed AXIS (\"CHAR-PAIR-\
40028 SLICE-EQUALS-ARRAY-RIGHT-COLUMN-VIOLATION\") for axis-\
40029 provenance-preserving failure diagnostics",
40030 );
40031 }
40032
40033 // ── `assert_char_array_is_concatenation_of_char_pair_array_column_
40034 // and_char_array` — the (char) scalar-row SEGMENTED-CONCATENATION-
40035 // through-PAIRED-COLUMN-PROJECTION verifier that binds `arr ==
40036 // col(head_table) ++ tail` at compile time on the substrate's Str-
40037 // payload escape-table (`ESCAPE_SOURCES`, `NAMED_ESCAPE_TABLE`,
40038 // `SELF_ESCAPE_TABLE`) LEFT-column-projection triple AND the
40039 // (`ESCAPE_DECODED`, `NAMED_ESCAPE_TABLE`, `SELF_ESCAPE_TABLE`)
40040 // RIGHT-column-projection triple, CROSS-ROW peer to the (char, char)
40041 // paired-row (`_is_concatenation_of_char_pair_array_and_char_array_
40042 // diagonal`) sibling on the (segmented-concatenation) column of the
40043 // (element-type × contract-shape) matrix. The runtime test surface
40044 // pins each of the helper's arms — accept the empty triple with
40045 // BOTH `take_right_column` values, accept a head-only + tail-only +
40046 // mixed small triple, accept the two family-wide substrate triples
40047 // for each `take_right_column` value, reject at every failure-arm
40048 // corner (CARDINALITY-MISMATCH, HEAD-SEGMENT-LEFT/RIGHT-COLUMN-
40049 // DIVERGENCE at head/tail positions, TAIL-SEGMENT-DIVERGENCE at
40050 // head/tail positions), pin panic-message provenance on the four
40051 // axes — so a regression that silently weakened the helper on ANY
40052 // arm is caught by the helper's OWN test surface rather than only
40053 // surfacing as a false-positive on some future scalar-composite
40054 // pin.
40055
40056 #[test]
40057 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_the_empty_triple_left_column(
40058 ) {
40059 // Empty arrays at `N == K == M == 0` with `take_right_column ==
40060 // false` — vacuously composite (no position exists to test).
40061 // Pins the outer sweep bounds' short-circuit and the LEFT-column
40062 // branch's dispatch on the empty head sweep.
40063 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<0, 0, 0>(
40064 &[],
40065 &[],
40066 &[],
40067 false,
40068 );
40069 }
40070
40071 #[test]
40072 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_the_empty_triple_right_column(
40073 ) {
40074 // Empty arrays at `N == K == M == 0` with `take_right_column ==
40075 // true` — vacuously composite. Pins the RIGHT-column branch's
40076 // dispatch on the empty head sweep.
40077 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<0, 0, 0>(
40078 &[],
40079 &[],
40080 &[],
40081 true,
40082 );
40083 }
40084
40085 #[test]
40086 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_head_only_triple_left_column(
40087 ) {
40088 // Head-only triple `arr = ['a']`, `head_table = [('a','x')]`,
40089 // `tail = []` with `take_right_column == false` — LEFT column
40090 // projection at position 0. Pins the `while j < M` guard's
40091 // short-circuit on empty tail and the LEFT-column arm firing.
40092 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<1, 1, 0>(
40093 &['a'],
40094 &[('a', 'x')],
40095 &[],
40096 false,
40097 );
40098 }
40099
40100 #[test]
40101 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_head_only_triple_right_column(
40102 ) {
40103 // Head-only triple `arr = ['x']`, `head_table = [('a','x')]`,
40104 // `tail = []` with `take_right_column == true` — RIGHT column
40105 // projection at position 0. Pins the RIGHT-column arm firing on
40106 // the empty-tail short-circuit.
40107 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<1, 1, 0>(
40108 &['x'],
40109 &[('a', 'x')],
40110 &[],
40111 true,
40112 );
40113 }
40114
40115 #[test]
40116 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_tail_only_triple(
40117 ) {
40118 // Tail-only triple `arr = ['q']`, `head_table = []`, `tail =
40119 // ['q']` at the `K == 0` corner — the composite reduces to
40120 // `tail` verbatim, no head-column projection to perform. Pins
40121 // the outer `while i < K` guard's short-circuit on empty head
40122 // (identical behavior on both `take_right_column` values —
40123 // exercised here at `false`; the peer at `true` at the empty-
40124 // triple test above already covers the RIGHT branch's empty-K
40125 // dispatch).
40126 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<1, 0, 1>(
40127 &['q'],
40128 &[],
40129 &['q'],
40130 false,
40131 );
40132 }
40133
40134 #[test]
40135 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_mixed_small_triple_left_column(
40136 ) {
40137 // Mixed small triple `arr = ['a', 'q']`, `head_table =
40138 // [('a','x')]`, `tail = ['q']` at `K == 1, M == 1` with
40139 // `take_right_column == false` — the smallest non-trivial case
40140 // exercising BOTH the LEFT-column head-projection arm and the
40141 // tail-verbatim arm. Pins that BOTH sweeps advance.
40142 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<2, 1, 1>(
40143 &['a', 'q'],
40144 &[('a', 'x')],
40145 &['q'],
40146 false,
40147 );
40148 }
40149
40150 #[test]
40151 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_mixed_small_triple_right_column(
40152 ) {
40153 // Mixed small triple `arr = ['x', 'q']`, `head_table =
40154 // [('a','x')]`, `tail = ['q']` at `K == 1, M == 1` with
40155 // `take_right_column == true` — the smallest non-trivial case
40156 // exercising BOTH the RIGHT-column head-projection arm and the
40157 // tail-verbatim arm.
40158 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<2, 1, 1>(
40159 &['x', 'q'],
40160 &[('a', 'x')],
40161 &['q'],
40162 true,
40163 );
40164 }
40165
40166 #[test]
40167 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_the_family_wide_substrate_left_column_triple(
40168 ) {
40169 // Positive pin on the family-wide (`Atom::ESCAPE_SOURCES`,
40170 // `Atom::NAMED_ESCAPE_TABLE`, `Atom::SELF_ESCAPE_TABLE`,
40171 // `take_right_column: false`) LEFT-column triple this helper
40172 // is applied to at compile time via the module-level `const
40173 // _:` witness. Runtime pin as a second-stage safety net if the
40174 // const-eval sweep is ever silently dropped. Complementary to
40175 // the pre-existing (`_pairwise_distinct`, `_within_char_
40176 // finite_set`) witnesses on the SAME scalar array — those
40177 // pins bind SET-level axes (INJECTIVITY, SUBSET-EMBEDDING);
40178 // this pin binds the SEGMENTED-CONCATENATION structural
40179 // identity axis at the (char) scalar-row cross-row bond.
40180 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array(
40181 &Atom::ESCAPE_SOURCES,
40182 &Atom::NAMED_ESCAPE_TABLE,
40183 &Atom::SELF_ESCAPE_TABLE,
40184 false,
40185 );
40186 }
40187
40188 #[test]
40189 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_accepts_the_family_wide_substrate_right_column_triple(
40190 ) {
40191 // Positive pin on the family-wide (`Atom::ESCAPE_DECODED`,
40192 // `Atom::NAMED_ESCAPE_TABLE`, `Atom::SELF_ESCAPE_TABLE`,
40193 // `take_right_column: true`) RIGHT-column triple this helper is
40194 // applied to at compile time via the SECOND module-level
40195 // `const _:` witness. Runtime pin as a second-stage safety net.
40196 // The two family-wide RUNTIME pins (LEFT above, RIGHT here)
40197 // mirror the two module-level `const _:` witnesses at ONE
40198 // runtime posture per column.
40199 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array(
40200 &Atom::ESCAPE_DECODED,
40201 &Atom::NAMED_ESCAPE_TABLE,
40202 &Atom::SELF_ESCAPE_TABLE,
40203 true,
40204 );
40205 }
40206
40207 #[test]
40208 #[should_panic(
40209 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40210 )]
40211 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_head_left_column_head_position_divergence(
40212 ) {
40213 // NEGATIVE PIN — HEAD-SEGMENT LEFT-COLUMN head-position
40214 // (i == 0) corner: `arr[0] = 'z'` diverges from `head_table[0].0
40215 // = 'a'` on the FIRST head-segment position with
40216 // `take_right_column == false`. Pins the HEAD-LEFT-COLUMN arm
40217 // firing at the head.
40218 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40219 &['z', 'b', 'q'],
40220 &[('a', 'x'), ('b', 'y')],
40221 &['q'],
40222 false,
40223 );
40224 }
40225
40226 #[test]
40227 #[should_panic(
40228 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40229 )]
40230 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_head_left_column_tail_position_divergence(
40231 ) {
40232 // NEGATIVE PIN — HEAD-SEGMENT LEFT-COLUMN tail-position
40233 // (i == K-1) corner: divergence at the LAST head-segment
40234 // position MUST also fire. Pins the outer head-sweep bound
40235 // `while i < K` — a regression that walked `while i < K - 1`
40236 // would silently accept a tail-of-head divergence.
40237 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40238 &['a', 'z', 'q'],
40239 &[('a', 'x'), ('b', 'y')],
40240 &['q'],
40241 false,
40242 );
40243 }
40244
40245 #[test]
40246 #[should_panic(
40247 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40248 )]
40249 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_head_right_column_head_position_divergence(
40250 ) {
40251 // NEGATIVE PIN — HEAD-SEGMENT RIGHT-COLUMN head-position
40252 // (i == 0) corner: symmetric to the HEAD-LEFT-COLUMN arm at
40253 // the head with `take_right_column == true`. Pins the HEAD-
40254 // RIGHT-COLUMN arm firing at position 0.
40255 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40256 &['z', 'y', 'q'],
40257 &[('a', 'x'), ('b', 'y')],
40258 &['q'],
40259 true,
40260 );
40261 }
40262
40263 #[test]
40264 #[should_panic(
40265 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40266 )]
40267 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_head_right_column_tail_position_divergence(
40268 ) {
40269 // NEGATIVE PIN — HEAD-SEGMENT RIGHT-COLUMN tail-position
40270 // (i == K-1) corner: divergence at the LAST head-segment
40271 // RIGHT-column position MUST also fire. Symmetric to the
40272 // LEFT-column tail-position arm.
40273 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40274 &['x', 'z', 'q'],
40275 &[('a', 'x'), ('b', 'y')],
40276 &['q'],
40277 true,
40278 );
40279 }
40280
40281 #[test]
40282 #[should_panic(
40283 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40284 )]
40285 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_tail_head_position_divergence(
40286 ) {
40287 // NEGATIVE PIN — TAIL-SEGMENT head-position (j == 0) corner:
40288 // divergence at `arr[K + 0] = 'z'` vs `tail[0] = 'q'` fires
40289 // after the head-sweep succeeds. Pins the outer tail-sweep's
40290 // `while j < M` guard firing at j == 0 and correctly using
40291 // `K + j` for `arr` indexing. INDEPENDENT of
40292 // `take_right_column` — the tail is scalar-verbatim, not
40293 // column-projected.
40294 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 1, 2>(
40295 &['a', 'z', 'r'],
40296 &[('a', 'x')],
40297 &['q', 'r'],
40298 false,
40299 );
40300 }
40301
40302 #[test]
40303 #[should_panic(
40304 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40305 )]
40306 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_tail_tail_position_divergence(
40307 ) {
40308 // NEGATIVE PIN — TAIL-SEGMENT tail-position (j == M-1) corner:
40309 // divergence at the LAST tail-segment position MUST also fire.
40310 // Pins the outer tail-sweep bound `while j < M` — a regression
40311 // that walked `while j < M - 1` (dropping the last tail entry)
40312 // would silently accept a tail-of-tail divergence.
40313 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 1, 2>(
40314 &['a', 'q', 'z'],
40315 &[('a', 'x')],
40316 &['q', 'r'],
40317 false,
40318 );
40319 }
40320
40321 #[test]
40322 #[should_panic(
40323 expected = "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array"
40324 )]
40325 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panics_at_runtime_on_cardinality_mismatch(
40326 ) {
40327 // NEGATIVE PIN — CARDINALITY-MISMATCH corner: fires BEFORE any
40328 // per-position sweep at the FIRST guard. `K + M == 3 + 3 == 6`
40329 // vs `N == 5` — the caller's turbofish is inconsistent. A
40330 // mistyped ARITY doesn't degenerate into a silent truncation
40331 // of the segment sweep.
40332 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<5, 3, 3>(
40333 &['a', 'b', 'c', 'q', 'r'],
40334 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40335 &['q', 'r', 's'],
40336 false,
40337 );
40338 }
40339
40340 #[test]
40341 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(
40342 ) {
40343 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-LEFT-COLUMN-
40344 // DIVERGENCE arm: distinct axis vocabulary distinguishing this
40345 // helper's LEFT-column head-divergence from the paired-row
40346 // diagonal-tail sibling's `HEAD-SEGMENT-LEFT-DIVERGENCE` axis
40347 // (this helper's `-COLUMN-` infix distinguishes them on any
40348 // downstream substring search) AND from the sibling
40349 // `_columns_equal_char_arrays`'s `LEFT-COLUMN-DIVERGENCE`
40350 // axis (this helper's `HEAD-SEGMENT-` prefix distinguishes
40351 // them).
40352 let outcome = std::panic::catch_unwind(|| {
40353 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40354 &['z', 'b', 'q'],
40355 &[('a', 'x'), ('b', 'y')],
40356 &['q'],
40357 false,
40358 );
40359 });
40360 let payload = outcome.expect_err(
40361 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40362 must panic on a HEAD-SEGMENT LEFT-COLUMN head-position \
40363 divergence",
40364 );
40365 let msg = payload
40366 .downcast_ref::<&'static str>()
40367 .map(|s| (*s).to_owned())
40368 .or_else(|| payload.downcast_ref::<String>().cloned())
40369 .expect(
40370 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40371 panic payload must be a static &str or String",
40372 );
40373 assert!(
40374 msg.contains("HEAD-SEGMENT-LEFT-COLUMN-DIVERGENCE"),
40375 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40376 panic message {msg:?} must name the failed AXIS \
40377 (\"HEAD-SEGMENT-LEFT-COLUMN-DIVERGENCE\") for axis-\
40378 provenance-preserving failure diagnostics",
40379 );
40380 }
40381
40382 #[test]
40383 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(
40384 ) {
40385 // PANIC-MESSAGE PROVENANCE PIN — HEAD-SEGMENT-RIGHT-COLUMN-
40386 // DIVERGENCE arm: distinct axis vocabulary at the RIGHT-column
40387 // dual of the LEFT-column head-divergence provenance arm.
40388 let outcome = std::panic::catch_unwind(|| {
40389 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 2, 1>(
40390 &['z', 'y', 'q'],
40391 &[('a', 'x'), ('b', 'y')],
40392 &['q'],
40393 true,
40394 );
40395 });
40396 let payload = outcome.expect_err(
40397 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40398 must panic on a HEAD-SEGMENT RIGHT-COLUMN head-position \
40399 divergence",
40400 );
40401 let msg = payload
40402 .downcast_ref::<&'static str>()
40403 .map(|s| (*s).to_owned())
40404 .or_else(|| payload.downcast_ref::<String>().cloned())
40405 .expect(
40406 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40407 panic payload must be a static &str or String",
40408 );
40409 assert!(
40410 msg.contains("HEAD-SEGMENT-RIGHT-COLUMN-DIVERGENCE"),
40411 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40412 panic message {msg:?} must name the failed AXIS \
40413 (\"HEAD-SEGMENT-RIGHT-COLUMN-DIVERGENCE\") for axis-\
40414 provenance-preserving failure diagnostics",
40415 );
40416 }
40417
40418 #[test]
40419 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panic_message_names_the_helper_and_tail_segment_divergence_axis(
40420 ) {
40421 // PANIC-MESSAGE PROVENANCE PIN — TAIL-SEGMENT-DIVERGENCE arm:
40422 // distinct axis vocabulary distinguishing this helper's tail-
40423 // arm from the paired-row diagonal-tail sibling's `DIAGONAL-
40424 // TAIL-SEGMENT-{LEFT,RIGHT}-DIVERGENCE` axes (this helper's
40425 // tail arm omits `DIAGONAL-` and `-{LEFT,RIGHT}` — the scalar
40426 // tail is column-invariant).
40427 let outcome = std::panic::catch_unwind(|| {
40428 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<3, 1, 2>(
40429 &['a', 'z', 'r'],
40430 &[('a', 'x')],
40431 &['q', 'r'],
40432 false,
40433 );
40434 });
40435 let payload = outcome.expect_err(
40436 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40437 must panic on a TAIL-SEGMENT head-position divergence",
40438 );
40439 let msg = payload
40440 .downcast_ref::<&'static str>()
40441 .map(|s| (*s).to_owned())
40442 .or_else(|| payload.downcast_ref::<String>().cloned())
40443 .expect(
40444 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40445 panic payload must be a static &str or String",
40446 );
40447 assert!(
40448 msg.contains("TAIL-SEGMENT-DIVERGENCE"),
40449 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40450 panic message {msg:?} must name the failed AXIS \
40451 (\"TAIL-SEGMENT-DIVERGENCE\") for axis-provenance-\
40452 preserving failure diagnostics",
40453 );
40454 }
40455
40456 #[test]
40457 fn assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array_panic_message_names_the_helper_and_cardinality_mismatch_axis(
40458 ) {
40459 // PANIC-MESSAGE PROVENANCE PIN — CARDINALITY-MISMATCH arm:
40460 // fires BEFORE any per-position sweep at a distinct axis
40461 // vocabulary. Pins the FIRST guard's provenance so a
40462 // diagnostic reading the axis distinguishes ARITY drift from
40463 // CONTENT drift.
40464 let outcome = std::panic::catch_unwind(|| {
40465 assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array::<5, 3, 3>(
40466 &['a', 'b', 'c', 'q', 'r'],
40467 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40468 &['q', 'r', 's'],
40469 false,
40470 );
40471 });
40472 let payload = outcome.expect_err(
40473 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40474 must panic on a CARDINALITY-MISMATCH `K + M != N`",
40475 );
40476 let msg = payload
40477 .downcast_ref::<&'static str>()
40478 .map(|s| (*s).to_owned())
40479 .or_else(|| payload.downcast_ref::<String>().cloned())
40480 .expect(
40481 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40482 panic payload must be a static &str or String",
40483 );
40484 assert!(
40485 msg.contains("CARDINALITY-MISMATCH"),
40486 "assert_char_array_is_concatenation_of_char_pair_array_column_and_char_array \
40487 panic message {msg:?} must name the failed AXIS \
40488 (\"CARDINALITY-MISMATCH\") for axis-provenance-preserving \
40489 failure diagnostics",
40490 );
40491 }
40492
40493 // ── `assert_char_pair_array_within_char_pair_finite_set` — the
40494 // `(char, char)` product-element SUBSET-EMBEDDING verifier that
40495 // binds `arr ⊆ set` at compile time on the paired-array vocabulary
40496 // via CONJOINED-pair equality, peer to the (char) + (u8) +
40497 // (`&'static str`) scalar rows' SUBSET-EMBEDDING helpers on the
40498 // (element-type × contract-shape) matrix at the (subset-embedding)
40499 // column. The runtime test surface pins each of the helper's arms
40500 // (accept-empty, accept-singleton-in-set at three positions,
40501 // accept-arr-equals-set, accept-family-wide-substrate-subset on
40502 // the pinned `NAMED_ESCAPE_TABLE ⊆ ESCAPE_TABLE` pair, accept-
40503 // repeated-array-entries-in-set, reject-out-of-set-pair, reject-
40504 // terminal-out-of-set-pair, reject-cross-row-alias, panic-message-
40505 // provenance on the CHAR-PAIR-SUBSET-VIOLATION axis, negative pin
40506 // on the DELEGATED SET-side BIJECTIVITY well-formedness arm at
40507 // BOTH the LEFT-column and RIGHT-column collision corners) so a
40508 // regression that silently weakened the helper on ANY arm is
40509 // caught by the helper's OWN test surface rather than only
40510 // surfacing as a false-positive on some future subset-embedded
40511 // `[(char, char); N]` array's compound pin.
40512
40513 #[test]
40514 fn assert_char_pair_array_within_char_pair_finite_set_accepts_the_empty_array_within_any_set() {
40515 // Empty array `arr = []` at the `[(char, char); 0]` corner —
40516 // vacuously a subset of every well-formed BIJECTIVE set (no
40517 // `i` position exists to test). Cross-arity coverage on the
40518 // trivial ARRAY corner of the const-N generic across three
40519 // witness-set widths (empty, singleton, multi-entry) to pin
40520 // the helper's OUTER-sweep arm across the whole (`N == 0` ×
40521 // `M`) axis. Turbofish binding required because there's no
40522 // other cue for the const parameters on the empty array
40523 // literal. Sibling posture to
40524 // `assert_char_array_within_char_finite_set_accepts_the_empty_array_within_any_set`
40525 // on the (char) scalar row-dual peer — the two share the
40526 // trivial-arity arm across the (scalar, paired) element-type
40527 // partition on the (subset-embedding) column.
40528 assert_char_pair_array_within_char_pair_finite_set::<0, 0>(&[], &[]);
40529 assert_char_pair_array_within_char_pair_finite_set::<0, 1>(&[], &[('a', 'x')]);
40530 assert_char_pair_array_within_char_pair_finite_set::<0, 3>(
40531 &[],
40532 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40533 );
40534 }
40535
40536 #[test]
40537 fn assert_char_pair_array_within_char_pair_finite_set_accepts_singleton_array_when_pair_in_set()
40538 {
40539 // Singleton array `arr = [P]` at the `[(char, char); 1]`
40540 // corner MUST pass when `P ∈ set` by CONJOINED-pair equality.
40541 // Cross-position coverage: the pair can sit at the FIRST,
40542 // MIDDLE, or LAST position of the `set` — pins the INNER
40543 // `while j < M` sweep terminates at the first-match position
40544 // rather than always at position `0` OR always at position
40545 // `M - 1`. A regression that narrowed the inner sweep to
40546 // `j == 0` would silently reject singleton arrays hitting
40547 // non-first set positions. Sibling posture to
40548 // `assert_char_array_within_char_finite_set_accepts_singleton_array_when_char_in_set`
40549 // on the (char) scalar row-dual peer — the two share the
40550 // FIRST/MIDDLE/LAST sweep-position arm across the (scalar,
40551 // paired) element-type partition.
40552 assert_char_pair_array_within_char_pair_finite_set::<1, 3>(
40553 &[('a', 'x')],
40554 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40555 );
40556 assert_char_pair_array_within_char_pair_finite_set::<1, 3>(
40557 &[('b', 'y')],
40558 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40559 );
40560 assert_char_pair_array_within_char_pair_finite_set::<1, 3>(
40561 &[('c', 'z')],
40562 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40563 );
40564 }
40565
40566 #[test]
40567 fn assert_char_pair_array_within_char_pair_finite_set_accepts_arr_equals_set() {
40568 // Boundary corner where `arr` and `set` cover pair-for-pair
40569 // identical distinct-value sets — the SUBSET relation
40570 // degenerates to EQUALITY. Pins that the helper does NOT
40571 // gratuitously require the SUBSET to be PROPER (strict):
40572 // equal-multisets pass the SUBSET check. Sibling posture to
40573 // `assert_char_array_within_char_finite_set_accepts_arr_equals_set`
40574 // on the (char) scalar row-dual peer.
40575 assert_char_pair_array_within_char_pair_finite_set(&[('a', 'x')], &[('a', 'x')]);
40576 assert_char_pair_array_within_char_pair_finite_set(
40577 &[('a', 'x'), ('b', 'y')],
40578 &[('a', 'x'), ('b', 'y')],
40579 );
40580 }
40581
40582 #[test]
40583 fn assert_char_pair_array_within_char_pair_finite_set_accepts_the_family_wide_substrate_subset()
40584 {
40585 // Runtime cross-check that the ONE (subset, superset)
40586 // paired-array pair the substrate's module-level `const _`
40587 // witness pins at COMPILE time is a proper SUBSET embedding
40588 // at runtime too. The pair enforces the theorem at TWO stages
40589 // of the toolchain: the const witness fires FIRST at `cargo
40590 // check` (through the module-level `const _: () =
40591 // assert_char_pair_array_within_char_pair_finite_set::<3, 5>(...)`
40592 // line), this runtime pin catches the drift at `cargo test`
40593 // as a safety net. Sibling posture to
40594 // `assert_char_pair_array_bijective_accepts_every_family_wide_substrate_array`
40595 // which sweeps the two family-wide `[(char, char); N]` arrays
40596 // at the paired-array INJECTIVITY axis; this pin sweeps the
40597 // ONE (subset, superset) PAIR at the paired-array SUBSET-
40598 // EMBEDDING axis. Together the two pins bind the (paired
40599 // array, paired subset-embedding) 2-corner face on the
40600 // `(char, char)` row of the (element-type × contract-shape)
40601 // matrix.
40602 assert_char_pair_array_within_char_pair_finite_set::<3, 5>(
40603 &Atom::NAMED_ESCAPE_TABLE,
40604 &Atom::ESCAPE_TABLE,
40605 );
40606 }
40607
40608 #[test]
40609 fn assert_char_pair_array_within_char_pair_finite_set_accepts_repeated_array_entries_in_set() {
40610 // Peer corner to a (future-lift) `_covers_char_pair_finite_
40611 // set`: this helper permits duplicates in `arr` because
40612 // SUBSET-membership is a DISTINCT-value predicate — the
40613 // pair-multiset `[('a', 'x'), ('a', 'x'), ('b', 'y')]` is a
40614 // subset of the bijective set `{('a', 'x'), ('b', 'y'),
40615 // ('c', 'z')}` even though the array is not pairwise-
40616 // distinct. Pins that the helper does NOT gratuitously
40617 // require BIJECTIVITY on `arr` (the BIJECTIVITY axis is a
40618 // DIFFERENT compile-time contract bound by
40619 // `assert_char_pair_array_bijective`; combining both binds
40620 // BOTH axes). Sibling posture to
40621 // `assert_char_array_within_char_finite_set_accepts_repeated_array_entries_in_set`
40622 // on the (char) scalar row-dual peer.
40623 assert_char_pair_array_within_char_pair_finite_set(
40624 &[('a', 'x'), ('a', 'x'), ('b', 'y')],
40625 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40626 );
40627 }
40628
40629 #[test]
40630 #[should_panic(expected = "CHAR-PAIR-SUBSET-VIOLATION")]
40631 fn assert_char_pair_array_within_char_pair_finite_set_panics_at_runtime_on_out_of_set_pair() {
40632 // NEGATIVE PIN — CHAR-PAIR-SUBSET-VIOLATION corner: an array
40633 // carrying a single pair NOT in the target set MUST panic at
40634 // runtime with the CHAR-PAIR-SUBSET-VIOLATION-named message.
40635 // Pins the helper's OWN reject arm — a regression that
40636 // silently returned without panicking on an out-of-set pair
40637 // would slip through the compile-time witness's failure mode
40638 // too. The offending pair `('z', 'w')` is intentionally
40639 // chosen OUTSIDE the target set to pin the OUT-OF-SET drift
40640 // mode.
40641 assert_char_pair_array_within_char_pair_finite_set(
40642 &[('a', 'x'), ('z', 'w')],
40643 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40644 );
40645 }
40646
40647 #[test]
40648 #[should_panic(expected = "CHAR-PAIR-SUBSET-VIOLATION")]
40649 fn assert_char_pair_array_within_char_pair_finite_set_panics_at_runtime_on_terminal_out_of_set_pair(
40650 ) {
40651 // NEGATIVE PIN — terminal-position drift: an out-of-set pair
40652 // at the LAST array position MUST panic — pins that the
40653 // outer `while i < N` loop reaches `i = N - 1` (else the
40654 // terminal drift would slip through). A regression that
40655 // narrowed the outer sweep to `while i < N - 1` (off-by-one
40656 // on the OUTER bound) would silently accept this array.
40657 // Sibling posture to
40658 // `assert_char_array_within_char_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
40659 // on the (char) scalar row-dual peer — both bind the outer-
40660 // sweep terminal bound at the ONE array-side outer loop the
40661 // helper carries.
40662 assert_char_pair_array_within_char_pair_finite_set(
40663 &[('a', 'x'), ('b', 'y'), ('c', 'z'), ('z', 'w')],
40664 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40665 );
40666 }
40667
40668 #[test]
40669 #[should_panic(expected = "CHAR-PAIR-SUBSET-VIOLATION")]
40670 fn assert_char_pair_array_within_char_pair_finite_set_panics_at_runtime_on_cross_row_alias_pair(
40671 ) {
40672 // KEY LOAD-BEARING NEGATIVE PIN — CONJOINED-pair equality
40673 // gate corner: a pair whose LEFT column matches ONE set
40674 // entry's LEFT column AND whose RIGHT column matches a
40675 // DIFFERENT set entry's RIGHT column but the CONJOINED pair
40676 // itself is NOT in the set MUST panic. This is the exact
40677 // property that distinguishes the CONJOINED-pair SUBSET
40678 // helper from a per-column-membership check: a regression
40679 // that silently split the CONJOINED equality gate into TWO
40680 // separate per-column membership sweeps (one over the LEFT
40681 // column, one over the RIGHT column) would accept the pair
40682 // `('a', 'y')` here (`'a'` appears in the set's LEFT column
40683 // via `('a', 'x')`, `'y'` appears in the set's RIGHT column
40684 // via `('b', 'y')`), but the CONJOINED pair `('a', 'y')` is
40685 // NOT in the set. A drift from `&&` to `||` on the CONJOINED
40686 // gate would silently accept this cross-row aliasing pair.
40687 assert_char_pair_array_within_char_pair_finite_set(
40688 &[('a', 'y')],
40689 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40690 );
40691 }
40692
40693 #[test]
40694 fn assert_char_pair_array_within_char_pair_finite_set_panic_message_names_the_helper_and_char_pair_subset_violation_axis(
40695 ) {
40696 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-SUBSET-VIOLATION
40697 // arm: the panic message MUST begin with the helper's own
40698 // name AND identify the failed AXIS as "CHAR-PAIR-SUBSET-
40699 // VIOLATION" so downstream diagnostics route the drift back
40700 // to (a) the helper by string search on
40701 // `"assert_char_pair_array_within_char_pair_finite_set"` and
40702 // (b) the axis by string search on `"CHAR-PAIR-SUBSET-
40703 // VIOLATION"`. Sibling posture to
40704 // `assert_char_array_within_char_finite_set_panic_message_names_the_helper_and_char_subset_violation_axis`
40705 // on the (char) scalar row-dual peer's provenance pin — the
40706 // two pins together bind the (helper, failed-axis)
40707 // provenance pair at ONE test per SUBSET helper on the
40708 // (element-type × contract-shape) matrix's SUBSET column.
40709 // The axis-provenance string `"CHAR-PAIR-SUBSET-VIOLATION"`
40710 // is chosen DISTINCT from EVERY sibling helper's axis
40711 // vocabulary (`"CHAR-SUBSET-VIOLATION"` on the (char) scalar
40712 // row-dual SUBSET peer; `"SUBSET-VIOLATION"` on the (u8)
40713 // scalar row-dual finite-set SUBSET peer; `"STR-SUBSET-
40714 // VIOLATION"` on the (`&'static str`) scalar row-dual
40715 // SUBSET peer; `"RANGE-SUBSET-VIOLATION"` on the (u8) range
40716 // SUBSET peer; `"CHAR-DISJOINTNESS-VIOLATION"` / `"U8-
40717 // DISJOINTNESS-VIOLATION"` / `"STR-DISJOINTNESS-VIOLATION"`
40718 // on the DISJOINTNESS-column row peers; `"LEFT column"` /
40719 // `"RIGHT column"` on the paired-array BIJECTIVITY sibling)
40720 // so a diagnostic that names the failed axis routes
40721 // UNAMBIGUOUSLY to (a) this specific paired-array SUBSET-
40722 // embedding helper, (b) the `arr` argument as the drift
40723 // site rather than the `set` argument specifying the target
40724 // paired superset.
40725 let outcome = std::panic::catch_unwind(|| {
40726 assert_char_pair_array_within_char_pair_finite_set(
40727 &[('a', 'x'), ('z', 'w')],
40728 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40729 );
40730 });
40731 let payload = outcome.expect_err(
40732 "assert_char_pair_array_within_char_pair_finite_set must \
40733 panic on an out-of-set pair — the reject-out-of-set arm \
40734 is the sole CHAR-PAIR-SUBSET-VIOLATION failure mode of \
40735 the helper",
40736 );
40737 let msg = payload
40738 .downcast_ref::<&'static str>()
40739 .map(|s| (*s).to_owned())
40740 .or_else(|| payload.downcast_ref::<String>().cloned())
40741 .expect(
40742 "assert_char_pair_array_within_char_pair_finite_set \
40743 panic payload must be a static &str or String",
40744 );
40745 assert!(
40746 msg.contains("assert_char_pair_array_within_char_pair_finite_set"),
40747 "assert_char_pair_array_within_char_pair_finite_set \
40748 panic message {msg:?} must name the helper for \
40749 provenance-preserving failure diagnostics",
40750 );
40751 assert!(
40752 msg.contains("CHAR-PAIR-SUBSET-VIOLATION"),
40753 "assert_char_pair_array_within_char_pair_finite_set \
40754 panic message {msg:?} must name the failed AXIS \
40755 (\"CHAR-PAIR-SUBSET-VIOLATION\") for axis-provenance-\
40756 preserving failure diagnostics",
40757 );
40758 }
40759
40760 #[test]
40761 #[should_panic(expected = "assert_char_pair_array_bijective")]
40762 fn assert_char_pair_array_within_char_pair_finite_set_panics_on_malformed_target_set_left_column_collision(
40763 ) {
40764 // NEGATIVE PIN — DELEGATED SET-side BIJECTIVITY well-
40765 // formedness at the LEFT-column collision corner: a
40766 // malformed target-set spec `[('a', 'x'), ('a', 'y')]`
40767 // (LEFT-column duplicate `'a'`) fed into the ARRAY-side
40768 // within helper MUST panic on the DELEGATED bijectivity arm
40769 // BEFORE the CHAR-PAIR-SUBSET-VIOLATION arm fires. Pins the
40770 // delegation chain: a regression that dropped the
40771 // `assert_char_pair_array_bijective(set)` call at the top of
40772 // `assert_char_pair_array_within_char_pair_finite_set` would
40773 // silently accept a malformed set and produce a false-
40774 // positive verdict on any `arr` embedded in the distinct-
40775 // value bijective subset. The panic message here surfaces
40776 // from the SIBLING helper (containing the paired-array
40777 // BIJECTIVITY helper's `"assert_char_pair_array_bijective"`
40778 // panic-name prefix rather than a bespoke `"SET-NOT-
40779 // BIJECTIVE"` axis string) because the paired row's
40780 // delegation reuses the ARRAY-side BIJECTIVITY helper
40781 // directly per the design choice documented on the SET-side-
40782 // well-formedness section of the helper's docstring. Sibling
40783 // posture to
40784 // `assert_char_array_within_char_finite_set_panics_on_malformed_target_set_spec`
40785 // on the (char) scalar row-dual peer's delegated-SET-well-
40786 // formedness pin. Peer corner to the RIGHT-column companion
40787 // pin below.
40788 assert_char_pair_array_within_char_pair_finite_set::<1, 2>(
40789 &[('a', 'x')],
40790 &[('a', 'x'), ('a', 'y')],
40791 );
40792 }
40793
40794 #[test]
40795 #[should_panic(expected = "assert_char_pair_array_bijective")]
40796 fn assert_char_pair_array_within_char_pair_finite_set_panics_on_malformed_target_set_right_column_collision(
40797 ) {
40798 // NEGATIVE PIN — DELEGATED SET-side BIJECTIVITY well-
40799 // formedness at the RIGHT-column collision corner: a
40800 // malformed target-set spec `[('a', 'x'), ('b', 'x')]`
40801 // (RIGHT-column duplicate `'x'`) fed into the ARRAY-side
40802 // within helper MUST panic on the DELEGATED bijectivity arm
40803 // BEFORE the CHAR-PAIR-SUBSET-VIOLATION arm fires. Column-
40804 // symmetric sibling to the LEFT-column pin above — a
40805 // regression that silently narrowed the delegated
40806 // bijectivity sweep to only the LEFT column (dropping the
40807 // RIGHT-column injectivity half) would slip a RIGHT-column-
40808 // colliding set past the delegation while still binding a
40809 // LEFT-column-colliding one. The two pins together bind the
40810 // FULL bijectivity delegation across BOTH columns of the
40811 // paired-array well-formedness contract.
40812 assert_char_pair_array_within_char_pair_finite_set::<1, 2>(
40813 &[('a', 'x')],
40814 &[('a', 'x'), ('b', 'x')],
40815 );
40816 }
40817
40818 // ── `assert_char_pair_arrays_disjoint` — the CHAR-PAIR-DISJOINTNESS-
40819 // VIOLATION verifier that binds `a ∩ b = ∅` at compile time on the
40820 // paired-array vocabulary via CONJOINED-pair equality, peer to the
40821 // (char) + (u8) + (`&'static str`) SCALAR rows' DISJOINTNESS helpers
40822 // on the (element-type × contract-shape) matrix at the (disjointness)
40823 // column. Contract-orthogonal peer to
40824 // `assert_char_pair_array_within_char_pair_finite_set` on the
40825 // (SUBSET-EMBEDDING, DISJOINTNESS) axis of the (contract-shape)
40826 // column on the SAME paired-array row. The runtime test surface
40827 // pins each of the helper's arms (accept-empty × 3, accept-disjoint
40828 // singletons, accept-family-wide-substrate-pair on the pinned
40829 // `NAMED_ESCAPE_TABLE` disjoint from SELF-as-pairs partition,
40830 // accept-per-column-alias-only corner (the CONJOINED-gate load-
40831 // bearing NON-INSTANCE of the stricter per-column disjointness
40832 // contract), reject-full-pair-collision, reject-terminal-position
40833 // collision, argument-order symmetry, panic-message provenance on
40834 // the CHAR-PAIR-DISJOINTNESS-VIOLATION axis) so a regression that
40835 // silently weakened the helper on ANY arm is caught by the helper's
40836 // OWN test surface rather than only surfacing as a false-positive on
40837 // some future disjoint `[(char, char); N]` paired-array pair's
40838 // compound pin.
40839
40840 #[test]
40841 fn assert_char_pair_arrays_disjoint_accepts_both_empty_arrays() {
40842 // Empty × empty at the `[(char, char); 0] × [(char, char); 0]`
40843 // corner — vacuously disjoint (no `(i, j)` position pair exists
40844 // to test). The module-level `const _: () =
40845 // assert_char_pair_arrays_disjoint(&[], &[])` witness would bind
40846 // this at compile time; this runtime pin catches the drift a
40847 // second time at test-run stage as a safety net. Turbofish
40848 // binding required because there's no other cue for the const
40849 // parameters on the two empty array literals. Sibling posture
40850 // to `assert_char_arrays_disjoint_accepts_both_empty_arrays` on
40851 // the (char) SCALAR row-dual peer.
40852 assert_char_pair_arrays_disjoint::<0, 0>(&[], &[]);
40853 }
40854
40855 #[test]
40856 fn assert_char_pair_arrays_disjoint_accepts_either_side_empty() {
40857 // Empty × non-empty (and non-empty × empty) at the outer-loop-
40858 // vacuous corners — the outer `while i < N` sweep terminates
40859 // immediately when `N == 0`, and the outer sweep executing with
40860 // a non-empty `a` against an empty `b` finds no `j` to test
40861 // (inner sweep vacuous). Cross-position coverage on the two
40862 // vacuous outer arms. Sibling posture to
40863 // `assert_char_arrays_disjoint_accepts_either_side_empty` on the
40864 // (char) SCALAR row-dual peer — the two share the vacuous-arm
40865 // arm across the (scalar, paired) element-type partition on the
40866 // (disjointness) column.
40867 assert_char_pair_arrays_disjoint::<0, 3>(&[], &[('a', 'x'), ('b', 'y'), ('c', 'z')]);
40868 assert_char_pair_arrays_disjoint::<3, 0>(&[('a', 'x'), ('b', 'y'), ('c', 'z')], &[]);
40869 }
40870
40871 #[test]
40872 fn assert_char_pair_arrays_disjoint_accepts_disjoint_singletons() {
40873 // Singleton × singleton at the `[(char, char); 1] × [(char,
40874 // char); 1]` corner with the two pairs distinct across BOTH
40875 // columns — the minimal non-vacuous accept arm. Pins the
40876 // CONJOINED-pair equality gate does NOT gratuitously match when
40877 // BOTH columns differ. Sibling posture to
40878 // `assert_char_arrays_disjoint_accepts_disjoint_singletons` on
40879 // the (char) SCALAR row-dual peer.
40880 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('b', 'y')]);
40881 }
40882
40883 #[test]
40884 fn assert_char_pair_arrays_disjoint_accepts_the_family_wide_substrate_pair() {
40885 // Runtime cross-check that the ONE (a, b) paired-array pair the
40886 // substrate's module-level `const _` witness pins at COMPILE
40887 // time is a proper DISJOINT relation at runtime too. The pair
40888 // enforces the theorem at TWO stages of the toolchain: the
40889 // const witness fires FIRST at `cargo check` (through the
40890 // module-level `const _: () = assert_char_pair_arrays_disjoint::
40891 // <3, 2>(...)` line), this runtime pin catches the drift at
40892 // `cargo test` as a safety net. Sibling posture to
40893 // `assert_char_pair_array_within_char_pair_finite_set_accepts_
40894 // the_family_wide_substrate_subset` which sweeps the ONE (arr,
40895 // set) pair at the paired-array SUBSET-EMBEDDING axis; this pin
40896 // sweeps the ONE (a, b) pair at the paired-array DISJOINTNESS
40897 // axis. Together the two pins bind the (paired subset-
40898 // embedding, paired disjointness) 2-corner face on the
40899 // `(char, char)` row of the (element-type × contract-shape)
40900 // matrix. The `NAMED_ESCAPE_TABLE` disjoint from SELF-as-pairs
40901 // partition of `ESCAPE_TABLE` is the substrate-load-bearing
40902 // instance: a NAMED escape source `('n' / 't' / 'r')` cannot
40903 // simultaneously ALSO be a SELF-escape source `STR_DELIMITER /
40904 // STR_ESCAPE_LEAD` — the two sub-vocabularies of
40905 // `Atom::decode_str_escape` MUST partition disjointly to route
40906 // each escape byte through EXACTLY ONE arm.
40907 assert_char_pair_arrays_disjoint::<3, 2>(
40908 &Atom::NAMED_ESCAPE_TABLE,
40909 &[
40910 (Atom::SELF_ESCAPE_TABLE[0], Atom::SELF_ESCAPE_TABLE[0]),
40911 (Atom::SELF_ESCAPE_TABLE[1], Atom::SELF_ESCAPE_TABLE[1]),
40912 ],
40913 );
40914 }
40915
40916 #[test]
40917 fn assert_char_pair_arrays_disjoint_accepts_per_column_alias_when_conjoined_pair_differs() {
40918 // KEY LOAD-BEARING ACCEPT PIN — CONJOINED-pair equality gate
40919 // NON-INSTANCE corner: two paired arrays whose LEFT columns
40920 // share an entry OR whose RIGHT columns share an entry but the
40921 // CONJOINED pairs themselves differ MUST be accepted as
40922 // disjoint. This is the exact property that distinguishes the
40923 // CONJOINED-pair DISJOINTNESS helper from a stricter per-column
40924 // disjointness check: a regression that split the CONJOINED
40925 // equality gate into TWO separate per-column membership sweeps
40926 // (one over the LEFT column, one over the RIGHT column) would
40927 // REJECT the pair `(&[('a', 'x')], &[('a', 'y')])` here as a
40928 // false-positive collision (`'a'` appears in BOTH arrays' LEFT
40929 // columns via `('a', 'x')` and `('a', 'y')` respectively), but
40930 // the CONJOINED pairs `('a', 'x')` and `('a', 'y')` themselves
40931 // differ so the paired arrays ARE disjoint. A drift from `&&`
40932 // to `||` on the CONJOINED gate would silently REJECT this
40933 // per-column-alias corner as a false-positive collision.
40934 // Symmetric pin on the RIGHT-column-alias-only sibling corner
40935 // — the two together bind the CONJOINED-gate semantics across
40936 // BOTH column-alias arms. Peer to
40937 // `assert_char_pair_array_within_char_pair_finite_set_panics_at_
40938 // runtime_on_cross_row_alias_pair` on the SUBSET-EMBEDDING
40939 // sibling's dual corner — where the SUBSET helper REJECTS the
40940 // cross-row alias pair as NOT-in-set, this DISJOINTNESS helper
40941 // ACCEPTS the analogous per-column alias as disjoint (the two
40942 // contract shapes flip the accept/reject arm across the
40943 // CONJOINED-gate corner).
40944 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('a', 'y')]);
40945 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('b', 'x')]);
40946 }
40947
40948 #[test]
40949 #[should_panic(expected = "CHAR-PAIR-DISJOINTNESS-VIOLATION")]
40950 fn assert_char_pair_arrays_disjoint_panics_at_runtime_on_collision() {
40951 // NEGATIVE PIN — CHAR-PAIR-DISJOINTNESS-VIOLATION corner: two
40952 // paired arrays sharing a CONJOINED pair MUST panic at runtime
40953 // with the CHAR-PAIR-DISJOINTNESS-VIOLATION-named message.
40954 // Pins the helper's OWN reject arm — a regression that
40955 // silently returned without panicking on a shared pair would
40956 // slip through the compile-time witness's failure mode too.
40957 // The offending pair `('b', 'y')` is intentionally chosen to
40958 // appear in BOTH arrays at their MIDDLE positions to pin that
40959 // the reject arm fires from an interior-position collision (not
40960 // only a first-position or last-position degenerate corner).
40961 assert_char_pair_arrays_disjoint(
40962 &[('a', 'x'), ('b', 'y'), ('c', 'z')],
40963 &[('p', 'q'), ('b', 'y'), ('r', 's')],
40964 );
40965 }
40966
40967 #[test]
40968 #[should_panic(expected = "CHAR-PAIR-DISJOINTNESS-VIOLATION")]
40969 fn assert_char_pair_arrays_disjoint_panics_at_runtime_on_terminal_position_collision() {
40970 // NEGATIVE PIN — terminal-position drift: a shared CONJOINED
40971 // pair at the LAST position of BOTH arrays MUST panic — pins
40972 // that BOTH the outer `while i < N` and inner `while j < M`
40973 // loops reach their terminal indices (else the terminal drift
40974 // would slip through). A regression that narrowed EITHER
40975 // bound to `< N - 1` / `< M - 1` (off-by-one on the OUTER or
40976 // INNER bound) would silently accept this array pair. Peer
40977 // sibling posture to
40978 // `assert_char_pair_array_within_char_pair_finite_set_panics_
40979 // at_runtime_on_terminal_out_of_set_pair` on the SUBSET-
40980 // EMBEDDING helper — both bind the terminal-position sweep
40981 // arm on the paired-array row.
40982 assert_char_pair_arrays_disjoint(&[('a', 'x'), ('z', 'w')], &[('p', 'q'), ('z', 'w')]);
40983 }
40984
40985 #[test]
40986 fn assert_char_pair_arrays_disjoint_is_symmetric_in_argument_order() {
40987 // SYMMETRY PIN — the disjointness relation is symmetric in
40988 // `(a, b)` so swapping arguments at the call site MUST produce
40989 // the SAME verdict (accept remains accept, reject remains
40990 // reject). Pins the two-loop sweep visits every `(i, j) ∈
40991 // [0, N) × [0, M)` pair without gratuitous argument-order
40992 // preference. Sibling posture to
40993 // `assert_char_arrays_disjoint_is_symmetric_in_argument_order`
40994 // on the (char) SCALAR row-dual peer — the two share the
40995 // symmetric-relation arm across the (scalar, paired) element-
40996 // type partition on the (disjointness) column.
40997 assert_char_pair_arrays_disjoint(&[('a', 'x'), ('b', 'y')], &[('c', 'z'), ('d', 'w')]);
40998 assert_char_pair_arrays_disjoint(&[('c', 'z'), ('d', 'w')], &[('a', 'x'), ('b', 'y')]);
40999 let swap_left_rejects = std::panic::catch_unwind(|| {
41000 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('a', 'x')]);
41001 })
41002 .is_err();
41003 let swap_right_rejects = std::panic::catch_unwind(|| {
41004 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('a', 'x')]);
41005 })
41006 .is_err();
41007 assert!(
41008 swap_left_rejects && swap_right_rejects,
41009 "assert_char_pair_arrays_disjoint must reject a shared \
41010 CONJOINED pair regardless of which argument carries the \
41011 `a` role vs. the `b` role — the disjointness relation \
41012 is symmetric in argument order",
41013 );
41014 }
41015
41016 #[test]
41017 fn assert_char_pair_arrays_disjoint_panic_message_names_the_helper_and_char_pair_disjointness_violation_axis(
41018 ) {
41019 // PANIC-MESSAGE PROVENANCE PIN — CHAR-PAIR-DISJOINTNESS-
41020 // VIOLATION arm: the panic message MUST begin with the
41021 // helper's own name AND identify the failed AXIS as "CHAR-
41022 // PAIR-DISJOINTNESS-VIOLATION" so downstream diagnostics
41023 // route the drift back to (a) the helper by string search on
41024 // `"assert_char_pair_arrays_disjoint"` and (b) the axis by
41025 // string search on `"CHAR-PAIR-DISJOINTNESS-VIOLATION"`.
41026 // Sibling posture to
41027 // `assert_char_arrays_disjoint_panic_message_names_the_helper_
41028 // and_char_disjointness_violation_axis` +
41029 // `assert_str_arrays_disjoint_panic_message_names_the_helper_
41030 // and_str_disjointness_violation_axis` +
41031 // `assert_u8_arrays_disjoint_panic_message_names_the_helper_
41032 // and_u8_disjointness_violation_axis` on the SCALAR row-dual
41033 // provenance pins. The axis-provenance string `"CHAR-PAIR-
41034 // DISJOINTNESS-VIOLATION"` is chosen DISTINCT from EVERY
41035 // sibling helper's axis vocabulary (`"CHAR-DISJOINTNESS-
41036 // VIOLATION"` on the (char) SCALAR row-dual DISJOINTNESS
41037 // peer; `"U8-DISJOINTNESS-VIOLATION"` on the (u8) SCALAR row-
41038 // dual peer; `"STR-DISJOINTNESS-VIOLATION"` on the
41039 // (`&'static str`) SCALAR row-dual peer; `"CHAR-PAIR-SUBSET-
41040 // VIOLATION"` on the paired-array SUBSET-embedding sibling;
41041 // `"LEFT column"` / `"RIGHT column"` on the paired-array
41042 // BIJECTIVITY sibling) so a diagnostic that names the failed
41043 // axis routes UNAMBIGUOUSLY to (a) this specific paired-array
41044 // DISJOINTNESS helper, (b) the failed axis-shape by the
41045 // `"DISJOINTNESS-VIOLATION"` suffix stem shared with the
41046 // three SCALAR row-dual siblings.
41047 let outcome = std::panic::catch_unwind(|| {
41048 assert_char_pair_arrays_disjoint(&[('a', 'x')], &[('a', 'x')]);
41049 });
41050 let payload = outcome.expect_err(
41051 "assert_char_pair_arrays_disjoint must panic on a shared \
41052 CONJOINED pair — the reject-on-collision arm is the sole \
41053 CHAR-PAIR-DISJOINTNESS-VIOLATION failure mode of the \
41054 helper",
41055 );
41056 let msg = payload
41057 .downcast_ref::<&'static str>()
41058 .map(|s| (*s).to_owned())
41059 .or_else(|| payload.downcast_ref::<String>().cloned())
41060 .expect(
41061 "assert_char_pair_arrays_disjoint panic payload must \
41062 be a static &str or String",
41063 );
41064 assert!(
41065 msg.contains("assert_char_pair_arrays_disjoint"),
41066 "assert_char_pair_arrays_disjoint panic message {msg:?} \
41067 must name the helper for provenance-preserving failure \
41068 diagnostics",
41069 );
41070 assert!(
41071 msg.contains("CHAR-PAIR-DISJOINTNESS-VIOLATION"),
41072 "assert_char_pair_arrays_disjoint panic message {msg:?} \
41073 must name the failed AXIS (\"CHAR-PAIR-DISJOINTNESS-\
41074 VIOLATION\") for axis-provenance-preserving failure \
41075 diagnostics",
41076 );
41077 }
41078
41079 // ── `assert_u8_array_covers_inclusive_range` — the SURJECTIVITY-
41080 // onto-a-range peer of `assert_u8_array_pairwise_distinct`'s
41081 // INJECTIVITY axis on the SAME `u8` cache-key element type. Where
41082 // the four sibling distinctness/bijectivity helpers close the
41083 // pairwise-DISTINCTNESS axis of the substrate's typed-array
41084 // vocabulary, this range-coverage helper opens the SURJECTIVITY-
41085 // onto-a-range axis at four family-wide `[u8; N]` arrays whose
41086 // distinct-value sets are intentionally-closed inclusive ranges
41087 // (`AtomKind::HASH_DISCRIMINATORS` covers `{0..=5}`, `QuoteForm::
41088 // HASH_DISCRIMINATORS` covers `{3..=6}`, `UnquoteForm::HASH_
41089 // DISCRIMINATORS` covers `{5..=6}`, `SexpShape::HASH_DISCRIMINATORS`
41090 // covers `{0..=6}` with a load-bearing six-fold collapse at `1u8`).
41091 // The runtime test surface matches the sibling-helpers' shape
41092 // (accept-singleton, accept-with-duplicates, accept-every-family-
41093 // wide-substrate-array, reject-above-HI, reject-below-LO, reject-
41094 // missing-range-byte, panic-message-provenance on the RANGE-BOUND
41095 // axis, panic-message-provenance on the FULL-COVERAGE axis) split
41096 // across BOTH axes so a regression that silently weakens the
41097 // helper on EITHER axis (e.g. dropping the `arr[i] < LO || arr[i]
41098 // > HI` guard, or dropping the `while cur <= HI` full-coverage
41099 // sweep) is caught by the helper's OWN test surface rather than
41100 // only surfacing as a false-positive on some future range-
41101 // covering `[u8; N]` array's coverage pin.
41102
41103 #[test]
41104 fn assert_u8_array_covers_inclusive_range_accepts_the_singleton_range() {
41105 // Singleton range `{K..=K}` at the `[u8; 1]` corner —
41106 // vacuously covering (the one entry MUST equal the one range
41107 // byte). Cross-arity coverage on the trivial-range corner of
41108 // the const-N generic; simultaneously pins BOTH axes (RANGE-
41109 // BOUND: `K in [K, K]`; FULL-COVERAGE: `K` appears in the
41110 // singleton array) at the smallest witness.
41111 assert_u8_array_covers_inclusive_range::<1, 7, 7>(&[7u8]);
41112 assert_u8_array_covers_inclusive_range::<1, 0, 0>(&[0u8]);
41113 assert_u8_array_covers_inclusive_range::<1, 255, 255>(&[255u8]);
41114 }
41115
41116 #[test]
41117 fn assert_u8_array_covers_inclusive_range_accepts_arrays_with_duplicates() {
41118 // KEY LOAD-BEARING PIN: duplicates in the entries are ACCEPTED
41119 // — the range-coverage contract is non-injective on the
41120 // ENTRIES axis, only requires each RANGE byte to appear. This
41121 // is the exact property that distinguishes this helper from
41122 // its `assert_u8_array_pairwise_distinct` sibling and lets
41123 // `SexpShape::HASH_DISCRIMINATORS`'s six-fold collapse at
41124 // `1u8` bind a compile-time coverage witness despite failing
41125 // distinctness. A regression that added a pairwise-distinct
41126 // guard on the entries axis (accidentally merging the two
41127 // sibling helpers' contracts) would fail-loudly here.
41128 assert_u8_array_covers_inclusive_range::<4, 0, 2>(&[0u8, 1u8, 1u8, 2u8]);
41129 assert_u8_array_covers_inclusive_range::<7, 0, 2>(&[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 2u8]);
41130 }
41131
41132 #[test]
41133 fn assert_u8_array_covers_inclusive_range_accepts_every_family_wide_substrate_array() {
41134 // Runtime cross-check that the SAME four range-covering arrays
41135 // the module-level `const _: () = ...` witnesses cover at
41136 // COMPILE time are range-covering. A regression that removes
41137 // ONE of the `const _` witnesses would still leave THIS
41138 // runtime pin as a safety net; the const witness fires FIRST
41139 // at `cargo check`, this runtime pin catches the drift at
41140 // `cargo test`. The pair enforces the theorem at TWO stages of
41141 // the toolchain. Sibling posture to
41142 // `assert_char_pair_array_bijective_accepts_every_family_wide_
41143 // substrate_array` at the paired-array vocabulary.
41144 assert_u8_array_covers_inclusive_range::<12, 0, 6>(
41145 &crate::error::SexpShape::HASH_DISCRIMINATORS,
41146 );
41147 assert_u8_array_covers_inclusive_range::<6, 0, 5>(&AtomKind::HASH_DISCRIMINATORS);
41148 assert_u8_array_covers_inclusive_range::<4, 3, 6>(&QuoteForm::HASH_DISCRIMINATORS);
41149 assert_u8_array_covers_inclusive_range::<2, 5, 6>(
41150 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
41151 );
41152 }
41153
41154 #[test]
41155 #[should_panic(expected = "OUT-OF-RANGE")]
41156 fn assert_u8_array_covers_inclusive_range_panics_at_runtime_on_entry_above_hi() {
41157 // NEGATIVE PIN — RANGE-BOUND above-HI corner: an entry
41158 // `HI + 1` MUST panic at runtime with the RANGE-BOUND-named
41159 // message. Pins the helper's OWN reject-above-HI arm — a
41160 // regression that silently returned without panicking on an
41161 // out-of-range entry above the upper bound would slip through
41162 // the compile-time witnesses' failure mode too.
41163 assert_u8_array_covers_inclusive_range::<3, 0, 2>(&[0u8, 1u8, 3u8]);
41164 }
41165
41166 #[test]
41167 #[should_panic(expected = "OUT-OF-RANGE")]
41168 fn assert_u8_array_covers_inclusive_range_panics_at_runtime_on_entry_below_lo() {
41169 // NEGATIVE PIN — RANGE-BOUND below-LO corner: an entry
41170 // `LO - 1` MUST panic at runtime with the RANGE-BOUND-named
41171 // message. Symmetric sibling to the above-HI pin — a
41172 // regression that dropped the `arr[i] < LO` half of the
41173 // OR-disjunction guard (leaving only the `arr[i] > HI` half)
41174 // would silently accept below-LO entries.
41175 assert_u8_array_covers_inclusive_range::<3, 1, 3>(&[0u8, 1u8, 2u8]);
41176 }
41177
41178 #[test]
41179 #[should_panic(expected = "MISSING")]
41180 fn assert_u8_array_covers_inclusive_range_panics_at_runtime_on_missing_range_byte() {
41181 // NEGATIVE PIN — FULL-COVERAGE corner: a range byte NOT
41182 // reached by any entry MUST panic at runtime with the FULL-
41183 // COVERAGE-named message. Pins the helper's OWN reject-
41184 // incomplete-coverage arm — a regression that silently
41185 // returned without panicking on an incomplete-coverage array
41186 // (e.g. dropping the `while cur <= HI` sweep entirely, or
41187 // narrowing it to `while cur < HI`) would slip through the
41188 // compile-time witnesses' failure mode too. The three entries
41189 // stay within `[0, 3]` (satisfying RANGE-BOUND) but skip the
41190 // middle byte `2u8` — the panic MUST fire specifically on
41191 // the FULL-COVERAGE axis, not on the RANGE-BOUND axis.
41192 assert_u8_array_covers_inclusive_range::<3, 0, 3>(&[0u8, 1u8, 3u8]);
41193 }
41194
41195 #[test]
41196 fn assert_u8_array_covers_inclusive_range_panic_message_names_the_helper_and_range_bound_axis()
41197 {
41198 // PANIC-MESSAGE PROVENANCE PIN — RANGE-BOUND arm: the panic
41199 // message MUST begin with the helper's own name AND identify
41200 // the failed AXIS as "OUT-OF-RANGE" so downstream diagnostics
41201 // route the drift back to (a) the helper by string search on
41202 // `"assert_u8_array_covers_inclusive_range"` and (b) the axis
41203 // by string search on `"OUT-OF-RANGE"`. Sibling posture to
41204 // `assert_char_pair_array_bijective_panic_message_names_the_
41205 // helper_and_left_column` on the paired-array bijectivity
41206 // helper's LEFT-column arm — both bind the (helper, failed-
41207 // axis) provenance pair at ONE test per axis.
41208 let outcome = std::panic::catch_unwind(|| {
41209 assert_u8_array_covers_inclusive_range::<3, 0, 2>(&[0u8, 1u8, 3u8]);
41210 });
41211 let payload = outcome.expect_err(
41212 "assert_u8_array_covers_inclusive_range must panic on an \
41213 out-of-range entry — the reject-out-of-range arm is one \
41214 of the two failure modes of the helper",
41215 );
41216 let msg = payload
41217 .downcast_ref::<&'static str>()
41218 .map(|s| (*s).to_owned())
41219 .or_else(|| payload.downcast_ref::<String>().cloned())
41220 .expect(
41221 "assert_u8_array_covers_inclusive_range panic payload \
41222 must be a static &str or String",
41223 );
41224 assert!(
41225 msg.contains("assert_u8_array_covers_inclusive_range"),
41226 "assert_u8_array_covers_inclusive_range RANGE-BOUND panic \
41227 message {msg:?} must name the helper for provenance-\
41228 preserving failure diagnostics",
41229 );
41230 assert!(
41231 msg.contains("OUT-OF-RANGE"),
41232 "assert_u8_array_covers_inclusive_range RANGE-BOUND panic \
41233 message {msg:?} must name the failed AXIS (\"OUT-OF-\
41234 RANGE\") for axis-provenance-preserving failure \
41235 diagnostics",
41236 );
41237 }
41238
41239 #[test]
41240 fn assert_u8_array_covers_inclusive_range_panic_message_names_the_helper_and_full_coverage_axis(
41241 ) {
41242 // PANIC-MESSAGE PROVENANCE PIN — FULL-COVERAGE arm: the panic
41243 // message MUST begin with the helper's own name AND identify
41244 // the failed AXIS as "MISSING" so downstream diagnostics route
41245 // the drift back to (a) the helper by string search and (b)
41246 // the axis by string search on `"MISSING"`. Axis-symmetric
41247 // sibling of the RANGE-BOUND panic-message provenance pin
41248 // above — a regression that silently unified the two panic
41249 // sites into ONE axis-anonymous message would collapse EITHER
41250 // this pin's `"MISSING"` substring assertion OR the sibling
41251 // pin's `"OUT-OF-RANGE"` substring assertion.
41252 let outcome = std::panic::catch_unwind(|| {
41253 assert_u8_array_covers_inclusive_range::<3, 0, 3>(&[0u8, 1u8, 3u8]);
41254 });
41255 let payload = outcome.expect_err(
41256 "assert_u8_array_covers_inclusive_range must panic on a \
41257 missing range byte — the reject-incomplete-coverage arm \
41258 is one of the two failure modes of the helper",
41259 );
41260 let msg = payload
41261 .downcast_ref::<&'static str>()
41262 .map(|s| (*s).to_owned())
41263 .or_else(|| payload.downcast_ref::<String>().cloned())
41264 .expect(
41265 "assert_u8_array_covers_inclusive_range panic payload \
41266 must be a static &str or String",
41267 );
41268 assert!(
41269 msg.contains("assert_u8_array_covers_inclusive_range"),
41270 "assert_u8_array_covers_inclusive_range FULL-COVERAGE panic \
41271 message {msg:?} must name the helper for provenance-\
41272 preserving failure diagnostics",
41273 );
41274 assert!(
41275 msg.contains("MISSING"),
41276 "assert_u8_array_covers_inclusive_range FULL-COVERAGE panic \
41277 message {msg:?} must name the failed AXIS (\"MISSING\") \
41278 for axis-provenance-preserving failure diagnostics",
41279 );
41280 }
41281
41282 // ── `assert_u8_array_covers_finite_set` — the non-contiguous-
41283 // finite-set peer of `assert_u8_array_covers_inclusive_range` on
41284 // the (contiguity) axis of the substrate's SURJECTIVITY-axis
41285 // coverage helpers. Where the sibling range-coverage helper closes
41286 // the contiguous corner of the target-partition axis at four
41287 // range-covering `[u8; N]` HASH_DISCRIMINATORS arrays, this helper
41288 // closes the non-contiguous corner at
41289 // `StructuralKind::HASH_DISCRIMINATORS` (`[u8; 2]` covering
41290 // `{0, 2}` with a load-bearing gap at `1u8` where the atomic-
41291 // carve outer marker lives). The runtime test surface matches
41292 // the sibling-helpers' shape (accept-singleton, accept-with-
41293 // duplicates, accept-every-family-wide-substrate-array, reject-
41294 // out-of-set, reject-set-byte-missing, panic-message-provenance
41295 // on the OUT-OF-SET axis, panic-message-provenance on the SET-
41296 // BYTE-MISSING axis) split across BOTH axes so a regression that
41297 // silently weakens the helper on EITHER axis (e.g. dropping the
41298 // `found` guard on the set-membership sweep, or dropping the
41299 // outer `while j < M` set-coverage sweep entirely) is caught by
41300 // the helper's OWN test surface rather than only surfacing as a
41301 // false-positive on some future finite-set-covering `[u8; N]`
41302 // array's coverage pin.
41303
41304 #[test]
41305 fn assert_u8_array_covers_finite_set_accepts_the_singleton_set() {
41306 // Singleton set `{K}` at the `[u8; 1]` × `[u8; 1]` corner —
41307 // vacuously covering (the one entry MUST equal the one set
41308 // byte). Cross-arity coverage on the trivial-set corner of
41309 // the const-M generic; simultaneously pins BOTH axes (OUT-OF-
41310 // SET: `K in {K}`; SET-BYTE-MISSING: `K` appears in the
41311 // singleton array) at the smallest witness.
41312 assert_u8_array_covers_finite_set::<1, 1>(&[7u8], &[7u8]);
41313 assert_u8_array_covers_finite_set::<1, 1>(&[0u8], &[0u8]);
41314 assert_u8_array_covers_finite_set::<1, 1>(&[255u8], &[255u8]);
41315 }
41316
41317 #[test]
41318 fn assert_u8_array_covers_finite_set_accepts_arrays_with_duplicates() {
41319 // KEY LOAD-BEARING PIN: duplicates in the entries are ACCEPTED
41320 // — the finite-set-coverage contract is non-injective on the
41321 // ENTRIES axis, only requires each SET byte to appear. This
41322 // is the exact property that distinguishes this helper from
41323 // its `assert_u8_array_pairwise_distinct` sibling and lets
41324 // any future array with a load-bearing collapse bind a
41325 // compile-time coverage witness despite failing distinctness.
41326 // A regression that added a pairwise-distinct guard on the
41327 // entries axis (accidentally merging the two sibling
41328 // helpers' contracts) would fail-loudly here.
41329 assert_u8_array_covers_finite_set::<4, 3>(&[0u8, 1u8, 1u8, 2u8], &[0u8, 1u8, 2u8]);
41330 assert_u8_array_covers_finite_set::<7, 3>(
41331 &[0u8, 1u8, 1u8, 1u8, 1u8, 1u8, 2u8],
41332 &[0u8, 1u8, 2u8],
41333 );
41334 }
41335
41336 #[test]
41337 fn assert_u8_array_covers_finite_set_accepts_the_non_contiguous_partition() {
41338 // KEY LOAD-BEARING PIN: a non-contiguous target set `{0, 2}`
41339 // (with a gap at `1u8`) is ACCEPTED — a two-element array
41340 // hitting exactly those two bytes binds the finite-set-
41341 // coverage contract even though NO contiguous `[LO..=HI]`
41342 // range describes the target partition. This is the exact
41343 // property that distinguishes this helper from its
41344 // `assert_u8_array_covers_inclusive_range` sibling on the
41345 // (contiguity) axis and lets
41346 // `StructuralKind::HASH_DISCRIMINATORS`'s `{0, 2}` gap-
41347 // partition bind a compile-time coverage witness where the
41348 // range sibling cannot. A regression that narrowed the
41349 // helper's target-set semantics to contiguous ranges only
41350 // (e.g. re-derived the `while cur <= HI` sweep instead of
41351 // the per-set-byte membership sweep) would fail-loudly here.
41352 assert_u8_array_covers_finite_set::<2, 2>(&[0u8, 2u8], &[0u8, 2u8]);
41353 assert_u8_array_covers_finite_set::<3, 3>(&[0u8, 2u8, 5u8], &[0u8, 2u8, 5u8]);
41354 }
41355
41356 #[test]
41357 fn assert_u8_array_covers_finite_set_accepts_every_family_wide_substrate_array() {
41358 // Runtime cross-check that the SAME array the module-level
41359 // `const _: () = ...` witness covers at COMPILE time is
41360 // finite-set-covering. A regression that removes the
41361 // `const _` witness would still leave THIS runtime pin as a
41362 // safety net; the const witness fires FIRST at `cargo check`,
41363 // this runtime pin catches the drift at `cargo test`. The
41364 // pair enforces the theorem at TWO stages of the toolchain.
41365 // Sibling posture to
41366 // `assert_u8_array_covers_inclusive_range_accepts_every_
41367 // family_wide_substrate_array` at the contiguous-range peer.
41368 assert_u8_array_covers_finite_set::<2, 2>(
41369 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
41370 &[0u8, 2u8],
41371 );
41372 }
41373
41374 #[test]
41375 #[should_panic(expected = "OUT-OF-SET")]
41376 fn assert_u8_array_covers_finite_set_panics_at_runtime_on_out_of_set_entry() {
41377 // NEGATIVE PIN — SET-MEMBERSHIP corner: an entry NOT in the
41378 // target set MUST panic at runtime with the OUT-OF-SET-named
41379 // message. Pins the helper's OWN reject-out-of-set arm — a
41380 // regression that silently returned without panicking on an
41381 // entry outside the target set would slip through the
41382 // compile-time witnesses' failure mode too. The two set
41383 // bytes `{0, 2}` witness that the FULL-COVERAGE axis is
41384 // INTACT — the panic MUST fire specifically on the SET-
41385 // MEMBERSHIP disjointness failure at the drift entry `3u8`.
41386 assert_u8_array_covers_finite_set::<3, 2>(&[0u8, 2u8, 3u8], &[0u8, 2u8]);
41387 }
41388
41389 #[test]
41390 #[should_panic(expected = "OUT-OF-SET")]
41391 fn assert_u8_array_covers_finite_set_panics_on_gap_byte_drift() {
41392 // NEGATIVE PIN — GAP-BYTE corner: an entry that drifts INTO
41393 // the intentional gap of a non-contiguous target set MUST
41394 // panic — this is the exact regression the archetype
41395 // `StructuralKind::HASH_DISCRIMINATORS` witness catches. A
41396 // regression that lifted a fresh `1u8` entry into the
41397 // `{0, 2}`-partitioned array would silently collide with
41398 // `AtomKind::OUTER_HASH_DISCRIMINATOR = 1u8` on the outer-
41399 // `Sexp` cache-key partition; this pin binds that failure
41400 // mode as an OUT-OF-SET rejection at the const-eval site.
41401 assert_u8_array_covers_finite_set::<3, 2>(&[0u8, 1u8, 2u8], &[0u8, 2u8]);
41402 }
41403
41404 #[test]
41405 #[should_panic(expected = "SET-BYTE-MISSING")]
41406 fn assert_u8_array_covers_finite_set_panics_at_runtime_on_missing_set_byte() {
41407 // NEGATIVE PIN — FULL-COVERAGE corner: a set byte NOT
41408 // reached by any entry MUST panic at runtime with the SET-
41409 // BYTE-MISSING-named message. Pins the helper's OWN reject-
41410 // incomplete-coverage arm — a regression that silently
41411 // returned without panicking on an incomplete-coverage array
41412 // (e.g. dropping the outer `while j < M` sweep entirely, or
41413 // narrowing it to `while j < M - 1`) would slip through the
41414 // compile-time witnesses' failure mode too. The two entries
41415 // stay within `{0, 2, 5}` (satisfying OUT-OF-SET) but skip
41416 // the middle byte `2u8` — the panic MUST fire specifically
41417 // on the FULL-COVERAGE axis, not on the SET-MEMBERSHIP axis.
41418 assert_u8_array_covers_finite_set::<2, 3>(&[0u8, 5u8], &[0u8, 2u8, 5u8]);
41419 }
41420
41421 #[test]
41422 fn assert_u8_array_covers_finite_set_panic_message_names_the_helper_and_out_of_set_axis() {
41423 // PANIC-MESSAGE PROVENANCE PIN — OUT-OF-SET arm: the panic
41424 // message MUST begin with the helper's own name AND identify
41425 // the failed AXIS as "OUT-OF-SET" so downstream diagnostics
41426 // route the drift back to (a) the helper by string search on
41427 // `"assert_u8_array_covers_finite_set"` and (b) the axis by
41428 // string search on `"OUT-OF-SET"`. Sibling posture to
41429 // `assert_u8_array_covers_inclusive_range_panic_message_
41430 // names_the_helper_and_range_bound_axis` on the contiguous-
41431 // range peer's RANGE-BOUND arm — both bind the (helper,
41432 // failed-axis) provenance pair at ONE test per axis. The
41433 // axis-provenance strings ("OUT-OF-SET" here vs. "OUT-OF-
41434 // RANGE" on the range sibling) are chosen DISTINCT so a
41435 // diagnostic that names the failed axis routes UNAMBIGUOUSLY
41436 // to the failed HELPER too — a regression that unified the
41437 // two helpers' axis-provenance strings would collapse either
41438 // this substring assertion or the sibling helper's.
41439 let outcome = std::panic::catch_unwind(|| {
41440 assert_u8_array_covers_finite_set::<3, 2>(&[0u8, 2u8, 3u8], &[0u8, 2u8]);
41441 });
41442 let payload = outcome.expect_err(
41443 "assert_u8_array_covers_finite_set must panic on an out-\
41444 of-set entry — the reject-out-of-set arm is one of the \
41445 two failure modes of the helper",
41446 );
41447 let msg = payload
41448 .downcast_ref::<&'static str>()
41449 .map(|s| (*s).to_owned())
41450 .or_else(|| payload.downcast_ref::<String>().cloned())
41451 .expect(
41452 "assert_u8_array_covers_finite_set panic payload must \
41453 be a static &str or String",
41454 );
41455 assert!(
41456 msg.contains("assert_u8_array_covers_finite_set"),
41457 "assert_u8_array_covers_finite_set OUT-OF-SET panic \
41458 message {msg:?} must name the helper for provenance-\
41459 preserving failure diagnostics",
41460 );
41461 assert!(
41462 msg.contains("OUT-OF-SET"),
41463 "assert_u8_array_covers_finite_set OUT-OF-SET panic \
41464 message {msg:?} must name the failed AXIS (\"OUT-OF-\
41465 SET\") for axis-provenance-preserving failure \
41466 diagnostics",
41467 );
41468 }
41469
41470 #[test]
41471 fn assert_u8_array_covers_finite_set_panic_message_names_the_helper_and_missing_set_byte_axis()
41472 {
41473 // PANIC-MESSAGE PROVENANCE PIN — SET-BYTE-MISSING arm: the
41474 // panic message MUST begin with the helper's own name AND
41475 // identify the failed AXIS as "SET-BYTE-MISSING" so
41476 // downstream diagnostics route the drift back to (a) the
41477 // helper by string search and (b) the axis by string search
41478 // on `"SET-BYTE-MISSING"`. Axis-symmetric sibling of the
41479 // OUT-OF-SET panic-message provenance pin above — a
41480 // regression that silently unified the two panic sites into
41481 // ONE axis-anonymous message would collapse EITHER this
41482 // pin's `"SET-BYTE-MISSING"` substring assertion OR the
41483 // sibling pin's `"OUT-OF-SET"` substring assertion. The
41484 // axis-provenance string ("SET-BYTE-MISSING" here vs.
41485 // "MISSING" on the range sibling) is chosen DISTINCT so a
41486 // diagnostic that names the failed axis routes UNAMBIGUOUSLY
41487 // to the failed HELPER too.
41488 let outcome = std::panic::catch_unwind(|| {
41489 assert_u8_array_covers_finite_set::<2, 3>(&[0u8, 5u8], &[0u8, 2u8, 5u8]);
41490 });
41491 let payload = outcome.expect_err(
41492 "assert_u8_array_covers_finite_set must panic on a \
41493 missing set byte — the reject-incomplete-coverage arm \
41494 is one of the two failure modes of the helper",
41495 );
41496 let msg = payload
41497 .downcast_ref::<&'static str>()
41498 .map(|s| (*s).to_owned())
41499 .or_else(|| payload.downcast_ref::<String>().cloned())
41500 .expect(
41501 "assert_u8_array_covers_finite_set panic payload must \
41502 be a static &str or String",
41503 );
41504 assert!(
41505 msg.contains("assert_u8_array_covers_finite_set"),
41506 "assert_u8_array_covers_finite_set SET-BYTE-MISSING panic \
41507 message {msg:?} must name the helper for provenance-\
41508 preserving failure diagnostics",
41509 );
41510 assert!(
41511 msg.contains("SET-BYTE-MISSING"),
41512 "assert_u8_array_covers_finite_set SET-BYTE-MISSING panic \
41513 message {msg:?} must name the failed AXIS (\"SET-BYTE-\
41514 MISSING\") for axis-provenance-preserving failure \
41515 diagnostics",
41516 );
41517 }
41518
41519 // ── `assert_u8_finite_set_pairwise_distinct` — the caller-side
41520 // SET-WELL-FORMEDNESS verifier that closes the pre-lift docstring-
41521 // level "assuming `set` is itself pairwise-distinct" caveat on
41522 // both `assert_u8_array_covers_finite_set` and
41523 // `assert_u8_array_permutes_finite_set` into a compile-time
41524 // theorem the substrate carries per call site. The runtime test
41525 // surface pins each of the helper's arms (accept-empty, accept-
41526 // singleton, accept-every-family-wide-substrate-target-set,
41527 // reject-binary-collision, reject-non-adjacent-collision, reject-
41528 // terminal-collision, panic-message-provenance on the SET-NOT-
41529 // PAIRWISE-DISTINCT axis, negative pins on BOTH downstream
41530 // covers/permutes helpers exercising the SET-side delegation
41531 // chain) so a regression that silently weakened the helper on
41532 // ANY arm is caught by the helper's OWN test surface rather than
41533 // only surfacing as a false-positive on some future finite-set-
41534 // covering `[u8; N]` array's compound pin.
41535
41536 #[test]
41537 fn assert_u8_finite_set_pairwise_distinct_accepts_the_empty_set() {
41538 // Empty set `{}` at the `[u8; 0]` corner — vacuously pairwise-
41539 // distinct (no `(i, j)` pair with `i < j` exists to test).
41540 // Cross-arity coverage on the trivial corner of the const-M
41541 // generic; a regression that fired on an empty set (e.g. by
41542 // computing `M - 1` and wrapping to `usize::MAX`) would
41543 // fail-loudly here. Turbofish binding required because there's
41544 // no other cue for the `M` const parameter on the empty array
41545 // literal.
41546 assert_u8_finite_set_pairwise_distinct::<0>(&[]);
41547 }
41548
41549 #[test]
41550 fn assert_u8_finite_set_pairwise_distinct_accepts_singleton_sets() {
41551 // Singleton set `{K}` at the `[u8; 1]` corner — vacuously
41552 // pairwise-distinct (no `(i, j)` pair with `i < j` exists).
41553 // Cross-value coverage on the singleton corner at THREE byte
41554 // widths (`0u8`, `7u8`, `255u8`) to pin the helper's SINGLETON
41555 // arm across the whole `u8` domain.
41556 assert_u8_finite_set_pairwise_distinct(&[0u8]);
41557 assert_u8_finite_set_pairwise_distinct(&[7u8]);
41558 assert_u8_finite_set_pairwise_distinct(&[255u8]);
41559 }
41560
41561 #[test]
41562 fn assert_u8_finite_set_pairwise_distinct_accepts_every_family_wide_target_set() {
41563 // Runtime cross-check that the ONE target-SET spec the
41564 // substrate's `const _` witness (at
41565 // `assert_u8_array_permutes_finite_set::<2, 2>(
41566 // &StructuralKind::HASH_DISCRIMINATORS, &[0u8, 2u8])`)
41567 // passes through this helper's SET-side delegation at
41568 // compile time is ALSO well-formed at runtime. The pair
41569 // enforces the theorem at TWO stages of the toolchain: the
41570 // const witness fires FIRST at `cargo check` (through the
41571 // delegated call chain), this runtime pin catches the drift
41572 // at `cargo test` as a safety net. Sibling posture to
41573 // `assert_u8_array_covers_finite_set_accepts_every_family_
41574 // wide_substrate_array` — the two pins together verify the
41575 // (ARRAY, SET) pair at BOTH sides of the finite-set-coverage
41576 // compound contract.
41577 assert_u8_finite_set_pairwise_distinct(&[0u8, 2u8]);
41578 }
41579
41580 #[test]
41581 fn assert_u8_finite_set_pairwise_distinct_accepts_the_structural_kind_hash_discriminators() {
41582 // Runtime cross-check that
41583 // `StructuralKind::HASH_DISCRIMINATORS` itself is pairwise-
41584 // distinct WHEN VIEWED AS A TARGET-SET SPEC (i.e. this
41585 // helper accepts arrays that would themselves be valid
41586 // target-set specs). While the SUBSTRATE'S primary call
41587 // site passes the caller-provided `&[0u8, 2u8]` literal as
41588 // the target-set spec (with `StructuralKind::HASH_
41589 // DISCRIMINATORS` as the ARRAY under verification), the two
41590 // arrays HAPPEN to have byte-equal contents by design (the
41591 // ARRAY is a permutation of the target SET); a regression
41592 // that silently unified two `StructuralKind` arms' cache-
41593 // key bytes would fail-loudly here on the ARRAY-as-SET
41594 // interpretation TOO, providing a redundant safety net past
41595 // the primary compound witness at the module level.
41596 assert_u8_finite_set_pairwise_distinct(&crate::error::StructuralKind::HASH_DISCRIMINATORS);
41597 }
41598
41599 #[test]
41600 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41601 fn assert_u8_finite_set_pairwise_distinct_panics_at_runtime_on_binary_collision() {
41602 // NEGATIVE PIN — binary-collision corner: the smallest
41603 // duplicate-carrying set `[K, K]` MUST panic at runtime with
41604 // the SET-NOT-PAIRWISE-DISTINCT-named message. Pins the
41605 // helper's OWN reject arm — a regression that silently
41606 // returned without panicking on an adjacent duplicate would
41607 // slip through the compile-time witnesses' failure mode too.
41608 assert_u8_finite_set_pairwise_distinct(&[42u8, 42u8]);
41609 }
41610
41611 #[test]
41612 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41613 fn assert_u8_finite_set_pairwise_distinct_panics_at_runtime_on_non_adjacent_collision() {
41614 // NEGATIVE PIN — non-adjacent-collision corner: a duplicate
41615 // separated by ONE intervening element MUST panic — pins
41616 // that the `(i, j)` pair-walk sweeps the FULL upper-triangle
41617 // rather than only adjacent pairs. A regression that
41618 // narrowed the inner `while j < M` loop to `j == i + 1`
41619 // would silently accept this set. Positions `(0, 2)` witness
41620 // a non-adjacent collision through the middle `2u8`
41621 // separator.
41622 assert_u8_finite_set_pairwise_distinct(&[1u8, 2u8, 1u8]);
41623 }
41624
41625 #[test]
41626 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41627 fn assert_u8_finite_set_pairwise_distinct_panics_at_runtime_on_terminal_collision() {
41628 // NEGATIVE PIN — terminal-collision corner: a duplicate at
41629 // the LAST two positions MUST panic — pins that the outer
41630 // `while i < M` loop reaches `i = M - 2` (else the terminal
41631 // duplicate at positions `(M - 2, M - 1)` would slip
41632 // through). A regression that narrowed the outer sweep to
41633 // `while i < M - 1` (off-by-one on the OUTER bound) would
41634 // silently accept this set.
41635 assert_u8_finite_set_pairwise_distinct(&[0u8, 1u8, 2u8, 3u8, 3u8]);
41636 }
41637
41638 #[test]
41639 fn assert_u8_finite_set_pairwise_distinct_panic_message_names_the_helper_and_set_axis() {
41640 // PANIC-MESSAGE PROVENANCE PIN — SET-NOT-PAIRWISE-DISTINCT
41641 // arm: the panic message MUST begin with the helper's own
41642 // name AND identify the failed AXIS as "SET-NOT-PAIRWISE-
41643 // DISTINCT" so downstream diagnostics route the drift back
41644 // to (a) the helper by string search on `"assert_u8_finite_
41645 // set_pairwise_distinct"` and (b) the axis by string search
41646 // on `"SET-NOT-PAIRWISE-DISTINCT"`. Sibling posture to
41647 // `assert_u8_array_covers_finite_set_panic_message_names_
41648 // the_helper_and_out_of_set_axis` on the ARRAY-side covers
41649 // helper's OUT-OF-SET arm — both bind the (helper, failed-
41650 // axis) provenance pair at ONE test per axis. The axis-
41651 // provenance string "SET-NOT-PAIRWISE-DISTINCT" is chosen
41652 // DISTINCT from EVERY sibling helper's axis vocabulary
41653 // (`"duplicate"` on the ARRAY-side pairwise-distinct
41654 // sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"` on the
41655 // covers-finite-set sibling; `"OUT-OF-RANGE"` / `"MISSING"`
41656 // on the covers-inclusive-range sibling; `"ARITY-MISMATCH"`
41657 // on both `_permutes_*` compound helpers) so a diagnostic
41658 // that names the failed axis routes UNAMBIGUOUSLY to (a)
41659 // this specific SET-side helper, (b) the CALLER'S TARGET-
41660 // SET SPEC as the drift site rather than the downstream
41661 // `arr` under verification.
41662 let outcome = std::panic::catch_unwind(|| {
41663 assert_u8_finite_set_pairwise_distinct(&[42u8, 42u8]);
41664 });
41665 let payload = outcome.expect_err(
41666 "assert_u8_finite_set_pairwise_distinct must panic on a \
41667 malformed target-set spec — the reject-duplicate arm \
41668 is the sole failure mode of the helper",
41669 );
41670 let msg = payload
41671 .downcast_ref::<&'static str>()
41672 .map(|s| (*s).to_owned())
41673 .or_else(|| payload.downcast_ref::<String>().cloned())
41674 .expect(
41675 "assert_u8_finite_set_pairwise_distinct panic payload \
41676 must be a static &str or String",
41677 );
41678 assert!(
41679 msg.contains("assert_u8_finite_set_pairwise_distinct"),
41680 "assert_u8_finite_set_pairwise_distinct panic message \
41681 {msg:?} must name the helper for provenance-preserving \
41682 failure diagnostics",
41683 );
41684 assert!(
41685 msg.contains("SET-NOT-PAIRWISE-DISTINCT"),
41686 "assert_u8_finite_set_pairwise_distinct panic message \
41687 {msg:?} must name the failed AXIS (\"SET-NOT-PAIRWISE-\
41688 DISTINCT\") for axis-provenance-preserving failure \
41689 diagnostics",
41690 );
41691 }
41692
41693 #[test]
41694 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41695 fn assert_u8_array_covers_finite_set_panics_on_malformed_target_set_spec() {
41696 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
41697 // malformed target-set spec `[0, 2, 2]` fed into the
41698 // ARRAY-side covers-finite-set helper MUST panic on the
41699 // DELEGATED SET-NOT-PAIRWISE-DISTINCT arm BEFORE the OUT-
41700 // OF-SET / SET-BYTE-MISSING arms fire. Pins the
41701 // delegation chain: a regression that dropped the
41702 // `assert_u8_finite_set_pairwise_distinct(set)` call at
41703 // the top of `assert_u8_array_covers_finite_set` would
41704 // silently accept a malformed set and produce a false-
41705 // positive verdict on any `arr` covering the DISTINCT-
41706 // value subset (`arr = [0, 2, 2]` here matches BOTH the
41707 // OUT-OF-SET and SET-BYTE-MISSING sweeps because every
41708 // entry appears in `set` and every set byte appears in
41709 // `arr`, so without the SET-well-formedness delegation
41710 // the intended cardinality-3 contract would silently
41711 // verify against a really-cardinality-2 set). The
41712 // `SET-NOT-PAIRWISE-DISTINCT` axis-provenance string
41713 // routes the operator to the CALLER'S SPEC.
41714 assert_u8_array_covers_finite_set::<3, 3>(&[0u8, 2u8, 2u8], &[0u8, 2u8, 2u8]);
41715 }
41716
41717 #[test]
41718 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41719 fn assert_u8_array_permutes_finite_set_panics_on_malformed_target_set_spec() {
41720 // NEGATIVE PIN — DELEGATED SET-side well-formedness on
41721 // the COMPOUND helper: a malformed target-set spec `[0, 2,
41722 // 2]` fed into `assert_u8_array_permutes_finite_set` MUST
41723 // panic on the DELEGATED SET-NOT-PAIRWISE-DISTINCT arm
41724 // BEFORE the ARITY-MISMATCH / pairwise-distinct-on-arr /
41725 // covers-finite-set arms fire. Pins the delegation chain at
41726 // the compound helper's ENTRY point: a regression that
41727 // dropped the `assert_u8_finite_set_pairwise_distinct(set)`
41728 // call at the top of the compound helper would silently
41729 // route a malformed set through the ARITY check (`N == M`
41730 // holds on the substrate's cardinality-3 pair, since BOTH
41731 // the array and the phantom-3 set have cardinality `3` at
41732 // the type level even though the SET really has DISTINCT-
41733 // cardinality `2`) and produce a false-positive permutation
41734 // verdict. The `SET-NOT-PAIRWISE-DISTINCT` panic here MUST
41735 // fire BEFORE the sibling `ARITY-MISMATCH` panic on the
41736 // same input (both arms would fire independently but
41737 // ordering routes the operator to the CALLER'S SPEC first,
41738 // not to a downstream arithmetic symptom).
41739 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 2u8, 5u8], &[0u8, 2u8, 2u8]);
41740 }
41741
41742 // ── `assert_u8_array_within_u8_finite_set` — the SET-MEMBERSHIP-
41743 // ONLY subset-embedding verifier that binds `arr ⊆ set` at compile
41744 // time (WITHOUT the additional `set ⊆ arr's entries` full-coverage
41745 // clause the sibling `_covers_finite_set` binds). The runtime test
41746 // surface pins each of the helper's arms (accept-empty, accept-
41747 // singleton-in-set, accept-arr-equals-set, accept-the-family-wide
41748 // substitution-subset substrate embedding, reject-single-out-of-
41749 // set-entry, reject-terminal-out-of-set-entry, panic-message-
41750 // provenance on the SUBSET-VIOLATION axis, negative pin on the
41751 // DELEGATED SET-side well-formedness arm) so a regression that
41752 // silently weakened the helper on ANY arm is caught by the
41753 // helper's OWN test surface rather than only surfacing as a
41754 // false-positive on some future subset-embedded `[u8; N]` array's
41755 // compound pin.
41756
41757 #[test]
41758 fn assert_u8_array_within_u8_finite_set_accepts_the_empty_array_within_any_set() {
41759 // Empty array `arr = []` at the `[u8; 0]` corner — vacuously
41760 // a subset of every set (no `i` position exists to test). Cross-
41761 // arity coverage on the trivial ARRAY corner of the const-N
41762 // generic across three witness-set widths (empty, singleton,
41763 // multi-element) to pin the helper's OUTER-sweep arm across
41764 // the whole (`N == 0` × `M`) axis. Turbofish binding required
41765 // because there's no other cue for the const parameters on
41766 // the empty array literal.
41767 assert_u8_array_within_u8_finite_set::<0, 0>(&[], &[]);
41768 assert_u8_array_within_u8_finite_set::<0, 1>(&[], &[42u8]);
41769 assert_u8_array_within_u8_finite_set::<0, 4>(&[], &[3u8, 4u8, 5u8, 6u8]);
41770 }
41771
41772 #[test]
41773 fn assert_u8_array_within_u8_finite_set_accepts_singleton_array_when_byte_in_set() {
41774 // Singleton array `arr = [K]` at the `[u8; 1]` corner MUST
41775 // pass when `K ∈ set`. Cross-position coverage: the byte can
41776 // sit at the FIRST, MIDDLE, or LAST position of the `set` —
41777 // pins the INNER `while j < M` sweep terminates at the
41778 // first-match position rather than always at position `0`
41779 // OR always at position `M - 1`. A regression that narrowed
41780 // the inner sweep to `j == 0` would silently reject singleton
41781 // arrays hitting non-first set positions.
41782 assert_u8_array_within_u8_finite_set::<1, 4>(&[3u8], &[3u8, 4u8, 5u8, 6u8]);
41783 assert_u8_array_within_u8_finite_set::<1, 4>(&[5u8], &[3u8, 4u8, 5u8, 6u8]);
41784 assert_u8_array_within_u8_finite_set::<1, 4>(&[6u8], &[3u8, 4u8, 5u8, 6u8]);
41785 }
41786
41787 #[test]
41788 fn assert_u8_array_within_u8_finite_set_accepts_arr_equals_set() {
41789 // Boundary corner where `arr` and `set` cover byte-for-byte
41790 // identical distinct-value sets — the SUBSET relation degenerates
41791 // to EQUALITY. Pins that the helper does NOT gratuitously
41792 // require the SUBSET to be PROPER (strict): equal-multisets
41793 // pass the SUBSET check. Sibling posture to the covers-finite-
41794 // set peer whose SET-BYTE-MISSING arm would ALSO accept this
41795 // input — the two helpers agree on the EQUAL-SETS corner
41796 // while disagreeing on the PROPER-SUBSET corner (only this
41797 // helper accepts proper subsets; the covers helper rejects
41798 // them at the SET-BYTE-MISSING arm).
41799 assert_u8_array_within_u8_finite_set(&[3u8, 4u8, 5u8, 6u8], &[3u8, 4u8, 5u8, 6u8]);
41800 assert_u8_array_within_u8_finite_set(&[0u8, 2u8], &[0u8, 2u8]);
41801 }
41802
41803 #[test]
41804 fn assert_u8_array_within_u8_finite_set_accepts_the_substitution_subset_embedding() {
41805 // Runtime cross-check that the ONE (subset, superset) pair
41806 // the substrate's module-level `const _` witness pins at
41807 // COMPILE time is a PROPER SUBSET embedding at runtime too.
41808 // The pair enforces the theorem at TWO stages of the
41809 // toolchain: the const witness fires FIRST at `cargo check`
41810 // (through the module-level `const _: () = assert_u8_array_
41811 // within_u8_finite_set::<2, 4>(...)` line), this runtime pin
41812 // catches the drift at `cargo test` as a safety net. Sibling
41813 // posture to
41814 // `assert_u8_finite_set_pairwise_distinct_accepts_every_family_wide_target_set`
41815 // (which runtime-checks the SAME `QuoteForm::HASH_DISCRIMINATORS`
41816 // superset viewed as a SET-side well-formedness input) — the
41817 // two pins together verify the (UnquoteForm subset, QuoteForm
41818 // superset) pair at BOTH sides of the subset-embedding
41819 // contract.
41820 assert_u8_array_within_u8_finite_set::<2, 4>(
41821 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
41822 &QuoteForm::HASH_DISCRIMINATORS,
41823 );
41824 }
41825
41826 #[test]
41827 fn assert_u8_array_within_u8_finite_set_accepts_repeated_array_entries_in_set() {
41828 // Peer corner to `_covers_finite_set`: this helper permits
41829 // duplicates in `arr` because SUBSET-membership is a
41830 // DISTINCT-value predicate — `[3, 3, 5]` is a subset of
41831 // `{3, 4, 5, 6}` even though the array is not pairwise-
41832 // distinct. Pins that the helper does NOT gratuitously
41833 // require INJECTIVITY on `arr` (the injectivity axis is a
41834 // DIFFERENT compile-time contract bound by
41835 // `assert_u8_array_pairwise_distinct`; combining both binds
41836 // BOTH axes). Sibling posture to
41837 // `assert_u8_array_covers_inclusive_range` which ALSO
41838 // permits array duplicates on its RANGE-BOUND arm.
41839 assert_u8_array_within_u8_finite_set(&[3u8, 3u8, 5u8], &[3u8, 4u8, 5u8, 6u8]);
41840 }
41841
41842 #[test]
41843 #[should_panic(expected = "SUBSET-VIOLATION")]
41844 fn assert_u8_array_within_u8_finite_set_panics_at_runtime_on_out_of_set_entry() {
41845 // NEGATIVE PIN — SUBSET-VIOLATION corner: an array carrying
41846 // a single entry NOT in the target set MUST panic at runtime
41847 // with the SUBSET-VIOLATION-named message. Pins the helper's
41848 // OWN reject arm — a regression that silently returned
41849 // without panicking on an out-of-set entry would slip through
41850 // the compile-time witness's failure mode too. The offending
41851 // byte `7u8` is intentionally chosen ONE PAST the superset's
41852 // maximum (`QuoteForm::HASH_DISCRIMINATORS`'s upper endpoint
41853 // is `6u8`) to pin the OVERSHOOT drift mode.
41854 assert_u8_array_within_u8_finite_set(&[5u8, 7u8], &[3u8, 4u8, 5u8, 6u8]);
41855 }
41856
41857 #[test]
41858 #[should_panic(expected = "SUBSET-VIOLATION")]
41859 fn assert_u8_array_within_u8_finite_set_panics_at_runtime_on_terminal_out_of_set_entry() {
41860 // NEGATIVE PIN — terminal-position drift: an out-of-set entry
41861 // at the LAST array position MUST panic — pins that the outer
41862 // `while i < N` loop reaches `i = N - 1` (else the terminal
41863 // drift would slip through). A regression that narrowed the
41864 // outer sweep to `while i < N - 1` (off-by-one on the OUTER
41865 // bound) would silently accept this array.
41866 assert_u8_array_within_u8_finite_set(&[3u8, 4u8, 5u8, 6u8, 7u8], &[3u8, 4u8, 5u8, 6u8]);
41867 }
41868
41869 #[test]
41870 #[should_panic(expected = "SUBSET-VIOLATION")]
41871 fn assert_u8_array_within_u8_finite_set_panics_at_runtime_on_undershoot_out_of_set_entry() {
41872 // NEGATIVE PIN — undershoot drift mode complementary to the
41873 // OVERSHOOT drift mode `_panics_at_runtime_on_out_of_set_entry`
41874 // above: an offending byte BELOW the superset's minimum
41875 // (`QuoteForm::HASH_DISCRIMINATORS`'s lower endpoint is `3u8`;
41876 // this array carries a `0u8` entry NOT in the set) MUST panic.
41877 // Pins that the helper's SET-MEMBERSHIP sweep does NOT
41878 // silently accept bytes UNDER the target set's minimum on
41879 // some misapplied range-min assumption — the helper binds a
41880 // FINITE-SET subset relation, not a RANGE subset relation.
41881 assert_u8_array_within_u8_finite_set(&[0u8, 5u8], &[3u8, 4u8, 5u8, 6u8]);
41882 }
41883
41884 #[test]
41885 fn assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis(
41886 ) {
41887 // PANIC-MESSAGE PROVENANCE PIN — SUBSET-VIOLATION arm: the
41888 // panic message MUST begin with the helper's own name AND
41889 // identify the failed AXIS as "SUBSET-VIOLATION" so
41890 // downstream diagnostics route the drift back to (a) the
41891 // helper by string search on
41892 // `"assert_u8_array_within_u8_finite_set"` and (b) the axis
41893 // by string search on `"SUBSET-VIOLATION"`. Sibling posture
41894 // to
41895 // `assert_u8_finite_set_pairwise_distinct_panic_message_names_the_helper_and_set_axis`
41896 // on the SET-side well-formedness sibling and
41897 // `assert_u8_array_covers_finite_set_panic_message_names_the_helper_and_out_of_set_axis`
41898 // on the ARRAY-side covers helper — all three bind the
41899 // (helper, failed-axis) provenance pair at ONE test per
41900 // helper. The axis-provenance string "SUBSET-VIOLATION" is
41901 // chosen DISTINCT from EVERY sibling helper's axis
41902 // vocabulary (`"duplicate"` on the ARRAY-side pairwise-
41903 // distinct sibling; `"OUT-OF-SET"` / `"SET-BYTE-MISSING"`
41904 // on the covers-finite-set sibling; `"OUT-OF-RANGE"` /
41905 // `"MISSING"` on the covers-inclusive-range sibling;
41906 // `"ARITY-MISMATCH"` on both `_permutes_*` compound helpers;
41907 // `"SET-NOT-PAIRWISE-DISTINCT"` on the SET-side well-
41908 // formedness sibling) so a diagnostic that names the failed
41909 // axis routes UNAMBIGUOUSLY to (a) this specific SUBSET-
41910 // embedding helper, (b) the `arr` argument as the drift
41911 // site rather than the `set` argument specifying the target
41912 // superset.
41913 let outcome = std::panic::catch_unwind(|| {
41914 assert_u8_array_within_u8_finite_set(&[5u8, 7u8], &[3u8, 4u8, 5u8, 6u8]);
41915 });
41916 let payload = outcome.expect_err(
41917 "assert_u8_array_within_u8_finite_set must panic on an \
41918 out-of-set entry — the reject-out-of-set arm is the \
41919 sole SUBSET-VIOLATION failure mode of the helper",
41920 );
41921 let msg = payload
41922 .downcast_ref::<&'static str>()
41923 .map(|s| (*s).to_owned())
41924 .or_else(|| payload.downcast_ref::<String>().cloned())
41925 .expect(
41926 "assert_u8_array_within_u8_finite_set panic payload \
41927 must be a static &str or String",
41928 );
41929 assert!(
41930 msg.contains("assert_u8_array_within_u8_finite_set"),
41931 "assert_u8_array_within_u8_finite_set panic message \
41932 {msg:?} must name the helper for provenance-preserving \
41933 failure diagnostics",
41934 );
41935 assert!(
41936 msg.contains("SUBSET-VIOLATION"),
41937 "assert_u8_array_within_u8_finite_set panic message \
41938 {msg:?} must name the failed AXIS (\"SUBSET-VIOLATION\") \
41939 for axis-provenance-preserving failure diagnostics",
41940 );
41941 }
41942
41943 #[test]
41944 #[should_panic(expected = "SET-NOT-PAIRWISE-DISTINCT")]
41945 fn assert_u8_array_within_u8_finite_set_panics_on_malformed_target_set_spec() {
41946 // NEGATIVE PIN — DELEGATED SET-side well-formedness: a
41947 // malformed target-set spec `[3, 4, 4]` fed into the ARRAY-
41948 // side within helper MUST panic on the DELEGATED SET-NOT-
41949 // PAIRWISE-DISTINCT arm BEFORE the SUBSET-VIOLATION arm
41950 // fires. Pins the delegation chain: a regression that
41951 // dropped the `assert_u8_finite_set_pairwise_distinct(set)`
41952 // call at the top of `assert_u8_array_within_u8_finite_set`
41953 // would silently accept a malformed set and produce a
41954 // false-positive verdict on any `arr` embedded in the
41955 // DISTINCT-value subset. Sibling posture to
41956 // `assert_u8_array_covers_finite_set_panics_on_malformed_target_set_spec`
41957 // and
41958 // `assert_u8_array_permutes_finite_set_panics_on_malformed_target_set_spec`
41959 // — the three DELEGATED tests pin the SET-side well-
41960 // formedness arm at the top of ALL THREE finite-set-family
41961 // ARRAY-side helpers.
41962 assert_u8_array_within_u8_finite_set::<2, 3>(&[3u8, 4u8], &[3u8, 4u8, 4u8]);
41963 }
41964
41965 // ── `assert_u8_array_within_inclusive_range` — the RANGE-MEMBERSHIP-
41966 // ONLY subset-embedding verifier that binds `arr ⊆ [LO..=HI]` at
41967 // compile time (WITHOUT the additional `[LO..=HI] ⊆ arr's entries`
41968 // full-coverage clause the sibling `_covers_inclusive_range` binds).
41969 // Contiguity-axis peer to `_within_u8_finite_set` on the (contiguity)
41970 // axis of the substrate's SUBSET-only verifiers. The runtime test
41971 // surface pins each of the helper's arms (accept-empty, accept-
41972 // singleton-in-range, accept-arr-equals-range-endpoints, accept-the-
41973 // family-wide structural-residual substrate embedding, accept-with-
41974 // duplicates, reject-above-HI-entry, reject-below-LO-entry, reject-
41975 // terminal-out-of-range-entry, panic-message-provenance on the
41976 // RANGE-SUBSET-VIOLATION axis) so a regression that silently weakens
41977 // the helper on ANY arm is caught by the helper's OWN test surface
41978 // rather than only surfacing as a false-positive on some future
41979 // range-subset-embedded `[u8; N]` array's compound pin.
41980
41981 #[test]
41982 fn assert_u8_array_within_inclusive_range_accepts_the_empty_array_within_any_range() {
41983 // Empty array `arr = []` at the `[u8; 0]` corner — vacuously a
41984 // subset of every inclusive range (no `i` position exists to
41985 // test). Cross-range coverage on the trivial ARRAY corner of
41986 // the const-N generic across three witness-range widths
41987 // (singleton, small, whole-`u8`-space) to pin the helper's
41988 // OUTER-sweep arm across the whole (`N == 0` × `[LO..=HI]`)
41989 // axis. Turbofish binding required because there's no other
41990 // cue for the const parameters on the empty array literal.
41991 assert_u8_array_within_inclusive_range::<0, 0, 0>(&[]);
41992 assert_u8_array_within_inclusive_range::<0, 0, 6>(&[]);
41993 assert_u8_array_within_inclusive_range::<0, 0, 255>(&[]);
41994 }
41995
41996 #[test]
41997 fn assert_u8_array_within_inclusive_range_accepts_singleton_array_when_byte_in_range() {
41998 // Singleton array `arr = [K]` at the `[u8; 1]` corner MUST pass
41999 // when `K in [LO..=HI]`. Cross-position coverage: the byte can
42000 // sit at the LO endpoint, an INTERIOR position, or the HI
42001 // endpoint — pins the OUTER-sweep's OR-disjunction guard
42002 // handles all three cases without gratuitously narrowing to
42003 // strict-inequality on either endpoint. A regression that
42004 // narrowed the guard to `arr[i] <= LO || arr[i] >= HI` (strict
42005 // endpoint exclusion) would silently reject singleton arrays
42006 // hitting the endpoints — this test catches BOTH endpoint
42007 // regressions in ONE forward sweep.
42008 assert_u8_array_within_inclusive_range::<1, 0, 6>(&[0u8]);
42009 assert_u8_array_within_inclusive_range::<1, 0, 6>(&[3u8]);
42010 assert_u8_array_within_inclusive_range::<1, 0, 6>(&[6u8]);
42011 }
42012
42013 #[test]
42014 fn assert_u8_array_within_inclusive_range_accepts_singleton_range_when_byte_equals_endpoint() {
42015 // Degenerate singleton-range corner `[K..=K]` where LO == HI —
42016 // the ONLY accepting arrays are those whose every entry equals
42017 // `K`. Pins the helper does NOT gratuitously reject the LO ==
42018 // HI degenerate case (a regression that narrowed `LO <= HI` to
42019 // `LO < HI` at some downstream bounds check would surface here
42020 // if this helper ever grew such a check; today it does not,
42021 // and this test locks it out).
42022 assert_u8_array_within_inclusive_range::<1, 42, 42>(&[42u8]);
42023 assert_u8_array_within_inclusive_range::<3, 42, 42>(&[42u8, 42u8, 42u8]);
42024 assert_u8_array_within_inclusive_range::<1, 0, 0>(&[0u8]);
42025 assert_u8_array_within_inclusive_range::<1, 255, 255>(&[255u8]);
42026 }
42027
42028 #[test]
42029 fn assert_u8_array_within_inclusive_range_accepts_arrays_with_duplicates() {
42030 // Peer corner to `_covers_inclusive_range`: this helper permits
42031 // duplicates in `arr` because RANGE-membership is a DISTINCT-
42032 // value predicate — `[3, 3, 5]` embeds in `[0..=6]` even
42033 // though the array is not pairwise-distinct. Pins that the
42034 // helper does NOT gratuitously require INJECTIVITY on `arr`
42035 // (the injectivity axis is a DIFFERENT compile-time contract
42036 // bound by `assert_u8_array_pairwise_distinct`; combining both
42037 // binds BOTH axes). Sibling posture to
42038 // `assert_u8_array_within_u8_finite_set_accepts_repeated_array_entries_in_set`
42039 // on the finite-set peer.
42040 assert_u8_array_within_inclusive_range::<3, 0, 6>(&[3u8, 3u8, 5u8]);
42041 assert_u8_array_within_inclusive_range::<4, 0, 6>(&[0u8, 0u8, 6u8, 6u8]);
42042 }
42043
42044 #[test]
42045 fn assert_u8_array_within_inclusive_range_accepts_the_structural_residual_subset_embedding() {
42046 // Runtime cross-check that the ONE (array, range) pair the
42047 // substrate's module-level `const _` witness pins at COMPILE
42048 // time is a PROPER SUBSET embedding at runtime too. The pair
42049 // enforces the theorem at TWO stages of the toolchain: the
42050 // const witness fires FIRST at `cargo check` (through the
42051 // module-level `const _: () = assert_u8_array_within_inclusive_
42052 // range::<2, 0, 6>(&StructuralKind::HASH_DISCRIMINATORS)`
42053 // line), this runtime pin catches the drift at `cargo test`
42054 // as a safety net. Sibling posture to
42055 // `assert_u8_array_within_u8_finite_set_accepts_the_substitution_subset_embedding`
42056 // on the finite-set-subset peer's substrate cross-check —
42057 // both pin the substrate's ONE-witness embedding at BOTH
42058 // stages of the toolchain.
42059 assert_u8_array_within_inclusive_range::<2, 0, 6>(
42060 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
42061 );
42062 }
42063
42064 #[test]
42065 #[should_panic(expected = "RANGE-SUBSET-VIOLATION")]
42066 fn assert_u8_array_within_inclusive_range_panics_at_runtime_on_entry_above_hi() {
42067 // NEGATIVE PIN — above-HI overshoot corner: an array carrying
42068 // a single entry `HI + 1` MUST panic at runtime with the
42069 // RANGE-SUBSET-VIOLATION-named message. Pins the helper's OWN
42070 // reject-above-HI arm — a regression that silently returned
42071 // without panicking on an out-of-range entry above the upper
42072 // bound would slip through the compile-time witness's failure
42073 // mode too. The offending byte `7u8` is intentionally chosen
42074 // ONE PAST the outer-`Sexp` partition's upper endpoint (`6u8`)
42075 // to pin the OVERSHOOT drift mode against the substrate's
42076 // load-bearing partition.
42077 assert_u8_array_within_inclusive_range::<2, 0, 6>(&[0u8, 7u8]);
42078 }
42079
42080 #[test]
42081 #[should_panic(expected = "RANGE-SUBSET-VIOLATION")]
42082 fn assert_u8_array_within_inclusive_range_panics_at_runtime_on_entry_below_lo() {
42083 // NEGATIVE PIN — below-LO undershoot corner symmetric to the
42084 // OVERSHOOT drift mode above: an offending byte BELOW the
42085 // range's minimum (`LO = 3u8`; this array carries a `0u8`
42086 // entry NOT in the range) MUST panic. Pins that the helper's
42087 // OR-disjunction guard does NOT drop the `arr[i] < LO` half
42088 // (leaving only the `arr[i] > HI` half) — a regression that
42089 // silently accepted below-LO entries would surface here.
42090 assert_u8_array_within_inclusive_range::<2, 3, 6>(&[0u8, 5u8]);
42091 }
42092
42093 #[test]
42094 #[should_panic(expected = "RANGE-SUBSET-VIOLATION")]
42095 fn assert_u8_array_within_inclusive_range_panics_at_runtime_on_terminal_out_of_range_entry() {
42096 // NEGATIVE PIN — terminal-position drift: an out-of-range
42097 // entry at the LAST array position MUST panic — pins that the
42098 // outer `while i < N` loop reaches `i = N - 1` (else the
42099 // terminal drift would slip through). A regression that
42100 // narrowed the outer sweep to `while i < N - 1` (off-by-one on
42101 // the OUTER bound) would silently accept this array. Sibling
42102 // posture to
42103 // `assert_u8_array_within_u8_finite_set_panics_at_runtime_on_terminal_out_of_set_entry`
42104 // on the finite-set-subset peer's terminal-position pin —
42105 // both bind the outer-sweep terminal bound at the ONE array-
42106 // side outer loop the helper carries.
42107 assert_u8_array_within_inclusive_range::<4, 0, 6>(&[0u8, 3u8, 6u8, 7u8]);
42108 }
42109
42110 #[test]
42111 fn assert_u8_array_within_inclusive_range_panic_message_names_the_helper_and_range_subset_violation_axis(
42112 ) {
42113 // PANIC-MESSAGE PROVENANCE PIN — RANGE-SUBSET-VIOLATION arm:
42114 // the panic message MUST begin with the helper's own name AND
42115 // identify the failed AXIS as "RANGE-SUBSET-VIOLATION" so
42116 // downstream diagnostics route the drift back to (a) the
42117 // helper by string search on
42118 // `"assert_u8_array_within_inclusive_range"` and (b) the axis
42119 // by string search on `"RANGE-SUBSET-VIOLATION"`. Sibling
42120 // posture to
42121 // `assert_u8_array_within_u8_finite_set_panic_message_names_the_helper_and_subset_violation_axis`
42122 // on the finite-set-subset peer's provenance pin — the two
42123 // pins together bind the (helper, failed-axis) provenance
42124 // pair at ONE test per SUBSET helper on the (contiguity)
42125 // 2×2 face. The axis-provenance string
42126 // `"RANGE-SUBSET-VIOLATION"` is chosen DISTINCT from EVERY
42127 // sibling helper's axis vocabulary (`"duplicate"` on the
42128 // ARRAY-side pairwise-distinct sibling; `"OUT-OF-SET"` /
42129 // `"SET-BYTE-MISSING"` on the covers-finite-set sibling;
42130 // `"OUT-OF-RANGE"` / `"MISSING"` on the covers-inclusive-
42131 // range sibling; `"SUBSET-VIOLATION"` on the finite-set
42132 // SUBSET-only sibling; `"ARITY-MISMATCH"` on both
42133 // `_permutes_*` compound helpers; `"SET-NOT-PAIRWISE-
42134 // DISTINCT"` on the SET-side well-formedness sibling) so a
42135 // diagnostic that names the failed axis routes UNAMBIGUOUSLY
42136 // to (a) this specific range SUBSET-embedding helper, (b)
42137 // the `arr` argument as the drift site rather than the
42138 // `LO`/`HI` const parameters specifying the target range.
42139 let outcome = std::panic::catch_unwind(|| {
42140 assert_u8_array_within_inclusive_range::<2, 0, 6>(&[0u8, 7u8]);
42141 });
42142 let payload = outcome.expect_err(
42143 "assert_u8_array_within_inclusive_range must panic on an \
42144 out-of-range entry — the reject-out-of-range arm is the \
42145 sole RANGE-SUBSET-VIOLATION failure mode of the helper",
42146 );
42147 let msg = payload
42148 .downcast_ref::<&'static str>()
42149 .map(|s| (*s).to_owned())
42150 .or_else(|| payload.downcast_ref::<String>().cloned())
42151 .expect(
42152 "assert_u8_array_within_inclusive_range panic payload \
42153 must be a static &str or String",
42154 );
42155 assert!(
42156 msg.contains("assert_u8_array_within_inclusive_range"),
42157 "assert_u8_array_within_inclusive_range panic message \
42158 {msg:?} must name the helper for provenance-preserving \
42159 failure diagnostics",
42160 );
42161 assert!(
42162 msg.contains("RANGE-SUBSET-VIOLATION"),
42163 "assert_u8_array_within_inclusive_range panic message \
42164 {msg:?} must name the failed AXIS (\"RANGE-SUBSET-\
42165 VIOLATION\") for axis-provenance-preserving failure \
42166 diagnostics",
42167 );
42168 }
42169
42170 // ── `assert_u8_array_permutes_inclusive_range` — the compound
42171 // (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) permutation-of-range
42172 // verifier that collapses the pre-existing weak-witness pair
42173 // (`assert_u8_array_pairwise_distinct` +
42174 // `assert_u8_array_covers_inclusive_range`) into ONE `const _`
42175 // line per array on the three permutation-shaped
42176 // HASH_DISCRIMINATORS arrays (`AtomKind`, `QuoteForm`,
42177 // `UnquoteForm`). The runtime test surface matches the sibling-
42178 // helpers' shape (accept-singleton, accept-every-family-wide-
42179 // substrate-array, reject-arity-below-cardinality, reject-
42180 // arity-above-cardinality, reject-duplicate, reject-out-of-range,
42181 // panic-message-provenance on the ARITY-MISMATCH axis, panic-
42182 // message-provenance on the DELEGATED range-coverage axis,
42183 // panic-message-provenance on the DELEGATED pairwise-distinct
42184 // axis) split across the THREE failure arms so a regression that
42185 // silently weakens the helper on ANY arm (e.g. dropping the arity
42186 // check, dropping the range-coverage delegation, or dropping the
42187 // pairwise-distinct delegation) is caught by the helper's OWN
42188 // test surface rather than only surfacing as a false-positive on
42189 // some future permutation-shaped `[u8; N]` array's compound pin.
42190
42191 #[test]
42192 fn assert_u8_array_permutes_inclusive_range_accepts_the_singleton_range() {
42193 // Singleton range `{K..=K}` at the `[u8; 1]` corner — a
42194 // singleton array `[K]` is vacuously a permutation of `{K}`.
42195 // Cross-arity coverage on the trivial-range corner of the
42196 // const-N generic; simultaneously pins ALL THREE arms
42197 // (ARITY: `1 == 1`; RANGE-MEMBERSHIP: `K in [K, K]`;
42198 // PAIRWISE-DISTINCT: vacuously true on a singleton) at the
42199 // smallest witness. Sibling posture to
42200 // `assert_u8_array_covers_inclusive_range_accepts_the_
42201 // singleton_range` on the delegated range-coverage helper.
42202 assert_u8_array_permutes_inclusive_range::<1, 7, 7>(&[7u8]);
42203 assert_u8_array_permutes_inclusive_range::<1, 0, 0>(&[0u8]);
42204 assert_u8_array_permutes_inclusive_range::<1, 255, 255>(&[255u8]);
42205 }
42206
42207 #[test]
42208 fn assert_u8_array_permutes_inclusive_range_accepts_every_family_wide_permutation_array() {
42209 // Runtime cross-check that the SAME three permutation-shaped
42210 // HASH_DISCRIMINATORS arrays the module-level `const _: () =
42211 // ...` witnesses cover at COMPILE time are permutations of
42212 // their target ranges. A regression that removes ONE of the
42213 // `const _` witnesses would still leave THIS runtime pin as
42214 // a safety net; the const witness fires FIRST at `cargo
42215 // check`, this runtime pin catches the drift at `cargo test`.
42216 // The pair enforces the theorem at TWO stages of the
42217 // toolchain. Sibling posture to
42218 // `assert_u8_array_covers_inclusive_range_accepts_every_
42219 // family_wide_substrate_array` on the delegated range-
42220 // coverage helper; where that pin sweeps FOUR arrays
42221 // (`SexpShape` + `AtomKind` + `QuoteForm` + `UnquoteForm`)
42222 // on the single-axis SURJECTIVITY corner, this pin sweeps
42223 // the THREE permutation-shaped arrays on the compound
42224 // (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) corner —
42225 // `SexpShape::HASH_DISCRIMINATORS` is intentionally OMITTED
42226 // per the twelve-shape → seven-byte collapse rule
42227 // (DISTINCTNESS does not hold; it binds SURJECTIVITY-only
42228 // on the sibling helper).
42229 assert_u8_array_permutes_inclusive_range::<6, 0, 5>(&AtomKind::HASH_DISCRIMINATORS);
42230 assert_u8_array_permutes_inclusive_range::<4, 3, 6>(&QuoteForm::HASH_DISCRIMINATORS);
42231 assert_u8_array_permutes_inclusive_range::<2, 5, 6>(
42232 &crate::error::UnquoteForm::HASH_DISCRIMINATORS,
42233 );
42234 }
42235
42236 #[test]
42237 #[should_panic(expected = "ARITY-MISMATCH")]
42238 fn assert_u8_array_permutes_inclusive_range_panics_at_runtime_on_arity_below_cardinality() {
42239 // NEGATIVE PIN — ARITY-MISMATCH below-cardinality corner: an
42240 // array of arity `N < HI - LO + 1` MUST panic at runtime with
42241 // the ARITY-MISMATCH-named message. Pins the compound
42242 // helper's OWN reject-below-cardinality arm — a regression
42243 // that silently dropped the arity-check gate would let this
42244 // array reach the delegated `assert_u8_array_covers_
42245 // inclusive_range` call, which would then panic with the
42246 // sibling helper's `"MISSING"` message on the range byte
42247 // absent from the too-short array — masking the ARITY-
42248 // provenance behind the coverage-provenance downstream.
42249 assert_u8_array_permutes_inclusive_range::<2, 0, 2>(&[0u8, 2u8]);
42250 }
42251
42252 #[test]
42253 #[should_panic(expected = "ARITY-MISMATCH")]
42254 fn assert_u8_array_permutes_inclusive_range_panics_at_runtime_on_arity_above_cardinality() {
42255 // NEGATIVE PIN — ARITY-MISMATCH above-cardinality corner: an
42256 // array of arity `N > HI - LO + 1` MUST panic at runtime with
42257 // the ARITY-MISMATCH-named message. Symmetric sibling to the
42258 // below-cardinality pin — a regression that dropped the
42259 // arity-check gate would let this array reach the delegated
42260 // `assert_u8_array_pairwise_distinct` call, which would then
42261 // panic with the sibling helper's generic-duplicate message
42262 // on the pigeonhole-forced collision, masking the ARITY-
42263 // provenance behind the pairwise-distinct-provenance
42264 // downstream. The three entries stay within `[0, 1]` and are
42265 // pairwise-distinct on the first two positions, so the arity
42266 // check is the ONLY axis that can distinguish this from a
42267 // valid permutation.
42268 assert_u8_array_permutes_inclusive_range::<3, 0, 1>(&[0u8, 1u8, 0u8]);
42269 }
42270
42271 #[test]
42272 #[should_panic(expected = "duplicate")]
42273 fn assert_u8_array_permutes_inclusive_range_panics_at_runtime_on_duplicate() {
42274 // NEGATIVE PIN — PAIRWISE-DISTINCT arm: an array whose arity
42275 // matches the range cardinality but which contains a
42276 // duplicate entry (necessarily missing a range byte by
42277 // pigeonhole) MUST panic at runtime. Pins the delegated
42278 // `assert_u8_array_pairwise_distinct` arm — a regression
42279 // that silently dropped the delegation would leave the
42280 // duplicate uncaught. Note: the panic message here surfaces
42281 // from the SIBLING helper (containing `"duplicate"`) rather
42282 // than a compound-helper-namespaced string, per the
42283 // delegation-based body design; the compound helper's OWN
42284 // name still appears in the const-eval panic trace for a
42285 // caller debugging a `cargo check` failure.
42286 assert_u8_array_permutes_inclusive_range::<3, 0, 2>(&[0u8, 1u8, 1u8]);
42287 }
42288
42289 #[test]
42290 #[should_panic(expected = "OUT-OF-RANGE")]
42291 fn assert_u8_array_permutes_inclusive_range_panics_at_runtime_on_out_of_range() {
42292 // NEGATIVE PIN — RANGE-MEMBERSHIP arm: an array whose arity
42293 // matches the range cardinality but which contains an
42294 // out-of-range entry MUST panic at runtime with the
42295 // delegated `assert_u8_array_covers_inclusive_range`'s
42296 // `"OUT-OF-RANGE"` axis-provenance message. Pins the
42297 // delegated range-membership arm — a regression that
42298 // silently dropped the delegation would leave the
42299 // out-of-range entry uncaught. The three entries `[0, 1, 3]`
42300 // exhaust the arity of `[0..=2]` (3 entries for a 3-byte
42301 // range) but include `3u8` outside the target range;
42302 // pairwise-distinct is satisfied so this array can ONLY be
42303 // rejected on the range-membership axis.
42304 assert_u8_array_permutes_inclusive_range::<3, 0, 2>(&[0u8, 1u8, 3u8]);
42305 }
42306
42307 #[test]
42308 fn assert_u8_array_permutes_inclusive_range_panic_message_names_the_helper_and_arity_mismatch_axis(
42309 ) {
42310 // PANIC-MESSAGE PROVENANCE PIN — ARITY-MISMATCH arm: the
42311 // panic message MUST begin with the compound helper's own
42312 // name AND identify the failed AXIS as "ARITY-MISMATCH" so
42313 // downstream diagnostics route the drift back to (a) THIS
42314 // compound helper by string search on
42315 // `"assert_u8_array_permutes_inclusive_range"` and (b) the
42316 // ARITY axis by string search on `"ARITY-MISMATCH"`. This
42317 // string is chosen DISTINCT from every sibling helper's axis
42318 // strings ("OUT-OF-RANGE" / "MISSING" on the range-coverage
42319 // helper; "OUT-OF-SET" / "SET-BYTE-MISSING" on the finite-
42320 // set-coverage helper) so a drift's axis-provenance routes
42321 // UNAMBIGUOUSLY to (helper, axis) even in the presence of
42322 // the multiple sibling helpers. Sibling posture to
42323 // `assert_u8_array_covers_inclusive_range_panic_message_
42324 // names_the_helper_and_range_bound_axis` on the range-
42325 // coverage helper — both bind the (helper, failed-axis)
42326 // provenance pair at ONE test per axis.
42327 let outcome = std::panic::catch_unwind(|| {
42328 assert_u8_array_permutes_inclusive_range::<2, 0, 2>(&[0u8, 2u8]);
42329 });
42330 let payload = outcome.expect_err(
42331 "assert_u8_array_permutes_inclusive_range must panic on \
42332 an arity-cardinality mismatch — the reject-arity-\
42333 mismatch arm is one of the three failure modes of the \
42334 compound helper",
42335 );
42336 let msg = payload
42337 .downcast_ref::<&'static str>()
42338 .map(|s| (*s).to_owned())
42339 .or_else(|| payload.downcast_ref::<String>().cloned())
42340 .expect(
42341 "assert_u8_array_permutes_inclusive_range panic \
42342 payload must be a static &str or String",
42343 );
42344 assert!(
42345 msg.contains("assert_u8_array_permutes_inclusive_range"),
42346 "assert_u8_array_permutes_inclusive_range ARITY-MISMATCH \
42347 panic message {msg:?} must name the helper for \
42348 provenance-preserving failure diagnostics",
42349 );
42350 assert!(
42351 msg.contains("ARITY-MISMATCH"),
42352 "assert_u8_array_permutes_inclusive_range ARITY-MISMATCH \
42353 panic message {msg:?} must name the failed AXIS \
42354 (\"ARITY-MISMATCH\") for axis-provenance-preserving \
42355 failure diagnostics — the string is chosen DISTINCT \
42356 from every sibling helper's axis strings so downstream \
42357 diagnostics route UNAMBIGUOUSLY to this compound \
42358 helper's arity arm",
42359 );
42360 }
42361
42362 // ── `assert_u8_array_permutes_finite_set` — the compound
42363 // (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) permutation-of-finite-set
42364 // verifier on the NON-CONTIGUOUS-FINITE-SET corner of the
42365 // (contiguity) axis peer to the pre-existing
42366 // `assert_u8_array_permutes_inclusive_range` (contiguous-
42367 // inclusive-range corner) sibling. Ships `StructuralKind::HASH_
42368 // DISCRIMINATORS` from the pre-lift weak-witness pair
42369 // (`assert_u8_array_pairwise_distinct` + `assert_u8_array_covers_
42370 // finite_set`) into the compound tier at ONE `const _` line while
42371 // adding the arity-cardinality-equality contract that neither
42372 // weak sibling carries alone. The runtime test surface mirrors
42373 // the sibling `assert_u8_array_permutes_inclusive_range` compound
42374 // helper's shape (accept-singleton-set, accept-every-family-wide-
42375 // substrate-array, reject-arity-below-cardinality, reject-arity-
42376 // above-cardinality, reject-duplicate, reject-out-of-set, panic-
42377 // message-provenance on the ARITY-MISMATCH axis) split across the
42378 // three failure arms so a regression that silently weakens the
42379 // helper on ANY arm (e.g. dropping the arity check, dropping the
42380 // covers-finite-set delegation, or dropping the pairwise-distinct
42381 // delegation) is caught by the helper's OWN test surface rather
42382 // than only surfacing as a false-positive on some future
42383 // permutation-of-finite-set-shaped `[u8; N]` array's compound pin.
42384
42385 #[test]
42386 fn assert_u8_array_permutes_finite_set_accepts_the_singleton_set() {
42387 // Singleton set `{K}` at the `[u8; 1]` corner — a singleton
42388 // array `[K]` is vacuously a permutation of `{K}`. Cross-arity
42389 // coverage on the trivial-set corner of the const-N generic;
42390 // simultaneously pins ALL THREE arms (ARITY: `1 == 1`; SET-
42391 // MEMBERSHIP: `K in {K}`; PAIRWISE-DISTINCT: vacuously true on
42392 // a singleton) at the smallest witness. Sibling posture to
42393 // `assert_u8_array_permutes_inclusive_range_accepts_the_
42394 // singleton_range` on the contiguous-range corner peer.
42395 assert_u8_array_permutes_finite_set::<1, 1>(&[7u8], &[7u8]);
42396 assert_u8_array_permutes_finite_set::<1, 1>(&[0u8], &[0u8]);
42397 assert_u8_array_permutes_finite_set::<1, 1>(&[255u8], &[255u8]);
42398 }
42399
42400 #[test]
42401 fn assert_u8_array_permutes_finite_set_accepts_every_family_wide_permutation_of_finite_set_array(
42402 ) {
42403 // Runtime cross-check that the SAME
42404 // `StructuralKind::HASH_DISCRIMINATORS` array the module-level
42405 // `const _: () = ...` witness covers at COMPILE time is a
42406 // permutation of the non-contiguous target set `{0, 2}`. A
42407 // regression that removes the `const _` witness would still
42408 // leave THIS runtime pin as a safety net; the const witness
42409 // fires FIRST at `cargo check`, this runtime pin catches the
42410 // drift at `cargo test`. The pair enforces the theorem at TWO
42411 // stages of the toolchain. Sibling posture to
42412 // `assert_u8_array_permutes_inclusive_range_accepts_every_
42413 // family_wide_permutation_array` on the contiguous-range
42414 // corner: where that pin sweeps THREE range-covering arrays
42415 // (`AtomKind` + `QuoteForm` + `UnquoteForm`), this pin binds
42416 // the ONE non-contiguous-covering permutation-shaped array
42417 // (`StructuralKind`). Together the two runtime pins close the
42418 // whole compound tier of the substrate's `[u8; N]`
42419 // HASH_DISCRIMINATORS vocabulary at runtime symmetrically to
42420 // the compile-time compound witnesses.
42421 assert_u8_array_permutes_finite_set::<2, 2>(
42422 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
42423 &[0u8, 2u8],
42424 );
42425 }
42426
42427 #[test]
42428 fn assert_u8_array_permutes_finite_set_accepts_a_synthetic_non_contiguous_partition() {
42429 // POSITIVE — a synthetic `[u8; 3]` permutation of the non-
42430 // contiguous partition `{0, 2, 5}` (two gaps: at `{1}` and at
42431 // `{3, 4}`). Isolates the helper's INJECTIVITY-∧-SURJECTIVITY-
42432 // ∧-ARITY verdict from the substrate constants: a green here
42433 // plus a red on any of the negative pins below constrains the
42434 // helper's behavior structurally on the non-contiguous corner,
42435 // independent of the substrate's specific byte layout. Sibling
42436 // posture to `assert_u8_array_covers_finite_set_accepts_the_
42437 // non_contiguous_partition` on the single-axis SURJECTIVITY
42438 // sibling — where that pin binds the (SET-MEMBERSHIP + FULL-
42439 // COVERAGE) axes on a non-contiguous set, THIS pin binds the
42440 // compound (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY) contract on
42441 // the same shape.
42442 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 2u8, 5u8], &[0u8, 2u8, 5u8]);
42443 assert_u8_array_permutes_finite_set::<3, 3>(&[5u8, 0u8, 2u8], &[0u8, 2u8, 5u8]);
42444 }
42445
42446 #[test]
42447 #[should_panic(expected = "ARITY-MISMATCH")]
42448 fn assert_u8_array_permutes_finite_set_panics_at_runtime_on_arity_below_cardinality() {
42449 // NEGATIVE PIN — ARITY-MISMATCH below-cardinality corner: an
42450 // array of arity `N < M` MUST panic at runtime with the ARITY-
42451 // MISMATCH-named message. Pins the compound helper's OWN
42452 // reject-below-cardinality arm — a regression that silently
42453 // dropped the arity-check gate would let this array reach the
42454 // delegated `assert_u8_array_covers_finite_set` call, which
42455 // would then panic with the sibling helper's `"SET-BYTE-
42456 // MISSING"` message on the set byte absent from the too-short
42457 // array — masking the ARITY-provenance behind the coverage-
42458 // provenance downstream. The array `[0]` covers set byte `0`
42459 // but is missing set byte `2`. Sibling posture to
42460 // `assert_u8_array_permutes_inclusive_range_panics_at_runtime_
42461 // on_arity_below_cardinality` on the contiguous-range corner.
42462 assert_u8_array_permutes_finite_set::<1, 2>(&[0u8], &[0u8, 2u8]);
42463 }
42464
42465 #[test]
42466 #[should_panic(expected = "ARITY-MISMATCH")]
42467 fn assert_u8_array_permutes_finite_set_panics_at_runtime_on_arity_above_cardinality() {
42468 // NEGATIVE PIN — ARITY-MISMATCH above-cardinality corner: an
42469 // array of arity `N > M` MUST panic at runtime with the
42470 // ARITY-MISMATCH-named message. Symmetric sibling to the
42471 // below-cardinality pin — a regression that dropped the arity-
42472 // check gate would let this array reach the delegated
42473 // `assert_u8_array_pairwise_distinct` call, which would then
42474 // panic with the sibling helper's generic-duplicate message on
42475 // the pigeonhole-forced collision, masking the ARITY-
42476 // provenance behind the pairwise-distinct-provenance
42477 // downstream. The three entries `[0, 2, 0]` stay within
42478 // `{0, 2}` (satisfying SET-MEMBERSHIP) but WOULD fail
42479 // pairwise-distinct downstream on the duplicate `0u8` —
42480 // however, the ARITY-check FIRES FIRST so the panic message
42481 // routes to the ARITY axis with the compound helper's own
42482 // name.
42483 assert_u8_array_permutes_finite_set::<3, 2>(&[0u8, 2u8, 0u8], &[0u8, 2u8]);
42484 }
42485
42486 #[test]
42487 #[should_panic(expected = "duplicate")]
42488 fn assert_u8_array_permutes_finite_set_panics_at_runtime_on_duplicate() {
42489 // NEGATIVE PIN — PAIRWISE-DISTINCT arm: an array whose arity
42490 // matches the set cardinality but which contains a duplicate
42491 // entry (necessarily missing a set byte by pigeonhole) MUST
42492 // panic at runtime. Pins the delegated
42493 // `assert_u8_array_pairwise_distinct` arm — a regression that
42494 // silently dropped the delegation would leave the duplicate
42495 // uncaught. Note: the panic message here surfaces from the
42496 // SIBLING helper (containing `"duplicate"`) rather than a
42497 // compound-helper-namespaced string, per the delegation-based
42498 // body design; the compound helper's OWN name still appears
42499 // in the const-eval panic trace for a caller debugging a
42500 // `cargo check` failure. Sibling posture to
42501 // `assert_u8_array_permutes_inclusive_range_panics_at_runtime_
42502 // on_duplicate` on the contiguous-range corner.
42503 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 2u8, 2u8], &[0u8, 2u8, 5u8]);
42504 }
42505
42506 #[test]
42507 #[should_panic(expected = "OUT-OF-SET")]
42508 fn assert_u8_array_permutes_finite_set_panics_at_runtime_on_out_of_set() {
42509 // NEGATIVE PIN — SET-MEMBERSHIP arm: an array whose arity
42510 // matches the set cardinality but which contains an entry
42511 // outside the target set MUST panic at runtime with the
42512 // delegated `assert_u8_array_covers_finite_set`'s `"OUT-OF-
42513 // SET"` axis-provenance message. Pins the delegated set-
42514 // membership arm — a regression that silently dropped the
42515 // delegation would leave the out-of-set entry uncaught. The
42516 // three entries `[0, 2, 3]` exhaust the arity of `{0, 2, 5}`
42517 // (3 entries for a 3-byte set) but include `3u8` outside the
42518 // target set; pairwise-distinct is satisfied so this array
42519 // can ONLY be rejected on the set-membership axis. Sibling
42520 // posture to `assert_u8_array_permutes_inclusive_range_panics_
42521 // at_runtime_on_out_of_range` on the contiguous-range corner
42522 // (`"OUT-OF-SET"` here vs. `"OUT-OF-RANGE"` there — chosen
42523 // DISTINCT so downstream diagnostics route UNAMBIGUOUSLY to
42524 // the failed contiguity corner).
42525 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 2u8, 3u8], &[0u8, 2u8, 5u8]);
42526 }
42527
42528 #[test]
42529 #[should_panic(expected = "OUT-OF-SET")]
42530 fn assert_u8_array_permutes_finite_set_panics_on_gap_byte_drift() {
42531 // NEGATIVE PIN — ARCHETYPE GAP-BYTE corner: an entry that
42532 // drifts INTO the intentional gap of a non-contiguous target
42533 // set MUST panic — this is the exact regression the archetype
42534 // `StructuralKind::HASH_DISCRIMINATORS` witness catches. A
42535 // regression that lifted a fresh `1u8` entry into the
42536 // `{0, 2}`-partitioned array would silently collide with
42537 // `AtomKind::OUTER_HASH_DISCRIMINATOR = 1u8` on the outer-
42538 // `Sexp` cache-key partition; this pin binds that failure mode
42539 // as an OUT-OF-SET rejection at the delegated covers-finite-
42540 // set arm's panic site. Sibling posture to `assert_u8_array_
42541 // covers_finite_set_panics_on_gap_byte_drift` at the single-
42542 // axis SURJECTIVITY sibling — where that pin binds the gap-
42543 // byte-drift failure at the (SET-MEMBERSHIP) axis alone, THIS
42544 // pin binds the SAME drift on the compound (INJECTIVITY ∧
42545 // SURJECTIVITY ∧ ARITY) contract. The four-entry witness
42546 // `[0, 1, 2, 5]` stays pairwise-distinct and matches the set
42547 // cardinality of `{0, 2, 1, 5}` but the middle `1u8` — if the
42548 // set were `{0, 2, 5}` and `[0, 1, 2]` were the array — would
42549 // fire the OUT-OF-SET arm; here the arity is aligned to test
42550 // the middle-gap drift with `1u8` in `arr` but NOT in `set`.
42551 assert_u8_array_permutes_finite_set::<3, 3>(&[0u8, 1u8, 2u8], &[0u8, 2u8, 5u8]);
42552 }
42553
42554 #[test]
42555 fn assert_u8_array_permutes_finite_set_panic_message_names_the_helper_and_arity_mismatch_axis()
42556 {
42557 // PANIC-MESSAGE PROVENANCE PIN — ARITY-MISMATCH arm: the panic
42558 // message MUST begin with the compound helper's own name AND
42559 // identify the failed AXIS as "ARITY-MISMATCH" so downstream
42560 // diagnostics route the drift back to (a) THIS compound helper
42561 // by string search on
42562 // `"assert_u8_array_permutes_finite_set"` and (b) the ARITY
42563 // axis by string search on `"ARITY-MISMATCH"`. The
42564 // "ARITY-MISMATCH" axis-name is SHARED with the contiguous-
42565 // range sibling `assert_u8_array_permutes_inclusive_range`
42566 // (both compound permutation helpers share the arity axis),
42567 // but the HELPER-name distinguishes the two: string search on
42568 // (`"assert_u8_array_permutes_finite_set"`, `"ARITY-MISMATCH"`)
42569 // routes UNAMBIGUOUSLY to the (non-contiguous corner, arity
42570 // arm) pair. Sibling posture to
42571 // `assert_u8_array_permutes_inclusive_range_panic_message_
42572 // names_the_helper_and_arity_mismatch_axis` on the contiguous-
42573 // range corner — both bind the (helper, failed-axis)
42574 // provenance pair at ONE test per axis.
42575 let outcome = std::panic::catch_unwind(|| {
42576 assert_u8_array_permutes_finite_set::<1, 2>(&[0u8], &[0u8, 2u8]);
42577 });
42578 let payload = outcome.expect_err(
42579 "assert_u8_array_permutes_finite_set must panic on an \
42580 arity-cardinality mismatch — the reject-arity-mismatch \
42581 arm is one of the three failure modes of the compound \
42582 helper",
42583 );
42584 let msg = payload
42585 .downcast_ref::<&'static str>()
42586 .map(|s| (*s).to_owned())
42587 .or_else(|| payload.downcast_ref::<String>().cloned())
42588 .expect(
42589 "assert_u8_array_permutes_finite_set panic payload \
42590 must be a static &str or String",
42591 );
42592 assert!(
42593 msg.contains("assert_u8_array_permutes_finite_set"),
42594 "assert_u8_array_permutes_finite_set ARITY-MISMATCH panic \
42595 message {msg:?} must name the helper for provenance-\
42596 preserving failure diagnostics — the HELPER-name is the \
42597 axis-name-tie-breaker between this helper and the \
42598 contiguous-range sibling which SHARES the \
42599 \"ARITY-MISMATCH\" axis-provenance string",
42600 );
42601 assert!(
42602 msg.contains("ARITY-MISMATCH"),
42603 "assert_u8_array_permutes_finite_set ARITY-MISMATCH panic \
42604 message {msg:?} must name the failed AXIS \
42605 (\"ARITY-MISMATCH\") for axis-provenance-preserving \
42606 failure diagnostics",
42607 );
42608 }
42609
42610 // ── `assert_scalar_plus_two_u8_arrays_permute_inclusive_range` —
42611 // the compound JOINT (INJECTIVITY ∧ SURJECTIVITY ∧ ARITY)
42612 // permutation-of-range verifier on the (scalar-plus-two-arrays)
42613 // corner of the (carving-shape) axis peer to the pre-existing
42614 // (single-array) corner. The runtime test surface matches the
42615 // sibling `assert_u8_array_permutes_inclusive_range` compound
42616 // helper's shape (accept-canonical-outer-Sexp-partition, accept-
42617 // synthetic-valid-partition, reject-arity-below-cardinality,
42618 // reject-arity-above-cardinality, reject-scalar-out-of-range,
42619 // reject-first-array-out-of-range, reject-second-array-out-of-
42620 // range, reject-cross-carving-duplicate, reject-intra-carving-
42621 // duplicate, panic-message-provenance on the JOINT ARITY-
42622 // MISMATCH axis, panic-message-provenance on the JOINT SCALAR-
42623 // OUT-OF-RANGE axis) split across the six failure arms so a
42624 // regression that silently weakens the helper on ANY arm is
42625 // caught by the helper's OWN test surface rather than only
42626 // surfacing as a false-positive on some future permutation-
42627 // shaped joint carving's compound pin.
42628
42629 #[test]
42630 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_accepts_canonical_outer_sexp_partition(
42631 ) {
42632 // Runtime cross-check that the SAME joint carving the
42633 // module-level `const _: () = ...` witness covers at COMPILE
42634 // time is a permutation of the outer-`Sexp` cache-key
42635 // discriminator range `{0..=6}`. A regression that removes
42636 // the `const _` witness would still leave THIS runtime pin as
42637 // a safety net; the const witness fires FIRST at `cargo
42638 // check`, this runtime pin catches the drift at `cargo test`.
42639 // The pair enforces the joint theorem at TWO stages of the
42640 // toolchain. Sibling posture to
42641 // `assert_u8_array_permutes_inclusive_range_accepts_every_
42642 // family_wide_permutation_array` on the (single-array)
42643 // corner — where that pin sweeps THREE arrays on the
42644 // single-array corner, THIS pin binds the ONE joint carving
42645 // on the (scalar-plus-two-arrays) corner.
42646 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42647 AtomKind::OUTER_HASH_DISCRIMINATOR,
42648 &crate::error::StructuralKind::HASH_DISCRIMINATORS,
42649 &QuoteForm::HASH_DISCRIMINATORS,
42650 );
42651 }
42652
42653 #[test]
42654 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_accepts_synthetic_valid_partition()
42655 {
42656 // POSITIVE — a valid `(scalar, [u8; 2], [u8; 4])` permutation
42657 // of `{0..=6}` byte-identical in shape to the outer-`Sexp`
42658 // partition but with the scalar at `1u8` remapped through
42659 // literals rather than the substrate constant. Isolates the
42660 // helper's INJECTIVITY-∧-SURJECTIVITY-∧-ARITY verdict from
42661 // the substrate constants: a green here plus a red on any
42662 // of the negative pins below constrains the helper's
42663 // behavior structurally, independent of the substrate's
42664 // specific byte layout.
42665 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42666 1u8,
42667 &[0u8, 2u8],
42668 &[3u8, 4u8, 5u8, 6u8],
42669 );
42670 }
42671
42672 #[test]
42673 #[should_panic(expected = "ARITY-MISMATCH")]
42674 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_arity_below_cardinality(
42675 ) {
42676 // NEGATIVE PIN — ARITY-MISMATCH below-cardinality corner: a
42677 // joint carving of arity `1 + M + N < HI - LO + 1` MUST
42678 // panic at runtime with the ARITY-MISMATCH-named message.
42679 // Pins the helper's OWN reject-below-cardinality arm — a
42680 // regression that silently dropped the arity-check gate
42681 // would let this triple reach the sweep-and-count loop,
42682 // which would then panic with `"MISSING"` on the range byte
42683 // absent from the too-short joint carving — masking the
42684 // JOINT ARITY-provenance behind the JOINT SURJECTIVITY-
42685 // provenance downstream. Sibling posture to
42686 // `assert_u8_array_permutes_inclusive_range_panics_at_
42687 // runtime_on_arity_below_cardinality` on the (single-array)
42688 // corner.
42689 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 3, 0, 6>(
42690 1u8,
42691 &[0u8, 2u8],
42692 &[3u8, 4u8, 5u8],
42693 );
42694 }
42695
42696 #[test]
42697 #[should_panic(expected = "ARITY-MISMATCH")]
42698 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_arity_above_cardinality(
42699 ) {
42700 // NEGATIVE PIN — ARITY-MISMATCH above-cardinality corner:
42701 // symmetric sibling to the below-cardinality pin. `1 + 2 + 3
42702 // = 6` slots for a `{0..=2}` range of cardinality 3 — the
42703 // arity check fires FIRST before the sweep-and-count would
42704 // catch the joint duplicates on `0`/`1`/`2`. Pins that the
42705 // ARITY-provenance surfaces even when downstream axes would
42706 // ALSO reject.
42707 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 3, 0, 2>(
42708 0u8,
42709 &[1u8, 2u8],
42710 &[0u8, 1u8, 2u8],
42711 );
42712 }
42713
42714 #[test]
42715 #[should_panic(expected = "OUT-OF-RANGE")]
42716 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_scalar_out_of_range(
42717 ) {
42718 // NEGATIVE PIN — SCALAR OUT-OF-RANGE arm: a joint carving
42719 // whose arity matches the range cardinality but whose SCALAR
42720 // lies outside `[LO, HI]` MUST panic at runtime. Pins the
42721 // helper's scalar-carving arm distinct from the array
42722 // carvings' out-of-range arms — a regression that skipped
42723 // the scalar check but kept the array checks would leave
42724 // this drift uncaught.
42725 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42726 7u8,
42727 &[0u8, 2u8],
42728 &[3u8, 4u8, 5u8, 6u8],
42729 );
42730 }
42731
42732 #[test]
42733 #[should_panic(expected = "OUT-OF-RANGE")]
42734 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_first_array_out_of_range(
42735 ) {
42736 // NEGATIVE PIN — FIRST-ARRAY OUT-OF-RANGE arm: a joint
42737 // carving whose arity + scalar are valid but whose FIRST
42738 // ARRAY carries an out-of-range entry MUST panic at runtime.
42739 // The `1u8` slot the scalar occupies is left absent from
42740 // the first array; the entry `7u8` lies outside `[0, 6]`.
42741 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42742 1u8,
42743 &[0u8, 7u8],
42744 &[3u8, 4u8, 5u8, 6u8],
42745 );
42746 }
42747
42748 #[test]
42749 #[should_panic(expected = "OUT-OF-RANGE")]
42750 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_second_array_out_of_range(
42751 ) {
42752 // NEGATIVE PIN — SECOND-ARRAY OUT-OF-RANGE arm: symmetric
42753 // sibling to the first-array pin. The entry `9u8` in the
42754 // second array lies outside `[0, 6]`; all other arms are
42755 // green.
42756 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42757 1u8,
42758 &[0u8, 2u8],
42759 &[3u8, 4u8, 5u8, 9u8],
42760 );
42761 }
42762
42763 #[test]
42764 #[should_panic(expected = "duplicate")]
42765 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_cross_carving_duplicate(
42766 ) {
42767 // NEGATIVE PIN — JOINT-duplicate arm (cross-carving): the
42768 // byte `1u8` appears in BOTH the scalar carving AND the
42769 // first array. Arity matches, all bytes are in-range, but
42770 // JOINT INJECTIVITY fails — the duplicate byte occurs at
42771 // `count == 2` in the sweep-and-count loop, masking the
42772 // pigeonhole-forced JOINT-MISSING at byte `2u8` behind the
42773 // duplicate arm's message. Pins the cross-carving collision
42774 // detection between the scalar arm and a downstream array
42775 // arm.
42776 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42777 1u8,
42778 &[0u8, 1u8],
42779 &[3u8, 4u8, 5u8, 6u8],
42780 );
42781 }
42782
42783 #[test]
42784 #[should_panic(expected = "duplicate")]
42785 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panics_at_runtime_on_intra_carving_duplicate(
42786 ) {
42787 // NEGATIVE PIN — JOINT-duplicate arm (intra-carving): the
42788 // byte `0u8` appears TWICE inside the first array. Arity
42789 // matches, all bytes are in-range, but JOINT INJECTIVITY
42790 // fails — the sweep-and-count discipline treats intra- and
42791 // cross-carving collisions with the SAME mechanism, so this
42792 // failure mode surfaces on the same `"duplicate"` axis-
42793 // provenance message as the cross-carving pin.
42794 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 4, 0, 6>(
42795 1u8,
42796 &[0u8, 0u8],
42797 &[3u8, 4u8, 5u8, 6u8],
42798 );
42799 }
42800
42801 #[test]
42802 fn assert_scalar_plus_two_u8_arrays_permute_inclusive_range_panic_message_names_the_helper_and_arity_mismatch_axis(
42803 ) {
42804 // PANIC-MESSAGE PROVENANCE PIN — ARITY-MISMATCH arm: the
42805 // panic message MUST begin with the compound helper's own
42806 // name AND identify the failed AXIS as "ARITY-MISMATCH".
42807 // Sibling posture to `assert_u8_array_permutes_inclusive_
42808 // range_panic_message_names_the_helper_and_arity_mismatch_
42809 // axis` on the (single-array) corner — both bind the
42810 // (helper, failed-axis) provenance pair.
42811 let outcome = std::panic::catch_unwind(|| {
42812 assert_scalar_plus_two_u8_arrays_permute_inclusive_range::<2, 3, 0, 6>(
42813 1u8,
42814 &[0u8, 2u8],
42815 &[3u8, 4u8, 5u8],
42816 );
42817 });
42818 let payload = outcome.expect_err(
42819 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range \
42820 must panic on an arity-cardinality mismatch",
42821 );
42822 let msg = payload
42823 .downcast_ref::<&'static str>()
42824 .map(|s| (*s).to_owned())
42825 .or_else(|| payload.downcast_ref::<String>().cloned())
42826 .expect(
42827 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range \
42828 panic payload must be a static &str or String",
42829 );
42830 assert!(
42831 msg.contains("assert_scalar_plus_two_u8_arrays_permute_inclusive_range"),
42832 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range \
42833 ARITY-MISMATCH panic message {msg:?} must name the helper \
42834 for provenance-preserving failure diagnostics",
42835 );
42836 assert!(
42837 msg.contains("ARITY-MISMATCH"),
42838 "assert_scalar_plus_two_u8_arrays_permute_inclusive_range \
42839 ARITY-MISMATCH panic message {msg:?} must name the failed \
42840 AXIS (\"ARITY-MISMATCH\") for axis-provenance-preserving \
42841 failure diagnostics",
42842 );
42843 }
42844
42845 // ── node_count: structural size on the outer Sexp algebra ──────────
42846
42847 #[test]
42848 fn node_count_nil_is_one() {
42849 // The residual-axis unit-payload arm contributes exactly one
42850 // node — the outer arm itself. Base case of the recursion.
42851 assert_eq!(Sexp::Nil.node_count(), 1);
42852 }
42853
42854 #[test]
42855 fn node_count_atom_is_one_for_every_atom_kind() {
42856 // Every atomic arm — Symbol, Keyword, Str, Int, Float, Bool —
42857 // is a leaf on the AST algebra. The payload's own length
42858 // (a string with 100 chars, an integer with a large value)
42859 // does not contribute; the projection counts NODES on the
42860 // typed algebra, not bytes on the payload.
42861 assert_eq!(Sexp::symbol("foo").node_count(), 1);
42862 assert_eq!(Sexp::keyword("k").node_count(), 1);
42863 assert_eq!(Sexp::string("hello world").node_count(), 1);
42864 assert_eq!(Sexp::int(42).node_count(), 1);
42865 assert_eq!(Sexp::float(1.5).node_count(), 1);
42866 assert_eq!(Sexp::boolean(true).node_count(), 1);
42867 assert_eq!(
42868 Sexp::symbol("a-symbol-with-a-very-long-name").node_count(),
42869 1
42870 );
42871 }
42872
42873 #[test]
42874 fn node_count_empty_list_is_one() {
42875 // The residual-axis payload-bearing arm with an empty payload
42876 // contributes exactly one node — the outer arm itself, no
42877 // children to sum over. Sits at the boundary between the
42878 // `Sexp::Nil` unit arm (also one node) and every non-empty
42879 // list.
42880 assert_eq!(Sexp::List(vec![]).node_count(), 1);
42881 }
42882
42883 #[test]
42884 fn node_count_flat_list_is_one_plus_child_count() {
42885 // `(a b c)` — one outer List arm plus three Atom children,
42886 // each contributing one node. Load-bearing arithmetic
42887 // identity: `list(items).node_count() == 1 +
42888 // sum(item.node_count())` with atom children.
42889 let form = Sexp::list([Sexp::symbol("a"), Sexp::symbol("b"), Sexp::symbol("c")]);
42890 assert_eq!(form.node_count(), 4);
42891 }
42892
42893 #[test]
42894 fn node_count_nested_list_sums_recursively() {
42895 // `(a (b c) d)` — one outer, plus a=1, plus inner (b c)=3,
42896 // plus d=1. Total 6. Pins the recursive sum over the tree.
42897 let form = Sexp::list([
42898 Sexp::symbol("a"),
42899 Sexp::list([Sexp::symbol("b"), Sexp::symbol("c")]),
42900 Sexp::symbol("d"),
42901 ]);
42902 assert_eq!(form.node_count(), 6);
42903 }
42904
42905 #[test]
42906 fn node_count_each_quote_family_wrapper_adds_one_plus_inner() {
42907 // The four homoiconic wrappers each contribute one node for
42908 // the wrapper arm plus the node count of the wrapped inner.
42909 // `'x` = 2 (Quote wrapper + Atom x).
42910 // `` `x `` = 2. `,x` = 2. `,@x` = 2.
42911 let x = Sexp::symbol("x");
42912 assert_eq!(Sexp::Quote(Box::new(x.clone())).node_count(), 2);
42913 assert_eq!(Sexp::Quasiquote(Box::new(x.clone())).node_count(), 2);
42914 assert_eq!(Sexp::Unquote(Box::new(x.clone())).node_count(), 2);
42915 assert_eq!(Sexp::UnquoteSplice(Box::new(x.clone())).node_count(), 2);
42916 // Nested inner: `'(a b)` = Quote wrapper (1) + inner (a b)
42917 // list (3) = 4. Pins the recursion through wrappers.
42918 let inner = Sexp::list([Sexp::symbol("a"), Sexp::symbol("b")]);
42919 assert_eq!(Sexp::Quote(Box::new(inner)).node_count(), 4);
42920 }
42921
42922 #[test]
42923 fn node_count_is_monotone_under_list_growth() {
42924 // Adding a strictly-larger subtree to a list strictly grows
42925 // the node count. LOAD-BEARING monotonicity pin — a resource
42926 // ceiling keyed on `node_count` relies on this identity so
42927 // that a strictly-larger expansion is bounded by a strictly-
42928 // larger count. A regression that ever ignored a child's
42929 // contribution (a bug that hardcoded arm-only counting)
42930 // fails this pin because the added subtree's own children
42931 // would silently drop out.
42932 let small = Sexp::list([Sexp::symbol("a")]);
42933 let large = Sexp::list([
42934 Sexp::symbol("a"),
42935 Sexp::list([Sexp::symbol("b"), Sexp::symbol("c")]),
42936 ]);
42937 assert!(large.node_count() > small.node_count());
42938 assert_eq!(small.node_count(), 2);
42939 assert_eq!(large.node_count(), 5);
42940 }
42941}