1use std::collections::HashMap;
6
7use crate::intern::InternedStr;
8use crate::source::SourceLocation;
9use crate::token::{Comment, Token, TokenKind};
10
11#[derive(Debug, Clone, PartialEq)]
13pub enum MacroKind {
14 Object,
16 Function {
18 params: Vec<InternedStr>,
19 is_variadic: bool,
20 },
21}
22
23#[derive(Debug, Clone)]
25pub struct MacroDef {
26 pub name: InternedStr,
28 pub kind: MacroKind,
30 pub body: Vec<Token>,
32 pub def_loc: SourceLocation,
34 pub leading_comments: Vec<Comment>,
36 pub is_builtin: bool,
38 pub is_target: bool,
40 pub has_token_pasting: bool,
42}
43
44impl MacroDef {
45 pub fn object(
47 name: InternedStr,
48 body: Vec<Token>,
49 def_loc: SourceLocation,
50 ) -> Self {
51 let has_token_pasting = body.iter()
52 .any(|t| matches!(t.kind, TokenKind::HashHash));
53 Self {
54 name,
55 kind: MacroKind::Object,
56 body,
57 def_loc,
58 leading_comments: Vec::new(),
59 is_builtin: false,
60 is_target: false,
61 has_token_pasting,
62 }
63 }
64
65 pub fn function(
67 name: InternedStr,
68 params: Vec<InternedStr>,
69 is_variadic: bool,
70 body: Vec<Token>,
71 def_loc: SourceLocation,
72 ) -> Self {
73 let has_token_pasting = body.iter()
74 .any(|t| matches!(t.kind, TokenKind::HashHash));
75 Self {
76 name,
77 kind: MacroKind::Function { params, is_variadic },
78 body,
79 def_loc,
80 leading_comments: Vec::new(),
81 is_builtin: false,
82 is_target: false,
83 has_token_pasting,
84 }
85 }
86
87 pub fn with_target(mut self, is_target: bool) -> Self {
89 self.is_target = is_target;
90 self
91 }
92
93 pub fn with_comments(mut self, comments: Vec<Comment>) -> Self {
95 self.leading_comments = comments;
96 self
97 }
98
99 pub fn as_builtin(mut self) -> Self {
101 self.is_builtin = true;
102 self
103 }
104
105 pub fn is_function(&self) -> bool {
107 matches!(self.kind, MacroKind::Function { .. })
108 }
109
110 pub fn param_count(&self) -> usize {
112 match &self.kind {
113 MacroKind::Object => 0,
114 MacroKind::Function { params, .. } => params.len(),
115 }
116 }
117
118 pub fn is_variadic(&self) -> bool {
120 matches!(self.kind, MacroKind::Function { is_variadic: true, .. })
121 }
122}
123
124#[derive(Debug, Default)]
126pub struct MacroTable {
127 macros: HashMap<InternedStr, MacroDef>,
128}
129
130impl MacroTable {
131 pub fn new() -> Self {
133 Self {
134 macros: HashMap::new(),
135 }
136 }
137
138 pub fn define(&mut self, def: MacroDef, interner: &crate::intern::StringInterner) -> Option<MacroDef> {
141 if let Some(existing) = self.macros.get(&def.name) {
143 if existing.is_builtin {
144 let name_str = interner.get(def.name);
145 if name_str.starts_with("__") {
146 return None;
147 }
148 }
149 }
150 self.macros.insert(def.name, def)
151 }
152
153 pub fn undefine(&mut self, name: InternedStr) -> Option<MacroDef> {
155 self.macros.remove(&name)
156 }
157
158 pub fn get(&self, name: InternedStr) -> Option<&MacroDef> {
160 self.macros.get(&name)
161 }
162
163 pub fn is_defined(&self, name: InternedStr) -> bool {
165 self.macros.contains_key(&name)
166 }
167
168 pub fn iter(&self) -> impl Iterator<Item = (&InternedStr, &MacroDef)> {
170 self.macros.iter()
171 }
172
173 pub fn iter_target_macros(&self) -> impl Iterator<Item = &MacroDef> {
175 self.macros.values().filter(|def| def.is_target)
176 }
177
178 pub fn len(&self) -> usize {
180 self.macros.len()
181 }
182
183 pub fn is_empty(&self) -> bool {
185 self.macros.is_empty()
186 }
187
188 pub fn user_defined(&self) -> impl Iterator<Item = (&InternedStr, &MacroDef)> {
190 self.macros.iter().filter(|(_, def)| !def.is_builtin)
191 }
192
193 pub fn dump_filtered(&self, filter: &str, interner: &crate::intern::StringInterner) {
198 let mut names: Vec<_> = self.macros.keys().collect();
199 names.sort_by_key(|id| interner.get(**id));
200
201 for &name in &names {
202 let name_str = interner.get(*name);
203 if !filter.is_empty() && !name_str.contains(filter) {
204 continue;
205 }
206
207 if let Some(def) = self.macros.get(name) {
208 let kind_str = match &def.kind {
209 MacroKind::Object => "object".to_string(),
210 MacroKind::Function { params, is_variadic } => {
211 let params_str: Vec<_> = params.iter()
212 .map(|p| interner.get(*p).to_string())
213 .collect();
214 if *is_variadic {
215 format!("function({}...)", params_str.join(", "))
216 } else {
217 format!("function({})", params_str.join(", "))
218 }
219 }
220 };
221
222 let body_str: String = def.body.iter()
223 .map(|t| t.kind.format(interner))
224 .collect::<Vec<_>>()
225 .join(" ");
226
227 let flags = format!(
228 "{}{}",
229 if def.is_target { "T" } else { "" },
230 if def.is_builtin { "B" } else { "" },
231 );
232
233 println!("#define {} [{}] {} = {}",
234 name_str,
235 kind_str,
236 if flags.is_empty() { "".to_string() } else { format!("({})", flags) },
237 body_str
238 );
239 }
240 }
241 }
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247 use crate::intern::StringInterner;
248 use crate::source::FileId;
249
250 #[test]
251 fn test_object_macro() {
252 let mut interner = StringInterner::new();
253 let name = interner.intern("FOO");
254 let loc = SourceLocation::new(FileId::default(), 1, 1);
255
256 let def = MacroDef::object(name, vec![], loc);
257 assert!(!def.is_function());
258 assert_eq!(def.param_count(), 0);
259 assert!(!def.is_variadic());
260 }
261
262 #[test]
263 fn test_function_macro() {
264 let mut interner = StringInterner::new();
265 let name = interner.intern("MAX");
266 let a = interner.intern("a");
267 let b = interner.intern("b");
268 let loc = SourceLocation::new(FileId::default(), 1, 1);
269
270 let def = MacroDef::function(name, vec![a, b], false, vec![], loc);
271 assert!(def.is_function());
272 assert_eq!(def.param_count(), 2);
273 assert!(!def.is_variadic());
274 }
275
276 #[test]
277 fn test_variadic_macro() {
278 let mut interner = StringInterner::new();
279 let name = interner.intern("PRINTF");
280 let fmt = interner.intern("fmt");
281 let loc = SourceLocation::new(FileId::default(), 1, 1);
282
283 let def = MacroDef::function(name, vec![fmt], true, vec![], loc);
284 assert!(def.is_function());
285 assert!(def.is_variadic());
286 }
287
288 #[test]
289 fn test_macro_table() {
290 let mut interner = StringInterner::new();
291 let mut table = MacroTable::new();
292
293 let foo = interner.intern("FOO");
294 let bar = interner.intern("BAR");
295 let loc = SourceLocation::new(FileId::default(), 1, 1);
296
297 assert!(table.define(MacroDef::object(foo, vec![], loc.clone()), &interner).is_none());
299 assert!(table.define(MacroDef::object(bar, vec![], loc.clone()), &interner).is_none());
300 assert_eq!(table.len(), 2);
301
302 assert!(table.is_defined(foo));
304 assert!(table.get(foo).is_some());
305
306 let old = table.define(MacroDef::object(foo, vec![], loc), &interner);
308 assert!(old.is_some());
309 assert_eq!(table.len(), 2);
310
311 assert!(table.undefine(foo).is_some());
313 assert!(!table.is_defined(foo));
314 assert_eq!(table.len(), 1);
315 }
316}