Merge branch 'master' into ast-format

This commit is contained in:
rocky
2016-06-19 00:49:57 -04:00
13 changed files with 259 additions and 128 deletions

View File

@@ -20,6 +20,14 @@ def for_range_stmt():
for i in range(2): for i in range(2):
i+1 i+1
# # FIXME: add this test - but for Python 2.7+ only
# def set_comp():
# {y for y in range(3)}
# FIXME: add this test
def list_comp():
[y for y in range(3)]
def get_parsed_for_fn(fn): def get_parsed_for_fn(fn):
code = fn.__code__ if PYTHON3 else fn.func_code code = fn.__code__ if PYTHON3 else fn.func_code
return deparse(PYTHON_VERSION, code) return deparse(PYTHON_VERSION, code)

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,5 @@
# Bug in python 3.x handling set comprehensions
{y for y in range(3)}
# Bug in python 3.4 (base64.py) in handling dict comprehension
b = {v: k for k, v in enumerate(b3)}

View File

@@ -0,0 +1,6 @@
# Bug in python 3.4 abc.py
# Set comprehension
abstracts = {name
for name, value in namespace.items()
if getattr(value, "__isabstractmethod__", False)}

View File

@@ -34,6 +34,7 @@ def uncompyle(version, co, out=None, showasm=False, showast=False,
except pysource.SourceWalkerError as e: except pysource.SourceWalkerError as e:
# deparsing failed # deparsing failed
print("\n") print("\n")
print(co.co_filename)
if real_out != out: if real_out != out:
print("\n", file=real_out) print("\n", file=real_out)
print(e, file=real_out) print(e, file=real_out)

View File

@@ -39,8 +39,15 @@ class PythonParser(GenericASTBuilder):
for i in dir(self): for i in dir(self):
setattr(self, i, None) setattr(self, i, None)
def error(self, token): def error(self, tokens, index):
raise ParserError(token, token.offset) start = index - 2 if index - 2 > 0 else 0
finish = index +2 if index + 2 < len(tokens) else len(tokens)
err_token = tokens[index]
print("Token context:")
for i in range(start, finish):
indent = ' ' if i != index else '-> '
print("%s%s" % (indent, tokens[i]))
raise ParserError(err_token, err_token.offset)
def typestring(self, token): def typestring(self, token):
return token.type return token.type
@@ -114,12 +121,9 @@ class PythonParser(GenericASTBuilder):
def p_dictcomp(self, args): def p_dictcomp(self, args):
''' '''
expr ::= dictcomp expr ::= dictcomp
dictcomp ::= LOAD_DICTCOMP MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1
stmt ::= dictcomp_func stmt ::= dictcomp_func
dictcomp_func ::= BUILD_MAP_0 LOAD_FAST FOR_ITER designator
dictcomp_func ::= BUILD_MAP LOAD_FAST FOR_ITER designator
comp_iter JUMP_BACK RETURN_VALUE RETURN_LAST comp_iter JUMP_BACK RETURN_VALUE RETURN_LAST
''' '''
def p_augmented_assign(self, args): def p_augmented_assign(self, args):
@@ -307,7 +311,6 @@ class PythonParser(GenericASTBuilder):
expr ::= LOAD_DEREF expr ::= LOAD_DEREF
expr ::= load_attr expr ::= load_attr
expr ::= binary_expr expr ::= binary_expr
expr ::= binary_expr_na
expr ::= build_list expr ::= build_list
expr ::= cmp expr ::= cmp
expr ::= mapexpr expr ::= mapexpr
@@ -441,12 +444,38 @@ class PythonParser(GenericASTBuilder):
# Positional arguments in make_function # Positional arguments in make_function
pos_arg ::= expr pos_arg ::= expr
nullexprlist ::=
expr32 ::= expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr32 ::= expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr expr
expr1024 ::= expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr1024 ::= expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32 expr32
''' '''
def p_designator(self, args):
'''
# Note. The below is right-recursive:
designList ::= designator designator
designList ::= designator DUP_TOP designList
## Can we replace with left-recursive, and redo with:
##
## designList ::= designLists designator designator
## designLists ::= designLists designator DUP_TOP
## designLists ::=
## Will need to redo semantic actiion
designator ::= STORE_FAST
designator ::= STORE_NAME
designator ::= STORE_GLOBAL
designator ::= STORE_DEREF
designator ::= expr STORE_ATTR
designator ::= expr STORE_SLICE+0
designator ::= expr expr STORE_SLICE+1
designator ::= expr expr STORE_SLICE+2
designator ::= expr expr expr STORE_SLICE+3
designator ::= store_subscr
store_subscr ::= expr expr STORE_SUBSCR
designator ::= unpack
designator ::= unpack_list
'''
def parse(p, tokens, customize): def parse(p, tokens, customize):
p.add_custom_rules(tokens, customize) p.add_custom_rules(tokens, customize)

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015 Rocky Bernstein # Copyright (c) 2015-2016 Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com> # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock # Copyright (c) 1999 John Aycock
""" """
@@ -64,8 +64,6 @@ class Python2Parser(PythonParser):
sstmt ::= ifelsestmtr sstmt ::= ifelsestmtr
sstmt ::= return_stmt RETURN_LAST sstmt ::= return_stmt RETURN_LAST
stmts_opt ::= stmts
stmts_opt ::= passstmt
passstmt ::= passstmt ::=
_stmts ::= _stmts stmt _stmts ::= _stmts stmt
@@ -113,23 +111,6 @@ class Python2Parser(PythonParser):
else_suitec ::= c_stmts else_suitec ::= c_stmts
else_suitec ::= return_stmts else_suitec ::= return_stmts
designList ::= designator designator
designList ::= designator DUP_TOP designList
designator ::= STORE_FAST
designator ::= STORE_NAME
designator ::= STORE_GLOBAL
designator ::= STORE_DEREF
designator ::= expr STORE_ATTR
designator ::= expr STORE_SLICE+0
designator ::= expr expr STORE_SLICE+1
designator ::= expr expr STORE_SLICE+2
designator ::= expr expr expr STORE_SLICE+3
designator ::= store_subscr
store_subscr ::= expr expr STORE_SUBSCR
designator ::= unpack
designator ::= unpack_list
stmt ::= classdef stmt ::= classdef
stmt ::= call_stmt stmt ::= call_stmt
@@ -172,7 +153,6 @@ class Python2Parser(PythonParser):
stmt ::= ifelsestmt stmt ::= ifelsestmt
stmt ::= whilestmt stmt ::= whilestmt
stmt ::= whilenotstmt
stmt ::= while1stmt stmt ::= while1stmt
stmt ::= whileelsestmt stmt ::= whileelsestmt
stmt ::= while1elsestmt stmt ::= while1elsestmt
@@ -307,6 +287,11 @@ class Python2Parser(PythonParser):
''' '''
def p_dictcomp2(self, args):
""""
dictcomp ::= LOAD_DICTCOMP MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1
"""
def p_genexpr2(self, args): def p_genexpr2(self, args):
''' '''
genexpr ::= LOAD_GENEXPR MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1 genexpr ::= LOAD_GENEXPR MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1
@@ -395,3 +380,8 @@ class Python2Parser(PythonParser):
class Python2ParserSingle(Python2Parser, PythonParserSingle): class Python2ParserSingle(Python2Parser, PythonParserSingle):
pass pass
if __name__ == '__main__':
# Check grammar
p = Python2Parser()
p.checkGrammar()

View File

@@ -6,29 +6,16 @@
import string import string
from spark_parser import GenericASTBuilder, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG from spark_parser import GenericASTBuilder, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
from uncompyle6.parsers.astnode import AST from uncompyle6.parsers.astnode import AST
from uncompyle6.parser import PythonParserSingle, ParserError, nop_func from uncompyle6.parser import PythonParser, PythonParserSingle, nop_func
class Python23Parser(PythonParser):
class Python23Parser(GenericASTBuilder):
def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG): def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
GenericASTBuilder.__init__(self, AST, 'stmts', debug=debug_parser) super(Python23Parser, self).__init__(AST, 'stmts', debug=debug_parser)
self.customized = {} self.customized = {}
def cleanup(self): # FIXME: A lot of the functions below overwrite what is in parse.py which
""" # have more rules. Probly that should be stripped down more instead.
Remove recursive references to allow garbage
collector to collect this object.
"""
for dict in (self.rule2func, self.rules, self.rule2name, self.first):
for i in dict.keys():
dict[i] = None
for i in dir(self):
setattr(self, i, None)
def error(self, token):
raise ParserError(token, token.offset)
def typestring(self, token):
return token.type
def p_funcdef(self, args): def p_funcdef(self, args):
''' '''
@@ -171,23 +158,6 @@ class Python23Parser(GenericASTBuilder):
stmts_opt ::= passstmt stmts_opt ::= passstmt
passstmt ::= passstmt ::=
designList ::= designator designator
designList ::= designator DUP_TOP designList
designator ::= STORE_FAST
designator ::= STORE_NAME
designator ::= STORE_GLOBAL
designator ::= STORE_DEREF
designator ::= expr STORE_ATTR
designator ::= expr STORE_SLICE+0
designator ::= expr expr STORE_SLICE+1
designator ::= expr expr STORE_SLICE+2
designator ::= expr expr expr STORE_SLICE+3
designator ::= store_subscr
store_subscr ::= expr expr STORE_SUBSCR
designator ::= unpack
designator ::= unpack_list
stmt ::= classdef stmt ::= classdef
stmt ::= call_stmt stmt ::= call_stmt
call_stmt ::= expr POP_TOP call_stmt ::= expr POP_TOP
@@ -208,7 +178,7 @@ class Python23Parser(GenericASTBuilder):
stmt ::= raise_stmt stmt ::= raise_stmt
raise_stmt ::= exprlist RAISE_VARARGS raise_stmt ::= exprlist RAISE_VARARGS
raise_stmt ::= nullexprlist RAISE_VARARGS raise_stmt ::= RAISE_VARARGS
stmt ::= exec_stmt stmt ::= exec_stmt
exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
@@ -285,12 +255,12 @@ class Python23Parser(GenericASTBuilder):
try_end ::= except_else try_end ::= except_else
except_else ::= END_FINALLY TRY_ELSE_START stmts TRY_ELSE_END except_else ::= END_FINALLY TRY_ELSE_START stmts TRY_ELSE_END
except_stmt ::= except_cond except_stmt except_stmt ::= except_stmt except_cond
except_stmt ::= except_conds try_end except_stmt ::= except_conds try_end
except_stmt ::= except try_end except_stmt ::= except try_end
except_stmt ::= try_end except_stmt ::= try_end
except_conds ::= except_cond except_conds except_conds ::= except_conds except_cond
except_conds ::= except_conds ::=
except_cond ::= except_cond1 except_cond ::= except_cond1
@@ -440,8 +410,6 @@ class Python23Parser(GenericASTBuilder):
exprlist ::= exprlist expr exprlist ::= exprlist expr
exprlist ::= expr exprlist ::= expr
nullexprlist ::=
''' '''
def nonterminal(self, nt, args): def nonterminal(self, nt, args):
@@ -532,5 +500,10 @@ class Python23Parser(GenericASTBuilder):
class Python23ParserSingle(Python23Parser, PythonParserSingle): class Python23ParserSingle(Python23Parser, PythonParserSingle):
pass pass
if __name__ == '__main__':
# Check grammar
p = Python23Parser()
p.checkGrammar()
# local variables: # local variables:
# tab-width: 4 # tab-width: 4

