diff options
author | Eli Bendersky <eliben@gmail.com> | 2015-04-20 07:17:37 -0700 |
---|---|---|
committer | Eli Bendersky <eliben@gmail.com> | 2015-04-20 07:17:37 -0700 |
commit | fe5a248c301abacce186f5cf726e2186007451cf (patch) | |
tree | 02240a508ca8926ec2a49bb26eafb3ba91b7e303 | |
parent | 8333e6c9610d1967ab6a60e8ed8ff6c2c0e8418e (diff) | |
download | pycparser-fe5a248c301abacce186f5cf726e2186007451cf.tar.gz |
Fix parsing order of nested PtrDecls
Closes #68
-rw-r--r-- | pycparser/c_parser.py | 40 | ||||
-rw-r--r-- | tests/test_c_generator.py | 8 | ||||
-rw-r--r-- | utils/internal/constptr.c | 9 |
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; +} |