Attempts to add // math operator (#5759)

* attempts to add `div` math operator

* allows `//` to be used too

* fmt:

* clippy issue

* returns appropriate type

* returns appropriate type 2

* fmt

* ensure consistency; rename to `fdiv`

* Update parser.rs
This commit is contained in:
pwygab 2022-06-13 18:54:47 +08:00 committed by GitHub
parent 43a218240c
commit caafd26deb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 236 additions and 1 deletions

View File

@ -112,6 +112,7 @@ impl CustomValue for ExprDb {
Operator::Multiply => Ok(BinaryOperator::Multiply),
Operator::Divide => Ok(BinaryOperator::Divide),
Operator::Modulo => Ok(BinaryOperator::Modulo),
Operator::FloorDivision => Ok(BinaryOperator::Divide),
Operator::And => Ok(BinaryOperator::And),
Operator::Or => Ok(BinaryOperator::Or),
Operator::In

View File

@ -100,6 +100,7 @@ fn with_operator(
Operator::Multiply => apply_arithmetic(left, right, lhs_span, Mul::mul),
Operator::Divide => apply_arithmetic(left, right, lhs_span, Div::div),
Operator::Modulo => apply_arithmetic(left, right, lhs_span, Rem::rem),
Operator::FloorDivision => apply_arithmetic(left, right, lhs_span, Div::div),
Operator::Equal => Ok(left
.clone()
.apply_with_expr(right.clone(), Expr::eq)

View File

@ -163,6 +163,89 @@ fn error_zero_division_decimal_decimal() {
assert!(actual.err.contains("division by zero"));
}
#[test]
fn floor_division_of_ints() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
5 // 2
"#
));
assert_eq!(actual.out, "2");
}
#[test]
fn floor_division_of_ints2() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
-3 // 2
"#
));
assert_eq!(actual.out, "-2");
}
#[test]
fn floor_division_of_floats() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
-3.0 // 2.0
"#
));
assert_eq!(actual.out, "-2");
}
#[test]
fn error_zero_floor_division_int_int() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
1 // 0
"#
));
assert!(actual.err.contains("division by zero"));
}
#[test]
fn error_zero_floor_division_decimal_int() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
1.0 // 0
"#
));
assert!(actual.err.contains("division by zero"));
}
#[test]
fn error_zero_floor_division_int_decimal() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
1 // 0.0
"#
));
assert!(actual.err.contains("division by zero"));
}
#[test]
fn error_zero_floor_division_decimal_decimal() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
1.0 // 0.0
"#
));
assert!(actual.err.contains("division by zero"));
}
#[test]
fn proper_precedence_history() {
let actual = nu!(

View File

@ -422,6 +422,10 @@ pub fn eval_expression(
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.modulo(op_span, &rhs, expr.span)
}
Operator::FloorDivision => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.floor_div(op_span, &rhs, expr.span)
}
Operator::Pow => {
let rhs = eval_expression(engine_state, stack, rhs)?;
lhs.pow(op_span, &rhs, expr.span)

View File

@ -4086,6 +4086,7 @@ pub fn parse_operator(
b"-" => Operator::Minus,
b"*" => Operator::Multiply,
b"/" => Operator::Divide,
b"//" => Operator::FloorDivision,
b"in" => Operator::In,
b"not-in" => Operator::NotIn,
b"mod" => Operator::Modulo,

View File

@ -181,6 +181,33 @@ pub fn math_result_type(
)
}
},
Operator::FloorDivision => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Int, None),
(Type::Int, Type::Float) => (Type::Int, None),
(Type::Float, Type::Float) => (Type::Int, None),
(Type::Filesize, Type::Filesize) => (Type::Int, None),
(Type::Duration, Type::Duration) => (Type::Int, None),
(Type::Filesize, Type::Int) => (Type::Filesize, None),
(Type::Duration, Type::Int) => (Type::Duration, 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(),
)),
)
}
},
Operator::And | Operator::Or => match (&lhs.ty, &rhs.ty) {
(Type::Bool, Type::Bool) => (Type::Bool, None),

View File

@ -30,7 +30,10 @@ impl Expression {
match operator {
Operator::Pow => 100,
Operator::Multiply | Operator::Divide | Operator::Modulo => 95,
Operator::Multiply
| Operator::Divide
| Operator::Modulo
| Operator::FloorDivision => 95,
Operator::Plus | Operator::Minus => 90,
Operator::NotRegexMatch
| Operator::RegexMatch

View File

@ -20,6 +20,7 @@ pub enum Operator {
In,
NotIn,
Modulo,
FloorDivision,
And,
Or,
Pow,
@ -43,6 +44,7 @@ impl Display for Operator {
Operator::In => write!(f, "in"),
Operator::NotIn => write!(f, "not-in"),
Operator::Modulo => write!(f, "mod"),
Operator::FloorDivision => write!(f, "fdiv"),
Operator::And => write!(f, "&&"),
Operator::Or => write!(f, "||"),
Operator::Pow => write!(f, "**"),

View File

@ -1769,6 +1769,119 @@ impl Value {
}),
}
}
pub fn floor_div(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if *rhs != 0 {
Ok(Value::Int {
val: (*lhs as f64 / *rhs as f64)
.max(std::i64::MIN as f64)
.min(std::i64::MAX as f64)
.floor() as i64,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if *rhs != 0.0 {
Ok(Value::Int {
val: (*lhs as f64 / *rhs)
.max(std::i64::MIN as f64)
.min(std::i64::MAX as f64)
.floor() as i64,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if *rhs != 0 {
Ok(Value::Int {
val: (*lhs / *rhs as f64)
.max(std::i64::MIN as f64)
.min(std::i64::MAX as f64)
.floor() as i64,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if *rhs != 0.0 {
Ok(Value::Int {
val: (lhs / rhs)
.max(std::i64::MIN as f64)
.min(std::i64::MAX as f64)
.floor() as i64,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
if *rhs != 0 {
Ok(Value::Int {
val: (*lhs as f64 / *rhs as f64)
.max(std::i64::MIN as f64)
.min(std::i64::MAX as f64)
.floor() as i64,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
if *rhs != 0 {
Ok(Value::Int {
val: (*lhs as f64 / *rhs as f64)
.max(std::i64::MIN as f64)
.min(std::i64::MAX as f64)
.floor() as i64,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if *rhs != 0 {
Ok(Value::Filesize {
val: lhs / rhs,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if *rhs != 0 {
Ok(Value::Duration {
val: lhs / rhs,
span,
})
} else {
Err(ShellError::DivisionByZero(op))
}
}
(Value::CustomValue { val: lhs, span }, rhs) => {
lhs.operation(*span, Operator::Divide, 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 lt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
if let (Value::CustomValue { val: lhs, span }, rhs) = (self, rhs) {
return lhs.operation(*span, Operator::LessThan, op, rhs);