use std::fmt::Display; use anyhow::bail; use crate::{common::loc::LocBox, validator::predefined::get_builtin_from_name}; use super::{expr::Expr, literal::Literal, Ident, Program}; #[derive(Debug, Clone, PartialEq, PartialOrd, Hash)] pub enum Type { Ref { inner: Box, mutable: bool, }, SizedArray { inner: Box, count: LocBox, }, UnsizedArray { inner: Box, }, Owned(Ident), Builtin { name: String, size: u8, signed: bool, } } impl Type { pub fn get_absolute_value(&self, program: &Program) -> anyhow::Result { match self { Self::Ref { inner, .. } => inner.get_absolute_value(program), Self::Owned(ident) => { if let Some(t) = get_builtin_from_name(ident.0.as_str()) { return Ok(t) } if let Some(t) = program.curr_fn_args.get(ident) { return Ok(t.inner().clone().get_absolute_value(program)?); } else if program.types.get(ident).is_some() || program.structs.get(ident).is_some() || program.enums.get(ident).is_some() { return Ok(self.clone()); } if let Some(var) = program.get_var(ident) { Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone().get_absolute_value(program)?) } else { bail!("owo? missing value: {ident}"); } } Self::SizedArray { .. } | Self::Builtin { .. } | Self::UnsizedArray { .. } => Ok(self.clone()), } } pub fn get_variable_type(&self, program: &Program) -> anyhow::Result { match self { Self::Owned(ident) => { if let Some(t) = program.types.get(ident) { Ok(t.inner().clone()) } else if let Some(var) = program.get_var(ident) { Ok(var.1.inner().typ.clone().expect("type should be computed if were already using it").inner().clone()) } else { bail!("owo? missing value: {ident}"); } } Self::Ref { .. } | Self::SizedArray { .. } | Self::Builtin { .. } | Self::UnsizedArray { .. } => Ok(self.clone()), } } pub fn is_numeric(&self, program: &Program) -> bool { match self { Self::Ref { inner, .. } => { inner.is_numeric(program) } Self::Owned(name) => { match program.types.get(&name) { Some(t) => t.inner().is_numeric(program), _ => false } }, Self::SizedArray { .. } => false, Self::UnsizedArray { .. } => false, Self::Builtin { name, .. } => { match name.as_str() { "u8" | "i8" | "u16" | "i16" | "u32" | "i32" | "u64" | "i64" | "usize" | "isize" => true, _ => false, } } } } // Returns None if non numeric pub fn is_signed(&self, program: &Program) -> Option { if self.is_ptr(program) { return Some(false); } match self { Self::Ref { inner, .. } => { inner.is_signed(program) } Self::Owned(name) => { match program.types.get(&name) { Some(t) => t.inner().is_signed(program), _ => None } }, Self::SizedArray { .. } => None, Self::UnsizedArray { .. } => None, Self::Builtin { name, .. } => { match name.as_str() { "i8" | "i16" | "i32" | "i64" | "isize" => Some(true), "u8" | "u16" | "u32" | "u64" | "usize" => Some(false), _ => None, } } } } pub fn is_ptr(&self, program: &Program) -> bool { match self { Self::Owned(name) => { match program.types.get(&name) { Some(t) => t.inner().is_ptr(program), _ => false } }, Self::Ref { .. } => true, _ => false } } pub fn is_bool(&self, program: &Program) -> bool { match self { Self::Owned(name) => { match program.types.get(&name) { Some(t) => t.inner().is_bool(program), _ => false } }, Self::Builtin { name, .. } if name.as_str() == "bool" => true, _ => false } } pub fn size_of(&self, program: &Program) -> anyhow::Result { fn f(t: &Type, program: &Program, behind_a_ptr: bool) -> anyhow::Result { match t { Type::Ref { .. } => { // TODO: Use the actual ptr size Ok(size_of::<*const ()>()) } Type::UnsizedArray { .. } => { if behind_a_ptr { Ok(size_of::<*const ()>()) } else { bail!("Unsized arrays dont have a known size and must be behind a pointer"); } } Type::SizedArray { inner, .. } => { Ok(inner.size_of(program)? ) } Type::Builtin { size, .. } => { Ok(*size as usize) } Type::Owned(name) => { if let Some(v) = program.structs.get(&name) { return Ok(v.inner().get_size(program)?); } if let Some(v) = program.types.get(&name) { return Ok(f(v.inner(), program, behind_a_ptr)?); } if let Some(_) = program.enums.get(&name) { return Ok(4); // TODO: Make enum size changeable } bail!("Unknown type '{name}'") } } } f(self, program, false) } pub fn size_of_allow_unsized_arrays(&self, program: &Program) -> anyhow::Result { match self.size_of(program) { Ok(v) => Ok(v), Err(e) => { if e.to_string().as_str() == "Unsized arrays dont have a known size and must be behind a pointer" { return Ok(0); } Err(e) } } } } impl Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Ref { inner, mutable } => { if *mutable { write!(f, "&mut {inner}") } else { write!(f, "&{inner}") } } Self::UnsizedArray { inner } => { write!(f, "[{inner}]") } Self::SizedArray { inner, count } => { match count.inner() { Expr::Path(p) => { write!(f, "[{inner}; {p}]") } Expr::Literal(_, Literal::Number(n)) => { write!(f, "[{inner}; {n}]") } _ => unreachable!() } } Self::Owned(ident) => write!(f, "{ident}"), Self::Builtin { name, size, signed } => { write!(f, "(builtin) {} {}({})", name, if *signed { "signed" } else { "unsigned" }, size) } } } }