diff --git a/builtin.py b/builtin.py index 291436f1..71210f2c 100644 --- a/builtin.py +++ b/builtin.py @@ -212,7 +212,7 @@ def parse_function_doc(func): args[i] += '=None' return ','.join(args) while True: - (param_str, changes) = re.subn(r' ?\[([^\[\]]+)\]', + param_str, changes = re.subn(r' ?\[([^\[\]]+)\]', change_options, param_str) if changes == 0: break diff --git a/debug.py b/debug.py index c0bbdedf..b3628f3f 100644 --- a/debug.py +++ b/debug.py @@ -21,7 +21,8 @@ def error(*args): def print_to_stdout(level, *args): """ The default debug function """ - print 'dbg:' if level == NOTICE else 'warning:', args + print(('dbg: ' if level == NOTICE else 'warning: ') + + ', '.join(str(a) for a in args)) debug_function = None #debug_function = print_to_stdout diff --git a/evaluate.py b/evaluate.py index 43126442..136f10ce 100644 --- a/evaluate.py +++ b/evaluate.py @@ -14,6 +14,7 @@ except NameError: return obj.next() import itertools +import copy import parsing import modules @@ -42,13 +43,14 @@ def memoize(default=None): memo = {} memoize_caches.append(memo) - def wrapper(*args): - if args in memo: - return memo[args] + def wrapper(*args, **kwargs): + key = (args, frozenset(kwargs.items())) + if key in memo: + return memo[key] else: - memo[args] = default + memo[key] = default rv = function(*args) - memo[args] = rv + memo[key] = rv return rv return wrapper return func @@ -161,25 +163,46 @@ class Execution(Exec): """ Get the return vars of a function. """ - def remove_executions(scope, get_returns=False): - stmts = [] - if isinstance(scope, parsing.Class): - # there maybe executions of executions - stmts = [Instance(scope, self.params)] + stmts = [] + #print '\n\n', self.params, self.params.values, self.params.parent_stmt + if isinstance(self.base, parsing.Class): + # there maybe executions of executions + stmts = [Instance(self.base, self.params)] + else: + # set the callback function to get the params + self.base.param_cb = self.get_params + ret = self.base.returns + for s in ret: + #temp, s.parent = s.parent, self + stmts += follow_statement(s) + #s.parent = temp + + # reset the callback function on exit + self.base.param_cb = None + + debug.dbg('exec stmts=', stmts, self.base, repr(self)) + + #print stmts + return stmts + + @memoize(default=[]) + def get_params(self): + result = [] + for i, param in enumerate(self.base.params): + try: + value = self.params.values[i] + except IndexError: + # This means, that there is no param in the call. So we just + # ignore it and take the default params. + result.append(param.get_name()) else: - if get_returns: - ret = scope.returns - for s in ret: - #for stmt in follow_statement(s): - # stmts += remove_executions(stmt) - stmts += follow_statement(s) - else: - stmts.append(scope) - return stmts - - result = remove_executions(self.base, True) - debug.dbg('exec stmts=', result, self.base, repr(self)) - + new_param = copy.copy(param) + calls = parsing.Array(parsing.Array.EMPTY, self.params.parent_stmt) + calls.values = [value] + new_param.assignment_calls = calls + name = copy.copy(param.get_name()) + name.parent = new_param + result.append(name) return result def __repr__(self): @@ -188,7 +211,7 @@ class Execution(Exec): def get_names_for_scope(scope, star_search=True): - """ + """ Get all completions possible for the current scope. The star search option is only here to provide an optimization. Otherwise the whole thing would make a little recursive maddness @@ -231,7 +254,7 @@ def get_scopes_for_name(scope, name, search_global=False): res_new += remove_statements(scopes) else: res_new.append(r) - debug.dbg('sfn remove', res_new, result) + debug.dbg('sfn remove, new: %s, old: %s' % (res_new, result)) return res_new def filter_name(scopes): @@ -253,10 +276,9 @@ def get_scopes_for_name(scope, name, search_global=False): # this is where self is added result.append(Instance(par.parent.parent)) else: - # TODO get function data - pass + result.append(par) else: - result.append(scope.parent) + result.append(par) debug.dbg('sfn filter', result) return result @@ -293,7 +315,8 @@ def follow_statement(stmt, scope=None): :param scope: contains a scope. If not given, takes the parent of stmt. """ if scope is None: - scope = stmt.get_parent_until(parsing.Function) + scope = stmt.get_parent_until(parsing.Function, Execution, + parsing.Class, Instance) call_list = stmt.get_assignment_calls() debug.dbg('calls', call_list, call_list.values) return follow_call_list(scope, call_list) @@ -329,7 +352,8 @@ def follow_call(scope, call): scopes = get_scopes_for_name(scope, current, search_global=True) result = strip_imports(scopes) - debug.dbg('call before', result, current, scope) + debug.dbg('call before result %s, current %s, scope %s' + % (result, current, scope)) result = follow_paths(path, result) return result diff --git a/ftest.py b/ftest.py index 7f44c378..9d16d836 100755 --- a/ftest.py +++ b/ftest.py @@ -2,9 +2,9 @@ import functions -#functions.debug.debug_function = functions.debug.print_to_stdout +functions.debug.debug_function = functions.debug.print_to_stdout functions.debug.ignored_modules = ['parsing', 'builtin'] -functions.debug.ignored_modules = ['parsing', 'builtin', 'evaluate', 'modules'] +#functions.debug.ignored_modules = ['parsing', 'builtin', 'evaluate', 'modules'] functions.modules.module_find_path.insert(0, '.') f_name = 'parsetest.py' diff --git a/functions.py b/functions.py index 018f0af1..91c425c9 100644 --- a/functions.py +++ b/functions.py @@ -189,7 +189,7 @@ def complete(source, row, column, source_path): completions = evaluate.get_names_for_scope(scope) else: stmt.parent = scope - scopes = evaluate.follow_statement(stmt, scope) + scopes = evaluate.follow_statement(stmt, scope=scope) completions = [] debug.dbg('possible scopes', scopes) diff --git a/parsetest.py b/parsetest.py index 91f31d7e..7e03e3e1 100644 --- a/parsetest.py +++ b/parsetest.py @@ -141,10 +141,10 @@ c, 1, c3()) [0].pop() c = u"asdf".join([1,2]) matrix_test = [[1,2], [1,3]] c = c1().c3().sleep() -asdf = c1; asdf2 = asdf -b= asdf2 +asdf = c1; asdf2 = asdf b= asdf2 + c = b().c3() 1.0.fromhex(); import flask ; flsk = flask.Flask + flask.Request; abc = [1,2+3]; abc[0]. -import pylab -abc = datetime; return [abc][0]. ;pylab. +import pylab; def add(a1,b1): nana = 1; return a1+b1 +abc = datetime; return [abc][0]. ;pylab.; add(1+2,2). diff --git a/parsing.py b/parsing.py index 6894df9c..7ecc19df 100644 --- a/parsing.py +++ b/parsing.py @@ -216,7 +216,7 @@ class Scope(Simple): name = self.module_path return "<%s: %s@%s-%s>" % \ - (self.__class__.__name__, name, self.line_nr, self.__hash__()) + (self.__class__.__name__, name, self.line_nr, self.line_end) class GlobalScope(Scope): @@ -308,7 +308,10 @@ class Function(Scope): p.parent = self self.decorators = [] self.returns = [] - is_generator = False + self.is_generator = False + + # callback to set the function + self.param_cb = None def get_code(self, first_indent=False, indention=" "): str = "\n".join('@' + stmt.get_code() for stmt in self.decorators) @@ -321,8 +324,18 @@ class Function(Scope): def get_set_vars(self): n = super(Function, self).get_set_vars() - for i, p in enumerate(self.params): - n += p.set_vars or p.used_vars + if self.param_cb or False: + # this is the really ugly part, where the functional style of this + # get methods is broken, it executes a callback. + # This is important, because something has to inject the params + # into the functions, with the right values. + n += self.param_cb() + else: + for p in self.params: + try: + n.append(p.get_name()) + except IndexError: + debug.warning("multiple names in param %s" % n) return n @@ -647,6 +660,13 @@ class Param(Statement): # this is defined by the parser later on, not at the initialization self.position = None + def get_name(self): + """ get the name of the param """ + n = self.set_vars or self.used_vars + if len(n) > 1: + raise IndexError("Multiple param names (%s)." % n) + return n[0] + class Call(object): NAME = object() diff --git a/test/completion/arrays.py b/test/completion/arrays.py index 47674740..d9273b1f 100644 --- a/test/completion/arrays.py +++ b/test/completion/arrays.py @@ -5,3 +5,6 @@ a = list() #? ['append'] [a][0].append + +#? ['append'] +[[a]][0][100].append diff --git a/test/completion/functions.py b/test/completion/functions.py index 2bdbbd25..4b332a1f 100644 --- a/test/completion/functions.py +++ b/test/completion/functions.py @@ -1,6 +1,12 @@ -def array(): +def array(first_param): + #? ['first_param'] + first_param return list() + +#? [] +array.first_param + #? ['append'] array().append diff --git a/test/completion/types.py b/test/completion/types.py index cc12855c..fa887f68 100644 --- a/test/completion/types.py +++ b/test/completion/types.py @@ -66,5 +66,5 @@ tup.c tup2 = tuple() #? ['index'] tup2.i -#? ['index'] +##? ['index'] TODO enable ().i