View File

@@ -21,3 +21,8 @@ class Python26Parser(Python2Parser):
class Python26ParserSingle(Python2Parser, PythonParserSingle): class Python26ParserSingle(Python2Parser, PythonParserSingle):
pass pass
if __name__ == '__main__':
# Check grammar
p = Python26Parser()
p.checkGrammar()

View File

@@ -1,7 +1,7 @@
# Copyright (c) 1999 John Aycock
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2015, 2016 Rocky Bernstein # Copyright (c) 2015, 2016 Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock
# #
# See LICENSE for license # See LICENSE for license
""" """
@@ -56,6 +56,11 @@ class Python3Parser(PythonParser):
# See also common Python p_list_comprehension # See also common Python p_list_comprehension
""" """
def p_dictcomp3(self, args):
""""
dictcomp ::= LOAD_DICTCOMP LOAD_CONST MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1
"""
def p_grammar(self, args): def p_grammar(self, args):
''' '''
sstmt ::= stmt sstmt ::= stmt
@@ -111,23 +116,6 @@ class Python3Parser(PythonParser):
else_suitec ::= c_stmts else_suitec ::= c_stmts
else_suitec ::= return_stmts else_suitec ::= return_stmts
designList ::= designator designator
designList ::= designator DUP_TOP designList
designator ::= STORE_FAST
designator ::= STORE_NAME
designator ::= STORE_GLOBAL
designator ::= STORE_DEREF
designator ::= expr STORE_ATTR
designator ::= expr STORE_SLICE+0
designator ::= expr expr STORE_SLICE+1
designator ::= expr expr STORE_SLICE+2
designator ::= expr expr expr STORE_SLICE+3
designator ::= store_subscr
store_subscr ::= expr expr STORE_SUBSCR
designator ::= unpack
designator ::= unpack_list
stmt ::= classdef stmt ::= classdef
stmt ::= call_stmt stmt ::= call_stmt
@@ -170,7 +158,6 @@ class Python3Parser(PythonParser):
stmt ::= ifelsestmt stmt ::= ifelsestmt
stmt ::= whilestmt stmt ::= whilestmt
stmt ::= whilenotstmt
stmt ::= while1stmt stmt ::= while1stmt
stmt ::= whileelsestmt stmt ::= whileelsestmt
stmt ::= while1elsestmt stmt ::= while1elsestmt
@@ -446,6 +433,10 @@ class Python3Parser(PythonParser):
GET_ITER CALL_FUNCTION_1 GET_ITER CALL_FUNCTION_1
listcomp ::= {expr}^n LOAD_LISTCOMP MAKE_CLOSURE listcomp ::= {expr}^n LOAD_LISTCOMP MAKE_CLOSURE
GET_ITER CALL_FUNCTION_1 GET_ITER CALL_FUNCTION_1
dictcomp ::= LOAD_DICTCOMP LOAD_CONST MAKE_FUNCTION_0 expr
GET_ITER CALL_FUNCTION_1
Python < 3.4 Python < 3.4
listcomp ::= LOAD_LISTCOMP MAKE_FUNCTION_0 expr listcomp ::= LOAD_LISTCOMP MAKE_FUNCTION_0 expr
GET_ITER CALL_FUNCTION_1 GET_ITER CALL_FUNCTION_1
@@ -457,6 +448,7 @@ class Python3Parser(PythonParser):
dictcomp ::= LOAD_DICTCOMP MAKE_FUNCTION_0 expr dictcomp ::= LOAD_DICTCOMP MAKE_FUNCTION_0 expr
GET_ITER CALL_FUNCTION_1 GET_ITER CALL_FUNCTION_1
# build_class (see load_build_class) # build_class (see load_build_class)
build_list ::= {expr}^n BUILD_LIST_n build_list ::= {expr}^n BUILD_LIST_n
@@ -494,6 +486,22 @@ class Python3Parser(PythonParser):
rule = ("listcomp ::= LOAD_LISTCOMP MAKE_FUNCTION_0 expr " rule = ("listcomp ::= LOAD_LISTCOMP MAKE_FUNCTION_0 expr "
"GET_ITER CALL_FUNCTION_1") "GET_ITER CALL_FUNCTION_1")
self.add_unique_rule(rule, opname, token.attr, customize) self.add_unique_rule(rule, opname, token.attr, customize)
elif opname == 'LOAD_DICTCOMP':
if self.version >= 3.4:
rule = ("dictcomp ::= LOAD_DICTCOMP LOAD_CONST MAKE_FUNCTION_0 expr "
"GET_ITER CALL_FUNCTION_1")
else:
rule = ("dictcomp ::= LOAD_DICTCOMP MAKE_FUNCTION_0 expr "
"GET_ITER CALL_FUNCTION_1")
self.add_unique_rule(rule, opname, token.attr, customize)
elif opname == 'LOAD_SETCOMP':
if self.version >= 3.4:
rule = ("setcomp ::= LOAD_SETCOMP LOAD_CONST MAKE_FUNCTION_0 expr "
"GET_ITER CALL_FUNCTION_1")
else:
rule = ("setcomp ::= LOAD_SETCOMP MAKE_FUNCTION_0 expr "
"GET_ITER CALL_FUNCTION_1")
self.add_unique_rule(rule, opname, token.attr, customize)
elif opname == 'LOAD_BUILD_CLASS': elif opname == 'LOAD_BUILD_CLASS':
self.custom_build_class_rule(opname, i, token, tokens, customize) self.custom_build_class_rule(opname, i, token, tokens, customize)
elif opname_base in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET'): elif opname_base in ('BUILD_LIST', 'BUILD_TUPLE', 'BUILD_SET'):
@@ -638,3 +646,8 @@ class Python34ParserSingle(Python34Parser, PythonParserSingle):
class Python35onParserSingle(Python35onParser, PythonParserSingle): class Python35onParserSingle(Python35onParser, PythonParserSingle):
pass pass
if __name__ == '__main__':
# Check grammar
p = Python3Parser()
p.checkGrammar()

