From b0a30ee84ef3c6e9505a8f872a260db373fdb641 Mon Sep 17 00:00:00 2001
From: Vivien Maisonneuve <v.maisonneuve@gmail.com>
Date: Thu, 26 Jun 2014 14:36:50 +0200
Subject: [PATCH 1/1] Improve Expression.fromstring(), Domain.fromstring()

---
 pypol/domains.py  | 29 +++++++++++++++++++----------
 pypol/linexprs.py |  5 ++++-
 2 files changed, 23 insertions(+), 11 deletions(-)

diff --git a/pypol/domains.py b/pypol/domains.py
index 20493fa..9bb403e 100644
--- a/pypol/domains.py
+++ b/pypol/domains.py
@@ -287,25 +287,34 @@ class Domain:
                 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):
-        # remove brackets
-        string = re.sub(r'^\{\s*|\s*\}$', '', string)
+        # remove curly brackets
+        string = cls._RE_BRACES.sub(r'', string)
         # replace '=' by '=='
-        string = re.sub(r'([^<=>])=([^<=>])', r'\1==\2', string)
+        string = cls._RE_EQ.sub(r'\1==\2', string)
         # 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:
-                # 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)
-        tree = ast.parse(string)
+        tree = ast.parse(string, 'eval')
         return cls._fromast(tree)
 
     def __repr__(self):
diff --git a/pypol/linexprs.py b/pypol/linexprs.py
index 0db7edd..9ab5c86 100644
--- a/pypol/linexprs.py
+++ b/pypol/linexprs.py
@@ -252,9 +252,12 @@ class Expression:
                 return left / right
         raise SyntaxError('invalid syntax')
 
+    _RE_NUM_VAR = re.compile(r'(\d+|\))\s*([^\W\d_]\w*|\()')
+
     @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)
 
-- 
2.20.1