Add support for expanding struct and typedef -- Issue 93 (#174) * Add support for expanding struct and typedef * Make expansion return a new node instead of in-place modification
diff --git a/examples/cdecl.py b/examples/cdecl.py index 39ac5e8..92ac715 100644 --- a/examples/cdecl.py +++ b/examples/cdecl.py
@@ -12,13 +12,28 @@ # # For example: # -# 'typedef int Node; const Node* (*ar)[10];' -# => -# ar is a pointer to array[10] of pointer to const Node +# c_decl = 'typedef int Node; const Node* (*ar)[10];' +# +# explain_c_declaration(c_decl) +# => ar is a pointer to array[10] of pointer to const Node +# +# struct and typedef are expanded when according arguments are set: +# +# explain_c_declaration(c_decl, expand_typedef=True) +# => ar is a pointer to array[10] of pointer to const int +# +# c_decl = 'struct P {int x; int y;} p;' +# +# explain_c_declaration(c_decl) +# => p is a struct P +# +# explain_c_declaration(c_decl, expand_struct=True) +# => p is a struct P containing {x is a int, y is a int} # # Eli Bendersky [http://eli.thegreenplace.net] # License: BSD #----------------------------------------------------------------- +import copy import sys # This is not required if you've installed pycparser into @@ -29,7 +44,7 @@ from pycparser import c_parser, c_ast -def explain_c_declaration(c_decl): +def explain_c_declaration(c_decl, expand_struct=False, expand_typedef=False): """ Parses the declaration in c_decl and returns a text explanation as a string. @@ -49,7 +64,14 @@ ): return "Not a valid declaration" - return _explain_decl_node(node.ext[-1]) + try: + expanded = expand_struct_typedef(node.ext[-1], node, + expand_struct=expand_struct, + expand_typedef=expand_typedef) + except Exception as e: + return "Not a valid declaration: " + str(e) + + return _explain_decl_node(expanded) def _explain_decl_node(decl_node): @@ -95,6 +117,72 @@ return ('function(%s) returning ' % (args) + _explain_type(decl.type)) + elif typ == c_ast.Struct: + decls = [_explain_decl_node(mem_decl) for mem_decl in decl.decls] + members = ', '.join(decls) + + return ('struct%s ' % (' ' + decl.name if decl.name else '') + + ('containing {%s}' % members if members else '')) + + +def expand_struct_typedef(cdecl, file_ast, expand_struct=False, expand_typedef=False): + """Expand struct & typedef in context of file_ast and return a new expanded node""" + decl_copy = copy.deepcopy(cdecl) + _expand_in_place(decl_copy, file_ast, expand_struct, expand_typedef) + return decl_copy + + +def _expand_in_place(decl, file_ast, expand_struct=False, expand_typedef=False): + """Recursively expand struct & typedef in place, throw Exception if + undeclared struct or typedef are used + """ + typ = type(decl) + + if typ in (c_ast.Decl, c_ast.TypeDecl, c_ast.PtrDecl, c_ast.ArrayDecl): + decl.type = _expand_in_place(decl.type, file_ast, expand_struct, expand_typedef) + + elif typ == c_ast.Struct: + if not decl.decls: + struct = _find_struct(decl.name, file_ast) + if not struct: + raise Exception('using undeclared struct %s' % decl.name) + decl.decls = struct.decls + + for i, mem_decl in enumerate(decl.decls): + decl.decls[i] = _expand_in_place(mem_decl, file_ast, expand_struct, expand_typedef) + + if not expand_struct: + decl.decls = [] + + elif (typ == c_ast.IdentifierType and + decl.names[0] not in ('int', 'char')): + typedef = _find_typedef(decl.names[0], file_ast) + if not typedef: + raise Exception('using undeclared type %s' % decl.names[0]) + + if expand_typedef: + return typedef.type + + return decl + + +def _find_struct(name, file_ast): + """Receives a struct name and return declared struct object in file_ast + """ + for node in file_ast.ext: + if (type(node) == c_ast.Decl and + type(node.type) == c_ast.Struct and + node.type.name == name): + return node.type + + +def _find_typedef(name, file_ast): + """Receives a type name and return typedef object in file_ast + """ + for node in file_ast.ext: + if type(node) == c_ast.Typedef and node.name == name: + return node + if __name__ == "__main__": if len(sys.argv) > 1: