blob: 4ecdbca0e68d72ad0684b347f4c2f96f173684c9 [file] [log] [blame]
eli.benderskyd5ba3452011-02-18 21:32:47 +02001#-----------------------------------------------------------------
2# pycparser: c-to-c.py
3#
eli.benderskyafcfaac2011-02-25 16:46:01 +02004# Example of a C code generator from pycparser AST nodes, serving
5# as a simplistic translator from C to AST and back to C.
eli.benderskyd5ba3452011-02-18 21:32:47 +02006#
7# Copyright (C) 2008-2011, Eli Bendersky
8# License: LGPL
9#-----------------------------------------------------------------
10from __future__ import print_function
11import sys
12
13# This is not required if you've installed pycparser into
14# your site-packages/ with setup.py
15#
16sys.path.insert(0, '..')
17
18from pycparser import c_parser, c_ast, parse_file
19
20
eli.benderskyafcfaac2011-02-25 16:46:01 +020021class CGenerator(object):
22 """ Uses the same visitor pattern as c_ast.NodeVisitor, but modified to
23 return a value from each visit method, using string accumulation in
24 generic_visit.
25 """
eli.benderskyd5ba3452011-02-18 21:32:47 +020026 def __init__(self):
27 self.output = ''
28
eli.bendersky3ae9f7a2011-02-27 07:19:41 +020029 # Statements start with indentation of self.indent_level spaces, using
30 # the _make_indent method
eli.benderskyafcfaac2011-02-25 16:46:01 +020031 #
32 self.indent_level = 0
33
eli.bendersky3ae9f7a2011-02-27 07:19:41 +020034 def _make_indent(self):
35 return ' ' * self.indent_level
36
eli.benderskyafcfaac2011-02-25 16:46:01 +020037 def visit(self, node):
38 method = 'visit_' + node.__class__.__name__
39 return getattr(self, method, self.generic_visit)(node)
40
41 def generic_visit(self, node):
42 #~ print('generic:', type(node))
43 if node is None:
44 return ''
45 else:
46 return ''.join(self.visit(c) for c in node.children())
eli.benderskyd5ba3452011-02-18 21:32:47 +020047
48 def visit_Constant(self, n):
eli.benderskyafcfaac2011-02-25 16:46:01 +020049 return n.value
50
eli.benderskyd5ba3452011-02-18 21:32:47 +020051 def visit_ID(self, n):
eli.benderskyafcfaac2011-02-25 16:46:01 +020052 return n.name
eli.bendersky3ae9f7a2011-02-27 07:19:41 +020053
54 def visit_ArrayRef(self, n):
55 return self.visit(n.name) + '[' + self.visit(n.subscript) + ']'
56
eli.bendersky724b1cc2011-03-05 10:45:08 +020057 def visit_StructRef(self, n):
58 return self.visit(n.name) + n.type + self.visit(n.field)
59
60 def visit_FuncCall(self, n):
61 return self.visit(n.name) + '(' + self.visit(n.args) + ')'
62
eli.benderskyae36e962011-02-27 08:25:05 +020063 def visit_UnaryOp(self, n):
64 if n.op == 'p++':
65 return '%s++' % self.visit(n.expr)
66 else:
67 return '%s%s' % (n.op, self.visit(n.expr))
68
eli.bendersky3ae9f7a2011-02-27 07:19:41 +020069 def visit_BinaryOp(self, n):
eli.bendersky724b1cc2011-03-05 10:45:08 +020070 # To avoid operator precedence issues, parenthesize both operands unless
71 # they're "simple" nodes that have precedence over all binary ops.
72 #
73 simplenodes = (
74 c_ast.Constant, c_ast.ID, c_ast.ArrayRef, c_ast.StructRef,
75 c_ast.FuncCall)
76 isnotsimple = lambda n: not isinstance(n, simplenodes)
77 lval_str = self._parenthesize_if(n.left, isnotsimple)
78 rval_str = self._parenthesize_if(n.right, isnotsimple)
79 return '%s %s %s' % (lval_str, n.op, rval_str)
eli.bendersky3ae9f7a2011-02-27 07:19:41 +020080
81 def visit_Assignment(self, n):
eli.bendersky724b1cc2011-03-05 10:45:08 +020082 rval_str = self._parenthesize_if(
83 n.rvalue,
84 lambda n: isinstance(n, c_ast.Assignment))
85 return '%s %s %s' % (self.visit(n.lvalue), n.op, rval_str)
eli.benderskyd5ba3452011-02-18 21:32:47 +020086
87 def visit_IdentifierType(self, n):
eli.bendersky3ae9f7a2011-02-27 07:19:41 +020088 return ' '.join(n.names)
eli.benderskyd5ba3452011-02-18 21:32:47 +020089
eli.benderskyafcfaac2011-02-25 16:46:01 +020090 def visit_Decl(self, n):
91 s = self._generate_decl(n)
92 if n.bitsize: s += ' : ' + self.visit(n.bitsize)
93 if n.init: s += ' = ' + self.visit(n.init)
94 return s
eli.benderskyd5ba3452011-02-18 21:32:47 +020095
eli.bendersky3ae9f7a2011-02-27 07:19:41 +020096 def visit_Typedef(self, n):
97 s = ''
98 if n.storage: s += ' '.join(n.storage) + ' '
99 s += self._generate_type(n.type)
100 return s
101
102 def visit_Cast(self, n):
103 s = '(' + self.visit(n.to_type) + ')'
104 return s + ' ' + self.visit(n.expr)
105
eli.bendersky724b1cc2011-03-05 10:45:08 +0200106 def visit_ExprList(self, n):
107 return ', '.join(self.visit(expr) for expr in n.exprs)
108
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200109 def visit_Enum(self, n):
110 s = 'enum'
111 if n.name: s += ' ' + n.name
112 if n.values:
113 s += ' {'
114 for i, enumerator in enumerate(n.values.enumerators):
115 s += enumerator.name
116 if enumerator.value:
117 s += ' = ' + self.visit(enumerator.value)
118 if i != len(n.values.enumerators) - 1:
119 s += ', '
120 s += '}'
121 return s
122
123 def visit_Struct(self, n):
124 s = 'struct'
125 if n.name: s += ' ' + n.name
126 if n.decls:
127 s += ' { \n'
128 for decl in n.decls:
129 s += ' ' + self.visit(decl) + ';\n'
130 s += '}'
131 return s
132
eli.benderskyafcfaac2011-02-25 16:46:01 +0200133 def visit_FuncDef(self, n):
134 decl = self.visit(n.decl)
135 self.indent_level = 0
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200136 # The body is a Compound node
eli.benderskyafcfaac2011-02-25 16:46:01 +0200137 body = self.visit(n.body)
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200138 return decl + '\n' + body + '\n'
139
140 def visit_FileAST(self, n):
141 s = ''
142 for ext in n.ext:
143 if isinstance(ext, c_ast.FuncDef):
144 s += self.visit(ext)
145 else:
146 s += self.visit(ext) + ';\n'
147 return s
148
149 def visit_Compound(self, n):
150 s = self._make_indent() + '{\n'
151 self.indent_level += 2
152 s += ''.join(self._generate_stmt(stmt) for stmt in n.block_items)
153 self.indent_level -= 2
154 s += self._make_indent() + '}\n'
155 return s
156
157 def visit_ParamList(self, n):
158 return ', '.join(self.visit(param) for param in n.params)
159
160 def visit_Return(self, n):
161 s = 'return'
162 if n.expr: s += ' ' + self.visit(n.expr)
163 return s + ';'
164
eli.benderskyfc96e5e2011-03-04 09:51:23 +0200165 def visit_Break(self, n):
166 return 'break;'
167
168 def visit_Continue(self, n):
169 return 'continue;'
eli.bendersky724b1cc2011-03-05 10:45:08 +0200170
171 def visit_TernaryOp(self, n):
172 s = self.visit(n.cond) + ' ? '
173 s += self.visit(n.iftrue) + ' : '
174 s += self.visit(n.iffalse)
175 return s
176
eli.benderskyae36e962011-02-27 08:25:05 +0200177 def visit_For(self, n):
178 s = 'for ('
179 if n.init: s += self.visit(n.init)
180 s += ';'
181 if n.cond: s += ' ' + self.visit(n.cond)
182 s += ';'
183 if n.next: s += ' ' + self.visit(n.next)
184 s += ')\n'
185 s += self._generate_stmt(n.stmt, add_indent=True)
186 return s
187
eli.benderskyfc96e5e2011-03-04 09:51:23 +0200188 def visit_While(self, n):
189 s = 'while ('
190 if n.cond: s += self.visit(n.cond)
191 s += ')\n'
192 s += self._generate_stmt(n.stmt, add_indent=True)
193 return s
194
eli.benderskyae36e962011-02-27 08:25:05 +0200195 def _generate_stmt(self, n, add_indent=False):
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200196 """ Generation from a statement node. This method exists as a wrapper
197 for individual visit_* methods to handle different treatment of
198 some statements in this context.
199 """
200 typ = type(n)
eli.benderskyae36e962011-02-27 08:25:05 +0200201 if add_indent: self.indent_level += 2
eli.benderskyfc96e5e2011-03-04 09:51:23 +0200202 indent = self._make_indent()
eli.benderskyae36e962011-02-27 08:25:05 +0200203 if add_indent: self.indent_level -= 2
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200204
eli.bendersky724b1cc2011-03-05 10:45:08 +0200205 if typ in (
206 c_ast.Decl, c_ast.Assignment, c_ast.Cast, c_ast.UnaryOp,
207 c_ast.BinaryOp, c_ast.TernaryOp, c_ast.FuncCall):
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200208 # These can also appear in an expression context so no semicolon
209 # is added to them automatically
210 #
eli.benderskyfc96e5e2011-03-04 09:51:23 +0200211 return indent + self.visit(n) + ';\n'
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200212 elif typ in (c_ast.Compound,):
213 # No extra indentation required before the opening brace of a
214 # compound - because it consists of multiple lines it has to
215 # compute its own indentation.
216 #
eli.benderskyae36e962011-02-27 08:25:05 +0200217 return self.visit(n)
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200218 else:
eli.benderskyfc96e5e2011-03-04 09:51:23 +0200219 return indent + self.visit(n) + '\n'
eli.benderskyafcfaac2011-02-25 16:46:01 +0200220
eli.benderskyd5ba3452011-02-18 21:32:47 +0200221 def _generate_decl(self, n):
222 """ Generation from a Decl node.
223 """
eli.benderskyafcfaac2011-02-25 16:46:01 +0200224 s = ''
225 if n.funcspec: s = ' '.join(n.funcspec) + ' '
226 if n.storage: s += ' '.join(n.storage) + ' '
227 s += self._generate_type(n.type)
228 return s
eli.benderskyd5ba3452011-02-18 21:32:47 +0200229
230 def _generate_type(self, n, modifiers=[]):
231 """ Recursive generation from a type node. n is the type node.
eli.benderskyafcfaac2011-02-25 16:46:01 +0200232 modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers
233 encountered on the way down to a TypeDecl, to allow proper
234 generation from it.
eli.benderskyd5ba3452011-02-18 21:32:47 +0200235 """
236 typ = type(n)
237 #~ print(n, modifiers)
238
239 if typ == c_ast.TypeDecl:
eli.benderskyafcfaac2011-02-25 16:46:01 +0200240 s = ''
241 if n.quals: s += ' '.join(n.quals) + ' '
242 s += self.visit(n.type)
eli.benderskyd5ba3452011-02-18 21:32:47 +0200243
244 nstr = n.declname if n.declname else ''
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200245 # Resolve modifiers.
eli.benderskyafcfaac2011-02-25 16:46:01 +0200246 # Wrap in parens to distinguish pointer to array and pointer to
247 # function syntax.
eli.benderskyd5ba3452011-02-18 21:32:47 +0200248 #
249 for i, modifier in enumerate(modifiers):
250 if isinstance(modifier, c_ast.ArrayDecl):
251 if (i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl)):
252 nstr = '(' + nstr + ')'
eli.benderskyafcfaac2011-02-25 16:46:01 +0200253 nstr += '[' + self.visit(modifier.dim) + ']'
254 elif isinstance(modifier, c_ast.FuncDecl):
255 if (i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl)):
256 nstr = '(' + nstr + ')'
257 nstr += '(' + self.visit(modifier.args) + ')'
eli.benderskyd5ba3452011-02-18 21:32:47 +0200258 elif isinstance(modifier, c_ast.PtrDecl):
259 nstr = '*' + nstr
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200260 s += ' ' + nstr
eli.benderskyafcfaac2011-02-25 16:46:01 +0200261 return s
eli.benderskyd5ba3452011-02-18 21:32:47 +0200262 elif typ in (c_ast.Typename, c_ast.Decl):
eli.benderskyafcfaac2011-02-25 16:46:01 +0200263 return self._generate_decl(n.type)
eli.benderskyd5ba3452011-02-18 21:32:47 +0200264 elif typ == c_ast.IdentifierType:
eli.benderskyafcfaac2011-02-25 16:46:01 +0200265 return ' '.join(n.names) + ' '
266 elif typ in (c_ast.ArrayDecl, c_ast.PtrDecl, c_ast.FuncDecl):
267 return self._generate_type(n.type, modifiers + [n])
eli.benderskyd5ba3452011-02-18 21:32:47 +0200268
eli.bendersky724b1cc2011-03-05 10:45:08 +0200269 def _parenthesize_if(self, n, condition):
270 """ Visits 'n' and returns its string representation, parenthesized
271 if the condition function applied to the node returns True.
272 """
273 s = self.visit(n)
274 if condition(n):
275 return '(' + s + ')'
276 else:
277 return s
278
eli.benderskyd5ba3452011-02-18 21:32:47 +0200279
280def translate_to_c(filename):
281 ast = parse_file(filename, use_cpp=True)
282 generator = CGenerator()
eli.benderskyafcfaac2011-02-25 16:46:01 +0200283 print(generator.visit(ast))
eli.benderskyd5ba3452011-02-18 21:32:47 +0200284
285
286if __name__ == "__main__":
287 if len(sys.argv) > 1:
288 translate_to_c(sys.argv[1])
289 else:
290 src = r'''
eli.bendersky724b1cc2011-03-05 10:45:08 +0200291typedef int koe;
eli.benderskyae36e962011-02-27 08:25:05 +0200292static unsigned int hash_func(const char* str, unsigned int table_size)
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200293{
eli.bendersky724b1cc2011-03-05 10:45:08 +0200294 print(joe ? moe : baba[4]);
eli.benderskyae36e962011-02-27 08:25:05 +0200295 a++;
296 ++a;
eli.bendersky724b1cc2011-03-05 10:45:08 +0200297
298 a = b->kwa = c.c + 2;
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200299
eli.benderskyae36e962011-02-27 08:25:05 +0200300 return hash_value;
301}
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200302
eli.benderskyd5ba3452011-02-18 21:32:47 +0200303 '''
304 parser = c_parser.CParser()
305 ast = parser.parse(src)
306 ast.show()
307 generator = CGenerator()
eli.benderskyafcfaac2011-02-25 16:46:01 +0200308 print(generator.visit(ast))
eli.benderskyd5ba3452011-02-18 21:32:47 +0200309
310 print("Please provide a filename as argument")
eli.bendersky3ae9f7a2011-02-27 07:19:41 +0200311
312
eli.benderskyfc96e5e2011-03-04 09:51:23 +0200313# ZZZ: turn self.indent_level += 2 ... -= 2 into a context manager!
314