aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/syntax
diff options
context:
space:
mode:
authorGravatar Laurent Le Brun <laurentlb@google.com>2015-05-08 14:47:26 +0000
committerGravatar Han-Wen Nienhuys <hanwen@google.com>2015-05-08 17:01:16 +0000
commite3f4ed7e67c9effb30fcf554fa97026857e394fa (patch)
tree1fb0802139281877086b57edf8d11e09c3de6660 /src/main/java/com/google/devtools/build/lib/syntax
parent5821646f64394747e8fa68733f362147931e9037 (diff)
Build language: Support 'not in' operator.
-- MOS_MIGRATED_REVID=93129861
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/syntax')
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java56
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/Operator.java1
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/Parser.java12
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java1
4 files changed, 45 insertions, 25 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
index b5bf4f5160..2a0bd54d68 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
@@ -70,6 +70,33 @@ public final class BinaryOperatorExpression extends Expression {
}
}
+ private boolean evalIn(Object lval, Object rval) throws EvalException {
+ if (rval instanceof SkylarkList) {
+ for (Object obj : (SkylarkList) rval) {
+ if (obj.equals(lval)) {
+ return true;
+ }
+ }
+ return false;
+ } else if (rval instanceof Collection<?>) {
+ return ((Collection<?>) rval).contains(lval);
+ } else if (rval instanceof Map<?, ?>) {
+ return ((Map<?, ?>) rval).containsKey(lval);
+ } else if (rval instanceof SkylarkNestedSet) {
+ return ((SkylarkNestedSet) rval).expandedSet().contains(lval);
+ } else if (rval instanceof String) {
+ if (lval instanceof String) {
+ return ((String) rval).contains((String) lval);
+ } else {
+ throw new EvalException(getLocation(),
+ "in operator only works on strings if the left operand is also a string");
+ }
+ } else {
+ throw new EvalException(getLocation(),
+ "in operator only works on lists, tuples, sets, dicts and strings");
+ }
+ }
+
@Override
Object eval(Environment env) throws EvalException, InterruptedException {
Object lval = lhs.eval(env);
@@ -264,30 +291,11 @@ public final class BinaryOperatorExpression extends Expression {
}
case IN: {
- if (rval instanceof SkylarkList) {
- for (Object obj : (SkylarkList) rval) {
- if (obj.equals(lval)) {
- return true;
- }
- }
- return false;
- } else if (rval instanceof Collection<?>) {
- return ((Collection<?>) rval).contains(lval);
- } else if (rval instanceof Map<?, ?>) {
- return ((Map<?, ?>) rval).containsKey(lval);
- } else if (rval instanceof SkylarkNestedSet) {
- return ((SkylarkNestedSet) rval).expandedSet().contains(lval);
- } else if (rval instanceof String) {
- if (lval instanceof String) {
- return ((String) rval).contains((String) lval);
- } else {
- throw new EvalException(getLocation(),
- "in operator only works on strings if the left operand is also a string");
- }
- } else {
- throw new EvalException(getLocation(),
- "in operator only works on lists, tuples, sets, dicts and strings");
- }
+ return evalIn(lval, rval);
+ }
+
+ case NOT_IN: {
+ return !evalIn(lval, rval);
}
default: {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Operator.java b/src/main/java/com/google/devtools/build/lib/syntax/Operator.java
index 8849583ac2..73b37cf717 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Operator.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Operator.java
@@ -30,6 +30,7 @@ public enum Operator {
MULT("*"),
NOT("not"),
NOT_EQUALS("!="),
+ NOT_IN("not in"),
OR("or"),
PERCENT("%"),
PLUS("+");
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
index e25ca98b4e..5c363a63f1 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
@@ -114,6 +114,7 @@ class Parser {
.put(TokenKind.LESS_EQUALS, Operator.LESS_EQUALS)
.put(TokenKind.MINUS, Operator.MINUS)
.put(TokenKind.NOT_EQUALS, Operator.NOT_EQUALS)
+ .put(TokenKind.NOT_IN, Operator.NOT_IN)
.put(TokenKind.OR, Operator.OR)
.put(TokenKind.PERCENT, Operator.PERCENT)
.put(TokenKind.SLASH, Operator.DIVIDE)
@@ -134,7 +135,7 @@ class Parser {
EnumSet.of(Operator.AND),
EnumSet.of(Operator.NOT),
EnumSet.of(Operator.EQUALS_EQUALS, Operator.NOT_EQUALS, Operator.LESS, Operator.LESS_EQUALS,
- Operator.GREATER, Operator.GREATER_EQUALS, Operator.IN),
+ Operator.GREATER, Operator.GREATER_EQUALS, Operator.IN, Operator.NOT_IN),
EnumSet.of(Operator.MINUS, Operator.PLUS),
EnumSet.of(Operator.DIVIDE, Operator.MULT, Operator.PERCENT));
@@ -924,6 +925,15 @@ class Parser {
// The loop is not strictly needed, but it prevents risks of stack overflow. Depth is
// limited to number of different precedence levels (operatorPrecedence.size()).
for (;;) {
+
+ if (token.kind == TokenKind.NOT) {
+ // If NOT appears when we expect a binary operator, it must be followed by IN.
+ // Since the code expects every operator to be a single token, we push a NOT_IN token.
+ expect(TokenKind.NOT);
+ expect(TokenKind.IN);
+ pushToken(new Token(TokenKind.NOT_IN, token.left, token.right));
+ }
+
if (!binaryOperators.containsKey(token.kind)) {
return expr;
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java b/src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java
index 57f5fc4876..0ada226f53 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/TokenKind.java
@@ -62,6 +62,7 @@ public enum TokenKind {
NONLOCAL("nonlocal"),
NOT("not"),
NOT_EQUALS("!="),
+ NOT_IN("not in"),
OR("or"),
OUTDENT("outdent"),
PASS("pass"),