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.
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 {

View File

@ -25,6 +25,9 @@ mod test {
assert_eq!(eval("true"), Ok(Value::Boolean(true)));
assert_eq!(eval("false"), Ok(Value::Boolean(false)));
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]
@ -41,6 +44,14 @@ mod test {
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.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]
@ -52,6 +63,36 @@ mod test {
assert_eq!(eval("5 / 4*2"), Ok(Value::Int(2)));
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.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 Add;
pub struct Sub;
pub struct Neg;
pub struct Mul;
pub struct Div;
pub struct Braced;
pub struct Const {
value: Value,
@ -75,9 +77,13 @@ impl Operator for Add {
expect_number(&arguments[1])?;
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 {
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])?;
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 {
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])?;
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 {
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])?;
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 {
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)
}
}
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)]
pub enum Token {
@ -7,6 +7,8 @@ pub enum Token {
Minus,
Star,
Slash,
LBrace,
RBrace,
Whitespace,
// Complex tokens
@ -28,6 +30,8 @@ fn char_to_token(c: char) -> PartialToken {
'-' => PartialToken::Token(Token::Minus),
'*' => PartialToken::Token(Token::Star),
'/' => PartialToken::Token(Token::Slash),
'(' => PartialToken::Token(Token::LBrace),
')' => PartialToken::Token(Token::RBrace),
c => {
if c.is_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.
fn str_to_tokens(string: &str) -> Vec<PartialToken> {
let mut result = Vec::new();

View File

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

View File

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