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 ExprKind::Unreachable => "unreachable".to_owned(),
434 }
435 }
436
437 fn operands(&mut self, kind: ExprKind) {
439 match kind {
440 ExprKind::Error
441 | ExprKind::Const(_)
442 | ExprKind::Str(_)
443 | ExprKind::Decl(_)
444 | ExprKind::LabelAddr(_)
445 | ExprKind::Unreachable => {}
446 ExprKind::CompoundLiteral(decl) => self.decl(decl),
449 ExprKind::StmtExpr(body) => self.stmt(body),
450 ExprKind::Member { base, .. }
451 | ExprKind::Cast(base)
452 | ExprKind::VaArg { list: base }
453 | ExprKind::VaStart { list: base }
454 | ExprKind::VaEnd { list: base }
455 | ExprKind::Convert { operand: base, .. }
456 | ExprKind::Unary { operand: base, .. } => self.expr(base),
457 ExprKind::Subscript { base: lhs, index: rhs }
458 | ExprKind::Binary { lhs, rhs, .. }
459 | ExprKind::Assign { lhs, rhs, .. }
460 | ExprKind::VaCopy { dst: lhs, src: rhs }
461 | ExprKind::Comma { lhs, rhs } => {
462 self.expr(lhs);
463 self.expr(rhs);
464 }
465 ExprKind::Call { callee, args } => {
466 self.expr(callee);
467 let args = self.tast[args].to_vec();
468 for arg in args {
469 self.expr(arg);
470 }
471 }
472 ExprKind::Cond { cond, then, otherwise } => {
473 self.expr(cond);
474 self.expr(then);
475 self.expr(otherwise);
476 }
477 ExprKind::Classify { lhs, rhs, .. } | ExprKind::Sign { lhs, rhs, .. } => {
478 self.expr(lhs);
479 if let Some(rhs) = rhs {
480 self.expr(rhs);
481 }
482 }
483 }
484 }
485
486 fn case(&mut self, id: CaseId) {
488 let case = self.tast[id];
489 let head = if case.low == case.high {
490 format!("case #{} {}", id.index(), case.low)
491 } else {
492 format!("case #{} {} ... {}", id.index(), case.low, case.high)
493 };
494 self.line(&head);
495 }
496
497 fn label(&self, id: LabelId) -> String {
499 format!("#{} {}", id.index(), self.names.resolve(self.tast[id].name))
500 }
501
502 fn field_name(&self, base: ExprId, field: u32) -> Option<&'a str> {
508 let ty = self.types.canonical(self.tast[base].ty);
509 let TypeKind::Record(record) = self.types.kind(ty) else { return None };
510 let field = self.types.record_info(record).fields.get(field as usize)?;
511 Some(self.names.resolve(field.name?))
512 }
513
514 fn group(&mut self, name: &str, write: impl FnOnce(&mut Printer<'a>)) {
516 self.line(name);
517 self.under(write);
518 }
519
520 fn under(&mut self, write: impl FnOnce(&mut Printer<'a>)) {
522 self.depth += 1;
523 write(self);
524 self.depth -= 1;
525 }
526
527 fn line(&mut self, text: &str) {
529 for _ in 0..self.depth {
530 self.out.push_str(" ");
531 }
532 self.out.push_str(text);
533 self.out.push('\n');
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 use rucc_ast::{BinaryOp, UnaryOp};
540 use rucc_diag::Span;
541 use rucc_types::{ArrayLen, IntKind};
542
543 use super::*;
544 use crate::decl::{Decl, DeclList, InitEntry};
545 use crate::expr::{Conversion, Expr};
546 use crate::stmt::Case;
547 use crate::tast::Label;
548
549 struct Fixture {
550 tast: Tast,
551 types: Types,
552 names: Interner,
553 }
554
555 impl Fixture {
556 fn new() -> Fixture {
557 Fixture { tast: Tast::new(), types: Types::new(), names: Interner::new() }
558 }
559
560 fn int(&self) -> rucc_types::TypeId {
561 self.types.int(IntKind::Int)
562 }
563
564 fn value(&mut self, kind: ExprKind, ty: rucc_types::TypeId) -> ExprId {
566 self.tast.expr(Expr::new(kind, ty, Category::Rvalue), Span::DUMMY)
567 }
568
569 fn constant(&mut self, value: i128, ty: rucc_types::TypeId) -> ExprId {
570 let id = self.tast.add_const(Const::Int(value));
571 self.value(ExprKind::Const(id), ty)
572 }
573
574 fn text(&self, write: impl FnOnce(&mut Printer<'_>)) -> String {
575 let mut printer = Printer::new(&self.tast, &self.types, &self.names);
576 write(&mut printer);
577 printer.finish()
578 }
579 }
580
581 #[test]
582 fn an_expression_carries_its_type_on_every_line() {
583 let mut f = Fixture::new();
584 let int = f.int();
585 let left = f.constant(1, int);
586 let right = f.constant(2, int);
587 let sum = f.value(ExprKind::Binary { op: BinaryOp::Add, lhs: left, rhs: right }, int);
588
589 assert_eq!(f.text(|p| p.expr(sum)), "binary + : int\n const 1 : int\n const 2 : int\n");
590 }
591
592 #[test]
593 fn a_conversion_is_what_the_dump_is_for() {
594 let mut f = Fixture::new();
595 let (char_type, long) = (f.types.int(IntKind::Char), f.types.int(IntKind::Long));
596 let object = f.tast.decl(object_decl(char_type), Span::DUMMY);
597 let name = f
598 .tast
599 .expr(Expr::new(ExprKind::Decl(object), char_type, Category::Lvalue), Span::DUMMY);
600 let read =
601 f.value(ExprKind::Convert { kind: Conversion::Lvalue, operand: name }, char_type);
602 let widened =
603 f.value(ExprKind::Convert { kind: Conversion::Arithmetic, operand: read }, long);
604
605 assert_eq!(
608 f.text(|p| p.expr(widened)),
609 "convert arithmetic : long\n convert lvalue : char\n decl #0 : char lvalue\n"
610 );
611 }
612
613 #[test]
614 fn a_category_is_written_and_an_rvalue_is_the_silent_one() {
615 let mut f = Fixture::new();
616 let int = f.int();
617 let object = f.tast.decl(object_decl(int), Span::DUMMY);
618 let name =
619 f.tast.expr(Expr::new(ExprKind::Decl(object), int, Category::Lvalue), Span::DUMMY);
620 let bits =
621 f.tast.expr(Expr::new(ExprKind::Decl(object), int, Category::Bitfield), Span::DUMMY);
622
623 assert_eq!(f.text(|p| p.expr(name)), "decl #0 : int lvalue\n");
624 assert_eq!(f.text(|p| p.expr(bits)), "decl #0 : int bit-field\n");
625 }
626
627 #[test]
628 fn a_postfix_operator_is_not_printed_as_the_prefix_one() {
629 let mut f = Fixture::new();
630 let int = f.int();
631 let one = f.constant(1, int);
632 let post = f.value(ExprKind::Unary { op: UnaryOp::PostInc, operand: one }, int);
633 let pre = f.value(ExprKind::Unary { op: UnaryOp::PreInc, operand: one }, int);
634
635 assert!(f.text(|p| p.expr(post)).starts_with("unary post ++"));
636 assert!(f.text(|p| p.expr(pre)).starts_with("unary ++ :"));
637 }
638
639 #[test]
640 fn a_compound_assignment_keeps_its_operator() {
641 let mut f = Fixture::new();
642 let int = f.int();
643 let one = f.constant(1, int);
644 let plain =
645 f.value(ExprKind::Assign { op: None, computation: int, lhs: one, rhs: one }, int);
646 let shl =
647 ExprKind::Assign { op: Some(BinaryOp::Shl), computation: int, lhs: one, rhs: one };
648 let compound = f.value(shl, int);
649
650 assert!(f.text(|p| p.expr(plain)).starts_with("assign = :"));
651 assert!(f.text(|p| p.expr(compound)).starts_with("assign <<= :"));
652 }
653
654 #[test]
655 fn a_case_is_a_reference_into_the_table_and_not_a_second_copy_of_it() {
656 let mut f = Fixture::new();
657 let int = f.int();
658 let cond = f.constant(0, int);
659 let empty = f.tast.stmt(Stmt::Empty, Span::DUMMY);
660 let cases = f.tast.add_cases(&[
661 Case { low: 1, high: 1, body: empty },
662 Case { low: 2, high: 9, body: empty },
663 ]);
664 let first = f.tast.stmt(
665 Stmt::Case { case: cases.iter().next().expect("a case"), body: empty },
666 Span::DUMMY,
667 );
668 let fallback = f.tast.stmt(Stmt::Default { body: empty }, Span::DUMMY);
669 let body = f.tast.add_stmt_refs(&[first, fallback]);
670 let body = f.tast.stmt(Stmt::Block(body), Span::DUMMY);
671 let switch =
672 f.tast.stmt(Stmt::Switch { cond, body, cases, default: Some(empty) }, Span::DUMMY);
673
674 assert_eq!(
675 f.text(|p| p.stmt(switch)),
676 "\
677switch
678 cond
679 const 0 : int
680 cases
681 case #0 1
682 case #1 2 ... 9
683 default
684 body
685 block
686 case #0
687 empty
688 default
689 empty
690"
691 );
692 }
693
694 #[test]
695 fn a_label_and_the_goto_that_reaches_it_carry_the_same_number() {
696 let mut f = Fixture::new();
697 let name = f.names.intern("done");
698 let label = f.tast.add_label(Label { name, stmt: None });
699 let empty = f.tast.stmt(Stmt::Empty, Span::DUMMY);
700 let target = f.tast.stmt(Stmt::Label { label, body: empty }, Span::DUMMY);
701 let jump = f.tast.stmt(Stmt::Goto(label), Span::DUMMY);
702 f.tast.define_label(label, target);
703
704 assert_eq!(f.text(|p| p.stmt(target)), "label #0 done\n empty\n");
705 assert_eq!(f.text(|p| p.stmt(jump)), "goto #0 done\n");
706 }
707
708 #[test]
709 fn a_declaration_says_what_it_is_and_an_empty_initializer_is_still_one() {
710 let mut f = Fixture::new();
711 let int = f.int();
712 let array = f.types.array(int, ArrayLen::Fixed(2));
713 let mut decl = object_decl(array);
714 decl.name = Some(f.names.intern("a"));
715 decl.linkage = Linkage::Internal;
716 decl.duration = StorageDuration::Static;
717 decl.alignment = Some(16);
718 decl.init = Some(f.tast.add_init_entries(&[]));
719 let id = f.tast.decl(decl, Span::DUMMY);
720
721 assert_eq!(
722 f.text(|p| p.decl(id)),
723 "decl #0 a : int[2] object internal static defined alignas 16\n init\n"
724 );
725 }
726
727 #[test]
728 fn an_initializer_prints_where_each_value_goes() {
729 let mut f = Fixture::new();
730 let int = f.int();
731 let array = f.types.array(int, ArrayLen::Fixed(2));
732 let one = f.constant(1, int);
733 let entries = f.tast.add_init_entries(&[
734 InitEntry::at(0, one),
735 InitEntry { offset: 4, value: one, bit_offset: 3, bit_width: 5 },
736 ]);
737 let mut decl = object_decl(array);
738 decl.init = Some(entries);
739 let id = f.tast.decl(decl, Span::DUMMY);
740
741 assert_eq!(
742 f.text(|p| p.decl(id)),
743 "\
744decl #0 : int[2] object automatic defined
745 init
746 +0
747 const 1 : int
748 +4 bit 3 width 5
749 const 1 : int
750"
751 );
752 }
753
754 #[test]
755 fn a_unit_is_its_declarations_in_order() {
756 let mut f = Fixture::new();
757 let int = f.int();
758 let first = f.tast.decl(object_decl(int), Span::DUMMY);
759 let second = f.tast.decl(object_decl(int), Span::DUMMY);
760 f.tast.add_top_level(first);
761 f.tast.add_top_level(second);
762
763 assert_eq!(
764 print(&f.tast, &f.types, &f.names),
765 "decl #0 : int object automatic defined\ndecl #1 : int object automatic defined\n"
766 );
767 }
768
769 fn object_decl(ty: rucc_types::TypeId) -> Decl {
770 Decl {
771 name: None,
772 ty,
773 kind: DeclKind::Object,
774 linkage: Linkage::None,
775 duration: StorageDuration::Automatic,
776 state: Definition::Defined,
777 alignment: None,
778 constant: false,
779 retained: false,
780 init: None,
781 params: DeclList::EMPTY,
782 body: None,
783 }
784 }
785}