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 either spelling: an annotation on the whole form,
143 /// `^:async (fn [..] ..)`, or on its first argument, `(fn ^:async [..] ..)`
144 /// / `(fn ^:async name [..] ..)`. Stacked annotations are searched in
145 /// full.
146 ///
147 /// Only the tree-walker can build an async closure; IR lowering refuses a
148 /// body containing one of these, so every tier agrees on what calling it
149 /// returns.
150 pub fn is_async_fn_form(&self) -> bool {
151 let (metas, inner) = self.peel_meta();
152 if !inner.is_fn_form() {
153 return false;
154 }
155 if metas.iter().any(|m| m.requests_async()) {
156 return true;
157 }
158 let FormKind::List(parts) = &inner.kind else {
159 return false;
160 };
161 parts
162 .get(1)
163 .is_some_and(|first| first.peel_meta().0.iter().any(|m| m.requests_async()))
164 }
165
166 /// The `^meta` forms attached to this form, outermost first, together with
167 /// the annotated form itself.
168 pub fn peel_meta(&self) -> (Vec<&Form>, &Form) {
169 let mut metas = Vec::new();
170 let mut form = self;
171 while let FormKind::Meta(meta, inner) = &form.kind {
172 metas.push(meta.as_ref());
173 form = inner;
174 }
175 (metas, form)
176 }
177
178 // ── Structural views ──────────────────────────────────────────────────────
179 //
180 // Every accessor below reports the shape of [`Form::unmeta`], so an
181 // annotated form has the same structural shape as the form it annotates.
182
183 /// The symbol name, if this form is a symbol.
184 pub fn as_symbol(&self) -> Option<&str> {
185 match &self.unmeta().kind {
186 FormKind::Symbol(s) => Some(s),
187 _ => None,
188 }
189 }
190
191 /// The keyword name (without the leading `:`), if this form is a keyword.
192 pub fn as_keyword(&self) -> Option<&str> {
193 match &self.unmeta().kind {
194 FormKind::Keyword(k) => Some(k),
195 _ => None,
196 }
197 }
198
199 /// The string contents, if this form is a string literal.
200 pub fn as_string(&self) -> Option<&str> {
201 match &self.unmeta().kind {
202 FormKind::Str(s) => Some(s),
203 _ => None,
204 }
205 }
206
207 /// The elements, if this form is a list.
208 pub fn as_list(&self) -> Option<&[Form]> {
209 match &self.unmeta().kind {
210 FormKind::List(v) => Some(v),
211 _ => None,
212 }
213 }
214
215 /// The elements, if this form is a vector.
216 pub fn as_vector(&self) -> Option<&[Form]> {
217 match &self.unmeta().kind {
218 FormKind::Vector(v) => Some(v),
219 _ => None,
220 }
221 }
222
223 /// The flat key/value pairs, if this form is a map literal.
224 pub fn as_map(&self) -> Option<&[Form]> {
225 match &self.unmeta().kind {
226 FormKind::Map(v) => Some(v),
227 _ => None,
228 }
229 }
230}
231
232impl FormKind {
233 /// Heap bytes owned by this node and all children.
234 pub fn heap_size(&self) -> usize {
235 match self {
236 // Inline scalars — no heap.
237 FormKind::Nil
238 | FormKind::Bool(_)
239 | FormKind::Int(_)
240 | FormKind::Float(_)
241 | FormKind::Char(_)
242 | FormKind::Symbolic(_) => 0,
243
244 // String payloads.
245 FormKind::BigInt(s)
246 | FormKind::BigDecimal(s)
247 | FormKind::Ratio(s)
248 | FormKind::Str(s)
249 | FormKind::Regex(s)
250 | FormKind::Symbol(s)
251 | FormKind::Keyword(s)
252 | FormKind::AutoKeyword(s)
253 | FormKind::AutoSymbol(s) => s.capacity(),
254
255 // Vec<Form> — Vec overhead + recursive children.
256 FormKind::List(v)
257 | FormKind::Vector(v)
258 | FormKind::Map(v)
259 | FormKind::Set(v)
260 | FormKind::AnonFn(v) => vec_heap_size(v),
261
262 // Box<Form> — one Form on heap.
263 FormKind::Quote(f)
264 | FormKind::SyntaxQuote(f)
265 | FormKind::Unquote(f)
266 | FormKind::UnquoteSplice(f)
267 | FormKind::Deref(f)
268 | FormKind::Var(f) => mem::size_of::<Form>() + f.heap_size(),
269
270 // Two Box<Form>.
271 FormKind::Meta(a, b) => mem::size_of::<Form>() * 2 + a.heap_size() + b.heap_size(),
272
273 // String + Box<Form>.
274 FormKind::TaggedLiteral(s, f) => s.capacity() + mem::size_of::<Form>() + f.heap_size(),
275
276 FormKind::ReaderCond { clauses, .. } => vec_heap_size(clauses),
277 }
278 }
279}
280
281impl PartialEq for Form {
282 fn eq(&self, other: &Self) -> bool {
283 self.kind == other.kind
284 }
285}
286
287/// The payload of a `Form` node.
288#[derive(Debug, Clone, PartialEq)]
289pub enum FormKind {
290 // ── Atoms ─────────────────────────────────────────────────────────────────
291 Nil,
292 Bool(bool),
293 Int(i64),
294 BigInt(String),
295 Float(f64), // NaN != NaN per IEEE 754 — acceptable for AST equality
296 BigDecimal(String),
297 Ratio(String),
298 Char(char),
299 Str(String),
300 Regex(String),
301 /// `##Inf` → `INFINITY`, `##-Inf` → `NEG_INFINITY`, `##NaN` → `NAN`
302 Symbolic(f64),
303
304 // ── Identifiers ───────────────────────────────────────────────────────────
305 Symbol(String),
306 Keyword(String),
307 /// `::kw` / `::alias/kw` - the namespace is resolved against the reading
308 /// namespace by the evaluator, not by the reader.
309 AutoKeyword(String),
310 /// A symbol whose namespace is auto-resolved the same way. There is no
311 /// surface syntax for one; the reader produces it for a bare symbol key in
312 /// an auto-resolved namespaced map (`#::{a 1}`, `#::alias{a 1}`).
313 AutoSymbol(String),
314
315 // ── Collections ───────────────────────────────────────────────────────────
316 List(Vec<Form>),
317 Vector(Vec<Form>),
318 /// Flat key/value pairs; length is always even.
319 Map(Vec<Form>),
320 Set(Vec<Form>),
321
322 // ── Wrapping reader macros ────────────────────────────────────────────────
323 Quote(Box<Form>),
324 SyntaxQuote(Box<Form>),
325 Unquote(Box<Form>),
326 UnquoteSplice(Box<Form>),
327 Deref(Box<Form>),
328 /// `#'symbol`
329 Var(Box<Form>),
330 /// `^meta-form annotated-form` — raw meta form kept as-is; evaluator
331 /// expands shorthand (`:kw` → `{:kw true}`, `Sym` → `{:tag Sym}`).
332 Meta(Box<Form>, Box<Form>),
333
334 // ── Dispatch forms ────────────────────────────────────────────────────────
335 /// `#(…)` anonymous function literal
336 AnonFn(Vec<Form>),
337 /// `#tag form` tagged literal
338 TaggedLiteral(String, Box<Form>),
339
340 // ── Reader conditionals ───────────────────────────────────────────────────
341 /// All branches are kept; the evaluator filters by `:rust`.
342 /// `clauses` is flat: `[keyword, form, keyword, form, …]`.
343 ReaderCond {
344 splicing: bool,
345 clauses: Vec<Form>,
346 },
347}
348
349fn vec_heap_size(forms: &[Form]) -> usize {
350 mem::size_of_val(forms) + forms.iter().map(|f| f.heap_size()).sum::<usize>()
351}