use std::{fmt::Display, mem::swap};
use crate::ccarp::error::{c2rust_err, unimpl_err, safe_remove, CCErr, Result};
use crate::ccarp::translator::CompilerFlags;
use crate::ccarp_c::decl::IdentifierList;
use crate::ccarp_c::{decl::{DeclPostfix, Declarator, DirectDeclarator, TypeQualifier},trans_unit::*, tt::Identifier};
use super::{defs::{print_vec, Context, RFrom, RInto}, rustdecl::{apply_decl_postfix_to_ty, Generic, Let, Qualifier, RDecl, RDeclSingle, RParam, RParamList, RType, TypeOrTypedef}, rustprimitives::RIdent, ruststmt::CompoundRStmt};
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RTransUnit(pub Vec<RExtDecl>);
impl RFrom<TranslationUnit> for RTransUnit {
fn rfrom(value: TranslationUnit,context: &mut Context) -> Result<Self> {
let mut decls=vec![];
for ext in value.0 {
match ext {
ExternalDeclaration::Fn(function_def) => {
let (decl,fun)=<(Option<RDecl>,RFnDef)>::rfrom(function_def, context)?;
if let Some(declaration)=decl { decls.push(RExtDecl::Decl(declaration)); }
decls.push(RExtDecl::Fn(Box::new(fun)));
},
ExternalDeclaration::Decl(declaration) => {
decls.push(RExtDecl::Decl(RDecl::rfrom(declaration, context)?));
},
}
}
Ok(Self(decls))
}
}
impl Display for RTransUnit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,"{}",print_vec(&self.0, "\n"))
}
}
impl RTransUnit {
pub fn contains_main(&self) -> bool {
self.0.iter().any(RExtDecl::contains_main)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum RExtDecl {
Fn(Box<RFnDef>),
Decl(RDecl)
}
impl Display for RExtDecl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Fn(rfn_def) => write!(f,"{rfn_def}"),
Self::Decl(rdecl) => write!(f,"{}",rdecl.print_without_fn_decl()),
}
}
}
impl RExtDecl {
pub fn contains_main(&self) -> bool {
match self {
Self::Fn(rfn_def) => rfn_def.1.0.as_str()=="main",
Self::Decl(_) => false,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RFnDef(pub Option<Qualifier>, pub RIdent, pub Vec<Generic>, pub RParamList, pub RType, pub CompoundRStmt);
impl RFrom<FunctionDef> for (Option<RDecl>,RFnDef) {
fn rfrom(value: FunctionDef, context: &mut Context) -> Result<Self> {
let FunctionDef(specs,declarator,list,body)=value;
if let TypeOrTypedef::A(ty,q)=TypeOrTypedef::rfrom(*specs, context)? {
context.top_level=false;
let (decl,mut param_list,rident,ty)=parameter_list_from_declarator(ty,*declarator,list.map(|x| *x),context)?;
if rident.0.as_str()=="main" { context.inside_main=true; }
context.return_type=ty.clone();
if let RType::Typedef(_,def)=&context.return_type { context.return_type = *def.clone(); }
let mut generics=vec![];
for i in &mut param_list.0 {
if let Some(val)=&i.1 {
context.scope.0.push(RDeclSingle::Let(Let(i.0,val.clone(),i.2.clone(),None)));
}
let mut tmp=RType::default();
swap(&mut tmp, &mut i.2);
i.2=tmp.transform(context.flags, &mut generics);
}
context.scope.0.push(RDeclSingle::Let(Let(Qualifier::Static, rident.clone(), RType::Function(param_list.0.iter().map(|x| x.2.clone()).collect(), Box::new(ty.clone())), None)));
let body=(*body).rinto(context)?;
context.inside_main=false;
context.top_level=true;
context.return_type=RType::Unit;
Ok((decl,RFnDef(if matches!(q,Qualifier::Inline) { Some(q) } else { None },rident,generics,param_list,ty,body)))
}
else { Err(c2rust_err!("Tried to typedef the return type of a function definition!")) }
}
}
impl Display for RFnDef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Self(q,ident,generics,params,ty,body)=self;
let ty=
if ident.0.as_str()=="main" && ty.is_numeric() { "std::process::ExitCode".to_string() }
else { ty.to_string() };
if generics.is_empty() {
match q {
Some(q) => write!(f,"{q}pub fn {ident}({}) -> {ty} {body}",params.print_fn_params()),
None => write!(f,"pub fn {ident}({}) -> {ty} {body}",params.print_fn_params()),
}
}
else {
match q {
Some(q) => write!(f,"{q}pub fn {ident}<{}>({}) -> {ty} {body}",print_vec(generics, ", "),params.print_fn_params()),
None => write!(f,"pub fn {ident}<{}>({}) -> {ty} {body}",print_vec(generics, ", "),params.print_fn_params()),
}
}
}
}
fn get_ident_and_ret_ty(direct: DirectDeclarator,mut ret_ty: RType,postfix: &mut Vec<DeclPostfix>, flags: CompilerFlags) -> (Identifier,RType) {
match direct {
DirectDeclarator::Ident(identifier, mut items) => {
items.reverse();
postfix.append(&mut items);
(identifier,ret_ty)
},
DirectDeclarator::Decl(mut declarator, mut items) => {
items.reverse();
postfix.append(&mut items);
let ident;
loop {
if let Some(pointer)=declarator.0 {
for i in pointer.0 {
match i {
Some(ty) => {
ret_ty=
if ty.0.contains(&TypeQualifier::Const) { ret_ty.unsized_array(flags) }
else { ret_ty.unsized_array_mut(flags) }
},
None => ret_ty=ret_ty.unsized_array_mut(flags),
}
}
}
match *declarator.1 {
DirectDeclarator::Ident(identifier, mut items) => {
items.reverse();
postfix.append(&mut items);
ident=identifier;
break;
},
DirectDeclarator::Decl(decl, mut items) => {
items.reverse();
postfix.append(&mut items);
declarator=decl;
},
}
}
(ident,ret_ty)
},
}
}
fn ident_list(identifier_list: Option<IdentifierList>, r_decl_let: &[Let], decl: Option<RDecl>, ident: RIdent, ret_ty: RType) -> Result<(Option<RDecl>,RParamList,RIdent,RType)> {
let mut params=vec![];
match identifier_list {
Some(list) => {
for ident in list.0 {
let rident=RIdent::from(ident);
if let Some((q,ty))=r_decl_let.iter().find(|x| x.1==rident).map(|x| (x.0,x.2.clone())) {
params.push(RParam(q,Some(rident),ty));
}
else { return Err(c2rust_err!("Identifier '{rident}' inside identifier list does not have an accessible type!")) }
}
Ok((decl,RParamList(params),ident,ret_ty))
},
None => Ok((decl,RParamList(params),ident,ret_ty)),
}
}
fn parameter_list_from_declarator(ret_ty: RType, decl: Declarator, list: Option<DeclarationList>, context: &mut Context) -> Result<(Option<RDecl>,RParamList,RIdent,RType)> {
let mut postfix=vec![];
let mut tmp =vec![];
for x in list.unwrap_or(DeclarationList(vec![])).0 {
tmp.push(RDecl::rfrom(x, context)?);
}
let mut r_decl_let=vec![];
let mut r_decls=vec![];
for decl in tmp {
for d in decl.0 {
match d {
RDeclSingle::Let(let_) => r_decl_let.push(let_),
_ => r_decls.push(d),
}
}
}
let (ident,mut ret_ty)=get_ident_and_ret_ty(*decl.1,ret_ty,&mut postfix,context.flags);
postfix.iter().position(|x| matches!(x,DeclPostfix::Param(_)|DeclPostfix::IdentList(_))).map_or_else(|| Err(c2rust_err!("Function lacks a valid argument list!")), |pos| {
let params=safe_remove(&mut postfix, pos, c2rust_err!("Found argument list outside the bounds of its container in function declaration!"))?;
for item in postfix {
ret_ty=apply_decl_postfix_to_ty(item, ret_ty, context)?;
}
let ident=RIdent::from(ident);
let decl=if r_decls.is_empty() { None } else { Some(RDecl(r_decls)) };
match params {
DeclPostfix::Param(parameter_type_list) => {
let params=RParamList::rfrom(parameter_type_list.clone(), context);
if let Ok(params)=params { Ok((decl,params,ident,ret_ty)) }
else { ident_list(Some(parameter_type_list.into()),&r_decl_let,decl,ident,ret_ty) }
},
DeclPostfix::IdentList(identifier_list) => ident_list(identifier_list,&r_decl_let,decl,ident,ret_ty),
_ => Err(unimpl_err!("Unreachable!"))
}
})
}