Skip to main content

cljrs_reader/
form.rs

1use std::mem;
2
3use cljrs_types::span::Span;
4
5/// A parsed Clojure form with its source location.
6///
7/// `PartialEq` ignores spans so test assertions can compare forms without
8/// constructing exact span values.
9#[derive(Debug, Clone)]
10pub struct Form {
11    pub kind: FormKind,
12    pub span: Span,
13}
14
15impl Form {
16    pub fn new(kind: FormKind, span: Span) -> Self {
17        Self { kind, span }
18    }
19
20    /// Total heap bytes owned by this form tree (excluding the `Form` itself).
21    pub fn heap_size(&self) -> usize {
22        mem::size_of::<FormKind>() + self.kind.heap_size()
23    }
24
25    /// The annotated form with every `^meta` wrapper removed.
26    ///
27    /// Returns `self` when the form carries no metadata. Stacked metadata
28    /// (`^:a ^:b x`) is peeled down to the innermost form.
29    pub fn unmeta(&self) -> &Form {
30        let mut form = self;
31        while let FormKind::Meta(_, inner) = &form.kind {
32            form = inner;
33        }
34        form
35    }
36
37    /// True when an evaluated-position `^meta` annotation on this form becomes
38    /// *runtime* metadata on the value it produces.
39    ///
40    /// Only a form that constructs an `IObj` qualifies — a collection literal
41    /// or a function. Every other form (a call, a symbol, `quote`, `if`, `do`)
42    /// takes the annotation as a compile-time hint and evaluates to an
43    /// unannotated value, so `(meta ^{:a 1} (list 1))` and `(meta ^{:a 1} x)`
44    /// are both `nil`.
45    ///
46    /// Every execution tier consults this one predicate: the tree-walker in
47    /// `interp::eval`, and IR lowering in `lower::anf` for the JIT and AOT
48    /// paths. A tier that disagreed would make `meta` depend on how hot the
49    /// code got.
50    ///
51    /// True when the value this form denotes *as data* can carry metadata.
52    ///
53    /// Inside `quote` every form is a literal, so whether an annotation lands
54    /// is a question about the form and needs no runtime test: `'^{:a 1} [1]`
55    /// carries it, `'^{:a 1} 42` cannot. Mirrors `supports_meta` in
56    /// `cljrs-runtime` over the values `form_to_value` produces — a reader
57    /// macro (`'x`, `@x`, `#'x`, `` `x ``) denotes a list, and `#(…)` denotes
58    /// the `fn*` list it expands to.
59    pub fn quoted_value_supports_meta(&self) -> bool {
60        match &self.kind {
61            FormKind::List(_)
62            | FormKind::Vector(_)
63            | FormKind::Map(_)
64            | FormKind::Set(_)
65            | FormKind::Symbol(_)
66            | FormKind::AutoSymbol(_)
67            | FormKind::AnonFn(_)
68            | FormKind::Quote(_)
69            | FormKind::SyntaxQuote(_)
70            | FormKind::Unquote(_)
71            | FormKind::UnquoteSplice(_)
72            | FormKind::Deref(_)
73            | FormKind::Var(_) => true,
74            FormKind::Meta(_, inner) => inner.quoted_value_supports_meta(),
75            _ => false,
76        }
77    }
78
79    /// Inside `quote` the rule does not apply: there the annotation is data and
80    /// lands on any value that can carry it.
81    pub fn takes_runtime_meta(&self) -> bool {
82        match &self.kind {
83            FormKind::Vector(_) | FormKind::Map(_) | FormKind::Set(_) | FormKind::AnonFn(_) => true,
84            // A list is a call, except when it *is* a function form.
85            FormKind::List(parts) => matches!(
86                parts.first().map(|f| &f.kind),
87                Some(FormKind::Symbol(s)) if s == "fn" || s == "fn*"
88            ),
89            // Metadata stacks: `^:a ^:b [1]` annotates the vector twice.
90            FormKind::Meta(_, inner) => inner.takes_runtime_meta(),
91            _ => false,
92        }
93    }
94
95    /// The `^meta` forms attached to this form, outermost first, together with
96    /// the annotated form itself.
97    pub fn peel_meta(&self) -> (Vec<&Form>, &Form) {
98        let mut metas = Vec::new();
99        let mut form = self;
100        while let FormKind::Meta(meta, inner) = &form.kind {
101            metas.push(meta.as_ref());
102            form = inner;
103        }
104        (metas, form)
105    }
106
107    // ── Structural views ──────────────────────────────────────────────────────
108    //
109    // Every accessor below reports the shape of [`Form::unmeta`], so an
110    // annotated form has the same structural shape as the form it annotates.
111
112    /// The symbol name, if this form is a symbol.
113    pub fn as_symbol(&self) -> Option<&str> {
114        match &self.unmeta().kind {
115            FormKind::Symbol(s) => Some(s),
116            _ => None,
117        }
118    }
119
120    /// The keyword name (without the leading `:`), if this form is a keyword.
121    pub fn as_keyword(&self) -> Option<&str> {
122        match &self.unmeta().kind {
123            FormKind::Keyword(k) => Some(k),
124            _ => None,
125        }
126    }
127
128    /// The string contents, if this form is a string literal.
129    pub fn as_string(&self) -> Option<&str> {
130        match &self.unmeta().kind {
131            FormKind::Str(s) => Some(s),
132            _ => None,
133        }
134    }
135
136    /// The elements, if this form is a list.
137    pub fn as_list(&self) -> Option<&[Form]> {
138        match &self.unmeta().kind {
139            FormKind::List(v) => Some(v),
140            _ => None,
141        }
142    }
143
144    /// The elements, if this form is a vector.
145    pub fn as_vector(&self) -> Option<&[Form]> {
146        match &self.unmeta().kind {
147            FormKind::Vector(v) => Some(v),
148            _ => None,
149        }
150    }
151
152    /// The flat key/value pairs, if this form is a map literal.
153    pub fn as_map(&self) -> Option<&[Form]> {
154        match &self.unmeta().kind {
155            FormKind::Map(v) => Some(v),
156            _ => None,
157        }
158    }
159}
160
161impl FormKind {
162    /// Heap bytes owned by this node and all children.
163    pub fn heap_size(&self) -> usize {
164        match self {
165            // Inline scalars — no heap.
166            FormKind::Nil
167            | FormKind::Bool(_)
168            | FormKind::Int(_)
169            | FormKind::Float(_)
170            | FormKind::Char(_)
171            | FormKind::Symbolic(_) => 0,
172
173            // String payloads.
174            FormKind::BigInt(s)
175            | FormKind::BigDecimal(s)
176            | FormKind::Ratio(s)
177            | FormKind::Str(s)
178            | FormKind::Regex(s)
179            | FormKind::Symbol(s)
180            | FormKind::Keyword(s)
181            | FormKind::AutoKeyword(s)
182            | FormKind::AutoSymbol(s) => s.capacity(),
183
184            // Vec<Form> — Vec overhead + recursive children.
185            FormKind::List(v)
186            | FormKind::Vector(v)
187            | FormKind::Map(v)
188            | FormKind::Set(v)
189            | FormKind::AnonFn(v) => vec_heap_size(v),
190
191            // Box<Form> — one Form on heap.
192            FormKind::Quote(f)
193            | FormKind::SyntaxQuote(f)
194            | FormKind::Unquote(f)
195            | FormKind::UnquoteSplice(f)
196            | FormKind::Deref(f)
197            | FormKind::Var(f) => mem::size_of::<Form>() + f.heap_size(),
198
199            // Two Box<Form>.
200            FormKind::Meta(a, b) => mem::size_of::<Form>() * 2 + a.heap_size() + b.heap_size(),
201
202            // String + Box<Form>.
203            FormKind::TaggedLiteral(s, f) => s.capacity() + mem::size_of::<Form>() + f.heap_size(),
204
205            FormKind::ReaderCond { clauses, .. } => vec_heap_size(clauses),
206        }
207    }
208}
209
210impl PartialEq for Form {
211    fn eq(&self, other: &Self) -> bool {
212        self.kind == other.kind
213    }
214}
215
216/// The payload of a `Form` node.
217#[derive(Debug, Clone, PartialEq)]
218pub enum FormKind {
219    // ── Atoms ─────────────────────────────────────────────────────────────────
220    Nil,
221    Bool(bool),
222    Int(i64),
223    BigInt(String),
224    Float(f64), // NaN != NaN per IEEE 754 — acceptable for AST equality
225    BigDecimal(String),
226    Ratio(String),
227    Char(char),
228    Str(String),
229    Regex(String),
230    /// `##Inf` → `INFINITY`, `##-Inf` → `NEG_INFINITY`, `##NaN` → `NAN`
231    Symbolic(f64),
232
233    // ── Identifiers ───────────────────────────────────────────────────────────
234    Symbol(String),
235    Keyword(String),
236    /// `::kw` / `::alias/kw` - the namespace is resolved against the reading
237    /// namespace by the evaluator, not by the reader.
238    AutoKeyword(String),
239    /// A symbol whose namespace is auto-resolved the same way. There is no
240    /// surface syntax for one; the reader produces it for a bare symbol key in
241    /// an auto-resolved namespaced map (`#::{a 1}`, `#::alias{a 1}`).
242    AutoSymbol(String),
243
244    // ── Collections ───────────────────────────────────────────────────────────
245    List(Vec<Form>),
246    Vector(Vec<Form>),
247    /// Flat key/value pairs; length is always even.
248    Map(Vec<Form>),
249    Set(Vec<Form>),
250
251    // ── Wrapping reader macros ────────────────────────────────────────────────
252    Quote(Box<Form>),
253    SyntaxQuote(Box<Form>),
254    Unquote(Box<Form>),
255    UnquoteSplice(Box<Form>),
256    Deref(Box<Form>),
257    /// `#'symbol`
258    Var(Box<Form>),
259    /// `^meta-form annotated-form` — raw meta form kept as-is; evaluator
260    /// expands shorthand (`:kw` → `{:kw true}`, `Sym` → `{:tag Sym}`).
261    Meta(Box<Form>, Box<Form>),
262
263    // ── Dispatch forms ────────────────────────────────────────────────────────
264    /// `#(…)` anonymous function literal
265    AnonFn(Vec<Form>),
266    /// `#tag form` tagged literal
267    TaggedLiteral(String, Box<Form>),
268
269    // ── Reader conditionals ───────────────────────────────────────────────────
270    /// All branches are kept; the evaluator filters by `:rust`.
271    /// `clauses` is flat: `[keyword, form, keyword, form, …]`.
272    ReaderCond {
273        splicing: bool,
274        clauses: Vec<Form>,
275    },
276}
277
278fn vec_heap_size(forms: &[Form]) -> usize {
279    mem::size_of_val(forms) + forms.iter().map(|f| f.heap_size()).sum::<usize>()
280}