Add comma operator and display implementations

+ Add comma token
 + Add tuple operator
 + Add tuple value type
 + Implement display for `tree::Node` and all related types
 + Add support for right-to-left chaining of operators with same precedence

The priority of chaining of operators with the same precedence remains left-to-right.
Only if two consecutive operators with the same precedence are right-to-left, they will be ordered right-to-left.

Implements #4.
This commit is contained in:
Sebastian Schmidt 2019-03-19 16:54:52 +02:00
parent 265b1214b0
commit c631171ff0
11 changed files with 454 additions and 34 deletions

View File

@ -191,3 +191,4 @@ See [LICENSE](LICENSE) for details.
## Closing Notes
If you have any ideas for features or see any problems in the code, architecture, interface, algorithmics or documentation, please open an issue on github.
If there is already an issue describing what you want to say, please add a thumbs up or whatever emoji you think fits to the issue, so I know which ones I should prioritize.

View File

@ -3,7 +3,11 @@ use token::PartialToken;
#[derive(Debug, PartialEq)]
pub enum Error {
WrongArgumentAmount {
WrongOperatorArgumentAmount {
expected: usize,
actual: usize,
},
WrongFunctionArgumentAmount {
expected: usize,
actual: usize,
},
@ -50,8 +54,12 @@ pub enum Error {
}
impl Error {
pub fn wrong_argument_amount(actual: usize, expected: usize) -> Self {
Error::WrongArgumentAmount { actual, expected }
pub fn wrong_operator_argument_amount(actual: usize, expected: usize) -> Self {
Error::WrongOperatorArgumentAmount { actual, expected }
}
pub fn wrong_function_argument_amount(actual: usize, expected: usize) -> Self {
Error::WrongFunctionArgumentAmount { actual, expected }
}
pub fn expected_number(actual: Value) -> Self {
@ -67,11 +75,19 @@ impl Error {
}
}
pub fn expect_argument_amount(actual: usize, expected: usize) -> Result<(), Error> {
pub fn expect_operator_argument_amount(actual: usize, expected: usize) -> Result<(), Error> {
if actual == expected {
Ok(())
} else {
Err(Error::wrong_argument_amount(actual, expected))
Err(Error::wrong_operator_argument_amount(actual, expected))
}
}
pub fn expect_function_argument_amount(actual: usize, expected: usize) -> Result<(), Error> {
if actual == expected {
Ok(())
} else {
Err(Error::wrong_function_argument_amount(actual, expected))
}
}

View File

@ -18,7 +18,7 @@ impl Function {
}
pub fn call(&self, arguments: &[Value]) -> Result<Value, Error> {
error::expect_argument_amount(self.argument_amount, arguments.len())?;
error::expect_function_argument_amount(arguments.len(), self.argument_amount)?;
(self.function)(arguments)
}
}

View File

@ -175,6 +175,8 @@
//! See [LICENSE](LICENSE) for details.
//!
extern crate core;
mod configuration;
pub mod error;
mod function;
@ -210,7 +212,7 @@ pub fn build_operator_tree(string: &str) -> Result<Node, Error> {
mod test {
use crate::{eval, value::Value};
use configuration::HashMapConfiguration;
use error::Error;
use error::{expect_number, Error};
use eval_with_configuration;
use Function;
@ -377,6 +379,88 @@ mod test {
);
}
#[test]
fn test_n_ary_functions() {
let mut configuration = HashMapConfiguration::new();
configuration.insert_function(
"sub2",
Function::new(
1,
Box::new(|arguments| {
if let Value::Int(int) = arguments[0] {
Ok(Value::Int(int - 2))
} else if let Value::Float(float) = arguments[0] {
Ok(Value::Float(float - 2.0))
} else {
Err(Error::expected_number(arguments[0].clone()))
}
}),
),
);
configuration.insert_function(
"avg",
Function::new(
2,
Box::new(|arguments| {
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
if let (Value::Int(a), Value::Int(b)) = (&arguments[0], &arguments[1]) {
Ok(Value::Int((a + b) / 2))
} else {
Ok(Value::Float(
(arguments[0].as_float()? + arguments[1].as_float()?) / 2.0,
))
}
}),
),
);
configuration.insert_function(
"muladd",
Function::new(
3,
Box::new(|arguments| {
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
expect_number(&arguments[2])?;
if let (Value::Int(a), Value::Int(b), Value::Int(c)) =
(&arguments[0], &arguments[1], &arguments[2])
{
Ok(Value::Int(a * b + c))
} else {
Ok(Value::Float(
arguments[0].as_float()? * arguments[1].as_float()?
+ arguments[2].as_float()?,
))
}
}),
),
);
configuration.insert_variable("five".to_string(), Value::Int(5));
assert_eq!(
eval_with_configuration("avg(7, 5)", &configuration),
Ok(Value::Int(6))
);
assert_eq!(
eval_with_configuration("avg(sub2 5, 5)", &configuration),
Ok(Value::Int(4))
);
assert_eq!(
eval_with_configuration("sub2(avg(3, 6))", &configuration),
Ok(Value::Int(2))
);
assert_eq!(
eval_with_configuration("sub2 avg(3, 6)", &configuration),
Ok(Value::Int(2))
);
assert_eq!(
eval_with_configuration("muladd(3, 6, -4)", &configuration),
Ok(Value::Int(14))
);
}
#[test]
fn test_errors() {
assert_eq!(
@ -387,7 +471,10 @@ mod test {
eval("1-true"),
Err(Error::expected_number(Value::Boolean(true)))
);
assert_eq!(eval("true-"), Err(Error::wrong_argument_amount(1, 2)));
assert_eq!(
eval("true-"),
Err(Error::wrong_operator_argument_amount(1, 2))
);
assert_eq!(eval("!(()true)"), Err(Error::AppendedToLeafNode));
}
}

122
src/operator/display.rs Normal file
View File

@ -0,0 +1,122 @@
use operator::*;
use std::fmt::{Display, Error, Formatter};
impl Display for RootNode {
fn fmt(&self, _f: &mut Formatter) -> Result<(), Error> {
Ok(())
}
}
impl Display for Add {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "+")
}
}
impl Display for Sub {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "-")
}
}
impl Display for Neg {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "-")
}
}
impl Display for Mul {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "*")
}
}
impl Display for Div {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "/")
}
}
impl Display for Mod {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "%")
}
}
impl Display for Eq {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "==")
}
}
impl Display for Neq {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "!=")
}
}
impl Display for Gt {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, ">")
}
}
impl Display for Lt {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "<")
}
}
impl Display for Geq {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, ">=")
}
}
impl Display for Leq {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "<=")
}
}
impl Display for And {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "&&")
}
}
impl Display for Or {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "||")
}
}
impl Display for Not {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "!")
}
}
impl Display for Tuple {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, ", ")
}
}
impl Display for Const {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}", self.value)
}
}
impl Display for VariableIdentifier {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}", self.identifier)
}
}
impl Display for FunctionIdentifier {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "{}", self.identifier)
}
}

