ccarp 0.1.2

(trans)Compile C And Rust Partially
Documentation
//! CCARP - (trans)Compile C And Rust Partially
//! 
//! CCARP is a partial transpiler which can translate some C programs into Rust.
//! 
//! Public Interface:
//! - `translate` - this function translates an entire C project into Rust, however it does so in memory
//! - `translate_single_file` - this function translates a singular C file into Rust and writes the results to disk
//! - `translate_project` - this function translates an entire C project and writes the results to disk

#![recursion_limit = "256"]
#![warn(clippy::pedantic,clippy::nursery,clippy::complexity,clippy::perf,clippy::correctness,clippy::all)]
#![warn(clippy::cognitive_complexity,clippy::large_const_arrays)]
#![warn(clippy::style,clippy::suspicious,large_assignments,rustdoc::all)]
#![warn(missing_docs)]
#![allow(clippy::must_use_candidate,clippy::wildcard_imports,clippy::enum_glob_use,clippy::missing_panics_doc,clippy::missing_errors_doc)]

pub(crate) mod ccarp;
pub(crate) mod ccarp_c;
pub(crate) mod ccarp_rust;

pub use ccarp::error::{CCErr,Result,ProjectResult};
pub use ccarp::translator::*;

#[cfg(test)]
mod unit_tests {
    use pest::Parser;

    use crate::{ccarp::error::{parse_err, rule_err}, ccarp_c::{decl::*, defs::*, expr::*, stmt::Statement, trans_unit::{FunctionDef, TranslationUnit}, tt::*}, ccarp_rust::{defs::{Context, RFrom}, rustdecl::RDecl, rustexpr::RExpr}, CCErr, Result};

    fn tokenise(string: &str) -> Result<(String,Vec<Include>)> {
        let string=string.replace("\\\n", "");
        let parsed=ParserTy::parse(Rule::tokentree, &string).map_err(|e| parse_err!(e))?.next().ok_or_else(|| rule_err!("Parsed code is empty!"))?;
        let tt=TokenTree::take(parsed)?;
        let (includes,tt): (Vec<Token>,Vec<Token>)=tt.0.into_iter().partition(|x| matches!(x,Token::Include(_)));
        let includes=includes.into_iter().map(|x| if let Token::Include(incl)=x { incl } else { Include(String::new()) /* unreachable */ }).collect();
        Ok((format!("{}",TokenTree(tt)),includes))
    }

