mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
Add ends-with operator and fix dataframe operator behavior (#5395)
* add ends-with operator * escape needles in dataframe operator regex patterns
This commit is contained in:
parent
07a7bb14bf
commit
49cbc30974
8 changed files with 68 additions and 2 deletions
|
@ -273,7 +273,7 @@ pub(super) fn compute_series_single_value(
|
||||||
compare_series_decimal(&lhs, *val, ChunkedArray::equal, lhs_span)
|
compare_series_decimal(&lhs, *val, ChunkedArray::equal, lhs_span)
|
||||||
}
|
}
|
||||||
Value::String { val, .. } => {
|
Value::String { val, .. } => {
|
||||||
let equal_pattern = format!("^{}$", val);
|
let equal_pattern = format!("^{}$", regex::escape(val));
|
||||||
contains_series_pat(&lhs, &equal_pattern, lhs_span)
|
contains_series_pat(&lhs, &equal_pattern, lhs_span)
|
||||||
}
|
}
|
||||||
Value::Date { val, .. } => {
|
Value::Date { val, .. } => {
|
||||||
|
@ -385,7 +385,7 @@ pub(super) fn compute_series_single_value(
|
||||||
},
|
},
|
||||||
Operator::StartsWith => match &right {
|
Operator::StartsWith => match &right {
|
||||||
Value::String { val, .. } => {
|
Value::String { val, .. } => {
|
||||||
let starts_with_pattern = format!("^{}", val);
|
let starts_with_pattern = format!("^{}", regex::escape(val));
|
||||||
contains_series_pat(&lhs, &starts_with_pattern, lhs_span)
|
contains_series_pat(&lhs, &starts_with_pattern, lhs_span)
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
|
@ -396,6 +396,19 @@ pub(super) fn compute_series_single_value(
|
||||||
rhs_span: right.span()?,
|
rhs_span: right.span()?,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
Operator::EndsWith => match &right {
|
||||||
|
Value::String { val, .. } => {
|
||||||
|
let ends_with_pattern = format!("{}$", regex::escape(val));
|
||||||
|
contains_series_pat(&lhs, &ends_with_pattern, lhs_span)
|
||||||
|
}
|
||||||
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
|
op_span: operator.span,
|
||||||
|
lhs_ty: left.get_type(),
|
||||||
|
lhs_span: left.span()?,
|
||||||
|
rhs_ty: right.get_type(),
|
||||||
|
rhs_span: right.span()?,
|
||||||
|
}),
|
||||||
|
},
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: operator.span,
|
op_span: operator.span,
|
||||||
lhs_ty: left.get_type(),
|
lhs_ty: left.get_type(),
|
||||||
|
|
|
@ -427,6 +427,10 @@ pub fn eval_expression(
|
||||||
let rhs = eval_expression(engine_state, stack, rhs)?;
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
||||||
lhs.starts_with(op_span, &rhs, expr.span)
|
lhs.starts_with(op_span, &rhs, expr.span)
|
||||||
}
|
}
|
||||||
|
Operator::EndsWith => {
|
||||||
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
||||||
|
lhs.ends_with(op_span, &rhs, expr.span)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Subexpression(block_id) => {
|
Expr::Subexpression(block_id) => {
|
||||||
|
|
|
@ -4049,6 +4049,7 @@ pub fn parse_operator(
|
||||||
b"not-in" => Operator::NotIn,
|
b"not-in" => Operator::NotIn,
|
||||||
b"mod" => Operator::Modulo,
|
b"mod" => Operator::Modulo,
|
||||||
b"starts-with" => Operator::StartsWith,
|
b"starts-with" => Operator::StartsWith,
|
||||||
|
b"ends-with" => Operator::EndsWith,
|
||||||
b"&&" | b"and" => Operator::And,
|
b"&&" | b"and" => Operator::And,
|
||||||
b"||" | b"or" => Operator::Or,
|
b"||" | b"or" => Operator::Or,
|
||||||
b"**" => Operator::Pow,
|
b"**" => Operator::Pow,
|
||||||
|
|
|
@ -337,6 +337,24 @@ pub fn math_result_type(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Operator::EndsWith => match (&lhs.ty, &rhs.ty) {
|
||||||
|
(Type::String, Type::String) => (Type::Bool, None),
|
||||||
|
(Type::Any, _) => (Type::Bool, None),
|
||||||
|
(_, Type::Any) => (Type::Bool, None),
|
||||||
|
_ => {
|
||||||
|
*op = Expression::garbage(op.span);
|
||||||
|
(
|
||||||
|
Type::Any,
|
||||||
|
Some(ParseError::UnsupportedOperation(
|
||||||
|
op.span,
|
||||||
|
lhs.span,
|
||||||
|
lhs.ty.clone(),
|
||||||
|
rhs.span,
|
||||||
|
rhs.ty.clone(),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
Operator::In => match (&lhs.ty, &rhs.ty) {
|
Operator::In => match (&lhs.ty, &rhs.ty) {
|
||||||
(t, Type::List(u)) if type_compatible(t, u) => (Type::Bool, None),
|
(t, Type::List(u)) if type_compatible(t, u) => (Type::Bool, None),
|
||||||
(Type::Int | Type::Float, Type::Range) => (Type::Bool, None),
|
(Type::Int | Type::Float, Type::Range) => (Type::Bool, None),
|
||||||
|
|
|
@ -35,6 +35,7 @@ impl Expression {
|
||||||
Operator::NotRegexMatch
|
Operator::NotRegexMatch
|
||||||
| Operator::RegexMatch
|
| Operator::RegexMatch
|
||||||
| Operator::StartsWith
|
| Operator::StartsWith
|
||||||
|
| Operator::EndsWith
|
||||||
| Operator::LessThan
|
| Operator::LessThan
|
||||||
| Operator::LessThanOrEqual
|
| Operator::LessThanOrEqual
|
||||||
| Operator::GreaterThan
|
| Operator::GreaterThan
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub enum Operator {
|
||||||
Or,
|
Or,
|
||||||
Pow,
|
Pow,
|
||||||
StartsWith,
|
StartsWith,
|
||||||
|
EndsWith,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Operator {
|
impl Display for Operator {
|
||||||
|
@ -48,6 +49,7 @@ impl Display for Operator {
|
||||||
Operator::LessThanOrEqual => write!(f, "<="),
|
Operator::LessThanOrEqual => write!(f, "<="),
|
||||||
Operator::GreaterThanOrEqual => write!(f, ">="),
|
Operator::GreaterThanOrEqual => write!(f, ">="),
|
||||||
Operator::StartsWith => write!(f, "starts-with"),
|
Operator::StartsWith => write!(f, "starts-with"),
|
||||||
|
Operator::EndsWith => write!(f, "ends-with"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2080,6 +2080,25 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ends_with(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||||
|
match (self, rhs) {
|
||||||
|
(Value::String { val: lhs, .. }, Value::String { val: rhs, .. }) => Ok(Value::Bool {
|
||||||
|
val: lhs.ends_with(rhs),
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
(Value::CustomValue { val: lhs, span }, rhs) => {
|
||||||
|
lhs.operation(*span, Operator::EndsWith, op, rhs)
|
||||||
|
}
|
||||||
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
|
op_span: op,
|
||||||
|
lhs_ty: self.get_type(),
|
||||||
|
lhs_span: self.span()?,
|
||||||
|
rhs_ty: rhs.get_type(),
|
||||||
|
rhs_span: rhs.span()?,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn modulo(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
pub fn modulo(&self, op: Span, rhs: &Value, span: Span) -> Result<Value, ShellError> {
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
|
(Value::Int { val: lhs, .. }, Value::Int { val: rhs, .. }) => {
|
||||||
|
|
|
@ -326,6 +326,14 @@ fn starts_with_operator_succeeds() -> TestResult {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ends_with_operator_succeeds() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"[Moe Larry Curly] | where $it ends-with ly | str collect"#,
|
||||||
|
"Curly",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn proper_missing_param() -> TestResult {
|
fn proper_missing_param() -> TestResult {
|
||||||
fail_test(r#"def foo [x y z w] { }; foo a b c"#, "missing w")
|
fail_test(r#"def foo [x y z w] { }; foo a b c"#, "missing w")
|
||||||
|
|
Loading…
Reference in a new issue