From 7d36ebe8df63be39a073e317bd88bc25b687e084 Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Thu, 28 Mar 2019 10:22:04 +0100 Subject: [PATCH] Add `Node` shortcut evaluations for `Value::Empty` Relates to #28 --- src/function/mod.rs | 2 +- src/lib.rs | 4 +++- src/tree/mod.rs | 32 +++++++++++++++++++++++++++++++- src/value/mod.rs | 3 +++ tests/integration.rs | 39 ++++++++++++++++++++++++++++++--------- 5 files changed, 68 insertions(+), 12 deletions(-) diff --git a/src/function/mod.rs b/src/function/mod.rs index 29f850a..745a88a 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -12,7 +12,7 @@ pub(crate) mod builtin; /// use evalexpr::*; /// /// let mut context = HashMapContext::new(); -/// context.set_function("id", Function::new(Some(1), Box::new(|arguments| { +/// context.set_function("id".into(), Function::new(Some(1), Box::new(|arguments| { /// Ok(arguments[0].clone()) /// }))).unwrap(); // Do proper error handling here /// assert_eq!(eval_with_context("id(4)", &context), Ok(Value::from(4))); diff --git a/src/lib.rs b/src/lib.rs index 2ea1942..b0a6ef6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,7 +282,9 @@ pub use error::{EvalexprError, EvalexprResult}; pub use function::Function; pub use interface::*; pub use tree::Node; -pub use value::{EmptyType, FloatType, IntType, TupleType, Value, value_type::ValueType}; +pub use value::{ + EMPTY_VALUE, EmptyType, FloatType, IntType, TupleType, Value, value_type::ValueType, +}; mod context; pub mod error; diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 6ce412d..e2bba9b 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -1,8 +1,9 @@ use EmptyContext; +use EmptyType; use FloatType; use IntType; use token::Token; -use value::TupleType; +use value::{EMPTY_VALUE, TupleType}; use crate::{ context::Context, @@ -145,6 +146,17 @@ impl Node { } } + /// Evaluates the operator tree rooted at this node into an empty value with an the given context. + /// + /// Fails, if one of the operators in the expression tree fails. + pub fn eval_empty_with_context(&self, context: &Context) -> EvalexprResult { + match self.eval_with_context(context) { + Ok(Value::Empty) => Ok(EMPTY_VALUE), + Ok(value) => Err(EvalexprError::expected_empty(value)), + Err(error) => Err(error), + } + } + /// Evaluates the operator tree rooted at this node into a string with an the given mutable context. /// /// Fails, if one of the operators in the expression tree fails. @@ -213,6 +225,17 @@ impl Node { } } + /// Evaluates the operator tree rooted at this node into an empty value with an the given mutable context. + /// + /// Fails, if one of the operators in the expression tree fails. + pub fn eval_empty_with_context_mut(&self, context: &mut Context) -> EvalexprResult { + match self.eval_with_context_mut(context) { + Ok(Value::Empty) => Ok(EMPTY_VALUE), + Ok(value) => Err(EvalexprError::expected_empty(value)), + Err(error) => Err(error), + } + } + /// Evaluates the operator tree rooted at this node into a string with an empty context. /// /// Fails, if one of the operators in the expression tree fails. @@ -256,6 +279,13 @@ impl Node { self.eval_tuple_with_context(&EmptyContext) } + /// Evaluates the operator tree rooted at this node into an empty value with an empty context. + /// + /// Fails, if one of the operators in the expression tree fails. + pub fn eval_empty(&self) -> EvalexprResult { + self.eval_empty_with_context(&EmptyContext) + } + fn children(&self) -> &[Node] { &self.children } diff --git a/src/value/mod.rs b/src/value/mod.rs index bd2a72a..67e766f 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -15,6 +15,9 @@ pub type TupleType = Vec; /// The type used to represent empty values in `Value::Empty`. pub type EmptyType = (); +/// The value of the empty type to be used in rust. +pub const EMPTY_VALUE: () = (); + /// The value type used by the parser. /// Values can be of different subtypes that are the variants of this enum. #[derive(Clone, Debug, PartialEq)] diff --git a/tests/integration.rs b/tests/integration.rs index 28e0dd9..febff0d 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -234,7 +234,17 @@ fn test_n_ary_functions() { "count".into(), Function::new( None, - Box::new(|arguments| Ok(Value::Int(arguments.len() as IntType))), + Box::new(|arguments| { + if arguments.len() == 1 { + if arguments[0] == Value::Empty { + Ok(Value::Int(0)) + } else { + Ok(Value::Int(1)) + } + } else { + Ok(Value::Int(arguments.len() as IntType)) + } + }), ), ) .unwrap(); @@ -259,13 +269,7 @@ fn test_n_ary_functions() { eval_with_context("muladd(3, 6, -4)", &context), Ok(Value::Int(14)) ); - assert_eq!( - eval_with_context("count()", &context), - Err(EvalexprError::WrongOperatorArgumentAmount { - actual: 0, - expected: 1 - }) - ); + assert_eq!(eval_with_context("count()", &context), Ok(Value::Int(0))); assert_eq!( eval_with_context("count(3, 5.5, 2)", &context), Ok(Value::Int(3)) @@ -407,6 +411,12 @@ fn test_shortcut_functions() { .eval_tuple_with_context(&context), Ok(vec![Value::Int(3), Value::Int(3)]) ); + assert_eq!( + build_operator_tree("") + .unwrap() + .eval_empty_with_context(&context), + Ok(EMPTY_VALUE) + ); assert_eq!( eval_string_with_context_mut("string", &mut context), @@ -458,9 +468,20 @@ fn test_shortcut_functions() { assert_eq!( build_operator_tree("3,3") .unwrap() - .eval_tuple_with_context(&mut context), + .eval_tuple_with_context_mut(&mut context), Ok(vec![Value::Int(3), Value::Int(3)]) ); + assert_eq!( + build_operator_tree("") + .unwrap() + .eval_empty_with_context_mut(&mut context), + Ok(EMPTY_VALUE) + ); +} + +#[test] +fn test_whitespace() { + assert!(eval_boolean("2 < = 3").is_err()); } #[cfg(feature = "serde")]