forked from extern/nushell
Rename calc
to math eval
and allow it to optionally take an expression as an argument (#2195)
* Rename `calc` to `math eval` and allow it to optionally take the expression as an argument * Moved calc tests to math eval Also added 2 tests and changed 1 test * Move calc docs to math eval
This commit is contained in:
parent
04e8aa31fe
commit
ffe3e2c16b
@ -264,7 +264,6 @@ pub fn create_default_context(
|
||||
whole_stream_command(Cpy),
|
||||
whole_stream_command(Date),
|
||||
whole_stream_command(Cal),
|
||||
whole_stream_command(Calc),
|
||||
whole_stream_command(Mkdir),
|
||||
whole_stream_command(Mv),
|
||||
whole_stream_command(Kill),
|
||||
@ -367,6 +366,7 @@ pub fn create_default_context(
|
||||
whole_stream_command(AutoenvUnTrust),
|
||||
whole_stream_command(Math),
|
||||
whole_stream_command(MathAverage),
|
||||
whole_stream_command(MathEval),
|
||||
whole_stream_command(MathMedian),
|
||||
whole_stream_command(MathMinimum),
|
||||
whole_stream_command(MathMode),
|
||||
|
@ -14,7 +14,6 @@ pub(crate) mod autoenv_untrust;
|
||||
pub(crate) mod autoview;
|
||||
pub(crate) mod build_string;
|
||||
pub(crate) mod cal;
|
||||
pub(crate) mod calc;
|
||||
pub(crate) mod cd;
|
||||
pub(crate) mod char_;
|
||||
pub(crate) mod classified;
|
||||
@ -134,7 +133,6 @@ pub(crate) use autoenv_trust::AutoenvTrust;
|
||||
pub(crate) use autoenv_untrust::AutoenvUnTrust;
|
||||
pub(crate) use build_string::BuildString;
|
||||
pub(crate) use cal::Cal;
|
||||
pub(crate) use calc::Calc;
|
||||
pub(crate) use char_::Char;
|
||||
pub(crate) use compact::Compact;
|
||||
pub(crate) use config::{
|
||||
@ -192,8 +190,8 @@ pub(crate) use last::Last;
|
||||
pub(crate) use lines::Lines;
|
||||
pub(crate) use ls::Ls;
|
||||
pub(crate) use math::{
|
||||
Math, MathAverage, MathMaximum, MathMedian, MathMinimum, MathMode, MathStddev, MathSummation,
|
||||
MathVariance,
|
||||
Math, MathAverage, MathEval, MathMaximum, MathMedian, MathMinimum, MathMode, MathStddev,
|
||||
MathSummation, MathVariance,
|
||||
};
|
||||
pub(crate) use merge::Merge;
|
||||
pub(crate) use mkdir::Mkdir;
|
||||
|
@ -1,88 +0,0 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue, Value};
|
||||
|
||||
pub struct Calc;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Calc {
|
||||
fn name(&self) -> &str {
|
||||
"calc"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Parse a math expression into a number"
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
calc(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Calculate math in the pipeline",
|
||||
example: "echo '10 / 4' | calc",
|
||||
result: Some(vec![UntaggedValue::decimal(2.5).into()]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn calc(
|
||||
args: CommandArgs,
|
||||
_registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let input = args.input;
|
||||
let name = args.call_info.name_tag.span;
|
||||
|
||||
Ok(input
|
||||
.map(move |input| {
|
||||
if let Ok(string) = input.as_string() {
|
||||
match parse(&string, &input.tag) {
|
||||
Ok(value) => ReturnSuccess::value(value),
|
||||
Err(err) => Err(ShellError::labeled_error(
|
||||
"Calculation error",
|
||||
err,
|
||||
&input.tag.span,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
name,
|
||||
))
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
pub fn parse(math_expression: &str, tag: impl Into<Tag>) -> Result<Value, String> {
|
||||
use std::f64;
|
||||
let num = meval::eval_str(math_expression);
|
||||
match num {
|
||||
Ok(num) => {
|
||||
if num == f64::INFINITY || num == f64::NEG_INFINITY {
|
||||
return Err(String::from("cannot represent result"));
|
||||
}
|
||||
Ok(UntaggedValue::from(Primitive::from(num)).into_value(tag))
|
||||
}
|
||||
Err(error) => Err(error.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Calc;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(Calc {})
|
||||
}
|
||||
}
|
107
crates/nu-cli/src/commands/math/eval.rs
Normal file
107
crates/nu-cli/src/commands/math/eval.rs
Normal file
@ -0,0 +1,107 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SubCommandArgs {
|
||||
expression: Option<Tagged<String>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math eval"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Evaluate a math expression into a number"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("math eval").desc(self.usage()).optional(
|
||||
"math expression",
|
||||
SyntaxShape::String,
|
||||
"the math expression to evaluate",
|
||||
)
|
||||
}
|
||||
|
||||
async fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
eval(args, registry).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Evalulate math in the pipeline",
|
||||
example: "echo '10 / 4' | math eval",
|
||||
result: Some(vec![UntaggedValue::decimal(2.5).into()]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn eval(
|
||||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name = args.call_info.name_tag.span;
|
||||
let (SubCommandArgs { expression }, input) = args.process(registry).await?;
|
||||
|
||||
Ok(input
|
||||
.map(move |x| {
|
||||
if let Some(Tagged {
|
||||
tag,
|
||||
item: expression,
|
||||
}) = &expression
|
||||
{
|
||||
UntaggedValue::string(expression).into_value(tag)
|
||||
} else {
|
||||
x
|
||||
}
|
||||
})
|
||||
.map(move |input| {
|
||||
if let Ok(string) = input.as_string() {
|
||||
match parse(&string, &input.tag) {
|
||||
Ok(value) => ReturnSuccess::value(value),
|
||||
Err(err) => Err(ShellError::labeled_error(
|
||||
"Math evaluation error",
|
||||
err,
|
||||
&input.tag.span,
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
name,
|
||||
))
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
pub fn parse<T: Into<Tag>>(math_expression: &str, tag: T) -> Result<Value, String> {
|
||||
match meval::eval_str(math_expression) {
|
||||
Ok(num) if num.is_infinite() || num.is_nan() => Err("cannot represent result".to_string()),
|
||||
Ok(num) => Ok(UntaggedValue::from(Primitive::from(num)).into_value(tag)),
|
||||
Err(error) => Err(error.to_string().to_lowercase()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
pub mod avg;
|
||||
pub mod command;
|
||||
pub mod eval;
|
||||
pub mod max;
|
||||
pub mod median;
|
||||
pub mod min;
|
||||
@ -11,6 +12,7 @@ pub mod variance;
|
||||
|
||||
pub use avg::SubCommand as MathAverage;
|
||||
pub use command::Command as Math;
|
||||
pub use eval::SubCommand as MathEval;
|
||||
pub use max::SubCommand as MathMaximum;
|
||||
pub use median::SubCommand as MathMedian;
|
||||
pub use min::SubCommand as MathMinimum;
|
||||
|
@ -1,49 +0,0 @@
|
||||
use nu_test_support::{nu, pipeline};
|
||||
|
||||
#[test]
|
||||
fn calculates_two_plus_two() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
echo "2 + 2" | calc
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("4.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calculates_two_to_the_power_six() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
echo "2 ^ 6" | calc
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("64.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calculates_three_multiplied_by_five() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
echo "3 * 5" | calc
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("15.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calculates_twenty_four_divided_by_two() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
echo "24 / 2" | calc
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("12.0"));
|
||||
}
|
73
crates/nu-cli/tests/commands/math/eval.rs
Normal file
73
crates/nu-cli/tests/commands/math/eval.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use nu_test_support::{nu, pipeline};
|
||||
|
||||
#[test]
|
||||
fn evaluates_two_plus_two() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
math eval "2 + 2"
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("4.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evaluates_two_to_the_power_four() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
echo "2 ^ 4" | math eval
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("16.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evaluates_three_multiplied_by_five() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
echo "3 * 5" | math eval
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("15.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evaluates_twenty_four_divided_by_two() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
echo "24 / 2" | math eval
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("12.0"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evaluates_twenty_eight_minus_seven() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
echo "28 - 7" | math eval
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("21"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn evaluates_pi() {
|
||||
let actual = nu!(
|
||||
cwd: ".", pipeline(
|
||||
r#"
|
||||
math eval pi
|
||||
"#
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("3.14"));
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
mod avg;
|
||||
mod eval;
|
||||
mod median;
|
||||
mod sum;
|
||||
|
||||
use nu_test_support::{nu, pipeline};
|
||||
|
||||
|
@ -4,7 +4,6 @@ mod autoenv;
|
||||
mod autoenv_trust;
|
||||
mod autoenv_untrust;
|
||||
mod cal;
|
||||
mod calc;
|
||||
mod cd;
|
||||
mod compact;
|
||||
mod cp;
|
||||
|
@ -1,6 +1,6 @@
|
||||
# calc
|
||||
# math eval
|
||||
|
||||
calc is a command that takes a math expression from the pipeline and calculates that into a number.
|
||||
math eval is a command that takes a math expression from the pipeline and evaluates that into a number. It also optionally takes the math expression as an argument.
|
||||
|
||||
This command supports the following operations -
|
||||
|
||||
@ -27,62 +27,62 @@ constants:
|
||||
## Examples
|
||||
|
||||
```shell
|
||||
> echo "1+2+3" | calc
|
||||
6.0
|
||||
> echo "1+2+3" | math eval
|
||||
6.0u
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo "1-2+3" | calc
|
||||
> echo "1-2+3" | math eval
|
||||
2.0
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo "-(-23)" | calc
|
||||
> echo "-(-23)" | math eval
|
||||
23.0
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo "5^2" | calc
|
||||
> echo "5^2" | math eval
|
||||
25.0
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo "5^3" | calc
|
||||
> echo "5^3" | math eval
|
||||
125.0
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo "min(5,4,3,2,1,0,-100,45)" | calc
|
||||
> echo "min(5,4,3,2,1,0,-100,45)" | math eval
|
||||
-100.0
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo "max(5,4,3,2,1,0,-100,45)" | calc
|
||||
> echo "max(5,4,3,2,1,0,-100,45)" | math eval
|
||||
45.0
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo sqrt(2) | calc
|
||||
> echo sqrt(2) | math eval
|
||||
1.414213562373095
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo pi | calc
|
||||
> echo pi | math eval
|
||||
3.141592653589793
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo e | calc
|
||||
> echo e | math eval
|
||||
2.718281828459045
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo "sin(pi / 2)" | calc
|
||||
> echo "sin(pi / 2)" | math eval
|
||||
1.0
|
||||
```
|
||||
|
||||
```shell
|
||||
> echo "floor(5999/1000)" | calc
|
||||
> echo "floor(5999/1000)" | math eval
|
||||
5.0
|
||||
```
|
||||
|
||||
@ -119,7 +119,7 @@ constants:
|
||||
```
|
||||
|
||||
```shell
|
||||
> open abc.json | format "({size} + 500) * 4" | calc
|
||||
> open abc.json | format "({size} + 500) * 4" | math eval
|
||||
───┬───────────
|
||||
# │
|
||||
───┼───────────
|
||||
@ -135,7 +135,7 @@ constants:
|
||||
```
|
||||
|
||||
```shell
|
||||
> open abc.json | format "({size} - 1000) * 4" | calc
|
||||
> open abc.json | format "({size} - 1000) * 4" | math eval
|
||||
───┬────────────
|
||||
# │
|
||||
───┼────────────
|
||||
@ -150,9 +150,9 @@ constants:
|
||||
───┴────────────
|
||||
```
|
||||
|
||||
Note that since `calc` uses floating-point numbers, the result may not always be precise.
|
||||
Note that since `math eval` uses floating-point numbers, the result may not always be precise.
|
||||
|
||||
```shell
|
||||
> echo "floor(5999999999999999999/1000000000000000000)" | calc
|
||||
> echo "floor(5999999999999999999/1000000000000000000)" | math eval
|
||||
6.0
|
||||
```
|
@ -4,6 +4,7 @@ Mathematical functions that generally only operate on a list of numbers (integer
|
||||
Currently the following functions are implemented:
|
||||
|
||||
* `math avg`: Finds the average of a list of numbers or tables
|
||||
* [`math eval`](math-eval.md): Evaluates a list of math expressions into numbers
|
||||
* `math min`: Finds the minimum within a list of numbers or tables
|
||||
* `math max`: Finds the maximum within a list of numbers or tables
|
||||
* `math median`: Finds the median of a list of numbers or tables
|
||||
@ -152,11 +153,3 @@ To get the sum of the characters that make up your present working directory.
|
||||
> echo [] | math avg
|
||||
error: Error: Unexpected: Cannot perform aggregate math operation on empty data
|
||||
```
|
||||
|
||||
Note `math` functions only work on list of numbers (integers, decimals, bytes) and tables of numbers, if any other types are piped into the function
|
||||
then unexpected results can occur.
|
||||
|
||||
```shell
|
||||
> echo [1 2 a ] | math avg
|
||||
0
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user