summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Bendersky <eliben@gmail.com>2021-09-13 21:09:29 -0700
committerEli Bendersky <eliben@gmail.com>2021-09-13 21:09:29 -0700
commitbdcc186728124e6a90ed865031f11fba84165c19 (patch)
tree3f53dfa6cc533198f42edbfff62917cf3affb901
parenta1b5e46cf3015fe83c1f2a9de1a0e6d2fce76c19 (diff)
downloadpycparser-bdcc186728124e6a90ed865031f11fba84165c19.tar.gz
A different, more robust appoach to fix _Atomic specifiers.
Now the ASTs produced are more correct, and more complex cases work like nested _Atomic(...) specifiers.
-rw-r--r--pycparser/ast_transforms.py55
-rw-r--r--pycparser/c_parser.py40
-rwxr-xr-xtests/test_c_parser.py21
-rw-r--r--utils/internal/zz_parse.py16
4 files changed, 90 insertions, 42 deletions
diff --git a/pycparser/ast_transforms.py b/pycparser/ast_transforms.py
index 0aeb88f..c756c66 100644
--- a/pycparser/ast_transforms.py
+++ b/pycparser/ast_transforms.py
@@ -104,3 +104,58 @@ def _extract_nested_case(case_node, stmts_list):
stmts_list.append(case_node.stmts.pop())
_extract_nested_case(stmts_list[-1], stmts_list)
+
+def fix_atomic_specifiers(decl):
+ """ Atomic specifiers like _Atomic(type) are unusually structured,
+ conferring a qualifier upon the contained type.
+
+ This function fixes a decl with atomic specifiers to have a sane AST
+ structure, by removing spurious Typename->TypeDecl pairs and attaching
+ the _Atomic qualifier in the right place.
+ """
+ # There can be multiple levels of _Atomic in a decl; fix them until a
+ # fixed point is reached.
+ while True:
+ decl, found = _fix_atomic_specifiers_once(decl)
+ if not found:
+ break
+
+ # Make sure to add an _Atomic qual on the topmost decl if needed.
+ typ = decl
+ while not isinstance(typ, c_ast.TypeDecl):
+ try:
+ typ = typ.type
+ except AttributeError:
+ return decl
+ if '_Atomic' in typ.quals and '_Atomic' not in decl.quals:
+ decl.quals.append('_Atomic')
+
+ return decl
+
+
+def _fix_atomic_specifiers_once(decl):
+ """ Performs one 'fix' round of atomic specifiers.
+ Returns (modified_decl, found) where found is True iff a fix was made.
+ """
+ parent = decl
+ grandparent = None
+ node = decl.type
+ while node is not None:
+ if isinstance(node, c_ast.Typename) and '_Atomic' in node.quals:
+ break
+ try:
+ grandparent = parent
+ parent = node
+ node = node.type
+ except AttributeError:
+ # If we've reached a node without a `type` field, it means we won't
+ # find what we're looking for at this point; give up the search
+ # and return the original decl unmodified.
+ return decl, False
+
+ assert isinstance(parent, c_ast.TypeDecl)
+ grandparent.type = node.type
+ if '_Atomic' not in node.type.quals:
+ node.type.quals.append('_Atomic')
+ return decl, True
+
diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py
index e6d1f3e..7d087f0 100644
--- a/pycparser/c_parser.py
+++ b/pycparser/c_parser.py
@@ -13,7 +13,7 @@ from .ply import yacc
from . import c_ast
from .c_lexer import CLexer
from .plyparser import PLYParser, Coord, ParseError, parameterized, template
-from .ast_transforms import fix_switch_cases
+from .ast_transforms import fix_switch_cases, fix_atomic_specifiers
@template
@@ -308,7 +308,7 @@ class CParser(PLYParser):
type = type.type
decl.name = type.declname
- type.quals = decl.quals
+ type.quals = decl.quals[:]
# The typename is a list of types. If any type in this
# list isn't an IdentifierType, it must be the only
@@ -321,14 +321,8 @@ class CParser(PLYParser):
self._parse_error(
"Invalid multiple types specified", tn.coord)
else:
- # This is a nested TypeDecl which we don't need.
- if isinstance(tn, c_ast.TypeDecl):
- type.type = tn.type
- type.quals.extend(tn.quals)
- return decl
- else:
- type.type = tn
- return decl
+ type.type = tn
+ return decl
if not typename:
# Functions default to returning int
@@ -382,7 +376,6 @@ class CParser(PLYParser):
declarations = []
# Bit-fields are allowed to be unnamed.
- #
if decls[0].get('bitsize') is not None:
pass
@@ -390,7 +383,6 @@ class CParser(PLYParser):
# problem can occur where the identifier gets grouped into
# spec['type'], leaving decl as None. This can only occur for the
# first declarator.
- #
elif decls[0]['decl'] is None:
if len(spec['type']) < 2 or len(spec['type'][-1].names) != 1 or \
not self._is_type_in_scope(spec['type'][-1].names[0]):
@@ -412,7 +404,6 @@ 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.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType)):
decls_0_tail = decls[0]['decl']
@@ -451,13 +442,13 @@ class CParser(PLYParser):
# Add the type name defined by typedef to a
# symbol table (for usage in the lexer)
- #
if typedef_namespace:
if is_typedef:
self._add_typedef_name(fixed_decl.name, fixed_decl.coord)
else:
self._add_identifier(fixed_decl.name, fixed_decl.coord)
+ fixed_decl = fix_atomic_specifiers(fixed_decl)
declarations.append(fixed_decl)
return declarations
@@ -859,27 +850,10 @@ class CParser(PLYParser):
p[0] = p[1]
# See section 6.7.2.4 of the C11 standard.
- # Note: according to the standard, "The type name in an atomic type
- # specifier shall not refer to an array type, a function type, an atomic
- # type, or a qualified type."
def p_type_specifier_2(self, p):
""" type_specifier : _ATOMIC LPAREN type_name RPAREN
"""
- assert isinstance(p[3], c_ast.Typename)
- typ = p[3].type
-
- # Since type_name may already include a TypeDecl, and this
- # type_specifier node itself creates a TypeDecl, do some de-duplication.
- # This will leave some cases unchanged, because we need to propagate the
- # atomic qualifier somewhere. These leftover cases will be de=duplicated
- # in _fix_decl_name_type.
- searchNode = typ
- while hasattr(searchNode, 'type') and searchNode.type is not None:
- if isinstance(searchNode.type, c_ast.TypeDecl):
- searchNode.type = searchNode.type.type
- break
- searchNode = searchNode.type
-
+ typ = p[3]
typ.quals.append('_Atomic')
p[0] = typ
@@ -1407,7 +1381,7 @@ class CParser(PLYParser):
"""
typename = c_ast.Typename(
name='',
- quals=p[1]['qual'],
+ quals=p[1]['qual'][:],
type=p[2] or c_ast.TypeDecl(None, None, None),
coord=self._token_coord(p, 2))
diff --git a/tests/test_c_parser.py b/tests/test_c_parser.py
index 492cbd1..40b53af 100755
--- a/tests/test_c_parser.py
+++ b/tests/test_c_parser.py
@@ -547,15 +547,30 @@ class TestCParser_fundamentals(TestCParser_base):
def test_atomic_specifier(self):
self.assertEqual(self.get_decl('_Atomic(int) ai;'),
- ['Decl',
- ['_Atomic'],
+ ['Decl', ['_Atomic'],
'ai',
['TypeDecl', ['IdentifierType', ['int']]]])
self.assertEqual(self.get_decl('_Atomic(int*) ai;'),
['Decl',
'ai',
- ['TypeDecl', ['PtrDecl', ['_Atomic'], ['IdentifierType', ['int']]]]])
+ ['PtrDecl', ['_Atomic'], ['TypeDecl', ['IdentifierType', ['int']]]]])
+
+ self.assertEqual(self.get_decl('_Atomic(_Atomic(int)*) aai;'),
+ ['Decl', ['_Atomic'],
+ 'aai',
+ ['PtrDecl', ['_Atomic'], ['TypeDecl', ['IdentifierType', ['int']]]]])
+
+ # Multiple declarations with _Atomic(...)
+ s = '_Atomic(int) foo, bar;'
+ self.assertEqual(self.get_decl(s, 0),
+ ['Decl', ['_Atomic'],
+ 'foo',
+ ['TypeDecl', ['IdentifierType', ['int']]]])
+ self.assertEqual(self.get_decl(s, 1),
+ ['Decl', ['_Atomic'],
+ 'bar',
+ ['TypeDecl', ['IdentifierType', ['int']]]])
def test_sizeof(self):
e = """
diff --git a/utils/internal/zz_parse.py b/utils/internal/zz_parse.py
index 2dce3fb..8f74daf 100644
--- a/utils/internal/zz_parse.py
+++ b/utils/internal/zz_parse.py
@@ -7,15 +7,19 @@ from pycparser import c_parser, c_generator, c_ast, parse_file
if __name__ == "__main__":
parser = c_parser.CParser()
code = r'''
- const int * const p;
+ const int ci;
+ const int* pci;
+ _Atomic(int) ai;
+ _Atomic(int*) pai;
+ _Atomic(_Atomic(int)*) ppai;
'''
print(code)
ast = parser.parse(code)
ast.show(attrnames=True, nodenames=True)
- print(ast.ext[0].__slots__)
- print(dir(ast.ext[0]))
+ #print(ast.ext[0].__slots__)
+ #print(dir(ast.ext[0]))
- print("==== From C generator:")
- generator = c_generator.CGenerator()
- print(generator.visit(ast))
+ #print("==== From C generator:")
+ #generator = c_generator.CGenerator()
+ #print(generator.visit(ast))