    #[test]
    fn parsing_ok() {
        assert!(ParserTy::parse(Rule::tokentree, "int main() { return 0; }").is_ok());
    }
    #[test]
    fn parsing_ident() {
        let parsed=ParserTy::parse(Rule::ident, "ident");
        assert!(parsed.is_ok());
        let ident=Identifier::take(parsed.unwrap().next().unwrap());
        assert!(ident.is_ok(),"Error: {}",ident.unwrap_err());
        assert_eq!(ident.unwrap(),Identifier(String::from("ident")));
    }
    #[test]
    fn parsing_tt() {
        let parsed=ParserTy::parse(Rule::tokentree, "struct ABStruct { int a,b; char* c; }");
        assert!(parsed.is_ok());
        let tt=TokenTree::take(parsed.unwrap().next().unwrap());
        assert!(tt.is_ok(),"Error: {}",tt.unwrap_err());
        
        assert_eq!(tt.unwrap(),TokenTree(vec![
            Token::Keyword(Keyword(String::from("struct"))),
            Token::Identifier(Identifier(String::from("ABStruct"))),
            Token::Punctuator(Punctuator(String::from("{"))),
            Token::Keyword(Keyword(String::from("int"))),
            Token::Identifier(Identifier(String::from("a"))),
            Token::Punctuator(Punctuator(String::from(","))),
            Token::Identifier(Identifier(String::from("b"))),
            Token::Punctuator(Punctuator(String::from(";"))),
            Token::Keyword(Keyword(String::from("char"))),
            Token::Punctuator(Punctuator(String::from("*"))),
            Token::Identifier(Identifier(String::from("c"))),
            Token::Punctuator(Punctuator(String::from(";"))),
            Token::Punctuator(Punctuator(String::from("}")))
        ]));
    }
    #[test]
    fn tokenisation() {
        let res=tokenise("int func(long long a, char *b) { return (int)a; }");
        assert!(res.is_ok());
        assert_eq!(res.unwrap().0.as_str(),"int #ifunc#i #p(#p long long #ia#i #p,#p char #p*#p #ib#i #p)#p #p{#p return #p(#p int #p)#p #ia#i #p;#p #p}#p");
    }
    #[test]
    fn parsing_expr() {
        let tt=tokenise("2+2");
        assert!(tt.is_ok());
        let tt=tt.unwrap();
        let pair=ParserTy::parse(Rule::expr, tt.0.as_str());
        let expr=Expression::take(pair.unwrap().next().unwrap());
        assert!(expr.is_ok());
    }
    #[test]
    fn parsing_decl() {
        let tt=tokenise("int a=2;");
        assert!(tt.is_ok());
        let tt=tt.unwrap();
        let pair=ParserTy::parse(Rule::decl, tt.0.as_str());
        let decl=Declaration::take(pair.unwrap().next().unwrap());
        assert!(decl.is_ok());
    }
    #[test]
    fn parsing_stmt() {
        let tt=tokenise("if (i<10) { for(int j=0;j<100;j++) i+=1; } else { return 0; }");
        assert!(tt.is_ok());
        let tt=tt.unwrap();
        let pair=ParserTy::parse(Rule::stmt, tt.0.as_str());
        let stmt=Statement::take(pair.unwrap().next().unwrap());
        assert!(stmt.is_ok());
    }
    #[test]
    fn parsing_fn() {
        let tt=tokenise("int main() { int a=1,b=0,*c,d=2; a++; b+=a+d; return 0; }");
        assert!(tt.is_ok());
        let tt=tt.unwrap();
        let pair=ParserTy::parse(Rule::fn_def, tt.0.as_str());
        let fun=FunctionDef::take(pair.unwrap().next().unwrap());
        assert!(fun.is_ok());
    }
    #[test]
    fn parsing_translation_unit() {
        let tt=tokenise("int a; char *b; struct CDStruct { int c,d; }; int main() { int a=1,b=0,*c,d=2; a++; b+=a+d; return 0; }");
        assert!(tt.is_ok());
        let tt=tt.unwrap();
        let pair=ParserTy::parse(Rule::trans_unit, tt.0.as_str());
        let unit=TranslationUnit::take(pair.unwrap().next().unwrap());
        assert!(unit.is_ok());
    }
    #[test]
    fn translate_expr() {
        let tt=tokenise("2+2");
        assert!(tt.is_ok());
        let tt=tt.unwrap();
        let pair=ParserTy::parse(Rule::expr, tt.0.as_str());
        let expr=Expression::take(pair.unwrap().next().unwrap());
        assert!(expr.is_ok());
        let expr=RExpr::rfrom(expr.unwrap(), &mut Context::default());
        assert!(expr.is_ok());
        assert!(expr.unwrap().to_string().as_str()=="2 + 2");
    }
    #[test]
    fn translate_decl() {
        let tt=tokenise("int a=0,b=2;");
        assert!(tt.is_ok());
        let tt=tt.unwrap();
        let pair=ParserTy::parse(Rule::decl, tt.0.as_str());
        let decl=Declaration::take(pair.unwrap().next().unwrap());
        assert!(decl.is_ok());
        let decl=RDecl::rfrom(decl.unwrap(), &mut Context::default());
        assert!(decl.is_ok());
        assert!(decl.unwrap().to_string().as_str()=="let mut a: i32=(0);\nlet mut b: i32=(2);");
    }
}