mclangc/src/parser/ast/typ.rs
2026-01-28 22:30:02 +02:00

245 lines
8.0 KiB
Rust

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<Type>,
mutable: bool,
},
SizedArray {
inner: Box<Type>,
count: LocBox<Expr>,
},
UnsizedArray {
inner: Box<Type>,
},
Owned(Ident),
Builtin {
name: String,
size: u8,
signed: bool,
}
}
impl Type {
pub fn get_absolute_value(&self, program: &Program) -> anyhow::Result<Self> {
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<Self> {
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<bool> {
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<usize> {
fn f(t: &Type, program: &Program, behind_a_ptr: bool) -> anyhow::Result<usize> {
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<usize> {
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)
}
}
}
}