use crate::ast::{Assignment, CmpOp, Symbol, Term}; use quote::ToTokens; use syn::{parse_quote, Expr, Ident, Stmt, Token}; impl Symbol { pub fn to_ident(&self) -> Ident { let sym = &self.0; syn::Ident::new(sym, proc_macro2::Span::call_site()) } pub fn to_value(&self) -> Expr { if self.0 == "pi" { parse_quote! { std::f64::consts::PI } } else { let s = self.to_ident(); parse_quote! { #s } } } } impl Term { pub fn ast_to_syn(&self) -> Expr { match self { Term::Number(x) => { parse_quote! { #x } } Term::EulersNumber => { parse_quote! { std::f64::consts::E } } Term::Symbol(sym) => sym.to_value(), Term::Sum(vec) => { let vec: Vec = vec.iter().map(Term::ast_to_syn).collect(); parse_quote! { #(#vec)+* } } Term::Mul(vec) => { let vec: Vec = vec.iter().map(Term::ast_to_syn).collect(); parse_quote! { #((#vec) *)* 1.0 } } Term::Pow(b) => { let base = b.0.ast_to_syn(); let exp = b.1.ast_to_syn(); let integer_exponent = match b.1 { Term::Number(x) => x == (x as i32) as f64, _ => false, }; if integer_exponent { // powi is faster than powf parse_quote! { (#base).powi(#exp as i32) } } else { parse_quote! { (#base).powf(#exp) } } } Term::Sin(b) => { let x = b.ast_to_syn(); parse_quote! { (#x).sin() } } Term::Cos(b) => { let x = b.ast_to_syn(); parse_quote! { (#x).cos() } } Term::SectionWise(v) => { let vec: Vec = v .iter() .map(|(term, cond)| { let op: Box = match cond.cmp_op { CmpOp::Equal => Box::new(::default()), CmpOp::NotEqual => Box::new(::default()), CmpOp::Lesser => Box::new(::default()), CmpOp::Greater => Box::new(]>::default()), CmpOp::LesserEqual => Box::new(::default()), CmpOp::GreaterEqual => Box::new(=]>::default()), }; let lhs = cond.lhs.ast_to_syn(); let rhs = cond.rhs.ast_to_syn(); let term = term.ast_to_syn(); let ret = parse_quote!( if #lhs #op #rhs { #term } ); ret }) .collect(); parse_quote! { #(#vec)else* else { panic!("outside of every section"); } } } } } } impl Assignment { #[allow(dead_code)] pub fn ast_to_syn(&self) -> Stmt { let lhs = self.lhs.to_ident(); let rhs = self.rhs.ast_to_syn(); parse_quote! { let #lhs = #rhs; } } } #[cfg(test)] mod tests { use crate::pest_parse; use quote::ToTokens; #[test] fn check_if_convertible() { let term = pest_parse::parse_term( r"(2\pi \sin\psi)\sin(\psi+\theta)/R^2 - \frac\pi 3 (1-\cos\psi)^2(2+\cos\psi) + \pi \sin^2\psi y/R", ); term.ast_to_syn().to_token_stream(); } }