diff --git a/jedi/evaluate/__init__.py b/jedi/evaluate/__init__.py index c458d786..8fcd21a3 100644 --- a/jedi/evaluate/__init__.py +++ b/jedi/evaluate/__init__.py @@ -82,6 +82,7 @@ from jedi.evaluate.cache import memoize_default from jedi.evaluate import stdlib from jedi.evaluate import finder from jedi.evaluate import compiled +from jedi.evaluate import precedence class Evaluator(object): @@ -138,25 +139,11 @@ class Evaluator(object): It is used to evaluate a two dimensional object, that has calls, arrays and operators in it. """ - def evaluate_list_comprehension(lc, parent=None): - input = lc.input - nested_lc = lc.input.token_list[0] - if isinstance(nested_lc, pr.ListComprehension): - # is nested LC - input = nested_lc.stmt - module = input.get_parent_until() - # create a for loop, which does the same as list comprehensions - loop = pr.ForFlow(module, [input], lc.stmt.start_pos, lc.middle, True) - - loop.parent = parent or lc.get_parent_until(pr.IsScope) - - if isinstance(nested_lc, pr.ListComprehension): - loop = evaluate_list_comprehension(nested_lc, loop) - return loop - debug.dbg('eval_expression_list: %s', expression_list) result = [] calls_iterator = iter(expression_list) + if len(expression_list) > 1: + print expression_list for call in calls_iterator: if pr.Array.is_type(call, pr.Array.NOARRAY): r = list(itertools.chain.from_iterable(self.eval_statement(s) @@ -166,7 +153,7 @@ class Evaluator(object): result += self.follow_path(call_path, r, call.parent, position=call.start_pos) elif isinstance(call, pr.ListComprehension): - loop = evaluate_list_comprehension(call) + loop = _evaluate_list_comprehension(call) # Caveat: parents are being changed, but this doesn't matter, # because nothing else uses it. call.stmt.parent = loop @@ -374,3 +361,20 @@ def filter_private_variable(scope, call_scope, var_name): if s != scope.base.base: return True return False + + +def _evaluate_list_comprehension(lc, parent=None): + input = lc.input + nested_lc = lc.input.token_list[0] + if isinstance(nested_lc, pr.ListComprehension): + # is nested LC + input = nested_lc.stmt + module = input.get_parent_until() + # create a for loop, which does the same as list comprehensions + loop = pr.ForFlow(module, [input], lc.stmt.start_pos, lc.middle, True) + + loop.parent = parent or lc.get_parent_until(pr.IsScope) + + if isinstance(nested_lc, pr.ListComprehension): + loop = _evaluate_list_comprehension(nested_lc, loop) + return loop diff --git a/jedi/evaluate/precedence.py b/jedi/evaluate/precedence.py new file mode 100644 index 00000000..9935bf43 --- /dev/null +++ b/jedi/evaluate/precedence.py @@ -0,0 +1,71 @@ +""" +Handles operator precedence. +""" + +from jedi.parser import representation as pr + + +class PythonGrammar(object): + """ + Some kind of mirror of http://docs.python.org/3/reference/grammar.html. + """ + + class AtStart(tuple): + pass + + class MultiPart(str): + def __new__(cls, first, second): + self = str.__new__(cls, first) + self.second = second + + factor = AtStart(('+', '-', '~')) + power = '**', + term = '*', '/', '%', '//' + arith_expr = '+', '-' + + shift_expr = '<<', '>>' + and_expr = '&', + xor_expr = '^', + expr = '|', + + comparison = ('<', '>', '==', '>=', '<=', '!=', 'in', + MultiPart('not', 'in'), 'is', MultiPart('is', 'not')) + + not_test = AtStart(('not',)) + and_test = 'and', + or_test = 'or', + + #test = or_test ['if' or_test 'else' test] | lambdef + + #sliceop = ':' [test] + #subscript = test | [test] ':' [test] [sliceop] + order = (factor, power, term, arith_expr, shift_expr, and_expr, xor_expr, + expr, comparison, not_test, and_test, or_test) + + +class Precedence(object): + def __init__(self, left, operator, right=None): + self.left = left + self.operator = operator + self.right = right + + +def create_precedence(expression_list, previous=None): + element = next(expression_list, None) + if element is None: + return previous + + if isinstance(element, pr.Call): + element = str(element.name) + elif not isinstance(element, str): + if previous is None: + previous = element + return create_precedence(expression_list, previous=previous) + + for term in PythonGrammar.order: + if element in term: + right = create_precedence(expression_list) + if right is None: + return previous + else: + return Precedence(previous, element, right) diff --git a/test/completion/operations.py b/test/completion/operations.py deleted file mode 100644 index 99348f95..00000000 --- a/test/completion/operations.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -Test Jedi's operation understanding. Jedi should understand simple additions, -multiplications, etc. -""" -x = [1, '', 'a', 1.0] - -#? int() str() float() -x[12] - -#? str() -x[1 + 1] - -index = 0 + 1 - -#? str() -x[index] - - -def calculate(number): - return number + constant - -constant = 1 - -#? float() -x[calculate(2)] diff --git a/test/test_evaluate/test_precedence.py b/test/test_evaluate/test_precedence.py new file mode 100644 index 00000000..ec158a32 --- /dev/null +++ b/test/test_evaluate/test_precedence.py @@ -0,0 +1,13 @@ +from jedi.evaluate import precedence + + +def test_simple(): + p = precedence.create_precedence(iter([1, '+', 2])) + assert p.left == 1 + assert p.operator == '+' + assert p.right == 2 + + +def test_invalid(): + """Should just return a simple operation""" + assert precedence.create_precedence(iter([1, '+'])) == 1