Fix parsing TYPEIDs in declarators (#169)
* Remove `init_declarator_list` workarounds
* Remove `struct_declaration` workaround
* Remove `declarator` pointer workaround
* Add `@parameterized` decorator for parser rules
* Rename `declarator` productions to `id_declarator` in preparation of adding `typeid_declarator`
* Use `id_declarator` in function definitions
* Add `typeid_declarator` and allow it as a `declarator`
* Create separate production for `type_specifier_no_typeid`
* Allow specifiers to be appended (useful for left-recursive lists)
* Change `specifier_qualifier_list` to be left-recursive and require at least one `type specifier`
* Change `declaration_specifiers` to require one `type_specifier` and disallow `typeid`s once we've seen a `type_specifier`
* Allow `decl_body` to omit a `type_specifier` if `init_declarator` doesn't start with a TYPEID
* Add `typeid_noparen_declarator` for use in `parameter_declaration`s
* Add test for multi-declarator declaration using a typedef name
* Move test into a more appropriate function and add another test
* Expand UnaryOp in `expand_init()`
* Add test for redefining name in the middle of a declaration
* Added info on the `append` parameter.
* Move rule template processing to a class constructor
* Auto-remove template methods and remove leading underscores
* Use xxx/yyy instead of XXX/YYY for better readability
* Add more documentation of the templating functions
* Add test for correct handling of ambiguity in parameter declarations
* Don't test incremental generation of declarators yet
diff --git a/tests/test_c_parser.py b/tests/test_c_parser.py
index 2bd43d1..95ae865 100755
--- a/tests/test_c_parser.py
+++ b/tests/test_c_parser.py
@@ -77,6 +77,8 @@
return ['Constant', init.type, init.value]
elif typ == ID:
return ['ID', init.name]
+ elif typ == UnaryOp:
+ return ['UnaryOp', init.op, expand_decl(init.expr)]
class TestCParser_base(unittest.TestCase):
@@ -1755,6 +1757,62 @@
'''
self.assertTrue(isinstance(self.parse(s2), FileAST))
+ def test_ambiguous_parameters(self):
+ # From ISO/IEC 9899:TC2, 6.7.5.3.11:
+ # "If, in a parameter declaration, an identifier can be treated either
+ # as a typedef name or as a parameter name, it shall be taken as a
+ # typedef name."
+
+ # foo takes an int named aa
+ # bar takes a function taking a TT
+ s1 = r'''
+ typedef char TT;
+ int foo(int (aa));
+ int bar(int (TT));
+ '''
+ s1_ast = self.parse(s1)
+ self.assertEqual(expand_decl(s1_ast.ext[1].type.args.params[0]),
+ ['Decl', 'aa', ['TypeDecl', ['IdentifierType', ['int']]]])
+ self.assertEqual(expand_decl(s1_ast.ext[2].type.args.params[0]),
+ ['Typename', ['FuncDecl',
+ [['Typename', ['TypeDecl', ['IdentifierType', ['TT']]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]])
+
+ # foo takes a function taking a char
+ # bar takes a function taking a function taking a char
+ s2 = r'''
+ typedef char TT;
+ int foo(int (aa (char)));
+ int bar(int (TT (char)));
+ '''
+ s2_ast = self.parse(s2)
+ self.assertEqual(expand_decl(s2_ast.ext[1].type.args.params[0]),
+ ['Decl', 'aa', ['FuncDecl',
+ [['Typename', ['TypeDecl', ['IdentifierType', ['char']]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]])
+ self.assertEqual(expand_decl(s2_ast.ext[2].type.args.params[0]),
+ ['Typename', ['FuncDecl',
+ [['Typename', ['FuncDecl',
+ [['Typename', ['TypeDecl', ['IdentifierType', ['char']]]]],
+ ['TypeDecl', ['IdentifierType', ['TT']]]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]])
+
+
+ # foo takes an int array named aa
+ # bar takes a function taking a TT array
+ s3 = r'''
+ typedef char TT;
+ int foo(int (aa[]));
+ int bar(int (TT[]));
+ '''
+ s3_ast = self.parse(s3)
+ self.assertEqual(expand_decl(s3_ast.ext[1].type.args.params[0]),
+ ['Decl', 'aa', ['ArrayDecl', '', [], ['TypeDecl', ['IdentifierType', ['int']]]]])
+ self.assertEqual(expand_decl(s3_ast.ext[2].type.args.params[0]),
+ ['Typename', ['FuncDecl',
+ [['Typename', ['ArrayDecl', '', [], ['TypeDecl', ['IdentifierType', ['TT']]]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]])
+
def test_innerscope_reuse_typedef_name(self):
# identifiers can be reused in inner scopes; the original should be
# restored at the end of the block
@@ -1823,6 +1881,55 @@
'''
self.assertRaises(ParseError, self.parse, s5)
+ # reusing a type name should work with multiple declarators
+ s6 = r'''
+ typedef char TT;
+ void foo(void) {
+ unsigned TT, uu;
+ }
+ '''
+ s6_ast = self.parse(s6)
+ items = s6_ast.ext[1].body.block_items
+ self.assertEqual(expand_decl(items[0]),
+ ['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['unsigned']]]])
+ self.assertEqual(expand_decl(items[1]),
+ ['Decl', 'uu', ['TypeDecl', ['IdentifierType', ['unsigned']]]])
+
+ # reusing a type name should work after a pointer
+ s7 = r'''
+ typedef char TT;
+ void foo(void) {
+ unsigned * TT;
+ }
+ '''
+ s7_ast = self.parse(s7)
+ items = s7_ast.ext[1].body.block_items
+ self.assertEqual(expand_decl(items[0]),
+ ['Decl', 'TT', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['unsigned']]]]])
+
+ # redefine a name in the middle of a multi-declarator declaration
+ s8 = r'''
+ typedef char TT;
+ void foo(void) {
+ int tt = sizeof(TT), TT, uu = sizeof(TT);
+ int uu = sizeof(tt);
+ }
+ '''
+ s8_ast = self.parse(s8)
+ items = s8_ast.ext[1].body.block_items
+ self.assertEqual(expand_decl(items[0]),
+ ['Decl', 'tt', ['TypeDecl', ['IdentifierType', ['int']]]])
+ self.assertEqual(expand_decl(items[1]),
+ ['Decl', 'TT', ['TypeDecl', ['IdentifierType', ['int']]]])
+ self.assertEqual(expand_decl(items[2]),
+ ['Decl', 'uu', ['TypeDecl', ['IdentifierType', ['int']]]])
+
+ # Don't test this until we have support for it
+ # self.assertEqual(expand_init(items[0].init),
+ # ['UnaryOp', 'sizeof', ['Typename', ['TypeDecl', ['IdentifierType', ['TT']]]]])
+ # self.assertEqual(expand_init(items[2].init),
+ # ['UnaryOp', 'sizeof', ['ID', 'TT']])
+
def test_parameter_reuse_typedef_name(self):
# identifiers can be reused as parameter names; parameter name scope
# begins and ends with the function body; it's important that TT is