use std::fmt::Display; use crate::ast::types::Type as AstType; use crate::ast::types::BinaryOp as AstBinaryOp; use crate::ir::err::IRError; pub enum IRInstr { Declare(Variable), DefineFunc(Function, Vec, Vec), Entry, Binary(Variable, Variable, BinaryOp, Variable), Exit(Option), FuncCall(Function, Vec, Option), // Goto, // Label, Move(Variable, MoveRValue), } impl Display for IRInstr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { IRInstr::Entry => write!(f, "entry"), IRInstr::Binary(dest, left, op, right) => write!(f, "{} = {} {} {}", dest, op, left, right), IRInstr::Exit(v) => if let Some(v) = v { write!(f, "exit {}", v) } else { write!(f, "exit") }, IRInstr::FuncCall(func, args, dest) => { if let Some(dest) = dest { write!(f, "{} = call {}", dest, func.to_call_string(args)) } else { write!(f, "call {}", func.to_call_string(args)) } } IRInstr::Move(dest, src) => write!(f, "{} = {}", dest, src), IRInstr::Declare(var) => write!(f, "declare {} {}", var.data_type, var), IRInstr::DefineFunc(func, args, body) => { let body_str = body.iter().map(|instr| format!(" {}", instr)).collect::>().join("\n"); write!(f, "define {} {{\n{}\n}}", func.to_decl_string(args), body_str) } } } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum IRType { I32, I1, Void, } impl Display for IRType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { IRType::I32 => write!(f, "i32"), IRType::Void => write!(f, "void"), IRType::I1 => write!(f, "i1"), } } } impl From for IRType { fn from(ast_type: AstType) -> Self { match ast_type { AstType::Int => IRType::I32, AstType::Void => IRType::Void, } } } pub enum MoveRValue { Var(Variable), ConstInt(i32), } impl Display for MoveRValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { MoveRValue::Var(v) => write!(f, "{}", v), MoveRValue::ConstInt(i) => write!(f, "{}", i), } } } #[derive(Clone, Copy)] pub enum VariableType { Global, ParamTemp, Local, Temp, } #[derive(Clone, Copy)] pub struct Variable { // pub name: String, pub index: usize, pub var_type: VariableType, pub data_type: IRType, } impl Display for Variable { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let prefix = match self.var_type { VariableType::Global => "@", VariableType::Local => "%l", VariableType::Temp => "%t", VariableType::ParamTemp => "%t", }; write!(f, "{}{}", prefix, self.index) } } #[derive(Debug, Clone)] pub struct Function { pub name: String, pub return_type: IRType, pub parameter_types: Vec, // pub parameters: Vec, } impl Function { pub fn to_call_string(&self, args: &Vec) -> String { assert!(args.len() == self.parameter_types.len()); let args_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param)| format!("{} {}", param, arg)).collect::>().join(", "); format!("{} @{}({})", self.return_type, self.name, args_str) } pub fn to_decl_string(&self, args: &Vec) -> String { let params_str = args.iter().zip(self.parameter_types.iter()).map(|(arg, param_type)| format!("{} {}", param_type, arg)).collect::>().join(", "); format!("{} @{}({})", self.return_type, self.name, params_str) } } pub enum BinaryOp { Add, Sub, Mul, Div, Mod, Le, Lt, Gt, Ge, Ne, Eq, } impl Display for BinaryOp { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let op_str = match self { BinaryOp::Add => "add", BinaryOp::Sub => "sub", BinaryOp::Mul => "mul", BinaryOp::Div => "div", BinaryOp::Mod => "mod", BinaryOp::Le => "le", BinaryOp::Lt => "lt", BinaryOp::Gt => "gt", BinaryOp::Ge => "ge", BinaryOp::Ne => "ne", BinaryOp::Eq => "eq", }; write!(f, "{}", op_str) } } impl From for BinaryOp { fn from(ast_op: AstBinaryOp) -> Self { match ast_op { AstBinaryOp::Add => BinaryOp::Add, AstBinaryOp::Sub => BinaryOp::Sub, AstBinaryOp::Mul => BinaryOp::Mul, AstBinaryOp::Div => BinaryOp::Div, AstBinaryOp::Mod => BinaryOp::Mod, AstBinaryOp::Equal => BinaryOp::Eq, AstBinaryOp::NotEqual => BinaryOp::Ne, AstBinaryOp::Less => BinaryOp::Lt, AstBinaryOp::LessEqual => BinaryOp::Le, AstBinaryOp::Greater => BinaryOp::Gt, AstBinaryOp::GreaterEqual => BinaryOp::Ge, } } }