mathew 0.0.2

Mathematical expression evaluator with context
Documentation
use syn::{BinOp, Expr, ExprBinary, ExprMethodCall, ExprParen, ExprPath, ExprUnary, Lit};

use super::{Operator, Output, Reflect, Value};

impl<'a> Reflect<'a> {
    pub(super) fn visit_expr(&mut self, e: &'a Expr) {
        use syn::Expr::*;
        match e {
            Binary(i) => self.visit_expr_binary(i),
            Lit(i) => self.visit_lit(&i.lit),
            MethodCall(i) => self.visit_expr_method_call(i),
            Paren(i) => self.visit_expr_paren(i),
            Path(i) => self.visit_expr_path(i),
            Unary(i) => self.visit_expr_unary(i),
            _ => self.on_err = true,
        }
    }

    #[inline]
    fn visit_bin_op(&mut self, b: &'a BinOp) {
        use syn::BinOp::*;
        match *b {
            Add(_) => self.push_op(Operator::Add),
            Sub(_) => self.push_op(Operator::Sub),
            Mul(_) => self.push_op(Operator::Mul),
            Div(_) => self.push_op(Operator::Div),
            Rem(_) => self.push_op(Operator::Rem),
            _ => self.on_err = true,
        }
    }

    #[inline]
    fn visit_expr_binary(
        &mut self,
        ExprBinary {
            left, op, right, ..
        }: &'a ExprBinary,
    ) {
        self.visit_expr(left);
        self.visit_bin_op(op);
        self.visit_expr(right);
    }

    #[inline]
    fn visit_expr_method_call(
        &mut self,
        ExprMethodCall {
            receiver,
            method,
            args,
            ..
        }: &'a ExprMethodCall,
    ) {
        self.push_op(Operator::ParenLeft);
        self.visit_expr(receiver);
        self.push_op(Operator::ParenRight);
        let method: &str = &quote!(#method).to_string();

        use super::function::Fun::*;
        let method = match method {
            "abs" => Abs,
            "acos" => Acos,
            "acosh" => Acosh,
            "asin" => Asin,
            "asinh" => Asinh,
            "atan" => Atan,
            "atan2" => Atan2,
            "atanh" => Atanh,
            "cbrt" => Cbrt,
            "ceil" => Ceil,
            "cos" => Cos,
            "cosh" => Cosh,
            "exp" => Exp,
            "exp2" => Exp2,
            "exp_m1" => ExpM1,
            "floor" => Floor,
            "fract" => Fract,
            "hypot" => Hypot,
            "ln" => Ln,
            "ln_1p" => Ln1p,
            "log" => Log,
            "log10" => Log10,
            "log2" => Log2,
            "max" => Max,
            "min" => Min,
            "powf" => PowF,
            "powi" => PowI,
            "recip" => Recip,
            "round" => Round,
            "signum" => Signum,
            "sin" => Sin,
            "sinh" => Sinh,
            "sqrt" => Sqrt,
            "tan" => Tan,
            "tanh" => Tanh,
            "to_degrees" => ToDegrees,
            "to_radians" => ToRadians,
            "trunc" => Trunc,
            _ => return self.on_err = true,
        };

        if method.arg() {
            if args.len() != 1 {
                return self.on_err = true;
            }

            self.push_op(Operator::ParenLeft);
            self.visit_expr(&args[0]);
            self.push_op(Operator::ParenRight);
            self.output.push(Output::Fn(method));
        } else {
            if !args.is_empty() {
                return self.on_err = true;
            }
            self.output.push(Output::Fn(method));
        }
    }

    #[inline]
    fn visit_expr_paren(&mut self, ExprParen { expr, .. }: &'a ExprParen) {
        self.push_op(Operator::ParenLeft);
        self.visit_expr(expr);
        self.push_op(Operator::ParenRight);
    }

    #[inline]
    fn visit_expr_path(&mut self, ExprPath { path, .. }: &'a ExprPath) {
        let path: &str = &quote!(#path).to_string();
        if let Some(src) = self.ctx.get(&path) {
            self.push_op(Operator::ParenLeft);
            self.visit_expr(src);
            self.push_op(Operator::ParenRight);
        } else {
            self.on_err = true;
        }
    }

    #[inline]
    fn visit_expr_unary(&mut self, ExprUnary { op, expr, .. }: &'a ExprUnary) {
        self.visit_expr(expr);
        use syn::UnOp::*;
        match op {
            Neg(_) => {
                self.push_op(Operator::Neg);
            }
            _ => return self.on_err = true,
        }
    }

    #[inline]
    fn visit_lit(&mut self, l: &'a Lit) {
        use super::Output::V;
        use syn::Lit::*;
        match l {
            Int(a) => self.output.push(V(a.value() as Value)),
            Float(a) => self.output.push(V(a.value() as Value)),
            _ => self.on_err = true,
        }
    }
}