1
0
forked from VimPlug/jedi

statement parsing

This commit is contained in:
David Halter
2012-02-21 15:39:28 +01:00
parent 7597ed05a0
commit 4ba5b7c83b
3 changed files with 92 additions and 38 deletions

View File

@@ -38,7 +38,7 @@ class Completer(object):
self.parser = PyFuzzyParser() self.parser = PyFuzzyParser()
def evalsource(self, text, line=0): def evalsource(self, text, line=0):
sc = self.parser.parse(text, line) sc = self.parser.parse(text)
self.sc = sc # TODO rm self.sc = sc # TODO rm
src = sc.get_code() src = sc.get_code()
dbg("source: %s" % src) dbg("source: %s" % src)
@@ -208,7 +208,10 @@ print ''
showdbg() showdbg()
print cmpl.parser.top.get_code() print cmpl.parser.top.get_code()
#print cmpl.parser.top.subscopes[1].subscopes[0].get_code()
p = cmpl.parser
s = p.top
import code import code
sh = code.InteractiveConsole(locals=locals()) sh = code.InteractiveConsole(locals=locals())
#sh.interact("InteractiveConsole") #sh.interact("InteractiveConsole")

View File

@@ -8,12 +8,16 @@ import cStringIO
def indent_block(text, indention=" "): def indent_block(text, indention=" "):
""" This function indents a text block with a default of four spaces """ """ This function indents a text block with a default of four spaces """
temp = ''
while text and text[-1] == '\n':
temp += text[-1]
text = text[:-1]
lines = text.split('\n') lines = text.split('\n')
return '\n'.join(map(lambda s: indention + s, lines)) return '\n'.join(map(lambda s: indention + s, lines)) + temp
class Scope(object): class Scope(object):
def __init__(self, name, indent, docstr=''): def __init__(self, name, indent, line_nr, docstr=''):
self.subscopes = [] self.subscopes = []
self.locals = [] self.locals = []
self.imports = [] self.imports = []
@@ -21,6 +25,7 @@ class Scope(object):
self.parent = None self.parent = None
self.name = name self.name = name
self.indent = indent self.indent = indent
self.line_nr = line_nr
def add_scope(self, sub): def add_scope(self, sub):
#print 'push scope: [%s@%s]' % (sub.name, sub.indent) #print 'push scope: [%s@%s]' % (sub.name, sub.indent)
@@ -56,30 +61,30 @@ class Scope(object):
self.locals.remove(l) self.locals.remove(l)
def get_code(self, first_indent=False, indention=" "): def get_code(self, first_indent=False, indention=" "):
str = "" string = ""
if len(self.docstr) > 0: if len(self.docstr) > 0:
str += '"""' + self.docstr + '"""\n' string += '"""' + self.docstr + '"""\n'
for i in self.imports: for i in self.imports:
str += i.get_code() + '\n' string += i.get_code() + '\n'
for sub in self.subscopes: for sub in self.subscopes:
str += sub.get_code(first_indent=True, indention=indention) string += str(sub.line_nr) + sub.get_code(first_indent=True, indention=indention)
for l in self.locals: for l in self.locals:
str += l + '\n' string += l + '\n'
if first_indent: if first_indent:
str = indent_block(str, indention=indention) string = indent_block(string, indention=indention)
return "_%s_%s" % (self.indent, str) return string
def is_empty(self): def is_empty(self):
""" """
this function returns true if there are no subscopes, imports, locals. this function returns true if there are no subscopes, imports, locals.
""" """
return not (self.locals, self.imports, self.subscopes) return not (self.locals or self.imports or self.subscopes)
class Class(Scope): class Class(Scope):
def __init__(self, name, supers, indent, docstr=''): def __init__(self, name, supers, indent, line_nr, docstr=''):
super(Class, self).__init__(name, indent, docstr) super(Class, self).__init__(name, indent, line_nr, docstr)
self.supers = supers self.supers = supers
def get_code(self, first_indent=False, indention=" "): def get_code(self, first_indent=False, indention=" "):
@@ -88,14 +93,15 @@ class Class(Scope):
str += '(%s)' % ','.join(self.supers) str += '(%s)' % ','.join(self.supers)
str += ':\n' str += ':\n'
str += super(Class, self).get_code(True, indention) str += super(Class, self).get_code(True, indention)
print "get_code class %s %i" % (self.name, self.is_empty())
if self.is_empty(): if self.is_empty():
str += indent_block("pass\n", indention=indention) str += "pass\n"
return str return str
class Function(Scope): class Function(Scope):
def __init__(self, name, params, indent, docstr=''): def __init__(self, name, params, indent, line_nr, docstr=''):
Scope.__init__(self, name, indent, docstr) Scope.__init__(self, name, indent, line_nr, docstr)
self.params = params self.params = params
def get_code(self, first_indent=False, indention=" "): def get_code(self, first_indent=False, indention=" "):
@@ -104,22 +110,24 @@ class Function(Scope):
# str += self.childindent()+'"""'+self.docstr+'"""\n' # str += self.childindent()+'"""'+self.docstr+'"""\n'
str += super(Function, self).get_code(True, indention) str += super(Function, self).get_code(True, indention)
if self.is_empty(): if self.is_empty():
str += indent_block("pass\n", indention=indention) str += "pass\n"
print "func", self.locals print "func", self.locals
return str return str
class Import(object): class Import(object):
""" """
stores the imports of class files stores the imports of any scopes
""" """ # TODO check star?
def __init__(self, namespace, alias='', from_ns='', star=False): def __init__(self, line_nr, namespace, alias='', from_ns='', star=False):
""" """
@param line_nr
@param namespace: the namespace which is imported @param namespace: the namespace which is imported
@param alias: the alias (valid in the current namespace) @param alias: the alias (valid in the current namespace)
@param from_ns: from declaration in an import @param from_ns: from declaration in an import
@param star: if a star is used -> from time import * @param star: if a star is used -> from time import *
""" """
self.line_nr = line_nr
self.namespace = namespace self.namespace = namespace
self.alias = alias self.alias = alias
self.from_ns = from_ns self.from_ns = from_ns
@@ -138,13 +146,56 @@ class Import(object):
return "test import " + ns_str return "test import " + ns_str
class Statement(object):
""" This is the super class for Local and Functions """
def __init__(self, line_nr, stmt):
"""
@param line_nr
@param stmt the statement string
"""
self.line_nr = line_nr
self. stmt = stmt
def get_code(self):
raise NotImplementedError()
class Local(object):
"""
stores locals variables of any scopes
"""
def __init__(self, line_nr, left, right=None, is_global=False):
"""
@param line_nr
@param left: the left part of the local assignment
@param right: the right part of the assignment, must not be set
(in case of global)
@param is_global: defines a global variable
"""
self.line_nr = line_nr
self.left = left
self.right = right
def get_code(self):
if self.alias:
ns_str = "%s as %s" % (self.namespace, self.alias)
else:
ns_str = self.namespace
if self.from_ns:
if self.star:
ns_str = '*'
return "test from %s import %s" % (self.from_ns, ns_str)
else:
return "test import " + ns_str
class PyFuzzyParser(object): class PyFuzzyParser(object):
""" """
This class is used to parse a Python file, it then divides them into a This class is used to parse a Python file, it then divides them into a
class structure of differnt scopes. class structure of differnt scopes.
""" """
def __init__(self): def __init__(self):
self.top = Scope('global', 0) self.top = Scope('global', 0, 0)
self.scope = self.top self.scope = self.top
def _parsedotname(self, pre=None): def _parsedotname(self, pre=None):
@@ -223,7 +274,7 @@ class PyFuzzyParser(object):
if colon != ':': if colon != ':':
return None return None
return Function(fname, params, indent) return Function(fname, params, indent, self.line_nr)
def _parseclass(self, indent): def _parseclass(self, indent):
tokentype, cname, ind = self.next() tokentype, cname, ind = self.next()
@@ -237,7 +288,7 @@ class PyFuzzyParser(object):
elif next != ':': elif next != ':':
return None return None
return Class(cname, super, indent) return Class(cname, super, indent, self.line_nr)
def _parseassignment(self): def _parseassignment(self):
assign = '' assign = ''
@@ -276,14 +327,11 @@ class PyFuzzyParser(object):
return "%s" % assign return "%s" % assign
def next(self): def next(self):
type, tok, (lineno, indent), end, self.parserline = self.gen.next() type, tok, position, dummy, self.parserline = self.gen.next()
if lineno == self.curline: (self.line_nr, indent) = position
self.currentscope = self.scope
return (type, tok, indent) return (type, tok, indent)
#p.parse(vim.current.buffer[:], vim.eval("line('.')")) def parse(self, text):
def parse(self, text, curline=0):
self.curline = int(curline)
buf = cStringIO.StringIO(''.join(text) + '\n') buf = cStringIO.StringIO(''.join(text) + '\n')
self.gen = tokenize.generate_tokens(buf.readline) self.gen = tokenize.generate_tokens(buf.readline)
self.currentscope = self.scope self.currentscope = self.scope
@@ -292,7 +340,8 @@ class PyFuzzyParser(object):
freshscope = True freshscope = True
while True: while True:
tokentype, tok, indent = self.next() tokentype, tok, indent = self.next()
dbg('main: tok=[%s] indent=[%s]' % (tok, indent)) dbg('main: tok=[%s] type=[%s] indent=[%s]'\
% (tok, tokentype, indent))
if tokentype == tokenize.DEDENT: if tokentype == tokenize.DEDENT:
self.scope = self.scope.parent self.scope = self.scope.parent
@@ -315,7 +364,7 @@ class PyFuzzyParser(object):
elif tok == 'import': elif tok == 'import':
imports = self._parseimportlist() imports = self._parseimportlist()
for mod, alias in imports: for mod, alias in imports:
self.scope.add_import(Import(mod, alias)) self.scope.add_import(Import(self.line_nr, mod, alias))
freshscope = False freshscope = False
elif tok == 'from': elif tok == 'from':
mod, tok = self._parsedotname() mod, tok = self._parsedotname()
@@ -324,7 +373,8 @@ class PyFuzzyParser(object):
continue continue
names = self._parseimportlist() names = self._parseimportlist()
for name, alias in names: for name, alias in names:
self.scope.add_import(Import(name, alias, mod)) i = Import(self.line_nr, name, alias, mod)
self.scope.add_import(i)
freshscope = False freshscope = False
elif tokentype == tokenize.STRING: elif tokentype == tokenize.STRING:
if freshscope: if freshscope:
@@ -342,7 +392,7 @@ class PyFuzzyParser(object):
#except: #except:
# dbg("parse error: %s, %s @ %s" % # dbg("parse error: %s, %s @ %s" %
# (sys.exc_info()[0], sys.exc_info()[1], self.parserline)) # (sys.exc_info()[0], sys.exc_info()[1], self.parserline))
return self.top # self._adjustvisibility() return self.top
def _sanitize(str): def _sanitize(str):

11
test.py
View File

@@ -10,7 +10,7 @@ from token import OP as OP_TEST, INDENT as INDENT_TEST
aaa = 6; bbb = 13 aaa = 6; bbb = 13
ccc = bbb; d = open("test.py"); ccc = bbb; d = open("test.py");
class Intro: class Intro(object):
def testing(self, string): def testing(self, string):
return string+"," return string+","
@@ -20,23 +20,24 @@ class Supi(A, datetime.datetime):
static_var = 0 static_var = 0
def __init__(): def __init__():
import time
pass pass
def test(self): def test(self):
import time
print 1 print 1
return A() return A()
class A(): class A():
class B(): #class B():
def test(self): def test(self):
return A() return A()
class C:
a = A() a = A()
b = a.test() b = a.test()
def blub: class Empty():
pass
def blub():
cdef = 5 cdef = 5
def func(): def func():