1use rucc_ast::AsmQuals;
49use rucc_base::Interner;
50use rucc_types::{TypeKind, Types, spell};
51
52use crate::asm::{AsmId, AsmOperandList};
53use crate::decl::{DeclId, DeclKind, Definition, Linkage, StorageDuration};
54use crate::expr::{Category, Expr, ExprId, ExprKind};
55use crate::stmt::{CaseId, Stmt, StmtId};
56use crate::tast::{Base, Const, LabelId, Tast};
57
58#[must_use]
60pub fn print(tast: &Tast, types: &Types, names: &Interner) -> String {
61 let mut printer = Printer::new(tast, types, names);
62 printer.unit();
63 printer.finish()
64}
65
66#[derive(Debug)]
71pub struct Printer<'a> {
72 tast: &'a Tast,
73 types: &'a Types,
74 names: &'a Interner,
75 out: String,
76 depth: usize,
77}
78
79impl<'a> Printer<'a> {
80 #[must_use]
82 pub fn new(tast: &'a Tast, types: &'a Types, names: &'a Interner) -> Printer<'a> {
83 Printer { tast, types, names, out: String::new(), depth: 0 }
84 }
85
86 #[must_use]
88 pub fn finish(self) -> String {
89 self.out
90 }
91
92 pub fn unit(&mut self) {
94 for &id in self.tast.top_level() {
95 self.decl(id);
96 }
97 }
98
99 pub fn decl(&mut self, id: DeclId) {
101 let node = &self.tast[id];
102 let mut head = format!("decl #{}", id.index());
103 if let Some(name) = node.name {
104 head.push(' ');
105 head.push_str(self.names.resolve(name));
106 }
107 head.push_str(" : ");
108 head.push_str(&spell(self.types, self.names, node.ty));
109 head.push_str(match node.kind {
110 DeclKind::Object => " object",
111 DeclKind::Function => " function",
112 });
113 head.push_str(match node.linkage {
114 Linkage::None => "",
115 Linkage::Internal => " internal",
116 Linkage::External => " external",
117 });
118 if node.kind == DeclKind::Object {
119 head.push_str(match node.duration {
120 StorageDuration::Static => " static",
121 StorageDuration::Thread => " thread",
122 StorageDuration::Automatic => " automatic",
123 });
124 }
125 head.push_str(match node.state {
126 Definition::Declared => " declared",
127 Definition::Tentative => " tentative",
128 Definition::Defined => " defined",
129 });
130 if node.constant {
131 head.push_str(" constexpr");
132 }
133 if let Some(align) = node.alignment {
134 head.push_str(&format!(" alignas {align}"));
135 }
136 self.line(&head);
137
138 if let Some(list) = node.init {
142 self.depth += 1;
143 self.line("init");
144 self.depth += 1;
145 let entries = self.tast[list].to_vec();
148 for entry in entries {
149 let mut at = format!("+{}", entry.offset);
150 if entry.is_bit_field() {
151 at.push_str(&format!(" bit {} width {}", entry.bit_offset, entry.bit_width));
152 }
153 self.line(&at);
154 self.depth += 1;
155 self.expr(entry.value);
156 self.depth -= 1;
157 }
158 self.depth -= 2;
159 }
160 let params = self.tast[id].params;
163 if !params.is_empty() {
164 self.depth += 1;
165 self.line("params");
166 self.depth += 1;
167 let params = self.tast[params].to_vec();
168 for param in params {
169 self.decl(param);
170 }
171 self.depth -= 2;
172 }
173 if let Some(body) = self.tast[id].body {
174 self.depth += 1;
175 self.line("body");
176 self.depth += 1;
177 self.stmt(body);
178 self.depth -= 2;
179 }
180 }
181
182 pub fn stmt(&mut self, id: StmtId) {
184 match self.tast[id] {
185 Stmt::Error => self.line("error"),
186 Stmt::Empty => self.line("empty"),
187 Stmt::Expr(value) => {
188 self.line("expr");
189 self.under(|p| p.expr(value));
190 }
191 Stmt::Block(body) => {
192 self.line("block");
193 self.depth += 1;
194 let body = self.tast[body].to_vec();
195 for stmt in body {
196 self.stmt(stmt);
197 }
198 self.depth -= 1;
199 }
200 Stmt::Decls(decls) => {
201 self.line("decls");
202 self.depth += 1;
203 let decls = self.tast[decls].to_vec();
204 for decl in decls {
205 self.decl(decl);
206 }
207 self.depth -= 1;
208 }
209 Stmt::If { cond, then, otherwise } => {
210 self.line("if");
211 self.depth += 1;
212 self.group("cond", |p| p.expr(cond));
213 self.group("then", |p| p.stmt(then));
214 if let Some(otherwise) = otherwise {
215 self.group("else", |p| p.stmt(otherwise));
216 }
217 self.depth -= 1;
218 }
219 Stmt::While { cond, body } => {
220 self.line("while");
221 self.depth += 1;
222 self.group("cond", |p| p.expr(cond));
223 self.group("body", |p| p.stmt(body));
224 self.depth -= 1;
225 }
226 Stmt::DoWhile { body, cond } => {
227 self.line("do-while");
228 self.depth += 1;
229 self.group("body", |p| p.stmt(body));
230 self.group("cond", |p| p.expr(cond));
231 self.depth -= 1;
232 }
233 Stmt::For { init, cond, step, body } => {
234 self.line("for");
235 self.depth += 1;
236 if let Some(init) = init {
237 self.group("init", |p| p.stmt(init));
238 }
239 if let Some(cond) = cond {
240 self.group("cond", |p| p.expr(cond));
241 }
242 if let Some(step) = step {
243 self.group("step", |p| p.expr(step));
244 }
245 self.group("body", |p| p.stmt(body));
246 self.depth -= 1;
247 }
248 Stmt::Switch { cond, body, cases, default } => {
249 self.line("switch");
250 self.depth += 1;
251 self.group("cond", |p| p.expr(cond));
252 self.line("cases");
253 self.depth += 1;
254 for index in cases.iter() {
255 self.case(index);
256 }
257 if default.is_some() {
258 self.line("default");
259 }
260 self.depth -= 1;
261 self.group("body", |p| p.stmt(body));
262 self.depth -= 1;
263 }
264 Stmt::Case { case, body } => {
267 self.line(&format!("case #{}", case.index()));
268 self.under(|p| p.stmt(body));
269 }
270 Stmt::Default { body } => {
271 self.line("default");
272 self.under(|p| p.stmt(body));
273 }
274 Stmt::Label { label, body } => {
275 let head = self.label(label);
276 self.line(&format!("label {head}"));
277 self.under(|p| p.stmt(body));
278 }
279 Stmt::Goto(label) => {
280 let target = self.label(label);
281 self.line(&format!("goto {target}"));
282 }
283 Stmt::IndirectGoto(target) => {
284 self.line("indirect-goto");
285 self.under(|p| p.expr(target));
286 }
287 Stmt::Asm(asm) => self.asm(asm),
288 Stmt::Break => self.line("break"),
289 Stmt::Continue => self.line("continue"),
290 Stmt::Return(None) => self.line("return"),
291 Stmt::Return(Some(value)) => {
292 self.line("return");
293 self.under(|p| p.expr(value));
294 }
295 }
296 }
297
298 fn asm(&mut self, id: AsmId) {
305 let node = self.tast[id];
306 let mut head = String::from("asm");
307 for (qual, name) in [
308 (AsmQuals::VOLATILE, " volatile"),
309 (AsmQuals::INLINE, " inline"),
310 (AsmQuals::GOTO, " goto"),
311 ] {
312 if node.quals.has(qual) {
313 head.push_str(name);
314 }
315 }
316 self.line(&head);
317 self.depth += 1;
318 self.line(&format!("template {}", self.tast[node.template].spell()));
319 self.asm_operands(node.outputs, "output");
320 self.asm_operands(node.inputs, "input");
321 for index in 0..self.tast[node.clobbers].len() {
322 let clobber = self.tast[node.clobbers][index];
323 self.line(&format!("clobber {}", self.tast[clobber].spell()));
324 }
325 for index in 0..self.tast[node.labels].len() {
326 let label = self.tast[node.labels][index];
327 let head = self.label(label);
328 self.line(&format!("label {head}"));
329 }
330 self.depth -= 1;
331 }
332
333 fn asm_operands(&mut self, list: AsmOperandList, what: &str) {
335 for index in 0..self.tast[list].len() {
336 let operand = self.tast[list][index];
337 let name = match operand.name {
338 Some(name) => format!(" [{}]", self.names.resolve(name)),
339 None => String::new(),
340 };
341 let memory = if operand.memory { " memory" } else { "" };
342 let constraint = self.tast[operand.constraint].spell();
343 self.line(&format!("{what}{name} {constraint}{memory}"));
344 self.under(|p| p.expr(operand.value));
345 }
346 }
347
348 pub fn expr(&mut self, id: ExprId) {
350 let node = self.tast[id];
351 let head = self.head(node);
352 let ty = spell(self.types, self.names, node.ty);
353 let category = match node.category {
354 Category::Rvalue => "",
355 Category::Lvalue => " lvalue",
356 Category::Bitfield => " bit-field",
357 Category::Function => " function",
358 };
359 self.line(&format!("{head} : {ty}{category}"));
360 self.depth += 1;
361 self.operands(node.kind);
362 self.depth -= 1;
363 }
364
365 fn head(&self, node: Expr) -> String {
367 match node.kind {
368 ExprKind::Error => "error".to_owned(),
369 ExprKind::Const(value) => match self.tast[value] {
370 Const::Int(value) => format!("const {value}"),
374 Const::Float(value) => format!("const {}", value.to_hex()),
375 Const::Address(address) => {
376 let base = match address.base {
377 Base::Decl(decl) => format!("decl #{}", decl.index()),
378 Base::Str(id) => format!("string {}", self.tast[id].spell()),
379 };
380 format!("const address {base} + {}", address.offset)
381 }
382 },
383 ExprKind::Str(value) => format!("string {}", self.tast[value].spell()),
384 ExprKind::Decl(decl) => {
385 let mut head = format!("decl #{}", decl.index());
386 if let Some(name) = self.tast[decl].name {
387 head.push(' ');
388 head.push_str(self.names.resolve(name));
389 }
390 head
391 }
392 ExprKind::Member { base, field } => {
393 let mut head = format!("member #{field}");
394 if let Some(name) = self.field_name(base, field) {
395 head.push(' ');
396 head.push_str(name);
397 }
398 head
399 }
400 ExprKind::Subscript { .. } => "subscript".to_owned(),
401 ExprKind::Call { .. } => "call".to_owned(),
402 ExprKind::Unary { op, .. } if op.is_postfix() => {
403 format!("unary post {}", op.spelling())
404 }
405 ExprKind::Unary { op, .. } => format!("unary {}", op.spelling()),
406 ExprKind::Binary { op, .. } => format!("binary {}", op.spelling()),
407 ExprKind::Assign { op, computation, .. } => {
410 let mut head = match op {
411 None => "assign =".to_owned(),
412 Some(op) => format!("assign {}=", op.spelling()),
413 };
414 if computation != node.ty {
415 let ty = spell(self.types, self.names, computation);
416 head.push_str(&format!(" in {ty}"));
417 }
418 head
419 }
420 ExprKind::Cond { .. } => "cond".to_owned(),
421 ExprKind::Comma { .. } => "comma".to_owned(),
422 ExprKind::Cast(_) => "cast".to_owned(),
423 ExprKind::Convert { kind, .. } => format!("convert {}", kind.as_str()),
424 ExprKind::CompoundLiteral(decl) => format!("compound-literal #{}", decl.index()),
425 ExprKind::StmtExpr(_) => "stmt-expr".to_owned(),
426 ExprKind::LabelAddr(label) => format!("label-addr {}", self.label(label)),
427 ExprKind::VaArg { .. } => "va-arg".to_owned(),
428 ExprKind::VaStart { .. } => "va-start".to_owned(),
429 ExprKind::VaEnd { .. } => "va-end".to_owned(),
430 ExprKind::VaCopy { .. } => "va-copy".to_owned(),
431 ExprKind::Classify { op, .. } => format!("classify {}", op.as_str()),
432 ExprKind::Sign { op, .. } => format!("sign {}", op.as_str()),
433 }
434 }
435
436 fn operands(&mut self, kind: ExprKind) {
438 match kind {
439 ExprKind::Error
440 | ExprKind::Const(_)
441 | ExprKind::Str(_)
442 | ExprKind::Decl(_)
443 | ExprKind::LabelAddr(_) => {}
444 ExprKind::CompoundLiteral(decl) => self.decl(decl),
447 ExprKind::StmtExpr(body) => self.stmt(body),
448 ExprKind::Member { base, .. }
449 | ExprKind::Cast(base)
450 | ExprKind::VaArg { list: base }
451 | ExprKind::VaStart { list: base }
452 | ExprKind::VaEnd { list: base }
453 | ExprKind::Convert { operand: base, .. }
454 | ExprKind::Unary { operand: base, .. } => self.expr(base),
455 ExprKind::Subscript { base: lhs, index: rhs }
456 | ExprKind::Binary { lhs, rhs, .. }
457 | ExprKind::Assign { lhs, rhs, .. }
458 | ExprKind::VaCopy { dst: lhs, src: rhs }
459 | ExprKind::Comma { lhs, rhs } => {
460 self.expr(lhs);
461 self.expr(rhs);
462 }
463 ExprKind::Call { callee, args } => {
464 self.expr(callee);
465 let args = self.tast[args].to_vec();
466 for arg in args {
467 self.expr(arg);
468 }
469 }
470 ExprKind::Cond { cond, then, otherwise } => {
471 self.expr(cond);
472 self.expr(then);
473 self.expr(otherwise);
474 }
475 ExprKind::Classify { lhs, rhs, .. } | ExprKind::Sign { lhs, rhs, .. } => {
476 self.expr(lhs);
477 if let Some(rhs) = rhs {
478 self.expr(rhs);
479 }
480 }
481 }
482 }
483
484 fn case(&mut self, id: CaseId) {
486 let case = self.tast[id];
487 let head = if case.low == case.high {
488 format!("case #{} {}", id.index(), case.low)
489 } else {
490 format!("case #{} {} ... {}", id.index(), case.low, case.high)
491 };
492 self.line(&head);
493 }
494
495 fn label(&self, id: LabelId) -> String {
497 format!("#{} {}", id.index(), self.names.resolve(self.tast[id].name))
498 }
499
500 fn field_name(&self, base: ExprId, field: u32) -> Option<&'a str> {
506 let ty = self.types.canonical(self.tast[base].ty);
507 let TypeKind::Record(record) = self.types.kind(ty) else { return None };
508 let field = self.types.record_info(record).fields.get(field as usize)?;
509 Some(self.names.resolve(field.name?))
510 }
511
512 fn group(&mut self, name: &str, write: impl FnOnce(&mut Printer<'a>)) {
514 self.line(name);
515 self.under(write);
516 }
517
518 fn under(&mut self, write: impl FnOnce(&mut Printer<'a>)) {
520 self.depth += 1;
521 write(self);
522 self.depth -= 1;
523 }
524
525 fn line(&mut self, text: &str) {
527 for _ in 0..self.depth {
528 self.out.push_str(" ");
529 }
530 self.out.push_str(text);
531 self.out.push('\n');
532 }
533}
534
535#[cfg(test)]
536mod tests {
537 use rucc_ast::{BinaryOp, UnaryOp};
538 use rucc_diag::Span;
539 use rucc_types::{ArrayLen, IntKind};
540
541 use super::*;
542 use crate::decl::{Decl, DeclList, InitEntry};
543 use crate::expr::{Conversion, Expr};
544 use crate::stmt::Case;
545 use crate::tast::Label;
546
547 struct Fixture {
548 tast: Tast,
549 types: Types,
550 names: Interner,
551 }
552
553 impl Fixture {
554 fn new() -> Fixture {
555 Fixture { tast: Tast::new(), types: Types::new(), names: Interner::new() }
556 }
557
558 fn int(&self) -> rucc_types::TypeId {
559 self.types.int(IntKind::Int)
560 }
561
562 fn value(&mut self, kind: ExprKind, ty: rucc_types::TypeId) -> ExprId {
564 self.tast.expr(Expr::new(kind, ty, Category::Rvalue), Span::DUMMY)
565 }
566
567 fn constant(&mut self, value: i128, ty: rucc_types::TypeId) -> ExprId {
568 let id = self.tast.add_const(Const::Int(value));
569 self.value(ExprKind::Const(id), ty)
570 }
571
572 fn text(&self, write: impl FnOnce(&mut Printer<'_>)) -> String {
573 let mut printer = Printer::new(&self.tast, &self.types, &self.names);
574 write(&mut printer);
575 printer.finish()
576 }
577 }
578
579 #[test]
580 fn an_expression_carries_its_type_on_every_line() {
581 let mut f = Fixture::new();
582 let int = f.int();
583 let left = f.constant(1, int);
584 let right = f.constant(2, int);
585 let sum = f.value(ExprKind::Binary { op: BinaryOp::Add, lhs: left, rhs: right }, int);
586
587 assert_eq!(f.text(|p| p.expr(sum)), "binary + : int\n const 1 : int\n const 2 : int\n");
588 }
589
590 #[test]
591 fn a_conversion_is_what_the_dump_is_for() {
592 let mut f = Fixture::new();
593 let (char_type, long) = (f.types.int(IntKind::Char), f.types.int(IntKind::Long));
594 let object = f.tast.decl(object_decl(char_type), Span::DUMMY);
595 let name = f
596 .tast
597 .expr(Expr::new(ExprKind::Decl(object), char_type, Category::Lvalue), Span::DUMMY);
598 let read =
599 f.value(ExprKind::Convert { kind: Conversion::Lvalue, operand: name }, char_type);
600 let widened =
601 f.value(ExprKind::Convert { kind: Conversion::Arithmetic, operand: read }, long);
602
603 assert_eq!(
606 f.text(|p| p.expr(widened)),
607 "convert arithmetic : long\n convert lvalue : char\n decl #0 : char lvalue\n"
608 );
609 }
610
611 #[test]
612 fn a_category_is_written_and_an_rvalue_is_the_silent_one() {
613 let mut f = Fixture::new();
614 let int = f.int();
615 let object = f.tast.decl(object_decl(int), Span::DUMMY);
616 let name =
617 f.tast.expr(Expr::new(ExprKind::Decl(object), int, Category::Lvalue), Span::DUMMY);
618 let bits =
619 f.tast.expr(Expr::new(ExprKind::Decl(object), int, Category::Bitfield), Span::DUMMY);
620
621 assert_eq!(f.text(|p| p.expr(name)), "decl #0 : int lvalue\n");
622 assert_eq!(f.text(|p| p.expr(bits)), "decl #0 : int bit-field\n");
623 }
624
625 #[test]
626 fn a_postfix_operator_is_not_printed_as_the_prefix_one() {
627 let mut f = Fixture::new();
628 let int = f.int();
629 let one = f.constant(1, int);
630 let post = f.value(ExprKind::Unary { op: UnaryOp::PostInc, operand: one }, int);
631 let pre = f.value(ExprKind::Unary { op: UnaryOp::PreInc, operand: one }, int);
632
633 assert!(f.text(|p| p.expr(post)).starts_with("unary post ++"));
634 assert!(f.text(|p| p.expr(pre)).starts_with("unary ++ :"));
635 }
636
637 #[test]
638 fn a_compound_assignment_keeps_its_operator() {
639 let mut f = Fixture::new();
640 let int = f.int();
641 let one = f.constant(1, int);
642 let plain =
643 f.value(ExprKind::Assign { op: None, computation: int, lhs: one, rhs: one }, int);
644 let shl =
645 ExprKind::Assign { op: Some(BinaryOp::Shl), computation: int, lhs: one, rhs: one };
646 let compound = f.value(shl, int);
647
648 assert!(f.text(|p| p.expr(plain)).starts_with("assign = :"));
649 assert!(f.text(|p| p.expr(compound)).starts_with("assign <<= :"));
650 }
651
652 #[test]
653 fn a_case_is_a_reference_into_the_table_and_not_a_second_copy_of_it() {
654 let mut f = Fixture::new();
655 let int = f.int();
656 let cond = f.constant(0, int);
657 let empty = f.tast.stmt(Stmt::Empty, Span::DUMMY);
658 let cases = f.tast.add_cases(&[
659 Case { low: 1, high: 1, body: empty },
660 Case { low: 2, high: 9, body: empty },
661 ]);
662 let first = f.tast.stmt(
663 Stmt::Case { case: cases.iter().next().expect("a case"), body: empty },
664 Span::DUMMY,
665 );
666 let fallback = f.tast.stmt(Stmt::Default { body: empty }, Span::DUMMY);
667 let body = f.tast.add_stmt_refs(&[first, fallback]);
668 let body = f.tast.stmt(Stmt::Block(body), Span::DUMMY);
669 let switch =
670 f.tast.stmt(Stmt::Switch { cond, body, cases, default: Some(empty) }, Span::DUMMY);
671
672 assert_eq!(
673 f.text(|p| p.stmt(switch)),
674 "\
675switch
676 cond
677 const 0 : int
678 cases
679 case #0 1
680 case #1 2 ... 9
681 default
682 body
683 block
684 case #0
685 empty
686 default
687 empty
688"
689 );
690 }
691
692 #[test]
693 fn a_label_and_the_goto_that_reaches_it_carry_the_same_number() {
694 let mut f = Fixture::new();
695 let name = f.names.intern("done");
696 let label = f.tast.add_label(Label { name, stmt: None });
697 let empty = f.tast.stmt(Stmt::Empty, Span::DUMMY);
698 let target = f.tast.stmt(Stmt::Label { label, body: empty }, Span::DUMMY);
699 let jump = f.tast.stmt(Stmt::Goto(label), Span::DUMMY);
700 f.tast.define_label(label, target);
701
702 assert_eq!(f.text(|p| p.stmt(target)), "label #0 done\n empty\n");
703 assert_eq!(f.text(|p| p.stmt(jump)), "goto #0 done\n");
704 }
705
706 #[test]
707 fn a_declaration_says_what_it_is_and_an_empty_initializer_is_still_one() {
708 let mut f = Fixture::new();
709 let int = f.int();
710 let array = f.types.array(int, ArrayLen::Fixed(2));
711 let mut decl = object_decl(array);
712 decl.name = Some(f.names.intern("a"));
713 decl.linkage = Linkage::Internal;
714 decl.duration = StorageDuration::Static;
715 decl.alignment = Some(16);
716 decl.init = Some(f.tast.add_init_entries(&[]));
717 let id = f.tast.decl(decl, Span::DUMMY);
718
719 assert_eq!(
720 f.text(|p| p.decl(id)),
721 "decl #0 a : int[2] object internal static defined alignas 16\n init\n"
722 );
723 }
724
725 #[test]
726 fn an_initializer_prints_where_each_value_goes() {
727 let mut f = Fixture::new();
728 let int = f.int();
729 let array = f.types.array(int, ArrayLen::Fixed(2));
730 let one = f.constant(1, int);
731 let entries = f.tast.add_init_entries(&[
732 InitEntry::at(0, one),
733 InitEntry { offset: 4, value: one, bit_offset: 3, bit_width: 5 },
734 ]);
735 let mut decl = object_decl(array);
736 decl.init = Some(entries);
737 let id = f.tast.decl(decl, Span::DUMMY);
738
739 assert_eq!(
740 f.text(|p| p.decl(id)),
741 "\
742decl #0 : int[2] object automatic defined
743 init
744 +0
745 const 1 : int
746 +4 bit 3 width 5
747 const 1 : int
748"
749 );
750 }
751
752 #[test]
753 fn a_unit_is_its_declarations_in_order() {
754 let mut f = Fixture::new();
755 let int = f.int();
756 let first = f.tast.decl(object_decl(int), Span::DUMMY);
757 let second = f.tast.decl(object_decl(int), Span::DUMMY);
758 f.tast.add_top_level(first);
759 f.tast.add_top_level(second);
760
761 assert_eq!(
762 print(&f.tast, &f.types, &f.names),
763 "decl #0 : int object automatic defined\ndecl #1 : int object automatic defined\n"
764 );
765 }
766
767 fn object_decl(ty: rucc_types::TypeId) -> Decl {
768 Decl {
769 name: None,
770 ty,
771 kind: DeclKind::Object,
772 linkage: Linkage::None,
773 duration: StorageDuration::Automatic,
774 state: Definition::Defined,
775 alignment: None,
776 constant: false,
777 init: None,
778 params: DeclList::EMPTY,
779 body: None,
780 }
781 }
782}