Cleanup operator errors

This commit is contained in:
Ian Manske 2024-11-23 22:11:18 -08:00
parent 916544a250
commit 183956eac1
7 changed files with 610 additions and 559 deletions

View file

@ -1,15 +1,12 @@
use nu_protocol::{ use nu_protocol::{
ast::{ ast::{Block, Comparison, Expr, Expression, Math, Operator, Pipeline, Range},
Assignment, Bits, Block, Boolean, Comparison, Expr, Expression, Math, Operator, Pipeline,
Range,
},
engine::StateWorkingSet, engine::StateWorkingSet,
ParseError, Type, ParseError, Span, Type,
}; };
fn type_error( fn type_error(
name: &'static str, op: Operator,
op: &Expression, op_span: Span,
lhs: &Expression, lhs: &Expression,
rhs: &Expression, rhs: &Expression,
is_supported: fn(&Type) -> bool, is_supported: fn(&Type) -> bool,
@ -17,25 +14,25 @@ fn type_error(
let is_supported = |ty| is_supported(ty) || matches!(ty, Type::Any | Type::Custom(_)); let is_supported = |ty| is_supported(ty) || matches!(ty, Type::Any | Type::Custom(_));
let err = match (is_supported(&lhs.ty), is_supported(&rhs.ty)) { let err = match (is_supported(&lhs.ty), is_supported(&rhs.ty)) {
(true, true) => ParseError::OperatorIncompatibleTypes { (true, true) => ParseError::OperatorIncompatibleTypes {
op: name, op: op.as_str(),
lhs: lhs.ty.clone(), lhs: lhs.ty.clone(),
rhs: rhs.ty.clone(), rhs: rhs.ty.clone(),
op_span: op.span, op_span,
lhs_span: lhs.span, lhs_span: lhs.span,
rhs_span: rhs.span, rhs_span: rhs.span,
help: None, help: None,
}, },
(true, false) => ParseError::OperatorUnsupportedType { (true, false) => ParseError::OperatorUnsupportedType {
op: name, op: op.as_str(),
unsupported: rhs.ty.clone(), unsupported: rhs.ty.clone(),
op_span: op.span, op_span,
unsupported_span: rhs.span, unsupported_span: rhs.span,
help: None, help: None,
}, },
(false, _) => ParseError::OperatorUnsupportedType { (false, _) => ParseError::OperatorUnsupportedType {
op: name, op: op.as_str(),
unsupported: lhs.ty.clone(), unsupported: lhs.ty.clone(),
op_span: op.span, op_span,
unsupported_span: lhs.span, unsupported_span: lhs.span,
help: None, help: None,
}, },
@ -141,7 +138,7 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("addition", op, lhs, rhs, |ty| { type_error(Operator::Math(Math::Add), op.span, lhs, rhs, |ty| {
matches!( matches!(
ty, ty,
Type::Int Type::Int
@ -175,7 +172,7 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("subtraction", op, lhs, rhs, |ty| { type_error(Operator::Math(Math::Subtract), op.span, lhs, rhs, |ty| {
matches!( matches!(
ty, ty,
Type::Int Type::Int
@ -212,7 +209,7 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("multiplication", op, lhs, rhs, |ty| { type_error(Operator::Math(Math::Multiply), op.span, lhs, rhs, |ty| {
matches!( matches!(
ty, ty,
Type::Int | Type::Float | Type::Number | Type::Duration | Type::Filesize, Type::Int | Type::Float | Type::Number | Type::Duration | Type::Filesize,
@ -242,7 +239,7 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("division", op, lhs, rhs, |ty| { type_error(Operator::Math(Math::Divide), op.span, lhs, rhs, |ty| {
matches!( matches!(
ty, ty,
Type::Int | Type::Float | Type::Number | Type::Filesize | Type::Duration Type::Int | Type::Float | Type::Number | Type::Filesize | Type::Duration
@ -272,7 +269,7 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("floor division", op, lhs, rhs, |ty| { type_error(Operator::Math(Math::FloorDivide), op.span, lhs, rhs, |ty| {
matches!( matches!(
ty, ty,
Type::Int | Type::Float | Type::Number | Type::Filesize | Type::Duration Type::Int | Type::Float | Type::Number | Type::Filesize | Type::Duration
@ -302,7 +299,7 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("modulo", op, lhs, rhs, |ty| { type_error(Operator::Math(Math::Modulo), op.span, lhs, rhs, |ty| {
matches!( matches!(
ty, ty,
Type::Int | Type::Float | Type::Number | Type::Filesize | Type::Duration Type::Int | Type::Float | Type::Number | Type::Filesize | Type::Duration
@ -326,7 +323,7 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("exponentiation", op, lhs, rhs, |ty| { type_error(Operator::Math(Math::Pow), op.span, lhs, rhs, |ty| {
matches!(ty, Type::Int | Type::Float | Type::Number) matches!(ty, Type::Int | Type::Float | Type::Number)
}) })
} }
@ -382,7 +379,7 @@ pub fn math_result_type(
}; };
let err = match (is_supported(&lhs.ty), is_supported(&rhs.ty)) { let err = match (is_supported(&lhs.ty), is_supported(&rhs.ty)) {
(true, true) => ParseError::OperatorIncompatibleTypes { (true, true) => ParseError::OperatorIncompatibleTypes {
op: "concatentation", op: Operator::Math(Math::Concatenate).as_str(),
lhs: lhs.ty.clone(), lhs: lhs.ty.clone(),
rhs: rhs.ty.clone(), rhs: rhs.ty.clone(),
op_span: op.span, op_span: op.span,
@ -391,14 +388,14 @@ pub fn math_result_type(
help, help,
}, },
(true, false) => ParseError::OperatorUnsupportedType { (true, false) => ParseError::OperatorUnsupportedType {
op: "concatentation", op: Operator::Math(Math::Concatenate).as_str(),
unsupported: rhs.ty.clone(), unsupported: rhs.ty.clone(),
op_span: op.span, op_span: op.span,
unsupported_span: rhs.span, unsupported_span: rhs.span,
help, help,
}, },
(false, _) => ParseError::OperatorUnsupportedType { (false, _) => ParseError::OperatorUnsupportedType {
op: "concatentation", op: Operator::Math(Math::Concatenate).as_str(),
unsupported: lhs.ty.clone(), unsupported: lhs.ty.clone(),
op_span: op.span, op_span: op.span,
unsupported_span: lhs.span, unsupported_span: lhs.span,
@ -416,12 +413,9 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
let name = match operator { type_error(Operator::Boolean(operator), op.span, lhs, rhs, |ty| {
Boolean::Or => "boolean or", matches!(ty, Type::Bool)
Boolean::Xor => "boolean xor", })
Boolean::And => "boolean and",
};
type_error(name, op, lhs, rhs, |ty| matches!(ty, Type::Bool))
} }
}, },
Operator::Comparison(Comparison::LessThan) => match (&lhs.ty, &rhs.ty) { Operator::Comparison(Comparison::LessThan) => match (&lhs.ty, &rhs.ty) {
@ -454,7 +448,12 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("less than comparison", op, lhs, rhs, |ty| { type_error(
Operator::Comparison(Comparison::LessThan),
op.span,
lhs,
rhs,
|ty| {
matches!( matches!(
ty, ty,
Type::Int Type::Int
@ -467,7 +466,8 @@ pub fn math_result_type(
| Type::Bool | Type::Bool
| Type::Nothing | Type::Nothing
) )
}) },
)
} }
}, },
Operator::Comparison(Comparison::LessThanOrEqual) => match (&lhs.ty, &rhs.ty) { Operator::Comparison(Comparison::LessThanOrEqual) => match (&lhs.ty, &rhs.ty) {
@ -500,7 +500,12 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("less than or equal to comparison", op, lhs, rhs, |ty| { type_error(
Operator::Comparison(Comparison::LessThanOrEqual),
op.span,
lhs,
rhs,
|ty| {
matches!( matches!(
ty, ty,
Type::Int Type::Int
@ -513,7 +518,8 @@ pub fn math_result_type(
| Type::Bool | Type::Bool
| Type::Nothing | Type::Nothing
) )
}) },
)
} }
}, },
Operator::Comparison(Comparison::GreaterThan) => match (&lhs.ty, &rhs.ty) { Operator::Comparison(Comparison::GreaterThan) => match (&lhs.ty, &rhs.ty) {
@ -546,7 +552,12 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("greater than comparison", op, lhs, rhs, |ty| { type_error(
Operator::Comparison(Comparison::GreaterThan),
op.span,
lhs,
rhs,
|ty| {
matches!( matches!(
ty, ty,
Type::Int Type::Int
@ -559,7 +570,8 @@ pub fn math_result_type(
| Type::Bool | Type::Bool
| Type::Nothing | Type::Nothing
) )
}) },
)
} }
}, },
Operator::Comparison(Comparison::GreaterThanOrEqual) => match (&lhs.ty, &rhs.ty) { Operator::Comparison(Comparison::GreaterThanOrEqual) => match (&lhs.ty, &rhs.ty) {
@ -592,7 +604,12 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("greater than or equal to comparison", op, lhs, rhs, |ty| { type_error(
Operator::Comparison(Comparison::GreaterThanOrEqual),
op.span,
lhs,
rhs,
|ty| {
matches!( matches!(
ty, ty,
Type::Int Type::Int
@ -605,7 +622,8 @@ pub fn math_result_type(
| Type::Bool | Type::Bool
| Type::Nothing | Type::Nothing
) )
}) },
)
} }
}, },
Operator::Comparison(Comparison::Equal) => match (&lhs.ty, &rhs.ty) { Operator::Comparison(Comparison::Equal) => match (&lhs.ty, &rhs.ty) {
@ -625,7 +643,13 @@ pub fn math_result_type(
(Type::Custom(a), _) => (Type::Custom(a.clone()), None), (Type::Custom(a), _) => (Type::Custom(a.clone()), None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("regex match", op, lhs, rhs, |ty| matches!(ty, Type::String)) type_error(
Operator::Comparison(Comparison::RegexMatch),
op.span,
lhs,
rhs,
|ty| matches!(ty, Type::String),
)
} }
}, },
Operator::Comparison(Comparison::NotRegexMatch) => match (&lhs.ty, &rhs.ty) { Operator::Comparison(Comparison::NotRegexMatch) => match (&lhs.ty, &rhs.ty) {
@ -635,9 +659,13 @@ pub fn math_result_type(
(Type::Custom(a), _) => (Type::Custom(a.clone()), None), (Type::Custom(a), _) => (Type::Custom(a.clone()), None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("negative regex match", op, lhs, rhs, |ty| { type_error(
matches!(ty, Type::String) Operator::Comparison(Comparison::NotRegexMatch),
}) op.span,
lhs,
rhs,
|ty| matches!(ty, Type::String),
)
} }
}, },
Operator::Comparison(Comparison::StartsWith) => match (&lhs.ty, &rhs.ty) { Operator::Comparison(Comparison::StartsWith) => match (&lhs.ty, &rhs.ty) {
@ -647,9 +675,13 @@ pub fn math_result_type(
(Type::Custom(a), _) => (Type::Custom(a.clone()), None), (Type::Custom(a), _) => (Type::Custom(a.clone()), None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("starts-with comparison", op, lhs, rhs, |ty| { type_error(
matches!(ty, Type::String) Operator::Comparison(Comparison::StartsWith),
}) op.span,
lhs,
rhs,
|ty| matches!(ty, Type::String),
)
} }
}, },
Operator::Comparison(Comparison::EndsWith) => match (&lhs.ty, &rhs.ty) { Operator::Comparison(Comparison::EndsWith) => match (&lhs.ty, &rhs.ty) {
@ -659,9 +691,13 @@ pub fn math_result_type(
(Type::Custom(a), _) => (Type::Custom(a.clone()), None), (Type::Custom(a), _) => (Type::Custom(a.clone()), None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
type_error("ends-with comparison", op, lhs, rhs, |ty| { type_error(
matches!(ty, Type::String) Operator::Comparison(Comparison::LessThan),
}) op.span,
lhs,
rhs,
|ty| matches!(ty, Type::String),
)
} }
}, },
Operator::Comparison(Comparison::In) => match (&lhs.ty, &rhs.ty) { Operator::Comparison(Comparison::In) => match (&lhs.ty, &rhs.ty) {
@ -685,7 +721,7 @@ pub fn math_result_type(
| Type::Any | Type::Any
) { ) {
ParseError::OperatorIncompatibleTypes { ParseError::OperatorIncompatibleTypes {
op: "'in' comparison", op: Operator::Comparison(Comparison::In).as_str(),
lhs: lhs.ty.clone(), lhs: lhs.ty.clone(),
rhs: rhs.ty.clone(), rhs: rhs.ty.clone(),
op_span: op.span, op_span: op.span,
@ -695,7 +731,7 @@ pub fn math_result_type(
} }
} else { } else {
ParseError::OperatorUnsupportedType { ParseError::OperatorUnsupportedType {
op: "'in' comparison", op: Operator::Comparison(Comparison::In).as_str(),
unsupported: rhs.ty.clone(), unsupported: rhs.ty.clone(),
op_span: op.span, op_span: op.span,
unsupported_span: rhs.span, unsupported_span: rhs.span,
@ -726,7 +762,7 @@ pub fn math_result_type(
| Type::Any | Type::Any
) { ) {
ParseError::OperatorIncompatibleTypes { ParseError::OperatorIncompatibleTypes {
op: "'not-in' comparison", op: Operator::Comparison(Comparison::NotIn).as_str(),
lhs: lhs.ty.clone(), lhs: lhs.ty.clone(),
rhs: rhs.ty.clone(), rhs: rhs.ty.clone(),
op_span: op.span, op_span: op.span,
@ -736,7 +772,7 @@ pub fn math_result_type(
} }
} else { } else {
ParseError::OperatorUnsupportedType { ParseError::OperatorUnsupportedType {
op: "'not-in' comparison", op: Operator::Comparison(Comparison::NotIn).as_str(),
unsupported: rhs.ty.clone(), unsupported: rhs.ty.clone(),
op_span: op.span, op_span: op.span,
unsupported_span: rhs.span, unsupported_span: rhs.span,
@ -752,14 +788,9 @@ pub fn math_result_type(
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
_ => { _ => {
*op = Expression::garbage(working_set, op.span); *op = Expression::garbage(working_set, op.span);
let name = match operator { type_error(Operator::Bits(operator), op.span, lhs, rhs, |ty| {
Bits::BitOr => "bitwise or", matches!(ty, Type::Int)
Bits::BitXor => "bitwise xor", })
Bits::BitAnd => "bitwise and",
Bits::ShiftLeft => "bit left shift",
Bits::ShiftRight => "bit right shift",
};
type_error(name, op, lhs, rhs, |ty| matches!(ty, Type::Int))
} }
}, },
// TODO: fix this // TODO: fix this
@ -771,16 +802,8 @@ pub fn math_result_type(
(Type::Nothing, None) (Type::Nothing, None)
} }
_ => { _ => {
let name = match operator {
Assignment::Assign => "variable assignment",
Assignment::AddAssign => "addition assignment",
Assignment::SubtractAssign => "subtraction assignment",
Assignment::MultiplyAssign => "multiplication assignment",
Assignment::DivideAssign => "division assignment",
Assignment::ConcatenateAssign => "concatenation assignment",
};
let err = ParseError::OperatorIncompatibleTypes { let err = ParseError::OperatorIncompatibleTypes {
op: name, op: Operator::Assignment(operator).as_str(),
lhs: lhs.ty.clone(), lhs: lhs.ty.clone(),
rhs: rhs.ty.clone(), rhs: rhs.ty.clone(),
op_span: op.span, op_span: op.span,

View file

@ -1,11 +1,10 @@
use crate::{ast::RedirectionSource, did_you_mean, Span, Type};
use miette::Diagnostic;
use serde::{Deserialize, Serialize};
use std::{ use std::{
fmt::Display, fmt::Display,
str::{from_utf8, Utf8Error}, str::{from_utf8, Utf8Error},
}; };
use crate::{ast::RedirectionSource, did_you_mean, Span, Type};
use miette::Diagnostic;
use serde::{Deserialize, Serialize};
use thiserror::Error; use thiserror::Error;
#[derive(Clone, Debug, Error, Diagnostic, Serialize, Deserialize)] #[derive(Clone, Debug, Error, Diagnostic, Serialize, Deserialize)]
@ -112,12 +111,12 @@ pub enum ParseError {
}, },
/// One or more of the values have types not supported by the operator. /// One or more of the values have types not supported by the operator.
#[error("{op} is not supported on values of type {unsupported}.")] #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
#[diagnostic(code(nu::parser::operator_unsupported_type))] #[diagnostic(code(nu::shell::operator_unsupported_type))]
OperatorUnsupportedType { OperatorUnsupportedType {
op: &'static str, op: &'static str,
unsupported: Type, unsupported: Type,
#[label = "does support this type"] #[label = "does not support '{unsupported}'"]
op_span: Span, op_span: Span,
#[label("{unsupported}")] #[label("{unsupported}")]
unsupported_span: Span, unsupported_span: Span,
@ -126,13 +125,13 @@ pub enum ParseError {
}, },
/// The operator supports the types of both values, but not the specific combination of their types. /// The operator supports the types of both values, but not the specific combination of their types.
#[error("{op} is not supported between types {lhs} and {rhs}.")] #[error("Types '{lhs}' and '{rhs}' are not compatiable for the '{op}' operator.")]
#[diagnostic(code(nu::parser::operator_incompatible_types))] #[diagnostic(code(nu::shell::operator_incompatible_types))]
OperatorIncompatibleTypes { OperatorIncompatibleTypes {
op: &'static str, op: &'static str,
lhs: Type, lhs: Type,
rhs: Type, rhs: Type,
#[label = "does not operate between these two types"] #[label = "does not operate between '{lhs}' and '{rhs}'"]
op_span: Span, op_span: Span,
#[label("{lhs}")] #[label("{lhs}")]
lhs_span: Span, lhs_span: Span,

View file

@ -12,33 +12,13 @@ use thiserror::Error;
/// and pass it into an error viewer to display to the user. /// and pass it into an error viewer to display to the user.
#[derive(Debug, Clone, Error, Diagnostic, PartialEq)] #[derive(Debug, Clone, Error, Diagnostic, PartialEq)]
pub enum ShellError { pub enum ShellError {
/// An operator received two arguments of incompatible types.
///
/// ## Resolution
///
/// Check each argument's type and convert one or both as needed.
///
#[deprecated = "use `OperatorUnsupportedType` or `OperatorTypeMismatch` instead"]
#[error("Type mismatch during operation.")]
#[diagnostic(code(nu::shell::type_mismatch))]
OperatorMismatch {
#[label = "type mismatch for operator"]
op_span: Span,
lhs_ty: String,
#[label("{lhs_ty}")]
lhs_span: Span,
rhs_ty: String,
#[label("{rhs_ty}")]
rhs_span: Span,
},
/// One or more of the values have types not supported by the operator. /// One or more of the values have types not supported by the operator.
#[error("{op} is not supported on values of type {unsupported}.")] #[error("The '{op}' operator does not work on values of type '{unsupported}'.")]
#[diagnostic(code(nu::shell::operator_unsupported_type))] #[diagnostic(code(nu::shell::operator_unsupported_type))]
OperatorUnsupportedType { OperatorUnsupportedType {
op: &'static str, op: Operator,
unsupported: Type, unsupported: Type,
#[label = "does support this type"] #[label = "does not support '{unsupported}'"]
op_span: Span, op_span: Span,
#[label("{unsupported}")] #[label("{unsupported}")]
unsupported_span: Span, unsupported_span: Span,
@ -47,13 +27,13 @@ pub enum ShellError {
}, },
/// The operator supports the types of both values, but not the specific combination of their types. /// The operator supports the types of both values, but not the specific combination of their types.
#[error("{op} is not supported between types {lhs} and {rhs}.")] #[error("Types '{lhs}' and '{rhs}' are not compatiable for the '{op}' operator.")]
#[diagnostic(code(nu::shell::operator_incompatible_types))] #[diagnostic(code(nu::shell::operator_incompatible_types))]
OperatorIncompatibleTypes { OperatorIncompatibleTypes {
op: &'static str, op: Operator,
lhs: Type, lhs: Type,
rhs: Type, rhs: Type,
#[label = "does not operate between these two types"] #[label = "does not operate between '{lhs}' and '{rhs}'"]
op_span: Span, op_span: Span,
#[label("{lhs}")] #[label("{lhs}")]
lhs_span: Span, lhs_span: Span,

View file

@ -2490,7 +2490,12 @@ impl Value {
(Value::Custom { val: lhs, .. }, rhs) => { (Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(self.span(), Operator::Math(Math::Add), op, rhs) lhs.operation(self.span(), Operator::Math(Math::Add), op, rhs)
} }
_ => Err(operator_type_error("addition", op, self, rhs, |val| { _ => Err(operator_type_error(
Operator::Math(Math::Add),
op,
self,
rhs,
|val| {
matches!( matches!(
val, val,
Value::Int { .. } Value::Int { .. }
@ -2500,7 +2505,8 @@ impl Value {
| Value::Duration { .. } | Value::Duration { .. }
| Value::Filesize { .. }, | Value::Filesize { .. },
) )
})), },
)),
} }
} }
@ -2573,7 +2579,12 @@ impl Value {
(Value::Custom { val: lhs, .. }, rhs) => { (Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(self.span(), Operator::Math(Math::Subtract), op, rhs) lhs.operation(self.span(), Operator::Math(Math::Subtract), op, rhs)
} }
_ => Err(operator_type_error("subtraction", op, self, rhs, |val| { _ => Err(operator_type_error(
Operator::Math(Math::Subtract),
op,
self,
rhs,
|val| {
matches!( matches!(
val, val,
Value::Int { .. } Value::Int { .. }
@ -2582,7 +2593,8 @@ impl Value {
| Value::Duration { .. } | Value::Duration { .. }
| Value::Filesize { .. }, | Value::Filesize { .. },
) )
})), },
)),
} }
} }
@ -2636,7 +2648,7 @@ impl Value {
lhs.operation(self.span(), Operator::Math(Math::Multiply), op, rhs) lhs.operation(self.span(), Operator::Math(Math::Multiply), op, rhs)
} }
_ => Err(operator_type_error( _ => Err(operator_type_error(
"multiplication", Operator::Math(Math::Multiply),
op, op,
self, self,
rhs, rhs,
@ -2758,7 +2770,12 @@ impl Value {
(Value::Custom { val: lhs, .. }, rhs) => { (Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs) lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs)
} }
_ => Err(operator_type_error("division", op, self, rhs, |val| { _ => Err(operator_type_error(
Operator::Math(Math::Divide),
op,
self,
rhs,
|val| {
matches!( matches!(
val, val,
Value::Int { .. } Value::Int { .. }
@ -2766,165 +2783,8 @@ impl Value {
| Value::Duration { .. } | Value::Duration { .. }
| Value::Filesize { .. }, | Value::Filesize { .. },
) )
})), },
} )),
}
pub fn modulo(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
// Based off the unstable `div_floor` function in the std library.
fn checked_mod_i64(dividend: i64, divisor: i64) -> Option<i64> {
let remainder = dividend.checked_rem(divisor)?;
if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) {
// Note that `remainder + divisor` cannot overflow, because `remainder` and
// `divisor` have opposite signs.
Some(remainder + divisor)
} else {
Some(remainder)
}
}
fn checked_mod_f64(dividend: f64, divisor: f64) -> Option<f64> {
if divisor == 0.0 {
None
} else {
let remainder = dividend % divisor;
if (remainder > 0.0 && divisor < 0.0) || (remainder < 0.0 && divisor > 0.0) {
Some(remainder + divisor)
} else {
Some(remainder)
}
}
}
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::int(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "modulo operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
Ok(Value::float(val, span))
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs, *rhs as f64) {
Ok(Value::float(val, span))
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs, *rhs) {
Ok(Value::float(val, span))
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::filesize(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "modulo operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::filesize(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "modulo operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
Ok(Value::filesize(val as i64, span))
} else {
Err(ShellError::OperatorOverflow {
msg: "modulo operation overflowed".into(),
span,
help: None,
})
}
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::duration(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "division operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::duration(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "division operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
Ok(Value::duration(val as i64, span))
} else {
Err(ShellError::OperatorOverflow {
msg: "division operation overflowed".into(),
span,
help: None,
})
}
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Math(Math::Modulo), op, rhs)
}
_ => Err(operator_type_error("modulo", op, self, rhs, |val| {
matches!(
val,
Value::Int { .. }
| Value::Float { .. }
| Value::Duration { .. }
| Value::Filesize { .. },
)
})),
} }
} }
@ -3074,7 +2934,7 @@ impl Value {
lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs) lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs)
} }
_ => Err(operator_type_error( _ => Err(operator_type_error(
"floor division", Operator::Math(Math::FloorDivide),
op, op,
self, self,
rhs, rhs,
@ -3091,6 +2951,205 @@ impl Value {
} }
} }
pub fn modulo(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
// Based off the unstable `div_floor` function in the std library.
fn checked_mod_i64(dividend: i64, divisor: i64) -> Option<i64> {
let remainder = dividend.checked_rem(divisor)?;
if (remainder > 0 && divisor < 0) || (remainder < 0 && divisor > 0) {
// Note that `remainder + divisor` cannot overflow, because `remainder` and
// `divisor` have opposite signs.
Some(remainder + divisor)
} else {
Some(remainder)
}
}
fn checked_mod_f64(dividend: f64, divisor: f64) -> Option<f64> {
if divisor == 0.0 {
None
} else {
let remainder = dividend % divisor;
if (remainder > 0.0 && divisor < 0.0) || (remainder < 0.0 && divisor > 0.0) {
Some(remainder + divisor)
} else {
Some(remainder)
}
}
}
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::int(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "modulo operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
Ok(Value::float(val, span))
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs, *rhs as f64) {
Ok(Value::float(val, span))
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs, *rhs) {
Ok(Value::float(val, span))
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Filesize { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::filesize(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "modulo operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::filesize(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "modulo operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Filesize { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
Ok(Value::filesize(val as i64, span))
} else {
Err(ShellError::OperatorOverflow {
msg: "modulo operation overflowed".into(),
span,
help: None,
})
}
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Duration { val: lhs, .. }, Value::Duration { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::duration(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "division operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = checked_mod_i64(*lhs, *rhs) {
Ok(Value::duration(val, span))
} else if *rhs == 0 {
Err(ShellError::DivisionByZero { span: op })
} else {
Err(ShellError::OperatorOverflow {
msg: "division operation overflowed".into(),
span,
help: None,
})
}
}
(Value::Duration { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
if let Some(val) = checked_mod_f64(*lhs as f64, *rhs) {
if i64::MIN as f64 <= val && val <= i64::MAX as f64 {
Ok(Value::duration(val as i64, span))
} else {
Err(ShellError::OperatorOverflow {
msg: "division operation overflowed".into(),
span,
help: None,
})
}
} else {
Err(ShellError::DivisionByZero { span: op })
}
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Math(Math::Modulo), op, rhs)
}
_ => Err(operator_type_error(
Operator::Math(Math::Modulo),
op,
self,
rhs,
|val| {
matches!(
val,
Value::Int { .. }
| Value::Float { .. }
| Value::Duration { .. }
| Value::Filesize { .. },
)
},
)),
}
}
pub fn pow(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = lhs.checked_pow(*rhs as u32) {
Ok(Value::int(val, span))
} else {
Err(ShellError::OperatorOverflow {
msg: "pow operation overflowed".into(),
span,
help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
})
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
Ok(Value::float((*lhs as f64).powf(*rhs), span))
}
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
Ok(Value::float(lhs.powf(*rhs as f64), span))
}
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
Ok(Value::float(lhs.powf(*rhs), span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Math(Math::Pow), op, rhs)
}
_ => Err(operator_type_error(
Operator::Math(Math::Pow),
op,
self,
rhs,
|val| matches!(val, Value::Int { .. } | Value::Float { .. }),
)),
}
}
pub fn concat(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> { pub fn concat(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) { match (self, rhs) {
(Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => { (Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => {
@ -3107,7 +3166,7 @@ impl Value {
lhs.operation(self.span(), Operator::Math(Math::Concatenate), op, rhs) lhs.operation(self.span(), Operator::Math(Math::Concatenate), op, rhs)
} }
_ => Err(operator_type_error( _ => Err(operator_type_error(
"concatentation", Operator::Math(Math::Concatenate),
op, op,
self, self,
rhs, rhs,
@ -3137,7 +3196,7 @@ impl Value {
if !type_compatible(self.get_type(), rhs.get_type()) { if !type_compatible(self.get_type(), rhs.get_type()) {
return Err(operator_type_error( return Err(operator_type_error(
"less than comparison", Operator::Comparison(Comparison::LessThan),
op, op,
self, self,
rhs, rhs,
@ -3179,7 +3238,7 @@ impl Value {
if !type_compatible(self.get_type(), rhs.get_type()) { if !type_compatible(self.get_type(), rhs.get_type()) {
return Err(operator_type_error( return Err(operator_type_error(
"less than or equal to comparison", Operator::Comparison(Comparison::LessThanOrEqual),
op, op,
self, self,
rhs, rhs,
@ -3224,7 +3283,7 @@ impl Value {
if !type_compatible(self.get_type(), rhs.get_type()) { if !type_compatible(self.get_type(), rhs.get_type()) {
return Err(operator_type_error( return Err(operator_type_error(
"greater than comparison", Operator::Comparison(Comparison::GreaterThan),
op, op,
self, self,
rhs, rhs,
@ -3266,7 +3325,7 @@ impl Value {
if !type_compatible(self.get_type(), rhs.get_type()) { if !type_compatible(self.get_type(), rhs.get_type()) {
return Err(operator_type_error( return Err(operator_type_error(
"greater than or equal to comparison", Operator::Comparison(Comparison::GreaterThanOrEqual),
op, op,
self, self,
rhs, rhs,
@ -3375,7 +3434,7 @@ impl Value {
| Value::Custom { .. } | Value::Custom { .. }
) { ) {
ShellError::OperatorIncompatibleTypes { ShellError::OperatorIncompatibleTypes {
op: "'in' comparison", op: Operator::Comparison(Comparison::In),
lhs: lhs.get_type(), lhs: lhs.get_type(),
rhs: rhs.get_type(), rhs: rhs.get_type(),
op_span: op, op_span: op,
@ -3385,7 +3444,7 @@ impl Value {
} }
} else { } else {
ShellError::OperatorUnsupportedType { ShellError::OperatorUnsupportedType {
op: "'in' comparison", op: Operator::Comparison(Comparison::In),
unsupported: rhs.get_type(), unsupported: rhs.get_type(),
op_span: op, op_span: op,
unsupported_span: rhs.span(), unsupported_span: rhs.span(),
@ -3447,7 +3506,7 @@ impl Value {
| Value::Custom { .. } | Value::Custom { .. }
) { ) {
ShellError::OperatorIncompatibleTypes { ShellError::OperatorIncompatibleTypes {
op: "'not-in' comparison", op: Operator::Comparison(Comparison::NotIn),
lhs: lhs.get_type(), lhs: lhs.get_type(),
rhs: rhs.get_type(), rhs: rhs.get_type(),
op_span: op, op_span: op,
@ -3457,7 +3516,7 @@ impl Value {
} }
} else { } else {
ShellError::OperatorUnsupportedType { ShellError::OperatorUnsupportedType {
op: "'not-in' comparison", op: Operator::Comparison(Comparison::NotIn),
unsupported: rhs.get_type(), unsupported: rhs.get_type(),
op_span: op, op_span: op,
unsupported_span: rhs.span(), unsupported_span: rhs.span(),
@ -3526,9 +3585,17 @@ impl Value {
op, op,
rhs, rhs,
), ),
_ => Err(operator_type_error("regex match", op, self, rhs, |val| { _ => Err(operator_type_error(
matches!(val, Value::String { .. }) if invert {
})), Operator::Comparison(Comparison::NotRegexMatch)
} else {
Operator::Comparison(Comparison::RegexMatch)
},
op,
self,
rhs,
|val| matches!(val, Value::String { .. }),
)),
} }
} }
@ -3544,7 +3611,7 @@ impl Value {
rhs, rhs,
), ),
_ => Err(operator_type_error( _ => Err(operator_type_error(
"starts-with comparison", Operator::Comparison(Comparison::StartsWith),
op, op,
self, self,
rhs, rhs,
@ -3565,7 +3632,7 @@ impl Value {
rhs, rhs,
), ),
_ => Err(operator_type_error( _ => Err(operator_type_error(
"ends-with comparison", Operator::Comparison(Comparison::EndsWith),
op, op,
self, self,
rhs, rhs,
@ -3574,6 +3641,60 @@ impl Value {
} }
} }
pub fn bit_or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
Ok(Value::int(*lhs | rhs, span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Bits(Bits::BitOr), op, rhs)
}
_ => Err(operator_type_error(
Operator::Bits(Bits::BitOr),
op,
self,
rhs,
|val| matches!(val, Value::Int { .. }),
)),
}
}
pub fn bit_xor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
Ok(Value::int(*lhs ^ rhs, span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Bits(Bits::BitXor), op, rhs)
}
_ => Err(operator_type_error(
Operator::Bits(Bits::BitXor),
op,
self,
rhs,
|val| matches!(val, Value::Int { .. }),
)),
}
}
pub fn bit_and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
Ok(Value::int(*lhs & rhs, span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Bits(Bits::BitAnd), op, rhs)
}
_ => Err(operator_type_error(
Operator::Bits(Bits::BitAnd),
op,
self,
rhs,
|val| matches!(val, Value::Int { .. }),
)),
}
}
pub fn bit_shl(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> { pub fn bit_shl(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) { match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => { (Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
@ -3594,7 +3715,7 @@ impl Value {
lhs.operation(span, Operator::Bits(Bits::ShiftLeft), op, rhs) lhs.operation(span, Operator::Bits(Bits::ShiftLeft), op, rhs)
} }
_ => Err(operator_type_error( _ => Err(operator_type_error(
"bit left shift", Operator::Bits(Bits::ShiftLeft),
op, op,
self, self,
rhs, rhs,
@ -3623,7 +3744,7 @@ impl Value {
lhs.operation(span, Operator::Bits(Bits::ShiftRight), op, rhs) lhs.operation(span, Operator::Bits(Bits::ShiftRight), op, rhs)
} }
_ => Err(operator_type_error( _ => Err(operator_type_error(
"bit right shift", Operator::Bits(Bits::ShiftRight),
op, op,
self, self,
rhs, rhs,
@ -3632,62 +3753,6 @@ impl Value {
} }
} }
pub fn bit_and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
Ok(Value::int(*lhs & rhs, span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Bits(Bits::BitAnd), op, rhs)
}
_ => Err(operator_type_error("bitwise and", op, self, rhs, |val| {
matches!(val, Value::Int { .. })
})),
}
}
pub fn bit_or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
Ok(Value::int(*lhs | rhs, span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Bits(Bits::BitOr), op, rhs)
}
_ => Err(operator_type_error("bitwise or", op, self, rhs, |val| {
matches!(val, Value::Int { .. })
})),
}
}
pub fn bit_xor(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
Ok(Value::int(*lhs ^ rhs, span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Bits(Bits::BitXor), op, rhs)
}
_ => Err(operator_type_error("bitwise xor", op, self, rhs, |val| {
matches!(val, Value::Int { .. })
})),
}
}
pub fn and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
Ok(Value::bool(*lhs && *rhs, span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Boolean(Boolean::And), op, rhs)
}
_ => Err(operator_type_error("boolean and", op, self, rhs, |val| {
matches!(val, Value::Bool { .. })
})),
}
}
pub fn or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> { pub fn or(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) { match (self, rhs) {
(Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => { (Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
@ -3696,9 +3761,13 @@ impl Value {
(Value::Custom { val: lhs, .. }, rhs) => { (Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Boolean(Boolean::Or), op, rhs) lhs.operation(span, Operator::Boolean(Boolean::Or), op, rhs)
} }
_ => Err(operator_type_error("boolean or", op, self, rhs, |val| { _ => Err(operator_type_error(
matches!(val, Value::Bool { .. }) Operator::Boolean(Boolean::Or),
})), op,
self,
rhs,
|val| matches!(val, Value::Bool { .. }),
)),
} }
} }
@ -3710,43 +3779,30 @@ impl Value {
(Value::Custom { val: lhs, .. }, rhs) => { (Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Boolean(Boolean::Xor), op, rhs) lhs.operation(span, Operator::Boolean(Boolean::Xor), op, rhs)
} }
_ => Err(operator_type_error("boolean xor", op, self, rhs, |val| {
matches!(val, Value::Bool { .. })
})),
}
}
pub fn pow(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
if let Some(val) = lhs.checked_pow(*rhs as u32) {
Ok(Value::int(val, span))
} else {
Err(ShellError::OperatorOverflow {
msg: "pow operation overflowed".into(),
span,
help: Some("Consider using floating point values for increased range by promoting operand with 'into float'. Note: float has reduced precision!".into()),
})
}
}
(Value::Int { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
Ok(Value::float((*lhs as f64).powf(*rhs), span))
}
(Value::Float { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
Ok(Value::float(lhs.powf(*rhs as f64), span))
}
(Value::Float { val: lhs, .. }, Value::Float { val: rhs, .. }) => {
Ok(Value::float(lhs.powf(*rhs), span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Math(Math::Pow), op, rhs)
}
_ => Err(operator_type_error( _ => Err(operator_type_error(
"exponentiation", Operator::Boolean(Boolean::Xor),
op, op,
self, self,
rhs, rhs,
|val| matches!(val, Value::Int { .. } | Value::Float { .. }), |val| matches!(val, Value::Bool { .. }),
)),
}
}
pub fn and(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
match (self, rhs) {
(Value::Bool { val: lhs, .. }, Value::Bool { val: rhs, .. }) => {
Ok(Value::bool(*lhs && *rhs, span))
}
(Value::Custom { val: lhs, .. }, rhs) => {
lhs.operation(span, Operator::Boolean(Boolean::And), op, rhs)
}
_ => Err(operator_type_error(
Operator::Boolean(Boolean::And),
op,
self,
rhs,
|val| matches!(val, Value::Bool { .. }),
)), )),
} }
} }
@ -3763,7 +3819,7 @@ fn type_compatible(a: Type, b: Type) -> bool {
} }
fn operator_type_error( fn operator_type_error(
op_name: &'static str, op: Operator,
op_span: Span, op_span: Span,
lhs: &Value, lhs: &Value,
rhs: &Value, rhs: &Value,
@ -3772,7 +3828,7 @@ fn operator_type_error(
let is_supported = |val| is_supported(val) || matches!(val, Value::Custom { .. }); let is_supported = |val| is_supported(val) || matches!(val, Value::Custom { .. });
match (is_supported(lhs), is_supported(rhs)) { match (is_supported(lhs), is_supported(rhs)) {
(true, true) => ShellError::OperatorIncompatibleTypes { (true, true) => ShellError::OperatorIncompatibleTypes {
op: op_name, op,
lhs: lhs.get_type(), lhs: lhs.get_type(),
rhs: rhs.get_type(), rhs: rhs.get_type(),
op_span, op_span,
@ -3781,14 +3837,14 @@ fn operator_type_error(
help: None, help: None,
}, },
(true, false) => ShellError::OperatorUnsupportedType { (true, false) => ShellError::OperatorUnsupportedType {
op: op_name, op,
unsupported: rhs.get_type(), unsupported: rhs.get_type(),
op_span, op_span,
unsupported_span: rhs.span(), unsupported_span: rhs.span(),
help: None, help: None,
}, },
(false, _) => ShellError::OperatorUnsupportedType { (false, _) => ShellError::OperatorUnsupportedType {
op: op_name, op,
unsupported: lhs.get_type(), unsupported: lhs.get_type(),
op_span, op_span,
unsupported_span: lhs.span(), unsupported_span: lhs.span(),

View file

@ -1,4 +1,7 @@
use nu_protocol::{ast, CustomValue, ShellError, Span, Value}; use nu_protocol::{
ast::{self, Math, Operator},
CustomValue, ShellError, Span, Value,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::cmp::Ordering; use std::cmp::Ordering;
@ -112,7 +115,7 @@ impl CustomValue for CoolCustomValue {
) -> Result<Value, ShellError> { ) -> Result<Value, ShellError> {
match operator { match operator {
// Append the string inside `cool` // Append the string inside `cool`
ast::Operator::Math(ast::Math::Concatenate) => { Operator::Math(Math::Concatenate) => {
if let Some(right) = right if let Some(right) = right
.as_custom_value() .as_custom_value()
.ok() .ok()
@ -126,7 +129,7 @@ impl CustomValue for CoolCustomValue {
)) ))
} else { } else {
Err(ShellError::OperatorUnsupportedType { Err(ShellError::OperatorUnsupportedType {
op: "concatenation", op: Operator::Math(Math::Concatenate),
unsupported: right.get_type(), unsupported: right.get_type(),
op_span, op_span,
unsupported_span: right.span(), unsupported_span: right.span(),

View file

@ -21,12 +21,12 @@ pub(super) fn between_dataframes(
Operator::Math(Math::Add) => { Operator::Math(Math::Add) => {
lhs.append_df(rhs, Axis::Row, Span::merge(left.span(), right.span())) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: left.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
} }
} }
@ -181,12 +181,12 @@ pub(super) fn compute_between_series(
span: operation_span, span: operation_span,
}), }),
}, },
_ => Err(ShellError::OperatorMismatch { op => Err(ShellError::OperatorUnsupportedType {
op,
unsupported: left.get_type(),
op_span: operator.span, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: left.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
} }
} }
@ -222,12 +222,12 @@ pub(super) fn compute_series_single_value(
right: &Value, right: &Value,
) -> Result<NuDataFrame, ShellError> { ) -> Result<NuDataFrame, ShellError> {
if !lhs.is_series() { if !lhs.is_series() {
return Err(ShellError::OperatorMismatch { return Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: left.get_type(),
op_span: operator.span, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: left.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}); });
} }
@ -243,12 +243,12 @@ pub(super) fn compute_series_single_value(
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::add, lhs_span) compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::add, lhs_span)
} }
Value::String { val, .. } => add_string_to_series(&lhs, val, 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Math(Math::Subtract) => match &right { Operator::Math(Math::Subtract) => match &right {
@ -258,12 +258,12 @@ pub(super) fn compute_series_single_value(
Value::Float { val, .. } => { Value::Float { val, .. } => {
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::sub, lhs_span) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Math(Math::Multiply) => match &right { Operator::Math(Math::Multiply) => match &right {
@ -273,12 +273,12 @@ pub(super) fn compute_series_single_value(
Value::Float { val, .. } => { Value::Float { val, .. } => {
compute_series_float(&lhs, *val, <ChunkedArray<Float64Type>>::mul, lhs_span) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Math(Math::Divide) => { 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) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
} }
} }
@ -319,12 +319,12 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => { Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::equal, lhs_span) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Comparison(Comparison::NotEqual) => match &right { Operator::Comparison(Comparison::NotEqual) => match &right {
@ -344,12 +344,12 @@ pub(super) fn compute_series_single_value(
ChunkedArray::not_equal, ChunkedArray::not_equal,
lhs_span, lhs_span,
), ),
_ => Err(ShellError::OperatorMismatch { _ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: right.get_type(),
op_span: operator.span, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Comparison(Comparison::LessThan) => match &right { Operator::Comparison(Comparison::LessThan) => match &right {
@ -360,12 +360,12 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => { Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::lt, lhs_span) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Comparison(Comparison::LessThanOrEqual) => match &right { Operator::Comparison(Comparison::LessThanOrEqual) => match &right {
@ -376,12 +376,12 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => { Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::lt_eq, lhs_span) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Comparison(Comparison::GreaterThan) => match &right { Operator::Comparison(Comparison::GreaterThan) => match &right {
@ -392,12 +392,12 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => { Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::gt, lhs_span) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Comparison(Comparison::GreaterThanOrEqual) => match &right { Operator::Comparison(Comparison::GreaterThanOrEqual) => match &right {
@ -408,23 +408,23 @@ pub(super) fn compute_series_single_value(
Value::Date { val, .. } => { Value::Date { val, .. } => {
compare_series_i64(&lhs, val.timestamp_millis(), ChunkedArray::gt_eq, lhs_span) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
// TODO: update this to do a regex match instead of a simple contains? // TODO: update this to do a regex match instead of a simple contains?
Operator::Comparison(Comparison::RegexMatch) => match &right { Operator::Comparison(Comparison::RegexMatch) => match &right {
Value::String { val, .. } => contains_series_pat(&lhs, val, lhs_span), 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Comparison(Comparison::StartsWith) => match &right { 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)); let starts_with_pattern = format!("^{}", fancy_regex::escape(val));
contains_series_pat(&lhs, &starts_with_pattern, lhs_span) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
Operator::Comparison(Comparison::EndsWith) => match &right { 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)); let ends_with_pattern = format!("{}$", fancy_regex::escape(val));
contains_series_pat(&lhs, &ends_with_pattern, lhs_span) 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, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: right.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
}, },
_ => Err(ShellError::OperatorMismatch { _ => Err(ShellError::OperatorUnsupportedType {
op: operator.item,
unsupported: left.get_type(),
op_span: operator.span, op_span: operator.span,
lhs_ty: left.get_type().to_string(), unsupported_span: left.span(),
lhs_span: left.span(), help: None,
rhs_ty: right.get_type().to_string(),
rhs_span: right.span(),
}), }),
} }
} }

View file

@ -133,23 +133,13 @@ fn with_operator(
.apply_with_expr(right.clone(), Expr::lt_eq) .apply_with_expr(right.clone(), Expr::lt_eq)
.cache(plugin, engine, lhs_span)? .cache(plugin, engine, lhs_span)?
.into_value(lhs_span)), .into_value(lhs_span)),
_ => op => Err(ShellError::OperatorUnsupportedType {
// Err(ShellError::OperatorMismatch { op,
// op_span,
// lhs_ty: Type::Custom(TYPE_NAME.into()).to_string(),
// lhs_span,
// rhs_ty: Type::Custom(TYPE_NAME.into()).to_string(),
// rhs_span,
// }),
{
Err(ShellError::OperatorUnsupportedType {
op: "TODO",
unsupported: Type::Custom(TYPE_NAME.into()), unsupported: Type::Custom(TYPE_NAME.into()),
op_span, op_span,
unsupported_span: lhs_span, unsupported_span: lhs_span,
help: None, help: None,
}) }),
}
} }
} }