Implemented and tested braces

This commit is contained in:
Sebastian Schmidt 2019-03-15 18:27:10 +02:00
parent 879fc4fe3d
commit 1bc2909c1b
6 changed files with 198 additions and 47 deletions

View File

@ -29,6 +29,12 @@ pub enum Error {
/// A value has the wrong type. /// A value has the wrong type.
TypeError, TypeError,
/// An opening brace without a matching closing brace was found.
UnmatchedLBrace,
/// A closing brace without a matching opening brace was found.
UnmatchedRBrace,
} }
impl Error { impl Error {

View File

@ -25,6 +25,9 @@ mod test {
assert_eq!(eval("true"), Ok(Value::Boolean(true))); assert_eq!(eval("true"), Ok(Value::Boolean(true)));
assert_eq!(eval("false"), Ok(Value::Boolean(false))); assert_eq!(eval("false"), Ok(Value::Boolean(false)));
assert_eq!(eval("blub"), Err(Error::IdentifierNotFound)); assert_eq!(eval("blub"), Err(Error::IdentifierNotFound));
assert_eq!(eval("-3"), Ok(Value::Int(-3)));
assert_eq!(eval("-3.6"), Ok(Value::Float(-3.6)));
assert_eq!(eval("----3"), Ok(Value::Int(3)));
} }
#[test] #[test]
@ -41,6 +44,14 @@ mod test {
assert_eq!(eval("5-3.0"), Ok(Value::Float(2.0))); assert_eq!(eval("5-3.0"), Ok(Value::Float(2.0)));
assert_eq!(eval("5 / 4.0"), Ok(Value::Float(1.25))); assert_eq!(eval("5 / 4.0"), Ok(Value::Float(1.25)));
assert_eq!(eval("5.0 *3"), Ok(Value::Float(15.0))); assert_eq!(eval("5.0 *3"), Ok(Value::Float(15.0)));
assert_eq!(eval("5.0 *-3"), Ok(Value::Float(-15.0)));
assert_eq!(eval("5.0 *- 3"), Ok(Value::Float(-15.0)));
assert_eq!(eval("5.0 * -3"), Ok(Value::Float(-15.0)));
assert_eq!(eval("5.0 * - 3"), Ok(Value::Float(-15.0)));
assert_eq!(eval("-5.0 *-3"), Ok(Value::Float(15.0)));
assert_eq!(eval("3+-1"), Ok(Value::Int(2)));
assert_eq!(eval("-3-5"), Ok(Value::Int(-8)));
assert_eq!(eval("-5--3"), Ok(Value::Int(-2)));
} }
#[test] #[test]
@ -52,6 +63,36 @@ mod test {
assert_eq!(eval("5 / 4*2"), Ok(Value::Int(2))); assert_eq!(eval("5 / 4*2"), Ok(Value::Int(2)));
assert_eq!(eval("1-5 *3/15"), Ok(Value::Int(0))); assert_eq!(eval("1-5 *3/15"), Ok(Value::Int(0)));
assert_eq!(eval("15/7/2.0"), Ok(Value::Float(1.0))); assert_eq!(eval("15/7/2.0"), Ok(Value::Float(1.0)));
assert_eq!(eval("15.0/7/2"), Ok(Value::Float(15.0/7.0/2.0))); assert_eq!(eval("15.0/7/2"), Ok(Value::Float(15.0 / 7.0 / 2.0)));
assert_eq!(eval("15.0/-7/2"), Ok(Value::Float(15.0 / -7.0 / 2.0)));
assert_eq!(eval("-15.0/7/2"), Ok(Value::Float(-15.0 / 7.0 / 2.0)));
assert_eq!(eval("-15.0/7/-2"), Ok(Value::Float(-15.0 / 7.0 / -2.0)));
} }
#[test]
fn test_braced_examples() {
assert_eq!(eval("(1)"), Ok(Value::Int(1)));
assert_eq!(eval("( 1.0 )"), Ok(Value::Float(1.0)));
assert_eq!(eval("( true)"), Ok(Value::Boolean(true)));
assert_eq!(eval("( -1 )"), Ok(Value::Int(-1)));
assert_eq!(eval("-(1)"), Ok(Value::Int(-1)));
assert_eq!(eval("-(1 + 3) * 7"), Ok(Value::Int(-28)));
assert_eq!(eval("(1 * 1) - 3"), Ok(Value::Int(-2)));
assert_eq!(eval("4 / (2 * 2)"), Ok(Value::Int(1)));
assert_eq!(eval("7/(7/(7/(7/(7/(7)))))"), Ok(Value::Int(1)));
}
#[test]
fn test_type_errors() {
assert_eq!(
eval("-true"),
Err(Error::expected_number(Value::Boolean(true)))
);
assert_eq!(
eval("1-true"),
Err(Error::expected_number(Value::Boolean(true)))
);
assert_eq!(eval("true-"), Err(Error::wrong_argument_amount(1, 2)));
}
} }

