Total Complexity | 90 |
Total Lines | 259 |
Duplicated Lines | 0 % |
Complex classes like CppTranspiler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | import sys |
||
70 | class CppTranspiler(CLikeTranspiler): |
||
71 | def __init__(self): |
||
72 | self.headers = set(['#include "sys.h"', '#include "builtins.h"', |
||
73 | '#include <iostream>', '#include <string>', |
||
74 | '#include <algorithm>', '#include <cmath>', |
||
75 | '#include <vector>', '#include <tuple>', |
||
76 | '#include <utility>', '#include "range.hpp"']) |
||
77 | self.usings = set([]) |
||
78 | self.use_catch_test_cases = False |
||
79 | |||
80 | def visit_FunctionDef(self, node): |
||
81 | body = "\n".join([self.visit(n) for n in node.body]) |
||
82 | |||
83 | if (self.use_catch_test_cases and |
||
84 | is_void_function(node) and |
||
85 | node.name.startswith("test")): |
||
86 | return generate_catch_test_case(node, body) |
||
87 | # is_void_function(node) or is_recursive(node): |
||
88 | return generate_template_fun(node, body) |
||
89 | # else: |
||
90 | # return generate_lambda_fun(node, body) |
||
91 | |||
92 | def visit_Attribute(self, node): |
||
93 | attr = node.attr |
||
94 | value_id = get_id(node.value) |
||
95 | if is_builtin_import(value_id): |
||
96 | return "py14::" + value_id + "::" + attr |
||
97 | elif value_id == "math": |
||
98 | if node.attr == "asin": |
||
99 | return "std::asin" |
||
100 | elif node.attr == "atan": |
||
101 | return "std::atan" |
||
102 | elif node.attr == "acos": |
||
103 | return "std::acos" |
||
104 | |||
105 | if is_list(node.value): |
||
106 | if node.attr == "append": |
||
107 | attr = "push_back" |
||
108 | return value_id + "." + attr |
||
109 | |||
110 | def visit_Call(self, node): |
||
111 | fname = self.visit(node.func) |
||
112 | if node.args: |
||
113 | args = [self.visit(a) for a in node.args] |
||
114 | args = ", ".join(args) |
||
115 | else: |
||
116 | args = '' |
||
117 | |||
118 | if fname == "int": |
||
119 | return "py14::to_int({0})".format(args) |
||
120 | elif fname == "str": |
||
121 | return "std::to_string({0})".format(args) |
||
122 | elif fname == "max": |
||
123 | return "std::max({0})".format(args) |
||
124 | elif fname == "range": |
||
125 | if sys.version_info[0] >= 3: |
||
126 | return "rangepp::xrange({0})".format(args) |
||
127 | else: |
||
128 | return "rangepp::range({0})".format(args) |
||
129 | elif fname == "xrange": |
||
130 | return "rangepp::xrange({0})".format(args) |
||
131 | elif fname == "len": |
||
132 | return "{0}.size()".format(self.visit(node.args[0])) |
||
133 | elif fname == "print": |
||
134 | buf = [] |
||
135 | for n in node.args: |
||
136 | value = self.visit(n) |
||
137 | if isinstance(n, ast.List) or isinstance(n, ast.Tuple): |
||
138 | buf.append("std::cout << {0} << std::endl;".format( |
||
139 | " << ".join([self.visit(el) for el in n.elts]))) |
||
140 | else: |
||
141 | buf.append('std::cout << {0} << std::endl;'.format(value)) |
||
142 | return '\n'.join(buf) |
||
143 | |||
144 | return '{0}({1})'.format(fname, args) |
||
145 | |||
146 | def visit_For(self, node): |
||
147 | target = self.visit(node.target) |
||
148 | it = self.visit(node.iter) |
||
149 | buf = [] |
||
150 | buf.append('for(auto {0} : {1}) {{'.format(target, it)) |
||
151 | buf.extend([self.visit(c) for c in node.body]) |
||
152 | buf.append("}") |
||
153 | return "\n".join(buf) |
||
154 | |||
155 | def visit_Expr(self, node): |
||
156 | s = self.visit(node.value) |
||
157 | if s.strip() and not s.endswith(';'): |
||
158 | s += ';' |
||
159 | if s == ';': |
||
160 | return '' |
||
161 | else: |
||
162 | return s |
||
163 | |||
164 | def visit_Str(self, node): |
||
165 | """Use a C++ 14 string literal instead of raw string""" |
||
166 | return ("std::string {" + |
||
167 | super(CppTranspiler, self).visit_Str(node) + "}") |
||
168 | |||
169 | def visit_Name(self, node): |
||
170 | if node.id == 'None': |
||
171 | return 'nullptr' |
||
172 | else: |
||
173 | return super(CppTranspiler, self).visit_Name(node) |
||
174 | |||
175 | def visit_NameConstant(self, node): |
||
176 | if node.value is True: |
||
177 | return "true" |
||
178 | elif node.value is False: |
||
179 | return "false" |
||
180 | else: |
||
181 | return super(CppTranspiler, self).visit_NameConstant(node) |
||
182 | |||
183 | def visit_If(self, node): |
||
184 | body_vars = set([get_id(v) for v in node.scopes[-1].body_vars]) |
||
185 | orelse_vars = set([get_id(v) for v in node.scopes[-1].orelse_vars]) |
||
186 | node.common_vars = body_vars.intersection(orelse_vars) |
||
187 | |||
188 | var_definitions = [] |
||
189 | for cv in node.common_vars: |
||
190 | definition = node.scopes.find(cv) |
||
191 | var_type = decltype(definition) |
||
192 | var_definitions.append("{0} {1};\n".format(var_type, cv)) |
||
193 | |||
194 | if self.visit(node.test) == '__name__ == std::string {"__main__"}': |
||
195 | buf = ["int main(int argc, char ** argv) {", |
||
196 | "py14::sys::argv = " |
||
197 | "std::vector<std::string>(argv, argv + argc);"] |
||
198 | buf.extend([self.visit(child) for child in node.body]) |
||
199 | buf.append("}") |
||
200 | return "\n".join(buf) |
||
201 | else: |
||
202 | return ("".join(var_definitions) + |
||
203 | super(CppTranspiler, self).visit_If(node)) |
||
204 | |||
205 | def visit_UnaryOp(self, node): |
||
206 | if isinstance(node.op, ast.USub): |
||
207 | if isinstance(node.operand, (ast.Call, ast.Num)): |
||
208 | # Shortcut if parenthesis are not needed |
||
209 | return "-{0}".format(self.visit(node.operand)) |
||
210 | else: |
||
211 | return "-({0})".format(self.visit(node.operand)) |
||
212 | else: |
||
213 | return super(CppTranspiler, self).visit_UnaryOp(node) |
||
214 | |||
215 | def visit_BinOp(self, node): |
||
216 | if (isinstance(node.left, ast.List) |
||
217 | and isinstance(node.op, ast.Mult) |
||
218 | and isinstance(node.right, ast.Num)): |
||
219 | return "std::vector ({0},{1})".format(self.visit(node.right), |
||
220 | self.visit(node.left.elts[0])) |
||
221 | else: |
||
222 | return super(CppTranspiler, self).visit_BinOp(node) |
||
223 | |||
224 | def visit_Module(self, node): |
||
225 | buf = [self.visit(b) for b in node.body] |
||
226 | return "\n".join(buf) |
||
227 | |||
228 | def visit_alias(self, node): |
||
229 | return '#include "{0}.h"'.format(node.name) |
||
230 | |||
231 | def visit_Import(self, node): |
||
232 | imports = [self.visit(n) for n in node.names] |
||
233 | return "\n".join(i for i in imports if i) |
||
234 | |||
235 | def visit_List(self, node): |
||
236 | if len(node.elts) > 0: |
||
237 | elements = [self.visit(e) for e in node.elts] |
||
238 | value_type = decltype(node.elts[0]) |
||
239 | return "std::vector<{0}>{{{1}}}".format(value_type, |
||
240 | ", ".join(elements)) |
||
241 | |||
242 | else: |
||
243 | raise ValueError("Cannot create vector without elements") |
||
244 | |||
245 | def visit_Subscript(self, node): |
||
246 | if isinstance(node.slice, ast.Ellipsis): |
||
247 | raise NotImplementedError('Ellipsis not supported') |
||
248 | |||
249 | if not isinstance(node.slice, ast.Index): |
||
250 | raise NotImplementedError("Advanced Slicing not supported") |
||
251 | |||
252 | value = self.visit(node.value) |
||
253 | return "{0}[{1}]".format(value, self.visit(node.slice.value)) |
||
254 | |||
255 | def visit_Tuple(self, node): |
||
256 | elts = [self.visit(e) for e in node.elts] |
||
257 | return "std::make_tuple({0})".format(", ".join(elts)) |
||
258 | |||
259 | def visit_TryExcept(self, node, finallybody=None): |
||
260 | buf = ['try {'] |
||
261 | buf += [self.visit(n) for n in node.body] |
||
262 | buf.append('} catch (const std::exception& e) {') |
||
263 | |||
264 | buf += [self.visit(h) for h in node.handlers] |
||
265 | |||
266 | if finallybody: |
||
267 | buf.append('try { // finally') |
||
268 | buf += [self.visit(b) for b in finallybody] |
||
269 | buf.append('} throw e;') |
||
270 | |||
271 | buf.append('}') |
||
272 | buf.append('catch (const std::overflow_error& e) ' |
||
273 | '{ std::cout << "OVERFLOW ERROR" << std::endl; }') |
||
274 | buf.append('catch (const std::runtime_error& e) ' |
||
275 | '{ std::cout << "RUNTIME ERROR" << std::endl; }') |
||
276 | buf.append('catch (...) ' |
||
277 | '{ std::cout << "UNKNOWN ERROR" << std::endl; 0}') |
||
278 | |||
279 | return '\n'.join(buf) |
||
280 | |||
281 | def visit_Assert(self, node): |
||
282 | return "REQUIRE({0});".format(self.visit(node.test)) |
||
283 | |||
284 | def visit_Assign(self, node): |
||
285 | target = node.targets[0] |
||
286 | |||
287 | if isinstance(target, ast.Tuple): |
||
288 | elts = [self.visit(e) for e in target.elts] |
||
289 | value = self.visit(node.value) |
||
290 | return "std::tie({0}) = {1};".format(", ".join(elts), value) |
||
291 | |||
292 | if isinstance(node.scopes[-1], ast.If): |
||
293 | outer_if = node.scopes[-1] |
||
294 | if target.id in outer_if.common_vars: |
||
295 | value = self.visit(node.value) |
||
296 | return "{0} = {1};".format(target.id, value) |
||
297 | |||
298 | if isinstance(target, ast.Subscript): |
||
299 | target = self.visit(target) |
||
300 | value = self.visit(node.value) |
||
301 | return "{0} = {1};".format(target, value) |
||
302 | |||
303 | definition = node.scopes.find(target.id) |
||
304 | if (isinstance(target, ast.Name) and |
||
305 | defined_before(definition, node)): |
||
306 | target = self.visit(target) |
||
307 | value = self.visit(node.value) |
||
308 | return "{0} = {1};".format(target, value) |
||
309 | elif isinstance(node.value, ast.List): |
||
310 | elements = [self.visit(e) for e in node.value.elts] |
||
311 | return "{0} {1} {{{2}}};".format(decltype(node), |
||
312 | self.visit(target), |
||
313 | ", ".join(elements)) |
||
314 | else: |
||
315 | target = self.visit(target) |
||
316 | value = self.visit(node.value) |
||
317 | return "auto {0} = {1};".format(target, value) |
||
318 | |||
319 | def visit_Print(self, node): |
||
320 | buf = [] |
||
321 | for n in node.values: |
||
322 | value = self.visit(n) |
||
323 | if isinstance(n, ast.List) or isinstance(n, ast.Tuple): |
||
324 | buf.append("std::cout << {0} << std::endl;".format( |
||
325 | " << ".join([self.visit(el) for el in n.elts]))) |
||
326 | else: |
||
327 | buf.append('std::cout << {0} << std::endl;'.format(value)) |
||
328 | return '\n'.join(buf) |
||
329 |