You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Merge branch 'python-3.3-to-3.5' into python-3.0-to-3.2
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
#!/bin/bash
|
||||
# Run tests over all Python versions in branch python-3.0-3.2
|
||||
set -e
|
||||
function finish {
|
||||
cd $owd
|
||||
}
|
||||
|
||||
owd=$(pwd)
|
||||
trap finish EXIT
|
||||
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
if ! source ./pyenv-3.0-3.2-versions ; then
|
||||
@@ -23,4 +28,4 @@ for version in $PYVERSIONS; do
|
||||
fi
|
||||
echo === $version ===
|
||||
done
|
||||
cd $owd
|
||||
finish
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#/bin/bash
|
||||
owd=$(pwd)
|
||||
uncompyle6_merge_24_owd=$(pwd)
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
if . ./setup-python-2.4.sh; then
|
||||
git merge python-3.0-to-3.2
|
||||
fi
|
||||
cd $owd
|
||||
cd $uncompyle6_merge_24_owd
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#/bin/bash
|
||||
owd=$(pwd)
|
||||
uncompyle6_merge_30_owd=$(pwd)
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
if . ./setup-python-3.0.sh; then
|
||||
git merge python-3.3-to-3.5
|
||||
fi
|
||||
cd $owd
|
||||
cd $uncompyle6_merge_30_owd
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#/bin/bash
|
||||
owd=$(pwd)
|
||||
uncompyle6_merge_33_owd=$(pwd)
|
||||
cd $(dirname ${BASH_SOURCE[0]})
|
||||
if . ./setup-python-3.3.sh; then
|
||||
git merge master
|
||||
fi
|
||||
cd $owd
|
||||
cd $uncompyle6_merge_33_owd
|
||||
|
BIN
test/bytecode_3.6_run/08_test_contextmanager.pyc
Normal file
BIN
test/bytecode_3.6_run/08_test_contextmanager.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8_run/08_test_contextmanager.pyc
Normal file
BIN
test/bytecode_3.8_run/08_test_contextmanager.pyc
Normal file
Binary file not shown.
21
test/simple_source/stmts/08_test_contextmanager.py
Normal file
21
test/simple_source/stmts/08_test_contextmanager.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
This program is self checking!
|
||||
"""
|
||||
|
||||
|
||||
class TestContextManager:
|
||||
def __enter__(self):
|
||||
return 1, 2
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||
return self, exc_type, exc_value, exc_tb
|
||||
|
||||
|
||||
with open(__file__) as a:
|
||||
assert a
|
||||
|
||||
with open(__file__) as a, open(__file__) as b:
|
||||
assert a.read() == b.read()
|
||||
|
||||
with TestContextManager() as a, b:
|
||||
assert (a, b) == (1, 2)
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2015-2023 Rocky Bernstein
|
||||
# Copyright (c) 2015-2024 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
|
||||
@@ -21,10 +21,11 @@ Common uncompyle6 parser routines.
|
||||
|
||||
import sys
|
||||
|
||||
from spark_parser import GenericASTBuilder, DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||
from uncompyle6.show import maybe_show_asm
|
||||
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG, GenericASTBuilder
|
||||
from xdis import iscode
|
||||
|
||||
from uncompyle6.show import maybe_show_asm
|
||||
|
||||
|
||||
class ParserError(Exception):
|
||||
def __init__(self, token, offset, debug=PARSER_DEFAULT_DEBUG):
|
||||
@@ -91,7 +92,14 @@ class PythonParser(GenericASTBuilder):
|
||||
# singleton reduction that we can simplify. It also happens to be optional
|
||||
# in its other derivation
|
||||
self.optional_nt |= frozenset(
|
||||
("come_froms", "suite_stmts", "l_stmts_opt", "c_stmts_opt", "stmts_opt", "stmt")
|
||||
(
|
||||
"come_froms",
|
||||
"suite_stmts",
|
||||
"l_stmts_opt",
|
||||
"c_stmts_opt",
|
||||
"stmts_opt",
|
||||
"stmt",
|
||||
)
|
||||
)
|
||||
|
||||
# Reduce singleton reductions in these nonterminals:
|
||||
@@ -113,10 +121,10 @@ class PythonParser(GenericASTBuilder):
|
||||
|
||||
def add_unique_rule(self, rule, opname, arg_count, customize):
|
||||
"""Add rule to grammar, but only if it hasn't been added previously
|
||||
opname and stack_count are used in the customize() semantic
|
||||
the actions to add the semantic action rule. Stack_count is
|
||||
used in custom opcodes like MAKE_FUNCTION to indicate how
|
||||
many arguments it has. Often it is not used.
|
||||
opname and stack_count are used in the customize() semantic
|
||||
the actions to add the semantic action rule. Stack_count is
|
||||
used in custom opcodes like MAKE_FUNCTION to indicate how
|
||||
many arguments it has. Often it is not used.
|
||||
"""
|
||||
if rule not in self.new_rules:
|
||||
# print("XXX ", rule) # debug
|
||||
@@ -223,7 +231,9 @@ class PythonParser(GenericASTBuilder):
|
||||
"""
|
||||
# Low byte indicates number of positional parameters,
|
||||
# high byte number of keyword parameters
|
||||
assert token.kind.startswith("CALL_FUNCTION") or token.kind.startswith("CALL_METHOD")
|
||||
assert token.kind.startswith("CALL_FUNCTION") or token.kind.startswith(
|
||||
"CALL_METHOD"
|
||||
)
|
||||
args_pos = token.attr & 0xFF
|
||||
args_kw = (token.attr >> 8) & 0xFF
|
||||
return args_pos, args_kw
|
||||
@@ -364,7 +374,7 @@ class PythonParser(GenericASTBuilder):
|
||||
stmt ::= tryelsestmt
|
||||
stmt ::= tryfinallystmt
|
||||
stmt ::= with
|
||||
stmt ::= withasstmt
|
||||
stmt ::= with_as
|
||||
|
||||
stmt ::= delete
|
||||
delete ::= DELETE_FAST
|
||||
@@ -907,7 +917,7 @@ def python_parser(
|
||||
if __name__ == "__main__":
|
||||
|
||||
def parse_test(co):
|
||||
from xdis import PYTHON_VERSION_TRIPLE, IS_PYPY
|
||||
from xdis import IS_PYPY, PYTHON_VERSION_TRIPLE
|
||||
|
||||
ast = python_parser(PYTHON_VERSION_TRIPLE, co, showasm=True, is_pypy=IS_PYPY)
|
||||
print(ast)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016-2018, 2020, 2022-2023 Rocky Bernstein
|
||||
# Copyright (c) 2016-2018, 2020, 2022-2024 Rocky Bernstein
|
||||
"""
|
||||
spark grammar differences over Python2.5 for Python 2.4.
|
||||
"""
|
||||
@@ -89,12 +89,14 @@ class Python24Parser(Python25Parser):
|
||||
while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK COME_FROM
|
||||
while1stmt ::= SETUP_LOOP returns COME_FROM
|
||||
whilestmt ::= SETUP_LOOP testexpr returns POP_BLOCK COME_FROM
|
||||
with ::= expr setupwith SETUP_FINALLY suite_stmts_opt POP_BLOCK
|
||||
LOAD_CONST COME_FROM with_cleanup
|
||||
with_as ::= expr setupwithas store suite_stmts_opt POP_BLOCK
|
||||
LOAD_CONST COME_FROM with_cleanup
|
||||
with_cleanup ::= LOAD_FAST DELETE_FAST WITH_CLEANUP END_FINALLY
|
||||
with_cleanup ::= LOAD_NAME DELETE_NAME WITH_CLEANUP END_FINALLY
|
||||
withasstmt ::= expr setupwithas store suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM with_cleanup
|
||||
with ::= expr setupwith SETUP_FINALLY suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM with_cleanup
|
||||
stmt ::= with
|
||||
stmt ::= withasstmt
|
||||
stmt ::= with
|
||||
stmt ::= with_as
|
||||
"""
|
||||
)
|
||||
super(Python24Parser, self).customize_grammar_rules(tokens, customize)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016-2018, 2020, 2022 Rocky Bernstein
|
||||
# Copyright (c) 2016-2018, 2020, 2022, 2024 Rocky Bernstein
|
||||
"""
|
||||
spark grammar differences over Python2.6 for Python 2.5.
|
||||
"""
|
||||
@@ -33,7 +33,7 @@ class Python25Parser(Python26Parser):
|
||||
POP_BLOCK LOAD_CONST COME_FROM with_cleanup
|
||||
|
||||
# Semantic actions want store to be at index 2
|
||||
withasstmt ::= expr setupwithas store suite_stmts_opt
|
||||
with_as ::= expr setupwithas store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM with_cleanup
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class Python25Parser(Python26Parser):
|
||||
|
||||
# Python 2.6 omits the LOAD_FAST DELETE_FAST below
|
||||
# withas is allowed as a "from future" in 2.5
|
||||
withasstmt ::= expr setupwithas store suite_stmts_opt
|
||||
with_as ::= expr setupwithas store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM
|
||||
with_cleanup
|
||||
|
||||
@@ -67,7 +67,7 @@ class Python25Parser(Python26Parser):
|
||||
setupwith ::= DUP_TOP LOAD_ATTR ROT_TWO LOAD_ATTR CALL_FUNCTION_0 POP_TOP
|
||||
with ::= expr setupwith SETUP_FINALLY suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM WITH_CLEANUP END_FINALLY
|
||||
withasstmt ::= expr setupwithas store suite_stmts_opt
|
||||
with_as ::= expr setupwithas store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM WITH_CLEANUP END_FINALLY
|
||||
assert2 ::= assert_expr jmp_true LOAD_ASSERT expr CALL_FUNCTION_1 RAISE_VARARGS_1
|
||||
classdefdeco ::= classdefdeco1 store
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2017-2023 Rocky Bernstein
|
||||
# Copyright (c) 2017-2024 Rocky Bernstein
|
||||
"""
|
||||
spark grammar differences over Python2 for Python 2.6.
|
||||
"""
|
||||
@@ -136,7 +136,7 @@ class Python26Parser(Python2Parser):
|
||||
POP_BLOCK LOAD_CONST COME_FROM WITH_CLEANUP END_FINALLY
|
||||
|
||||
# Semantic actions want store to be at index 2
|
||||
withasstmt ::= expr setupwithas store suite_stmts_opt
|
||||
with_as ::= expr setupwithas store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM WITH_CLEANUP END_FINALLY
|
||||
|
||||
# This is truly weird. 2.7 does this (not including POP_TOP) with
|
||||
@@ -352,9 +352,9 @@ class Python26Parser(Python2Parser):
|
||||
def customize_grammar_rules(self, tokens, customize):
|
||||
self.remove_rules(
|
||||
"""
|
||||
withasstmt ::= expr SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
with_as ::= expr SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
"""
|
||||
)
|
||||
super(Python26Parser, self).customize_grammar_rules(tokens, customize)
|
||||
@@ -391,7 +391,6 @@ class Python26Parser(Python2Parser):
|
||||
("and", ("expr", "jmp_false", "expr", "come_from_opt")),
|
||||
("assert_expr_and", ("assert_expr", "jmp_false", "expr")),
|
||||
):
|
||||
|
||||
# FIXME: workaround profiling bug
|
||||
if ast[1] is None:
|
||||
return False
|
||||
@@ -491,7 +490,6 @@ class Python26Parser(Python2Parser):
|
||||
("JUMP_FORWARD", "RETURN_VALUE")
|
||||
) or (tokens[last - 3] == "JUMP_FORWARD" and tokens[last - 3].attr != 2)
|
||||
elif lhs == "tryelsestmt":
|
||||
|
||||
# We need to distinguish "try_except" from "tryelsestmt"; we do that
|
||||
# by making sure that the jump before the except handler jumps to
|
||||
# code somewhere before the end of the construct.
|
||||
|
@@ -161,9 +161,9 @@ class Python27Parser(Python2Parser):
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
|
||||
withasstmt ::= expr SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
with_as ::= expr SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
|
||||
whilestmt ::= SETUP_LOOP testexpr returns
|
||||
_come_froms POP_BLOCK COME_FROM
|
||||
|
@@ -287,9 +287,9 @@ class Python3Parser(PythonParser):
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
|
||||
withasstmt ::= expr SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
with_as ::= expr SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
|
||||
expr_jt ::= expr jmp_true
|
||||
expr_jitop ::= expr JUMP_IF_TRUE_OR_POP
|
||||
|
@@ -66,7 +66,7 @@ class Python30Parser(Python31Parser):
|
||||
iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE COME_FROM POP_TOP
|
||||
|
||||
|
||||
withasstmt ::= expr setupwithas store suite_stmts_opt
|
||||
with_as ::= expr setupwithas store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_FINALLY
|
||||
LOAD_FAST DELETE_FAST WITH_CLEANUP END_FINALLY
|
||||
setupwithas ::= DUP_TOP LOAD_ATTR STORE_FAST LOAD_ATTR CALL_FUNCTION_0 setup_finally
|
||||
@@ -222,12 +222,17 @@ class Python30Parser(Python31Parser):
|
||||
# The were found using grammar coverage
|
||||
while1stmt ::= SETUP_LOOP l_stmts COME_FROM JUMP_BACK COME_FROM_LOOP
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM_LOOP
|
||||
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK else_suitel COME_FROM_LOOP
|
||||
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM_LOOP
|
||||
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK JUMP_BACK COME_FROM_LOOP
|
||||
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
else_suitel COME_FROM_LOOP
|
||||
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK JUMP_BACK
|
||||
COME_FROM_LOOP
|
||||
whilestmt ::= SETUP_LOOP testexpr returns POP_TOP POP_BLOCK COME_FROM_LOOP
|
||||
withasstmt ::= expr SETUP_WITH store suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM_WITH WITH_CLEANUP END_FINALLY
|
||||
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM_WITH WITH_CLEANUP END_FINALLY
|
||||
with_as ::= expr SETUP_WITH store suite_stmts_opt POP_BLOCK LOAD_CONST
|
||||
COME_FROM_WITH WITH_CLEANUP END_FINALLY
|
||||
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK LOAD_CONST
|
||||
COME_FROM_WITH WITH_CLEANUP END_FINALLY
|
||||
|
||||
# lc_body ::= LOAD_FAST expr LIST_APPEND
|
||||
# lc_body ::= LOAD_NAME expr LIST_APPEND
|
||||
|
@@ -23,7 +23,7 @@ class Python31Parser(Python32Parser):
|
||||
# Keeps Python 3.1 "with .. as" designator in the same position as it is in other version.
|
||||
setupwithas31 ::= setupwithas SETUP_FINALLY load delete
|
||||
|
||||
withasstmt ::= expr setupwithas31 store
|
||||
with_as ::= expr setupwithas31 store
|
||||
suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_FINALLY
|
||||
load delete WITH_CLEANUP END_FINALLY
|
||||
|
@@ -1,15 +1,17 @@
|
||||
# Copyright (c) 2016-2017, 2019, 2021, 2023 Rocky Bernstein
|
||||
# Copyright (c) 2016-2017, 2019, 2021, 2023-2024
|
||||
# Rocky Bernstein
|
||||
"""
|
||||
spark grammar differences over Python 3.4 for Python 3.5.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
from uncompyle6.parser import PythonParserSingle, nop_func
|
||||
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||
|
||||
from uncompyle6.parser import PythonParserSingle, nop_func
|
||||
from uncompyle6.parsers.parse34 import Python34Parser
|
||||
|
||||
class Python35Parser(Python34Parser):
|
||||
|
||||
class Python35Parser(Python34Parser):
|
||||
def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
|
||||
super(Python35Parser, self).__init__(debug_parser)
|
||||
self.customized = {}
|
||||
@@ -55,7 +57,7 @@ class Python35Parser(Python34Parser):
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
|
||||
withasstmt ::= expr
|
||||
with_as ::= expr
|
||||
SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
@@ -135,40 +137,42 @@ class Python35Parser(Python34Parser):
|
||||
"""
|
||||
|
||||
def customize_grammar_rules(self, tokens, customize):
|
||||
self.remove_rules("""
|
||||
self.remove_rules(
|
||||
"""
|
||||
yield_from ::= expr GET_ITER LOAD_CONST YIELD_FROM
|
||||
yield_from ::= expr expr YIELD_FROM
|
||||
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
withasstmt ::= expr SETUP_WITH store suite_stmts_opt
|
||||
with_as ::= expr SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
""")
|
||||
"""
|
||||
)
|
||||
super(Python35Parser, self).customize_grammar_rules(tokens, customize)
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.kind
|
||||
if opname == 'LOAD_ASSERT':
|
||||
if 'PyPy' in customize:
|
||||
if opname == "LOAD_ASSERT":
|
||||
if "PyPy" in customize:
|
||||
rules_str = """
|
||||
stmt ::= JUMP_IF_NOT_DEBUG stmts COME_FROM
|
||||
"""
|
||||
self.add_unique_doc_rules(rules_str, customize)
|
||||
# FIXME: I suspect this is wrong for 3.6 and 3.5, but
|
||||
# I haven't verified what the 3.7ish fix is
|
||||
elif opname == 'BUILD_MAP_UNPACK_WITH_CALL':
|
||||
elif opname == "BUILD_MAP_UNPACK_WITH_CALL":
|
||||
if self.version < (3, 7):
|
||||
self.addRule("expr ::= unmapexpr", nop_func)
|
||||
nargs = token.attr % 256
|
||||
map_unpack_n = "map_unpack_%s" % nargs
|
||||
rule = map_unpack_n + ' ::= ' + 'expr ' * (nargs)
|
||||
rule = map_unpack_n + " ::= " + "expr " * (nargs)
|
||||
self.addRule(rule, nop_func)
|
||||
rule = "unmapexpr ::= %s %s" % (map_unpack_n, opname)
|
||||
self.addRule(rule, nop_func)
|
||||
call_token = tokens[i+1]
|
||||
rule = 'call ::= expr unmapexpr ' + call_token.kind
|
||||
call_token = tokens[i + 1]
|
||||
rule = "call ::= expr unmapexpr " + call_token.kind
|
||||
self.addRule(rule, nop_func)
|
||||
elif opname == 'BEFORE_ASYNC_WITH' and self.version < (3, 8):
|
||||
elif opname == "BEFORE_ASYNC_WITH" and self.version < (3, 8):
|
||||
# Some Python 3.5+ async additions
|
||||
rules_str = """
|
||||
stmt ::= async_with_stmt
|
||||
@@ -199,24 +203,27 @@ class Python35Parser(Python34Parser):
|
||||
async_with_post
|
||||
"""
|
||||
self.addRule(rules_str, nop_func)
|
||||
elif opname == 'BUILD_MAP_UNPACK':
|
||||
self.addRule("""
|
||||
elif opname == "BUILD_MAP_UNPACK":
|
||||
self.addRule(
|
||||
"""
|
||||
expr ::= dict_unpack
|
||||
dict_unpack ::= dict_comp BUILD_MAP_UNPACK
|
||||
""", nop_func)
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
|
||||
elif opname == 'SETUP_WITH':
|
||||
elif opname == "SETUP_WITH":
|
||||
# Python 3.5+ has WITH_CLEANUP_START/FINISH
|
||||
rules_str = """
|
||||
with ::= expr
|
||||
SETUP_WITH POP_TOP suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
with ::= expr
|
||||
SETUP_WITH POP_TOP suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
|
||||
withasstmt ::= expr
|
||||
SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
with_as ::= expr
|
||||
SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
"""
|
||||
self.addRule(rules_str, nop_func)
|
||||
pass
|
||||
@@ -230,19 +237,24 @@ class Python35Parser(Python34Parser):
|
||||
# 1 for CALL_FUNCTION_VAR or CALL_FUNCTION_KW
|
||||
# 2 for * and ** args (CALL_FUNCTION_VAR_KW).
|
||||
# Yes, this computation based on instruction name is a little bit hoaky.
|
||||
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
|
||||
nak = (len(opname) - len("CALL_FUNCTION")) // 3
|
||||
uniq_param = args_kw + args_pos
|
||||
|
||||
if frozenset(('GET_AWAITABLE', 'YIELD_FROM')).issubset(self.seen_ops):
|
||||
rule = ('async_call ::= expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) +
|
||||
'expr ' * nak + token.kind +
|
||||
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
|
||||
if frozenset(("GET_AWAITABLE", "YIELD_FROM")).issubset(self.seen_ops):
|
||||
rule = (
|
||||
"async_call ::= expr "
|
||||
+ ("pos_arg " * args_pos)
|
||||
+ ("kwarg " * args_kw)
|
||||
+ "expr " * nak
|
||||
+ token.kind
|
||||
+ " GET_AWAITABLE LOAD_CONST YIELD_FROM"
|
||||
)
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
self.add_unique_rule('expr ::= async_call', token.kind, uniq_param, customize)
|
||||
self.add_unique_rule(
|
||||
"expr ::= async_call", token.kind, uniq_param, customize
|
||||
)
|
||||
|
||||
if opname.startswith('CALL_FUNCTION_VAR'):
|
||||
if opname.startswith("CALL_FUNCTION_VAR"):
|
||||
# Python 3.5 changes the stack position of *args. KW args come
|
||||
# after *args.
|
||||
|
||||
@@ -250,43 +262,55 @@ class Python35Parser(Python34Parser):
|
||||
# CALL_FUNCTION_VAR_KW with CALL_FUNCTION_EX
|
||||
|
||||
token.kind = self.call_fn_name(token)
|
||||
if opname.endswith('KW'):
|
||||
kw = 'expr '
|
||||
if opname.endswith("KW"):
|
||||
kw = "expr "
|
||||
else:
|
||||
kw = ''
|
||||
rule = ('call ::= expr expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) + kw + token.kind)
|
||||
kw = ""
|
||||
rule = (
|
||||
"call ::= expr expr "
|
||||
+ ("pos_arg " * args_pos)
|
||||
+ ("kwarg " * args_kw)
|
||||
+ kw
|
||||
+ token.kind
|
||||
)
|
||||
|
||||
# Note: semantic actions make use of the fact of whether "args_pos"
|
||||
# zero or not in creating a template rule.
|
||||
self.add_unique_rule(rule, token.kind, args_pos, customize)
|
||||
else:
|
||||
super(Python35Parser, self).custom_classfunc_rule(opname, token, customize, *args
|
||||
super(Python35Parser, self).custom_classfunc_rule(
|
||||
opname, token, customize, *args
|
||||
)
|
||||
|
||||
|
||||
class Python35ParserSingle(Python35Parser, PythonParserSingle):
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Check grammar
|
||||
p = Python35Parser()
|
||||
p.check_grammar()
|
||||
from xdis.version_info import PYTHON_VERSION_TRIPLE, IS_PYPY
|
||||
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
|
||||
|
||||
if PYTHON_VERSION_TRIPLE[:2] == (3, 5):
|
||||
lhs, rhs, tokens, right_recursive, dup_rhs = p.check_sets()
|
||||
from uncompyle6.scanner import get_scanner
|
||||
|
||||
s = get_scanner(PYTHON_VERSION_TRIPLE, IS_PYPY)
|
||||
opcode_set = set(s.opc.opname).union(set(
|
||||
"""JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
|
||||
opcode_set = set(s.opc.opname).union(
|
||||
set(
|
||||
"""JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
|
||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_CLASSNAME
|
||||
LAMBDA_MARKER RETURN_LAST
|
||||
""".split()))
|
||||
""".split()
|
||||
)
|
||||
)
|
||||
remain_tokens = set(tokens) - opcode_set
|
||||
import re
|
||||
remain_tokens = set([re.sub(r'_\d+$', '', t) for t in remain_tokens])
|
||||
remain_tokens = set([re.sub('_CONT$', '', t) for t in remain_tokens])
|
||||
|
||||
remain_tokens = set([re.sub(r"_\d+$", "", t) for t in remain_tokens])
|
||||
remain_tokens = set([re.sub("_CONT$", "", t) for t in remain_tokens])
|
||||
remain_tokens = set(remain_tokens) - opcode_set
|
||||
print(remain_tokens)
|
||||
# print(sorted(p.rule2name.items()))
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016-2020, 2022-2023 Rocky Bernstein
|
||||
# Copyright (c) 2016-2020, 2022-2024 Rocky Bernstein
|
||||
#
|
||||
# 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
|
||||
@@ -17,24 +17,25 @@ spark grammar differences over Python 3.5 for Python 3.6.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
from uncompyle6.parser import PythonParserSingle, nop_func
|
||||
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||
|
||||
from uncompyle6.parser import PythonParserSingle, nop_func
|
||||
from uncompyle6.parsers.parse35 import Python35Parser
|
||||
from uncompyle6.scanners.tok import Token
|
||||
|
||||
class Python36Parser(Python35Parser):
|
||||
|
||||
class Python36Parser(Python35Parser):
|
||||
def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
|
||||
super(Python36Parser, self).__init__(debug_parser)
|
||||
self.customized = {}
|
||||
|
||||
|
||||
def p_36_jump(self, args):
|
||||
"""
|
||||
# Zero or one COME_FROM
|
||||
# And/or expressions have this
|
||||
come_from_opt ::= COME_FROM?
|
||||
"""
|
||||
|
||||
def p_36_misc(self, args):
|
||||
"""sstmt ::= sstmt RETURN_LAST
|
||||
|
||||
@@ -207,7 +208,8 @@ class Python36Parser(Python35Parser):
|
||||
# self.remove_rules("""
|
||||
# """)
|
||||
super(Python36Parser, self).customize_grammar_rules(tokens, customize)
|
||||
self.remove_rules("""
|
||||
self.remove_rules(
|
||||
"""
|
||||
_ifstmts_jumpl ::= c_stmts_opt
|
||||
_ifstmts_jumpl ::= _ifstmts_jump
|
||||
except_handler ::= JUMP_FORWARD COME_FROM_EXCEPT except_stmts END_FINALLY COME_FROM
|
||||
@@ -234,7 +236,8 @@ class Python36Parser(Python35Parser):
|
||||
for_block pb_ja
|
||||
else_suite COME_FROM_LOOP
|
||||
|
||||
""")
|
||||
"""
|
||||
)
|
||||
self.check_reduce["call_kw"] = "AST"
|
||||
|
||||
# Opcode names in the custom_ops_processed set have rules that get added
|
||||
@@ -247,24 +250,23 @@ class Python36Parser(Python35Parser):
|
||||
# the start.
|
||||
custom_ops_processed = set()
|
||||
|
||||
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.kind
|
||||
|
||||
if opname == 'FORMAT_VALUE':
|
||||
if opname == "FORMAT_VALUE":
|
||||
rules_str = """
|
||||
expr ::= formatted_value1
|
||||
formatted_value1 ::= expr FORMAT_VALUE
|
||||
"""
|
||||
self.add_unique_doc_rules(rules_str, customize)
|
||||
elif opname == 'FORMAT_VALUE_ATTR':
|
||||
elif opname == "FORMAT_VALUE_ATTR":
|
||||
rules_str = """
|
||||
expr ::= formatted_value2
|
||||
formatted_value2 ::= expr expr FORMAT_VALUE_ATTR
|
||||
"""
|
||||
self.add_unique_doc_rules(rules_str, customize)
|
||||
elif opname == 'MAKE_FUNCTION_CLOSURE':
|
||||
if 'LOAD_DICTCOMP' in self.seen_ops:
|
||||
elif opname == "MAKE_FUNCTION_CLOSURE":
|
||||
if "LOAD_DICTCOMP" in self.seen_ops:
|
||||
# Is there something general going on here?
|
||||
rule = """
|
||||
dict_comp ::= load_closure LOAD_DICTCOMP LOAD_STR
|
||||
@@ -272,7 +274,7 @@ class Python36Parser(Python35Parser):
|
||||
GET_ITER CALL_FUNCTION_1
|
||||
"""
|
||||
self.addRule(rule, nop_func)
|
||||
elif 'LOAD_SETCOMP' in self.seen_ops:
|
||||
elif "LOAD_SETCOMP" in self.seen_ops:
|
||||
rule = """
|
||||
set_comp ::= load_closure LOAD_SETCOMP LOAD_STR
|
||||
MAKE_FUNCTION_CLOSURE expr
|
||||
@@ -280,7 +282,7 @@ class Python36Parser(Python35Parser):
|
||||
"""
|
||||
self.addRule(rule, nop_func)
|
||||
|
||||
elif opname == 'BEFORE_ASYNC_WITH':
|
||||
elif opname == "BEFORE_ASYNC_WITH":
|
||||
rules_str = """
|
||||
stmt ::= async_with_stmt
|
||||
async_with_pre ::= BEFORE_ASYNC_WITH GET_AWAITABLE LOAD_CONST YIELD_FROM SETUP_ASYNC_WITH
|
||||
@@ -306,30 +308,37 @@ class Python36Parser(Python35Parser):
|
||||
"""
|
||||
self.addRule(rules_str, nop_func)
|
||||
|
||||
elif opname.startswith('BUILD_STRING'):
|
||||
elif opname.startswith("BUILD_STRING"):
|
||||
v = token.attr
|
||||
rules_str = """
|
||||
expr ::= joined_str
|
||||
joined_str ::= %sBUILD_STRING_%d
|
||||
""" % ("expr " * v, v)
|
||||
""" % (
|
||||
"expr " * v,
|
||||
v,
|
||||
)
|
||||
self.add_unique_doc_rules(rules_str, customize)
|
||||
if 'FORMAT_VALUE_ATTR' in self.seen_ops:
|
||||
if "FORMAT_VALUE_ATTR" in self.seen_ops:
|
||||
rules_str = """
|
||||
formatted_value_attr ::= expr expr FORMAT_VALUE_ATTR expr BUILD_STRING
|
||||
expr ::= formatted_value_attr
|
||||
"""
|
||||
self.add_unique_doc_rules(rules_str, customize)
|
||||
elif opname.startswith('BUILD_MAP_UNPACK_WITH_CALL'):
|
||||
elif opname.startswith("BUILD_MAP_UNPACK_WITH_CALL"):
|
||||
v = token.attr
|
||||
rule = 'build_map_unpack_with_call ::= %s%s' % ('expr ' * v, opname)
|
||||
rule = "build_map_unpack_with_call ::= %s%s" % ("expr " * v, opname)
|
||||
self.addRule(rule, nop_func)
|
||||
elif opname.startswith('BUILD_TUPLE_UNPACK_WITH_CALL'):
|
||||
elif opname.startswith("BUILD_TUPLE_UNPACK_WITH_CALL"):
|
||||
v = token.attr
|
||||
rule = ('build_tuple_unpack_with_call ::= ' + 'expr1024 ' * int(v//1024) +
|
||||
'expr32 ' * int((v//32) % 32) +
|
||||
'expr ' * (v % 32) + opname)
|
||||
rule = (
|
||||
"build_tuple_unpack_with_call ::= "
|
||||
+ "expr1024 " * int(v // 1024)
|
||||
+ "expr32 " * int((v // 32) % 32)
|
||||
+ "expr " * (v % 32)
|
||||
+ opname
|
||||
)
|
||||
self.addRule(rule, nop_func)
|
||||
rule = ('starred ::= %s %s' % ('expr ' * v, opname))
|
||||
rule = "starred ::= %s %s" % ("expr " * v, opname)
|
||||
self.addRule(rule, nop_func)
|
||||
elif opname == "GET_AITER":
|
||||
self.addRule(
|
||||
@@ -475,7 +484,6 @@ class Python36Parser(Python35Parser):
|
||||
)
|
||||
custom_ops_processed.add(opname)
|
||||
|
||||
|
||||
elif opname == "GET_ANEXT":
|
||||
self.addRule(
|
||||
"""
|
||||
@@ -500,7 +508,7 @@ class Python36Parser(Python35Parser):
|
||||
)
|
||||
custom_ops_processed.add(opname)
|
||||
|
||||
elif opname == 'SETUP_ANNOTATIONS':
|
||||
elif opname == "SETUP_ANNOTATIONS":
|
||||
# 3.6 Variable Annotations PEP 526
|
||||
# This seems to come before STORE_ANNOTATION, and doesn't
|
||||
# correspond to direct Python source code.
|
||||
@@ -516,7 +524,7 @@ class Python36Parser(Python35Parser):
|
||||
"""
|
||||
self.addRule(rule, nop_func)
|
||||
# Check to combine assignment + annotation into one statement
|
||||
self.check_reduce['assign'] = 'token'
|
||||
self.check_reduce["assign"] = "token"
|
||||
elif opname == "WITH_CLEANUP_START":
|
||||
rules_str = """
|
||||
stmt ::= with_null
|
||||
@@ -524,13 +532,13 @@ class Python36Parser(Python35Parser):
|
||||
with_suffix ::= WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
"""
|
||||
self.addRule(rules_str, nop_func)
|
||||
elif opname == 'SETUP_WITH':
|
||||
elif opname == "SETUP_WITH":
|
||||
rules_str = """
|
||||
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt COME_FROM_WITH
|
||||
with_suffix
|
||||
|
||||
# Removes POP_BLOCK LOAD_CONST from 3.6-
|
||||
withasstmt ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
|
||||
with_as ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
|
||||
with_suffix
|
||||
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
|
||||
BEGIN_FINALLY COME_FROM_WITH
|
||||
@@ -542,7 +550,6 @@ class Python36Parser(Python35Parser):
|
||||
return
|
||||
|
||||
def custom_classfunc_rule(self, opname, token, customize, next_token, is_pypy):
|
||||
|
||||
args_pos, args_kw = self.get_pos_kw(token)
|
||||
|
||||
# Additional exprs for * and ** args:
|
||||
@@ -550,140 +557,186 @@ class Python36Parser(Python35Parser):
|
||||
# 1 for CALL_FUNCTION_VAR or CALL_FUNCTION_KW
|
||||
# 2 for * and ** args (CALL_FUNCTION_VAR_KW).
|
||||
# Yes, this computation based on instruction name is a little bit hoaky.
|
||||
nak = ( len(opname)-len('CALL_FUNCTION') ) // 3
|
||||
nak = (len(opname) - len("CALL_FUNCTION")) // 3
|
||||
uniq_param = args_kw + args_pos
|
||||
|
||||
if frozenset(('GET_AWAITABLE', 'YIELD_FROM')).issubset(self.seen_ops):
|
||||
rule = ('async_call ::= expr ' +
|
||||
('pos_arg ' * args_pos) +
|
||||
('kwarg ' * args_kw) +
|
||||
'expr ' * nak + token.kind +
|
||||
' GET_AWAITABLE LOAD_CONST YIELD_FROM')
|
||||
if frozenset(("GET_AWAITABLE", "YIELD_FROM")).issubset(self.seen_ops):
|
||||
rule = (
|
||||
"async_call ::= expr "
|
||||
+ ("pos_arg " * args_pos)
|
||||
+ ("kwarg " * args_kw)
|
||||
+ "expr " * nak
|
||||
+ token.kind
|
||||
+ " GET_AWAITABLE LOAD_CONST YIELD_FROM"
|
||||
)
|
||||
self.add_unique_rule(rule, token.kind, uniq_param, customize)
|
||||
self.add_unique_rule('expr ::= async_call', token.kind, uniq_param, customize)
|
||||
self.add_unique_rule(
|
||||
"expr ::= async_call", token.kind, uniq_param, customize
|
||||
)
|
||||
|
||||
if opname.startswith('CALL_FUNCTION_KW'):
|
||||
if opname.startswith("CALL_FUNCTION_KW"):
|
||||
if is_pypy:
|
||||
# PYPY doesn't follow CPython 3.6 CALL_FUNCTION_KW conventions
|
||||
super(Python36Parser, self).custom_classfunc_rule(opname, token, customize, next_token, is_pypy)
|
||||
super(Python36Parser, self).custom_classfunc_rule(
|
||||
opname, token, customize, next_token, is_pypy
|
||||
)
|
||||
else:
|
||||
self.addRule("expr ::= call_kw36", nop_func)
|
||||
values = 'expr ' * token.attr
|
||||
rule = "call_kw36 ::= expr {values} LOAD_CONST {opname}".format(**locals())
|
||||
values = "expr " * token.attr
|
||||
rule = "call_kw36 ::= expr {values} LOAD_CONST {opname}".format(
|
||||
**locals()
|
||||
)
|
||||
self.add_unique_rule(rule, token.kind, token.attr, customize)
|
||||
elif opname == 'CALL_FUNCTION_EX_KW':
|
||||
elif opname == "CALL_FUNCTION_EX_KW":
|
||||
# Note: this doesn't exist in 3.7 and later
|
||||
self.addRule("""expr ::= call_ex_kw4
|
||||
self.addRule(
|
||||
"""expr ::= call_ex_kw4
|
||||
call_ex_kw4 ::= expr
|
||||
expr
|
||||
expr
|
||||
CALL_FUNCTION_EX_KW
|
||||
""",
|
||||
nop_func)
|
||||
if 'BUILD_MAP_UNPACK_WITH_CALL' in self.seen_op_basenames:
|
||||
self.addRule("""expr ::= call_ex_kw
|
||||
nop_func,
|
||||
)
|
||||
if "BUILD_MAP_UNPACK_WITH_CALL" in self.seen_op_basenames:
|
||||
self.addRule(
|
||||
"""expr ::= call_ex_kw
|
||||
call_ex_kw ::= expr expr build_map_unpack_with_call
|
||||
CALL_FUNCTION_EX_KW
|
||||
""", nop_func)
|
||||
if 'BUILD_TUPLE_UNPACK_WITH_CALL' in self.seen_op_basenames:
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
if "BUILD_TUPLE_UNPACK_WITH_CALL" in self.seen_op_basenames:
|
||||
# FIXME: should this be parameterized by EX value?
|
||||
self.addRule("""expr ::= call_ex_kw3
|
||||
self.addRule(
|
||||
"""expr ::= call_ex_kw3
|
||||
call_ex_kw3 ::= expr
|
||||
build_tuple_unpack_with_call
|
||||
expr
|
||||
CALL_FUNCTION_EX_KW
|
||||
""", nop_func)
|
||||
if 'BUILD_MAP_UNPACK_WITH_CALL' in self.seen_op_basenames:
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
if "BUILD_MAP_UNPACK_WITH_CALL" in self.seen_op_basenames:
|
||||
# FIXME: should this be parameterized by EX value?
|
||||
self.addRule("""expr ::= call_ex_kw2
|
||||
self.addRule(
|
||||
"""expr ::= call_ex_kw2
|
||||
call_ex_kw2 ::= expr
|
||||
build_tuple_unpack_with_call
|
||||
build_map_unpack_with_call
|
||||
CALL_FUNCTION_EX_KW
|
||||
""", nop_func)
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
|
||||
elif opname == 'CALL_FUNCTION_EX':
|
||||
self.addRule("""
|
||||
elif opname == "CALL_FUNCTION_EX":
|
||||
self.addRule(
|
||||
"""
|
||||
expr ::= call_ex
|
||||
starred ::= expr
|
||||
call_ex ::= expr starred CALL_FUNCTION_EX
|
||||
""", nop_func)
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
if self.version >= (3, 6):
|
||||
if 'BUILD_MAP_UNPACK_WITH_CALL' in self.seen_ops:
|
||||
self.addRule("""
|
||||
if "BUILD_MAP_UNPACK_WITH_CALL" in self.seen_ops:
|
||||
self.addRule(
|
||||
"""
|
||||
expr ::= call_ex_kw
|
||||
call_ex_kw ::= expr expr
|
||||
build_map_unpack_with_call CALL_FUNCTION_EX
|
||||
""", nop_func)
|
||||
if 'BUILD_TUPLE_UNPACK_WITH_CALL' in self.seen_ops:
|
||||
self.addRule("""
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
if "BUILD_TUPLE_UNPACK_WITH_CALL" in self.seen_ops:
|
||||
self.addRule(
|
||||
"""
|
||||
expr ::= call_ex_kw3
|
||||
call_ex_kw3 ::= expr
|
||||
build_tuple_unpack_with_call
|
||||
%s
|
||||
CALL_FUNCTION_EX
|
||||
""" % 'expr ' * token.attr, nop_func)
|
||||
"""
|
||||
% "expr "
|
||||
* token.attr,
|
||||
nop_func,
|
||||
)
|
||||
pass
|
||||
|
||||
# FIXME: Is this right?
|
||||
self.addRule("""
|
||||
self.addRule(
|
||||
"""
|
||||
expr ::= call_ex_kw4
|
||||
call_ex_kw4 ::= expr
|
||||
expr
|
||||
expr
|
||||
CALL_FUNCTION_EX
|
||||
""", nop_func)
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
pass
|
||||
else:
|
||||
super(Python36Parser, self).custom_classfunc_rule(opname, token, customize, next_token, is_pypy)
|
||||
super(Python36Parser, self).custom_classfunc_rule(
|
||||
opname, token, customize, next_token, is_pypy
|
||||
)
|
||||
|
||||
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||
invalid = super(Python36Parser,
|
||||
self).reduce_is_invalid(rule, ast,
|
||||
tokens, first, last)
|
||||
invalid = super(Python36Parser, self).reduce_is_invalid(
|
||||
rule, ast, tokens, first, last
|
||||
)
|
||||
if invalid:
|
||||
return invalid
|
||||
if rule[0] == 'assign':
|
||||
if rule[0] == "assign":
|
||||
# Try to combine assignment + annotation into one statement
|
||||
if (len(tokens) >= last + 1 and
|
||||
tokens[last] == 'LOAD_NAME' and
|
||||
tokens[last+1] == 'STORE_ANNOTATION' and
|
||||
tokens[last-1].pattr == tokens[last+1].pattr):
|
||||
if (
|
||||
len(tokens) >= last + 1
|
||||
and tokens[last] == "LOAD_NAME"
|
||||
and tokens[last + 1] == "STORE_ANNOTATION"
|
||||
and tokens[last - 1].pattr == tokens[last + 1].pattr
|
||||
):
|
||||
# Will handle as ann_assign_init_value
|
||||
return True
|
||||
pass
|
||||
if rule[0] == 'call_kw':
|
||||
if rule[0] == "call_kw":
|
||||
# Make sure we don't derive call_kw
|
||||
nt = ast[0]
|
||||
while not isinstance(nt, Token):
|
||||
if nt[0] == 'call_kw':
|
||||
if nt[0] == "call_kw":
|
||||
return True
|
||||
nt = nt[0]
|
||||
pass
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
class Python36ParserSingle(Python36Parser, PythonParserSingle):
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Check grammar
|
||||
p = Python36Parser()
|
||||
p.check_grammar()
|
||||
from xdis.version_info import PYTHON_VERSION_TRIPLE, IS_PYPY
|
||||
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
|
||||
|
||||
if PYTHON_VERSION_TRIPLE[:2] == (3, 6):
|
||||
lhs, rhs, tokens, right_recursive, dup_rhs = p.check_sets()
|
||||
from uncompyle6.scanner import get_scanner
|
||||
|
||||
s = get_scanner(PYTHON_VERSION_TRIPLE, IS_PYPY)
|
||||
opcode_set = set(s.opc.opname).union(set(
|
||||
"""JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
|
||||
opcode_set = set(s.opc.opname).union(
|
||||
set(
|
||||
"""JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
|
||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_CLASSNAME
|
||||
LAMBDA_MARKER RETURN_LAST
|
||||
""".split()))
|
||||
""".split()
|
||||
)
|
||||
)
|
||||
remain_tokens = set(tokens) - opcode_set
|
||||
import re
|
||||
remain_tokens = set([re.sub(r'_\d+$', '', t) for t in remain_tokens])
|
||||
remain_tokens = set([re.sub('_CONT$', '', t) for t in remain_tokens])
|
||||
|
||||
remain_tokens = set([re.sub(r"_\d+$", "", t) for t in remain_tokens])
|
||||
remain_tokens = set([re.sub("_CONT$", "", t) for t in remain_tokens])
|
||||
remain_tokens = set(remain_tokens) - opcode_set
|
||||
print(remain_tokens)
|
||||
# print(sorted(p.rule2name.items()))
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2017-2020, 2022-2023 Rocky Bernstein
|
||||
# Copyright (c) 2017-2020, 2022-2024 Rocky Bernstein
|
||||
#
|
||||
# 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
|
||||
@@ -17,10 +17,12 @@ Python 3.7 grammar for the spark Earley-algorithm parser.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
from uncompyle6.scanners.tok import Token
|
||||
from uncompyle6.parser import PythonParserSingle, nop_func
|
||||
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||
|
||||
from uncompyle6.parser import PythonParserSingle, nop_func
|
||||
from uncompyle6.parsers.parse37base import Python37BaseParser
|
||||
from uncompyle6.scanners.tok import Token
|
||||
|
||||
|
||||
class Python37Parser(Python37BaseParser):
|
||||
def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG):
|
||||
@@ -249,8 +251,7 @@ class Python37Parser(Python37BaseParser):
|
||||
"""
|
||||
|
||||
def p_generator_exp(self, args):
|
||||
"""
|
||||
"""
|
||||
""" """
|
||||
|
||||
def p_jump(self, args):
|
||||
"""
|
||||
@@ -757,7 +758,7 @@ class Python37Parser(Python37BaseParser):
|
||||
"""
|
||||
|
||||
def p_dict_comp3(self, args):
|
||||
""""
|
||||
""" "
|
||||
expr ::= dict_comp
|
||||
stmt ::= dict_comp_func
|
||||
|
||||
@@ -1554,7 +1555,7 @@ class Python37Parser(Python37BaseParser):
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
|
||||
# Removes POP_BLOCK LOAD_CONST from 3.6-
|
||||
withasstmt ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
|
||||
with_as ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
"""
|
||||
if self.version < (3, 8):
|
||||
@@ -1575,7 +1576,6 @@ class Python37Parser(Python37BaseParser):
|
||||
pass
|
||||
|
||||
def custom_classfunc_rule(self, opname, token, customize, next_token):
|
||||
|
||||
args_pos, args_kw = self.get_pos_kw(token)
|
||||
|
||||
# Additional exprs for * and ** args:
|
||||
@@ -1718,6 +1718,7 @@ class Python37Parser(Python37BaseParser):
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def info(args):
|
||||
# Check grammar
|
||||
p = Python37Parser()
|
||||
@@ -1748,7 +1749,7 @@ if __name__ == "__main__":
|
||||
# FIXME: DRY this with other parseXX.py routines
|
||||
p = Python37Parser()
|
||||
p.check_grammar()
|
||||
from xdis.version_info import PYTHON_VERSION_TRIPLE, IS_PYPY
|
||||
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
|
||||
|
||||
if PYTHON_VERSION_TRIPLE[:2] == (3, 7):
|
||||
lhs, rhs, tokens, right_recursive, dup_rhs = p.check_sets()
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2016-2017, 2019-2020, 2022-2023 Rocky Bernstein
|
||||
# Copyright (c) 2016-2017, 2019-2020, 2022-2024 Rocky Bernstein
|
||||
"""
|
||||
Python 3.7 base code. We keep non-custom-generated grammar rules out of this file.
|
||||
"""
|
||||
@@ -1061,14 +1061,14 @@ class Python37BaseParser(PythonParser):
|
||||
elif opname == "SETUP_WITH":
|
||||
rules_str = """
|
||||
stmt ::= with
|
||||
stmt ::= withasstmt
|
||||
stmt ::= with_as
|
||||
|
||||
with ::= expr
|
||||
SETUP_WITH POP_TOP
|
||||
suite_stmts_opt
|
||||
COME_FROM_WITH
|
||||
with_suffix
|
||||
withasstmt ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
|
||||
with_as ::= expr SETUP_WITH store suite_stmts_opt COME_FROM_WITH
|
||||
with_suffix
|
||||
|
||||
with ::= expr
|
||||
@@ -1077,7 +1077,7 @@ class Python37BaseParser(PythonParser):
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
with_suffix
|
||||
|
||||
withasstmt ::= expr
|
||||
with_as ::= expr
|
||||
SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
with_suffix
|
||||
@@ -1086,7 +1086,7 @@ class Python37BaseParser(PythonParser):
|
||||
SETUP_WITH POP_TOP suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
with_suffix
|
||||
withasstmt ::= expr
|
||||
with_as ::= expr
|
||||
SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
with_suffix
|
||||
@@ -1104,17 +1104,18 @@ class Python37BaseParser(PythonParser):
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
with_suffix
|
||||
|
||||
withasstmt ::= expr
|
||||
SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
|
||||
withasstmt ::= expr
|
||||
SETUP_WITH store suite_stmts
|
||||
POP_BLOCK BEGIN_FINALLY COME_FROM_WITH with_suffix
|
||||
|
||||
with ::= expr SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK
|
||||
BEGIN_FINALLY COME_FROM_WITH
|
||||
with_suffix
|
||||
|
||||
with_as ::= expr
|
||||
SETUP_WITH store suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
|
||||
with_as ::= expr
|
||||
SETUP_WITH store suite_stmts
|
||||
POP_BLOCK BEGIN_FINALLY COME_FROM_WITH with_suffix
|
||||
"""
|
||||
self.addRule(rules_str, nop_func)
|
||||
|
||||
|
@@ -586,6 +586,15 @@ class Python38Parser(Python37Parser):
|
||||
GET_ITER CALL_FUNCTION_1
|
||||
"""
|
||||
self.addRule(rule, nop_func)
|
||||
elif opname == "SETUP_WITH":
|
||||
rules_str = """
|
||||
stmt ::= with_as_pass
|
||||
with_as_pass ::= expr
|
||||
SETUP_WITH store pass
|
||||
POP_BLOCK BEGIN_FINALLY COME_FROM_WITH
|
||||
with_suffix
|
||||
"""
|
||||
self.addRule(rules_str, nop_func)
|
||||
|
||||
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||
invalid = super(Python38Parser, self).reduce_is_invalid(
|
||||
|
@@ -272,8 +272,6 @@ TABLE_DIRECT = {
|
||||
(2, NO_PARENTHESIS_EVER)
|
||||
),
|
||||
|
||||
"IMPORT_FROM": ("%{pattr}",),
|
||||
"IMPORT_NAME_ATTR": ("%{pattr}",),
|
||||
"attribute": ("%c.%[1]{pattr}", (0, "expr")),
|
||||
"delete_subscript": (
|
||||
"%|del %p[%c]\n",
|
||||
@@ -380,6 +378,46 @@ TABLE_DIRECT = {
|
||||
(0, PRECEDENCE["named_expr"]-1)),
|
||||
"break": ("%|break\n",),
|
||||
"continue": ("%|continue\n",),
|
||||
|
||||
"except": ("%|except:\n%+%c%-", 3),
|
||||
"except_cond1": ("%|except %c:\n", 1),
|
||||
"except_cond2": ("%|except %c as %c:\n", (1, "expr"), (5, "store")),
|
||||
"except_suite": ("%+%c%-%C", 0, (1, maxint, "")),
|
||||
# In Python 3.6+, this is more complicated in the presence of "returns"
|
||||
"except_suite_finalize": ("%+%c%-%C", 1, (3, maxint, "")),
|
||||
|
||||
"expr_stmt": (
|
||||
"%|%p\n",
|
||||
# When a statement contains only a named_expr (:=)
|
||||
# the named_expr should have parenthesis around it.
|
||||
(0, "expr", PRECEDENCE["named_expr"] - 1)
|
||||
),
|
||||
|
||||
# Note: Python 3.8+ changes this
|
||||
"for": ("%|for %c in %c:\n%+%c%-\n\n", (3, "store"), (1, "expr"), (4, "for_block")),
|
||||
|
||||
"forelsestmt": (
|
||||
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
|
||||
(3, "store"),
|
||||
(1, "expr"),
|
||||
(4, "for_block"),
|
||||
-2,
|
||||
),
|
||||
"forelselaststmt": (
|
||||
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-",
|
||||
(3, "store"),
|
||||
(1, "expr"),
|
||||
(4, "for_block"),
|
||||
-2,
|
||||
),
|
||||
"forelselaststmtl": (
|
||||
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
|
||||
(3, "store"),
|
||||
(1, "expr"),
|
||||
(4, "for_block"),
|
||||
-2,
|
||||
),
|
||||
|
||||
"raise_stmt0": ("%|raise\n",),
|
||||
"raise_stmt1": ("%|raise %c\n", 0),
|
||||
"raise_stmt3": ("%|raise %c, %c, %c\n", 0, 1, 2),
|
||||
@@ -421,63 +459,7 @@ TABLE_DIRECT = {
|
||||
1,
|
||||
3,
|
||||
), # has COME_FROM
|
||||
"whileTruestmt": ("%|while True:\n%+%c%-\n\n", 1),
|
||||
"whilestmt": ("%|while %c:\n%+%c%-\n\n", 1, 2),
|
||||
"while1stmt": ("%|while 1:\n%+%c%-\n\n", 1),
|
||||
"while1elsestmt": ("%|while 1:\n%+%c%-%|else:\n%+%c%-\n\n", 1, -2),
|
||||
"whileelsestmt": ("%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n", 1, 2, -2),
|
||||
"whileelsestmt2": ("%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n", 1, 2, -3),
|
||||
"whileelselaststmt": ("%|while %c:\n%+%c%-%|else:\n%+%c%-", 1, 2, -2),
|
||||
|
||||
"expr_stmt": (
|
||||
"%|%p\n",
|
||||
# When a statement contains only a named_expr (:=)
|
||||
# the named_expr should have parenthesis around it.
|
||||
(0, "expr", PRECEDENCE["named_expr"] - 1)
|
||||
),
|
||||
|
||||
# Note: Python 3.8+ changes this
|
||||
"for": ("%|for %c in %c:\n%+%c%-\n\n", (3, "store"), (1, "expr"), (4, "for_block")),
|
||||
|
||||
"forelsestmt": (
|
||||
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
|
||||
(3, "store"),
|
||||
(1, "expr"),
|
||||
(4, "for_block"),
|
||||
-2,
|
||||
),
|
||||
"forelselaststmt": (
|
||||
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-",
|
||||
(3, "store"),
|
||||
(1, "expr"),
|
||||
(4, "for_block"),
|
||||
-2,
|
||||
),
|
||||
"forelselaststmtl": (
|
||||
"%|for %c in %c:\n%+%c%-%|else:\n%+%c%-\n\n",
|
||||
(3, "store"),
|
||||
(1, "expr"),
|
||||
(4, "for_block"),
|
||||
-2,
|
||||
),
|
||||
"try_except": ("%|try:\n%+%c%-%c\n\n", 1, 3),
|
||||
"tryelsestmt": ("%|try:\n%+%c%-%c%|else:\n%+%c%-\n\n", 1, 3, 4),
|
||||
"tryelsestmtc": ("%|try:\n%+%c%-%c%|else:\n%+%c%-", 1, 3, 4),
|
||||
"tryelsestmtl": ("%|try:\n%+%c%-%c%|else:\n%+%c%-", 1, 3, 4),
|
||||
# Note: this is generated generated by grammar rules but in this phase.
|
||||
"tf_try_except": ("%c%-%c%+", 1, 3),
|
||||
"tf_tryelsestmt": ("%c%-%c%|else:\n%+%c", 1, 3, 4),
|
||||
"tryfinallystmt": ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n", 1, 5),
|
||||
"except": ("%|except:\n%+%c%-", 3),
|
||||
"except_cond1": ("%|except %c:\n", 1),
|
||||
"except_cond2": ("%|except %c as %c:\n", (1, "expr"), (5, "store")),
|
||||
"except_suite": ("%+%c%-%C", 0, (1, maxint, "")),
|
||||
# In Python 3.6+, this is more complicated in the presence of "returns"
|
||||
"except_suite_finalize": ("%+%c%-%C", 1, (3, maxint, "")),
|
||||
"pass": ("%|pass\n",),
|
||||
"STORE_FAST": ("%{pattr}",),
|
||||
"kv": ("%c: %c", 3, 1),
|
||||
"kv2": ("%c: %c", 1, 2),
|
||||
"import": ("%|import %c\n", 2),
|
||||
"importlist": ("%C", (0, maxint, ", ")),
|
||||
|
||||
@@ -491,6 +473,36 @@ TABLE_DIRECT = {
|
||||
"import_from_star": (
|
||||
"%|from %[2]{pattr} import *\n",
|
||||
),
|
||||
|
||||
"kv": ("%c: %c", 3, 1),
|
||||
"kv2": ("%c: %c", 1, 2),
|
||||
"pass": ("%|pass\n",),
|
||||
|
||||
"whileTruestmt": ("%|while True:\n%+%c%-\n\n", 1),
|
||||
"whilestmt": ("%|while %c:\n%+%c%-\n\n", 1, 2),
|
||||
"while1stmt": ("%|while 1:\n%+%c%-\n\n", 1),
|
||||
"while1elsestmt": ("%|while 1:\n%+%c%-%|else:\n%+%c%-\n\n", 1, -2),
|
||||
"whileelsestmt": ("%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n", 1, 2, -2),
|
||||
"whileelsestmt2": ("%|while %c:\n%+%c%-%|else:\n%+%c%-\n\n", 1, 2, -3),
|
||||
"whileelselaststmt": ("%|while %c:\n%+%c%-%|else:\n%+%c%-", 1, 2, -2),
|
||||
|
||||
# If there are situations where we need "with ... as ()"
|
||||
# We may need to customize this in n_with_as
|
||||
"with_as": (
|
||||
"%|with %c as %c:\n%+%c%-",
|
||||
(0, "expr"),
|
||||
(2, "store"),
|
||||
(3, ("suite_stmts_opt", "suite_stmts")),
|
||||
),
|
||||
|
||||
"try_except": ("%|try:\n%+%c%-%c\n\n", 1, 3),
|
||||
"tryelsestmt": ("%|try:\n%+%c%-%c%|else:\n%+%c%-\n\n", 1, 3, 4),
|
||||
"tryelsestmtc": ("%|try:\n%+%c%-%c%|else:\n%+%c%-", 1, 3, 4),
|
||||
"tryelsestmtl": ("%|try:\n%+%c%-%c%|else:\n%+%c%-", 1, 3, 4),
|
||||
# Note: this is generated generated by grammar rules but in this phase.
|
||||
"tf_try_except": ("%c%-%c%+", 1, 3),
|
||||
"tf_tryelsestmt": ("%c%-%c%|else:\n%+%c", 1, 3, 4),
|
||||
"tryfinallystmt": ("%|try:\n%+%c%-%|finally:\n%+%c%-\n\n", 1, 5),
|
||||
}
|
||||
|
||||
|
||||
|
@@ -17,23 +17,24 @@
|
||||
|
||||
from uncompyle6.semantics.consts import TABLE_DIRECT
|
||||
|
||||
|
||||
#######################
|
||||
# Python 2.5+ Changes #
|
||||
#######################
|
||||
def customize_for_version25(self, version):
|
||||
|
||||
########################
|
||||
# Import style for 2.5+
|
||||
########################
|
||||
TABLE_DIRECT.update({
|
||||
'importmultiple': ( '%|import %c%c\n', 2, 3 ),
|
||||
'import_cont' : ( ', %c', 2 ),
|
||||
# With/as is allowed as "from future" thing in 2.5
|
||||
# Note: It is safe to put the variables after "as" in parenthesis,
|
||||
# and sometimes it is needed.
|
||||
'with': ( '%|with %c:\n%+%c%-', 0, 3),
|
||||
'withasstmt': ( '%|with %c as (%c):\n%+%c%-', 0, 2, 3),
|
||||
})
|
||||
TABLE_DIRECT.update(
|
||||
{
|
||||
"importmultiple": ("%|import %c%c\n", 2, 3),
|
||||
"import_cont": (", %c", 2),
|
||||
# With/as is allowed as "from future" thing in 2.5
|
||||
# Note: It is safe to put the variables after "as" in parenthesis,
|
||||
# and sometimes it is needed.
|
||||
"with": ("%|with %c:\n%+%c%-", 0, 3),
|
||||
}
|
||||
)
|
||||
|
||||
# In 2.5+ "except" handlers and the "finally" can appear in one
|
||||
# "try" statement. So the below has the effect of combining the
|
||||
@@ -41,16 +42,18 @@ def customize_for_version25(self, version):
|
||||
# FIXME: something doesn't smell right, since the semantics
|
||||
# are different. See test_fileio.py for an example that shows this.
|
||||
def tryfinallystmt(node):
|
||||
if len(node[1][0]) == 1 and node[1][0][0] == 'stmt':
|
||||
if node[1][0][0][0] == 'try_except':
|
||||
node[1][0][0][0].kind = 'tf_try_except'
|
||||
if node[1][0][0][0] == 'tryelsestmt':
|
||||
node[1][0][0][0].kind = 'tf_tryelsestmt'
|
||||
if len(node[1][0]) == 1 and node[1][0][0] == "stmt":
|
||||
if node[1][0][0][0] == "try_except":
|
||||
node[1][0][0][0].kind = "tf_try_except"
|
||||
if node[1][0][0][0] == "tryelsestmt":
|
||||
node[1][0][0][0].kind = "tf_tryelsestmt"
|
||||
self.default(node)
|
||||
|
||||
self.n_tryfinallystmt = tryfinallystmt
|
||||
|
||||
def n_import_from(node):
|
||||
if node[0].pattr > 0:
|
||||
node[2].pattr = ("." * node[0].pattr) + node[2].pattr
|
||||
self.default(node)
|
||||
|
||||
self.n_import_from = n_import_from
|
||||
|
@@ -51,7 +51,6 @@ def customize_for_version3(self, version):
|
||||
"tf_tryelsestmtl3": ("%c%-%c%|else:\n%+%c", 1, 3, 5),
|
||||
"store_locals": ("%|# inspect.currentframe().f_locals = __locals__\n",),
|
||||
"with": ("%|with %c:\n%+%c%-", 0, 3),
|
||||
"withasstmt": ("%|with %c as (%c):\n%+%c%-", 0, 2, 3),
|
||||
}
|
||||
)
|
||||
|
||||
|
@@ -128,6 +128,11 @@ def customize_for_version38(self, version):
|
||||
-2,
|
||||
),
|
||||
"ifpoplaststmtc": ("%|if %c:\n%+%c%-", (0, "testexpr"), (2, "l_stmts")),
|
||||
"named_expr": ( # AKA "walrus operator"
|
||||
"%c := %p",
|
||||
(2, "store"),
|
||||
(0, "expr", PRECEDENCE["named_expr"] - 1),
|
||||
),
|
||||
"pop_return": ("%|return %c\n", (1, "return_expr")),
|
||||
"popb_return": ("%|return %c\n", (0, "return_expr")),
|
||||
"pop_ex_return": ("%|return %c\n", (0, "return_expr")),
|
||||
@@ -222,10 +227,11 @@ def customize_for_version38(self, version):
|
||||
(2, "suite_stmts_opt"),
|
||||
(8, "suite_stmts_opt"),
|
||||
),
|
||||
"named_expr": ( # AKA "walrus operator"
|
||||
"%c := %p",
|
||||
"with_as_pass": (
|
||||
"%|with %c as %c:\n%+%c%-",
|
||||
(0, "expr"),
|
||||
(2, "store"),
|
||||
(0, "expr", PRECEDENCE["named_expr"] - 1),
|
||||
(3, "pass"),
|
||||
),
|
||||
}
|
||||
)
|
||||
|
Reference in New Issue
Block a user