mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 09:45:50 +02:00
Math operators (#1601)
* Add some math operations * WIP for adding compound expressions * precedence parsing * paren expressions * better lhs handling * add compound comparisons and shorthand lefthand parsing * Add or comparison and shorthand paths
This commit is contained in:
@ -710,6 +710,7 @@ async fn process_line(
|
||||
|
||||
let pipeline = nu_parser::classify_pipeline(&result, ctx.registry());
|
||||
|
||||
debug!("{:#?}", pipeline);
|
||||
//println!("{:#?}", pipeline);
|
||||
|
||||
if let Some(failure) = pipeline.failed {
|
||||
|
@ -156,6 +156,12 @@ pub fn autoview(context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
} => {
|
||||
out!("{}", n);
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Boolean(b)),
|
||||
..
|
||||
} => {
|
||||
out!("{}", b);
|
||||
}
|
||||
|
||||
Value { value: UntaggedValue::Primitive(Primitive::Binary(ref b)), .. } => {
|
||||
if let Some(binary) = binary {
|
||||
|
@ -16,7 +16,7 @@ impl WholeStreamCommand for SkipWhile {
|
||||
Signature::build("skip-while")
|
||||
.required(
|
||||
"condition",
|
||||
SyntaxShape::Condition,
|
||||
SyntaxShape::Math,
|
||||
"the condition that must be met to continue skipping",
|
||||
)
|
||||
.filter()
|
||||
|
@ -18,7 +18,7 @@ impl PerItemCommand for Where {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("where").required(
|
||||
"condition",
|
||||
SyntaxShape::Condition,
|
||||
SyntaxShape::Math,
|
||||
"the condition that must match",
|
||||
)
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ use std::time::SystemTime;
|
||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new, Serialize)]
|
||||
pub struct Operation {
|
||||
pub(crate) left: Value,
|
||||
pub(crate) operator: hir::CompareOperator,
|
||||
pub(crate) operator: hir::Operator,
|
||||
pub(crate) right: Value,
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ pub(crate) enum CompareValues {
|
||||
Decimals(BigDecimal, BigDecimal),
|
||||
String(String, String),
|
||||
Date(DateTime<Utc>, DateTime<Utc>),
|
||||
DateDuration(DateTime<Utc>, u64),
|
||||
DateDuration(DateTime<Utc>, i64),
|
||||
}
|
||||
|
||||
impl CompareValues {
|
||||
@ -101,7 +101,11 @@ impl CompareValues {
|
||||
use std::time::Duration;
|
||||
|
||||
// Create the datetime we're comparing against, as duration is an offset from now
|
||||
let right: DateTime<Utc> = (SystemTime::now() - Duration::from_secs(*right)).into();
|
||||
let right: DateTime<Utc> = if *right < 0 {
|
||||
(SystemTime::now() + Duration::from_secs((*right * -1) as u64)).into()
|
||||
} else {
|
||||
(SystemTime::now() - Duration::from_secs(*right as u64)).into()
|
||||
};
|
||||
right.cmp(left)
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ pub enum InlineShape {
|
||||
Pattern(String),
|
||||
Boolean(bool),
|
||||
Date(DateTime<Utc>),
|
||||
Duration(u64),
|
||||
Duration(i64),
|
||||
Path(PathBuf),
|
||||
Binary,
|
||||
|
||||
|
@ -3,7 +3,8 @@ use crate::data::base::shape::{Column, InlineShape};
|
||||
use crate::data::primitive::style_primitive;
|
||||
use chrono::DateTime;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::CompareOperator;
|
||||
use nu_protocol::hir::Operator;
|
||||
use nu_protocol::ShellTypeName;
|
||||
use nu_protocol::{Primitive, Type, UntaggedValue};
|
||||
use nu_source::{DebugDocBuilder, PrettyDebug, Tagged};
|
||||
|
||||
@ -21,8 +22,91 @@ pub fn date_from_str(s: Tagged<&str>) -> Result<UntaggedValue, ShellError> {
|
||||
Ok(UntaggedValue::Primitive(Primitive::Date(date)))
|
||||
}
|
||||
|
||||
pub fn compute_values(
|
||||
operator: Operator,
|
||||
left: &UntaggedValue,
|
||||
right: &UntaggedValue,
|
||||
) -> Result<UntaggedValue, (&'static str, &'static str)> {
|
||||
match (left, right) {
|
||||
(UntaggedValue::Primitive(lhs), UntaggedValue::Primitive(rhs)) => match (lhs, rhs) {
|
||||
(Primitive::Bytes(x), Primitive::Bytes(y)) => {
|
||||
let result = match operator {
|
||||
Operator::Plus => Ok(x + y),
|
||||
Operator::Minus => Ok(x - y),
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}?;
|
||||
Ok(UntaggedValue::Primitive(Primitive::Bytes(result)))
|
||||
}
|
||||
(Primitive::Int(x), Primitive::Int(y)) => match operator {
|
||||
Operator::Plus => Ok(UntaggedValue::Primitive(Primitive::Int(x + y))),
|
||||
Operator::Minus => Ok(UntaggedValue::Primitive(Primitive::Int(x - y))),
|
||||
Operator::Multiply => Ok(UntaggedValue::Primitive(Primitive::Int(x * y))),
|
||||
Operator::Divide => {
|
||||
if x - (y * (x / y)) == num_bigint::BigInt::from(0) {
|
||||
Ok(UntaggedValue::Primitive(Primitive::Int(x / y)))
|
||||
} else {
|
||||
Ok(UntaggedValue::Primitive(Primitive::Decimal(
|
||||
bigdecimal::BigDecimal::from(x.clone())
|
||||
/ bigdecimal::BigDecimal::from(y.clone()),
|
||||
)))
|
||||
}
|
||||
}
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
},
|
||||
(Primitive::Decimal(x), Primitive::Int(y)) => {
|
||||
let result = match operator {
|
||||
Operator::Plus => Ok(x + bigdecimal::BigDecimal::from(y.clone())),
|
||||
Operator::Minus => Ok(x - bigdecimal::BigDecimal::from(y.clone())),
|
||||
Operator::Multiply => Ok(x * bigdecimal::BigDecimal::from(y.clone())),
|
||||
Operator::Divide => Ok(x / bigdecimal::BigDecimal::from(y.clone())),
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}?;
|
||||
Ok(UntaggedValue::Primitive(Primitive::Decimal(result)))
|
||||
}
|
||||
(Primitive::Int(x), Primitive::Decimal(y)) => {
|
||||
let result = match operator {
|
||||
Operator::Plus => Ok(bigdecimal::BigDecimal::from(x.clone()) + y),
|
||||
Operator::Minus => Ok(bigdecimal::BigDecimal::from(x.clone()) - y),
|
||||
Operator::Multiply => Ok(bigdecimal::BigDecimal::from(x.clone()) * y),
|
||||
Operator::Divide => Ok(bigdecimal::BigDecimal::from(x.clone()) / y),
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}?;
|
||||
Ok(UntaggedValue::Primitive(Primitive::Decimal(result)))
|
||||
}
|
||||
(Primitive::Decimal(x), Primitive::Decimal(y)) => {
|
||||
let result = match operator {
|
||||
Operator::Plus => Ok(x + y),
|
||||
Operator::Minus => Ok(x - y),
|
||||
Operator::Multiply => Ok(x * y),
|
||||
Operator::Divide => Ok(x / y),
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}?;
|
||||
Ok(UntaggedValue::Primitive(Primitive::Decimal(result)))
|
||||
}
|
||||
(Primitive::Date(x), Primitive::Date(y)) => {
|
||||
let result = match operator {
|
||||
Operator::Minus => Ok(x.signed_duration_since(*y).num_seconds()),
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}?;
|
||||
Ok(UntaggedValue::Primitive(Primitive::Duration(result)))
|
||||
}
|
||||
(Primitive::Date(x), Primitive::Duration(y)) => {
|
||||
let result = match operator {
|
||||
Operator::Plus => Ok(x
|
||||
.checked_add_signed(chrono::Duration::seconds(*y as i64))
|
||||
.expect("Overflowing add of duration")),
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}?;
|
||||
Ok(UntaggedValue::Primitive(Primitive::Date(result)))
|
||||
}
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
},
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compare_values(
|
||||
operator: CompareOperator,
|
||||
operator: Operator,
|
||||
left: &UntaggedValue,
|
||||
right: &UntaggedValue,
|
||||
) -> Result<bool, (&'static str, &'static str)> {
|
||||
@ -34,15 +118,16 @@ pub fn compare_values(
|
||||
use std::cmp::Ordering;
|
||||
|
||||
let result = match (operator, ordering) {
|
||||
(CompareOperator::Equal, Ordering::Equal) => true,
|
||||
(CompareOperator::NotEqual, Ordering::Less)
|
||||
| (CompareOperator::NotEqual, Ordering::Greater) => true,
|
||||
(CompareOperator::LessThan, Ordering::Less) => true,
|
||||
(CompareOperator::GreaterThan, Ordering::Greater) => true,
|
||||
(CompareOperator::GreaterThanOrEqual, Ordering::Greater)
|
||||
| (CompareOperator::GreaterThanOrEqual, Ordering::Equal) => true,
|
||||
(CompareOperator::LessThanOrEqual, Ordering::Less)
|
||||
| (CompareOperator::LessThanOrEqual, Ordering::Equal) => true,
|
||||
(Operator::Equal, Ordering::Equal) => true,
|
||||
(Operator::NotEqual, Ordering::Less) | (Operator::NotEqual, Ordering::Greater) => {
|
||||
true
|
||||
}
|
||||
(Operator::LessThan, Ordering::Less) => true,
|
||||
(Operator::GreaterThan, Ordering::Greater) => true,
|
||||
(Operator::GreaterThanOrEqual, Ordering::Greater)
|
||||
| (Operator::GreaterThanOrEqual, Ordering::Equal) => true,
|
||||
(Operator::LessThanOrEqual, Ordering::Less)
|
||||
| (Operator::LessThanOrEqual, Ordering::Equal) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
@ -31,6 +31,7 @@ pub(crate) fn evaluate_baseline_expr(
|
||||
Expression::Command(_) => evaluate_command(tag, scope),
|
||||
Expression::ExternalCommand(external) => evaluate_external(external, scope),
|
||||
Expression::Binary(binary) => {
|
||||
// TODO: If we want to add short-circuiting, we'll need to move these down
|
||||
let left = evaluate_baseline_expr(&binary.left, registry, scope)?;
|
||||
let right = evaluate_baseline_expr(&binary.right, registry, scope)?;
|
||||
|
||||
@ -103,7 +104,7 @@ pub(crate) fn evaluate_baseline_expr(
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", possible_matches[0].1),
|
||||
&tag,
|
||||
&member.span,
|
||||
));
|
||||
} else {
|
||||
return Err(err);
|
||||
|
@ -1,30 +1,43 @@
|
||||
use crate::data::value;
|
||||
use nu_protocol::hir::CompareOperator;
|
||||
use nu_protocol::hir::Operator;
|
||||
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
|
||||
use std::ops::Not;
|
||||
|
||||
pub fn apply_operator(
|
||||
op: CompareOperator,
|
||||
op: Operator,
|
||||
left: &Value,
|
||||
right: &Value,
|
||||
) -> Result<UntaggedValue, (&'static str, &'static str)> {
|
||||
match op {
|
||||
CompareOperator::Equal
|
||||
| CompareOperator::NotEqual
|
||||
| CompareOperator::LessThan
|
||||
| CompareOperator::GreaterThan
|
||||
| CompareOperator::LessThanOrEqual
|
||||
| CompareOperator::GreaterThanOrEqual => {
|
||||
Operator::Equal
|
||||
| Operator::NotEqual
|
||||
| Operator::LessThan
|
||||
| Operator::GreaterThan
|
||||
| Operator::LessThanOrEqual
|
||||
| Operator::GreaterThanOrEqual => {
|
||||
value::compare_values(op, left, right).map(UntaggedValue::boolean)
|
||||
}
|
||||
CompareOperator::Contains => contains(left, right).map(UntaggedValue::boolean),
|
||||
CompareOperator::NotContains => contains(left, right)
|
||||
Operator::Contains => string_contains(left, right).map(UntaggedValue::boolean),
|
||||
Operator::NotContains => string_contains(left, right)
|
||||
.map(Not::not)
|
||||
.map(UntaggedValue::boolean),
|
||||
Operator::Plus => value::compute_values(op, left, right),
|
||||
Operator::Minus => value::compute_values(op, left, right),
|
||||
Operator::Multiply => value::compute_values(op, left, right),
|
||||
Operator::Divide => value::compute_values(op, left, right),
|
||||
Operator::In => table_contains(left, right).map(UntaggedValue::boolean),
|
||||
Operator::And => match (left.as_bool(), right.as_bool()) {
|
||||
(Ok(left), Ok(right)) => Ok(UntaggedValue::boolean(left && right)),
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
},
|
||||
Operator::Or => match (left.as_bool(), right.as_bool()) {
|
||||
(Ok(left), Ok(right)) => Ok(UntaggedValue::boolean(left || right)),
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn contains(
|
||||
fn string_contains(
|
||||
left: &UntaggedValue,
|
||||
right: &UntaggedValue,
|
||||
) -> Result<bool, (&'static str, &'static str)> {
|
||||
@ -48,3 +61,14 @@ fn contains(
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}
|
||||
}
|
||||
|
||||
fn table_contains(
|
||||
left: &UntaggedValue,
|
||||
right: &UntaggedValue,
|
||||
) -> Result<bool, (&'static str, &'static str)> {
|
||||
let left = left.clone();
|
||||
match right {
|
||||
UntaggedValue::Table(values) => Ok(values.iter().any(|x| x.value == left)),
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ impl Painter {
|
||||
FlatShape::ItVariable | FlatShape::Keyword => Color::Purple.bold(),
|
||||
FlatShape::Variable | FlatShape::Identifier => Color::Purple.normal(),
|
||||
FlatShape::Type => Color::Blue.bold(),
|
||||
FlatShape::CompareOperator => Color::Yellow.normal(),
|
||||
FlatShape::Operator => Color::Yellow.normal(),
|
||||
FlatShape::DotDot => Color::Yellow.bold(),
|
||||
FlatShape::Dot => Style::new().fg(Color::White),
|
||||
FlatShape::InternalCommand => Color::Cyan.bold(),
|
||||
|
@ -2,7 +2,7 @@ use crate::data::value::compare_values;
|
||||
use crate::data::TaggedListBuilder;
|
||||
use chrono::{DateTime, NaiveDate, Utc};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::CompareOperator;
|
||||
use nu_protocol::hir::Operator;
|
||||
use nu_protocol::{Primitive, TaggedDictBuilder, UntaggedValue, Value};
|
||||
use nu_source::{SpannedItem, Tag, Tagged, TaggedItem};
|
||||
use nu_value_ext::{get_data_by_key, ValueExt};
|
||||
@ -317,7 +317,7 @@ pub fn map_max(
|
||||
let right = &acc.value;
|
||||
|
||||
if let Ok(is_greater_than) =
|
||||
compare_values(CompareOperator::GreaterThan, left, right)
|
||||
compare_values(Operator::GreaterThan, left, right)
|
||||
{
|
||||
if is_greater_than {
|
||||
value.clone()
|
||||
@ -336,9 +336,7 @@ pub fn map_max(
|
||||
let left = &value.value;
|
||||
let right = &max.value;
|
||||
|
||||
if let Ok(is_greater_than) =
|
||||
compare_values(CompareOperator::GreaterThan, left, right)
|
||||
{
|
||||
if let Ok(is_greater_than) = compare_values(Operator::GreaterThan, left, right) {
|
||||
if is_greater_than {
|
||||
value
|
||||
} else {
|
||||
|
Reference in New Issue
Block a user