Refactor operators into an enum

(This commit is not formatted properly)

Relates to #46
This commit is contained in:
Sebastian Schmidt 2019-04-14 16:54:35 +02:00
parent 697fbb5d45
commit 6c078c49e5
10 changed files with 589 additions and 962 deletions

View File

@ -18,18 +18,22 @@ impl fmt::Display for EvalexprError {
),
ExpectedString { actual } => {
write!(f, "Expected a Value::String, but got {:?}.", actual)
},
}
ExpectedInt { actual } => write!(f, "Expected a Value::Int, but got {:?}.", actual),
ExpectedFloat { actual } => write!(f, "Expected a Value::Float, but got {:?}.", actual),
ExpectedNumber { actual } => {
write!(f, "Expected a Value::Float or Value::Int, but got {:?}.", actual)
},
ExpectedNumberOrString { actual } => {
write!(f, "Expected a Value::Number or a Value::String, but got {:?}.", actual)
},
ExpectedNumber { actual } => write!(
f,
"Expected a Value::Float or Value::Int, but got {:?}.",
actual
),
ExpectedNumberOrString { actual } => write!(
f,
"Expected a Value::Number or a Value::String, but got {:?}.",
actual
),
ExpectedBoolean { actual } => {
write!(f, "Expected a Value::Boolean, but got {:?}.", actual)
},
}
ExpectedTuple { actual } => write!(f, "Expected a Value::Tuple, but got {:?}.", actual),
ExpectedEmpty { actual } => write!(f, "Expected a Value::Empty, but got {:?}.", actual),
AppendedToLeafNode => write!(f, "Tried to append a node to a leaf node."),
@ -49,7 +53,7 @@ impl fmt::Display for EvalexprError {
),
TypeError { expected, actual } => {
write!(f, "Expected one of {:?}, but got {:?}.", expected, actual)
},
}
UnmatchedLBrace => write!(f, "Found an unmatched opening parenthesis '('."),
UnmatchedRBrace => write!(f, "Found an unmatched closing parenthesis ')'."),
UnmatchedPartialToken { first, second } => {
@ -67,7 +71,7 @@ impl fmt::Display for EvalexprError {
first
)
}
},
}
AdditionError { augend, addend } => write!(f, "Error adding {} + {}", augend, addend),
SubtractionError {
minuend,
@ -80,11 +84,15 @@ impl fmt::Display for EvalexprError {
} => write!(f, "Error multiplying {} * {}", multiplicand, multiplier),
DivisionError { dividend, divisor } => {
write!(f, "Error dividing {} / {}", dividend, divisor)
},
}
ModulationError { dividend, divisor } => {
write!(f, "Error modulating {} % {}", dividend, divisor)
},
InvalidRegex { regex, message } => write!(f, "Regular expression {:?} is invalid: {:?}", regex, message),
}
InvalidRegex { regex, message } => write!(
f,
"Regular expression {:?} is invalid: {:?}",
regex, message
),
ContextNotManipulable => write!(f, "Cannot manipulate context"),
IllegalEscapeSequence(string) => write!(f, "Illegal escape sequence: {}", string),
CustomMessage(message) => write!(f, "Error: {}", message),

View File

