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