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    /// True when this form, used as a variadic `&` rest pattern, selects
26    /// Clojure's keyword-argument convention.
27    ///
28    /// A map-shaped rest pattern (`[& {:keys [a b]}]`) does not destructure the
29    /// rest *sequence*; the trailing arguments are first normalized into a map
30    /// (`Value::from_kwargs_rest`) and the pattern is applied to that.  Every
31    /// execution tier has to make this call the same way — the tree-walker in
32    /// `bind_fn_params`, and both lowerers on their way to `KnownFn::KwargsMap`
33    /// — so they share this predicate rather than three copies of the match.
34    /// Issue #368 was those copies disagreeing.
35    pub fn is_kwargs_rest_pattern(&self) -> bool {
36        matches!(self.kind, FormKind::Map(_))
37    }
38
39    /// The annotated form with every `^meta` wrapper removed.
40    ///
41    /// Returns `self` when the form carries no metadata. Stacked metadata
42    /// (`^:a ^:b x`) is peeled down to the innermost form.
43    pub fn unmeta(&self) -> &Form {
44        let mut form = self;
45        while let FormKind::Meta(_, inner) = &form.kind {
46            form = inner;
47        }
48        form
49    }
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    /// True when an evaluated-position `^meta` annotation on this form becomes
80    /// *runtime* metadata on the value it produces.
81    ///
82    /// Only a form that constructs an `IObj` qualifies — a collection literal
83    /// (the empty list `()` included) or a function. Every other form (a call,
84    /// a symbol, `quote`, `if`, `do`) takes the annotation as a compile-time
85    /// hint and evaluates to an unannotated value, so `(meta ^{:a 1} (list 1))`
86    /// and `(meta ^{:a 1} x)` are both `nil`.
87    ///
88    /// Every execution tier consults this one predicate: the tree-walker in
89    /// `interp::eval`, and IR lowering in `lower::anf` for the JIT and AOT
90    /// paths. A tier that disagreed would make `meta` depend on how hot the
91    /// code got.
92    ///
93    /// Inside `quote` the rule does not apply: there the annotation is data and
94    /// lands on any value that can carry it (see
95    /// [`Form::quoted_value_supports_meta`]).
96    pub fn takes_runtime_meta(&self) -> bool {
97        match &self.kind {
98            FormKind::Vector(_) | FormKind::Map(_) | FormKind::Set(_) | FormKind::AnonFn(_) => true,
99            // `()` has no head, so it is not a call: it is the empty-list
100            // literal, and attaches exactly as `[]`, `{}` and `#{}` do.
101            FormKind::List(parts) if parts.is_empty() => true,
102            // Any other list is a call, except when it *is* a function form.
103            FormKind::List(_) => self.is_fn_form(),
104            // Metadata stacks: `^:a ^:b [1]` annotates the vector twice.
105            FormKind::Meta(_, inner) => inner.takes_runtime_meta(),
106            _ => false,
107        }
108    }
109
110    /// True when this form is a function form: a list headed by `fn` or `fn*`.
111    pub fn is_fn_form(&self) -> bool {
112        match &self.kind {
113            FormKind::List(parts) => matches!(
114                parts.first().map(|f| &f.kind),
115                Some(FormKind::Symbol(s)) if s == "fn" || s == "fn*"
116            ),
117            _ => false,
118        }
119    }
120
121    /// True when this form, used as a `^meta` annotation or a `defn` attr-map,
122    /// requests `:async`.
123    ///
124    /// Handles the keyword shorthand `^:async` and an explicit map such as
125    /// `^{:async true}` or `{:async true}`; a `false` or `nil` value does not
126    /// request it.
127    pub fn requests_async(&self) -> bool {
128        match &self.kind {
129            FormKind::Keyword(k) => k == "async",
130            FormKind::Map(entries) => entries.chunks(2).any(|kv| {
131                matches!(&kv[0].kind, FormKind::Keyword(k) if k == "async")
132                    && !matches!(
133                        kv.get(1).map(|f| &f.kind),
134                        None | Some(FormKind::Bool(false)) | Some(FormKind::Nil)
135                    )
136            }),
137            _ => false,
138        }
139    }
140
141    /// True when this form is an anonymous function that asks to be `^:async`,
142    /// in any spelling: an annotation on the whole form,
143    /// `^:async (fn [..] ..)`, or one accepted by [`Form::fn_args_request_async`]
144    /// on its arguments. Stacked annotations are searched in full.
145    ///
146    /// Only the tree-walker can build an async closure; IR lowering refuses a
147    /// body containing one of these, so every tier agrees on what calling it
148    /// returns.
149    pub fn is_async_fn_form(&self) -> bool {
150        let (metas, inner) = self.peel_meta();
151        if !inner.is_fn_form() {
152            return false;
153        }
154        if metas.iter().any(|m| m.requests_async()) {
155            return true;
156        }
157        let FormKind::List(parts) = &inner.kind else {
158            return false;
159        };
160        Form::fn_args_request_async(&parts[1..])
161    }
162
163    /// True when the arguments of an `fn`/`fn*` form (everything after the
164    /// head) request `:async` through metadata on the first argument,
165    /// `(fn ^:async [..] ..)` / `(fn ^:async name [..] ..)`, or on a single-arity
166    /// params vector that follows a name, `(fn name ^:async [..] ..)`. The last
167    /// is also what `(defn name ^:async [..] ..)` becomes once `defn` rebuilds
168    /// its `fn` arguments. Stacked annotations are searched in full.
169    pub fn fn_args_request_async(args: &[Form]) -> bool {
170        let requests = |f: &Form| f.peel_meta().0.iter().any(|m| m.requests_async());
171        let Some(first) = args.first() else {
172            return false;
173        };
174        if requests(first) {
175            return true;
176        }
177        first.as_symbol().is_some() && args.get(1).is_some_and(requests)
178    }
179
180    /// The `^meta` forms attached to this form, outermost first, together with
181    /// the annotated form itself.
182    pub fn peel_meta(&self) -> (Vec<&Form>, &Form) {
183        let mut metas = Vec::new();
184        let mut form = self;
185        while let FormKind::Meta(meta, inner) = &form.kind {
186            metas.push(meta.as_ref());
187            form = inner;
188        }
189        (metas, form)
190    }
191
192    // ── Structural views ──────────────────────────────────────────────────────
193    //
194    // Every accessor below reports the shape of [`Form::unmeta`], so an
195    // annotated form has the same structural shape as the form it annotates.
196
197    /// The symbol name, if this form is a symbol.
198    pub fn as_symbol(&self) -> Option<&str> {
199        match &self.unmeta().kind {
200            FormKind::Symbol(s) => Some(s),
201            _ => None,
202        }
203    }
204
205    /// The keyword name (without the leading `:`), if this form is a keyword.
206    pub fn as_keyword(&self) -> Option<&str> {
207        match &self.unmeta().kind {
208            FormKind::Keyword(k) => Some(k),
209            _ => None,
210        }
211    }
212
213    /// The string contents, if this form is a string literal.
214    pub fn as_string(&self) -> Option<&str> {
215        match &self.unmeta().kind {
216            FormKind::Str(s) => Some(s),
217            _ => None,
218        }
219    }
220
221    /// The elements, if this form is a list.
222    pub fn as_list(&self) -> Option<&[Form]> {
223        match &self.unmeta().kind {
224            FormKind::List(v) => Some(v),
225            _ => None,
226        }
227    }
228
229    /// The elements, if this form is a vector.
230    pub fn as_vector(&self) -> Option<&[Form]> {
231        match &self.unmeta().kind {
232            FormKind::Vector(v) => Some(v),
233            _ => None,
234        }
235    }
236
237    /// The flat key/value pairs, if this form is a map literal.
238    pub fn as_map(&self) -> Option<&[Form]> {
239        match &self.unmeta().kind {
240            FormKind::Map(v) => Some(v),
241            _ => None,
242        }
243    }
244}
245
246impl FormKind {
247    /// Heap bytes owned by this node and all children.
248    pub fn heap_size(&self) -> usize {
249        match self {
250            // Inline scalars — no heap.
251            FormKind::Nil
252            | FormKind::Bool(_)
253            | FormKind::Int(_)
254            | FormKind::Float(_)
255            | FormKind::Char(_)
256            | FormKind::Symbolic(_) => 0,
257
258            // String payloads.
259            FormKind::BigInt(s)
260            | FormKind::BigDecimal(s)
261            | FormKind::Ratio(s)
262            | FormKind::Str(s)
263            | FormKind::Regex(s)
264            | FormKind::Symbol(s)
265            | FormKind::Keyword(s)
266            | FormKind::AutoKeyword(s)
267            | FormKind::AutoSymbol(s) => s.capacity(),
268
269            // Vec<Form> — Vec overhead + recursive children.
270            FormKind::List(v)
271            | FormKind::Vector(v)
272            | FormKind::Map(v)
273            | FormKind::Set(v)
274            | FormKind::AnonFn(v) => vec_heap_size(v),
275
276            // Box<Form> — one Form on heap.
277            FormKind::Quote(f)
278            | FormKind::SyntaxQuote(f)
279            | FormKind::Unquote(f)
280            | FormKind::UnquoteSplice(f)
281            | FormKind::Deref(f)
282            | FormKind::Var(f) => mem::size_of::<Form>() + f.heap_size(),
283
284            // Two Box<Form>.
285            FormKind::Meta(a, b) => mem::size_of::<Form>() * 2 + a.heap_size() + b.heap_size(),
286
287            // String + Box<Form>.
288            FormKind::TaggedLiteral(s, f) => s.capacity() + mem::size_of::<Form>() + f.heap_size(),
289
290            FormKind::ReaderCond { clauses, .. } => vec_heap_size(clauses),
291        }
292    }
293}
294
295impl PartialEq for Form {
296    fn eq(&self, other: &Self) -> bool {
297        self.kind == other.kind
298    }
299}
300
301/// The payload of a `Form` node.
302#[derive(Debug, Clone, PartialEq)]
303pub enum FormKind {
304    // ── Atoms ─────────────────────────────────────────────────────────────────
305    Nil,
306    Bool(bool),
307    Int(i64),
308    BigInt(String),
309    Float(f64), // NaN != NaN per IEEE 754 — acceptable for AST equality
310    BigDecimal(String),
311    Ratio(String),
312    Char(char),
313    Str(String),
314    Regex(String),
315    /// `##Inf` → `INFINITY`, `##-Inf` → `NEG_INFINITY`, `##NaN` → `NAN`
316    Symbolic(f64),
317
318    // ── Identifiers ───────────────────────────────────────────────────────────
319    Symbol(String),
320    Keyword(String),
321    /// `::kw` / `::alias/kw` - the namespace is resolved against the reading
322    /// namespace by the evaluator, not by the reader.
323    AutoKeyword(String),
324    /// A symbol whose namespace is auto-resolved the same way. There is no
325    /// surface syntax for one; the reader produces it for a bare symbol key in
326    /// an auto-resolved namespaced map (`#::{a 1}`, `#::alias{a 1}`).
327    AutoSymbol(String),
328
329    // ── Collections ───────────────────────────────────────────────────────────
330    List(Vec<Form>),
331    Vector(Vec<Form>),
332    /// Flat key/value pairs; length is always even.
333    Map(Vec<Form>),
334    Set(Vec<Form>),
335
336    // ── Wrapping reader macros ────────────────────────────────────────────────
337    Quote(Box<Form>),
338    SyntaxQuote(Box<Form>),
339    Unquote(Box<Form>),
340    UnquoteSplice(Box<Form>),
341    Deref(Box<Form>),
342    /// `#'symbol`
343    Var(Box<Form>),
344    /// `^meta-form annotated-form` — raw meta form kept as-is; evaluator
345    /// expands shorthand (`:kw` → `{:kw true}`, `Sym` → `{:tag Sym}`).
346    Meta(Box<Form>, Box<Form>),
347
348    // ── Dispatch forms ────────────────────────────────────────────────────────
349    /// `#(…)` anonymous function literal
350    AnonFn(Vec<Form>),
351    /// `#tag form` tagged literal
352    TaggedLiteral(String, Box<Form>),
353
354    // ── Reader conditionals ───────────────────────────────────────────────────
355    /// All branches are kept; the runtime picks one with
356    /// `cljrs_runtime::builtins::form::select_reader_cond` (feature keys
357    /// `:cljrs`/legacy `:rust`, then `:default`, first match in clause order).
358    /// Never compare the feature keyword by hand.
359    /// `clauses` is flat: `[keyword, form, keyword, form, …]`.
360    ReaderCond {
361        splicing: bool,
362        clauses: Vec<Form>,
363    },
364}
365
366fn vec_heap_size(forms: &[Form]) -> usize {
367    mem::size_of_val(forms) + forms.iter().map(|f| f.heap_size()).sum::<usize>()
368}