From 77d153d315a2a5909840ebcd47491e4cef14428b Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 15 Mar 2022 15:27:36 +0100 Subject: Add `in` and `not in` operators --- src/eval/array.rs | 5 +++++ src/eval/dict.rs | 5 +++++ src/eval/mod.rs | 2 ++ src/eval/ops.rs | 28 ++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+) (limited to 'src/eval') diff --git a/src/eval/array.rs b/src/eval/array.rs index 5cd8df82..2da1a5f4 100644 --- a/src/eval/array.rs +++ b/src/eval/array.rs @@ -66,6 +66,11 @@ impl Array { Arc::make_mut(&mut self.0).push(value); } + /// Whether the array contains a specific value. + pub fn contains(&self, value: &Value) -> bool { + self.0.contains(value) + } + /// Clear the array. pub fn clear(&mut self) { if Arc::strong_count(&self.0) == 1 { diff --git a/src/eval/dict.rs b/src/eval/dict.rs index 03871fa0..9127b2eb 100644 --- a/src/eval/dict.rs +++ b/src/eval/dict.rs @@ -61,6 +61,11 @@ impl Dict { Arc::make_mut(&mut self.0).insert(key, value); } + /// Whether the dictionary contains a specific key. + pub fn contains_key(&self, key: &str) -> bool { + self.0.contains_key(key) + } + /// Clear the dictionary. pub fn clear(&mut self) { if Arc::strong_count(&self.0) == 1 { diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 4ccf377b..2c864036 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -344,6 +344,8 @@ impl Eval for BinaryExpr { BinOp::Leq => self.apply(ctx, scp, ops::leq), BinOp::Gt => self.apply(ctx, scp, ops::gt), BinOp::Geq => self.apply(ctx, scp, ops::geq), + BinOp::In => self.apply(ctx, scp, ops::in_), + BinOp::NotIn => self.apply(ctx, scp, ops::not_in), BinOp::Assign => self.assign(ctx, scp, |_, b| Ok(b)), BinOp::AddAssign => self.assign(ctx, scp, ops::add), BinOp::SubAssign => self.assign(ctx, scp, ops::sub), diff --git a/src/eval/ops.rs b/src/eval/ops.rs index 04a13fd1..6a8f5284 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -335,3 +335,31 @@ pub fn compare(lhs: &Value, rhs: &Value) -> Option { _ => Option::None, } } + +/// Test whether one value is "in" another one. +pub fn in_(lhs: Value, rhs: Value) -> StrResult { + if let Some(b) = contains(&lhs, &rhs) { + Ok(Bool(b)) + } else { + mismatch!("cannot apply 'in' to {} and {}", lhs, rhs) + } +} + +/// Test whether one value is "not in" another one. +pub fn not_in(lhs: Value, rhs: Value) -> StrResult { + if let Some(b) = contains(&lhs, &rhs) { + Ok(Bool(!b)) + } else { + mismatch!("cannot apply 'not in' to {} and {}", lhs, rhs) + } +} + +/// Test for containment. +pub fn contains(lhs: &Value, rhs: &Value) -> Option { + Some(match (lhs, rhs) { + (Value::Str(a), Value::Str(b)) => b.contains(a.as_str()), + (Value::Str(a), Value::Dict(b)) => b.contains_key(a), + (a, Value::Array(b)) => b.contains(a), + _ => return Option::None, + }) +} -- cgit v1.2.3