diff --git a/evaluate.py b/evaluate.py index effee528..d99271be 100644 --- a/evaluate.py +++ b/evaluate.py @@ -10,7 +10,7 @@ TODO include super classes try: next except NameError: - def next(obj): + def next(obj): return obj.next() import itertools @@ -55,8 +55,9 @@ def memoize(default=None): class Exec(object): - def __init__(self, base): + def __init__(self, base, params): self.base = base + self.params = params def get_parent_until(self, *args): return self.base.get_parent_until(*args) @@ -95,6 +96,60 @@ class Instance(Exec): (self.__class__.__name__, self.base) +class Array(object): + """ + Used as a mirror to parsing.Array, if needed. It defines some getter + methods which are important in this module. + """ + def __init__(self, array): + self._array = array + + def get_index_type(self, index): + #print self._array.values, index.values + values = self._array.values + #print 'ui', index.values, index.values[0][0].type + iv = index.values + if len(iv) == 1 and len(iv[0]) == 1 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)]] + except: + pass + 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' + scope = get_scopes_for_name(builtin.Builtin.scope, self._array.type)[0] + names = scope.get_defined_names() + return [ArrayElement(n) for n in names] + + def __repr__(self): + return "<%s of %s>" % (self.__class__.__name__, self._array) + + +class ArrayElement(object): + def __init__(self, name): + self.name = name + + @property + def parent(self): + raise NotImplementedError("This shouldn't happen") + return + + @property + def returns(self): + return self.name.parent.returns + + @property + def names(self): + return self.name.names + + def __repr__(self): + return "<%s of %s>" % (self.__class__.__name__, self.name) + + class Execution(Exec): """ This class is used to evaluate functions and their returns. @@ -110,7 +165,7 @@ class Execution(Exec): stmts = [] if isinstance(scope, parsing.Class): # there maybe executions of executions - stmts = [Instance(scope)] + stmts = [Instance(scope, self.params)] else: if get_returns: ret = scope.returns @@ -139,11 +194,11 @@ def get_names_for_scope(scope): while scope: # class variables/functions are only availabe if not isinstance(scope, parsing.Class) or scope == start_scope: - compl += scope.get_set_vars() + compl += scope.get_defined_names() scope = scope.parent # add builtins to the global scope - compl += builtin.Builtin.scope.get_set_vars() + compl += builtin.Builtin.scope.get_defined_names() #print 'gnfs', scope, compl return compl @@ -184,27 +239,30 @@ def get_scopes_for_name(scope, name, search_global=False): # result += filter_name(i) #else: if [name] == list(scope.names): - par = scope.parent - if isinstance(par, parsing.Flow): - # TODO get Flow data, which is defined by the loop (or - # with) - pass - elif isinstance(par, parsing.Param): - if isinstance(par.parent.parent, parsing.Class) \ - and par.position == 0: - result.append(Instance(par.parent.parent)) - else: - # TODO get function data - pass + if isinstance(scope, ArrayElement): + result.append(scope) else: - result.append(scope.parent) + par = scope.parent + if isinstance(par, parsing.Flow): + # TODO get Flow data, which is defined by the loop + # (or with) + pass + elif isinstance(par, parsing.Param): + if isinstance(par.parent.parent, parsing.Class) \ + and par.position == 0: + result.append(Instance(par.parent.parent)) + else: + # TODO get function data + pass + else: + result.append(scope.parent) debug.dbg('sfn filter', result) return result if search_global: names = get_names_for_scope(scope) else: - names = scope.get_set_vars() + names = scope.get_defined_names() return remove_statements(filter_name(names)) @@ -225,7 +283,8 @@ def strip_imports(scopes): else: result += new_scopes for n in new_scopes: - result += strip_imports(i for i in n.get_imports() if i.star) + result += strip_imports(i for i in n.get_imports() + if i.star) else: result.append(s) return result @@ -239,14 +298,19 @@ def follow_statement(stmt, scope=None): """ if scope is None: scope = stmt.get_parent_until(parsing.Function) + call_list = stmt.get_assignment_calls() + debug.dbg('calls', call_list, call_list.values) + return follow_call_list(scope, call_list) + + +def follow_call_list(scope, call_list): + """ The call list has a special structure """ result = [] - calls = stmt.get_assignment_calls() - debug.dbg('calls', calls, calls.values) - for tokens in calls: - for tok in tokens: - if not isinstance(tok, str): + for calls in call_list: + for call in calls: + if not isinstance(call, str): # the string tokens are just operations (+, -, etc.) - result += follow_call(scope, tok) + result += follow_call(scope, call) return result @@ -256,7 +320,7 @@ def follow_call(scope, call): current = next(path) if isinstance(current, parsing.Array): - result = [current] + result = [Array(current)] else: # TODO add better care for int/unicode, now str/float are just used # instead @@ -286,6 +350,9 @@ def follow_paths(path, results): for i, r in enumerate(results): results_new += follow_path(iter_paths[i], r) except StopIteration: + #if isinstance(s, parsing.Array): + # completions += s. + #else: return results return results_new @@ -303,11 +370,12 @@ def follow_path(path, input): if isinstance(current, parsing.Array): # this must be an execution, either () or [] if current.type == parsing.Array.LIST: - result = [] # TODO eval lists - elif current.type not in [parsing.Array.DICT, parsing]: + result = scope.get_index_type(current) + elif current.type not in [parsing.Array.DICT]: # scope must be a class or func - make an instance or execution debug.dbg('befexec', scope) - result = strip_imports(Execution(scope).get_return_types()) + exe = Execution(scope, current) + result = strip_imports(exe.get_return_types()) debug.dbg('exec', result) #except AttributeError: # debug.dbg('cannot execute:', scope) diff --git a/ftest.py b/ftest.py index 0434a10f..357c966c 100755 --- a/ftest.py +++ b/ftest.py @@ -9,7 +9,7 @@ functions.modules.module_find_path.insert(0, '.') f_name = 'parsetest.py' import os -path = os.getcwd() + '/' + f_name +path = os.path.join(os.getcwd(), f_name) f = open(path) code = f.read() diff --git a/parsetest.py b/parsetest.py index 65f3f4e8..7627e5c7 100644 --- a/parsetest.py +++ b/parsetest.py @@ -140,11 +140,11 @@ class c1(): c, 1, c3()) [0].pop() c = u"asdf".join([1,2]) -matrx_test = [[1,2], [1,3]] +matrix_test = [[1,2], [1,3]] c = c1().c3().sleep() asdf = c1; asdf2 = asdf b= asdf2 #import parsing as test c = b().c3() 1.0.fromhex(); import flask ; flsk = flask.Flask + flask.Request; -matrix_test. +abc = [1,2+3]; abc[0]. diff --git a/parsing.py b/parsing.py index 7d3146fc..00cbc3c3 100644 --- a/parsing.py +++ b/parsing.py @@ -520,10 +520,13 @@ class Statement(Simple): This is not done in the main parser, because it might be slow and most of the statements won't need this data anyway. This is something 'like' a lazy execution. + + This is not really nice written, sorry for that. If you plan to replace + it and make it nicer, that would be cool :-) """ if self.assignment_calls: return self.assignment_calls - result = Array(Array.EMPTY) + result = Array(Array.EMPTY, self) top = result level = 0 is_chain = False @@ -540,7 +543,7 @@ class Statement(Simple): # TODO there may be multiple assignments: a = b = 1 # initialize the first item - result = Array(Array.EMPTY) + result = Array(Array.EMPTY, self) top = result continue except TypeError: @@ -560,7 +563,7 @@ class Statement(Simple): c_type = Call.NUMBER if is_chain: - call = Call(tok, c_type, result) + call = Call(tok, c_type, self, result) result = result.set_next_chain_call(call) is_chain = False close_brackets = False @@ -568,17 +571,19 @@ class Statement(Simple): if close_brackets: result = result.parent close_brackets = False - call = Call(tok, c_type, result) + call = Call(tok, c_type, self, result) result.add_to_current_field(call) result = call elif tok in brackets.keys(): # brackets level += 1 + # TODO why is this not working, when the two statements ar not + # in the if/else clause (they are exactly the same!!!) if is_call_or_close(): - result = Array(brackets[tok], result) + result = Array(brackets[tok], self, result) result = result.parent.add_execution(result) close_brackets = False else: - result = Array(brackets[tok], result) + result = Array(brackets[tok], self, result) result.parent.add_to_current_field(result) elif tok == ':': if is_call_or_close(): @@ -586,7 +591,8 @@ class Statement(Simple): close_brackets = False result.add_dictionary_key() elif tok == '.': - if close_brackets: + if close_brackets and result.parent != top: + # only get out of the array, if it is a array execution result = result.parent close_brackets = False is_chain = True @@ -645,7 +651,7 @@ class Call(object): STRING = object() """ The statement object of functions, to """ - def __init__(self, name, type, parent=None): + def __init__(self, name, type, parent_stmt, parent=None): self.name = name # parent is not the oposite of next. The parent of c: a = [b.c] would # be an array. @@ -654,6 +660,7 @@ class Call(object): self.next = None self.execution = None + self.parent_stmt = parent_stmt def set_next_chain_call(self, call): """ Adds another part of the statement""" @@ -705,14 +712,14 @@ class Array(Call): below. :type array_type: int """ - EMPTY = object() - TUPLE = object() - LIST = object() - DICT = object() - SET = object() + EMPTY = None + TUPLE = 'tuple' + LIST = 'list' + DICT = 'dict' + SET = 'set' - def __init__(self, arr_type, parent=None): - super(Array, self).__init__(None, arr_type, parent) + def __init__(self, arr_type, parent_stmt, parent=None): + super(Array, self).__init__(None, arr_type, parent_stmt, parent) self.values = [] self.keys = [] @@ -1024,6 +1031,8 @@ class PyFuzzyParser(object): :type pre_used_token: set :return: Statement + last parsed token. :rtype: (Statement, str) + + TODO improve abort criterion of not closing parentheses """ string = ''