configurationengine/source/cone/public/rules.py
changeset 3 e7e0ae78773e
parent 0 2e8eeb919028
equal deleted inserted replaced
2:87cfa131b535 3:e7e0ae78773e
    15 #
    15 #
    16 
    16 
    17 import operator as ops
    17 import operator as ops
    18 import logging
    18 import logging
    19 import tokenize
    19 import tokenize
    20 from token import ENDMARKER, NAME, ERRORTOKEN
    20 import re
       
    21 from token import ENDMARKER, NAME, ERRORTOKEN, OP
    21 import StringIO
    22 import StringIO
    22 
    23 
    23 from cone.public import container
    24 from cone.public import container, exceptions, utils
       
    25 import types
    24 
    26 
    25 RELATIONS = {}
    27 RELATIONS = {}
    26 
    28 
    27 def abstract():
    29 def abstract():
    28     import inspect
    30     import inspect
    29     caller = inspect.getouterframes(inspect.currentframe())[1][3]
    31     caller = inspect.getouterframes(inspect.currentframe())[1][3]
    30     raise NotImplementedError(caller + ' needs to be implemented')
    32     raise NotImplementedError(caller + ' needs to be implemented')
       
    33 
       
    34 REF_DOLLAR = '$'
       
    35 REF_START_BRACE = '{'
       
    36 REF_END_BRACE = '}'
       
    37 REF_REGEX = re.compile('(?P<ref>\${[\w\.\*]*})', re.UNICODE)
    31 
    38 
    32 def get_tokens(tokenstr):
    39 def get_tokens(tokenstr):
    33     result = []
    40     result = []
    34     tokens = []
    41     tokens = []
    35     tokenstr = tokenstr.replace('\r', '')
    42     tokenstr = tokenstr.replace('\r', '')
    36     name_buffer = [] # Temp buffer for reading name tokens
    43     name_buffer = [] # Temp buffer for reading name tokens
    37     last_epos = None
    44     last_epos = None
       
    45     
       
    46     ref_start = False
    38     for toknum, tokval, spos, epos, _  in tokenize.generate_tokens(StringIO.StringIO(unicode(tokenstr)).readline):
    47     for toknum, tokval, spos, epos, _  in tokenize.generate_tokens(StringIO.StringIO(unicode(tokenstr)).readline):
    39         #print "toknum: %r, tokval: %r, spos: %r, epos: %r" % (toknum, tokval, spos, epos)
       
    40         val = tokval.strip('\r\n\t ')
    48         val = tokval.strip('\r\n\t ')
    41         
    49         
    42         if toknum == ENDMARKER and name_buffer:
    50         if toknum == ENDMARKER and name_buffer:
    43             tokens.append(''.join(name_buffer))
    51             tokens.append(''.join(name_buffer))
    44         
    52         
    45         # Ignore whitespace (this ignores also the end marker,
    53         # Ignore whitespace (this ignores also the end marker,
    46         # since its value is empty)
    54         # since its value is empty)
    47         if val == '': continue
    55         if val == '': continue
    48         
    56         
       
    57         # Handle the references here, ${ref} is the format
       
    58         if toknum in (OP, ERRORTOKEN) and\
       
    59                 tokval in (REF_DOLLAR, REF_START_BRACE, REF_END_BRACE) or\
       
    60                 ref_start:
       
    61             if tokval == REF_DOLLAR:
       
    62                 ref_start = True
       
    63             elif tokval == REF_END_BRACE:
       
    64                 ref_start = False
       
    65             if name_buffer and spos[1] != last_epos[1]:
       
    66                 tokens.append(''.join(name_buffer))
       
    67                 name_buffer = []
       
    68             name_buffer.append(tokval)
       
    69             last_epos = epos
    49         # Put NAME, and ERRORTOKEN tokens through the temp
    70         # Put NAME, and ERRORTOKEN tokens through the temp
    50         # buffer
    71         # buffer
    51         if toknum in (NAME, ERRORTOKEN):
    72         elif toknum in (NAME, ERRORTOKEN):
    52             # If this and the previous token in the temp buffer are not adjacent,
    73             # If this and the previous token in the temp buffer are not adjacent,
    53             # they belong to separate tokens
    74             # they belong to separate tokens
    54             if name_buffer and spos[1] != last_epos[1]:
    75             if name_buffer and spos[1] != last_epos[1]:
    55                 tokens.append(''.join(name_buffer))
    76                 tokens.append(''.join(name_buffer))
    56                 name_buffer = []
    77                 name_buffer = []
    57             
    78 
    58             name_buffer.append(val)
    79             name_buffer.append(val)
    59             last_epos = epos
    80             last_epos = epos
    60         # Other tokens can just go directly to the token list
    81         # Other tokens can just go directly to the token list
    61         else:
    82         else:
    62             if name_buffer:
    83             if name_buffer:
    87     e.g. Relation depends, that can be applied in Rule
   108     e.g. Relation depends, that can be applied in Rule
    88     """
   109     """
    89     relation_name = "RelationBase"
   110     relation_name = "RelationBase"
    90     def __init__(self, data, left, right):
   111     def __init__(self, data, left, right):
    91         self.description = ""
   112         self.description = ""
       
   113         self.ref = None
       
   114         self.lineno = None
    92         self.data = data or container.DataContainer()
   115         self.data = data or container.DataContainer()
    93         self.left = left
   116         self.left = left
    94         self.right = right
   117         self.right = right
    95 
   118 
    96     def __str__(self):
   119     def __str__(self):
    97         """
   120         """
    98         @return: A string presentation of the relation object
   121         @return: A string presentation of the relation object
    99         """
   122         """
   100         return "%s %s %s" % (self.left,self.relation_name,self.right)
   123         return "%s %s %s" % (self.left,self.relation_name,self.right)
       
   124     
       
   125     def __repr__(self):
       
   126         return "%s(ref=%r, lineno=%r)" % (self.__class__.__name__, self.ref, self.lineno)
   101 
   127 
   102     def get_name(self):
   128     def get_name(self):
   103         """
   129         """
   104         @return: The relation name.
   130         @return: The relation name.
   105         """
   131         """
   109         """
   135         """
   110         @return: a possible description of the relation.
   136         @return: a possible description of the relation.
   111         """
   137         """
   112         return self.description
   138         return self.description
   113         
   139         
   114     def execute(self):
   140     def execute(self, context=None):
   115         """
   141         """
   116         Execute the relation object.
   142         Execute the relation object.
   117         """
   143         """
   118         pass
   144         pass
   119 
   145 
   159     def has_errors(self): abstract()
   185     def has_errors(self): abstract()
   160 
   186 
   161 class RelationContainerImpl(RelationContainer):
   187 class RelationContainerImpl(RelationContainer):
   162     """ Base implementation for RelationContainer to use in ConE rules
   188     """ Base implementation for RelationContainer to use in ConE rules
   163     """
   189     """
   164     def execute(self):
   190     def execute(self, context=None):
   165         ret = True
   191         ret = True
   166         i = 0
   192         i = 0
   167         for relation in self:
   193         for relation in self:
   168             i += 1
   194             i += 1
   169             r = relation.execute()
   195             r = relation.execute(context)
   170             ret = ret and r
   196             ret = ret and r
   171         return ret
   197         return ret
   172 
   198 
   173     def find_relations(self, refs):
   199     def find_relations(self, refs):
   174         relations = []
   200         relations = []
   223         left = self.expand_rule_elements(left)
   249         left = self.expand_rule_elements(left)
   224         right = self.expand_rule_elements(right)
   250         right = self.expand_rule_elements(right)
   225         super(BaseRelation, self).__init__(data, left, right)
   251         super(BaseRelation, self).__init__(data, left, right)
   226         self.interpreter = ASTInterpreter(context=self.context)
   252         self.interpreter = ASTInterpreter(context=self.context)
   227 
   253 
   228     def execute(self):
   254     def execute(self, context=None):
   229         """
   255         """
       
   256         @param context: The context for execution can be given as a parameter. 
   230         @return Returns error dictionary
   257         @return Returns error dictionary
   231 
   258 
   232         In the client code proper way to check if the rule applies:
   259         In the client code proper way to check if the rule applies:
   233         info = relation.execute()
   260         info = relation.execute()
   234         if not info.has_errors():
   261         if not info.has_errors():
   235         else: HANDLE ERRORS
   262         else: HANDLE ERRORS
   236         """
   263         """
   237         # logger.debug("Interpreter context %s" % self.interpreter.context)
   264         # logger.debug("Interpreter context %s" % self.interpreter.context)
   238         self.interpreter.create_ast('%s %s %s' % (self.left, self.KEY, self.right))
   265         self.interpreter.create_ast('%s %s %s' % (self.left, self.KEY, self.right))
   239         ret = self.interpreter.eval()
   266         ret = self.interpreter.eval(context, relation=self)
   240         return ret
   267         return ret
   241 
   268 
   242     def get_keys(self):
   269     def get_keys(self):
   243         """ Returns the references from this relation.
   270         """ Returns the references from this relation.
   244         """
   271         """
   253 
   280 
   254     def has_errors(self):
   281     def has_errors(self):
   255         return bool(self.interpreter.errors)
   282         return bool(self.interpreter.errors)
   256 
   283 
   257     def get_refs(self):
   284     def get_refs(self):
   258         return (ASTInterpreter.extract_refs(self.left), ASTInterpreter.extract_refs(self.right))
   285         """
   259 
   286         Get a list of left side references and right side references.
       
   287         @return: left refs
       
   288         """
       
   289         try:
       
   290             refs = []
       
   291             tempast = ASTInterpreter()
       
   292             tempast.create_ast("%s" % self.left)
       
   293             for exp in tempast.expression_list:
       
   294                 refs += exp.get_refs()
       
   295         except Exception, e:
       
   296             utils.log_exception(logging.getLogger('cone.rules'), "Exception in get_refs() of relation %r: %s" % (self, e))
       
   297             return []
       
   298         return refs
       
   299 
       
   300     def get_set_refs(self):
       
   301         """
       
   302         Get a list of references that could get altered by set expression in this rule. 
       
   303         This list is empty if the relation does not have any set expressions.
       
   304         @return: a list of references.
       
   305         """
       
   306         
       
   307         return [exp.left.get_ref() for exp in self.get_set_expressions()]
       
   308 
       
   309     def get_expressions(self):
       
   310         if not self.interpreter.expression_list:
       
   311             self.interpreter.create_ast('%s %s %s' % (self.left, self.KEY, self.right))
       
   312         return self.interpreter.expression_list
       
   313     
       
   314     def get_set_expressions(self):
       
   315         setelems = []
       
   316         if not self.interpreter.expression_list:
       
   317             self.interpreter.create_ast('%s %s %s' % (self.left, self.KEY, self.right))
       
   318         for elem in self.interpreter.expression_list:
       
   319             if isinstance(elem, SetExpression):
       
   320                 setelems.append(elem)
       
   321         return setelems
       
   322         
   260     def _eval_rside_value(self, value): abstract()
   323     def _eval_rside_value(self, value): abstract()
   261     def _compare_value(self, value): abstract()
   324     def _compare_value(self, value): abstract()
   262 
   325 
   263     def extract_erroneus_features_with_values(self):
   326     def extract_erroneus_features_with_values(self):
   264         """
   327         """
   277 
   340 
   278     def get_errors(self):
   341     def get_errors(self):
   279         return self.interpreter.errors
   342         return self.interpreter.errors
   280 
   343 
   281     def expand_rule_elements(self, rule):
   344     def expand_rule_elements(self, rule):
   282         """ Expans rule elements base on the reference.
   345         """ Expands rule elements base on the reference.
   283         Context is used for fetching the child elements for parent references
   346         Context is used for fetching the child elements for parent references
   284         which uses asterisk identifier for selecting all child features: 
   347         which uses asterisk identifier for selecting all child features: 
   285         'parent_feature.*' -> 'child_fea_1 and child_fea_2'.
   348         'parent_feature.*' -> 'child_fea_1 and child_fea_2'.
   286         """
   349         """
   287         tokens = get_tokens(rule) # [token for token in rule.split()]
   350         tokens = get_tokens(rule) # [token for token in rule.split()]
   296                 if expanded_rule:
   359                 if expanded_rule:
   297                     expanded_rule = '%s and %s' % (expanded_rule, expanded_element.rstrip())
   360                     expanded_rule = '%s and %s' % (expanded_rule, expanded_element.rstrip())
   298                 else:
   361                 else:
   299                     expanded_rule = expanded_element.rstrip()
   362                     expanded_rule = expanded_element.rstrip()
   300             elif token.lower() in OPERATORS:
   363             elif token.lower() in OPERATORS:
   301                 expanded_rule += ' %s ' % token
   364                 operator_class = OPERATORS[token]
       
   365                 if operator_class.PARAM_COUNT == 2:
       
   366                     expanded_rule += ' %s ' % token
       
   367                 else:
       
   368                     expanded_rule += '%s ' % token
   302             else:
   369             else:
   303                 if expanded_rule:
   370                 if expanded_rule:
   304                     expanded_rule += '%s'% token
   371                     expanded_rule += '%s'% token
   305                 else:
   372                 else:
   306                     expanded_rule = token
   373                     expanded_rule = token
   335         self.data = data
   402         self.data = data
   336 
   403 
   337     def eval(self, ast, expression, value):
   404     def eval(self, ast, expression, value):
   338         pass
   405         pass
   339 
   406 
   340     def get_keys(self, refs):
   407     def set(self, expression, value):
   341         return ASTInterpreter.extract_refs(refs)
   408         """
       
   409         set a element described with expression to value
       
   410         @param expression: the expression refering to a element 
       
   411         @param value:  the value to set 
       
   412         @raise exception: when the setting value to expression fails.  
       
   413         """
       
   414         pass
   342 
   415 
   343     def get_children_for_reference(self, reference):
   416     def get_children_for_reference(self, reference):
   344         # implement ConE specific children expansion
   417         # implement ConE specific children expansion
   345         pass
   418         pass
       
   419 
       
   420     def convert_value(self, value):
       
   421         if value in ('True', 'true', '1'):
       
   422             return True
       
   423         elif value in ('False', 'false', '0'):
       
   424             return False
       
   425         elif value in ('None',):
       
   426             return None
       
   427         else:
       
   428             try:
       
   429                 return int(value)
       
   430             except:
       
   431                 return value
   346 
   432 
   347     def handle_terminal(self, expression):
   433     def handle_terminal(self, expression):
   348         try:
   434         try:
   349             return int(expression)
   435             return int(expression)
   350         except:
   436         except:
   372         self.value = None
   458         self.value = None
   373 
   459 
   374     def get_title(self):
   460     def get_title(self):
   375         return self.KEY
   461         return self.KEY
   376 
   462 
   377     def eval(self, context): pass
   463     def is_terminal(self):
       
   464         return False
       
   465 
       
   466     def eval(self, context, **kwargs): pass
       
   467 
       
   468     def get_refs(self): return []
   378 
   469 
   379 class OneParamExpression(Expression):
   470 class OneParamExpression(Expression):
   380     PARAM_COUNT = 1
   471     PARAM_COUNT = 1
       
   472     # OP that return itself 
       
   473     OP = lambda _, x : x
       
   474 
   381     def __init__(self, ast, expression):
   475     def __init__(self, ast, expression):
   382         super(OneParamExpression, self).__init__(ast)
   476         super(OneParamExpression, self).__init__(ast)
   383         self.expression = expression
   477         self.expression = expression
   384 
   478 
   385     def __unicode__(self):
   479     def __unicode__(self):
   386         return u'%s %s' % (self.KEY, self.expression)
   480         return u'%s %s' % (self.KEY, self.expression)
   387 
   481 
   388     def eval(self, context):
   482     def __str__(self):
   389         self.value = self.OP(self.expression.eval(context))
   483         return '%s %s' % (self.KEY, self.expression)
       
   484 
       
   485     def eval(self, context, **kwargs):
       
   486         self.value = self.OP(self.expression.eval(context)) 
   390         context.eval(self.ast, self, self.value)
   487         context.eval(self.ast, self, self.value)
   391         return self.value
   488         return self.value
   392 
   489 
   393 class TwoOperatorExpression(Expression):
   490 class TwoOperatorExpression(Expression):
   394     PARAM_COUNT = 2
   491     PARAM_COUNT = 2
   401         self.right = right
   498         self.right = right
   402 
   499 
   403     def __unicode__(self):
   500     def __unicode__(self):
   404         return u'%s %s %s' % (self.left, self.KEY, self.right)
   501         return u'%s %s %s' % (self.left, self.KEY, self.right)
   405 
   502 
   406     def eval(self, context):
   503     def __str__(self):
       
   504         return '%s %s %s' % (self.left, self.KEY, self.right)
       
   505 
       
   506     def eval(self, context, **kwargs):
   407         self.value = self.OP(self.left.eval(context), self.right.eval(context))
   507         self.value = self.OP(self.left.eval(context), self.right.eval(context))
   408         context.eval(self.ast, self, self.value)
   508         context.eval(self.ast, self, self.value)
   409         return self.value
   509         return self.value
   410 
   510 
   411 class TwoOperatorBooleanExpression(TwoOperatorExpression):
   511 class TwoOperatorBooleanExpression(TwoOperatorExpression):
   412     def eval(self, context):
   512     def eval(self, context, **kwargs):
   413         self.value = self.OP(bool(self.left.eval(context)), bool(self.right.eval(context)))
   513         self.value = self.OP(bool(self.left.eval(context, **kwargs)), bool(self.right.eval(context, **kwargs)))
   414         context.eval(self.ast, self, self.value)
   514         context.eval(self.ast, self, self.value)
   415         return self.value         
   515         return self.value         
   416 
   516 
   417 class TerminalExpression(Expression):
   517 class ReferenceTerminal(Expression):
   418     KEY = 'terminal'
   518     PARAM_COUNT = 0
       
   519     KEY = 'reference'
   419 
   520 
   420     def __init__(self, ast, expression):
   521     def __init__(self, ast, expression):
   421         super(TerminalExpression, self).__init__(ast)
   522         super(ReferenceTerminal, self).__init__(ast)
       
   523         if not ASTInterpreter.is_ref(unicode(expression)):
       
   524             expression = ASTInterpreter.create_ref(expression)
   422         self.expression = expression
   525         self.expression = expression
   423 
   526 
   424     def eval(self, context):
   527     def is_terminal(self):
       
   528         return True
       
   529 
       
   530     def eval(self, context, **kwargs):
   425         """ Use context to eval the value
   531         """ Use context to eval the value
   426         Expression on TerminalExpression is feature reference or value
   532         Expression on ReferenceTerminal is feature reference or value
   427         context should handle the reference conversion to correct value
   533         context should handle the reference conversion to correct value
   428         """
   534         """
   429         self.value = context.handle_terminal(self.expression)
   535         self.value = context.handle_terminal(ASTInterpreter.clean_ref(self.expression))
   430         return self.value
   536         return self.value
       
   537     
       
   538     def get_ref(self):
       
   539         """
       
   540         @return: The setting reference, e.g. 'MyFeature.MySetting'
       
   541         """
       
   542         return ASTInterpreter.clean_ref(self.expression)
       
   543 
       
   544     def get_refs(self):
       
   545         """
       
   546         """
       
   547         return [u'%s' % self.get_ref()]
   431 
   548 
   432     def __unicode__(self):
   549     def __unicode__(self):
   433         return self.expression
   550         return self.expression
   434     
   551     
       
   552     def __str__(self):
       
   553         return "(%s => %s)" % (self.expression, self.value)
       
   554 
   435     def __repr__(self):
   555     def __repr__(self):
   436         return self.expression
   556         return self.expression
       
   557 
       
   558 class ValueTerminal(Expression):
       
   559     PARAM_COUNT = 0
       
   560     KEY = 'value_terminal'
       
   561 
       
   562     def __init__(self, ast, expression):
       
   563         super(ValueTerminal, self).__init__(ast)
       
   564         self.expression = expression
       
   565 
       
   566     def is_terminal(self):
       
   567         return True
       
   568 
       
   569     def eval(self, context, **kwargs):
       
   570         self.value = context.convert_value(self.expression)
       
   571         return self.value
       
   572 
       
   573     def __unicode__(self):
       
   574         return self.expression
       
   575     
       
   576     def __str__(self):
       
   577         return self.expression
       
   578 
       
   579 class TypeCoercionError(exceptions.ConeException):
       
   580     pass
       
   581 
       
   582 class AutoValueTerminal(Expression):
       
   583     PARAM_COUNT = 0
       
   584     KEY = 'autovalue_terminal'
       
   585 
       
   586     def __init__(self, ast, expression):
       
   587         super(AutoValueTerminal, self).__init__(ast)
       
   588         self.expression = expression
       
   589 
       
   590     def is_terminal(self):
       
   591         return True
       
   592 
       
   593     def eval(self, context, **kwargs):
       
   594         type = kwargs.get('type', None)
       
   595         
       
   596         if self.expression in ("None", None):
       
   597             self.value = None
       
   598         elif type in (types.IntType, types.FloatType):
       
   599             try:
       
   600                 self.value = type(self.expression)
       
   601             except ValueError:
       
   602                 raise TypeCoercionError("Cannot coerce %r to %s" % (self.expression, type))
       
   603         elif type == types.BooleanType:
       
   604             if self.expression in ('True', 'true', True):
       
   605                 self.value = True
       
   606             elif self.expression in ('False', 'false', False):
       
   607                 self.value = False
       
   608             else:
       
   609                 raise TypeCoercionError("Cannot coerce %r to %s" % (self.expression, type))
       
   610         elif type in types.StringTypes:
       
   611             self.value = unicode(self.expression)
       
   612         elif type == types.ListType:
       
   613             self.value = list(self.expression)
       
   614         else:
       
   615             raise TypeCoercionError("Cannot coerce %r to %s" % (self.expression, type))
       
   616         return self.value
       
   617     
       
   618     def __unicode__(self):
       
   619         return self.expression
       
   620     
       
   621     def __str__(self):
       
   622         return self.expression
       
   623 
   437 
   624 
   438 class NegExpression(OneParamExpression):
   625 class NegExpression(OneParamExpression):
   439     PRECEDENCE = PRECEDENCES['PREFIX_OPERATORS']
   626     PRECEDENCE = PRECEDENCES['PREFIX_OPERATORS']
   440     KEY= '-'
   627     KEY= '-'
   441     OP = ops.neg
   628     OP = ops.neg
   468 class EqualExpression(TwoOperatorExpression):
   655 class EqualExpression(TwoOperatorExpression):
   469     PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
   656     PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
   470     KEY = '=='
   657     KEY = '=='
   471     OP = ops.eq
   658     OP = ops.eq
   472 
   659 
       
   660     def eval(self, context, **kwargs):
       
   661         self.value = self.OP(self.left.eval(context), self.right.eval(context))
       
   662         context.eval(self.ast, self, self.value)
       
   663         return self.value
       
   664 
   473 class NotEqualExpression(TwoOperatorExpression):
   665 class NotEqualExpression(TwoOperatorExpression):
   474     PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
   666     PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
   475     KEY = '!='
   667     KEY = '!='
   476     OP = ops.ne
   668     OP = ops.ne
   477 
   669 
   492 
   684 
   493 class GreaterThanEqualExpression(TwoOperatorExpression):
   685 class GreaterThanEqualExpression(TwoOperatorExpression):
   494     PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
   686     PRECEDENCE = PRECEDENCES['COMPARISON_OPERATORS']
   495     KEY = '>='
   687     KEY = '>='
   496     OP = ops.ge
   688     OP = ops.ge
       
   689 
       
   690 
       
   691 def handle_multiply(self, left, right):
       
   692     return left * right
       
   693 
       
   694 class MultiplyExpression(TwoOperatorExpression):
       
   695     expression = "multiply_operation"
       
   696     PRECEDENCE = PRECEDENCES['MULDIV_OPERATORS']
       
   697     KEY= '*'
       
   698     OP = handle_multiply
       
   699 
       
   700 def handle_divide(self, left, right):
       
   701     return left / right
       
   702 
       
   703 class DivideExpression(TwoOperatorExpression):
       
   704     expression = "divide_operation"
       
   705     PRECEDENCE = PRECEDENCES['MULDIV_OPERATORS']
       
   706     KEY= '/'
       
   707     OP = handle_divide
       
   708 
       
   709 def handle_plus(self, left, right):
       
   710     return left + right
       
   711 
       
   712 class PlusExpression(TwoOperatorExpression):
       
   713     expression = "plus_operation"
       
   714     PRECEDENCE = PRECEDENCES['ADDSUB_OPERATORS']
       
   715     KEY= '+'
       
   716     OP = handle_plus
       
   717 
       
   718 def handle_minus(self, left, right):
       
   719     return left - right
       
   720 
       
   721 class MinusExpression(TwoOperatorExpression):
       
   722     expression = "minus_operation"
       
   723     PRECEDENCE = PRECEDENCES['ADDSUB_OPERATORS']
       
   724     KEY= '-'
       
   725     OP = handle_minus
       
   726 
       
   727 def handle_set(self, left, right):
       
   728     left.set_value(right)
       
   729 
       
   730 class SetExpression(TwoOperatorExpression):
       
   731     PRECEDENCE = PRECEDENCES['SET_OPERATORS']
       
   732     KEY= '='
       
   733     OP = handle_set
       
   734 
       
   735     def eval(self, context, **kwargs):
       
   736         if not isinstance(self.left, ReferenceTerminal):
       
   737             raise RuntimeError("Can only set the value of a setting, '%s' is not a setting reference. Did you forget to use ${}?" % self.left.expression)
       
   738         
       
   739         value = self.right.eval(context, **kwargs)
       
   740         context.set(self.left.get_ref(), value, **kwargs)
       
   741         return True
   497 
   742 
   498 
   743 
   499 def handle_require(expression, left, right):
   744 def handle_require(expression, left, right):
   500     if left and right:
   745     if left and right:
   501         return True
   746         return True
   506 class RequireExpression(TwoOperatorExpression):
   751 class RequireExpression(TwoOperatorExpression):
   507     PRECEDENCE = PRECEDENCES['RELATION_OPERATORS']
   752     PRECEDENCE = PRECEDENCES['RELATION_OPERATORS']
   508     KEY = 'requires'
   753     KEY = 'requires'
   509     OP = handle_require
   754     OP = handle_require
   510 
   755 
   511     def eval(self, context):
   756     def eval(self, context, **kwargs):
   512         super(RequireExpression, self).eval(context)
   757         super(RequireExpression, self).eval(context)
   513         if not self.value:
   758         if not self.value:
   514             left_keys = []
   759             left_keys = []
   515             for ref in self.ast.extract_refs(unicode(self.left)):
   760             for ref in self.ast.extract_refs(unicode(self.left)):
   516                 for key in context.get_keys(ref):
   761                 left_keys.append(ref)
   517                     left_keys.append(key)
       
   518 
   762 
   519             for key in left_keys:
   763             for key in left_keys:
   520                 self.ast.add_error(key, { 'error_string' : 'REQUIRES right side value is "False"',
   764                 self.ast.add_error(key, { 'error_string' : 'REQUIRES right side value is "False"',
   521                                           'left_key' : key,
   765                                           'left_key' : key,
   522                                           'rule' : self.ast.expression
   766                                           'rule' : self.ast.expression
   533 class ExcludeExpression(TwoOperatorExpression):
   777 class ExcludeExpression(TwoOperatorExpression):
   534     PRECEDENCE = PRECEDENCES['RELATION_OPERATORS']
   778     PRECEDENCE = PRECEDENCES['RELATION_OPERATORS']
   535     KEY = 'excludes'
   779     KEY = 'excludes'
   536     OP = handle_exclude
   780     OP = handle_exclude
   537 
   781 
   538     def eval(self, context):
   782     def eval(self, context, **kwargs):
   539         super(ExcludeExpression, self).eval(context)
   783         super(ExcludeExpression, self).eval(context)
   540         if not self.value:
   784         if not self.value:
   541             left_keys = []
   785             left_keys = []
   542             for ref in self.ast.extract_refs(unicode(self.left)):
   786             for ref in self.ast.extract_refs(unicode(self.left)):
   543                 for key in context.get_keys(ref):
   787                 left_keys.append(ref)
   544                     left_keys.append(key)
       
   545                     
   788                     
   546             for key in left_keys:
   789             for key in left_keys:
   547                 self.ast.add_error(key, { 'error_string' : 'EXCLUDE right side value is "True"',
   790                 self.ast.add_error(key, { 'error_string' : 'EXCLUDE right side value is "True"',
   548                                           'left_key' : key,
   791                                           'left_key' : key,
   549                                           'rule' : self.ast.expression
   792                                           'rule' : self.ast.expression
   566 class SimpleCondition(EqualExpression):
   809 class SimpleCondition(EqualExpression):
   567     """
   810     """
   568     A simple condition object that can refer to a model object and evaluate if the value matches  
   811     A simple condition object that can refer to a model object and evaluate if the value matches  
   569     """
   812     """
   570     def __init__(self, left, right):
   813     def __init__(self, left, right):
   571         lterm = TerminalExpression(None, left)
   814         if isinstance(left, basestring) and ASTInterpreter.is_ref(left):
   572         rterm = TerminalExpression(None, right)
   815             lterm = ReferenceTerminal(None, left)
       
   816         else:
       
   817             lterm = ValueTerminal(None, left)
       
   818         if isinstance(right, basestring) and ASTInterpreter.is_ref(right):
       
   819             rterm = ReferenceTerminal(None, right)
       
   820         else:
       
   821             rterm = AutoValueTerminal(None, right)
   573         EqualExpression.__init__(self, None, lterm, rterm)
   822         EqualExpression.__init__(self, None, lterm, rterm)
   574 
   823 
   575 
   824     def eval(self, context, **kwargs):
       
   825         left_value = self.left.eval(context)
       
   826         try:
       
   827             right_value = self.right.eval(context, type=type(left_value))
       
   828         except TypeCoercionError:
       
   829             # If type coercion fails, the result is always False
       
   830             self.value = False
       
   831             return self.value
       
   832         
       
   833         # Type coercion successful, perform value comparison
       
   834         self.value = self.OP(left_value, right_value)
       
   835         context.eval(self.ast, self, self.value)
       
   836         return self.value
       
   837     
       
   838     def get_refs(self):
       
   839         result = []
       
   840         if isinstance(self.left, ReferenceTerminal):
       
   841             result.append(self.left.get_ref())
       
   842         if isinstance(self.right, ReferenceTerminal):
       
   843             result.append(self.right.get_ref())
       
   844         return result
       
   845     
   576 # in format KEY : OPERATOR CLASS
   846 # in format KEY : OPERATOR CLASS
   577 OPERATORS = {
   847 OPERATORS = {
   578     'and' : AndExpression,
   848     'and' : AndExpression,
   579     'nand' : NandExpression,
   849     'nand' : NandExpression,
   580     'or' : OrExpression,
   850     'or' : OrExpression,
   588     '>' : GreaterThanExpression,
   858     '>' : GreaterThanExpression,
   589     '<=' : LessThanEqualExpression,
   859     '<=' : LessThanEqualExpression,
   590     '>=' : GreaterThanEqualExpression,
   860     '>=' : GreaterThanEqualExpression,
   591     'requires' : RequireExpression,
   861     'requires' : RequireExpression,
   592     'excludes' : ExcludeExpression,
   862     'excludes' : ExcludeExpression,
   593     '-' : NegExpression
   863     '-' : MinusExpression,
       
   864     '+' : PlusExpression,
       
   865     '*' : MultiplyExpression,
       
   866     '/' : DivideExpression,
       
   867     '=' : SetExpression
   594     }
   868     }
   595 
   869 
   596 def add_operator(key, operator_class=None, baseclass=RequireExpression):
   870 def add_operator(key, operator_class=None, baseclass=RequireExpression):
   597     """
   871     """
   598     Add new operator key and operator class.
   872     Add new operator key and operator class.
   610     class_prefix = ''.join([key_piece.capitalize() for key_piece in key_pieces])
   884     class_prefix = ''.join([key_piece.capitalize() for key_piece in key_pieces])
   611     new_class = type(class_prefix + 'Expression', (baseclass,), ns)
   885     new_class = type(class_prefix + 'Expression', (baseclass,), ns)
   612     return new_class
   886     return new_class
   613 
   887 
   614 class ParseException(Exception): pass
   888 class ParseException(Exception): pass
       
   889 
       
   890 def is_str_literal(value):
       
   891     """
       
   892     return true if the value is a string literal. A string that begins and ends with single or douple quotes.
       
   893     @param value: the value to investigate
       
   894     @return: Boolean
       
   895     """
       
   896     if  isinstance(value, (str, unicode)):
       
   897         if re.match("[\"\'].*[\"\']", value):
       
   898             return True
       
   899     return False
       
   900 
       
   901 def get_str_literal(value):
       
   902     """
       
   903     return the string literal value
       
   904     @param value: the value to convert
       
   905     @return: string or unicode based on the input value
       
   906     """
       
   907     if  isinstance(value, (str, unicode)):
       
   908         m =  re.match("[\"\'](.*)[\"\']", value)
       
   909         if m:
       
   910             return m.group(1)
       
   911     return None
   615 
   912 
   616 class ASTInterpreter(object):
   913 class ASTInterpreter(object):
   617     def __init__(self, infix_expression=None, context=None):
   914     def __init__(self, infix_expression=None, context=None):
   618         """ Takes infix expression as string """
   915         """ Takes infix expression as string """
   619         self.context = context or DefaultContext(None)
   916         self.context = context or DefaultContext(None)
   627         self.value = None
   924         self.value = None
   628         self.warnings = {}
   925         self.warnings = {}
   629         self.errors = {}
   926         self.errors = {}
   630         self.postfix_array = []
   927         self.postfix_array = []
   631         self.parse_tree = []
   928         self.parse_tree = []
       
   929         self.expression_list = []
   632         self.expression = infix_expression
   930         self.expression = infix_expression
   633 
   931 
   634     def __unicode__(self):
   932     def __unicode__(self):
   635         s = ''
   933         s = ''
   636         for expr in self.parse_tree:
   934         for expr in self.parse_tree:
   650         self._create_parse_tree()
   948         self._create_parse_tree()
   651         return self.parse_tree
   949         return self.parse_tree
   652 
   950 
   653     def _infix_to_postfix(self):
   951     def _infix_to_postfix(self):
   654         """
   952         """
   655         Shunting yard algorithm used to convert infix presentation to postfix.
   953         Shunting yard algorithm used to convert infix presentation to postxfix.
   656         """
   954         """
   657         if not self.expression:
   955         if not self.expression:
   658             raise ParseException('Expression is None')
   956             raise ParseException('Expression is None')
   659         tokens = get_tokens(self.expression) # [token for token in self.expression.split()]
   957         tokens = get_tokens(self.expression)
   660         stack = []
   958         stack = []
   661         # logger.debug('TOKENS: %s' % tokens)
   959         # logger.debug('TOKENS: %s' % tokens)
   662         for token in tokens:
   960         for token in tokens:
   663             # logger.debug('TOKEN: %s' % token)
   961             # logger.debug('TOKEN: %s' % token)
   664             if token.lower() in OPERATORS:
   962             if token.lower() in OPERATORS:
   719         return self.postfix_array
  1017         return self.postfix_array
   720     
  1018     
   721     def _create_parse_tree(self):
  1019     def _create_parse_tree(self):
   722         self.parse_tree = []
  1020         self.parse_tree = []
   723         for token in self.postfix_array:
  1021         for token in self.postfix_array:
       
  1022             is_ref = ASTInterpreter.is_ref(token)
   724             if token in OPERATORS:
  1023             if token in OPERATORS:
   725                 # logger.debug('OP: %s' % (token))
  1024                 # logger.debug('OP: %s' % (token))
   726                 expression_class = OPERATORS[token]
  1025                 expression_class = OPERATORS[token]
   727                 params = []
  1026                 params = []
   728                 for i in range(expression_class.PARAM_COUNT):
  1027                 for i in range(expression_class.PARAM_COUNT):
   733                 params.reverse()
  1032                 params.reverse()
   734                 expression = expression_class(self, *params)
  1033                 expression = expression_class(self, *params)
   735 
  1034 
   736                 # logger.debug('The operation: %s' % expression)
  1035                 # logger.debug('The operation: %s' % expression)
   737                 self.parse_tree.append(expression)
  1036                 self.parse_tree.append(expression)
       
  1037                 self.expression_list.append(expression)
       
  1038             elif not is_ref:
       
  1039                 expression = ValueTerminal(self, token)
       
  1040                 self.parse_tree.append(expression)
       
  1041                 self.expression_list.append(expression)
   738             else:
  1042             else:
   739                 expression = TerminalExpression(self, token)
  1043                 expression = ReferenceTerminal(self, token)
   740                 self.parse_tree.append(expression)
  1044                 self.parse_tree.append(expression)
       
  1045                 self.expression_list.append(expression)
   741 
  1046 
   742         #logger.debug('THE STACK: %s' % self.parse_tree)
  1047         #logger.debug('THE STACK: %s' % self.parse_tree)
   743         #for s in self.parse_tree:
  1048         #for s in self.parse_tree:
   744         #    logger.debug('Stack e: %s' % str(s))
  1049         #    logger.debug('Stack e: %s' % str(s))
   745 
  1050 
   746         return self.parse_tree
  1051         return self.parse_tree
   747 
  1052 
   748     def eval(self):
  1053     def eval(self, context=None, **kwargs):
   749         """ Evals the AST
  1054         """ Evals the AST
   750         If empty expression is given, None is returned
  1055         If empty expression is given, None is returned
   751         """
  1056         """
   752         for expression in self.parse_tree:
  1057         for expression in self.parse_tree:
   753             self.value = expression.eval(self.context)
  1058             self.value = expression.eval(context or self.context, **kwargs)
   754         return self.value
  1059         return self.value
   755 
  1060 
   756     @staticmethod
  1061     @staticmethod
   757     def extract_refs(expression):
  1062     def extract_refs(expression):
   758         tokens = get_tokens(expression)
  1063         tokens = get_tokens(expression)
   759         refs = []
  1064         return [ASTInterpreter.clean_ref(t) for t in tokens if t.lower() not in OPERATORS and\
   760         for token in tokens:
  1065                     t not in (LEFT_PARENTHESIS, RIGHT_PARENTHESIS) and\
   761             if not token.lower() in OPERATORS and token != LEFT_PARENTHESIS and token != RIGHT_PARENTHESIS:
  1066                     ASTInterpreter.is_ref(t)]
   762                 refs.append(token.strip('%s%s' % (LEFT_PARENTHESIS, RIGHT_PARENTHESIS)))
  1067 
   763         return refs
  1068     @staticmethod
       
  1069     def extract_non_operators(expression):
       
  1070         tokens = get_tokens(expression)
       
  1071         return [ASTInterpreter.clean_ref(t) for t in tokens if t.lower() not in OPERATORS and\
       
  1072                     t not in (LEFT_PARENTHESIS, RIGHT_PARENTHESIS)]
       
  1073 
       
  1074     @staticmethod
       
  1075     def clean_ref(ref):
       
  1076         return ref.replace('$', '').replace('{', '').replace('}', '')
       
  1077 
       
  1078     @staticmethod
       
  1079     def create_ref(ref):
       
  1080         return '${%s}' % ref
       
  1081 
       
  1082     @staticmethod
       
  1083     def is_ref(val):
       
  1084         mo = REF_REGEX.match(val)
       
  1085         if mo and len(mo.groups()) > 0 and mo.group() == val:
       
  1086             return True
       
  1087         return False
   764 
  1088 
   765 ##################################################################
  1089 ##################################################################
   766 # Create and configure the main level logger
  1090 # Create and configure the main level logger
   767 logger = logging.getLogger('cone')
  1091 logger = logging.getLogger('cone')