formula 0.1.0

A parser and evaluator of spreadsheet-like formulas
Documentation
use crate::{error::Error, Expr, Formula, Result, Rule};
use pest::iterators::Pair;

impl Formula<'_> {
    pub(crate) fn parse_add(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => operand1 + operand2,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Number(res))
    }

    pub(crate) fn parse_sub(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => operand1 - operand2,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Number(res))
    }

    pub(crate) fn parse_mul(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => operand1 * operand2,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Number(res))
    }

    pub(crate) fn parse_div(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => {
                if operand2 == 0.0 {
                    Expr::Null
                } else {
                    Expr::Number(operand1 / operand2)
                }
            }
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(res)
    }

    pub(crate) fn parse_pow(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => operand1.powf(operand2),
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Number(res))
    }

    pub(crate) fn parse_eq(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => operand1 == operand2,
            (Expr::String(operand1), Expr::String(operand2)) => operand1 == operand2,
            (Expr::Time(operand1), Expr::Time(operand2)) => operand1 == operand2,
            (Expr::Datetime(operand1), Expr::Datetime(operand2)) => operand1 == operand2,
            (Expr::Date(operand1), Expr::Date(operand2)) => operand1 == operand2,
            (Expr::Bool(operand1), Expr::Bool(operand2)) => operand1 == operand2,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Bool(res))
    }

    pub(crate) fn parse_ne(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => (operand1 - operand2).abs() > 0.000_000_1,
            (Expr::String(operand1), Expr::String(operand2)) => operand1 != operand2,
            (Expr::Time(operand1), Expr::Time(operand2)) => operand1 != operand2,
            (Expr::Datetime(operand1), Expr::Datetime(operand2)) => operand1 != operand2,
            (Expr::Date(operand1), Expr::Date(operand2)) => operand1 != operand2,
            (Expr::Bool(operand1), Expr::Bool(operand2)) => operand1 != operand2,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Bool(res))
    }

    pub(crate) fn parse_gt(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => operand1 > operand2,
            (Expr::String(operand1), Expr::String(operand2)) => operand1 > operand2,
            (Expr::Time(operand1), Expr::Time(operand2)) => operand1 > operand2,
            (Expr::Datetime(operand1), Expr::Datetime(operand2)) => operand1 > operand2,
            (Expr::Date(operand1), Expr::Date(operand2)) => operand1 > operand2,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Bool(res))
    }

    pub(crate) fn parse_lt(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => operand1 < operand2,
            (Expr::String(operand1), Expr::String(operand2)) => operand1 < operand2,
            (Expr::Time(operand1), Expr::Time(operand2)) => operand1 < operand2,
            (Expr::Datetime(operand1), Expr::Datetime(operand2)) => operand1 < operand2,
            (Expr::Date(operand1), Expr::Date(operand2)) => operand1 < operand2,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Bool(res))
    }

    pub(crate) fn parse_gte(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => operand1 >= operand2,
            (Expr::String(operand1), Expr::String(operand2)) => operand1 >= operand2,
            (Expr::Time(operand1), Expr::Time(operand2)) => operand1 >= operand2,
            (Expr::Datetime(operand1), Expr::Datetime(operand2)) => operand1 >= operand2,
            (Expr::Date(operand1), Expr::Date(operand2)) => operand1 >= operand2,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Bool(res))
    }

    pub(crate) fn parse_lte(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand1 = Self::get_formula(&mut args, &rule_name)?;
        let operand2 = Self::get_formula(&mut args, &rule_name)?;

        let res = match (operand1, operand2) {
            (Expr::Number(operand1), Expr::Number(operand2)) => operand1 <= operand2,
            (Expr::String(operand1), Expr::String(operand2)) => operand1 <= operand2,
            (Expr::Time(operand1), Expr::Time(operand2)) => operand1 <= operand2,
            (Expr::Datetime(operand1), Expr::Datetime(operand2)) => operand1 <= operand2,
            (Expr::Date(operand1), Expr::Date(operand2)) => operand1 <= operand2,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Bool(res))
    }

    pub(crate) fn parse_percent(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand = Self::get_formula(&mut args, &rule_name)?;

        let res = match operand {
            Expr::Number(operand) => operand / 100.0,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Number(res))
    }

    pub(crate) fn parse_negate(pair: Pair<Rule>) -> Result<Expr> {
        let rule_name = format!("{:?}", &pair.as_rule());
        let mut args = pair.into_inner();
        let operand = Self::get_formula(&mut args, &rule_name)?;

        let res = match operand {
            Expr::Number(operand) => -operand,
            _ => return Err(Error::Parser(rule_name)),
        };
        Ok(Expr::Number(res))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::Formula;

    #[test]
    #[allow(clippy::too_many_lines)]
    fn test_parse_web_types() {
        let formula = Formula::new("=F.ADD(5,6)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Number(11.0));

        let formula = Formula::new("=F.SUB(5,6)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Number(-1.0));

        let formula = Formula::new("=F.MUL(5,6)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Number(30.0));

        let formula = Formula::new("=F.DIV(10,2)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Number(5.0));

        let formula = Formula::new("=F.DIV(10,0)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Null);

        let formula = Formula::new("=F.MUL(5,6)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Number(30.0));

        let formula = Formula::new("=F.POW(5,2)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Number(25.0));

        let formula = Formula::new("=F.EQ(5,2)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Bool(false));

        let formula = Formula::new("=F.NE('a','a')").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Bool(false));

        let formula = Formula::new("=F.GT('a','a')").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Bool(false));

        let formula = Formula::new("=F.LT(5,6)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Bool(true));

        let formula = Formula::new("=F.GTE(5,5)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Bool(true));

        let formula = Formula::new("=F.LTE(6,5)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Bool(false));

        let formula = Formula::new("=F.PERCENT(5)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Number(0.05));

        let formula = Formula::new("=F.NEGATE(5)").unwrap();
        let value = formula.parse().unwrap();
        assert_eq!(value, Expr::Number(-5.0));
    }
}