| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- #-----------------------------------------------------------------
- # 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 [http://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")
|