Add band and bor operator for bit operations (#5936)

* Add `band` and `bor` Operator

* Add tests
This commit is contained in:
Justin Ma 2022-07-03 02:03:36 +08:00 committed by GitHub
parent 84caf8859f
commit b82dccf0bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 91 additions and 17 deletions

View file

@ -118,6 +118,8 @@ impl CustomValue for ExprDb {
Operator::In
| Operator::NotIn
| Operator::Pow
| Operator::BitOr
| Operator::BitAnd
| Operator::ShiftLeft
| Operator::ShiftRight
| Operator::StartsWith

View file

@ -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)

View file

@ -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,

View file

@ -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);

View file

@ -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,
}

View file

@ -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, "<="),

View file

@ -2278,6 +2278,44 @@ impl Value {
}
}
pub fn bor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
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<Value, ShellError> {
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<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {

View file

@ -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")