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: |
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 """ |
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 |
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) |
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') |