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 /// The `^meta` forms attached to this form, outermost first, together with
38 /// the annotated form itself.
39 pub fn peel_meta(&self) -> (Vec<&Form>, &Form) {
40 let mut metas = Vec::new();
41 let mut form = self;
42 while let FormKind::Meta(meta, inner) = &form.kind {
43 metas.push(meta.as_ref());
44 form = inner;
45 }
46 (metas, form)
47 }
48
49 // ── Structural views ──────────────────────────────────────────────────────
50 //
51 // Every accessor below reports the shape of [`Form::unmeta`], so an
52 // annotated form has the same structural shape as the form it annotates.
53
54 /// The symbol name, if this form is a symbol.
55 pub fn as_symbol(&self) -> Option<&str> {
56 match &self.unmeta().kind {
57 FormKind::Symbol(s) => Some(s),
58 _ => None,
59 }
60 }
61
62 /// The keyword name (without the leading `:`), if this form is a keyword.
63 pub fn as_keyword(&self) -> Option<&str> {
64 match &self.unmeta().kind {
65 FormKind::Keyword(k) => Some(k),
66 _ => None,
67 }
68 }
69
70 /// The string contents, if this form is a string literal.
71 pub fn as_string(&self) -> Option<&str> {
72 match &self.unmeta().kind {
73 FormKind::Str(s) => Some(s),
74 _ => None,
75 }
76 }
77
78 /// The elements, if this form is a list.
79 pub fn as_list(&self) -> Option<&[Form]> {
80 match &self.unmeta().kind {
81 FormKind::List(v) => Some(v),
82 _ => None,
83 }
84 }
85
86 /// The elements, if this form is a vector.
87 pub fn as_vector(&self) -> Option<&[Form]> {
88 match &self.unmeta().kind {
89 FormKind::Vector(v) => Some(v),
90 _ => None,
91 }
92 }
93
94 /// The flat key/value pairs, if this form is a map literal.
95 pub fn as_map(&self) -> Option<&[Form]> {
96 match &self.unmeta().kind {
97 FormKind::Map(v) => Some(v),
98 _ => None,
99 }
100 }
101}
102
103impl FormKind {
104 /// Heap bytes owned by this node and all children.
105 pub fn heap_size(&self) -> usize {
106 match self {
107 // Inline scalars — no heap.
108 FormKind::Nil
109 | FormKind::Bool(_)
110 | FormKind::Int(_)
111 | FormKind::Float(_)
112 | FormKind::Char(_)
113 | FormKind::Symbolic(_) => 0,
114
115 // String payloads.
116 FormKind::BigInt(s)
117 | FormKind::BigDecimal(s)
118 | FormKind::Ratio(s)
119 | FormKind::Str(s)
120 | FormKind::Regex(s)
121 | FormKind::Symbol(s)
122 | FormKind::Keyword(s)
123 | FormKind::AutoKeyword(s)
124 | FormKind::AutoSymbol(s) => s.capacity(),
125
126 // Vec<Form> — Vec overhead + recursive children.
127 FormKind::List(v)
128 | FormKind::Vector(v)
129 | FormKind::Map(v)
130 | FormKind::Set(v)
131 | FormKind::AnonFn(v) => vec_heap_size(v),
132
133 // Box<Form> — one Form on heap.
134 FormKind::Quote(f)
135 | FormKind::SyntaxQuote(f)
136 | FormKind::Unquote(f)
137 | FormKind::UnquoteSplice(f)
138 | FormKind::Deref(f)
139 | FormKind::Var(f) => mem::size_of::<Form>() + f.heap_size(),
140
141 // Two Box<Form>.
142 FormKind::Meta(a, b) => mem::size_of::<Form>() * 2 + a.heap_size() + b.heap_size(),
143
144 // String + Box<Form>.
145 FormKind::TaggedLiteral(s, f) => s.capacity() + mem::size_of::<Form>() + f.heap_size(),
146
147 FormKind::ReaderCond { clauses, .. } => vec_heap_size(clauses),
148 }
149 }
150}
151
152impl PartialEq for Form {
153 fn eq(&self, other: &Self) -> bool {
154 self.kind == other.kind
155 }
156}
157
158/// The payload of a `Form` node.
159#[derive(Debug, Clone, PartialEq)]
160pub enum FormKind {
161 // ── Atoms ─────────────────────────────────────────────────────────────────
162 Nil,
163 Bool(bool),
164 Int(i64),
165 BigInt(String),
166 Float(f64), // NaN != NaN per IEEE 754 — acceptable for AST equality
167 BigDecimal(String),
168 Ratio(String),
169 Char(char),
170 Str(String),
171 Regex(String),
172 /// `##Inf` → `INFINITY`, `##-Inf` → `NEG_INFINITY`, `##NaN` → `NAN`
173 Symbolic(f64),
174
175 // ── Identifiers ───────────────────────────────────────────────────────────
176 Symbol(String),
177 Keyword(String),
178 /// `::kw` / `::alias/kw` - the namespace is resolved against the reading
179 /// namespace by the evaluator, not by the reader.
180 AutoKeyword(String),
181 /// A symbol whose namespace is auto-resolved the same way. There is no
182 /// surface syntax for one; the reader produces it for a bare symbol key in
183 /// an auto-resolved namespaced map (`#::{a 1}`, `#::alias{a 1}`).
184 AutoSymbol(String),
185
186 // ── Collections ───────────────────────────────────────────────────────────
187 List(Vec<Form>),
188 Vector(Vec<Form>),
189 /// Flat key/value pairs; length is always even.
190 Map(Vec<Form>),
191 Set(Vec<Form>),
192
193 // ── Wrapping reader macros ────────────────────────────────────────────────
194 Quote(Box<Form>),
195 SyntaxQuote(Box<Form>),
196 Unquote(Box<Form>),
197 UnquoteSplice(Box<Form>),
198 Deref(Box<Form>),
199 /// `#'symbol`
200 Var(Box<Form>),
201 /// `^meta-form annotated-form` — raw meta form kept as-is; evaluator
202 /// expands shorthand (`:kw` → `{:kw true}`, `Sym` → `{:tag Sym}`).
203 Meta(Box<Form>, Box<Form>),
204
205 // ── Dispatch forms ────────────────────────────────────────────────────────
206 /// `#(…)` anonymous function literal
207 AnonFn(Vec<Form>),
208 /// `#tag form` tagged literal
209 TaggedLiteral(String, Box<Form>),
210
211 // ── Reader conditionals ───────────────────────────────────────────────────
212 /// All branches are kept; the evaluator filters by `:rust`.
213 /// `clauses` is flat: `[keyword, form, keyword, form, …]`.
214 ReaderCond {
215 splicing: bool,
216 clauses: Vec<Form>,
217 },
218}
219
220fn vec_heap_size(forms: &[Form]) -> usize {
221 mem::size_of_val(forms) + forms.iter().map(|f| f.heap_size()).sum::<usize>()
222}