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
26impl FormKind {
27 /// Heap bytes owned by this node and all children.
28 pub fn heap_size(&self) -> usize {
29 match self {
30 // Inline scalars — no heap.
31 FormKind::Nil
32 | FormKind::Bool(_)
33 | FormKind::Int(_)
34 | FormKind::Float(_)
35 | FormKind::Char(_)
36 | FormKind::Symbolic(_) => 0,
37
38 // String payloads.
39 FormKind::BigInt(s)
40 | FormKind::BigDecimal(s)
41 | FormKind::Ratio(s)
42 | FormKind::Str(s)
43 | FormKind::Regex(s)
44 | FormKind::Symbol(s)
45 | FormKind::Keyword(s)
46 | FormKind::AutoKeyword(s)
47 | FormKind::AutoSymbol(s) => s.capacity(),
48
49 // Vec<Form> — Vec overhead + recursive children.
50 FormKind::List(v)
51 | FormKind::Vector(v)
52 | FormKind::Map(v)
53 | FormKind::Set(v)
54 | FormKind::AnonFn(v) => vec_heap_size(v),
55
56 // Box<Form> — one Form on heap.
57 FormKind::Quote(f)
58 | FormKind::SyntaxQuote(f)
59 | FormKind::Unquote(f)
60 | FormKind::UnquoteSplice(f)
61 | FormKind::Deref(f)
62 | FormKind::Var(f) => mem::size_of::<Form>() + f.heap_size(),
63
64 // Two Box<Form>.
65 FormKind::Meta(a, b) => mem::size_of::<Form>() * 2 + a.heap_size() + b.heap_size(),
66
67 // String + Box<Form>.
68 FormKind::TaggedLiteral(s, f) => s.capacity() + mem::size_of::<Form>() + f.heap_size(),
69
70 FormKind::ReaderCond { clauses, .. } => vec_heap_size(clauses),
71 }
72 }
73}
74
75impl PartialEq for Form {
76 fn eq(&self, other: &Self) -> bool {
77 self.kind == other.kind
78 }
79}
80
81/// The payload of a `Form` node.
82#[derive(Debug, Clone, PartialEq)]
83pub enum FormKind {
84 // ── Atoms ─────────────────────────────────────────────────────────────────
85 Nil,
86 Bool(bool),
87 Int(i64),
88 BigInt(String),
89 Float(f64), // NaN != NaN per IEEE 754 — acceptable for AST equality
90 BigDecimal(String),
91 Ratio(String),
92 Char(char),
93 Str(String),
94 Regex(String),
95 /// `##Inf` → `INFINITY`, `##-Inf` → `NEG_INFINITY`, `##NaN` → `NAN`
96 Symbolic(f64),
97
98 // ── Identifiers ───────────────────────────────────────────────────────────
99 Symbol(String),
100 Keyword(String),
101 /// `::kw` / `::alias/kw` - the namespace is resolved against the reading
102 /// namespace by the evaluator, not by the reader.
103 AutoKeyword(String),
104 /// A symbol whose namespace is auto-resolved the same way. There is no
105 /// surface syntax for one; the reader produces it for a bare symbol key in
106 /// an auto-resolved namespaced map (`#::{a 1}`, `#::alias{a 1}`).
107 AutoSymbol(String),
108
109 // ── Collections ───────────────────────────────────────────────────────────
110 List(Vec<Form>),
111 Vector(Vec<Form>),
112 /// Flat key/value pairs; length is always even.
113 Map(Vec<Form>),
114 Set(Vec<Form>),
115
116 // ── Wrapping reader macros ────────────────────────────────────────────────
117 Quote(Box<Form>),
118 SyntaxQuote(Box<Form>),
119 Unquote(Box<Form>),
120 UnquoteSplice(Box<Form>),
121 Deref(Box<Form>),
122 /// `#'symbol`
123 Var(Box<Form>),
124 /// `^meta-form annotated-form` — raw meta form kept as-is; evaluator
125 /// expands shorthand (`:kw` → `{:kw true}`, `Sym` → `{:tag Sym}`).
126 Meta(Box<Form>, Box<Form>),
127
128 // ── Dispatch forms ────────────────────────────────────────────────────────
129 /// `#(…)` anonymous function literal
130 AnonFn(Vec<Form>),
131 /// `#tag form` tagged literal
132 TaggedLiteral(String, Box<Form>),
133
134 // ── Reader conditionals ───────────────────────────────────────────────────
135 /// All branches are kept; the evaluator filters by `:rust`.
136 /// `clauses` is flat: `[keyword, form, keyword, form, …]`.
137 ReaderCond {
138 splicing: bool,
139 clauses: Vec<Form>,
140 },
141}
142
143fn vec_heap_size(forms: &[Form]) -> usize {
144 mem::size_of_val(forms) + forms.iter().map(|f| f.heap_size()).sum::<usize>()
145}