Add logical xor operator (#7242)

We already have the binary `bit-xor` and the shortcircuiting logical
`or`(`||`) and `and`(`&&`).
This introduces `xor` as a compact form for both brevity and clarity.
You can express the operation through `not`/`and`/`or` with a slight
risk of introducing bugs through typos.

Operator precedence

`and` > `xor` > `or`

Added logic and precedence tests.
This commit is contained in:
Stefan Holderbach
2022-11-26 17:02:37 +01:00
committed by GitHub
parent 3e76ed9122
commit 2ccb91dc6a
9 changed files with 57 additions and 1 deletions

View File

@ -404,6 +404,10 @@ pub fn eval_expression(
lhs.or(op_span, &rhs, expr.span)
}
}
Boolean::Xor => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.xor(op_span, &rhs, expr.span)
}
}
}
Operator::Math(math) => {

View File

@ -4502,6 +4502,7 @@ pub fn parse_operator(
b"ends-with" => Operator::Comparison(Comparison::EndsWith),
b"&&" | b"and" => Operator::Boolean(Boolean::And),
b"||" | b"or" => Operator::Boolean(Boolean::Or),
b"xor" => Operator::Boolean(Boolean::Xor),
b"**" => Operator::Math(Math::Pow),
_ => {
return (

View File

@ -244,7 +244,9 @@ pub fn math_result_type(
)
}
},
Operator::Boolean(Boolean::And) | Operator::Boolean(Boolean::Or) => {
Operator::Boolean(Boolean::And)
| Operator::Boolean(Boolean::Or)
| Operator::Boolean(Boolean::Xor) => {
match (&lhs.ty, &rhs.ty) {
(Type::Bool, Type::Bool) => (Type::Bool, None),

View File

@ -54,6 +54,7 @@ impl Expression {
Operator::Bits(Bits::BitXor) => 70,
Operator::Bits(Bits::BitOr) => 60,
Operator::Boolean(Boolean::And) => 50,
Operator::Boolean(Boolean::Xor) => 45,
Operator::Boolean(Boolean::Or) => 40,
Operator::Assignment(_) => 10,
}

View File

@ -35,6 +35,7 @@ pub enum Math {
pub enum Boolean {
And,
Or,
Xor,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -94,6 +95,7 @@ impl Display for Operator {
Operator::Math(Math::Pow) => write!(f, "**"),
Operator::Boolean(Boolean::And) => write!(f, "&&"),
Operator::Boolean(Boolean::Or) => write!(f, "||"),
Operator::Boolean(Boolean::Xor) => write!(f, "xor"),
Operator::Bits(Bits::BitOr) => write!(f, "bit-or"),
Operator::Bits(Bits::BitXor) => write!(f, "bit-xor"),
Operator::Bits(Bits::BitAnd) => write!(f, "bit-and"),

View File

@ -2865,6 +2865,25 @@ impl Value {
}
}
pub fn xor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => Ok(Value::Bool {
val: (*lhs && !*rhs) || (!*lhs && *rhs),
span,
}),
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Boolean(Boolean::Xor), op, rhs)
}
_ => Err(ShellError::OperatorMismatch {
op_span: op,
lhs_ty: self.get_type(),
lhs_span: self.span()?,
rhs_ty: rhs.get_type(),
rhs_span: rhs.span()?,
}),
}
}
pub fn pow(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {