Implement value decomposition API
* Removed expect_... methods and replaced them with .as_...() methods. This removes the need to import the free-standing methods every time and makes the code cleaner. * Changed all the examples appropriately. Implements #53
This commit is contained in:
parent
502ec0adce
commit
e6c19077b6
@ -35,6 +35,7 @@ impl fmt::Display for EvalexprError {
|
|||||||
write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
|
write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
|
||||||
},
|
},
|
||||||
ExpectedTuple { actual } => write!(f, "Expected a Value::Tuple, but got {:?}.", actual),
|
ExpectedTuple { actual } => write!(f, "Expected a Value::Tuple, but got {:?}.", actual),
|
||||||
|
ExpectedFixedLenTuple { expected_len, actual } => write!(f, "Expected a Value::Tuple of len {}, but got {:?}.", expected_len, actual),
|
||||||
ExpectedEmpty { actual } => write!(f, "Expected a Value::Empty, but got {:?}.", actual),
|
ExpectedEmpty { actual } => write!(f, "Expected a Value::Empty, but got {:?}.", actual),
|
||||||
AppendedToLeafNode => write!(f, "Tried to append a node to a leaf node."),
|
AppendedToLeafNode => write!(f, "Tried to append a node to a leaf node."),
|
||||||
PrecedenceViolation => write!(
|
PrecedenceViolation => write!(
|
||||||
|
@ -75,6 +75,14 @@ pub enum EvalexprError {
|
|||||||
actual: Value,
|
actual: Value,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// A tuple value of a certain length was expected.
|
||||||
|
ExpectedFixedLenTuple {
|
||||||
|
/// The expected len
|
||||||
|
expected_len: usize,
|
||||||
|
/// The actual value.
|
||||||
|
actual: Value,
|
||||||
|
},
|
||||||
|
|
||||||
/// An empty value was expected.
|
/// An empty value was expected.
|
||||||
ExpectedEmpty {
|
ExpectedEmpty {
|
||||||
/// The actual value.
|
/// The actual value.
|
||||||
@ -234,6 +242,11 @@ impl EvalexprError {
|
|||||||
EvalexprError::ExpectedTuple { actual }
|
EvalexprError::ExpectedTuple { actual }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs `Error::ExpectedFixedLenTuple{expected_len, actual}`.
|
||||||
|
pub fn expected_fixed_len_tuple(expected_len: usize, actual: Value) -> Self {
|
||||||
|
EvalexprError::ExpectedFixedLenTuple { expected_len, actual }
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs `Error::ExpectedEmpty{actual}`.
|
/// Constructs `Error::ExpectedEmpty{actual}`.
|
||||||
pub fn expected_empty(actual: Value) -> Self {
|
pub fn expected_empty(actual: Value) -> Self {
|
||||||
EvalexprError::ExpectedEmpty { actual }
|
EvalexprError::ExpectedEmpty { actual }
|
||||||
@ -319,24 +332,6 @@ pub fn expect_function_argument_amount(actual: usize, expected: usize) -> Evalex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `Ok(&str)` if the given value is a `Value::String`, or `Err(Error::ExpectedString)` otherwise.
|
|
||||||
pub fn expect_string(actual: &Value) -> EvalexprResult<&str> {
|
|
||||||
match actual {
|
|
||||||
Value::String(string) => Ok(string),
|
|
||||||
_ => Err(EvalexprError::expected_string(actual.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `Ok(())` if the given value is numeric.
|
|
||||||
/// Numeric types are `Value::Int` and `Value::Float`.
|
|
||||||
/// Otherwise, `Err(Error::ExpectedNumber)` is returned.
|
|
||||||
pub fn expect_number(actual: &Value) -> EvalexprResult<()> {
|
|
||||||
match actual {
|
|
||||||
Value::Float(_) | Value::Int(_) => Ok(()),
|
|
||||||
_ => Err(EvalexprError::expected_number(actual.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `Ok(())` if the given value is a string or a numeric
|
/// Returns `Ok(())` if the given value is a string or a numeric
|
||||||
pub fn expect_number_or_string(actual: &Value) -> EvalexprResult<()> {
|
pub fn expect_number_or_string(actual: &Value) -> EvalexprResult<()> {
|
||||||
match actual {
|
match actual {
|
||||||
@ -345,22 +340,6 @@ pub fn expect_number_or_string(actual: &Value) -> EvalexprResult<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `Ok(bool)` if the given value is a `Value::Boolean`, or `Err(Error::ExpectedBoolean)` otherwise.
|
|
||||||
pub fn expect_boolean(actual: &Value) -> EvalexprResult<bool> {
|
|
||||||
match actual {
|
|
||||||
Value::Boolean(boolean) => Ok(*boolean),
|
|
||||||
_ => Err(EvalexprError::expected_boolean(actual.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `Ok(&[Value])` if the given value is a `Value::Tuple`, or `Err(Error::ExpectedTuple)` otherwise.
|
|
||||||
pub fn expect_tuple(actual: &Value) -> EvalexprResult<&TupleType> {
|
|
||||||
match actual {
|
|
||||||
Value::Tuple(tuple) => Ok(tuple),
|
|
||||||
_ => Err(EvalexprError::expected_tuple(actual.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for EvalexprError {}
|
impl std::error::Error for EvalexprError {}
|
||||||
|
|
||||||
/// Standard result type used by this crate.
|
/// Standard result type used by this crate.
|
||||||
|
@ -10,16 +10,16 @@ use Value;
|
|||||||
pub fn builtin_function(identifier: &str) -> Option<Function> {
|
pub fn builtin_function(identifier: &str) -> Option<Function> {
|
||||||
match identifier {
|
match identifier {
|
||||||
"min" => Some(Function::new(Box::new(|argument| {
|
"min" => Some(Function::new(Box::new(|argument| {
|
||||||
let arguments = expect_tuple(argument)?;
|
let arguments = argument.as_tuple()?;
|
||||||
let mut min_int = IntType::max_value();
|
let mut min_int = IntType::max_value();
|
||||||
let mut min_float = 1.0f64 / 0.0f64;
|
let mut min_float = 1.0f64 / 0.0f64;
|
||||||
debug_assert!(min_float.is_infinite());
|
debug_assert!(min_float.is_infinite());
|
||||||
|
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
if let Value::Float(float) = argument {
|
if let Value::Float(float) = argument {
|
||||||
min_float = min_float.min(*float);
|
min_float = min_float.min(float);
|
||||||
} else if let Value::Int(int) = argument {
|
} else if let Value::Int(int) = argument {
|
||||||
min_int = min_int.min(*int);
|
min_int = min_int.min(int);
|
||||||
} else {
|
} else {
|
||||||
return Err(EvalexprError::expected_number(argument.clone()));
|
return Err(EvalexprError::expected_number(argument.clone()));
|
||||||
}
|
}
|
||||||
@ -32,16 +32,16 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
|
|||||||
}
|
}
|
||||||
}))),
|
}))),
|
||||||
"max" => Some(Function::new(Box::new(|argument| {
|
"max" => Some(Function::new(Box::new(|argument| {
|
||||||
let arguments = expect_tuple(argument)?;
|
let arguments = argument.as_tuple()?;
|
||||||
let mut max_int = IntType::min_value();
|
let mut max_int = IntType::min_value();
|
||||||
let mut max_float = -1.0f64 / 0.0f64;
|
let mut max_float = -1.0f64 / 0.0f64;
|
||||||
debug_assert!(max_float.is_infinite());
|
debug_assert!(max_float.is_infinite());
|
||||||
|
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
if let Value::Float(float) = argument {
|
if let Value::Float(float) = argument {
|
||||||
max_float = max_float.max(*float);
|
max_float = max_float.max(float);
|
||||||
} else if let Value::Int(int) = argument {
|
} else if let Value::Int(int) = argument {
|
||||||
max_int = max_int.max(*int);
|
max_int = max_int.max(int);
|
||||||
} else {
|
} else {
|
||||||
return Err(EvalexprError::expected_number(argument.clone()));
|
return Err(EvalexprError::expected_number(argument.clone()));
|
||||||
}
|
}
|
||||||
@ -55,19 +55,19 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
|
|||||||
}))),
|
}))),
|
||||||
|
|
||||||
"len" => Some(Function::new(Box::new(|argument| {
|
"len" => Some(Function::new(Box::new(|argument| {
|
||||||
let subject = expect_string(argument)?;
|
let subject = argument.as_string()?;
|
||||||
Ok(Value::from(subject.len() as i64))
|
Ok(Value::from(subject.len() as i64))
|
||||||
}))),
|
}))),
|
||||||
|
|
||||||
// string functions
|
// string functions
|
||||||
#[cfg(feature = "regex_support")]
|
#[cfg(feature = "regex_support")]
|
||||||
"str::regex_matches" => Some(Function::new(Box::new(|argument| {
|
"str::regex_matches" => Some(Function::new(Box::new(|argument| {
|
||||||
let arguments = expect_tuple(argument)?;
|
let arguments = argument.as_tuple()?;
|
||||||
|
|
||||||
let subject = expect_string(&arguments[0])?;
|
let subject = arguments[0].as_string()?;
|
||||||
let re_str = expect_string(&arguments[1])?;
|
let re_str = arguments[1].as_string()?;
|
||||||
match Regex::new(re_str) {
|
match Regex::new(&re_str) {
|
||||||
Ok(re) => Ok(Value::Boolean(re.is_match(subject))),
|
Ok(re) => Ok(Value::Boolean(re.is_match(&subject))),
|
||||||
Err(err) => Err(EvalexprError::invalid_regex(
|
Err(err) => Err(EvalexprError::invalid_regex(
|
||||||
re_str.to_string(),
|
re_str.to_string(),
|
||||||
format!("{}", err),
|
format!("{}", err),
|
||||||
@ -76,13 +76,13 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
|
|||||||
}))),
|
}))),
|
||||||
#[cfg(feature = "regex_support")]
|
#[cfg(feature = "regex_support")]
|
||||||
"str::regex_replace" => Some(Function::new(Box::new(|argument| {
|
"str::regex_replace" => Some(Function::new(Box::new(|argument| {
|
||||||
let arguments = expect_tuple(argument)?;
|
let arguments = argument.as_tuple()?;
|
||||||
|
|
||||||
let subject = expect_string(&arguments[0])?;
|
let subject = arguments[0].as_string()?;
|
||||||
let re_str = expect_string(&arguments[1])?;
|
let re_str = arguments[1].as_string()?;
|
||||||
let repl = expect_string(&arguments[2])?;
|
let repl = arguments[2].as_string()?;
|
||||||
match Regex::new(re_str) {
|
match Regex::new(&re_str) {
|
||||||
Ok(re) => Ok(Value::String(re.replace_all(subject, repl).to_string())),
|
Ok(re) => Ok(Value::String(re.replace_all(&subject, repl.as_str()).to_string())),
|
||||||
Err(err) => Err(EvalexprError::invalid_regex(
|
Err(err) => Err(EvalexprError::invalid_regex(
|
||||||
re_str.to_string(),
|
re_str.to_string(),
|
||||||
format!("{}", err),
|
format!("{}", err),
|
||||||
@ -90,15 +90,15 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
|
|||||||
}
|
}
|
||||||
}))),
|
}))),
|
||||||
"str::to_lowercase" => Some(Function::new(Box::new(|argument| {
|
"str::to_lowercase" => Some(Function::new(Box::new(|argument| {
|
||||||
let subject = expect_string(argument)?;
|
let subject = argument.as_string()?;
|
||||||
Ok(Value::from(subject.to_lowercase()))
|
Ok(Value::from(subject.to_lowercase()))
|
||||||
}))),
|
}))),
|
||||||
"str::to_uppercase" => Some(Function::new(Box::new(|argument| {
|
"str::to_uppercase" => Some(Function::new(Box::new(|argument| {
|
||||||
let subject = expect_string(argument)?;
|
let subject = argument.as_string()?;
|
||||||
Ok(Value::from(subject.to_uppercase()))
|
Ok(Value::from(subject.to_uppercase()))
|
||||||
}))),
|
}))),
|
||||||
"str::trim" => Some(Function::new(Box::new(|argument| {
|
"str::trim" => Some(Function::new(Box::new(|argument| {
|
||||||
let subject = expect_string(argument)?;
|
let subject = argument.as_string()?;
|
||||||
Ok(Value::from(subject.trim()))
|
Ok(Value::from(subject.trim()))
|
||||||
}))),
|
}))),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -49,24 +49,21 @@
|
|||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
//! use evalexpr::*;
|
//! use evalexpr::*;
|
||||||
//! use evalexpr::error::{expect_number, expect_tuple};
|
|
||||||
//!
|
//!
|
||||||
//! let context = context_map! {
|
//! let context = context_map! {
|
||||||
//! "five" => 5,
|
//! "five" => 5,
|
||||||
//! "twelve" => 12,
|
//! "twelve" => 12,
|
||||||
//! "f" => Function::new(Box::new(|argument| {
|
//! "f" => Function::new(Box::new(|argument| {
|
||||||
//! if let Value::Int(int) = argument {
|
//! if let Ok(int) = argument.as_int() {
|
||||||
//! Ok(Value::Int(int / 2))
|
//! Ok(Value::Int(int / 2))
|
||||||
//! } else if let Value::Float(float) = argument {
|
//! } else if let Ok(float) = argument.as_float() {
|
||||||
//! Ok(Value::Float(float / 2.0))
|
//! Ok(Value::Float(float / 2.0))
|
||||||
//! } else {
|
//! } else {
|
||||||
//! Err(EvalexprError::expected_number(argument.clone()))
|
//! Err(EvalexprError::expected_number(argument.clone()))
|
||||||
//! }
|
//! }
|
||||||
//! })),
|
//! })),
|
||||||
//! "avg" => Function::new(Box::new(|argument| {
|
//! "avg" => Function::new(Box::new(|argument| {
|
||||||
//! let arguments = expect_tuple(argument)?;
|
//! let arguments = argument.as_tuple()?;
|
||||||
//! expect_number(&arguments[0])?;
|
|
||||||
//! expect_number(&arguments[1])?;
|
|
||||||
//!
|
//!
|
||||||
//! if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) {
|
//! if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) {
|
||||||
//! Ok(Value::Int((a + b) / 2))
|
//! Ok(Value::Int((a + b) / 2))
|
||||||
|
@ -161,8 +161,8 @@ impl Operator {
|
|||||||
},
|
},
|
||||||
Sub => {
|
Sub => {
|
||||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||||
expect_number(&arguments[0])?;
|
arguments[0].as_number()?;
|
||||||
expect_number(&arguments[1])?;
|
arguments[1].as_number()?;
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
||||||
let result = a.checked_sub(b);
|
let result = a.checked_sub(b);
|
||||||
@ -182,7 +182,7 @@ impl Operator {
|
|||||||
},
|
},
|
||||||
Neg => {
|
Neg => {
|
||||||
expect_operator_argument_amount(arguments.len(), 1)?;
|
expect_operator_argument_amount(arguments.len(), 1)?;
|
||||||
expect_number(&arguments[0])?;
|
arguments[0].as_number()?;
|
||||||
|
|
||||||
if let Ok(a) = arguments[0].as_int() {
|
if let Ok(a) = arguments[0].as_int() {
|
||||||
let result = a.checked_neg();
|
let result = a.checked_neg();
|
||||||
@ -197,8 +197,8 @@ impl Operator {
|
|||||||
},
|
},
|
||||||
Mul => {
|
Mul => {
|
||||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||||
expect_number(&arguments[0])?;
|
arguments[0].as_number()?;
|
||||||
expect_number(&arguments[1])?;
|
arguments[1].as_number()?;
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
||||||
let result = a.checked_mul(b);
|
let result = a.checked_mul(b);
|
||||||
@ -218,8 +218,8 @@ impl Operator {
|
|||||||
},
|
},
|
||||||
Div => {
|
Div => {
|
||||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||||
expect_number(&arguments[0])?;
|
arguments[0].as_number()?;
|
||||||
expect_number(&arguments[1])?;
|
arguments[1].as_number()?;
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
||||||
let result = a.checked_div(b);
|
let result = a.checked_div(b);
|
||||||
@ -239,8 +239,8 @@ impl Operator {
|
|||||||
},
|
},
|
||||||
Mod => {
|
Mod => {
|
||||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||||
expect_number(&arguments[0])?;
|
arguments[0].as_number()?;
|
||||||
expect_number(&arguments[1])?;
|
arguments[1].as_number()?;
|
||||||
|
|
||||||
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
if let (Ok(a), Ok(b)) = (arguments[0].as_int(), arguments[1].as_int()) {
|
||||||
let result = a.checked_rem(b);
|
let result = a.checked_rem(b);
|
||||||
@ -260,8 +260,8 @@ impl Operator {
|
|||||||
},
|
},
|
||||||
Exp => {
|
Exp => {
|
||||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||||
expect_number(&arguments[0])?;
|
arguments[0].as_number()?;
|
||||||
expect_number(&arguments[1])?;
|
arguments[1].as_number()?;
|
||||||
|
|
||||||
Ok(Value::Float(
|
Ok(Value::Float(
|
||||||
arguments[0]
|
arguments[0]
|
||||||
@ -390,8 +390,8 @@ impl Operator {
|
|||||||
},
|
},
|
||||||
And => {
|
And => {
|
||||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||||
let a = expect_boolean(&arguments[0])?;
|
let a = arguments[0].as_boolean()?;
|
||||||
let b = expect_boolean(&arguments[1])?;
|
let b = arguments[1].as_boolean()?;
|
||||||
|
|
||||||
if a && b {
|
if a && b {
|
||||||
Ok(Value::Boolean(true))
|
Ok(Value::Boolean(true))
|
||||||
@ -401,8 +401,8 @@ impl Operator {
|
|||||||
},
|
},
|
||||||
Or => {
|
Or => {
|
||||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||||
let a = expect_boolean(&arguments[0])?;
|
let a = arguments[0].as_boolean()?;
|
||||||
let b = expect_boolean(&arguments[1])?;
|
let b = arguments[1].as_boolean()?;
|
||||||
|
|
||||||
if a || b {
|
if a || b {
|
||||||
Ok(Value::Boolean(true))
|
Ok(Value::Boolean(true))
|
||||||
@ -412,7 +412,7 @@ impl Operator {
|
|||||||
},
|
},
|
||||||
Not => {
|
Not => {
|
||||||
expect_operator_argument_amount(arguments.len(), 1)?;
|
expect_operator_argument_amount(arguments.len(), 1)?;
|
||||||
let a = expect_boolean(&arguments[0])?;
|
let a = arguments[0].as_boolean()?;
|
||||||
|
|
||||||
if !a {
|
if !a {
|
||||||
Ok(Value::Boolean(true))
|
Ok(Value::Boolean(true))
|
||||||
@ -470,7 +470,7 @@ impl Operator {
|
|||||||
match self {
|
match self {
|
||||||
Assign => {
|
Assign => {
|
||||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||||
let target = expect_string(&arguments[0])?;
|
let target = arguments[0].as_string()?;
|
||||||
context.set_value(target.into(), arguments[1].clone())?;
|
context.set_value(target.into(), arguments[1].clone())?;
|
||||||
|
|
||||||
Ok(Value::Empty)
|
Ok(Value::Empty)
|
||||||
|
@ -143,6 +143,20 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clones the value stored in `self` as `TupleType` or returns `Err` if `self` is not a `Value::Tuple` of the required length.
|
||||||
|
pub fn as_fixed_len_tuple(&self, len: usize) -> EvalexprResult<TupleType> {
|
||||||
|
match self {
|
||||||
|
Value::Tuple(tuple) => {
|
||||||
|
if tuple.len() == len {
|
||||||
|
Ok(tuple.clone())
|
||||||
|
} else {
|
||||||
|
Err(EvalexprError::expected_fixed_len_tuple(len, self.clone()))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
value => Err(EvalexprError::expected_tuple(value.clone())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `()`, or returns`Err` if `self` is not a `Value::Tuple`.
|
/// Returns `()`, or returns`Err` if `self` is not a `Value::Tuple`.
|
||||||
pub fn as_empty(&self) -> EvalexprResult<()> {
|
pub fn as_empty(&self) -> EvalexprResult<()> {
|
||||||
match self {
|
match self {
|
||||||
|
@ -183,9 +183,9 @@ fn test_n_ary_functions() {
|
|||||||
.set_function(
|
.set_function(
|
||||||
"avg".into(),
|
"avg".into(),
|
||||||
Function::new(Box::new(|argument| {
|
Function::new(Box::new(|argument| {
|
||||||
let arguments = expect_tuple(argument)?;
|
let arguments = argument.as_tuple()?;
|
||||||
expect_number(&arguments[0])?;
|
arguments[0].as_number()?;
|
||||||
expect_number(&arguments[1])?;
|
arguments[1].as_number()?;
|
||||||
|
|
||||||
if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) {
|
if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) {
|
||||||
Ok(Value::Int((a + b) / 2))
|
Ok(Value::Int((a + b) / 2))
|
||||||
@ -201,10 +201,10 @@ fn test_n_ary_functions() {
|
|||||||
.set_function(
|
.set_function(
|
||||||
"muladd".into(),
|
"muladd".into(),
|
||||||
Function::new(Box::new(|argument| {
|
Function::new(Box::new(|argument| {
|
||||||
let arguments = expect_tuple(argument)?;
|
let arguments = argument.as_tuple()?;
|
||||||
expect_number(&arguments[0])?;
|
arguments[0].as_number()?;
|
||||||
expect_number(&arguments[1])?;
|
arguments[1].as_number()?;
|
||||||
expect_number(&arguments[2])?;
|
arguments[2].as_number()?;
|
||||||
|
|
||||||
if let (Value::Int(a), Value::Int(b), Value::Int(c)) =
|
if let (Value::Int(a), Value::Int(b), Value::Int(c)) =
|
||||||
(&arguments[0], &arguments[1], &arguments[2])
|
(&arguments[0], &arguments[1], &arguments[2])
|
||||||
|
Loading…
Reference in New Issue
Block a user