Skip to content

Commit

Permalink
Add Array support
Browse files Browse the repository at this point in the history
- Array lexing, parsing and evaluation
- Integration tests
  • Loading branch information
BijanVeyssi committed Dec 28, 2023
1 parent fcfe542 commit 1afdcd5
Show file tree
Hide file tree
Showing 15 changed files with 425 additions and 164 deletions.
109 changes: 55 additions & 54 deletions README.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/error/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ impl fmt::Display for EvalexprError {
ExpectedBoolean { actual } => {
write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
},
ExpectedArray { actual } => write!(f, "Expected a Value::Array, but got {:?}.", actual),
ExpectedTuple { actual } => write!(f, "Expected a Value::Tuple, but got {:?}.", actual),
ExpectedFixedLengthTuple {
expected_length,
Expand All @@ -60,6 +61,11 @@ impl fmt::Display for EvalexprError {
expected_length.end(),
actual
),
ExpectedVec { actual } => write!(
f,
"Expected a Value which can be interpreted as vec, 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."),
PrecedenceViolation => write!(
Expand All @@ -86,6 +92,8 @@ impl fmt::Display for EvalexprError {
),
UnmatchedLBrace => write!(f, "Found an unmatched opening parenthesis '('."),
UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."),
UnmatchedLCurlyBrace => write!(f, "Found an unmatched opening curly brace '{{'."),
UnmatchedRCurlyBrace => write!(f, "Found an unmatched closing curly brace '}}'."),
UnmatchedDoubleQuote => write!(f, "Found an unmatched double quote '\"'"),
MissingOperatorOutsideOfBrace { .. } => write!(
f,
Expand Down
29 changes: 29 additions & 0 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ pub enum EvalexprError {
actual: Value,
},

/// A array value was expected.
ExpectedArray {
/// The actual value.
actual: Value,
},

/// A tuple value of a certain length was expected.
ExpectedFixedLengthTuple {
/// The expected length.
Expand All @@ -95,6 +101,12 @@ pub enum EvalexprError {
actual: Value,
},

/// A value which can be interpreted as vec was expected
ExpectedVec {
/// The actual value.
actual: Value,
},

/// An empty value was expected.
ExpectedEmpty {
/// The actual value.
Expand Down Expand Up @@ -139,6 +151,12 @@ pub enum EvalexprError {
/// A closing brace without a matching opening brace was found.
UnmatchedRBrace,

/// An opening curly brace without a matching closing brace was found.
UnmatchedLCurlyBrace,

/// A closing curly brace without a matching opening brace was found.
UnmatchedRCurlyBrace,

/// A double quote without a matching second double quote was found.
UnmatchedDoubleQuote,

Expand Down Expand Up @@ -296,6 +314,11 @@ impl EvalexprError {
EvalexprError::ExpectedTuple { actual }
}

/// Constructs `EvalexprError::ExpectedArray{actual}`.
pub fn expected_array(actual: Value) -> Self {
EvalexprError::ExpectedArray { actual }
}

/// Constructs `EvalexprError::ExpectedFixedLenTuple{expected_len, actual}`.
pub fn expected_fixed_len_tuple(expected_len: usize, actual: Value) -> Self {
EvalexprError::ExpectedFixedLengthTuple {
Expand All @@ -312,6 +335,11 @@ impl EvalexprError {
}
}

/// Constructs `EvalexprError::ExpectedVec{actual}`.
pub fn expected_vec(actual: Value) -> Self {
EvalexprError::ExpectedVec { actual }
}

/// Constructs `EvalexprError::ExpectedEmpty{actual}`.
pub fn expected_empty(actual: Value) -> Self {
EvalexprError::ExpectedEmpty { actual }
Expand All @@ -325,6 +353,7 @@ impl EvalexprError {
ValueType::Float => Self::expected_float(actual),
ValueType::Boolean => Self::expected_boolean(actual),
ValueType::Tuple => Self::expected_tuple(actual),
ValueType::Array => Self::expected_array(actual),
ValueType::Empty => Self::expected_empty(actual),
}
}
Expand Down
77 changes: 33 additions & 44 deletions src/function/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
Value::Int(_) => "int",
Value::Boolean(_) => "boolean",
Value::Tuple(_) => "tuple",
Value::Array(_) => "array",
Value::Empty => "empty",
}
.into())
Expand Down Expand Up @@ -159,67 +160,55 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
})),
"contains" => Some(Function::new(move |argument| {
let arguments = argument.as_fixed_len_tuple(2)?;
if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) = b {
Ok(a.contains(b).into())
} else {
Err(EvalexprError::type_error(
b.clone(),
vec![
ValueType::String,
ValueType::Int,
ValueType::Float,
ValueType::Boolean,
],
))
}
let (a, b) = (arguments[0].as_slice()?, &arguments[1].clone());
if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) = b {
Ok(a.contains(b).into())
} else {
Err(EvalexprError::expected_tuple(arguments[0].clone()))
Err(EvalexprError::type_error(
b.clone(),
vec![
ValueType::String,
ValueType::Int,
ValueType::Float,
ValueType::Boolean,
],
))
}
})),
"contains_any" => Some(Function::new(move |argument| {
let arguments = argument.as_fixed_len_tuple(2)?;
if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
if let Value::Tuple(b) = b {
let mut contains = false;
for value in b {
if let Value::String(_)
| Value::Int(_)
| Value::Float(_)
| Value::Boolean(_) = value
{
if a.contains(value) {
contains = true;
}
} else {
return Err(EvalexprError::type_error(
value.clone(),
vec![
ValueType::String,
ValueType::Int,
ValueType::Float,
ValueType::Boolean,
],
));
}
let (a, b) = (arguments[0].as_slice()?, arguments[1].as_slice()?);
let mut contains = false;
for value in b {
if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) =
value
{
if a.contains(value) {
contains = true;
}
Ok(contains.into())
} else {
Err(EvalexprError::expected_tuple(b.clone()))
return Err(EvalexprError::type_error(
value.clone(),
vec![
ValueType::String,
ValueType::Int,
ValueType::Float,
ValueType::Boolean,
],
));
}
} else {
Err(EvalexprError::expected_tuple(arguments[0].clone()))
}
Ok(contains.into())
})),
"len" => Some(Function::new(|argument| {
if let Ok(subject) = argument.as_string() {
Ok(Value::from(subject.len() as IntType))
} else if let Ok(subject) = argument.as_tuple() {
} else if let Ok(subject) = argument.as_slice() {
Ok(Value::from(subject.len() as IntType))
} else {
Err(EvalexprError::type_error(
argument.clone(),
vec![ValueType::String, ValueType::Tuple],
vec![ValueType::String, ValueType::Tuple, ValueType::Array],
))
}
})),
Expand Down
27 changes: 25 additions & 2 deletions src/interface/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::{
token, tree, value::TupleType, Context, ContextWithMutableVariables, EmptyType, EvalexprError,
EvalexprResult, FloatType, HashMapContext, IntType, Node, Value, EMPTY_VALUE,
token, tree,
value::{ArrayType, TupleType},
Context, ContextWithMutableVariables, EmptyType, EvalexprError, EvalexprResult, FloatType,
HashMapContext, IntType, Node, Value, EMPTY_VALUE,
};

/// Evaluate the given expression string.
Expand Down Expand Up @@ -130,6 +132,13 @@ pub fn eval_tuple(string: &str) -> EvalexprResult<TupleType> {
eval_tuple_with_context_mut(string, &mut HashMapContext::new())
}

/// Evaluate the given expression string into a array.
///
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
pub fn eval_array(string: &str) -> EvalexprResult<ArrayType> {
eval_array_with_context_mut(string, &mut HashMapContext::new())
}

/// Evaluate the given expression string into an empty value.
///
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
Expand Down Expand Up @@ -305,6 +314,20 @@ pub fn eval_tuple_with_context_mut<C: ContextWithMutableVariables>(
}
}

/// Evaluate the given expression string into a array with the given mutable context.
///
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
pub fn eval_array_with_context_mut<C: ContextWithMutableVariables>(
string: &str,
context: &mut C,
) -> EvalexprResult<ArrayType> {
match eval_with_context_mut(string, context) {
Ok(Value::Array(array)) => Ok(array),
Ok(value) => Err(EvalexprError::expected_array(value)),
Err(error) => Err(error),
}
}

/// Evaluate the given expression string into an empty value with the given mutable context.
///
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
Expand Down
Loading

0 comments on commit 1afdcd5

Please sign in to comment.