1#----------------------------------------------------------------- 2# pycparser: cdecl.py 3# 4# Example of the CDECL tool using pycparser. CDECL "explains" C type 5# declarations in plain English. 6# 7# The AST generated by pycparser from the given declaration is traversed 8# recursively to build the explanation. Note that the declaration must be a 9# valid external declaration in C. As shown below, typedef can be optionally 10# expanded. 11# 12# For example: 13# 14# c_decl = 'typedef int Node; const Node* (*ar)[10];' 15# 16# explain_c_declaration(c_decl) 17# => ar is a pointer to array[10] of pointer to const Node 18# 19# struct and typedef can be optionally expanded: 20# 21# explain_c_declaration(c_decl, expand_typedef=True) 22# => ar is a pointer to array[10] of pointer to const int 23# 24# c_decl = 'struct P {int x; int y;} p;' 25# 26# explain_c_declaration(c_decl) 27# => p is a struct P 28# 29# explain_c_declaration(c_decl, expand_struct=True) 30# => p is a struct P containing {x is a int, y is a int} 31# 32# Eli Bendersky [https://eli.thegreenplace.net/] 33# License: BSD 34#----------------------------------------------------------------- 35import copy 36import sys 37 38# This is not required if you've installed pycparser into 39# your site-packages/ with setup.py 40# 41sys.path.extend(['.', '..']) 42 43from pycparser import c_parser, c_ast 44 45 46def explain_c_declaration(c_decl, expand_struct=False, expand_typedef=False): 47 """ Parses the declaration in c_decl and returns a text 48 explanation as a string. 49 50 The last external node of the string is used, to allow earlier typedefs 51 for used types. 52 53 expand_struct=True will spell out struct definitions recursively. 54 expand_typedef=True will expand typedef'd types. 55 """ 56 parser = c_parser.CParser() 57 58 try: 59 node = parser.parse(c_decl, filename='<stdin>') 60 except c_parser.ParseError: 61 e = sys.exc_info()[1] 62 return "Parse error:" + str(e) 63 64 if (not isinstance(node, c_ast.FileAST) or 65 not isinstance(node.ext[-1], c_ast.Decl) 66 ): 67 return "Not a valid declaration" 68 69 try: 70 expanded = expand_struct_typedef(node.ext[-1], node, 71 expand_struct=expand_struct, 72 expand_typedef=expand_typedef) 73 except Exception as e: 74 return "Not a valid declaration: " + str(e) 75 76 return _explain_decl_node(expanded) 77 78 79def _explain_decl_node(decl_node): 80 """ Receives a c_ast.Decl note and returns its explanation in 81 English. 82 """ 83 storage = ' '.join(decl_node.storage) + ' ' if decl_node.storage else '' 84 85 return (decl_node.name + 86 " is a " + 87 storage + 88 _explain_type(decl_node.type)) 89 90 91def _explain_type(decl): 92 """ Recursively explains a type decl node 93 """ 94 typ = type(decl) 95 96 if typ == c_ast.TypeDecl: 97 quals = ' '.join(decl.quals) + ' ' if decl.quals else '' 98 return quals + _explain_type(decl.type) 99 elif typ == c_ast.Typename or typ == c_ast.Decl: 100 return _explain_type(decl.type) 101 elif typ == c_ast.IdentifierType: 102 return ' '.join(decl.names) 103 elif typ == c_ast.PtrDecl: 104 quals = ' '.join(decl.quals) + ' ' if decl.quals else '' 105 return quals + 'pointer to ' + _explain_type(decl.type) 106 elif typ == c_ast.ArrayDecl: 107 arr = 'array' 108 if decl.dim: arr += '[%s]' % decl.dim.value 109 110 return arr + " of " + _explain_type(decl.type) 111 112 elif typ == c_ast.FuncDecl: 113 if decl.args: 114 params = [_explain_type(param) for param in decl.args.params] 115 args = ', '.join(params) 116 else: 117 args = '' 118 119 return ('function(%s) returning ' % (args) + 120 _explain_type(decl.type)) 121 122 elif typ == c_ast.Struct: 123 decls = [_explain_decl_node(mem_decl) for mem_decl in decl.decls] 124 members = ', '.join(decls) 125 126 return ('struct%s ' % (' ' + decl.name if decl.name else '') + 127 ('containing {%s}' % members if members else '')) 128 129 130def expand_struct_typedef(cdecl, file_ast, 131 expand_struct=False, 132 expand_typedef=False): 133 """Expand struct & typedef and return a new expanded node.""" 134 decl_copy = copy.deepcopy(cdecl) 135 _expand_in_place(decl_copy, file_ast, expand_struct, expand_typedef) 136 return decl_copy 137 138 139def _expand_in_place(decl, file_ast, expand_struct=False, expand_typedef=False): 140 """Recursively expand struct & typedef in place, throw RuntimeError if 141 undeclared struct or typedef are used 142 """ 143 typ = type(decl) 144 145 if typ in (c_ast.Decl, c_ast.TypeDecl, c_ast.PtrDecl, c_ast.ArrayDecl): 146 decl.type = _expand_in_place(decl.type, file_ast, expand_struct, 147 expand_typedef) 148 149 elif typ == c_ast.Struct: 150 if not decl.decls: 151 struct = _find_struct(decl.name, file_ast) 152 if not struct: 153 raise RuntimeError('using undeclared struct %s' % decl.name) 154 decl.decls = struct.decls 155 156 for i, mem_decl in enumerate(decl.decls): 157 decl.decls[i] = _expand_in_place(mem_decl, file_ast, expand_struct, 158 expand_typedef) 159 if not expand_struct: 160 decl.decls = [] 161 162 elif (typ == c_ast.IdentifierType and 163 decl.names[0] not in ('int', 'char')): 164 typedef = _find_typedef(decl.names[0], file_ast) 165 if not typedef: 166 raise RuntimeError('using undeclared type %s' % decl.names[0]) 167 168 if expand_typedef: 169 return typedef.type 170 171 return decl 172 173 174def _find_struct(name, file_ast): 175 """Receives a struct name and return declared struct object in file_ast 176 """ 177 for node in file_ast.ext: 178 if (type(node) == c_ast.Decl and 179 type(node.type) == c_ast.Struct and 180 node.type.name == name): 181 return node.type 182 183 184def _find_typedef(name, file_ast): 185 """Receives a type name and return typedef object in file_ast 186 """ 187 for node in file_ast.ext: 188 if type(node) == c_ast.Typedef and node.name == name: 189 return node 190 191 192if __name__ == "__main__": 193 if len(sys.argv) > 1: 194 c_decl = sys.argv[1] 195 else: 196 c_decl = "char *(*(**foo[][8])())[];" 197 198 print("Explaining the declaration: " + c_decl + "\n") 199 print(explain_c_declaration(c_decl) + "\n") 200