Add =~ and !~ operators on strings

`left =~ right` return true if left contains right, using Rust's
`String::contains`. `!~` is the negated version.

A new `apply_operator` function is added which decouples evaluation from
`Value::compare`. This returns a `Value` and opens the door to
implementing `+` for example, though it wouldn't be useful immediately.

The `operator!` macro had to be changed slightly as it would choke on
`~` in arguments.
This commit is contained in:
Belhorma Bendebiche
2019-11-21 12:18:00 -05:00
parent 06857fbc52
commit fbc6f01cfb
6 changed files with 176 additions and 11 deletions

View File

@ -1,5 +1,6 @@
use crate::data::base::Block;
use crate::errors::ArgumentError;
use crate::evaluate::operator::apply_operator;
use crate::parser::hir::path::{ColumnPath, RawPathMember};
use crate::parser::{
hir::{self, Expression, RawExpression},
@ -79,8 +80,8 @@ pub(crate) fn evaluate_baseline_expr(
trace!("left={:?} right={:?}", left.item, right.item);
match left.compare(binary.op(), &*right) {
Ok(result) => Ok(Value::boolean(result).tagged(tag)),
match apply_operator(binary.op(), &*left, &*right) {
Ok(result) => Ok(result.tagged(tag)),
Err((left_type, right_type)) => Err(ShellError::coerce_error(
left_type.spanned(binary.left().span),
right_type.spanned(binary.right().span),

View File

@ -1,3 +1,4 @@
pub(crate) mod evaluator;
pub(crate) mod operator;
pub(crate) use evaluator::{evaluate_baseline_expr, Scope};

33
src/evaluate/operator.rs Normal file
View File

@ -0,0 +1,33 @@
use crate::data::Primitive;
use crate::data::Value;
use crate::parser::Operator;
use crate::traits::ShellTypeName;
use std::ops::Not;
pub fn apply_operator(
op: &Operator,
left: &Value,
right: &Value,
) -> Result<Value, (&'static str, &'static str)> {
match *op {
Operator::Equal
| Operator::NotEqual
| Operator::LessThan
| Operator::GreaterThan
| Operator::LessThanOrEqual
| Operator::GreaterThanOrEqual => left.compare(op, right).map(Value::boolean),
Operator::Dot => Ok(Value::boolean(false)),
Operator::Contains => contains(left, right).map(Value::boolean),
Operator::NotContains => contains(left, right).map(Not::not).map(Value::boolean),
}
}
fn contains(left: &Value, right: &Value) -> Result<bool, (&'static str, &'static str)> {
if let (Value::Primitive(Primitive::String(l)), Value::Primitive(Primitive::String(r))) =
(left, right)
{
Ok(l.contains(r))
} else {
Err((left.type_name(), right.type_name()))
}
}