Python 2 grammar restricion to match recent Python 3

This commit is contained in:
rocky
2017-12-14 14:54:40 -05:00
parent eb5706ee4b
commit 6c552bec07
2 changed files with 59 additions and 26 deletions

View File

@@ -223,8 +223,10 @@ class Python2Parser(PythonParser):
subclassing is, well, is pretty base. And we want it that way: lean and subclassing is, well, is pretty base. And we want it that way: lean and
mean so that parsing will go faster. mean so that parsing will go faster.
Here, we add additional rules based on specific instructions Here, we add additional grammar rules based on specific instructions
that are in the instruction/token stream. 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 For example if we see a pretty rare JUMP_IF_NOT_DEBUG
instruction we'll add the grammar for that. 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 Without custom rules, there can be an super-exponential number of
derivations. See the deparsing paper for an elaboration of derivations. See the deparsing paper for an elaboration of
this. this.
""" """
if 'PyPy' in customize: if 'PyPy' in customize:
@@ -259,14 +260,20 @@ class Python2Parser(PythonParser):
'LOAD', 'LOOKUP', 'MAKE', 'SETUP', 'LOAD', 'LOOKUP', 'MAKE', 'SETUP',
'RAISE', 'UNPACK')) '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): for i, token in enumerate(tokens):
opname = token.kind opname = token.kind
# FIXME # Do a quick breakout before testing potentially
if (opname[:opname.find('_')] # each of the dozen or so instruction in if elif.
not in customize_instruction_basenames): if (opname[:opname.find('_')] not in customize_instruction_basenames
or opname in custom_ops_seen):
# if opname not in customize:
continue continue
opname_base = opname[:opname.rfind('_')] opname_base = opname[:opname.rfind('_')]
@@ -347,6 +354,25 @@ class Python2Parser(PythonParser):
+ 'expr ' * nak + opname + 'expr ' * nak + opname
elif opname == 'CONTINUE_LOOP': elif opname == 'CONTINUE_LOOP':
self.addRule('continue ::= CONTINUE_LOOP', nop_func) 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 continue
elif opname_base in ('DUP_TOPX', 'RAISE_VARARGS'): elif opname_base in ('DUP_TOPX', 'RAISE_VARARGS'):
# FIXME: remove these conditions if they are not needed. # FIXME: remove these conditions if they are not needed.
@@ -375,20 +401,20 @@ class Python2Parser(PythonParser):
""", nop_func) """, nop_func)
continue continue
elif opname == 'LOAD_LISTCOMP': elif opname == 'LOAD_LISTCOMP':
self.add_unique_rules([ self.addRule("expr ::= listcomp", nop_func)
"expr ::= listcomp", custom_ops_seen.add(opname)
], customize)
continue continue
elif opname == 'LOAD_SETCOMP': elif opname == 'LOAD_SETCOMP':
self.add_unique_rules([ self.add_unique_rules([
"expr ::= set_comp", "expr ::= set_comp",
"set_comp ::= LOAD_SETCOMP MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1" "set_comp ::= LOAD_SETCOMP MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1"
], customize) ], customize)
custom_ops_seen.add(opname)
continue continue
elif opname == 'LOOKUP_METHOD': elif opname == 'LOOKUP_METHOD':
# A PyPy speciality - DRY with parse3 # A PyPy speciality - DRY with parse3
self.add_unique_rule("attribute ::= expr LOOKUP_METHOD", self.addRule("attribute ::= expr LOOKUP_METHOD", nop_func)
opname, token.attr, customize) custom_ops_seen.add(opname)
continue continue
elif opname_base == 'MAKE_FUNCTION': elif opname_base == 'MAKE_FUNCTION':
if i > 0 and tokens[i-1] == 'LOAD_LAMBDA': 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", "try_except_pypy ::= SETUP_EXCEPT suite_stmts_opt except_handler_pypy",
"except_handler_pypy ::= COME_FROM except_stmts END_FINALLY COME_FROM" "except_handler_pypy ::= COME_FROM except_stmts END_FINALLY COME_FROM"
], customize) ], customize)
custom_ops_seen.add(opname)
continue continue
elif opname == 'SETUP_FINALLY': elif opname == 'SETUP_FINALLY':
# FIXME: have a way here to detect PyPy. Right now we # FIXME: have a way here to detect PyPy. Right now we
# only have SETUP_EXCEPT customization for PyPy, but that might not # only have SETUP_EXCEPT customization for PyPy, but that might not
# always be the case. # always be the case.
self.add_unique_rules([ self.addRule("""
"stmt ::= tryfinallystmt_pypy", stmt ::= tryfinallystmt_pypy
"tryfinallystmt_pypy ::= SETUP_FINALLY suite_stmts_opt COME_FROM_FINALLY " tryfinallystmt_pypy ::= SETUP_FINALLY suite_stmts_opt COME_FROM_FINALLY
"suite_stmts_opt END_FINALLY" suite_stmts_opt END_FINALLY""", nop_func)
], customize)
custom_ops_seen.add(opname)
continue continue
elif opname_base in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'): elif opname_base in ('UNPACK_TUPLE', 'UNPACK_SEQUENCE'):
custom_ops_seen.add(opname)
rule = 'unpack ::= ' + opname + ' store' * token.attr rule = 'unpack ::= ' + opname + ' store' * token.attr
elif opname_base == 'UNPACK_LIST': elif opname_base == 'UNPACK_LIST':
custom_ops_seen.add(opname)
rule = 'unpack_list ::= ' + opname + ' store' * token.attr rule = 'unpack_list ::= ' + opname + ' store' * token.attr
else: else:
continue continue

