From c631171ff03e22d66426c37d555d5c45e8a0b4da Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Tue, 19 Mar 2019 16:54:52 +0200 Subject: [PATCH] Add comma operator and display implementations + Add comma token + Add tuple operator + Add tuple value type + Implement display for `tree::Node` and all related types + Add support for right-to-left chaining of operators with same precedence The priority of chaining of operators with the same precedence remains left-to-right. Only if two consecutive operators with the same precedence are right-to-left, they will be ordered right-to-left. Implements #4. --- README.md | 1 + src/error/mod.rs | 26 ++++-- src/function/mod.rs | 2 +- src/lib.rs | 91 ++++++++++++++++++++- src/operator/display.rs | 122 ++++++++++++++++++++++++++++ src/operator/mod.rs | 171 +++++++++++++++++++++++++++++++++++----- src/token/mod.rs | 12 ++- src/tree/display.rs | 12 +++ src/tree/mod.rs | 14 +++- src/value/display.rs | 26 ++++++ src/value/mod.rs | 11 ++- 11 files changed, 454 insertions(+), 34 deletions(-) create mode 100644 src/operator/display.rs create mode 100644 src/tree/display.rs create mode 100644 src/value/display.rs diff --git a/README.md b/README.md index 20b8c6a..badf0a9 100644 --- a/README.md +++ b/README.md @@ -191,3 +191,4 @@ See [LICENSE](LICENSE) for details. ## Closing Notes If you have any ideas for features or see any problems in the code, architecture, interface, algorithmics or documentation, please open an issue on github. +If there is already an issue describing what you want to say, please add a thumbs up or whatever emoji you think fits to the issue, so I know which ones I should prioritize. diff --git a/src/error/mod.rs b/src/error/mod.rs index 9eef933..39792c8 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -3,7 +3,11 @@ use token::PartialToken; #[derive(Debug, PartialEq)] pub enum Error { - WrongArgumentAmount { + WrongOperatorArgumentAmount { + expected: usize, + actual: usize, + }, + WrongFunctionArgumentAmount { expected: usize, actual: usize, }, @@ -50,8 +54,12 @@ pub enum Error { } impl Error { - pub fn wrong_argument_amount(actual: usize, expected: usize) -> Self { - Error::WrongArgumentAmount { actual, expected } + pub fn wrong_operator_argument_amount(actual: usize, expected: usize) -> Self { + Error::WrongOperatorArgumentAmount { actual, expected } + } + + pub fn wrong_function_argument_amount(actual: usize, expected: usize) -> Self { + Error::WrongFunctionArgumentAmount { actual, expected } } pub fn expected_number(actual: Value) -> Self { @@ -67,11 +75,19 @@ impl Error { } } -pub fn expect_argument_amount(actual: usize, expected: usize) -> Result<(), Error> { +pub fn expect_operator_argument_amount(actual: usize, expected: usize) -> Result<(), Error> { if actual == expected { Ok(()) } else { - Err(Error::wrong_argument_amount(actual, expected)) + Err(Error::wrong_operator_argument_amount(actual, expected)) + } +} + +pub fn expect_function_argument_amount(actual: usize, expected: usize) -> Result<(), Error> { + if actual == expected { + Ok(()) + } else { + Err(Error::wrong_function_argument_amount(actual, expected)) } } diff --git a/src/function/mod.rs b/src/function/mod.rs index 96516a8..ee282e9 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -18,7 +18,7 @@ impl Function { } pub fn call(&self, arguments: &[Value]) -> Result { - error::expect_argument_amount(self.argument_amount, arguments.len())?; + error::expect_function_argument_amount(arguments.len(), self.argument_amount)?; (self.function)(arguments) } } diff --git a/src/lib.rs b/src/lib.rs index ae2db39..5b30b74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -175,6 +175,8 @@ //! See [LICENSE](LICENSE) for details. //! +extern crate core; + mod configuration; pub mod error; mod function; @@ -210,7 +212,7 @@ pub fn build_operator_tree(string: &str) -> Result { mod test { use crate::{eval, value::Value}; use configuration::HashMapConfiguration; - use error::Error; + use error::{expect_number, Error}; use eval_with_configuration; use Function; @@ -377,6 +379,88 @@ mod test { ); } + #[test] + fn test_n_ary_functions() { + let mut configuration = HashMapConfiguration::new(); + configuration.insert_function( + "sub2", + Function::new( + 1, + Box::new(|arguments| { + if let Value::Int(int) = arguments[0] { + Ok(Value::Int(int - 2)) + } else if let Value::Float(float) = arguments[0] { + Ok(Value::Float(float - 2.0)) + } else { + Err(Error::expected_number(arguments[0].clone())) + } + }), + ), + ); + configuration.insert_function( + "avg", + Function::new( + 2, + Box::new(|arguments| { + expect_number(&arguments[0])?; + expect_number(&arguments[1])?; + + if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) { + Ok(Value::Int((a + b) / 2)) + } else { + Ok(Value::Float( + (arguments[0].as_float()? + arguments[1].as_float()?) / 2.0, + )) + } + }), + ), + ); + configuration.insert_function( + "muladd", + Function::new( + 3, + Box::new(|arguments| { + expect_number(&arguments[0])?; + expect_number(&arguments[1])?; + expect_number(&arguments[2])?; + + if let (Value::Int(a), Value::Int(b), Value::Int(c)) = + (&arguments[0], &arguments[1], &arguments[2]) + { + Ok(Value::Int(a * b + c)) + } else { + Ok(Value::Float( + arguments[0].as_float()? * arguments[1].as_float()? + + arguments[2].as_float()?, + )) + } + }), + ), + ); + configuration.insert_variable("five".to_string(), Value::Int(5)); + + assert_eq!( + eval_with_configuration("avg(7, 5)", &configuration), + Ok(Value::Int(6)) + ); + assert_eq!( + eval_with_configuration("avg(sub2 5, 5)", &configuration), + Ok(Value::Int(4)) + ); + assert_eq!( + eval_with_configuration("sub2(avg(3, 6))", &configuration), + Ok(Value::Int(2)) + ); + assert_eq!( + eval_with_configuration("sub2 avg(3, 6)", &configuration), + Ok(Value::Int(2)) + ); + assert_eq!( + eval_with_configuration("muladd(3, 6, -4)", &configuration), + Ok(Value::Int(14)) + ); + } + #[test] fn test_errors() { assert_eq!( @@ -387,7 +471,10 @@ mod test { eval("1-true"), Err(Error::expected_number(Value::Boolean(true))) ); - assert_eq!(eval("true-"), Err(Error::wrong_argument_amount(1, 2))); + assert_eq!( + eval("true-"), + Err(Error::wrong_operator_argument_amount(1, 2)) + ); assert_eq!(eval("!(()true)"), Err(Error::AppendedToLeafNode)); } } diff --git a/src/operator/display.rs b/src/operator/display.rs new file mode 100644 index 0000000..8b81a31 --- /dev/null +++ b/src/operator/display.rs @@ -0,0 +1,122 @@ +use operator::*; +use std::fmt::{Display, Error, Formatter}; + +impl Display for RootNode { + fn fmt(&self, _f: &mut Formatter) -> Result<(), Error> { + Ok(()) + } +} + +impl Display for Add { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "+") + } +} + +impl Display for Sub { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "-") + } +} + +impl Display for Neg { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "-") + } +} + +impl Display for Mul { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "*") + } +} + +impl Display for Div { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "/") + } +} + +impl Display for Mod { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "%") + } +} + +impl Display for Eq { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "==") + } +} + +impl Display for Neq { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "!=") + } +} + +impl Display for Gt { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, ">") + } +} + +impl Display for Lt { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "<") + } +} + +impl Display for Geq { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, ">=") + } +} + +impl Display for Leq { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "<=") + } +} + +impl Display for And { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "&&") + } +} + +impl Display for Or { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "||") + } +} + +impl Display for Not { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "!") + } +} + +impl Display for Tuple { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, ", ") + } +} + +impl Display for Const { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "{}", self.value) + } +} + +impl Display for VariableIdentifier { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "{}", self.identifier) + } +} + +impl Display for FunctionIdentifier { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + write!(f, "{}", self.identifier) + } +} diff --git a/src/operator/mod.rs b/src/operator/mod.rs index dbcd3c0..dffc0ae 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -1,12 +1,20 @@ use crate::{configuration::Configuration, error::*, value::Value}; -use std::fmt::Debug; +use std::fmt::{Debug, Display}; -pub trait Operator: Debug { +mod display; + +pub trait Operator: Debug + Display { /// Returns the precedence of the operator. /// A high precedence means that the operator has priority to be deeper in the tree. // Make this a const fn once #57563 is resolved fn precedence(&self) -> i32; + /// Returns true if chains of operators with the same precedence as this one should be evaluated left-to-right, + /// and false if they should be evaluated right-to-left. + /// Left-to-right chaining has priority if operators with different order but same precedence are chained. + // Make this a const fn once #57563 is resolved + fn is_left_to_right(&self) -> bool; + /// True if this operator is a leaf, meaning it accepts no arguments. // Make this a const fn once #57563 is resolved fn is_leaf(&self) -> bool { @@ -56,6 +64,9 @@ pub struct Or; #[derive(Debug)] pub struct Not; +#[derive(Debug)] +pub struct Tuple; + #[derive(Debug)] pub struct Const { value: Value, @@ -94,12 +105,16 @@ impl Operator for RootNode { 200 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 1 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 1)?; + expect_operator_argument_amount(arguments.len(), 1)?; Ok(arguments[0].clone()) } } @@ -109,12 +124,16 @@ impl Operator for Add { 95 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -135,12 +154,16 @@ impl Operator for Sub { 95 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -161,12 +184,16 @@ impl Operator for Neg { 110 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 1 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 1)?; + expect_operator_argument_amount(arguments.len(), 1)?; expect_number(&arguments[0])?; if arguments[0].is_int() { @@ -182,12 +209,16 @@ impl Operator for Mul { 100 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -208,12 +239,16 @@ impl Operator for Div { 100 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -234,12 +269,16 @@ impl Operator for Mod { 100 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -260,12 +299,16 @@ impl Operator for Eq { 80 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; if arguments[0] == arguments[1] { Ok(Value::Boolean(true)) @@ -280,12 +323,16 @@ impl Operator for Neq { 80 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; if arguments[0] != arguments[1] { Ok(Value::Boolean(true)) @@ -300,12 +347,16 @@ impl Operator for Gt { 80 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -330,12 +381,16 @@ impl Operator for Lt { 80 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -360,12 +415,16 @@ impl Operator for Geq { 80 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -390,12 +449,16 @@ impl Operator for Leq { 80 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; expect_number(&arguments[0])?; expect_number(&arguments[1])?; @@ -420,12 +483,16 @@ impl Operator for And { 75 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; let a = expect_boolean(&arguments[0])?; let b = expect_boolean(&arguments[1])?; @@ -442,12 +509,16 @@ impl Operator for Or { 70 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 2 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 2)?; + expect_operator_argument_amount(arguments.len(), 2)?; let a = expect_boolean(&arguments[0])?; let b = expect_boolean(&arguments[1])?; @@ -464,12 +535,16 @@ impl Operator for Not { 110 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 1 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 1)?; + expect_operator_argument_amount(arguments.len(), 1)?; let a = expect_boolean(&arguments[0])?; if !a { @@ -480,17 +555,58 @@ impl Operator for Not { } } +impl Operator for Tuple { + fn precedence(&self) -> i32 { + 40 + } + + fn is_left_to_right(&self) -> bool { + true + } + + fn argument_amount(&self) -> usize { + 2 + } + + fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { + if let Value::Tuple(tuple) = &arguments[0] { + let mut tuple = tuple.clone(); + if let Value::Tuple(tuple2) = &arguments[1] { + tuple.extend(tuple2.iter().cloned()); + } else { + tuple.push(arguments[1].clone()); + } + Ok(Value::from(tuple)) + } else { + if let Value::Tuple(tuple) = &arguments[1] { + let mut tuple = tuple.clone(); + tuple.insert(0, arguments[0].clone()); + Ok(Value::from(tuple)) + } else { + Ok(Value::from(vec![ + arguments[0].clone(), + arguments[1].clone(), + ])) + } + } + } +} + impl Operator for Const { fn precedence(&self) -> i32 { 200 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 0 } fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result { - expect_argument_amount(arguments.len(), 0)?; + expect_operator_argument_amount(arguments.len(), 0)?; Ok(self.value.clone()) } @@ -501,6 +617,10 @@ impl Operator for VariableIdentifier { 200 } + fn is_left_to_right(&self) -> bool { + true + } + fn argument_amount(&self) -> usize { 0 } @@ -519,13 +639,24 @@ impl Operator for FunctionIdentifier { 190 } + fn is_left_to_right(&self) -> bool { + false + } + fn argument_amount(&self) -> usize { 1 } fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result { + expect_operator_argument_amount(arguments.len(), 1)?; + + let arguments = if let Value::Tuple(arguments) = &arguments[0] { + arguments + } else { + arguments + }; + if let Some(function) = configuration.get_function(&self.identifier) { - // Function::call checks for correct argument amount function.call(arguments) } else { Err(Error::FunctionIdentifierNotFound(self.identifier.clone())) diff --git a/src/token/mod.rs b/src/token/mod.rs index d7c13d7..bc07f11 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -3,7 +3,6 @@ use value::{FloatType, IntType}; #[derive(Clone, PartialEq, Debug)] pub enum Token { - // Single character tokens // Arithmetic Plus, Minus, @@ -26,7 +25,10 @@ pub enum Token { LBrace, RBrace, - // Complex tokens + // Aggregation + Comma, + + // Values, Variables and Functions Identifier(String), Float(FloatType), Int(IntType), @@ -65,6 +67,8 @@ fn char_to_partial_token(c: char) -> PartialToken { '(' => PartialToken::Token(Token::LBrace), ')' => PartialToken::Token(Token::RBrace), + ',' => PartialToken::Token(Token::Comma), + c => { if c.is_whitespace() { PartialToken::Whitespace @@ -98,6 +102,8 @@ impl Token { Token::LBrace => true, Token::RBrace => false, + Token::Comma => false, + Token::Identifier(_) => true, Token::Float(_) => true, Token::Int(_) => true, @@ -127,6 +133,8 @@ impl Token { Token::LBrace => false, Token::RBrace => true, + Token::Comma => false, + Token::Identifier(_) => true, Token::Float(_) => true, Token::Int(_) => true, diff --git a/src/tree/display.rs b/src/tree/display.rs new file mode 100644 index 0000000..1460187 --- /dev/null +++ b/src/tree/display.rs @@ -0,0 +1,12 @@ +use std::fmt::{Display, Error, Formatter}; +use Node; + +impl Display for Node { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + self.operator.fmt(f)?; + for child in self.children() { + write!(f, " {}", child)?; + } + Ok(()) + } +} diff --git a/src/tree/mod.rs b/src/tree/mod.rs index e31e074..3d5b1e6 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -1,6 +1,8 @@ use crate::{configuration::Configuration, error::Error, operator::*, value::Value}; use token::Token; +mod display; + #[derive(Debug)] pub struct Node { children: Vec, @@ -40,14 +42,18 @@ impl Node { } fn insert_back_prioritized(&mut self, node: Node, is_root_node: bool) -> Result<(), Error> { - if self.operator().precedence() < node.operator().precedence() || is_root_node { + if self.operator().precedence() < node.operator().precedence() || is_root_node + // Right-to-left chaining + || (self.operator().precedence() == node.operator().precedence() && !self.operator().is_left_to_right() && !node.operator().is_left_to_right()) + { if self.operator().is_leaf() { Err(Error::AppendedToLeafNode) } else if self.has_correct_amount_of_children() { if self.children.last().unwrap().operator().precedence() < node.operator().precedence() - // Function call - //|| self.children().last().unwrap() + // Right-to-left chaining + || (self.children.last().unwrap().operator().precedence() + == node.operator().precedence() && !self.children.last().unwrap().operator().is_left_to_right() && !node.operator().is_left_to_right()) { self.children .last_mut() @@ -118,6 +124,8 @@ pub fn tokens_to_operator_tree(tokens: Vec) -> Result { } } + Token::Comma => Some(Node::new(Tuple)), + Token::Identifier(identifier) => { let mut result = Some(Node::new(VariableIdentifier::new(identifier.clone()))); if let Some(next) = next { diff --git a/src/value/display.rs b/src/value/display.rs new file mode 100644 index 0000000..172ff75 --- /dev/null +++ b/src/value/display.rs @@ -0,0 +1,26 @@ +use std::fmt::{Display, Error, Formatter}; +use Value; + +impl Display for Value { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + match self { + Value::String(string) => write!(f, "\"{}\"", string), + Value::Float(float) => write!(f, "{}", float), + Value::Int(int) => write!(f, "{}", int), + Value::Boolean(boolean) => write!(f, "{}", boolean), + Value::Tuple(tuple) => { + write!(f, "(")?; + let mut once = false; + for value in tuple { + if once { + write!(f, ", ")?; + } else { + once = true; + } + value.fmt(f)?; + } + write!(f, ")") + } + } + } +} diff --git a/src/value/mod.rs b/src/value/mod.rs index 9107c09..a312c66 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,5 +1,7 @@ use error::Error; +mod display; + pub type IntType = i64; pub type FloatType = f64; @@ -9,6 +11,7 @@ pub enum Value { Float(FloatType), Int(IntType), Boolean(bool), + Tuple(Vec), } impl Value { @@ -72,8 +75,14 @@ impl From for Value { } } +impl From> for Value { + fn from(tuple: Vec) -> Self { + Value::Tuple(tuple) + } +} + impl From for Result { fn from(value: Value) -> Self { Ok(value) } -} \ No newline at end of file +}