View File

@ -23,8 +23,10 @@ pub trait Operator {
pub struct RootNode; pub struct RootNode;
pub struct Add; pub struct Add;
pub struct Sub; pub struct Sub;
pub struct Neg;
pub struct Mul; pub struct Mul;
pub struct Div; pub struct Div;
pub struct Braced;
pub struct Const { pub struct Const {
value: Value, value: Value,
@ -75,9 +77,13 @@ impl Operator for Add {
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
if arguments[0].is_int() && arguments[1].is_int() { if arguments[0].is_int() && arguments[1].is_int() {
Ok(Value::Int(arguments[0].as_int().unwrap() + arguments[1].as_int().unwrap())) Ok(Value::Int(
arguments[0].as_int().unwrap() + arguments[1].as_int().unwrap(),
))
} else { } else {
Ok(Value::Float(arguments[0].as_float().unwrap() + arguments[1].as_float().unwrap())) Ok(Value::Float(
arguments[0].as_float().unwrap() + arguments[1].as_float().unwrap(),
))
} }
} }
} }
@ -97,9 +103,34 @@ impl Operator for Sub {
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
if arguments[0].is_int() && arguments[1].is_int() { if arguments[0].is_int() && arguments[1].is_int() {
Ok(Value::Int(arguments[0].as_int().unwrap() - arguments[1].as_int().unwrap())) Ok(Value::Int(
arguments[0].as_int().unwrap() - arguments[1].as_int().unwrap(),
))
} else { } else {
Ok(Value::Float(arguments[0].as_float().unwrap() - arguments[1].as_float().unwrap())) Ok(Value::Float(
arguments[0].as_float().unwrap() - arguments[1].as_float().unwrap(),
))
}
}
}
impl Operator for Neg {
fn precedence(&self) -> i32 {
110
}
fn argument_amount(&self) -> usize {
1
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 1)?;
expect_number(&arguments[0])?;
if arguments[0].is_int() {
Ok(Value::Int(-arguments[0].as_int().unwrap()))
} else {
Ok(Value::Float(-arguments[0].as_float().unwrap()))
} }
} }
} }
@ -119,9 +150,13 @@ impl Operator for Mul {
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
if arguments[0].is_int() && arguments[1].is_int() { if arguments[0].is_int() && arguments[1].is_int() {
Ok(Value::Int(arguments[0].as_int().unwrap() * arguments[1].as_int().unwrap())) Ok(Value::Int(
arguments[0].as_int().unwrap() * arguments[1].as_int().unwrap(),
))
} else { } else {
Ok(Value::Float(arguments[0].as_float().unwrap() * arguments[1].as_float().unwrap())) Ok(Value::Float(
arguments[0].as_float().unwrap() * arguments[1].as_float().unwrap(),
))
} }
} }
} }
@ -141,9 +176,13 @@ impl Operator for Div {
expect_number(&arguments[1])?; expect_number(&arguments[1])?;
if arguments[0].is_int() && arguments[1].is_int() { if arguments[0].is_int() && arguments[1].is_int() {
Ok(Value::Int(arguments[0].as_int().unwrap() / arguments[1].as_int().unwrap())) Ok(Value::Int(
arguments[0].as_int().unwrap() / arguments[1].as_int().unwrap(),
))
} else { } else {
Ok(Value::Float(arguments[0].as_float().unwrap() / arguments[1].as_float().unwrap())) Ok(Value::Float(
arguments[0].as_float().unwrap() / arguments[1].as_float().unwrap(),
))
} }
} }
} }
@ -179,3 +218,18 @@ impl Operator for Identifier {
configuration.get_value(&self.identifier) configuration.get_value(&self.identifier)
} }
} }
impl Operator for Braced {
fn precedence(&self) -> i32 {
200
}
fn argument_amount(&self) -> usize {
1
}
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 1)?;
Ok(arguments[0].clone())
}
}

