You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 09:22:40 +08:00
Merge branch 'master' into python-2.4
This commit is contained in:
@@ -18,15 +18,19 @@ def test_grammar():
|
||||
right_recursive, dup_rhs) = p.check_sets()
|
||||
|
||||
# We have custom rules that create the below
|
||||
expect_lhs = set(['pos_arg', 'get_iter', 'attribute'])
|
||||
expect_lhs = set(['pos_arg', 'attribute'])
|
||||
if PYTHON_VERSION < 3.8:
|
||||
expect_lhs.add('get_iter')
|
||||
|
||||
|
||||
unused_rhs = set(['list', 'mkfunc',
|
||||
'mklambda',
|
||||
'unpack',])
|
||||
|
||||
expect_right_recursive = set([('designList',
|
||||
('store', 'DUP_TOP', 'designList'))])
|
||||
|
||||
if PYTHON_VERSION != 3.7:
|
||||
if PYTHON_VERSION < 3.7:
|
||||
unused_rhs.add('call')
|
||||
|
||||
if PYTHON_VERSION > 2.6:
|
||||
@@ -61,7 +65,11 @@ def test_grammar():
|
||||
expect_lhs.add('kwarg')
|
||||
|
||||
assert expect_lhs == set(lhs)
|
||||
assert unused_rhs == set(rhs)
|
||||
|
||||
# FIXME
|
||||
if PYTHON_VERSION != 3.8:
|
||||
assert unused_rhs == set(rhs)
|
||||
|
||||
assert expect_right_recursive == right_recursive
|
||||
|
||||
expect_dup_rhs = frozenset([('COME_FROM',), ('CONTINUE',), ('JUMP_ABSOLUTE',),
|
||||
|
BIN
test/bytecode_3.6/02_async_for.pyc
Normal file
BIN
test/bytecode_3.6/02_async_for.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.7/02_async_for.pyc
Normal file
BIN
test/bytecode_3.7/02_async_for.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8/01_while_if_then.pyc
Normal file
BIN
test/bytecode_3.8/01_while_if_then.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8/02_async_for.pyc
Normal file
BIN
test/bytecode_3.8/02_async_for.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8/02_tryfinally_return.pyc
Normal file
BIN
test/bytecode_3.8/02_tryfinally_return.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8/02_while_and.pyc
Normal file
BIN
test/bytecode_3.8/02_while_and.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8/03_if_try.pyc
Normal file
BIN
test/bytecode_3.8/03_if_try.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8/04_try_finally.pyc
Normal file
BIN
test/bytecode_3.8/04_try_finally.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8/05_return_in_else.pyc
Normal file
BIN
test/bytecode_3.8/05_return_in_else.pyc
Normal file
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# Mode: -*- python -*-
|
||||
#
|
||||
# Copyright (c) 2015-2017 by Rocky Bernstein
|
||||
# Copyright (c) 2015-2017, 2019 by Rocky Bernstein
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
#
|
||||
import sys, os, getopt, time
|
||||
@@ -29,7 +29,8 @@ Options:
|
||||
-> /tmp/bla/fasel.pyc_dis, /tmp/bar/foo.pyc_dis
|
||||
uncompyle6 -o /tmp /usr/lib/python1.5
|
||||
-> /tmp/smtplib.pyc_dis ... /tmp/lib-tk/FixTk.pyc_dis
|
||||
-c <file> attempts a disassembly after compiling <file>
|
||||
--compile | -c <python-file>
|
||||
attempts a decompilation after compiling <python-file>
|
||||
-d print timestamps
|
||||
-p <integer> use <integer> number of processes
|
||||
-r recurse directories looking for .pyc and .pyo files
|
||||
@@ -42,10 +43,10 @@ Options:
|
||||
--help show this message
|
||||
|
||||
Debugging Options:
|
||||
--asm -a include byte-code (disables --verify)
|
||||
--grammar -g show matching grammar
|
||||
--tree -t include syntax tree (disables --verify)
|
||||
--tree++ add template rules to --tree when possible
|
||||
--asm | -a include byte-code (disables --verify)
|
||||
--grammar | -g show matching grammar
|
||||
--tree | -t include syntax tree (disables --verify)
|
||||
--tree++ add template rules to --tree when possible
|
||||
|
||||
Extensions of generated files:
|
||||
'.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify)
|
||||
@@ -60,10 +61,7 @@ from uncompyle6.main import main, status_msg
|
||||
from uncompyle6.version import VERSION
|
||||
|
||||
def usage():
|
||||
print("""usage:
|
||||
%s [--verify | --weak-verify ] [--asm] [--tree[+]] [--grammar] [-o <path>] FILE|DIR...
|
||||
%s [--help | -h | --version | -V]
|
||||
""" % (program, program))
|
||||
print(__doc__)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -77,13 +75,13 @@ def main_bin():
|
||||
numproc = 0
|
||||
outfile = '-'
|
||||
out_base = None
|
||||
codes = []
|
||||
source_paths = []
|
||||
timestamp = False
|
||||
timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
|
||||
|
||||
try:
|
||||
opts, files = getopt.getopt(sys.argv[1:], 'hagtdrVo:c:p:',
|
||||
'help asm grammar linemaps recurse '
|
||||
opts, pyc_paths = getopt.getopt(sys.argv[1:], 'hac:gtdrVo:p:',
|
||||
'help asm compile= grammar linemaps recurse '
|
||||
'timestamp tree tree+ '
|
||||
'fragments verify verify-run version '
|
||||
'weak-verify '
|
||||
@@ -125,8 +123,8 @@ def main_bin():
|
||||
outfile = val
|
||||
elif opt in ('--timestamp', '-d'):
|
||||
timestamp = True
|
||||
elif opt == '-c':
|
||||
codes.append(val)
|
||||
elif opt in ('--compile', '-c'):
|
||||
source_paths.append(val)
|
||||
elif opt == '-p':
|
||||
numproc = int(val)
|
||||
elif opt in ('--recurse', '-r'):
|
||||
@@ -138,25 +136,25 @@ def main_bin():
|
||||
# expand directory if specified
|
||||
if recurse_dirs:
|
||||
expanded_files = []
|
||||
for f in files:
|
||||
for f in pyc_paths:
|
||||
if os.path.isdir(f):
|
||||
for root, _, dir_files in os.walk(f):
|
||||
for df in dir_files:
|
||||
if df.endswith('.pyc') or df.endswith('.pyo'):
|
||||
expanded_files.append(os.path.join(root, df))
|
||||
files = expanded_files
|
||||
pyc_paths = expanded_files
|
||||
|
||||
# argl, commonprefix works on strings, not on path parts,
|
||||
# thus we must handle the case with files in 'some/classes'
|
||||
# and 'some/cmds'
|
||||
src_base = os.path.commonprefix(files)
|
||||
src_base = os.path.commonprefix(pyc_paths)
|
||||
if src_base[-1:] != os.sep:
|
||||
src_base = os.path.dirname(src_base)
|
||||
if src_base:
|
||||
sb_len = len( os.path.join(src_base, '') )
|
||||
files = [f[sb_len:] for f in files]
|
||||
pyc_paths = [f[sb_len:] for f in pyc_paths]
|
||||
|
||||
if not files:
|
||||
if not pyc_paths:
|
||||
sys.stderr.write("No files given\n")
|
||||
usage()
|
||||
|
||||
@@ -164,7 +162,7 @@ def main_bin():
|
||||
outfile = None # use stdout
|
||||
elif outfile and os.path.isdir(outfile):
|
||||
out_base = outfile; outfile = None
|
||||
elif outfile and len(files) > 1:
|
||||
elif outfile and len(pyc_paths) > 1:
|
||||
out_base = outfile; outfile = None
|
||||
|
||||
if timestamp:
|
||||
@@ -172,10 +170,10 @@ def main_bin():
|
||||
|
||||
if numproc <= 1:
|
||||
try:
|
||||
result = main(src_base, out_base, files, None, outfile,
|
||||
result = main(src_base, out_base, pyc_paths, source_paths, outfile,
|
||||
**options)
|
||||
result = list(result) + [options.get('do_verify', None)]
|
||||
if len(files) > 1:
|
||||
if len(pyc_paths) > 1:
|
||||
mess = status_msg(do_verify, *result)
|
||||
print('# ' + mess)
|
||||
pass
|
||||
@@ -191,8 +189,8 @@ def main_bin():
|
||||
except ImportError:
|
||||
from queue import Empty
|
||||
|
||||
fqueue = Queue(len(files)+numproc)
|
||||
for f in files:
|
||||
fqueue = Queue(len(pyc_paths)+numproc)
|
||||
for f in pyc_paths:
|
||||
fqueue.put(f)
|
||||
for i in range(numproc):
|
||||
fqueue.put(None)
|
||||
|
@@ -12,7 +12,6 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime, os, subprocess, sys
|
||||
|
||||
from uncompyle6 import verify, IS_PYPY, PYTHON_VERSION
|
||||
@@ -131,6 +130,22 @@ def decompile(
|
||||
# deparsing failed
|
||||
raise pysource.SourceWalkerError(str(e))
|
||||
|
||||
def compile_file(source_path):
|
||||
if source_path.endswith('.py'):
|
||||
basename = source_path[:-3]
|
||||
else:
|
||||
basename = source_path
|
||||
|
||||
if hasattr(sys, 'pypy_version_info'):
|
||||
bytecode_path = "%s-pypy%s.pyc" % (basename, PYTHON_VERSION)
|
||||
else:
|
||||
bytecode_path = "%s-%s.pyc" % (basename, PYTHON_VERSION)
|
||||
|
||||
print("compiling %s to %s" % (source_path, bytecode_path))
|
||||
py_compile.compile(source_path, bytecode_path, 'exec')
|
||||
return bytecode_path
|
||||
|
||||
|
||||
def decompile_file(filename, outstream=None, showasm=None, showast=False,
|
||||
showgrammar=False, mapstream=None, do_fragments=False):
|
||||
"""
|
||||
@@ -162,7 +177,7 @@ def decompile_file(filename, outstream=None, showasm=None, showast=False,
|
||||
|
||||
|
||||
# FIXME: combine into an options parameter
|
||||
def main(in_base, out_base, files, codes, outfile=None,
|
||||
def main(in_base, out_base, compiled_files, source_files, outfile=None,
|
||||
showasm=None, showast=False, do_verify=False,
|
||||
showgrammar=False, raise_on_error=False,
|
||||
do_linemaps=False, do_fragments=False):
|
||||
@@ -172,8 +187,6 @@ def main(in_base, out_base, files, codes, outfile=None,
|
||||
files list of filenames to be uncompyled (relative to in_base)
|
||||
outfile write output to this filename (overwrites out_base)
|
||||
|
||||
Note: `codes` is not use. Historical compatability?
|
||||
|
||||
For redirecting output to
|
||||
- <filename> outfile=<filename> (out_base is ignored)
|
||||
- files below out_base out_base=...
|
||||
@@ -183,7 +196,10 @@ def main(in_base, out_base, files, codes, outfile=None,
|
||||
current_outfile = outfile
|
||||
linemap_stream = None
|
||||
|
||||
for filename in files:
|
||||
for source_path in source_files:
|
||||
compiled_files.append(compile_file(source_path))
|
||||
|
||||
for filename in compiled_files:
|
||||
infile = os.path.join(in_base, filename)
|
||||
# print("XXX", infile)
|
||||
if not os.path.exists(infile):
|
||||
|
@@ -159,7 +159,7 @@ class Python35Parser(Python34Parser):
|
||||
call_token = tokens[i+1]
|
||||
rule = 'call ::= expr unmapexpr ' + call_token.kind
|
||||
self.addRule(rule, nop_func)
|
||||
elif opname == 'BEFORE_ASYNC_WITH':
|
||||
elif opname == 'BEFORE_ASYNC_WITH' and self.version < 3.8:
|
||||
# Some Python 3.5+ async additions
|
||||
rules_str = """
|
||||
async_with_stmt ::= expr
|
||||
|
@@ -67,6 +67,22 @@ class Python36Parser(Python35Parser):
|
||||
for_block POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
stmt ::= async_for_stmt36
|
||||
|
||||
async_for_stmt36 ::= SETUP_LOOP expr
|
||||
GET_AITER
|
||||
LOAD_CONST YIELD_FROM
|
||||
SETUP_EXCEPT GET_ANEXT LOAD_CONST
|
||||
YIELD_FROM
|
||||
store
|
||||
POP_BLOCK JUMP_BACK COME_FROM_EXCEPT DUP_TOP
|
||||
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
|
||||
END_FINALLY for_block
|
||||
COME_FROM
|
||||
POP_TOP POP_TOP POP_TOP POP_EXCEPT
|
||||
POP_TOP POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
async_forelse_stmt ::= SETUP_LOOP expr
|
||||
GET_AITER
|
||||
LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
|
||||
@@ -362,6 +378,7 @@ class Python36Parser(Python35Parser):
|
||||
pass
|
||||
pass
|
||||
return False
|
||||
|
||||
class Python36ParserSingle(Python36Parser, PythonParserSingle):
|
||||
pass
|
||||
|
||||
|
@@ -30,6 +30,7 @@ class Python37Parser(Python36Parser):
|
||||
"""
|
||||
# Where does the POP_TOP really belong?
|
||||
stmt ::= import37
|
||||
stmt ::= async_for_stmt37
|
||||
import37 ::= import POP_TOP
|
||||
|
||||
async_for_stmt ::= SETUP_LOOP expr
|
||||
@@ -45,6 +46,19 @@ class Python37Parser(Python36Parser):
|
||||
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
# Order of LOAD_CONST YIELD_FROM is switched from 3.6 to save a LOAD_CONST
|
||||
async_for_stmt37 ::= SETUP_LOOP expr
|
||||
GET_AITER
|
||||
SETUP_EXCEPT GET_ANEXT
|
||||
LOAD_CONST YIELD_FROM
|
||||
store
|
||||
POP_BLOCK JUMP_BACK COME_FROM_EXCEPT DUP_TOP
|
||||
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
|
||||
END_FINALLY for_block COME_FROM
|
||||
POP_TOP POP_TOP POP_TOP POP_EXCEPT
|
||||
POP_TOP POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
async_forelse_stmt ::= SETUP_LOOP expr
|
||||
GET_AITER
|
||||
SETUP_EXCEPT GET_ANEXT LOAD_CONST
|
||||
@@ -92,6 +106,18 @@ class Python37Parser(Python36Parser):
|
||||
JUMP_ABSOLUTE END_FINALLY COME_FROM
|
||||
for_block POP_BLOCK
|
||||
else_suite COME_FROM_LOOP
|
||||
stmt ::= async_for_stmt36
|
||||
async_for_stmt36 ::= SETUP_LOOP expr
|
||||
GET_AITER
|
||||
LOAD_CONST YIELD_FROM SETUP_EXCEPT GET_ANEXT LOAD_CONST
|
||||
YIELD_FROM
|
||||
store
|
||||
POP_BLOCK JUMP_BACK COME_FROM_EXCEPT DUP_TOP
|
||||
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
|
||||
END_FINALLY continues COME_FROM
|
||||
POP_TOP POP_TOP POP_TOP POP_EXCEPT
|
||||
POP_TOP POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
""")
|
||||
super(Python37Parser, self).customize_grammar_rules(tokens, customize)
|
||||
|
||||
|
@@ -25,10 +25,52 @@ class Python38Parser(Python37Parser):
|
||||
|
||||
def p_38misc(self, args):
|
||||
"""
|
||||
stmt ::= async_for_stmt38
|
||||
stmt ::= for38
|
||||
stmt ::= forelsestmt38
|
||||
stmt ::= forelselaststmt38
|
||||
stmt ::= forelselaststmtl38
|
||||
stmt ::= tryfinally38
|
||||
stmt ::= try_except_ret38
|
||||
stmt ::= try_except38
|
||||
stmt ::= whilestmt38
|
||||
stmt ::= whileTruestmt38
|
||||
stmt ::= call
|
||||
|
||||
# FIXME this should be restricted to being inside a try block
|
||||
stmt ::= except_ret38
|
||||
|
||||
# FIXME this should be added only when seeing GET_AITER or YIELD_FROM
|
||||
async_for_stmt38 ::= expr
|
||||
GET_AITER
|
||||
SETUP_FINALLY
|
||||
GET_ANEXT
|
||||
LOAD_CONST
|
||||
YIELD_FROM
|
||||
POP_BLOCK
|
||||
store for_block
|
||||
COME_FROM_FINALLY
|
||||
END_ASYNC_FOR
|
||||
|
||||
async_with_stmt ::= expr BEFORE_ASYNC_WITH GET_AWAITABLE LOAD_CONST YIELD_FROM
|
||||
SETUP_ASYNC_WITH POP_TOP
|
||||
suite_stmts
|
||||
POP_TOP POP_BLOCK
|
||||
BEGIN_FINALLY
|
||||
WITH_CLEANUP_START
|
||||
GET_AWAITABLE LOAD_CONST YIELD_FROM
|
||||
WITH_CLEANUP_FINISH END_FINALLY
|
||||
|
||||
async_with_as_stmt ::= expr BEFORE_ASYNC_WITH GET_AWAITABLE LOAD_CONST YIELD_FROM
|
||||
SETUP_ASYNC_WITH store
|
||||
suite_stmts
|
||||
POP_TOP POP_BLOCK
|
||||
BEGIN_FINALLY
|
||||
WITH_CLEANUP_START
|
||||
GET_AWAITABLE LOAD_CONST YIELD_FROM
|
||||
WITH_CLEANUP_FINISH END_FINALLY
|
||||
|
||||
return ::= ret_expr ROT_TWO POP_TOP RETURN_VALUE
|
||||
|
||||
for38 ::= expr get_iter store for_block JUMP_BACK
|
||||
for38 ::= expr for_iter store for_block JUMP_BACK
|
||||
@@ -37,18 +79,55 @@ class Python38Parser(Python37Parser):
|
||||
forelsestmt38 ::= expr for_iter store for_block POP_BLOCK else_suite
|
||||
forelselaststmt38 ::= expr for_iter store for_block POP_BLOCK else_suitec
|
||||
forelselaststmtl38 ::= expr for_iter store for_block POP_BLOCK else_suitel
|
||||
whilestmt ::= testexpr l_stmts_opt COME_FROM JUMP_BACK POP_BLOCK
|
||||
whilestmt ::= testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
whilestmt ::= testexpr returns POP_BLOCK
|
||||
while1elsestmt ::= l_stmts JUMP_BACK
|
||||
whileelsestmt ::= testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
whileTruestmt ::= l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
|
||||
whilestmt38 ::= testexpr l_stmts_opt COME_FROM JUMP_BACK POP_BLOCK
|
||||
whilestmt38 ::= testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
whilestmt38 ::= testexpr returns POP_BLOCK
|
||||
whilestmt38 ::= testexpr l_stmts JUMP_BACK
|
||||
|
||||
# while1elsestmt ::= l_stmts JUMP_BACK
|
||||
whileTruestmt ::= l_stmts JUMP_BACK POP_BLOCK
|
||||
while1stmt ::= l_stmts COME_FROM_LOOP
|
||||
while1stmt ::= l_stmts COME_FROM JUMP_BACK COME_FROM_LOOP
|
||||
while1elsestmt ::= l_stmts JUMP_BACK
|
||||
whileTruestmt ::= l_stmts_opt JUMP_BACK NOP
|
||||
whileTruestmt ::= l_stmts_opt JUMP_BACK POP_BLOCK NOP
|
||||
whileTruestmt38 ::= l_stmts JUMP_BACK
|
||||
|
||||
for_block ::= l_stmts_opt _come_from_loops JUMP_BACK
|
||||
|
||||
except_cond1 ::= DUP_TOP expr COMPARE_OP jmp_false
|
||||
POP_TOP POP_TOP POP_TOP
|
||||
POP_EXCEPT
|
||||
try_except ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
|
||||
except_handler38
|
||||
try_except38 ::= SETUP_FINALLY POP_BLOCK POP_TOP suite_stmts_opt
|
||||
except_handler38a
|
||||
try_except_ret38 ::= SETUP_FINALLY expr POP_BLOCK
|
||||
RETURN_VALUE except_ret38a
|
||||
|
||||
except_ret38 ::= SETUP_FINALLY expr ROT_FOUR POP_BLOCK POP_EXCEPT
|
||||
CALL_FINALLY RETURN_VALUE COME_FROM_FINALLY
|
||||
suite_stmts_opt END_FINALLY
|
||||
except_ret38a ::= COME_FROM_FINALLY POP_TOP POP_TOP POP_TOP
|
||||
expr ROT_FOUR
|
||||
POP_EXCEPT RETURN_VALUE END_FINALLY
|
||||
except_handler38 ::= JUMP_FORWARD COME_FROM_FINALLY
|
||||
except_stmts END_FINALLY opt_come_from_except
|
||||
except_handler38a ::= COME_FROM_FINALLY POP_TOP POP_TOP POP_TOP
|
||||
POP_EXCEPT POP_TOP stmts END_FINALLY
|
||||
|
||||
tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
|
||||
BEGIN_FINALLY COME_FROM_FINALLY suite_stmts_opt
|
||||
END_FINALLY
|
||||
tryfinally38 ::= SETUP_FINALLY POP_BLOCK CALL_FINALLY
|
||||
returns
|
||||
COME_FROM_FINALLY END_FINALLY suite_stmts
|
||||
tryfinally38 ::= SETUP_FINALLY POP_BLOCK CALL_FINALLY
|
||||
returns
|
||||
COME_FROM_FINALLY POP_FINALLY returns
|
||||
END_FINALLY
|
||||
tryfinally_return_stmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
|
||||
BEGIN_FINALLY COME_FROM_FINALLY
|
||||
POP_FINALLY suite_stmts_opt END_FINALLY
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
|
||||
@@ -57,17 +136,76 @@ class Python38Parser(Python37Parser):
|
||||
|
||||
def customize_grammar_rules(self, tokens, customize):
|
||||
self.remove_rules("""
|
||||
stmt ::= async_for_stmt37
|
||||
stmt ::= for
|
||||
stmt ::= forelsestmt
|
||||
stmt ::= try_except36
|
||||
|
||||
async_for_stmt37 ::= SETUP_LOOP expr
|
||||
GET_AITER
|
||||
SETUP_EXCEPT GET_ANEXT
|
||||
LOAD_CONST YIELD_FROM
|
||||
store
|
||||
POP_BLOCK JUMP_BACK COME_FROM_EXCEPT DUP_TOP
|
||||
LOAD_GLOBAL COMPARE_OP POP_JUMP_IF_TRUE
|
||||
END_FINALLY for_block COME_FROM
|
||||
POP_TOP POP_TOP POP_TOP POP_EXCEPT
|
||||
POP_TOP POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
for ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK
|
||||
for ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK NOP
|
||||
|
||||
for_block ::= l_stmts_opt COME_FROM_LOOP JUMP_BACK
|
||||
forelsestmt38 ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suite
|
||||
forelselaststmt38 ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitec
|
||||
forelselaststmtl38 ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitel
|
||||
forelsestmt ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suite
|
||||
forelselaststmt ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitec
|
||||
forelselaststmtl ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitel
|
||||
try_except ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
|
||||
except_handler opt_come_from_except
|
||||
tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
|
||||
LOAD_CONST COME_FROM_FINALLY suite_stmts_opt
|
||||
END_FINALLY
|
||||
tryfinally36 ::= SETUP_FINALLY returns
|
||||
COME_FROM_FINALLY suite_stmts_opt END_FINALLY
|
||||
tryfinally_return_stmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
|
||||
LOAD_CONST COME_FROM_FINALLY
|
||||
|
||||
|
||||
""")
|
||||
super(Python37Parser, self).customize_grammar_rules(tokens, customize)
|
||||
self.check_reduce['ifstmt'] = 'tokens'
|
||||
self.check_reduce['whileTruestmt38'] = 'tokens'
|
||||
|
||||
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||
invalid = super(Python38Parser,
|
||||
self).reduce_is_invalid(rule, ast,
|
||||
tokens, first, last)
|
||||
if invalid:
|
||||
return invalid
|
||||
if rule[0] == 'ifstmt':
|
||||
# Make sure jumps don't extend beyond the end of the if statement.
|
||||
l = last
|
||||
if l == len(tokens):
|
||||
l -= 1
|
||||
if isinstance(tokens[l].offset, str):
|
||||
last_offset = int(tokens[l].offset.split('_')[0], 10)
|
||||
else:
|
||||
last_offset = tokens[l].offset
|
||||
for i in range(first, l):
|
||||
t = tokens[i]
|
||||
if t.kind == 'POP_JUMP_IF_FALSE':
|
||||
if t.attr > last_offset:
|
||||
return True
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
elif rule[0] == 'whileTruestmt38':
|
||||
t = tokens[last-1]
|
||||
if t.kind == 'JUMP_BACK':
|
||||
return t.attr != tokens[first].offset
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
class Python38ParserSingle(Python38Parser, PythonParserSingle):
|
||||
pass
|
||||
|
@@ -880,7 +880,7 @@ class Scanner3(Scanner):
|
||||
if_end = self.get_target(pre_rtarget)
|
||||
|
||||
# If the jump target is back, we are looping
|
||||
if (if_end < pre_rtarget and
|
||||
if (if_end < pre_rtarget and self.version < 3.8 and
|
||||
(code[prev_op[if_end]] == self.opc.SETUP_LOOP)):
|
||||
if (if_end > start):
|
||||
return
|
||||
@@ -924,7 +924,7 @@ class Scanner3(Scanner):
|
||||
# Python 3.5 may remove as dead code a JUMP
|
||||
# instruction after a RETURN_VALUE. So we check
|
||||
# based on seeing SETUP_EXCEPT various places.
|
||||
if code[rtarget] == self.opc.SETUP_EXCEPT:
|
||||
if self.version < 3.8 and code[rtarget] == self.opc.SETUP_EXCEPT:
|
||||
return
|
||||
# Check that next instruction after pops and jump is
|
||||
# not from SETUP_EXCEPT
|
||||
@@ -936,7 +936,7 @@ class Scanner3(Scanner):
|
||||
if next_op in targets:
|
||||
for try_op in targets[next_op]:
|
||||
come_from_op = code[try_op]
|
||||
if come_from_op == self.opc.SETUP_EXCEPT:
|
||||
if self.version < 3.8 and come_from_op == self.opc.SETUP_EXCEPT:
|
||||
return
|
||||
pass
|
||||
pass
|
||||
|
@@ -11,9 +11,10 @@ before reduction and don't reduce when there is a problem.
|
||||
def checker(ast, in_loop, errors):
|
||||
if ast is None:
|
||||
return
|
||||
in_loop = in_loop or ast.kind in ('while1stmt', 'whileTruestmt',
|
||||
in_loop = (in_loop or (ast.kind in ('while1stmt', 'whileTruestmt',
|
||||
'whilestmt', 'whileelsestmt', 'while1elsestmt',
|
||||
'for_block')
|
||||
'for_block'))
|
||||
or ast.kind.startswith('async_for'))
|
||||
if ast.kind in ('aug_assign1', 'aug_assign2') and ast[0][0] == 'and':
|
||||
text = str(ast)
|
||||
error_text = '\n# improper augmented assigment (e.g. +=, *=, ...):\n#\t' + '\n# '.join(text.split("\n")) + '\n'
|
||||
|
@@ -285,14 +285,16 @@ def customize_for_version3(self, version):
|
||||
'await_expr': ( 'await %c', 0),
|
||||
'await_stmt': ( '%|%c\n', 0),
|
||||
'async_for_stmt': (
|
||||
'%|async for %c in %c:\n%+%c%-\n\n', 9, 1, 25 ),
|
||||
'%|async for %c in %c:\n%+%|%c%-\n\n', 9, 1, 25 ),
|
||||
'async_forelse_stmt': (
|
||||
'%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
|
||||
9, 1, 25, (27, 'else_suite') ),
|
||||
'async_with_stmt': (
|
||||
'%|async with %c:\n%+%c%-', 0, 7),
|
||||
'%|async with %c:\n%+%|%c%-',
|
||||
(0, 'expr'), 7 ),
|
||||
'async_with_as_stmt': (
|
||||
'%|async with %c as %c:\n%+%c%-', 0, 6, 7),
|
||||
'%|async with %c as %c:\n%+%|%c%-',
|
||||
(0, 'expr'), (6, 'store'), 7),
|
||||
'unmap_dict': ( '{**%C}', (0, -1, ', **') ),
|
||||
# 'unmapexpr': ( '{**%c}', 0), # done by n_unmapexpr
|
||||
|
||||
@@ -516,6 +518,10 @@ def customize_for_version3(self, version):
|
||||
'tryfinally_return_stmt':
|
||||
( '%|try:\n%+%c%-%|finally:\n%+%|return%-\n\n', 1 ),
|
||||
|
||||
'async_for_stmt36': (
|
||||
'%|async for %c in %c:\n%+%c%-%-\n\n',
|
||||
(9, 'store'), (1, 'expr'), (18, 'for_block') ),
|
||||
|
||||
'call_ex' : (
|
||||
'%c(%p)',
|
||||
(0, 'expr'), (1, 100)),
|
||||
@@ -914,13 +920,16 @@ def customize_for_version3(self, version):
|
||||
|
||||
PRECEDENCE['attribute37'] = 2
|
||||
TABLE_DIRECT.update({
|
||||
'attribute37': ( '%c.%[1]{pattr}', 0 ),
|
||||
'async_forelse_stmt': (
|
||||
'%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n',
|
||||
7, 1, 17, (25, 'else_suite') ),
|
||||
(7, 'store'), (1, 'expr'), (17, 'for_block'), (25, 'else_suite') ),
|
||||
'async_for_stmt': (
|
||||
'%|async for %c in %c:\n%+%c%-%-\n\n',
|
||||
7, 1, 17),
|
||||
(7, 'store'), (1, 'expr'), (17, 'for_block')),
|
||||
'async_for_stmt37': (
|
||||
'%|async for %c in %c:\n%+%c%-%-\n\n',
|
||||
(7, 'store'), (1, 'expr'), (16, 'for_block') ),
|
||||
'attribute37': ( '%c.%[1]{pattr}', 0 ),
|
||||
'compare_chained1a_37': ( ' %[3]{pattr.replace("-", " ")} %p %p',
|
||||
(0, 19),
|
||||
(-4, 19)),
|
||||
@@ -936,9 +945,31 @@ def customize_for_version3(self, version):
|
||||
########################
|
||||
# Python 3.8+ changes
|
||||
#######################
|
||||
for lhs in 'for forelsestmt forelselaststmt forelselaststmtl'.split():
|
||||
del TABLE_DIRECT[lhs]
|
||||
|
||||
# FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest.
|
||||
# for lhs in 'for forelsestmt forelselaststmt '
|
||||
# 'forelselaststmtl tryfinally38'.split():
|
||||
# del TABLE_DIRECT[lhs]
|
||||
|
||||
TABLE_DIRECT.update({
|
||||
'async_for_stmt38': (
|
||||
'%|async for %c in %c:\n%+%c%-%-\n\n',
|
||||
(7, 'store'), (0, 'expr'), (8, 'for_block') ),
|
||||
|
||||
'async_with_stmt38': (
|
||||
'%|async with %c:\n%+%|%c%-',
|
||||
(0, 'expr'), 7),
|
||||
|
||||
'async_with_as_stmt38': (
|
||||
'%|async with %c as %c:\n%+%|%c%-',
|
||||
(0, 'expr'), (6, 'store'),
|
||||
(7, 'suite_stmts') ),
|
||||
|
||||
'except_handler38a': (
|
||||
'%c', (-2, 'stmts') ),
|
||||
'except_ret38a': (
|
||||
'return %c', (4, 'expr') ),
|
||||
'except_ret38': ( '%|return %c\n', (1, 'expr') ),
|
||||
'for38': (
|
||||
'%|for %c in %c:\n%+%c%-\n\n',
|
||||
(2, 'store'),
|
||||
@@ -959,8 +990,22 @@ def customize_for_version3(self, version):
|
||||
(2, 'store'),
|
||||
(0, 'expr'),
|
||||
(3, 'for_block'), -2 ),
|
||||
|
||||
'whilestmt38': ( '%|while %c:\n%+%c%-\n\n',
|
||||
(0, 'testexpr'), (1, 'l_stmts') ),
|
||||
'whileTruestmt38': ( '%|while True:\n%+%c%-\n\n',
|
||||
(0, 'l_stmts') ),
|
||||
'tryfinally38': (
|
||||
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
|
||||
(3, 'returns'), 6 ),
|
||||
'try_except38': (
|
||||
'%|try:\n%+%c\n%-%|except:\n%|%-%c\n\n',
|
||||
(-2, 'suite_stmts_opt'), (-1, 'except_handler38a') ),
|
||||
'try_except_ret38': (
|
||||
'%|try:\n%+%|return %c%-\n%|except:\n%+%|%c%-\n\n',
|
||||
(1, 'expr'), (-1, 'except_ret38a') ),
|
||||
})
|
||||
pass
|
||||
pass # version >= 3.8
|
||||
pass
|
||||
|
||||
pass # version >= 3.6
|
||||
|
@@ -1815,7 +1815,6 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
# except_cond3 is only in Python <= 2.6
|
||||
n_except_cond3 = n_except_cond2
|
||||
|
||||
|
||||
def template_engine(self, entry, startnode):
|
||||
"""The format template interpetation engine. See the comment at the
|
||||
beginning of this module for the how we interpret format
|
||||
@@ -1861,8 +1860,8 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
index = entry[arg]
|
||||
if isinstance(index, tuple):
|
||||
assert node[index[0]] == index[1], (
|
||||
"at %s[%d], expected %s node; got %s" % (
|
||||
node.kind, arg, node[index[0]].kind, index[1])
|
||||
"at %s[%d], expected '%s' node; got '%s'" % (
|
||||
node.kind, arg, index[1], node[index[0]].kind)
|
||||
)
|
||||
index = index[0]
|
||||
assert isinstance(index, int), (
|
||||
|
Reference in New Issue
Block a user