blob: 92ac715027467315920ef822a017facb01075bae [file] [log] [blame]
eli.benderskyfe5c2bb2010-12-04 17:38:11 +02001#-----------------------------------------------------------------
2# pycparser: cdecl.py
3#
Eli Bendersky27797e82013-09-25 06:30:17 -07004# Example of the CDECL tool using pycparser. CDECL "explains" C type
5# declarations in plain English.
eli.benderskyfe5c2bb2010-12-04 17:38:11 +02006#
Eli Bendersky27797e82013-09-25 06:30:17 -07007# 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. All the types used in it must be defined with
10# typedef, or parsing will fail. The definition can be arbitrary - pycparser
11# doesn't really care what the type is defined to be, only that it's a type.
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020012#
13# For example:
14#
Hart Chub23e2672017-02-26 21:56:44 +080015# c_decl = 'typedef int Node; const Node* (*ar)[10];'
16#
17# explain_c_declaration(c_decl)
18# => ar is a pointer to array[10] of pointer to const Node
19#
20# struct and typedef are expanded when according arguments are set:
21#
22# explain_c_declaration(c_decl, expand_typedef=True)
23# => ar is a pointer to array[10] of pointer to const int
24#
25# c_decl = 'struct P {int x; int y;} p;'
26#
27# explain_c_declaration(c_decl)
28# => p is a struct P
29#
30# explain_c_declaration(c_decl, expand_struct=True)
31# => p is a struct P containing {x is a int, y is a int}
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020032#
Eli Bendersky6d45ff72017-02-02 05:59:50 -080033# Eli Bendersky [http://eli.thegreenplace.net]
eli.bendersky84a6a632011-04-29 09:00:43 +030034# License: BSD
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020035#-----------------------------------------------------------------
Hart Chub23e2672017-02-26 21:56:44 +080036import copy
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020037import sys
38
39# This is not required if you've installed pycparser into
40# your site-packages/ with setup.py
41#
Benf86dea12012-02-03 06:24:55 +020042sys.path.extend(['.', '..'])
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020043
44from pycparser import c_parser, c_ast
45
46
Hart Chub23e2672017-02-26 21:56:44 +080047def explain_c_declaration(c_decl, expand_struct=False, expand_typedef=False):
Eli Bendersky27797e82013-09-25 06:30:17 -070048 """ Parses the declaration in c_decl and returns a text
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020049 explanation as a string.
Eli Bendersky27797e82013-09-25 06:30:17 -070050
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020051 The last external node of the string is used, to allow
Eli Bendersky3921e8e2010-05-21 09:05:39 +030052 earlier typedefs for used types.
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020053 """
54 parser = c_parser.CParser()
Eli Bendersky27797e82013-09-25 06:30:17 -070055
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020056 try:
57 node = parser.parse(c_decl, filename='<stdin>')
58 except c_parser.ParseError:
59 e = sys.exc_info()[1]
60 return "Parse error:" + str(e)
61
Eli Bendersky27797e82013-09-25 06:30:17 -070062 if (not isinstance(node, c_ast.FileAST) or
63 not isinstance(node.ext[-1], c_ast.Decl)
64 ):
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020065 return "Not a valid declaration"
66
Hart Chub23e2672017-02-26 21:56:44 +080067 try:
68 expanded = expand_struct_typedef(node.ext[-1], node,
69 expand_struct=expand_struct,
70 expand_typedef=expand_typedef)
71 except Exception as e:
72 return "Not a valid declaration: " + str(e)
73
74 return _explain_decl_node(expanded)
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020075
76
Eli Bendersky3921e8e2010-05-21 09:05:39 +030077def _explain_decl_node(decl_node):
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020078 """ Receives a c_ast.Decl note and returns its explanation in
Eli Bendersky3921e8e2010-05-21 09:05:39 +030079 English.
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020080 """
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020081 storage = ' '.join(decl_node.storage) + ' ' if decl_node.storage else ''
Eli Bendersky27797e82013-09-25 06:30:17 -070082
83 return (decl_node.name +
84 " is a " +
85 storage +
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020086 _explain_type(decl_node.type))
87
88
Eli Bendersky3921e8e2010-05-21 09:05:39 +030089def _explain_type(decl):
90 """ Recursively explains a type decl node
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020091 """
92 typ = type(decl)
Eli Bendersky27797e82013-09-25 06:30:17 -070093
eli.benderskyfe5c2bb2010-12-04 17:38:11 +020094 if typ == c_ast.TypeDecl:
95 quals = ' '.join(decl.quals) + ' ' if decl.quals else ''
96 return quals + _explain_type(decl.type)
97 elif typ == c_ast.Typename or typ == c_ast.Decl:
98 return _explain_type(decl.type)
99 elif typ == c_ast.IdentifierType:
100 return ' '.join(decl.names)
101 elif typ == c_ast.PtrDecl:
102 quals = ' '.join(decl.quals) + ' ' if decl.quals else ''
103 return quals + 'pointer to ' + _explain_type(decl.type)
104 elif typ == c_ast.ArrayDecl:
105 arr = 'array'
106 if decl.dim: arr += '[%s]' % decl.dim.value
Eli Bendersky27797e82013-09-25 06:30:17 -0700107
eli.benderskyfe5c2bb2010-12-04 17:38:11 +0200108 return arr + " of " + _explain_type(decl.type)
Eli Bendersky27797e82013-09-25 06:30:17 -0700109
eli.benderskyfe5c2bb2010-12-04 17:38:11 +0200110 elif typ == c_ast.FuncDecl:
111 if decl.args:
112 params = [_explain_type(param) for param in decl.args.params]
113 args = ', '.join(params)
114 else:
115 args = ''
Eli Bendersky27797e82013-09-25 06:30:17 -0700116
eli.benderskyfe5c2bb2010-12-04 17:38:11 +0200117 return ('function(%s) returning ' % (args) +
118 _explain_type(decl.type))
119
Hart Chub23e2672017-02-26 21:56:44 +0800120 elif typ == c_ast.Struct:
121 decls = [_explain_decl_node(mem_decl) for mem_decl in decl.decls]
122 members = ', '.join(decls)
123
124 return ('struct%s ' % (' ' + decl.name if decl.name else '') +
125 ('containing {%s}' % members if members else ''))
126
127
128def expand_struct_typedef(cdecl, file_ast, expand_struct=False, expand_typedef=False):
129 """Expand struct & typedef in context of file_ast and return a new expanded node"""
130 decl_copy = copy.deepcopy(cdecl)
131 _expand_in_place(decl_copy, file_ast, expand_struct, expand_typedef)
132 return decl_copy
133
134
135def _expand_in_place(decl, file_ast, expand_struct=False, expand_typedef=False):
136 """Recursively expand struct & typedef in place, throw Exception if
137 undeclared struct or typedef are used
138 """
139 typ = type(decl)
140
141 if typ in (c_ast.Decl, c_ast.TypeDecl, c_ast.PtrDecl, c_ast.ArrayDecl):
142 decl.type = _expand_in_place(decl.type, file_ast, expand_struct, expand_typedef)
143
144 elif typ == c_ast.Struct:
145 if not decl.decls:
146 struct = _find_struct(decl.name, file_ast)
147 if not struct:
148 raise Exception('using undeclared struct %s' % decl.name)
149 decl.decls = struct.decls
150
151 for i, mem_decl in enumerate(decl.decls):
152 decl.decls[i] = _expand_in_place(mem_decl, file_ast, expand_struct, expand_typedef)
153
154 if not expand_struct:
155 decl.decls = []
156
157 elif (typ == c_ast.IdentifierType and
158 decl.names[0] not in ('int', 'char')):
159 typedef = _find_typedef(decl.names[0], file_ast)
160 if not typedef:
161 raise Exception('using undeclared type %s' % decl.names[0])
162
163 if expand_typedef:
164 return typedef.type
165
166 return decl
167
168
169def _find_struct(name, file_ast):
170 """Receives a struct name and return declared struct object in file_ast
171 """
172 for node in file_ast.ext:
173 if (type(node) == c_ast.Decl and
174 type(node.type) == c_ast.Struct and
175 node.type.name == name):
176 return node.type
177
178
179def _find_typedef(name, file_ast):
180 """Receives a type name and return typedef object in file_ast
181 """
182 for node in file_ast.ext:
183 if type(node) == c_ast.Typedef and node.name == name:
184 return node
185
eli.benderskyfe5c2bb2010-12-04 17:38:11 +0200186
187if __name__ == "__main__":
188 if len(sys.argv) > 1:
189 c_decl = sys.argv[1]
190 else:
191 c_decl = "char *(*(**foo[][8])())[];"
192
193 print("Explaining the declaration: " + c_decl + "\n")
Eli Bendersky27797e82013-09-25 06:30:17 -0700194 print(explain_c_declaration(c_decl) + "\n")