From 879f1fcd22cdc4dbc2a32a901557f34b041ed3ce Mon Sep 17 00:00:00 2001 From: Sebastian Schmidt Date: Mon, 18 Mar 2019 19:21:07 +0200 Subject: [PATCH] Evaluate arbitrary functions --- src/error/mod.rs | 7 ++- src/function.rs | 23 ++++++- src/lib.rs | 43 ++++++++++++- src/operator/mod.rs | 67 +++++++++++++++++---- src/token/mod.rs | 143 +++++++++++++++++++++++++++----------------- src/tree/mod.rs | 41 ++++++++----- 6 files changed, 238 insertions(+), 86 deletions(-) diff --git a/src/error/mod.rs b/src/error/mod.rs index d1cded5..c0f627f 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -28,8 +28,11 @@ pub enum Error { /// Tried to append a child to a node such that the precedence of the child is not higher. PrecedenceViolation, - /// An identifier operation did not find its value in the configuration. - IdentifierNotFound(String), + /// A `VariableIdentifier` operation did not find its value in the configuration. + VariableIdentifierNotFound(String), + + /// A `FunctionIdentifier` operation did not find its value in the configuration. + FunctionIdentifierNotFound(String), /// A value has the wrong type. TypeError, diff --git a/src/function.rs b/src/function.rs index cb3bdd6..96516a8 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,7 +1,24 @@ -use error::Error; +use error::{self, Error}; use value::Value; pub struct Function { - parameter_amount: usize, - function: fn() -> Result, // TODO continue type + argument_amount: usize, + function: Box Result>, +} + +impl Function { + pub fn new( + argument_amount: usize, + function: Box Result>, + ) -> Self { + Self { + argument_amount, + function, + } + } + + pub fn call(&self, arguments: &[Value]) -> Result { + error::expect_argument_amount(self.argument_amount, arguments.len())?; + (self.function)(arguments) + } } diff --git a/src/lib.rs b/src/lib.rs index 13e8d8d..64d2780 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ mod test { use configuration::HashMapConfiguration; use error::Error; use eval_with_configuration; + use Function; #[test] fn test_unary_examples() { @@ -39,7 +40,7 @@ mod test { assert_eq!(eval("false"), Ok(Value::Boolean(false))); assert_eq!( eval("blub"), - Err(Error::IdentifierNotFound("blub".to_string())) + Err(Error::VariableIdentifierNotFound("blub".to_string())) ); assert_eq!(eval("-3"), Ok(Value::Int(-3))); assert_eq!(eval("-3.6"), Ok(Value::Float(-3.6))); @@ -152,6 +153,46 @@ mod test { ); } + #[test] + fn test_functions() { + let mut configuration = HashMapConfiguration::new(); + configuration.insert_function( + "sub2".to_string(), + Function::new( + 1, + Box::new(|arguments| { + if let Value::Int(int) = arguments[0] { + Ok(Value::Int(int - 2)) + } else { + Err(Error::expected_number(arguments[0].clone())) + } + }), + ), + ); + configuration.insert_variable("five".to_string(), Value::Int(5)); + + assert_eq!( + eval_with_configuration("sub2 5", &configuration), + Ok(Value::Int(3)) + ); + assert_eq!( + eval_with_configuration("sub2(5)", &configuration), + Ok(Value::Int(3)) + ); + assert_eq!( + eval_with_configuration("sub2 five", &configuration), + Ok(Value::Int(3)) + ); + assert_eq!( + eval_with_configuration("sub2(five)", &configuration), + Ok(Value::Int(3)) + ); + assert_eq!( + eval_with_configuration("sub2(3) + five", &configuration), + Ok(Value::Int(6)) + ); + } + #[test] fn test_errors() { assert_eq!( diff --git a/src/operator/mod.rs b/src/operator/mod.rs index 8b75b39..dbcd3c0 100644 --- a/src/operator/mod.rs +++ b/src/operator/mod.rs @@ -1,6 +1,7 @@ use crate::{configuration::Configuration, error::*, value::Value}; +use std::fmt::Debug; -pub trait Operator { +pub trait Operator: Debug { /// 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 @@ -20,25 +21,42 @@ pub trait Operator { fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result; } +#[derive(Debug)] pub struct RootNode; +#[derive(Debug)] pub struct Add; +#[derive(Debug)] pub struct Sub; +#[derive(Debug)] pub struct Neg; +#[derive(Debug)] pub struct Mul; +#[derive(Debug)] pub struct Div; +#[derive(Debug)] pub struct Mod; +#[derive(Debug)] pub struct Eq; +#[derive(Debug)] pub struct Neq; +#[derive(Debug)] pub struct Gt; +#[derive(Debug)] pub struct Lt; +#[derive(Debug)] pub struct Geq; +#[derive(Debug)] pub struct Leq; +#[derive(Debug)] pub struct And; +#[derive(Debug)] pub struct Or; +#[derive(Debug)] pub struct Not; +#[derive(Debug)] pub struct Const { value: Value, } @@ -49,11 +67,23 @@ impl Const { } } -pub struct Identifier { +#[derive(Debug)] +pub struct VariableIdentifier { identifier: String, } -impl Identifier { +impl VariableIdentifier { + pub fn new(identifier: String) -> Self { + Self { identifier } + } +} + +#[derive(Debug)] +pub struct FunctionIdentifier { + identifier: String, +} + +impl FunctionIdentifier { pub fn new(identifier: String) -> Self { Self { identifier } } @@ -466,24 +496,39 @@ impl Operator for Const { } } -impl Operator for Identifier { +impl Operator for VariableIdentifier { fn precedence(&self) -> i32 { 200 } + fn argument_amount(&self) -> usize { + 0 + } + + fn eval(&self, _arguments: &[Value], configuration: &Configuration) -> Result { + if let Some(value) = configuration.get_value(&self.identifier).cloned() { + Ok(value) + } else { + Err(Error::VariableIdentifierNotFound(self.identifier.clone())) + } + } +} + +impl Operator for FunctionIdentifier { + fn precedence(&self) -> i32 { + 190 + } + fn argument_amount(&self) -> usize { 1 } fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result { - if arguments.len() == 0 { - if let Some(value) = configuration.get_value(&self.identifier).cloned() { - Ok(value) - } else { - Err(Error::IdentifierNotFound(self.identifier.clone())) - } + if let Some(function) = configuration.get_function(&self.identifier) { + // Function::call checks for correct argument amount + function.call(arguments) } else { - unimplemented!() + Err(Error::FunctionIdentifierNotFound(self.identifier.clone())) } } } diff --git a/src/token/mod.rs b/src/token/mod.rs index 3356399..d7c13d7 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -25,7 +25,6 @@ pub enum Token { // Precedence LBrace, RBrace, - Whitespace, // Complex tokens Identifier(String), @@ -38,6 +37,7 @@ pub enum Token { pub enum PartialToken { Token(Token), Literal(String), + Whitespace, Eq, ExclamationMark, Gt, @@ -67,7 +67,7 @@ fn char_to_partial_token(c: char) -> PartialToken { c => { if c.is_whitespace() { - PartialToken::Token(Token::Whitespace) + PartialToken::Whitespace } else { PartialToken::Literal(c.to_string()) } @@ -77,7 +77,36 @@ fn char_to_partial_token(c: char) -> PartialToken { impl Token { // Make this a const fn as soon as match gets stable (issue #57563) - pub fn is_value(&self) -> bool { + pub fn is_leftsided_value(&self) -> bool { + match self { + Token::Plus => false, + Token::Minus => false, + Token::Star => false, + Token::Slash => false, + Token::Percent => false, + + Token::Eq => false, + Token::Neq => false, + Token::Gt => false, + Token::Lt => false, + Token::Geq => false, + Token::Leq => false, + Token::And => false, + Token::Or => false, + Token::Not => false, + + Token::LBrace => true, + Token::RBrace => false, + + Token::Identifier(_) => true, + Token::Float(_) => true, + Token::Int(_) => true, + Token::Boolean(_) => true, + } + } + + // Make this a const fn as soon as match gets stable (issue #57563) + pub fn is_rightsided_value(&self) -> bool { match self { Token::Plus => false, Token::Minus => false, @@ -97,7 +126,6 @@ impl Token { Token::LBrace => false, Token::RBrace => true, - Token::Whitespace => false, Token::Identifier(_) => true, Token::Float(_) => true, @@ -138,57 +166,64 @@ fn resolve_literals(mut tokens: &[PartialToken]) -> Result, Error> { let second = tokens.get(1).cloned(); let mut cutoff = 2; - result.push(match first { - PartialToken::Token(token) => { - cutoff = 1; - token + result.extend( + match first { + PartialToken::Token(token) => { + cutoff = 1; + Some(token) + } + PartialToken::Literal(literal) => { + cutoff = 1; + if let Ok(number) = literal.parse::() { + Some(Token::Int(number)) + } else if let Ok(number) = literal.parse::() { + Some(Token::Float(number)) + } else if let Ok(boolean) = literal.parse::() { + Some(Token::Boolean(boolean)) + } else { + Some(Token::Identifier(literal.to_string())) + } + } + PartialToken::Whitespace => { + cutoff = 1; + None + } + PartialToken::Eq => match second { + Some(PartialToken::Eq) => Some(Token::Eq), + _ => return Err(Error::unmatched_partial_token(first, second)), + }, + PartialToken::ExclamationMark => match second { + Some(PartialToken::Eq) => Some(Token::Eq), + _ => { + cutoff = 1; + Some(Token::Not) + } + }, + PartialToken::Gt => match second { + Some(PartialToken::Eq) => Some(Token::Geq), + _ => { + cutoff = 1; + Some(Token::Gt) + } + }, + PartialToken::Lt => match second { + Some(PartialToken::Eq) => Some(Token::Leq), + _ => { + cutoff = 1; + Some(Token::Lt) + } + }, + PartialToken::Ampersand => match second { + Some(PartialToken::Ampersand) => Some(Token::And), + _ => return Err(Error::unmatched_partial_token(first, second)), + }, + PartialToken::VerticalBar => match second { + Some(PartialToken::VerticalBar) => Some(Token::Or), + _ => return Err(Error::unmatched_partial_token(first, second)), + }, } - PartialToken::Literal(literal) => { - cutoff = 1; - if let Ok(number) = literal.parse::() { - Token::Int(number) - } else if let Ok(number) = literal.parse::() { - Token::Float(number) - } else if let Ok(boolean) = literal.parse::() { - Token::Boolean(boolean) - } else { - Token::Identifier(literal.to_string()) - } - } - PartialToken::Eq => match second { - Some(PartialToken::Eq) => Token::Eq, - _ => return Err(Error::unmatched_partial_token(first, second)), - }, - PartialToken::ExclamationMark => match second { - Some(PartialToken::Eq) => Token::Eq, - _ => { - cutoff = 1; - Token::Not - } - }, - PartialToken::Gt => match second { - Some(PartialToken::Eq) => Token::Geq, - _ => { - cutoff = 1; - Token::Gt - } - }, - PartialToken::Lt => match second { - Some(PartialToken::Eq) => Token::Leq, - _ => { - cutoff = 1; - Token::Lt - } - }, - PartialToken::Ampersand => match second { - Some(PartialToken::Ampersand) => Token::And, - _ => return Err(Error::unmatched_partial_token(first, second)), - }, - PartialToken::VerticalBar => match second { - Some(PartialToken::VerticalBar) => Token::Or, - _ => return Err(Error::unmatched_partial_token(first, second)), - }, - }); + .into_iter(), + ); tokens = &tokens[cutoff..]; } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index 242053c..e31e074 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -1,6 +1,7 @@ use crate::{configuration::Configuration, error::Error, operator::*, value::Value}; use token::Token; +#[derive(Debug)] pub struct Node { children: Vec, operator: Box, @@ -43,24 +44,26 @@ impl Node { if self.operator().is_leaf() { Err(Error::AppendedToLeafNode) } else if self.has_correct_amount_of_children() { - if self.children.last_mut().unwrap().operator().precedence() + if self.children.last().unwrap().operator().precedence() < node.operator().precedence() + // Function call + //|| self.children().last().unwrap() { self.children .last_mut() .unwrap() .insert_back_prioritized(node, false) } else { + if node.operator().is_leaf() { + return Err(Error::AppendedToLeafNode); + } + let last_child = self.children.pop().unwrap(); self.children.push(node); let node = self.children.last_mut().unwrap(); - if node.operator().is_leaf() { - Err(Error::AppendedToLeafNode) - } else { - node.children.push(last_child); - Ok(()) - } + node.children.push(last_child); + Ok(()) } } else { self.children.push(node); @@ -74,13 +77,16 @@ impl Node { pub fn tokens_to_operator_tree(tokens: Vec) -> Result { let mut root = vec![Node::root_node()]; - let mut last_non_whitespace_token_is_value = false; + let mut last_token_is_rightsided_value = false; + let mut token_iter = tokens.iter().peekable(); + + while let Some(token) = token_iter.next().cloned() { + let next = token_iter.peek().cloned(); - for token in tokens { let node = match token.clone() { Token::Plus => Some(Node::new(Add)), Token::Minus => { - if last_non_whitespace_token_is_value { + if last_token_is_rightsided_value { Some(Node::new(Sub)) } else { Some(Node::new(Neg)) @@ -111,9 +117,16 @@ pub fn tokens_to_operator_tree(tokens: Vec) -> Result { root.pop() } } - Token::Whitespace => None, - Token::Identifier(identifier) => Some(Node::new(Identifier::new(identifier))), + Token::Identifier(identifier) => { + let mut result = Some(Node::new(VariableIdentifier::new(identifier.clone()))); + if let Some(next) = next { + if next.is_leftsided_value() { + result = Some(Node::new(FunctionIdentifier::new(identifier))); + } + } + result + } Token::Float(number) => Some(Node::new(Const::new(Value::Float(number)))), Token::Int(number) => Some(Node::new(Const::new(Value::Int(number)))), Token::Boolean(boolean) => Some(Node::new(Const::new(Value::Boolean(boolean)))), @@ -127,9 +140,7 @@ pub fn tokens_to_operator_tree(tokens: Vec) -> Result { } } - if token != Token::Whitespace { - last_non_whitespace_token_is_value = token.is_value(); - } + last_token_is_rightsided_value = token.is_rightsided_value(); } if root.len() > 1 {