From 986b42703815c96c2da3f26d5c5a126837fe4017 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 7 Sep 2020 12:12:55 +1200 Subject: [PATCH] Add modulo operator and simplify in/not-in (#2505) --- crates/nu-cli/src/evaluate/operator.rs | 8 ++++++++ crates/nu-cli/tests/commands/math/mod.rs | 12 ++++++++++++ crates/nu-cli/tests/commands/merge.rs | 2 +- crates/nu-cli/tests/commands/where_.rs | 4 ++-- crates/nu-data/src/value.rs | 7 +++++++ crates/nu-parser/src/parse.rs | 5 +++-- crates/nu-protocol/src/hir.rs | 3 ++- 7 files changed, 35 insertions(+), 6 deletions(-) diff --git a/crates/nu-cli/src/evaluate/operator.rs b/crates/nu-cli/src/evaluate/operator.rs index c5bfee949..a1584999f 100644 --- a/crates/nu-cli/src/evaluate/operator.rs +++ b/crates/nu-cli/src/evaluate/operator.rs @@ -33,6 +33,14 @@ pub fn apply_operator( )), _ => res, }), + Operator::Modulo => value::compute_values(op, left, right).map(|res| match res { + UntaggedValue::Error(_) => UntaggedValue::Error(ShellError::labeled_error( + "Evaluation error", + "division by zero", + &right.tag.span, + )), + _ => res, + }), Operator::In => table_contains(left, right).map(UntaggedValue::boolean), Operator::NotIn => table_contains(left, right).map(|x| UntaggedValue::boolean(!x)), Operator::And => match (left.as_bool(), right.as_bool()) { diff --git a/crates/nu-cli/tests/commands/math/mod.rs b/crates/nu-cli/tests/commands/math/mod.rs index 2db1fbe56..36ecaf55c 100644 --- a/crates/nu-cli/tests/commands/math/mod.rs +++ b/crates/nu-cli/tests/commands/math/mod.rs @@ -161,6 +161,18 @@ fn parens_precedence() { assert_eq!(actual.out, "12"); } +#[test] +fn modulo() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + = 9 mod 2 + "# + )); + + assert_eq!(actual.out, "1"); +} + #[test] fn duration_math() { let actual = nu!( diff --git a/crates/nu-cli/tests/commands/merge.rs b/crates/nu-cli/tests/commands/merge.rs index cda5a9178..52d48fb76 100644 --- a/crates/nu-cli/tests/commands/merge.rs +++ b/crates/nu-cli/tests/commands/merge.rs @@ -31,7 +31,7 @@ fn row() { r#" open caballeros.csv | merge { open new_caballeros.csv } - | where country in: ["Guayaquil Ecuador" "New Zealand"] + | where country in ["Guayaquil Ecuador" "New Zealand"] | get luck | math sum | echo $it diff --git a/crates/nu-cli/tests/commands/where_.rs b/crates/nu-cli/tests/commands/where_.rs index b8979ee9b..9f6b1b88e 100644 --- a/crates/nu-cli/tests/commands/where_.rs +++ b/crates/nu-cli/tests/commands/where_.rs @@ -27,7 +27,7 @@ fn filters_with_nothing_comparison() { fn where_in_table() { let actual = nu!( cwd: "tests/fixtures/formats", - r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name in: ["foo"] | get size | math sum | echo $it"# + r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name in ["foo"] | get size | math sum | echo $it"# ); assert_eq!(actual.out, "5"); @@ -37,7 +37,7 @@ fn where_in_table() { fn where_not_in_table() { let actual = nu!( cwd: "tests/fixtures/formats", - r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name not-in: ["foo"] | get size | math sum | echo $it"# + r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name not-in ["foo"] | get size | math sum | echo $it"# ); assert_eq!(actual.out, "4"); diff --git a/crates/nu-data/src/value.rs b/crates/nu-data/src/value.rs index 292140ecb..eecc2e9fa 100644 --- a/crates/nu-data/src/value.rs +++ b/crates/nu-data/src/value.rs @@ -131,6 +131,13 @@ pub fn compute_values( ))) } } + Operator::Modulo => { + if y.is_zero() { + Ok(zero_division_error()) + } else { + Ok(UntaggedValue::Primitive(Primitive::Int(x % y))) + } + } _ => Err((left.type_name(), right.type_name())), }, (Primitive::Decimal(x), Primitive::Int(y)) => { diff --git a/crates/nu-parser/src/parse.rs b/crates/nu-parser/src/parse.rs index 0beff84e6..b05878761 100644 --- a/crates/nu-parser/src/parse.rs +++ b/crates/nu-parser/src/parse.rs @@ -286,8 +286,9 @@ fn parse_operator(lite_arg: &Spanned) -> (SpannedExpression, Option Operator::Minus, "*" => Operator::Multiply, "/" => Operator::Divide, - "in:" => Operator::In, - "not-in:" => Operator::NotIn, + "in" => Operator::In, + "not-in" => Operator::NotIn, + "mod" => Operator::Modulo, "&&" => Operator::And, "||" => Operator::Or, _ => { diff --git a/crates/nu-protocol/src/hir.rs b/crates/nu-protocol/src/hir.rs index 71dd4f53e..a164b36f3 100644 --- a/crates/nu-protocol/src/hir.rs +++ b/crates/nu-protocol/src/hir.rs @@ -575,7 +575,7 @@ impl SpannedExpression { // Higher precedence binds tighter match operator { - Operator::Multiply | Operator::Divide => 100, + Operator::Multiply | Operator::Divide | Operator::Modulo => 100, Operator::Plus | Operator::Minus => 90, Operator::NotContains | Operator::Contains @@ -848,6 +848,7 @@ pub enum Operator { Divide, In, NotIn, + Modulo, And, Or, }