From b82dccf0bdaa42ef3e51eea861f5132a38dc9a5e Mon Sep 17 00:00:00 2001 From: Justin Ma Date: Sun, 3 Jul 2022 02:03:36 +0800 Subject: [PATCH] Add `band` and `bor` operator for bit operations (#5936) * Add `band` and `bor` Operator * Add tests --- .../src/database/values/dsl/expression.rs | 2 + crates/nu-engine/src/eval.rs | 8 ++++ crates/nu-parser/src/parser.rs | 2 + crates/nu-parser/src/type_check.rs | 36 +++++++++--------- crates/nu-protocol/src/ast/expression.rs | 3 ++ crates/nu-protocol/src/ast/operator.rs | 4 ++ crates/nu-protocol/src/value/mod.rs | 38 +++++++++++++++++++ src/tests/test_math.rs | 15 ++++++++ 8 files changed, 91 insertions(+), 17 deletions(-) diff --git a/crates/nu-command/src/database/values/dsl/expression.rs b/crates/nu-command/src/database/values/dsl/expression.rs index 9cfb5ddaa3..d576044d1c 100644 --- a/crates/nu-command/src/database/values/dsl/expression.rs +++ b/crates/nu-command/src/database/values/dsl/expression.rs @@ -118,6 +118,8 @@ impl CustomValue for ExprDb { Operator::In | Operator::NotIn | Operator::Pow + | Operator::BitOr + | Operator::BitAnd | Operator::ShiftLeft | Operator::ShiftRight | Operator::StartsWith diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 274d08894e..0419aa14ea 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -438,6 +438,14 @@ pub fn eval_expression( let rhs = eval_expression(engine_state, stack, rhs)?; lhs.ends_with(op_span, &rhs, expr.span) } + Operator::BitOr => { + let rhs = eval_expression(engine_state, stack, rhs)?; + lhs.bor(op_span, &rhs, expr.span) + } + Operator::BitAnd => { + let rhs = eval_expression(engine_state, stack, rhs)?; + lhs.band(op_span, &rhs, expr.span) + } Operator::ShiftRight => { let rhs = eval_expression(engine_state, stack, rhs)?; lhs.bshr(op_span, &rhs, expr.span) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 5e926c27e8..52173d6c88 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -4106,6 +4106,8 @@ pub fn parse_operator( b"in" => Operator::In, b"not-in" => Operator::NotIn, b"mod" => Operator::Modulo, + b"bor" => Operator::BitOr, + b"band" => Operator::BitAnd, b"bshl" => Operator::ShiftLeft, b"bshr" => Operator::ShiftRight, b"starts-with" => Operator::StartsWith, diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index 561f0789a7..44cb30ce9b 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -492,25 +492,27 @@ pub fn math_result_type( ) } }, - Operator::ShiftLeft | Operator::ShiftRight => match (&lhs.ty, &rhs.ty) { - (Type::Int, Type::Int) => (Type::Int, None), + Operator::ShiftLeft | Operator::ShiftRight | Operator::BitAnd | Operator::BitOr => { + match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Int, None), - (Type::Any, _) => (Type::Any, None), - (_, Type::Any) => (Type::Any, None), - _ => { - *op = Expression::garbage(op.span); - ( - Type::Any, - Some(ParseError::UnsupportedOperation( - op.span, - lhs.span, - lhs.ty.clone(), - rhs.span, - rhs.ty.clone(), - )), - ) + (Type::Any, _) => (Type::Any, None), + (_, Type::Any) => (Type::Any, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Any, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } } - }, + } }, _ => { *op = Expression::garbage(op.span); diff --git a/crates/nu-protocol/src/ast/expression.rs b/crates/nu-protocol/src/ast/expression.rs index 75fd1466b8..11519e5810 100644 --- a/crates/nu-protocol/src/ast/expression.rs +++ b/crates/nu-protocol/src/ast/expression.rs @@ -48,6 +48,9 @@ impl Expression { | Operator::NotEqual | Operator::In | Operator::NotIn => 80, + Operator::BitAnd => 75, + // Operator::BitXor => 70, + Operator::BitOr => 60, Operator::And => 50, Operator::Or => 40, } diff --git a/crates/nu-protocol/src/ast/operator.rs b/crates/nu-protocol/src/ast/operator.rs index b9488263b9..ccf5a5e9cb 100644 --- a/crates/nu-protocol/src/ast/operator.rs +++ b/crates/nu-protocol/src/ast/operator.rs @@ -26,6 +26,8 @@ pub enum Operator { Pow, StartsWith, EndsWith, + BitOr, + BitAnd, ShiftLeft, ShiftRight, } @@ -50,6 +52,8 @@ impl Display for Operator { Operator::And => write!(f, "&&"), Operator::Or => write!(f, "||"), Operator::Pow => write!(f, "**"), + Operator::BitOr => write!(f, "bor"), + Operator::BitAnd => write!(f, "band"), Operator::ShiftLeft => write!(f, "bshl"), Operator::ShiftRight => write!(f, "bshr"), Operator::LessThanOrEqual => write!(f, "<="), diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index 7968a28f80..6a619ebab6 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -2278,6 +2278,44 @@ impl Value { } } + pub fn bor(&self, op: Span, rhs: &Value, span: Span) -> Result { + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int { + span, + val: *lhs | rhs, + }), + (Value::CustomValue { val: lhs, span }, rhs) => { + lhs.operation(*span, Operator::BitOr, 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 band(&self, op: Span, rhs: &Value, span: Span) -> Result { + match (self, rhs) { + (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => Ok(Value::Int { + span, + val: *lhs & rhs, + }), + (Value::CustomValue { val: lhs, span }, rhs) => { + lhs.operation(*span, Operator::BitAnd, 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 modulo(&self, op: Span, rhs: &Value, span: Span) -> Result { match (self, rhs) { (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => { diff --git a/src/tests/test_math.rs b/src/tests/test_math.rs index 3c5f0873d1..86793f31d1 100644 --- a/src/tests/test_math.rs +++ b/src/tests/test_math.rs @@ -55,6 +55,21 @@ fn or() -> TestResult { run_test("true || false", "true") } +#[test] +fn band() -> TestResult { + run_test("2 band 4", "0") +} + +#[test] +fn bor() -> TestResult { + run_test("2 bor 4", "6") +} + +#[test] +fn bit_and_or() -> TestResult { + run_test("2 bor 4 band 1 + 2", "2") +} + #[test] fn pow() -> TestResult { run_test("3 ** 3", "27")