pub enum Atom {
Symbol(String),
Keyword(String),
Str(String),
Int(i64),
Float(f64),
Bool(bool),
}Variants§
Symbol(String)
Plain symbol (foo, defpoint, seph.1).
Keyword(String)
Keyword (:parent, :attr) — a symbol bound to itself.
Str(String)
String literal.
Int(i64)
Integer literal.
Float(f64)
Floating literal.
Bool(bool)
Boolean literal (#t, #f).
Implementations§
Source§impl Atom
impl Atom
Sourcepub const KEYWORD_MARKER: &'static str = ":"
pub const KEYWORD_MARKER: &'static str = ":"
The canonical : marker prefix that a Self::Keyword payload
projects THROUGH when rendered as / classified from its
canonical-string surface across the substrate’s four
Keyword-round-trip sites — the reader-entry classifier
(Self::from_lexeme), the Lisp-canonical-form projection
([fmt::Display for Atom]), the JSON-canonical-form projection
(Self::to_json), and the iac-forge-canonical-form projection
(Atom::to_iac_forge_sexpr (removed)).
Pre-lift the same ":" byte lived inline at four sites: ':'
(as a char pattern) at the Self::from_lexeme strip site
and ":{s}" (three byte-identical format-string literals) at the
three canonical-form projection sites. Post-lift the marker
byte lives at ONE canonical constant on the Atom algebra
that all four sites bind to; a future refactor that swaps the
marker (e.g. a Racket-compat port to #:name, a Clojure-compat
port to ::name) touches ONE line rather than four inline
bytes that would silently drift out of round-trip agreement if
one was updated without the others.
Load-bearing round-trip contract:
Self::from_lexeme(&Self::keyword(name).to_string()) == Self::keyword(name) — the reader-entry classifier’s
strip_prefix(Self::KEYWORD_MARKER) gate and the Lisp-canonical
[Display]’s write!(f, "{}{name}", Self::KEYWORD_MARKER)
emission both bind to THIS constant so the pair cannot drift.
Cross-surface round-trip: Self::to_json and
Atom::to_iac_forge_sexpr (removed) emit the same prefix so any BLAKE3
attestation over an iac-forge canonical form of a Keyword atom
matches the JSON canonical form byte-for-byte on the prefix
axis.
Sibling-shape lift to the workspace’s other prefix-marker
constants: crate::error::UnquoteForm::ALL projects each
template-marker variant to its punctuation prefix via
crate::ast::QuoteForm::prefix ("'", "“, ”,“, ”,@“) — a peer axis on the substrate's marker-byte algebra. This constant sits on the [Atom] algebra at the atomic-payload axis where the [QuoteForm::prefix`] projection sits at the
homoiconic-wrapper axis.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Keyword payload, canonical : prefix) pairing now binds at
ONE constant on the closed-set Atom algebra regardless of
which of the four reader-entry / rendering surfaces reaches in.
THEORY.md §VI.1 — generation over composition; the four
byte-identical ":" / ":{s}" inline literals collapse onto
ONE named constant, matching the substrate’s three-times rule
(four occurrences, well past the ≥2 lift threshold).
THEORY.md §V.1 — knowable platform; the canonical
keyword-marker byte becomes a TYPE-level constant on the
substrate algebra rather than four inline bytes at four
consumer surfaces.
Sourcepub const KEYWORD_MARKER_LEAD: char = ':'
pub const KEYWORD_MARKER_LEAD: char = ':'
Canonical : LEAD char of the Self::KEYWORD_MARKER prefix
(":") — the ONE canonical char on the Atom algebra the
substrate’s Keyword-prefix lead-byte disjointness contract binds
to.
Sibling posture to the closed set of pub const reader-punctuation
canonical char bytes on the substrate: Self::STR_DELIMITER
('"'), Self::STR_ESCAPE_LEAD ('\\'),
Self::BOOL_LITERAL_LEAD ('#'), Sexp::LIST_OPEN ('('),
Sexp::LIST_CLOSE (')'), Sexp::COMMENT_LEAD (';'),
Sexp::COMMENT_TERM ('\n'), QuoteForm::SPLICE_DISCRIMINATOR
('@') — every canonical per-role byte the reader’s tokenizer
specialises on is a pub const on its owning closed-set algebra.
This constant closes the Keyword-prefix lead byte at the SAME
algebra as its one-char Self::KEYWORD_MARKER &'static str
projection — the ONE char that the &'static str projects to
when the substrate’s consumer needs a char (not a &str) for
cross-axis marker-byte comparisons or for the reader’s
specific-arm outer-dispatch.
Structural round-trip contract:
Self::KEYWORD_MARKER.starts_with(Self::KEYWORD_MARKER_LEAD) —
pinned by
atom_keyword_marker_lead_prefixes_keyword_marker. A regression
that drifts EITHER the constant OR the Self::KEYWORD_MARKER
spelling surfaces at the pin rather than at a silent Keyword-
prefix reader drift where :foo classifies as Self::Symbol
instead of Self::Keyword. Sibling-shape peer of
Self::BOOL_LITERAL_LEAD’s
Self::bool_literal(b).starts_with(Self::BOOL_LITERAL_LEAD)
round-trip law: where that pin binds the Bool-family two spellings
to their shared lead byte, this pin binds the Keyword-marker
one-char prefix to its projected lead byte.
Disjointness contract: KEYWORD_MARKER_LEAD’s byte MUST differ
from Self::STR_DELIMITER ('"'), Self::STR_ESCAPE_LEAD
('\\'), Self::BOOL_LITERAL_LEAD ('#'),
Sexp::LIST_OPEN ('('), Sexp::LIST_CLOSE (')'),
Sexp::COMMENT_LEAD (';'), Sexp::COMMENT_TERM
('\n'), every QuoteForm::lead_char projection AND
QuoteForm::SPLICE_DISCRIMINATOR ('@') — every other
closed-set outer-marker byte the reader’s tokenizer specialises
on. A collision would silently break the reader’s outer
dispatch: a :-prefixed bare atom :foo would collide with
whichever marker it aliased. Pinned by
atom_keyword_marker_lead_distinct_from_every_other_algebra_marker.
Consumer sites this constant closes: SEVEN test-surface sites
that pre-lift each extracted the lead byte via
Atom::KEYWORD_MARKER.chars().next().expect(_) (or .unwrap()) —
the Keyword arm of the Sexp::is_bare_atom_boundary negative
sweep AND the six cross-axis disjointness pins on the sibling
marker-byte algebras
(QuoteForm::SPLICE_DISCRIMINATOR, Self::BOOL_LITERAL_LEAD,
Self::STR_DELIMITER, Self::STR_ESCAPE_LEAD,
Sexp::LIST_OPEN / Sexp::LIST_CLOSE,
Sexp::COMMENT_LEAD, Sexp::COMMENT_TERM) — collapse onto
ONE named byte on the substrate algebra.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Keyword-prefix lead byte, canonical ':') pairing binds at ONE
constant on the closed-set outer Atom algebra regardless of
which reader-surface consumer reaches in. THEORY.md §II.1
invariant 5 — composition preserves proofs; the
Self::KEYWORD_MARKER.starts_with(Self::KEYWORD_MARKER_LEAD)
round-trip law is a coherence proof BETWEEN the paired projection
(Self::KEYWORD_MARKER) AND its projected lead byte (this
constant) on ONE algebra — a regression that drifts either side
surfaces at the pin rather than as a silent Keyword-prefix reader
drift. THEORY.md §V.1 — knowable platform; the canonical
Keyword-prefix lead byte becomes a TYPE-level constant on the
substrate algebra rather than an inline
.chars().next().expect(_) extraction at every test surface AND
an inline ':' mention across every docstring pinning the
disjointness contract.
Frontier inspiration: Racket’s read-syntax colon-prefix reader
hook (#:name for keyword args, ::name for module-scoped bindings
in some dialects) — where the : prefix dispatches keyword-family
reader-macros keyed on ONE typed lead byte on the port’s reader
table. Translated to tatara-lisp: Atom::KEYWORD_MARKER_LEAD
becomes the ONE typed lead byte on the closed-set outer Atom
algebra that a future keyword-family peer method (e.g. a
Clojure-compat port to ::foo namespaced keywords) can extend
through — the paired-prefix discipline that Racket’s read-syntax
table carries lands here as a pub const on the algebra rather
than as an inline char literal at every consumer site.
Sourcepub const TRUE_LITERAL: &'static str = "#t"
pub const TRUE_LITERAL: &'static str = "#t"
Canonical &'static str Scheme-bool spelling for the true
element of the closed bool domain — the "#t" bytes
Self::bool_literal projects true to, AND the
Self::from_lexeme reader classifier gates on. Sibling
posture to Self::FALSE_LITERAL ("#f") on the same
bool-spelling algebra layer.
Pre-lift the same "#t" bytes lived inline at Self::bool_literal’s
true-arm plus at five test-surface sites that pin the
canonical spelling — the ≥2 PRIME-DIRECTIVE trigger. Post-lift
the (true, canonical Scheme spelling) pairing binds at ONE
pub const on the closed-set Atom algebra: the
Self::bool_literal true-arm AND every consumer that pins
the exact bytes route through this constant, so a spelling
migration (e.g. a Common-Lisp-compat port to "T", a JSON-compat
port to "true", a Racket-compat port to "#true") is ONE
edit HERE with the Self::bool_literal projection AND every
downstream reader/Display consumer mechanically picking it up.
Sibling posture to the closed set of per-role canonical
pub const bytes on the substrate’s other closed-set outer
algebras: Self::STR_DELIMITER ('"'),
Self::KEYWORD_MARKER (":"),
Sexp::LIST_OPEN ('('),
crate::error::MacroDefHead::DEFMACRO_KEYWORD ("defmacro"),
crate::macro_expand::MacroParams::REST_MARKER ("&rest"),
crate::macro_expand::MacroParams::OPTIONAL_MARKER ("&optional").
Structural round-trip contract (composed with the algebra’s
Self::BOOL_LITERAL_LEAD axis peer):
Self::TRUE_LITERAL.starts_with(Self::BOOL_LITERAL_LEAD) — the
lead-byte-prefixes-spelling law from
atom_bool_literal_lead_prefixes_every_bool_literal_spelling
specialises byte-for-byte to this constant, pinning the
(BOOL_LITERAL_LEAD, TRUE_LITERAL) pairing on ONE typed
algebra.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(true, canonical Scheme spelling) pairing binds at ONE
pub const on the closed-set outer Atom algebra regardless
of which of the reader/Display surface consumers reaches in.
THEORY.md §VI.1 — generation over composition; the multi-site
inline "#t" literal collapses onto ONE named pub const,
matching the substrate’s three-times rule. THEORY.md §V.1 —
knowable platform; the canonical true-spelling becomes a
TYPE-level constant on the substrate algebra rather than an
inline byte literal at every consumer surface.
Sourcepub const FALSE_LITERAL: &'static str = "#f"
pub const FALSE_LITERAL: &'static str = "#f"
Canonical &'static str Scheme-bool spelling for the false
element of the closed bool domain — the "#f" bytes
Self::bool_literal projects false to, AND the
Self::from_lexeme reader classifier gates on. Sibling
posture to Self::TRUE_LITERAL ("#t") on the same
bool-spelling algebra layer.
Pre-lift the same "#f" bytes lived inline at Self::bool_literal’s
false-arm plus at five test-surface sites that pin the
canonical spelling — the ≥2 PRIME-DIRECTIVE trigger. Post-lift
the (false, canonical Scheme spelling) pairing binds at ONE
pub const on the closed-set Atom algebra: the
Self::bool_literal false-arm AND every consumer that pins
the exact bytes route through this constant.
Structural round-trip contract (composed with the algebra’s
Self::BOOL_LITERAL_LEAD axis peer):
Self::FALSE_LITERAL.starts_with(Self::BOOL_LITERAL_LEAD).
Sourcepub const BOOL_LITERALS: [&'static str; 2]
pub const BOOL_LITERALS: [&'static str; 2]
The closed set of two canonical &'static str Scheme-bool
spellings — the Self::TRUE_LITERAL ("#t") canonical true
spelling followed by the Self::FALSE_LITERAL ("#f")
canonical false spelling. Canonical declaration order matches
the [true, false] sweep order every existing sibling test in
the crate uses (see e.g. the for b in [true, false] sweeps at
atom_bool_literal_lead_prefixes_every_bool_literal_spelling)
so Self::BOOL_LITERALS[i] == Self::bool_literal([true, false][i])
element-wise — pinned by
atom_bool_literals_align_with_bool_literal_by_index.
Sibling posture to
crate::error::MacroDefHead::KEYWORDS ([&'static str; 3]),
crate::macro_expand::MacroParams::LAMBDA_LIST_KEYWORDS
([&'static str; 2]) — every closed-set spelling algebra now
pins its projection ALL array at the declaration site via a
forced-arity [&'static str; N] array whose length fails
compilation if a new spelling lands without being added to the
set. The closed bool domain admits exactly two spellings by
construction (a hypothetical third would require extending the
underlying bool type itself), so this array’s N == 2 is
pinned by the mathematics — the forced-arity [_; 2] here
records that invariant on the substrate algebra so a future
tri-valued-logic extension surfaces at the array-arity check
rather than as a silent drift.
Future consumers that compose against Self::BOOL_LITERALS:
an LSP / REPL completion provider surfacing every #… partial
input against the closed set (Self::BOOL_LITERALS.iter() is
the ONE typed sweep over every legal bool spelling), a
tatara-check coverage assertion (every workspace .lisp file’s
bare-#-prefixed lexeme must classify to some entry of
Self::BOOL_LITERALS OR be routed through the broader
hash-prefix reader-macro family), any future audit-trail metric
jointly labeled by the canonical Scheme spelling (e.g.
tatara_lisp_bool_lexeme_total{literal="#t"}) — the metric
label set IS Self::BOOL_LITERALS.
Sourcepub const BOOL_LITERAL_LEAD: char = '#'
pub const BOOL_LITERAL_LEAD: char = '#'
Canonical # LEAD byte shared across both Self::bool_literal
spellings ("#t" for true, "#f" for false) — the ONE
canonical char on the Atom algebra the substrate’s Bool-
prefix disjointness contract binds to.
Sibling posture to the closed set of pub const reader-punctuation
canonical bytes on the substrate: Self::STR_DELIMITER ('"'),
Self::STR_ESCAPE_LEAD ('\\'), Sexp::LIST_OPEN ('('),
Sexp::LIST_CLOSE (')'), Sexp::COMMENT_LEAD (';'),
Sexp::COMMENT_TERM ('\n'), QuoteForm::SPLICE_DISCRIMINATOR
('@') — every canonical per-role byte the reader’s tokenizer
specialises on is now a pub const on its owning closed-set
algebra. This constant closes the Bool-family lead byte at the
SAME algebra as its two-char Self::bool_literal projections
so a delimiter swap (e.g. a Common-Lisp-compat port from
Scheme #t/#f to CL T/NIL, a JSON-compat port to
true/false, or an extension for the broader Scheme
hash-prefix reader-macro family #\char / #(vector) /
#|block-comment|# / #;datum-comment) lands at ONE constant
on the algebra rather than at inline '#' char literals
scattered across the test surface AND the docstrings pinning
the disjointness contract.
Structural round-trip contract:
Self::bool_literal(b).starts_with(Self::BOOL_LITERAL_LEAD)
for every b: bool — pinned by
atom_bool_literal_lead_prefixes_every_bool_literal_spelling.
A regression that drifts EITHER the constant OR the two
Self::bool_literal arms surfaces at the pin rather than at
a silent bool-family reader drift where #t / #f classify as
Self::Symbol instead of Self::Bool.
Disjointness contract: BOOL_LITERAL_LEAD’s byte MUST differ
from Self::STR_DELIMITER ('"'), Self::STR_ESCAPE_LEAD
('\\'), Self::KEYWORD_MARKER’s lead byte (':'),
Sexp::LIST_OPEN ('('), Sexp::LIST_CLOSE (')'),
Sexp::COMMENT_LEAD (';'), Sexp::COMMENT_TERM
('\n'), every QuoteForm::lead_char projection AND
QuoteForm::SPLICE_DISCRIMINATOR ('@') — every other
closed-set outer-marker byte the reader’s tokenizer specialises
on. A collision would silently break the reader’s outer
dispatch: a #-prefixed bare atom #t would collide with
whichever marker it aliased. Pinned by
atom_bool_literal_lead_distinct_from_every_other_algebra_marker.
Consumer sites this constant closes: the three test-surface
sites that pre-lift each extracted the lead byte via
Self::bool_literal(b).chars().next().unwrap() (or .expect(_))
— the Bool-arm of Sexp::is_bare_atom_boundary’s negative
sweep AND the two QuoteForm::SPLICE_DISCRIMINATOR
disjointness assertions — collapse onto ONE named byte on the
substrate algebra. The two SPLICE_DISCRIMINATOR assertions
(one per bool_literal spelling) further collapse to ONE
assertion since the lead byte is by construction the SAME
across both spellings.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Bool-family lead byte, canonical '#') pairing binds at ONE
constant on the closed-set outer Atom algebra regardless of
which reader-surface consumer reaches in. THEORY.md §II.1
invariant 5 — composition preserves proofs; the
[Self::bool_literal(b).starts_with(Self::BOOL_LITERAL_LEAD)]
round-trip law is a coherence proof BETWEEN the paired
projection (Self::bool_literal) AND the shared lead byte
(this constant) on ONE algebra — a regression that drifts
either side surfaces at the pin rather than as a silent
hash-prefix reader-family drift. THEORY.md §V.1 — knowable
platform; the canonical Bool-family lead byte becomes a
TYPE-level constant on the substrate algebra rather than an
inline .chars().next().unwrap() extraction at every test
surface AND an inline '#' mention across every docstring
pinning the disjointness contract.
Sourcepub const STR_DELIMITER: char = '"'
pub const STR_DELIMITER: char = '"'
Canonical " delimiter that opens AND closes a Self::Str
atom in the reader’s tokenizer AND self-escapes inside a
backslash-escape sequence — ONE canonical char on the
Atom algebra the substrate’s FOUR "-round-trip inline
char literals at [crate::reader::tokenize] bind to.
Sibling constant of Self::KEYWORD_MARKER on the atomic-
payload marker/delimiter axis of the closed-set Atom
algebra: where KEYWORD_MARKER is the ONE &'static str
prefix a Self::Keyword payload composes WITH at four
canonical-form round-trip sites (reader-entry classifier,
Lisp-canonical Display, JSON canonical form, iac-forge
canonical form) and Self::bool_literal is the ONE
projection a Self::Bool payload composes THROUGH at its
two Bool-round-trip sites, this constant is the ONE canonical
delimiter a Self::Str payload pairs with at the reader’s
four "-round-trip sites inside [crate::reader::tokenize]:
- The outer-match string-opening arm — the
"byte that begins a [crate::reader::Token::Str] tokenization run. - The escape-handler mapping —
\"unescapes to a bare"character inside the accumulated string payload (the only self-escape arm on the reader’s five-arm escape table:\n → \n,\t → \t,\r → \r,\" → ",\\ → \\). - The string-closing arm — the
"byte that terminates the current [crate::reader::Token::Str] tokenization run and emits the accumulated payload. - The bare-atom tokenizer’s break-disjunct —
"is one of the seven characters that terminates a [crate::reader::Token::Atom] run so a bare atom followed by a string (e.g.foo"body") tokenizes as two distinct tokens rather than one Symbol payload.
Pre-lift the same " byte lived inline at four char
literals scattered across crate::reader::tokenize: two outer-
match arm patterns (opening + escape-handler), one inner-loop
termination pattern (closing), one bare-atom termination
disjunct. Post-lift the (Str payload, canonical " delimiter)
pairing binds at ONE char constant on the Atom algebra
that every reader consumer routes through; a refactor that
swaps the delimiter (e.g. a Racket-compat port to #"…"#
heredoc mode, a Python-compat port that also accepts ',
a triple-quoted heredoc mode) touches ONE constant + one
reader table rather than four inline byte literals that would
silently drift out of round-trip agreement if one was updated
without the others (e.g. an opening # without a matching
closing # would round-trip a broken string with a
silently-truncated payload).
Load-bearing round-trip contract:
read(&format!("{}{s}{}", Atom::STR_DELIMITER, Atom::STR_DELIMITER))[0] == Sexp::Atom(Atom::string(s)) for
every escape-free s: &str. The reader’s opening + closing
arms both bind to THIS constant so the delimiter cannot drift
silently between opener and closer; a regression that swaps
ONE arm’s pattern to a different byte fails the round-trip
even when the byte-value at the other arm still agrees at
the surface. Guards the CLAUDE.md-implicit convention that
operator-visible strings use ONE canonical delimiter across
the reader entry surface — the delimiter is a first-class
algebra fact rather than a per-callsite reader convention.
Sibling-shape peer of Self::KEYWORD_MARKER on the closed-
set Atom algebra: where KEYWORD_MARKER (":") partitions
bare-atom lexemes at reader-entry classifier strip_prefix
gate into Keyword vs default Symbol, this constant ('"')
partitions the reader’s outer tokenizer arm into
Token::Str vs Token::Atom — both are the ONE canonical
marker byte the reader binds to when discriminating an
Atom variant’s typed-entry classification path. A Str
payload takes the Token::Str reader branch (with THIS
delimiter) NOT the Token::Atom reader branch (routed
through Self::from_lexeme) — the two paths remain
structurally disjoint through the reader’s delimiter
dispatch.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Str payload, canonical " delimiter) pairing now binds at
ONE constant on the closed-set Atom algebra regardless of
which of the four reader tokenizer sites reaches in.
THEORY.md §VI.1 — generation over composition; four byte-
identical inline '"' char literals in crate::reader::tokenize
collapse onto ONE named constant. Four occurrences, well past
the ≥2 lift threshold — the substrate’s three-times rule at
the Str-delimiter axis. THEORY.md §V.1 — knowable platform;
the canonical string-delimiter byte becomes a TYPE-level
constant on the substrate algebra rather than four inline
bytes at four consumer sites inside one reader file.
Sourcepub const STR_ESCAPE_LEAD: char = '\\'
pub const STR_ESCAPE_LEAD: char = '\\'
Canonical \ escape-lead byte that OPENS a backslash-escape
sequence inside a Self::Str payload AND self-escapes to the
same byte inside that sequence — ONE canonical char on the
Atom algebra the substrate’s TWO \-round-trip inline
char literals at [crate::reader::tokenize] bind to.
Sibling constant of Self::STR_DELIMITER on the same
Str-payload delimiter axis of the closed-set Atom algebra:
where STR_DELIMITER is the ONE canonical delimiter that
BOUNDS a Str payload from the outside (opener, closer, self-
escape, bare-atom terminator), this constant is the ONE
canonical escape lead that ESCAPES-IN a following byte from
the INSIDE of the same payload. The two constants together
span the Str-tokenization boundary — every char the reader’s
Token::Str accumulation loop specialises on binds to one of
them.
The reader’s TWO \-round-trip sites inside
[crate::reader::tokenize]:
- The escape-lead outer arm — the
\byte that triggers the inner escape-handler branch that consumes the following byte as an escape sequence. - The escape-handler’s self-escape arm — inside the reader’s
six-arm escape table (
\n → \n,\t → \t,\r → \r,\" → ",\\ → \, passthrough), the self-escape arm on the escape-lead axis: pattern AND mapped value both bind to THIS constant so\\unescapes to a single\byte in the accumulated payload. Sibling posture to the analogous self-escape arm onSelf::STR_DELIMITERaxis (\"unescapes to") — the two self-escape arms are the escape table’s ONLY pattern-equals-value arms; every other arm is pattern-distinct-from-value.
Pre-lift the same \ byte lived inline at two char literals
scattered across crate::reader::tokenize: one outer-arm
pattern (escape-lead detection), one inner-loop escape-handler
arm’s pattern + value pair (the self-escape mapping). Post-
lift the (Str-payload escape lead, canonical \ byte) pairing
binds at ONE char constant on the Atom algebra that every
reader consumer routes through; a refactor that swaps the
escape lead (e.g. a Rust-compat port to \\ byte-strings, a
hypothetical Racket-compat port that adopts #\ prefix syntax
as the escape lead, or a heredoc mode that suspends escaping
altogether) touches ONE constant + one reader table rather
than two inline byte literals that would silently drift out of
round-trip agreement if one was updated without the other
(e.g. the outer arm’s pattern updated without the inner self-
escape’s pattern + value would leak a stale escape lead through
the wrong branch).
Load-bearing round-trip contract:
read(&format!("{}{}{}{}", Atom::STR_DELIMITER, Atom::STR_ESCAPE_LEAD, Atom::STR_ESCAPE_LEAD, Atom::STR_DELIMITER))[0] == Sexp::Atom(Atom::string(Atom::STR_ESCAPE_LEAD.to_string())).
The \\ inside a STR_DELIMITER-wrapped payload unescapes to
ONE \ byte on the accumulated payload — pinning the (self-
escape pattern, self-escape mapped value) pair against
re-inlining. A regression that swaps ONE side of the self-
escape arm to a different byte fails this round-trip even when
the pattern OR value at the other side still agrees at the
surface, because both sides bind to THIS constant.
Sibling-shape peer of Self::STR_DELIMITER on the closed-set
Atom algebra: where STR_DELIMITER partitions the outer-
tokenizer arm into Token::Str vs Token::Atom, this constant
partitions the inner-tokenizer arm (inside Token::Str
accumulation) into the escape-handler branch vs the passthrough
Some((_, ch)) branch — both are the ONE canonical marker
byte the reader binds to when discriminating the Str-payload
accumulation loop’s branch dispatch.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Str-payload escape lead, canonical \ byte) pairing now binds
at ONE constant on the closed-set Atom algebra regardless
of which of the two reader tokenizer sites reaches in.
THEORY.md §VI.1 — generation over composition; two byte-
identical inline '\\' char literals in crate::reader::tokenize
collapse onto ONE named constant. Two occurrences at the ≥2
lift threshold — the substrate’s three-times rule at the
Str-escape-lead axis. THEORY.md §V.1 — knowable platform; the
canonical string-escape-lead byte becomes a TYPE-level constant
on the substrate algebra rather than two inline bytes at two
consumer sites inside one reader file.
Sourcepub const NEWLINE_ESCAPE_SOURCE: char = 'n'
pub const NEWLINE_ESCAPE_SOURCE: char = 'n'
Canonical Str-payload escape-table projection — total closed-set
decode from the ONE post-escape-lead source byte the reader’s
escape-handler branch consumes to the ONE decoded byte pushed
onto the accumulated Str payload. ONE typed projection on the
closed-set Atom algebra that the substrate’s Str-escape
decode boundary binds to; every consumer that reaches inside a
Self::Str payload for a backslash-escape sequence routes
through THIS method rather than a per-site inline
match esc { … } table.
Closes the Str-payload tokenization boundary that
Self::STR_DELIMITER + Self::STR_ESCAPE_LEAD began: where
those two constants pin the ONE canonical delimiter byte AND
the ONE canonical escape-lead byte the reader’s outer + inner
branch dispatch specialises on, this method pins the ONE
canonical decode table the escape-handler’s inner branch
consumes AFTER the escape-lead byte fires. The three constants
together span the Str-payload tokenization axis — every byte
the reader’s Token::Str accumulation loop reads either
terminates the payload (STR_DELIMITER), triggers the escape
handler (STR_ESCAPE_LEAD), pushes through unchanged
(passthrough), OR is fed into THIS method as the escape source
byte AND its result pushed onto the payload.
Total function: EVERY char maps to exactly one decoded
char. The five typed arms bind the substrate’s canonical
escape shorthand:
| esc source | decoded byte |
|---|---|
'n' | '\n' |
't' | '\t' |
'r' | '\r' |
Self::STR_DELIMITER | Self::STR_DELIMITER |
Self::STR_ESCAPE_LEAD | Self::STR_ESCAPE_LEAD |
any other char | itself (passthrough) |
The two self-escape arms (STR_DELIMITER → STR_DELIMITER,
STR_ESCAPE_LEAD → STR_ESCAPE_LEAD) are the ONLY
pattern-equals-value arms in the table; both bind through the
closed-set Atom algebra constants so a delimiter-swap or
escape-lead-swap on the algebra propagates through pattern AND
value at ONE site rather than as scattered inline byte literals
that would silently drift out of round-trip agreement if one
was updated without the other.
The three named-escape arms ('n' / 't' / 'r') are the
substrate’s canonical whitespace shorthand — pattern-distinct-
from-value on every arm (each maps a printable ASCII letter to
its corresponding C0 control byte). Pre-lift the whole table
lived inline at ONE site inside [crate::reader::tokenize]’s
escape-handler branch; post-lift the table lives at ONE typed
projection on the Atom algebra that the reader consumes
through a single Self::decode_str_escape(esc) call. Adding a
sixth named-escape arm (e.g. '0' → '\0' for the NUL byte, or
an 'x' hex-byte-prefix arm) extends THIS method’s match
rather than mutating the reader’s inline block.
Load-bearing round-trip contract: for every esc: char,
read(&format!("{}{}{}{}", Atom::STR_DELIMITER, Atom::STR_ESCAPE_LEAD, esc, Atom::STR_DELIMITER))[0] == Sexp::Atom(Atom::string(Atom::decode_str_escape(esc) .to_string())). Every escape-source byte inside a
STR_DELIMITER-wrapped payload decodes through THIS projection
end-to-end — pinning the (reader escape-handler branch, this
method) pairing against a silent drift at either side. A
regression that re-inlines the reader’s table would break the
pin the moment a new arm lands here but NOT there (or vice
versa).
Sibling-shape peer of QuoteForm::from_lead_char on the
closed-set QuoteForm algebra: where from_lead_char is
the ONE typed dispatch on the outer-tokenizer quote-family
axis (four homoiconic prefix chars decode to Option<Self>),
this method is the ONE typed dispatch on the inner-tokenizer
Str-escape axis (every escape-source char decodes to a
resolved char). Both are the substrate’s canonical
closed-set projections the reader consumes through ONE call
site each; both close the reader’s per-char specialization
point onto the algebra.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Str-escape source byte, decoded byte) pairing now binds at
ONE typed projection on the closed-set Atom algebra
regardless of which consumer reaches in. THEORY.md §VI.1 —
generation over composition; the reader’s five-arm inline
table plus one passthrough arm at [crate::reader::tokenize]
collapses onto ONE named projection. THEORY.md §V.1 — knowable
platform; the canonical Str-escape decode table becomes a
TYPE-level method on the substrate algebra rather than an
inline block inside one reader file — a future decoder
(e.g. a Racket-compat port, a heredoc mode, a raw-string
mode) plugs a peer projection onto the same algebra rather
than forking the reader.
Canonical newline-escape SOURCE char — the ONE 'n' byte
Self::decode_str_escape’s newline arm pattern-matches on.
Sibling constant of Self::NEWLINE_ESCAPE_DECODED on the
same (source, decoded) escape-arm axis: the ('n', '\n')
pairing is the first of the substrate’s three named-escape
arms; each pairing binds at ONE pub const per role rather
than at an inline char literal at the match-arm pattern +
value. Sibling posture to Self::STR_DELIMITER /
Self::STR_ESCAPE_LEAD one axis over on the same
Str-payload tokenization boundary — those two constants pin
the ONE canonical delimiter byte AND the ONE canonical
escape-lead byte the reader’s outer + inner branch dispatch
specialises on; this constant + its five per-role peers
close the (named-escape, self-escape) decode-arm surface at
the same closed-set Atom algebra.
Sourcepub const NEWLINE_ESCAPE_DECODED: char = '\n'
pub const NEWLINE_ESCAPE_DECODED: char = '\n'
Canonical newline-escape DECODED byte — the ONE '\n' C0
control byte Self::decode_str_escape’s newline arm
value-emits when the source char is Self::NEWLINE_ESCAPE_SOURCE.
Peer of the SOURCE constant on the SAME (source, decoded)
escape-arm axis; the pairing (NEWLINE_ESCAPE_SOURCE,
NEWLINE_ESCAPE_DECODED) IS the first row of the
Self::NAMED_ESCAPE_TABLE two-column algebra.
Sourcepub const TAB_ESCAPE_SOURCE: char = 't'
pub const TAB_ESCAPE_SOURCE: char = 't'
Canonical tab-escape SOURCE char — the ONE 't' byte
Self::decode_str_escape’s tab arm pattern-matches on.
Second of the three named-escape SOURCE pub const peers.
Sourcepub const TAB_ESCAPE_DECODED: char = '\t'
pub const TAB_ESCAPE_DECODED: char = '\t'
Canonical tab-escape DECODED byte — the ONE '\t' C0
control byte Self::decode_str_escape’s tab arm
value-emits when the source char is Self::TAB_ESCAPE_SOURCE.
Peer of the SOURCE constant on the SAME (source, decoded)
escape-arm axis; the pairing (TAB_ESCAPE_SOURCE,
TAB_ESCAPE_DECODED) IS the second row of the
Self::NAMED_ESCAPE_TABLE two-column algebra.
Sourcepub const CARRIAGE_RETURN_ESCAPE_SOURCE: char = 'r'
pub const CARRIAGE_RETURN_ESCAPE_SOURCE: char = 'r'
Canonical carriage-return-escape SOURCE char — the ONE 'r'
byte Self::decode_str_escape’s carriage-return arm
pattern-matches on. Third of the three named-escape SOURCE
pub const peers.
Sourcepub const CARRIAGE_RETURN_ESCAPE_DECODED: char = '\r'
pub const CARRIAGE_RETURN_ESCAPE_DECODED: char = '\r'
Canonical carriage-return-escape DECODED byte — the ONE
'\r' C0 control byte Self::decode_str_escape’s
carriage-return arm value-emits when the source char is
Self::CARRIAGE_RETURN_ESCAPE_SOURCE. Peer of the SOURCE
constant on the SAME (source, decoded) escape-arm axis; the
pairing (CARRIAGE_RETURN_ESCAPE_SOURCE,
CARRIAGE_RETURN_ESCAPE_DECODED) IS the third row of the
Self::NAMED_ESCAPE_TABLE two-column algebra.
Sourcepub const NAMED_ESCAPE_TABLE: [(char, char); 3]
pub const NAMED_ESCAPE_TABLE: [(char, char); 3]
Canonical NAMED-escape table — the closed-set ALL array over
the substrate’s three (SOURCE, DECODED) pairings the
pattern-distinct-from-value named-escape arms of
Self::decode_str_escape emit, in canonical declaration
order (newline / tab / carriage-return). Forced-arity
[(char, char); 3] composition — a hypothetical fourth
named-escape arm (e.g. '0' → '\0' for the NUL byte, 'e' → '\x1b' for ESC, or a Racket-compat 'a' → '\x07' for
BEL) extends Self::decode_str_escape’s match ONCE +
this array ONCE + TWO new per-role pub consts (one on
each axis) in lockstep; rustc’s forced-arity check on
[(char, char); N] binds the extension through the array
declaration site. Sibling posture to
QuoteForm::PREFIXES / QuoteForm::IAC_FORGE_TAGS on
the outer-tokenizer quote-family axis — where those ALL
arrays close the reader-prefix + canonical-form byte
vocabularies on the QuoteForm closed set, this array
closes the named-escape (source, decoded) pairing vocabulary
on the inner-tokenizer Str-escape axis of the Atom
closed set.
The [(char, char); 3] shape (rather than a [char; 3]
singleton) is load-bearing: the escape-arm is a projection
from a source byte to a decoded byte, both bytes are typed
data, and pinning them as a PAIR at the array declaration
site closes the drift channel between pattern (source) and
value (decoded) that a scalar array would leave open.
Excludes the two pattern-equals-value self-escape arms
(Self::STR_DELIMITER → Self::STR_DELIMITER,
Self::STR_ESCAPE_LEAD → Self::STR_ESCAPE_LEAD) because
those bind through Self::STR_DELIMITER +
Self::STR_ESCAPE_LEAD one axis over on the Str-payload
delimiter axis — the (source, decoded) pairing there is
definitionally the identity by algebra design (a delimiter-
swap propagates through pattern AND value at ONE constant
per axis). This array closes the OTHER three arms — the
pattern-DISTINCT-from-value named-escape rows.
Sourcepub const SELF_ESCAPE_TABLE: [char; 2]
pub const SELF_ESCAPE_TABLE: [char; 2]
Canonical SELF-escape table — the closed-set ALL array over the
substrate’s TWO pattern-EQUALS-value arms of
Self::decode_str_escape, in canonical declaration order
(Self::STR_DELIMITER, Self::STR_ESCAPE_LEAD) matching
the projection’s match-arm order. Forced-arity [char; 2]
composition — a hypothetical third self-escape byte (e.g. a
raw-string mode adopting '#' as an additional self-escaping
delimiter, or a Racket-compat '|' verbatim-symbol boundary)
extends Self::decode_str_escape’s match ONCE + this array
ONCE + ONE new pub const on the closed-set Atom algebra
in lockstep; rustc’s forced-arity check on [char; N] binds
the extension through the array declaration site.
Peer to Self::NAMED_ESCAPE_TABLE on the SAME Str-payload
tokenization boundary: where NAMED_ESCAPE_TABLE closes the
THREE pattern-DISTINCT-from-value named-escape rows ('n' → '\n', 't' → '\t', 'r' → '\r'), this array closes the TWO
pattern-EQUALS-value self-escape rows (STR_DELIMITER → STR_DELIMITER, STR_ESCAPE_LEAD → STR_ESCAPE_LEAD). The two
arrays together span the FIVE non-passthrough arms of
Self::decode_str_escape — every byte the reader’s
Token::Str escape-handler branch specialises on lives in
exactly ONE of the two closed-set arrays OR falls through the
other => other passthrough at the algebra’s projection.
The [char; 2] shape (rather than a [(char, char); 2]
pairing peer to NAMED_ESCAPE_TABLE) is load-bearing: the
self-escape arm is definitionally the identity by algebra
design (a delimiter-swap propagates through pattern AND value
at ONE constant per axis), so the PAIRING collapses to a
SCALAR on this sub-vocabulary. The SHAPE ASYMMETRY between the
two peer arrays ([(char, char); N] vs [char; N]) IS the
structural axis distinguishing the pattern-DISTINCT-from-value
vocabulary from the pattern-EQUALS-value vocabulary — a
consumer that reaches for the array shape encodes its
vocabulary’s identity relation in the SHAPE it iterates.
Sibling posture to QuoteForm::PREFIXES +
QuoteForm::IAC_FORGE_TAGS on the outer-tokenizer
quote-family axis — where those ALL arrays close the TWO
byte-vocabulary axes (reader prefix + iac-forge tag) of the
outer-tokenizer QuoteForm closed set with parallel shapes,
NAMED_ESCAPE_TABLE + SELF_ESCAPE_TABLE close the TWO
sub-vocabularies (pattern-distinct + pattern-equals) of the
inner-tokenizer Atom Str-escape closed set with asymmetric
shapes reflecting the identity-relation asymmetry.
Sourcepub const ESCAPE_SOURCES: [char; 5]
pub const ESCAPE_SOURCES: [char; 5]
Canonical closed-set ALL array over every escape-SOURCE byte
Self::decode_str_escape has a non-passthrough arm for — the
SPAN of the two peer sub-vocabulary source columns
(Self::NAMED_ESCAPE_TABLE’s three (SOURCE, DECODED) pairs’
SOURCE column + Self::SELF_ESCAPE_TABLE’s two rows) in
canonical declaration order matching decode_str_escape’s
match-arm order. Forced-arity [char; 5] composition — a
hypothetical sixth non-passthrough arm (e.g. a '0' → '\0'
NUL-byte extension, a Racket-compat 'a' → '\x07' BEL, a raw-
string mode adopting '#' as an additional self-escaping
delimiter) extends Self::decode_str_escape’s match ONCE +
EITHER NAMED_ESCAPE_TABLE or SELF_ESCAPE_TABLE ONCE + this
ALL array ONCE + one new per-role pub const (or pair) in
lockstep; rustc’s forced-arity check on [char; N] binds the
extension through the array declaration site.
Cross-sub-vocabulary SPAN peer to Self::NAMED_ESCAPE_TABLE +
Self::SELF_ESCAPE_TABLE at the ALL-array level: where those
two arrays partition the FIVE non-passthrough arms of
Self::decode_str_escape into the pattern-DISTINCT-from-value
sub-vocabulary (3 rows, [(char, char); 3]) AND the pattern-
EQUALS-value sub-vocabulary (2 rows, [char; 2]), this array
closes the UNION of the SOURCE columns at ONE typed
[char; 5] on the SAME closed-set Atom algebra. Pre-lift
the SPAN identity lived at TWO sites: the runtime iterator
chain
Atom::NAMED_ESCAPE_TABLE.iter().map(|&(src, _)| src) .chain(Atom::SELF_ESCAPE_TABLE.iter().copied()).collect() at
atom_decode_str_escape_composes_end_to_end_through_reader_for_every_named_arm
AND the prose docstring for Self::SELF_ESCAPE_TABLE naming
“the FIVE non-passthrough arms” as a cardinality identity. Post-
lift the SPAN binds at ONE forced-arity [char; 5] on the
Atom algebra so consumers that want “every char for which
decode_str_escape is not the identity passthrough” iterate the
typed array rather than reassembling the two peer arrays at
each callsite.
Also sibling-shape to Sexp::LIST_DELIMITERS ([char; 2] on
the outer-structural paired-delimiter axis of the closed-set
Sexp algebra), Sexp::COMMENT_DELIMITERS ([char; 2] on
the reader-discard paired-delimiter axis of the SAME Sexp
algebra), Atom::BOOL_LITERALS ([&'static str; 2] on the
Scheme-bool spelling axis of this same Atom algebra), and
QuoteForm::LEADS ([char; 3] on the DISTINCT-lead-byte
sub-vocabulary axis of the closed-set QuoteForm algebra) —
every closed-set outer projection on the substrate that carries
a scalar [char; N] sub-vocabulary now pins its canonical
bytes at ONE pub const per role plus a forced-arity ALL array
for family-wide consumers.
Composition law (SPAN): ESCAPE_SOURCES == [NAMED_ESCAPE_TABLE[0].0, NAMED_ESCAPE_TABLE[1].0, NAMED_ESCAPE_TABLE[2].0, SELF_ESCAPE_TABLE[0], SELF_ESCAPE_TABLE[1]] AND ESCAPE_SOURCES.len() == NAMED_ESCAPE_TABLE.len() + SELF_ESCAPE_TABLE.len() — the
forced-arity + canonical declaration order together pin every
downstream index-sweep consumer to the (named-source-column
prefix, self-source-column suffix) partition at rustc time; a
reorder that broke the partition (e.g. interleaving the two
sub-vocabularies’ rows) fails at the composition pin below.
Path-uniformity contract carried at the row level: for every
esc in ESCAPE_SOURCES, Self::decode_str_escape(esc) != esc
iff esc is a NAMED_ESCAPE_TABLE SOURCE row (the three
pattern-DISTINCT-from-value arms), and Self::decode_str_escape (esc) == esc iff esc is a SELF_ESCAPE_TABLE row (the two
pattern-EQUALS-value arms). The union is exhaustive over the
FIVE non-passthrough arms so no ESCAPE_SOURCES row projects
through the other => other passthrough branch — the arm-set
closure is pinned structurally at
atom_escape_sources_every_row_projects_through_a_non_passthrough_arm_of_decode_str_escape.
Pairwise disjointness: every row is distinct from every other
row — the closed-set SPAN inherits pairwise disjointness from
the two peer sub-vocabularies (each already pairwise distinct
via atom_named_escape_table_sources_pairwise_distinct +
atom_self_escape_table_pairwise_distinct) PLUS the cross-
sub-vocabulary disjointness pinned by
atom_self_escape_table_disjoint_from_named_escape_table. This
ALL array’s own atom_escape_sources_pairwise_distinct test
closes the disjointness contract at the SPAN-level so a future
refactor that added a sixth arm whose SOURCE aliased an
existing arm surfaces HERE rather than at a distant reader
round-trip.
Future consumers that compose against Self::ESCAPE_SOURCES:
- LSP / REPL completion for the escape-source vocabulary — the
completion set IS
Self::ESCAPE_SOURCESrather than a per-consumer chain over the two peer arrays. tatara-checkcoverage assertions that a.lispcorpus exercises every non-passthrough escape arm — the sweep ISSelf::ESCAPE_SOURCES.iter()rather than a runtime chain overNAMED_ESCAPE_TABLE.iter().map(|&(src, _)| src) .chain(SELF_ESCAPE_TABLE.iter().copied()).- Any future syntax-highlighter that colors escape sequences —
the classifier binds through
Self::ESCAPE_SOURCESrather than through two parallel per-sub-vocabulary lookups. - Any future fuzz-input generator that biases toward escape
sequences — the source-byte pool IS
Self::ESCAPE_SOURCESrather than reassembled from the two peer arrays.
Theory anchor: THEORY.md §III — the typescape; the FIVE non-
passthrough source bytes now bind at ONE typed [char; 5] on
the closed-set Atom algebra rather than at a runtime chain
over the two peer sub-vocabulary arrays reassembled per
consumer. THEORY.md §V.1 — knowable platform; the non-
passthrough source-byte SPAN becomes load-bearing typed data at
the algebra level. THEORY.md §VI.1 — generation over
composition; the “FIVE non-passthrough arms” identity that
lived as prose in the Self::SELF_ESCAPE_TABLE docstring AND
as a runtime iterator chain at one test site regenerates
identically through this ONE typed forced-arity array.
Sourcepub const ESCAPE_DECODED: [char; 5]
pub const ESCAPE_DECODED: [char; 5]
Canonical closed-set ALL array over every DECODED byte
Self::decode_str_escape can emit from a non-passthrough arm
— the SPAN of the two peer sub-vocabulary DECODED columns
(Self::NAMED_ESCAPE_TABLE’s three (SOURCE, DECODED) pairs’
DECODED column + Self::SELF_ESCAPE_TABLE’s two rows, which
are pattern-EQUALS-value so their DECODED column is definitionally
the row byte itself) in canonical declaration order matching
decode_str_escape’s match-arm order. Forced-arity [char; 5]
composition — a hypothetical sixth non-passthrough arm (e.g. a
'0' → '\0' NUL-byte extension, a Racket-compat 'a' → '\x07'
BEL, a raw-string mode adopting '#' as an additional
self-escaping delimiter) extends Self::decode_str_escape’s
match ONCE + EITHER NAMED_ESCAPE_TABLE or SELF_ESCAPE_TABLE
ONCE + this ALL array ONCE + Self::ESCAPE_SOURCES ONCE + one
new per-role pub const (or pair) in lockstep; rustc’s forced-
arity check on [char; N] binds the extension through the array
declaration site.
Column-dual peer to Self::ESCAPE_SOURCES on the SAME closed-set
Atom algebra: where ESCAPE_SOURCES closes the SOURCE column
of the FIVE non-passthrough arms at ONE typed [char; 5], this
array closes the DECODED column at the SAME shape. Together the
two arrays close the (SOURCE, DECODED) cross-product of
decode_str_escape’s non-passthrough arm-set at two byte-
identical [char; 5] shapes on the SAME closed-set Atom
algebra — the shape symmetry across the two columns of the SAME
arm-set is itself a typed load-bearing invariant carrying the
column-dual identity relation on the algebra.
Cross-sub-vocabulary SPAN peer to Self::NAMED_ESCAPE_TABLE +
Self::SELF_ESCAPE_TABLE at the ALL-array level: where those
two arrays partition the FIVE non-passthrough arms of
Self::decode_str_escape into the pattern-DISTINCT-from-value
sub-vocabulary (3 rows, [(char, char); 3]) AND the pattern-
EQUALS-value sub-vocabulary (2 rows, [char; 2]), this array
closes the UNION of the DECODED columns at ONE typed
[char; 5] on the SAME closed-set Atom algebra. Pre-lift
the DECODED SPAN identity lived at ZERO callsites — the substrate
had a typed SOURCE-column SPAN (Self::ESCAPE_SOURCES) but the
DECODED-column SPAN was only reachable by iterating the two peer
sub-vocabulary arrays’ DECODED columns per consumer OR by mapping
ESCAPE_SOURCES through Self::decode_str_escape at runtime;
post-lift the DECODED SPAN binds at ONE forced-arity [char; 5]
on the Atom algebra so consumers that want “every byte
decode_str_escape can emit from a typed non-passthrough arm”
iterate the typed array rather than reassembling it per callsite.
Also sibling-shape to Sexp::LIST_DELIMITERS ([char; 2] on
the outer-structural paired-delimiter axis of the closed-set
Sexp algebra), Sexp::COMMENT_DELIMITERS ([char; 2] on
the reader-discard paired-delimiter axis of the SAME Sexp
algebra), Atom::BOOL_LITERALS ([&'static str; 2] on the
Scheme-bool spelling axis of this same Atom algebra), and
QuoteForm::LEADS ([char; 3] on the DISTINCT-lead-byte
sub-vocabulary axis of the closed-set QuoteForm algebra) —
every closed-set outer projection on the substrate that carries
a scalar [char; N] sub-vocabulary now pins its canonical bytes
at ONE pub const per role plus a forced-arity ALL array for
family-wide consumers.
Composition law (SPAN): ESCAPE_DECODED == [NAMED_ESCAPE_TABLE[0].1, NAMED_ESCAPE_TABLE[1].1, NAMED_ESCAPE_TABLE[2].1, SELF_ESCAPE_TABLE[0], SELF_ESCAPE_TABLE[1]] AND ESCAPE_DECODED.len() == NAMED_ESCAPE_TABLE.len() + SELF_ESCAPE_TABLE.len() — the
forced-arity + canonical declaration order together pin every
downstream index-sweep consumer to the (named-decoded-column
prefix, self-decoded-column suffix) partition at rustc time; a
reorder that broke the partition (e.g. interleaving the two
sub-vocabularies’ rows) fails at the composition pin below.
Column-dual pointwise projection law: for every index i in
0..5, ESCAPE_DECODED[i] == Self::decode_str_escape( ESCAPE_SOURCES[i]). The two forced-arity [char; 5] arrays are
the SOURCE column and DECODED column of decode_str_escape’s
non-passthrough arm-set — the pointwise projection identity is
pinned structurally at
atom_escape_decoded_projects_pointwise_from_escape_sources_through_decode_str_escape.
Pairwise disjointness: every row is distinct from every other
row — '\n', '\t', '\r', '"', '\\' are five distinct
C0-and-ASCII bytes. Distinctness on the DECODED column follows
from (a) the NAMED sub-vocabulary’s DECODED-column pairwise
distinctness at atom_named_escape_table_decoded_pairwise_distinct,
(b) the SELF sub-vocabulary’s pairwise distinctness at
atom_self_escape_table_pairwise_distinct, and (c) the fact
that no NAMED-DECODED byte ('\n', '\t', '\r' — all C0
control bytes) can alias a SELF byte ('"', '\\' — printable
ASCII bytes). The pairwise-distinctness pin below closes the
contract at the SPAN level so a future refactor that added a
sixth arm whose DECODED aliased an existing row surfaces HERE
rather than at a distant reader round-trip.
Future consumers that compose against Self::ESCAPE_DECODED:
- A Str-render / encoder consumer that needs to escape a DECODED
byte back into its SOURCE form — the classifier IS “is this
byte in
Self::ESCAPE_DECODED?” rather than a per-consumer chain over the two peer sub-vocabularies’ DECODED columns. - LSP / REPL diagnostic rendering that needs to name every byte
the substrate’s escape-handler can emit — the completion set
IS
Self::ESCAPE_DECODEDrather than a runtime map throughdecode_str_escapefromESCAPE_SOURCES. - A syntax-highlighter that colors decoded-escape byte payloads —
the classifier binds through
Self::ESCAPE_DECODEDrather than through two parallel per-sub-vocabulary lookups. - A fuzz-input generator that biases toward decoded-escape byte
payloads (probing the reader’s error-recovery path when a
raw C0 byte appears inside a Str payload) — the target-byte
pool IS
Self::ESCAPE_DECODEDrather than reassembled from the two peer arrays.
Theory anchor: THEORY.md §III — the typescape; the FIVE
DECODED bytes of the non-passthrough arm-set now bind at ONE
typed [char; 5] on the closed-set Atom algebra rather than
at a runtime map through decode_str_escape from
ESCAPE_SOURCES. THEORY.md §V.1 — knowable platform; the
non-passthrough DECODED-byte SPAN becomes load-bearing typed
data at the algebra level. THEORY.md §VI.1 — generation over
composition; the column-dual identity that lived only as a
runtime decode_str_escape projection of the peer SOURCE-column
SPAN regenerates identically through this ONE typed forced-arity
array.
Sourcepub const ESCAPE_TABLE: [(char, char); 5]
pub const ESCAPE_TABLE: [(char, char); 5]
Canonical closed-set ALL array over every (SOURCE, DECODED)
pair Self::decode_str_escape projects at a non-passthrough
arm — the paired-column SPAN closing BOTH columns of the FIVE
non-passthrough arms at ONE typed forced-arity
[(char, char); 5] on the closed-set Atom algebra.
Composes as NAMED_ESCAPE_TABLE’s three pattern-DISTINCT-from-
value rows (which are already (char, char) shape) followed by
SELF_ESCAPE_TABLE’s two pattern-EQUALS-value rows re-shaped as
(row, row) pairs (the definitional-identity closure of the
self-escape sub-vocabulary at the paired shape), in canonical
declaration order matching decode_str_escape’s match-arm order.
Cross-column peer-collapse of Self::ESCAPE_SOURCES +
Self::ESCAPE_DECODED — the two byte-identical [char; 5]
column-dual arrays close the SOURCE column and DECODED column
SEPARATELY at ONE forced-arity [char; 5] each; this array
closes BOTH columns TOGETHER at ONE typed forced-arity
[(char, char); 5] on the SAME closed-set Atom algebra.
Together the three arrays close the FIVE non-passthrough arms
at THREE typed forced-arity shapes: [char; 5] on the SOURCE
column, [char; 5] on the DECODED column, and [(char, char); 5] on the paired-column composition. The shape symmetry
(SOURCE, DECODED) pair at row i IS
(ESCAPE_SOURCES[i], ESCAPE_DECODED[i]) is a load-bearing
pointwise identity carrying the two-column composition relation
on the algebra.
Paired-column SPAN peer to Self::NAMED_ESCAPE_TABLE +
Self::SELF_ESCAPE_TABLE at the paired-shape level: where
those two arrays partition the FIVE arms into the pattern-
DISTINCT-from-value sub-vocabulary (3 rows already at
[(char, char); 3] shape by algebra design) AND the pattern-
EQUALS-value sub-vocabulary (2 rows at [char; 2] shape by
definitional-identity collapse), this array closes the UNION of
the two sub-vocabularies at ONE typed [(char, char); 5] — the
self-escape rows re-shaped from char to (char, char) at the
SPAN level via the definitional-identity (row, row)
composition. Pre-lift the paired-column SPAN identity lived at
ZERO callsites at the paired shape — consumers wanting “every
(SOURCE, DECODED) pair decode_str_escape projects at a non-
passthrough arm on the closed-set Atom algebra” had to zip
the two column-dual [char; 5] peer arrays at their sites OR
iterate NAMED_ESCAPE_TABLE and re-shape SELF_ESCAPE_TABLE’s
rows to (row, row) per callsite. Post-lift the paired-column
SPAN binds at ONE forced-arity [(char, char); 5] on the
Atom algebra.
Also sibling-shape to Self::NAMED_ESCAPE_TABLE
([(char, char); 3] on the same paired-column axis, one sub-
vocabulary over on the pattern-DISTINCT-from-value axis of the
SAME closed-set Atom algebra) — the paired-column shape is
the substrate-canonical shape for closing a (SOURCE, DECODED)
cross-product at ONE typed array on the algebra; extending it
from the NAMED sub-vocabulary’s 3-row shape to the SPAN’s 5-row
shape is a mechanical arity extension. A hypothetical sixth
non-passthrough arm (e.g. a '0' → '\0' NUL-byte extension,
a Racket-compat 'a' → '\x07' BEL, a raw-string mode adopting
'#' as an additional self-escaping delimiter) extends
Self::decode_str_escape’s match ONCE plus EITHER
NAMED_ESCAPE_TABLE or SELF_ESCAPE_TABLE ONCE plus this ALL
array ONCE plus both column-dual [char; 5] peer arrays
(Self::ESCAPE_SOURCES and Self::ESCAPE_DECODED) ONCE
each plus one new per-role pub const (or pair) in lockstep;
rustc’s forced-arity check on [(char, char); N] binds the
extension through the array declaration site.
Composition law (paired SPAN): for every index i in 0..5,
ESCAPE_TABLE[i] == (ESCAPE_SOURCES[i], ESCAPE_DECODED[i]).
The forced-arity [(char, char); 5] + canonical declaration
order pin every downstream index-sweep consumer to the (named
prefix, self suffix) partition at rustc time. The paired-SPAN
row identity is pinned structurally at
atom_escape_table_composes_pointwise_from_escape_sources_and_escape_decoded_column_duals.
Sub-vocabulary partition law: ESCAPE_TABLE[0..3] == NAMED_ESCAPE_TABLE (the pattern-DISTINCT-from-value sub-
vocabulary at its native paired shape passes through
identically), AND for i in 3..5, ESCAPE_TABLE[i] == (SELF_ESCAPE_TABLE[i-3], SELF_ESCAPE_TABLE[i-3]) (the pattern-
EQUALS-value sub-vocabulary re-shapes from char to
(row, row) at the SPAN level via the definitional-identity
collapse). Pinned structurally at
atom_escape_table_partitions_into_named_paired_prefix_and_self_reshape_suffix.
Pattern-classification partition law: for every index i in
0..3, ESCAPE_TABLE[i].0 != ESCAPE_TABLE[i].1 (the NAMED
pattern-DISTINCT-from-value prefix); for every index i in
3..5, ESCAPE_TABLE[i].0 == ESCAPE_TABLE[i].1 (the SELF
pattern-EQUALS-value suffix). The classification carried in the
row’s per-column identity relation IS the sub-vocabulary
identity — a consumer classifying an escape arm reads the sub-
vocabulary off row.0 == row.1 rather than off an index range
or a separate tag. Pinned structurally at
atom_escape_table_named_prefix_is_pattern_distinct_and_self_suffix_is_pattern_equals.
Pointwise projection law: for every (src, decoded) in
ESCAPE_TABLE, Self::decode_str_escape(src) == decoded. The
paired-column SPAN closes the (input, output) cross-product of
decode_str_escape’s non-passthrough arm-set at ONE typed
array so a refactor that drifted the arm-set (swapped rows,
added a row without extending the array, or removed a row
without extending the array) surfaces HERE at the first drifted
pair rather than at a distant sweep site.
Future consumers that compose against Self::ESCAPE_TABLE:
- A round-trip reader/renderer that pairs SOURCE bytes with
their DECODED payloads — the paired vocabulary IS
Self::ESCAPE_TABLErather than a zip of the two column-dual peer arrays. - LSP / REPL diagnostic rendering that names both columns of
every non-passthrough arm — the completion set IS
Self::ESCAPE_TABLErather than reassembled from the two sub-vocabulary arrays via achar → (char, char)re-shape on the SELF rows. - A syntax-highlighter that colors both the SOURCE byte and the
DECODED payload of every escape arm — the classifier binds
through
Self::ESCAPE_TABLErather than through two parallel per-column lookups. - A fuzz-input generator that exercises
decode_str_escape’s projection round-trip — the input-output pool ISSelf::ESCAPE_TABLErather than a runtime zip of the peer arrays. - A hypothetical
EscapeArmtyped sum enumerating the FIVE non-passthrough arms at ONE closed-set enum on the algebra (withESCAPE_TABLEbecoming its.pair()projection).
Theory anchor: THEORY.md §III — the typescape; the FIVE
(SOURCE, DECODED) pairs of the non-passthrough arm-set now
bind at ONE typed [(char, char); 5] on the closed-set
Atom algebra rather than at a runtime zip of the two peer
column-dual arrays. THEORY.md §V.1 — knowable platform; the
non-passthrough paired-column SPAN becomes load-bearing typed
data at the algebra level. THEORY.md §VI.1 — generation over
composition; the paired-column identity that lived only as a
runtime zip of the peer column-dual [char; 5] SPANs
regenerates identically through this ONE typed forced-arity
pair-array. THEORY.md §II.1 invariant 5 — composition preserves
proofs; the pointwise composition law ESCAPE_TABLE[i] == (ESCAPE_SOURCES[i], ESCAPE_DECODED[i]) is a load-bearing
structural invariant carrying the two-column composition
relation between the paired SPAN and its two column-dual peer
SPANs. A refactor that drifted any of the three arrays’
declaration orders (paired SPAN, SOURCE-column SPAN, DECODED-
column SPAN) OR drifted decode_str_escape’s match-arm
ordering surfaces at the first drifted index across the three
pointwise composition pins.
Sourcepub fn keyword_qualified(name: &str) -> String
pub fn keyword_qualified(name: &str) -> String
Project a bare keyword name to its canonical qualified rendering
— format!("{}{name}", Self::KEYWORD_MARKER), i.e. the ONE
canonical String on the Atom algebra that composes
Self::KEYWORD_MARKER with a bare keyword name to produce the
substrate-canonical ":name" spelling.
Sibling projection to Self::bool_literal on the atomic-payload
canonical-rendering axis: where bool_literal(b) projects the
closed-set bool domain to its canonical Scheme spelling
"#t" / "#f" (&'static str because the set is finite), THIS
method projects the open-set bare-name domain to its canonical
qualified spelling ":name" (String because the set is
unbounded). The Atom algebra’s atomic-payload canonical-
rendering axis now carries a typed projection for BOTH the
prefix-marked variable-payload variant (Keyword) and the self-
marked closed-set-payload variant (Bool).
Pre-lift the same format!("{}{s}", Self::KEYWORD_MARKER)
composition lived inline at THREE Keyword-arm sites on the
atomic-payload rendering axis:
Self::to_json’sSelf::Keywordarm —serde_json::Value::String(format!(...))for the JSON- canonical projection.Atom::to_iac_forge_sexpr(removed)’sSelf::Keywordarm —SExpr::Symbol(format!(...))for the iac-forge canonical- attestation projection.- [
fmt::Display for Atom]’sSelf::Keywordarm —write!(f, "{}{s}", Self::KEYWORD_MARKER)for the Lisp- canonical-form Display projection.
Post-lift the two allocating sites (1) + (2) bind at ONE typed
projection on the algebra; the [fmt::Display for Atom] site
(3) keeps its allocation-free write! path but its byte output
is pinned bit-identical to this projection by
atom_display_keyword_arm_agrees_with_keyword_qualified_bytes
so the three canonical-rendering surfaces cannot drift out of
agreement. Adding a fourth Keyword-rendering site (e.g. a future
YAML canonical projection, an LSP hover renderer) binds through
THIS projection rather than composing Self::KEYWORD_MARKER
inline — the (Keyword payload, canonical qualified rendering)
pairing lives at ONE algebra layer.
Round-trip contract (with Self::from_lexeme):
Self::from_lexeme(&Self::keyword_qualified(name)) == Self::Keyword(name.to_owned()) for every name: &str that
does NOT itself parse as a Bool spelling, integer, or float
(the four typed-entry classification arms preceding the
Self::KEYWORD_MARKER-prefix arm). The classifier’s
s.strip_prefix(Self::KEYWORD_MARKER) gate is the LEFT-inverse
of THIS projection on the Keyword-payload subset — pinned by
atom_from_lexeme_inverts_keyword_qualified_on_bare_name.
Composition preserves proofs across the (typed-EXIT rendering,
typed-ENTRY classification) round-trip at ONE algebra site.
Sibling posture to QuoteForm::prefix’s composition with
homoiconic inner forms in [fmt::Display for Sexp]’s quote-
family arm: where QuoteForm::prefix is the [&'static str]
prefix a quote-family variant composes with an inner rendering
via write!(f, "{}{inner}", qf.prefix()), THIS method is the
composed String a Keyword atom composes with a bare name.
Both projections close a (marker, payload) pair on their owning
closed-set algebra.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Keyword payload, canonical qualified rendering) pairing binds
at ONE projection on the closed-set Atom algebra regardless
of which of the three canonical-rendering surfaces reaches in.
THEORY.md §II.1 invariant 5 — composition preserves proofs; the
round-trip law
Self::from_lexeme(&Self::keyword_qualified(n)) == Self::Keyword(n.into()) is a coherence proof BETWEEN the
paired typed-EXIT rendering (this method) AND the typed-ENTRY
classification (Self::from_lexeme) on ONE algebra — a
regression that drifts either side surfaces at the pin rather
than as a silent Keyword-round-trip drift. THEORY.md §V.1 —
knowable platform; the (Keyword payload → canonical qualified
rendering) composition becomes a TYPE-level projection on the
substrate algebra rather than an inline
format!("{}{s}", Self::KEYWORD_MARKER) at every consumer
site. THEORY.md §VI.1 — generation over composition; three
byte-identical inline compositions collapse onto ONE named
projection (two routed sites + one Display byte-identity pin),
matching the substrate’s three-times rule.
Frontier inspiration: Racket’s syntax/parse keyword-form
canonical-rendering hook — where #:name keyword args have
ONE canonical printer that composes the port’s KEYWORD_PREFIX
with the bare name rather than N per-consumer printf
compositions. Translated to tatara-lisp:
Atom::keyword_qualified becomes the ONE canonical-rendering
projection on the closed-set outer Atom algebra so a
future prefix migration (a Racket-compat port to #:name, a
Clojure-compat port to ::name) touches ONE
Self::KEYWORD_MARKER constant + zero rendering sites,
rather than the pre-lift three inline format! compositions
that would silently drift.
Sourcepub fn bool_literal(b: bool) -> &'static str
pub fn bool_literal(b: bool) -> &'static str
Project the closed-set bool domain to its canonical Scheme-
spelling &'static str — "#t" for true, "#f" for false.
ONE projection on the Atom algebra that the substrate’s
FOUR Self::Bool-round-trip inline byte-literals across TWO
consumer sites (the two-arm Bool(true|false) fork inside
[fmt::Display for Atom] and the two-line if s == "#t" /
if s == "#f" cascade inside Self::from_lexeme) collapse
onto — parallel to how Self::KEYWORD_MARKER is the ONE
canonical prefix the four Keyword-round-trip sites bind to.
Pre-lift the same "#t" / "#f" bytes lived inline at four
sites: two f.write_str("#t"|"#f") arms at the Display impl and
two if s == "#t"|"#f" gates at Self::from_lexeme. Post-
lift the (typed bool, canonical Scheme spelling) pairing binds
at ONE projection on the Atom algebra that every consumer
routes through; a refactor that swaps the spelling (e.g. a
Common-Lisp-compat port to T / NIL, a JSON-compat port to
true / false) touches ONE method rather than four inline
bytes that would silently drift out of round-trip agreement if
one was updated without the others. The Display arm also
collapses from TWO variant-branches (Bool(true) => "#t",
Bool(false) => "#f") to ONE variant-branch
(Bool(b) => Self::bool_literal(*b)) — the fork on bool
happens at the projection, not at every consumer’s match body.
Load-bearing round-trip contract:
Self::from_lexeme(&Self::boolean(b).to_string()) == Self::boolean(b) for every b: bool — the reader-entry
classifier’s s == Self::bool_literal(true|false) gates and the
Lisp-canonical [Display]’s
f.write_str(Self::bool_literal(*b)) emission both bind to THIS
projection so the pair cannot drift. Guards against the
CLAUDE.md pin (“bare true/false are symbols → strings, not
bools”) — the closed-set bool domain projects only through
this typed method, so a reader extension that later accepts
bare true/false extends the projection (or its reverse) at
ONE algebra site rather than at every callsite in lockstep.
Sibling-shape peer of Self::KEYWORD_MARKER: where
KEYWORD_MARKER is the ONE &'static str prefix a Keyword
payload composes with at four round-trip sites, this method is
the ONE projection a Bool payload composes THROUGH at its two
round-trip sites. The Atom algebra’s atomic-payload axis
now carries a canonical-marker/spelling for BOTH prefix-marked
(Keyword) and self-marked (Bool) variants.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Bool payload, canonical Scheme spelling) pairing now binds at
ONE projection on the closed-set Atom algebra regardless of
which of the two reader-entry / rendering surfaces reaches in.
THEORY.md §VI.1 — generation over composition; the four byte-
identical "#t" / "#f" inline literals collapse onto ONE
named projection, matching the substrate’s three-times rule.
THEORY.md §V.1 — knowable platform; the canonical Scheme-bool
spellings become a TYPE-level projection on the substrate
algebra rather than four inline bytes at two consumer surfaces.
Sourcepub fn escape_str_payload(s: &str) -> String
pub fn escape_str_payload(s: &str) -> String
A string payload as source text, quoted and escaped so that
Self::decode_str_escape (plus the reader’s \u{…} arm) reads back
exactly these bytes.
The inverse of the reader, deliberately written as one: anything the
reader cannot decode is emitted as \u{…} rather than as a shorter
escape that would mean something different.
pub const fn decode_str_escape(esc: char) -> char
Sourcepub fn symbol(s: impl Into<String>) -> Atom
pub fn symbol(s: impl Into<String>) -> Atom
Canonical Self::Symbol constructor — first of the six per-
variant typed-construct methods on the closed-set Atom
algebra. Takes impl Into<String> so the consumer composes any
&str / String / Cow<'_, str> into the typed payload without
pre-coercing at its site — the .into() boundary lives at this
method on the algebra, parallel to how the Sexp outer
constructors (Sexp::symbol, Sexp::keyword,
Sexp::string) accept the same impl Into<String> shape at
the outer algebra layer.
Sibling typed-construct family on the closed-set Atom
algebra — paired section-for-retraction with the soft-projection
family (Self::as_symbol, Self::as_keyword,
Self::as_string, Self::as_int, Self::as_float,
Self::as_bool). Pre-lift the typed-construct family was
missing from the algebra: consumers reached for the bare
Self::Symbol(s.into()) tuple-variant constructor + .into()
coercion at every site (with no impl Into ergonomy on the
algebra), AND the soft-projection family had no constructor
peer — section-for-retraction was uneven. Post-lift every
consumer that builds an Atom from a typed payload at one
site AND projects an Atom back to its typed payload at
another binds to ONE method per direction on the algebra. The
six Sexp outer constructors (Sexp::symbol through
Sexp::boolean) route through Self::Atom(Atom::X(_)) —
.into() ergonomy on the inner algebra is reused at the outer
algebra without re-derivation.
Round-trip law binding it to the soft-projection sibling: for
every s: &str, Atom::symbol(s).as_symbol() == Some(s) —
every other arm projects to None. Same posture across the
five sibling pairs (Atom::keyword(s).as_keyword() == Some(s),
…). The kind() projection (Self::kind) similarly
round-trips through the construct face: Atom::symbol(_).kind() == AtomKind::Symbol.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle;
every consumer that constructs an Atom of a typed kind binds
to ONE typed method on the algebra rather than to the bare
tuple-variant constructor + per-site .into() coercion.
THEORY.md §V.1 — knowable platform; the (AtomKind variant, typed construct method) pair becomes a TYPE projection on the
substrate’s Atom algebra. THEORY.md §VI.1 — generation over
composition; the [Sexp; 6] outer constructors at
Sexp::symbol–Sexp::boolean regenerate identically
through Self::Atom(Atom::X(_)) composition rather than
re-deriving the .into() + tuple-variant pair per outer
constructor.
Frontier inspiration: Racket’s (symbol 'x) / (string s) —
the typed-construct face the consumer reaches for a typed
atomic value paired one-for-one with (symbol? v) /
(symbol->string v) predicate/projection siblings; the
substrate’s Self::symbol / Self::as_symbol pair is the
Rust-typed peer on the closed-set Atom algebra, with
impl Into<String> standing in for Racket’s typed-pair coerce
face. MLIR’s mlir::SymbolAttr::get(ctx, name) — typed-IR
attribute construction routes through ONE typed factory paired
with mlir::dyn_cast<SymbolAttr>(attr) on the projection face;
Atom::symbol is the substrate’s unstructured-Rust peer.
Sourcepub fn keyword(s: impl Into<String>) -> Atom
pub fn keyword(s: impl Into<String>) -> Atom
Canonical Self::Keyword constructor — second of the six
per-variant typed-construct methods on the closed-set Atom
algebra. See Self::symbol for the algebra-level docstring.
Sourcepub fn string(s: impl Into<String>) -> Atom
pub fn string(s: impl Into<String>) -> Atom
Canonical Self::Str constructor — third of the six per-variant
typed-construct methods. The method name is string for
consumer-vocabulary continuity with Self::as_string /
Sexp::string / crate::error::SexpShape::String (the typed
payload variant is Str for String shortening; the consumer-
facing method keeps string for symmetry).
Sourcepub fn int(n: i64) -> Atom
pub fn int(n: i64) -> Atom
Canonical Self::Int constructor — fourth of the six per-variant
typed-construct methods. The i64 is taken by value (no
impl Into<…> widening) — strict typed identity at the algebra
boundary, the same posture Self::as_int preserves on the
soft-projection face (Atom::Int(n) projects to Some(n) only;
the Sexp::as_float consumer is where Int→Float widening lives).
Sourcepub fn float(n: f64) -> Atom
pub fn float(n: f64) -> Atom
Canonical Self::Float constructor — fifth of the six
per-variant typed-construct methods. The f64 is taken by value
(no impl Into<…> widening), matching Self::int’s strict
typed-identity posture at the algebra boundary.
Sourcepub fn boolean(b: bool) -> Atom
pub fn boolean(b: bool) -> Atom
Canonical Self::Bool constructor — sixth and last of the six
per-variant typed-construct methods on the closed-set Atom
algebra. Together with the five siblings (Self::symbol,
Self::keyword, Self::string, Self::int,
Self::float) the per-Atom-variant typed-construct family is
complete across all six closed-set arms, and pairs section-for-
retraction with the soft-projection family (Self::as_symbol,
Self::as_keyword, Self::as_string, Self::as_int,
Self::as_float, Self::as_bool) — every consumer that
constructs an Atom from a typed payload at one site AND
projects an Atom back to its typed payload at another binds
to ONE method per direction on the algebra rather than to the
bare tuple-variant constructor + the soft-projection method
asymmetrically.
The closed-set bool payload’s Scheme-canonical #t / #f
reader lexemes are dispatched at Self::from_lexeme (the
typed-ENTRY classifier) — this method is the construction face
the consumer composes the typed bool value into when building
an Atom from already-typed Rust, parallel to how
Self::int and Self::float take their typed payload by
value.
Sourcepub fn kind(&self) -> AtomKind
pub fn kind(&self) -> AtomKind
Project the atomic value into its closed-set AtomKind marker —
Symbol(_) → AtomKind::Symbol, Keyword(_) → AtomKind::Keyword,
Str(_) → AtomKind::Str, Int(_) → AtomKind::Int,
Float(_) → AtomKind::Float, Bool(_) → AtomKind::Bool. The
projection discards the payload and surfaces the typed
discriminator that every per-atom-kind dispatch site (Hash cache-
key bytes via AtomKind::hash_discriminator, outer-shape
projection via AtomKind::sexp_shape, diagnostic label via
AtomKind::label) keys on.
Soft-projection peer of Sexp::as_quote_form: where
as_quote_form decomposes the four homoiconic prefix wrappers
into (QuoteForm, &Sexp), kind decomposes the six atomic
payloads into AtomKind alone — there is no inner-sexp body to
surface, so the projection’s return type is just the marker.
Sibling-arm sweep with the quote-family as_quote_form /
QuoteForm algebra lifts the (Atom variant, byte-discriminator,
canonical-label, SexpShape variant) quadruple from per-callsite
discipline (Hash for Atom’s six byte literals AND
domain::sexp_shape’s six SexpShape literals) onto ONE typed
algebra the substrate’s diagnostic + cache-key surfaces both
route through.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Atom variant, downstream-consumer-payload) pairing now binds at
ONE typed projection site (this method composed with
AtomKind’s arms) regardless of which consumer surface
(cache-key Hash, diagnostic SexpShape, future LSP completion
label) needs it. A regression that drifts ONE consumer’s pairing
from the others cannot reach the substrate’s runtime.
Sourcepub fn label(&self) -> &'static str
pub fn label(&self) -> &'static str
Project the atomic payload to its canonical &'static str
diagnostic label — "symbol" for Self::Symbol, "keyword"
for Self::Keyword, "string" for Self::Str, "int" for
Self::Int, "float" for Self::Float, "bool" for
Self::Bool. The outer-Atom peer on the Atom algebra of
AtomKind::label (the marker-level label projection on the
closed-set atomic-kind algebra) and crate::ast::Sexp::type_name
(the outer-value label projection on the crate::ast::Sexp
algebra composed through crate::ast::Sexp::shape +
crate::error::SexpShape::label). Every label is byte-for-byte
identical to the corresponding crate::error::SexpShape variant’s
label — the AtomKind ⊂ SexpShape label-vocabulary containment
established by AtomKind::label’s composition through
AtomKind::sexp_shape surfaces at the outer-Atom layer through
this projection.
Composition law: atom.label() == atom.kind().label() == atom.kind().sexp_shape().label() for every atom: &Atom. The
body composes Self::kind (the typed projection lifting each
Atom variant into its peer AtomKind marker) with
AtomKind::label (the canonical &'static str projection on the
closed-set atomic-payload algebra), so the six atomic-arm labels
live at ONE canonical site (crate::error::SexpShape::label’s
atomic arms, via AtomKind::label’s composition through
AtomKind::sexp_shape) rather than at TWO
(crate::error::SexpShape::label AND a parallel six-arm match
on the outer Atom algebra, pre-lift). Cross-algebra agreement
law: Sexp::Atom(atom.clone()).type_name() == atom.label() for
every atom: Atom — the outer-crate::ast::Sexp label
projection at the atomic-payload arms routes through
crate::ast::Sexp::shape’s
Self::Atom(a) => a.kind().sexp_shape() arm which composes with
crate::error::SexpShape::label byte-for-byte with this
projection’s self.kind().label() composition, so the (outer
Sexp label, outer Atom label) agreement is a TYPED CONSEQUENCE
of the two typed compositions rather than literal discipline at
two sites.
Sibling-shape lift to Self::kind (the closed-set atomic-kind
projection): where kind() carries the typed AtomKind marker
on the Atom algebra, label() carries the &'static str
literal the rendered diagnostic surface wants (still derived from
the typed marker, but flattened through AtomKind::label for
substring-grep callers, future
crate::error::LispError::TypeMismatch got slots keyed on an
atomic witness before the outer crate::ast::Sexp wrap, and
future LSP hover / REPL completion / audit-trail metric surfaces
that hold an Atom value directly rather than a wrapped
crate::ast::Sexp::Atom). The &'static str lifetime is
load-bearing: the composition allocates nothing at runtime
(Self::kind returns a Copy value and AtomKind::label
yields &'static str).
Pre-lift the (Atom variant, &'static str diagnostic label)
pairing had no typed projection on the outer-Atom algebra —
a consumer with a typed Atom in hand (a hand-authored
Atom value at a test-harness diagnostic, a future
crate::domain typed-kwarg gate that rejects on an atomic
witness before the outer crate::ast::Sexp wrap, a future LSP
hover surface that emits an atomic-payload identity without an
enclosing crate::ast::Sexp::Atom wrap, a future audit-trail
metric keyed on the observed atomic kind) wanting the canonical
diagnostic label had to spell the two-step composition
atom.kind().label() at every callsite, OR go through
[crate::ast::Sexp::Atom(atom.clone()).type_name()] which wraps
and unwraps for no runtime purpose. Post-lift the composition
binds at ONE typed-algebra method on the outer Atom value-
carrier — the SIXTH consumer of the outer-Atom projection
surface (sibling of Self::kind, Self::to_json,
Atom::to_iac_forge_sexpr (removed), Self::from_lexeme, and the six
per-variant soft-projection methods Self::as_symbol /
Self::as_keyword / Self::as_string / Self::as_int /
Self::as_float / Self::as_bool + the composite
Self::as_symbol_or_string).
Theory anchor: THEORY.md §V.1 — knowable platform; the (Atom
variant, &'static str diagnostic label) pairing becomes a TYPE
projection on the outer-Atom algebra rather than a per-
callsite .kind().label() two-step OR a wrap-through-Sexp
[crate::ast::Sexp::Atom(atom.clone()).type_name()] round-trip.
A typo or swap at the outer-Atom label site is no longer a
runtime label drift but a compile error against the typed
composition — the Atom ↔ AtomKind ↔ label chain is
rustc-enforced end-to-end. THEORY.md §II.1 invariant 2 — free
middle; the outer-Atom diagnostic-label projection now binds
at ONE site on the outer-Atom algebra, composing through the
pre-existing marker-level label projection (AtomKind::label)
rather than duplicating the six-arm match. THEORY.md §VI.1 —
generation over composition; the outer-Atom label projection is
the missing algebra layer between the outer Atom value-carrier
and the pre-existing AtomKind marker-level label projection —
the three pre-existing typed layers (Atom → AtomKind →
crate::error::SexpShape → &'static str) become a full
four-layer typed composition through ONE new named projection on
the outer value-carrier.
Frontier inspiration: MLIR’s mlir::Attribute::getAbstractAttribute() .getName() typed projection composed with the attribute-kind’s
typed string identity — narrowing an attribute-carrier value
through its typed kind identity yields the canonical diagnostic
string identity in ONE typed composition on the outer attribute
algebra. Translated through the substrate’s outer-Atom
value-carrier algebra, atom.kind().label() closes the (outer
value, canonical diagnostic label) pairing at ONE typed projection
on the value-carrier algebra composed through the marker-level
diagnostic-label face. Racket’s (syntax-kind stx) composed with
(kind-label kind) on the datum-kind taxonomy — the typed
diagnostic label emerges from a two-hop composition on the outer
datum-carrier through the typed kind identity. Atom::label is
the Rust-typed peer on the closed-set outer-Atom algebra with
AtomKind standing in for Racket’s datum-kind taxonomy.
Sourcepub fn sexp_shape(&self) -> SexpShape
pub fn sexp_shape(&self) -> SexpShape
Project the atomic value into its outer-shape SexpShape
variant — Symbol(_) → SexpShape::Symbol,
Keyword(_) → SexpShape::Keyword, Str(_) → SexpShape::String,
Int(_) → SexpShape::Int, Float(_) → SexpShape::Float,
Bool(_) → SexpShape::Bool. The outer-value peer of
AtomKind::sexp_shape one algebra layer down and of
Sexp::shape one algebra layer up. Body composes through
self.kind().sexp_shape() — routing through Self::kind
(the typed 6-arm outer-value → marker projection) then
AtomKind::sexp_shape (the canonical 6-of-12 atomic-payload
carving of SexpShape) so the (Atom variant, SexpShape
variant) pairing lives at ONE canonical site
(AtomKind::sexp_shape’s six match arms in ast.rs) rather
than at six byte-identical inline arms across consumers.
Same composition-through-carving-marker posture as Self::label
(self.kind().label()) one vocabulary axis over on the
outer-Atom algebra: Self::label closes the diagnostic-label
axis, this method closes the outer-shape-projection axis, and
both compose through the SAME typed marker layer
(Self::kind into AtomKind) into the outer-shape’s
per-axis canonical site. The two methods now paint the
outer-Atom value with typed projections onto BOTH the
diagnostic-label vocabulary AND the outer-shape closed-set —
the pair mirrors how Sexp::type_name and Sexp::shape
paint the outer-Sexp value one algebra layer up.
Composition law: atom.sexp_shape() == atom.kind().sexp_shape()
for every atom: &Atom. Pinned by
atom_sexp_shape_composes_through_kind_sexp_shape_for_every_variant
across a representative payload sweep (including NaN via
f64::to_bits round-trip on the Float arm, matching
[Hash for Atom]’s posture; both empty and non-empty
String/Symbol/Keyword arms; i64::{MIN, MAX} on the Int arm;
both Bool arms). Sibling of
atom_label_composes_through_kind_label_for_every_variant one
vocabulary axis over.
Cross-algebra agreement law: for every atom: &Atom,
atom.sexp_shape() == Sexp::Atom(atom.clone()).shape(). The
outer-Atom shape projection routes into the SAME canonical
site the outer-Sexp Sexp::shape projection lands on for
every atomic-payload arm — pinned by
atom_sexp_shape_agrees_with_sexp_shape_at_every_atom_arm via
byte-equality on the SexpShape variant across all six atomic
arms. Sibling of
atom_label_agrees_with_sexp_type_name_at_every_atom_arm one
vocabulary axis over.
Round-trip through the outer-shape’s soft-projection sibling:
atom.sexp_shape().as_atom_kind() == Some(atom.kind()) for
every atom: &Atom — the typed embed Atom → AtomKind → SexpShape inverts through the soft-projection retraction
SexpShape → AtomKind exactly on the 6-of-12 atomic-payload
image. Pinned by
atom_sexp_shape_round_trips_through_sexp_shape_as_atom_kind.
The SexpShape return type (owned; SexpShape is not Copy
because its Unknown(String) arm carries a String) is the
outer-shape closed set; consumers that want the diagnostic
label render string compose atom.sexp_shape().label(), and
that composition IS atom.label() byte-for-byte by the
composition-through-carving-marker posture the two methods
share.
Theory anchor: THEORY.md §V.1 — knowable platform; the (outer
Atom variant, SexpShape variant) pairing becomes a TYPE
projection on the outermost atomic value-carrier algebra
composed through the pre-existing marker-level projection,
rather than at parallel inline match arms each future consumer
of the outer-shape from an outer-Atom value has to re-derive.
THEORY.md §II.1 invariant 2 — free middle; the outer-Atom
outer-shape algebra now closes over THREE typed layers (outer
Atom → AtomKind → SexpShape) with rustc-enforced
consistency across each — a regression that drifts ONE layer’s
shape mapping from the others cannot reach the substrate’s
runtime typed-witness surface, LispError::TypeMismatch.got
slot, or SexpWitness::shape projection. THEORY.md §VI.1 —
generation over composition; the outer-value projection is the
missing algebra layer between the outer Atom and the
pre-existing marker-level shape projection — the two
pre-existing typed layers become a full THREE-layer typed
composition through ONE new named projection.
Frontier inspiration: MLIR’s mlir::Attribute::getType()
typed projection composed with the attribute-kind’s typed
outer-type identity — narrowing an attribute-carrier value
through its typed kind identity yields the outer-type identity
in ONE typed composition on the outer attribute algebra.
Translated through the substrate’s outer-Atom value-carrier
algebra, atom.kind().sexp_shape() closes the (outer value,
outer-shape) pairing at ONE typed projection on the value-
carrier algebra composed through the marker-level
outer-shape face.
Sourcepub fn to_json(&self) -> Value
pub fn to_json(&self) -> Value
Project the atomic payload to its canonical serde_json::Value
rendering — the typed-algebra peer of [fmt::Display for Atom] at
the JSON-projection boundary. Lifts six inline atom arms inside
crate::domain::sexp_to_json’s outer match (one
Sexp::Atom(Atom::<variant>(payload)) => JValue::<…>(…) arm
per AtomKind variant) onto ONE typed-algebra method that
every consumer routes through. Sibling-shape lift to the prior
Display for Atom (the canonical-string rendering surface),
Hash for Atom (the cache-key bytes surface via
AtomKind::hash_discriminator), and the upcoming
Atom::to_iac_forge_sexpr (the canonical-SExpr rendering
surface, feature-gated iac-forge) — every per-Atom-variant
projection now binds at ONE method on the closed-set algebra
rather than at six inline arms inside its consumer.
Mapping (preserves the byte-identical pre-lift behavior at the
sexp_to_json callsite):
Self::Symbolpayloads→serde_json::Value::Stringofscloned (Symbols are enum discriminants — the JSON deserializer reads them as the string-form variant tag).Self::Keywordpayloads→serde_json::Value::Stringof":{s}"(Keywords prefix with:in their canonical wire-form;json_to_sexp’s inverse strips the prefix).Self::Strpayloads→serde_json::Value::Stringofscloned.Self::Intpayloadn→serde_json::Value::Numberofn(lossless viaserde_json::Number::from(i64)).Self::Floatpayloadn→serde_json::Value::NumberofnIFFnis finite (NaN / ±∞ collapse toserde_json::Value::Null; this is JSON’s structural inexpressibility of those f64 values, NOT a substrate choice). The NaN/∞→Null branch is pinned at one test below (atom_to_json_float_nan_and_infinity_collapse_to_null).Self::Boolpayloadb→serde_json::Value::Boolofb.
Bidirectional contract anchored by tests in this module:
atom_to_json_projects_each_variant_to_canonical_json_value— sweeps a representative atom of eachAtomKindvariant and pins each variant’s canonical JValue mapping byte-for-byte against the pre-lift inline rule, so a future regression that drifts ONE arm (e.g. swapsSymbol’s mapping to a Number, or dropsKeyword’s:prefix) fails loudly.atom_to_json_float_nan_and_infinity_collapse_to_null— pins the JSON-structural inexpressibility branch at the atom layer directly, so a future Atom-Display-style refactor that bypassesserde_json::Number::from_f64(e.g. tries to emitNaNas the string"NaN") surfaces at the typed-algebra boundary without requiring a Sexp wrap.sexp_to_json_atom_arms_route_through_atom_to_json(in [crate::domain::tests]) — pins the lifted boundary:sexp_to_json(&Sexp::Atom(a.clone())) == Ok(a.to_json())for every atomic payload variant. Catches a future drift where one surface’s per-variant body changes without the other.
Theory anchor: THEORY.md §VI.1 — generation over composition;
the (Atom variant, canonical JValue rendering) pair lived inline
at the sexp_to_json site as six byte-identical arms. The lift
retires the per-site fan-out onto ONE method on the Atom
algebra. THEORY.md §II.1 invariant 2 — free middle; the typed-
exit JSON projection, the Display-surface rendering, the
diagnostic surface, and any future canonical-form surface
(e.g. Atom::to_iac_forge_sexpr) all route through ONE
per-variant projection family rather than per-callsite
re-derivation. THEORY.md §V.1 — knowable platform; a future
seventh atomic kind (e.g. Char for #\x reader syntax) lands
at one AtomKind::ALL entry plus one arm here plus one arm
per sibling projection — exhaustively checked by the compiler,
not by per-consumer convention.
Frontier inspiration: MLIR’s mlir::AsmPrinter::printAttribute
— the typed-IR attribute printer dispatches on the closed-set
AttributeKind so every printer body for a kind lives at ONE
implementation site; Atom::to_json is the unstructured-Rust
peer on the Atom algebra for the JSON canonical-form surface
(where Display for Atom is the Lisp-canonical-form peer and
From<&Sexp> for iac_forge::SExpr is the canonical-attestation-
form peer). Racket’s (syntax->datum stx) then a serializer
over the datum prim — to_json is the substrate’s serializer
at the atomic-payload layer, with the closed-set AtomKind
standing in for Racket’s datum-prim taxonomy.
Sourcepub fn from_json_number(n: &Number) -> Atom
pub fn from_json_number(n: &Number) -> Atom
Inverse of Self::to_json restricted to the JSON Number
discriminator — the ONE typed inverse projection on the closed-set
Atom algebra that names the (serde_json::Number →
Self::Int / Self::Float) bifurcation. Lifts the pre-lift
inline three-arm cascade inside crate::ast::Sexp::from_json’s
serde_json::Value::Number(n) outer-match arm — first
n.as_i64()? sink to Self::Int then n.as_f64()? sink to
Self::Float then a Self::Int(0) typed floor for the
structural-impossibility residual — onto ONE typed projection on
the Atom algebra so the paired FORWARD (Self::to_json’s
Self::Int / Self::Float arms) and INVERSE (this method)
numeric-axis projections live at ONE algebra layer.
Mapping (byte-identical to the pre-lift cascade in
crate::ast::Sexp::from_json):
serde_json::Number shape | result |
|---|---|
n.as_i64() == Some(i) | Self::Int(i) |
n.as_i64() == None, .as_f64() == Some(f) | Self::Float(f) |
n.as_i64() == None, .as_f64() == None | Self::Int(0) — typed floor |
Every serde_json::Number today is either i64-fitting,
u64-fitting (projected through f64), or f64-fitting — the
Int(0) residual arm is a static-invariant statement that
serde_json::Number’s closed-set discriminator excludes the
“neither i64 nor f64” case in practice; the typed floor stays
explicit so a future serde_json extension does NOT silently
misroute through an unreachable-panic. The Self::int(0)
composition in the pre-lift code equalled Self::Atom(Atom::Int(0))
via the Sexp::int sugar; post-lift the Atom algebra owns
the typed floor at the atomic layer directly.
Round-trip laws (paired with Self::to_json’s numeric arms):
- For every
i: i64,Atom::from_json_number(&i.into()) == Atom::Int(i)— theSelf::Int→JValue::Number→Self::Intround-trip is byte-identical. - For every finite non-integer-valued
f: f64,Atom::from_json_number(&serde_json::Number::from_f64(f) .unwrap()) == Atom::Float(f)— theSelf::Float→JValue::Number→Self::Floatround-trip is byte-identical forf64values that don’t overlap the i64-fitting subset ofserde_json::Number’s discriminator (i.e. non-integer-valued finite floats; integer-valued floats round-trip through theSelf::Intarm byas_i64’s eager check). - Non-finite floats (
f64::NAN,f64::INFINITY,f64::NEG_INFINITY) collapse toserde_json::Value::NullinSelf::to_json— they NEVER produce aserde_json::Numbervalue, so the round-trip law does not apply to them. This asymmetry is JSON’s structural inexpressibility of non-finite floats (pinned atatom_to_json_float_nan_and_infinity_collapse_to_null), not a substrate choice.
ONE consumer entrypoint the substrate binds against: the outer
crate::ast::Sexp::from_json’s serde_json::Value::Number(n)
arm was pre-lift a hand-rolled three-branch cascade
(if let Some(i) = n.as_i64() { Self::int(i) } else if let Some(f) = n.as_f64() { Self::float(f) } else { Self::int(0) });
post-lift the outer arm collapses to
Self::Atom(Atom::from_json_number(n)) — the ONE typed inverse
on the Atom algebra owns the numeric-axis bifurcation, the
outer arm delegates. A regression that drifts the outer arm
(e.g. re-inlines the bifurcation and swaps the as_i64/as_f64
order so 42.0 sinks to Self::Float instead of
Self::Int) becomes structurally unreachable — there is
exactly ONE numeric decode both directions of the round-trip
consume.
Sibling-lift posture: this method mirrors Self::from_lexeme
on the typed-entry classification axis — that method decodes a
bare-atom lexeme (&str) into the six-way Atom taxonomy;
THIS method decodes a JSON Number into the two-way (
Self::Int / Self::Float) numeric subtaxonomy on the SAME
algebra. Together with the seven typed-EXIT projections on
Atom ([fmt::Display for Atom], Self::to_json,
Atom::to_iac_forge_sexpr (removed), Self::label,
Self::sexp_shape, Self::hash_discriminator,
Self::bool_literal) and the two typed-ENTRY projections on
Atom (Self::from_lexeme, THIS method) the algebra’s
canonical-form bidirectional sweep is complete across every
production-site rendering + parsing surface — every consumer’s
(Atom variant, canonical rendering) OR (canonical source,
Atom variant) pairing binds at ONE method per direction per
surface on the closed-set algebra rather than at inline arms
scattered across per-consumer sites.
Theory anchor: THEORY.md §II.1 invariant 1 — typed entry; the
(JSON Number → typed Atom numeric variant) projection IS
the typed-entry gate on the JSON numeric axis. Placing the
paired forward (Atom::to_json’s Self::Int / Self::Float
arms) AND inverse (THIS method) on the Atom algebra closes
the round-trip closure at ONE algebra layer — future numeric
taxonomy extensions (e.g. u64-fitting arm for the
serde-preserve-order feature’s arbitrary_precision mode, a
[Self::Bigint] variant for arbitrary-precision integers, a
[Self::Rational] variant for [num_rational::Rational64])
extend the algebra ONCE at Self::to_json’s match AND ONCE at
this method’s cascade — both edits land on the SAME algebra
rather than across the Atom module AND the Sexp module
boundary. THEORY.md §V.1 — knowable platform; the numeric
inverse projection becomes a NAMED primitive on the substrate’s
Atom algebra rather than an inline three-arm cascade at the
Sexp::from_json consumer. THEORY.md §II.1 invariant 5 —
composition preserves proofs; the round-trip law
Atom::from_json_number(&Atom::Int(n).to_json_as_number()) == Atom::Int(n) (pinned at
atom_from_json_number_round_trips_atom_to_json_int_arm) is a
coherence proof BETWEEN the paired projections on ONE algebra —
a regression that drifts either side surfaces at the pin
rather than as a silent Sexp ↔ JSON round-trip drift.
Frontier inspiration: MLIR’s mlir::parseAttribute(str, ctx) —
the typed-IR parser inverse of printAttribute lives on the
SAME Attribute algebra as its printer dual; the substrate’s
Self::from_json_number is the unstructured-Rust peer on the
Atom algebra for the JSON-numeric canonical-form inverse,
paired with Self::to_json’s numeric arms as the closed
numeric-axis round-trip. Racket’s
(json->racket (racket->json v)) numeric identity — the
round-trip law that a JSON-projected numeric datum recovers to
its source Racket numeric primitive; THIS method’s round-trip
pins are the Rust-typed peer on the Atom algebra with the
closed-set numeric taxonomy (Self::Int / Self::Float)
standing in for Racket’s numeric tower.
Sourcepub fn from_lexeme(s: &str) -> Atom
pub fn from_lexeme(s: &str) -> Atom
Classify a bare reader-token lexeme into its typed Atom
variant — the typed-ENTRY mirror of the three typed-EXIT
projections on the Atom algebra ([fmt::Display for Atom],
Self::to_json, Atom::to_iac_forge_sexpr (removed)). Lifts the
five-statement classification cascade that lived inline at the
reader’s private atom_from_str helper onto ONE typed-algebra
method on the closed-set Atom algebra; the reader’s
Token::Atom(s) arm collapses to Sexp::Atom(Atom::from_lexeme(&s)).
Completes the bidirectional sweep across the four production-site
per-Atom-variant projection shapes (typed-exit Display, JSON,
iac-forge canonical attestation, AND now typed-entry
classification) onto the algebra.
Classification rule (byte-identical to the pre-lift reader
atom_from_str cascade):
"#t"/"#f"→Self::Bool— the Scheme bool spellings; baretrue/falsere-read asSelf::Symbol(the CLAUDE.md “Lisp bools” warning — every:values-overlaypayload depends on this forValue::Boolround-trip).:foo(leading:) →Self::Keyword— strips the:so the inversefmt::Displayrule (Keyword(s) → ":{s}") round-trips.i64::from_strsucceeds →Self::Int— load-bearing ORDERING: tried BEFOREf64so"1"classifies asSelf::Int(1), NOTSelf::Float(1.0). Typed-int- vs-typed-float distinction at the Display→read boundary is the dual offmt_float’s.0-suffix discipline.f64::from_strsucceeds →Self::Float.- Default →
Self::Symbol.
Composition laws (pinned by tests below):
Atom::from_lexeme(&a.to_string()) == afor every variant EXCEPTSelf::Str(Display renders Str with quote marks — strings take the reader’s"-quoted tokenizer branch, NOT the bare-atom branch).read(s)for every canonical bare-atom source lexeme equalsvec.
Theory anchor: THEORY.md §II.1 invariant 1 — typed entry;
atom_from_str was the typed-entry gate as a free function in
reader.rs, outside the typed Atom algebra. Naming it on the
algebra brings the typed-entry side INTO the same closed-set
match family the typed-exit projections live on, so a future
seventh atomic kind (e.g. Char for #\x reader syntax) lands
at ONE AtomKind::ALL entry plus ONE arm here plus ONE arm
per typed-exit projection — exhaustively checked by rustc
across all FOUR per-variant projection families. THEORY.md
§II.1 invariant 2 — free middle; FOUR consumers (typed-entry
classification, Display rendering, JSON projection, canonical-
attestation-form projection) now route through ONE
per-Atom-variant projection family on the closed-set algebra.
THEORY.md §VI.1 — generation over composition; this lift
completes the bidirectional sweep across the four production
surfaces the prior runs in this series named.
Frontier inspiration: Racket’s (read-syntax …) dispatches a
bare-atom lexeme through a closed-set classifier keyed on
prefix + parse-as-numeric cascade; Atom::from_lexeme is the
substrate’s typed-Rust peer, with AtomKind standing in for
Racket’s datum-prim taxonomy. MLIR’s
mlir::AsmParser::parseAttribute dispatches on the closed-set
AttributeKind so every parser body for a kind lives at ONE
implementation site; Atom::from_lexeme is the
unstructured-Rust peer on the Atom algebra for the
typed-entry classification surface.
Sourcepub fn as_symbol(&self) -> Option<&str>
pub fn as_symbol(&self) -> Option<&str>
Soft projection onto the Self::Symbol payload — Some(&str)
iff this is a Self::Symbol variant, None for every other
atomic kind (Keyword, Str, Int, Float, Bool).
FIRST of the six per-variant soft-projection methods on the typed
Atom algebra — the typed-EXIT soft-projection peer of the
typed-EXIT canonical-form projections ([fmt::Display for Atom],
Self::to_json, Atom::to_iac_forge_sexpr (removed)) and the typed-ENTRY
classifier (Self::from_lexeme). Where the canonical-form trio
projects the atomic payload to a rendered canonical surface
(string / JSON / iac-forge SExpr) and the classifier projects a
lexeme to the typed Atom, this method projects the typed Atom
to its inner payload — the soft-decomposition face of the closed
set, completing the algebra surface across BOTH bidirectional axes
(canonical-form rendering + classification on the typed-ENTRY/
typed-EXIT axis; soft decomposition on the typed-EXIT side at the
payload axis).
Sibling soft-projection peer of Sexp::as_quote_form: where
as_quote_form soft-decomposes the four homoiconic prefix
wrappers into Option<(QuoteForm, &Sexp)>, this method (and its
five as_* siblings on Atom) soft-decompose the six atomic
payloads into Option<&str> / Option<i64> / Option<f64> /
Option<bool> — there is no inner-sexp body to surface, so the
projection’s return type is just the payload. The
Sexp::as_symbol consumer at the Sexp algebra layer composes
this projection with Sexp::as_atom (the structural lift to
the inner Atom) — Sexp::as_symbol(self) == self.as_atom().and_then(Atom::as_symbol) — so the per-Atom-
variant soft-projection binds at ONE method on the typed algebra
rather than at six inline Self::Atom(Atom::X(s)) => Some(s) arms
inside the Sexp consumer.
Lifts the inline Self::Atom(Atom::Symbol(s)) => Some(s) arm at
Sexp::as_symbol’s match body onto ONE typed-algebra projection
the Sexp consumer routes through via the structural lift
Sexp::as_atom. Sibling-shape lift to the typed-EXIT
canonical-form projections (Display for Atom, Atom::to_json,
Atom::to_iac_forge_sexpr) and the typed-ENTRY classifier
(Atom::from_lexeme) — every per-Atom-variant projection
across both the rendering surfaces AND the soft-decomposition
surface now binds at ONE method on the closed-set algebra rather
than at inline arms inside its consumer.
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Atom variant, downstream-consumer-payload) pairing now binds at
ONE typed projection per consumer surface (six canonical-form
surfaces — Display, JSON, iac-forge, plus the soft-projection
FAMILY this method opens), regardless of which consumer reaches
in. THEORY.md §VI.1 — generation over composition; the six inline
Self::Atom(Atom::X(s)) => Some(_) arms at Sexp::as_X sites
(well past the ≥2 PRIME-DIRECTIVE trigger once the structural
shape is named) collapse onto the closed-set Atom algebra so a
future seventh atomic kind (e.g. Char for #\x reader syntax,
Bigint for arbitrary-precision integers) extends Atom::ALL +
the per-variant soft-projection method ONCE and rustc enforces
matching across every consumer through the closed-set match.
THEORY.md §V.1 — knowable platform; the (Atom variant, payload)
pairing becomes a TYPE projection on the substrate algebra
rather than six inline arms at the Sexp consumer. A typo or
swap at the soft-projection site is no longer a runtime drift
but a compile error against the typed projection.
Frontier inspiration: Racket’s (symbol? v) / (symbol->string v) pair — the typed-predicate + typed-projection pair at the
atomic-payload layer; this method (and its five as_* siblings)
is the substrate’s typed soft-projection peer on the closed-set
Atom algebra, with Option<&str> standing in for the
predicate-AND-projection pair Racket carries as two functions.
MLIR’s mlir::dyn_cast<SymbolAttribute>(attr) — the typed-IR
soft-downcast onto a closed-set attribute family; Atom::as_symbol
is the unstructured-Rust peer on the Atom algebra for the
soft-projection face, with the closed-set AtomKind standing in
for MLIR’s AttributeKind taxonomy.
Sourcepub fn as_keyword(&self) -> Option<&str>
pub fn as_keyword(&self) -> Option<&str>
Soft projection onto the Self::Keyword payload — Some(&str)
iff this is a Self::Keyword variant, None for every other
atomic kind. The returned &str is the payload AFTER the :
prefix has been stripped at the typed-ENTRY classifier
boundary (Self::from_lexeme strips : when constructing a
Keyword; this projection surfaces the bare identifier).
SECOND of the six per-variant soft-projection methods on the
typed Atom algebra — see Self::as_symbol for the
algebra-level docstring.
Sourcepub fn as_string(&self) -> Option<&str>
pub fn as_string(&self) -> Option<&str>
Soft projection onto the Self::Str payload — Some(&str) iff
this is a Self::Str variant (the typed "…"-quoted string
literal payload at the reader’s [crate::reader::Token::Str]
branch), None for every other atomic kind. THIRD of the six
per-variant soft-projection methods — named as_string at the
Sexp consumer for consumer-vocabulary continuity with the
pre-lift Sexp::as_string projection (the typed payload variant
is Str for String shortening; the consumer-facing method
keeps string for symmetry with the ExpectedKwargShape::String
label and the SexpShape::String outer-shape marker).
Sourcepub fn as_int(&self) -> Option<i64>
pub fn as_int(&self) -> Option<i64>
Soft projection onto the Self::Int payload — Some(i64) iff
this is a Self::Int variant, None for every other atomic
kind. FOURTH of the six per-variant soft-projection methods.
The i64 is returned by value (the payload is Copy); contrast
with Self::as_symbol / Self::as_keyword / Self::as_string
which borrow the underlying String payload as &str because
String is not Copy.
Strict typed identity: this method projects Atom::Int(n) to
Some(n) only. The Sexp::as_float consumer at the Sexp
algebra layer widens Int to Float (Atom::Int(n) → Some(n as f64)) for caller convenience at the numeric-kwarg boundary; the
Atom-level projection here stays strict so the typed-identity
distinction Int(1) vs Float(1.0) (the load-bearing typed
identity at the Self::from_lexeme ⇄ Display round-trip
boundary, dual of [fmt_float]’s .0-suffix discipline) is
preserved at the algebra layer. The widening lives at the
Sexp::as_float consumer (a.as_float().or_else(|| a.as_int() .map(|n| n as f64))) where the convenience is wanted, not at
the algebra-level projection where the typed identity is
load-bearing.
Sourcepub fn as_float(&self) -> Option<f64>
pub fn as_float(&self) -> Option<f64>
Soft projection onto the Self::Float payload — Some(f64)
iff this is a Self::Float variant, None for every other
atomic kind. FIFTH of the six per-variant soft-projection
methods.
Strict typed identity: Atom::Int(n) does NOT project through
this method (it stays None). The Sexp::as_float consumer
widens Int to Float at the Sexp algebra layer for caller
convenience; this algebra-level projection stays strict. See
Self::as_int’s docstring for the typed-identity contract.
Sourcepub fn as_bool(&self) -> Option<bool>
pub fn as_bool(&self) -> Option<bool>
Soft projection onto the Self::Bool payload — Some(bool)
iff this is a Self::Bool variant, None for every other
atomic kind. SIXTH and LAST of the six per-variant soft-projection
methods on the typed Atom algebra; together with the five
siblings (Self::as_symbol, Self::as_keyword,
Self::as_string, Self::as_int, Self::as_float) the
per-Atom-variant soft-projection family is complete across all
six closed-set arms. The CLAUDE.md-pinned "#t" / "#f" Scheme
bool spellings the reader’s typed-ENTRY classifier
Self::from_lexeme dispatches on bind the lexeme → typed
Self::Bool direction; this method binds the typed
Self::Bool → payload direction at the soft-decomposition
face.
Sourcepub fn as_symbol_or_string(&self) -> Option<&str>
pub fn as_symbol_or_string(&self) -> Option<&str>
Soft projection onto the symbol-or-string union — Some(&str) iff
this is a Self::Symbol variant OR a Self::Str variant, None
for every other atomic kind (Keyword, Int, Float, Bool).
The atomic-payload peer of Sexp::as_symbol_or_string —
disjunctive composition of Self::as_symbol + Self::as_string
at the typed Atom algebra rather than at the Sexp consumer
layer where the union previously composed two distinct
Sexp::as_atom traversals.
Sibling soft-projection peer of the six per-variant projections
(Self::as_symbol, Self::as_keyword, Self::as_string,
Self::as_int, Self::as_float, Self::as_bool) — this
union projection completes the soft-decomposition family on the
closed-set Atom algebra by naming the (Symbol ⊎ Str) union
the substrate’s named-form NAME gate (crate::compile::split_name_slot
via Sexp::as_symbol_or_string) keys on. Both NAME-author
surfaces ((defcompiler my-name …) — bare symbol; (defcompiler "my-name" …) — quoted string) project to Some("my-name")
through one method on the algebra.
Composition law binding it to Sexp::as_symbol_or_string: for
every Sexp s,
s.as_symbol_or_string() == s.as_atom().and_then(Atom::as_symbol_or_string)
— the same structural-lift composition pattern Sexp::as_symbol
/ Sexp::as_keyword / Sexp::as_string / Sexp::as_int /
Sexp::as_bool route through on the six per-variant axis.
Lifts the self.as_symbol().or_else(|| self.as_string())
disjunctive composition at Sexp::as_symbol_or_string’s body
(TWO Sexp::as_atom traversals pre-lift) onto ONE typed-algebra
projection the Sexp consumer routes through via the structural
lift Sexp::as_atom (ONE Sexp::as_atom traversal post-lift).
Theory anchor: THEORY.md §II.1 invariant 2 — free middle; the
(Symbol ⊎ Str) union projection now binds at ONE method on the
closed-set Atom algebra regardless of which consumer reaches
in. THEORY.md §VI.1 — generation over composition; the
disjunctive as_symbol().or_else(|| as_string()) composition at
Sexp::as_symbol_or_string’s body collapses onto a SINGLE
structural lift through Sexp::as_atom + the algebra-level
union projection, eliminating the double-traversal redundancy
the pre-lift consumer-layer composition carried. THEORY.md §V.1
— knowable platform; the (Symbol-or-Str) NAME-slot union becomes
a TYPE projection on the substrate algebra rather than a
disjunctive composition at every NAME-gate consumer.
Frontier inspiration: Racket’s (or/c symbol? string?)
contract — a typed disjunctive predicate the consumer binds to
in one place rather than re-deriving the disjunction at every
callsite; Self::as_symbol_or_string is the substrate’s
unstructured-Rust peer with the typed projection (Option<&str>)
surfacing the underlying payload alongside the predicate face.
MLIR’s mlir::dyn_cast<StringLike>(attr) — typed soft-downcast
onto a closed-set attribute union; Self::as_symbol_or_string
is the substrate’s Atom-algebra peer for the
(Symbol ⊎ Str) union, with Option<&str> standing in for MLIR’s
typed downcast result.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Atom
impl<'de> Deserialize<'de> for Atom
Source§fn deserialize<__D>(
__deserializer: __D,
) -> Result<Atom, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(
__deserializer: __D,
) -> Result<Atom, <__D as Deserializer<'de>>::Error>where
__D: Deserializer<'de>,
Source§impl Display for Atom
Canonical reader-round-trippable rendering of a single atomic payload —
Symbol(s) → "{s}", Keyword(s) → ":{s}", Str(s) → "{s:?}" (the
debug-quoted form: \"…\" with embedded " and \ escaped), Int(n) → "{n}", Float(n) through [fmt_float] so integral values render
with the .0 suffix that preserves the typed-Float-vs-typed-Int
distinction at the Display→read boundary, Bool(true) → "#t",
Bool(false) → "#f" (the Scheme bool spellings the reader’s
typed-entry classifier Atom::from_lexeme dispatches on — true
/ false re-read as symbols, NOT as bools — see the CLAUDE.md
“Lisp bools” warning).
impl Display for Atom
Canonical reader-round-trippable rendering of a single atomic payload —
Symbol(s) → "{s}", Keyword(s) → ":{s}", Str(s) → "{s:?}" (the
debug-quoted form: \"…\" with embedded " and \ escaped), Int(n) → "{n}", Float(n) through [fmt_float] so integral values render
with the .0 suffix that preserves the typed-Float-vs-typed-Int
distinction at the Display→read boundary, Bool(true) → "#t",
Bool(false) → "#f" (the Scheme bool spellings the reader’s
typed-entry classifier Atom::from_lexeme dispatches on — true
/ false re-read as symbols, NOT as bools — see the CLAUDE.md
“Lisp bools” warning).
This is the atomic-payload Display surface — the typed-exit-side
peer of Atom::from_lexeme’s atomic-payload typed-entry surface
(the FOURTH and LAST of the per-Atom-variant projection sites
lifted onto the closed-set algebra, after the typed-exit Display
[this impl], JSON Atom::to_json, and iac-forge canonical
attestation Atom::to_iac_forge_sexpr (removed) projections — completing
the bidirectional typed-entry/typed-exit sweep). Before this lift
the per-variant rendering arms
lived inline at the Sexp::Atom(a) => match a { … } arm of
[fmt::Display for Sexp]; routing the outer arm through this impl
lifts the seven inline sub-arms (the Bool variant splits into
true/false to short-circuit the if-else branch) into ONE
typed-algebra method the Sexp Display arm calls into via
fmt::Display::fmt(a, f). Sibling closed-set lift to
QuoteForm::prefix (the four homoiconic prefix wrappers) and
AtomKind::label (the six diagnostic labels) — those name the
quote-family and atomic-discriminator pairings at the Sexp and
Atom algebras respectively; this names the atomic-payload
rendering at the Atom algebra so future consumers of “render a
bare atom” land on this impl directly without unwrapping through
Sexp::Atom(_).to_string() and stripping the outer wrap.
Three production-site sibling shapes the substrate carries that
route through a per-Atom-variant projection, all 6/7-arm inline
matches pre-lift:
- [
fmt::Display for Sexp]’s atom arm — 7 sub-arms (Bool splits), produces afmt::Formatterbody. Post-lift collapses to ONEfmt::Display::fmt(a, f)delegation. crate::domain::sexp_to_json’s atom arms — 6 inline arms producingserde_json::Value. Now lifted ontoAtom::to_jsonin the sibling pattern this impl’s docstring named; thesexp_to_jsonsite collapses to ONESexp::Atom(a) => a.to_json()arm.crate::interop’sFrom<&Sexp> for SExpr(removed)’s atom arm (feature-gatediac-forge) — 6 inline arms producingiac_forge::sexpr::SExpr. Now lifted ontoAtom::to_iac_forge_sexpr(removed) in the sibling pattern this impl’s docstring named; the interop site collapses to ONESexp::Atom(a) => a.to_iac_forge_sexpr()arm. THIRD and LAST of the three production-site atom-arm shapes lifted onto the typedAtomalgebra; the sweep across the Lisp / JSON / iac-forge canonical-form surfaces is complete.
The (Atom variant, rendered prefix/suffix/body) quadruple now lives
at ONE typed-algebra Display impl rather than at seven inline
sub-arms inside Display for Sexp’s outer Atom arm. A regression
that drifts the Bool spelling (#t/#f vs true/false) — the
CLAUDE.md-pinned reader-round-trip invariant — now lands at ONE
site, and the test surface pins each variant’s canonical rendering
AND the round-trip identity through the reader at the Atom level
directly (no Sexp wrap required to exercise the round-trip).
Bidirectional contract anchored by tests in this module:
atom_display_renders_each_variant_to_canonical_form— sweepsAtomKind::ALLand pins each variant’s canonical rendering byte-for-byte against the pre-lift inline literal, so a future regression that drifts ONE arm (e.g. swaps#t/#ffortrue/false, or stripsStr’s quote marks) fails loudly.sexp_atom_display_arm_routes_through_atom_display_for_every_variant— pins the lifted boundary:Sexp::Atom(a).to_string() == a.to_string()for every atomic payload variant, AND that both equal the legacy inline rendering. Catches a future drift where one surface’s per-variant body changes without the other.atom_display_round_trips_through_reader_preserving_typed_identity— sweeps a representative atom of each variant, renders it viaAtom::Display, parses the rendering throughcrate::reader::read, and pins the parsed atom equals the seed atom (moduloStr’s debug-quoted spelling — pinned separately because the reader expects unquoted source-level"foo"). Pins that the (Atom::Display, reader) pair forms a typed round-trip at the atom layer, the same invariant [fmt_float]’s.0suffix preserves for the float-vs-int distinction at the Sexp layer.
Theory anchor: THEORY.md §VI.1 — generation over composition; the
(Atom variant, canonical rendering) pair appeared inline at THREE
production sites (Display for Sexp’s 7-sub-arm atom arm,
sexp_to_json’s 6 atom arms, From<&Sexp> for SExpr’s 6 atom arms)
— well past the ≥2 PRIME-DIRECTIVE trigger once the structural
shape is named. THIS lift retires the Display-surface site by
naming the typed primitive on the Atom algebra; future runs route
the JSON and iac-forge sites through parallel sibling projections
(Atom::to_json, Atom::to_iac_forge_sexpr) the same pattern
names. THEORY.md §II.1 invariant 1 — typed entry; the substrate’s
Atom::from_lexeme is the typed-entry gate at the atomic-payload
boundary (lifted onto the typed Atom algebra from the reader’s
pre-lift free function), and this impl is the typed-exit-side
mirror — the closed-set AtomKind algebra now threads BOTH gates
through ONE projection family, so a regression that drifts one side’s
per-variant rendering from the other (e.g. extends Atom with a
Char variant the reader accepts but the writer can’t emit) is no
longer a silent two-site divergence — rustc binds both sides to
the same closed-set enum. THEORY.md §II.1 invariant 2 — free middle;
the typed-exit rendering, the reader, the diagnostic surface
(LispError::TypeMismatch.got slot rendering an atomic witness),
and any future authoring tool (LSP / REPL pretty-printer) all
route through ONE per-variant rendering rather than per-callsite
re-derivation.
Frontier inspiration: Racket’s (syntax->datum stx) / write pair
— where syntax->datum unwraps the homoiconic surface to its
atomic-payload layer and write emits the canonical S-expression
rendering bound to the reader’s read inverse; Atom::Display
is the substrate’s typed-algebra peer at the atomic-payload boundary,
with the closed-set AtomKind standing in for Racket’s
datum-prim taxonomy. MLIR’s mlir::AsmPrinter::printAttribute — the
typed-IR attribute printer dispatches on the closed-set
AttributeKind so every printer body for a kind lives at ONE
implementation site; Atom::Display is the unstructured Rust peer
for the Sexp/Atom algebra, with fmt::Display standing in for
MLIR’s AsmPrinter interface.