Begin new math implementation for Value; Clean up
This commit is contained in:
parent
d27c98e393
commit
c82f631524
@ -1,3 +1,5 @@
|
||||
use std::ops::AddAssign;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
@ -129,13 +131,12 @@ impl AbstractTree for Assignment {
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Context) -> Result<Value, RuntimeError> {
|
||||
let value = self.statement.run(source, context)?;
|
||||
let right = self.statement.run(source, context)?;
|
||||
|
||||
let new_value = match self.operator {
|
||||
AssignmentOperator::PlusEqual => {
|
||||
if let Some(mut previous_value) = context.get_value(&self.identifier)? {
|
||||
previous_value += value;
|
||||
previous_value
|
||||
if let Some(left) = context.get_value(&self.identifier)? {
|
||||
left.add_assign(right)?
|
||||
} else {
|
||||
return Err(RuntimeError::VariableIdentifierNotFound(
|
||||
self.identifier.clone(),
|
||||
@ -143,16 +144,15 @@ impl AbstractTree for Assignment {
|
||||
}
|
||||
}
|
||||
AssignmentOperator::MinusEqual => {
|
||||
if let Some(mut previous_value) = context.get_value(&self.identifier)? {
|
||||
previous_value -= value;
|
||||
previous_value
|
||||
if let Some(mut left) = context.get_value(&self.identifier)? {
|
||||
left.sub_assign(right)?
|
||||
} else {
|
||||
return Err(RuntimeError::VariableIdentifierNotFound(
|
||||
self.identifier.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
AssignmentOperator::Equal => value,
|
||||
AssignmentOperator::Equal => right,
|
||||
};
|
||||
|
||||
context.set_value(self.identifier.clone(), new_value)?;
|
||||
|
@ -108,12 +108,12 @@ impl AbstractTree for For {
|
||||
|
||||
if let Value::List(list) = &expression_run {
|
||||
if self.is_async {
|
||||
list.items().par_iter().try_for_each(|value| {
|
||||
list.items()?.par_iter().try_for_each(|value| {
|
||||
self.context.set_value(key.clone(), value.clone())?;
|
||||
self.block.run(source, &self.context).map(|_value| ())
|
||||
})?;
|
||||
} else {
|
||||
for value in list.items().iter() {
|
||||
for value in list.items()?.iter() {
|
||||
self.context.set_value(key.clone(), value.clone())?;
|
||||
self.block.run(source, &self.context)?;
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ impl AbstractTree for Index {
|
||||
match value {
|
||||
Value::List(list) => {
|
||||
let index = self.index.run(source, context)?.as_integer()? as usize;
|
||||
let item = list.items().get(index).cloned().unwrap_or_default();
|
||||
let item = list.items()?.get(index).cloned().unwrap_or_default();
|
||||
|
||||
Ok(item)
|
||||
}
|
||||
|
@ -49,12 +49,12 @@ impl AbstractTree for Math {
|
||||
let left = self.left.run(source, context)?;
|
||||
let right = self.right.run(source, context)?;
|
||||
let value = match self.operator {
|
||||
MathOperator::Add => left + right,
|
||||
MathOperator::Subtract => left - right,
|
||||
MathOperator::Multiply => left * right,
|
||||
MathOperator::Divide => left / right,
|
||||
MathOperator::Modulo => left % right,
|
||||
}?;
|
||||
MathOperator::Add => left.add_assign(right)?,
|
||||
MathOperator::Subtract => todo!(),
|
||||
MathOperator::Multiply => todo!(),
|
||||
MathOperator::Divide => todo!(),
|
||||
MathOperator::Modulo => todo!(),
|
||||
};
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ impl Callable for BuiltInFunction {
|
||||
|
||||
let value = arguments.first().unwrap();
|
||||
let length = if let Ok(list) = value.as_list() {
|
||||
list.items().len()
|
||||
list.items()?.len()
|
||||
} else if let Ok(map) = value.as_map() {
|
||||
map.inner().len()
|
||||
} else if let Ok(str) = value.as_string() {
|
||||
@ -163,7 +163,7 @@ impl Callable for BuiltInFunction {
|
||||
let value = arguments.first().unwrap();
|
||||
|
||||
if let Ok(list) = value.as_list() {
|
||||
let items = list.items();
|
||||
let items = list.items()?;
|
||||
|
||||
if items.len() == 0 {
|
||||
Ok(Value::none())
|
||||
|
@ -334,7 +334,7 @@ impl Callable for StrFunction {
|
||||
RuntimeError::expect_argument_amount(self.name(), 3, arguments.len())?;
|
||||
|
||||
let mut string = arguments.first().unwrap().as_string()?.clone();
|
||||
let range = arguments.get(1).unwrap().as_list()?.items();
|
||||
let range = arguments.get(1).unwrap().as_list()?.items()?;
|
||||
let start = range[0].as_integer()? as usize;
|
||||
let end = range[1].as_integer()? as usize;
|
||||
let pattern = arguments.get(2).unwrap().as_string()?;
|
||||
|
@ -117,7 +117,7 @@ impl Context {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Modify a context to take the functions and type definitions of another.
|
||||
/// Modify a context to take all the information of another.
|
||||
///
|
||||
/// In the case of the conflict, the inherited value will override the previous
|
||||
/// value.
|
||||
@ -173,7 +173,7 @@ impl Context {
|
||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, RwLockError> {
|
||||
if let Some(value_data) = self.inner.read()?.get(identifier) {
|
||||
match value_data {
|
||||
ValueData::Value { inner, .. } => return Ok(Some(inner.r#type())),
|
||||
ValueData::Value { inner, .. } => return Ok(Some(inner.r#type()?)),
|
||||
ValueData::TypeHint { inner, .. } => return Ok(Some(inner.clone())),
|
||||
ValueData::TypeDefinition(_) => todo!(),
|
||||
}
|
||||
@ -181,7 +181,7 @@ impl Context {
|
||||
|
||||
for built_in_value in all_built_in_values() {
|
||||
if built_in_value.name() == identifier.inner().as_ref() {
|
||||
return Ok(Some(built_in_value.get().r#type()));
|
||||
return Ok(Some(built_in_value.get().r#type()?));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,12 @@ impl RuntimeError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ValidationError> for RuntimeError {
|
||||
fn from(error: ValidationError) -> Self {
|
||||
RuntimeError::ValidationFailure(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<csv::Error> for RuntimeError {
|
||||
fn from(error: csv::Error) -> Self {
|
||||
RuntimeError::Csv(error.to_string())
|
||||
@ -184,12 +190,6 @@ impl From<FromUtf8Error> for RuntimeError {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RwLockError> for RuntimeError {
|
||||
fn from(error: RwLockError) -> Self {
|
||||
RuntimeError::RwLock(error)
|
||||
@ -201,3 +201,9 @@ impl<T> From<PoisonError<T>> for RuntimeError {
|
||||
RuntimeError::RwLock(RwLockError)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,18 @@ pub enum ValidationError {
|
||||
/// The 'assert' macro did not resolve successfully.
|
||||
AssertFailed { position: SourcePosition },
|
||||
|
||||
/// Two value are incompatible for addition.
|
||||
CannotAdd { left: Value, right: Value },
|
||||
|
||||
/// Two value are incompatible for subtraction.
|
||||
CannotSubtract { left: Value, right: Value },
|
||||
|
||||
/// Two value are incompatible for multiplication.
|
||||
CannotMultiply { left: Value, right: Value },
|
||||
|
||||
/// Two value are incompatible for dividing.
|
||||
CannotDivide { left: Value, right: Value },
|
||||
|
||||
/// The attempted conversion is impossible.
|
||||
ConversionImpossible {
|
||||
initial_type: Type,
|
||||
|
@ -1,4 +1,8 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt::{self, Display, Formatter},
|
||||
sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
||||
};
|
||||
|
||||
use stanza::{
|
||||
renderer::{console::Console, Renderer},
|
||||
@ -6,10 +10,10 @@ use stanza::{
|
||||
table::{Cell, Content, Row, Table},
|
||||
};
|
||||
|
||||
use crate::Value;
|
||||
use crate::{error::rw_lock_error::RwLockError, Value};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct List(Vec<Value>);
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct List(Arc<RwLock<Vec<Value>>>);
|
||||
|
||||
impl Default for List {
|
||||
fn default() -> Self {
|
||||
@ -19,28 +23,29 @@ impl Default for List {
|
||||
|
||||
impl List {
|
||||
pub fn new() -> Self {
|
||||
List(Vec::new())
|
||||
List(Arc::new(RwLock::new(Vec::new())))
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
List(Vec::with_capacity(capacity))
|
||||
List(Arc::new(RwLock::new(Vec::with_capacity(capacity))))
|
||||
}
|
||||
|
||||
pub fn with_items(items: Vec<Value>) -> Self {
|
||||
List(items)
|
||||
List(Arc::new(RwLock::new(items)))
|
||||
}
|
||||
|
||||
pub fn items(&self) -> &Vec<Value> {
|
||||
&self.0
|
||||
pub fn items(&self) -> Result<RwLockReadGuard<Vec<Value>>, RwLockError> {
|
||||
Ok(self.0.read()?)
|
||||
}
|
||||
|
||||
pub fn items_mut(&mut self) -> &mut Vec<Value> {
|
||||
&mut self.0
|
||||
pub fn items_mut(&self) -> Result<RwLockWriteGuard<Vec<Value>>, RwLockError> {
|
||||
Ok(self.0.write()?)
|
||||
}
|
||||
|
||||
pub fn as_text_table(&self) -> Table {
|
||||
let cells: Vec<Cell> = self
|
||||
.items()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|value| {
|
||||
if let Value::List(list) = value {
|
||||
@ -76,3 +81,43 @@ impl Display for List {
|
||||
f.write_str(&renderer.render(&self.as_text_table()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for List {}
|
||||
|
||||
impl PartialEq for List {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if let Ok(left) = self.items() {
|
||||
if let Ok(right) = other.items() {
|
||||
if left.len() != right.len() {
|
||||
return false;
|
||||
} else {
|
||||
for (i, j) in left.iter().zip(right.iter()) {
|
||||
if i != j {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for List {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for List {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if let Ok(left) = self.items() {
|
||||
if let Ok(right) = other.items() {
|
||||
return left.cmp(&right);
|
||||
}
|
||||
}
|
||||
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! Types that represent runtime values.
|
||||
use crate::{
|
||||
built_in_values::BuiltInValue, error::RuntimeError, Identifier, Type, TypeSpecification,
|
||||
built_in_values::BuiltInValue,
|
||||
error::{rw_lock_error::RwLockError, RuntimeError, ValidationError},
|
||||
Identifier, Type, TypeSpecification,
|
||||
};
|
||||
|
||||
use serde::{
|
||||
@ -68,17 +70,17 @@ impl Value {
|
||||
Value::Range(start..=end)
|
||||
}
|
||||
|
||||
pub fn r#type(&self) -> Type {
|
||||
match self {
|
||||
pub fn r#type(&self) -> Result<Type, RwLockError> {
|
||||
let r#type = match self {
|
||||
Value::List(list) => {
|
||||
let mut previous_type = None;
|
||||
|
||||
for value in list.items().iter() {
|
||||
for value in list.items()?.iter() {
|
||||
let value_type = value.r#type();
|
||||
|
||||
if let Some(previous) = &previous_type {
|
||||
if &value_type != previous {
|
||||
return Type::List(Box::new(Type::Any));
|
||||
return Ok(Type::List(Box::new(Type::Any)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +88,7 @@ impl Value {
|
||||
}
|
||||
|
||||
if let Some(previous) = previous_type {
|
||||
Type::List(Box::new(previous))
|
||||
Type::List(Box::new(previous?))
|
||||
} else {
|
||||
Type::List(Box::new(Type::Any))
|
||||
}
|
||||
@ -97,7 +99,7 @@ impl Value {
|
||||
for (key, value) in map.inner() {
|
||||
identifier_types.push((
|
||||
Identifier::new(key.inner()),
|
||||
TypeSpecification::new(value.r#type()),
|
||||
TypeSpecification::new(value.r#type()?),
|
||||
));
|
||||
}
|
||||
|
||||
@ -111,7 +113,9 @@ impl Value {
|
||||
Value::Range(_) => todo!(),
|
||||
Value::Struct(_) => todo!(),
|
||||
Value::Enum(_) => todo!(),
|
||||
}
|
||||
};
|
||||
|
||||
Ok(r#type)
|
||||
}
|
||||
|
||||
pub fn is_string(&self) -> bool {
|
||||
@ -150,7 +154,8 @@ impl Value {
|
||||
self == &Value::none()
|
||||
}
|
||||
|
||||
/// Borrows the value stored in `self` as `&String`, or returns `Err` if `self` is not a `Value::String`.
|
||||
/// Borrows the value stored in `self` as `&String`, or returns `Err` if
|
||||
/// `self` is not a `Value::String`.
|
||||
pub fn as_string(&self) -> Result<&String, RuntimeError> {
|
||||
match self {
|
||||
Value::String(string) => Ok(string),
|
||||
@ -160,7 +165,8 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the value stored in `self` as `i64`, or returns `Err` if `self` is not a `Value::Int`
|
||||
/// Copies the value stored in `self` as `i64`, or returns `Err` if `self`
|
||||
/// is not a `Value::Int`
|
||||
pub fn as_integer(&self) -> Result<i64, RuntimeError> {
|
||||
match self {
|
||||
Value::Integer(i) => Ok(*i),
|
||||
@ -170,7 +176,8 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the value stored in `self` as `f64`, or returns `Err` if `self` is not a `Primitive::Float`.
|
||||
/// Copies the value stored in `self` as `f64`, or returns `Err` if `self`
|
||||
/// is not a `Primitive::Float`.
|
||||
pub fn as_float(&self) -> Result<f64, RuntimeError> {
|
||||
match self {
|
||||
Value::Float(f) => Ok(*f),
|
||||
@ -180,8 +187,11 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the value stored in `self` as `f64`, or returns `Err` if `self` is not a `Primitive::Float` or `Value::Int`.
|
||||
/// Note that this method silently converts `i64` to `f64`, if `self` is a `Value::Int`.
|
||||
/// Copies the value stored in `self` as `f64`, or returns `Err` if `self`
|
||||
/// is not a `Primitive::Float` or `Value::Int`.
|
||||
///
|
||||
/// Note that this method silently converts `i64` to `f64`, if `self` is
|
||||
/// a `Value::Int`.
|
||||
pub fn as_number(&self) -> Result<f64, RuntimeError> {
|
||||
match self {
|
||||
Value::Float(f) => Ok(*f),
|
||||
@ -192,7 +202,8 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the value stored in `self` as `bool`, or returns `Err` if `self` is not a `Primitive::Boolean`.
|
||||
/// Copies the value stored in `self` as `bool`, or returns `Err` if `self`
|
||||
/// is not a `Primitive::Boolean`.
|
||||
pub fn as_boolean(&self) -> Result<bool, RuntimeError> {
|
||||
match self {
|
||||
Value::Boolean(boolean) => Ok(*boolean),
|
||||
@ -202,7 +213,8 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrows the value stored in `self` as `Vec<Value>`, or returns `Err` if `self` is not a `Value::List`.
|
||||
/// Borrows the value stored in `self` as `Vec<Value>`, or returns `Err` if
|
||||
/// `self` is not a `Value::List`.
|
||||
pub fn as_list(&self) -> Result<&List, RuntimeError> {
|
||||
match self {
|
||||
Value::List(list) => Ok(list),
|
||||
@ -212,7 +224,8 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes ownership of the value stored in `self` as `Vec<Value>`, or returns `Err` if `self` is not a `Value::List`.
|
||||
/// Takes ownership of the value stored in `self` as `Vec<Value>`, or
|
||||
/// returns `Err` if `self` is not a `Value::List`.
|
||||
pub fn into_inner_list(self) -> Result<List, RuntimeError> {
|
||||
match self {
|
||||
Value::List(list) => Ok(list),
|
||||
@ -222,7 +235,8 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrows the value stored in `self` as `Vec<Value>`, or returns `Err` if `self` is not a `Value::Map`.
|
||||
/// Borrows the value stored in `self` as `Vec<Value>`, or returns `Err` if
|
||||
/// `self` is not a `Value::Map`.
|
||||
pub fn as_map(&self) -> Result<&Map, RuntimeError> {
|
||||
match self {
|
||||
Value::Map(map) => Ok(map),
|
||||
@ -242,6 +256,35 @@ impl Value {
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_assign(self, other: Self) -> Result<Value, ValidationError> {
|
||||
match (self, other) {
|
||||
(Value::Float(_), Value::Float(_)) => todo!(),
|
||||
(Value::Float(_), Value::Integer(_)) => todo!(),
|
||||
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float((left as f64) + right)),
|
||||
(Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left + right)),
|
||||
(Value::List(list), value) | (value, Value::List(list)) => {
|
||||
list.items_mut()?.push(value);
|
||||
|
||||
Ok(Value::List(list))
|
||||
}
|
||||
(Value::Map(_), _) | (_, Value::Map(_)) => todo!(),
|
||||
(Value::String(_), Value::String(_)) => todo!(),
|
||||
(left, right) => Err(ValidationError::CannotAdd { left, right }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_assign(self, other: Self) -> Result<Value, ValidationError> {
|
||||
match (self, other) {
|
||||
(Value::Float(_), Value::Float(_)) => todo!(),
|
||||
(Value::Float(_), Value::Integer(_)) => todo!(),
|
||||
(Value::Integer(left), Value::Float(right)) => Ok(Value::Float((left as f64) + right)),
|
||||
(Value::Integer(left), Value::Integer(right)) => Ok(Value::Integer(left + right)),
|
||||
(Value::Map(_), _) | (_, Value::Map(_)) => todo!(),
|
||||
(Value::String(_), Value::String(_)) => todo!(),
|
||||
(left, right) => Err(ValidationError::CannotSubtract { left, right }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
@ -360,7 +403,6 @@ impl AddAssign for Value {
|
||||
(Value::Float(left), Value::Float(right)) => *left += right,
|
||||
(Value::Float(left), Value::Integer(right)) => *left += right as f64,
|
||||
(Value::String(left), Value::String(right)) => *left += &right,
|
||||
(Value::List(list), value) => list.items_mut().push(value),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -372,17 +414,6 @@ impl SubAssign for Value {
|
||||
(Value::Integer(left), Value::Integer(right)) => *left -= right,
|
||||
(Value::Float(left), Value::Float(right)) => *left -= right,
|
||||
(Value::Float(left), Value::Integer(right)) => *left -= right as f64,
|
||||
(Value::List(list), value) => {
|
||||
let index_to_remove = list
|
||||
.items()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(i, list_value)| if list_value == &value { Some(i) } else { None });
|
||||
|
||||
if let Some(index) = index_to_remove {
|
||||
list.items_mut().remove(index);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -465,7 +496,12 @@ impl Serialize for Value {
|
||||
Value::Integer(inner) => serializer.serialize_i64(*inner),
|
||||
Value::Boolean(inner) => serializer.serialize_bool(*inner),
|
||||
Value::List(inner) => {
|
||||
let items = inner.items();
|
||||
let items = if let Ok(items) = inner.items() {
|
||||
items
|
||||
} else {
|
||||
return Err(serde::ser::Error::custom("failed to obtain a read lock"));
|
||||
};
|
||||
|
||||
let mut list = serializer.serialize_tuple(items.len())?;
|
||||
|
||||
for value in items.iter() {
|
||||
|
@ -21,7 +21,7 @@ impl Display for StructInstance {
|
||||
writeln!(f, "{{")?;
|
||||
|
||||
for (key, value) in self.map.inner() {
|
||||
writeln!(f, " {key} <{}> = {value}", value.r#type())?;
|
||||
writeln!(f, " {key} <{}> = {value}", value.r#type().unwrap())?;
|
||||
}
|
||||
|
||||
write!(f, "}}")
|
||||
|
@ -42,12 +42,12 @@ fn modify_list() {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(Value::List(List::with_items(vec![
|
||||
Value::Integer(1),
|
||||
Value::Integer(2),
|
||||
Value::Integer(3),
|
||||
]))),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ fn modify_map() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn do_not_modify_list_values() {
|
||||
fn modify_list_values() {
|
||||
let result = interpret(
|
||||
"
|
||||
list = [1 2 3]
|
||||
@ -84,12 +84,12 @@ fn do_not_modify_list_values() {
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(Value::List(List::with_items(vec![
|
||||
Value::Integer(1),
|
||||
Value::Integer(2),
|
||||
Value::Integer(3),
|
||||
Value::Integer(4),
|
||||
]))),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user