View File

@ -1,12 +1,20 @@
use crate::{configuration::Configuration, error::*, value::Value};
use std::fmt::Debug;
use std::fmt::{Debug, Display};
pub trait Operator: Debug {
mod display;
pub trait Operator: Debug + Display {
/// 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
fn precedence(&self) -> i32;
/// Returns true if chains of operators with the same precedence as this one should be evaluated left-to-right,
/// and false if they should be evaluated right-to-left.
/// Left-to-right chaining has priority if operators with different order but same precedence are chained.
// Make this a const fn once #57563 is resolved
fn is_left_to_right(&self) -> bool;
/// True if this operator is a leaf, meaning it accepts no arguments.
// Make this a const fn once #57563 is resolved
fn is_leaf(&self) -> bool {
@ -56,6 +64,9 @@ pub struct Or;
#[derive(Debug)]
pub struct Not;
#[derive(Debug)]
pub struct Tuple;
#[derive(Debug)]
pub struct Const {
value: Value,
@ -94,12 +105,16 @@ impl Operator for RootNode {
200
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
1
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 1)?;
expect_operator_argument_amount(arguments.len(), 1)?;
Ok(arguments[0].clone())
}
}
@ -109,12 +124,16 @@ impl Operator for Add {
95
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -135,12 +154,16 @@ impl Operator for Sub {
95
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -161,12 +184,16 @@ impl Operator for Neg {
110
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
1
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 1)?;
expect_operator_argument_amount(arguments.len(), 1)?;
expect_number(&arguments[0])?;
if arguments[0].is_int() {
@ -182,12 +209,16 @@ impl Operator for Mul {
100
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -208,12 +239,16 @@ impl Operator for Div {
100
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -234,12 +269,16 @@ impl Operator for Mod {
100
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -260,12 +299,16 @@ impl Operator for Eq {
80
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
if arguments[0] == arguments[1] {
Ok(Value::Boolean(true))
@ -280,12 +323,16 @@ impl Operator for Neq {
80
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
if arguments[0] != arguments[1] {
Ok(Value::Boolean(true))
@ -300,12 +347,16 @@ impl Operator for Gt {
80
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -330,12 +381,16 @@ impl Operator for Lt {
80
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -360,12 +415,16 @@ impl Operator for Geq {
80
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -390,12 +449,16 @@ impl Operator for Leq {
80
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
expect_number(&arguments[0])?;
expect_number(&arguments[1])?;
@ -420,12 +483,16 @@ impl Operator for And {
75
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
let a = expect_boolean(&arguments[0])?;
let b = expect_boolean(&arguments[1])?;
@ -442,12 +509,16 @@ impl Operator for Or {
70
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 2)?;
expect_operator_argument_amount(arguments.len(), 2)?;
let a = expect_boolean(&arguments[0])?;
let b = expect_boolean(&arguments[1])?;
@ -464,12 +535,16 @@ impl Operator for Not {
110
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
1
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 1)?;
expect_operator_argument_amount(arguments.len(), 1)?;
let a = expect_boolean(&arguments[0])?;
if !a {
@ -480,17 +555,58 @@ impl Operator for Not {
}
}
impl Operator for Tuple {
fn precedence(&self) -> i32 {
40
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
2
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
if let Value::Tuple(tuple) = &arguments[0] {
let mut tuple = tuple.clone();
if let Value::Tuple(tuple2) = &arguments[1] {
tuple.extend(tuple2.iter().cloned());
} else {
tuple.push(arguments[1].clone());
}
Ok(Value::from(tuple))
} else {
if let Value::Tuple(tuple) = &arguments[1] {
let mut tuple = tuple.clone();
tuple.insert(0, arguments[0].clone());
Ok(Value::from(tuple))
} else {
Ok(Value::from(vec![
arguments[0].clone(),
arguments[1].clone(),
]))
}
}
}
}
impl Operator for Const {
fn precedence(&self) -> i32 {
200
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
0
}
fn eval(&self, arguments: &[Value], _configuration: &Configuration) -> Result<Value, Error> {
expect_argument_amount(arguments.len(), 0)?;
expect_operator_argument_amount(arguments.len(), 0)?;
Ok(self.value.clone())
}
@ -501,6 +617,10 @@ impl Operator for VariableIdentifier {
200
}
fn is_left_to_right(&self) -> bool {
true
}
fn argument_amount(&self) -> usize {
0
}
@ -519,13 +639,24 @@ impl Operator for FunctionIdentifier {
190
}
fn is_left_to_right(&self) -> bool {
false
}
fn argument_amount(&self) -> usize {
1
}
fn eval(&self, arguments: &[Value], configuration: &Configuration) -> Result<Value, Error> {
expect_operator_argument_amount(arguments.len(), 1)?;
let arguments = if let Value::Tuple(arguments) = &arguments[0] {
arguments
} else {
arguments
};
if let Some(function) = configuration.get_function(&self.identifier) {
// Function::call checks for correct argument amount
function.call(arguments)
} else {
Err(Error::FunctionIdentifierNotFound(self.identifier.clone()))

View File

@ -3,7 +3,6 @@ use value::{FloatType, IntType};
#[derive(Clone, PartialEq, Debug)]
pub enum Token {
// Single character tokens
// Arithmetic
Plus,
Minus,
@ -26,7 +25,10 @@ pub enum Token {
LBrace,
RBrace,
// Complex tokens
// Aggregation
Comma,
// Values, Variables and Functions
Identifier(String),
Float(FloatType),
Int(IntType),
@ -65,6 +67,8 @@ fn char_to_partial_token(c: char) -> PartialToken {
'(' => PartialToken::Token(Token::LBrace),
')' => PartialToken::Token(Token::RBrace),
',' => PartialToken::Token(Token::Comma),
c => {
if c.is_whitespace() {
PartialToken::Whitespace
@ -98,6 +102,8 @@ impl Token {
Token::LBrace => true,
Token::RBrace => false,
Token::Comma => false,
Token::Identifier(_) => true,
Token::Float(_) => true,
Token::Int(_) => true,
@ -127,6 +133,8 @@ impl Token {
Token::LBrace => false,
Token::RBrace => true,
Token::Comma => false,
Token::Identifier(_) => true,
Token::Float(_) => true,
Token::Int(_) => true,

12
src/tree/display.rs Normal file
View File

@ -0,0 +1,12 @@
use std::fmt::{Display, Error, Formatter};
use Node;
impl Display for Node {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
self.operator.fmt(f)?;
for child in self.children() {
write!(f, " {}", child)?;
}
Ok(())
}
}

View File

@ -1,6 +1,8 @@
use crate::{configuration::Configuration, error::Error, operator::*, value::Value};
use token::Token;
mod display;
#[derive(Debug)]
pub struct Node {
children: Vec<Node>,
@ -40,14 +42,18 @@ impl Node {
}
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().precedence() < node.operator().precedence() || is_root_node
// Right-to-left chaining
|| (self.operator().precedence() == node.operator().precedence() && !self.operator().is_left_to_right() && !node.operator().is_left_to_right())
{
if self.operator().is_leaf() {
Err(Error::AppendedToLeafNode)
} else if self.has_correct_amount_of_children() {
if self.children.last().unwrap().operator().precedence()
< node.operator().precedence()
// Function call
//|| self.children().last().unwrap()
// Right-to-left chaining
|| (self.children.last().unwrap().operator().precedence()
== node.operator().precedence() && !self.children.last().unwrap().operator().is_left_to_right() && !node.operator().is_left_to_right())
{
self.children
.last_mut()
@ -118,6 +124,8 @@ pub fn tokens_to_operator_tree(tokens: Vec<Token>) -> Result<Node, Error> {
}
}
Token::Comma => Some(Node::new(Tuple)),
Token::Identifier(identifier) => {
let mut result = Some(Node::new(VariableIdentifier::new(identifier.clone())));
if let Some(next) = next {

26
src/value/display.rs Normal file
View File

@ -0,0 +1,26 @@
use std::fmt::{Display, Error, Formatter};
use Value;
impl Display for Value {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match self {
Value::String(string) => write!(f, "\"{}\"", string),
Value::Float(float) => write!(f, "{}", float),
Value::Int(int) => write!(f, "{}", int),
Value::Boolean(boolean) => write!(f, "{}", boolean),
Value::Tuple(tuple) => {
write!(f, "(")?;
let mut once = false;
for value in tuple {
if once {
write!(f, ", ")?;
} else {
once = true;
}
value.fmt(f)?;
}
write!(f, ")")
}
}
}
}

View File

@ -1,5 +1,7 @@
use error::Error;
mod display;
pub type IntType = i64;
pub type FloatType = f64;
@ -9,6 +11,7 @@ pub enum Value {
Float(FloatType),
Int(IntType),
Boolean(bool),
Tuple(Vec<Value>),
}
impl Value {
@ -72,8 +75,14 @@ impl From<bool> for Value {
}
}
impl From<Vec<Value>> for Value {
fn from(tuple: Vec<Value>) -> Self {
Value::Tuple(tuple)
}
}
impl From<Value> for Result<Value, Error> {
fn from(value: Value) -> Self {
Ok(value)
}
}
}