From 69c720423d77a8ed776ae13865bcbaac4fed86b0 Mon Sep 17 00:00:00 2001 From: David Halter Date: Tue, 24 Apr 2012 13:13:37 +0200 Subject: [PATCH] tuple support --- evaluate.py | 67 ++++++++++++++++++++++++++++++++------- parsetest.py | 2 +- parsing.py | 18 +++++++---- test/completion/arrays.py | 7 ++++ test/completion/basic.py | 6 ---- 5 files changed, 75 insertions(+), 25 deletions(-) diff --git a/evaluate.py b/evaluate.py index 38d2b8a0..df0ff623 100644 --- a/evaluate.py +++ b/evaluate.py @@ -44,7 +44,7 @@ def memoize(default=None): return memo[key] else: memo[key] = default - rv = function(*args) + rv = function(*args, **kwargs) memo[key] = rv return rv return wrapper @@ -138,7 +138,7 @@ class Execution(Exec): result.append(param.get_name()) else: new_param = copy.copy(param) - calls = parsing.Array(parsing.Array.EMPTY, + calls = parsing.Array(parsing.Array.NOARRAY, self.params.parent_stmt) calls.values = [value] new_param._assignment_calls = calls @@ -171,12 +171,17 @@ class Array(object): and iv[0][0].type == parsing.Call.NUMBER \ and self._array.type != parsing.Array.DICT: try: - values = [self._array.values[int(iv[0][0].name)]] + values = [self._array[int(iv[0][0].name)]] except: pass scope = self._array.parent_stmt.parent return follow_call_list(scope, values) + def get_exact_index_types(self, index): + values = [self._array[index]] + scope = self._array.parent_stmt.parent + return follow_call_list(scope, values) + def get_defined_names(self): """ This method generates all ArrayElements for one parsing.Array. """ # array.type is a string with the type, e.g. 'list' @@ -185,7 +190,7 @@ class Array(object): return [ArrayElement(n) for n in names] def __repr__(self): - return "<%s of %s>" % (self.__class__.__name__, self._array) + return "" % (self.__class__.__name__, self._array) class ArrayElement(object): @@ -248,7 +253,7 @@ def get_scopes_for_name(scope, name, search_global=False): res_new = [] for r in result: if isinstance(r, parsing.Statement): - scopes = follow_statement(r) + scopes = follow_statement(r, seek_name=name) res_new += remove_statements(scopes) else: res_new.append(r) @@ -312,9 +317,44 @@ def strip_imports(scopes): result.append(s) return result +def assign_tuples(scope, tuples, results, seek_name): + """ + This is a normal assignment checker. In python functions and other things + can return tuples: + >>> a, b = 1, "" + >>> a, (b, c) = 1, ("", 1.0) + + Here, if seek_name is "a", the number type will be returned. + The first part (before `=`) is the param tuples, the second one result. + """ + def eval_results(index): + types = [] + for r in results: + types += r.get_exact_index_types(index) + return types + + result = [] + for i, t in enumerate(tuples): + # used in an assignment, there is just one call and no other things, + # therefor we can just assume, that the first part is important. + t = t[0] + # check the left part, if it's still tuples in it or a Call + if isinstance(t, parsing.Array): + # these are "sub" tuples + print 'arr', t + result += assign_tuples(scope, t, eval_results(i), seek_name) + else: + if t.name.names[-1] == seek_name: + result += eval_results(i) + print 't', t, result + else: + print 'name not found' + #print follow_call_list(scope, result[i]) + print seek_name, tuples, results + return result @memoize(default=[]) -def follow_statement(stmt, scope=None): +def follow_statement(stmt, scope=None, seek_name=None): """ :param stmt: contains a statement :param scope: contains a scope. If not given, takes the parent of stmt. @@ -326,13 +366,18 @@ def follow_statement(stmt, scope=None): debug.dbg('calls', call_list, call_list) result = set(follow_call_list(scope, call_list)) - if stmt.assignment_details: + # assignment checking is only important if the statement defines multiple + # variables + if len(stmt.get_set_vars()) > 1 and seek_name and stmt.assignment_details: + print 'seek', seek_name new_result = [] for op, set_vars in stmt.assignment_details: - stmt.assignment_details[0] - #print '\n\nlala', op, set_vars.values, call_list.values - #print stmt, scope - #result = new_result + print '\ntup' + new_result += assign_tuples(scope, set_vars, result, seek_name) + print new_result + print '\n\nlala', op, set_vars.values, call_list.values + print stmt, scope + result = new_result return result diff --git a/parsetest.py b/parsetest.py index 9c34138f..8bb1ec2b 100644 --- a/parsetest.py +++ b/parsetest.py @@ -147,4 +147,4 @@ c = b().c3(); abc = datetime; return [abc][0]. ;pylab.; add(1+2,2).; for fi in [ 1.0.fromhex(); import flask ; flsk = flask.Flask + flask.Request; abc = [1,2+3]; abc[0]. import pylab; def add(a1,b1): nana = 1; return a1+b1 -flow_test.; a12, b12 = (1,""); a12. +flow_test.; a12, (b12, c12) = (1,("", "")); a12. diff --git a/parsing.py b/parsing.py index 60b598b9..7ef3871b 100644 --- a/parsing.py +++ b/parsing.py @@ -555,7 +555,7 @@ class Statement(Simple): if self._assignment_calls: return self._assignment_calls self._assignment_details = [] - result = Array(Array.EMPTY, self) + result = Array(Array.NOARRAY, self) top = result level = 0 is_chain = False @@ -578,15 +578,19 @@ class Statement(Simple): # TODO there may be multiple assignments: a = b = 1 self._assignment_details.append((tok, top)) - # initialize the first item - result = Array(Array.EMPTY, self) + # All these calls wouldn't be important if nonlocal would + # exist. -> Initialize the first item again. + result = Array(Array.NOARRAY, self) top = result + level = 0 + close_brackets = False + is_chain = False continue elif tok == 'as': next(tok_iter) continue - brackets = {'(': Array.EMPTY, '[': Array.LIST, '{': Array.SET} + brackets = {'(': Array.NOARRAY, '[': Array.LIST, '{': Array.SET} is_call = lambda: result.__class__ == Call is_call_or_close = lambda: is_call() or close_brackets @@ -638,7 +642,7 @@ class Statement(Simple): close_brackets = False result.add_field() # important - it cannot be empty anymore - if result.type == Array.EMPTY: + if result.type == Array.NOARRAY: result.type = Array.TUPLE elif tok in [')', '}', ']']: while is_call_or_close(): @@ -755,7 +759,7 @@ class Array(Call): below. :type array_type: int """ - EMPTY = None + NOARRAY = None TUPLE = 'tuple' LIST = 'list' DICT = 'dict' @@ -808,7 +812,7 @@ class Array(Call): return self.values.__iter__() def __repr__(self): - if self.type == self.EMPTY: + if self.type == self.NOARRAY: temp = 'empty' elif self.type == self.TUPLE: temp = 'tuple' diff --git a/test/completion/arrays.py b/test/completion/arrays.py index d9273b1f..9518cf89 100644 --- a/test/completion/arrays.py +++ b/test/completion/arrays.py @@ -8,3 +8,10 @@ a = list() #? ['append'] [[a]][0][100].append + + +a1, b1 = (1, "") +#? ['real'] +a1.real +#? ['lower'] +b1.lower diff --git a/test/completion/basic.py b/test/completion/basic.py index 2baebae5..13da2275 100644 --- a/test/completion/basic.py +++ b/test/completion/basic.py @@ -1,9 +1,3 @@ -a1, b1 = (1, "") -#? ['real'] -a1.real -#? ['lower'] -a1.lower - for a in [1,2]: #? ['real'] a.real