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