diff options
author | Zecong Hu <huzecong@gmail.com> | 2020-07-18 08:48:31 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-18 05:48:31 -0700 |
commit | 6da5009936d0c220b14f8c70deb0a2c7f82ac922 (patch) | |
tree | af9739246eab5a361de9d0dc3bdb49635690e82b | |
parent | 61eac63fda12f9ba4504def70d0d76cd3ac1bbff (diff) | |
download | pycparser-6da5009936d0c220b14f8c70deb0a2c7f82ac922.tar.gz |
Fix issues #378, #379, #385 (#387)
* Fix #385: generate code with nested sizeofs
* Fix #378: replace assertion with check
Only the assertion inside `_build_function_definition` is replaced. The
assertion is not appropriate because there are possible inputs that
would trigger the assertion, they're just grammatically incorrect. Thus,
it is replaced with a check that raises `ParseError` on failure.
* Fix #379: parse struct with nested enum
-rw-r--r-- | pycparser/c_generator.py | 15 | ||||
-rw-r--r-- | pycparser/c_parser.py | 12 | ||||
-rw-r--r-- | tests/test_c_generator.py | 7 | ||||
-rwxr-xr-x | tests/test_c_parser.py | 39 |
4 files changed, 61 insertions, 12 deletions
diff --git a/pycparser/c_generator.py b/pycparser/c_generator.py index 973d24a..53c26fd 100644 --- a/pycparser/c_generator.py +++ b/pycparser/c_generator.py @@ -59,17 +59,18 @@ class CGenerator(object): return fref + '(' + self.visit(n.args) + ')' def visit_UnaryOp(self, n): - operand = self._parenthesize_unless_simple(n.expr) - if n.op == 'p++': - return '%s++' % operand - elif n.op == 'p--': - return '%s--' % operand - elif n.op == 'sizeof': + if n.op == 'sizeof': # Always parenthesize the argument of sizeof since it can be # a name. return 'sizeof(%s)' % self.visit(n.expr) else: - return '%s%s' % (n.op, operand) + operand = self._parenthesize_unless_simple(n.expr) + if n.op == 'p++': + return '%s++' % operand + elif n.op == 'p--': + return '%s--' % operand + else: + return '%s%s' % (n.op, operand) def visit_BinaryOp(self, n): lval_str = self._parenthesize_if(n.left, diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py index 744ede8..c2d82f7 100644 --- a/pycparser/c_parser.py +++ b/pycparser/c_parser.py @@ -413,8 +413,8 @@ class CParser(PLYParser): # A similar problem can occur where the declaration ends up looking # like an abstract declarator. Give it a name if this is the case. # - elif not isinstance(decls[0]['decl'], - (c_ast.Struct, c_ast.Union, c_ast.IdentifierType)): + elif not isinstance(decls[0]['decl'], ( + c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType)): decls_0_tail = decls[0]['decl'] while not isinstance(decls_0_tail, c_ast.TypeDecl): decls_0_tail = decls_0_tail.type @@ -442,8 +442,9 @@ class CParser(PLYParser): bitsize=decl.get('bitsize'), coord=decl['decl'].coord) - if isinstance(declaration.type, - (c_ast.Struct, c_ast.Union, c_ast.IdentifierType)): + if isinstance(declaration.type, ( + c_ast.Enum, c_ast.Struct, c_ast.Union, + c_ast.IdentifierType)): fixed_decl = declaration else: fixed_decl = self._fix_decl_name_type(declaration, spec['type']) @@ -464,7 +465,8 @@ class CParser(PLYParser): def _build_function_definition(self, spec, decl, param_decls, body): """ Builds a function definition. """ - assert 'typedef' not in spec['storage'] + if 'typedef' in spec['storage']: + self._parse_error("Invalid typedef", decl.coord) declaration = self._build_declarations( spec=spec, diff --git a/tests/test_c_generator.py b/tests/test_c_generator.py index dd19a11..159c763 100644 --- a/tests/test_c_generator.py +++ b/tests/test_c_generator.py @@ -354,6 +354,13 @@ class TestCtoC(unittest.TestCase): self.assertEqual(generator.visit(ast.ext[0].type.type.type), 'const int') + def test_nested_sizeof(self): + src = '1' + for _ in range(30): + src = 'sizeof(' + src + ')' + src = 'int x = ' + src + ';' + self._assert_ctoc_correct(src) + class TestCasttoC(unittest.TestCase): def _find_file(self, name): diff --git a/tests/test_c_parser.py b/tests/test_c_parser.py index b6ecdd5..d33941c 100755 --- a/tests/test_c_parser.py +++ b/tests/test_c_parser.py @@ -33,6 +33,13 @@ def expand_decl(decl): elif typ in [Struct, Union]: decls = [expand_decl(d) for d in decl.decls or []] return [typ.__name__, decl.name, decls] + elif typ == Enum: + if decl.values is None: + values = None + else: + assert isinstance(decl.values, EnumeratorList) + values = [enum.name for enum in decl.values.enumerators] + return ['Enum', decl.name, values] else: nested = expand_decl(decl.type) @@ -855,6 +862,31 @@ class TestCParser_fundamentals(TestCParser_base): ['Decl', 'heads', ['PtrDecl', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['Node']]]]]]]]]]) + def test_struct_enum(self): + s1 = """ + struct Foo { + enum Bar { A = 1 }; + }; + """ + self.assertEqual(expand_decl(self.parse(s1).ext[0]), + ['Decl', None, + ['Struct', 'Foo', + [['Decl', None, + ['Enum', 'Bar', ['A']]]]]]) + s2 = """ + struct Foo { + enum Bar { A = 1, B, C } bar; + enum Baz { D = A } baz; + } foo; + """ + self.assertEqual(expand_decl(self.parse(s2).ext[0]), + ['Decl', 'foo', + ['TypeDecl', ['Struct', 'Foo', + [['Decl', 'bar', + ['TypeDecl', ['Enum', 'Bar', ['A', 'B', 'C']]]], + ['Decl', 'baz', + ['TypeDecl', ['Enum', 'Baz', ['D']]]]]]]]) + def test_struct_with_extra_semis_inside(self): s1 = """ struct { @@ -1190,6 +1222,13 @@ class TestCParser_fundamentals(TestCParser_base): for b in bad: self.assertRaises(ParseError, self.parse, b) + def test_invalid_typedef_storage_qual_error(self): + """ Tests that using typedef as a storage qualifier is correctly flagged + as an error. + """ + bad = 'typedef const int foo(int a) { return 0; }' + self.assertRaises(ParseError, self.parse, bad) + def test_duplicate_typedef(self): """ Tests that redeclarations of existing types are parsed correctly. This is non-standard, but allowed by many compilers. |