ccarp 0.1.2

(trans)Compile C And Rust Partially
Documentation
//! Helper Module
//! 
//! This helper defines some traits and macros which are used inside the module (`ccarp_c`).
//! 
//! Important definitions:
//! - `Context`, which signifies a translation context (from C to Rust)
//! - `RFrom` and `RInto`, which signify conversion traits
//! - `GetType` and `CastInto`, which are traits for dealing with type conversions
use std::fmt::Display;

use crate::ccarp::{error::Result, translator::CompilerFlags};

use super::{rustdecl::{EnumDecl, Let, Qualifier, RDecl, RDeclSingle, REnumerator, RInit, RType}, rustexpr::*};

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Context {
    pub scope: RDecl,
    pub return_type: RType,
    pub inside_main: bool,
    pub top_level: bool,
    pub flags: CompilerFlags,
}
impl Context {
    pub fn get_const_integers(&self) -> Vec<(&str,i128)> {
        let mut out=vec![];
        for decl in &self.scope.0 {
            if let RDeclSingle::Let(Let(qual,ident,_,init))=decl {
                if [Qualifier::Const,Qualifier::Static,Qualifier::ExternStatic].contains(qual) {
                    if let Some(rin)=init {
                        if let RInit::Expr(expr)=rin.as_ref() {
                            if let Ok(val)=expr.eval(self) {
                                out.push((ident.0.as_str(),val));
                            }
                        }
                    }
                }
            }
        }
        out
    }
    pub fn get_enum_vals(&self) -> Vec<(&str,i128)> {
        let mut out=vec![];
        for decl in &self.scope.0 {
            let mut val=0;
            if let RDeclSingle::Enum(enum_decl)=decl {
                for enumerator in &enum_decl.1 {
                    match enumerator {
                        REnumerator::Const(renum_const) => {
                            out.push((renum_const.0.0.as_str(),val));
                            val+=1;
                        },
                        REnumerator::Val(renum_const, rternary) => {
                            if let Ok(eval)=rternary.eval(self) {
                                val=eval;
                                out.push((renum_const.0.0.as_str(),val));
                                val+=1;
                            }
                        },
                    }
                }
            }
        }
        out
    }
    pub fn get_extern_vars(&self) -> Vec<&Let> {
        let mut out=vec![];
        for decl in &self.scope.0 {
            if let RDeclSingle::Let(let_)=decl {
                if matches!(let_.0,Qualifier::ExternStatic|Qualifier::ExternStaticMut) {
                    out.push(let_);
                }
            }
        }
        out
    }
    pub fn get_variable_decl(&self,var: &str) -> Option<&Let> {
        for decl in &self.scope.0 {
            if let RDeclSingle::Let(let_)=decl {
                if let_.1.0==var {
                    return Some(let_)
                }
            }
        }
        None
    }
    pub fn get_variable_ty(&self,var: &str) -> Option<RType> {
        for decl in self.scope.0.iter().rev() {
            if let RDeclSingle::Let(Let(_,ident,ty,_))=decl {
                if ident.0.as_str()==var { return Some(ty.clone()) }
            }
            else if let RDeclSingle::Enum(EnumDecl(ident,enums))=decl {
                for en in enums {
                    let s=
                    match en {
                        REnumerator::Const(renum_const)|REnumerator::Val(renum_const, _)  => renum_const.0.0.as_str(),
                    };
                    if var==s { return Some(RType::Enum(ident.clone()))}
                }
            }
        }
        None
    }
    pub fn get_struct_fields(&self,struct_name: &str) -> Vec<(&str,&RType)> {
        let mut out=vec![];
        for decl in self.scope.0.iter().rev() {
            if let RDeclSingle::Struct(struct_decl)=decl {
                if struct_name==struct_decl.1.0.as_str() {
                    for decl in &struct_decl.3 {
                        out.append(&mut decl.0.iter().map(|x| (x.0.0.as_str(),&x.1)).collect());
                    }
                }
            }
        }
        out
    }
    pub fn get_enum_fields(&self,enum_name: &str) -> Vec<&str> {
        let mut out=vec![];
        for decl in self.scope.0.iter().rev() {
            if let RDeclSingle::Enum(enum_decl)=decl {
                if enum_name==enum_decl.0.0.as_str() {
                    for decl in &enum_decl.1 {
                        match decl {
                            REnumerator::Const(renum_const)|REnumerator::Val(renum_const, _) => {
                                out.push(renum_const.0.0.as_str());
                            }
                        }
                    }
                }
            }
        }
        out
    }
    pub fn get_typedef_alias(&self,typedef: &str) -> Option<RType> {
        for decl in self.scope.0.iter().rev() {
            if let RDeclSingle::Typedef(rtypedef)=decl {
                if rtypedef.0.0.as_str()==typedef {
                    return Some(rtypedef.1.clone().get_type_alias())
                }
            }
        }
        None
    }
    #[allow(clippy::cast_sign_loss,clippy::cast_possible_truncation,clippy::cast_precision_loss)]
    pub fn get_enum_size(&self,enum_name: &str) -> usize {
        let len=self.get_enum_fields(enum_name).len();
        (len as f64).log2().ceil() as usize/8
    }
    #[allow(clippy::missing_errors_doc)]
    pub fn get_union_size(&self,union_name: &str) -> Result<usize> {
        let types=self.get_struct_fields(union_name).into_iter().map(|x| x.1);
        let mut max=0;
        for ty in types {
            max=Ord::max(max, ty.sizeof(self)?);
        }
        Ok(max)
    }
    pub fn get_fn_return_value(&self, post: &RPostfixExpr) -> Option<RType> {
        if let RPostfixExpr::Field(field)=post {
            if let RFieldExpr(RPrimaryExpr::Ident(ident),v,_)=field.as_ref() {
                if v.is_empty() {
                    for i in &self.scope.0 {
                        if let RDeclSingle::Let(let_)=i {
                            if let_.1==ident.0 {
                                match &let_.2 {
                                    RType::Function(_, rtype) => return Some((**rtype).clone()),
                                    _ => return None
                                }
                            }
                        }
                    }
                }
            }
        }
        None
    }
    pub fn get_field_ty(&self, ty: RType, field: &RField) -> Option<RType> {
        let ident=
        match &field {
            RField::Field(rident)|RField::UnsafeField(rident) => rident,
            RField::Method(..)|RField::UnsafeMethod(..) => return None,
        };
        match ty {
            RType::Ref(_,rtype)|RType::RefMut(_,rtype)|RType::Point(rtype)|RType::PointMut(rtype)|
            RType::ThinRef(_,rtype)|RType::ThinRefMut(_,rtype)|RType::ThinPoint(rtype)|RType::ThinPointMut(rtype) => self.get_field_ty(*rtype, field),
            RType::Typedef(_, ty) => {
                self.get_field_ty(*ty, field)
            },
            RType::Struct(rident)|RType::Union(rident) => {
                let fields=self.get_struct_fields(&rident.0);
                for (name,ty) in fields {
                    if name==ident.0 { return Some(ty.clone()) }
                }
                None
            },
            _ => None
        }
    }
    pub fn new_nameless_enum(&self) -> String {
        for decl in self.scope.0.iter().rev() {
            if let RDeclSingle::Enum(enum_decl)=decl {
                if let Some(opt_num)=enum_decl.0.0.strip_prefix("_NamelessEnum") {
                    if let Ok(num)=str::parse::<usize>(opt_num) {
                        return format!("_NamelessEnum{}",num+1)
                    }
                }
            }
        }
        "_NamelessEnum".to_string()
    }
    pub fn new_nameless_union(&self) -> String {
        for decl in self.scope.0.iter().rev() {
            if let RDeclSingle::Struct(struct_decl)=decl {
                if let Some(opt_num)=struct_decl.1.0.strip_prefix("_NamelessUnion") {
                    if let Ok(num)=str::parse::<usize>(opt_num) {
                        return format!("_NamelessUnion{}",num+1)
                    }
                }
            }
        }
        "_NamelessUnion".to_string()
    }
    pub fn new_nameless_struct(&self) -> String {
        for decl in self.scope.0.iter().rev() {
            if let RDeclSingle::Struct(struct_decl)=decl {
                if let Some(opt_num)=struct_decl.1.0.strip_prefix("_NamelessStruct") {
                    if let Ok(num)=str::parse::<usize>(opt_num) {
                        return format!("_NamelessStruct{}",num+1)
                    }
                }
            }
        }
        "_NamelessStruct".to_string()
    }
    pub fn is_variable_mutable_global(&self, name: &str) -> bool {
        for decl in &self.scope.0 {
            if let RDeclSingle::Let(Let(q, ident,..))=decl {
                if ident.0.as_str()==name { return matches!(q,Qualifier::StaticMut|Qualifier::ExternStaticMut); }
            }
        }
        false
    }
}
impl Default for Context {
    fn default() -> Self {
        Self { scope: (RDecl(vec![])), return_type: (RType::default()), inside_main: (false), top_level: (false), flags: (CompilerFlags::empty()) }
    }
}

/// Conversion trait with added `Context`
/// 
/// # Errors
/// If at any point there is a problem during conversion, this function
/// returns with an error
pub trait RFrom<T> where Self : Sized {
    /// `From` with added context
    /// 
    /// # Errors
    /// If at any point there is a problem during conversion, this function
    /// returns with an error
    fn rfrom(value: T, context: &mut Context) -> Result<Self>;
}

/// Conversion trait with added `Context`
/// 
/// # Errors
/// If at any point there is a problem during conversion, this function
/// returns with an error
pub trait RInto<U> {
    /// `Into` with added context
    /// 
    /// # Errors
    /// If at any point there is a problem during conversion, this function
    /// returns with an error
    fn rinto(self, context: &mut Context) -> Result<U>;
}

impl<U,T: RFrom<U>> RInto<T> for U {
    fn rinto(self, context: &mut Context) -> Result<T> {
        T::rfrom(self, context)
    }
}

pub trait GetType {
    fn get_type(&self) -> RType;
}

pub trait CastInto {
    #[must_use]
    fn cast(self, ty: &RType) -> Self;
}

macro_rules! sum {
    ($name:ident; $($t:ty),+; $($u:ty),+) => {
        pub enum $name {
            A($($t),+),
            B($($u),+)
        }
    };
    ($name:ident; $($t:ty),+; $($u:ty),+; $($v:ty),+) => {
        pub enum $name {
            A($($t),+),
            B($($u),+),
            C($($v),+)
        }
    };
}
pub(crate) use sum;

pub fn print_vec<T: Display>(v: &[T], sep: &str) -> String {
    v.iter().map(ToString::to_string).collect::<Vec<_>>().join(sep)
}
pub fn print_vec2<T>(v: &[T], f: impl Fn(&T) -> String, sep: &str) -> String {
    v.iter().map(f).collect::<Vec<_>>().join(sep)
}

/// Trait for Evaluating constant expressions
/// 
/// # Errors
/// Currently if at any point the evaluation runs into a non-integer
/// type, it returns with an `Err`
pub trait Eval {
    /// Evaluate an expression at translation time
    /// 
    /// # Errors
    /// Currently if at any point the evaluation runs into a non-integer
    /// type, it returns with an `Err`
    fn eval(&self, context: &Context) -> Result<i128>;
}

pub trait IsUnsafe {
    fn is_unsafe(&self) -> bool;
}

pub trait NoReturn {
    #[must_use]
    fn simplify(self) -> Self;
}

pub trait IsPrimary {
    fn is_primary(&self) -> bool;
    fn into_primary(self) -> RPrimaryExpr;
}