Improve Expression.fromstring(), Domain.fromstring()
authorVivien Maisonneuve <v.maisonneuve@gmail.com>
Thu, 26 Jun 2014 12:36:50 +0000 (14:36 +0200)
committerVivien Maisonneuve <v.maisonneuve@gmail.com>
Thu, 26 Jun 2014 13:00:28 +0000 (15:00 +0200)
pypol/domains.py
pypol/linexprs.py

index 20493fa..9bb403e 100644 (file)
@@ -287,25 +287,34 @@ class Domain:
                 return Polyhedron(equalities, inequalities)
         raise SyntaxError('invalid syntax')
 
                 return Polyhedron(equalities, inequalities)
         raise SyntaxError('invalid syntax')
 
+    _RE_BRACES = re.compile(r'^\{\s*|\s*\}$')
+    _RE_EQ = re.compile(r'([^<=>])=([^<=>])')
+    _RE_AND = re.compile(r'\band\b|,|&&|/\\|∧|∩')
+    _RE_OR = re.compile(r'\bor\b|;|\|\||\\/|∨|∪')
+    _RE_NOT = re.compile(r'\bnot\b|!|¬')
+    _RE_NUM_VAR = Expression._RE_NUM_VAR
+    _RE_OPERATORS = re.compile(r'(&|\||~)')
+
     @classmethod
     def fromstring(cls, string):
     @classmethod
     def fromstring(cls, string):
-        # remove brackets
-        string = re.sub(r'^\{\s*|\s*\}$', '', string)
+        # remove curly brackets
+        string = cls._RE_BRACES.sub(r'', string)
         # replace '=' by '=='
         # replace '=' by '=='
-        string = re.sub(r'([^<=>])=([^<=>])', r'\1==\2', string)
+        string = cls._RE_EQ.sub(r'\1==\2', string)
         # replace 'and', 'or', 'not'
         # replace 'and', 'or', 'not'
-        string = re.sub(r'\band\b|,|&&|/\\|∧|∩', r' & ', string)
-        string = re.sub(r'\bor\b|;|\|\||\\/|∨|∪', r' | ', string)
-        string = re.sub(r'\bnot\b|!|¬', r' ~', string)
-        tokens = re.split(r'(&|\||~)', string)
+        string = cls._RE_AND.sub(r' & ', string)
+        string = cls._RE_OR.sub(r' | ', string)
+        string = cls._RE_NOT.sub(r' ~', string)
+        # add implicit multiplication operators, e.g. '5x' -> '5*x'
+        string = cls._RE_NUM_VAR.sub(r'\1*\2', string)
+        # add parentheses to force precedence
+        tokens = cls._RE_OPERATORS.split(string)
         for i, token in enumerate(tokens):
             if i % 2 == 0:
         for i, token in enumerate(tokens):
             if i % 2 == 0:
-                # add implicit multiplication operators, e.g. '5x' -> '5*x'
-                token = re.sub(r'(\d+|\))\s*([^\W\d_]\w*|\()', r'\1*\2', token)
                 token = '({})'.format(token)
                 tokens[i] = token
         string = ''.join(tokens)
                 token = '({})'.format(token)
                 tokens[i] = token
         string = ''.join(tokens)
-        tree = ast.parse(string)
+        tree = ast.parse(string, 'eval')
         return cls._fromast(tree)
 
     def __repr__(self):
         return cls._fromast(tree)
 
     def __repr__(self):
index 0db7edd..9ab5c86 100644 (file)
@@ -252,9 +252,12 @@ class Expression:
                 return left / right
         raise SyntaxError('invalid syntax')
 
                 return left / right
         raise SyntaxError('invalid syntax')
 
+    _RE_NUM_VAR = re.compile(r'(\d+|\))\s*([^\W\d_]\w*|\()')
+
     @classmethod
     def fromstring(cls, string):
     @classmethod
     def fromstring(cls, string):
-        string = re.sub(r'(\d+|\))\s*([^\W\d_]\w*|\()', r'\1*\2', string)
+        # add implicit multiplication operators, e.g. '5x' -> '5*x'
+        string = cls._RE_NUM_VAR.sub(r'\1*\2', string)
         tree = ast.parse(string, 'eval')
         return cls._fromast(tree)
 
         tree = ast.parse(string, 'eval')
         return cls._fromast(tree)