From 910ebea5597e315447b122d064bb7c43e6977be5 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Fri, 1 Sep 2023 15:41:27 +0100 Subject: [PATCH 1/2] Add support for __attribute__(()) in enum/struct/union Add support for GNU __attribute__(()) in enum/struct/union definitions, both on the type itself and on enumerators. Fixes https://github.com/inducer/pycparserext/issues/42 Signed-off-by: Douglas Raillard --- pycparserext/ext_c_generator.py | 33 +++++ pycparserext/ext_c_parser.py | 207 ++++++++++++++++++++++++++++++++ test/test_pycparserext.py | 164 +++++++++++++++++++++++++ 3 files changed, 404 insertions(+) diff --git a/pycparserext/ext_c_generator.py b/pycparserext/ext_c_generator.py index a5747fa..90aa5ad 100644 --- a/pycparserext/ext_c_generator.py +++ b/pycparserext/ext_c_generator.py @@ -1,3 +1,5 @@ +import copy + from pycparser.c_generator import CGenerator as CGeneratorBaseBuggy from pycparserext.ext_c_parser import FuncDeclExt, TypeDeclExt import pycparser.c_ast as c_ast @@ -39,6 +41,37 @@ def visit_Asm(self, n): " : ".join( self.visit(c) for c in components)) + def _generate_struct_union_enum_ext(self, n, visitor): + if n.attributes: + # Adding __attribute__ at the end does not always work, so instead + # we change the name on a shallow copy so that the existing + # infrastructure just prints the attributes right before the name + # (as recommended by the GNU doc). + n = copy.copy(n) + n.name = ( + '__attribute__((' + self.visit(n.attributes) + ')) ' + + (n.name or '') + ) + return visitor(n) + + def visit_StructExt(self, n, *args, **kwargs): + return self._generate_struct_union_enum_ext(n, self.visit_Struct) + + def visit_UnionExt(self, n, *args, **kwargs): + return self._generate_struct_union_enum_ext(n, self.visit_Union) + + def visit_EnumExt(self, n, *args, **kwargs): + return self._generate_struct_union_enum_ext(n, self.visit_Enum) + + def visit_EnumeratorExt(self, n, *args, **kwargs): + if n.attributes: + n = copy.copy(n) + n.name = ( + (n.name or '') + + ' __attribute__((' + self.visit(n.attributes) + ')) ' + ) + return super().visit_Enumerator(n) + def _generate_type(self, n, modifiers=None, emit_declname=True): """ Recursive generation from a type node. n is the type node. modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers diff --git a/pycparserext/ext_c_parser.py b/pycparserext/ext_c_parser.py index 157e63e..9668496 100644 --- a/pycparserext/ext_c_parser.py +++ b/pycparserext/ext_c_parser.py @@ -252,6 +252,38 @@ def __iter__(self): attr_names = () + +class _StructUnionEnumMixin: + def __init__(self, *args, attributes=None, **kwargs): + super().__init__(*args, **kwargs) + self.attributes = attributes + + def children(self): + children = list(super().children()) + if self.attributes: + children.append(("attributes", self.attributes)) + return children + + def __iter__(self): + yield from super().__iter__() + if self.attributes: + yield self.attributes + + +class EnumExt(_StructUnionEnumMixin, c_ast.Enum): + pass + + +class EnumeratorExt(_StructUnionEnumMixin, c_ast.Enumerator): + pass + + +class StructExt(_StructUnionEnumMixin, c_ast.Struct): + pass + + +class UnionExt(_StructUnionEnumMixin, c_ast.Union): + pass # }}} @@ -480,6 +512,181 @@ def p_direct_abstract_declarator_6(self, p): p[0] = self._type_modify_decl(decl=p[1], modifier=func) + def _select_struct_union_class(self, token): + klass = super()._select_struct_union_class(token) + return { + c_ast.Struct: StructExt, + c_ast.Union: UnionExt, + }[klass] + + def p_struct_or_union_specifier_with_attr_1(self, p): + """ struct_or_union_specifier : struct_or_union ID brace_open brace_close attributes_opt + | struct_or_union TYPEID brace_open brace_close attributes_opt + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=p[2], + decls=[], + attributes=p[5], + coord=self._token_coord(p, 2)) + + def p_struct_or_union_specifier_with_attr_2(self, p): + """ struct_or_union_specifier : struct_or_union attributes_opt ID brace_open brace_close + | struct_or_union attributes_opt TYPEID brace_open brace_close + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=p[3], + decls=[], + attributes=p[2], + coord=self._token_coord(p, 3)) + + def p_struct_or_union_specifier_with_attr_3(self, p): + """ struct_or_union_specifier : attributes_opt struct_or_union ID brace_open brace_close + | attributes_opt struct_or_union TYPEID brace_open brace_close + """ + klass = self._select_struct_union_class(p[2]) + p[0] = klass( + name=p[3], + decls=[], + attributes=p[1], + coord=self._token_coord(p, 3)) + + def p_struct_or_union_specifier_with_attr_4(self, p): + """ struct_or_union_specifier : struct_or_union ID brace_open struct_declaration_list brace_close attributes_opt + | struct_or_union TYPEID brace_open struct_declaration_list brace_close attributes_opt + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=p[2], + decls=p[4], + attributes=p[6], + coord=self._token_coord(p, 2)) + + def p_struct_or_union_specifier_with_attr_5(self, p): + """ struct_or_union_specifier : struct_or_union attributes_opt ID brace_open struct_declaration_list brace_close + | struct_or_union attributes_opt TYPEID brace_open struct_declaration_list brace_close + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=p[3], + decls=p[5], + attributes=p[2], + coord=self._token_coord(p, 3)) + + def p_struct_or_union_specifier_with_attr_6(self, p): + """ struct_or_union_specifier : attributes_opt struct_or_union ID brace_open struct_declaration_list brace_close + | attributes_opt struct_or_union TYPEID brace_open struct_declaration_list brace_close + """ + klass = self._select_struct_union_class(p[2]) + p[0] = klass( + name=p[3], + decls=p[5], + attributes=p[1], + coord=self._token_coord(p, 3)) + + def p_struct_or_union_specifier_with_attr_7(self, p): + """ struct_or_union_specifier : struct_or_union attributes_opt ID + | struct_or_union attributes_opt TYPEID + """ + klass = self._select_struct_union_class(p[1]) + # None means no list of members + p[0] = klass( + name=p[3], + decls=None, + attributes=p[2], + coord=self._token_coord(p, 3)) + + def p_struct_or_union_specifier_with_attr_8(self, p): + """ struct_or_union_specifier : struct_or_union brace_open brace_close attributes_opt + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=None, + decls=[], + attributes=p[4], + coord=self._token_coord(p, 2)) + + def p_struct_or_union_specifier_with_attr_9(self, p): + """ struct_or_union_specifier : struct_or_union attributes_opt brace_open brace_close + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=None, + decls=[], + attributes=p[2], + coord=self._token_coord(p, 3)) + + def p_struct_or_union_specifier_with_attr_10(self, p): + """ struct_or_union_specifier : struct_or_union brace_open struct_declaration_list brace_close attributes_opt + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=None, + decls=p[3], + attributes=p[5], + coord=self._token_coord(p, 2)) + + def p_struct_or_union_specifier_with_attr_11(self, p): + """ struct_or_union_specifier : struct_or_union attributes_opt brace_open struct_declaration_list brace_close + """ + klass = self._select_struct_union_class(p[1]) + p[0] = klass( + name=None, + decls=p[4], + attributes=p[2], + coord=self._token_coord(p, 3)) + + def p_enum_specifier_with_attr_1(self, p): + """ enum_specifier : ENUM attributes_opt ID + | ENUM attributes_opt TYPEID + """ + p[0] = EnumExt(p[3], None, self._token_coord(p, 1), attributes=p[2]) + + def p_enum_specifier_with_attr_2(self, p): + """ enum_specifier : ENUM attributes_opt brace_open enumerator_list brace_close + """ + p[0] = EnumExt(None, p[4], self._token_coord(p, 1), attributes=p[2]) + + def p_enum_specifier_with_attr_3(self, p): + """ enum_specifier : ENUM brace_open enumerator_list brace_close attributes_opt + """ + p[0] = EnumExt(None, p[3], self._token_coord(p, 1), attributes=p[5]) + + def p_enum_specifier_with_attr_4(self, p): + """ enum_specifier : ENUM attributes_opt ID brace_open enumerator_list brace_close + | ENUM attributes_opt TYPEID brace_open enumerator_list brace_close + """ + p[0] = EnumExt(p[3], p[5], self._token_coord(p, 1), attributes=p[2]) + + def p_enum_specifier_with_attr_5(self, p): + """ enum_specifier : ENUM ID brace_open enumerator_list brace_close attributes_opt + | ENUM TYPEID brace_open enumerator_list brace_close attributes_opt + """ + p[0] = EnumExt(p[2], p[4], self._token_coord(p, 1), attributes=p[6]) + + def p_enumerator(self, p): + """ enumerator : ID + | ID EQUALS constant_expression + | ID attributes_opt + | ID attributes_opt EQUALS constant_expression + """ + if len(p) in (2, 4): + super().p_enumerator(p) + else: + if len(p) == 3: + enumerator = EnumeratorExt( + p[1], None, + self._token_coord(p, 1), + attributes=p[2]) + else: + enumerator = EnumeratorExt( + p[1], p[4], + self._token_coord(p, 1), + attributes=p[2]) + self._add_identifier(enumerator.name, enumerator.coord) + p[0] = enumerator + # }}} # }}} diff --git a/test/test_pycparserext.py b/test/test_pycparserext.py index 183769f..70933d8 100644 --- a/test/test_pycparserext.py +++ b/test/test_pycparserext.py @@ -363,6 +363,170 @@ def test_empty_struct_declaration(): print(GnuCGenerator().visit(ast)) +def test_empty_struct_declaration_attr_1(): + src = """ + typedef struct __attribute__((packed)) Foo { + } Foo_t; + """ + + from pycparserext.ext_c_parser import GnuCParser + p = GnuCParser() + ast = p.parse(src) + ast.show() + + from pycparserext.ext_c_generator import GnuCGenerator + print(GnuCGenerator().visit(ast)) + + assert len(ast.ext[0].type.type.attributes.exprs) == 1 + assert ast.ext[0].type.type.attributes.exprs[0].name == "packed" + + +def test_empty_struct_declaration_attr_2(): + src = """ + typedef struct Foo { + } __attribute__((packed)) Foo_t; + """ + + from pycparserext.ext_c_parser import GnuCParser + p = GnuCParser() + ast = p.parse(src) + ast.show() + + from pycparserext.ext_c_generator import GnuCGenerator + print(GnuCGenerator().visit(ast)) + + assert len(ast.ext[0].type.type.attributes.exprs) == 1 + assert ast.ext[0].type.type.attributes.exprs[0].name == "packed" + + +def test_empty_struct_declaration_attr_3(): + src = """ + struct __attribute__((packed)) { + }; + """ + + from pycparserext.ext_c_parser import GnuCParser + p = GnuCParser() + ast = p.parse(src) + ast.show() + + from pycparserext.ext_c_generator import GnuCGenerator + print(GnuCGenerator().visit(ast)) + + assert len(ast.ext[0].type.attributes.exprs) == 1 + assert ast.ext[0].type.attributes.exprs[0].name == "packed" + + +def test_empty_struct_declaration_attr_4(): + src = """ + struct { + } __attribute__((packed)) ; + """ + + from pycparserext.ext_c_parser import GnuCParser + p = GnuCParser() + ast = p.parse(src) + ast.show() + + from pycparserext.ext_c_generator import GnuCGenerator + print(GnuCGenerator().visit(ast)) + + assert len(ast.ext[0].type.attributes.exprs) == 1 + assert ast.ext[0].type.attributes.exprs[0].name == "packed" + + +def test_empty_struct_declaration_attr_5(): + src = """ + struct __attribute__((packed)) Foo; + """ + + from pycparserext.ext_c_parser import GnuCParser + p = GnuCParser() + ast = p.parse(src) + ast.show() + + from pycparserext.ext_c_generator import GnuCGenerator + print(GnuCGenerator().visit(ast)) + + assert len(ast.ext[0].type.attributes.exprs) == 1 + assert ast.ext[0].type.attributes.exprs[0].name == "packed" + + +def test_enum_declaration_attr_1(): + src = """ + enum __attribute__((deprecated)) Foo {CASE1}; + """ + + from pycparserext.ext_c_parser import GnuCParser + p = GnuCParser() + ast = p.parse(src) + ast.show() + + from pycparserext.ext_c_generator import GnuCGenerator + print(GnuCGenerator().visit(ast)) + + assert len(ast.ext[0].type.attributes.exprs) == 1 + assert ast.ext[0].type.attributes.exprs[0].name == "deprecated" + + +def test_enum_declaration_attr_2(): + src = """ + enum Foo {CASE1} __attribute__((deprecated)); + """ + + from pycparserext.ext_c_parser import GnuCParser + p = GnuCParser() + ast = p.parse(src) + ast.show() + + from pycparserext.ext_c_generator import GnuCGenerator + print(GnuCGenerator().visit(ast)) + + assert len(ast.ext[0].type.attributes.exprs) == 1 + assert ast.ext[0].type.attributes.exprs[0].name == "deprecated" + + +def test_enum_declaration_attr_3(): + src = """ + enum __attribute__((deprecated)) Foo { + CASE1, + CASE2 = 42, + CASE3 __attribute__((deprecated)), + CASE4 __attribute__((deprecated)) = 43, + }; + """ + + from pycparserext.ext_c_parser import GnuCParser + p = GnuCParser() + ast = p.parse(src) + ast.show() + + from pycparserext.ext_c_generator import GnuCGenerator + print(GnuCGenerator().visit(ast)) + + enum = ast.ext[0].type + assert len(enum.attributes.exprs) == 1 + assert enum.attributes.exprs[0].name == "deprecated" + + expected = [ + ("CASE1", None, None), + ("CASE2", 42, None), + ("CASE3", None, ["deprecated"]), + ("CASE4", 43, ["deprecated"]), + ] + + for value, (name, val, attrs) in zip(enum.values, expected): + assert value.name == name + + if val is None: + assert value.value is None + else: + assert value.value.value == str(val) + + if attrs: + assert [attr.name for attr in value.attributes] == attrs + + def test_nesty_c_declarator(): src = """ struct a { From cde112827bfa722bc1db963117bc5959268a0664 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Fri, 8 Sep 2023 16:32:01 +0100 Subject: [PATCH 2/2] WIP --- pycparserext/ext_c_parser.py | 143 ++++++++++++++--------------------- test/test_pycparserext.py | 6 +- 2 files changed, 61 insertions(+), 88 deletions(-) diff --git a/pycparserext/ext_c_parser.py b/pycparserext/ext_c_parser.py index 9668496..f92d42b 100644 --- a/pycparserext/ext_c_parser.py +++ b/pycparserext/ext_c_parser.py @@ -1,5 +1,7 @@ from __future__ import division +import itertools + import pycparser.c_parser import pycparser.c_ast as c_ast try: @@ -290,21 +292,40 @@ class UnionExt(_StructUnionEnumMixin, c_ast.Union): # {{{ attributes class _AttributesMixin(object): + + def _merge_attributes(self, p, attrs): + print(999,attrs) + attrs = [ + attr + for attr in attrs + if attr is not None + ] + if attrs: + fst, *others = attrs + fst.exprs.extend(itertools.chain.from_iterable( + attr.exprs + for attr in others + )) + return fst + else: + return c_ast.ExprList([], self._coord(p.lineno(1))) + def p_attributes_opt_1(self, p): """ attributes_opt : attribute_decl attributes_opt """ - p[1].exprs.extend(p[2].exprs) - p[0] = p[1] + print(991) + p[0] = self._merge_attributes(p, (p[1], p[2])) def p_attributes_opt_2(self, p): """ attributes_opt : empty """ - p[0] = c_ast.ExprList([], self._coord(p.lineno(1))) + p[0] = self._merge_attributes(p, []) def p_attribute_decl(self, p): """ attribute_decl : __ATTRIBUTE__ LPAREN LPAREN attribute_list RPAREN RPAREN | __ATTRIBUTE LPAREN LPAREN attribute_list RPAREN RPAREN """ + print(992) p[0] = p[4] def p_attribute_list_1(self, p): @@ -315,8 +336,7 @@ def p_attribute_list_1(self, p): def p_attribute_list_2(self, p): """ attribute_list : attribute_list COMMA attribute """ - p[1].exprs.append(p[3]) - p[0] = p[1] + p[0] = self._merge_attributes(p, (p[1], p[3])) def p_attribute_1(self, p): """ attribute : CONST @@ -331,6 +351,7 @@ def p_attribute_3(self, p): def p_function_specifier_attr(self, p): """ function_specifier : attribute_decl """ + print(993) p[0] = AttributeSpecifier(p[1]) # }}} @@ -421,6 +442,7 @@ class _AsmAndAttributesMixin(_AsmMixin, _AttributesMixin): def p_xxx_declarator_1(self, p): """ xxx_declarator : direct_xxx_declarator asm_label_opt attributes_opt """ + print(555, p[3]) if p[2] or p[3].exprs: if isinstance(p[1], (c_ast.ArrayDecl, c_ast.FuncDecl)): decl_ext = to_decl_ext(p[1].type) @@ -450,6 +472,7 @@ def p_xxx_declarator_2(self, p): | pointer attributes_opt direct_xxx_declarator \ asm_label_opt """ + print(666) if hasattr(p[4], "exprs"): attr_decl = p[4] asm_label = p[3] @@ -490,6 +513,7 @@ def p_direct_xxx_declarator_6(self, p): LPAREN identifier_list_opt RPAREN \ asm_label_opt attributes_opt """ + print(777) func = FuncDeclExt( args=p[3], type=None, @@ -503,6 +527,7 @@ def p_direct_abstract_declarator_6(self, p): """ direct_abstract_declarator : direct_abstract_declarator \ LPAREN parameter_type_list_opt RPAREN asm_label_opt attributes_opt """ + print(888) func = FuncDeclExt( args=p[3], type=None, @@ -520,149 +545,92 @@ def _select_struct_union_class(self, token): }[klass] def p_struct_or_union_specifier_with_attr_1(self, p): - """ struct_or_union_specifier : struct_or_union ID brace_open brace_close attributes_opt - | struct_or_union TYPEID brace_open brace_close attributes_opt - """ - klass = self._select_struct_union_class(p[1]) - p[0] = klass( - name=p[2], - decls=[], - attributes=p[5], - coord=self._token_coord(p, 2)) - - def p_struct_or_union_specifier_with_attr_2(self, p): - """ struct_or_union_specifier : struct_or_union attributes_opt ID brace_open brace_close - | struct_or_union attributes_opt TYPEID brace_open brace_close + """ struct_or_union_specifier : struct_or_union attributes_opt ID brace_open brace_close attributes_opt + | struct_or_union attributes_opt TYPEID brace_open brace_close attributes_opt """ + print(111) klass = self._select_struct_union_class(p[1]) - p[0] = klass( - name=p[3], - decls=[], - attributes=p[2], - coord=self._token_coord(p, 3)) + attrs = self._merge_attributes(p, (p[2], p[6])) - def p_struct_or_union_specifier_with_attr_3(self, p): - """ struct_or_union_specifier : attributes_opt struct_or_union ID brace_open brace_close - | attributes_opt struct_or_union TYPEID brace_open brace_close - """ - klass = self._select_struct_union_class(p[2]) p[0] = klass( name=p[3], decls=[], - attributes=p[1], + attributes=attrs, coord=self._token_coord(p, 3)) - def p_struct_or_union_specifier_with_attr_4(self, p): - """ struct_or_union_specifier : struct_or_union ID brace_open struct_declaration_list brace_close attributes_opt - | struct_or_union TYPEID brace_open struct_declaration_list brace_close attributes_opt - """ - klass = self._select_struct_union_class(p[1]) - p[0] = klass( - name=p[2], - decls=p[4], - attributes=p[6], - coord=self._token_coord(p, 2)) - def p_struct_or_union_specifier_with_attr_5(self, p): - """ struct_or_union_specifier : struct_or_union attributes_opt ID brace_open struct_declaration_list brace_close - | struct_or_union attributes_opt TYPEID brace_open struct_declaration_list brace_close + """ struct_or_union_specifier : struct_or_union attributes_opt ID brace_open struct_declaration_list brace_close attributes_opt + | struct_or_union attributes_opt TYPEID brace_open struct_declaration_list brace_close attributes_opt """ + print(222) klass = self._select_struct_union_class(p[1]) - p[0] = klass( - name=p[3], - decls=p[5], - attributes=p[2], - coord=self._token_coord(p, 3)) + attrs = self._merge_attributes(p, (p[2], p[7])) - def p_struct_or_union_specifier_with_attr_6(self, p): - """ struct_or_union_specifier : attributes_opt struct_or_union ID brace_open struct_declaration_list brace_close - | attributes_opt struct_or_union TYPEID brace_open struct_declaration_list brace_close - """ - klass = self._select_struct_union_class(p[2]) p[0] = klass( name=p[3], decls=p[5], - attributes=p[1], + attributes=attrs, coord=self._token_coord(p, 3)) def p_struct_or_union_specifier_with_attr_7(self, p): - """ struct_or_union_specifier : struct_or_union attributes_opt ID - | struct_or_union attributes_opt TYPEID + """ struct_or_union_specifier : struct_or_union attributes_opt ID attributes_opt + | struct_or_union attributes_opt TYPEID attributes_opt """ + print(333) klass = self._select_struct_union_class(p[1]) + attrs = self._merge_attributes(p, (p[2], p[4])) + # None means no list of members p[0] = klass( name=p[3], decls=None, - attributes=p[2], + attributes=attrs, coord=self._token_coord(p, 3)) def p_struct_or_union_specifier_with_attr_8(self, p): - """ struct_or_union_specifier : struct_or_union brace_open brace_close attributes_opt + """ struct_or_union_specifier : struct_or_union attributes_opt brace_open brace_close attributes_opt """ + print(444) klass = self._select_struct_union_class(p[1]) - p[0] = klass( - name=None, - decls=[], - attributes=p[4], - coord=self._token_coord(p, 2)) + attrs = self._merge_attributes(p, (p[2], p[5])) - def p_struct_or_union_specifier_with_attr_9(self, p): - """ struct_or_union_specifier : struct_or_union attributes_opt brace_open brace_close - """ - klass = self._select_struct_union_class(p[1]) p[0] = klass( name=None, decls=[], - attributes=p[2], - coord=self._token_coord(p, 3)) - - def p_struct_or_union_specifier_with_attr_10(self, p): - """ struct_or_union_specifier : struct_or_union brace_open struct_declaration_list brace_close attributes_opt - """ - klass = self._select_struct_union_class(p[1]) - p[0] = klass( - name=None, - decls=p[3], - attributes=p[5], - coord=self._token_coord(p, 2)) - - def p_struct_or_union_specifier_with_attr_11(self, p): - """ struct_or_union_specifier : struct_or_union attributes_opt brace_open struct_declaration_list brace_close - """ - klass = self._select_struct_union_class(p[1]) - p[0] = klass( - name=None, - decls=p[4], - attributes=p[2], + attributes=attrs, coord=self._token_coord(p, 3)) def p_enum_specifier_with_attr_1(self, p): """ enum_specifier : ENUM attributes_opt ID | ENUM attributes_opt TYPEID """ + print(991) p[0] = EnumExt(p[3], None, self._token_coord(p, 1), attributes=p[2]) def p_enum_specifier_with_attr_2(self, p): """ enum_specifier : ENUM attributes_opt brace_open enumerator_list brace_close """ + print(992) p[0] = EnumExt(None, p[4], self._token_coord(p, 1), attributes=p[2]) def p_enum_specifier_with_attr_3(self, p): """ enum_specifier : ENUM brace_open enumerator_list brace_close attributes_opt """ + print(993) p[0] = EnumExt(None, p[3], self._token_coord(p, 1), attributes=p[5]) def p_enum_specifier_with_attr_4(self, p): """ enum_specifier : ENUM attributes_opt ID brace_open enumerator_list brace_close | ENUM attributes_opt TYPEID brace_open enumerator_list brace_close """ + print(994) p[0] = EnumExt(p[3], p[5], self._token_coord(p, 1), attributes=p[2]) def p_enum_specifier_with_attr_5(self, p): """ enum_specifier : ENUM ID brace_open enumerator_list brace_close attributes_opt | ENUM TYPEID brace_open enumerator_list brace_close attributes_opt """ + print(995) p[0] = EnumExt(p[2], p[4], self._token_coord(p, 1), attributes=p[6]) def p_enumerator(self, p): @@ -671,6 +639,7 @@ def p_enumerator(self, p): | ID attributes_opt | ID attributes_opt EQUALS constant_expression """ + print(996) if len(p) in (2, 4): super().p_enumerator(p) else: diff --git a/test/test_pycparserext.py b/test/test_pycparserext.py index 70933d8..9443e4d 100644 --- a/test/test_pycparserext.py +++ b/test/test_pycparserext.py @@ -386,10 +386,14 @@ def test_empty_struct_declaration_attr_2(): typedef struct Foo { } __attribute__((packed)) Foo_t; """ + src = """ + typedef struct __attribute__((packed)) Foo { + } Foo_t; + """ from pycparserext.ext_c_parser import GnuCParser p = GnuCParser() - ast = p.parse(src) + ast = p.parse(src, debuglevel=1) ast.show() from pycparserext.ext_c_generator import GnuCGenerator