View File

@ -1,4 +1,4 @@
use value::{IntType, FloatType}; use value::{FloatType, IntType};
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum Token { pub enum Token {
@ -7,6 +7,8 @@ pub enum Token {
Minus, Minus,
Star, Star,
Slash, Slash,
LBrace,
RBrace,
Whitespace, Whitespace,
// Complex tokens // Complex tokens
@ -28,6 +30,8 @@ fn char_to_token(c: char) -> PartialToken {
'-' => PartialToken::Token(Token::Minus), '-' => PartialToken::Token(Token::Minus),
'*' => PartialToken::Token(Token::Star), '*' => PartialToken::Token(Token::Star),
'/' => PartialToken::Token(Token::Slash), '/' => PartialToken::Token(Token::Slash),
'(' => PartialToken::Token(Token::LBrace),
')' => PartialToken::Token(Token::RBrace),
c => { c => {
if c.is_whitespace() { if c.is_whitespace() {
PartialToken::Token(Token::Whitespace) PartialToken::Token(Token::Whitespace)
@ -38,6 +42,25 @@ fn char_to_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 {
match self {
Token::Plus => false,
Token::Minus => false,
Token::Star => false,
Token::Slash => false,
Token::LBrace => false,
Token::RBrace => true,
Token::Whitespace => false,
Token::Identifier(_) => true,
Token::Float(_) => true,
Token::Int(_) => true,
Token::Boolean(_) => true,
}
}
}
/// Converts a string to a vector of partial tokens. /// Converts a string to a vector of partial tokens.
fn str_to_tokens(string: &str) -> Vec<PartialToken> { fn str_to_tokens(string: &str) -> Vec<PartialToken> {
let mut result = Vec::new(); let mut result = Vec::new();

View File

@ -7,18 +7,19 @@ pub struct Node {
} }
impl Node { impl Node {
fn new(operator: Box<dyn Operator>) -> Self { fn new<T: Operator + 'static>(operator: T) -> Self {
Self { Self {
children: Vec::new(), children: Vec::new(),
operator, operator: Box::new(operator),
} }
} }
fn root_node() -> Self { fn root_node() -> Self {
Self { Self::new(RootNode)
children: Vec::new(),
operator: Box::new(RootNode),
} }
fn braced_node() -> Self {
Self::new(Braced)
} }
pub fn eval(&self, configuration: &Configuration) -> Result<Value, Error> { pub fn eval(&self, configuration: &Configuration) -> Result<Value, Error> {
@ -41,32 +42,31 @@ impl Node {
self.children().len() == self.operator().argument_amount() self.children().len() == self.operator().argument_amount()
} }
fn insert_back_prioritized(&mut self, operator: Box<dyn Operator>) -> Result<(), Error> { fn insert_back_prioritized(&mut self, node: Node, is_root_node: bool) -> Result<(), Error> {
if self.operator().precedence() < operator.precedence() { if self.operator().precedence() < node.operator().precedence() || is_root_node {
if self.operator().is_leaf() { if self.operator().is_leaf() {
Err(Error::AppendedToLeafNode) Err(Error::AppendedToLeafNode)
} else if self.has_correct_amount_of_children() { } else if self.has_correct_amount_of_children() {
if self.children.last_mut().unwrap().operator().precedence() < operator.precedence() if self.children.last_mut().unwrap().operator().precedence() < node.operator().precedence()
{ {
self.children self.children
.last_mut() .last_mut()
.unwrap() .unwrap()
.insert_back_prioritized(operator) .insert_back_prioritized(node, false)
} else { } else {
let new_node = Node::new(operator);
let last_child = self.children.pop().unwrap(); let last_child = self.children.pop().unwrap();
self.children.push(new_node); self.children.push(node);
let new_node = self.children.last_mut().unwrap(); let node = self.children.last_mut().unwrap();
if new_node.operator().is_leaf() { if node.operator().is_leaf() {
Err(Error::AppendedToLeafNode) Err(Error::AppendedToLeafNode)
} else { } else {
new_node.children.push(last_child); node.children.push(last_child);
Ok(()) Ok(())
} }
} }
} else { } else {
self.children.push(Node::new(operator)); self.children.push(node);
Ok(()) Ok(())
} }
} else { } else {
@ -76,29 +76,62 @@ impl Node {
} }
pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> { pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
let mut root = Node::root_node(); let mut root = vec![Node::root_node()];
let mut last_non_whitespace_token_is_value = false;
for token in tokens { for token in tokens {
let operator: Option<Box<dyn Operator>> = match token { let node = match token.clone() {
Token::Plus => Some(Box::new(Add)), Token::Plus => Some(Node::new(Add)),
Token::Minus => Some(Box::new(Sub)), Token::Minus => {
Token::Star => Some(Box::new(Mul)), if last_non_whitespace_token_is_value {
Token::Slash => Some(Box::new(Div)), Some(Node::new(Sub))
} else {
Some(Node::new(Neg))
}
}
Token::Star => Some(Node::new(Mul)),
Token::Slash => Some(Node::new(Div)),
Token::LBrace => {
root.push(Node::braced_node());
None
},
Token::RBrace => {
if root.len() < 2 {
return Err(Error::UnmatchedRBrace);
} else {
root.pop()
}
}
Token::Whitespace => None, Token::Whitespace => None,
Token::Identifier(identifier) => Some(Box::new(Identifier::new(identifier))), Token::Identifier(identifier) => Some(Node::new(Identifier::new(identifier))),
Token::Float(number) => Some(Box::new(Const::new(Value::Float(number)))), Token::Float(number) => Some(Node::new(Const::new(Value::Float(number)))),
Token::Int(number) => Some(Box::new(Const::new(Value::Int(number)))), Token::Int(number) => Some(Node::new(Const::new(Value::Int(number)))),
Token::Boolean(boolean) => Some(Box::new(Const::new(Value::Boolean(boolean)))), Token::Boolean(boolean) => Some(Node::new(Const::new(Value::Boolean(boolean)))),
}; };
if let Some(operator) = operator { if let Some(node) = node {
root.insert_back_prioritized(operator)?; if let Some(root) = root.last_mut() {
root.insert_back_prioritized(node, true)?;
} else {
return Err(Error::UnmatchedRBrace);
} }
} }
if token != Token::Whitespace {
last_non_whitespace_token_is_value = token.is_value();
}
}
if root.len() > 1 {
Err(Error::UnmatchedLBrace)
} else if root.len() == 0 {
Err(Error::UnmatchedRBrace)
} else {
let mut root = root.pop().unwrap();
if root.children().len() == 1 { if root.children().len() == 1 {
Ok(root.children.pop().unwrap()) Ok(root.children.pop().unwrap())
} else { } else {
Err(Error::EmptyExpression) Err(Error::EmptyExpression)
} }
}
} }

View File

@ -41,9 +41,3 @@ impl Value {
} }
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub enum Number {
Float(FloatType),
Int(IntType),
}