mirror of
https://github.com/nushell/nushell
synced 2024-12-27 21:43:09 +00:00
Edit operator type errors
This commit is contained in:
parent
9fb54a8f46
commit
ca6db77a63
7 changed files with 435 additions and 397 deletions
|
@ -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)
|
||||
|
|
|
@ -215,12 +215,7 @@ pub fn math_result_type(
|
|||
type_error("multiplication", op, lhs, rhs, |ty| {
|
||||
matches!(
|
||||
ty,
|
||||
Type::Int
|
||||
| Type::Float
|
||||
| Type::Number
|
||||
| Type::Date
|
||||
| Type::Duration
|
||||
| Type::Filesize,
|
||||
Type::Int | Type::Float | Type::Number | Type::Duration | Type::Filesize,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ pub enum ShellError {
|
|||
/// ## 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 {
|
||||
|
@ -30,6 +32,37 @@ pub enum ShellError {
|
|||
rhs_span: Span,
|
||||
},
|
||||
|
||||
/// One or more of the values have types not supported by the operator.
|
||||
#[error("{op} is not supported on values of type {unsupported}.")]
|
||||
#[diagnostic(code(nu::shell::operator_unsupported_type))]
|
||||
OperatorUnsupportedType {
|
||||
op: &'static str,
|
||||
unsupported: Type,
|
||||
#[label = "does support this type"]
|
||||
op_span: Span,
|
||||
#[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("{op} is not supported between types {lhs} and {rhs}.")]
|
||||
#[diagnostic(code(nu::shell::operator_type_mismatch))]
|
||||
OperatorTypeMismatch {
|
||||
op: &'static str,
|
||||
lhs: Type,
|
||||
rhs: Type,
|
||||
#[label = "does not operate between these two types"]
|
||||
op_span: Span,
|
||||
#[label("{lhs}")]
|
||||
lhs_span: Span,
|
||||
#[label("{rhs}")]
|
||||
rhs_span: Span,
|
||||
#[help]
|
||||
help: Option<&'static str>,
|
||||
},
|
||||
|
||||
/// An arithmetic operation's resulting value overflowed its possible size.
|
||||
///
|
||||
/// ## Resolution
|
||||
|
|
|
@ -2443,7 +2443,6 @@ impl Value {
|
|||
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
|
||||
Ok(Value::string(lhs.to_string() + rhs, span))
|
||||
}
|
||||
|
||||
(Value::Duration { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
|
||||
if let Some(val) = rhs.checked_add_signed(chrono::Duration::nanoseconds(*lhs)) {
|
||||
Ok(Value::date(val, span))
|
||||
|
@ -2488,43 +2487,20 @@ impl Value {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(self.span(), Operator::Math(Math::Plus), op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn concat(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||
match (self, rhs) {
|
||||
(Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => {
|
||||
Ok(Value::list([lhs.as_slice(), rhs.as_slice()].concat(), span))
|
||||
}
|
||||
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
|
||||
Ok(Value::string([lhs.as_str(), rhs.as_str()].join(""), span))
|
||||
}
|
||||
(Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => Ok(Value::binary(
|
||||
[lhs.as_slice(), rhs.as_slice()].concat(),
|
||||
span,
|
||||
)),
|
||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(self.span(), Operator::Math(Math::Concat), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error("addition", op, self, rhs, |val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::String { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Filesize { .. },
|
||||
)
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2552,7 +2528,6 @@ impl Value {
|
|||
}
|
||||
(Value::Date { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
|
||||
let result = lhs.signed_duration_since(*rhs);
|
||||
|
||||
if let Some(v) = result.num_nanoseconds() {
|
||||
Ok(Value::duration(v, span))
|
||||
} else {
|
||||
|
@ -2595,18 +2570,19 @@ impl Value {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(self.span(), Operator::Math(Math::Minus), op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error("subtraction", op, self, rhs, |val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Filesize { .. },
|
||||
)
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2659,13 +2635,21 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(self.span(), Operator::Math(Math::Multiply), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error(
|
||||
"multiplication",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Filesize { .. },
|
||||
)
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2774,14 +2758,15 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error("division", op, self, rhs, |val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Filesize { .. },
|
||||
)
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2931,14 +2916,15 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(span, Operator::Math(Math::Modulo), op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error("modulo", op, self, rhs, |val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Filesize { .. },
|
||||
)
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3087,13 +3073,51 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(self.span(), Operator::Math(Math::Divide), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error(
|
||||
"floor division",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Filesize { .. },
|
||||
)
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn concat(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||
match (self, rhs) {
|
||||
(Value::List { vals: lhs, .. }, Value::List { vals: rhs, .. }) => {
|
||||
Ok(Value::list([lhs.as_slice(), rhs.as_slice()].concat(), span))
|
||||
}
|
||||
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
|
||||
Ok(Value::string([lhs.as_str(), rhs.as_str()].join(""), span))
|
||||
}
|
||||
(Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => Ok(Value::binary(
|
||||
[lhs.as_slice(), rhs.as_slice()].concat(),
|
||||
span,
|
||||
)),
|
||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(self.span(), Operator::Math(Math::Concat), op, rhs)
|
||||
}
|
||||
_ => Err(operator_type_error(
|
||||
"concatentation",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::List { .. } | Value::String { .. } | Value::Binary { .. },
|
||||
)
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3111,30 +3135,32 @@ impl Value {
|
|||
return Ok(Value::nothing(span));
|
||||
}
|
||||
|
||||
if !type_compatible(self.get_type(), rhs.get_type())
|
||||
&& (self.get_type() != Type::Any)
|
||||
&& (rhs.get_type() != Type::Any)
|
||||
{
|
||||
return Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
});
|
||||
if !type_compatible(self.get_type(), rhs.get_type()) {
|
||||
return Err(operator_type_error(
|
||||
"less than comparison",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::String { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Bool { .. }
|
||||
| Value::Nothing { .. }
|
||||
)
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(ordering) = self.partial_cmp(rhs) {
|
||||
Ok(Value::bool(matches!(ordering, Ordering::Less), span))
|
||||
} else {
|
||||
Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
})
|
||||
}
|
||||
Ok(Value::bool(
|
||||
matches!(self.partial_cmp(rhs), Some(Ordering::Less)),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn lte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||
|
@ -3151,28 +3177,35 @@ impl Value {
|
|||
return Ok(Value::nothing(span));
|
||||
}
|
||||
|
||||
if !type_compatible(self.get_type(), rhs.get_type())
|
||||
&& (self.get_type() != Type::Any)
|
||||
&& (rhs.get_type() != Type::Any)
|
||||
{
|
||||
return Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
});
|
||||
if !type_compatible(self.get_type(), rhs.get_type()) {
|
||||
return Err(operator_type_error(
|
||||
"less than or equal to comparison",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::String { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Bool { .. }
|
||||
| Value::Nothing { .. }
|
||||
)
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
self.partial_cmp(rhs)
|
||||
.map(|ordering| Value::bool(matches!(ordering, Ordering::Less | Ordering::Equal), span))
|
||||
.ok_or(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
})
|
||||
Ok(Value::bool(
|
||||
matches!(
|
||||
self.partial_cmp(rhs),
|
||||
Some(Ordering::Less | Ordering::Equal)
|
||||
),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn gt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||
|
@ -3189,28 +3222,32 @@ impl Value {
|
|||
return Ok(Value::nothing(span));
|
||||
}
|
||||
|
||||
if !type_compatible(self.get_type(), rhs.get_type())
|
||||
&& (self.get_type() != Type::Any)
|
||||
&& (rhs.get_type() != Type::Any)
|
||||
{
|
||||
return Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
});
|
||||
if !type_compatible(self.get_type(), rhs.get_type()) {
|
||||
return Err(operator_type_error(
|
||||
"greater than comparison",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::String { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Bool { .. }
|
||||
| Value::Nothing { .. }
|
||||
)
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
self.partial_cmp(rhs)
|
||||
.map(|ordering| Value::bool(matches!(ordering, Ordering::Greater), span))
|
||||
.ok_or(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
})
|
||||
Ok(Value::bool(
|
||||
matches!(self.partial_cmp(rhs), Some(Ordering::Greater)),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn gte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||
|
@ -3227,32 +3264,35 @@ impl Value {
|
|||
return Ok(Value::nothing(span));
|
||||
}
|
||||
|
||||
if !type_compatible(self.get_type(), rhs.get_type())
|
||||
&& (self.get_type() != Type::Any)
|
||||
&& (rhs.get_type() != Type::Any)
|
||||
{
|
||||
return Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
});
|
||||
if !type_compatible(self.get_type(), rhs.get_type()) {
|
||||
return Err(operator_type_error(
|
||||
"greater than or equal to comparison",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| {
|
||||
matches!(
|
||||
val,
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::String { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Bool { .. }
|
||||
| Value::Nothing { .. }
|
||||
)
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
match self.partial_cmp(rhs) {
|
||||
Some(ordering) => Ok(Value::bool(
|
||||
matches!(ordering, Ordering::Greater | Ordering::Equal),
|
||||
span,
|
||||
)),
|
||||
None => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
}
|
||||
Ok(Value::bool(
|
||||
matches!(
|
||||
self.partial_cmp(rhs),
|
||||
Some(Ordering::Greater | Ordering::Equal)
|
||||
),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn eq(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||
|
@ -3265,22 +3305,10 @@ impl Value {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(ordering) = self.partial_cmp(rhs) {
|
||||
Ok(Value::bool(matches!(ordering, Ordering::Equal), span))
|
||||
} else {
|
||||
match (self, rhs) {
|
||||
(Value::Nothing { .. }, _) | (_, Value::Nothing { .. }) => {
|
||||
Ok(Value::bool(false, span))
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
Ok(Value::bool(
|
||||
matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn ne(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||
|
@ -3293,22 +3321,10 @@ impl Value {
|
|||
);
|
||||
}
|
||||
|
||||
if let Some(ordering) = self.partial_cmp(rhs) {
|
||||
Ok(Value::bool(!matches!(ordering, Ordering::Equal), span))
|
||||
} else {
|
||||
match (self, rhs) {
|
||||
(Value::Nothing { .. }, _) | (_, Value::Nothing { .. }) => {
|
||||
Ok(Value::bool(true, span))
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
Ok(Value::bool(
|
||||
!matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
|
||||
span,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn r#in(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||
|
@ -3349,13 +3365,34 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(self.span(), Operator::Comparison(Comparison::In), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
(lhs, rhs) => Err(
|
||||
if matches!(
|
||||
rhs,
|
||||
Value::List { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::String { .. }
|
||||
| Value::Record { .. }
|
||||
| Value::Custom { .. }
|
||||
) {
|
||||
ShellError::OperatorTypeMismatch {
|
||||
op: "'in' comparison",
|
||||
lhs: lhs.get_type(),
|
||||
rhs: rhs.get_type(),
|
||||
op_span: op,
|
||||
lhs_span: lhs.span(),
|
||||
rhs_span: rhs.span(),
|
||||
help: None,
|
||||
}
|
||||
} else {
|
||||
ShellError::OperatorUnsupportedType {
|
||||
op: "'in' comparison",
|
||||
unsupported: rhs.get_type(),
|
||||
op_span: op,
|
||||
unsupported_span: rhs.span(),
|
||||
help: None,
|
||||
}
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3400,13 +3437,34 @@ impl Value {
|
|||
op,
|
||||
rhs,
|
||||
),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
(lhs, rhs) => Err(
|
||||
if matches!(
|
||||
rhs,
|
||||
Value::List { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::String { .. }
|
||||
| Value::Record { .. }
|
||||
| Value::Custom { .. }
|
||||
) {
|
||||
ShellError::OperatorTypeMismatch {
|
||||
op: "'not-in' comparison",
|
||||
lhs: lhs.get_type(),
|
||||
rhs: rhs.get_type(),
|
||||
op_span: op,
|
||||
lhs_span: lhs.span(),
|
||||
rhs_span: rhs.span(),
|
||||
help: None,
|
||||
}
|
||||
} else {
|
||||
ShellError::OperatorUnsupportedType {
|
||||
op: "'not-in' comparison",
|
||||
unsupported: rhs.get_type(),
|
||||
op_span: op,
|
||||
unsupported_span: rhs.span(),
|
||||
help: None,
|
||||
}
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3468,13 +3526,9 @@ impl Value {
|
|||
op,
|
||||
rhs,
|
||||
),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error("regex match", op, self, rhs, |val| {
|
||||
matches!(val, Value::String { .. })
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3489,13 +3543,13 @@ impl Value {
|
|||
op,
|
||||
rhs,
|
||||
),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error(
|
||||
"starts-with comparison",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| matches!(val, Value::String { .. }),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3510,13 +3564,13 @@ impl Value {
|
|||
op,
|
||||
rhs,
|
||||
),
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error(
|
||||
"ends-with comparison",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| matches!(val, Value::String { .. }),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3539,13 +3593,13 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(span, Operator::Bits(Bits::ShiftLeft), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error(
|
||||
"bit left shift",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| matches!(val, Value::Int { .. }),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3568,49 +3622,13 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(span, Operator::Bits(Bits::ShiftRight), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
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(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
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(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error(
|
||||
"bit right shift",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| matches!(val, Value::Int { .. }),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3622,13 +3640,37 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(span, Operator::Bits(Bits::BitAnd), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => 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 { .. })
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3640,13 +3682,9 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(span, Operator::Boolean(Boolean::And), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error("boolean and", op, self, rhs, |val| {
|
||||
matches!(val, Value::Bool { .. })
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3658,13 +3696,9 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(span, Operator::Boolean(Boolean::Or), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error("boolean or", op, self, rhs, |val| {
|
||||
matches!(val, Value::Bool { .. })
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3676,13 +3710,9 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(span, Operator::Boolean(Boolean::Xor), op, rhs)
|
||||
}
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error("boolean xor", op, self, rhs, |val| {
|
||||
matches!(val, Value::Bool { .. })
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3711,14 +3741,13 @@ impl Value {
|
|||
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||
lhs.operation(span, Operator::Math(Math::Pow), op, rhs)
|
||||
}
|
||||
|
||||
_ => Err(ShellError::OperatorMismatch {
|
||||
op_span: op,
|
||||
lhs_ty: self.get_type().to_string(),
|
||||
lhs_span: self.span(),
|
||||
rhs_ty: rhs.get_type().to_string(),
|
||||
rhs_span: rhs.span(),
|
||||
}),
|
||||
_ => Err(operator_type_error(
|
||||
"exponentiation",
|
||||
op,
|
||||
self,
|
||||
rhs,
|
||||
|val| matches!(val, Value::Int { .. } | Value::Float { .. }),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3733,6 +3762,41 @@ fn type_compatible(a: Type, b: Type) -> bool {
|
|||
matches!((a, b), (Type::Int, Type::Float) | (Type::Float, Type::Int))
|
||||
}
|
||||
|
||||
fn operator_type_error(
|
||||
op_name: &'static str,
|
||||
op_span: Span,
|
||||
lhs: &Value,
|
||||
rhs: &Value,
|
||||
is_supported: fn(&Value) -> bool,
|
||||
) -> ShellError {
|
||||
let is_supported = |val| is_supported(val) || matches!(val, Value::Custom { .. });
|
||||
match (is_supported(lhs), is_supported(rhs)) {
|
||||
(true, true) => ShellError::OperatorTypeMismatch {
|
||||
op: op_name,
|
||||
lhs: lhs.get_type(),
|
||||
rhs: rhs.get_type(),
|
||||
op_span,
|
||||
lhs_span: lhs.span(),
|
||||
rhs_span: rhs.span(),
|
||||
help: None,
|
||||
},
|
||||
(true, false) => ShellError::OperatorUnsupportedType {
|
||||
op: op_name,
|
||||
unsupported: rhs.get_type(),
|
||||
op_span,
|
||||
unsupported_span: rhs.span(),
|
||||
help: None,
|
||||
},
|
||||
(false, _) => ShellError::OperatorUnsupportedType {
|
||||
op: op_name,
|
||||
unsupported: lhs.get_type(),
|
||||
op_span,
|
||||
unsupported_span: lhs.span(),
|
||||
help: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Record, Value};
|
||||
|
|
|
@ -105,7 +105,7 @@ impl CustomValue for CoolCustomValue {
|
|||
|
||||
fn operation(
|
||||
&self,
|
||||
lhs_span: Span,
|
||||
_lhs_span: Span,
|
||||
operator: ast::Operator,
|
||||
op_span: Span,
|
||||
right: &Value,
|
||||
|
@ -125,12 +125,12 @@ impl CustomValue for CoolCustomValue {
|
|||
op_span,
|
||||
))
|
||||
} else {
|
||||
Err(ShellError::OperatorMismatch {
|
||||
Err(ShellError::OperatorUnsupportedType {
|
||||
op: "concatenation",
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue