Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions pycparserext/ext_c_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ def _generate_type(self, n, modifiers=None, emit_declname=True):
else:
return self.visit(n)

def visit_AttributeSpecifier(self, n):
return "__attribute__((" + self.visit(n.exprlist) + "))"


class GnuCGenerator(AsmAndAttributesMixin, CGeneratorBase):
def _generate_decl(self, n):
""" Generation from a Decl node.
"""
Expand All @@ -145,11 +150,6 @@ def funcspec_to_str(i):
s += self._generate_type(n.type)
return s

def visit_AttributeSpecifier(self, n):
return " __attribute__((" + self.visit(n.exprlist) + "))"


class GnuCGenerator(AsmAndAttributesMixin, CGeneratorBase):
def visit_TypeOfDeclaration(self, n):
return "%s(%s)" % (n.typeof_keyword, self.visit(n.declaration))

Expand Down
87 changes: 86 additions & 1 deletion pycparserext/ext_c_parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import pycparser.c_ast as c_ast
import pycparser.c_parser

Expand Down Expand Up @@ -59,6 +58,39 @@ class AttributeSpecifier(c_ast.Node):
def __init__(self, exprlist):
self.exprlist = exprlist

def __eq__(self, other):
if not isinstance(other, AttributeSpecifier):
return False
# Recursively compare the exprlist nodes
return self._compare_ast_nodes(self.exprlist, other.exprlist)

def _compare_ast_nodes(self, node1, node2):
"""Recursively compare two AST nodes for structural equality."""
if type(node1) is not type(node2):
return False

# Compare attributes
if hasattr(node1, "attr_names"):
for attr in node1.attr_names:
val1 = getattr(node1, attr)
val2 = getattr(node2, attr)
if val1 != val2:
return False

# Compare children
if hasattr(node1, "children"):
children1 = node1.children()
children2 = node2.children()
if len(children1) != len(children2):
return False
for (name1, child1), (name2, child2) in zip(children1, children2):
if name1 != name2:
return False
if not self._compare_ast_nodes(child1, child2):
return False

return True

def children(self):
return [("exprlist", self.exprlist)]

Expand All @@ -71,6 +103,32 @@ def __iter__(self):
attr_names = ()


class DeclExt(c_ast.Decl):
@staticmethod
def from_pycparser(decl):
assert isinstance(decl, c_ast.Decl)
new_decl = DeclExt(
name=decl.name,
quals=decl.quals,
align=decl.align,
storage=decl.storage,
funcspec=decl.funcspec,
type=decl.type,
init=decl.init,
bitsize=decl.bitsize,
coord=decl.coord,
)
if hasattr(decl, "attributes"):
new_decl.attributes = decl.attributes
return new_decl

def children(self):
nodelist = super().children()
if hasattr(self, "attributes"):
nodelist = (*nodelist, ("attributes", self.attributes))
return nodelist


class Asm(c_ast.Node):
def __init__(self, asm_keyword, template, output_operands,
input_operands, clobbered_regs, coord=None):
Expand Down Expand Up @@ -580,6 +638,33 @@ def p_gnu_unary_expression(self, p):
p[2] if len(p) == 3 else p[3],
self._token_coord(p, 1))

def p_specifier_qualifier_list_fs(self, p):
""" specifier_qualifier_list : function_specifier specifier_qualifier_list
"""
self._p_specifier_qualifier_list_left_recursion(p)

def _p_specifier_qualifier_list_left_recursion(self, p):
# The PLY documentation says that left-recursive rules are supported,
# but it keeps complaining about reduce/reduce conflicts.
#
# See `_p_specifier_qualifier_list_right_recursion` for a non-complaining
# version.
spec = p[1]
spec_dict = p[2]

if isinstance(spec, AttributeSpecifier):
spec_dict["function"].append(spec)
elif isinstance(spec, str):
spec_dict["qual"].append(spec)
elif isinstance(spec, c_ast.Node):
if "type" not in spec_dict:
spec_dict["type"] = []
spec_dict["type"].append(spec)
else:
raise TypeError(f"Unknown specifier {spec!r} of type {type(spec)}")

p[0] = spec_dict

def p_statement(self, p):
""" statement : labeled_statement
| expression_statement
Expand Down
10 changes: 10 additions & 0 deletions test/test_pycparserext.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,16 @@ def test_packed_anonymous_struct_in_struct_after():
assert _round_trip_matches(code)


def test_attribute_in_struct():
# https://github.com/inducer/pycparserext/issues/75
src = """
typedef struct {
__attribute__((__deprecated__)) int test1;
} test2;
"""
assert _round_trip_matches(src)


if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
Expand Down
Loading