diff options
author | Eli Bendersky <eliben@gmail.com> | 2021-09-13 21:09:29 -0700 |
---|---|---|
committer | Eli Bendersky <eliben@gmail.com> | 2021-09-13 21:09:29 -0700 |
commit | bdcc186728124e6a90ed865031f11fba84165c19 (patch) | |
tree | 3f53dfa6cc533198f42edbfff62917cf3affb901 | |
parent | a1b5e46cf3015fe83c1f2a9de1a0e6d2fce76c19 (diff) | |
download | pycparser-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.py | 55 | ||||
-rw-r--r-- | pycparser/c_parser.py | 40 | ||||
-rwxr-xr-x | tests/test_c_parser.py | 21 | ||||
-rw-r--r-- | utils/internal/zz_parse.py | 16 |
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)) |