Skip to content

Commit 1afdcd5

Browse files
committed
Add Array support
- Array lexing, parsing and evaluation - Integration tests
1 parent fcfe542 commit 1afdcd5

File tree

15 files changed

+425
-164
lines changed

15 files changed

+425
-164
lines changed

README.md

Lines changed: 55 additions & 54 deletions
Large diffs are not rendered by default.

src/error/display.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ impl fmt::Display for EvalexprError {
4141
ExpectedBoolean { actual } => {
4242
write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
4343
},
44+
ExpectedArray { actual } => write!(f, "Expected a Value::Array, but got {:?}.", actual),
4445
ExpectedTuple { actual } => write!(f, "Expected a Value::Tuple, but got {:?}.", actual),
4546
ExpectedFixedLengthTuple {
4647
expected_length,
@@ -60,6 +61,11 @@ impl fmt::Display for EvalexprError {
6061
expected_length.end(),
6162
actual
6263
),
64+
ExpectedVec { actual } => write!(
65+
f,
66+
"Expected a Value which can be interpreted as vec, but got {:?}.",
67+
actual
68+
),
6369
ExpectedEmpty { actual } => write!(f, "Expected a Value::Empty, but got {:?}.", actual),
6470
AppendedToLeafNode => write!(f, "Tried to append a node to a leaf node."),
6571
PrecedenceViolation => write!(
@@ -86,6 +92,8 @@ impl fmt::Display for EvalexprError {
8692
),
8793
UnmatchedLBrace => write!(f, "Found an unmatched opening parenthesis '('."),
8894
UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."),
95+
UnmatchedLCurlyBrace => write!(f, "Found an unmatched opening curly brace '{{'."),
96+
UnmatchedRCurlyBrace => write!(f, "Found an unmatched closing curly brace '}}'."),
8997
UnmatchedDoubleQuote => write!(f, "Found an unmatched double quote '\"'"),
9098
MissingOperatorOutsideOfBrace { .. } => write!(
9199
f,

src/error/mod.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ pub enum EvalexprError {
7979
actual: Value,
8080
},
8181

82+
/// A array value was expected.
83+
ExpectedArray {
84+
/// The actual value.
85+
actual: Value,
86+
},
87+
8288
/// A tuple value of a certain length was expected.
8389
ExpectedFixedLengthTuple {
8490
/// The expected length.
@@ -95,6 +101,12 @@ pub enum EvalexprError {
95101
actual: Value,
96102
},
97103

104+
/// A value which can be interpreted as vec was expected
105+
ExpectedVec {
106+
/// The actual value.
107+
actual: Value,
108+
},
109+
98110
/// An empty value was expected.
99111
ExpectedEmpty {
100112
/// The actual value.
@@ -139,6 +151,12 @@ pub enum EvalexprError {
139151
/// A closing brace without a matching opening brace was found.
140152
UnmatchedRBrace,
141153

154+
/// An opening curly brace without a matching closing brace was found.
155+
UnmatchedLCurlyBrace,
156+
157+
/// A closing curly brace without a matching opening brace was found.
158+
UnmatchedRCurlyBrace,
159+
142160
/// A double quote without a matching second double quote was found.
143161
UnmatchedDoubleQuote,
144162

@@ -296,6 +314,11 @@ impl EvalexprError {
296314
EvalexprError::ExpectedTuple { actual }
297315
}
298316

317+
/// Constructs `EvalexprError::ExpectedArray{actual}`.
318+
pub fn expected_array(actual: Value) -> Self {
319+
EvalexprError::ExpectedArray { actual }
320+
}
321+
299322
/// Constructs `EvalexprError::ExpectedFixedLenTuple{expected_len, actual}`.
300323
pub fn expected_fixed_len_tuple(expected_len: usize, actual: Value) -> Self {
301324
EvalexprError::ExpectedFixedLengthTuple {
@@ -312,6 +335,11 @@ impl EvalexprError {
312335
}
313336
}
314337

338+
/// Constructs `EvalexprError::ExpectedVec{actual}`.
339+
pub fn expected_vec(actual: Value) -> Self {
340+
EvalexprError::ExpectedVec { actual }
341+
}
342+
315343
/// Constructs `EvalexprError::ExpectedEmpty{actual}`.
316344
pub fn expected_empty(actual: Value) -> Self {
317345
EvalexprError::ExpectedEmpty { actual }
@@ -325,6 +353,7 @@ impl EvalexprError {
325353
ValueType::Float => Self::expected_float(actual),
326354
ValueType::Boolean => Self::expected_boolean(actual),
327355
ValueType::Tuple => Self::expected_tuple(actual),
356+
ValueType::Array => Self::expected_array(actual),
328357
ValueType::Empty => Self::expected_empty(actual),
329358
}
330359
}

src/function/builtin.rs

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
104104
Value::Int(_) => "int",
105105
Value::Boolean(_) => "boolean",
106106
Value::Tuple(_) => "tuple",
107+
Value::Array(_) => "array",
107108
Value::Empty => "empty",
108109
}
109110
.into())
@@ -159,67 +160,55 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
159160
})),
160161
"contains" => Some(Function::new(move |argument| {
161162
let arguments = argument.as_fixed_len_tuple(2)?;
162-
if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
163-
if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) = b {
164-
Ok(a.contains(b).into())
165-
} else {
166-
Err(EvalexprError::type_error(
167-
b.clone(),
168-
vec![
169-
ValueType::String,
170-
ValueType::Int,
171-
ValueType::Float,
172-
ValueType::Boolean,
173-
],
174-
))
175-
}
163+
let (a, b) = (arguments[0].as_slice()?, &arguments[1].clone());
164+
if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) = b {
165+
Ok(a.contains(b).into())
176166
} else {
177-
Err(EvalexprError::expected_tuple(arguments[0].clone()))
167+
Err(EvalexprError::type_error(
168+
b.clone(),
169+
vec![
170+
ValueType::String,
171+
ValueType::Int,
172+
ValueType::Float,
173+
ValueType::Boolean,
174+
],
175+
))
178176
}
179177
})),
180178
"contains_any" => Some(Function::new(move |argument| {
181179
let arguments = argument.as_fixed_len_tuple(2)?;
182-
if let (Value::Tuple(a), b) = (&arguments[0].clone(), &arguments[1].clone()) {
183-
if let Value::Tuple(b) = b {
184-
let mut contains = false;
185-
for value in b {
186-
if let Value::String(_)
187-
| Value::Int(_)
188-
| Value::Float(_)
189-
| Value::Boolean(_) = value
190-
{
191-
if a.contains(value) {
192-
contains = true;
193-
}
194-
} else {
195-
return Err(EvalexprError::type_error(
196-
value.clone(),
197-
vec![
198-
ValueType::String,
199-
ValueType::Int,
200-
ValueType::Float,
201-
ValueType::Boolean,
202-
],
203-
));
204-
}
180+
let (a, b) = (arguments[0].as_slice()?, arguments[1].as_slice()?);
181+
let mut contains = false;
182+
for value in b {
183+
if let Value::String(_) | Value::Int(_) | Value::Float(_) | Value::Boolean(_) =
184+
value
185+
{
186+
if a.contains(value) {
187+
contains = true;
205188
}
206-
Ok(contains.into())
207189
} else {
208-
Err(EvalexprError::expected_tuple(b.clone()))
190+
return Err(EvalexprError::type_error(
191+
value.clone(),
192+
vec![
193+
ValueType::String,
194+
ValueType::Int,
195+
ValueType::Float,
196+
ValueType::Boolean,
197+
],
198+
));
209199
}
210-
} else {
211-
Err(EvalexprError::expected_tuple(arguments[0].clone()))
212200
}
201+
Ok(contains.into())
213202
})),
214203
"len" => Some(Function::new(|argument| {
215204
if let Ok(subject) = argument.as_string() {
216205
Ok(Value::from(subject.len() as IntType))
217-
} else if let Ok(subject) = argument.as_tuple() {
206+
} else if let Ok(subject) = argument.as_slice() {
218207
Ok(Value::from(subject.len() as IntType))
219208
} else {
220209
Err(EvalexprError::type_error(
221210
argument.clone(),
222-
vec![ValueType::String, ValueType::Tuple],
211+
vec![ValueType::String, ValueType::Tuple, ValueType::Array],
223212
))
224213
}
225214
})),

src/interface/mod.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::{
2-
token, tree, value::TupleType, Context, ContextWithMutableVariables, EmptyType, EvalexprError,
3-
EvalexprResult, FloatType, HashMapContext, IntType, Node, Value, EMPTY_VALUE,
2+
token, tree,
3+
value::{ArrayType, TupleType},
4+
Context, ContextWithMutableVariables, EmptyType, EvalexprError, EvalexprResult, FloatType,
5+
HashMapContext, IntType, Node, Value, EMPTY_VALUE,
46
};
57

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

135+
/// Evaluate the given expression string into a array.
136+
///
137+
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
138+
pub fn eval_array(string: &str) -> EvalexprResult<ArrayType> {
139+
eval_array_with_context_mut(string, &mut HashMapContext::new())
140+
}
141+
133142
/// Evaluate the given expression string into an empty value.
134143
///
135144
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
@@ -305,6 +314,20 @@ pub fn eval_tuple_with_context_mut<C: ContextWithMutableVariables>(
305314
}
306315
}
307316

317+
/// Evaluate the given expression string into a array with the given mutable context.
318+
///
319+
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*
320+
pub fn eval_array_with_context_mut<C: ContextWithMutableVariables>(
321+
string: &str,
322+
context: &mut C,
323+
) -> EvalexprResult<ArrayType> {
324+
match eval_with_context_mut(string, context) {
325+
Ok(Value::Array(array)) => Ok(array),
326+
Ok(value) => Err(EvalexprError::expected_array(value)),
327+
Err(error) => Err(error),
328+
}
329+
}
330+
308331
/// Evaluate the given expression string into an empty value with the given mutable context.
309332
///
310333
/// *See the [crate doc](index.html) for more examples and explanations of the expression format.*

0 commit comments

Comments
 (0)