diff options
author | dbluhm <bluhmdan@gmail.com> | 2018-03-03 06:18:26 -0700 |
---|---|---|
committer | Eli Bendersky <eliben@users.noreply.github.com> | 2018-03-03 05:18:26 -0800 |
commit | 7af2fb909b7cf4ee7cb1dd37bbe08d7e10f41349 (patch) | |
tree | 05b51a87056b0d02d73af3604f030e952a4cdb81 /pycparser/c_parser.py | |
parent | 4992410bf8c2d6d7eb94703d0f6f94b5a9acaa0a (diff) | |
download | pycparser-7af2fb909b7cf4ee7cb1dd37bbe08d7e10f41349.tar.gz |
Fix #235: Pragma displacing real statements (#236)
* Fix #235: Pragma displacing real statements
Diffstat (limited to 'pycparser/c_parser.py')
-rw-r--r-- | pycparser/c_parser.py | 73 |
1 files changed, 63 insertions, 10 deletions
diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py index f01f67f..47d958f 100644 --- a/pycparser/c_parser.py +++ b/pycparser/c_parser.py @@ -616,6 +616,59 @@ class CParser(PLYParser): """ p[0] = p[1] + # A pragma is generally considered a decorator rather than an actual statement. + # Still, for the purposes of analyzing an abstract syntax tree of C code, + # pragma's should not be ignored and were previously treated as a statement. + # This presents a problem for constructs that take a statement such as labeled_statements, + # selection_statements, and iteration_statements, causing a misleading structure + # in the AST. For example, consider the following C code. + # + # for (int i = 0; i < 3; i++) + # #pragma omp critical + # sum += 1; + # + # This code will compile and execute "sum += 1;" as the body of the for loop. + # Previous implementations of PyCParser would render the AST for this + # block of code as follows: + # + # For: + # DeclList: + # Decl: i, [], [], [] + # TypeDecl: i, [] + # IdentifierType: ['int'] + # Constant: int, 0 + # BinaryOp: < + # ID: i + # Constant: int, 3 + # UnaryOp: p++ + # ID: i + # Pragma: omp critical + # Assignment: += + # ID: sum + # Constant: int, 1 + # + # This AST misleadingly takes the Pragma as the body of the loop and the + # assignment then becomes a sibling of the loop. + # + # To solve edge cases like these, the pragmacomp_or_statement rule groups + # a pragma and its following statement (which would otherwise be orphaned) + # using a compound block, effectively turning the above code into: + # + # for (int i = 0; i < 3; i++) { + # #pragma omp critical + # sum += 1; + # } + def p_pragmacomp_or_statement(self, p): + """ pragmacomp_or_statement : pppragma_directive statement + | statement + """ + if isinstance(p[1], c_ast.Pragma) and len(p) == 3: + p[0] = c_ast.Compound( + block_items=[p[1], p[2]], + coord=self._token_coord(p, 1)) + else: + p[0] = p[1] + # In C, declarations can come several in a line: # int x, *px, romulo = 5; # @@ -1410,44 +1463,44 @@ class CParser(PLYParser): coord=self._token_coord(p, 1)) def p_labeled_statement_1(self, p): - """ labeled_statement : ID COLON statement """ + """ labeled_statement : ID COLON pragmacomp_or_statement """ p[0] = c_ast.Label(p[1], p[3], self._token_coord(p, 1)) def p_labeled_statement_2(self, p): - """ labeled_statement : CASE constant_expression COLON statement """ + """ labeled_statement : CASE constant_expression COLON pragmacomp_or_statement """ p[0] = c_ast.Case(p[2], [p[4]], self._token_coord(p, 1)) def p_labeled_statement_3(self, p): - """ labeled_statement : DEFAULT COLON statement """ + """ labeled_statement : DEFAULT COLON pragmacomp_or_statement """ p[0] = c_ast.Default([p[3]], self._token_coord(p, 1)) def p_selection_statement_1(self, p): - """ selection_statement : IF LPAREN expression RPAREN statement """ + """ selection_statement : IF LPAREN expression RPAREN pragmacomp_or_statement """ 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 statement """ + """ selection_statement : IF LPAREN expression RPAREN 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): - """ selection_statement : SWITCH LPAREN expression RPAREN statement """ + """ selection_statement : SWITCH LPAREN expression RPAREN pragmacomp_or_statement """ p[0] = fix_switch_cases( c_ast.Switch(p[3], p[5], self._token_coord(p, 1))) def p_iteration_statement_1(self, p): - """ iteration_statement : WHILE LPAREN expression RPAREN statement """ + """ iteration_statement : WHILE LPAREN expression RPAREN pragmacomp_or_statement """ p[0] = c_ast.While(p[3], p[5], self._token_coord(p, 1)) def p_iteration_statement_2(self, p): - """ iteration_statement : DO statement WHILE LPAREN expression RPAREN SEMI """ + """ 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)) def p_iteration_statement_3(self, p): - """ iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN statement """ + """ iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """ p[0] = c_ast.For(p[3], p[5], p[7], p[9], self._token_coord(p, 1)) def p_iteration_statement_4(self, p): - """ iteration_statement : FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN statement """ + """ iteration_statement : FOR LPAREN declaration expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """ p[0] = c_ast.For(c_ast.DeclList(p[3], self._token_coord(p, 1)), p[4], p[6], p[8], self._token_coord(p, 1)) |