Skip to content

Commit

Permalink
Fix eliben#478 (pragma-before-semicolon)
Browse files Browse the repository at this point in the history
  • Loading branch information
ldore committed Nov 14, 2022
1 parent f5ca028 commit 61c3230
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 13 deletions.
51 changes: 38 additions & 13 deletions pycparser/c_parser.py
Expand Up @@ -480,6 +480,13 @@ def _select_struct_union_class(self, token):
else:
return c_ast.Union

def _to_list(self, p):
""" Normalize the input to a list: if given a list, return as-is,
else return single-element list.
"""
return p if isinstance(p, list) else [p]


##
## Precedence and associativity of operators
##
Expand Down Expand Up @@ -682,10 +689,10 @@ def p_pragmacomp_or_statement(self, p):
"""
if len(p) == 3:
p[0] = c_ast.Compound(
block_items=p[1]+[p[2]],
block_items=p[1] + self._to_list(p[2]),
coord=self._token_coord(p, 1))
else:
p[0] = p[1]
p[0] = c_ast.Compound(block_items=p[1], coord=self._token_coord(p, 1)) if isinstance(p[1], list) else p[1]

# In C, declarations can come several in a line:
# int x, *px, romulo = 5;
Expand Down Expand Up @@ -1529,7 +1536,7 @@ def p_block_item(self, p):
""" block_item : declaration
| statement
"""
p[0] = p[1] if isinstance(p[1], list) else [p[1]]
p[0] = self._to_list(p[1])

# Since we made block_item a list, this just combines lists
#
Expand Down Expand Up @@ -1563,7 +1570,7 @@ def p_selection_statement_1(self, p):
p[0] = c_ast.If(p[3], p[5], None, self._token_coord(p, 1))

def p_selection_statement_2(self, p):
""" selection_statement : IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement """
""" selection_statement : IF LPAREN expression RPAREN pragmacomp_or_statement ELSE pragmacomp_or_statement """
p[0] = c_ast.If(p[3], p[5], p[7], self._token_coord(p, 1))

def p_selection_statement_3(self, p):
Expand All @@ -1576,8 +1583,10 @@ def p_iteration_statement_1(self, p):
p[0] = c_ast.While(p[3], p[5], self._token_coord(p, 1))

def p_iteration_statement_2(self, p):
""" iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI """
p[0] = c_ast.DoWhile(p[5], p[2], self._token_coord(p, 1))
""" iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI
| DO pragmacomp_or_statement WHILE LPAREN expression RPAREN pppragma_directive_list SEMI
"""
p[0] = c_ast.DoWhile(p[5], p[2], self._token_coord(p, 1)) if len(p) == 8 else [c_ast.DoWhile(p[5], p[2], self._token_coord(p, 1))] + p[7]

def p_iteration_statement_3(self, p):
""" iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """
Expand All @@ -1589,27 +1598,43 @@ def p_iteration_statement_4(self, p):
p[4], p[6], p[8], self._token_coord(p, 1))

def p_jump_statement_1(self, p):
""" jump_statement : GOTO ID SEMI """
p[0] = c_ast.Goto(p[2], self._token_coord(p, 1))
""" jump_statement : GOTO ID SEMI
| GOTO ID pppragma_directive_list SEMI
"""
p[0] = c_ast.Goto(p[2], self._token_coord(p, 1)) if len(p) == 4 else [c_ast.Goto(p[2], self._token_coord(p, 1))] + p[3]

def p_jump_statement_2(self, p):
""" jump_statement : BREAK SEMI """
p[0] = c_ast.Break(self._token_coord(p, 1))
""" jump_statement : BREAK SEMI
| BREAK pppragma_directive_list SEMI
"""
p[0] = c_ast.Break(self._token_coord(p, 1)) if len(p) == 3 else [c_ast.Break(self._token_coord(p, 1))] + p[2]

def p_jump_statement_3(self, p):
""" jump_statement : CONTINUE SEMI """
p[0] = c_ast.Continue(self._token_coord(p, 1))
""" jump_statement : CONTINUE SEMI
| CONTINUE pppragma_directive_list SEMI
"""
p[0] = c_ast.Continue(self._token_coord(p, 1)) if len(p) == 3 else [c_ast.Continue(self._token_coord(p, 1))] + p[2]

def p_jump_statement_4(self, p):
""" jump_statement : RETURN expression SEMI
| RETURN SEMI
"""
p[0] = c_ast.Return(p[2] if len(p) == 4 else None, self._token_coord(p, 1))

def p_jump_statement_5(self, p):
""" jump_statement : RETURN expression pppragma_directive_list SEMI
| RETURN pppragma_directive_list SEMI
"""
p[0] = [c_ast.Return(p[2] if len(p) == 5 else None, self._token_coord(p, 1))] + p[3 if len(p) == 5 else 2]

def p_expression_statement(self, p):
""" expression_statement : expression_opt SEMI """
""" expression_statement : expression_opt SEMI
| expression pppragma_directive_list SEMI
"""
if p[1] is None:
p[0] = c_ast.EmptyStatement(self._token_coord(p, 2))
elif len(p) == 4:
p[0] = [p[1]] + p[2]
else:
p[0] = p[1]

Expand Down
135 changes: 135 additions & 0 deletions tests/test_c_parser.py
Expand Up @@ -1810,6 +1810,141 @@ def test_pragmacomp_or_statement(self):
self.assertIsInstance(s1_ast.ext[0].body.block_items[5].stmt.stmts[0].block_items[1],
Assignment)

def test_pragma_in_statement(self):
s1 = r'''
void f(void) {
a = 1;
a = 1
#pragma foo
;
a = 1
#pragma bar
#pragma fuz
;
f();
f()
#pragma foo
;
f()
#pragma bar
#pragma fuz
;
do {
break
#pragma in_break
;
} while (1)
#pragma after_while
;
if (1)
a += 1
#pragma in_if
;
else
a +=2
#pragma in_else
;
return
#pragma after_return
;
return 1
#pragma after_return
;
goto target
#pragma after_goto
;
if (1)
a += 1
#pragma in_if_noelse
;
while (0)
a += 1
#pragma in_while
;
target:
return;
}
'''
s1_ast = self.parse(s1)
self.assertIsInstance(s1_ast.ext[0].body.block_items[2], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[2].string, 'foo')
self.assertEqual(s1_ast.ext[0].body.block_items[2].coord.line, 5)

self.assertIsInstance(s1_ast.ext[0].body.block_items[4], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[4].string, 'bar')
self.assertEqual(s1_ast.ext[0].body.block_items[4].coord.line, 8)

self.assertIsInstance(s1_ast.ext[0].body.block_items[5], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[5].string, 'fuz')
self.assertEqual(s1_ast.ext[0].body.block_items[5].coord.line, 9)

self.assertIsInstance(s1_ast.ext[0].body.block_items[8], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[8].string, 'foo')
self.assertEqual(s1_ast.ext[0].body.block_items[8].coord.line, 13)

self.assertIsInstance(s1_ast.ext[0].body.block_items[10], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[10].string, 'bar')
self.assertEqual(s1_ast.ext[0].body.block_items[10].coord.line, 16)

self.assertIsInstance(s1_ast.ext[0].body.block_items[11], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[11].string, 'fuz')
self.assertEqual(s1_ast.ext[0].body.block_items[11].coord.line, 17)

self.assertIsInstance(s1_ast.ext[0].body.block_items[12], DoWhile)
self.assertIsInstance(s1_ast.ext[0].body.block_items[12].stmt, Compound)
self.assertIsInstance(s1_ast.ext[0].body.block_items[12].stmt.block_items[1], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[12].stmt.block_items[1].string, 'in_break')
self.assertEqual(s1_ast.ext[0].body.block_items[12].stmt.block_items[1].coord.line, 22)

self.assertIsInstance(s1_ast.ext[0].body.block_items[13], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[13].string, 'after_while')
self.assertEqual(s1_ast.ext[0].body.block_items[13].coord.line, 25)

self.assertIsInstance(s1_ast.ext[0].body.block_items[14], If)
self.assertIsInstance(s1_ast.ext[0].body.block_items[14].iftrue, Compound)
self.assertIsInstance(s1_ast.ext[0].body.block_items[14].iftrue.block_items[1], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[14].iftrue.block_items[1].string, 'in_if')
self.assertEqual(s1_ast.ext[0].body.block_items[14].iftrue.block_items[1].coord.line, 30)
self.assertIsInstance(s1_ast.ext[0].body.block_items[14].iffalse, Compound)
self.assertIsInstance(s1_ast.ext[0].body.block_items[14].iffalse.block_items[1], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[14].iffalse.block_items[1].string, 'in_else')
self.assertEqual(s1_ast.ext[0].body.block_items[14].iffalse.block_items[1].coord.line, 34)

self.assertIsInstance(s1_ast.ext[0].body.block_items[15], Return)
self.assertIsInstance(s1_ast.ext[0].body.block_items[16], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[16].string, 'after_return')
self.assertEqual(s1_ast.ext[0].body.block_items[16].coord.line, 38)

self.assertIsInstance(s1_ast.ext[0].body.block_items[17], Return)
self.assertIsInstance(s1_ast.ext[0].body.block_items[18], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[18].string, 'after_return')
self.assertEqual(s1_ast.ext[0].body.block_items[18].coord.line, 41)

self.assertIsInstance(s1_ast.ext[0].body.block_items[19], Goto)
self.assertIsInstance(s1_ast.ext[0].body.block_items[20], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[20].string, 'after_goto')
self.assertEqual(s1_ast.ext[0].body.block_items[20].coord.line, 45)

self.assertIsInstance(s1_ast.ext[0].body.block_items[21], If)
self.assertIsInstance(s1_ast.ext[0].body.block_items[21].iftrue, Compound)
self.assertIsInstance(s1_ast.ext[0].body.block_items[21].iftrue.block_items[1], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[21].iftrue.block_items[1].string, 'in_if_noelse')
self.assertEqual(s1_ast.ext[0].body.block_items[21].iftrue.block_items[1].coord.line, 50)

self.assertIsInstance(s1_ast.ext[0].body.block_items[22], While)
self.assertIsInstance(s1_ast.ext[0].body.block_items[22].stmt, Compound)
self.assertIsInstance(s1_ast.ext[0].body.block_items[22].stmt.block_items[1], Pragma)
self.assertEqual(s1_ast.ext[0].body.block_items[22].stmt.block_items[1].string, 'in_while')
self.assertEqual(s1_ast.ext[0].body.block_items[22].stmt.block_items[1].coord.line, 55)



class TestCParser_whole_code(TestCParser_base):
""" Testing of parsing whole chunks of code.
Expand Down

0 comments on commit 61c3230

Please sign in to comment.