mirror of
https://github.com/nushell/nushell.git
synced 2025-03-29 00:56:45 +01:00
Rework operator type errors (#14429)
# Description This PR adds two new `ParseError` and `ShellError` cases for type errors relating to operators. - `OperatorUnsupportedType` is used when a type is not supported by an operator in any way, shape, or form. E.g., `+` does not support `bool`. - `OperatorIncompatibleTypes` is used when a operator is used with types it supports, but the combination of types provided cannot be used together. E.g., `filesize + duration` is not a valid combination. The other preexisting error cases related to operators have been removed and replaced with the new ones above. Namely: - `ShellError::OperatorMismatch` - `ShellError::UnsupportedOperator` - `ParseError::UnsupportedOperationLHS` - `ParseError::UnsupportedOperationRHS` - `ParseError::UnsupportedOperationTernary` # User-Facing Changes - `help operators` now lists the precedence of `not` as 55 instead of 0 (above the other boolean operators). Fixes #13675. - `math median` and `math mode` now ignore NaN values so that `[NaN NaN] | math median` and `[NaN NaN] | math mode` no longer trigger a type error. Instead, it's now an empty input error. Fixing this in earnest can be left for a future PR. - Comparisons with `nan` now return false instead of causing an error. E.g., `1 == nan` is now `false`. - All the operator type errors have been standardized and reworked. In particular, they can now have a help message, which is currently used for types errors relating to `++`. ```nu [1] ++ 2 ``` ``` Error: nu::parser::operator_unsupported_type × The '++' operator does not work on values of type 'int'. ╭─[entry #1:1:5] 1 │ [1] ++ 2 · ─┬ ┬ · │ ╰── int · ╰── does not support 'int' ╰──── help: if you meant to append a value to a list or a record to a table, use the `append` command or wrap the value in a list. For example: `$list ++ $value` should be `$list ++ [$value]` or `$list | append $value`. ```
This commit is contained in:
parent
2e1b6acc0e
commit
62e56d3581
@ -240,7 +240,9 @@ fn test_computable_style_closure_errors() {
|
||||
];
|
||||
let actual_repl = nu!(nu_repl_code(&inp));
|
||||
// Check that the error was printed
|
||||
assert!(actual_repl.err.contains("type mismatch for operator"));
|
||||
assert!(actual_repl
|
||||
.err
|
||||
.contains("nu::shell::operator_incompatible_types"));
|
||||
// Check that the value was printed
|
||||
assert!(actual_repl.out.contains("bell"));
|
||||
}
|
||||
|
@ -30,11 +30,11 @@ impl Command for HelpOperators {
|
||||
let head = call.head;
|
||||
let mut operators = [
|
||||
Operator::Assignment(Assignment::Assign),
|
||||
Operator::Assignment(Assignment::PlusAssign),
|
||||
Operator::Assignment(Assignment::ConcatAssign),
|
||||
Operator::Assignment(Assignment::MinusAssign),
|
||||
Operator::Assignment(Assignment::AddAssign),
|
||||
Operator::Assignment(Assignment::SubtractAssign),
|
||||
Operator::Assignment(Assignment::MultiplyAssign),
|
||||
Operator::Assignment(Assignment::DivideAssign),
|
||||
Operator::Assignment(Assignment::ConcatenateAssign),
|
||||
Operator::Comparison(Comparison::Equal),
|
||||
Operator::Comparison(Comparison::NotEqual),
|
||||
Operator::Comparison(Comparison::LessThan),
|
||||
@ -49,22 +49,22 @@ impl Command for HelpOperators {
|
||||
Operator::Comparison(Comparison::NotHas),
|
||||
Operator::Comparison(Comparison::StartsWith),
|
||||
Operator::Comparison(Comparison::EndsWith),
|
||||
Operator::Math(Math::Plus),
|
||||
Operator::Math(Math::Concat),
|
||||
Operator::Math(Math::Minus),
|
||||
Operator::Math(Math::Add),
|
||||
Operator::Math(Math::Subtract),
|
||||
Operator::Math(Math::Multiply),
|
||||
Operator::Math(Math::Divide),
|
||||
Operator::Math(Math::FloorDivide),
|
||||
Operator::Math(Math::Modulo),
|
||||
Operator::Math(Math::FloorDivision),
|
||||
Operator::Math(Math::Pow),
|
||||
Operator::Math(Math::Concatenate),
|
||||
Operator::Bits(Bits::BitOr),
|
||||
Operator::Bits(Bits::BitXor),
|
||||
Operator::Bits(Bits::BitAnd),
|
||||
Operator::Bits(Bits::ShiftLeft),
|
||||
Operator::Bits(Bits::ShiftRight),
|
||||
Operator::Boolean(Boolean::And),
|
||||
Operator::Boolean(Boolean::Or),
|
||||
Operator::Boolean(Boolean::Xor),
|
||||
Operator::Boolean(Boolean::And),
|
||||
]
|
||||
.into_iter()
|
||||
.map(|op| {
|
||||
@ -87,7 +87,7 @@ impl Command for HelpOperators {
|
||||
"operator" => Value::string("not", head),
|
||||
"name" => Value::string("Not", head),
|
||||
"description" => Value::string("Negates a value or expression.", head),
|
||||
"precedence" => Value::int(0, head),
|
||||
"precedence" => Value::int(55, head),
|
||||
},
|
||||
head,
|
||||
));
|
||||
@ -151,32 +151,32 @@ fn description(operator: &Operator) -> &'static str {
|
||||
}
|
||||
Operator::Comparison(Comparison::StartsWith) => "Checks if a string starts with another.",
|
||||
Operator::Comparison(Comparison::EndsWith) => "Checks if a string ends with another.",
|
||||
Operator::Math(Math::Plus) => "Adds two values.",
|
||||
Operator::Math(Math::Concat) => {
|
||||
"Concatenates two lists, two strings, or two binary values."
|
||||
}
|
||||
Operator::Math(Math::Minus) => "Subtracts two values.",
|
||||
Operator::Math(Math::Add) => "Adds two values.",
|
||||
Operator::Math(Math::Subtract) => "Subtracts two values.",
|
||||
Operator::Math(Math::Multiply) => "Multiplies two values.",
|
||||
Operator::Math(Math::Divide) => "Divides two values.",
|
||||
Operator::Math(Math::FloorDivide) => "Divides two values and floors the result.",
|
||||
Operator::Math(Math::Modulo) => "Divides two values and returns the remainder.",
|
||||
Operator::Math(Math::FloorDivision) => "Divides two values and floors the result.",
|
||||
Operator::Math(Math::Pow) => "Raises one value to the power of another.",
|
||||
Operator::Boolean(Boolean::And) => "Checks if both values are true.",
|
||||
Operator::Math(Math::Concatenate) => {
|
||||
"Concatenates two lists, two strings, or two binary values."
|
||||
}
|
||||
Operator::Boolean(Boolean::Or) => "Checks if either value is true.",
|
||||
Operator::Boolean(Boolean::Xor) => "Checks if one value is true and the other is false.",
|
||||
Operator::Boolean(Boolean::And) => "Checks if both values are true.",
|
||||
Operator::Bits(Bits::BitOr) => "Performs a bitwise OR on two values.",
|
||||
Operator::Bits(Bits::BitXor) => "Performs a bitwise XOR on two values.",
|
||||
Operator::Bits(Bits::BitAnd) => "Performs a bitwise AND on two values.",
|
||||
Operator::Bits(Bits::ShiftLeft) => "Bitwise shifts a value left by another.",
|
||||
Operator::Bits(Bits::ShiftRight) => "Bitwise shifts a value right by another.",
|
||||
Operator::Assignment(Assignment::Assign) => "Assigns a value to a variable.",
|
||||
Operator::Assignment(Assignment::PlusAssign) => "Adds a value to a variable.",
|
||||
Operator::Assignment(Assignment::ConcatAssign) => {
|
||||
"Concatenates two lists, two strings, or two binary values."
|
||||
}
|
||||
Operator::Assignment(Assignment::MinusAssign) => "Subtracts a value from a variable.",
|
||||
Operator::Assignment(Assignment::AddAssign) => "Adds a value to a variable.",
|
||||
Operator::Assignment(Assignment::SubtractAssign) => "Subtracts a value from a variable.",
|
||||
Operator::Assignment(Assignment::MultiplyAssign) => "Multiplies a variable by a value.",
|
||||
Operator::Assignment(Assignment::DivideAssign) => "Divides a variable by a value.",
|
||||
Operator::Assignment(Assignment::ConcatenateAssign) => {
|
||||
"Concatenates a list, a string, or a binary value to a variable of the same type."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,45 +91,26 @@ pub fn median(values: &[Value], span: Span, head: Span) -> Result<Value, ShellEr
|
||||
Pick::Median
|
||||
};
|
||||
|
||||
let mut sorted = vec![];
|
||||
|
||||
for item in values {
|
||||
sorted.push(item.clone());
|
||||
}
|
||||
|
||||
if let Some(Err(values)) = values
|
||||
.windows(2)
|
||||
.map(|elem| {
|
||||
if elem[0].partial_cmp(&elem[1]).is_none() {
|
||||
return Err(ShellError::OperatorMismatch {
|
||||
op_span: head,
|
||||
lhs_ty: elem[0].get_type().to_string(),
|
||||
lhs_span: elem[0].span(),
|
||||
rhs_ty: elem[1].get_type().to_string(),
|
||||
rhs_span: elem[1].span(),
|
||||
});
|
||||
}
|
||||
Ok(elem[0].partial_cmp(&elem[1]).unwrap_or(Ordering::Equal))
|
||||
})
|
||||
.find(|elem| elem.is_err())
|
||||
{
|
||||
return Err(values);
|
||||
}
|
||||
let mut sorted = values
|
||||
.iter()
|
||||
.filter(|x| !x.as_float().is_ok_and(f64::is_nan))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
|
||||
|
||||
match take {
|
||||
Pick::Median => {
|
||||
let idx = (values.len() as f64 / 2.0).floor() as usize;
|
||||
let out = sorted
|
||||
Ok(sorted
|
||||
.get(idx)
|
||||
.ok_or_else(|| ShellError::UnsupportedInput {
|
||||
msg: "Empty input".to_string(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: head,
|
||||
input_span: span,
|
||||
})?;
|
||||
Ok(out.clone())
|
||||
})?
|
||||
.to_owned()
|
||||
.to_owned())
|
||||
}
|
||||
Pick::MedianAverage => {
|
||||
let idx_end = values.len() / 2;
|
||||
@ -143,7 +124,8 @@ pub fn median(values: &[Value], span: Span, head: Span) -> Result<Value, ShellEr
|
||||
msg_span: head,
|
||||
input_span: span,
|
||||
})?
|
||||
.clone();
|
||||
.to_owned()
|
||||
.to_owned();
|
||||
|
||||
let right = sorted
|
||||
.get(idx_end)
|
||||
@ -153,7 +135,8 @@ pub fn median(values: &[Value], span: Span, head: Span) -> Result<Value, ShellEr
|
||||
msg_span: head,
|
||||
input_span: span,
|
||||
})?
|
||||
.clone();
|
||||
.to_owned()
|
||||
.to_owned();
|
||||
|
||||
average(&[left, right], span, head)
|
||||
}
|
||||
|
@ -111,29 +111,12 @@ impl Command for SubCommand {
|
||||
}
|
||||
|
||||
pub fn mode(values: &[Value], _span: Span, head: Span) -> Result<Value, ShellError> {
|
||||
if let Some(Err(values)) = values
|
||||
.windows(2)
|
||||
.map(|elem| {
|
||||
if elem[0].partial_cmp(&elem[1]).is_none() {
|
||||
return Err(ShellError::OperatorMismatch {
|
||||
op_span: head,
|
||||
lhs_ty: elem[0].get_type().to_string(),
|
||||
lhs_span: elem[0].span(),
|
||||
rhs_ty: elem[1].get_type().to_string(),
|
||||
rhs_span: elem[1].span(),
|
||||
});
|
||||
}
|
||||
Ok(elem[0].partial_cmp(&elem[1]).unwrap_or(Ordering::Equal))
|
||||
})
|
||||
.find(|elem| elem.is_err())
|
||||
{
|
||||
return Err(values);
|
||||
}
|
||||
//In e-q, Value doesn't implement Hash or Eq, so we have to get the values inside
|
||||
// But f64 doesn't implement Hash, so we get the binary representation to use as
|
||||
// key in the HashMap
|
||||
let hashable_values = values
|
||||
.iter()
|
||||
.filter(|x| !x.as_float().is_ok_and(f64::is_nan))
|
||||
.map(|val| match val {
|
||||
Value::Int { val, .. } => Ok(HashableType::new(val.to_ne_bytes(), NumberTypes::Int)),
|
||||
Value::Duration { val, .. } => {
|
||||
|
@ -32,18 +32,8 @@ pub fn max(data: Vec<Value>, span: Span, head: Span) -> Result<Value, ShellError
|
||||
.clone();
|
||||
|
||||
for value in &data {
|
||||
if let Some(result) = value.partial_cmp(&biggest) {
|
||||
if result == Ordering::Greater {
|
||||
biggest = value.clone();
|
||||
}
|
||||
} else {
|
||||
return Err(ShellError::OperatorMismatch {
|
||||
op_span: head,
|
||||
lhs_ty: biggest.get_type().to_string(),
|
||||
lhs_span: biggest.span(),
|
||||
rhs_ty: value.get_type().to_string(),
|
||||
rhs_span: value.span(),
|
||||
});
|
||||
if value.partial_cmp(&biggest) == Some(Ordering::Greater) {
|
||||
biggest = value.clone();
|
||||
}
|
||||
}
|
||||
Ok(biggest)
|
||||
@ -61,18 +51,8 @@ pub fn min(data: Vec<Value>, span: Span, head: Span) -> Result<Value, ShellError
|
||||
.clone();
|
||||
|
||||
for value in &data {
|
||||
if let Some(result) = value.partial_cmp(&smallest) {
|
||||
if result == Ordering::Less {
|
||||
smallest = value.clone();
|
||||
}
|
||||
} else {
|
||||
return Err(ShellError::OperatorMismatch {
|
||||
op_span: head,
|
||||
lhs_ty: smallest.get_type().to_string(),
|
||||
lhs_span: smallest.span(),
|
||||
rhs_ty: value.get_type().to_string(),
|
||||
rhs_span: value.span(),
|
||||
});
|
||||
if value.partial_cmp(&smallest) == Some(Ordering::Less) {
|
||||
smallest = value.clone();
|
||||
}
|
||||
}
|
||||
Ok(smallest)
|
||||
|
@ -62,7 +62,9 @@ fn concat_assign_type_mismatch() {
|
||||
$a ++= 'str'
|
||||
"#);
|
||||
|
||||
assert!(actual.err.contains("nu::parser::unsupported_operation"));
|
||||
assert!(actual
|
||||
.err
|
||||
.contains("nu::parser::operator_incompatible_types"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -72,5 +74,7 @@ fn concat_assign_runtime_type_mismatch() {
|
||||
$a ++= if true { 'str' }
|
||||
"#);
|
||||
|
||||
assert!(actual.err.contains("nu::shell::type_mismatch"));
|
||||
assert!(actual
|
||||
.err
|
||||
.contains("nu::shell::operator_incompatible_types"));
|
||||
}
|
||||
|
@ -107,7 +107,9 @@ fn error_reduce_fold_type_mismatch() {
|
||||
"echo a b c | reduce --fold 0 { |it, acc| $acc + $it }"
|
||||
));
|
||||
|
||||
assert!(actual.err.contains("mismatch"));
|
||||
assert!(actual
|
||||
.err
|
||||
.contains("nu::shell::operator_incompatible_types"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -164,13 +164,13 @@ pub(crate) fn compile_expression(
|
||||
Ok(())
|
||||
}
|
||||
Expr::BinaryOp(lhs, op, rhs) => {
|
||||
if let Expr::Operator(ref operator) = op.expr {
|
||||
if let Expr::Operator(operator) = op.expr {
|
||||
drop_input(builder)?;
|
||||
compile_binary_op(
|
||||
working_set,
|
||||
builder,
|
||||
lhs,
|
||||
operator.clone().into_spanned(op.span),
|
||||
operator.into_spanned(op.span),
|
||||
rhs,
|
||||
expr.span,
|
||||
out_reg,
|
||||
|
@ -150,11 +150,11 @@ pub(crate) fn compile_binary_op(
|
||||
pub(crate) fn decompose_assignment(assignment: Assignment) -> Option<Operator> {
|
||||
match assignment {
|
||||
Assignment::Assign => None,
|
||||
Assignment::PlusAssign => Some(Operator::Math(Math::Plus)),
|
||||
Assignment::ConcatAssign => Some(Operator::Math(Math::Concat)),
|
||||
Assignment::MinusAssign => Some(Operator::Math(Math::Minus)),
|
||||
Assignment::AddAssign => Some(Operator::Math(Math::Add)),
|
||||
Assignment::SubtractAssign => Some(Operator::Math(Math::Subtract)),
|
||||
Assignment::MultiplyAssign => Some(Operator::Math(Math::Multiply)),
|
||||
Assignment::DivideAssign => Some(Operator::Math(Math::Divide)),
|
||||
Assignment::ConcatenateAssign => Some(Operator::Math(Math::Concatenate)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,11 +520,11 @@ impl Eval for EvalRuntime {
|
||||
|
||||
let rhs = match assignment {
|
||||
Assignment::Assign => rhs,
|
||||
Assignment::PlusAssign => {
|
||||
Assignment::AddAssign => {
|
||||
let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
|
||||
lhs.add(op_span, &rhs, op_span)?
|
||||
}
|
||||
Assignment::MinusAssign => {
|
||||
Assignment::SubtractAssign => {
|
||||
let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
|
||||
lhs.sub(op_span, &rhs, op_span)?
|
||||
}
|
||||
@ -536,7 +536,7 @@ impl Eval for EvalRuntime {
|
||||
let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
|
||||
lhs.div(op_span, &rhs, op_span)?
|
||||
}
|
||||
Assignment::ConcatAssign => {
|
||||
Assignment::ConcatenateAssign => {
|
||||
let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
|
||||
lhs.concat(op_span, &rhs, op_span)?
|
||||
}
|
||||
|
@ -970,19 +970,19 @@ fn binary_op(
|
||||
Comparison::EndsWith => lhs_val.ends_with(op_span, &rhs_val, span)?,
|
||||
},
|
||||
Operator::Math(mat) => match mat {
|
||||
Math::Plus => lhs_val.add(op_span, &rhs_val, span)?,
|
||||
Math::Concat => lhs_val.concat(op_span, &rhs_val, span)?,
|
||||
Math::Minus => lhs_val.sub(op_span, &rhs_val, span)?,
|
||||
Math::Add => lhs_val.add(op_span, &rhs_val, span)?,
|
||||
Math::Subtract => lhs_val.sub(op_span, &rhs_val, span)?,
|
||||
Math::Multiply => lhs_val.mul(op_span, &rhs_val, span)?,
|
||||
Math::Divide => lhs_val.div(op_span, &rhs_val, span)?,
|
||||
Math::FloorDivide => lhs_val.floor_div(op_span, &rhs_val, span)?,
|
||||
Math::Modulo => lhs_val.modulo(op_span, &rhs_val, span)?,
|
||||
Math::FloorDivision => lhs_val.floor_div(op_span, &rhs_val, span)?,
|
||||
Math::Pow => lhs_val.pow(op_span, &rhs_val, span)?,
|
||||
Math::Concatenate => lhs_val.concat(op_span, &rhs_val, span)?,
|
||||
},
|
||||
Operator::Boolean(bl) => match bl {
|
||||
Boolean::And => lhs_val.and(op_span, &rhs_val, span)?,
|
||||
Boolean::Or => lhs_val.or(op_span, &rhs_val, span)?,
|
||||
Boolean::Xor => lhs_val.xor(op_span, &rhs_val, span)?,
|
||||
Boolean::And => lhs_val.and(op_span, &rhs_val, span)?,
|
||||
},
|
||||
Operator::Bits(bit) => match bit {
|
||||
Bits::BitOr => lhs_val.bit_or(op_span, &rhs_val, span)?,
|
||||
|
@ -5136,11 +5136,11 @@ pub fn parse_assignment_operator(working_set: &mut StateWorkingSet, span: Span)
|
||||
|
||||
let operator = match contents {
|
||||
b"=" => Operator::Assignment(Assignment::Assign),
|
||||
b"+=" => Operator::Assignment(Assignment::PlusAssign),
|
||||
b"++=" => Operator::Assignment(Assignment::ConcatAssign),
|
||||
b"-=" => Operator::Assignment(Assignment::MinusAssign),
|
||||
b"+=" => Operator::Assignment(Assignment::AddAssign),
|
||||
b"-=" => Operator::Assignment(Assignment::SubtractAssign),
|
||||
b"*=" => Operator::Assignment(Assignment::MultiplyAssign),
|
||||
b"/=" => Operator::Assignment(Assignment::DivideAssign),
|
||||
b"++=" => Operator::Assignment(Assignment::ConcatenateAssign),
|
||||
_ => {
|
||||
working_set.error(ParseError::Expected("assignment operator", span));
|
||||
return garbage(working_set, span);
|
||||
@ -5276,28 +5276,28 @@ pub fn parse_operator(working_set: &mut StateWorkingSet, span: Span) -> Expressi
|
||||
b">=" => Operator::Comparison(Comparison::GreaterThanOrEqual),
|
||||
b"=~" | b"like" => Operator::Comparison(Comparison::RegexMatch),
|
||||
b"!~" | b"not-like" => Operator::Comparison(Comparison::NotRegexMatch),
|
||||
b"+" => Operator::Math(Math::Plus),
|
||||
b"++" => Operator::Math(Math::Concat),
|
||||
b"-" => Operator::Math(Math::Minus),
|
||||
b"*" => Operator::Math(Math::Multiply),
|
||||
b"/" => Operator::Math(Math::Divide),
|
||||
b"//" => Operator::Math(Math::FloorDivision),
|
||||
b"in" => Operator::Comparison(Comparison::In),
|
||||
b"not-in" => Operator::Comparison(Comparison::NotIn),
|
||||
b"has" => Operator::Comparison(Comparison::Has),
|
||||
b"not-has" => Operator::Comparison(Comparison::NotHas),
|
||||
b"starts-with" => Operator::Comparison(Comparison::StartsWith),
|
||||
b"ends-with" => Operator::Comparison(Comparison::EndsWith),
|
||||
b"+" => Operator::Math(Math::Add),
|
||||
b"-" => Operator::Math(Math::Subtract),
|
||||
b"*" => Operator::Math(Math::Multiply),
|
||||
b"/" => Operator::Math(Math::Divide),
|
||||
b"//" => Operator::Math(Math::FloorDivide),
|
||||
b"mod" => Operator::Math(Math::Modulo),
|
||||
b"**" => Operator::Math(Math::Pow),
|
||||
b"++" => Operator::Math(Math::Concatenate),
|
||||
b"bit-or" => Operator::Bits(Bits::BitOr),
|
||||
b"bit-xor" => Operator::Bits(Bits::BitXor),
|
||||
b"bit-and" => Operator::Bits(Bits::BitAnd),
|
||||
b"bit-shl" => Operator::Bits(Bits::ShiftLeft),
|
||||
b"bit-shr" => Operator::Bits(Bits::ShiftRight),
|
||||
b"starts-with" => Operator::Comparison(Comparison::StartsWith),
|
||||
b"ends-with" => Operator::Comparison(Comparison::EndsWith),
|
||||
b"and" => Operator::Boolean(Boolean::And),
|
||||
b"or" => Operator::Boolean(Boolean::Or),
|
||||
b"xor" => Operator::Boolean(Boolean::Xor),
|
||||
b"**" => Operator::Math(Math::Pow),
|
||||
b"and" => Operator::Boolean(Boolean::And),
|
||||
// WARNING: not actual operators below! Error handling only
|
||||
pow @ (b"^" | b"pow") => {
|
||||
working_set.error(ParseError::UnknownOperator(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1934,17 +1934,10 @@ mod range {
|
||||
|
||||
let _ = parse(&mut working_set, None, code.as_bytes(), true);
|
||||
|
||||
assert!(
|
||||
working_set.parse_errors.len() == 1,
|
||||
"Errors: {:?}",
|
||||
working_set.parse_errors
|
||||
);
|
||||
let err = &working_set.parse_errors[0].to_string();
|
||||
assert!(
|
||||
err.contains("range is not supported"),
|
||||
"Expected unsupported operation error, got {}",
|
||||
err
|
||||
);
|
||||
assert!(matches!(
|
||||
&working_set.parse_errors[..],
|
||||
[ParseError::OperatorUnsupportedType { .. }]
|
||||
),);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1489,7 +1489,7 @@ fn prepare_plugin_call_custom_value_op() {
|
||||
span,
|
||||
},
|
||||
CustomValueOp::Operation(
|
||||
Operator::Math(Math::Concat).into_spanned(span),
|
||||
Operator::Math(Math::Concatenate).into_spanned(span),
|
||||
cv_ok_val.clone(),
|
||||
),
|
||||
),
|
||||
@ -1502,7 +1502,7 @@ fn prepare_plugin_call_custom_value_op() {
|
||||
span,
|
||||
},
|
||||
CustomValueOp::Operation(
|
||||
Operator::Math(Math::Concat).into_spanned(span),
|
||||
Operator::Math(Math::Concatenate).into_spanned(span),
|
||||
cv_bad_val.clone(),
|
||||
),
|
||||
),
|
||||
|
@ -1,11 +1,9 @@
|
||||
use crate::{ShellError, Span};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
|
||||
use super::{Expr, Expression};
|
||||
use crate::{ShellError, Span};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Comparison {
|
||||
Equal,
|
||||
NotEqual,
|
||||
@ -23,26 +21,90 @@ pub enum Comparison {
|
||||
EndsWith,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
impl Comparison {
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Equal => "==",
|
||||
Self::NotEqual => "!=",
|
||||
Self::LessThan => "<",
|
||||
Self::GreaterThan => ">",
|
||||
Self::LessThanOrEqual => "<=",
|
||||
Self::GreaterThanOrEqual => ">=",
|
||||
Self::RegexMatch => "=~",
|
||||
Self::NotRegexMatch => "!~",
|
||||
Self::In => "in",
|
||||
Self::NotIn => "not-in",
|
||||
Self::Has => "has",
|
||||
Self::NotHas => "not-has",
|
||||
Self::StartsWith => "starts-with",
|
||||
Self::EndsWith => "ends-with",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Comparison {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Math {
|
||||
Plus,
|
||||
Concat,
|
||||
Minus,
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Divide,
|
||||
FloorDivide,
|
||||
Modulo,
|
||||
FloorDivision,
|
||||
Pow,
|
||||
Concatenate,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
impl Math {
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Add => "+",
|
||||
Self::Subtract => "-",
|
||||
Self::Multiply => "*",
|
||||
Self::Divide => "/",
|
||||
Self::FloorDivide => "//",
|
||||
Self::Modulo => "mod",
|
||||
Self::Pow => "**",
|
||||
Self::Concatenate => "++",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Math {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Boolean {
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
And,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
impl Boolean {
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Or => "or",
|
||||
Self::Xor => "xor",
|
||||
Self::And => "and",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Boolean {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Bits {
|
||||
BitOr,
|
||||
BitXor,
|
||||
@ -51,17 +113,54 @@ pub enum Bits {
|
||||
ShiftRight,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Assignment {
|
||||
Assign,
|
||||
PlusAssign,
|
||||
ConcatAssign,
|
||||
MinusAssign,
|
||||
MultiplyAssign,
|
||||
DivideAssign,
|
||||
impl Bits {
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::BitOr => "bit-or",
|
||||
Self::BitXor => "bit-xor",
|
||||
Self::BitAnd => "bit-and",
|
||||
Self::ShiftLeft => "bit-shl",
|
||||
Self::ShiftRight => "bit-shr",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
impl fmt::Display for Bits {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Assignment {
|
||||
Assign,
|
||||
AddAssign,
|
||||
SubtractAssign,
|
||||
MultiplyAssign,
|
||||
DivideAssign,
|
||||
ConcatenateAssign,
|
||||
}
|
||||
|
||||
impl Assignment {
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Assign => "=",
|
||||
Self::AddAssign => "+=",
|
||||
Self::SubtractAssign => "-=",
|
||||
Self::MultiplyAssign => "*=",
|
||||
Self::DivideAssign => "/=",
|
||||
Self::ConcatenateAssign => "++=",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Assignment {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum Operator {
|
||||
Comparison(Comparison),
|
||||
Math(Math),
|
||||
@ -71,14 +170,24 @@ pub enum Operator {
|
||||
}
|
||||
|
||||
impl Operator {
|
||||
pub fn precedence(&self) -> u8 {
|
||||
pub const fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Comparison(comparison) => comparison.as_str(),
|
||||
Self::Math(math) => math.as_str(),
|
||||
Self::Boolean(boolean) => boolean.as_str(),
|
||||
Self::Bits(bits) => bits.as_str(),
|
||||
Self::Assignment(assignment) => assignment.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn precedence(&self) -> u8 {
|
||||
match self {
|
||||
Self::Math(Math::Pow) => 100,
|
||||
Self::Math(Math::Multiply)
|
||||
| Self::Math(Math::Divide)
|
||||
| Self::Math(Math::Modulo)
|
||||
| Self::Math(Math::FloorDivision) => 95,
|
||||
Self::Math(Math::Plus) | Self::Math(Math::Minus) => 90,
|
||||
| Self::Math(Math::FloorDivide) => 95,
|
||||
Self::Math(Math::Add) | Self::Math(Math::Subtract) => 90,
|
||||
Self::Bits(Bits::ShiftLeft) | Self::Bits(Bits::ShiftRight) => 85,
|
||||
Self::Comparison(Comparison::NotRegexMatch)
|
||||
| Self::Comparison(Comparison::RegexMatch)
|
||||
@ -94,7 +203,7 @@ impl Operator {
|
||||
| Self::Comparison(Comparison::NotIn)
|
||||
| Self::Comparison(Comparison::Has)
|
||||
| Self::Comparison(Comparison::NotHas)
|
||||
| Self::Math(Math::Concat) => 80,
|
||||
| Self::Math(Math::Concatenate) => 80,
|
||||
Self::Bits(Bits::BitAnd) => 75,
|
||||
Self::Bits(Bits::BitXor) => 70,
|
||||
Self::Bits(Bits::BitOr) => 60,
|
||||
@ -106,46 +215,9 @@ impl Operator {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Operator {
|
||||
impl fmt::Display for Operator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Operator::Assignment(Assignment::Assign) => write!(f, "="),
|
||||
Operator::Assignment(Assignment::PlusAssign) => write!(f, "+="),
|
||||
Operator::Assignment(Assignment::ConcatAssign) => write!(f, "++="),
|
||||
Operator::Assignment(Assignment::MinusAssign) => write!(f, "-="),
|
||||
Operator::Assignment(Assignment::MultiplyAssign) => write!(f, "*="),
|
||||
Operator::Assignment(Assignment::DivideAssign) => write!(f, "/="),
|
||||
Operator::Comparison(Comparison::Equal) => write!(f, "=="),
|
||||
Operator::Comparison(Comparison::NotEqual) => write!(f, "!="),
|
||||
Operator::Comparison(Comparison::LessThan) => write!(f, "<"),
|
||||
Operator::Comparison(Comparison::GreaterThan) => write!(f, ">"),
|
||||
Operator::Comparison(Comparison::RegexMatch) => write!(f, "=~ or like"),
|
||||
Operator::Comparison(Comparison::NotRegexMatch) => write!(f, "!~ or not-like"),
|
||||
Operator::Comparison(Comparison::LessThanOrEqual) => write!(f, "<="),
|
||||
Operator::Comparison(Comparison::GreaterThanOrEqual) => write!(f, ">="),
|
||||
Operator::Comparison(Comparison::StartsWith) => write!(f, "starts-with"),
|
||||
Operator::Comparison(Comparison::EndsWith) => write!(f, "ends-with"),
|
||||
Operator::Comparison(Comparison::In) => write!(f, "in"),
|
||||
Operator::Comparison(Comparison::NotIn) => write!(f, "not-in"),
|
||||
Operator::Comparison(Comparison::Has) => write!(f, "has"),
|
||||
Operator::Comparison(Comparison::NotHas) => write!(f, "not-has"),
|
||||
Operator::Math(Math::Plus) => write!(f, "+"),
|
||||
Operator::Math(Math::Concat) => write!(f, "++"),
|
||||
Operator::Math(Math::Minus) => write!(f, "-"),
|
||||
Operator::Math(Math::Multiply) => write!(f, "*"),
|
||||
Operator::Math(Math::Divide) => write!(f, "/"),
|
||||
Operator::Math(Math::Modulo) => write!(f, "mod"),
|
||||
Operator::Math(Math::FloorDivision) => write!(f, "//"),
|
||||
Operator::Math(Math::Pow) => write!(f, "**"),
|
||||
Operator::Boolean(Boolean::And) => write!(f, "and"),
|
||||
Operator::Boolean(Boolean::Or) => write!(f, "or"),
|
||||
Operator::Boolean(Boolean::Xor) => write!(f, "xor"),
|
||||
Operator::Bits(Bits::BitOr) => write!(f, "bit-or"),
|
||||
Operator::Bits(Bits::BitXor) => write!(f, "bit-xor"),
|
||||
Operator::Bits(Bits::BitAnd) => write!(f, "bit-and"),
|
||||
Operator::Bits(Bits::ShiftLeft) => write!(f, "bit-shl"),
|
||||
Operator::Bits(Bits::ShiftRight) => write!(f, "bit-shr"),
|
||||
}
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +234,7 @@ pub struct RangeOperator {
|
||||
pub next_op_span: Span,
|
||||
}
|
||||
|
||||
impl Display for RangeOperator {
|
||||
impl fmt::Display for RangeOperator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.inclusion {
|
||||
RangeInclusion::Inclusive => write!(f, ".."),
|
||||
@ -176,7 +248,7 @@ pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
|
||||
Expression {
|
||||
expr: Expr::Operator(operator),
|
||||
..
|
||||
} => Ok(operator.clone()),
|
||||
} => Ok(*operator),
|
||||
Expression { span, expr, .. } => Err(ShellError::UnknownOperator {
|
||||
op_token: format!("{expr:?}"),
|
||||
span: *span,
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::{ast::RedirectionSource, did_you_mean, Span, Type};
|
||||
use miette::Diagnostic;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt::Display,
|
||||
str::{from_utf8, Utf8Error},
|
||||
};
|
||||
|
||||
use crate::{ast::RedirectionSource, did_you_mean, Span, Type};
|
||||
use miette::Diagnostic;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone, Debug, Error, Diagnostic, Serialize, Deserialize)]
|
||||
@ -115,38 +114,36 @@ pub enum ParseError {
|
||||
span: Span,
|
||||
},
|
||||
|
||||
#[error("{0} is not supported on values of type {3}")]
|
||||
#[diagnostic(code(nu::parser::unsupported_operation))]
|
||||
UnsupportedOperationLHS(
|
||||
String,
|
||||
#[label = "doesn't support this value"] Span,
|
||||
#[label("{3}")] Span,
|
||||
Type,
|
||||
),
|
||||
/// One or more of the values have types not supported by the operator.
|
||||
#[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
|
||||
#[diagnostic(code(nu::parser::operator_unsupported_type))]
|
||||
OperatorUnsupportedType {
|
||||
op: &'static str,
|
||||
unsupported: Type,
|
||||
#[label = "does not support '{unsupported}'"]
|
||||
op_span: Span,
|
||||
#[label("{unsupported}")]
|
||||
unsupported_span: Span,
|
||||
#[help]
|
||||
help: Option<&'static str>,
|
||||
},
|
||||
|
||||
#[error("{0} is not supported between {3} and {5}.")]
|
||||
#[diagnostic(code(nu::parser::unsupported_operation))]
|
||||
UnsupportedOperationRHS(
|
||||
String,
|
||||
#[label = "doesn't support these values"] Span,
|
||||
#[label("{3}")] Span,
|
||||
Type,
|
||||
#[label("{5}")] Span,
|
||||
Type,
|
||||
),
|
||||
|
||||
#[error("{0} is not supported between {3}, {5}, and {7}.")]
|
||||
#[diagnostic(code(nu::parser::unsupported_operation))]
|
||||
UnsupportedOperationTernary(
|
||||
String,
|
||||
#[label = "doesn't support these values"] Span,
|
||||
#[label("{3}")] Span,
|
||||
Type,
|
||||
#[label("{5}")] Span,
|
||||
Type,
|
||||
#[label("{7}")] Span,
|
||||
Type,
|
||||
),
|
||||
/// The operator supports the types of both values, but not the specific combination of their types.
|
||||
#[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
|
||||
#[diagnostic(code(nu::parser::operator_incompatible_types))]
|
||||
OperatorIncompatibleTypes {
|
||||
op: &'static str,
|
||||
lhs: Type,
|
||||
rhs: Type,
|
||||
#[label = "does not operate between '{lhs}' and '{rhs}'"]
|
||||
op_span: Span,
|
||||
#[label("{lhs}")]
|
||||
lhs_span: Span,
|
||||
#[label("{rhs}")]
|
||||
rhs_span: Span,
|
||||
#[help]
|
||||
help: Option<&'static str>,
|
||||
},
|
||||
|
||||
#[error("Capture of mutable variable.")]
|
||||
#[diagnostic(code(nu::parser::expected_keyword))]
|
||||
@ -564,9 +561,8 @@ impl ParseError {
|
||||
ParseError::ExpectedWithStringMsg(_, s) => *s,
|
||||
ParseError::ExpectedWithDidYouMean(_, _, s) => *s,
|
||||
ParseError::Mismatch(_, _, s) => *s,
|
||||
ParseError::UnsupportedOperationLHS(_, _, s, _) => *s,
|
||||
ParseError::UnsupportedOperationRHS(_, _, _, _, s, _) => *s,
|
||||
ParseError::UnsupportedOperationTernary(_, _, _, _, _, _, s, _) => *s,
|
||||
ParseError::OperatorUnsupportedType { op_span, .. } => *op_span,
|
||||
ParseError::OperatorIncompatibleTypes { op_span, .. } => *op_span,
|
||||
ParseError::ExpectedKeyword(_, s) => *s,
|
||||
ParseError::UnexpectedKeyword(_, s) => *s,
|
||||
ParseError::CantAliasKeyword(_, s) => *s,
|
||||
|
@ -17,22 +17,35 @@ pub mod location;
|
||||
/// and pass it into an error viewer to display to the user.
|
||||
#[derive(Debug, Clone, Error, Diagnostic, PartialEq)]
|
||||
pub enum ShellError {
|
||||
/// An operator received two arguments of incompatible types.
|
||||
///
|
||||
/// ## Resolution
|
||||
///
|
||||
/// Check each argument's type and convert one or both as needed.
|
||||
#[error("Type mismatch during operation.")]
|
||||
#[diagnostic(code(nu::shell::type_mismatch))]
|
||||
OperatorMismatch {
|
||||
#[label = "type mismatch for operator"]
|
||||
/// One or more of the values have types not supported by the operator.
|
||||
#[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
|
||||
#[diagnostic(code(nu::shell::operator_unsupported_type))]
|
||||
OperatorUnsupportedType {
|
||||
op: Operator,
|
||||
unsupported: Type,
|
||||
#[label = "does not support '{unsupported}'"]
|
||||
op_span: Span,
|
||||
lhs_ty: String,
|
||||
#[label("{lhs_ty}")]
|
||||
#[label("{unsupported}")]
|
||||
unsupported_span: Span,
|
||||
#[help]
|
||||
help: Option<&'static str>,
|
||||
},
|
||||
|
||||
/// The operator supports the types of both values, but not the specific combination of their types.
|
||||
#[error("Types '{lhs}' and '{rhs}' are not compatible for the '{op}' operator.")]
|
||||
#[diagnostic(code(nu::shell::operator_incompatible_types))]
|
||||
OperatorIncompatibleTypes {
|
||||
op: Operator,
|
||||
lhs: Type,
|
||||
rhs: Type,
|
||||
#[label = "does not operate between '{lhs}' and '{rhs}'"]
|
||||
op_span: Span,
|
||||
#[label("{lhs}")]
|
||||
lhs_span: Span,
|
||||
rhs_ty: String,
|
||||
#[label("{rhs_ty}")]
|
||||
#[label("{rhs}")]
|
||||
rhs_span: Span,
|
||||
#[help]
|
||||
help: Option<&'static str>,
|
||||
},
|
||||
|
||||
/// An arithmetic operation's resulting value overflowed its possible size.
|
||||
@ -156,20 +169,6 @@ pub enum ShellError {
|
||||
call_span: Span,
|
||||
},
|
||||
|
||||
/// This value cannot be used with this operator.
|
||||
///
|
||||
/// ## Resolution
|
||||
///
|
||||
/// Not all values, for example custom values, can be used with all operators. Either
|
||||
/// implement support for the operator on this type, or convert the type to a supported one.
|
||||
#[error("Unsupported operator: {operator}.")]
|
||||
#[diagnostic(code(nu::shell::unsupported_operator))]
|
||||
UnsupportedOperator {
|
||||
operator: Operator,
|
||||
#[label = "unsupported operator"]
|
||||
span: Span,
|
||||
},
|
||||
|
||||
/// Invalid assignment left-hand side
|
||||
///
|
||||
/// ## Resolution
|
||||
|
@ -235,14 +235,14 @@ pub trait Eval {
|
||||
let rhs = Self::eval::<D>(state, mut_state, rhs)?;
|
||||
|
||||
match math {
|
||||
Math::Plus => lhs.add(op_span, &rhs, expr_span),
|
||||
Math::Minus => lhs.sub(op_span, &rhs, expr_span),
|
||||
Math::Add => lhs.add(op_span, &rhs, expr_span),
|
||||
Math::Subtract => lhs.sub(op_span, &rhs, expr_span),
|
||||
Math::Multiply => lhs.mul(op_span, &rhs, expr_span),
|
||||
Math::Divide => lhs.div(op_span, &rhs, expr_span),
|
||||
Math::Concat => lhs.concat(op_span, &rhs, expr_span),
|
||||
Math::FloorDivide => lhs.floor_div(op_span, &rhs, expr_span),
|
||||
Math::Modulo => lhs.modulo(op_span, &rhs, expr_span),
|
||||
Math::FloorDivision => lhs.floor_div(op_span, &rhs, expr_span),
|
||||
Math::Pow => lhs.pow(op_span, &rhs, expr_span),
|
||||
Math::Concatenate => lhs.concat(op_span, &rhs, expr_span),
|
||||
}
|
||||
}
|
||||
Operator::Comparison(comparison) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{cmp::Ordering, fmt};
|
||||
|
||||
use crate::{ast::Operator, ShellError, Span, Value};
|
||||
use crate::{ast::Operator, ShellError, Span, Type, Value};
|
||||
|
||||
/// Trait definition for a custom [`Value`](crate::Value) type
|
||||
#[typetag::serde(tag = "type")]
|
||||
@ -68,7 +68,7 @@ pub trait CustomValue: fmt::Debug + Send + Sync {
|
||||
///
|
||||
/// The Operator enum is used to indicate the expected operation.
|
||||
///
|
||||
/// Default impl raises [`ShellError::UnsupportedOperator`].
|
||||
/// Default impl raises [`ShellError::OperatorUnsupportedType`].
|
||||
fn operation(
|
||||
&self,
|
||||
lhs_span: Span,
|
||||
@ -77,7 +77,13 @@ pub trait CustomValue: fmt::Debug + Send + Sync {
|
||||
right: &Value,
|
||||
) -> Result<Value, ShellError> {
|
||||
let _ = (lhs_span, right);
|
||||
Err(ShellError::UnsupportedOperator { operator, span: op })
|
||||
Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator,
|
||||
unsupported: Type::Custom(self.type_name().into()),
|
||||
op_span: op,
|
||||
unsupported_span: lhs_span,
|
||||
help: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// For custom values in plugins: return `true` here if you would like to be notified when all
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -32,45 +32,48 @@ def command-not-found-error [span: record] {
|
||||
throw-error "std::help::command_not_found" "command not found" $span
|
||||
}
|
||||
|
||||
def get-all-operators [] { return [
|
||||
[type, operator, name, description, precedence];
|
||||
|
||||
[Assignment, =, Assign, "Assigns a value to a variable.", 10]
|
||||
[Assignment, +=, PlusAssign, "Adds a value to a variable.", 10]
|
||||
[Assignment, ++=, ConcatAssign, "Concatenate two lists, two strings, or two binary values.", 10]
|
||||
[Assignment, -=, MinusAssign, "Subtracts a value from a variable.", 10]
|
||||
[Assignment, *=, MultiplyAssign, "Multiplies a variable by a value.", 10]
|
||||
[Assignment, /=, DivideAssign, "Divides a variable by a value.", 10]
|
||||
[Comparison, ==, Equal, "Checks if two values are equal.", 80]
|
||||
[Comparison, !=, NotEqual, "Checks if two values are not equal.", 80]
|
||||
[Comparison, <, LessThan, "Checks if a value is less than another.", 80]
|
||||
[Comparison, <=, LessThanOrEqual, "Checks if a value is less than or equal to another.", 80]
|
||||
[Comparison, >, GreaterThan, "Checks if a value is greater than another.", 80]
|
||||
[Comparison, >=, GreaterThanOrEqual, "Checks if a value is greater than or equal to another.", 80]
|
||||
[Comparison, '=~ or like', RegexMatch, "Checks if a value matches a regular expression.", 80]
|
||||
[Comparison, '!~ or not-like', NotRegexMatch, "Checks if a value does not match a regular expression.", 80]
|
||||
[Comparison, in, In, "Checks if a value is in a list or string.", 80]
|
||||
[Comparison, not-in, NotIn, "Checks if a value is not in a list or string.", 80]
|
||||
[Comparison, starts-with, StartsWith, "Checks if a string starts with another.", 80]
|
||||
[Comparison, ends-with, EndsWith, "Checks if a string ends with another.", 80]
|
||||
[Comparison, not, UnaryNot, "Negates a value or expression.", 0]
|
||||
[Math, +, Plus, "Adds two values.", 90]
|
||||
[Math, ++, Concat, "Concatenate two lists, two strings, or two binary values.", 80]
|
||||
[Math, -, Minus, "Subtracts two values.", 90]
|
||||
[Math, *, Multiply, "Multiplies two values.", 95]
|
||||
[Math, /, Divide, "Divides two values.", 95]
|
||||
[Math, //, FloorDivision, "Divides two values and floors the result.", 95]
|
||||
[Math, mod, Modulo, "Divides two values and returns the remainder.", 95]
|
||||
[Math, **, "Pow ", "Raises one value to the power of another.", 100]
|
||||
[Bitwise, bit-or, BitOr, "Performs a bitwise OR on two values.", 60]
|
||||
[Bitwise, bit-xor, BitXor, "Performs a bitwise XOR on two values.", 70]
|
||||
[Bitwise, bit-and, BitAnd, "Performs a bitwise AND on two values.", 75]
|
||||
[Bitwise, bit-shl, ShiftLeft, "Shifts a value left by another.", 85]
|
||||
[Bitwise, bit-shr, ShiftRight, "Shifts a value right by another.", 85]
|
||||
[Boolean, and, And, "Checks if two values are true.", 50]
|
||||
[Boolean, or, Or, "Checks if either value is true.", 40]
|
||||
[Boolean, xor, Xor, "Checks if one value is true and the other is false.", 45]
|
||||
]}
|
||||
def get-all-operators [] {
|
||||
[
|
||||
[type, operator, name, description, precedence];
|
||||
[Assignment, =, Assign, 'Assigns a value to a variable.', 10]
|
||||
[Assignment, +=, AddAssign, 'Adds a value to a variable.', 10]
|
||||
[Assignment, -=, SubtractAssign, 'Subtracts a value from a variable.', 10]
|
||||
[Assignment, *=, MultiplyAssign, 'Multiplies a variable by a value.', 10]
|
||||
[Assignment, /=, DivideAssign, 'Divides a variable by a value.', 10]
|
||||
[Assignment, ++=, ConcatenateAssign, 'Concatenates a list, a string, or a binary value to a variable of the same type.', 10]
|
||||
[Comparison, ==, Equal, 'Checks if two values are equal.', 80]
|
||||
[Comparison, !=, NotEqual, 'Checks if two values are not equal.', 80]
|
||||
[Comparison, <, LessThan, 'Checks if a value is less than another.', 80]
|
||||
[Comparison, >, GreaterThan, 'Checks if a value is greater than another.', 80]
|
||||
[Comparison, <=, LessThanOrEqual, 'Checks if a value is less than or equal to another.', 80]
|
||||
[Comparison, >=, GreaterThanOrEqual, 'Checks if a value is greater than or equal to another.', 80]
|
||||
[Comparison, '=~ or like', RegexMatch, 'Checks if a value matches a regular expression.', 80]
|
||||
[Comparison, '!~ or not-like', NotRegexMatch, 'Checks if a value does not match a regular expression.', 80]
|
||||
[Comparison, in, In, 'Checks if a value is in a list, is part of a string, or is a key in a record.', 80]
|
||||
[Comparison, not-in, NotIn, 'Checks if a value is not in a list, is not part of a string, or is not a key in a record.', 80]
|
||||
[Comparison, has, Has, 'Checks if a list contains a value, a string contains another, or if a record has a key.', 80]
|
||||
[Comparison, not-has, NotHas, 'Checks if a list does not contains a value, a string does not contains another, or if a record does not have a key.', 80]
|
||||
[Comparison, starts-with, StartsWith, 'Checks if a string starts with another.', 80]
|
||||
[Comparison, ends-with, EndsWith, 'Checks if a string ends with another.', 80]
|
||||
[Math, +, Add, 'Adds two values.', 90]
|
||||
[Math, -, Subtract, 'Subtracts two values.', 90]
|
||||
[Math, *, Multiply, 'Multiplies two values.', 95]
|
||||
[Math, /, Divide, 'Divides two values.', 95]
|
||||
[Math, //, FloorDivide, 'Divides two values and floors the result.', 95]
|
||||
[Math, mod, Modulo, 'Divides two values and returns the remainder.', 95]
|
||||
[Math, **, Pow, 'Raises one value to the power of another.', 100]
|
||||
[Math, ++, Concatenate, 'Concatenates two lists, two strings, or two binary values.', 80]
|
||||
[Bitwise, bit-or, BitOr, 'Performs a bitwise OR on two values.', 60]
|
||||
[Bitwise, bit-xor, BitXor, 'Performs a bitwise XOR on two values.', 70]
|
||||
[Bitwise, bit-and, BitAnd, 'Performs a bitwise AND on two values.', 75]
|
||||
[Bitwise, bit-shl, ShiftLeft, 'Bitwise shifts a value left by another.', 85]
|
||||
[Bitwise, bit-shr, ShiftRight, 'Bitwise shifts a value right by another.', 85]
|
||||
[Boolean, or, Or, 'Checks if either value is true.', 40]
|
||||
[Boolean, xor, Xor, 'Checks if one value is true and the other is false.', 45]
|
||||
[Boolean, and, And, 'Checks if both values are true.', 50]
|
||||
[Boolean, not, Not, 'Negates a value or expression.', 55]
|
||||
]
|
||||
}
|
||||
|
||||
def "nu-complete list-aliases" [] {
|
||||
scope aliases | select name description | rename value description
|
||||
|
@ -1,4 +1,7 @@
|
||||
use nu_protocol::{ast, CustomValue, ShellError, Span, Value};
|
||||
use nu_protocol::{
|
||||
ast::{self, Math, Operator},
|
||||
CustomValue, ShellError, Span, Type, Value,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
@ -112,7 +115,7 @@ impl CustomValue for CoolCustomValue {
|
||||
) -> Result<Value, ShellError> {
|
||||
match operator {
|
||||
// Append the string inside `cool`
|
||||
ast::Operator::Math(ast::Math::Concat) => {
|
||||
Operator::Math(Math::Concatenate) => {
|
||||
if let Some(right) = right
|
||||
.as_custom_value()
|
||||
.ok()
|
||||
@ -125,18 +128,21 @@ impl CustomValue for CoolCustomValue {
|
||||
op_span,
|
||||
))
|
||||
} else {
|
||||
Err(ShellError::OperatorMismatch {
|
||||
Err(ShellError::OperatorUnsupportedType {
|
||||
op: Operator::Math(Math::Concatenate),
|
||||
unsupported: right.get_type(),
|
||||
op_span,
|
||||
lhs_ty: self.typetag_name().into(),
|
||||
lhs_span,
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::UnsupportedOperator {
|
||||
operator,
|
||||
span: op_span,
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: Operator::Math(Math::Concatenate),
|
||||
unsupported: Type::Custom(self.type_name().into()),
|
||||
op_span,
|
||||
unsupported_span: lhs_span,
|
||||
help: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -7,20 +7,21 @@ mod nu_schema;
|
||||
mod nu_when;
|
||||
pub mod utils;
|
||||
|
||||
use crate::{Cacheable, PolarsPlugin};
|
||||
use nu_plugin::EngineInterface;
|
||||
use nu_protocol::{
|
||||
ast::Operator, CustomValue, PipelineData, ShellError, Span, Spanned, Type, Value,
|
||||
};
|
||||
use std::{cmp::Ordering, fmt};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub use file_type::PolarsFileType;
|
||||
pub use nu_dataframe::{Axis, Column, NuDataFrame, NuDataFrameCustomValue};
|
||||
pub use nu_expression::{NuExpression, NuExpressionCustomValue};
|
||||
pub use nu_lazyframe::{NuLazyFrame, NuLazyFrameCustomValue};
|
||||
pub use nu_lazygroupby::{NuLazyGroupBy, NuLazyGroupByCustomValue};
|
||||
use nu_plugin::EngineInterface;
|
||||
use nu_protocol::{ast::Operator, CustomValue, PipelineData, ShellError, Span, Spanned, Value};
|
||||
pub use nu_schema::{str_to_dtype, NuSchema};
|
||||
pub use nu_when::{NuWhen, NuWhenCustomValue, NuWhenType};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{Cacheable, PolarsPlugin};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PolarsPluginType {
|
||||
@ -217,13 +218,16 @@ pub trait PolarsPluginCustomValue: CustomValue {
|
||||
&self,
|
||||
_plugin: &PolarsPlugin,
|
||||
_engine: &EngineInterface,
|
||||
_lhs_span: Span,
|
||||
lhs_span: Span,
|
||||
operator: Spanned<Operator>,
|
||||
_right: Value,
|
||||
) -> Result<Value, ShellError> {
|
||||
Err(ShellError::UnsupportedOperator {
|
||||
operator: operator.item,
|
||||
span: operator.span,
|
||||
Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: Type::Custom(self.type_name().into()),
|
||||
op_span: operator.span,
|
||||
unsupported_span: lhs_span,
|
||||
help: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -18,15 +18,15 @@ pub(super) fn between_dataframes(
|
||||
rhs: &NuDataFrame,
|
||||
) -> Result<NuDataFrame, ShellError> {
|
||||
match operator.item {
|
||||
Operator::Math(Math::Plus) => {
|
||||
Operator::Math(Math::Add) => {
|
||||
lhs.append_df(rhs, Axis::Row, Span::merge(left.span(), right.span()))
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op => Err(ShellError::OperatorUnsupportedType {
|
||||
op,
|
||||
unsupported: left.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: left.span(),
|
||||
help: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -40,7 +40,7 @@ pub(super) fn compute_between_series(
|
||||
) -> Result<NuDataFrame, ShellError> {
|
||||
let operation_span = Span::merge(left.span(), right.span());
|
||||
match operator.item {
|
||||
Operator::Math(Math::Plus) => {
|
||||
Operator::Math(Math::Add) => {
|
||||
let mut res = (lhs + rhs).map_err(|e| ShellError::GenericError {
|
||||
error: format!("Addition error: {e}"),
|
||||
msg: "".into(),
|
||||
@ -52,7 +52,7 @@ pub(super) fn compute_between_series(
|
||||
res.rename(name.into());
|
||||
NuDataFrame::try_from_series(res, operation_span)
|
||||
}
|
||||
Operator::Math(Math::Minus) => {
|
||||
Operator::Math(Math::Subtract) => {
|
||||
let mut res = (lhs - rhs).map_err(|e| ShellError::GenericError {
|
||||
error: format!("Subtraction error: {e}"),
|
||||
msg: "".into(),
|
||||
@ -181,12 +181,12 @@ pub(super) fn compute_between_series(
|
||||
span: operation_span,
|
||||
}),
|
||||
},
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op => Err(ShellError::OperatorUnsupportedType {
|
||||
op,
|
||||
unsupported: left.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: left.span(),
|
||||
help: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -222,12 +222,12 @@ pub(super) fn compute_series_single_value(
|
||||
right: &Value,
|
||||
) -> Result<NuDataFrame, ShellError> {
|
||||
if !lhs.is_series() {
|
||||
return Err(ShellError::OperatorMismatch {
|
||||
return Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: left.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: left.span(),
|
||||
help: None,
|
||||
});
|
||||
}
|
||||
|
||||
@ -235,7 +235,7 @@ pub(super) fn compute_series_single_value(
|
||||
let lhs = lhs.as_series(lhs_span)?;
|
||||
|
||||
match operator.item {
|
||||
Operator::Math(Math::Plus) => match &right {
|
||||
Operator::Math(Math::Add) => match &right {
|
||||
Value::Int { val, .. } => {
|
||||
compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::add, lhs_span)
|
||||
}
|
||||
@ -243,27 +243,27 @@ pub(super) fn compute_series_single_value(
|
||||
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::add, lhs_span)
|
||||
}
|
||||
Value::String { val, .. } => add_string_to_series(&lhs, val, lhs_span),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Math(Math::Minus) => match &right {
|
||||
Operator::Math(Math::Subtract) => match &right {
|
||||
Value::Int { val, .. } => {
|
||||
compute_series_i64(&lhs, *val, <ChunkedArray<Int64Type>>::sub, lhs_span)
|
||||
}
|
||||
Value::Float { val, .. } => {
|
||||
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::sub, lhs_span)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Math(Math::Multiply) => match &right {
|
||||
@ -273,12 +273,12 @@ pub(super) fn compute_series_single_value(
|
||||
Value::Float { val, .. } => {
|
||||
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::mul, lhs_span)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Math(Math::Divide) => {
|
||||
@ -298,12 +298,12 @@ pub(super) fn compute_series_single_value(
|
||||
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::div, lhs_span)
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -319,12 +319,12 @@ pub(super) fn compute_series_single_value(
|
||||
Value::Date { val, .. } => {
|
||||
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::equal, lhs_span)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Comparison(Comparison::NotEqual) => match &right {
|
||||
@ -344,12 +344,12 @@ pub(super) fn compute_series_single_value(
|
||||
ChunkedArray::not_equal,
|
||||
lhs_span,
|
||||
),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Comparison(Comparison::LessThan) => match &right {
|
||||
@ -360,12 +360,12 @@ pub(super) fn compute_series_single_value(
|
||||
Value::Date { val, .. } => {
|
||||
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::lt, lhs_span)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Comparison(Comparison::LessThanOrEqual) => match &right {
|
||||
@ -376,12 +376,12 @@ pub(super) fn compute_series_single_value(
|
||||
Value::Date { val, .. } => {
|
||||
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::lt_eq, lhs_span)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Comparison(Comparison::GreaterThan) => match &right {
|
||||
@ -392,12 +392,12 @@ pub(super) fn compute_series_single_value(
|
||||
Value::Date { val, .. } => {
|
||||
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::gt, lhs_span)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Comparison(Comparison::GreaterThanOrEqual) => match &right {
|
||||
@ -408,23 +408,23 @@ pub(super) fn compute_series_single_value(
|
||||
Value::Date { val, .. } => {
|
||||
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::gt_eq, lhs_span)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
// TODO: update this to do a regex match instead of a simple contains?
|
||||
Operator::Comparison(Comparison::RegexMatch) => match &right {
|
||||
Value::String { val, .. } => contains_series_pat(&lhs, val, lhs_span),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Comparison(Comparison::StartsWith) => match &right {
|
||||
@ -432,12 +432,12 @@ pub(super) fn compute_series_single_value(
|
||||
let starts_with_pattern = format!("^{}", fancy_regex::escape(val));
|
||||
contains_series_pat(&lhs, &starts_with_pattern, lhs_span)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
Operator::Comparison(Comparison::EndsWith) => match &right {
|
||||
@ -445,20 +445,20 @@ pub(super) fn compute_series_single_value(
|
||||
let ends_with_pattern = format!("{}$", fancy_regex::escape(val));
|
||||
contains_series_pat(&lhs, &ends_with_pattern, lhs_span)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: right.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: right.span(),
|
||||
help: None,
|
||||
}),
|
||||
},
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
_ => Err(ShellError::OperatorUnsupportedType {
|
||||
op: operator.item,
|
||||
unsupported: left.get_type(),
|
||||
op_span: operator.span,
|
||||
lhs_ty: left.get_type().to_string(),
|
||||
lhs_span: left.span(),
|
||||
rhs_ty: right.get_type().to_string(),
|
||||
rhs_span: right.span(),
|
||||
unsupported_span: left.span(),
|
||||
help: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -81,14 +81,14 @@ fn with_operator(
|
||||
left: &NuExpression,
|
||||
right: &NuExpression,
|
||||
lhs_span: Span,
|
||||
rhs_span: Span,
|
||||
_rhs_span: Span,
|
||||
op_span: Span,
|
||||
) -> Result<Value, ShellError> {
|
||||
match operator {
|
||||
Operator::Math(Math::Plus) => {
|
||||
Operator::Math(Math::Add) => {
|
||||
apply_arithmetic(plugin, engine, left, right, lhs_span, Add::add)
|
||||
}
|
||||
Operator::Math(Math::Minus) => {
|
||||
Operator::Math(Math::Subtract) => {
|
||||
apply_arithmetic(plugin, engine, left, right, lhs_span, Sub::sub)
|
||||
}
|
||||
Operator::Math(Math::Multiply) => {
|
||||
@ -100,7 +100,7 @@ fn with_operator(
|
||||
Operator::Math(Math::Modulo) => {
|
||||
apply_arithmetic(plugin, engine, left, right, lhs_span, Rem::rem)
|
||||
}
|
||||
Operator::Math(Math::FloorDivision) => {
|
||||
Operator::Math(Math::FloorDivide) => {
|
||||
apply_arithmetic(plugin, engine, left, right, lhs_span, Div::div)
|
||||
}
|
||||
Operator::Comparison(Comparison::Equal) => Ok(left
|
||||
@ -133,12 +133,12 @@ fn with_operator(
|
||||
.apply_with_expr(right.clone(), Expr::lt_eq)
|
||||
.cache(plugin, engine, lhs_span)?
|
||||
.into_value(lhs_span)),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op => Err(ShellError::OperatorUnsupportedType {
|
||||
op,
|
||||
unsupported: Type::Custom(TYPE_NAME.into()),
|
||||
op_span,
|
||||
lhs_ty: Type::Custom(TYPE_NAME.into()).to_string(),
|
||||
lhs_span,
|
||||
rhs_ty: Type::Custom(TYPE_NAME.into()).to_string(),
|
||||
rhs_span,
|
||||
unsupported_span: lhs_span,
|
||||
help: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ fn const_binary_operator(#[case] inp: &[&str], #[case] expect: &str) {
|
||||
#[case(&["const x = 1 / 0", "$x"], "division by zero")]
|
||||
#[case(&["const x = 10 ** 10000000", "$x"], "pow operation overflowed")]
|
||||
#[case(&["const x = 2 ** 62 * 2", "$x"], "multiply operation overflowed")]
|
||||
#[case(&["const x = 1 ++ 0", "$x"], "doesn't support this value")]
|
||||
#[case(&["const x = 1 ++ 0", "$x"], "nu::parser::operator_unsupported_type")]
|
||||
fn const_operator_error(#[case] inp: &[&str], #[case] expect: &str) {
|
||||
let actual = nu!(&inp.join("; "));
|
||||
assert!(actual.err.contains(expect));
|
||||
|
@ -27,7 +27,7 @@ fn float_in_dec_range() -> TestResult {
|
||||
|
||||
#[test]
|
||||
fn non_number_in_range() -> TestResult {
|
||||
fail_test(r#"'a' in 1..3"#, "subset comparison is not supported")
|
||||
fail_test(r#"'a' in 1..3"#, "nu::parser::operator_incompatible_types")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -73,10 +73,10 @@ fn invalid_not_regex_fails() -> TestResult {
|
||||
|
||||
#[test]
|
||||
fn regex_on_int_fails() -> TestResult {
|
||||
fail_test(r#"33 =~ foo"#, "is not supported")
|
||||
fail_test(r#"33 =~ foo"#, "nu::parser::operator_unsupported_type")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_regex_on_int_fails() -> TestResult {
|
||||
fail_test(r#"33 !~ foo"#, "is not supported")
|
||||
fail_test(r#"33 !~ foo"#, "nu::parser::operator_unsupported_type")
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ fn string_in_string() -> TestResult {
|
||||
|
||||
#[test]
|
||||
fn non_string_in_string() -> TestResult {
|
||||
fail_test(r#"42 in 'abc'"#, "is not supported")
|
||||
fail_test(r#"42 in 'abc'"#, "nu::parser::operator_incompatible_types")
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -32,7 +32,7 @@ fn string_in_record() -> TestResult {
|
||||
fn non_string_in_record() -> TestResult {
|
||||
fail_test(
|
||||
r#"4 in ('{ "a": 13, "b": 14 }' | from json)"#,
|
||||
"mismatch during operation",
|
||||
"nu::shell::operator_incompatible_types",
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,10 @@ fn type_in_list_of_this_type() -> TestResult {
|
||||
|
||||
#[test]
|
||||
fn type_in_list_of_non_this_type() -> TestResult {
|
||||
fail_test(r#"'hello' in [41 42 43]"#, "is not supported")
|
||||
fail_test(
|
||||
r#"'hello' in [41 42 43]"#,
|
||||
"nu::parser::operator_incompatible_types",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -40,7 +43,10 @@ fn date_minus_duration() -> TestResult {
|
||||
|
||||
#[test]
|
||||
fn duration_minus_date_not_supported() -> TestResult {
|
||||
fail_test("2day - 2023-04-22", "doesn't support these values")
|
||||
fail_test(
|
||||
"2day - 2023-04-22",
|
||||
"nu::parser::operator_incompatible_types",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -770,8 +770,10 @@ fn filesize_math() {
|
||||
|
||||
#[test]
|
||||
fn filesize_math2() {
|
||||
let actual = nu!("100 / 10kb");
|
||||
assert!(actual.err.contains("doesn't support"));
|
||||
let actual = nu!("100 / 10kB");
|
||||
assert!(actual
|
||||
.err
|
||||
.contains("nu::parser::operator_incompatible_types"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
Loading…
Reference in New Issue
Block a user