mirror of
https://github.com/nushell/nushell
synced 2025-01-15 22:54:16 +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
|
Pick::Median
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut sorted = vec![];
|
let mut sorted = values
|
||||||
|
.iter()
|
||||||
for item in values {
|
.filter(|x| !x.as_float().is_ok_and(f64::is_nan))
|
||||||
sorted.push(item.clone());
|
.collect::<Vec<_>>();
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
|
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
|
||||||
|
|
||||||
match take {
|
match take {
|
||||||
Pick::Median => {
|
Pick::Median => {
|
||||||
let idx = (values.len() as f64 / 2.0).floor() as usize;
|
let idx = (values.len() as f64 / 2.0).floor() as usize;
|
||||||
let out = sorted
|
Ok(sorted
|
||||||
.get(idx)
|
.get(idx)
|
||||||
.ok_or_else(|| ShellError::UnsupportedInput {
|
.ok_or_else(|| ShellError::UnsupportedInput {
|
||||||
msg: "Empty input".to_string(),
|
msg: "Empty input".to_string(),
|
||||||
input: "value originates from here".into(),
|
input: "value originates from here".into(),
|
||||||
msg_span: head,
|
msg_span: head,
|
||||||
input_span: span,
|
input_span: span,
|
||||||
})?;
|
})?
|
||||||
Ok(out.clone())
|
.to_owned()
|
||||||
|
.to_owned())
|
||||||
}
|
}
|
||||||
Pick::MedianAverage => {
|
Pick::MedianAverage => {
|
||||||
let idx_end = values.len() / 2;
|
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,
|
msg_span: head,
|
||||||
input_span: span,
|
input_span: span,
|
||||||
})?
|
})?
|
||||||
.clone();
|
.to_owned()
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
let right = sorted
|
let right = sorted
|
||||||
.get(idx_end)
|
.get(idx_end)
|
||||||
|
@ -153,7 +135,8 @@ pub fn median(values: &[Value], span: Span, head: Span) -> Result<Value, ShellEr
|
||||||
msg_span: head,
|
msg_span: head,
|
||||||
input_span: span,
|
input_span: span,
|
||||||
})?
|
})?
|
||||||
.clone();
|
.to_owned()
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
average(&[left, right], span, head)
|
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> {
|
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
|
//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
|
// But f64 doesn't implement Hash, so we get the binary representation to use as
|
||||||
// key in the HashMap
|
// key in the HashMap
|
||||||
let hashable_values = values
|
let hashable_values = values
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|x| !x.as_float().is_ok_and(f64::is_nan))
|
||||||
.map(|val| match val {
|
.map(|val| match val {
|
||||||
Value::Int { val, .. } => Ok(HashableType::new(val.to_ne_bytes(), NumberTypes::Int)),
|
Value::Int { val, .. } => Ok(HashableType::new(val.to_ne_bytes(), NumberTypes::Int)),
|
||||||
Value::Duration { val, .. } => {
|
Value::Duration { val, .. } => {
|
||||||
|
|
|
@ -32,18 +32,8 @@ pub fn max(data: Vec<Value>, span: Span, head: Span) -> Result<Value, ShellError
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
for value in &data {
|
for value in &data {
|
||||||
if let Some(result) = value.partial_cmp(&biggest) {
|
if value.partial_cmp(&biggest) == Some(Ordering::Greater) {
|
||||||
if result == Ordering::Greater {
|
biggest = value.clone();
|
||||||
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(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(biggest)
|
Ok(biggest)
|
||||||
|
@ -61,18 +51,8 @@ pub fn min(data: Vec<Value>, span: Span, head: Span) -> Result<Value, ShellError
|
||||||
.clone();
|
.clone();
|
||||||
|
|
||||||
for value in &data {
|
for value in &data {
|
||||||
if let Some(result) = value.partial_cmp(&smallest) {
|
if value.partial_cmp(&smallest) == Some(Ordering::Less) {
|
||||||
if result == Ordering::Less {
|
smallest = value.clone();
|
||||||
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(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(smallest)
|
Ok(smallest)
|
||||||
|
|
|
@ -215,12 +215,7 @@ pub fn math_result_type(
|
||||||
type_error("multiplication", op, lhs, rhs, |ty| {
|
type_error("multiplication", op, lhs, rhs, |ty| {
|
||||||
matches!(
|
matches!(
|
||||||
ty,
|
ty,
|
||||||
Type::Int
|
Type::Int | Type::Float | Type::Number | Type::Duration | Type::Filesize,
|
||||||
| Type::Float
|
|
||||||
| Type::Number
|
|
||||||
| Type::Date
|
|
||||||
| Type::Duration
|
|
||||||
| Type::Filesize,
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ pub enum ShellError {
|
||||||
/// ## Resolution
|
/// ## Resolution
|
||||||
///
|
///
|
||||||
/// Check each argument's type and convert one or both as needed.
|
/// Check each argument's type and convert one or both as needed.
|
||||||
|
///
|
||||||
|
#[deprecated = "use `OperatorUnsupportedType` or `OperatorTypeMismatch` instead"]
|
||||||
#[error("Type mismatch during operation.")]
|
#[error("Type mismatch during operation.")]
|
||||||
#[diagnostic(code(nu::shell::type_mismatch))]
|
#[diagnostic(code(nu::shell::type_mismatch))]
|
||||||
OperatorMismatch {
|
OperatorMismatch {
|
||||||
|
@ -30,6 +32,37 @@ pub enum ShellError {
|
||||||
rhs_span: Span,
|
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.
|
/// An arithmetic operation's resulting value overflowed its possible size.
|
||||||
///
|
///
|
||||||
/// ## Resolution
|
/// ## Resolution
|
||||||
|
|
|
@ -2443,7 +2443,6 @@ impl Value {
|
||||||
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
|
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => {
|
||||||
Ok(Value::string(lhs.to_string() + rhs, span))
|
Ok(Value::string(lhs.to_string() + rhs, span))
|
||||||
}
|
}
|
||||||
|
|
||||||
(Value::Duration { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
|
(Value::Duration { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
|
||||||
if let Some(val) = rhs.checked_add_signed(chrono::Duration::nanoseconds(*lhs)) {
|
if let Some(val) = rhs.checked_add_signed(chrono::Duration::nanoseconds(*lhs)) {
|
||||||
Ok(Value::date(val, span))
|
Ok(Value::date(val, span))
|
||||||
|
@ -2488,43 +2487,20 @@ impl Value {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(self.span(), Operator::Math(Math::Plus), op, rhs)
|
lhs.operation(self.span(), Operator::Math(Math::Plus), op, rhs)
|
||||||
}
|
}
|
||||||
|
_ => Err(operator_type_error("addition", op, self, rhs, |val| {
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
matches!(
|
||||||
op_span: op,
|
val,
|
||||||
lhs_ty: self.get_type().to_string(),
|
Value::Int { .. }
|
||||||
lhs_span: self.span(),
|
| Value::Float { .. }
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
| Value::String { .. }
|
||||||
rhs_span: rhs.span(),
|
| Value::Date { .. }
|
||||||
}),
|
| 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(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(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2552,7 +2528,6 @@ impl Value {
|
||||||
}
|
}
|
||||||
(Value::Date { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
|
(Value::Date { val: lhs, .. }, Value::Date { val: rhs, .. }) => {
|
||||||
let result = lhs.signed_duration_since(*rhs);
|
let result = lhs.signed_duration_since(*rhs);
|
||||||
|
|
||||||
if let Some(v) = result.num_nanoseconds() {
|
if let Some(v) = result.num_nanoseconds() {
|
||||||
Ok(Value::duration(v, span))
|
Ok(Value::duration(v, span))
|
||||||
} else {
|
} else {
|
||||||
|
@ -2595,18 +2570,19 @@ impl Value {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(self.span(), Operator::Math(Math::Minus), op, rhs)
|
lhs.operation(self.span(), Operator::Math(Math::Minus), op, rhs)
|
||||||
}
|
}
|
||||||
|
_ => Err(operator_type_error("subtraction", op, self, rhs, |val| {
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
matches!(
|
||||||
op_span: op,
|
val,
|
||||||
lhs_ty: self.get_type().to_string(),
|
Value::Int { .. }
|
||||||
lhs_span: self.span(),
|
| Value::Float { .. }
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
| Value::Date { .. }
|
||||||
rhs_span: rhs.span(),
|
| Value::Duration { .. }
|
||||||
}),
|
| Value::Filesize { .. },
|
||||||
|
)
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2659,13 +2635,21 @@ impl Value {
|
||||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(self.span(), Operator::Math(Math::Multiply), op, rhs)
|
lhs.operation(self.span(), Operator::Math(Math::Multiply), op, rhs)
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error(
|
||||||
op_span: op,
|
"multiplication",
|
||||||
lhs_ty: self.get_type().to_string(),
|
op,
|
||||||
lhs_span: self.span(),
|
self,
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
rhs,
|
||||||
rhs_span: rhs.span(),
|
|val| {
|
||||||
}),
|
matches!(
|
||||||
|
val,
|
||||||
|
Value::Int { .. }
|
||||||
|
| Value::Float { .. }
|
||||||
|
| Value::Duration { .. }
|
||||||
|
| Value::Filesize { .. },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2774,14 +2758,15 @@ 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(ShellError::OperatorMismatch {
|
matches!(
|
||||||
op_span: op,
|
val,
|
||||||
lhs_ty: self.get_type().to_string(),
|
Value::Int { .. }
|
||||||
lhs_span: self.span(),
|
| Value::Float { .. }
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
| Value::Duration { .. }
|
||||||
rhs_span: rhs.span(),
|
| Value::Filesize { .. },
|
||||||
}),
|
)
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2931,14 +2916,15 @@ impl Value {
|
||||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(span, Operator::Math(Math::Modulo), op, rhs)
|
lhs.operation(span, Operator::Math(Math::Modulo), op, rhs)
|
||||||
}
|
}
|
||||||
|
_ => Err(operator_type_error("modulo", op, self, rhs, |val| {
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
matches!(
|
||||||
op_span: op,
|
val,
|
||||||
lhs_ty: self.get_type().to_string(),
|
Value::Int { .. }
|
||||||
lhs_span: self.span(),
|
| Value::Float { .. }
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
| Value::Duration { .. }
|
||||||
rhs_span: rhs.span(),
|
| Value::Filesize { .. },
|
||||||
}),
|
)
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3087,13 +3073,51 @@ 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(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error(
|
||||||
op_span: op,
|
"floor division",
|
||||||
lhs_ty: self.get_type().to_string(),
|
op,
|
||||||
lhs_span: self.span(),
|
self,
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
rhs,
|
||||||
rhs_span: rhs.span(),
|
|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));
|
return Ok(Value::nothing(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !type_compatible(self.get_type(), rhs.get_type())
|
if !type_compatible(self.get_type(), rhs.get_type()) {
|
||||||
&& (self.get_type() != Type::Any)
|
return Err(operator_type_error(
|
||||||
&& (rhs.get_type() != Type::Any)
|
"less than comparison",
|
||||||
{
|
op,
|
||||||
return Err(ShellError::OperatorMismatch {
|
self,
|
||||||
op_span: op,
|
rhs,
|
||||||
lhs_ty: self.get_type().to_string(),
|
|val| {
|
||||||
lhs_span: self.span(),
|
matches!(
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
val,
|
||||||
rhs_span: rhs.span(),
|
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(
|
||||||
Ok(Value::bool(matches!(ordering, Ordering::Less), span))
|
matches!(self.partial_cmp(rhs), Some(Ordering::Less)),
|
||||||
} else {
|
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(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
pub fn lte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||||
|
@ -3151,28 +3177,35 @@ impl Value {
|
||||||
return Ok(Value::nothing(span));
|
return Ok(Value::nothing(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !type_compatible(self.get_type(), rhs.get_type())
|
if !type_compatible(self.get_type(), rhs.get_type()) {
|
||||||
&& (self.get_type() != Type::Any)
|
return Err(operator_type_error(
|
||||||
&& (rhs.get_type() != Type::Any)
|
"less than or equal to comparison",
|
||||||
{
|
op,
|
||||||
return Err(ShellError::OperatorMismatch {
|
self,
|
||||||
op_span: op,
|
rhs,
|
||||||
lhs_ty: self.get_type().to_string(),
|
|val| {
|
||||||
lhs_span: self.span(),
|
matches!(
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
val,
|
||||||
rhs_span: rhs.span(),
|
Value::Int { .. }
|
||||||
});
|
| Value::Float { .. }
|
||||||
|
| Value::String { .. }
|
||||||
|
| Value::Filesize { .. }
|
||||||
|
| Value::Duration { .. }
|
||||||
|
| Value::Date { .. }
|
||||||
|
| Value::Bool { .. }
|
||||||
|
| Value::Nothing { .. }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.partial_cmp(rhs)
|
Ok(Value::bool(
|
||||||
.map(|ordering| Value::bool(matches!(ordering, Ordering::Less | Ordering::Equal), span))
|
matches!(
|
||||||
.ok_or(ShellError::OperatorMismatch {
|
self.partial_cmp(rhs),
|
||||||
op_span: op,
|
Some(Ordering::Less | Ordering::Equal)
|
||||||
lhs_ty: self.get_type().to_string(),
|
),
|
||||||
lhs_span: self.span(),
|
span,
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
))
|
||||||
rhs_span: rhs.span(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
pub fn gt(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||||
|
@ -3189,28 +3222,32 @@ impl Value {
|
||||||
return Ok(Value::nothing(span));
|
return Ok(Value::nothing(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !type_compatible(self.get_type(), rhs.get_type())
|
if !type_compatible(self.get_type(), rhs.get_type()) {
|
||||||
&& (self.get_type() != Type::Any)
|
return Err(operator_type_error(
|
||||||
&& (rhs.get_type() != Type::Any)
|
"greater than comparison",
|
||||||
{
|
op,
|
||||||
return Err(ShellError::OperatorMismatch {
|
self,
|
||||||
op_span: op,
|
rhs,
|
||||||
lhs_ty: self.get_type().to_string(),
|
|val| {
|
||||||
lhs_span: self.span(),
|
matches!(
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
val,
|
||||||
rhs_span: rhs.span(),
|
Value::Int { .. }
|
||||||
});
|
| Value::Float { .. }
|
||||||
|
| Value::String { .. }
|
||||||
|
| Value::Filesize { .. }
|
||||||
|
| Value::Duration { .. }
|
||||||
|
| Value::Date { .. }
|
||||||
|
| Value::Bool { .. }
|
||||||
|
| Value::Nothing { .. }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
self.partial_cmp(rhs)
|
Ok(Value::bool(
|
||||||
.map(|ordering| Value::bool(matches!(ordering, Ordering::Greater), span))
|
matches!(self.partial_cmp(rhs), Some(Ordering::Greater)),
|
||||||
.ok_or(ShellError::OperatorMismatch {
|
span,
|
||||||
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 gte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
pub fn gte(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||||
|
@ -3227,32 +3264,35 @@ impl Value {
|
||||||
return Ok(Value::nothing(span));
|
return Ok(Value::nothing(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !type_compatible(self.get_type(), rhs.get_type())
|
if !type_compatible(self.get_type(), rhs.get_type()) {
|
||||||
&& (self.get_type() != Type::Any)
|
return Err(operator_type_error(
|
||||||
&& (rhs.get_type() != Type::Any)
|
"greater than or equal to comparison",
|
||||||
{
|
op,
|
||||||
return Err(ShellError::OperatorMismatch {
|
self,
|
||||||
op_span: op,
|
rhs,
|
||||||
lhs_ty: self.get_type().to_string(),
|
|val| {
|
||||||
lhs_span: self.span(),
|
matches!(
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
val,
|
||||||
rhs_span: rhs.span(),
|
Value::Int { .. }
|
||||||
});
|
| Value::Float { .. }
|
||||||
|
| Value::String { .. }
|
||||||
|
| Value::Filesize { .. }
|
||||||
|
| Value::Duration { .. }
|
||||||
|
| Value::Date { .. }
|
||||||
|
| Value::Bool { .. }
|
||||||
|
| Value::Nothing { .. }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.partial_cmp(rhs) {
|
Ok(Value::bool(
|
||||||
Some(ordering) => Ok(Value::bool(
|
matches!(
|
||||||
matches!(ordering, Ordering::Greater | Ordering::Equal),
|
self.partial_cmp(rhs),
|
||||||
span,
|
Some(Ordering::Greater | Ordering::Equal)
|
||||||
)),
|
),
|
||||||
None => Err(ShellError::OperatorMismatch {
|
span,
|
||||||
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 eq(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
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(
|
||||||
Ok(Value::bool(matches!(ordering, Ordering::Equal), span))
|
matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
|
||||||
} else {
|
span,
|
||||||
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(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ne(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
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(
|
||||||
Ok(Value::bool(!matches!(ordering, Ordering::Equal), span))
|
!matches!(self.partial_cmp(rhs), Some(Ordering::Equal)),
|
||||||
} else {
|
span,
|
||||||
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(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn r#in(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
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) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(self.span(), Operator::Comparison(Comparison::In), op, rhs)
|
lhs.operation(self.span(), Operator::Comparison(Comparison::In), op, rhs)
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
(lhs, rhs) => Err(
|
||||||
op_span: op,
|
if matches!(
|
||||||
lhs_ty: self.get_type().to_string(),
|
rhs,
|
||||||
lhs_span: self.span(),
|
Value::List { .. }
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
| Value::Range { .. }
|
||||||
rhs_span: rhs.span(),
|
| 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,
|
op,
|
||||||
rhs,
|
rhs,
|
||||||
),
|
),
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
(lhs, rhs) => Err(
|
||||||
op_span: op,
|
if matches!(
|
||||||
lhs_ty: self.get_type().to_string(),
|
rhs,
|
||||||
lhs_span: self.span(),
|
Value::List { .. }
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
| Value::Range { .. }
|
||||||
rhs_span: rhs.span(),
|
| 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,
|
op,
|
||||||
rhs,
|
rhs,
|
||||||
),
|
),
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error("regex match", op, self, rhs, |val| {
|
||||||
op_span: op,
|
matches!(val, Value::String { .. })
|
||||||
lhs_ty: self.get_type().to_string(),
|
})),
|
||||||
lhs_span: self.span(),
|
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
|
||||||
rhs_span: rhs.span(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3489,13 +3543,13 @@ impl Value {
|
||||||
op,
|
op,
|
||||||
rhs,
|
rhs,
|
||||||
),
|
),
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error(
|
||||||
op_span: op,
|
"starts-with comparison",
|
||||||
lhs_ty: self.get_type().to_string(),
|
op,
|
||||||
lhs_span: self.span(),
|
self,
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
rhs,
|
||||||
rhs_span: rhs.span(),
|
|val| matches!(val, Value::String { .. }),
|
||||||
}),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3510,13 +3564,13 @@ impl Value {
|
||||||
op,
|
op,
|
||||||
rhs,
|
rhs,
|
||||||
),
|
),
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error(
|
||||||
op_span: op,
|
"ends-with comparison",
|
||||||
lhs_ty: self.get_type().to_string(),
|
op,
|
||||||
lhs_span: self.span(),
|
self,
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
rhs,
|
||||||
rhs_span: rhs.span(),
|
|val| matches!(val, Value::String { .. }),
|
||||||
}),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3539,13 +3593,13 @@ impl Value {
|
||||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(span, Operator::Bits(Bits::ShiftLeft), op, rhs)
|
lhs.operation(span, Operator::Bits(Bits::ShiftLeft), op, rhs)
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error(
|
||||||
op_span: op,
|
"bit left shift",
|
||||||
lhs_ty: self.get_type().to_string(),
|
op,
|
||||||
lhs_span: self.span(),
|
self,
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
rhs,
|
||||||
rhs_span: rhs.span(),
|
|val| matches!(val, Value::Int { .. }),
|
||||||
}),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3568,49 +3622,13 @@ impl Value {
|
||||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(span, Operator::Bits(Bits::ShiftRight), op, rhs)
|
lhs.operation(span, Operator::Bits(Bits::ShiftRight), op, rhs)
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error(
|
||||||
op_span: op,
|
"bit right shift",
|
||||||
lhs_ty: self.get_type().to_string(),
|
op,
|
||||||
lhs_span: self.span(),
|
self,
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
rhs,
|
||||||
rhs_span: rhs.span(),
|
|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(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(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3622,13 +3640,37 @@ impl Value {
|
||||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(span, Operator::Bits(Bits::BitAnd), op, rhs)
|
lhs.operation(span, Operator::Bits(Bits::BitAnd), op, rhs)
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error("bitwise and", op, self, rhs, |val| {
|
||||||
op_span: op,
|
matches!(val, Value::Int { .. })
|
||||||
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(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) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(span, Operator::Boolean(Boolean::And), op, rhs)
|
lhs.operation(span, Operator::Boolean(Boolean::And), op, rhs)
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error("boolean and", op, self, rhs, |val| {
|
||||||
op_span: op,
|
matches!(val, Value::Bool { .. })
|
||||||
lhs_ty: self.get_type().to_string(),
|
})),
|
||||||
lhs_span: self.span(),
|
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
|
||||||
rhs_span: rhs.span(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3658,13 +3696,9 @@ 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(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error("boolean or", op, self, rhs, |val| {
|
||||||
op_span: op,
|
matches!(val, Value::Bool { .. })
|
||||||
lhs_ty: self.get_type().to_string(),
|
})),
|
||||||
lhs_span: self.span(),
|
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
|
||||||
rhs_span: rhs.span(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3676,13 +3710,9 @@ 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(ShellError::OperatorMismatch {
|
_ => Err(operator_type_error("boolean xor", op, self, rhs, |val| {
|
||||||
op_span: op,
|
matches!(val, Value::Bool { .. })
|
||||||
lhs_ty: self.get_type().to_string(),
|
})),
|
||||||
lhs_span: self.span(),
|
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
|
||||||
rhs_span: rhs.span(),
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3711,14 +3741,13 @@ impl Value {
|
||||||
(Value::Custom { val: lhs, .. }, rhs) => {
|
(Value::Custom { val: lhs, .. }, rhs) => {
|
||||||
lhs.operation(span, Operator::Math(Math::Pow), op, rhs)
|
lhs.operation(span, Operator::Math(Math::Pow), op, rhs)
|
||||||
}
|
}
|
||||||
|
_ => Err(operator_type_error(
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
"exponentiation",
|
||||||
op_span: op,
|
op,
|
||||||
lhs_ty: self.get_type().to_string(),
|
self,
|
||||||
lhs_span: self.span(),
|
rhs,
|
||||||
rhs_ty: rhs.get_type().to_string(),
|
|val| matches!(val, Value::Int { .. } | Value::Float { .. }),
|
||||||
rhs_span: rhs.span(),
|
)),
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3733,6 +3762,41 @@ fn type_compatible(a: Type, b: Type) -> bool {
|
||||||
matches!((a, b), (Type::Int, Type::Float) | (Type::Float, Type::Int))
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{Record, Value};
|
use super::{Record, Value};
|
||||||
|
|
|
@ -105,7 +105,7 @@ impl CustomValue for CoolCustomValue {
|
||||||
|
|
||||||
fn operation(
|
fn operation(
|
||||||
&self,
|
&self,
|
||||||
lhs_span: Span,
|
_lhs_span: Span,
|
||||||
operator: ast::Operator,
|
operator: ast::Operator,
|
||||||
op_span: Span,
|
op_span: Span,
|
||||||
right: &Value,
|
right: &Value,
|
||||||
|
@ -125,12 +125,12 @@ impl CustomValue for CoolCustomValue {
|
||||||
op_span,
|
op_span,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::OperatorMismatch {
|
Err(ShellError::OperatorUnsupportedType {
|
||||||
|
op: "concatenation",
|
||||||
|
unsupported: right.get_type(),
|
||||||
op_span,
|
op_span,
|
||||||
lhs_ty: self.typetag_name().into(),
|
unsupported_span: right.span(),
|
||||||
lhs_span,
|
help: None,
|
||||||
rhs_ty: right.get_type().to_string(),
|
|
||||||
rhs_span: right.span(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue