summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Bendersky <eliben@gmail.com>2015-04-20 07:17:37 -0700
committerEli Bendersky <eliben@gmail.com>2015-04-20 07:17:37 -0700
commitfe5a248c301abacce186f5cf726e2186007451cf (patch)
tree02240a508ca8926ec2a49bb26eafb3ba91b7e303
parent8333e6c9610d1967ab6a60e8ed8ff6c2c0e8418e (diff)
downloadpycparser-fe5a248c301abacce186f5cf726e2186007451cf.tar.gz
Fix parsing order of nested PtrDecls
Closes #68
-rw-r--r--pycparser/c_parser.py40
-rw-r--r--tests/test_c_generator.py8
-rw-r--r--utils/internal/constptr.c9
3 files changed, 44 insertions, 13 deletions
diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py
index cd8fafb..2041d4f 100644
--- a/pycparser/c_parser.py
+++ b/pycparser/c_parser.py
@@ -211,13 +211,11 @@ class CParser(PLYParser):
# The basic declaration here is 'int c', and the pointer and
# the array are the modifiers.
#
- # Basic declarations are represented by TypeDecl (from module
- # c_ast) and the modifiers are FuncDecl, PtrDecl and
- # ArrayDecl.
+ # Basic declarations are represented by TypeDecl (from module c_ast) and the
+ # modifiers are FuncDecl, PtrDecl and ArrayDecl.
#
- # The standard states that whenever a new modifier is parsed,
- # it should be added to the end of the list of modifiers. For
- # example:
+ # The standard states that whenever a new modifier is parsed, it should be
+ # added to the end of the list of modifiers. For example:
#
# K&R2 A.8.6.2: Array Declarators
#
@@ -236,7 +234,6 @@ class CParser(PLYParser):
# useful for pointers, that can come as a chain from the rule
# p_pointer. In this case, the whole modifier list is spliced
# into the new location.
- #
def _type_modify_decl(self, decl, modifier):
""" Tacks a type modifier on a declarator, and returns
the modified declarator.
@@ -1061,11 +1058,30 @@ class CParser(PLYParser):
| TIMES type_qualifier_list_opt pointer
"""
coord = self._coord(p.lineno(1))
-
- p[0] = c_ast.PtrDecl(
- quals=p[2] or [],
- type=p[3] if len(p) > 3 else None,
- coord=coord)
+ # Pointer decls nest from inside out. This is important when different
+ # levels have different qualifiers. For example:
+ #
+ # char * const * p;
+ #
+ # Means "pointer to const pointer to char"
+ #
+ # While:
+ #
+ # char ** const p;
+ #
+ # Means "const pointer to pointer to char"
+ #
+ # So when we construct PtrDecl nestings, the leftmost pointer goes in
+ # as the most nested type.
+ nested_type = c_ast.PtrDecl(quals=p[2] or [], type=None, coord=coord)
+ if len(p) > 3:
+ tail_type = p[3]
+ while tail_type.type is not None:
+ tail_type = tail_type.type
+ tail_type.type = nested_type
+ p[0] = p[3]
+ else:
+ p[0] = nested_type
def p_type_qualifier_list(self, p):
""" type_qualifier_list : type_qualifier
diff --git a/tests/test_c_generator.py b/tests/test_c_generator.py
index e5fd9db..dd6e5ed 100644
--- a/tests/test_c_generator.py
+++ b/tests/test_c_generator.py
@@ -70,7 +70,8 @@ class TestCtoC(unittest.TestCase):
AST.
"""
src2 = self._run_c_to_c(src)
- self.assertTrue(compare_asts(parse_to_ast(src), parse_to_ast(src2)), src2)
+ self.assertTrue(compare_asts(parse_to_ast(src), parse_to_ast(src2)),
+ src2)
def test_trivial_decls(self):
self._assert_ctoc_correct('int a;')
@@ -81,6 +82,11 @@ class TestCtoC(unittest.TestCase):
self._assert_ctoc_correct('int** (*a)(void);')
self._assert_ctoc_correct('int** (*a)(void*, int);')
self._assert_ctoc_correct('int (*b)(char * restrict k, float);')
+ self._assert_ctoc_correct('int test(const char* const* arg);')
+ self._assert_ctoc_correct('int test(const char** const arg);')
+
+ #s = 'int test(const char* const* arg);'
+ #parse_to_ast(s).show()
def test_casts(self):
self._assert_ctoc_correct(r'''
diff --git a/utils/internal/constptr.c b/utils/internal/constptr.c
new file mode 100644
index 0000000..2fe14bf
--- /dev/null
+++ b/utils/internal/constptr.c
@@ -0,0 +1,9 @@
+void foo(char * const * arg) {
+ arg += 1;
+ (*arg) += 1;
+}
+
+void foo2(char ** const arg) {
+ arg += 1;
+ (*arg) += 1;
+}