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.
|
/// 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 {
|
||||||
|
41
src/lib.rs
41
src/lib.rs
@ -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]
|
||||||
@ -53,5 +64,35 @@ mod test {
|
|||||||
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)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -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