You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
6.3 KiB
200 lines
6.3 KiB
#-----------------------------------------------------------------
|
|
# 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")
|