mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 00:13:21 +01:00
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:
parent
3e76ed9122
commit
2ccb91dc6a
@ -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) => {
|
||||
|
@ -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 (
|
||||
|
@ -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),
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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"),
|
||||
|
@ -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, .. }) => {
|
||||
|
@ -246,6 +246,11 @@ fn shortcircuiting_or() -> TestResult {
|
||||
run_test(r#"true || (5 / 0; false)"#, "true")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonshortcircuiting_xor() -> TestResult {
|
||||
run_test(r#"true xor (print "hello"; false) | ignore"#, "hello")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn open_ended_range() -> TestResult {
|
||||
run_test(r#"1.. | first 100000 | length"#, "100000")
|
||||
|
@ -55,6 +55,16 @@ fn or() -> TestResult {
|
||||
run_test("true || false", "true")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xor_1() -> TestResult {
|
||||
run_test("false xor true", "true")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xor_2() -> TestResult {
|
||||
run_test("true xor true", "false")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bit_xor() -> TestResult {
|
||||
run_test("4 bit-xor 4", "0")
|
||||
|
@ -430,3 +430,15 @@ fn date_literal() -> TestResult {
|
||||
fn and_and_or() -> TestResult {
|
||||
run_test(r#"true and false or true"#, "true")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn and_and_xor() -> TestResult {
|
||||
// Assumes the precedence NOT > AND > XOR > OR
|
||||
run_test(r#"true and true xor true and false"#, "true")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn or_and_xor() -> TestResult {
|
||||
// Assumes the precedence NOT > AND > XOR > OR
|
||||
run_test(r#"true or false xor true or false"#, "true")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user