mirror of
https://github.com/nushell/nushell.git
synced 2024-11-25 18:03:51 +01:00
Split unit into duration and filesize (#3453)
This commit is contained in:
parent
4fdbf30308
commit
28388b4e3a
@ -81,29 +81,29 @@ lazy_static! {
|
||||
static ref MULT_DIV_LOOKUP_TABLE: HashMap<(Operator, BinarySide, SyntaxShape), Vec<SyntaxShape>> = {
|
||||
vec![
|
||||
((Operator::Divide, BinarySide::Left, SyntaxShape::Number), // expr => possible var shapes
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var / number => Unit, Int, Number
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Number, SyntaxShape::Int]), //$var / number => Unit, Int, Number
|
||||
((Operator::Divide, BinarySide::Left, SyntaxShape::Int),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var / int => Unit, Int, Number
|
||||
((Operator::Divide, BinarySide::Left, SyntaxShape::Unit),
|
||||
vec![SyntaxShape::Unit]), //$var / unit => Unit
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Number, SyntaxShape::Int]), //$var / int => Unit, Int, Number
|
||||
((Operator::Divide, BinarySide::Left, SyntaxShape::Filesize),
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Duration, SyntaxShape::Filesize]), //$var / unit => Unit
|
||||
((Operator::Divide, BinarySide::Right, SyntaxShape::Number),
|
||||
vec![SyntaxShape::Number, SyntaxShape::Int]), //number / $var => Int, Number
|
||||
((Operator::Divide, BinarySide::Right, SyntaxShape::Int),
|
||||
vec![SyntaxShape::Number, SyntaxShape::Int]), //int / $var => Int, Number
|
||||
((Operator::Divide, BinarySide::Right, SyntaxShape::Unit),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //unit / $var => unit, int, number
|
||||
((Operator::Divide, BinarySide::Right, SyntaxShape::Filesize),
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //unit / $var => unit, int, number
|
||||
|
||||
((Operator::Multiply, BinarySide::Left, SyntaxShape::Number),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var * number => Unit, Int, Number
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //$var * number => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Left, SyntaxShape::Int),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //$var * int => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Left, SyntaxShape::Unit),
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //$var * int => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Left, SyntaxShape::Filesize),
|
||||
vec![SyntaxShape::Int, SyntaxShape::Number]), //$var * unit => int, number //TODO this changes as soon as more complex units arrive
|
||||
((Operator::Multiply, BinarySide::Right, SyntaxShape::Number),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //number * $var => Unit, Int, Number
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //number * $var => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Right, SyntaxShape::Int),
|
||||
vec![SyntaxShape::Unit, SyntaxShape::Number, SyntaxShape::Int]), //int * $var => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Right, SyntaxShape::Unit),
|
||||
vec![SyntaxShape::Filesize, SyntaxShape::Number, SyntaxShape::Int]), //int * $var => Unit, Int, Number
|
||||
((Operator::Multiply, BinarySide::Right, SyntaxShape::Filesize),
|
||||
vec![SyntaxShape::Int, SyntaxShape::Number]), //unit * $var => int, number //TODO this changes as soon as more complex units arrive
|
||||
].into_iter().collect()
|
||||
};
|
||||
@ -241,8 +241,8 @@ fn get_result_shape_of(
|
||||
l_shape
|
||||
}
|
||||
Operator::Multiply => {
|
||||
if l_shape == SyntaxShape::Unit || r_shape == SyntaxShape::Unit {
|
||||
SyntaxShape::Unit
|
||||
if l_shape == SyntaxShape::Duration || r_shape == SyntaxShape::Duration {
|
||||
SyntaxShape::Duration
|
||||
} else {
|
||||
SyntaxShape::Number
|
||||
}
|
||||
@ -250,7 +250,7 @@ fn get_result_shape_of(
|
||||
Operator::Divide => {
|
||||
if l_shape == r_shape {
|
||||
SyntaxShape::Number
|
||||
} else if l_shape == SyntaxShape::Unit {
|
||||
} else if l_shape == SyntaxShape::Duration {
|
||||
l_shape
|
||||
} else {
|
||||
SyntaxShape::Number
|
||||
@ -277,7 +277,7 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
|
||||
nu_protocol::hir::Number::Int(_) => Some(SyntaxShape::Int),
|
||||
nu_protocol::hir::Number::Decimal(_) => Some(SyntaxShape::Number),
|
||||
},
|
||||
nu_protocol::hir::Literal::Size(_, _) => Some(SyntaxShape::Unit),
|
||||
nu_protocol::hir::Literal::Size(_, _) => Some(SyntaxShape::Duration),
|
||||
nu_protocol::hir::Literal::String(_) => Some(SyntaxShape::String),
|
||||
//Rest should have failed at parsing stage?
|
||||
nu_protocol::hir::Literal::GlobPattern(_) => Some(SyntaxShape::String),
|
||||
@ -843,12 +843,21 @@ impl VarSyntaxShapeDeductor {
|
||||
),
|
||||
)?;
|
||||
}
|
||||
SyntaxShape::Unit => {
|
||||
SyntaxShape::Duration => {
|
||||
self.checked_insert(
|
||||
var,
|
||||
VarShapeDeduction::from_usage_with_alternatives(
|
||||
&var.span,
|
||||
&[SyntaxShape::Unit],
|
||||
&[SyntaxShape::Duration],
|
||||
),
|
||||
)?;
|
||||
}
|
||||
SyntaxShape::Filesize => {
|
||||
self.checked_insert(
|
||||
var,
|
||||
VarShapeDeduction::from_usage_with_alternatives(
|
||||
&var.span,
|
||||
&[SyntaxShape::Filesize],
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ impl WholeStreamCommand for Sleep {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("sleep")
|
||||
.required("duration", SyntaxShape::Unit, "time to sleep")
|
||||
.rest(SyntaxShape::Unit, "additional time")
|
||||
.required("duration", SyntaxShape::Duration, "time to sleep")
|
||||
.rest(SyntaxShape::Duration, "additional time")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -438,6 +438,15 @@ pub fn compute_values(
|
||||
Err(_) => Err(("Date", "Duration overflow")),
|
||||
}
|
||||
}
|
||||
Operator::Minus => {
|
||||
match Primitive::into_chrono_duration(rhs.clone(), Span::unknown()) {
|
||||
Ok(y) => match x.checked_sub_signed(y) {
|
||||
Some(value) => Ok(value),
|
||||
None => Err(("Date", "Duration and date addition overflow")),
|
||||
},
|
||||
Err(_) => Err(("Date", "Duration overflow")),
|
||||
}
|
||||
}
|
||||
_ => Err((left.type_name(), right.type_name())),
|
||||
}?;
|
||||
Ok(UntaggedValue::Primitive(Primitive::Date(result)))
|
||||
|
@ -331,8 +331,76 @@ fn parse_operator(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<Pars
|
||||
)
|
||||
}
|
||||
|
||||
/// Parse a duration type, eg '10day'
|
||||
fn parse_duration(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
|
||||
fn parse_decimal_str_to_number(decimal: &str) -> Option<i64> {
|
||||
let string_to_parse = format!("0.{}", decimal);
|
||||
if let Ok(x) = string_to_parse.parse::<f64>() {
|
||||
return Some((1_f64 / x) as i64);
|
||||
}
|
||||
None
|
||||
}
|
||||
let unit_groups = [
|
||||
(Unit::Nanosecond, "NS", None),
|
||||
(Unit::Microsecond, "US", Some((Unit::Nanosecond, 1000))),
|
||||
(Unit::Millisecond, "MS", Some((Unit::Microsecond, 1000))),
|
||||
(Unit::Second, "SEC", Some((Unit::Millisecond, 1000))),
|
||||
(Unit::Minute, "MIN", Some((Unit::Second, 60))),
|
||||
(Unit::Hour, "HR", Some((Unit::Minute, 60))),
|
||||
(Unit::Day, "DAY", Some((Unit::Minute, 1440))),
|
||||
(Unit::Week, "WK", Some((Unit::Day, 7))),
|
||||
];
|
||||
if let Some(unit) = unit_groups
|
||||
.iter()
|
||||
.find(|&x| lite_arg.to_uppercase().ends_with(x.1))
|
||||
{
|
||||
let mut lhs = lite_arg.item.clone();
|
||||
for _ in 0..unit.1.len() {
|
||||
lhs.pop();
|
||||
}
|
||||
|
||||
let input: Vec<&str> = lhs.split('.').collect();
|
||||
let (value, unit_to_use) = match &input[..] {
|
||||
[number_str] => (number_str.parse::<i64>().ok(), unit.0),
|
||||
[number_str, decimal_part_str] => match unit.2 {
|
||||
Some(unit_to_convert_to) => match (
|
||||
number_str.parse::<i64>(),
|
||||
parse_decimal_str_to_number(decimal_part_str),
|
||||
) {
|
||||
(Ok(number), Some(decimal_part)) => (
|
||||
Some(
|
||||
(number * unit_to_convert_to.1) + (unit_to_convert_to.1 / decimal_part),
|
||||
),
|
||||
unit_to_convert_to.0,
|
||||
),
|
||||
_ => (None, unit.0),
|
||||
},
|
||||
None => (None, unit.0),
|
||||
},
|
||||
_ => (None, unit.0),
|
||||
};
|
||||
|
||||
if let Some(x) = value {
|
||||
let lhs_span = Span::new(lite_arg.span.start(), lite_arg.span.start() + lhs.len());
|
||||
let unit_span = Span::new(lite_arg.span.start() + lhs.len(), lite_arg.span.end());
|
||||
return (
|
||||
SpannedExpression::new(
|
||||
Expression::unit(x.spanned(lhs_span), unit_to_use.spanned(unit_span)),
|
||||
lite_arg.span,
|
||||
),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
garbage(lite_arg.span),
|
||||
Some(ParseError::mismatch("duration", lite_arg.clone())),
|
||||
)
|
||||
}
|
||||
|
||||
/// Parse a unit type, eg '10kb'
|
||||
fn parse_unit(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
|
||||
fn parse_filesize(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
|
||||
fn parse_decimal_str_to_number(decimal: &str) -> Option<i64> {
|
||||
let string_to_parse = format!("0.{}", decimal);
|
||||
if let Ok(x) = string_to_parse.parse::<f64>() {
|
||||
@ -352,14 +420,6 @@ fn parse_unit(lite_arg: &Spanned<String>) -> (SpannedExpression, Option<ParseErr
|
||||
(Unit::Tebibyte, "TIB", Some((Unit::Gibibyte, 1024))),
|
||||
(Unit::Pebibyte, "PIB", Some((Unit::Tebibyte, 1024))),
|
||||
(Unit::Byte, "B", None),
|
||||
(Unit::Nanosecond, "NS", None),
|
||||
(Unit::Microsecond, "US", Some((Unit::Nanosecond, 1000))),
|
||||
(Unit::Millisecond, "MS", Some((Unit::Microsecond, 1000))),
|
||||
(Unit::Second, "SEC", Some((Unit::Millisecond, 1000))),
|
||||
(Unit::Minute, "MIN", Some((Unit::Second, 60))),
|
||||
(Unit::Hour, "HR", Some((Unit::Minute, 60))),
|
||||
(Unit::Day, "DAY", Some((Unit::Minute, 1440))),
|
||||
(Unit::Week, "WK", Some((Unit::Day, 7))),
|
||||
];
|
||||
if let Some(unit) = unit_groups
|
||||
.iter()
|
||||
@ -467,7 +527,11 @@ fn parse_dollar_expr(
|
||||
scope: &dyn ParserScope,
|
||||
) -> (SpannedExpression, Option<ParseError>) {
|
||||
trace!("Parsing dollar expression: {:?}", lite_arg.item);
|
||||
if lite_arg.item.starts_with("$\"") && lite_arg.item.len() > 1 && lite_arg.item.ends_with('"') {
|
||||
if (lite_arg.item.starts_with("$\"") && lite_arg.item.len() > 1 && lite_arg.item.ends_with('"'))
|
||||
|| (lite_arg.item.starts_with("$'")
|
||||
&& lite_arg.item.len() > 1
|
||||
&& lite_arg.item.ends_with('\''))
|
||||
{
|
||||
// This is an interpolated string
|
||||
parse_interpolated_string(&lite_arg, scope)
|
||||
} else if let (expr, None) = parse_range(lite_arg, scope) {
|
||||
@ -844,7 +908,8 @@ fn parse_arg(
|
||||
|
||||
SyntaxShape::Range => parse_range(&lite_arg, scope),
|
||||
SyntaxShape::Operator => parse_operator(&lite_arg),
|
||||
SyntaxShape::Unit => parse_unit(&lite_arg),
|
||||
SyntaxShape::Filesize => parse_filesize(&lite_arg),
|
||||
SyntaxShape::Duration => parse_duration(&lite_arg),
|
||||
SyntaxShape::FilePath => {
|
||||
let trimmed = trim_quotes(&lite_arg.item);
|
||||
let expanded = expand_path(&trimmed).to_string();
|
||||
@ -861,7 +926,8 @@ fn parse_arg(
|
||||
SyntaxShape::Int,
|
||||
SyntaxShape::Number,
|
||||
SyntaxShape::Range,
|
||||
SyntaxShape::Unit,
|
||||
SyntaxShape::Filesize,
|
||||
SyntaxShape::Duration,
|
||||
SyntaxShape::Block,
|
||||
SyntaxShape::Table,
|
||||
SyntaxShape::String,
|
||||
@ -2164,7 +2230,7 @@ fn unit_parse_byte_units() {
|
||||
let input_len = case.string.len();
|
||||
let value_len = case.value.to_string().len();
|
||||
let input = case.string.clone().spanned(Span::new(0, input_len));
|
||||
let result = parse_unit(&input);
|
||||
let result = parse_filesize(&input);
|
||||
assert_eq!(result.1, None);
|
||||
assert_eq!(
|
||||
result.0.expr,
|
||||
@ -2252,7 +2318,7 @@ fn unit_parse_byte_units_decimal() {
|
||||
let input_len = case.string.len();
|
||||
let value_len = case.value_str.to_string().len();
|
||||
let input = case.string.clone().spanned(Span::new(0, input_len));
|
||||
let result = parse_unit(&input);
|
||||
let result = parse_filesize(&input);
|
||||
assert_eq!(result.1, None);
|
||||
assert_eq!(
|
||||
result.0.expr,
|
||||
|
@ -166,7 +166,8 @@ pub fn parse_type_token(type_: &Token) -> (SyntaxShape, Option<ParseError>) {
|
||||
"string" => (SyntaxShape::String, None),
|
||||
"path" => (SyntaxShape::FilePath, None),
|
||||
"table" => (SyntaxShape::Table, None),
|
||||
"unit" => (SyntaxShape::Unit, None),
|
||||
"duration" => (SyntaxShape::Duration, None),
|
||||
"filesize" => (SyntaxShape::Filesize, None),
|
||||
"number" => (SyntaxShape::Number, None),
|
||||
"pattern" => (SyntaxShape::GlobPattern, None),
|
||||
"range" => (SyntaxShape::Range, None),
|
||||
|
@ -26,8 +26,10 @@ pub enum SyntaxShape {
|
||||
Block,
|
||||
/// A table is allowed, eg `[first second]`
|
||||
Table,
|
||||
/// A unit value is allowed, eg `10kb`
|
||||
Unit,
|
||||
/// A filesize value is allowed, eg `10kb`
|
||||
Filesize,
|
||||
/// A duration value is allowed, eg `19day`
|
||||
Duration,
|
||||
/// An operator
|
||||
Operator,
|
||||
/// A math expression which expands shorthand forms on the lefthand side, eg `foo > 1`
|
||||
@ -52,7 +54,8 @@ impl PrettyDebug for SyntaxShape {
|
||||
SyntaxShape::GlobPattern => "pattern",
|
||||
SyntaxShape::Block => "block",
|
||||
SyntaxShape::Table => "table",
|
||||
SyntaxShape::Unit => "unit",
|
||||
SyntaxShape::Duration => "duration",
|
||||
SyntaxShape::Filesize => "filesize",
|
||||
SyntaxShape::Operator => "operator",
|
||||
SyntaxShape::RowCondition => "condition",
|
||||
SyntaxShape::MathExpression => "math expression",
|
||||
|
Loading…
Reference in New Issue
Block a user