View File

@@ -101,6 +101,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.classes = [] self.classes = []
self.pending_newlines = 0 self.pending_newlines = 0
self.hide_internal = False self.hide_internal = False
self.name = None
self.offsets = {} self.offsets = {}
self.last_finish = -1 self.last_finish = -1
@@ -285,11 +286,17 @@ class FragmentsWalker(pysource.SourceWalker, object):
node[0].parent = node node[0].parent = node
self.last_finish = len(self.f.getvalue()) self.last_finish = len(self.f.getvalue())
self.preorder(node[0]) self.preorder(node[0])
finish = len(self.f.getvalue())
if hasattr(node[0], 'offset'):
self.set_pos_info(node[0], self.last_finish, )
self.write(')') self.write(')')
self.last_finish = len(self.f.getvalue()) self.last_finish = finish + 1
else: else:
node[0].parent = node node[0].parent = node
start = len(self.f.getvalue())
self.preorder(node[0]) self.preorder(node[0])
if hasattr(node[0], 'offset'):
self.set_pos_info(node[0], start, len(self.f.getvalue()))
self.prec = p self.prec = p
self.set_pos_info(node, start, len(self.f.getvalue())) self.set_pos_info(node, start, len(self.f.getvalue()))
self.prune() self.prune()
@@ -461,7 +468,6 @@ class FragmentsWalker(pysource.SourceWalker, object):
def n_mkfunc(self, node): def n_mkfunc(self, node):
start = len(self.f.getvalue()) start = len(self.f.getvalue())
old_name = self.name
if self.version >= 3.0: if self.version >= 3.0:
# LOAD_CONST code object .. # LOAD_CONST code object ..
# LOAD_CONST 'x0' if >= 3.3 # LOAD_CONST 'x0' if >= 3.3
@@ -484,7 +490,6 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.write(func_name) self.write(func_name)
self.indentMore() self.indentMore()
self.make_function(node, isLambda=False, code_index=code_index) self.make_function(node, isLambda=False, code_index=code_index)
self.name = old_name
self.set_pos_info(node, start, len(self.f.getvalue())) self.set_pos_info(node, start, len(self.f.getvalue()))
if len(self.param_stack) > 1: if len(self.param_stack) > 1:
self.write('\n\n') self.write('\n\n')
@@ -493,6 +498,29 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.indentLess() self.indentLess()
self.prune() # stop recursing self.prune() # stop recursing
def n_list_compr(self, node):
"""List comprehensions the way they are done in Python 2."""
p = self.prec
self.prec = 27
n = node[-1]
assert n == 'list_iter'
# find innermost node
while n == 'list_iter':
n = n[0] # recurse one step
if n == 'list_for': n = n[3]
elif n == 'list_if': n = n[2]
elif n == 'list_if_not': n= n[2]
assert n == 'lc_body'
if node[0].type.startswith('BUILD_LIST'):
start = len(self.f.getvalue())
self.set_pos_info(node[0], start, start+1)
self.write( '[ ')
self.preorder(n[0]) # lc_body
self.preorder(node[-1]) # for/if parts
self.write( ' ]')
self.prec = p
self.prune() # stop recursing
def comprehension_walk(self, node, iter_index, code_index=-5): def comprehension_walk(self, node, iter_index, code_index=-5):
p = self.prec p = self.prec
self.prec = 27 self.prec = 27
@@ -557,27 +585,43 @@ class FragmentsWalker(pysource.SourceWalker, object):
ast = self.build_ast(code._tokens, code._customize) ast = self.build_ast(code._tokens, code._customize)
self.customize(code._customize) self.customize(code._customize)
ast = ast[0][0][0][0][0] # skip over stmts sstmt smt
ast = ast[0][0][0]
n = ast[iter_index] if ast in ['setcomp_func', 'dictcomp_func']:
assert n == 'list_iter' for k in ast:
if k == 'comp_iter':
n = k
elif k == 'designator':
designator = k
pass
pass
pass
else:
ast = ast[0][0]
n = ast[iter_index]
assert n == 'list_iter'
## FIXME: I'm not totally sure this is right.
# find innermost node # find innermost node
while n == 'list_iter': if_node = None
while n in ('list_iter', 'comp_iter'):
n = n[0] # recurse one step n = n[0] # recurse one step
if n == 'list_for': if n == 'list_for':
designator = n[2] if n[2] == 'designator':
designator = n[2]
n = n[3] n = n[3]
elif n == 'list_if': elif n in ['list_if', 'list_if_not', 'comp_if']:
# FIXME: just a guess if_node = n[0]
designator = n[1] if n[1] == 'designator':
designator = n[1]
n = n[2]
pass
pass
n = n[2] assert n.type in ('lc_body', 'comp_body'), ast
elif n == 'list_if_not': assert designator, "Couldn't find designator in list/set comprehension"
# FIXME: just a guess
designator = n[1]
n = n[2]
assert n == 'lc_body', ast
self.preorder(n[0]) self.preorder(n[0])
self.write(' for ') self.write(' for ')
@@ -589,7 +633,9 @@ class FragmentsWalker(pysource.SourceWalker, object):
node[-3].parent = node node[-3].parent = node
self.preorder(node[-3]) self.preorder(node[-3])
self.set_pos_info(node[-3], start, len(self.f.getvalue())) self.set_pos_info(node[-3], start, len(self.f.getvalue()))
# self.preorder(ast[iter_index]) if if_node:
self.write(' if ')
self.preorder(if_node)
self.prec = p self.prec = p
def listcomprehension_walk2(self, node): def listcomprehension_walk2(self, node):
@@ -657,11 +703,28 @@ class FragmentsWalker(pysource.SourceWalker, object):
def n_setcomp(self, node): def n_setcomp(self, node):
start = len(self.f.getvalue()) start = len(self.f.getvalue())
self.write('{') self.write('{')
self.comprehension_walk(node, 4) if node[0] in ['LOAD_SETCOMP', 'LOAD_DICTCOMP']:
start = len(self.f.getvalue())
self.set_pos_info(node[0], start-1, start)
self.listcomprehension_walk3(node, 1, 0)
else:
self.comprehension_walk(node, iter_index=4)
self.write('}') self.write('}')
self.set_pos_info(node, start, len(self.f.getvalue())) self.set_pos_info(node, start, len(self.f.getvalue()))
self.prune() self.prune()
def n_listcomp(self, node):
self.write('[')
if node[0].type == 'load_closure':
self.listcomprehension_walk2(node)
else:
if node[0] == 'LOAD_LISTCOMP':
start = len(self.f.getvalue())
self.set_pos_info(node[0], start-1, start)
self.listcomprehension_walk3(node, 1, 0)
self.write(']')
self.prune()
def n_classdef(self, node): def n_classdef(self, node):
# class definition ('class X(A,B,C):') # class definition ('class X(A,B,C):')
cclass = self.currentclass cclass = self.currentclass
@@ -669,6 +732,10 @@ class FragmentsWalker(pysource.SourceWalker, object):
if self.version > 3.0: if self.version > 3.0:
currentclass = node[1][0].pattr currentclass = node[1][0].pattr
buildclass = node[0] buildclass = node[0]
if buildclass[0] == 'LOAD_BUILD_CLASS':
start = len(self.f.getvalue())
self.set_pos_info(buildclass[0], start, start + len('class')+2)
if buildclass[1][0] == 'kwargs': if buildclass[1][0] == 'kwargs':
subclass = buildclass[1][1].attr subclass = buildclass[1][1].attr
subclass_info = node[0] subclass_info = node[0]
@@ -736,6 +803,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
rn = self.return_none rn = self.return_none
self.return_none = returnNone self.return_none = returnNone
old_name = self.name
self.name = name self.name = name
# if code would be empty, append 'pass' # if code would be empty, append 'pass'
if len(ast) == 0: if len(ast) == 0:
@@ -743,6 +811,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
else: else:
self.customize(customize) self.customize(customize)
self.text = self.traverse(ast, isLambda=isLambda) self.text = self.traverse(ast, isLambda=isLambda)
self.name = old_name
self.return_none = rn self.return_none = rn
def build_ast(self, tokens, customize, isLambda=False, noneInNames=False): def build_ast(self, tokens, customize, isLambda=False, noneInNames=False):
@@ -1549,6 +1618,9 @@ if __name__ == '__main__':
def get_code_for_fn(fn): def get_code_for_fn(fn):
return fn.__code__ return fn.__code__
def test():
[x for x in range(3)]
def gcd(a, b): def gcd(a, b):
if a > b: if a > b:
(a, b) = (b, a) (a, b) = (b, a)
@@ -1561,7 +1633,7 @@ if __name__ == '__main__':
return gcd(b-a, a) return gcd(b-a, a)
# check_args(['3', '5']) # check_args(['3', '5'])
deparse_test(get_code_for_fn(gcd))
# deparse_test(get_code_for_fn(gcd)) # deparse_test(get_code_for_fn(gcd))
deparse_test(get_code_for_fn(test))
# deparse_test(get_code_for_fn(FragmentsWalker.fixup_offsets)) # deparse_test(get_code_for_fn(FragmentsWalker.fixup_offsets))
# deparse_test(inspect.currentframe().f_code) # deparse_test(inspect.currentframe().f_code)