@ -290,7 +290,7 @@ impl EvalexprError {
/// Constructs `EvalexprError::InvalidRegex(regex)`
pub fn invalid_regex(regex: String, message: String) -> Self {
EvalexprError::InvalidRegex{ regex, message }
EvalexprError::InvalidRegex { regex, message }
}
}
@ -309,10 +309,7 @@ pub(crate) fn expect_operator_argument_amount(
}
/// Returns `Ok(())` if the actual and expected parameters are equal, and `Err(Error::WrongFunctionArgumentAmount)` otherwise.
pub fn expect_function_argument_amount(
actual: usize,
expected: usize,
) -> EvalexprResult<()> {
pub fn expect_function_argument_amount(actual: usize, expected: usize) -> EvalexprResult<()> {
if actual == expected {
Ok(())
} else {

View File

@ -67,7 +67,6 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
)),
// string functions
#[cfg(feature = "regex_support")]
"str::regex_matches" => Some(Function::new(
Some(2),
@ -76,7 +75,10 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
let re_str = expect_string(&arguments[1])?;
match Regex::new(re_str) {
Ok(re) => Ok(Value::Boolean(re.is_match(subject))),
Err(err) => Err(EvalexprError::invalid_regex(re_str.to_string(), format!("{}", err)))
Err(err) => Err(EvalexprError::invalid_regex(
re_str.to_string(),
format!("{}", err),
)),
}
}),
)),
@ -89,7 +91,10 @@ pub fn builtin_function(identifier: &str) -> Option<Function> {
let repl = expect_string(&arguments[2])?;
match Regex::new(re_str) {
Ok(re) => Ok(Value::String(re.replace_all(subject, repl).to_string())),
Err(err) => Err(EvalexprError::invalid_regex(re_str.to_string(), format!("{}", err))),
Err(err) => Err(EvalexprError::invalid_regex(
re_str.to_string(),
format!("{}", err),
)),
}
}),
)),

View File

