diff --git a/evaluate.py b/evaluate.py index d147c8eb..85a9fc9e 100644 --- a/evaluate.py +++ b/evaluate.py @@ -1,4 +1,34 @@ import parsing +import __builtin__ +import itertools + + +class Arr(object): + """ + - caching one function + - iterating over arrays objects + - test + """ + cache = [] + + def __init__(self, scope, ): + self.counter = 0 + self.types = [] # each an array, like: [[list], [A,B]] + + def __iter__(self): + return self + + def next(self): + if self.counter < len(Arr.cache): + return Arr.cache[self.counter] + else: + if blub: + Arr.cache.append(1) + self.counter += 1 + return Arr.cache[self.counter] + else: + raise StopIteration + def get_names_for_scope(scope): """ Get all completions possible for the current scope. """ @@ -16,15 +46,69 @@ def get_names_for_scope(scope): # point: chaining # execution: -> eval returns default & ? def follow_statement(scope, stmt): - arr = stmt.get_assignment_calls() - return call + arr = stmt.get_assignment_calls().values[0][0] + print arr + + path = arr.generate_call_list() + path, path_print = itertools.tee(path) + print '\n\ncalls:' + for c in path_print: + print c + + print '\n\nfollow' + current = next(path) + result = [] + if isinstance(current, parsing.Array): + if current.arr_type == parsing.Array.EMPTY: + # the normal case - no array type + print 'length', len(current) + elif current.arr_type == parsing.Array.LIST: + result.append(__builtin__.list()) + elif current.arr_type == parsing.Array.SET: + result.append(__builtin__.set()) + elif current.arr_type == parsing.Array.TUPLE: + result.append(__builtin__.tuple()) + elif current.arr_type == parsing.Array.DICT: + result.append(__builtin__.dict()) + else: + print current + pass + + print result + result = follow_paths(path, result) + print result + exit() + + return result + + +def follow_paths(path, results): + results_new = [] + try: + if len(results) > 1: + iter_paths = itertools.tee(path, len(results)) + else: + iter_paths = [path] + for i, r in enumerate(results): + results_new += follow_path(iter_paths[i], r) + except StopIteration: + return results + return results_new + + +def follow_path(path, result): + current = next(path) + #result = [] + + result = follow_paths(path, result) + return result def follow_array(scope, array): - return + yield 1 -def follow_path(scope, path): +def follow_path_old(scope, path): """ Follow a path of python names. recursive! diff --git a/functions.py b/functions.py index 87f62072..0902730e 100644 --- a/functions.py +++ b/functions.py @@ -178,7 +178,7 @@ def complete(source, row, column, file_callback=None): column = 17 row = 140 - column = 2 + column = 200 f = File(source=source, row=row) scope = f.parser.user_scope @@ -198,14 +198,14 @@ def complete(source, row, column, file_callback=None): result = [] if path: + # just parse one statement + r = parsing.PyFuzzyParser(".".join(path)) + print 'p', r.top.get_code().replace('\n', r'\n'), r.top.statements[0] + evaluate.follow_statement(scope, r.top.statements[0]) + exit() + name = path.pop() if path: - # just parse one statement - r = parsing.PyFuzzyParser(".".join(path)) - print 'p', r.top.get_code(), r.top.statements[0] - evaluate.follow_statement(scope, r.top.statements[0]) - exit() - scopes = evaluate.follow_path(scope, tuple(path)) dbg('possible scopes', scopes) diff --git a/parsing.py b/parsing.py index 989f693e..faf5cd98 100644 --- a/parsing.py +++ b/parsing.py @@ -499,11 +499,14 @@ class Statement(Simple): 'like' a lazy execution. """ result = Array(Array.EMPTY) + top = result level = 0 is_chain = False + close_brackets = False - for tok_temp in self.token_list: - #print 'tok', tok_temp + print 'tok_list', self.token_list + for i, tok_temp in enumerate(self.token_list): + #print 'tok', tok_temp, result try: token_type, tok, indent = tok_temp if level == 0 and \ @@ -519,56 +522,126 @@ class Statement(Simple): tok = tok_temp brackets = {'(': Array.EMPTY, '[': Array.LIST, '{': Array.SET} - is_call = isinstance(result, Call) + is_call = lambda: result.__class__ == Call + is_call_or_close = lambda: is_call() or close_brackets if isinstance(tok, Name): - call = Call(tok, result) if is_chain: + call = Call(tok, result) result = result.set_next_chain_call(call) is_chain = False + close_brackets = False else: + if close_brackets: + result = result.parent + close_brackets = False + call = Call(tok, result) result.add_to_current_field(call) result = call elif tok in brackets.keys(): level += 1 - result = Array(brackets[tok], result) - if is_call: + if is_call_or_close(): + result = Array(brackets[tok], result) result = result.parent.add_execution(result) + close_brackets = False else: + result = Array(brackets[tok], result) result.parent.add_to_current_field(result) elif tok == ':': - if is_call: + if is_call_or_close(): result = result.parent + close_brackets = False result.add_dictionary_key() elif tok == '.': + if close_brackets: + result = result.parent + close_brackets = False is_chain = True elif tok == ',': - if is_call: + if is_call_or_close(): result = result.parent + close_brackets = False result.add_field() # important - it cannot be empty anymore if result.arr_type == Array.EMPTY: result.arr_type = Array.TUPLE elif tok in [')', '}', ']']: + while is_call_or_close(): + result = result.parent + close_brackets = False + if tok == '}' and not len(result): + # this is a really special case - empty brackets {} are + # always dictionaries and not sets. + result.arr_type = Array.DICT level -= 1 - result = result.parent + #result = result.parent + close_brackets = True else: # TODO catch numbers and strings -> token_type and make # calls out of them - if is_call: + if is_call_or_close(): result = result.parent + close_brackets = False result.add_to_current_field(tok) - if isinstance(result, Call): - # if the last added object was a name, the result will not be the - # top tree. - result = result.parent + print 'tok_end', tok_temp, result, close_brackets + if level != 0: raise ParserError("Brackets don't match: %s. This is not normal " "behaviour. Please submit a bug" % level) - return result + return top -class Array(object): +class Call(object): + """ The statement object of functions, to """ + def __init__(self, name, parent=None): + self.name = name + # parent is not the oposite of next. The parent of c: a = [b.c] would + # be an array. + self.parent = parent + + self.next = None + self.execution = None + + def set_next_chain_call(self, call): + """ Adds another part of the statement""" + self.next = call + #print '\n\npar', call.parent, self.parent, type(call), type(self) + call.parent = self.parent + return call + + def add_execution(self, call): + """ + An execution is nothing else than brackets, with params in them, which + shows access on the internals of this name. + """ + self.execution = call + # there might be multiple executions, like a()[0], in that case, they + # have the same parent. Otherwise it's not possible to parse proper. + if self.parent.execution == self: + call.parent = self.parent + else: + call.parent = self + return call + + def generate_call_list(self): + try: + for name_part in self.name.names: + yield name_part + except AttributeError: + yield self + if self.execution is not None: + for y in self.execution.generate_call_list(): + yield y + if self.next is not None: + for y in self.next.generate_call_list(): + yield y + + def __repr__(self): + return "<%s: %s of %s>" % \ + (self.__class__.__name__, self.name, self.parent) + + +class Array(Call): """ Describes the different python types for an array, but also empty statements. In the Python syntax definitions this type is named 'atom'. @@ -586,14 +659,18 @@ class Array(object): SET = object() def __init__(self, arr_type, parent=None): + super(Array, self).__init__(None, parent) + self.arr_type = arr_type self.values = [] self.keys = [] - self.parent = parent def add_field(self): """ Just add a new field to the values. + + Each value has a sub-array, because there may be different tokens in + one array. """ self.values.append([]) self.keys.append(None) @@ -624,15 +701,17 @@ class Array(object): def __iter__(self): if self.arr_type == self.DICT: - return self.values.items() + return self.values.items().__iter__() else: - return self.values + return self.values.__iter__() def __repr__(self): if self.arr_type == self.EMPTY: temp = 'empty' elif self.arr_type == self.TUPLE: temp = 'tuple' + elif self.arr_type == self.LIST: + temp = 'list' elif self.arr_type == self.DICT: temp = 'dict' elif self.arr_type == self.SET: @@ -641,33 +720,8 @@ class Array(object): (self.__class__.__name__, temp, self.parent) -class Call(object): - """ The statement object of functions, to """ - def __init__(self, name, parent): - self.name = name - self.parent = parent - - self.next = None - self.param_array = None - self.executions = [] - - def set_next_chain_call(self, call): - """ Adds another part of the statement""" - self.next = call - call.parent = self.parent - return call - - def add_execution(self, call): - """ - An execution is nothing else than brackets, with params in them, which - shows access on the internals of this name. - """ - self.executions.append(call) - return call - - def __repr__(self): - return "<%s: %s of %s>" % \ - (self.__class__.__name__, self.name, self.parent) +class NamePart(str): + pass class Name(Simple): @@ -679,7 +733,7 @@ class Name(Simple): """ def __init__(self, names, indent, line_nr, line_end): super(Name, self).__init__(indent, line_nr, line_end) - self.names = tuple(names) + self.names = tuple(NamePart(n) for n in names) def get_code(self): """ Returns the names in a full string format """ diff --git a/test.py b/test.py index 68be53d5..31f5a76b 100644 --- a/test.py +++ b/test.py @@ -136,5 +136,7 @@ class c1(): -c1().c2.\ -c +(c1().c2.\ +c, 1, c3()) [0].pop() + +c = "a".join([1,2])