[PATCH] D104572: [lit] Add the ability to parse regexes in Lit boolean expressions

Louis Dionne via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 18 14:59:14 PDT 2021


ldionne created this revision.
ldionne added reviewers: yln, jdenny, mstorsjo.
Herald added subscribers: pengfei, delcypher, kristof.beyls.
ldionne requested review of this revision.
Herald added a project: LLVM.
Herald added a subscriber: llvm-commits.

This patch augments Lit with the ability to parse regular expressions
in boolean expressions. This includes REQUIRES:, XFAIL:, UNSUPPORTED:,
and all other special Lit markup that evaluates to a boolean expression.

Regular expressions can be specified by enclosing them in {{...}},
similarly to how FileCheck handles such regular expressions. The regular
expression can either be on its own, or it can be part of an identifier.
For example, a match expression like {{.+}}-apple-darwin{{.+}} would match
the following variables:

  x86_64-apple-darwin20.0
  arm64-apple-darwin20.0
  arm64-apple-darwin22.0
  etc...

In the long term, this could be used to remove the need to handle the
target triple specially when parsing boolean expressions.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D104572

Files:
  llvm/utils/lit/lit/BooleanExpression.py


Index: llvm/utils/lit/lit/BooleanExpression.py
===================================================================
--- llvm/utils/lit/lit/BooleanExpression.py
+++ llvm/utils/lit/lit/BooleanExpression.py
@@ -9,13 +9,19 @@
     #   and_expr   :: not_expr ('&&' not_expr)*
     #   not_expr   :: '!' not_expr
     #                 '(' or_expr ')'
+    #                 match_expr
+    #   match_expr :: regex
     #                 identifier
+    #                 regex match_expr
+    #                 identifier match_expr
     #   identifier :: [-+=._a-zA-Z0-9]+
+    #   regex      :: '{{' any-regex '}}'
 
     # Evaluates `string` as a boolean expression.
     # Returns True or False. Throws a ValueError on syntax error.
     #
     # Variables in `variables` are true.
+    # Regexes that match any variable in `variables` are true.
     # Substrings of `triple` are true.
     # 'true' is true.
     # All other identifiers are false.
@@ -41,7 +47,7 @@
     END = object()
 
     # Tokenization pattern.
-    Pattern = re.compile(r'\A\s*([()]|[-+=._a-zA-Z0-9]+|&&|\|\||!)\s*(.*)\Z')
+    Pattern = re.compile(r'\A\s*([()]|&&|\|\||!|(?:[-+=._a-zA-Z0-9]+|\{\{.+?\}\})+)\s*(.*)\Z')
 
     @staticmethod
     def tokenize(string):
@@ -86,6 +92,18 @@
             return False
         return True
 
+    def parseMATCH(self):
+        regex = ''
+        for part in filter(None, re.split(r'(\{\{.+?\}\})', self.token)):
+            if part.startswith('{{'):
+                assert part.endswith('}}')
+                regex += part[2:-2]
+            else:
+                regex += re.escape(part)
+        regex = re.compile(regex)
+        self.value = self.token in self.triple or any(regex.fullmatch(var) for var in self.variables)
+        self.token = next(self.tokens)
+
     def parseNOT(self):
         if self.accept('!'):
             self.parseNOT()
@@ -97,9 +115,7 @@
             raise ValueError("expected: '!' or '(' or identifier\nhave: %s" %
                              self.quote(self.token))
         else:
-            self.value = (self.token in self.variables or
-                          self.token in self.triple)
-            self.token = next(self.tokens)
+            self.parseMATCH()
 
     def parseAND(self):
         self.parseNOT()
@@ -143,12 +159,20 @@
         self.assertTrue(BooleanExpression.evaluate('under_score', variables))
         self.assertTrue(BooleanExpression.evaluate('e=quals', variables))
         self.assertTrue(BooleanExpression.evaluate('d1g1ts', variables))
+        self.assertTrue(BooleanExpression.evaluate('{{its.+}}', variables))
+        self.assertTrue(BooleanExpression.evaluate('{{false-[lo]+-true}}', variables))
+        self.assertTrue(BooleanExpression.evaluate('{{(true|false)-lol-(true|false)}}', variables))
+        self.assertTrue(BooleanExpression.evaluate('d1g{{[0-9]}}ts', variables))
+        self.assertTrue(BooleanExpression.evaluate('d1g{{[0-9]}}t{{[a-z]}}', variables))
+        self.assertTrue(BooleanExpression.evaluate('{{d}}1g{{[0-9]}}t{{[a-z]}}', variables))
+        self.assertTrue(BooleanExpression.evaluate('d1{{(g|1)+}}ts', variables))
 
         self.assertFalse(BooleanExpression.evaluate('false', variables))
         self.assertFalse(BooleanExpression.evaluate('True', variables))
         self.assertFalse(BooleanExpression.evaluate('true-ish', variables))
         self.assertFalse(BooleanExpression.evaluate('not_true', variables))
         self.assertFalse(BooleanExpression.evaluate('tru', variables))
+        self.assertFalse(BooleanExpression.evaluate('{{its-true.+}}', variables))
 
     def test_triple(self):
         triple = 'arch-vendor-os'
@@ -248,5 +272,13 @@
                             "have: ')'\n" +
                             "in expression: '( )'")
 
+        self.checkException("abc{{def",
+                            "couldn't parse text: '{{def'\n" +
+                            "in expression: 'abc{{def'")
+
+        self.checkException("{{}}",
+                            "couldn't parse text: '{{}}'\n" +
+                            "in expression: '{{}}'")
+
 if __name__ == '__main__':
     unittest.main()


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D104572.353106.patch
Type: text/x-patch
Size: 4159 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20210618/fbde1cd0/attachment.bin>


More information about the llvm-commits mailing list