diff --git a/NEWS b/NEWS index 93a28cb200..f66cdd617e 100644 --- a/NEWS +++ b/NEWS @@ -249,6 +249,15 @@ New Functionality '^' are binary "and", "or" and "xor" operators, and '~' is a unary ones-complement operator. +- Added support for set union, intersection, difference, and comparison + operations. The corresponding operators for the first three are + "s1 | s2", "s1 & s2", and "s1 - s2". Relationals are in terms + of subsets, so "s1 < s2" yields true if s1 is a proper subset of s2 + and "s1 == s2" if the two sets have exactly the same elements. + "s1 <= s2" holds for subsets or equality, and similarly "s1 != s2", + "s1 > s2", and "s1 >= s2" have the expected meanings in terms + of non-equality, proper superset, and superset-or-equal. + Changed Functionality --------------------- diff --git a/doc/script-reference/types.rst b/doc/script-reference/types.rst index 44dcbbdfb8..5459ae8514 100644 --- a/doc/script-reference/types.rst +++ b/doc/script-reference/types.rst @@ -526,6 +526,15 @@ Here is a more detailed description of each type: |s| + You can compute the union, intersection, or difference of two sets + using the ``|``, ``&``, and ``-`` operators. You can compare + sets for equality (they have exactly the same elements) using ``==``. + The ``<`` operator returns ``T`` if the lefthand operand is a proper + subset of the righthand operand. Similarly, ``<=`` returns ``T`` + if the lefthand operator is a subset (not necessarily proper, i.e., + it may be equal to the righthand operand). The operators ``!=``, ``>`` + and ``>=`` provide the expected complementary operations. + See the :bro:keyword:`for` statement for info on how to iterate over the elements in a set. diff --git a/testing/btest/Baseline/language.set/out b/testing/btest/Baseline/language.set/out index fc157cf7d9..0128420cbf 100644 --- a/testing/btest/Baseline/language.set/out +++ b/testing/btest/Baseline/language.set/out @@ -42,3 +42,30 @@ remove element (PASS) !in operator (PASS) remove element (PASS) !in operator (PASS) +union (PASS) +intersection (FAIL) +difference (PASS) +difference (PASS) +union/inter. (PASS) +relational (PASS) +relational (PASS) +subset (FAIL) +subset (FAIL) +subset (PASS) +superset (FAIL) +superset (FAIL) +superset (FAIL) +superset (PASS) +non-ordering (FAIL) +non-ordering (PASS) +superset (PASS) +superset (FAIL) +superset (PASS) +superset (PASS) +superset (PASS) +superset (FAIL) +equality (PASS) +equality (FAIL) +non-equality (PASS) +equality (FAIL) +magnitude (FAIL) diff --git a/testing/btest/language/set.bro b/testing/btest/language/set.bro index d1eef7e6f0..56cd649b49 100644 --- a/testing/btest/language/set.bro +++ b/testing/btest/language/set.bro @@ -136,5 +136,50 @@ event bro_init() delete sg3["curly"]; test_case( "remove element", |sg3| == 3 ); test_case( "!in operator", "curly" !in sg3 ); + + + local a = set(1,5,7,9,8,14); + local b = set(1,7,9,2); + + local a_plus_b = set(1,2,5,7,9,8,14); + local a_also_b = set(1,7,9); + local a_sans_b = set(5,8,14); + local b_sans_a = set(2); + + local a_or_b = a | b; + local a_and_b = a & b; + + test_case( "union", a_or_b == a_plus_b ); + test_case( "intersection", a_and_b == a_plus_b ); + test_case( "difference", a - b == a_sans_b ); + test_case( "difference", b - a == b_sans_a ); + + test_case( "union/inter.", |b & set(1,7,9,2)| == |b | set(1,7,2,9)| ); + test_case( "relational", |b & a_or_b| == |b| && |b| < |a_or_b| ); + test_case( "relational", b < a_or_b && a < a_or_b && a_or_b > a_and_b ); + + test_case( "subset", b < a ); + test_case( "subset", a < b ); + test_case( "subset", b < (a | set(2)) ); + test_case( "superset", b > a ); + test_case( "superset", b > (a | set(2)) ); + test_case( "superset", b | set(8, 14, 5) > (a | set(2)) ); + test_case( "superset", b | set(8, 14, 99, 5) > (a | set(2)) ); + + test_case( "non-ordering", (a <= b) || (a >= b) ); + test_case( "non-ordering", (a <= a_or_b) && (a_or_b >= b) ); + + test_case( "superset", (b | set(14, 5)) > a - set(8) ); + test_case( "superset", (b | set(14)) > a - set(8) ); + test_case( "superset", (b | set(14)) > a - set(8,5) ); + test_case( "superset", b >= a - set(5,8,14) ); + test_case( "superset", b > a - set(5,8,14) ); + test_case( "superset", (b - set(2)) > a - set(5,8,14) ); + test_case( "equality", a == a | set(5) ); + test_case( "equality", a == a | set(5,11) ); + test_case( "non-equality", a != a | set(5,11) ); + test_case( "equality", a == a | set(5,11) ); + + test_case( "magnitude", |a_and_b| == |a_or_b|); }