Edit operator type errors

This commit is contained in:
Ian Manske 2024-11-23 20:59:39 -08:00
parent 9fb54a8f46
commit ca6db77a63
7 changed files with 435 additions and 397 deletions

View file

@ -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)
}

View file

@ -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, .. } => {

View file

@ -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)

View file

@ -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,
)
})
}

View file

@ -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

View file

@ -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};

View file

@ -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,
})
}
}