You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Python 2 grammar restricion to match recent Python 3
This commit is contained in:
@@ -223,8 +223,10 @@ class Python2Parser(PythonParser):
|
||||
subclassing is, well, is pretty base. And we want it that way: lean and
|
||||
mean so that parsing will go faster.
|
||||
|
||||
Here, we add additional rules based on specific instructions
|
||||
that are in the instruction/token stream.
|
||||
Here, we add additional grammar rules based on specific instructions
|
||||
that are in the instruction/token stream. In classes that
|
||||
inherit from from here and other versions, grammar rules may
|
||||
also be removed.
|
||||
|
||||
For example if we see a pretty rare JUMP_IF_NOT_DEBUG
|
||||
instruction we'll add the grammar for that.
|
||||
@@ -236,7 +238,6 @@ class Python2Parser(PythonParser):
|
||||
Without custom rules, there can be an super-exponential number of
|
||||
derivations. See the deparsing paper for an elaboration of
|
||||
this.
|
||||
|
||||
"""
|
||||
|
||||
if 'PyPy' in customize:
|
||||
@@ -259,14 +260,20 @@ class Python2Parser(PythonParser):
|
||||
'LOAD', 'LOOKUP', 'MAKE', 'SETUP',
|
||||
'RAISE', 'UNPACK'))
|
||||
|
||||
# Opcode names in the custom_ops_seen set have rules that get added
|
||||
# unconditionally and the rules are constant. So they need to be done
|
||||
# only once and if we see the opcode a second we don't have to consider
|
||||
# adding more rules.
|
||||
#
|
||||
custom_ops_seen = set()
|
||||
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.kind
|
||||
|
||||
# FIXME
|
||||
if (opname[:opname.find('_')]
|
||||
not in customize_instruction_basenames):
|
||||
|
||||
# if opname not in customize:
|
||||
# Do a quick breakout before testing potentially
|
||||
# each of the dozen or so instruction in if elif.
|
||||
if (opname[:opname.find('_')] not in customize_instruction_basenames
|
||||
or opname in custom_ops_seen):
|
||||
continue
|
||||
|
||||
opname_base = opname[:opname.rfind('_')]
|
||||
@@ -347,6 +354,25 @@ class Python2Parser(PythonParser):
|
||||
+ 'expr ' * nak + opname
|
||||
elif opname == 'CONTINUE_LOOP':
|
||||
self.addRule('continue ::= CONTINUE_LOOP', nop_func)
|
||||
custom_ops_seen.add(opname)
|
||||
continue
|
||||
elif opname == 'DELETE_ATTR':
|
||||
self.addRule('del_stmt ::= expr DELETE_ATTR', nop_func)
|
||||
custom_ops_seen.add(opname)
|
||||
continue
|
||||
elif opname == 'DELETE_DEREF':
|
||||
self.addRule("""
|
||||
stmt ::= del_deref_stmt
|
||||
del_deref_stmt ::= DELETE_DEREF
|
||||
""", nop_func)
|
||||
custom_ops_seen.add(opname)
|
||||
continue
|
||||
elif opname == 'DELETE_SUBSCR':
|
||||
self.addRule("""
|
||||
del_stmt ::= delete_subscr
|
||||
delete_subscr ::= expr expr DELETE_SUBSCR
|
||||
""", nop_func)
|
||||
custom_ops_seen.add(opname)
|
||||
continue
|
||||
elif opname_base in ('DUP_TOPX', 'RAISE_VARARGS'):
|
||||
# FIXME: remove these conditions if they are not needed.
|
||||
@@ -375,20 +401,20 @@ class Python2Parser(PythonParser):
|
||||
""", nop_func)
|
||||
continue
|
||||
elif opname == 'LOAD_LISTCOMP':
|
||||
self.add_unique_rules([
|
||||
"expr ::= listcomp",
|
||||
], customize)
|
||||
self.addRule("expr ::= listcomp", nop_func)
|
||||
custom_ops_seen.add(opname)
|
||||
continue
|
||||
elif opname == 'LOAD_SETCOMP':
|
||||
self.add_unique_rules([
|
||||
"expr ::= set_comp",
|
||||
"set_comp ::= LOAD_SETCOMP MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1"
|
||||
], customize)
|
||||
custom_ops_seen.add(opname)
|
||||
continue
|
||||
elif opname == 'LOOKUP_METHOD':
|
||||
# A PyPy speciality - DRY with parse3
|
||||
self.add_unique_rule("attribute ::= expr LOOKUP_METHOD",
|
||||
opname, token.attr, customize)
|
||||
self.addRule("attribute ::= expr LOOKUP_METHOD", nop_func)
|
||||
custom_ops_seen.add(opname)
|
||||
continue
|
||||
elif opname_base == 'MAKE_FUNCTION':
|
||||
if i > 0 and tokens[i-1] == 'LOAD_LAMBDA':
|
||||
@@ -439,20 +465,24 @@ class Python2Parser(PythonParser):
|
||||
"try_except_pypy ::= SETUP_EXCEPT suite_stmts_opt except_handler_pypy",
|
||||
"except_handler_pypy ::= COME_FROM except_stmts END_FINALLY COME_FROM"
|
||||
], customize)
|
||||
custom_ops_seen.add(opname)
|
||||
continue
|
||||
elif opname == 'SETUP_FINALLY':
|
||||
# FIXME: have a way here to detect PyPy. Right now we
|
||||
# only have SETUP_EXCEPT customization for PyPy, but that might not
|
||||
# always be the case.
|
||||
self.add_unique_rules([
|
||||
"stmt ::= tryfinallystmt_pypy",
|
||||
"tryfinallystmt_pypy ::= SETUP_FINALLY suite_stmts_opt COME_FROM_FINALLY "
|
||||
"suite_stmts_opt END_FINALLY"
|
||||
], customize)
|
||||
self.addRule("""
|
||||
stmt ::= tryfinallystmt_pypy
|
||||
tryfinallystmt_pypy ::= SETUP_FINALLY suite_stmts_opt COME_FROM_FINALLY
|
||||
suite_stmts_opt END_FINALLY""", nop_func)
|
||||
|
||||
custom_ops_seen.add(opname)
|
||||
continue
|
||||
elif opname_base in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
|
||||
custom_ops_seen.add(opname)
|
||||
rule = 'unpack ::= ' + opname + ' store' * token.attr
|
||||
elif opname_base == 'UNPACK_LIST':
|
||||
custom_ops_seen.add(opname)
|
||||
rule = 'unpack_list ::= ' + opname + ' store' * token.attr
|
||||
else:
|
||||
continue
|
||||
|
@@ -93,7 +93,6 @@ class Python3Parser(PythonParser):
|
||||
|
||||
stmt ::= continue
|
||||
continue ::= CONTINUE
|
||||
continue ::= CONTINUE_LOOP
|
||||
continues ::= _stmts lastl_stmt continue
|
||||
continues ::= lastl_stmt continue
|
||||
continues ::= continue
|
||||
@@ -539,8 +538,10 @@ class Python3Parser(PythonParser):
|
||||
subclassing is, well, is pretty base. And we want it that way: lean and
|
||||
mean so that parsing will go faster.
|
||||
|
||||
Here, we add additional rules based on specific instructions
|
||||
that are in the instruction/token stream.
|
||||
Here, we add additional grammra rules based on specific instructions
|
||||
that are in the instruction/token stream. In classes that
|
||||
inherit from from here and other versions, grammar rules may
|
||||
also be removed.
|
||||
|
||||
For example if we see a pretty rare DELETE_DEREF instruction we'll
|
||||
add the grammar for that.
|
||||
@@ -552,6 +553,7 @@ class Python3Parser(PythonParser):
|
||||
Without custom rules, there can be an super-exponential number of
|
||||
derivations. See the deparsing paper for an elaboration of
|
||||
this.
|
||||
|
||||
"""
|
||||
|
||||
is_pypy = False
|
||||
@@ -560,8 +562,8 @@ class Python3Parser(PythonParser):
|
||||
# include instructions that don't need customization,
|
||||
# but we'll do a finer check after the rough breakout.
|
||||
customize_instruction_basenames = frozenset(
|
||||
('BUILD', 'CALL', 'DELETE',
|
||||
'JUMP', 'LOAD', 'LOOKUP', 'MAKE',
|
||||
('BUILD', 'CALL', 'CONTINUE', 'DELETE',
|
||||
'JUMP', 'LOAD', 'LOOKUP', 'MAKE',
|
||||
'RAISE', 'UNPACK'))
|
||||
|
||||
# Opcode names in the custom_ops_seen set have rules that get added
|
||||
@@ -731,10 +733,11 @@ class Python3Parser(PythonParser):
|
||||
('kwarg ' * args_kw) +
|
||||
'expr ' * nak + opname)
|
||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||
elif opname == 'CONTINUE_LOOP':
|
||||
self.addRule('continue ::= CONTINUE_LOOP', nop_func)
|
||||
custom_ops_seen.add(opname)
|
||||
elif opname == 'DELETE_ATTR':
|
||||
self.addRule("""
|
||||
del_stmt ::= expr DELETE_ATTR
|
||||
""", nop_func)
|
||||
self.addRule('del_stmt ::= expr DELETE_ATTR', nop_func)
|
||||
custom_ops_seen.add(opname)
|
||||
elif opname == 'DELETE_DEREF':
|
||||
self.addRule("""
|
||||
|
Reference in New Issue
Block a user