Merge pull request #107 from ISibboI/sebschmi/i106_identifier_is_variable_bug
Split VariableIdentifier node into read and write variants
This commit is contained in:
commit
c0b46a4e8d
@ -9,6 +9,7 @@
|
|||||||
* Builtin functions to check for nan, infinity and subnormality in floats (#101)
|
* Builtin functions to check for nan, infinity and subnormality in floats (#101)
|
||||||
* Builtin random function (#102)
|
* Builtin random function (#102)
|
||||||
* Implement `TryFrom<Value>` for all types a value can hold (#105)
|
* Implement `TryFrom<Value>` for all types a value can hold (#105)
|
||||||
|
* Split VariableIdentifier node into read and write variants (#106)
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ My warmhearted thanks goes to:
|
|||||||
|
|
||||||
* [Ophir LOJKINE](https://github.com/lovasoa)
|
* [Ophir LOJKINE](https://github.com/lovasoa)
|
||||||
* [Joe Grund](https://github.com/jgrund)
|
* [Joe Grund](https://github.com/jgrund)
|
||||||
|
* [Luka Maljic](https://github.com/malj)
|
||||||
|
|
||||||
## [7.2.0](https://github.com/ISibboI/evalexpr/compare/7.1.1...7.2.0) - 2022-03-16
|
## [7.2.0](https://github.com/ISibboI/evalexpr/compare/7.1.1...7.2.0) - 2022-03-16
|
||||||
|
|
||||||
|
@ -41,7 +41,9 @@ impl Display for Operator {
|
|||||||
Chain => write!(f, "; "),
|
Chain => write!(f, "; "),
|
||||||
|
|
||||||
Const { value } => write!(f, "{}", value),
|
Const { value } => write!(f, "{}", value),
|
||||||
VariableIdentifier { identifier } => write!(f, "{}", identifier),
|
VariableIdentifierWrite { identifier } | VariableIdentifierRead { identifier } => {
|
||||||
|
write!(f, "{}", identifier)
|
||||||
|
},
|
||||||
FunctionIdentifier { identifier } => write!(f, "{}", identifier),
|
FunctionIdentifier { identifier } => write!(f, "{}", identifier),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,13 @@ pub enum Operator {
|
|||||||
/** The value of the constant. */
|
/** The value of the constant. */
|
||||||
value: Value,
|
value: Value,
|
||||||
},
|
},
|
||||||
/// A variable identifier.
|
/// A write to a variable identifier.
|
||||||
VariableIdentifier {
|
VariableIdentifierWrite {
|
||||||
|
/// The identifier of the variable.
|
||||||
|
identifier: String,
|
||||||
|
},
|
||||||
|
/// A read from a variable identifier.
|
||||||
|
VariableIdentifierRead {
|
||||||
/// The identifier of the variable.
|
/// The identifier of the variable.
|
||||||
identifier: String,
|
identifier: String,
|
||||||
},
|
},
|
||||||
@ -92,8 +97,12 @@ impl Operator {
|
|||||||
Operator::Const { value }
|
Operator::Const { value }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn variable_identifier(identifier: String) -> Self {
|
pub(crate) fn variable_identifier_write(identifier: String) -> Self {
|
||||||
Operator::VariableIdentifier { identifier }
|
Operator::VariableIdentifierWrite { identifier }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn variable_identifier_read(identifier: String) -> Self {
|
||||||
|
Operator::VariableIdentifierRead { identifier }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn function_identifier(identifier: String) -> Self {
|
pub(crate) fn function_identifier(identifier: String) -> Self {
|
||||||
@ -123,9 +132,9 @@ impl Operator {
|
|||||||
Tuple => 40,
|
Tuple => 40,
|
||||||
Chain => 0,
|
Chain => 0,
|
||||||
|
|
||||||
Const { value: _ } => 200,
|
Const { .. } => 200,
|
||||||
VariableIdentifier { identifier: _ } => 200,
|
VariableIdentifierWrite { .. } | VariableIdentifierRead { .. } => 200,
|
||||||
FunctionIdentifier { identifier: _ } => 190,
|
FunctionIdentifier { .. } => 190,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +143,7 @@ impl Operator {
|
|||||||
/// Left-to-right chaining has priority if operators with different order but same precedence are chained.
|
/// Left-to-right chaining has priority if operators with different order but same precedence are chained.
|
||||||
pub(crate) const fn is_left_to_right(&self) -> bool {
|
pub(crate) const fn is_left_to_right(&self) -> bool {
|
||||||
use crate::operator::Operator::*;
|
use crate::operator::Operator::*;
|
||||||
!matches!(self, Assign | FunctionIdentifier { identifier: _ })
|
!matches!(self, Assign | FunctionIdentifier { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if chains of this operator should be flattened into one operator with many arguments.
|
/// Returns true if chains of this operator should be flattened into one operator with many arguments.
|
||||||
@ -158,9 +167,9 @@ impl Operator {
|
|||||||
| AndAssign | OrAssign => Some(2),
|
| AndAssign | OrAssign => Some(2),
|
||||||
Tuple | Chain => None,
|
Tuple | Chain => None,
|
||||||
Not | Neg | RootNode => Some(1),
|
Not | Neg | RootNode => Some(1),
|
||||||
Const { value: _ } => Some(0),
|
Const { .. } => Some(0),
|
||||||
VariableIdentifier { identifier: _ } => Some(0),
|
VariableIdentifierWrite { .. } | VariableIdentifierRead { .. } => Some(0),
|
||||||
FunctionIdentifier { identifier: _ } => Some(1),
|
FunctionIdentifier { .. } => Some(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -422,7 +431,12 @@ impl Operator {
|
|||||||
|
|
||||||
Ok(value.clone())
|
Ok(value.clone())
|
||||||
},
|
},
|
||||||
VariableIdentifier { identifier } => {
|
VariableIdentifierWrite { identifier } => {
|
||||||
|
expect_operator_argument_amount(arguments.len(), 0)?;
|
||||||
|
|
||||||
|
Ok(identifier.clone().into())
|
||||||
|
},
|
||||||
|
VariableIdentifierRead { identifier } => {
|
||||||
expect_operator_argument_amount(arguments.len(), 0)?;
|
expect_operator_argument_amount(arguments.len(), 0)?;
|
||||||
|
|
||||||
if let Some(value) = context.get_value(identifier).cloned() {
|
if let Some(value) = context.get_value(identifier).cloned() {
|
||||||
@ -473,7 +487,7 @@ impl Operator {
|
|||||||
expect_operator_argument_amount(arguments.len(), 2)?;
|
expect_operator_argument_amount(arguments.len(), 2)?;
|
||||||
|
|
||||||
let target = arguments[0].as_string()?;
|
let target = arguments[0].as_string()?;
|
||||||
let left_value = Operator::VariableIdentifier {
|
let left_value = Operator::VariableIdentifierRead {
|
||||||
identifier: target.clone(),
|
identifier: target.clone(),
|
||||||
}
|
}
|
||||||
.eval(&Vec::new(), context)?;
|
.eval(&Vec::new(), context)?;
|
||||||
|
@ -444,7 +444,7 @@ pub(crate) fn tokenize(string: &str) -> EvalexprResult<Vec<Token>> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::token::{char_to_partial_token, tokenize};
|
use crate::token::{char_to_partial_token, tokenize, Token};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -474,4 +474,17 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(token_string, result_string);
|
assert_eq!(token_string, result_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assignment_lhs_is_identifier() {
|
||||||
|
let tokens = tokenize("a = 1").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
tokens.as_slice(),
|
||||||
|
[
|
||||||
|
Token::Identifier("a".to_string()),
|
||||||
|
Token::Assign,
|
||||||
|
Token::Int(1)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,8 @@ impl Node {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn iter_identifiers(&self) -> impl Iterator<Item = &str> {
|
pub fn iter_identifiers(&self) -> impl Iterator<Item = &str> {
|
||||||
self.iter().filter_map(|node| match node.operator() {
|
self.iter().filter_map(|node| match node.operator() {
|
||||||
Operator::VariableIdentifier { identifier }
|
Operator::VariableIdentifierWrite { identifier }
|
||||||
|
| Operator::VariableIdentifierRead { identifier }
|
||||||
| Operator::FunctionIdentifier { identifier } => Some(identifier.as_str()),
|
| Operator::FunctionIdentifier { identifier } => Some(identifier.as_str()),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
@ -92,7 +93,8 @@ impl Node {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn iter_variable_identifiers(&self) -> impl Iterator<Item = &str> {
|
pub fn iter_variable_identifiers(&self) -> impl Iterator<Item = &str> {
|
||||||
self.iter().filter_map(|node| match node.operator() {
|
self.iter().filter_map(|node| match node.operator() {
|
||||||
Operator::VariableIdentifier { identifier } => Some(identifier.as_str()),
|
Operator::VariableIdentifierWrite { identifier }
|
||||||
|
| Operator::VariableIdentifierRead { identifier } => Some(identifier.as_str()),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -616,10 +618,14 @@ pub(crate) fn tokens_to_operator_tree(tokens: Vec<Token>) -> EvalexprResult<Node
|
|||||||
Token::Semicolon => Some(Node::new(Operator::Chain)),
|
Token::Semicolon => Some(Node::new(Operator::Chain)),
|
||||||
|
|
||||||
Token::Identifier(identifier) => {
|
Token::Identifier(identifier) => {
|
||||||
let mut result = Some(Node::new(Operator::variable_identifier(identifier.clone())));
|
let mut result = Some(Node::new(Operator::variable_identifier_read(
|
||||||
|
identifier.clone(),
|
||||||
|
)));
|
||||||
if let Some(next) = next {
|
if let Some(next) = next {
|
||||||
if next.is_assignment() {
|
if next.is_assignment() {
|
||||||
result = Some(Node::new(Operator::value(identifier.clone().into())));
|
result = Some(Node::new(Operator::variable_identifier_write(
|
||||||
|
identifier.clone(),
|
||||||
|
)));
|
||||||
} else if next.is_leftsided_value() {
|
} else if next.is_leftsided_value() {
|
||||||
result = Some(Node::new(Operator::function_identifier(identifier)));
|
result = Some(Node::new(Operator::function_identifier(identifier)));
|
||||||
}
|
}
|
||||||
|
@ -2083,3 +2083,28 @@ fn test_try_from() {
|
|||||||
);
|
);
|
||||||
assert_eq!(EmptyType::try_from(value.clone()), Ok(()));
|
assert_eq!(EmptyType::try_from(value.clone()), Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assignment_lhs_is_identifier() {
|
||||||
|
let tree = build_operator_tree("a = 1").unwrap();
|
||||||
|
let operators: Vec<_> = tree.iter().map(|node| node.operator().clone()).collect();
|
||||||
|
|
||||||
|
let mut context = HashMapContext::new();
|
||||||
|
tree.eval_with_context_mut(&mut context).unwrap();
|
||||||
|
assert_eq!(context.get_value("a"), Some(&Value::Int(1)));
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
matches!(
|
||||||
|
operators.as_slice(),
|
||||||
|
[
|
||||||
|
Operator::Assign,
|
||||||
|
Operator::VariableIdentifierWrite { identifier: value },
|
||||||
|
Operator::Const {
|
||||||
|
value: Value::Int(1)
|
||||||
|
}
|
||||||
|
] if value == "a"
|
||||||
|
),
|
||||||
|
"actual: {:#?}",
|
||||||
|
operators
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user