Towards single compilation

This commit is contained in:
rocky 2016-04-17 22:37:05 -04:00
parent 2c7fcf9e62
commit 7e0526d627
5 changed files with 49 additions and 7 deletions

View File

@ -0,0 +1,22 @@
import pytest
from uncompyle6 import PYTHON_VERSION, PYTHON3, deparse_code
@pytest.mark.skip(reason="Reinstate when we have compiilation single")
def test_single_mode():
single_expressions = (
'i = 1',
'i and (j or k)',
'i += 1',
'i = j % 4',
'i = {}',
'i = []',
'while i < 1 or stop:\n i\n',
'while i < 1 or stop:\n print%s\n' % ('(i)' if PYTHON3 else ' i'),
'for i in range(10):\n i\n',
'for i in range(10):\n for j in range(10):\n i + j\n',
'try:\n i\nexcept Exception:\n j\nelse:\n k\n'
)
for expr in single_expressions:
code = compile(expr + '\n', '<string>', 'single')
assert deparse_code(PYTHON_VERSION, code, compile_mode='single').text == expr + '\n'

View File

@ -76,10 +76,13 @@ def parse(p, tokens, customize):
return ast
def get_python_parser(version, debug_parser):
def get_python_parser(version, debug_parser, compile_mode='exec'):
"""
Returns parser object for Python version 2 or 3
depending on the parameter passed.
depending on the parameter passed. *compile_mode*
is either 'exec', 'eval', or 'single'. See
https://docs.python.org/3.6/library/functions.html#compile for an explanation
of the different modes.
"""
if version < 3.0:
import uncompyle6.parsers.parse2 as parse2

View File

@ -209,10 +209,14 @@ class Python2Parser(PythonParser):
load_attrs ::= load_attrs LOAD_ATTR
'''
def p_grammar(self, args):
def p_start(self, args):
'''
stmts ::= stmts sstmt
stmts ::= sstmt
'''
def p_grammar(self, args):
'''
sstmt ::= stmt
sstmt ::= ifelsestmtr
sstmt ::= return_stmt RETURN_LAST
@ -710,3 +714,8 @@ class Python2Parser(PythonParser):
else:
raise Exception('unknown customize token %s' % k)
self.addRule(rule, nop_func)
class Python2ParserSingle(Python2Parser):
# Add:
# call_stmt ::= expr PRINT_EXPR
pass

View File

@ -818,3 +818,8 @@ class Python3Parser(PythonParser):
% ('expr ' * token.attr, opname))
self.add_unique_rule(rule, opname, token.attr, customize)
return
class Python3ParserSingle(Python3Parser):
# Add:
# call_stmt ::= expr PRINT_EXPR
pass

View File

@ -487,7 +487,8 @@ class SourceWalker(GenericASTTraversal, object):
stacked_params = ('f', 'indent', 'isLambda', '_globals')
def __init__(self, version, out, scanner, showast=False,
debug_parser=PARSER_DEFAULT_DEBUG):
debug_parser=PARSER_DEFAULT_DEBUG,
compile_mode='exec'):
GenericASTTraversal.__init__(self, ast=None)
self.scanner = scanner
params = {
@ -495,7 +496,8 @@ class SourceWalker(GenericASTTraversal, object):
'indent': '',
}
self.version = version
self.p = get_python_parser(version, debug_parser=debug_parser)
self.p = get_python_parser(version, debug_parser=debug_parser,
compile_mode=compile_mode)
self.debug_parser = dict(debug_parser)
self.showast = showast
self.params = params
@ -1615,7 +1617,7 @@ class SourceWalker(GenericASTTraversal, object):
return ast
def deparse_code(version, co, out=sys.stdout, showasm=False, showast=False,
showgrammar=False, code_objects={}):
showgrammar=False, code_objects={}, compile_mode='exec'):
"""
disassembles and deparses a given code block 'co'
"""
@ -1633,7 +1635,8 @@ def deparse_code(version, co, out=sys.stdout, showasm=False, showast=False,
debug_parser['reduce'] = showgrammar
# Build AST from disassembly.
deparsed = SourceWalker(version, out, scanner, showast=showast, debug_parser=debug_parser)
deparsed = SourceWalker(version, out, scanner, showast=showast,
debug_parser=debug_parser, compile_mode=compile_mode)
deparsed.ast = deparsed.build_ast(tokens, customize)