From ba827ea23b170365e86564912bf7c213af1b0c2f Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Wed, 7 Aug 2024 07:46:17 -0400 Subject: calc: refactor to freestanding functions --- calc/src/lib.rs | 272 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 137 insertions(+), 135 deletions(-) diff --git a/calc/src/lib.rs b/calc/src/lib.rs index 05b83a9..c3ca3fd 100644 --- a/calc/src/lib.rs +++ b/calc/src/lib.rs @@ -1,14 +1,20 @@ -use lazy_static::lazy_static; -use pest::iterators::Pair; -use rand::Rng; +use pest::{ + iterators::{ + Pair, + Pairs, + }, + pratt_parser::PrattParser, + Parser, +}; +use rand::Rng as _; #[derive(pest_derive::Parser)] #[grammar = "calc.pest"] pub struct Calc; #[derive(Copy, Clone, thiserror::Error, Debug, PartialEq, Eq, Hash)] -pub enum CalcError { - #[error("pest was unable to parse the input")] +pub enum Error { + #[error("unable to parse input")] Pest, #[error("invalid number format")] @@ -18,151 +24,147 @@ pub enum CalcError { ArgCount, } -impl Calc { - pub fn eval>(s: S) -> Result { - use pest::{ - iterators::Pairs, - pratt_parser::PrattParser, - Parser, +lazy_static::lazy_static! { + static ref PRATT_PARSER: PrattParser = { + use pest::pratt_parser::{ + Assoc::*, + Op, }; + use Rule::*; + + PrattParser::new() + .op(Op::infix(add, Left) | Op::infix(sub, Left) | Op::infix(modulo, Left)) + .op(Op::infix(mul, Left) | Op::infix(div, Left)) + .op(Op::infix(dice, Left)) + .op(Op::infix(pow, Right)) + .op(Op::postfix(EOI)) // discarded below + }; +} - use self::Rule::*; - - lazy_static! { - static ref CLIMBER: PrattParser = { - use pest::pratt_parser::{ - Assoc::*, - Op, - }; - - PrattParser::new() - .op(Op::infix(add, Left) | Op::infix(sub, Left) | Op::infix(modulo, Left)) - .op(Op::infix(mul, Left) | Op::infix(div, Left)) - .op(Op::infix(dice, Left)) - .op(Op::infix(pow, Right)) - .op(Op::postfix(EOI)) // discarded below +impl Calc { + #[inline] + pub fn eval>(s: S) -> Result { + let result = Calc::parse(Rule::calc, s.as_ref()).map_err(|_| Error::Pest)?; + eval_expr(result) + } +} + +fn eval_single_pair(pair: Pair) -> Result { + use Rule::*; + + let result = match pair.as_rule() { + oct | hex | binary => { + let base = match pair.as_rule() { + hex => 16, + oct => 8, + binary => 2, + _ => unreachable!(), }; - } - - let result = Calc::parse(calc, s.as_ref()).map_err(|_| CalcError::Pest)?; - - fn eval_single_pair(pair: Pair) -> Result { - let result = match pair.as_rule() { - oct | hex | binary => { - let base = match pair.as_rule() { - hex => 16, - oct => 8, - binary => 2, - _ => unreachable!(), - }; - - u64::from_str_radix(&pair.as_str()[2..], base) - .map_err(|_| CalcError::NumberFormat)? as f64 - }, - float => pair.as_str().parse::().map_err(|_| CalcError::NumberFormat)?, - expr | num => eval_expr(pair.into_inner())?, - unary_expr => { - let mut p = pair.into_inner(); - - let op = p.next().ok_or(CalcError::ArgCount)?; - let arg = eval_expr(p)?; - - match op.as_rule() { - log => arg.ln(), - sqrt => arg.sqrt(), - sgn => arg.signum(), - - sin => arg.sin(), - cos => arg.cos(), - tan => arg.tan(), - asin => arg.asin(), - acos => arg.acos(), - atan => arg.atan(), - - sinh => arg.sinh(), - cosh => arg.cosh(), - tanh => arg.tanh(), - asinh => arg.asinh(), - acosh => arg.acosh(), - atanh => arg.atanh(), - - exp => arg.exp(), - abs => arg.abs(), - ceil => arg.ceil(), - floor => arg.floor(), - round => arg.round(), - _ => unreachable!(), - } - }, - binary_expr => { - let mut p = pair.into_inner(); - let op = p.next().ok_or(CalcError::ArgCount)?; + u64::from_str_radix(&pair.as_str()[2..], base).map_err(|_| Error::NumberFormat)? as f64 + }, + float => pair.as_str().parse::().map_err(|_| Error::NumberFormat)?, + expr | num => eval_expr(pair.into_inner())?, + unary_expr => { + let mut p = pair.into_inner(); + + let op = p.next().ok_or(Error::ArgCount)?; + let arg = eval_expr(p)?; + + match op.as_rule() { + log => arg.ln(), + sqrt => arg.sqrt(), + sgn => arg.signum(), + + sin => arg.sin(), + cos => arg.cos(), + tan => arg.tan(), + asin => arg.asin(), + acos => arg.acos(), + atan => arg.atan(), + + sinh => arg.sinh(), + cosh => arg.cosh(), + tanh => arg.tanh(), + asinh => arg.asinh(), + acosh => arg.acosh(), + atanh => arg.atanh(), + + exp => arg.exp(), + abs => arg.abs(), + ceil => arg.ceil(), + floor => arg.floor(), + round => arg.round(), + _ => unreachable!(), + } + }, + binary_expr => { + let mut p = pair.into_inner(); - let arg1 = eval_single_pair(p.next().ok_or(CalcError::ArgCount)?)?; - let arg2 = eval_single_pair(p.next().ok_or(CalcError::ArgCount)?)?; + let op = p.next().ok_or(Error::ArgCount)?; - assert!(p.next().is_none()); + let arg1 = eval_single_pair(p.next().ok_or(Error::ArgCount)?)?; + let arg2 = eval_single_pair(p.next().ok_or(Error::ArgCount)?)?; - match op.as_rule() { - min => arg1.min(arg2), - max => arg1.max(arg2), - atan2 => arg1.atan2(arg2), - _ => unreachable!(), - } - }, - suffix_expr => { - let mut p = pair.into_inner(); + assert!(p.next().is_none()); + + match op.as_rule() { + min => arg1.min(arg2), + max => arg1.max(arg2), + atan2 => arg1.atan2(arg2), + _ => unreachable!(), + } + }, + suffix_expr => { + let mut p = pair.into_inner(); + + let arg = eval_expr(p.next().ok_or(Error::ArgCount)?.into_inner())?; + let op = p.next().ok_or(Error::ArgCount)?; - let arg = eval_expr(p.next().ok_or(CalcError::ArgCount)?.into_inner())?; - let op = p.next().ok_or(CalcError::ArgCount)?; + assert!(p.next().is_none()); - assert!(p.next().is_none()); + match op.as_rule() { + factorial => statrs::function::gamma::gamma(arg + 1.), + _ => unreachable!(), + } + }, + _ => unreachable!(), + }; - match op.as_rule() { - factorial => statrs::function::gamma::gamma(arg + 1.), - _ => unreachable!(), - } + Ok(result) +} + +fn eval_expr(p: Pairs) -> Result { + use Rule::*; + + PRATT_PARSER + .map_primary(eval_single_pair) + .map_infix(|lhs, op, rhs| { + let lhs = lhs?; + let rhs = rhs?; + + let result = match op.as_rule() { + add => lhs + rhs, + sub => lhs - rhs, + mul => lhs * rhs, + div => lhs / rhs, + pow => lhs.powf(rhs), + dice => { + let dice_count = lhs as usize; + let dice_faces = rhs as usize; + + let mut rng = rand::thread_rng(); + (0..dice_count) + .map(|_| rng.gen_range(1..(dice_faces + 1))) + .sum::() as f64 }, _ => unreachable!(), }; Ok(result) - } - - pub fn eval_expr(p: Pairs) -> Result { - CLIMBER - .map_primary(eval_single_pair) - .map_infix(|lhs, op, rhs| { - let lhs = lhs?; - let rhs = rhs?; - - let result = match op.as_rule() { - add => lhs + rhs, - sub => lhs - rhs, - mul => lhs * rhs, - div => lhs / rhs, - pow => lhs.powf(rhs), - dice => { - let dice_count = lhs as usize; - let dice_faces = rhs as usize; - - let mut rng = rand::thread_rng(); - (0..dice_count) - .map(|_| rng.gen_range(1..(dice_faces + 1))) - .sum::() as f64 - }, - _ => unreachable!(), - }; - - Ok(result) - }) - .map_postfix(|arg, _post| arg) // discard EOI - .parse(p) - } - - eval_expr(result) - } + }) + .map_postfix(|arg, _post| arg) // discard EOI + .parse(p) } #[cfg(test)] -- cgit v1.3.1