aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Florian Weikert <fwe@google.com>2015-12-16 12:38:38 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2015-12-16 12:56:06 +0000
commit233a46e905f9d1fab490fd4ffebf0959d9e94bec (patch)
treea5424d8313bd4986fce6b5d30acd47e1ee833103 /src
parent3e08d11ba6c3368a5687f609cb9040cf2a11bd6e (diff)
Skylark: implemented all() and any()
-- MOS_MIGRATED_REVID=110348607
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java25
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java48
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java10
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java87
4 files changed, 156 insertions, 14 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
index e63b850f69..205075cffc 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
@@ -378,18 +378,16 @@ public final class EvalUtils {
public static final StackManipulation toCollection =
ByteCodeUtils.invoke(EvalUtils.class, "toCollection", Object.class, Location.class);
- @SuppressWarnings("unchecked")
public static Collection<?> toCollection(Object o, Location loc) throws EvalException {
if (o instanceof Collection) {
- return (Collection<Object>) o;
+ return (Collection<?>) o;
} else if (o instanceof SkylarkList) {
return ((SkylarkList) o).getList();
- } else if (o instanceof Map<?, ?>) {
- Map<Comparable<?>, Object> dict = (Map<Comparable<?>, Object>) o;
+ } else if (o instanceof Map) {
// For dictionaries we iterate through the keys only
// For determinism, we sort the keys.
try {
- return SKYLARK_COMPARATOR.sortedCopy(dict.keySet());
+ return SKYLARK_COMPARATOR.sortedCopy(((Map<?, ?>) o).keySet());
} catch (ComparisonException e) {
throw new EvalException(loc, e);
}
@@ -404,17 +402,14 @@ public final class EvalUtils {
public static final StackManipulation toIterable =
ByteCodeUtils.invoke(EvalUtils.class, "toIterable", Object.class, Location.class);
- @SuppressWarnings("unchecked")
public static Iterable<?> toIterable(Object o, Location loc) throws EvalException {
if (o instanceof String) {
// This is not as efficient as special casing String in for and dict and list comprehension
// statements. However this is a more unified way.
- // The regex matches every character in the string until the end of the string,
- // so "abc" will be split into ["a", "b", "c"].
- return ImmutableList.<Object>copyOf(((String) o).split("(?!^)"));
+ return split((String) o);
} else if (o instanceof Iterable) {
- return (Iterable<Object>) o;
- } else if (o instanceof Map<?, ?>) {
+ return (Iterable<?>) o;
+ } else if (o instanceof Map) {
return toCollection(o, loc);
} else {
throw new EvalException(loc,
@@ -422,6 +417,14 @@ public final class EvalUtils {
}
}
+ private static ImmutableList<String> split(String value) {
+ ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
+ for (char c : value.toCharArray()) {
+ builder.add(String.valueOf(c));
+ }
+ return builder.build();
+ }
+
/**
* @return the size of the Skylark object or -1 in case the object doesn't have a size.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
index a199ab4bcd..dc3f50b72e 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodLibrary.java
@@ -1030,6 +1030,51 @@ public class MethodLibrary {
return (list.size() == 1) ? EvalUtils.toIterable(list.get(0), loc) : list;
}
+ @SkylarkSignature(
+ name = "all",
+ returnType = Boolean.class,
+ doc = "Returns true if all elements evaluate to True or if the collection is empty.",
+ mandatoryPositionals = {
+ @Param(name = "elements", type = Object.class, doc = "A string or a collection of elements.")
+ },
+ useLocation = true
+ )
+ private static BuiltinFunction all =
+ new BuiltinFunction("all") {
+ @SuppressWarnings("unused") // Accessed via Reflection.
+ public Boolean invoke(Object collection, Location loc) throws EvalException {
+ return !hasElementWithBooleanValue(collection, false, loc);
+ }
+ };
+
+ @SkylarkSignature(
+ name = "any",
+ returnType = Boolean.class,
+ doc = "Returns true if at least one element evaluates to True.",
+ mandatoryPositionals = {
+ @Param(name = "elements", type = Object.class, doc = "A string or a collection of elements.")
+ },
+ useLocation = true
+ )
+ private static BuiltinFunction any =
+ new BuiltinFunction("any") {
+ @SuppressWarnings("unused") // Accessed via Reflection.
+ public Boolean invoke(Object collection, Location loc) throws EvalException {
+ return hasElementWithBooleanValue(collection, true, loc);
+ }
+ };
+
+ private static boolean hasElementWithBooleanValue(Object collection, boolean value, Location loc)
+ throws EvalException {
+ Iterable<?> iterable = EvalUtils.toIterable(collection, loc);
+ for (Object obj : iterable) {
+ if (EvalUtils.toBoolean(obj) == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
// supported list methods
@SkylarkSignature(
name = "sorted",
@@ -1846,10 +1891,9 @@ public class MethodLibrary {
static final List<BaseFunction> skylarkGlobalFunctions =
ImmutableList.<BaseFunction>builder()
.addAll(buildGlobalFunctions)
- .add(dir, fail, getattr, hasattr, max, min, print, set, struct, type)
+ .add(all, any, dir, fail, getattr, hasattr, max, min, print, set, struct, type)
.build();
-
/**
* Collect global functions for the validation environment.
*/
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java
index 6a6bf70a7b..f5cb497f67 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvalUtilsTest.java
@@ -49,6 +49,16 @@ public class EvalUtilsTest {
}
@Test
+ public void testEmptyStringToIterable() throws Exception {
+ assertThat(EvalUtils.toIterable("", null)).isEmpty();
+ }
+
+ @Test
+ public void testStringToIterable() throws Exception {
+ assertThat(EvalUtils.toIterable("abc", null)).hasSize(3);
+ }
+
+ @Test
public void testDataTypeNames() throws Exception {
assertEquals("string", EvalUtils.getDataTypeName("foo"));
assertEquals("int", EvalUtils.getDataTypeName(3));
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
index de20fc2f7e..f649ce2ab1 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/MethodLibraryTest.java
@@ -266,6 +266,91 @@ public class MethodLibraryTest extends EvaluationTestCase {
}
@Test
+ public void testAllWithEmptyValue() throws Exception {
+ new SkylarkTest()
+ .testStatement("all('')", true)
+ .testStatement("all([])", true)
+ .testIfExactError("type 'NoneType' is not iterable", "any(None)");
+ }
+
+ @Test
+ public void testAllWithPrimitiveType() throws Exception {
+ new SkylarkTest()
+ .testStatement("all('test')", true)
+ .testIfErrorContains("", "all(1)");
+ }
+
+ @Test
+ public void testAllWithList() throws Exception {
+ new SkylarkTest()
+ .testStatement("all([False])", false)
+ .testStatement("all([True, False])", false)
+ .testStatement("all([False, False])", false)
+ .testStatement("all([False, True])", false)
+ .testStatement("all(['', True])", false)
+ .testStatement("all([0, True])", false)
+ .testStatement("all([[], True])", false)
+ .testStatement("all([True, 't', 1])", true);
+ }
+
+ @Test
+ public void testAllWithSet() throws Exception {
+ new SkylarkTest()
+ .testStatement("all(set([0]))", false)
+ .testStatement("all(set([1, 0]))", false)
+ .testStatement("all(set([1]))", true);
+ }
+
+ @Test
+ public void testAllWithDict() throws Exception {
+ new SkylarkTest()
+ .testStatement("all({1 : None})", true)
+ .testStatement("all({None : 1})", false);
+ }
+
+ @Test
+ public void testAnyWithEmptyValue() throws Exception {
+ new SkylarkTest()
+ .testStatement("any('')", false)
+ .testStatement("any([])", false)
+ .testIfExactError("type 'NoneType' is not iterable", "any(None)");
+ }
+
+ @Test
+ public void testAnyWithPrimitiveType() throws Exception {
+ new SkylarkTest()
+ .testStatement("any('test')", true)
+ .testIfErrorContains("", "any(1)");
+ }
+
+ @Test
+ public void testAnyWithList() throws Exception {
+ new SkylarkTest()
+ .testStatement("any([False])", false)
+ .testStatement("any([0])", false)
+ .testStatement("any([''])", false)
+ .testStatement("any([[]])", false)
+ .testStatement("any([True, False])", true)
+ .testStatement("any([False, False])", false)
+ .testStatement("any([False, '', 0])", false)
+ .testStatement("any([False, '', 42])", true);
+ }
+
+ @Test
+ public void testAnyWithSet() throws Exception {
+ new SkylarkTest()
+ .testStatement("any(set([0]))", false)
+ .testStatement("any(set([1, 0]))", true);
+ }
+
+ @Test
+ public void testAnyWithDict() throws Exception {
+ new SkylarkTest()
+ .testStatement("any({1 : None, '' : None})", true)
+ .testStatement("any({None : 1, '' : 2})", false);
+ }
+
+ @Test
public void testStackTraceLocation() throws Exception {
new SkylarkTest().testIfErrorContains(
"Traceback (most recent call last):\n\t"
@@ -867,7 +952,7 @@ public class MethodLibraryTest extends EvaluationTestCase {
@Test
public void testReversedWithStrings() throws Exception {
new BothModesTest()
- .testEval("reversed('')", "['']")
+ .testEval("reversed('')", "[]")
.testEval("reversed('a')", "['a']")
.testEval("reversed('abc')", "['c', 'b', 'a']")
.testEval("reversed('__test ')", "[' ', ' ', 't', 's', 'e', 't', '_', '_']")