]> mj.ucw.cz Git - moe.git/blobdiff - t/moe/conf.py
Changed clause->formula in ConfigCondition, added formula pretty-printing
[moe.git] / t / moe / conf.py
index 6d61b33d59b025bcbcd25264cf90229b4379fb02..994214124aeb3a1add5afea3e229ec63f913a6bd 100644 (file)
@@ -1,41 +1,37 @@
-import types, itertools, re
-import logging as log
-
 """
-Lazy conditional string evaluation module for configuration variables.
+conf.py
+-------
+
+Lazy conditional string evaluation module for Moe configuration variables.
 
 
-* Each variable has ordered list of operations (definitions), each SETs or APPENDs an expression 
-to the value. Each operation may be guarded by condition
+* Each variable has ordered list of operations (definitions), each defining operation either 
+assigns (SET) or appends (APPEND) value of an expression to the variable. Each operation may be guarded by condition(s)
 
-NOTE: Variable is undefined even if some 'APPEND' apply but no 'SET' applies. This might change.
+NOTE: If no 'SET' applies, a variable is still undefined even if some 'APPEND' applies. This might change.
 
-* Each condition is a formula (tree consisting of 'AND', 'OR', 'NOT' and '==', '!=' between
-two expressions.
+* Each condition is a formula (tree consisting of 'AND', 'OR', 'NOT' and '==', '!=' between two expressions.
 
 * Expression is a list of strings and variables to be expanded.
 
 NOTE: All expanded data should be (or is converted to) unicode 
 
+
+TODO: Fixing value of variables.
 TODO: Cleanup of unused undefined variables.
 TODO: Better variable name checking (no name '.'-structural prefix of another)
 TODO: Implemet "subtree" listing.
 TODO: Test conditions and unicode
 """
 
-c_tree_sep = u'.'
-c_comment = u'#'
-c_open = u'{'
-c_close = u'}'
-c_if = u'if'
-
-"Variable name regexp, dots (separators) must be separated from edges and each other."
-re_key = re.compile(r'\A([A-Za-z0-9_-]+\.)*[A-Za-z0-9_-]+\Z')
+import types, itertools, re
+import logging as log
+from confparser import VARNAME_re
 
-"Allowed depth of recursion -- includes ALL recursive calls, so should quite high."
+"Allowed depth of recursion - includes ALL recursive calls, so should quite high."
 c_maxdepth = 256
 
-"Maximum attained depth of recursion"
+"Maximum attained depth of recursion - for debug/testing"
 debug_maxdepth = 0 
 
 def check_depth(depth):
@@ -67,7 +63,7 @@ class ConfigTree(object):
     if not key in self.variables:
       if not create:
        raise ConfigError('Config variable %r undefined.', key)
-      if not re_key.match(key):
+      if not VARNAME_re.match(key):
        raise ConfigError('Invalid variable identifier %r in config', key)
       self.variables[key] = ConfigVar(key)
     return self.variables[key]
@@ -122,13 +118,15 @@ class ConfigCondition(ConfigElem):
   ('AND', c1, c1), ('OR', c1, c2), ('NOT', c1), 
   ('==', e1, e2), ('!=', e1, e2) where e1, e2 are `ConfigExpression`s.
   """
-  def __init__(self, text, clause, parent=None):
+  def __init__(self, formula, text=None, parent=None):
     """
-    Condition defined by `text` (informative), `clause` as in class definition, 
+    Condition defined by `text` (informative), `formula` as in class definition, 
     `parent` is the parent condition (if any).
     """
+    if not text:
+      text = self.formula_string(formula)
     super(ConfigVar, self).__init__(text)
-    self.clause = clause
+    self.formula = formula
     self.parent = parent
     # Setup dependencies on used variables (not on the parent condition)
     for v in self.variables():
@@ -136,9 +134,9 @@ class ConfigCondition(ConfigElem):
     if self.parent:
       self.parent.dependants.add(self)
   def variables(self, cl=None):
-    "Return an iterator of variables used in clause `cl`"
+    "Return an iterator of variables used in formula `cl`"
     if not cl: 
-      cl = self.clause
+      cl = self.formula
     if cl[0] in ['==','!=']:
       return itertools.chain(cl[1].variables(), cl[2].variables())
     if cl[0] in ['AND','OR']:
@@ -151,11 +149,11 @@ class ConfigCondition(ConfigElem):
     if self.parent:
       self.parent.dependants.discard(self)
   def evaluate(self, cl=None, depth=0):
-    """Evaluate clause `cl` (or the entire condition).
+    """Evaluate formula `cl` (or the entire condition).
     Partial evaluation for AND and OR. Tests the parent condition first."""
     check_depth(depth)
     if not cl: 
-      cl = self.clause
+      cl = self.formula
     if self.parent and not self.parent.value():
       return False
     if cl[0] in ['==','!=']:
@@ -168,6 +166,17 @@ class ConfigCondition(ConfigElem):
     if cl[0] == 'OR' and v1: return True
     if cl[0] == 'AND' and not v1: return False
     return self.evaluate(cl[2], depth+1)
+  def formula_string(self, formula):
+    "Create a string representation of a formula."
+    if formula[0] == 'AND':
+      return itertools.chain(['('], self.formula_string(formula[1]), [' and '], self.formula_string(formula[2]),[')'])
+    elif formula[0] == 'OR':
+      return itertools.chain(['('], self.formula_string(formula[1]), [' or '], self.formula_string(formula[2]),[')'])
+    elif formula[0] == 'NOT':
+      return itertools.chain(['(not '], self.formula_string(formula[1]),[')'])
+    elif formula[0] in ['==', '!=']:
+      return itertools.chain(formula[1], formula[0], formula[2])
+    return iter(['<invalid formula>'])
   def str(self, parents=False):
     "Retur the defining expression, if `parents` set, then prefixed with parent conditions."
     if parents and self.parent: