Move Value to helpers, separate span call (#10121)

# Description

As part of the refactor to split spans off of Value, this moves to using
helper functions to create values, and using `.span()` instead of
matching span out of Value directly.

Hoping to get a few more helping hands to finish this, as there are a
lot of commands to update :)

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to
check that you're using the standard code style
- `cargo test --workspace` to check that all tests pass (on Windows make
sure to [enable developer
mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging))
- `cargo run -- -c "use std testing; testing run-tests --path
crates/nu-std"` to run the tests for the standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->

---------

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
Co-authored-by: WindSoilder <windsoilder@outlook.com>
This commit is contained in:
JT
2023-09-04 02:27:29 +12:00
committed by GitHub
parent af79eb2943
commit 6cdfee3573
372 changed files with 5811 additions and 7448 deletions

View File

@ -54,39 +54,34 @@ impl Command for SubCommand {
vec![Example {
description: "Compute absolute value of each number in a list of numbers",
example: "[-50 -100.0 25] | math abs",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(50),
Value::test_float(100.0),
Value::test_int(25),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
}]
}
}
fn abs_helper(val: Value, head: Span) -> Value {
let span = val.span();
match val {
Value::Int { val, span } => Value::int(val.abs(), span),
Value::Float { val, span } => Value::Float {
val: val.abs(),
span,
},
Value::Duration { val, span } => Value::Duration {
val: val.abs(),
span,
},
Value::Int { val, .. } => Value::int(val.abs(), span),
Value::Float { val, .. } => Value::float(val.abs(), span),
Value::Duration { val, .. } => Value::duration(val.abs(), span),
Value::Error { .. } => val,
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "numeric".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
span: head,
},
},
head,
),
}
}

View File

@ -53,15 +53,10 @@ impl Command for SubCommand {
pub fn average(values: &[Value], span: Span, head: Span) -> Result<Value, ShellError> {
let sum = reducer_for(Reduce::Summation);
let total = &sum(Value::int(0, head), values.to_vec(), span, head)?;
let span = total.span();
match total {
Value::Filesize { val, span } => Ok(Value::Filesize {
val: val / values.len() as i64,
span: *span,
}),
Value::Duration { val, span } => Ok(Value::Duration {
val: val / values.len() as i64,
span: *span,
}),
Value::Filesize { val, .. } => Ok(Value::filesize(val / values.len() as i64, span)),
Value::Duration { val, .. } => Ok(Value::duration(val / values.len() as i64, span)),
_ => total.div(head, &Value::int(values.len() as i64, head), head),
}
}

View File

@ -53,31 +53,29 @@ impl Command for SubCommand {
vec![Example {
description: "Apply the ceil function to a list of numbers",
example: "[1.5 2.3 -3.1] | math ceil",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(-3)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(3), Value::test_int(-3)],
Span::test_data(),
)),
}]
}
}
fn operate(value: Value, head: Span) -> Value {
let span = value.span();
match value {
Value::Int { .. } => value,
Value::Float { val, span } => Value::Int {
val: val.ceil() as i64,
span,
},
Value::Float { val, .. } => Value::int(val.ceil() as i64, span),
Value::Error { .. } => value,
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "numeric".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
span: head,
},
},
head,
),
}
}

View File

@ -53,31 +53,29 @@ impl Command for SubCommand {
vec![Example {
description: "Apply the floor function to a list of numbers",
example: "[1.5 2.3 -3.1] | math floor",
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(-4)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(1), Value::test_int(2), Value::test_int(-4)],
Span::test_data(),
)),
}]
}
}
fn operate(value: Value, head: Span) -> Value {
let span = value.span();
match value {
Value::Int { .. } => value,
Value::Float { val, span } => Value::Int {
val: val.floor() as i64,
span,
},
Value::Float { val, .. } => Value::int(val.floor() as i64, span),
Value::Error { .. } => value,
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "numeric".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
span: head,
},
},
head,
),
}
}

View File

@ -78,39 +78,40 @@ impl Command for SubCommand {
Example {
example: "[16 8 4] | math log 2",
description: "Get the log2 of a list of values",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_float(4.0),
Value::test_float(3.0),
Value::test_float(2.0),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
}
fn operate(value: Value, head: Span, base: f64) -> Value {
let span = value.span();
match value {
numeric @ (Value::Int { .. } | Value::Float { .. }) => {
let (val, span) = match numeric {
Value::Int { val, span } => (val as f64, span),
Value::Float { val, span } => (val, span),
Value::Int { val, .. } => (val as f64, span),
Value::Float { val, .. } => (val, span),
_ => unreachable!(),
};
if val <= 0.0 {
return Value::Error {
error: Box::new(ShellError::UnsupportedInput(
return Value::error(
ShellError::UnsupportedInput(
"'math log' undefined for values outside the open interval (0, Inf)."
.into(),
"value originates from here".into(),
head,
span,
)),
),
span,
};
);
}
// Specialize for better precision/performance
let val = if base == 10.0 {
@ -121,18 +122,18 @@ fn operate(value: Value, head: Span, base: f64) -> Value {
val.log(base)
};
Value::Float { val, span }
Value::float(val, span)
}
Value::Error { .. } => value,
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "numeric".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
span: head,
},
},
head,
),
}
}

View File

@ -34,16 +34,16 @@ impl Command for MathCommand {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(Value::String {
val: get_full_help(
Ok(Value::string(
get_full_help(
&MathCommand.signature(),
&MathCommand.examples(),
engine_state,
stack,
self.is_parser_keyword(),
),
span: call.head,
}
call.head,
)
.into_pipeline_data())
}
}

View File

@ -82,10 +82,10 @@ impl Command for SubCommand {
Example {
description: "Compute the mode(s) of a list of numbers",
example: "[3 3 9 12 12 15] | math mode",
result: Some(Value::List {
vals: vec![Value::test_int(3), Value::test_int(12)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(3), Value::test_int(12)],
Span::test_data(),
)),
},
Example {
description: "Compute the mode(s) of the columns of a table",
@ -93,14 +93,11 @@ impl Command for SubCommand {
result: Some(Value::test_record(Record {
cols: vec!["a".to_string(), "b".to_string()],
vals: vec![
Value::List {
vals: vec![Value::test_int(1)],
span: Span::test_data(),
},
Value::List {
vals: vec![Value::test_int(-1), Value::test_int(3), Value::test_int(5)],
span: Span::test_data(),
},
Value::list(vec![Value::test_int(1)], Span::test_data()),
Value::list(
vec![Value::test_int(-1), Value::test_int(3), Value::test_int(5)],
Span::test_data(),
),
],
})),
},
@ -176,10 +173,7 @@ pub fn mode(values: &[Value], _span: Span, head: Span) -> Result<Value, ShellErr
}
modes.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
Ok(Value::List {
vals: modes,
span: head,
})
Ok(Value::list(modes, head))
}
fn recreate_value(hashable_value: &HashableType, head: Span) -> Value {
@ -187,14 +181,8 @@ fn recreate_value(hashable_value: &HashableType, head: Span) -> Value {
match &hashable_value.original_type {
NumberTypes::Int => Value::int(i64::from_ne_bytes(bytes), head),
NumberTypes::Float => Value::float(f64::from_ne_bytes(bytes), head),
NumberTypes::Duration => Value::Duration {
val: i64::from_ne_bytes(bytes),
span: head,
},
NumberTypes::Filesize => Value::Filesize {
val: i64::from_ne_bytes(bytes),
span: head,
},
NumberTypes::Duration => Value::duration(i64::from_ne_bytes(bytes), head),
NumberTypes::Filesize => Value::filesize(i64::from_ne_bytes(bytes), head),
}
}

View File

@ -86,22 +86,22 @@ pub fn sum(data: Vec<Value>, span: Span, head: Span) -> Result<Value, ShellError
let initial_value = data.get(0);
let mut acc = match initial_value {
Some(Value::Filesize { span, .. }) => Ok(Value::Filesize {
val: 0,
span: *span,
}),
Some(Value::Duration { span, .. }) => Ok(Value::Duration {
val: 0,
span: *span,
}),
Some(Value::Int { span, .. }) | Some(Value::Float { span, .. }) => Ok(Value::int(0, *span)),
Some(v) => {
let span = v.span();
match v {
Value::Filesize { .. } => Ok(Value::filesize(0, span)),
Value::Duration { .. } => Ok(Value::duration(0, span)),
Value::Int { .. } | Value::Float { .. } => Ok(Value::int(0, span)),
_ => Ok(Value::nothing(head)),
}
}
None => Err(ShellError::UnsupportedInput(
"Empty input".to_string(),
"value originates from here".into(),
head,
span,
)),
_ => Ok(Value::nothing(head)),
}?;
for value in &data {
@ -130,14 +130,19 @@ pub fn product(data: Vec<Value>, span: Span, head: Span) -> Result<Value, ShellE
let initial_value = data.get(0);
let mut acc = match initial_value {
Some(Value::Int { span, .. }) | Some(Value::Float { span, .. }) => Ok(Value::int(1, *span)),
Some(v) => {
let span = v.span();
match v {
Value::Int { .. } | Value::Float { .. } => Ok(Value::int(1, span)),
_ => Ok(Value::nothing(head)),
}
}
None => Err(ShellError::UnsupportedInput(
"Empty input".to_string(),
"value originates from here".into(),
head,
span,
)),
_ => Ok(Value::nothing(head)),
}?;
for value in &data {

View File

@ -64,34 +64,34 @@ impl Command for SubCommand {
Example {
description: "Apply the round function to a list of numbers",
example: "[1.5 2.3 -3.1] | math round",
result: Some(Value::List {
vals: vec![Value::test_int(2), Value::test_int(2), Value::test_int(-3)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(2), Value::test_int(2), Value::test_int(-3)],
Span::test_data(),
)),
},
Example {
description: "Apply the round function with precision specified",
example: "[1.555 2.333 -3.111] | math round -p 2",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_float(1.56),
Value::test_float(2.33),
Value::test_float(-3.11),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
Example {
description: "Apply negative precision to a list of numbers",
example: "[123, 123.3, -123.4] | math round -p -1",
result: Some(Value::List {
vals: vec![
result: Some(Value::list(
vec![
Value::test_int(120),
Value::test_int(120),
Value::test_int(-120),
],
span: Span::test_data(),
}),
Span::test_data(),
)),
},
]
}
@ -99,37 +99,32 @@ impl Command for SubCommand {
fn operate(value: Value, head: Span, precision: Option<i64>) -> Value {
// We treat int values as float values in order to avoid code repetition in the match closure
let value = if let Value::Int { val, span } = value {
Value::Float {
val: val as f64,
span,
}
let span = value.span();
let value = if let Value::Int { val, .. } = value {
Value::float(val as f64, span)
} else {
value
};
match value {
Value::Float { val, span } => match precision {
Some(precision_number) => Value::Float {
val: ((val * ((10_f64).powf(precision_number as f64))).round()
/ (10_f64).powf(precision_number as f64)),
Value::Float { val, .. } => match precision {
Some(precision_number) => Value::float(
(val * ((10_f64).powf(precision_number as f64))).round()
/ (10_f64).powf(precision_number as f64),
span,
},
None => Value::Int {
val: val.round() as i64,
span,
},
),
None => Value::int(val.round() as i64, span),
},
Value::Error { .. } => value,
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "numeric".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
span: head,
},
},
head,
),
}
}

View File

@ -53,53 +53,54 @@ impl Command for SubCommand {
vec![Example {
description: "Compute the square root of each number in a list",
example: "[9 16] | math sqrt",
result: Some(Value::List {
vals: vec![Value::test_int(3), Value::test_int(4)],
span: Span::test_data(),
}),
result: Some(Value::list(
vec![Value::test_int(3), Value::test_int(4)],
Span::test_data(),
)),
}]
}
}
fn operate(value: Value, head: Span) -> Value {
let span = value.span();
match value {
Value::Int { val, span } => {
Value::Int { val, .. } => {
let squared = (val as f64).sqrt();
if squared.is_nan() {
return error_negative_sqrt(head, span);
}
Value::Float { val: squared, span }
Value::float(squared, span)
}
Value::Float { val, span } => {
Value::Float { val, .. } => {
let squared = val.sqrt();
if squared.is_nan() {
return error_negative_sqrt(head, span);
}
Value::Float { val: squared, span }
Value::float(squared, span)
}
Value::Error { .. } => value,
other => Value::Error {
error: Box::new(ShellError::OnlySupportsThisInputType {
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "numeric".into(),
wrong_type: other.get_type().to_string(),
dst_span: head,
src_span: other.span(),
}),
span: head,
},
},
head,
),
}
}
fn error_negative_sqrt(head: Span, span: Span) -> Value {
Value::Error {
error: Box::new(ShellError::UnsupportedInput(
Value::error(
ShellError::UnsupportedInput(
String::from("Can't square root a negative number"),
"value originates from here".into(),
head,
span,
)),
),
span,
}
)
}
#[cfg(test)]

View File

@ -67,19 +67,13 @@ impl Command for SubCommand {
pub fn compute_stddev(sample: bool) -> impl Fn(&[Value], Span, Span) -> Result<Value, ShellError> {
move |values: &[Value], span: Span, head: Span| {
let variance = variance(sample)(values, span, head);
// variance() produces its own usable error, so we can use `?` to propagated the error.
let variance = variance(sample)(values, span, head)?;
let val_span = variance.span();
match variance {
Ok(Value::Float { val, span }) => Ok(Value::Float {
val: val.sqrt(),
span,
}),
Ok(Value::Int { val, span }) => Ok(Value::Float {
val: (val as f64).sqrt(),
span,
}),
// variance() produces its own usable error, which can simply be propagated.
Err(e) => Err(e),
other => other,
Value::Float { val, .. } => Ok(Value::float(val.sqrt(), val_span)),
Value::Int { val, .. } => Ok(Value::float((val as f64).sqrt(), val_span)),
other => Ok(other),
}
}
}

View File

@ -71,7 +71,7 @@ pub fn calculate(
PipelineData::ListStream(s, ..) => {
helper_for_tables(&s.collect::<Vec<Value>>(), span, name, mf)
}
PipelineData::Value(Value::List { ref vals, span }, ..) => match &vals[..] {
PipelineData::Value(Value::List { ref vals, .. }, ..) => match &vals[..] {
[Value::Record { .. }, _end @ ..] => helper_for_tables(
vals,
values.span().expect("PipelineData::Value had no span"),
@ -80,7 +80,7 @@ pub fn calculate(
),
_ => mf(vals, span, name),
},
PipelineData::Value(Value::Record { val: record, span }, ..) => {
PipelineData::Value(Value::Record { val: record, .. }, ..) => {
let new_vals: Result<Vec<Value>, ShellError> = record
.vals
.into_iter()
@ -97,7 +97,7 @@ pub fn calculate(
Err(err) => Err(err),
}
}
PipelineData::Value(Value::Range { val, span, .. }, ..) => {
PipelineData::Value(Value::Range { val, .. }, ..) => {
let new_vals: Result<Vec<Value>, ShellError> = val
.into_range_iter(None)?
.map(|val| mf(&[val], span, name))