Implemented and tested braces
This commit is contained in:
parent
879fc4fe3d
commit
1bc2909c1b
@ -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 {
|
||||
|
43
src/lib.rs
43
src/lib.rs
@ -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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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.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 {
|
||||
Ok(root.children.pop().unwrap())
|
||||
} else {
|
||||
Err(Error::EmptyExpression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,9 +41,3 @@ impl Value {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Number {
|
||||
Float(FloatType),
|
||||
Int(IntType),
|
||||
}
|
Loading…
Reference in New Issue
Block a user