Sync up with decompile3's 3.8 try/else handling

This commit is contained in:
rocky
2022-06-08 12:04:12 -04:00
parent 6597737709
commit 7fb483c566
3 changed files with 325 additions and 141 deletions

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2017-2020 Rocky Bernstein # Copyright (c) 2017-2020, 2022 Rocky Bernstein
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -22,34 +22,31 @@ from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
from uncompyle6.parsers.parse37 import Python37Parser from uncompyle6.parsers.parse37 import Python37Parser
class Python38Parser(Python37Parser): class Python38Parser(Python37Parser):
def p_38walrus(self, args): def p_38_stmt(self, args):
"""
# named_expr is also known as the "walrus op" :=
expr ::= named_expr
named_expr ::= expr DUP_TOP store
"""
def p_38misc(self, args):
""" """
stmt ::= async_for_stmt38 stmt ::= async_for_stmt38
stmt ::= async_forelse_stmt38 stmt ::= async_forelse_stmt38
stmt ::= call_stmt
stmt ::= continue
stmt ::= for38 stmt ::= for38
stmt ::= forelsestmt38
stmt ::= forelselaststmt38 stmt ::= forelselaststmt38
stmt ::= forelselaststmtl38 stmt ::= forelselaststmtl38
stmt ::= tryfinally38stmt stmt ::= forelsestmt38
stmt ::= try_elsestmtl38
stmt ::= try_except38
stmt ::= try_except38r
stmt ::= try_except38r2
stmt ::= try_except38r3
stmt ::= try_except38r4
stmt ::= try_except_as
stmt ::= try_except_ret38
stmt ::= tryfinally38astmt
stmt ::= tryfinally38rstmt stmt ::= tryfinally38rstmt
stmt ::= tryfinally38rstmt2 stmt ::= tryfinally38rstmt2
stmt ::= tryfinally38rstmt3 stmt ::= tryfinally38rstmt3
stmt ::= tryfinally38astmt stmt ::= tryfinally38stmt
stmt ::= try_elsestmtl38
stmt ::= try_except_ret38
stmt ::= try_except38
stmt ::= try_except_as
stmt ::= whilestmt38
stmt ::= whileTruestmt38 stmt ::= whileTruestmt38
stmt ::= call_stmt stmt ::= whilestmt38
stmt ::= continue
call_stmt ::= call call_stmt ::= call
break ::= POP_BLOCK BREAK_LOOP break ::= POP_BLOCK BREAK_LOOP
@@ -129,6 +126,10 @@ class Python38Parser(Python37Parser):
forelselaststmt38 ::= expr get_for_iter store for_block POP_BLOCK else_suitec forelselaststmt38 ::= expr get_for_iter store for_block POP_BLOCK else_suitec
forelselaststmtl38 ::= expr get_for_iter store for_block POP_BLOCK else_suitel forelselaststmtl38 ::= expr get_for_iter store for_block POP_BLOCK else_suitel
returns_in_except ::= _stmts except_return_value
except_return_value ::= POP_BLOCK return
except_return_value ::= expr POP_BLOCK RETURN_VALUE
whilestmt38 ::= _come_froms testexpr l_stmts_opt COME_FROM JUMP_BACK POP_BLOCK whilestmt38 ::= _come_froms testexpr l_stmts_opt COME_FROM JUMP_BACK POP_BLOCK
whilestmt38 ::= _come_froms testexpr l_stmts_opt JUMP_BACK POP_BLOCK whilestmt38 ::= _come_froms testexpr l_stmts_opt JUMP_BACK POP_BLOCK
whilestmt38 ::= _come_froms testexpr l_stmts_opt JUMP_BACK come_froms whilestmt38 ::= _come_froms testexpr l_stmts_opt JUMP_BACK come_froms
@@ -155,9 +156,53 @@ class Python38Parser(Python37Parser):
else_suitel opt_come_from_except else_suitel opt_come_from_except
try_except ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK try_except ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
except_handler38 except_handler38
try_except38 ::= SETUP_FINALLY POP_BLOCK POP_TOP suite_stmts_opt try_except38 ::= SETUP_FINALLY POP_BLOCK POP_TOP suite_stmts_opt
except_handler38a except_handler38a
# suite_stmts has a return
try_except38 ::= SETUP_FINALLY POP_BLOCK suite_stmts
except_handler38b
try_except38r ::= SETUP_FINALLY return_except
except_handler38b
return_except ::= stmts POP_BLOCK return
# In 3.8 there seems to be some sort of code fiddle with POP_EXCEPT when there
# is a final return in the "except" block.
# So we treat the "return" separate from the other statements
cond_except_stmt ::= except_cond1 except_stmts
cond_except_stmts_opt ::= cond_except_stmt*
try_except38r2 ::= SETUP_FINALLY
suite_stmts_opt
POP_BLOCK JUMP_FORWARD
COME_FROM_FINALLY POP_TOP POP_TOP POP_TOP
cond_except_stmts_opt
POP_EXCEPT return
END_FINALLY
COME_FROM
try_except38r3 ::= SETUP_FINALLY
suite_stmts_opt
POP_BLOCK JUMP_FORWARD
COME_FROM_FINALLY
cond_except_stmts_opt
POP_EXCEPT return
COME_FROM
END_FINALLY
COME_FROM
try_except38r4 ::= SETUP_FINALLY
returns_in_except
COME_FROM_FINALLY
except_cond1
return
COME_FROM
END_FINALLY
# suite_stmts has a return # suite_stmts has a return
try_except38 ::= SETUP_FINALLY POP_BLOCK suite_stmts try_except38 ::= SETUP_FINALLY POP_BLOCK suite_stmts
except_handler38b except_handler38b
@@ -234,6 +279,13 @@ class Python38Parser(Python37Parser):
POP_FINALLY POP_TOP suite_stmts_opt END_FINALLY POP_TOP POP_FINALLY POP_TOP suite_stmts_opt END_FINALLY POP_TOP
""" """
def p_38walrus(self, args):
"""
# named_expr is also known as the "walrus op" :=
expr ::= named_expr
named_expr ::= expr DUP_TOP store
"""
def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG): def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
super(Python38Parser, self).__init__(debug_parser) super(Python38Parser, self).__init__(debug_parser)
self.customized = {} self.customized = {}

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2019-2020 by Rocky Bernstein # Copyright (c) 2019-2020, 2022 by Rocky Bernstein
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -25,133 +25,265 @@ def customize_for_version38(self, version):
# FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest. # FIXME: pytest doesn't add proper keys in testing. Reinstate after we have fixed pytest.
# for lhs in 'for forelsestmt forelselaststmt ' # for lhs in 'for forelsestmt forelselaststmt '
# 'forelselaststmtl tryfinally38'.split(): # 'forelselaststmtc tryfinally38'.split():
# del TABLE_DIRECT[lhs] # del TABLE_DIRECT[lhs]
TABLE_DIRECT.update({ TABLE_DIRECT.update(
{
"async_for_stmt38": ( "async_for_stmt38": (
"%|async for %c in %c:\n%+%c%-%-\n\n", "%|async for %c in %c:\n%+%c%-%-\n\n",
(2, "store"), (0, "expr"), (3, "for_block") ), (2, "store"),
(0, "expr"),
'async_forelse_stmt38': ( (3, "for_block"),
'%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', ),
(7, 'store'), (0, 'expr'), (8, 'for_block'), (-1, 'else_suite') ), "async_forelse_stmt38": (
"%|async for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
(7, 'store'),
(0, 'expr'),
(8, 'for_block'),
(-1, 'else_suite')
),
"async_with_stmt38": ( "async_with_stmt38": (
"%|async with %c:\n%+%|%c%-", "%|async with %c:\n%+%c%-\n",
(0, "expr"), 7), (0, "expr"),
(7, ("l_stmts_opt", "l_stmts", "pass")),
),
"async_with_as_stmt38": ( "async_with_as_stmt38": (
"%|async with %c as %c:\n%+%|%c%-", "%|async with %c as %c:\n%+%|%c%-",
(0, "expr"), (6, "store"), (0, "expr"),
(7, "suite_stmts") (6, "store"),
(7, "suite_stmts"),
), ),
"c_forelsestmt38": (
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
(2, "store"),
(0, "expr"),
(3, "for_block"),
-1,
),
"c_tryfinallystmt38": (
"%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
(1, "c_suite_stmts_opt"),
(-2, "c_suite_stmts_opt"),
),
"except_cond1a": ("%|except %c:\n", (1, "expr"),),
"except_cond_as": ( "except_cond_as": (
"%|except %c as %c:\n", "%|except %c as %c:\n",
(1, "expr"), (1, "expr"),
(-2, "STORE_FAST"), (-2, "STORE_FAST"),
), ),
"except_handler38": ("%c", (2, "except_stmts")),
'except_handler38': ( "except_handler38a": ("%c", (-2, "stmts")),
'%c', (2, 'except_stmts') ), "except_handler38c": (
"%c%+%c%-",
'except_handler38a': ( (1, "except_cond1a"),
'%c', (-2, 'stmts') ), (2, "except_stmts"),
),
"except_handler_as": ( "except_handler_as": (
"%c%+\n%+%c%-", "%c%+\n%+%c%-",
(1, "except_cond_as"), (1, "except_cond_as"),
(2, "tryfinallystmt"), (2, "tryfinallystmt"),
), ),
"except_ret38a": ("return %c", (4, "expr")),
'except_ret38a': (
'return %c', (4, 'expr') ),
# Note: there is a suite_stmts_opt which seems # Note: there is a suite_stmts_opt which seems
# to be bookkeeping which is not expressed in source code # to be bookkeeping which is not expressed in source code
'except_ret38': ( '%|return %c\n', (1, 'expr') ), "except_ret38": ("%|return %c\n", (1, "expr")),
"for38": (
'for38': ( "%|for %c in %c:\n%+%c%-\n\n",
'%|for %c in %c:\n%+%c%-\n\n', (2, "store"),
(2, 'store'), (0, "expr"),
(0, 'expr'), (3, "for_block"),
(3, 'for_block') ), ),
"forelsestmt38": ( "forelsestmt38": (
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n", "%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
(2, "store"), (2, "store"),
(0, "expr"), (0, "expr"),
(3, "for_block"), -1 ), (3, "for_block"),
-1,
'forelselaststmt38': ( ),
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-', "forelselaststmt38": (
(2, 'store'), "%|for %c in %c:\n%+%c%-%|else:\n%+%c%-",
(0, 'expr'), (2, "store"),
(3, 'for_block'), -2 ), (0, "expr"),
'forelselaststmtl38': ( (3, "for_block"),
'%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n', -2,
(2, 'store'), ),
(0, 'expr'), "forelselaststmtc38": (
(3, 'for_block'), -2 ), "%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
(2, "store"),
'ifpoplaststmtl': ( '%|if %c:\n%+%c%-', (0, "expr"),
(0, "testexpr"), (3, "for_block"),
(2, "c_stmts" ) ), -2,
),
'ifstmtl': ( '%|if %c:\n%+%c%-', "ifpoplaststmtc": ("%|if %c:\n%+%c%-", (0, "testexpr"), (2, "l_stmts")),
(0, "testexpr"), "pop_return": ("%|return %c\n", (1, "return_expr")),
(1, "_ifstmts_jumpl") ), "popb_return": ("%|return %c\n", (0, "return_expr")),
"pop_ex_return": ("%|return %c\n", (0, "return_expr")),
'whilestmt38': ( '%|while %c:\n%+%c%-\n\n', "set_for": (" for %c in %c", (2, "store"), (0, "expr_or_arg"),),
(1, 'testexpr'), "whilestmt38": (
2 ), # "l_stmts" or "pass" "%|while %c:\n%+%c%-\n\n",
'whileTruestmt38': ( '%|while True:\n%+%c%-\n\n', (1, "testexpr"),
1 ), # "l_stmts" or "pass" (2, ("l_stmts", "pass")),
'try_elsestmtl38': ( ),
'%|try:\n%+%c%-%c%|else:\n%+%c%-', "whileTruestmt38": ("%|while True:\n%+%c%-\n\n", (1, "l_stmts", "pass"),),
(1, 'suite_stmts_opt'), "try_elsestmtl38": (
(3, 'except_handler38'), "%|try:\n%+%c%-%c%|else:\n%+%c%-",
(5, 'else_suitel') ), (1, "suite_stmts_opt"),
'try_except38': ( (3, "except_handler38"),
'%|try:\n%+%c\n%-%|except:\n%|%-%c\n\n', (5, "else_suitel"),
(-2, 'suite_stmts_opt'), (-1, 'except_handler38a') ), ),
"try_except38": (
"%|try:\n%+%c\n%-%|except:\n%+%c%-\n\n",
(2, ("suite_stmts_opt", "suite_stmts")),
(3, ("except_handler38a", "except_handler38b", "except_handler38c")),
),
"try_except38r": (
"%|try:\n%+%c\n%-%|except:\n%+%c%-\n\n",
(1, "return_except"),
(2, "except_handler38b"),
),
"try_except38r2": (
"%|try:\n%+%c\n%-%|except:\n%+%c%c%-\n\n",
(1, "suite_stmts_opt"),
(8, "cond_except_stmts_opt"),
(10, "return"),
),
"try_except38r4": (
"%|try:\n%+%c\n%-%|except:\n%+%c%c%-\n\n",
(1, "returns_in_except"),
(3, "except_cond1"),
(4, "return"),
),
"try_except_as": ( "try_except_as": (
"%|try:\n%+%c%-\n%|%-%c\n\n", "%|try:\n%+%c%-\n%|%-%c\n\n",
(-4, "suite_stmts"), # Go from the end because of POP_BLOCK variation (
-4,
("suite_stmts", "_stmts"),
), # Go from the end because of POP_BLOCK variation
(-3, "except_handler_as"), (-3, "except_handler_as"),
), ),
"try_except_ret38": ( "try_except_ret38": (
"%|try:\n%+%c%-\n%|except:\n%+%|%c%-\n\n", "%|try:\n%+%c%-\n%|except:\n%+%|%c%-\n\n",
(1, "returns"), (1, "returns"),
(2, "except_ret38a"), (2, "except_ret38a"),
), ),
'tryfinally38rstmt': ( "try_except_ret38a": (
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', "%|try:\n%+%c%-%c\n\n",
(1, "returns"),
(2, "except_handler38c"),
),
"tryfinally38rstmt": (
"%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
(0, "sf_pb_call_returns"), (0, "sf_pb_call_returns"),
(-1, ("ss_end_finally", "suite_stmts")), (-1, ("ss_end_finally", "suite_stmts", "_stmts")),
), ),
"tryfinally38rstmt2": ( "tryfinally38rstmt2": (
"%|try:\n%+%c%-%|finally:\n%+%c%-\n\n", "%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
(4, "returns"), (4, "returns"),
-2, "ss_end_finally" -2,
"ss_end_finally",
), ),
"tryfinally38rstmt3": ( "tryfinally38rstmt3": (
"%|try:\n%+%|return %c%-\n%|finally:\n%+%c%-\n\n", "%|try:\n%+%|return %c%-\n%|finally:\n%+%c%-\n\n",
(1, "expr"), (1, "expr"),
(-1, "ss_end_finally") (-1, "ss_end_finally"),
), ),
'tryfinally38stmt': ( "tryfinally38rstmt4": (
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', "%|try:\n%+%c%-\n%|finally:\n%+%c%-\n\n",
(1, "suite_stmts_opt"), (1, "suite_stmts_opt"),
(6, "suite_stmts_opt") ), (5, "suite_stmts_return"),
'tryfinally38astmt': ( ),
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', "tryfinally38stmt": (
"%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
(1, "suite_stmts_opt"),
(6, "suite_stmts_opt"),
),
"tryfinally38astmt": (
"%|try:\n%+%c%-%|finally:\n%+%c%-\n\n",
(2, "suite_stmts_opt"), (2, "suite_stmts_opt"),
(8, "suite_stmts_opt") ), (8, "suite_stmts_opt"),
),
"named_expr": ( # AKA "walrus operator" "named_expr": ( # AKA "walrus operator"
"%c := %p", (2, "store"), (0, "expr", PRECEDENCE["named_expr"]-1) "%c := %p",
(2, "store"),
(0, "expr", PRECEDENCE["named_expr"] - 1),
),
}
) )
})
def except_return_value(node):
if node[0] == "POP_BLOCK":
self.default(node[1])
else:
self.template_engine(("%|return %c\n", (0, "expr")), node)
self.prune()
self.n_except_return_value = except_return_value
# FIXME: now that we've split out cond_except_stmt,
# we should be able to get this working as a pure transformation rule,
# so no procedure is needed here.
def try_except38r3(node):
self.template_engine(("%|try:\n%+%c\n%-", (1, "suite_stmts_opt")), node)
cond_except_stmts_opt = node[5]
assert cond_except_stmts_opt == "cond_except_stmts_opt"
for child in cond_except_stmts_opt:
if child == "cond_except_stmt":
if child[0] == "except_cond1":
self.template_engine(
("%c\n", (0, "except_cond1"), (1, "expr")), child
)
self.template_engine(("%+%c%-\n", (1, "except_stmts")), child)
pass
pass
self.template_engine(("%+%c%-\n", (7, "return")), node)
self.prune()
self.n_try_except38r3 = try_except38r3
def n_list_afor(node):
if len(node) == 2:
# list_afor ::= get_iter list_afor
self.comprehension_walk_newer(node, 0)
else:
list_iter_index = 2 if node[2] == "list_iter" else 3
self.template_engine(
(
" async for %[1]{%c} in %c%[1]{%c}",
(1, "store"),
(0, "get_aiter"),
(list_iter_index, "list_iter"),
),
node,
)
self.prune()
self.n_list_afor = n_list_afor
def n_set_afor(node):
if len(node) == 2:
self.template_engine(
(" async for %[1]{%c} in %c", (1, "store"), (0, "get_aiter")), node
)
else:
self.template_engine(
" async for %[1]{%c} in %c%c",
(1, "store"),
(0, "get_aiter"),
(2, "set_iter"),
)
self.prune()
self.n_set_afor = n_set_afor
def n_suite_stmts_return(node):
if len(node) > 1:
assert len(node) == 2
self.template_engine(
("%c\n%|return %c", (0, ("_stmts", "suite_stmts")), (1, "expr")), node
)
else:
self.template_engine(("%|return %c", (0, "expr")), node)
self.prune()
self.n_suite_stmts_return = n_suite_stmts_return