mirror of
https://github.com/nushell/nushell.git
synced 2025-03-29 17:16:49 +01:00
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:
parent
43a218240c
commit
caafd26deb
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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!(
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
||||
|
@ -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
|
||||
|
@ -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, "**"),
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user