• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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