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);
|
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.
|
/// Clear the array.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
if Arc::strong_count(&self.0) == 1 {
|
if Arc::strong_count(&self.0) == 1 {
|
||||||
|
@ -61,6 +61,11 @@ impl Dict {
|
|||||||
Arc::make_mut(&mut self.0).insert(key, value);
|
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.
|
/// Clear the dictionary.
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
if Arc::strong_count(&self.0) == 1 {
|
if Arc::strong_count(&self.0) == 1 {
|
||||||
|
@ -344,6 +344,8 @@ impl Eval for BinaryExpr {
|
|||||||
BinOp::Leq => self.apply(ctx, scp, ops::leq),
|
BinOp::Leq => self.apply(ctx, scp, ops::leq),
|
||||||
BinOp::Gt => self.apply(ctx, scp, ops::gt),
|
BinOp::Gt => self.apply(ctx, scp, ops::gt),
|
||||||
BinOp::Geq => self.apply(ctx, scp, ops::geq),
|
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::Assign => self.assign(ctx, scp, |_, b| Ok(b)),
|
||||||
BinOp::AddAssign => self.assign(ctx, scp, ops::add),
|
BinOp::AddAssign => self.assign(ctx, scp, ops::add),
|
||||||
BinOp::SubAssign => self.assign(ctx, scp, ops::sub),
|
BinOp::SubAssign => self.assign(ctx, scp, ops::sub),
|
||||||
|
@ -335,3 +335,31 @@ pub fn compare(lhs: &Value, rhs: &Value) -> Option<Ordering> {
|
|||||||
_ => Option::None,
|
_ => 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("max", utility::max);
|
||||||
std.def_fn("even", utility::even);
|
std.def_fn("even", utility::even);
|
||||||
std.def_fn("odd", utility::odd);
|
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("range", utility::range);
|
||||||
std.def_fn("rgb", utility::rgb);
|
std.def_fn("rgb", utility::rgb);
|
||||||
std.def_fn("cmyk", utility::cmyk);
|
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.
|
/// 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: v1, span: span1 } = args.expect("integer or float")?;
|
||||||
let Spanned { v: v2, span: span2 } = 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)?;
|
with_expr(p, marker)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let op = match p.peek().and_then(BinOp::from_token) {
|
let op = if p.eat_if(&NodeKind::Not) {
|
||||||
Some(binop) => binop,
|
if p.at(&NodeKind::In) {
|
||||||
None => break,
|
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();
|
let mut prec = op.precedence();
|
||||||
|
@ -551,9 +551,17 @@ node! {
|
|||||||
impl BinaryExpr {
|
impl BinaryExpr {
|
||||||
/// The binary operator: `+`.
|
/// The binary operator: `+`.
|
||||||
pub fn op(&self) -> BinOp {
|
pub fn op(&self) -> BinOp {
|
||||||
|
let mut not = false;
|
||||||
self.0
|
self.0
|
||||||
.children()
|
.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")
|
.expect("binary expression is missing operator")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,6 +609,10 @@ pub enum BinOp {
|
|||||||
Geq,
|
Geq,
|
||||||
/// The assignment operator: `=`.
|
/// The assignment operator: `=`.
|
||||||
Assign,
|
Assign,
|
||||||
|
/// The containment operator: `in`.
|
||||||
|
In,
|
||||||
|
/// The inversed containment operator: `not in`.
|
||||||
|
NotIn,
|
||||||
/// The add-assign operator: `+=`.
|
/// The add-assign operator: `+=`.
|
||||||
AddAssign,
|
AddAssign,
|
||||||
/// The subtract-assign oeprator: `-=`.
|
/// The subtract-assign oeprator: `-=`.
|
||||||
@ -628,6 +640,7 @@ impl BinOp {
|
|||||||
NodeKind::Gt => Self::Gt,
|
NodeKind::Gt => Self::Gt,
|
||||||
NodeKind::GtEq => Self::Geq,
|
NodeKind::GtEq => Self::Geq,
|
||||||
NodeKind::Eq => Self::Assign,
|
NodeKind::Eq => Self::Assign,
|
||||||
|
NodeKind::In => Self::In,
|
||||||
NodeKind::PlusEq => Self::AddAssign,
|
NodeKind::PlusEq => Self::AddAssign,
|
||||||
NodeKind::HyphEq => Self::SubAssign,
|
NodeKind::HyphEq => Self::SubAssign,
|
||||||
NodeKind::StarEq => Self::MulAssign,
|
NodeKind::StarEq => Self::MulAssign,
|
||||||
@ -649,6 +662,8 @@ impl BinOp {
|
|||||||
Self::Leq => 4,
|
Self::Leq => 4,
|
||||||
Self::Gt => 4,
|
Self::Gt => 4,
|
||||||
Self::Geq => 4,
|
Self::Geq => 4,
|
||||||
|
Self::In => 4,
|
||||||
|
Self::NotIn => 4,
|
||||||
Self::And => 3,
|
Self::And => 3,
|
||||||
Self::Or => 2,
|
Self::Or => 2,
|
||||||
Self::Assign => 1,
|
Self::Assign => 1,
|
||||||
@ -674,6 +689,8 @@ impl BinOp {
|
|||||||
Self::Leq => Associativity::Left,
|
Self::Leq => Associativity::Left,
|
||||||
Self::Gt => Associativity::Left,
|
Self::Gt => Associativity::Left,
|
||||||
Self::Geq => Associativity::Left,
|
Self::Geq => Associativity::Left,
|
||||||
|
Self::In => Associativity::Left,
|
||||||
|
Self::NotIn => Associativity::Left,
|
||||||
Self::Assign => Associativity::Right,
|
Self::Assign => Associativity::Right,
|
||||||
Self::AddAssign => Associativity::Right,
|
Self::AddAssign => Associativity::Right,
|
||||||
Self::SubAssign => Associativity::Right,
|
Self::SubAssign => Associativity::Right,
|
||||||
@ -697,6 +714,8 @@ impl BinOp {
|
|||||||
Self::Leq => "<=",
|
Self::Leq => "<=",
|
||||||
Self::Gt => ">",
|
Self::Gt => ">",
|
||||||
Self::Geq => ">=",
|
Self::Geq => ">=",
|
||||||
|
Self::In => "in",
|
||||||
|
Self::NotIn => "not in",
|
||||||
Self::Assign => "=",
|
Self::Assign => "=",
|
||||||
Self::AddAssign => "+=",
|
Self::AddAssign => "+=",
|
||||||
Self::SubAssign => "-=",
|
Self::SubAssign => "-=",
|
||||||
|
@ -165,7 +165,26 @@
|
|||||||
{ x += "thing" } #test(x, "something")
|
{ 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.
|
// Apply positional arguments.
|
||||||
#let add(x, y) = x + y
|
#let add(x, y) = x + y
|
||||||
|
Loading…
x
Reference in New Issue
Block a user