1use std::collections::HashMap;
11
12use rucc_base::{Interner, Symbol};
13use rucc_diag::{Diagnostic, Span};
14use rucc_lex::{PpToken, PpTokenKind, Punct, TokenFlags};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum Builtin {
26 File,
28 FileName,
30 BaseFile,
32 Line,
34 IncludeLevel,
36 Counter,
38}
39
40impl Builtin {
41 pub const ALL: [(&'static str, Builtin); 6] = [
46 ("__FILE__", Builtin::File),
47 ("__FILE_NAME__", Builtin::FileName),
48 ("__BASE_FILE__", Builtin::BaseFile),
49 ("__LINE__", Builtin::Line),
50 ("__INCLUDE_LEVEL__", Builtin::IncludeLevel),
51 ("__COUNTER__", Builtin::Counter),
52 ];
53}
54
55#[derive(Debug, Clone)]
57pub struct MacroDef {
58 pub name: Symbol,
60 pub function_like: bool,
63 pub params: Vec<Symbol>,
65 pub variadic: Option<Symbol>,
68 pub body: Vec<PpToken>,
70 pub span: Span,
72 pub builtin: Option<Builtin>,
74}
75
76impl MacroDef {
77 #[inline]
79 pub fn is_variadic(&self) -> bool {
80 self.variadic.is_some()
81 }
82
83 #[inline]
85 pub fn arity(&self) -> usize {
86 self.params.len()
87 }
88
89 pub fn param_index(&self, name: Symbol) -> Option<usize> {
92 if let Some(at) = self.params.iter().position(|&p| p == name) {
93 return Some(at);
94 }
95 if self.variadic == Some(name) { Some(self.params.len()) } else { None }
96 }
97
98 #[inline]
100 pub fn is_variadic_param(&self, name: Symbol) -> bool {
101 self.variadic == Some(name)
102 }
103
104 pub fn same_definition_as(&self, other: &MacroDef) -> bool {
110 if self.function_like != other.function_like
111 || self.params != other.params
112 || self.variadic != other.variadic
113 || self.builtin != other.builtin
114 || self.body.len() != other.body.len()
115 {
116 return false;
117 }
118 self.body.iter().zip(&other.body).enumerate().all(|(at, (a, b))| {
119 a.kind == b.kind
120 && a.value == b.value
121 && (at == 0
124 || a.flags.has(TokenFlags::LEADING_SPACE)
125 == b.flags.has(TokenFlags::LEADING_SPACE))
126 })
127 }
128}
129
130#[derive(Debug, Default)]
132pub struct MacroTable {
133 by_name: HashMap<Symbol, MacroDef>,
134 saved: HashMap<Symbol, Vec<Option<MacroDef>>>,
141}
142
143impl MacroTable {
144 pub fn new() -> MacroTable {
146 MacroTable::default()
147 }
148
149 #[inline]
151 pub fn lookup(&self, name: Symbol) -> Option<&MacroDef> {
152 self.by_name.get(&name)
153 }
154
155 #[inline]
157 pub fn is_defined(&self, name: Symbol) -> bool {
158 self.by_name.contains_key(&name)
159 }
160
161 pub fn len(&self) -> usize {
163 self.by_name.len()
164 }
165
166 pub fn is_empty(&self) -> bool {
168 self.by_name.is_empty()
169 }
170
171 pub fn define(&mut self, def: MacroDef, interner: &Interner) -> Option<Diagnostic> {
178 let complaint =
179 self.by_name.get(&def.name).filter(|old| !old.same_definition_as(&def)).map(|old| {
180 Diagnostic::warning(format!("`{}` redefined", interner.resolve(def.name)), def.span)
181 .with_code("W0301")
182 .note("previous definition was here", old.span)
183 });
184 self.by_name.insert(def.name, def);
185 complaint
186 }
187
188 pub fn define_builtin(&mut self, name: Symbol, builtin: Builtin, span: Span) {
193 let def = MacroDef {
194 name,
195 function_like: false,
196 params: Vec::new(),
197 variadic: None,
198 body: Vec::new(),
199 span,
200 builtin: Some(builtin),
201 };
202 self.by_name.insert(name, def);
203 }
204
205 pub fn undef(&mut self, name: Symbol) -> Option<MacroDef> {
207 self.by_name.remove(&name)
208 }
209
210 pub fn push_macro(&mut self, name: Symbol) {
217 let current = self.by_name.get(&name).cloned();
218 self.saved.entry(name).or_default().push(current);
219 }
220
221 pub fn pop_macro(&mut self, name: Symbol) {
227 let Some(stack) = self.saved.get_mut(&name) else {
228 return;
229 };
230 let Some(was) = stack.pop() else {
231 return;
232 };
233 if stack.is_empty() {
234 self.saved.remove(&name);
235 }
236 match was {
237 Some(def) => {
238 self.by_name.insert(name, def);
239 }
240 None => {
241 self.by_name.remove(&name);
242 }
243 }
244 }
245
246 pub fn sorted(&self) -> Vec<&MacroDef> {
251 let mut all: Vec<&MacroDef> = self.by_name.values().collect();
252 all.sort_by_key(|m| m.name);
253 all
254 }
255}
256
257pub fn parse_define(
268 tokens: &[PpToken],
269 interner: &mut Interner,
270) -> (Option<MacroDef>, Vec<Diagnostic>) {
271 let mut diagnostics = Vec::new();
272 let Some(&first) = tokens.first() else {
273 return (None, vec![Diagnostic::error("no macro name given in `#define`", Span::DUMMY)]);
274 };
275 if first.kind != PpTokenKind::Ident {
276 diagnostics.push(
277 Diagnostic::error("macro name must be an identifier", first.span).with_code("E0300"),
278 );
279 return (None, diagnostics);
280 }
281 let name = first.value.expect("the lexer interns every identifier");
282 let span = first.span;
283 let rest = &tokens[1..];
284
285 let opens_params = rest.first().is_some_and(|t| {
289 t.punct() == Some(Punct::LParen) && !t.flags.has(TokenFlags::LEADING_SPACE)
290 });
291
292 let (function_like, params, variadic, body) = if opens_params {
293 match parse_params(&rest[1..], interner, &mut diagnostics) {
294 Some((params, variadic, consumed)) => (true, params, variadic, &rest[1 + consumed..]),
295 None => return (None, diagnostics),
296 }
297 } else {
298 (false, Vec::new(), None, rest)
299 };
300
301 let def = MacroDef {
302 name,
303 function_like,
304 params,
305 variadic,
306 body: body.to_vec(),
307 span,
308 builtin: None,
309 };
310 check_body(&def, interner, &mut diagnostics);
311 (Some(def), diagnostics)
312}
313
314fn parse_params(
319 tokens: &[PpToken],
320 interner: &mut Interner,
321 diagnostics: &mut Vec<Diagnostic>,
322) -> Option<(Vec<Symbol>, Option<Symbol>, usize)> {
323 let va_args = interner.intern("__VA_ARGS__");
324 let mut params: Vec<Symbol> = Vec::new();
325 let mut variadic = None;
326 let mut at = 0;
327
328 if tokens.first().is_some_and(|t| t.punct() == Some(Punct::RParen)) {
329 return Some((params, None, 1));
330 }
331
332 loop {
333 let Some(&tok) = tokens.get(at) else {
334 diagnostics.push(
335 Diagnostic::error("missing `)` in macro parameter list", last_span(tokens))
336 .with_code("E0301"),
337 );
338 return None;
339 };
340 at += 1;
341
342 if tok.punct() == Some(Punct::Ellipsis) {
343 variadic = Some(va_args);
344 } else if tok.kind == PpTokenKind::Ident {
345 let sym = tok.value.expect("the lexer interns every identifier");
346 if tokens.get(at).is_some_and(|t| t.punct() == Some(Punct::Ellipsis)) {
348 at += 1;
349 variadic = Some(sym);
350 } else if sym == va_args {
351 diagnostics.push(
352 Diagnostic::error("`__VA_ARGS__` cannot be used as a parameter name", tok.span)
353 .with_code("E0302"),
354 );
355 return None;
356 } else if params.contains(&sym) {
357 diagnostics.push(
358 Diagnostic::error(
359 format!("duplicate macro parameter `{}`", interner.resolve(sym)),
360 tok.span,
361 )
362 .with_code("E0303"),
363 );
364 return None;
365 } else {
366 params.push(sym);
367 }
368 } else {
369 diagnostics.push(
370 Diagnostic::error("macro parameter must be an identifier", tok.span)
371 .with_code("E0301"),
372 );
373 return None;
374 }
375
376 match tokens.get(at).and_then(|t| t.punct()) {
377 Some(Punct::RParen) => return Some((params, variadic, at + 1)),
378 Some(Punct::Comma) if variadic.is_none() => at += 1,
379 Some(Punct::Comma) => {
380 diagnostics.push(
381 Diagnostic::error("`...` must be the last macro parameter", tokens[at].span)
382 .with_code("E0301"),
383 );
384 return None;
385 }
386 _ => {
387 diagnostics.push(
388 Diagnostic::error("missing `)` in macro parameter list", last_span(tokens))
389 .with_code("E0301"),
390 );
391 return None;
392 }
393 }
394 }
395}
396
397fn check_body(def: &MacroDef, interner: &mut Interner, diagnostics: &mut Vec<Diagnostic>) {
399 let va_opt = interner.intern("__VA_OPT__");
400 let va_args = interner.intern("__VA_ARGS__");
401
402 if let Some(first) = def.body.first().filter(|t| t.punct() == Some(Punct::HashHash)) {
403 diagnostics.push(
404 Diagnostic::error("`##` cannot appear at the start of a replacement list", first.span)
405 .with_code("E0304"),
406 );
407 }
408 let trailing =
410 def.body.last().filter(|t| def.body.len() > 1 && t.punct() == Some(Punct::HashHash));
411 if let Some(last) = trailing {
412 diagnostics.push(
413 Diagnostic::error("`##` cannot appear at the end of a replacement list", last.span)
414 .with_code("E0304"),
415 );
416 }
417
418 for (at, tok) in def.body.iter().enumerate() {
419 if def.function_like && tok.punct() == Some(Punct::Hash) {
422 let operand = def.body.get(at + 1);
423 let names_param = operand.is_some_and(|t| {
424 t.value.is_some_and(|v| def.param_index(v).is_some())
425 || (def.is_variadic() && t.value == Some(va_opt))
426 });
427 if !names_param {
428 diagnostics.push(
429 Diagnostic::error("`#` must be followed by a macro parameter", tok.span)
430 .with_code("E0305"),
431 );
432 }
433 }
434
435 if tok.kind != PpTokenKind::Ident {
436 continue;
437 }
438 if tok.value == Some(va_args) && !def.is_variadic() {
439 diagnostics.push(
440 Diagnostic::error("`__VA_ARGS__` can only appear in a variadic macro", tok.span)
441 .with_code("E0306"),
442 );
443 }
444 if tok.value == Some(va_opt) {
445 if !def.is_variadic() {
446 diagnostics.push(
447 Diagnostic::error("`__VA_OPT__` can only appear in a variadic macro", tok.span)
448 .with_code("E0306"),
449 );
450 } else if !def.body.get(at + 1).is_some_and(|t| t.punct() == Some(Punct::LParen)) {
451 diagnostics.push(
452 Diagnostic::error("`__VA_OPT__` must be followed by `(`", tok.span)
453 .with_code("E0307"),
454 );
455 }
456 }
457 }
458}
459
460fn last_span(tokens: &[PpToken]) -> Span {
462 tokens.last().map_or(Span::DUMMY, |t| t.span)
463}
464
465#[cfg(test)]
466mod tests {
467 use rucc_diag::Severity;
468 use rucc_lex::{Options, tokenize};
469
470 use super::*;
471
472 fn define(src: &str, interner: &mut Interner) -> (Option<MacroDef>, Vec<Diagnostic>) {
473 let (tokens, lex_errors) = tokenize(src.as_bytes(), 0, Options::new(), interner);
474 assert!(lex_errors.is_empty(), "the test input should lex cleanly");
475 let body: Vec<PpToken> =
476 tokens.into_iter().filter(|t| t.kind != PpTokenKind::Eof).collect();
477 parse_define(&body, interner)
478 }
479
480 #[test]
481 fn an_object_like_macro_has_no_parameter_list() {
482 let mut i = Interner::new();
483 let (def, errors) = define("PI 3.14", &mut i);
484 let def = def.expect("should parse");
485 assert!(errors.is_empty());
486 assert!(!def.function_like);
487 assert_eq!(def.body.len(), 1);
488 }
489
490 #[test]
491 fn a_space_before_the_parenthesis_makes_it_object_like() {
492 let mut i = Interner::new();
493 let (def, _) = define("A (x)", &mut i);
494 let def = def.expect("should parse");
495 assert!(!def.function_like, "`#define A (x)` defines A as the token sequence `(x)`");
496 assert_eq!(def.body.len(), 3);
497 }
498
499 #[test]
500 fn a_function_like_macro_with_no_parameters_is_not_object_like() {
501 let mut i = Interner::new();
502 let (def, _) = define("A() 1", &mut i);
503 let def = def.expect("should parse");
504 assert!(def.function_like);
505 assert_eq!(def.arity(), 0);
506 }
507
508 #[test]
509 fn the_standard_ellipsis_names_the_variadic_va_args() {
510 let mut i = Interner::new();
511 let (def, errors) = define("F(a, ...) a", &mut i);
512 let def = def.expect("should parse");
513 assert!(errors.is_empty());
514 assert_eq!(def.arity(), 1);
515 assert_eq!(def.variadic, Some(i.intern("__VA_ARGS__")));
516 }
517
518 #[test]
519 fn the_gnu_form_names_the_variadic_itself() {
520 let mut i = Interner::new();
521 let (def, errors) = define("F(a, rest...) a", &mut i);
522 let def = def.expect("should parse");
523 assert!(errors.is_empty());
524 assert_eq!(def.variadic, Some(i.intern("rest")));
525 assert_eq!(def.param_index(i.intern("rest")), Some(1));
526 }
527
528 #[test]
529 fn a_duplicate_parameter_is_rejected() {
530 let mut i = Interner::new();
531 let (def, errors) = define("F(a, a) a", &mut i);
532 assert!(def.is_none());
533 assert_eq!(errors[0].code, Some("E0303"));
534 }
535
536 #[test]
537 fn paste_cannot_start_or_end_a_replacement_list() {
538 let mut i = Interner::new();
539 let (_, start) = define("A ## b", &mut i);
540 assert_eq!(start[0].code, Some("E0304"));
541 let (_, end) = define("A b ##", &mut i);
542 assert_eq!(end[0].code, Some("E0304"));
543 }
544
545 #[test]
546 fn stringify_must_name_a_parameter_but_only_in_a_function_like_macro() {
547 let mut i = Interner::new();
548 let (_, bad) = define("F(a) # b", &mut i);
549 assert_eq!(bad[0].code, Some("E0305"));
550 let (_, fine) = define("HASH #", &mut i);
551 assert!(fine.is_empty(), "a bare `#` in an object-like macro is just a token");
552 }
553
554 #[test]
555 fn va_args_outside_a_variadic_macro_is_rejected() {
556 let mut i = Interner::new();
557 let (_, errors) = define("F(a) __VA_ARGS__", &mut i);
558 assert_eq!(errors[0].code, Some("E0306"));
559 }
560
561 #[test]
562 fn va_opt_must_be_called() {
563 let mut i = Interner::new();
564 let (_, errors) = define("F(...) __VA_OPT__", &mut i);
565 assert_eq!(errors[0].code, Some("E0307"));
566 }
567
568 #[test]
569 fn redefining_a_macro_to_the_same_thing_is_silent() {
570 let mut i = Interner::new();
571 let mut table = MacroTable::new();
572 let (first, _) = define("A 1 + 2", &mut i);
573 let (again, _) = define("A 1 + 2", &mut i);
574 assert!(table.define(first.expect("should parse"), &i).is_none());
575 assert!(table.define(again.expect("should parse"), &i).is_none());
576 assert_eq!(table.len(), 1);
577 }
578
579 #[test]
580 fn redefining_a_macro_differently_warns_and_takes_the_new_one() {
581 let mut i = Interner::new();
582 let mut table = MacroTable::new();
583 let (first, _) = define("A 1", &mut i);
584 let (again, _) = define("A 2", &mut i);
585 table.define(first.expect("should parse"), &i);
586 let warning = table.define(again.expect("should parse"), &i).expect("should warn");
587 assert_eq!(warning.severity, Severity::Warning);
588 assert_eq!(warning.code, Some("W0301"));
589 assert_eq!(table.lookup(i.intern("A")).expect("still defined").body.len(), 1);
590 }
591
592 #[test]
593 fn whitespace_inside_the_replacement_list_is_part_of_the_definition() {
594 let mut i = Interner::new();
595 let (a, _) = define("A x+y", &mut i);
596 let (b, _) = define("A x + y", &mut i);
597 assert!(
598 !a.expect("should parse").same_definition_as(&b.expect("should parse")),
599 "the standard compares spelling including whitespace separation"
600 );
601 }
602
603 #[test]
604 fn a_builtin_macro_is_defined_like_any_other() {
605 let mut i = Interner::new();
606 let mut table = MacroTable::new();
607 let name = i.intern("__LINE__");
608 table.define_builtin(name, Builtin::Line, Span::new(0, 0));
609 assert!(table.is_defined(name), "`#ifdef __LINE__` is true");
610 assert_eq!(table.lookup(name).and_then(|d| d.builtin), Some(Builtin::Line));
611 assert!(table.undef(name).is_some(), "`#undef __LINE__` is allowed, as it is in GCC");
612 }
613
614 #[test]
615 fn redefining_a_builtin_is_a_redefinition() {
616 let mut i = Interner::new();
617 let mut table = MacroTable::new();
618 let name = i.intern("__FILE__");
619 table.define_builtin(name, Builtin::File, Span::new(0, 0));
620 let (def, _) = define("__FILE__", &mut i);
623 let warning = table.define(def.expect("should parse"), &i).expect("should warn");
624 assert_eq!(warning.code, Some("W0301"));
625 assert!(table.lookup(name).expect("still defined").builtin.is_none());
626 }
627
628 #[test]
629 fn undefining_something_that_was_never_defined_is_fine() {
630 let mut i = Interner::new();
631 let mut table = MacroTable::new();
632 assert!(table.undef(i.intern("nothing")).is_none());
633 }
634
635 #[test]
636 fn a_push_and_a_pop_leave_the_table_where_they_found_it() {
637 let mut i = Interner::new();
638 let mut table = MacroTable::new();
639 let name = i.intern("X");
640 let (first, _) = define("X 1", &mut i);
641 table.define(first.expect("should parse"), &i);
642 table.push_macro(name);
643 table.undef(name);
644 let (second, _) = define("X 2", &mut i);
645 table.define(second.expect("should parse"), &i);
646 assert_eq!(table.lookup(name).expect("defined").body.len(), 1);
647 table.pop_macro(name);
648 assert!(table.is_defined(name), "the pop brought the first definition back");
649 assert_eq!(table.len(), 1, "and did not leave the second one behind as well");
650 }
651
652 #[test]
653 fn pushing_a_name_that_is_not_defined_pushes_the_absence() {
654 let mut i = Interner::new();
655 let mut table = MacroTable::new();
656 let name = i.intern("X");
657 table.push_macro(name);
658 let (def, _) = define("X 1", &mut i);
659 table.define(def.expect("should parse"), &i);
660 table.pop_macro(name);
661 assert!(!table.is_defined(name), "the state restored is the one that was saved");
662 }
663
664 #[test]
665 fn a_pop_with_nothing_pushed_changes_nothing() {
666 let mut i = Interner::new();
667 let mut table = MacroTable::new();
668 let name = i.intern("X");
669 let (def, _) = define("X 1", &mut i);
670 table.define(def.expect("should parse"), &i);
671 table.pop_macro(name);
672 table.pop_macro(i.intern("never_seen"));
673 assert!(table.is_defined(name));
674 }
675}