Add in
and not in
operators
This commit is contained in:
parent
ae0a56cdff
commit
77d153d315
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
|
@ -335,3 +335,31 @@ pub fn compare(lhs: &Value, rhs: &Value) -> Option<Ordering> {
|
||||
_ => Option::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Test whether one value is "in" another one.
|
||||
pub fn in_(lhs: Value, rhs: Value) -> StrResult<Value> {
|
||||
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<Value> {
|
||||
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<bool> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ pub fn new() -> Scope {
|
||||
std.def_fn("max", utility::max);
|
||||
std.def_fn("even", utility::even);
|
||||
std.def_fn("odd", utility::odd);
|
||||
std.def_fn("mod", utility::modulo);
|
||||
std.def_fn("mod", utility::mod_);
|
||||
std.def_fn("range", utility::range);
|
||||
std.def_fn("rgb", utility::rgb);
|
||||
std.def_fn("cmyk", utility::cmyk);
|
||||
|
@ -59,7 +59,7 @@ pub fn odd(_: &mut Context, args: &mut Args) -> TypResult<Value> {
|
||||
}
|
||||
|
||||
/// The modulo of two numbers.
|
||||
pub fn modulo(_: &mut Context, args: &mut Args) -> TypResult<Value> {
|
||||
pub fn mod_(_: &mut Context, args: &mut Args) -> TypResult<Value> {
|
||||
let Spanned { v: v1, span: span1 } = args.expect("integer or float")?;
|
||||
let Spanned { v: v2, span: span2 } = args.expect("integer or float")?;
|
||||
|
||||
|
@ -376,9 +376,18 @@ fn expr_prec(p: &mut Parser, atomic: bool, min_prec: usize) -> ParseResult {
|
||||
with_expr(p, marker)?;
|
||||
}
|
||||
|
||||
let op = match p.peek().and_then(BinOp::from_token) {
|
||||
Some(binop) => binop,
|
||||
None => break,
|
||||
let op = if p.eat_if(&NodeKind::Not) {
|
||||
if p.at(&NodeKind::In) {
|
||||
BinOp::NotIn
|
||||
} else {
|
||||
p.expected("keyword `in`");
|
||||
return Err(ParseError);
|
||||
}
|
||||
} else {
|
||||
match p.peek().and_then(BinOp::from_token) {
|
||||
Some(binop) => binop,
|
||||
None => break,
|
||||
}
|
||||
};
|
||||
|
||||
let mut prec = op.precedence();
|
||||
|
@ -551,9 +551,17 @@ node! {
|
||||
impl BinaryExpr {
|
||||
/// The binary operator: `+`.
|
||||
pub fn op(&self) -> BinOp {
|
||||
let mut not = false;
|
||||
self.0
|
||||
.children()
|
||||
.find_map(|node| BinOp::from_token(node.kind()))
|
||||
.find_map(|node| match node.kind() {
|
||||
NodeKind::Not => {
|
||||
not = true;
|
||||
None
|
||||
}
|
||||
NodeKind::In if not => Some(BinOp::NotIn),
|
||||
_ => BinOp::from_token(node.kind()),
|
||||
})
|
||||
.expect("binary expression is missing operator")
|
||||
}
|
||||
|
||||
@ -601,6 +609,10 @@ pub enum BinOp {
|
||||
Geq,
|
||||
/// The assignment operator: `=`.
|
||||
Assign,
|
||||
/// The containment operator: `in`.
|
||||
In,
|
||||
/// The inversed containment operator: `not in`.
|
||||
NotIn,
|
||||
/// The add-assign operator: `+=`.
|
||||
AddAssign,
|
||||
/// The subtract-assign oeprator: `-=`.
|
||||
@ -628,6 +640,7 @@ impl BinOp {
|
||||
NodeKind::Gt => Self::Gt,
|
||||
NodeKind::GtEq => Self::Geq,
|
||||
NodeKind::Eq => Self::Assign,
|
||||
NodeKind::In => Self::In,
|
||||
NodeKind::PlusEq => Self::AddAssign,
|
||||
NodeKind::HyphEq => Self::SubAssign,
|
||||
NodeKind::StarEq => Self::MulAssign,
|
||||
@ -649,6 +662,8 @@ impl BinOp {
|
||||
Self::Leq => 4,
|
||||
Self::Gt => 4,
|
||||
Self::Geq => 4,
|
||||
Self::In => 4,
|
||||
Self::NotIn => 4,
|
||||
Self::And => 3,
|
||||
Self::Or => 2,
|
||||
Self::Assign => 1,
|
||||
@ -674,6 +689,8 @@ impl BinOp {
|
||||
Self::Leq => Associativity::Left,
|
||||
Self::Gt => Associativity::Left,
|
||||
Self::Geq => Associativity::Left,
|
||||
Self::In => Associativity::Left,
|
||||
Self::NotIn => Associativity::Left,
|
||||
Self::Assign => Associativity::Right,
|
||||
Self::AddAssign => Associativity::Right,
|
||||
Self::SubAssign => Associativity::Right,
|
||||
@ -697,6 +714,8 @@ impl BinOp {
|
||||
Self::Leq => "<=",
|
||||
Self::Gt => ">",
|
||||
Self::Geq => ">=",
|
||||
Self::In => "in",
|
||||
Self::NotIn => "not in",
|
||||
Self::Assign => "=",
|
||||
Self::AddAssign => "+=",
|
||||
Self::SubAssign => "-=",
|
||||
|
@ -165,7 +165,26 @@
|
||||
{ x += "thing" } #test(x, "something")
|
||||
|
||||
---
|
||||
// Test with operator.
|
||||
// Test `in` operator.
|
||||
#test("hi" in "worship", true)
|
||||
#test("hi" in ("we", "hi", "bye"), true)
|
||||
#test("Hey" in "abHeyCd", true)
|
||||
#test("Hey" in "abheyCd", false)
|
||||
#test(5 in range(10), true)
|
||||
#test(12 in range(10), false)
|
||||
#test("" in (), false)
|
||||
#test("key" in (key: "value"), true)
|
||||
#test("value" in (key: "value"), false)
|
||||
#test("Hey" not in "abheyCd", true)
|
||||
#test("a" not
|
||||
/* fun comment? */ in "abc", false)
|
||||
|
||||
---
|
||||
// Error: 9 expected keyword `in`
|
||||
{"a" not}
|
||||
|
||||
---
|
||||
// Test `with` operator.
|
||||
|
||||
// Apply positional arguments.
|
||||
#let add(x, y) = x + y
|
||||
|
Loading…
x
Reference in New Issue
Block a user