Implemented mixing of declarations and statements in compound statements for C99.
-> In the Compound AST node, decls and stmts were removed and replaced by a single list named block_items (this is an API change!)
diff --git a/TODO.txt b/TODO.txt
index d1dbfb7..5395b5a 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -7,7 +7,7 @@
V- new keywords restrict, inline
- mix declarations and statements inside a block
-- VLAs (non-constants in arr[n]), including [*] for parameter lists
+V- VLAs (non-constants in arr[n]), including [*] for parameter lists
V- declarations in the first expression of "for" loops
- named initializers for structs and arrays
diff --git a/examples/cdecl.py b/examples/cdecl.py
index 1db5149..3df6efc 100644
--- a/examples/cdecl.py
+++ b/examples/cdecl.py
@@ -17,7 +17,7 @@
# =>
# ar is a pointer to array[10] of pointer to const Node
#
-# Copyright (C) 2008, Eli Bendersky
+# Copyright (C) 2008-2010, Eli Bendersky
# License: LGPL
#-----------------------------------------------------------------
import sys
diff --git a/examples/explore_ast.py b/examples/explore_ast.py
index 7daf15b..f845c0f 100644
--- a/examples/explore_ast.py
+++ b/examples/explore_ast.py
@@ -9,7 +9,7 @@
# information from the AST.
# It helps to have the _c_ast.yaml file in front of you.
#
-# Copyright (C) 2008, Eli Bendersky
+# Copyright (C) 2008-2010, Eli Bendersky
# License: LGPL
#-----------------------------------------------------------------
import sys
@@ -92,23 +92,19 @@
#
function_body = ast.ext[2].body
-# The following displays the variable declarations in the function
+# The following displays the declarations and statements in the function
# body
#
-#~ for decl in function_body.decls:
+#~ for decl in function_body.block_items:
#~ decl.show()
-# We can see a single variable, i, declared to be a simple type
-# declaration of type 'unsigned int'.
+# We can see a single variable declaration, i, declared to be a simple type
+# declaration of type 'unsigned int', followed by statements.
#
-# Let's look at the statemts now:
-#
-#~ for stmt in function_body.stmts:
- #~ stmt.show()
-# stmts is a list, so the second element is the For statement:
+# block_items is a list, so the third element is the For statement:
#
-for_stmt = function_body.stmts[1]
+for_stmt = function_body.block_items[2]
#~ for_stmt.show()
# As you can see in _c_ast.yaml, For's children are 'init, cond,
@@ -118,7 +114,7 @@
#
# Let's dig deeper, to the while statement inside the for loop:
#
-while_stmt = for_stmt.stmt.stmts[0]
+while_stmt = for_stmt.stmt.block_items[1]
#~ while_stmt.show()
# While is simpler, it only has a condition node and a stmt node.
diff --git a/pycparser/_c_ast.yaml b/pycparser/_c_ast.yaml
index 97fbca6..3e386ae 100644
--- a/pycparser/_c_ast.yaml
+++ b/pycparser/_c_ast.yaml
@@ -30,9 +30,10 @@
Cast: [to_type*, expr*]
-# Compound statement: { declarations... statements...}
+# Compound statement in C99 is a list of block items (declarations or
+# statements).
#
-Compound: [decls**, stmts**]
+Compound: [block_items**]
# type: int, char, float, etc. see CLexer for constant token types
#
diff --git a/pycparser/c_ast.py b/pycparser/c_ast.py
index 1dfe354..a3f6405 100644
--- a/pycparser/c_ast.py
+++ b/pycparser/c_ast.py
@@ -637,15 +637,13 @@
class Compound(Node):
- def __init__(self, decls, stmts, coord=None):
- self.decls = decls
- self.stmts = stmts
+ def __init__(self, block_items, coord=None):
+ self.block_items = block_items
self.coord = coord
def children(self):
nodelist = []
- if self.decls is not None: nodelist.extend(self.decls)
- if self.stmts is not None: nodelist.extend(self.stmts)
+ if self.block_items is not None: nodelist.extend(self.block_items)
return tuple(nodelist)
def show(self, buf=sys.stdout, offset=0, attrnames=False, showcoord=False):
diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py
index 2d0a8b6..d36f197 100644
--- a/pycparser/c_parser.py
+++ b/pycparser/c_parser.py
@@ -84,7 +84,7 @@
'init_declarator_list',
'parameter_type_list',
'specifier_qualifier_list',
- 'statement_list',
+ 'block_item_list',
'type_qualifier_list',
]
@@ -981,43 +981,29 @@
args=p[2],
type=c_ast.TypeDecl(None, None, None),
coord=self._coord(p.lineno(1)))
-
- def p_compound_statement_1(self, p):
- """ compound_statement : LBRACE statement_list_opt RBRACE """
- p[0] = c_ast.Compound(
- decls=None,
- stmts=p[2],
- coord=self._coord(p.lineno(1)))
- def p_compound_statement_2(self, p):
- """ compound_statement : LBRACE declaration_list RBRACE """
- p[0] = c_ast.Compound(
- decls=p[2],
- stmts=None,
- coord=self._coord(p.lineno(1)))
-
- def p_compound_statement_3(self, p):
- """ compound_statement : LBRACE declaration_list statement_list RBRACE """
- #~ print '(((((('
- #~ print p[2]
- #~ print p[3]
- #~ print '(((((('
- p[0] = c_ast.Compound(
- decls=p[2],
- stmts=p[3],
- coord=self._coord(p.lineno(1)))
-
- # Note: this doesn't create an AST node, but a list of AST
- # nodes that will be used as the statement list of a compound
+ # declaration is a list, statement isn't. To make it consistent, block_item
+ # will always be a list
#
- def p_statement_list(self, p):
- """ statement_list : statement
- | statement_list statement
+ def p_block_item(self, p):
+ """ block_item : declaration
+ | statement
"""
- if len(p) == 2: # single expr
- p[0] = [p[1]] if p[1] else []
- else:
- p[0] = p[1] + ([p[2]] if p[2] else [])
+ p[0] = p[1] if isinstance(p[1], list) else [p[1]]
+
+ # Since we made block_item a list, this just combines lists
+ #
+ def p_block_item_list(self, p):
+ """ block_item_list : block_item
+ | block_item_list block_item
+ """
+ p[0] = p[1] if len(p) == 2 else p[1] + p[2]
+
+ def p_compound_statement_1(self, p):
+ """ compound_statement : LBRACE block_item_list_opt RBRACE """
+ p[0] = c_ast.Compound(
+ block_items=p[2],
+ coord=self._coord(p.lineno(1)))
def p_labeled_statement_1(self, p):
""" labeled_statement : ID COLON statement """
diff --git a/tests/test_c_ast.py b/tests/test_c_ast.py
index d5794e1..2f1007f 100644
--- a/tests/test_c_ast.py
+++ b/tests/test_c_ast.py
@@ -71,8 +71,7 @@
right=c2)
comp = c_ast.Compound(
- decls=[b1, b2],
- stmts=[c1, c2])
+ block_items=[b1, b2, c1, c2])
cv = self.ConstantVisitor()
cv.visit(comp)
diff --git a/tests/test_c_parser.py b/tests/test_c_parser.py
index ef6f85c..532fe26 100644
--- a/tests/test_c_parser.py
+++ b/tests/test_c_parser.py
@@ -136,8 +136,8 @@
return 0;
}'''
f1_1 = self.parse(t1_1, filename='test.c')
- self.assert_coord(f1_1.ext[0].body.stmts[0], 3, 'test.c')
- self.assert_coord(f1_1.ext[0].body.stmts[1], 4, 'test.c')
+ self.assert_coord(f1_1.ext[0].body.block_items[0], 3, 'test.c')
+ self.assert_coord(f1_1.ext[0].body.block_items[1], 4, 'test.c')
t2 = """
#line 99
@@ -350,17 +350,17 @@
"""
compound = self.parse(e).ext[0].body
- s1 = compound.decls[0].init
+ s1 = compound.block_items[0].init
self.assertTrue(isinstance(s1, UnaryOp))
self.assertEqual(s1.op, 'sizeof')
self.assertTrue(isinstance(s1.expr, ID))
self.assertEqual(s1.expr.name, 'k')
- s2 = compound.decls[1].init
+ s2 = compound.block_items[1].init
self.assertEqual(expand_decl(s2.expr),
['Typename', ['TypeDecl', ['IdentifierType', ['int']]]])
- s3 = compound.decls[2].init
+ s3 = compound.block_items[2].init
self.assertEqual(expand_decl(s3.expr),
['Typename',
['PtrDecl',
@@ -791,8 +791,7 @@
[['Decl', 'p', ['TypeDecl', ['IdentifierType', ['int']]]]],
['TypeDecl', ['IdentifierType', ['int']]]]])
- self.assertEqual(type(f1.body.stmts[0]), Return)
- self.assertEqual(f1.body.decls, None)
+ self.assertEqual(type(f1.body.block_items[0]), Return)
f2 = parse_fdef('''
char* zzz(int p, char* c)
@@ -813,9 +812,8 @@
['TypeDecl', ['IdentifierType', ['char']]]]]],
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
- self.assertEqual(list(map(type, f2.body.stmts)),
- [Assignment, Return])
- self.assertEqual(len(f2.body.decls), 2)
+ self.assertEqual(list(map(type, f2.body.block_items)),
+ [Decl, Decl, Assignment, Return])
f3 = parse_fdef('''
char* zzz(p, c)
@@ -836,9 +834,8 @@
['ID', 'c']],
['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
- self.assertEqual(list(map(type, f3.body.stmts)),
- [Assignment, Return])
- self.assertEqual(len(f3.body.decls), 2)
+ self.assertEqual(list(map(type, f3.body.block_items)),
+ [Decl, Decl, Assignment, Return])
self.assertEqual(expand_decl(f3.param_decls[0]),
['Decl', 'p', ['TypeDecl', ['IdentifierType', ['long']]]])
@@ -866,7 +863,7 @@
''')
self.assertEqual(
- d3.ext[0].body.stmts[0].args.exprs[1].value,
+ d3.ext[0].body.block_items[0].args.exprs[1].value,
r'"Wrong Params?\nUsage:\n%s <binary_file_path>\n"')
d4 = self.get_decl_init('char* s = "" "foobar";')
@@ -889,8 +886,8 @@
int var2[*];
}
''')
- self.failUnless(isinstance(ps2.ext[0].body.decls[1].type.dim, Assignment))
- self.failUnless(isinstance(ps2.ext[0].body.decls[2].type.dim, ID))
+ self.failUnless(isinstance(ps2.ext[0].body.block_items[1].type.dim, Assignment))
+ self.failUnless(isinstance(ps2.ext[0].body.block_items[2].type.dim, ID))
class TestCParser_whole_code(unittest.TestCase):