mod consts;
mod error;
mod lex;
mod parser;
mod types;
pub use error::{MexeError, Result};
use types::{Operator, Token};
pub fn eval<T>(expression: T) -> Result<f64>
where
T: AsRef<str>,
{
let tokens = lex::get_tokens(expression.as_ref())?;
parser::parse_and_evaluate(tokens)
}
#[deprecated(since="0.2.0", note="please use `eval` instead")]
pub fn eval_binary<T>(expression: T) -> Result<f64>
where
T: AsRef<str>,
{
let tokens = lex::get_tokens(expression.as_ref())?;
if tokens.len() != 4 || tokens[3] != Token::EOI {
return Err(MexeError::InvalidBinaryExpression);
}
let lhs = match tokens.get(0).unwrap() {
Token::Number(n) => n,
_ => return Err(MexeError::MissingOperand),
};
let rhs = match tokens.get(2).unwrap() {
Token::Number(n) => n,
_ => return Err(MexeError::MissingOperand),
};
match tokens.get(1).unwrap() {
Token::Op(Operator::Plus) => Ok(lhs + rhs),
Token::Op(Operator::Minus) => Ok(lhs - rhs),
Token::Op(Operator::Mul) => Ok(lhs * rhs),
Token::Op(Operator::Div) => Ok(lhs / rhs),
_ => Err(MexeError::MissingOperator),
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! float_eq {
($op1:expr, $op2:expr) => {
assert!(float_cmp::approx_eq!(f64, $op1, $op2));
};
}
#[test]
fn eval_does_not_panic_with_empty_input() {
let _val = eval("");
let empty: &[u8] = &[];
let _val = eval(std::str::from_utf8(empty).unwrap());
}
#[test]
fn eval_does_not_panic_with_bad_input() {
let exprs = ["(1"];
for expr in exprs.iter() {
let _val = eval(expr);
}
}
#[test]
fn test_eval() {
float_eq!(1.0, eval("1").unwrap());
float_eq!(-1.0, eval("-1").unwrap());
float_eq!(1.0, eval("(1)").unwrap());
float_eq!(1.0, eval("((1))").unwrap());
float_eq!(-1.0, eval("-(1)").unwrap());
float_eq!(2.0, eval("1 + 1").unwrap());
float_eq!(0.0, eval("1 - 1").unwrap());
float_eq!(1.1, eval("(1+1.1) - 1").unwrap());
float_eq!(1.1, eval("(1+(1.1)) - 1").unwrap());
float_eq!(1.1, eval("(1+(1.1 + 0)) - 1").unwrap());
float_eq!(3.0, eval("(1+(1.0 + 0) + 2) - 1").unwrap());
float_eq!(2.0, eval("(((1))) + ((((1))))").unwrap());
float_eq!(21.0, eval("(1 + (4 * 5))").unwrap());
float_eq!(10.5, eval("(1 + (4 * 5)) / 2").unwrap());
float_eq!(18.0, eval("1 + (4 * 5) - 9 / 3").unwrap());
float_eq!(8.4, eval("(1 + (4 * 5)) / 2 - 3 * 0.7").unwrap());
float_eq!(9.9, eval("(1 + ((4 * 5) + (3))) / 2 - 3 * 0.7").unwrap());
float_eq!(0.45, eval("0.15 + 0.15 + 0.15").unwrap());
}
#[test]
fn test_eval_failures() {
let exprs = ["(((1", "((1", "(1", "1)))", "1))", "1)"];
for expr in exprs.iter() {
if let Ok(_) = eval(expr) {
panic!("{} should not be parsed", expr);
}
}
}
#[test]
fn correct_errors_are_returned() {
assert_eq!(eval("1++"), Err(MexeError::UnexpectedToken("+".to_owned())));
}
}