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