View File

@@ -93,7 +93,6 @@ class Python3Parser(PythonParser):
stmt ::= continue stmt ::= continue
continue ::= CONTINUE continue ::= CONTINUE
continue ::= CONTINUE_LOOP
continues ::= _stmts lastl_stmt continue continues ::= _stmts lastl_stmt continue
continues ::= lastl_stmt continue continues ::= lastl_stmt continue
continues ::= continue continues ::= continue
@@ -539,8 +538,10 @@ class Python3Parser(PythonParser):
subclassing is, well, is pretty base. And we want it that way: lean and subclassing is, well, is pretty base. And we want it that way: lean and
mean so that parsing will go faster. mean so that parsing will go faster.
Here, we add additional rules based on specific instructions Here, we add additional grammra rules based on specific instructions
that are in the instruction/token stream. 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 For example if we see a pretty rare DELETE_DEREF instruction we'll
add the grammar for that. add the grammar for that.
@@ -552,6 +553,7 @@ class Python3Parser(PythonParser):
Without custom rules, there can be an super-exponential number of Without custom rules, there can be an super-exponential number of
derivations. See the deparsing paper for an elaboration of derivations. See the deparsing paper for an elaboration of
this. this.
""" """
is_pypy = False is_pypy = False
@@ -560,8 +562,8 @@ class Python3Parser(PythonParser):
# include instructions that don't need customization, # include instructions that don't need customization,
# but we'll do a finer check after the rough breakout. # but we'll do a finer check after the rough breakout.
customize_instruction_basenames = frozenset( customize_instruction_basenames = frozenset(
('BUILD', 'CALL', 'DELETE', ('BUILD', 'CALL', 'CONTINUE', 'DELETE',
'JUMP', 'LOAD', 'LOOKUP', 'MAKE', 'JUMP', 'LOAD', 'LOOKUP', 'MAKE',
'RAISE', 'UNPACK')) 'RAISE', 'UNPACK'))
# Opcode names in the custom_ops_seen set have rules that get added # Opcode names in the custom_ops_seen set have rules that get added
@@ -731,10 +733,11 @@ class Python3Parser(PythonParser):
('kwarg ' * args_kw) + ('kwarg ' * args_kw) +
'expr ' * nak + opname) 'expr ' * nak + opname)
self.add_unique_rule(rule, opname, token.attr, customize) 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': elif opname == 'DELETE_ATTR':
self.addRule(""" self.addRule('del_stmt ::= expr DELETE_ATTR', nop_func)
del_stmt ::= expr DELETE_ATTR
""", nop_func)
custom_ops_seen.add(opname) custom_ops_seen.add(opname)
elif opname == 'DELETE_DEREF': elif opname == 'DELETE_DEREF':
self.addRule(""" self.addRule("""