From c3157b13559a4dfc9766302eafa8ea5f7da5a722 Mon Sep 17 00:00:00 2001 From: Janis Date: Tue, 13 Aug 2024 00:00:19 +0200 Subject: [PATCH] not really sure, i guess this is an IR? --- src/ast.rs | 139 ++++++++++- src/lib.rs | 3 +- src/parser.rs | 49 ++-- src/triples.rs | 608 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 764 insertions(+), 35 deletions(-) create mode 100644 src/triples.rs diff --git a/src/ast.rs b/src/ast.rs index 5d424c9..c680476 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -186,30 +186,30 @@ pub enum LetOrVar { Var, } -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct IntegralType { pub signed: bool, pub bits: u16, } -impl ToString for IntegralType { - fn to_string(&self) -> String { - format!("{}{}", if self.signed { "i" } else { "u" }, self.bits) +impl core::fmt::Display for IntegralType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}{}", if self.signed { "i" } else { "u" }, self.bits) } } -#[derive(Debug)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum FloatingType { Binary32, Binary64, } -impl ToString for FloatingType { - fn to_string(&self) -> String { - match self { - FloatingType::Binary32 => "binary32".to_owned(), - FloatingType::Binary64 => "binary64".to_owned(), - } +impl core::fmt::Display for FloatingType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + FloatingType::Binary32 => "binary32", + FloatingType::Binary64 => "binary64", + }) } } @@ -222,6 +222,123 @@ impl IntegralType { } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Type { + Any, + Void, + Bool, + Integer(IntegralType), + Floating(FloatingType), + Pointer { + constness: bool, + pointee: Box, + }, + Fn { + parameter_types: Vec, + return_type: Box, + }, +} + +impl Type { + pub fn void() -> Type { + Self::Void + } + pub fn bool() -> Type { + Self::Void + } + pub fn any() -> Type { + Self::Void + } + pub fn into_ptr(self) -> Type { + Self::Pointer { + constness: false, + pointee: Box::new(self), + } + } + + pub fn can_logical_and_or(&self) -> bool { + match self { + Type::Bool => true, + _ => false, + } + } + + pub fn can_negate(&self) -> bool { + match self { + Type::Bool | Type::Integer(_) => true, + _ => false, + } + } + + pub fn can_bitxor_and_or(&self) -> bool { + match self { + Type::Bool | Type::Integer(_) => true, + _ => false, + } + } + + pub fn can_add_sub(&self) -> bool { + match self { + Type::Pointer { .. } | Type::Floating(_) | Type::Integer(_) => true, + _ => false, + } + } + pub fn can_shift(&self) -> bool { + match self { + Type::Integer(_) => true, + _ => false, + } + } + pub fn can_eq(&self) -> bool { + match self { + Type::Pointer { .. } | Type::Bool | Type::Floating(_) | Type::Integer(_) => true, + _ => false, + } + } + pub fn can_cmp(&self) -> bool { + match self { + Type::Pointer { .. } | Type::Floating(_) | Type::Integer(_) => true, + _ => false, + } + } + pub fn can_mul_div_rem(&self) -> bool { + match self { + Type::Floating(_) | Type::Integer(_) => true, + _ => false, + } + } + + pub fn is_integer(&self) -> bool { + match self { + Type::Integer(_) => true, + _ => false, + } + } + pub fn remove_ptr(self) -> crate::parser::Result { + match self { + Type::Pointer { pointee, .. } => Ok(Box::into_inner(pointee)), + _ => Err(crate::parser::Error::TriedToDerefNonPointer), + } + } + pub fn align_of(&self) -> u32 { + self.size_of() + } + pub fn size_of(&self) -> u32 { + match self { + Type::Any => 0, + Type::Void => 0, + Type::Bool => 1, + Type::Integer(t) => t.bits.div_ceil(8) as u32, + Type::Floating(t) => match t { + FloatingType::Binary32 => 4, + FloatingType::Binary64 => 8, + }, + Type::Pointer { .. } => 8, + Type::Fn { .. } => 8, + } + } +} + #[derive(Debug)] pub enum PrimitiveType { FloatingType(FloatingType), diff --git a/src/lib.rs b/src/lib.rs index 578129b..24a7986 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(extract_if, iter_advance_by)] +#![feature(extract_if, iter_advance_by, box_into_inner)] #![allow(dead_code, unused_macros)] pub mod ast; @@ -6,6 +6,7 @@ pub mod common; pub mod lexer; pub mod parser; pub mod tokens; +pub mod triples; pub fn tokenize<'a>( bytes: &'a [u8], diff --git a/src/parser.rs b/src/parser.rs index 7b165a3..7347864 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use itertools::Itertools; use crate::{ - ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag}, + ast::{FloatingType, IntegralType, LetOrVar, Node, PrimitiveType, Tag, Type}, common::NextIf, lexer::{Radix, TokenIterator}, tokens::Token, @@ -21,17 +21,22 @@ pub enum Error { ExpectedLetOrVar, #[error("Dummy message.")] IntegralTypeTooWide, + #[error("Dummy message.")] + TriedToDerefNonPointer, } pub type Result = core::result::Result; +// TODO: add a string-table which stores strings and maybe other bytes and +// returns a range for identifiers, constants, etc. where bytes are stored +// flatly, and next to each other. #[derive(Debug)] pub struct Tree { nodes: Vec, - global_decls: Vec, + pub global_decls: Vec, } -fn write_indented_inner( +pub fn write_indented_inner( dst: &mut W, indent: u32, nl: bool, @@ -47,15 +52,17 @@ fn write_indented_inner( Ok(()) } +#[macro_export] macro_rules! write_indented { ($indent:expr, $w:expr, $($arg:tt)*) => { - write_indented_inner($w, $indent, false, format_args!($($arg)*)) + $crate::parser::write_indented_inner($w, $indent, false, format_args!($($arg)*)) }; } +#[macro_export] macro_rules! writeln_indented { ($indent:expr, $w:expr, $($arg:tt)*) => { - write_indented_inner($w, $indent, true, format_args!($($arg)*)) + $crate::parser::write_indented_inner($w, $indent, true, format_args!($($arg)*)) }; } @@ -73,18 +80,16 @@ impl Tree { node } - fn set_node(&mut self, node: Node, tag: Tag) -> Option<()> { - *self.get_node_mut(node)? = tag; - - Some(()) + fn set_node(&mut self, node: Node, tag: Tag) { + *self.get_node_mut(node) = tag; } - fn get_node_mut(&mut self, node: Node) -> Option<&mut Tag> { - self.nodes.get_mut(node.get() as usize) + fn get_node_mut(&mut self, node: Node) -> &mut Tag { + self.nodes.get_mut(node.get() as usize).unwrap() } - fn get_node(&self, node: Node) -> Option<&Tag> { - self.nodes.get(node.get() as usize) + pub fn get_node(&self, node: Node) -> &Tag { + self.nodes.get(node.get() as usize).unwrap() } fn push_tag(&mut self, tag: Tag) -> Node { @@ -301,10 +306,10 @@ impl Tree { explicit_type, assignment, }, - ) - .unwrap(); + ); - Ok(node) + // return assignment if it exists, to make rendering and visiting easier + Ok(assignment.unwrap_or(node)) } pub fn parse_global_decl(&mut self, tokens: &mut TokenIterator) -> Result { @@ -726,7 +731,7 @@ impl Tree { self.parse_program(&mut tokens) } - fn get_ident_str(&self, node: Node) -> Option<&str> { + pub fn get_ident_str(&self, node: Node) -> Option<&str> { match &self.nodes[node.get() as usize] { Tag::Ident { name } => Some(name.as_str()), _ => None, @@ -734,7 +739,7 @@ impl Tree { } fn get_typename_str(&self, node: Node) -> Option { - match self.get_node(node)? { + match self.get_node(node) { Tag::IntegralType(i) => Some(i.to_string()), Tag::Ident { name } => Some(name.clone()), Tag::Pointer { pointee } => self.get_typename_str(*pointee), @@ -769,7 +774,7 @@ impl Tree { writeln!(writer, "}}") } Tag::ParameterList { parameters } => { - writeln_indented!(indent, writer, "%{} = ParameterList = [", node.get())?; + writeln_indented!(indent, writer, "%{} = ParameterList [", node.get())?; for param in parameters { self.render_node(writer, *param, indent + 1)?; } @@ -864,7 +869,7 @@ impl Tree { let_or_var, name, explicit_type, - assignment, + .. } => { self.render_node(writer, *name, indent)?; explicit_type.map(|ty| self.render_node(writer, ty, indent)); @@ -887,9 +892,6 @@ impl Tree { write!(writer, ", ty: {}", self.get_typename_str(*ty).unwrap())?; } writeln!(writer, ");")?; - if let Some(assignment) = assignment { - self.render_node(writer, *assignment, indent)?; - } Ok(()) } Tag::CallExpr { lhs, rhs } => todo!(), @@ -1145,6 +1147,7 @@ impl Tree { ) } Tag::Assign { lhs, rhs } => { + self.render_node(writer, *lhs, indent)?; self.render_node(writer, *rhs, indent)?; writeln_indented!( indent, diff --git a/src/triples.rs b/src/triples.rs new file mode 100644 index 0000000..d4bec9d --- /dev/null +++ b/src/triples.rs @@ -0,0 +1,608 @@ +#![allow(dead_code)] + +use std::{collections::HashMap, marker::PhantomData, num::NonZero}; + +use crate::{ + ast::{FloatingType, IntegralType, Node as AstNode, PrimitiveType, Tag, Type}, + parser::Tree, + writeln_indented, +}; + +struct SymbolRecord { + name: String, + decl: AstNode, + ty: Type, +} + +struct SymbolTable<'a> { + tree: &'a Tree, + identifiers: Vec, + parent: Option>>, +} + +impl<'a> SymbolTable<'a> { + pub fn root(tree: &'a Tree) -> Self { + Self { + tree, + identifiers: Vec::new(), + parent: None, + } + } + + fn insert_symbol_with_name(&mut self, ast_node: AstNode, name: String) { + let ty = self.type_of_node(ast_node); + self.identifiers.push(SymbolRecord { + name, + decl: ast_node, + ty, + }); + } + + pub fn insert_return_symbol(&mut self, ast_node: AstNode) { + self.insert_symbol_with_name(ast_node, "return".to_string()) + } + + pub fn insert_symbol(&mut self, ast_node: AstNode) { + let name = match self.tree.get_node(ast_node) { + Tag::VarDecl { name, .. } => self.tree.get_ident_str(*name).unwrap().to_string(), + Tag::Parameter { name, .. } => self.tree.get_ident_str(*name).unwrap().to_string(), + Tag::FunctionProto { name, .. } => self.tree.get_ident_str(*name).unwrap().to_string(), + _ => { + panic!("ast_node wasn't any kind of decl!"); + } + }; + + self.insert_symbol_with_name(ast_node, name) + } + + pub fn find_symbol_name(&self, name: &str) -> Option<&SymbolRecord> { + self.identifiers + .iter() + .find(|r| r.name.as_str() == name) + .or_else(|| { + self.parent + .as_ref() + .and_then(|parent| parent.find_symbol_name(name)) + }) + } + + pub fn find_symbol(&self, ident_node: AstNode) -> Option<&SymbolRecord> { + self.identifiers + .iter() + .find(|r| Some(r.name.as_str()) == self.tree.get_ident_str(ident_node)) + .or_else(|| { + self.parent + .as_ref() + .and_then(|parent| parent.find_symbol(ident_node)) + }) + } + pub fn into_child_in_place(&mut self) { + let mut parent = Self { + parent: None, + tree: self.tree, + identifiers: Vec::new(), + }; + core::mem::swap(self, &mut parent); + self.parent = Some(Box::new(parent)); + } + + pub fn into_child(self) -> SymbolTable<'a> { + Self { + identifiers: Vec::new(), + tree: self.tree, + parent: Some(Box::new(self)), + } + } + + pub fn parent_mut(&mut self) -> Option<&mut SymbolTable<'a>> { + self.parent.as_mut().map(|parent| parent.as_mut()) + } + + pub fn into_parent_in_place(&mut self) -> Option> { + if let Some(child) = self.parent.take() { + let mut child = Box::into_inner(child); + core::mem::swap(self, &mut child); + Some(child) + } else { + None + } + } + + pub fn into_parent(self) -> Option> { + self.parent.map(|parent| Box::into_inner(parent)) + } + + fn type_of_node(&self, node: AstNode) -> crate::ast::Type { + match self.tree.get_node(node) { + Tag::Ident { name } => self + .find_symbol(node) + .map(|r| r.ty.clone()) + .expect(&format!("identifier '{name}' not found in SymbolTable!")), + Tag::FunctionDecl { proto, .. } => self.type_of_node(*proto), + Tag::FunctionProto { + parameters, + return_type, + .. + } => { + let return_type = self.type_of_node(*return_type); + let parameter_types = parameters + .map(|p| match self.tree.get_node(p) { + Tag::ParameterList { parameters } => parameters + .iter() + .map(|p| self.type_of_node(*p)) + .collect::>(), + _ => panic!("parameters is not a parameterlist!"), + }) + .unwrap_or(Vec::new()); + + crate::ast::Type::Fn { + parameter_types, + return_type: Box::new(return_type), + } + } + Tag::Parameter { ty, .. } => self.type_of_node(*ty), + Tag::Pointer { pointee } => Type::Pointer { + constness: false, + pointee: Box::new(self.type_of_node(*pointee)), + }, + Tag::IntegralType(t) => Type::Integer(*t), + Tag::PrimitiveType(t) => match t { + PrimitiveType::FloatingType(t) => Type::Floating(*t), + PrimitiveType::IntegralType(t) => self.type_of_node(*t), + PrimitiveType::Bool => Type::bool(), + PrimitiveType::Void => Type::void(), + }, + Tag::IntegralConstant { ty, .. } => Type::Integer(*ty), + Tag::FloatingConstant { ty, .. } => Type::Floating(*ty), + Tag::Block { trailing_expr, .. } => trailing_expr + .map(|n| self.type_of_node(n)) + .unwrap_or(Type::void()), + Tag::VarDecl { + explicit_type, + assignment, // this is a Tag::Assign + .. + } => { + let lhs = explicit_type.map(|n| self.type_of_node(n)); + let rhs = assignment.map(|n| match self.tree.get_node(n) { + Tag::Assign { rhs, .. } => self.type_of_node(*rhs), + _ => unreachable!(), + }); + if lhs.as_ref().zip(rhs.as_ref()).map(|(l, r)| l != r) == Some(true) { + eprintln!("vardecl: incompatible types {lhs:?} and {rhs:?}."); + } + lhs.or(rhs) + .expect("Type could not be automatically deduced.") + } + Tag::CallExpr { lhs, .. } => self.type_of_node(*lhs), + Tag::ExplicitCast { typename, .. } => self.type_of_node(*typename), + Tag::Deref { lhs } => self.type_of_node(*lhs).remove_ptr().unwrap(), + Tag::Ref { lhs } => self.type_of_node(*lhs).into_ptr(), + Tag::Not { lhs } => self.type_of_node(*lhs), + Tag::Negate { lhs } => self.type_of_node(*lhs), + Tag::Or { lhs, .. } => self.type_of_node(*lhs), + Tag::And { lhs, .. } => self.type_of_node(*lhs), + Tag::BitOr { lhs, .. } => self.type_of_node(*lhs), + Tag::BitAnd { lhs, .. } => self.type_of_node(*lhs), + Tag::BitXOr { lhs, .. } => self.type_of_node(*lhs), + Tag::Shl { lhs, .. } => self.type_of_node(*lhs), + Tag::Shr { lhs, .. } => self.type_of_node(*lhs), + Tag::Add { lhs, .. } => self.type_of_node(*lhs), + Tag::Sub { lhs, .. } => self.type_of_node(*lhs), + Tag::Mul { lhs, .. } => self.type_of_node(*lhs), + Tag::Rem { lhs, .. } => self.type_of_node(*lhs), + Tag::Div { lhs, .. } => self.type_of_node(*lhs), + Tag::Eq { .. } => Type::bool(), + Tag::NEq { .. } => Type::bool(), + Tag::Lt { .. } => Type::bool(), + Tag::Gt { .. } => Type::bool(), + Tag::Le { .. } => Type::bool(), + Tag::Ge { .. } => Type::bool(), + _ => Type::void(), + } + } +} + +type Node = u32; + +enum Inst { + Label(String), + Constant(Value), + Parameter, + Add { lhs: Node, rhs: Node }, + Sub { lhs: Node, rhs: Node }, + Div { lhs: Node, rhs: Node }, + Mul { lhs: Node, rhs: Node }, + Rem { lhs: Node, rhs: Node }, + BitAnd { lhs: Node, rhs: Node }, + BitOr { lhs: Node, rhs: Node }, + BitXOr { lhs: Node, rhs: Node }, + Negate { lhs: Node }, + ReturnValue { lhs: Node }, + Return, + Alloc { size: u32, align: u32 }, + AddressOf(Node), + Load { source: Node }, + Store { dest: Node, source: Node }, +} + +enum Value { + Int { kind: IntegralType, bits: u64 }, + Float { kind: FloatingType, bits: u64 }, +} + +impl core::fmt::Display for Value { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Value::Int { kind, bits } => { + write!(f, "{kind} {}", bits) + } + Value::Float { kind, bits } => { + write!(f, "{kind} {{{}}}", bits) + } + } + } +} + +struct IRBuilder<'tree, 'ir> { + ir: &'ir mut IR, + st: SymbolTable<'tree>, + lookup: HashMap, +} + +impl<'tree, 'ir> IRBuilder<'tree, 'ir> { + fn new(ir: &'ir mut IR, st: SymbolTable<'tree>) -> Self { + Self { + ir, + st, + lookup: HashMap::new(), + } + } + + fn visit(&mut self, node: AstNode) -> Node { + println!("visiting %{}", node.get()); + match self.st.tree.get_node(node) { + Tag::FunctionDecl { proto, body } => { + self.visit(*proto); + self.st.into_child_in_place(); + let value = self.visit(*body); + // TODO: return value of body expression + let node = if value != !0 { + self.type_check(self.st.find_symbol_name("return").unwrap().decl, *body); + self.ir.push(Inst::ReturnValue { lhs: value }) + } else { + !0 + }; + + self.st.into_parent_in_place(); + node + } + Tag::FunctionProto { + parameters, + return_type, + name, + } => { + self.st.insert_symbol(node); + self.st.insert_return_symbol(*return_type); + parameters.map(|p| self.visit(p)); + + let label = self.ir.push(Inst::Label( + self.st.tree.get_ident_str(*name).unwrap().to_string(), + )); + + self.lookup.insert(node, label); + + label + } + Tag::ParameterList { parameters } => { + for param in parameters { + self.visit(*param); + } + !0 + } + Tag::Parameter { .. } => { + self.st.insert_symbol(node); + let param = self.ir.push(Inst::Parameter); + + self.lookup.insert(node, param); + param + } + Tag::Block { + statements, + trailing_expr, + } => { + for stmt in statements { + self.visit(*stmt); + } + + if let Some(expr) = trailing_expr { + self.visit(*expr) + } else { + !0 + } + } + Tag::VarDecl { .. } => { + let ty = self.st.type_of_node(node); + let alloca = self.ir.push(Inst::Alloc { + size: ty.size_of(), + align: ty.align_of(), + }); + self.st.insert_symbol(node); + self.lookup.insert(node, alloca); + alloca + } + Tag::ReturnStmt { expr } => { + if let Some(expr) = expr { + let expr = self.visit(*expr); + self.ir.push(Inst::ReturnValue { lhs: expr }) + } else { + self.ir.push(Inst::Return) + } + } + Tag::ExprStmt { expr } => self.visit(*expr), + Tag::Deref { lhs } => { + let lhs = self.visit(*lhs); + self.ir.push(Inst::Load { source: lhs }) + } + Tag::IntegralConstant { bits, ty } => self.ir.push(Inst::Constant(Value::Int { + kind: *ty, + bits: *bits, + })), + Tag::FloatingConstant { bits, ty } => self.ir.push(Inst::Constant(Value::Float { + kind: *ty, + bits: *bits, + })), + Tag::Assign { lhs, rhs } => { + self.type_check(*lhs, *rhs); + + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir.push(Inst::Store { + dest: lhs, + source: rhs, + }) + } + Tag::Add { lhs, rhs } => { + let ty = self.type_check(*lhs, *rhs); + if !ty.can_add_sub() { + eprintln!("add is not available for type {ty:?}"); + } + + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir.push(Inst::Add { lhs, rhs }) + } + Tag::Sub { lhs, rhs } => { + let ty = self.type_check(*lhs, *rhs); + if !ty.can_add_sub() { + eprintln!("sub is not available for type {ty:?}"); + } + + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir.push(Inst::Sub { lhs, rhs }) + } + Tag::Mul { lhs, rhs } => { + let ty = self.type_check(*lhs, *rhs); + if !ty.can_mul_div_rem() { + eprintln!("mul is not available for type {ty:?}"); + } + + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir.push(Inst::Mul { lhs, rhs }) + } + Tag::Div { lhs, rhs } => { + let ty = self.type_check(*lhs, *rhs); + if !ty.can_mul_div_rem() { + eprintln!("div is not available for type {ty:?}"); + } + + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir.push(Inst::Div { lhs, rhs }) + } + Tag::Rem { lhs, rhs } => { + let ty = self.type_check(*lhs, *rhs); + if !ty.can_mul_div_rem() { + eprintln!("rem is not available for type {ty:?}"); + } + + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir.push(Inst::Rem { lhs, rhs }) + } + Tag::BitAnd { lhs, rhs } => { + let ty = self.type_check(*lhs, *rhs); + if !ty.can_bitxor_and_or() { + eprintln!("bitand is not available for type {ty:?}"); + } + + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir.push(Inst::BitAnd { lhs, rhs }) + } + Tag::BitOr { lhs, rhs } => { + let ty = self.type_check(*lhs, *rhs); + if !ty.can_bitxor_and_or() { + eprintln!("bitor is not available for type {ty:?}"); + } + + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir.push(Inst::BitOr { lhs, rhs }) + } + Tag::BitXOr { lhs, rhs } => { + let ty = self.type_check(*lhs, *rhs); + if !ty.can_bitxor_and_or() { + eprintln!("bitxor is not available for type {ty:?}"); + } + + let lhs = self.visit(*lhs); + let rhs = self.visit(*rhs); + self.ir.push(Inst::BitXOr { lhs, rhs }) + } + Tag::Negate { lhs } => { + let ty = self.st.type_of_node(*lhs); + if !ty.can_negate() { + eprintln!("negation is not available for type {ty:?}"); + } + + let lhs = self.visit(*lhs); + self.ir.push(Inst::Negate { lhs }) + } + Tag::Ident { name } => { + let decl = self + .st + .find_symbol(node) + .expect(&format!("symbol '{name}' not found in SymbolMap!")) + .decl; + + *self.lookup.get(&decl).unwrap() + } + Tag::Ref { lhs } => { + let lhs = self.visit(*lhs); + self.ir.push(Inst::AddressOf(lhs)) + } + _ => { + dbg!(self.st.tree.get_node(node)); + todo!() + } + } + } + + fn type_check(&self, lhs: AstNode, rhs: AstNode) -> Type { + let t_lhs = self.st.type_of_node(lhs); + let t_rhs = self.st.type_of_node(rhs); + if t_lhs != t_rhs { + eprintln!("incompatible types {t_lhs:?} and {t_rhs:?}!"); + } + t_lhs + } +} + +struct IR { + nodes: Vec, +} + +impl IR { + pub fn new() -> Self { + Self { nodes: Vec::new() } + } + + fn push(&mut self, inst: Inst) -> u32 { + let node = self.nodes.len() as u32; + self.nodes.push(inst); + node + } + + pub fn build(&mut self, tree: &Tree, ast_node: crate::ast::Node) { + let st = SymbolTable::root(tree); + let mut builder = IRBuilder::new(self, st); + + builder.visit(ast_node); + } + + fn render_node( + &self, + w: &mut W, + node: Node, + indent: u32, + ) -> core::fmt::Result { + match &self.nodes[node as usize] { + Inst::Label(label) => { + writeln_indented!(indent - 1, w, "%{} = {label}:", node)?; + } + Inst::Parameter => { + writeln_indented!(indent, w, "%{} = Param", node)?; + } + Inst::Constant(value) => { + writeln_indented!(indent, w, "%{} = {}", node, value)?; + } + Inst::Add { lhs, rhs } => { + writeln_indented!(indent, w, "%{} = %{} + %{}", node, lhs, rhs)?; + } + Inst::Sub { lhs, rhs } => { + writeln_indented!(indent, w, "%{} = %{} - %{}", node, lhs, rhs)?; + } + Inst::Mul { lhs, rhs } => { + writeln_indented!(indent, w, "%{} = %{} * %{}", node, lhs, rhs)?; + } + Inst::Div { lhs, rhs } => { + writeln_indented!(indent, w, "%{} = %{} / %{}", node, lhs, rhs)?; + } + Inst::Rem { lhs, rhs } => { + writeln_indented!(indent, w, "%{} = %{} % %{}", node, lhs, rhs)?; + } + Inst::BitAnd { lhs, rhs } => { + writeln_indented!(indent, w, "%{} = %{} & %{}", node, lhs, rhs)?; + } + Inst::BitOr { lhs, rhs } => { + writeln_indented!(indent, w, "%{} = %{} | %{}", node, lhs, rhs)?; + } + Inst::BitXOr { lhs, rhs } => { + writeln_indented!(indent, w, "%{} = %{} ^ %{}", node, lhs, rhs)?; + } + Inst::Negate { lhs } => { + writeln_indented!(indent, w, "%{} = !%{}", node, lhs)?; + } + Inst::ReturnValue { lhs } => { + writeln_indented!(indent, w, "%{} = return %{}", node, lhs)?; + } + Inst::Return => { + writeln_indented!(indent, w, "%{} = return", node)?; + } + Inst::Alloc { size, align } => { + writeln_indented!(indent, w, "%{} = alloca {size} (algin: {align})", node)?; + } + Inst::AddressOf(val) => { + writeln_indented!(indent, w, "%{} = addr %{val}", node)?; + } + Inst::Load { source } => { + writeln_indented!(indent, w, "%{} = load ptr %{source}", node)?; + } + Inst::Store { dest, source } => { + writeln_indented!(indent, w, "%{} = store ptr %{dest} from %{source}", node)?; + } + } + Ok(()) + } + + pub fn render(&self, w: &mut W) -> core::fmt::Result { + for node in 0..self.nodes.len() { + self.render_node(w, node as u32, 1)?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::lexer::Tokenizer; + + use super::*; + + #[test] + fn ir() { + let src = " +fn main() -> u32 { +let a: u32 = 0 + 3; +let ptr_a = &a; +return *ptr_a * 2; +} +"; + let tokens = Tokenizer::new(src.as_bytes()).unwrap(); + + let mut tree = Tree::new(); + tree.parse(tokens.iter()).unwrap(); + + let mut buf = String::new(); + tree.render(&mut buf).unwrap(); + println!("{buf}"); + + let mut ir = IR::new(); + ir.build(&tree, *tree.global_decls.first().unwrap()); + let mut buf = String::new(); + ir.render(&mut buf).unwrap(); + println!("{buf}"); + } +}