parent
b9c4b34a2f
commit
b7233a3337
@ -25,9 +25,17 @@ impl Display for Operator {
|
||||
Or => write!(f, "||"),
|
||||
Not => write!(f, "!"),
|
||||
|
||||
Tuple => write!(f, ", "),
|
||||
Assign => write!(f, " = "),
|
||||
AddAssign => write!(f, " += "),
|
||||
SubAssign => write!(f, " -= "),
|
||||
MulAssign => write!(f, " *= "),
|
||||
DivAssign => write!(f, " /= "),
|
||||
ModAssign => write!(f, " %= "),
|
||||
ExpAssign => write!(f, " ^= "),
|
||||
AndAssign => write!(f, " &&= "),
|
||||
OrAssign => write!(f, " ||= "),
|
||||
|
||||
Tuple => write!(f, ", "),
|
||||
Chain => write!(f, "; "),
|
||||
|
||||
Const { value } => write!(f, "{}", value),
|
||||
|
@ -26,9 +26,17 @@ pub enum Operator {
|
||||
Or,
|
||||
Not,
|
||||
|
||||
Tuple,
|
||||
Assign,
|
||||
AddAssign,
|
||||
SubAssign,
|
||||
MulAssign,
|
||||
DivAssign,
|
||||
ModAssign,
|
||||
ExpAssign,
|
||||
AndAssign,
|
||||
OrAssign,
|
||||
|
||||
Tuple,
|
||||
Chain,
|
||||
|
||||
Const { value: Value },
|
||||
@ -67,9 +75,10 @@ impl Operator {
|
||||
Or => 70,
|
||||
Not => 110,
|
||||
|
||||
Tuple => 40,
|
||||
Assign => 50,
|
||||
Assign | AddAssign | SubAssign | MulAssign | DivAssign | ModAssign | ExpAssign
|
||||
| AndAssign | OrAssign => 50,
|
||||
|
||||
Tuple => 40,
|
||||
Chain => 0,
|
||||
|
||||
Const { value: _ } => 200,
|
||||
@ -113,7 +122,8 @@ impl Operator {
|
||||
use crate::operator::Operator::*;
|
||||
match self {
|
||||
Add | Sub | Mul | Div | Mod | Exp | Eq | Neq | Gt | Lt | Geq | Leq | And | Or
|
||||
| Assign => Some(2),
|
||||
| Assign | AddAssign | SubAssign | MulAssign | DivAssign | ModAssign | ExpAssign
|
||||
| AndAssign | OrAssign => Some(2),
|
||||
Tuple | Chain => None,
|
||||
Not | Neg | RootNode => Some(1),
|
||||
Const { value: _ } => Some(0),
|
||||
@ -420,8 +430,9 @@ impl Operator {
|
||||
Ok(Value::Boolean(false))
|
||||
}
|
||||
},
|
||||
Assign | AddAssign | SubAssign | MulAssign | DivAssign | ModAssign | ExpAssign
|
||||
| AndAssign | OrAssign => Err(EvalexprError::ContextNotManipulable),
|
||||
Tuple => Ok(Value::Tuple(arguments.into())),
|
||||
Assign => Err(EvalexprError::ContextNotManipulable),
|
||||
Chain => {
|
||||
if arguments.is_empty() {
|
||||
return Err(EvalexprError::wrong_operator_argument_amount(0, 1));
|
||||
@ -435,6 +446,8 @@ impl Operator {
|
||||
Ok(value.clone())
|
||||
},
|
||||
VariableIdentifier { identifier } => {
|
||||
expect_operator_argument_amount(arguments.len(), 0)?;
|
||||
|
||||
if let Some(value) = context.get_value(&identifier).cloned() {
|
||||
Ok(value)
|
||||
} else {
|
||||
@ -475,6 +488,35 @@ impl Operator {
|
||||
|
||||
Ok(Value::Empty)
|
||||
},
|
||||
AddAssign | SubAssign | MulAssign | DivAssign | ModAssign | ExpAssign | AndAssign
|
||||
| OrAssign => {
|
||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||
|
||||
let target = arguments[0].as_string()?;
|
||||
let left_value = Operator::VariableIdentifier {
|
||||
identifier: target.clone(),
|
||||
}
|
||||
.eval(&Vec::new(), context)?;
|
||||
let arguments = vec![left_value, arguments[1].clone()];
|
||||
|
||||
let result = match self {
|
||||
AddAssign => Operator::Add.eval(&arguments, context),
|
||||
SubAssign => Operator::Sub.eval(&arguments, context),
|
||||
MulAssign => Operator::Mul.eval(&arguments, context),
|
||||
DivAssign => Operator::Div.eval(&arguments, context),
|
||||
ModAssign => Operator::Mod.eval(&arguments, context),
|
||||
ExpAssign => Operator::Exp.eval(&arguments, context),
|
||||
AndAssign => Operator::And.eval(&arguments, context),
|
||||
OrAssign => Operator::Or.eval(&arguments, context),
|
||||
_ => unreachable!(
|
||||
"Forgot to add a match arm for an assign operation: {}",
|
||||
self
|
||||
),
|
||||
}?;
|
||||
context.set_value(target.into(), result)?;
|
||||
|
||||
Ok(Value::Empty)
|
||||
},
|
||||
_ => self.eval(arguments, context),
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,19 @@ impl fmt::Display for Token {
|
||||
LBrace => write!(f, "("),
|
||||
RBrace => write!(f, ")"),
|
||||
|
||||
// Assignment
|
||||
Assign => write!(f, "="),
|
||||
PlusAssign => write!(f, "+="),
|
||||
MinusAssign => write!(f, "-="),
|
||||
StarAssign => write!(f, "*="),
|
||||
SlashAssign => write!(f, "/="),
|
||||
PercentAssign => write!(f, "%="),
|
||||
HatAssign => write!(f, "^="),
|
||||
AndAssign => write!(f, "&&="),
|
||||
OrAssign => write!(f, "||="),
|
||||
|
||||
// Special
|
||||
Comma => write!(f, ","),
|
||||
Assign => write!(f, "="),
|
||||
Semicolon => write!(f, ";"),
|
||||
|
||||
// Values => write!(f, ""), Variables and Functions
|
||||
@ -50,6 +60,12 @@ impl fmt::Display for PartialToken {
|
||||
Token(token) => token.fmt(f),
|
||||
Literal(literal) => literal.fmt(f),
|
||||
Whitespace => write!(f, " "),
|
||||
Plus => write!(f, "+"),
|
||||
Minus => write!(f, "-"),
|
||||
Star => write!(f, "*"),
|
||||
Slash => write!(f, "/"),
|
||||
Percent => write!(f, "%"),
|
||||
Hat => write!(f, "^"),
|
||||
Eq => write!(f, "="),
|
||||
ExclamationMark => write!(f, "!"),
|
||||
Gt => write!(f, ">"),
|
||||
|
120
src/token/mod.rs
120
src/token/mod.rs
@ -30,9 +30,19 @@ pub enum Token {
|
||||
LBrace,
|
||||
RBrace,
|
||||
|
||||
// Assignment
|
||||
Assign,
|
||||
PlusAssign,
|
||||
MinusAssign,
|
||||
StarAssign,
|
||||
SlashAssign,
|
||||
PercentAssign,
|
||||
HatAssign,
|
||||
AndAssign,
|
||||
OrAssign,
|
||||
|
||||
// Special
|
||||
Comma,
|
||||
Assign,
|
||||
Semicolon,
|
||||
|
||||
// Values, Variables and Functions
|
||||
@ -47,6 +57,12 @@ pub enum Token {
|
||||
pub enum PartialToken {
|
||||
Token(Token),
|
||||
Literal(String),
|
||||
Plus,
|
||||
Minus,
|
||||
Star,
|
||||
Slash,
|
||||
Percent,
|
||||
Hat,
|
||||
Whitespace,
|
||||
Eq,
|
||||
ExclamationMark,
|
||||
@ -59,12 +75,12 @@ pub enum PartialToken {
|
||||
// Make this a const fn as soon as match gets stable (issue #57563)
|
||||
fn char_to_partial_token(c: char) -> PartialToken {
|
||||
match c {
|
||||
'+' => PartialToken::Token(Token::Plus),
|
||||
'-' => PartialToken::Token(Token::Minus),
|
||||
'*' => PartialToken::Token(Token::Star),
|
||||
'/' => PartialToken::Token(Token::Slash),
|
||||
'%' => PartialToken::Token(Token::Percent),
|
||||
'^' => PartialToken::Token(Token::Hat),
|
||||
'+' => PartialToken::Plus,
|
||||
'-' => PartialToken::Minus,
|
||||
'*' => PartialToken::Star,
|
||||
'/' => PartialToken::Slash,
|
||||
'%' => PartialToken::Percent,
|
||||
'^' => PartialToken::Hat,
|
||||
|
||||
'(' => PartialToken::Token(Token::LBrace),
|
||||
')' => PartialToken::Token(Token::RBrace),
|
||||
@ -114,9 +130,18 @@ impl Token {
|
||||
Token::RBrace => false,
|
||||
|
||||
Token::Comma => false,
|
||||
Token::Assign => false,
|
||||
Token::Semicolon => false,
|
||||
|
||||
Token::Assign => false,
|
||||
Token::PlusAssign => false,
|
||||
Token::MinusAssign => false,
|
||||
Token::StarAssign => false,
|
||||
Token::SlashAssign => false,
|
||||
Token::PercentAssign => false,
|
||||
Token::HatAssign => false,
|
||||
Token::AndAssign => false,
|
||||
Token::OrAssign => false,
|
||||
|
||||
Token::Identifier(_) => true,
|
||||
Token::Float(_) => true,
|
||||
Token::Int(_) => true,
|
||||
@ -149,9 +174,18 @@ impl Token {
|
||||
Token::RBrace => true,
|
||||
|
||||
Token::Comma => false,
|
||||
Token::Assign => false,
|
||||
Token::Semicolon => false,
|
||||
|
||||
Token::Assign => false,
|
||||
Token::PlusAssign => false,
|
||||
Token::MinusAssign => false,
|
||||
Token::StarAssign => false,
|
||||
Token::SlashAssign => false,
|
||||
Token::PercentAssign => false,
|
||||
Token::HatAssign => false,
|
||||
Token::AndAssign => false,
|
||||
Token::OrAssign => false,
|
||||
|
||||
Token::Identifier(_) => true,
|
||||
Token::Float(_) => true,
|
||||
Token::Int(_) => true,
|
||||
@ -159,6 +193,15 @@ impl Token {
|
||||
Token::String(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_assignment(&self) -> bool {
|
||||
use Token::*;
|
||||
match self {
|
||||
Assign | PlusAssign | MinusAssign | StarAssign | SlashAssign | PercentAssign
|
||||
| HatAssign | AndAssign | OrAssign => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses an escape sequence within a string literal.
|
||||
@ -228,6 +271,7 @@ fn partial_tokens_to_tokens(mut tokens: &[PartialToken]) -> EvalexprResult<Vec<T
|
||||
while tokens.len() > 0 {
|
||||
let first = tokens[0].clone();
|
||||
let second = tokens.get(1).cloned();
|
||||
let third = tokens.get(2).cloned();
|
||||
let mut cutoff = 2;
|
||||
|
||||
result.extend(
|
||||
@ -236,6 +280,48 @@ fn partial_tokens_to_tokens(mut tokens: &[PartialToken]) -> EvalexprResult<Vec<T
|
||||
cutoff = 1;
|
||||
Some(token)
|
||||
},
|
||||
PartialToken::Plus => match second {
|
||||
Some(PartialToken::Eq) => Some(Token::PlusAssign),
|
||||
_ => {
|
||||
cutoff = 1;
|
||||
Some(Token::Plus)
|
||||
},
|
||||
},
|
||||
PartialToken::Minus => match second {
|
||||
Some(PartialToken::Eq) => Some(Token::MinusAssign),
|
||||
_ => {
|
||||
cutoff = 1;
|
||||
Some(Token::Minus)
|
||||
},
|
||||
},
|
||||
PartialToken::Star => match second {
|
||||
Some(PartialToken::Eq) => Some(Token::StarAssign),
|
||||
_ => {
|
||||
cutoff = 1;
|
||||
Some(Token::Star)
|
||||
},
|
||||
},
|
||||
PartialToken::Slash => match second {
|
||||
Some(PartialToken::Eq) => Some(Token::SlashAssign),
|
||||
_ => {
|
||||
cutoff = 1;
|
||||
Some(Token::Slash)
|
||||
},
|
||||
},
|
||||
PartialToken::Percent => match second {
|
||||
Some(PartialToken::Eq) => Some(Token::PercentAssign),
|
||||
_ => {
|
||||
cutoff = 1;
|
||||
Some(Token::Percent)
|
||||
},
|
||||
},
|
||||
PartialToken::Hat => match second {
|
||||
Some(PartialToken::Eq) => Some(Token::HatAssign),
|
||||
_ => {
|
||||
cutoff = 1;
|
||||
Some(Token::Hat)
|
||||
},
|
||||
},
|
||||
PartialToken::Literal(literal) => {
|
||||
cutoff = 1;
|
||||
if let Ok(number) = literal.parse::<IntType>() {
|
||||
@ -281,11 +367,23 @@ fn partial_tokens_to_tokens(mut tokens: &[PartialToken]) -> EvalexprResult<Vec<T
|
||||
},
|
||||
},
|
||||
PartialToken::Ampersand => match second {
|
||||
Some(PartialToken::Ampersand) => Some(Token::And),
|
||||
Some(PartialToken::Ampersand) => match third {
|
||||
Some(PartialToken::Eq) => {
|
||||
cutoff = 3;
|
||||
Some(Token::AndAssign)
|
||||
},
|
||||
_ => Some(Token::And),
|
||||
},
|
||||
_ => return Err(EvalexprError::unmatched_partial_token(first, second)),
|
||||
},
|
||||
PartialToken::VerticalBar => match second {
|
||||
Some(PartialToken::VerticalBar) => Some(Token::Or),
|
||||
Some(PartialToken::VerticalBar) => match third {
|
||||
Some(PartialToken::Eq) => {
|
||||
cutoff = 3;
|
||||
Some(Token::OrAssign)
|
||||
},
|
||||
_ => Some(Token::Or),
|
||||
},
|
||||
_ => return Err(EvalexprError::unmatched_partial_token(first, second)),
|
||||
},
|
||||
}
|
||||
|
@ -529,14 +529,23 @@ pub(crate) fn tokens_to_operator_tree(tokens: Vec<Token>) -> EvalexprResult<Node
|
||||
}
|
||||
},
|
||||
|
||||
Token::Comma => Some(Node::new(Operator::Tuple)),
|
||||
Token::Assign => Some(Node::new(Operator::Assign)),
|
||||
Token::PlusAssign => Some(Node::new(Operator::AddAssign)),
|
||||
Token::MinusAssign => Some(Node::new(Operator::SubAssign)),
|
||||
Token::StarAssign => Some(Node::new(Operator::MulAssign)),
|
||||
Token::SlashAssign => Some(Node::new(Operator::DivAssign)),
|
||||
Token::PercentAssign => Some(Node::new(Operator::ModAssign)),
|
||||
Token::HatAssign => Some(Node::new(Operator::ExpAssign)),
|
||||
Token::AndAssign => Some(Node::new(Operator::AndAssign)),
|
||||
Token::OrAssign => Some(Node::new(Operator::OrAssign)),
|
||||
|
||||
Token::Comma => Some(Node::new(Operator::Tuple)),
|
||||
Token::Semicolon => Some(Node::new(Operator::Chain)),
|
||||
|
||||
Token::Identifier(identifier) => {
|
||||
let mut result = Some(Node::new(Operator::variable_identifier(identifier.clone())));
|
||||
if let Some(next) = next {
|
||||
if next == &Token::Assign {
|
||||
if next.is_assignment() {
|
||||
result = Some(Node::new(Operator::value(identifier.clone().into())));
|
||||
} else if next.is_leftsided_value() {
|
||||
result = Some(Node::new(Operator::function_identifier(identifier)));
|
||||
|
@ -636,3 +636,62 @@ fn test_implicit_context() {
|
||||
Ok("xyzabc".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_operator_assignments() {
|
||||
let mut context = HashMapContext::new();
|
||||
assert_eq!(eval_empty_with_context_mut("a = 5", &mut context), Ok(()));
|
||||
assert_eq!(eval_empty_with_context_mut("a += 5", &mut context), Ok(()));
|
||||
assert_eq!(eval_empty_with_context_mut("a -= 5", &mut context), Ok(()));
|
||||
assert_eq!(eval_empty_with_context_mut("a *= 5", &mut context), Ok(()));
|
||||
assert_eq!(eval_empty_with_context_mut("b = 5.0", &mut context), Ok(()));
|
||||
assert_eq!(eval_empty_with_context_mut("b /= 5", &mut context), Ok(()));
|
||||
assert_eq!(eval_empty_with_context_mut("b %= 5", &mut context), Ok(()));
|
||||
assert_eq!(eval_empty_with_context_mut("b ^= 5", &mut context), Ok(()));
|
||||
assert_eq!(
|
||||
eval_empty_with_context_mut("c = true", &mut context),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
eval_empty_with_context_mut("c &&= false", &mut context),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
eval_empty_with_context_mut("c ||= true", &mut context),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
let mut context = HashMapContext::new();
|
||||
assert_eq!(eval_int_with_context_mut("a = 5; a", &mut context), Ok(5));
|
||||
assert_eq!(eval_int_with_context_mut("a += 3; a", &mut context), Ok(8));
|
||||
assert_eq!(eval_int_with_context_mut("a -= 5; a", &mut context), Ok(3));
|
||||
assert_eq!(eval_int_with_context_mut("a *= 5; a", &mut context), Ok(15));
|
||||
assert_eq!(
|
||||
eval_float_with_context_mut("b = 5.0; b", &mut context),
|
||||
Ok(5.0)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_float_with_context_mut("b /= 2; b", &mut context),
|
||||
Ok(2.5)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_float_with_context_mut("b %= 2; b", &mut context),
|
||||
Ok(0.5)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_float_with_context_mut("b ^= 2; b", &mut context),
|
||||
Ok(0.25)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_boolean_with_context_mut("c = true; c", &mut context),
|
||||
Ok(true)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_boolean_with_context_mut("c &&= false; c", &mut context),
|
||||
Ok(false)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_boolean_with_context_mut("c ||= true; c", &mut context),
|
||||
Ok(true)
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user