""" Handles operator precedence. """ from jedi.parser import representation as pr from jedi import debug from jedi.common import PushBackIterator class PythonGrammar(object): """ Some kind of mirror of http://docs.python.org/3/reference/grammar.html. """ class MultiPart(str): def __new__(cls, first, second): self = str.__new__(cls, first) self.second = second def __str__(self): return self.__str__() + ' ' + self.second FACTOR = '+', '-', '~' POWER = '**', TERM = '*', '/', '%', '//' ARITH_EXPR = '+', '-' SHIFT_EXPR = '<<', '>>' AND_EXPR = '&', XOR_EXPR = '^', EXPR = '|', COMPARISON = ('<', '>', '==', '>=', '<=', '!=', 'in', MultiPart('not', 'in'), MultiPart('is', 'not')), 'is' NOT_TEST = 'not', AND_TEST = 'and', OR_TEST = 'or', #TEST = or_test ['if' or_test 'else' test] | lambdef #SLICEOP = ':' [test] #SUBSCRIPT = test | [test] ':' [test] [sliceop] ORDER = (POWER, TERM, ARITH_EXPR, SHIFT_EXPR, AND_EXPR, XOR_EXPR, EXPR, COMPARISON, AND_TEST, OR_TEST) FACTOR_PRIORITY = 0 # highest priority LOWEST_PRIORITY = len(ORDER) NOT_TEST_PRIORITY = LOWEST_PRIORITY - 2 # priority only lower for `and`/`or` class Precedence(object): def __init__(self, left, operator, right): self.left = left self.operator = operator self.right = right def parse_tree(self, strip_literals=False): def process(which): try: which = which.parse_tree() except AttributeError: pass if strip_literals and isinstance(which, pr.Literal): which = which.value return which return (process(self.left), self.operator, process(self.right)) def __repr__(self): return '(%s %s %s)' % (self.left, self.operator, self.right) def create_precedence(expression_list): iterator = PushBackIterator(expression_list) return _check_operator(iterator) def _syntax_error(element, msg='SyntaxError in precedence'): debug.warning('%s: %s, %s' % (msg, element, element.start_pos)) def _get_number(iterator, priority=PythonGrammar.LOWEST_PRIORITY): el = next(iterator) if isinstance(el, pr.Operator): if el in PythonGrammar.FACTOR: right = _get_number(iterator, PythonGrammar.FACTOR_PRIORITY) elif el in PythonGrammar.NOT_TEST and priority <= PythonGrammar.FACTOR_PRIORITY: right = _get_number(iterator, PythonGrammar.NOT_TEST_PRIORITY) else: _syntax_error(el) return _get_number(iterator, priority) return Precedence(None, el, right) else: return el def _check_operator(iterator, priority=PythonGrammar.LOWEST_PRIORITY): """ """ try: left = _get_number(iterator, priority) except StopIteration: _syntax_error(iterator.current, 'SyntaxError operand missing') return None for el in iterator: if not isinstance(el, pr.Operator): _syntax_error(el) continue operator = None for check_prio, check in enumerate(PythonGrammar.ORDER): if check_prio >= priority: break # respect priorities. try: match_index = check.index(el) except ValueError: continue match = check[match_index] if isinstance(match, PythonGrammar.MultiPart): next_tok = next(iterator) if next_tok != match.second: iterator.push_back(next_tok) continue operator = match break if operator is None: _syntax_error(el) continue right = _check_operator(iterator, check_prio) if right is not None: left = Precedence(left, operator, right) return left