| #----------------------------------------------------------------- |
| # 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='<stdin>') |
| 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") |