Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ version = "0.2.0"
exclude = ["README.tpl", ".travis.yml"]

[dependencies]
fnv = "1.0.5"
nom = "1.0.0"
fnv = "1.0.7"
nom = "7.1.0"
serde = { version = "1", optional = true }

[dev-dependencies]
gnuplot = "0.0.23"
gnuplot = "0.0.37"
serde_test = "1"
serde_derive = "1"
serde_json = "1"
toml = "0.4.5"
toml = "0.5.8"

[features]
default = []
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ supported:
- functions implemented using functions of the same name in [Rust std library][std-float]:

- `sqrt`, `abs`
- `exp`, `ln`, `log10`
- `exp`, `ln`, `log10` (`log10` can also be called as `log`)
- `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`
- `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`
- `floor`, `ceil`, `round`
Expand Down
55 changes: 27 additions & 28 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl Expr {
where
C: ContextProvider + 'a,
{
try!(self.check_context(((var, 0.), &ctx)));
self.check_context(((var, 0.), &ctx))?;
let var = var.to_owned();
Ok(move |x| {
self.eval_with_context(((&var, x), &ctx))
Expand Down Expand Up @@ -194,7 +194,7 @@ impl Expr {
where
C: ContextProvider + 'a,
{
try!(self.check_context(([(var1, 0.), (var2, 0.)], &ctx)));
self.check_context(([(var1, 0.), (var2, 0.)], &ctx))?;
let var1 = var1.to_owned();
let var2 = var2.to_owned();
Ok(move |x, y| {
Expand Down Expand Up @@ -239,7 +239,7 @@ impl Expr {
where
C: ContextProvider + 'a,
{
try!(self.check_context(([(var1, 0.), (var2, 0.), (var3, 0.)], &ctx)));
self.check_context(([(var1, 0.), (var2, 0.), (var3, 0.)], &ctx))?;
let var1 = var1.to_owned();
let var2 = var2.to_owned();
let var3 = var3.to_owned();
Expand Down Expand Up @@ -287,7 +287,7 @@ impl Expr {
where
C: ContextProvider + 'a,
{
try!(self.check_context(([(var1, 0.), (var2, 0.), (var3, 0.), (var4, 0.)], &ctx)));
self.check_context(([(var1, 0.), (var2, 0.), (var3, 0.), (var4, 0.)], &ctx))?;
let var1 = var1.to_owned();
let var2 = var2.to_owned();
let var3 = var3.to_owned();
Expand Down Expand Up @@ -338,10 +338,10 @@ impl Expr {
where
C: ContextProvider + 'a,
{
try!(self.check_context((
self.check_context((
[(var1, 0.), (var2, 0.), (var3, 0.), (var4, 0.), (var5, 0.)],
&ctx
)));
&ctx,
))?;
let var1 = var1.to_owned();
let var2 = var2.to_owned();
let var3 = var3.to_owned();
Expand Down Expand Up @@ -389,17 +389,15 @@ impl Expr {
C: ContextProvider + 'a,
{
let n = vars.len();
try!(self.check_context((
vars.into_iter()
.zip(vec![0.; n].into_iter())
.collect::<Vec<_>>(),
&ctx
)));
self.check_context((
vars.iter().zip(vec![0.; n].into_iter()).collect::<Vec<_>>(),
&ctx,
))?;
let vars = vars.iter().map(|v| v.to_owned()).collect::<Vec<_>>();
Ok(move |x: &[f64]| {
self.eval_with_context((
vars.iter()
.zip(x.into_iter())
.zip(x.iter())
.map(|(v, x)| (v, *x))
.collect::<Vec<_>>(),
&ctx,
Expand Down Expand Up @@ -447,7 +445,7 @@ impl Expr {

/// Evaluates a string with built-in constants and functions.
pub fn eval_str<S: AsRef<str>>(expr: S) -> Result<f64, Error> {
let expr = try!(Expr::from_str(expr.as_ref()));
let expr = Expr::from_str(expr.as_ref())?;

expr.eval_with_context(builtin())
}
Expand All @@ -456,11 +454,11 @@ impl FromStr for Expr {
type Err = Error;
/// Constructs an expression by parsing a string.
fn from_str(s: &str) -> Result<Self, Self::Err> {
let tokens = try!(tokenize(s));
let tokens = tokenize(s)?;

let rpn = try!(to_rpn(&tokens));
let rpn = to_rpn(&tokens)?;

Ok(Expr { rpn: rpn })
Ok(Expr { rpn })
}
}

Expand All @@ -471,7 +469,7 @@ pub fn eval_str_with_context<S: AsRef<str>, C: ContextProvider>(
expr: S,
ctx: C,
) -> Result<f64, Error> {
let expr = try!(Expr::from_str(expr.as_ref()));
let expr = Expr::from_str(expr.as_ref())?;

expr.eval_with_context(ctx)
}
Expand Down Expand Up @@ -599,21 +597,21 @@ pub fn builtin<'a>() -> Context<'a> {

impl<'a, T: ContextProvider> ContextProvider for &'a T {
fn get_var(&self, name: &str) -> Option<f64> {
(&**self).get_var(name)
(**self).get_var(name)
}

fn eval_func(&self, name: &str, args: &[f64]) -> Result<f64, FuncEvalError> {
(&**self).eval_func(name, args)
(**self).eval_func(name, args)
}
}

impl<'a, T: ContextProvider> ContextProvider for &'a mut T {
fn get_var(&self, name: &str) -> Option<f64> {
(&**self).get_var(name)
(**self).get_var(name)
}

fn eval_func(&self, name: &str, args: &[f64]) -> Result<f64, FuncEvalError> {
(&**self).eval_func(name, args)
(**self).eval_func(name, args)
}
}

Expand Down Expand Up @@ -725,6 +723,7 @@ impl<'a> Context<'a> {
ctx.func("exp", f64::exp);
ctx.func("ln", f64::ln);
ctx.func("log10", f64::log10);
ctx.func("log", f64::log10);
ctx.func("abs", f64::abs);
ctx.func("sin", f64::sin);
ctx.func("cos", f64::cos);
Expand Down Expand Up @@ -856,7 +855,7 @@ impl<'a> Default for Context<'a> {
}
}

type GuardedFunc<'a> = Rc<Fn(&[f64]) -> Result<f64, FuncEvalError> + 'a>;
type GuardedFunc<'a> = Rc<dyn Fn(&[f64]) -> Result<f64, FuncEvalError> + 'a>;

/// Trait for types that can specify the number of required arguments for a function with a
/// variable number of arguments.
Expand Down Expand Up @@ -1170,20 +1169,20 @@ mod tests {
);

let expr = Expr::from_str("x + y^2 + z^3").unwrap();
let func = expr.clone().bind3("x", "y", "z").unwrap();
let func = expr.bind3("x", "y", "z").unwrap();
assert_eq!(func(1., 2., 3.), 32.);

let expr = Expr::from_str("sin(x)").unwrap();
let func = expr.clone().bind("x").unwrap();
let func = expr.bind("x").unwrap();
assert_eq!(func(1.), (1f64).sin());

let expr = Expr::from_str("sin(x,2)").unwrap();
match expr.clone().bind("x") {
match expr.bind("x") {
Err(Error::Function(_, FuncEvalError::NumberArgs(1))) => {}
_ => panic!("bind did not error"),
}
let expr = Expr::from_str("hey(x,2)").unwrap();
match expr.clone().bind("x") {
match expr.bind("x") {
Err(Error::Function(_, FuncEvalError::UnknownFunction)) => {}
_ => panic!("bind did not error"),
}
Expand Down
10 changes: 5 additions & 5 deletions src/extra_math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
// This is to take advantage of the fact that std::f64::MAX >>> std::u64::MAX
fn factorial_unsafe(num: f64) -> f64 {
if num == 0. || num == 1. {
return 1.;
1.
} else {
return num * factorial_unsafe(num - 1.);
num * factorial_unsafe(num - 1.)
}
}

pub fn factorial(num: f64) -> Result<f64, &'static str> {
if num.fract() != 0. || num < 0. {
return Err("Number must be non-negative with no fractional component!");
Err("Number must be non-negative with no fractional component!")
} else if num > 170. {
return Ok(std::f64::INFINITY);
Ok(std::f64::INFINITY)
} else {
return Ok(factorial_unsafe(num));
Ok(factorial_unsafe(num))
}
}

Expand Down
26 changes: 8 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,15 +208,15 @@ pub mod de;

pub use expr::*;
pub use shunting_yard::RPNError;
pub use tokenizer::ParseError;
pub use tokenizer::TokenParseError;

/// An error produced during parsing or evaluation.
#[derive(Debug, Clone, PartialEq)]
pub enum Error {
UnknownVariable(String),
Function(String, FuncEvalError),
/// An error returned by the parser.
ParseError(ParseError),
ParseError(TokenParseError),
/// The shunting-yard algorithm returned an error.
RPNError(RPNError),
// A catch all for all other errors during evaluation
Expand All @@ -233,23 +233,23 @@ impl fmt::Display for Error {
write!(f, "Evaluation error: function `{}`: {}", name, e)
}
Error::ParseError(ref e) => {
try!(write!(f, "Parse error: "));
write!(f, "Parse error: ")?;
e.fmt(f)
}
Error::RPNError(ref e) => {
try!(write!(f, "RPN error: "));
write!(f, "RPN error: ")?;
e.fmt(f)
}
Error::EvalError(ref e) => {
try!(write!(f, "Eval error: "));
write!(f, "Eval error: ")?;
e.fmt(f)
}
}
}
}

impl From<ParseError> for Error {
fn from(err: ParseError) -> Error {
impl From<TokenParseError> for Error {
fn from(err: TokenParseError) -> Error {
Error::ParseError(err)
}
}
Expand All @@ -261,17 +261,7 @@ impl From<RPNError> for Error {
}

impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::UnknownVariable(_) => "unknown variable",
Error::Function(_, _) => "function evaluation error",
Error::EvalError(_) => "eval error",
Error::ParseError(ref e) => e.description(),
Error::RPNError(ref e) => e.description(),
}
}

fn cause(&self) -> Option<&std::error::Error> {
fn cause(&self) -> Option<&dyn std::error::Error> {
match *self {
Error::ParseError(ref e) => Some(e),
Error::RPNError(ref e) => Some(e),
Expand Down
Loading