View File

@@ -536,6 +536,7 @@ class SourceWalker(GenericASTTraversal, object):
self.classes = [] self.classes = []
self.pending_newlines = 0 self.pending_newlines = 0
self.hide_internal = True self.hide_internal = True
self.name = None
self.version = version self.version = version
if 2.0 <= version <= 2.3: if 2.0 <= version <= 2.3:
@@ -1052,7 +1053,10 @@ class SourceWalker(GenericASTTraversal, object):
def n_setcomp(self, node): def n_setcomp(self, node):
self.write('{') self.write('{')
self.comprehension_walk(node, iter_index=4) if node[0] in ['LOAD_SETCOMP', 'LOAD_DICTCOMP']:
self.listcomprehension_walk3(node, 1, 0)
else:
self.comprehension_walk(node, iter_index=4)
self.write('}') self.write('}')
self.prune() self.prune()
@@ -1071,40 +1075,52 @@ class SourceWalker(GenericASTTraversal, object):
ast = self.build_ast(code._tokens, code._customize) ast = self.build_ast(code._tokens, code._customize)
self.customize(code._customize) self.customize(code._customize)
ast = ast[0][0][0][0][0] # skip over stmts sstmt smt
ast = ast[0][0][0]
n = ast[iter_index] designator = None
assert n == 'list_iter' if ast in ['setcomp_func', 'dictcomp_func']:
for k in ast:
if k == 'comp_iter':
n = k
elif k == 'designator':
designator = k
pass
pass
pass
else:
ast = ast[0][0]
n = ast[iter_index]
assert n == 'list_iter'
## FIXME: I'm not totally sure this is right. ## FIXME: I'm not totally sure this is right.
# find innermost node # find innermost node
designator = None if_node = None
list_if_node = None while n in ('list_iter', 'comp_iter'):
while n == 'list_iter':
n = n[0] # recurse one step n = n[0] # recurse one step
if n == 'list_for': if n == 'list_for':
if n[2] == 'designator': if n[2] == 'designator':
designator = n[2] designator = n[2]
n = n[3] n = n[3]
elif n in ['list_if', 'list_if_not']: elif n in ['list_if', 'list_if_not', 'comp_if']:
list_if_node = n[0] if_node = n[0]
if n[1] == 'designator': if n[1] == 'designator':
designator = n[1] designator = n[1]
n = n[2] n = n[2]
pass pass
pass pass
assert n == 'lc_body', ast
assert designator, "Couldn't find designator in list comprehension" assert n.type in ('lc_body', 'comp_body'), ast
assert designator, "Couldn't find designator in list/set comprehension"
self.preorder(n[0]) self.preorder(n[0])
self.write(' for ') self.write(' for ')
self.preorder(designator) self.preorder(designator)
self.write(' in ') self.write(' in ')
self.preorder(node[-3]) self.preorder(node[-3])
if list_if_node: if if_node:
self.write(' if ') self.write(' if ')
self.preorder(list_if_node) self.preorder(if_node)
self.prec = p self.prec = p
def listcomprehension_walk2(self, node): def listcomprehension_walk2(self, node):
@@ -1359,8 +1375,15 @@ class SourceWalker(GenericASTTraversal, object):
self.prec = 100 self.prec = 100
lastnode = node.pop() lastnode = node.pop()
lastnodetype = lastnode.type lastnodetype = lastnode.type
have_star = False
if lastnodetype.startswith('BUILD_LIST'): if lastnodetype.startswith('BUILD_LIST'):
self.write('['); endchar = ']' # 3.5+ has BUILD_LIST_UNPACK
if lastnodetype == 'BUILD_LIST_UNPACK':
# FIXME: need to handle range of BUILD_LIST_UNPACK
have_star = True
endchar = ''
else:
self.write('['); endchar = ']'
elif lastnodetype.startswith('BUILD_TUPLE'): elif lastnodetype.startswith('BUILD_TUPLE'):
self.write('('); endchar = ')' self.write('('); endchar = ')'
elif lastnodetype.startswith('BUILD_SET'): elif lastnodetype.startswith('BUILD_SET'):
@@ -1399,6 +1422,8 @@ class SourceWalker(GenericASTTraversal, object):
value = self.traverse(elem) value = self.traverse(elem)
self.write(sep, value) self.write(sep, value)
sep = line_separator sep = line_separator
if have_star:
sep += '*'
if lastnode.attr == 1 and lastnodetype.startswith('BUILD_TUPLE'): if lastnode.attr == 1 and lastnodetype.startswith('BUILD_TUPLE'):
self.write(',') self.write(',')
self.write(endchar) self.write(endchar)
@@ -1807,7 +1832,9 @@ class SourceWalker(GenericASTTraversal, object):
for g in find_globals(ast, set()): for g in find_globals(ast, set()):
self.println(indent, 'global ', g) self.println(indent, 'global ', g)
old_name = self.name
self.gen_source(ast, code.co_name, code._customize) self.gen_source(ast, code.co_name, code._customize)
self.name = old_name
code._tokens = None; code._customize = None # save memory code._tokens = None; code._customize = None # save memory
self.classes.pop(-1) self.classes.pop(-1)
@@ -1816,6 +1843,7 @@ class SourceWalker(GenericASTTraversal, object):
rn = self.return_none rn = self.return_none
self.return_none = returnNone self.return_none = returnNone
old_name = self.name
self.name = name self.name = name
# if code would be empty, append 'pass' # if code would be empty, append 'pass'
if len(ast) == 0: if len(ast) == 0:
@@ -1827,6 +1855,7 @@ class SourceWalker(GenericASTTraversal, object):
else: else:
self.text = self.traverse(ast, isLambda=isLambda) self.text = self.traverse(ast, isLambda=isLambda)
self.println(self.text) self.println(self.text)
self.name = old_name
self.return_none = rn self.return_none = rn
def build_ast(self, tokens, customize, isLambda=False, def build_ast(self, tokens, customize, isLambda=False,