1use rucc_base::Interner;
16use rucc_lex::{PpTokenKind, TokenFlags};
17
18use crate::include::spelling;
19use crate::macros::{Builtin, MacroDef, MacroTable};
20use crate::token::Tok;
21
22#[must_use]
27pub fn macros(table: &MacroTable, interner: &Interner) -> String {
28 let mut all = table.sorted();
29 all.sort_by_key(|m| interner.resolve(m.name));
30 let mut out = String::new();
31 for def in all {
32 define(&mut out, def, interner);
33 out.push('\n');
34 }
35 out
36}
37
38fn define(out: &mut String, def: &MacroDef, interner: &Interner) {
40 out.push_str("#define ");
41 out.push_str(interner.resolve(def.name));
42 if def.function_like {
43 out.push('(');
44 for (at, param) in def.params.iter().enumerate() {
45 if at > 0 {
46 out.push_str(", ");
47 }
48 out.push_str(interner.resolve(*param));
49 }
50 if let Some(rest) = def.variadic {
51 if !def.params.is_empty() {
52 out.push_str(", ");
53 }
54 let name = interner.resolve(rest);
59 if name != "__VA_ARGS__" {
60 out.push_str(name);
61 }
62 out.push_str("...");
63 }
64 out.push(')');
65 }
66 if let Some(builtin) = def.builtin {
67 out.push(' ');
71 out.push_str(match builtin {
72 Builtin::File => "__FILE__",
73 Builtin::FileName => "__FILE_NAME__",
74 Builtin::BaseFile => "__BASE_FILE__",
75 Builtin::Line => "__LINE__",
76 Builtin::IncludeLevel => "__INCLUDE_LEVEL__",
77 Builtin::Counter => "__COUNTER__",
78 });
79 return;
80 }
81 for (at, token) in def.body.iter().enumerate() {
82 if at == 0 || token.flags.has(TokenFlags::LEADING_SPACE) {
86 out.push(' ');
87 }
88 out.push_str(text(*token, interner));
89 }
90}
91
92fn text(token: rucc_lex::PpToken, interner: &Interner) -> &str {
94 match token.kind {
95 PpTokenKind::Eof => "",
98 _ => spelling(Tok::new(token), interner),
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use rucc_diag::Span;
105
106 use super::*;
107 use crate::macros::parse_define;
108
109 fn table(lines: &[&str]) -> (MacroTable, Interner) {
111 let mut interner = Interner::new();
112 let mut table = MacroTable::new();
113 for line in lines {
114 let mut lexer = rucc_lex::Lexer::new(line.as_bytes(), 0, rucc_lex::Options::new());
115 let mut tokens = Vec::new();
116 loop {
117 let token = lexer.next_token(&mut interner);
118 if token.is_eof() {
119 break;
120 }
121 tokens.push(token);
122 }
123 let (def, _) = parse_define(&tokens, &mut interner);
124 table.define(def.expect("the test wrote a valid define"), &interner);
125 }
126 (table, interner)
127 }
128
129 fn dump(lines: &[&str]) -> String {
130 let (table, interner) = table(lines);
131 macros(&table, &interner)
132 }
133
134 #[test]
135 fn an_object_like_macro_comes_back_the_way_it_went_in() {
136 assert_eq!(dump(&["N 2"]), "#define N 2\n");
137 assert_eq!(dump(&["EMPTY"]), "#define EMPTY\n");
138 }
139
140 #[test]
141 fn the_output_is_sorted_by_name_because_the_point_of_it_is_being_diffed() {
142 assert_eq!(dump(&["Z 1", "A 2", "M 3"]), "#define A 2\n#define M 3\n#define Z 1\n");
145 }
146
147 #[test]
148 fn a_function_like_macro_keeps_its_parameters_and_the_two_variadic_spellings_apart() {
149 assert_eq!(dump(&["ADD(a, b) a + b"]), "#define ADD(a, b) a + b\n");
150 assert_eq!(dump(&["F() 1"]), "#define F() 1\n");
151 assert_eq!(dump(&["V(...) __VA_ARGS__"]), "#define V(...) __VA_ARGS__\n");
152 assert_eq!(dump(&["W(a, ...) __VA_ARGS__"]), "#define W(a, ...) __VA_ARGS__\n");
153 assert_eq!(dump(&["G(rest...) rest"]), "#define G(rest...) rest\n");
156 }
157
158 #[test]
159 fn the_spacing_inside_a_body_is_the_spacing_that_was_written() {
160 assert_eq!(dump(&["A 1+2"]), "#define A 1+2\n");
163 assert_eq!(dump(&["B 1 + 2"]), "#define B 1 + 2\n");
164 assert_eq!(dump(&["C (x)"]), "#define C (x)\n");
165 }
166
167 #[test]
168 fn a_builtin_has_no_body_and_says_its_own_name() {
169 let mut interner = Interner::new();
170 let mut table = MacroTable::new();
171 for (name, builtin) in Builtin::ALL {
172 table.define_builtin(interner.intern(name), builtin, Span::new(0, 1));
173 }
174 let text = macros(&table, &interner);
175 assert!(text.contains("#define __FILE__ __FILE__\n"), "{text}");
176 assert!(text.contains("#define __COUNTER__ __COUNTER__\n"), "{text}");
177 assert_eq!(text.lines().count(), Builtin::ALL.len());
178 }
179}