#----------------------------------------------------------------- # pycparser: cdecl.py # # Example of the CDECL tool using pycparser. CDECL "explains" C type # declarations in plain English. # # The AST generated by pycparser from the given declaration is traversed # recursively to build the explanation. Note that the declaration must be a # valid external declaration in C. As shown below, typedef can be optionally # expanded. # # For example: # # 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 can be optionally expanded: # # 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 [https://eli.thegreenplace.net/] # License: BSD #----------------------------------------------------------------- import copy import sys # This is not required if you've installed pycparser into # your site-packages/ with setup.py # sys.path.extend(['.', '..']) from pycparser import c_parser, c_ast 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. The last external node of the string is used, to allow earlier typedefs for used types. expand_struct=True will spell out struct definitions recursively. expand_typedef=True will expand typedef'd types. """ parser = c_parser.CParser() try: node = parser.parse(c_decl, filename='') except c_parser.ParseError: e = sys.exc_info()[1] return "Parse error:" + str(e) if (not isinstance(node, c_ast.FileAST) or not isinstance(node.ext[-1], c_ast.Decl) ): return "Not a valid declaration" 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): """ Receives a c_ast.Decl note and returns its explanation in English. """ storage = ' '.join(decl_node.storage) + ' ' if decl_node.storage else '' return (decl_node.name + " is a " + storage + _explain_type(decl_node.type)) def _explain_type(decl): """ Recursively explains a type decl node """ typ = type(decl) if typ == c_ast.TypeDecl: quals = ' '.join(decl.quals) + ' ' if decl.quals else '' return quals + _explain_type(decl.type) elif typ == c_ast.Typename or typ == c_ast.Decl: return _explain_type(decl.type) elif typ == c_ast.IdentifierType: return ' '.join(decl.names) elif typ == c_ast.PtrDecl: quals = ' '.join(decl.quals) + ' ' if decl.quals else '' return quals + 'pointer to ' + _explain_type(decl.type) elif typ == c_ast.ArrayDecl: arr = 'array' if decl.dim: arr += '[%s]' % decl.dim.value return arr + " of " + _explain_type(decl.type) elif typ == c_ast.FuncDecl: if decl.args: params = [_explain_type(param) for param in decl.args.params] args = ', '.join(params) else: args = '' 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 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 RuntimeError 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 RuntimeError('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 RuntimeError('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: c_decl = sys.argv[1] else: c_decl = "char *(*(**foo[][8])())[];" print("Explaining the declaration: " + c_decl + "\n") print(explain_c_declaration(c_decl) + "\n")