2.6, 2.7 Parse if else inside list comprehension

Fixes #171
This commit is contained in:
rocky
2018-04-28 20:44:09 -04:00
parent aab951280b
commit 269f4f2e1b
7 changed files with 34 additions and 4 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
# Bug from issue #171: parsing "if x if a else y" inside a list comprehension on 2.7
# This is RUNNABLE!
assert [False, True, True, True, True] == [False if not a else True for a in range(5)]
assert [True, False, False, False, False] == [False if a else True for a in range(5)]

View File

@@ -264,6 +264,9 @@ class Python26Parser(Python2Parser):
dict ::= BUILD_MAP kvlist
kvlist ::= kvlist kv3
expr ::= conditional_not
conditional_not ::= expr jmp_true expr _jump COME_FROM POP_TOP expr COME_FROM
conditional ::= expr jmp_false expr jf_cf_pop expr come_from_opt
and ::= expr JUMP_IF_FALSE POP_TOP expr JUMP_IF_FALSE POP_TOP

View File

@@ -165,6 +165,9 @@ class Python27Parser(Python2Parser):
::= expr jmp_true expr return_if_lambda
return_stmt_lambda LAMBDA_MARKER
expr ::= conditional_not
conditional_not ::= expr jmp_true expr _jump expr COME_FROM
kv3 ::= expr expr STORE_MAP
"""
@@ -172,12 +175,17 @@ class Python27Parser(Python2Parser):
# 2.7 changes COME_FROM to COME_FROM_FINALLY
self.remove_rules("""
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suite COME_FROM
tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM suite_stmts_opt END_FINALLY
tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt
POP_BLOCK LOAD_CONST COME_FROM suite_stmts_opt
END_FINALLY
""")
super(Python27Parser, self).customize_grammar_rules(tokens, customize)
self.check_reduce['and'] = 'AST'
# self.check_reduce['or'] = 'AST'
self.check_reduce['raise_stmt1'] = 'AST'
# self.check_reduce['conditional_true'] = 'AST'
self.check_reduce['list_if_not'] = 'AST'
self.check_reduce['list_if'] = 'AST'
self.check_reduce['conditional_true'] = 'AST'
return
def reduce_is_invalid(self, rule, ast, tokens, first, last):
@@ -195,6 +203,21 @@ class Python27Parser(Python2Parser):
tokens[last].pattr == jmp_false.pattr)
elif rule[0] == ('raise_stmt1'):
return ast[0] == 'expr' and ast[0][0] == 'or'
elif rule == ('list_if_not', ('expr', 'jmp_true', 'list_iter')):
jump_inst = ast[1][0]
jump_offset = jump_inst.attr
return jump_offset > jump_inst.offset and jump_offset < tokens[last].offset
elif rule == ('list_if', ('expr', 'jmp_false', 'list_iter')):
jump_inst = ast[1][0]
jump_offset = jump_inst.attr
return jump_offset > jump_inst.offset and jump_offset < tokens[last].offset
elif rule == ('or', ('expr', 'jmp_true', 'expr', '\\e_come_from_opt')):
# Test that jmp_true doesn't jump inside the middle the "or"
# or that it jumps to the same place as the end of "and"
jmp_true = ast[1][0]
jmp_target = jmp_true.offset + jmp_true.attr + 3
return not (jmp_target == tokens[last].offset or
tokens[last].pattr == jmp_true.pattr)
# elif rule[0] == ('conditional_true'):
# # FIXME: the below is a hack: we check expr for
# # nodes that could have possibly been a been a Boolean.

View File

@@ -207,7 +207,7 @@ TABLE_DIRECT = {
'conditional': ( '%p if %p else %p', (2, 27), (0, 27), (4, 27) ),
'conditional_true': ( '%p if 1 else %p', (0, 27), (2, 27) ),
'ret_cond': ( '%p if %p else %p', (2, 27), (0, 27), (-1, 27) ),
'conditionalnot': ( '%p if not %p else %p', (2, 27), (0, 22), (4, 27) ),
'conditional_not': ( '%p if not %p else %p', (2, 27), (0, 22), (4, 27) ),
'ret_cond_not': ( '%p if not %p else %p', (2, 27), (0, 22), (-1, 27) ),
'conditional_lambda':
( '%c if %c else %c',

View File

@@ -784,7 +784,7 @@ class SourceWalker(GenericASTTraversal, object):
def n_list_comp(self, node):
"""List comprehensions"""
p = self.prec
self.prec = 27
self.prec = 100
if self.version >= 2.7:
if self.is_pypy:
self.n_list_comp_pypy27(node)