11//! AST to TACKY IR conversion routines
22
3- use crate :: { ast, tacky} ;
3+ use std:: collections:: HashSet ;
4+
5+ use crate :: { ast, shared_types:: Identifier , tacky} ;
46
57/// Converter object that does the work going from AST to TACKY
6- struct AstToTacky { }
8+ struct AstToTacky {
9+ label_set : HashSet < String > ,
10+ }
711
812type InstructionVec = Vec < tacky:: Instruction > ;
913
1014impl AstToTacky {
15+ fn new ( ) -> AstToTacky {
16+ AstToTacky {
17+ label_set : HashSet :: new ( ) ,
18+ }
19+ }
20+
1121 fn convert_program ( & mut self , prog : & ast:: Program ) -> tacky:: Program {
1222 let function = self . convert_function ( & prog. function ) ;
1323 return tacky:: Program { function } ;
1424 }
1525
16- fn convert_function ( & self , func : & ast:: Function ) -> tacky:: Function {
26+ fn convert_function ( & mut self , func : & ast:: Function ) -> tacky:: Function {
1727 // let mut instructions: InstructionVec = func
1828 // .body
1929 // .iter()
@@ -40,7 +50,7 @@ impl AstToTacky {
4050 function
4151 }
4252
43- fn convert_block_item ( & self , block : & ast:: BlockItem , instructions : & mut InstructionVec ) {
53+ fn convert_block_item ( & mut self , block : & ast:: BlockItem , instructions : & mut InstructionVec ) {
4454 match block {
4555 ast:: BlockItem :: Declaration ( identifier, expr) => {
4656 if let Some ( init) = expr {
@@ -56,7 +66,7 @@ impl AstToTacky {
5666 } ;
5767 }
5868
59- fn convert_stmt ( & self , stmt : & ast:: Stmt , instructions : & mut InstructionVec ) {
69+ fn convert_stmt ( & mut self , stmt : & ast:: Stmt , instructions : & mut InstructionVec ) {
6070 match stmt {
6171 ast:: Stmt :: Return ( expr) => {
6272 let expr = self . convert_expr ( expr, instructions) ;
@@ -74,26 +84,76 @@ impl AstToTacky {
7484 false_stmt,
7585 } => self . convert_if_stmt ( cond, true_stmt, false_stmt. as_ref ( ) , instructions) ,
7686 } ;
77- todo ! ( )
7887 }
7988
80- fn convert_expr ( & self , expr : & ast:: Expr , instructions : & mut InstructionVec ) -> tacky:: Value {
89+ fn convert_expr ( & mut self , expr : & ast:: Expr , instructions : & mut InstructionVec ) -> tacky:: Value {
8190 todo ! ( )
8291 }
8392
8493 fn convert_if_stmt (
85- & self ,
94+ & mut self ,
8695 cond : & ast:: Expr ,
8796 true_stmt : & ast:: Stmt ,
8897 false_stmt : Option < & Box < ast:: Stmt > > ,
8998 instructions : & mut InstructionVec ,
9099 ) {
91- todo ! ( )
100+ let cond_val = self . convert_expr ( cond, instructions) ;
101+ let end_name = "if_end_label" ;
102+ let end_label: tacky:: Instruction = self . make_label ( end_name) ;
103+
104+ if let Some ( false_stmt) = false_stmt {
105+ /*
106+ * With an else branch we emit something like
107+ *
108+ * Condition instructions
109+ * Jump to else if condition is 0
110+ * True branch instructions
111+ * Jump to end
112+ * Else label
113+ * False branch instructions
114+ * End label
115+ */
116+ let else_name = "else_label" ;
117+ let else_label = self . make_label ( else_name) ;
118+ let jump_else = tacky:: Instruction :: JumpIfZero {
119+ cond : cond_val,
120+ target : Identifier :: new ( else_name) ,
121+ } ;
122+ let jump_end = tacky:: Instruction :: Jump ( Identifier :: new ( end_name) ) ;
123+ instructions. push ( jump_else) ;
124+ self . convert_stmt ( true_stmt, instructions) ;
125+ instructions. push ( jump_end) ;
126+ instructions. push ( else_label) ;
127+ self . convert_stmt ( false_stmt, instructions) ;
128+ instructions. push ( end_label) ;
129+ } else {
130+ // Without an else branch we just jump to the end label if the
131+ // condition is false and execute the true branch statements otherwise
132+ let jump_end = tacky:: Instruction :: JumpIfZero {
133+ cond : cond_val,
134+ target : Identifier :: new ( end_name) ,
135+ } ;
136+ instructions. push ( jump_end) ;
137+ self . convert_stmt ( true_stmt, instructions) ;
138+ instructions. push ( end_label) ;
139+ }
140+ }
141+
142+ fn make_label ( & mut self , name : & str ) -> tacky:: Instruction {
143+ let mut counter = 0 ;
144+ let mut label_name: String = name. to_string ( ) ;
145+ while self . label_set . contains ( & label_name) {
146+ label_name = format ! ( "{name}{counter}" ) ;
147+ counter += 1 ;
148+ }
149+ let label = tacky:: Instruction :: Label ( Identifier :: new ( & label_name) ) ;
150+ self . label_set . insert ( label_name) ;
151+ label
92152 }
93153}
94154
95155/// Convert an ast::Program to a tacky::Program
96156pub fn ast_to_tacky ( ast : & ast:: Program ) -> tacky:: Program {
97- let mut converter = AstToTacky { } ;
157+ let mut converter = AstToTacky :: new ( ) ;
98158 return converter. convert_program ( ast) ;
99159}
0 commit comments