@ -2,140 +2,37 @@ use std::fmt::{Display, Error, Formatter};
use operator::*;
impl Display for RootNode {
fn fmt(&self, _f: &mut Formatter) -> Result<(), Error> {
Ok(())
}
}
impl Display for Add {
impl Display for Operator {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "+")
}
}
use crate::operator::Operator::*;
match self {
RootNode => Ok(()),
Add => write!(f, "+"),
Sub => write!(f, "-"),
Neg => write!(f, "-"),
Mul => write!(f, "*"),
Div => write!(f, "/"),
Mod => write!(f, "%"),
Exp => write!(f, "^"),
impl Display for Sub {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "-")
}
}
Eq => write!(f, "=="),
Neq => write!(f, "!="),
Gt => write!(f, ">"),
Lt => write!(f, "<"),
Geq => write!(f, ">="),
Leq => write!(f, "<="),
And => write!(f, "&&"),
Or => write!(f, "||"),
Not => write!(f, "!"),
impl Display for Neg {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "-")
}
}
Tuple => write!(f, ", "),
Assign => write!(f, " = "),
impl Display for Mul {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "*")
}
}
Chain => 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 Exp {
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 Assign {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "; ")
}
}
impl Display for Chain {
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)
Const { value } => write!(f, "{}", value),
VariableIdentifier { identifier } => write!(f, "{}", identifier),
FunctionIdentifier { identifier } => write!(f, "{}", identifier),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -83,7 +83,7 @@ fn char_to_partial_token(c: char) -> PartialToken {
} else {
PartialToken::Literal(c.to_string())
}
},
}
}
}
@ -233,7 +233,7 @@ fn partial_tokens_to_tokens(mut tokens: &[PartialToken]) -> EvalexprResult<Vec<T
PartialToken::Token(token) => {
cutoff = 1;
Some(token)
},
}
PartialToken::Literal(literal) => {
cutoff = 1;
if let Ok(number) = literal.parse::<IntType>() {
@ -245,38 +245,38 @@ fn partial_tokens_to_tokens(mut tokens: &[PartialToken]) -> EvalexprResult<Vec<T
} else {
Some(Token::Identifier(literal.to_string()))
}
},
}
PartialToken::Whitespace => {
cutoff = 1;
None
},
}
PartialToken::Eq => match second {
Some(PartialToken::Eq) => Some(Token::Eq),
_ => {
cutoff = 1;
Some(Token::Assign)
},
}
},
PartialToken::ExclamationMark => match second {
Some(PartialToken::Eq) => Some(Token::Eq),
_ => {
cutoff = 1;
Some(Token::Not)
},
}
},
PartialToken::Gt => match second {
Some(PartialToken::Eq) => Some(Token::Geq),
_ => {
cutoff = 1;
Some(Token::Gt)
},
}
},
PartialToken::Lt => match second {
Some(PartialToken::Eq) => Some(Token::Leq),
_ => {
cutoff = 1;
Some(Token::Lt)
},
}
},
PartialToken::Ampersand => match second {
Some(PartialToken::Ampersand) => Some(Token::And),

View File

@ -1,5 +1,5 @@
use Node;
use std::slice::Iter;
use Node;
/// An iterator that traverses an operator tree in pre-order.
pub struct NodeIter<'a> {
@ -47,7 +47,7 @@ impl<'a> Iterator for NodeIter<'a> {
impl Node {
/// Returns an iterator over all nodes in this tree.
pub fn iter(&self) -> impl Iterator<Item=&Node> {
pub fn iter(&self) -> impl Iterator<Item = &Node> {
NodeIter::new(self)
}
}

View File

@ -35,19 +35,19 @@ mod iter;
#[derive(Debug)]
pub struct Node {
children: Vec<Node>,
operator: Box<dyn Operator>,
operator: Operator,
}
impl Node {
fn new<T: Operator + 'static>(operator: T) -> Self {
fn new(operator: Operator) -> Self {
Self {
children: Vec::new(),
operator: Box::new(operator),
operator,
}
}
fn root_node() -> Self {
Self::new(RootNode)
Self::new(Operator::RootNode)
}
/// Returns an iterator over all identifiers in this expression.
@ -67,7 +67,11 @@ impl Node {
/// assert_eq!(iter.next(), None);
/// ```
pub fn iter_identifiers(&self) -> impl Iterator<Item = &str> {
self.iter().filter_map(|node| node.operator.identifier())
self.iter().filter_map(|node| match node.operator() {
Operator::VariableIdentifier { identifier }
| Operator::FunctionIdentifier { identifier } => Some(identifier.as_str()),
_ => None,
})
}
/// Returns an iterator over all variable identifiers in this expression.
@ -86,7 +90,10 @@ impl Node {
/// assert_eq!(iter.next(), None);
/// ```
pub fn iter_variable_identifiers(&self) -> impl Iterator<Item = &str> {
self.iter().filter_map(|node| node.operator.variable_identifier())
self.iter().filter_map(|node| match node.operator() {
Operator::VariableIdentifier { identifier } => Some(identifier.as_str()),
_ => None,
})
}
/// Returns an iterator over all function identifiers in this expression.
@ -103,7 +110,10 @@ impl Node {
/// assert_eq!(iter.next(), None);
/// ```
pub fn iter_function_identifiers(&self) -> impl Iterator<Item = &str> {
self.iter().filter_map(|node| node.operator.function_identifier())
self.iter().filter_map(|node| match node.operator() {
Operator::FunctionIdentifier { identifier } => Some(identifier.as_str()),
_ => None,
})
}
/// Evaluates the operator tree rooted at this node with the given context.
@ -347,7 +357,7 @@ impl Node {
&self.children
}
fn operator(&self) -> &Box<dyn Operator> {
fn operator(&self) -> &Operator {
&self.operator
}
@ -404,60 +414,60 @@ pub(crate) fn tokens_to_operator_tree(tokens: Vec<Token>) -> EvalexprResult<Node
let next = token_iter.peek().cloned();
let node = match token.clone() {
Token::Plus => Some(Node::new(Add)),
Token::Plus => Some(Node::new(Operator::Add)),
Token::Minus => {
if last_token_is_rightsided_value {
Some(Node::new(Sub))
Some(Node::new(Operator::Sub))
} else {
Some(Node::new(Neg))
Some(Node::new(Operator::Neg))
}
},
Token::Star => Some(Node::new(Mul)),
Token::Slash => Some(Node::new(Div)),
Token::Percent => Some(Node::new(Mod)),
Token::Hat => Some(Node::new(Exp)),
}
Token::Star => Some(Node::new(Operator::Mul)),
Token::Slash => Some(Node::new(Operator::Div)),
Token::Percent => Some(Node::new(Operator::Mod)),
Token::Hat => Some(Node::new(Operator::Exp)),
Token::Eq => Some(Node::new(Eq)),
Token::Neq => Some(Node::new(Neq)),
Token::Gt => Some(Node::new(Gt)),
Token::Lt => Some(Node::new(Lt)),
Token::Geq => Some(Node::new(Geq)),
Token::Leq => Some(Node::new(Leq)),
Token::And => Some(Node::new(And)),
Token::Or => Some(Node::new(Or)),
Token::Not => Some(Node::new(Not)),
Token::Eq => Some(Node::new(Operator::Eq)),
Token::Neq => Some(Node::new(Operator::Neq)),
Token::Gt => Some(Node::new(Operator::Gt)),
Token::Lt => Some(Node::new(Operator::Lt)),
Token::Geq => Some(Node::new(Operator::Geq)),
Token::Leq => Some(Node::new(Operator::Leq)),
Token::And => Some(Node::new(Operator::And)),
Token::Or => Some(Node::new(Operator::Or)),
Token::Not => Some(Node::new(Operator::Not)),
Token::LBrace => {
root.push(Node::root_node());
None
},
}
Token::RBrace => {
if root.len() < 2 {
return Err(EvalexprError::UnmatchedRBrace);
} else {
root.pop()
}
},
}
Token::Comma => Some(Node::new(Tuple)),
Token::Assign => Some(Node::new(Assign)),
Token::Semicolon => Some(Node::new(Chain)),
Token::Comma => Some(Node::new(Operator::Tuple)),
Token::Assign => Some(Node::new(Operator::Assign)),
Token::Semicolon => Some(Node::new(Operator::Chain)),
Token::Identifier(identifier) => {
let mut result = Some(Node::new(VariableIdentifier::new(identifier.clone())));
let mut result = Some(Node::new(Operator::variable_identifier(identifier.clone())));
if let Some(next) = next {
if next == &Token::Assign {
result = Some(Node::new(Const::new(identifier.clone().into())));
result = Some(Node::new(Operator::value(identifier.clone().into())));
} else if next.is_leftsided_value() {
result = Some(Node::new(FunctionIdentifier::new(identifier)));
result = Some(Node::new(Operator::function_identifier(identifier)));
}
}
result
},
Token::Float(float) => Some(Node::new(Const::new(Value::Float(float)))),
Token::Int(int) => Some(Node::new(Const::new(Value::Int(int)))),
Token::Boolean(boolean) => Some(Node::new(Const::new(Value::Boolean(boolean)))),
Token::String(string) => Some(Node::new(Const::new(Value::String(string)))),
}
Token::Float(float) => Some(Node::new(Operator::value(Value::Float(float)))),
Token::Int(int) => Some(Node::new(Operator::value(Value::Int(int)))),
Token::Boolean(boolean) => Some(Node::new(Operator::value(Value::Boolean(boolean)))),
Token::String(string) => Some(Node::new(Operator::value(Value::String(string)))),
};
if let Some(node) = node {

View File

@ -21,7 +21,7 @@ impl Display for Value {
value.fmt(f)?;
}
write!(f, ")")
},
}
Value::Empty => write!(f, "()"),
}
}

View File

@ -279,18 +279,9 @@ fn test_n_ary_functions() {
#[test]
fn test_builtin_functions() {
assert_eq!(
eval("min(4.0, 3)"),
Ok(Value::Int(3))
);
assert_eq!(
eval("max(4.0, 3)"),
Ok(Value::Float(4.0))
);
assert_eq!(
eval("len(\"foobar\")"),
Ok(Value::Int(6))
);
assert_eq!(eval("min(4.0, 3)"), Ok(Value::Int(3)));
assert_eq!(eval("max(4.0, 3)"), Ok(Value::Float(4.0)));
assert_eq!(eval("len(\"foobar\")"), Ok(Value::Int(6)));
assert_eq!(
eval("str::to_lowercase(\"FOOBAR\")"),
Ok(Value::from("foobar"))
@ -317,10 +308,10 @@ fn test_regex_functions() {
Ok(Value::Boolean(false))
);
match eval("str::regex_matches(\"foo\", \"[\")") {
Err(EvalexprError::InvalidRegex{ regex, message }) => {
Err(EvalexprError::InvalidRegex { regex, message }) => {
assert_eq!(regex, "[");
assert!(message.contains("unclosed character class"));
},
}
v => panic!(v),
};
assert_eq!(